From 7abff2516fdd3f4216f07784458e7fe994875552 Mon Sep 17 00:00:00 2001 From: adam01110 Date: Sat, 28 Feb 2026 15:44:00 +0100 Subject: [PATCH] overzicht --- .gitignore | 1 + README.md | 309 ++++-------------------- common/Appearance.qml | 135 +++++++---- common/Config.qml | 64 ++++- common/functions/ColorUtils.qml | 7 +- common/widgets/StyledToolTip.qml | 2 +- common/widgets/StyledToolTipContent.qml | 6 +- flake.nix | 17 ++ modules/overview/Overview.qml | 45 ++-- modules/overview/OverviewWidget.qml | 168 +++++-------- modules/overview/OverviewWindow.qml | 14 +- nix/home-module.nix | 118 +++++++++ nix/nixos-module.nix | 54 +++++ nix/package.nix | 39 +++ parts/exports.nix | 21 ++ parts/treefmt.nix | 23 ++ quickshell-overview.qml | 41 ---- services/HyprlandData.qml | 4 +- 18 files changed, 548 insertions(+), 520 deletions(-) create mode 100644 .gitignore create mode 100644 flake.nix create mode 100644 nix/home-module.nix create mode 100644 nix/nixos-module.nix create mode 100644 nix/package.nix create mode 100644 parts/exports.nix create mode 100644 parts/treefmt.nix delete mode 100644 quickshell-overview.qml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b6959d0 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.rumdl_cache diff --git a/README.md b/README.md index 0157b44..513f406 100644 --- a/README.md +++ b/README.md @@ -1,295 +1,72 @@ -# Quickshell Overview for Hyprland +# Overzicht -
+Standalone Quickshell workspace overview for Hyprland. -A standalone workspace overview module for Hyprland using Quickshell - shows all workspaces with live window previews, drag-and-drop support, and Super+Tab keybind. +## Overview -![Quickshell](https://img.shields.io/badge/Quickshell-0.2.0-blue?style=flat-square) -![Hyprland](https://img.shields.io/badge/Hyprland-Compatible-purple?style=flat-square) -![Qt6](https://img.shields.io/badge/Qt-6-green?style=flat-square) -![License](https://img.shields.io/badge/License-GPL-orange?style=flat-square) +Overzicht shows a full-screen grid of your workspaces with live window previews. +It is controlled over Quickshell IPC and is designed for keyboard-first workflow. +- Toggle: `overzicht ipc call overview toggle` +- Open: `overzicht ipc call overview open` +- Close: `overzicht ipc call overview close` -
+`WlrLayershell.namespace` is `quickshell:overzicht`. ---- - -## 📸 Preview +## Preview ![Overview Screenshot](assets/image.png) https://github.com/user-attachments/assets/9c3d2488-1c24-4cdd-84cd-87c4397d02a8 -> *Workspace overview showing live window previews with drag-and-drop support* - ---- - -## ✨ Features - -- 🖼️ Visual workspace overview showing all workspaces and windows -- 🖥️ Multi-monitor support with proper scaling and vertical/rotated monitors [in experimental branch] -- 📐 Smart row hiding - optionally hide empty workspace rows -- 🎯 Click windows to focus them -- 🖱️ Middle-click windows to close them -- 🔄 Drag and drop windows between workspaces -- ⌨️ Keyboard navigation (Arrow keys, vim keys, number shortcuts) -- 💡 Hover tooltips showing window information -- 🎨 Material Design 3 theming -- ⚡ Smooth animations and transitions - -## 📦 Installation - -### Arch Linux (AUR) - -For Arch Linux users, you can install directly from the AUR: - -```bash -# Using yay -yay -S quickshell-overview-git - -# Using paru -paru -S quickshell-overview-git -``` - -Then add the keybind and auto-start to your Hyprland config (see Setup steps 2-4 below). +## Support Policy -### Prerequisites +Nix is the only supported installation path. -- **Hyprland** compositor -- **Quickshell** ([installation guide](https://quickshell.org/docs/v0.1.0/guide/install-setup/)) -- **Qt 6** with modules: QtQuick, QtQuick.Controls +- No AUR package. +- No manual non-Nix installer. -### Setup +## Flake Outputs -1. **Clone this repository** to your Quickshell config directory: - ```bash - git clone https://github.com/Shanu-Kumawat/quickshell-overview ~/.config/quickshell/overview - ``` +- `packages..overzicht` +- `packages..default` +- `overlays.default` +- `homeModules.default` +- `nixosModules.default` -2. **Add keybind** to your Hyprland config (`~/.config/hypr/hyprland.conf`): - ```conf - bind = Super, TAB, exec, qs ipc -c overview call overview toggle - ``` +## Home Manager Module -3. **Auto-start** the overview (add to Hyprland config): - ```conf - exec-once = qs -c overview - ``` +Option namespace: `programs.overzicht`. -4. **Reload Hyprland**: - ```bash - hyprctl reload - ``` +- `enable` +- `systemd.enable` +- `package` +- `settings` +- `colors` -### Manual Start (if needed) +Runtime files generated by the module: -```bash -qs -c overview & -``` +- `~/.config/overzicht/settings.json` +- `~/.config/overzicht/colors.json` -### NixOS +`settings.json` controls layout and behavior. +`colors.json` controls the full `m3*` palette used in `common/Appearance.qml`. -For NixOS users, ensure Quickshell has access to required Qt6 modules: +Minimal Home Manager example: ```nix -# In your configuration.nix or home-manager config -environment.systemPackages = with pkgs; [ - quickshell - qt6.qtwayland -]; -``` - -If you're using home-manager: - -```nix -home.packages = with pkgs; [ - quickshell - qt6.qtwayland -]; -``` - -## 🎮 Usage - -| Action | Description | -|--------|-------------| -| **Super + Tab** | Toggle the overview | -| **Arrow Keys / h/l** | Navigate left/right within current row* | -| **Up/Down / j/k** | Navigate between workspace rows | -| **1-9, 0** | Jump to Nth workspace in current group (0 = 10th) | -| **Escape / Enter** | Close the overview | -| **Click workspace** | Switch to that workspace | -| **Click window** | Focus that window | -| **Middle-click window** | Close that window | -| **Drag window** | Move window to different workspace | - -> *When `hideEmptyRows` is enabled, left/right navigation wraps within the current visible row for better UX - ---- - -## ⚙️ Configuration - -> **⚠️ Want to change the size, position, or number of workspaces?** -> Edit `~/.config/quickshell/overview/common/Config.qml` - it's all there! - -### Workspace Grid - -Edit `~/.config/quickshell/overview/common/Config.qml`: - -```qml -property QtObject overview: QtObject { - property int rows: 2 // Number of workspace rows - property int columns: 5 // Number of workspace columns (10 total workspaces) - property real scale: 0.16 // Overview scale factor (0.1-0.3, smaller = more compact) - property bool enable: true - property bool hideEmptyRows: true // Hide workspace rows with no windows (recommended!) -} -``` - -**Common adjustments:** -- **Too small?** Increase `scale` (try 0.20 or 0.25) -- **Too big?** Decrease `scale` (try 0.12 or 0.14) -- **More workspaces?** Change `rows` and `columns` (e.g., 3 rows × 4 columns = 12 workspaces) - -**Hide empty workspace rows:** -- Set `hideEmptyRows: true` to automatically hide rows that have no windows -- Keeps your overview clean by only showing rows with active workspaces -- The current workspace row is always visible, even if empty -- Arrow key navigation (left/right) stays within the current row when enabled -- Great for 2-row setups where you rarely use workspaces 6-10 - -### Position - -Edit `~/.config/quickshell/overview/modules/overview/Overview.qml` (line ~111): - -```qml -anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - topMargin: 100 // Change this value to move up/down -} -``` - -### Theme & Colors - -Edit `~/.config/quickshell/overview/common/Appearance.qml` to customize: -- Colors (m3colors and colors objects) -- Font families and sizes -- Animation curves and durations -- Border radius values - -### Matugen (Dynamic Colors from Wallpaper) - -[Matugen](https://github.com/InioX/matugen) lets you generate Material You colors from your wallpaper and apply them to the overview automatically. - -**1. Install matugen** - follow [matugen's install guide](https://github.com/InioX/matugen?tab=readme-ov-file#installation) - -**2. Copy the template** from this repo to matugen's templates folder: -```bash -mkdir -p ~/.config/matugen/templates -cp ~/.config/quickshell/overview/quickshell-overview.qml ~/.config/matugen/templates/ -``` - -**3. Add this to `~/.config/matugen/config.toml`** (create the file if it doesn't exist): -```toml -[templates.quickshell_overview] -input_path = "./templates/quickshell-overview.qml" -output_path = "~/.config/quickshell/overview/common/Appearance.colors.qml" -``` - -**4. Enable it** in `~/.config/quickshell/overview/common/Config.qml`: -```qml -property QtObject appearance: QtObject { - property bool useMatugenColors: true // change from false to true +{ + programs.overzicht = { + enable = true; + systemd.enable = true; + }; } ``` -**5. Run matugen** with your wallpaper to generate colors: -```bash -matugen image /path/to/your/wallpaper.jpg -``` - -This generates `Appearance.colors.qml` which the overview loads automatically. Re-run step 5 whenever you change your wallpaper. - ---- - -## 📋 Requirements - -- **Hyprland** compositor (tested on latest versions) -- **Quickshell** (Qt6-based shell framework) -- **Qt 6** with the following modules: - - QtQuick - - QtQuick.Controls - - QtQuick.Layouts - - Quickshell.Wayland - - Quickshell.Hyprland - -## 🚫 Removed Features (from original illogical-impulse) - -The following features were removed to make it standalone: - -- App search functionality -- Emoji picker -- Clipboard history integration -- Search widget -- Integration with the full illogical-impulse shell ecosystem - -## 📁 File Structure - -``` -~/.config/quickshell/overview/ -├── shell.qml # Main entry point -├── README.md # This file -├── hyprland-config.conf # Configuration reference -├── common/ -│ ├── Appearance.qml # Theme and styling -│ ├── Config.qml # Configuration options -│ ├── functions/ -│ │ └── ColorUtils.qml # Color manipulation utilities -│ └── widgets/ -│ ├── StyledText.qml # Styled text component -│ ├── StyledRectangularShadow.qml -│ ├── StyledToolTip.qml -│ └── StyledToolTipContent.qml -├── services/ -│ ├── GlobalStates.qml # Global state management -│ └── HyprlandData.qml # Hyprland data provider -└── modules/ - └── overview/ - ├── Overview.qml # Main overview component - ├── OverviewWidget.qml # Workspace grid widget - └── OverviewWindow.qml # Individual window preview -``` - -## 🎯 IPC Commands - -```bash -# Toggle overview -qs ipc -c overview call overview toggle - -# Open overview -qs ipc -c overview call overview open - -# Close overview -qs ipc -c overview call overview close -``` - -## 🐛 Known Issues - -- Window icons may fallback to generic icon if app class name doesn't match icon theme -- Potential crashes during rapid window state changes due to Wayland screencopy buffer management - -## Credits - -Extracted from the overview feature in [illogical-impulse](https://github.com/end-4/dots-hyprland) by [end-4](https://github.com/end-4). - -Adapted as a standalone component for Hyprland + Quickshell users who want just the overview functionality. - ---- - -
- -**Note:** Maintenance will be limited due to time constraints, but **PRs and code improvements are welcome!** Feel free to contribute or fork for your own needs. +## NixOS Module -Made with ❤️ for the Hyprland community +Option namespace: `services.overzicht`. -
+- `enable` +- `package` +- `target` diff --git a/common/Appearance.qml b/common/Appearance.qml index e8c6c2a..50f7cb9 100644 --- a/common/Appearance.qml +++ b/common/Appearance.qml @@ -2,15 +2,44 @@ pragma Singleton pragma ComponentBehavior: Bound import QtQuick +import QtCore import Quickshell +import Quickshell.Io import "functions" -import "." as Common Singleton { id: root - property QtObject m3colors: Common.Config.options.appearance.useMatugenColors && matugenLoader.item - ? matugenLoader.item - : defaultColors + function themeValue(path, fallback = undefined) { + let current = root.themeData; + + for (let i = 0; i < path.length; i++) { + if (current === null || current === undefined) + return fallback; + + current = current[path[i]]; + } + + return current === undefined || current === null ? fallback : current; + } + + function parseThemeData() { + try { + const content = themeFile.text(); + + if (!content || content.trim().length === 0) + return ({}); + + return JSON.parse(content); + } catch (error) { + console.warn(`Failed to parse theme json: ${error}`); + return ({}); + } + } + + property string configDirectory: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0] + property var themeData: ({}) + + property QtObject m3colors property QtObject animation property QtObject animationCurves property QtObject colors @@ -18,37 +47,45 @@ Singleton { property QtObject font property QtObject sizes - Loader { - id: matugenLoader - active: Common.Config.options.appearance.useMatugenColors - source: "Appearance.colors.qml" + FileView { + id: themeFile + path: `${root.configDirectory}/overzicht/colors.json` + blockLoading: true + watchChanges: true + onLoadedChanged: if (loaded) + root.themeData = root.parseThemeData() + onFileChanged: { + reload(); + root.themeData = root.parseThemeData(); + } } - property QtObject defaultColors: QtObject { - property bool darkmode: true - property color m3primary: "#E5B6F2" - property color m3onPrimary: "#452152" - property color m3primaryContainer: "#5D386A" - property color m3onPrimaryContainer: "#F9D8FF" - property color m3secondary: "#D5C0D7" - property color m3onSecondary: "#392C3D" - property color m3secondaryContainer: "#534457" - property color m3onSecondaryContainer: "#F2DCF3" - property color m3background: "#161217" - property color m3onBackground: "#EAE0E7" - property color m3surface: "#161217" - property color m3surfaceContainerLow: "#1F1A1F" - property color m3surfaceContainer: "#231E23" - property color m3surfaceContainerHigh: "#2D282E" - property color m3surfaceContainerHighest: "#383339" - property color m3onSurface: "#EAE0E7" - property color m3surfaceVariant: "#4C444D" - property color m3onSurfaceVariant: "#CFC3CD" - property color m3inverseSurface: "#EAE0E7" - property color m3inverseOnSurface: "#342F34" - property color m3outline: "#988E97" - property color m3outlineVariant: "#4C444D" - property color m3shadow: "#000000" + Component.onCompleted: themeData = parseThemeData() + + m3colors: QtObject { + property color m3primary: root.themeValue(["m3primary"], "#E5B6F2") + property color m3onPrimary: root.themeValue(["m3onPrimary"], "#452152") + property color m3primaryContainer: root.themeValue(["m3primaryContainer"], "#5D386A") + property color m3onPrimaryContainer: root.themeValue(["m3onPrimaryContainer"], "#F9D8FF") + property color m3onSecondary: root.themeValue(["m3onSecondary"], "#392C3D") + property color m3secondaryContainer: root.themeValue(["m3secondaryContainer"], "#534457") + property color m3onSecondaryContainer: root.themeValue(["m3onSecondaryContainer"], "#F2DCF3") + property color m3onBackground: root.themeValue(["m3onBackground"], "#EAE0E7") + property color m3surface: root.themeValue(["m3surface"], "#161217") + property color m3surfaceContainerHigh: root.themeValue(["m3surfaceContainerHigh"], "#2D282E") + property color m3surfaceContainerHighest: root.themeValue(["m3surfaceContainerHighest"], "#383339") + property color m3surfaceVariant: root.themeValue(["m3surfaceVariant"], "#4C444D") + property color m3background: root.themeValue(["m3background"], "#161217") + property color m3secondary: root.themeValue(["m3secondary"], "#D5C0D7") + property color m3surfaceContainerLow: root.themeValue(["m3surfaceContainerLow"], "#1F1A1F") + property color m3surfaceContainer: root.themeValue(["m3surfaceContainer"], "#231E23") + property color m3onSurface: root.themeValue(["m3onSurface"], "#EAE0E7") + property color m3onSurfaceVariant: root.themeValue(["m3onSurfaceVariant"], "#CFC3CD") + property color m3inverseSurface: root.themeValue(["m3inverseSurface"], "#EAE0E7") + property color m3inverseOnSurface: root.themeValue(["m3inverseOnSurface"], "#342F34") + property color m3outline: root.themeValue(["m3outline"], "#988E97") + property color m3outlineVariant: root.themeValue(["m3outlineVariant"], "#4C444D") + property color m3shadow: root.themeValue(["m3shadow"], "#000000") } colors: QtObject { @@ -77,28 +114,28 @@ Singleton { } rounding: QtObject { - property int unsharpen: 2 - property int verysmall: 8 - property int small: 12 - property int normal: 17 - property int large: 23 - property int full: 9999 - property int screenRounding: large - property int windowRounding: 18 + property int unsharpen: 0 + property int verysmall: 0 + property int small: 0 + property int normal: 0 + property int large: 0 + property int full: 0 + property int screenRounding: 0 + property int windowRounding: 0 } font: QtObject { property QtObject family: QtObject { - property string main: "sans-serif" - property string title: "sans-serif" - property string expressive: "sans-serif" + property string main: root.themeValue(["font", "family", "main"], "sans-serif") + property string title: root.themeValue(["font", "family", "title"], "sans-serif") + property string expressive: root.themeValue(["font", "family", "expressive"], "sans-serif") } property QtObject pixelSize: QtObject { - property int smaller: 12 - property int small: 15 - property int normal: 16 - property int larger: 19 - property int huge: 22 + property int smaller: root.themeValue(["font", "pixelSize", "smaller"], 12) + property int small: root.themeValue(["font", "pixelSize", "small"], 15) + property int normal: root.themeValue(["font", "pixelSize", "normal"], 16) + property int larger: root.themeValue(["font", "pixelSize", "larger"], 19) + property int huge: root.themeValue(["font", "pixelSize", "huge"], 22) } } diff --git a/common/Config.qml b/common/Config.qml index d1caa93..2b30420 100644 --- a/common/Config.qml +++ b/common/Config.qml @@ -2,26 +2,68 @@ pragma Singleton pragma ComponentBehavior: Bound import QtQuick +import QtCore import Quickshell +import Quickshell.Io Singleton { id: root - - property QtObject options: QtObject { - property QtObject appearance: QtObject { - property bool useMatugenColors: false + + function optionValue(path, fallback) { + let current = root.settingsData; + + for (let i = 0; i < path.length; i++) { + if (current === null || current === undefined) + return fallback; + + current = current[path[i]]; + } + + return current === undefined || current === null ? fallback : current; + } + + function parseSettingsData() { + try { + const content = settingsFile.text(); + + if (!content || content.trim().length === 0) + return ({}); + + return JSON.parse(content); + } catch (error) { + console.warn(`Failed to parse overview settings json: ${error}`); + return ({}); } + } + + property string configDirectory: StandardPaths.standardLocations(StandardPaths.ConfigLocation)[0] + property var settingsData: ({}) + FileView { + id: settingsFile + path: `${root.configDirectory}/overzicht/settings.json` + blockLoading: true + watchChanges: true + onLoadedChanged: if (loaded) + root.settingsData = root.parseSettingsData() + onFileChanged: { + reload(); + root.settingsData = root.parseSettingsData(); + } + } + + Component.onCompleted: settingsData = parseSettingsData() + + property QtObject options: QtObject { property QtObject overview: QtObject { - property int rows: 2 - property int columns: 5 - property real scale: 0.16 - property bool enable: true - property bool hideEmptyRows: true // Hide workspace rows with no windows + property int rows: root.optionValue(["overview", "rows"], 2) + 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 QtObject hacks: QtObject { - property int arbitraryRaceConditionDelay: 150 + property int arbitraryRaceConditionDelay: root.optionValue(["hacks", "arbitraryRaceConditionDelay"], 150) } } } diff --git a/common/functions/ColorUtils.qml b/common/functions/ColorUtils.qml index 6162df1..fa40753 100644 --- a/common/functions/ColorUtils.qml +++ b/common/functions/ColorUtils.qml @@ -47,12 +47,7 @@ Singleton { function mix(color1, color2, percentage = 0.5) { var c1 = Qt.color(color1); var c2 = Qt.color(color2); - return Qt.rgba( - percentage * c1.r + (1 - percentage) * c2.r, - percentage * c1.g + (1 - percentage) * c2.g, - percentage * c1.b + (1 - percentage) * c2.b, - percentage * c1.a + (1 - percentage) * c2.a - ); + return Qt.rgba(percentage * c1.r + (1 - percentage) * c2.r, percentage * c1.g + (1 - percentage) * c2.g, percentage * c1.b + (1 - percentage) * c2.b, percentage * c1.a + (1 - percentage) * c2.a); } function transparentize(color, percentage = 1) { diff --git a/common/widgets/StyledToolTip.qml b/common/widgets/StyledToolTip.qml index 4d4678c..4214675 100644 --- a/common/widgets/StyledToolTip.qml +++ b/common/widgets/StyledToolTip.qml @@ -12,7 +12,7 @@ ToolTip { background: null visible: internalVisibleCondition - + contentItem: StyledToolTipContent { id: contentItem text: root.text diff --git a/common/widgets/StyledToolTipContent.qml b/common/widgets/StyledToolTipContent.qml index b8c29c1..3a2c711 100644 --- a/common/widgets/StyledToolTipContent.qml +++ b/common/widgets/StyledToolTipContent.qml @@ -19,8 +19,8 @@ Item { bottom: root.bottom horizontalCenter: root.horizontalCenter } - color: Appearance?.colors.colTooltip ?? "#3C4043" - radius: Appearance?.rounding.verysmall ?? 7 + color: Appearance?.colors.colTooltip + radius: Appearance?.rounding.verysmall opacity: shown ? 1 : 0 implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0 implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0 @@ -45,5 +45,5 @@ Item { color: Appearance?.colors.colOnTooltip ?? "#FFFFFF" wrapMode: Text.Wrap } - } + } } diff --git a/flake.nix b/flake.nix new file mode 100644 index 0000000..5af8dc8 --- /dev/null +++ b/flake.nix @@ -0,0 +1,17 @@ +{ + description = "Quickshell overview."; + + inputs = { + nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable"; + flake-parts.url = "github:hercules-ci/flake-parts"; + import-tree.url = "github:vic/import-tree"; + systems.url = "github:nix-systems/default"; + treefmt-nix.url = "github:numtide/treefmt-nix"; + }; + + outputs = inputs @ {flake-parts, ...}: + flake-parts.lib.mkFlake {inherit inputs;} { + systems = import inputs.systems; + imports = with inputs; [(import-tree ./parts)]; + }; +} diff --git a/modules/overview/Overview.qml b/modules/overview/Overview.qml index 1b7b433..ea1cacb 100644 --- a/modules/overview/Overview.qml +++ b/modules/overview/Overview.qml @@ -22,7 +22,7 @@ Scope { screen: modelData visible: GlobalStates.overviewOpen - WlrLayershell.namespace: "quickshell:overview" + WlrLayershell.namespace: "quickshell:overzicht" WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive color: "transparent" @@ -34,8 +34,8 @@ Scope { anchors { top: true bottom: true - left: !(Config?.options.overview.enable ?? true) - right: !(Config?.options.overview.enable ?? true) + left: !(Config?.options.overview.enable ?? true) + right: !(Config?.options.overview.enable ?? true) } HyprlandFocusGrab { @@ -92,42 +92,31 @@ Scope { const currentGroup = Math.floor((currentId - 1) / workspacesPerGroup); const minWorkspaceId = currentGroup * workspacesPerGroup + 1; const maxWorkspaceId = minWorkspaceId + workspacesPerGroup - 1; - - // When hideEmptyRows is enabled, constrain navigation to current row - 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; - // Wrap within visible workspaces - if (Config.options.overview.hideEmptyRows) { - if (targetId < rowMinId) targetId = rowMaxId; - } else { - if (targetId < minWorkspaceId) targetId = maxWorkspaceId; - } + if (targetId < minWorkspaceId) + targetId = maxWorkspaceId; } else if (event.key === Qt.Key_Right || event.key === Qt.Key_L) { targetId = currentId + 1; - // Wrap within visible workspaces - if (Config.options.overview.hideEmptyRows) { - if (targetId > rowMaxId) targetId = rowMinId; - } else { - if (targetId > maxWorkspaceId) targetId = minWorkspaceId; - } + if (targetId > maxWorkspaceId) + targetId = minWorkspaceId; } else if (event.key === Qt.Key_Up || event.key === Qt.Key_K) { targetId = currentId - Config.options.overview.columns; - if (targetId < minWorkspaceId) targetId += workspacesPerGroup; + if (targetId < minWorkspaceId) + targetId += workspacesPerGroup; } else if (event.key === Qt.Key_Down || event.key === Qt.Key_J) { targetId = currentId + Config.options.overview.columns; - if (targetId > maxWorkspaceId) targetId -= workspacesPerGroup; - } + if (targetId > maxWorkspaceId) + targetId -= workspacesPerGroup; + } else // Number keys: jump to workspace within the current group // 1-9 map to positions 1-9, 0 maps to position 10 - else if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) { + if (event.key >= Qt.Key_1 && event.key <= Qt.Key_9) { const position = event.key - Qt.Key_0; // 1-9 if (position <= workspacesPerGroup) { targetId = minWorkspaceId + position - 1; @@ -149,11 +138,7 @@ Scope { ColumnLayout { id: columnLayout visible: GlobalStates.overviewOpen - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - topMargin: 100 - } + anchors.centerIn: parent Loader { id: overviewLoader @@ -166,7 +151,7 @@ Scope { } } } - + IpcHandler { target: "overview" diff --git a/modules/overview/OverviewWidget.qml b/modules/overview/OverviewWidget.qml index 64d2876..9ebd84b 100644 --- a/modules/overview/OverviewWidget.qml +++ b/modules/overview/OverviewWidget.qml @@ -24,12 +24,8 @@ Item { property real scale: Config.options.overview.scale property color activeBorderColor: Appearance.colors.colSecondary - property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ? - ((monitor.height / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale) : - ((monitor.width / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale) - property real workspaceImplicitHeight: (monitorData?.transform % 2 === 1) ? - ((monitor.width / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale) : - ((monitor.height / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale) + property real workspaceImplicitWidth: (monitorData?.transform % 2 === 1) ? ((monitor.height / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale) : ((monitor.width / monitor.scale - (monitorData?.reserved?.[0] ?? 0) - (monitorData?.reserved?.[2] ?? 0)) * root.scale) + property real workspaceImplicitHeight: (monitorData?.transform % 2 === 1) ? ((monitor.width / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale) : ((monitor.height / monitor.scale - (monitorData?.reserved?.[1] ?? 0) - (monitorData?.reserved?.[3] ?? 0)) * root.scale) property real workspaceNumberMargin: 80 property real workspaceNumberSize: 250 * monitor.scale @@ -41,33 +37,6 @@ Item { property int draggingFromWorkspace: -1 property int draggingTargetWorkspace: -1 - // Calculate which rows have windows or current workspace - property var rowsWithContent: { - if (!Config.options.overview.hideEmptyRows) return null; - - let rows = new Set(); - const firstWorkspace = root.workspaceGroup * root.workspacesShown + 1; - const lastWorkspace = (root.workspaceGroup + 1) * root.workspacesShown; - - // Add row containing current workspace - const currentWorkspace = monitor.activeWorkspace?.id ?? 1; - if (currentWorkspace >= firstWorkspace && currentWorkspace <= lastWorkspace) { - rows.add(Math.floor((currentWorkspace - firstWorkspace) / Config.options.overview.columns)); - } - - // Add rows with windows - for (let addr in windowByAddress) { - const win = windowByAddress[addr]; - const wsId = win?.workspace?.id; - if (wsId >= firstWorkspace && wsId <= lastWorkspace) { - const rowIndex = Math.floor((wsId - firstWorkspace) / Config.options.overview.columns); - rows.add(rowIndex); - } - } - - return rows; - } - implicitWidth: overviewBackground.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: overviewBackground.implicitHeight + Appearance.sizes.elevationMargin * 2 @@ -85,7 +54,7 @@ Item { implicitWidth: workspaceColumnLayout.implicitWidth + padding * 2 implicitHeight: workspaceColumnLayout.implicitHeight + padding * 2 - radius: Appearance.rounding.screenRounding * root.scale + padding + radius: Appearance.rounding.screenRounding color: Appearance.colors.colLayer0 border.width: 1 border.color: Appearance.colors.colLayer0Border @@ -102,11 +71,9 @@ Item { id: row property int rowIndex: index spacing: workspaceSpacing - visible: !Config.options.overview.hideEmptyRows || - (root.rowsWithContent && root.rowsWithContent.has(rowIndex)) - height: visible ? implicitHeight : 0 - Repeater { // Workspace repeater + Repeater { + // Workspace repeater model: Config.options.overview.columns Rectangle { // Workspace id: workspace @@ -120,7 +87,7 @@ Item { implicitWidth: root.workspaceImplicitWidth implicitHeight: root.workspaceImplicitHeight color: hoveredWhileDragging ? hoveredWorkspaceColor : defaultWorkspaceColor - radius: Appearance.rounding.screenRounding * root.scale + radius: Appearance.rounding.screenRounding border.width: 2 border.color: hoveredWhileDragging ? hoveredBorderColor : "transparent" @@ -143,8 +110,8 @@ Item { acceptedButtons: Qt.LeftButton onClicked: { if (root.draggingTargetWorkspace === -1) { - GlobalStates.overviewOpen = false - Hyprland.dispatch(`workspace ${workspaceValue}`) + GlobalStates.overviewOpen = false; + Hyprland.dispatch(`workspace ${workspaceValue}`); } } } @@ -152,16 +119,17 @@ Item { DropArea { anchors.fill: parent onEntered: { - root.draggingTargetWorkspace = workspaceValue - if (root.draggingFromWorkspace == root.draggingTargetWorkspace) return; - hoveredWhileDragging = true + root.draggingTargetWorkspace = workspaceValue; + if (root.draggingFromWorkspace == root.draggingTargetWorkspace) + return; + hoveredWhileDragging = true; } onExited: { - hoveredWhileDragging = false - if (root.draggingTargetWorkspace == workspaceValue) root.draggingTargetWorkspace = -1 + hoveredWhileDragging = false; + if (root.draggingTargetWorkspace == workspaceValue) + root.draggingTargetWorkspace = -1; } } - } } } @@ -174,35 +142,36 @@ Item { implicitWidth: workspaceColumnLayout.implicitWidth implicitHeight: workspaceColumnLayout.implicitHeight - Repeater { // Window repeater + Repeater { + // Window repeater model: ScriptModel { values: { - return ToplevelManager.toplevels.values.filter((toplevel) => { - const address = `0x${toplevel.HyprlandToplevel.address}` - var win = windowByAddress[address] - const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) + return ToplevelManager.toplevels.values.filter(toplevel => { + const address = `0x${toplevel.HyprlandToplevel.address}`; + var win = windowByAddress[address]; + const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown); return inWorkspaceGroup; }).sort((a, b) => { // Proper stacking order based on Hyprland's window properties - const addrA = `0x${a.HyprlandToplevel.address}` - const addrB = `0x${b.HyprlandToplevel.address}` - const winA = windowByAddress[addrA] - const winB = windowByAddress[addrB] - + const addrA = `0x${a.HyprlandToplevel.address}`; + const addrB = `0x${b.HyprlandToplevel.address}`; + const winA = windowByAddress[addrA]; + const winB = windowByAddress[addrB]; + // 1. Pinned windows are always on top if (winA?.pinned !== winB?.pinned) { - return winA?.pinned ? 1 : -1 + return winA?.pinned ? 1 : -1; } - + // 2. Floating windows above tiled windows if (winA?.floating !== winB?.floating) { - return winA?.floating ? 1 : -1 + return winA?.floating ? 1 : -1; } - + // 3. Within same category, sort by focus history // Lower focusHistoryID = more recently focused = higher in stack - return (winB?.focusHistoryID ?? 0) - (winA?.focusHistoryID ?? 0) - }) + return (winB?.focusHistoryID ?? 0) - (winA?.focusHistoryID ?? 0); + }); } } delegate: OverviewWindow { @@ -215,21 +184,14 @@ Item { windowData: windowByAddress[address] toplevel: modelData monitorData: monitor - + // Calculate scale relative to window's source monitor - property real sourceMonitorWidth: (monitor?.transform % 2 === 1) ? - (monitor?.height ?? 1920) / (monitor?.scale ?? 1) - (monitor?.reserved?.[0] ?? 0) - (monitor?.reserved?.[2] ?? 0) : - (monitor?.width ?? 1920) / (monitor?.scale ?? 1) - (monitor?.reserved?.[0] ?? 0) - (monitor?.reserved?.[2] ?? 0) - property real sourceMonitorHeight: (monitor?.transform % 2 === 1) ? - (monitor?.width ?? 1080) / (monitor?.scale ?? 1) - (monitor?.reserved?.[1] ?? 0) - (monitor?.reserved?.[3] ?? 0) : - (monitor?.height ?? 1080) / (monitor?.scale ?? 1) - (monitor?.reserved?.[1] ?? 0) - (monitor?.reserved?.[3] ?? 0) - + property real sourceMonitorWidth: (monitor?.transform % 2 === 1) ? (monitor?.height ?? 1920) / (monitor?.scale ?? 1) - (monitor?.reserved?.[0] ?? 0) - (monitor?.reserved?.[2] ?? 0) : (monitor?.width ?? 1920) / (monitor?.scale ?? 1) - (monitor?.reserved?.[0] ?? 0) - (monitor?.reserved?.[2] ?? 0) + property real sourceMonitorHeight: (monitor?.transform % 2 === 1) ? (monitor?.width ?? 1080) / (monitor?.scale ?? 1) - (monitor?.reserved?.[1] ?? 0) - (monitor?.reserved?.[3] ?? 0) : (monitor?.height ?? 1080) / (monitor?.scale ?? 1) - (monitor?.reserved?.[1] ?? 0) - (monitor?.reserved?.[3] ?? 0) + // Scale windows to fit the workspace size, accounting for different monitor sizes - scale: Math.min( - root.workspaceImplicitWidth / sourceMonitorWidth, - root.workspaceImplicitHeight / sourceMonitorHeight - ) - + scale: Math.min(root.workspaceImplicitWidth / sourceMonitorWidth, root.workspaceImplicitHeight / sourceMonitorHeight) + availableWorkspaceWidth: root.workspaceImplicitWidth availableWorkspaceHeight: root.workspaceImplicitHeight widgetMonitorId: root.monitor.id @@ -247,8 +209,8 @@ Item { repeat: false running: false onTriggered: { - window.x = Math.round(Math.max((windowData?.at[0] - (monitor?.x ?? 0) - (monitorData?.reserved?.[0] ?? 0)) * root.scale, 0) + xOffset) - window.y = Math.round(Math.max((windowData?.at[1] - (monitor?.y ?? 0) - (monitorData?.reserved?.[1] ?? 0)) * root.scale, 0) + yOffset) + window.x = Math.round(Math.max((windowData?.at[0] - (monitor?.x ?? 0) - (monitorData?.reserved?.[0] ?? 0)) * root.scale, 0) + xOffset); + window.y = Math.round(Math.max((windowData?.at[1] - (monitor?.y ?? 0) - (monitorData?.reserved?.[1] ?? 0)) * root.scale, 0) + yOffset); } } @@ -263,38 +225,38 @@ Item { onExited: hovered = false acceptedButtons: Qt.LeftButton | Qt.MiddleButton drag.target: parent - onPressed: (mouse) => { - root.draggingFromWorkspace = windowData?.workspace.id - window.pressed = true - window.Drag.active = true - window.Drag.source = window - window.Drag.hotSpot.x = mouse.x - window.Drag.hotSpot.y = mouse.y + onPressed: mouse => { + root.draggingFromWorkspace = windowData?.workspace.id; + window.pressed = true; + window.Drag.active = true; + window.Drag.source = window; + window.Drag.hotSpot.x = mouse.x; + window.Drag.hotSpot.y = mouse.y; } onReleased: { - const targetWorkspace = root.draggingTargetWorkspace - window.pressed = false - window.Drag.active = false - root.draggingFromWorkspace = -1 + const targetWorkspace = root.draggingTargetWorkspace; + window.pressed = false; + window.Drag.active = false; + root.draggingFromWorkspace = -1; if (targetWorkspace !== -1 && targetWorkspace !== windowData?.workspace.id) { - Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`) - updateWindowPosition.restart() - } - else { - window.x = window.initX - window.y = window.initY + Hyprland.dispatch(`movetoworkspacesilent ${targetWorkspace}, address:${window.windowData?.address}`); + updateWindowPosition.restart(); + } else { + window.x = window.initX; + window.y = window.initY; } } - onClicked: (event) => { - if (!windowData) return; + onClicked: event => { + if (!windowData) + return; if (event.button === Qt.LeftButton) { - GlobalStates.overviewOpen = false - Hyprland.dispatch(`focuswindow address:${windowData.address}`) - event.accepted = true + GlobalStates.overviewOpen = false; + Hyprland.dispatch(`focuswindow address:${windowData.address}`); + event.accepted = true; } else if (event.button === Qt.MiddleButton) { - Hyprland.dispatch(`closewindow address:${windowData.address}`) - event.accepted = true + Hyprland.dispatch(`closewindow address:${windowData.address}`); + event.accepted = true; } } @@ -318,7 +280,7 @@ Item { width: root.workspaceImplicitWidth height: root.workspaceImplicitHeight color: "transparent" - radius: Appearance.rounding.screenRounding * root.scale + radius: Appearance.rounding.screenRounding border.width: 2 border.color: root.activeBorderColor Behavior on x { diff --git a/modules/overview/OverviewWindow.qml b/modules/overview/OverviewWindow.qml index d225ba4..73b96ef 100644 --- a/modules/overview/OverviewWindow.qml +++ b/modules/overview/OverviewWindow.qml @@ -20,7 +20,7 @@ Item { // Window property real xOffset: 0 property real yOffset: 0 property int widgetMonitorId: 0 - + property var targetWindowWidth: (windowData?.size[0] ?? 100) * scale property var targetWindowHeight: (windowData?.size[1] ?? 100) * scale property bool hovered: false @@ -34,7 +34,7 @@ Item { // Window property bool compactMode: Appearance.font.pixelSize.smaller * 4 > targetWindowHeight || Appearance.font.pixelSize.smaller * 4 > targetWindowWidth property bool indicateXWayland: windowData?.xwayland ?? false - + x: initX y: initY width: Math.min((windowData?.size[0] ?? 100) * root.scale, availableWorkspaceWidth) @@ -64,12 +64,10 @@ Item { // Window Rectangle { anchors.fill: parent - radius: Appearance.rounding.windowRounding * root.scale - color: pressed ? ColorUtils.transparentize(Appearance.colors.colLayer2Active, 0.5) : - hovered ? ColorUtils.transparentize(Appearance.colors.colLayer2Hover, 0.7) : - ColorUtils.transparentize(Appearance.colors.colLayer2) - border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7) - border.width : 1 + radius: Appearance.rounding.windowRounding + color: pressed ? ColorUtils.transparentize(Appearance.colors.colLayer2Active, 0.5) : hovered ? ColorUtils.transparentize(Appearance.colors.colLayer2Hover, 0.7) : ColorUtils.transparentize(Appearance.colors.colLayer2) + border.color: ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.7) + border.width: 1 } ColumnLayout { diff --git a/nix/home-module.nix b/nix/home-module.nix new file mode 100644 index 0000000..bc62d54 --- /dev/null +++ b/nix/home-module.nix @@ -0,0 +1,118 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit + (lib) + mkEnableOption + mkOption + mkIf + getExe + types + literalExpression + ; + inherit (pkgs) callPackage; + + cfg = config.programs.overzicht; + jsonFormat = pkgs.formats.json {}; + + generateJson = name: value: + if types.str.check value + then pkgs.writeText name value + else if types.path.check value || types.package.check value + then value + else jsonFormat.generate name value; +in { + options.programs.overzicht = { + enable = mkEnableOption "quickshell overview shell"; + + systemd.enable = mkEnableOption "systemd user service for overzicht"; + + package = mkOption { + type = types.package; + default = callPackage ./package.nix {}; + defaultText = literalExpression "callPackage ./package.nix {}"; + description = "Package that provides the `overzicht` executable."; + }; + + settings = mkOption { + type = types.oneOf [jsonFormat.type types.str types.path]; + default = { + overview = { + rows = 2; + columns = 4; + scale = 0.12; + enable = true; + }; + + hacks.arbitraryRaceConditionDelay = 150; + }; + description = "Settings written to `~/.config/overzicht/settings.json`."; + }; + + colors = mkOption { + type = types.oneOf [jsonFormat.type types.str types.path]; + default = { + 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"; + }; + description = "Colors written to `~/.config/overzicht/colors.json`."; + }; + }; + + config = mkIf cfg.enable { + home.packages = [cfg.package]; + + xdg.configFile = { + "overzicht/settings.json".source = generateJson "overzicht-settings.json" cfg.settings; + "overzicht/colors.json".source = generateJson "overzicht-colors.json" cfg.colors; + }; + + systemd.user.services.overzicht = mkIf cfg.systemd.enable { + Unit = { + Description = "overzicht"; + After = ["graphical-session-pre.target"]; + PartOf = ["graphical-session.target"]; + }; + + Service = { + ExecStart = getExe cfg.package; + Restart = "on-failure"; + RestartSec = 1; + }; + + Install.WantedBy = ["graphical-session.target"]; + }; + + assertions = [ + { + assertion = cfg.package != null || !cfg.systemd.enable; + message = "programs.overzicht.package cannot be null when systemd is enabled."; + } + ]; + }; +} diff --git a/nix/nixos-module.nix b/nix/nixos-module.nix new file mode 100644 index 0000000..d567bf4 --- /dev/null +++ b/nix/nixos-module.nix @@ -0,0 +1,54 @@ +{ + config, + lib, + pkgs, + ... +}: +let + inherit + (lib) + mkEnableOption + mkOption + literalExpression + mkIf + getExe + types + ; + inherit (pkgs) callPackage; + + cfg = config.services.overzicht; +in { + options.services.overzicht = { + enable = mkEnableOption "overzicht user service"; + + package = mkOption { + type = types.package; + default = callPackage ./package.nix {}; + defaultText = literalExpression "callPackage ./package.nix {}"; + description = "Package that provides the `overzicht` executable."; + }; + + target = mkOption { + type = types.str; + default = "graphical-session.target"; + description = "User systemd target for overzicht."; + }; + }; + + config = mkIf cfg.enable { + environment.systemPackages = [cfg.package]; + + systemd.user.services.overzicht = { + description = "overzicht"; + wantedBy = [cfg.target]; + after = ["graphical-session-pre.target"]; + partOf = [cfg.target]; + + serviceConfig = { + ExecStart = getExe cfg.package; + Restart = "on-failure"; + RestartSec = 1; + }; + }; + }; +} diff --git a/nix/package.nix b/nix/package.nix new file mode 100644 index 0000000..0df0473 --- /dev/null +++ b/nix/package.nix @@ -0,0 +1,39 @@ +{ + lib, + stdenvNoCC, + makeWrapper, + quickshell, +}: let + inherit + (lib) + cleanSource + getExe + ; +in + stdenvNoCC.mkDerivation { + pname = "overzicht"; + version = "unstable"; + + src = cleanSource ../.; + + nativeBuildInputs = [makeWrapper]; + + installPhase = '' + runHook preInstall + + install -dm755 "$out/share/overzicht" + cp -r ./* "$out/share/overzicht/" + + install -dm755 "$out/bin" + makeWrapper ${getExe quickshell} "$out/bin/overzicht" \ + --add-flags "-p $out/share/overzicht" + + runHook postInstall + ''; + + meta = with lib; { + description = "Quickshell-based workspace overview"; + mainProgram = "overzicht"; + platforms = platforms.linux; + }; + } diff --git a/parts/exports.nix b/parts/exports.nix new file mode 100644 index 0000000..58b1bf4 --- /dev/null +++ b/parts/exports.nix @@ -0,0 +1,21 @@ +_: { + flake = { + overlays.default = final: _: { + overzicht = final.callPackage ../nix/package.nix {}; + }; + + homeModules.default = import ../nix/home-module.nix; + nixosModules.default = import ../nix/nixos-module.nix; + }; + + perSystem = { + config, + pkgs, + ... + }: { + packages = { + overzicht = pkgs.callPackage ../nix/package.nix {}; + default = config.packages.overzicht; + }; + }; +} diff --git a/parts/treefmt.nix b/parts/treefmt.nix new file mode 100644 index 0000000..dc7f3c6 --- /dev/null +++ b/parts/treefmt.nix @@ -0,0 +1,23 @@ +{inputs, ...}: +# Treefmt configuration for repository formatting. +{ + imports = [inputs.treefmt-nix.flakeModule]; + + perSystem = _: { + treefmt = { + projectRootFile = "flake.nix"; + + programs = { + alejandra.enable = true; + nixf-diagnose.enable = true; + deadnix.enable = true; + statix.enable = true; + + qmlformat.enable = true; + + rumdl-check.enable = true; + rumdl-format.enable = true; + }; + }; + }; +} diff --git a/quickshell-overview.qml b/quickshell-overview.qml deleted file mode 100644 index 95176b4..0000000 --- a/quickshell-overview.qml +++ /dev/null @@ -1,41 +0,0 @@ - -import QtQuick - -QtObject { - id: m3 - - property color m3primary: "{{colors.primary.default.hex}}" - property color m3onPrimary: "{{colors.on_primary.default.hex}}" - - property color m3primaryContainer: "{{colors.primary_container.default.hex}}" - property color m3onPrimaryContainer: "{{colors.on_primary_container.default.hex}}" - - property color m3secondary: "{{colors.secondary.default.hex}}" - property color m3onSecondary: "{{colors.on_secondary.default.hex}}" - - property color m3secondaryContainer: "{{colors.secondary_container.default.hex}}" - property color m3onSecondaryContainer: "{{colors.on_secondary_container.default.hex}}" - - property color m3background: "{{colors.background.default.hex}}" - property color m3onBackground: "{{colors.on_background.default.hex}}" - - property color m3surface: "{{colors.surface.default.hex}}" - - property color m3surfaceContainerLow: "{{colors.surface_container_low.default.hex}}" - property color m3surfaceContainer: "{{colors.surface_container.default.hex}}" - property color m3surfaceContainerHigh: "{{colors.surface_container_high.default.hex}}" - property color m3surfaceContainerHighest: "{{colors.surface_container_highest.default.hex}}" - - property color m3onSurface: "{{colors.on_surface.default.hex}}" - - property color m3surfaceVariant: "{{colors.surface_variant.default.hex}}" - property color m3onSurfaceVariant: "{{colors.on_surface_variant.default.hex}}" - - property color m3inverseSurface: "{{colors.inverse_surface.default.hex}}" - property color m3inverseOnSurface: "{{colors.inverse_on_surface.default.hex}}" - - property color m3outline: "{{colors.outline.default.hex}}" - property color m3outlineVariant: "{{colors.outline_variant.default.hex}}" - - property color m3shadow: "#000000" -} diff --git a/services/HyprlandData.qml b/services/HyprlandData.qml index e23472d..264f44e 100644 --- a/services/HyprlandData.qml +++ b/services/HyprlandData.qml @@ -62,7 +62,7 @@ Singleton { target: Hyprland function onRawEvent(event) { - updateAll() + updateAll(); } } @@ -72,7 +72,7 @@ Singleton { stdout: StdioCollector { id: clientsCollector onStreamFinished: { - root.windowList = JSON.parse(clientsCollector.text) + root.windowList = JSON.parse(clientsCollector.text); let tempWinByAddress = {}; for (var i = 0; i < root.windowList.length; ++i) { var win = root.windowList[i]; -- 2.51.2