diff --git a/EXAMPLE.md b/EXAMPLE.md new file mode 100644 index 0000000..3f0e920 --- /dev/null +++ b/EXAMPLE.md @@ -0,0 +1,54 @@ +# Configuration Examples + +Complete examples for the two runtime JSON files generated/used by Overzicht. + +## settings.json + +Path: `~/.config/overzicht/settings.json` + +```json +{ + "overview": { + "rows": 2, + "columns": 4, + "scale": 0.12, + "enable": true, + "hideEmptyRows": false + }, + "hacks": { + "arbitraryRaceConditionDelay": 150 + } +} +``` + +## colors.json + +Path: `~/.config/overzicht/colors.json` + +```json +{ + "m3primary": "#fb4934", + "m3onPrimary": "#282828", + "m3primaryContainer": "#3c3836", + "m3onPrimaryContainer": "#fbf1c7", + "m3onSecondary": "#282828", + "m3secondaryContainer": "#3c3836", + "m3onSecondaryContainer": "#fbf1c7", + "m3onBackground": "#ebdbb2", + "m3surface": "#282828", + "m3surfaceContainerHigh": "#3c3836", + "m3surfaceContainerHighest": "#504945", + "m3surfaceVariant": "#504945", + "m3background": "#3c3836", + "m3secondary": "#b8bb26", + "m3surfaceContainerLow": "#282828", + "m3surfaceContainer": "#b8bb26", + "m3onSurface": "#282828", + "m3onSurfaceVariant": "#fbf1c7", + "m3inverseSurface": "#504945", + "m3inverseOnSurface": "#fbf1c7", + "m3outline": "#fbf1c7", + "m3outlineVariant": "#665c54", + "m3shadow": "#282828" +} +``` diff --git a/README.md b/README.md index 509069d..9a4228a 100644 --- a/README.md +++ b/README.md @@ -30,9 +30,8 @@ Nix is the only supported installation path. Compared to the quickshell-overview project, the following functionality was removed: -- Matugen-driven dynamic color loading (`useMatugenColors` and `Appearance.colors.qml` template flow). -- Smart row hiding (`hideEmptyRows`) for empty workspace rows. -- Row-constrained left/right keyboard navigation that depended on `hideEmptyRows`. +- Matugen-driven dynamic color loading + (`useMatugenColors` and `Appearance.colors.qml` template flow). ## Flake Outputs @@ -56,20 +55,16 @@ Runtime files generated by the module: - `~/.config/overzicht/settings.json` - `~/.config/overzicht/colors.json` +- Complete examples: [`EXAMPLE.md`](EXAMPLE.md) `settings.json` controls layout and behavior. `colors.json` controls the full `m3*` palette used in `common/Appearance.qml`. -Minimal Home Manager example: +Optional setting: `overview.hideEmptyRows` (default `false`) hides workspace +rows in the active group that contain no windows, while keeping the current +workspace row visible. -```nix -{ - programs.overzicht = { - enable = true; - systemd.enable = true; - }; -} -``` +For full JSON examples of both runtime files, see [`EXAMPLE.md`](EXAMPLE.md). ## NixOS Module diff --git a/common/Config.qml b/common/Config.qml index 71ee180..ac47012 100644 --- a/common/Config.qml +++ b/common/Config.qml @@ -60,6 +60,7 @@ Singleton { property int columns: root.optionValue(["overview", "columns"], 4) property real scale: root.optionValue(["overview", "scale"], 0.12) property bool enable: root.optionValue(["overview", "enable"], true) + property bool hideEmptyRows: root.optionValue(["overview", "hideEmptyRows"], false) } property QtObject hacks: QtObject { diff --git a/modules/overview/Overview.qml b/modules/overview/Overview.qml index 13506dc..b9f674d 100644 --- a/modules/overview/Overview.qml +++ b/modules/overview/Overview.qml @@ -92,17 +92,26 @@ Scope { const currentGroup = Math.floor((currentId - 1) / workspacesPerGroup); const minWorkspaceId = currentGroup * workspacesPerGroup + 1; const maxWorkspaceId = minWorkspaceId + workspacesPerGroup - 1; + const currentRow = Math.floor((currentId - minWorkspaceId) / Config.options.overview.columns); + const rowMinId = minWorkspaceId + currentRow * Config.options.overview.columns; + const rowMaxId = rowMinId + Config.options.overview.columns - 1; let targetId = null; // Arrow keys and vim-style hjkl if (event.key === Qt.Key_Left || event.key === Qt.Key_H) { targetId = currentId - 1; - if (targetId < minWorkspaceId) + if (Config.options.overview.hideEmptyRows) { + if (targetId < rowMinId) + targetId = rowMaxId; + } else if (targetId < minWorkspaceId) targetId = maxWorkspaceId; } else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) { targetId = currentId + 1; - if (targetId > maxWorkspaceId) + if (Config.options.overview.hideEmptyRows) { + if (targetId > rowMaxId) + targetId = rowMinId; + } else if (targetId > maxWorkspaceId) targetId = minWorkspaceId; } else if (event.key === Qt.Key_Up || event.key === Qt.Key_K) { targetId = currentId - Config.options.overview.columns; diff --git a/modules/overview/OverviewWidget.qml b/modules/overview/OverviewWidget.qml index 9ebd84b..e86a21b 100644 --- a/modules/overview/OverviewWidget.qml +++ b/modules/overview/OverviewWidget.qml @@ -36,6 +36,44 @@ Item { property int draggingFromWorkspace: -1 property int draggingTargetWorkspace: -1 + property bool hideEmptyRows: Config.options.overview.hideEmptyRows + + property var rowsWithContent: { + if (!root.hideEmptyRows) + return null; + + let rows = new Set(); + const firstWorkspace = root.workspaceGroup * root.workspacesShown + 1; + const lastWorkspace = (root.workspaceGroup + 1) * root.workspacesShown; + + const currentWorkspace = monitor.activeWorkspace?.id ?? 1; + if (currentWorkspace >= firstWorkspace && currentWorkspace <= lastWorkspace) + rows.add(Math.floor((currentWorkspace - firstWorkspace) / Config.options.overview.columns)); + + for (let addr in windowByAddress) { + const win = windowByAddress[addr]; + const wsId = win?.workspace?.id; + if (wsId >= firstWorkspace && wsId <= lastWorkspace) + rows.add(Math.floor((wsId - firstWorkspace) / Config.options.overview.columns)); + } + + return rows; + } + + property var visibleRows: { + if (!root.hideEmptyRows || !root.rowsWithContent) + return null; + + return Array.from(root.rowsWithContent).sort((a, b) => a - b); + } + + function mappedRowIndex(rowIndex) { + if (!root.hideEmptyRows || !root.visibleRows) + return rowIndex; + + const mappedIndex = root.visibleRows.indexOf(rowIndex); + return mappedIndex === -1 ? rowIndex : mappedIndex; + } implicitWidth: overviewBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: overviewBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 @@ -71,6 +109,8 @@ Item { id: row property int rowIndex: index spacing: workspaceSpacing + visible: !root.hideEmptyRows || (root.rowsWithContent && root.rowsWithContent.has(rowIndex)) + height: visible ? implicitHeight : 0 Repeater { // Workspace repeater @@ -200,8 +240,9 @@ Item { property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.columns property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.columns) + property int workspaceVisibleRowIndex: root.mappedRowIndex(workspaceRowIndex) xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex - yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex + yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceVisibleRowIndex Timer { id: updateWindowPosition @@ -275,7 +316,7 @@ Item { property int activeWorkspaceRowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.columns) property int activeWorkspaceColIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.columns x: (root.workspaceImplicitWidth + workspaceSpacing) * activeWorkspaceColIndex - y: (root.workspaceImplicitHeight + workspaceSpacing) * activeWorkspaceRowIndex + y: (root.workspaceImplicitHeight + workspaceSpacing) * root.mappedRowIndex(activeWorkspaceRowIndex) z: root.windowZ width: root.workspaceImplicitWidth height: root.workspaceImplicitHeight diff --git a/nix/home-module.nix b/nix/home-module.nix index e6e3ad1..8243e9a 100644 --- a/nix/home-module.nix +++ b/nix/home-module.nix @@ -45,6 +45,7 @@ in { columns = 4; scale = 0.12; enable = true; + hideEmptyRows = false; }; hacks.arbitraryRaceConditionDelay = 150;