From c695a4879c12cbea0de491f2cf9f9e53b04d5c54 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 22:46:11 +0200 Subject: [PATCH 01/84] cheatsheet: put in loader --- .../modules/cheatsheet/Cheatsheet.qml | 264 +++++++++--------- 1 file changed, 131 insertions(+), 133 deletions(-) diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 03e91f1e6..154bd709c 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -19,137 +19,151 @@ Scope { // Scope id: cheatsheetVariants model: Quickshell.screens - PanelWindow { // Window - id: cheatsheetRoot - visible: false - focusable: true - + Loader { + id: cheatsheetLoader + active: false property var modelData + + sourceComponent: PanelWindow { // Window + id: cheatsheetRoot + visible: cheatsheetLoader.active + focusable: true - screen: modelData - exclusiveZone: 0 - implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2 - implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2 - WlrLayershell.namespace: "quickshell:cheatsheet" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: "transparent" - - mask: Region { - item: cheatsheetBackground - } - - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ cheatsheetRoot ] - active: false - onCleared: () => { - if (!active) cheatsheetRoot.visible = false + anchors { + top: true + bottom: true + left: true + right: true } - } - Connections { - target: cheatsheetRoot - function onVisibleChanged() { - delayedGrabTimer.start() + function hide() { + cheatsheetLoader.active = false } - } - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = cheatsheetRoot.visible + screen: modelData + exclusiveZone: 0 + implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2 + implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2 + WlrLayershell.namespace: "quickshell:cheatsheet" + // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab + // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + color: "transparent" + + mask: Region { + item: cheatsheetBackground } - } - // Background - Rectangle { - id: cheatsheetBackground - anchors.centerIn: parent - color: Appearance.colors.colLayer0 - radius: Appearance.rounding.windowRounding - property real padding: 30 - implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 - implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 - - Keys.onPressed: (event) => { // Esc to close - if (event.key === Qt.Key_Escape) { - cheatsheetRoot.visible = false + HyprlandFocusGrab { // Click outside to close + id: grab + windows: [ cheatsheetRoot ] + active: false + onCleared: () => { + if (!active) cheatsheetRoot.hide() } } - Button { // Close button - id: closeButton - focus: cheatsheetRoot.visible - implicitWidth: 40 - implicitHeight: 40 - anchors { - top: parent.top - right: parent.right - topMargin: 20 - rightMargin: 20 + Connections { + target: cheatsheetRoot + function onVisibleChanged() { + delayedGrabTimer.start() + } + } + + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + grab.active = cheatsheetRoot.visible + } + } + + // Background + Rectangle { + id: cheatsheetBackground + anchors.centerIn: parent + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.windowRounding + property real padding: 30 + implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 + implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 + + Keys.onPressed: (event) => { // Esc to close + if (event.key === Qt.Key_Escape) { + cheatsheetRoot.hide() + } } - PointingHandInteraction {} - onClicked: { - cheatsheetRoot.visible = false - } + Button { // Close button + id: closeButton + focus: cheatsheetRoot.visible + implicitWidth: 40 + implicitHeight: 40 + anchors { + top: parent.top + right: parent.right + topMargin: 20 + rightMargin: 20 + } - background: Item {} - contentItem: Rectangle { - anchors.fill: parent - radius: Appearance.rounding.full - color: closeButton.pressed ? Appearance.colors.colLayer0Active : - closeButton.hovered ? Appearance.colors.colLayer0Hover : - Appearance.transparentize(Appearance.colors.colLayer0, 1) - - Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + PointingHandInteraction {} + onClicked: { + cheatsheetRoot.hide() + } + + background: Item {} + contentItem: Rectangle { + anchors.fill: parent + radius: Appearance.rounding.full + color: closeButton.pressed ? Appearance.colors.colLayer0Active : + closeButton.hovered ? Appearance.colors.colLayer0Hover : + Appearance.transparentize(Appearance.colors.colLayer0, 1) + + Behavior on color { + ColorAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + + MaterialSymbol { + anchors.centerIn: parent + font.pixelSize: Appearance.font.pixelSize.title + text: "close" } } + } - MaterialSymbol { - anchors.centerIn: parent + ColumnLayout { // Real content + id: cheatsheetColumnLayout + anchors.centerIn: parent + spacing: 20 + + StyledText { + id: cheatsheetTitle + Layout.alignment: Qt.AlignHCenter + font.family: Appearance.font.family.title font.pixelSize: Appearance.font.pixelSize.title - text: "close" + text: qsTr("Cheat sheet") } + CheatsheetKeybinds {} } } - ColumnLayout { // Real content - id: cheatsheetColumnLayout - anchors.centerIn: parent - spacing: 20 - - StyledText { - id: cheatsheetTitle - Layout.alignment: Qt.AlignHCenter - font.family: Appearance.font.family.title - font.pixelSize: Appearance.font.pixelSize.title - text: qsTr("Cheat sheet") - } - CheatsheetKeybinds {} + // Shadow + DropShadow { + anchors.fill: cheatsheetBackground + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + source: cheatsheetBackground } - } - // Shadow - DropShadow { - anchors.fill: cheatsheetBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: cheatsheetBackground } - } - } IpcHandler { @@ -157,30 +171,22 @@ Scope { // Scope function toggle(): void { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = !panelWindow.visible; - if(panelWindow.visible) Notifications.timeoutAll(); - } + const loader = cheatsheetVariants.instances[i]; + loader.active = !loader.active; } } function close(): void { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = false; - } + const loader = cheatsheetVariants.instances[i]; + loader.active = false } } function open(): void { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = true; - if(panelWindow.visible) Notifications.timeoutAll(); - } + const loader = cheatsheetVariants.instances[i]; + loader.active = true; } } } @@ -191,11 +197,8 @@ Scope { // Scope onPressed: { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = !panelWindow.visible; - if(panelWindow.visible) Notifications.timeoutAll(); - } + const loader = cheatsheetVariants.instances[i]; + loader.active = !loader.active; } } } @@ -206,11 +209,8 @@ Scope { // Scope onPressed: { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = true; - if(panelWindow.visible) Notifications.timeoutAll(); - } + const loader = cheatsheetVariants.instances[i]; + loader.active = true; } } } @@ -221,10 +221,8 @@ Scope { // Scope onPressed: { for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - let panelWindow = cheatsheetVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = false; - } + const loader = cheatsheetVariants.instances[i]; + loader.active = false; } } } From f15ca250c1f08561c0383acb65d097bf5af1bd35 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 22:46:31 +0200 Subject: [PATCH 02/84] HyprlandKeybinds: fix children undefined --- .config/quickshell/services/HyprlandKeybinds.qml | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/.config/quickshell/services/HyprlandKeybinds.qml b/.config/quickshell/services/HyprlandKeybinds.qml index 81c63a08e..1b2972fe9 100644 --- a/.config/quickshell/services/HyprlandKeybinds.qml +++ b/.config/quickshell/services/HyprlandKeybinds.qml @@ -11,19 +11,15 @@ import Quickshell.Hyprland Singleton { id: root - property var defaultKeybinds: [] - property var userKeybinds: [] + property var defaultKeybinds: {"children": []} + property var userKeybinds: {"children": []} property var keybinds: ({ children: [ - ...defaultKeybinds.children, - ...userKeybinds.children, + ...(defaultKeybinds.children ?? []), + ...(userKeybinds.children ?? []), ] }) - // onKeybindsChanged: { - // console.log("[CheatsheetKeybinds] Keybinds changed:", JSON.stringify(keybinds, null, 2)) - // } - Connections { target: Hyprland From e9e7b74c1b39271825aacd5fe9c43e43c5b9d958 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 22:58:12 +0200 Subject: [PATCH 03/84] Update fuzzel-emoji --- .local/bin/fuzzel-emoji | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/.local/bin/fuzzel-emoji b/.local/bin/fuzzel-emoji index a7d9291a1..a526a4646 100755 --- a/.local/bin/fuzzel-emoji +++ b/.local/bin/fuzzel-emoji @@ -1,10 +1,26 @@ #!/bin/bash -if [ $? -eq 0 ] -then - sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy -else - sed '1,/^### DATA ###$/d' $0 | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n' | wl-copy -fi +set -euo pipefail + +MODE="${1:-type}" + +emoji="$(sed '1,/^### DATA ###$/d' "$0" | fuzzel --match-mode fzf --dmenu | cut -d ' ' -f 1 | tr -d '\n')" + +case "$MODE" in + type) + wtype "${emoji}" || wl-copy "${emoji}" + ;; + copy) + wl-copy "${emoji}" + ;; + both) + wtype "${emoji}" || true + wl-copy "${emoji}" + ;; + *) + echo "Usage: $0 [type|copy|both]" + exit 1 + ;; +esac exit ### DATA ### 😀 grinning face face smile happy joy :D grin From aa06056faca4fffa7b6e614c41cecd5a0c57e564 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:00:29 +0200 Subject: [PATCH 04/84] remove unnecessary rubyshot --- .local/bin/rubyshot | 6 ------ 1 file changed, 6 deletions(-) delete mode 100755 .local/bin/rubyshot diff --git a/.local/bin/rubyshot b/.local/bin/rubyshot deleted file mode 100755 index 8431bd693..000000000 --- a/.local/bin/rubyshot +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/bash - -WORKSPACES="$(hyprctl monitors -j | jq -r 'map(.activeWorkspace.id)')" -WINDOWS="$(hyprctl clients -j | jq -r --argjson workspaces "$WORKSPACES" 'map(select([.workspace.id] | inside($workspaces)))' )" -GEOM=$(echo "$WINDOWS" | jq -r '.[] | "\(.at[0]),\(.at[1]) \(.size[0])x\(.size[1])"' | slurp -f '%x %y %w %h') -wayshot -s "$GEOM" --stdout ${#:+"$@"} \ No newline at end of file From 502c50f15506b90a70d54ce613cda823a4b9c2c8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:00:35 +0200 Subject: [PATCH 05/84] add wtype dep --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a6fc98402..22d7b7b58 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ - **Assumption**: You are already using the AGS illogical-impulse - **Install Qt packages** (idk which are actually needed so this is everything I have): `qt5-base qt5-declarative qt5-graphicaleffects qt5-imageformats qt5-quickcontrols qt5-quickcontrols2 qt5-svg qt5-translations qt5-wayland qt5-x11extras qt6-5compat qt6-base qt6-declarative qt6-imageformats qt6-multimedia qt6-positioning qt6-quicktimeline qt6-sensors qt6-svg qt6-tools qt6-translations qt6-virtualkeyboard qt6-wayland qt6-webchannel qt6-webengine qt6-websockets qt6-webview syntax-highlighting` -- **Install quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast` +- **Install quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast wtype` - **Copy** `.config/quickshell` folder and hyprland config files in `.config/hypr/hyprland/` (backing up is your responsibility) (or you can create a new user) - **Run quickshell** with `qs` and see how things are - it's not finished, but **feedback is very welcome** - We currently have bar, right sidebar, search/overview From 97442f3c11a8da9d81b2a8a7765970c362f451ef Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:14:08 +0200 Subject: [PATCH 06/84] clipboard history: also type directly --- .config/hypr/hyprland.conf | 2 +- .config/hypr/hyprland/keybinds.conf | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/hypr/hyprland.conf b/.config/hypr/hyprland.conf index 1255db74a..eed207898 100644 --- a/.config/hypr/hyprland.conf +++ b/.config/hypr/hyprland.conf @@ -1,5 +1,5 @@ # This file sources other files in `hyprland` and `custom` folders -# You wanna add your stuff in file in `custom` +# You wanna add your stuff in files in `custom` exec = hyprctl dispatch submap global # DO NOT REMOVE THIS OR YOU WON'T BE ABLE TO USE ANY KEYBIND submap = global # This is required for catchall to work diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index a2a485c79..496e9899e 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -46,8 +46,8 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi ##! Utilities # Screenshot, Record, OCR, Color picker, Clipboard history -bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard -bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Pick emoji >> clipboard +bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel && exit || entry="$(cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode)"; [ -n "$entry" ] && wl-copy -- "$entry" && wtype -- "${entry//$'\n'/ }" # Clipboard history +bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Emoji bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate # OCR From 11087142af1229c9fcc4723e3877262304d143a1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:14:33 +0200 Subject: [PATCH 07/84] re enable anims for fuzzel --- .config/hypr/hyprland/rules.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/.config/hypr/hyprland/rules.conf b/.config/hypr/hyprland/rules.conf index 876f45b53..423651f77 100644 --- a/.config/hypr/hyprland/rules.conf +++ b/.config/hypr/hyprland/rules.conf @@ -113,7 +113,6 @@ layerrule = blur, quickshell:session layerrule = noanim, quickshell:session # Launchers need to be FAST layerrule = noanim, quickshell:overview -layerrule = noanim, launcher layerrule = noanim, gtk4-layer-shell ## outfoxxed's stuff layerrule = blur, shell:bar From 84f28f6411ac330c98c6b86cd5b289defd4841f5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:50:20 +0200 Subject: [PATCH 08/84] tabs: nicer indicator anim --- .../modules/common/widgets/PrimaryTabBar.qml | 53 ++++++++++--------- .../common/widgets/SecondaryTabButton.qml | 2 +- .../modules/sidebarRight/todo/TodoWidget.qml | 50 +++++++++-------- 3 files changed, 55 insertions(+), 50 deletions(-) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml index e0fbd6d90..d8eff3378 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml @@ -52,38 +52,39 @@ ColumnLayout { root.enableIndicatorAnimation = true } } + Rectangle { + id: indicator + property int tabCount: root.tabButtonList.length + property real fullTabSize: root.width / tabCount; + property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + + implicitWidth: targetWidth + anchors { + top: parent.top + bottom: parent.bottom + } + + x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2 + color: Appearance.m3colors.m3primary radius: Appearance.rounding.full - z: 2 - anchors.fill: parent - // TODO: make the end point in the moving direction go first - anchors.leftMargin: { - const tabCount = root.tabButtonList.length - const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth - const fullTabSize = root.width / tabCount; - return fullTabSize * root.externalTrackedTab + (fullTabSize - targetWidth) / 2; + Behavior on x { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } } - anchors.rightMargin: { - const tabCount = root.tabButtonList.length - const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth - const fullTabSize = root.width / tabCount; - return fullTabSize * (tabCount - root.externalTrackedTab - 1) + (fullTabSize - targetWidth) / 2; + + Behavior on implicitWidth { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } } - Behavior on anchors.leftMargin { - enabled: root.enableIndicatorAnimation - SmoothedAnimation { - velocity: Appearance.animation.positionShift.velocity - } - } - Behavior on anchors.rightMargin { - enabled: root.enableIndicatorAnimation - SmoothedAnimation { - velocity: Appearance.animation.positionShift.velocity - } - } - } } diff --git a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml index 050cec6c3..015324451 100644 --- a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -70,7 +70,7 @@ TabButton { ColorAnimation { duration: Appearance.animation.elementMove.duration easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } } diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index b545eba3b..c735f451f 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -79,37 +79,41 @@ Item { tabIndicator.enableIndicatorAnimation = true } } + Rectangle { + id: indicator + property int tabCount: root.tabButtonList.length + property real fullTabSize: root.width / tabCount; + property real targetWidth: tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth + + implicitWidth: targetWidth + anchors { + top: parent.top + bottom: parent.bottom + } + + x: tabBar.currentIndex * fullTabSize + (fullTabSize - targetWidth) / 2 + color: Appearance.m3colors.m3primary radius: Appearance.rounding.full - z: 2 - anchors.fill: parent - anchors.leftMargin: { - const tabCount = root.tabButtonList.length - const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth - const fullTabSize = tabBar.width / tabCount; - return fullTabSize * currentTab + (fullTabSize - targetWidth) / 2; - } - anchors.rightMargin: { - const tabCount = root.tabButtonList.length - const targetWidth = tabBar.contentItem.children[0].children[tabBar.currentIndex].tabContentWidth - const fullTabSize = tabBar.width / tabCount; - return fullTabSize * (tabCount - currentTab - 1) + (fullTabSize - targetWidth) / 2; - } - Behavior on anchors.leftMargin { + Behavior on x { enabled: tabIndicator.enableIndicatorAnimation - SmoothedAnimation { - velocity: Appearance.animation.positionShift.velocity - } + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } } - Behavior on anchors.rightMargin { + + Behavior on implicitWidth { enabled: tabIndicator.enableIndicatorAnimation - SmoothedAnimation { - velocity: Appearance.animation.positionShift.velocity - } + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } } - } } From 18f6f2ee9a09daabd4cfb2a6f6571d9bd9913528 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 16 May 2025 23:52:44 +0200 Subject: [PATCH 09/84] replace background empty item with null --- .config/quickshell/modules/cheatsheet/Cheatsheet.qml | 2 +- .config/quickshell/modules/common/widgets/NavRailButton.qml | 2 +- .config/quickshell/modules/common/widgets/StyledToolTip.qml | 2 +- .config/quickshell/modules/overview/SearchWidget.qml | 2 +- .config/quickshell/modules/sidebarLeft/AiChat.qml | 2 +- .config/quickshell/modules/sidebarLeft/Anime.qml | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 154bd709c..3cba244e7 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -111,7 +111,7 @@ Scope { // Scope cheatsheetRoot.hide() } - background: Item {} + background: null contentItem: Rectangle { anchors.fill: parent radius: Appearance.rounding.full diff --git a/.config/quickshell/modules/common/widgets/NavRailButton.qml b/.config/quickshell/modules/common/widgets/NavRailButton.qml index 13bf2f949..0c2ffbfdf 100644 --- a/.config/quickshell/modules/common/widgets/NavRailButton.qml +++ b/.config/quickshell/modules/common/widgets/NavRailButton.qml @@ -16,7 +16,7 @@ Button { implicitHeight: columnLayout.implicitHeight implicitWidth: columnLayout.implicitWidth - background: Item {} + background: null PointingHandInteraction {} // Real stuff diff --git a/.config/quickshell/modules/common/widgets/StyledToolTip.qml b/.config/quickshell/modules/common/widgets/StyledToolTip.qml index 838129351..627dfb965 100644 --- a/.config/quickshell/modules/common/widgets/StyledToolTip.qml +++ b/.config/quickshell/modules/common/widgets/StyledToolTip.qml @@ -26,7 +26,7 @@ ToolTip { } } - background: Item {} + background: null contentItem: Item { id: contentItemBackground diff --git a/.config/quickshell/modules/overview/SearchWidget.qml b/.config/quickshell/modules/overview/SearchWidget.qml index 3be7b915f..c861b4549 100644 --- a/.config/quickshell/modules/overview/SearchWidget.qml +++ b/.config/quickshell/modules/overview/SearchWidget.qml @@ -227,7 +227,7 @@ Item { // Wrapper } } - background: Item {} + background: null cursorDelegate: Rectangle { width: 1 diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index dba4fefa3..f8472cb79 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -383,7 +383,7 @@ int main(int argc, char* argv[]) { placeholderText: StringUtils.format(qsTr('Message the model... "{0}" for commands'), root.commandPrefix) placeholderTextColor: Appearance.m3colors.m3outline - background: Item {} + background: null onTextChanged: { // Handle suggestions if(messageInputField.text.length === 0) { diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index 29b571b43..5188ff3c9 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -419,7 +419,7 @@ Item { placeholderText: StringUtils.format(qsTr('Enter tags, or "{0}" for commands'), root.commandPrefix) placeholderTextColor: Appearance.m3colors.m3outline - background: Item {} + background: null property Timer searchTimer: Timer { // Timer for tag suggestions interval: root.tagSuggestionDelay From 7b97b4060c162df7064edd0b51ba73ae44340edc Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 00:12:49 +0200 Subject: [PATCH 10/84] primary tab buttons: ripple --- .../common/widgets/PrimaryTabButton.qml | 77 ++++++++++++++++++- 1 file changed, 76 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index e5993d8e0..6b865cf77 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -12,11 +12,73 @@ TabButton { property string buttonIcon property bool selected: false property int tabContentWidth: contentItem.children[0].implicitWidth + property int rippleDuration: 1000 height: buttonBackground.height PointingHandInteraction {} - background: Rectangle { + component RippleAnim: NumberAnimation { + duration: rippleDuration + easing.type: Appearance.animation.elementMoveEnter.type + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve + } + + MouseArea { + anchors.fill: parent + onPressed: (event) => { + + const {x,y} = event + const stateY = buttonBackground.y; + rippleAnim.x = x; + rippleAnim.y = y - stateY; + + const dist = (ox,oy) => ox*ox + oy*oy + const stateEndY = stateY + buttonBackground.height + rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY))) + + rippleAnim.restart(); + event.accepted = false + } + } + + SequentialAnimation { + id: rippleAnim + + property real x + property real y + property real radius + + PropertyAction { + target: ripple + property: "x" + value: rippleAnim.x + } + PropertyAction { + target: ripple + property: "y" + value: rippleAnim.y + } + PropertyAction { + target: ripple + property: "opacity" + value: 0.1 + } + ParallelAnimation { + RippleAnim { + target: ripple + properties: "implicitWidth,implicitHeight" + from: 0 + to: rippleAnim.radius * 2 + } + RippleAnim { + target: ripple + property: "opacity" + to: 0 + } + } + } + + background: ClippingRectangle { id: buttonBackground radius: Appearance.rounding.small implicitHeight: 50 @@ -29,6 +91,19 @@ TabButton { easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } + + Rectangle { + id: ripple + + radius: Appearance.rounding.full + color: button.current ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface + opacity: 0 + + transform: Translate { + x: -ripple.width / 2 + y: -ripple.height / 2 + } + } } contentItem: Item { anchors.centerIn: buttonBackground From 625e2992a58372a8ce680dadb6e74b8ae4cfa066 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 00:19:08 +0200 Subject: [PATCH 11/84] adjust ripple anim --- .../quickshell/modules/common/widgets/PrimaryTabButton.qml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index 6b865cf77..8bfc70b04 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -12,7 +12,7 @@ TabButton { property string buttonIcon property bool selected: false property int tabContentWidth: contentItem.children[0].implicitWidth - property int rippleDuration: 1000 + property int rippleDuration: 1200 height: buttonBackground.height PointingHandInteraction {} @@ -20,13 +20,12 @@ TabButton { component RippleAnim: NumberAnimation { duration: rippleDuration easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve + easing.bezierCurve: Appearance.animationCurves.standardDecel } MouseArea { anchors.fill: parent - onPressed: (event) => { - + onPressed: (event) => { const {x,y} = event const stateY = buttonBackground.y; rippleAnim.x = x; From 8355ea842cec745d06a67d3c9584411c62c8837e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:11:33 +0200 Subject: [PATCH 12/84] make bar slide instead of pop in --- .config/hypr/hyprland/rules.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/hypr/hyprland/rules.conf b/.config/hypr/hyprland/rules.conf index 423651f77..13bc45012 100644 --- a/.config/hypr/hyprland/rules.conf +++ b/.config/hypr/hyprland/rules.conf @@ -105,6 +105,7 @@ layerrule = ignorealpha 0.6, osk[0-9]* # Quickshell ## My stuff +layerrule = animation slide, quickshell:bar layerrule = animation fade, quickshell:screenCorners layerrule = animation slide right, quickshell:sidebarRight layerrule = animation slide left, quickshell:sidebarLeft From 2b778dcf6eca3ff0491d158c737451158c0c69ca Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:15:01 +0200 Subject: [PATCH 13/84] add spacing to tab pages --- .config/quickshell/modules/sidebarLeft/SidebarLeft.qml | 1 + .config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml | 1 + .config/quickshell/modules/sidebarRight/todo/TodoWidget.qml | 1 + 3 files changed, 3 insertions(+) diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index b6eb13bea..865b5e58d 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -146,6 +146,7 @@ Scope { // Scope Layout.topMargin: 5 Layout.fillWidth: true Layout.fillHeight: true + spacing: 10 currentIndex: sidebarRoot.selectedTab onCurrentIndexChanged: { tabBar.enableIndicatorAnimation = true diff --git a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml index ede9e7219..bb871993a 100644 --- a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml @@ -62,6 +62,7 @@ Rectangle { Layout.topMargin: 5 Layout.fillWidth: true Layout.fillHeight: true + spacing: 10 currentIndex: root.selectedTab onCurrentIndexChanged: { tabBar.enableIndicatorAnimation = true diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index c735f451f..3bbb4fc42 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -129,6 +129,7 @@ Item { Layout.topMargin: 10 Layout.fillWidth: true Layout.fillHeight: true + spacing: 10 clip: true currentIndex: currentTab onCurrentIndexChanged: { From 705a659d19fc52992c44f38cf8ee8873951f8dcd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:15:15 +0200 Subject: [PATCH 14/84] adjust layer 1 colors --- .config/quickshell/modules/common/Appearance.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index b7d4ec608..e15d264d7 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -118,8 +118,8 @@ Singleton { property color colOnLayer2Disabled: mix(colOnLayer2, m3colors.m3background, 0.4); property color colLayer3: mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96); property color colOnLayer3: m3colors.m3onSurface; - property color colLayer1Hover: mix(colLayer1, colOnLayer1, 0.88); - property color colLayer1Active: mix(colLayer1, colOnLayer1, 0.77); + property color colLayer1Hover: mix(colLayer1, colOnLayer1, 0.92); + property color colLayer1Active: mix(colLayer1, colOnLayer1, 0.85); property color colLayer2Hover: mix(colLayer2, colOnLayer2, 0.90); property color colLayer2Active: mix(colLayer2, colOnLayer2, 0.80); property color colLayer2Disabled: mix(colLayer2, m3colors.m3background, 0.8); From 50221e938b653c8b4d5732c776fefaa2a62533d7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:37:37 +0200 Subject: [PATCH 15/84] tabs: make ripple function correctly on hold --- .../common/widgets/PrimaryTabButton.qml | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index 8bfc70b04..2f9174497 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -25,6 +26,7 @@ TabButton { MouseArea { anchors.fill: parent + propagateComposedEvents: true onPressed: (event) => { const {x,y} = event const stateY = buttonBackground.y; @@ -35,9 +37,20 @@ TabButton { const stateEndY = stateY + buttonBackground.height rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY))) + rippleFadeAnim.complete(); rippleAnim.restart(); - event.accepted = false } + onReleased: (event) => { + button.click() // Because the MouseArea already consumed the event + rippleFadeAnim.restart(); + } + } + + RippleAnim { + id: rippleFadeAnim + target: ripple + property: "opacity" + to: 0 } SequentialAnimation { @@ -60,7 +73,7 @@ TabButton { PropertyAction { target: ripple property: "opacity" - value: 0.1 + value: 1 } ParallelAnimation { RippleAnim { @@ -69,19 +82,22 @@ TabButton { from: 0 to: rippleAnim.radius * 2 } - RippleAnim { - target: ripple - property: "opacity" - to: 0 - } } } - background: ClippingRectangle { + background: Rectangle { id: buttonBackground radius: Appearance.rounding.small implicitHeight: 50 - color: (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + color: (button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: buttonBackground.width + height: buttonBackground.height + radius: buttonBackground.radius + } + } Behavior on color { ColorAnimation { @@ -95,7 +111,7 @@ TabButton { id: ripple radius: Appearance.rounding.full - color: button.current ? Appearance.m3colors.m3primary : Appearance.m3colors.m3onSurface + color: Appearance.colors.colLayer1Active opacity: 0 transform: Translate { From edec446aed32557afa1df18f628433dba9476259 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:47:44 +0200 Subject: [PATCH 16/84] space --- .config/quickshell/modules/common/widgets/PrimaryTabButton.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index 2f9174497..9839f0a57 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -120,6 +120,7 @@ TabButton { } } } + contentItem: Item { anchors.centerIn: buttonBackground ColumnLayout { From f5137ada133f4f55510b679ad9014808ad9beec3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:47:54 +0200 Subject: [PATCH 17/84] give secondary tab button ripple --- .../common/widgets/SecondaryTabButton.qml | 95 ++++++++++++++++++- 1 file changed, 93 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml index 015324451..c591b867f 100644 --- a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -11,25 +12,115 @@ TabButton { property string buttonText property string buttonIcon property bool selected: false + property int rippleDuration: 1200 height: buttonBackground.height property int tabContentWidth: buttonBackground.width - buttonBackground.radius*2 PointingHandInteraction {} + component RippleAnim: NumberAnimation { + duration: rippleDuration + easing.type: Appearance.animation.elementMoveEnter.type + easing.bezierCurve: Appearance.animationCurves.standardDecel + } + + MouseArea { + anchors.fill: parent + propagateComposedEvents: true + onPressed: (event) => { + const {x,y} = event + const stateY = buttonBackground.y; + rippleAnim.x = x; + rippleAnim.y = y - stateY; + + const dist = (ox,oy) => ox*ox + oy*oy + const stateEndY = stateY + buttonBackground.height + rippleAnim.radius = Math.sqrt(Math.max(dist(0, stateY), dist(0, stateEndY), dist(width, stateY), dist(width, stateEndY))) + + rippleFadeAnim.complete(); + rippleAnim.restart(); + } + onReleased: (event) => { + button.click() // Because the MouseArea already consumed the event + rippleFadeAnim.restart(); + } + } + + RippleAnim { + id: rippleFadeAnim + target: ripple + property: "opacity" + to: 0 + } + + SequentialAnimation { + id: rippleAnim + + property real x + property real y + property real radius + + PropertyAction { + target: ripple + property: "x" + value: rippleAnim.x + } + PropertyAction { + target: ripple + property: "y" + value: rippleAnim.y + } + PropertyAction { + target: ripple + property: "opacity" + value: 1 + } + ParallelAnimation { + RippleAnim { + target: ripple + properties: "implicitWidth,implicitHeight" + from: 0 + to: rippleAnim.radius * 2 + } + } + } + background: Rectangle { id: buttonBackground radius: Appearance.rounding.small implicitHeight: 37 - color: (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + color: (button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: buttonBackground.width + height: buttonBackground.height + radius: buttonBackground.radius + } + } Behavior on color { ColorAnimation { duration: Appearance.animation.elementMove.duration easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } + } + + Rectangle { + id: ripple + + radius: Appearance.rounding.full + color: Appearance.colors.colLayer1Active + opacity: 0 + + transform: Translate { + x: -ripple.width / 2 + y: -ripple.height / 2 } } } + contentItem: Item { anchors.centerIn: buttonBackground RowLayout { From e8d899d4d0475cd9bdb4c760982e1d0142d9e2b5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 22:54:09 +0200 Subject: [PATCH 18/84] adjust osd padding --- .../quickshell/modules/onScreenDisplay/OsdValueIndicator.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml index f13cd7fc3..649d34bf6 100644 --- a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml +++ b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml @@ -15,7 +15,7 @@ Item { required property string name property bool rotateIcon: false - property real valueIndicatorVerticalPadding: 5 + property real valueIndicatorVerticalPadding: 9 property real valueIndicatorLeftPadding: 10 property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding From 8d91007a89e5ba0f478c577eaec64582c79f09e3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:03:41 +0200 Subject: [PATCH 19/84] readme: remove some unnecessary deps --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 22d7b7b58..11f7e7675 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ ## Not ready, but feel free to try it. It's simple: - **Assumption**: You are already using the AGS illogical-impulse -- **Install Qt packages** (idk which are actually needed so this is everything I have): `qt5-base qt5-declarative qt5-graphicaleffects qt5-imageformats qt5-quickcontrols qt5-quickcontrols2 qt5-svg qt5-translations qt5-wayland qt5-x11extras qt6-5compat qt6-base qt6-declarative qt6-imageformats qt6-multimedia qt6-positioning qt6-quicktimeline qt6-sensors qt6-svg qt6-tools qt6-translations qt6-virtualkeyboard qt6-wayland qt6-webchannel qt6-webengine qt6-websockets qt6-webview syntax-highlighting` +- **Install Qt packages** (idk which are actually needed so this is everything I have): `qt5-base qt5-declarative qt5-graphicaleffects qt5-imageformats qt5-svg qt5-translations qt5-wayland qt6-5compat qt6-base qt6-declarative qt6-imageformats qt6-multimedia qt6-positioning qt6-quicktimeline qt6-sensors qt6-svg qt6-tools qt6-translations qt6-virtualkeyboard qt6-wayland syntax-highlighting` - **Install quickshell and more stuff**: `yay -S quickshell matugen-bin grimblast wtype` - **Copy** `.config/quickshell` folder and hyprland config files in `.config/hypr/hyprland/` (backing up is your responsibility) (or you can create a new user) - **Run quickshell** with `qs` and see how things are - it's not finished, but **feedback is very welcome** From c8bbdbc47235080519ed1a3f0dc7097c65f3286a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:04:00 +0200 Subject: [PATCH 20/84] tab buttons: fix pointing hand cursor --- .../quickshell/modules/common/widgets/PrimaryTabButton.qml | 4 +--- .../quickshell/modules/common/widgets/SecondaryTabButton.qml | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index 9839f0a57..c05f3e8fa 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -16,8 +16,6 @@ TabButton { property int rippleDuration: 1200 height: buttonBackground.height - PointingHandInteraction {} - component RippleAnim: NumberAnimation { duration: rippleDuration easing.type: Appearance.animation.elementMoveEnter.type @@ -26,7 +24,7 @@ TabButton { MouseArea { anchors.fill: parent - propagateComposedEvents: true + cursorShape: Qt.PointingHandCursor onPressed: (event) => { const {x,y} = event const stateY = buttonBackground.y; diff --git a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml index c591b867f..eb6209121 100644 --- a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -26,7 +26,7 @@ TabButton { MouseArea { anchors.fill: parent - propagateComposedEvents: true + cursorShape: Qt.PointingHandCursor onPressed: (event) => { const {x,y} = event const stateY = buttonBackground.y; From e7e08cda591a3a3f74fa6bf5e8314282965abbbf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:19:41 +0200 Subject: [PATCH 21/84] right sidebar: dont create multiple for each monitor --- .../modules/sidebarRight/SidebarRight.qml | 393 ++++++++---------- 1 file changed, 184 insertions(+), 209 deletions(-) diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index e9ef5ffdc..713a80ced 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -17,217 +17,201 @@ Scope { property int sidebarWidth: Appearance.sizes.sidebarWidth property int sidebarPadding: 15 - Variants { - id: sidebarVariants - model: Quickshell.screens - - Loader { - id: sidebarLoader - active: false - property var modelData - onActiveChanged: { - GlobalStates.sidebarRightOpenCount += active ? 1 : -1 - } - - PanelWindow { - id: sidebarRoot - visible: sidebarLoader.active - focusable: true - - onVisibleChanged: { - if (!visible) sidebarLoader.active = false - } - - function hide() { - sidebarLoader.active = false - } - - screen: modelData - exclusiveZone: 0 - implicitWidth: sidebarWidth - WlrLayershell.namespace: "quickshell:sidebarRight" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: "transparent" - - anchors { - top: true - right: true - bottom: true - } - - HyprlandFocusGrab { - id: grab - windows: [ sidebarRoot ] - active: false - onCleared: () => { - if (!active) sidebarRoot.hide() - } - } - - Connections { - target: sidebarRoot - function onVisibleChanged() { - delayedGrabTimer.start() - } - } - - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = sidebarRoot.visible - } - } - - // Background - Rectangle { - id: sidebarRightBackground - - anchors.centerIn: parent - width: parent.width - Appearance.sizes.hyprlandGapsOut * 2 - height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 - color: Appearance.colors.colLayer0 - radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 - - Keys.onPressed: (event) => { - if (event.key === Qt.Key_Escape) { - sidebarRoot.hide(); - } - } - - ColumnLayout { - anchors.fill: parent - anchors.margins: sidebarPadding - - spacing: sidebarPadding - - RowLayout { - Layout.fillHeight: false - spacing: 10 - Layout.margins: 10 - Layout.topMargin: 5 - Layout.bottomMargin: 0 - - Item { - implicitWidth: distroIcon.width - implicitHeight: distroIcon.height - CustomIcon { - id: distroIcon - width: 25 - height: 25 - source: SystemInfo.distroIcon - } - ColorOverlay { - anchors.fill: distroIcon - source: distroIcon - color: Appearance.colors.colOnLayer0 - } - } - - StyledText { - font.pixelSize: Appearance.font.pixelSize.normal - color: Appearance.colors.colOnLayer0 - text: `Uptime: ${DateTime.uptime}` - textFormat: Text.MarkdownText - } - - Item { - Layout.fillWidth: true - } - - QuickToggleButton { - toggled: false - buttonIcon: "power_settings_new" - onClicked: { - Hyprland.dispatch("global quickshell:sessionOpen") - } - StyledToolTip { - content: qsTr("Session") - } - } - } - - Rectangle { - Layout.alignment: Qt.AlignHCenter - Layout.fillHeight: false - radius: Appearance.rounding.full - color: Appearance.colors.colLayer1 - implicitWidth: sidebarQuickControlsRow.implicitWidth + 10 - implicitHeight: sidebarQuickControlsRow.implicitHeight + 10 - - - RowLayout { - id: sidebarQuickControlsRow - anchors.fill: parent - anchors.margins: 5 - spacing: 5 - - NetworkToggle {} - BluetoothToggle {} - NightLight {} - GameMode {} - IdleInhibitor {} - - } - } - - // Center widget group - CenterWidgetGroup { - focus: sidebarRoot.visible - Layout.alignment: Qt.AlignHCenter - Layout.fillHeight: true - Layout.fillWidth: true - } - - BottomWidgetGroup { - Layout.alignment: Qt.AlignHCenter - Layout.fillHeight: false - Layout.fillWidth: true - Layout.preferredHeight: implicitHeight - } - } - } - - // Shadow - DropShadow { - anchors.fill: sidebarRightBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: sidebarRightBackground - } - - } + Loader { + id: sidebarLoader + active: false + property var modelData + onActiveChanged: { + GlobalStates.sidebarRightOpenCount += active ? 1 : -1 } + PanelWindow { + id: sidebarRoot + visible: sidebarLoader.active + focusable: true + + onVisibleChanged: { + if (!visible) sidebarLoader.active = false + } + + function hide() { + sidebarLoader.active = false + } + + exclusiveZone: 0 + implicitWidth: sidebarWidth + WlrLayershell.namespace: "quickshell:sidebarRight" + // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab + // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + color: "transparent" + + anchors { + top: true + right: true + bottom: true + } + + HyprlandFocusGrab { + id: grab + windows: [ sidebarRoot ] + active: false + onCleared: () => { + if (!active) sidebarRoot.hide() + } + } + + Connections { + target: sidebarRoot + function onVisibleChanged() { + delayedGrabTimer.start() + } + } + + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + grab.active = sidebarRoot.visible + } + } + + // Background + Rectangle { + id: sidebarRightBackground + + anchors.centerIn: parent + width: parent.width - Appearance.sizes.hyprlandGapsOut * 2 + height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape) { + sidebarRoot.hide(); + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: sidebarPadding + + spacing: sidebarPadding + + RowLayout { + Layout.fillHeight: false + spacing: 10 + Layout.margins: 10 + Layout.topMargin: 5 + Layout.bottomMargin: 0 + + Item { + implicitWidth: distroIcon.width + implicitHeight: distroIcon.height + CustomIcon { + id: distroIcon + width: 25 + height: 25 + source: SystemInfo.distroIcon + } + ColorOverlay { + anchors.fill: distroIcon + source: distroIcon + color: Appearance.colors.colOnLayer0 + } + } + + StyledText { + font.pixelSize: Appearance.font.pixelSize.normal + color: Appearance.colors.colOnLayer0 + text: `Uptime: ${DateTime.uptime}` + textFormat: Text.MarkdownText + } + + Item { + Layout.fillWidth: true + } + + QuickToggleButton { + toggled: false + buttonIcon: "power_settings_new" + onClicked: { + Hyprland.dispatch("global quickshell:sessionOpen") + } + StyledToolTip { + content: qsTr("Session") + } + } + } + + Rectangle { + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: false + radius: Appearance.rounding.full + color: Appearance.colors.colLayer1 + implicitWidth: sidebarQuickControlsRow.implicitWidth + 10 + implicitHeight: sidebarQuickControlsRow.implicitHeight + 10 + + + RowLayout { + id: sidebarQuickControlsRow + anchors.fill: parent + anchors.margins: 5 + spacing: 5 + + NetworkToggle {} + BluetoothToggle {} + NightLight {} + GameMode {} + IdleInhibitor {} + + } + } + + // Center widget group + CenterWidgetGroup { + focus: sidebarRoot.visible + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: true + Layout.fillWidth: true + } + + BottomWidgetGroup { + Layout.alignment: Qt.AlignHCenter + Layout.fillHeight: false + Layout.fillWidth: true + Layout.preferredHeight: implicitHeight + } + } + } + + // Shadow + DropShadow { + anchors.fill: sidebarRightBackground + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + source: sidebarRightBackground + } + + } } IpcHandler { target: "sidebarRight" function toggle(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = !loader.active; - } + sidebarLoader.active = !sidebarLoader.active; } function close(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = false; - } + sidebarLoader.active = false; } function open(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = true; - } + sidebarLoader.active = true; } } @@ -236,10 +220,7 @@ Scope { description: "Toggles right sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = !loader.active; - } + sidebarLoader.active = !sidebarLoader.active; } } GlobalShortcut { @@ -247,10 +228,7 @@ Scope { description: "Opens right sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = true; - } + sidebarLoader.active = true; } } GlobalShortcut { @@ -258,10 +236,7 @@ Scope { description: "Closes right sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let loader = sidebarVariants.instances[i]; - loader.active = false; - } + sidebarLoader.active = false; } } From 889cff18883d7a0a8a94e86ea0db7db733843979 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:41:04 +0200 Subject: [PATCH 22/84] cheatsheet: dont create multiple for each monitor --- .../modules/cheatsheet/Cheatsheet.qml | 314 ++++++++---------- 1 file changed, 144 insertions(+), 170 deletions(-) diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 3cba244e7..738fc621d 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -15,154 +15,146 @@ import Quickshell.Hyprland Scope { // Scope id: root - Variants { // Window repeater - id: cheatsheetVariants - model: Quickshell.screens - - Loader { - id: cheatsheetLoader - active: false - property var modelData - - sourceComponent: PanelWindow { // Window - id: cheatsheetRoot - visible: cheatsheetLoader.active - focusable: true - - anchors { - top: true - bottom: true - left: true - right: true - } - - function hide() { - cheatsheetLoader.active = false - } - - screen: modelData - exclusiveZone: 0 - implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2 - implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2 - WlrLayershell.namespace: "quickshell:cheatsheet" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: "transparent" - - mask: Region { - item: cheatsheetBackground - } - - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ cheatsheetRoot ] - active: false - onCleared: () => { - if (!active) cheatsheetRoot.hide() - } - } - - Connections { - target: cheatsheetRoot - function onVisibleChanged() { - delayedGrabTimer.start() - } - } - - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = cheatsheetRoot.visible - } - } - - // Background - Rectangle { - id: cheatsheetBackground - anchors.centerIn: parent - color: Appearance.colors.colLayer0 - radius: Appearance.rounding.windowRounding - property real padding: 30 - implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 - implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 - - Keys.onPressed: (event) => { // Esc to close - if (event.key === Qt.Key_Escape) { - cheatsheetRoot.hide() - } - } - - Button { // Close button - id: closeButton - focus: cheatsheetRoot.visible - implicitWidth: 40 - implicitHeight: 40 - anchors { - top: parent.top - right: parent.right - topMargin: 20 - rightMargin: 20 - } - - PointingHandInteraction {} - onClicked: { - cheatsheetRoot.hide() - } - - background: null - contentItem: Rectangle { - anchors.fill: parent - radius: Appearance.rounding.full - color: closeButton.pressed ? Appearance.colors.colLayer0Active : - closeButton.hovered ? Appearance.colors.colLayer0Hover : - Appearance.transparentize(Appearance.colors.colLayer0, 1) - - Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - } - - MaterialSymbol { - anchors.centerIn: parent - font.pixelSize: Appearance.font.pixelSize.title - text: "close" - } - } - } - - ColumnLayout { // Real content - id: cheatsheetColumnLayout - anchors.centerIn: parent - spacing: 20 - - StyledText { - id: cheatsheetTitle - Layout.alignment: Qt.AlignHCenter - font.family: Appearance.font.family.title - font.pixelSize: Appearance.font.pixelSize.title - text: qsTr("Cheat sheet") - } - CheatsheetKeybinds {} - } - } - - // Shadow - DropShadow { - anchors.fill: cheatsheetBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: cheatsheetBackground - } + Loader { + id: cheatsheetLoader + active: false + + sourceComponent: PanelWindow { // Window + id: cheatsheetRoot + visible: cheatsheetLoader.active + focusable: true + anchors { + top: true + bottom: true + left: true + right: true } + + function hide() { + cheatsheetLoader.active = false + } + exclusiveZone: 0 + implicitWidth: cheatsheetBackground.width + Appearance.sizes.elevationMargin * 2 + implicitHeight: cheatsheetBackground.height + Appearance.sizes.elevationMargin * 2 + WlrLayershell.namespace: "quickshell:cheatsheet" + // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab + // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + color: "transparent" + + mask: Region { + item: cheatsheetBackground + } + + HyprlandFocusGrab { // Click outside to close + id: grab + windows: [ cheatsheetRoot ] + active: false + onCleared: () => { + if (!active) cheatsheetRoot.hide() + } + } + + Connections { + target: cheatsheetRoot + function onVisibleChanged() { + delayedGrabTimer.start() + } + } + + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + grab.active = cheatsheetRoot.visible + } + } + + // Background + Rectangle { + id: cheatsheetBackground + anchors.centerIn: parent + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.windowRounding + property real padding: 30 + implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 + implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 + + Keys.onPressed: (event) => { // Esc to close + if (event.key === Qt.Key_Escape) { + cheatsheetRoot.hide() + } + } + + Button { // Close button + id: closeButton + focus: cheatsheetRoot.visible + implicitWidth: 40 + implicitHeight: 40 + anchors { + top: parent.top + right: parent.right + topMargin: 20 + rightMargin: 20 + } + + PointingHandInteraction {} + onClicked: { + cheatsheetRoot.hide() + } + + background: null + contentItem: Rectangle { + anchors.fill: parent + radius: Appearance.rounding.full + color: closeButton.pressed ? Appearance.colors.colLayer0Active : + closeButton.hovered ? Appearance.colors.colLayer0Hover : + Appearance.transparentize(Appearance.colors.colLayer0, 1) + + Behavior on color { + ColorAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + + MaterialSymbol { + anchors.centerIn: parent + font.pixelSize: Appearance.font.pixelSize.title + text: "close" + } + } + } + + ColumnLayout { // Real content + id: cheatsheetColumnLayout + anchors.centerIn: parent + spacing: 20 + + StyledText { + id: cheatsheetTitle + Layout.alignment: Qt.AlignHCenter + font.family: Appearance.font.family.title + font.pixelSize: Appearance.font.pixelSize.title + text: qsTr("Cheat sheet") + } + CheatsheetKeybinds {} + } + } + + // Shadow + DropShadow { + anchors.fill: cheatsheetBackground + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + source: cheatsheetBackground + } + } } @@ -170,24 +162,15 @@ Scope { // Scope target: "cheatsheet" function toggle(): void { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = !loader.active; - } + cheatsheetLoader.active = !cheatsheetLoader.active } function close(): void { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = false - } + cheatsheetLoader.active = false } function open(): void { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = true; - } + cheatsheetLoader.active = true } } @@ -196,10 +179,7 @@ Scope { // Scope description: "Toggles cheatsheet on press" onPressed: { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = !loader.active; - } + cheatsheetLoader.active = !cheatsheetLoader.active; } } @@ -208,10 +188,7 @@ Scope { // Scope description: "Opens cheatsheet on press" onPressed: { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = true; - } + cheatsheetLoader.active = true; } } @@ -220,10 +197,7 @@ Scope { // Scope description: "Closes cheatsheet on press" onPressed: { - for (let i = 0; i < cheatsheetVariants.instances.length; i++) { - const loader = cheatsheetVariants.instances[i]; - loader.active = false; - } + cheatsheetLoader.active = false; } } From e66606170b66bc7da60934546cac615b2f87e0fd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:41:36 +0200 Subject: [PATCH 23/84] qs: set basic as base qtquick controls style fixes stuff for fox --- .config/quickshell/shell.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index fae85be7d..2280482b7 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -1,4 +1,5 @@ //@ pragma UseQApplication +//@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic import "./modules/bar/" import "./modules/cheatsheet/" From 01cb51d6b42354bf26c78bc3f92eb7bbd8218687 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:42:16 +0200 Subject: [PATCH 24/84] left sidebar: pinning, single instance --- .../modules/sidebarLeft/SidebarLeft.qml | 338 ++++++++---------- 1 file changed, 153 insertions(+), 185 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 865b5e58d..d6f4d53cd 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -17,174 +17,172 @@ Scope { // Scope property int sidebarPadding: 15 property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}] - Variants { // Window repeater - id: sidebarVariants - model: Quickshell.screens + PanelWindow { // Window + id: sidebarRoot + visible: false + focusable: true + property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab + property bool extend: false + property bool pin: false + property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth - PanelWindow { // Window - id: sidebarRoot - visible: false - focusable: true - property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab - property bool extend: false - property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth + onVisibleChanged: { + GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1 + } - onVisibleChanged: { - GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1 + exclusiveZone: pin ? sidebarWidth : 0 + implicitWidth: Appearance.sizes.sidebarWidthExtended + WlrLayershell.namespace: "quickshell:sidebarLeft" + // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + top: true + left: true + bottom: true + } + + mask: Region { + item: sidebarLeftBackground + } + + HyprlandFocusGrab { // Click outside to close + id: grab + windows: [ sidebarRoot ] + active: false + onCleared: () => { + if (!active) sidebarRoot.visible = false } + } - property var modelData - - screen: modelData - exclusiveZone: 0 - implicitWidth: Appearance.sizes.sidebarWidthExtended - WlrLayershell.namespace: "quickshell:sidebarLeft" - // Hyprland 0.49: Focus is always exclusive and setting this breaks mouse focus grab - // WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: "transparent" - - anchors { - top: true - left: true - bottom: true + Connections { + target: sidebarRoot + function onVisibleChanged() { + delayedGrabTimer.start() + swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() } - - mask: Region { - item: sidebarLeftBackground + function onPinChanged() { + grab.active = !sidebarRoot.pin } + } - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ sidebarRoot ] - active: false - onCleared: () => { - if (!active) sidebarRoot.visible = false + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + grab.active = sidebarRoot.visible && !sidebarRoot.pin + } + } + + // Background + Rectangle { + id: sidebarLeftBackground + + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: Appearance.sizes.hyprlandGapsOut + anchors.leftMargin: Appearance.sizes.hyprlandGapsOut + width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2 + height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 + focus: sidebarRoot.visible + + Behavior on width { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } - Connections { - target: sidebarRoot - function onVisibleChanged() { - delayedGrabTimer.start() - swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() + Keys.onPressed: (event) => { + // console.log("Key pressed: " + event.key) + if (event.key === Qt.Key_Escape) { + sidebarRoot.visible = false; + } + if (event.modifiers === Qt.ControlModifier) { + if (event.key === Qt.Key_PageDown) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)) + } + else if (event.key === Qt.Key_PageUp) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0)) + } + else if (event.key === Qt.Key_Tab) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length); + } + else if (event.key === Qt.Key_Backtab) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); + } + else if (event.key === Qt.Key_O) { + sidebarRoot.extend = !sidebarRoot.extend; + } + else if (event.key === Qt.Key_P) { + sidebarRoot.pin = !sidebarRoot.pin; + } + event.accepted = true; } } - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = sidebarRoot.visible - } - } + ColumnLayout { + anchors.fill: parent + anchors.margins: sidebarPadding + + spacing: sidebarPadding - // Background - Rectangle { - id: sidebarLeftBackground - - anchors.top: parent.top - anchors.left: parent.left - anchors.topMargin: Appearance.sizes.hyprlandGapsOut - anchors.leftMargin: Appearance.sizes.hyprlandGapsOut - width: sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2 - height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 - color: Appearance.colors.colLayer0 - radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 - focus: sidebarRoot.visible - - Behavior on width { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + PrimaryTabBar { // Tab strip + id: tabBar + tabButtonList: root.tabButtonList + externalTrackedTab: sidebarRoot.selectedTab + function onCurrentIndexChanged(currentIndex) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) } } - Keys.onPressed: (event) => { - // console.log("Key pressed: " + event.key) - if (event.key === Qt.Key_Escape) { - sidebarRoot.visible = false; + SwipeView { // Content pages + id: swipeView + Layout.topMargin: 5 + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 10 + currentIndex: sidebarRoot.selectedTab + onCurrentIndexChanged: { + tabBar.enableIndicatorAnimation = true + PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) } - if (event.modifiers === Qt.ControlModifier) { - if (event.key === Qt.Key_PageDown) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)) - } - else if (event.key === Qt.Key_PageUp) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0)) + + clip: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small } - else if (event.key === Qt.Key_Tab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length); - } - else if (event.key === Qt.Key_Backtab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); - } - else if (event.key === Qt.Key_O) { - sidebarRoot.extend = !sidebarRoot.extend; - } - event.accepted = true; + } + + AiChat { + panelWindow: sidebarRoot + } + Anime { + panelWindow: sidebarRoot } } - - ColumnLayout { - anchors.fill: parent - anchors.margins: sidebarPadding - - spacing: sidebarPadding - - PrimaryTabBar { // Tab strip - id: tabBar - tabButtonList: root.tabButtonList - externalTrackedTab: sidebarRoot.selectedTab - function onCurrentIndexChanged(currentIndex) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) - } - } - - SwipeView { // Content pages - id: swipeView - Layout.topMargin: 5 - Layout.fillWidth: true - Layout.fillHeight: true - spacing: 10 - currentIndex: sidebarRoot.selectedTab - onCurrentIndexChanged: { - tabBar.enableIndicatorAnimation = true - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) - } - - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small - } - } - - AiChat { - panelWindow: sidebarRoot - } - Anime { - panelWindow: sidebarRoot - } - } - - } - } - - // Shadow - DropShadow { - anchors.fill: sidebarLeftBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: sidebarLeftBackground + } + } + // Shadow + DropShadow { + anchors.fill: sidebarLeftBackground + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + source: sidebarLeftBackground } } @@ -193,32 +191,17 @@ Scope { // Scope target: "sidebarLeft" function toggle(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = !panelWindow.visible; - if(panelWindow.visible) Notifications.timeoutAll(); - } - } + sidebarRoot.visible = !sidebarRoot.visible; + if(sidebarRoot.visible) Notifications.timeoutAll(); } function close(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = false; - } - } + sidebarRoot.visible = false; } function open(): void { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = true; - if(panelWindow.visible) Notifications.timeoutAll(); - } - } + sidebarRoot.visible = true; + if(sidebarRoot.visible) Notifications.timeoutAll(); } } @@ -227,13 +210,8 @@ Scope { // Scope description: "Toggles left sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = !panelWindow.visible; - if(panelWindow.visible) Notifications.timeoutAll(); - } - } + sidebarRoot.visible = !sidebarRoot.visible; + if(sidebarRoot.visible) Notifications.timeoutAll(); } } @@ -242,13 +220,8 @@ Scope { // Scope description: "Opens left sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = true; - if(panelWindow.visible) Notifications.timeoutAll(); - } - } + sidebarRoot.visible = true; + if(sidebarRoot.visible) Notifications.timeoutAll(); } } @@ -257,12 +230,7 @@ Scope { // Scope description: "Closes left sidebar on press" onPressed: { - for (let i = 0; i < sidebarVariants.instances.length; i++) { - let panelWindow = sidebarVariants.instances[i]; - if (panelWindow.modelData.name == Hyprland.focusedMonitor.name) { - panelWindow.visible = false; - } - } + sidebarRoot.visible = false; } } From 2b98b8dadae54d6a28c276b8f751e8e44e1ed6e5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:45:38 +0200 Subject: [PATCH 25/84] remove unused var --- .config/quickshell/modules/sidebarRight/SidebarRight.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index 713a80ced..d9910a522 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -20,7 +20,6 @@ Scope { Loader { id: sidebarLoader active: false - property var modelData onActiveChanged: { GlobalStates.sidebarRightOpenCount += active ? 1 : -1 } From 8533310f294d6facc85b610d33ddc979f2e4f7c9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:45:53 +0200 Subject: [PATCH 26/84] left sidebar: put in loader --- .../modules/sidebarLeft/SidebarLeft.qml | 308 +++++++++--------- 1 file changed, 158 insertions(+), 150 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index d6f4d53cd..6068d59e1 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -17,191 +17,199 @@ Scope { // Scope property int sidebarPadding: 15 property var tabButtonList: [{"icon": "neurology", "name": qsTr("Intelligence")}, {"icon": "bookmark_heart", "name": qsTr("Anime")}] - PanelWindow { // Window - id: sidebarRoot - visible: false - focusable: true - property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab - property bool extend: false - property bool pin: false - property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth - - onVisibleChanged: { - GlobalStates.sidebarLeftOpenCount += visible ? 1 : -1 + Loader { + id: sidebarLoader + active: false + onActiveChanged: { + GlobalStates.sidebarLeftOpenCount += active ? 1 : -1 } + + PanelWindow { // Window + id: sidebarRoot + visible: sidebarLoader.active + focusable: true + property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab + property bool extend: false + property bool pin: false + property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth - exclusiveZone: pin ? sidebarWidth : 0 - implicitWidth: Appearance.sizes.sidebarWidthExtended - WlrLayershell.namespace: "quickshell:sidebarLeft" - // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - color: "transparent" - - anchors { - top: true - left: true - bottom: true - } - - mask: Region { - item: sidebarLeftBackground - } - - HyprlandFocusGrab { // Click outside to close - id: grab - windows: [ sidebarRoot ] - active: false - onCleared: () => { - if (!active) sidebarRoot.visible = false + function hide() { + sidebarLoader.active = false } - } - Connections { - target: sidebarRoot - function onVisibleChanged() { - delayedGrabTimer.start() - swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() + exclusiveZone: pin ? sidebarWidth : 0 + implicitWidth: Appearance.sizes.sidebarWidthExtended + WlrLayershell.namespace: "quickshell:sidebarLeft" + // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + top: true + left: true + bottom: true } - function onPinChanged() { - grab.active = !sidebarRoot.pin + + mask: Region { + item: sidebarLeftBackground } - } - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = sidebarRoot.visible && !sidebarRoot.pin - } - } - - // Background - Rectangle { - id: sidebarLeftBackground - - anchors.top: parent.top - anchors.left: parent.left - anchors.topMargin: Appearance.sizes.hyprlandGapsOut - anchors.leftMargin: Appearance.sizes.hyprlandGapsOut - width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2 - height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 - color: Appearance.colors.colLayer0 - radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 - focus: sidebarRoot.visible - - Behavior on width { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + HyprlandFocusGrab { // Click outside to close + id: grab + windows: [ sidebarRoot ] + active: false + onCleared: () => { + if (!active) sidebarRoot.hide() } } - Keys.onPressed: (event) => { - // console.log("Key pressed: " + event.key) - if (event.key === Qt.Key_Escape) { - sidebarRoot.visible = false; + Connections { + target: sidebarRoot + function onVisibleChanged() { + delayedGrabTimer.start() + swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() } - if (event.modifiers === Qt.ControlModifier) { - if (event.key === Qt.Key_PageDown) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)) - } - else if (event.key === Qt.Key_PageUp) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0)) - } - else if (event.key === Qt.Key_Tab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length); - } - else if (event.key === Qt.Key_Backtab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); - } - else if (event.key === Qt.Key_O) { - sidebarRoot.extend = !sidebarRoot.extend; - } - else if (event.key === Qt.Key_P) { - sidebarRoot.pin = !sidebarRoot.pin; - } - event.accepted = true; + function onPinChanged() { + grab.active = !sidebarRoot.pin } } - ColumnLayout { - anchors.fill: parent - anchors.margins: sidebarPadding - - spacing: sidebarPadding + Timer { + id: delayedGrabTimer + interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + repeat: false + onTriggered: { + grab.active = sidebarRoot.visible && !sidebarRoot.pin + } + } - PrimaryTabBar { // Tab strip - id: tabBar - tabButtonList: root.tabButtonList - externalTrackedTab: sidebarRoot.selectedTab - function onCurrentIndexChanged(currentIndex) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) + // Background + Rectangle { + id: sidebarLeftBackground + + anchors.top: parent.top + anchors.left: parent.left + anchors.topMargin: Appearance.sizes.hyprlandGapsOut + anchors.leftMargin: Appearance.sizes.hyprlandGapsOut + width: sidebarRoot.sidebarWidth - Appearance.sizes.hyprlandGapsOut * 2 + height: parent.height - Appearance.sizes.hyprlandGapsOut * 2 + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 + focus: sidebarRoot.visible + + Behavior on width { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } - SwipeView { // Content pages - id: swipeView - Layout.topMargin: 5 - Layout.fillWidth: true - Layout.fillHeight: true - spacing: 10 - currentIndex: sidebarRoot.selectedTab - onCurrentIndexChanged: { - tabBar.enableIndicatorAnimation = true - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) + Keys.onPressed: (event) => { + // console.log("Key pressed: " + event.key) + if (event.key === Qt.Key_Escape) { + sidebarRoot.hide(); } + if (event.modifiers === Qt.ControlModifier) { + if (event.key === Qt.Key_PageDown) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)) + } + else if (event.key === Qt.Key_PageUp) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0)) + } + else if (event.key === Qt.Key_Tab) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length); + } + else if (event.key === Qt.Key_Backtab) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); + } + else if (event.key === Qt.Key_O) { + sidebarRoot.extend = !sidebarRoot.extend; + } + else if (event.key === Qt.Key_P) { + sidebarRoot.pin = !sidebarRoot.pin; + } + event.accepted = true; + } + } - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small + ColumnLayout { + anchors.fill: parent + anchors.margins: sidebarPadding + + spacing: sidebarPadding + + PrimaryTabBar { // Tab strip + id: tabBar + tabButtonList: root.tabButtonList + externalTrackedTab: sidebarRoot.selectedTab + function onCurrentIndexChanged(currentIndex) { + PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) } } - AiChat { - panelWindow: sidebarRoot - } - Anime { - panelWindow: sidebarRoot + SwipeView { // Content pages + id: swipeView + Layout.topMargin: 5 + Layout.fillWidth: true + Layout.fillHeight: true + spacing: 10 + currentIndex: sidebarRoot.selectedTab + onCurrentIndexChanged: { + tabBar.enableIndicatorAnimation = true + PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) + } + + clip: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } + } + + AiChat { + panelWindow: sidebarRoot + } + Anime { + panelWindow: sidebarRoot + } } + } - } - } - // Shadow - DropShadow { - anchors.fill: sidebarLeftBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: sidebarLeftBackground - } + // Shadow + DropShadow { + anchors.fill: sidebarLeftBackground + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + source: sidebarLeftBackground + } + } } IpcHandler { target: "sidebarLeft" function toggle(): void { - sidebarRoot.visible = !sidebarRoot.visible; - if(sidebarRoot.visible) Notifications.timeoutAll(); + sidebarLoader.active = !sidebarLoader.active + if(sidebarLoader.active) Notifications.timeoutAll(); } function close(): void { - sidebarRoot.visible = false; + sidebarLoader.active = false } function open(): void { - sidebarRoot.visible = true; - if(sidebarRoot.visible) Notifications.timeoutAll(); + sidebarLoader.active = true + if(sidebarLoader.active) Notifications.timeoutAll(); } } @@ -210,8 +218,8 @@ Scope { // Scope description: "Toggles left sidebar on press" onPressed: { - sidebarRoot.visible = !sidebarRoot.visible; - if(sidebarRoot.visible) Notifications.timeoutAll(); + sidebarLoader.active = !sidebarLoader.active; + if(sidebarLoader.active) Notifications.timeoutAll(); } } @@ -220,8 +228,8 @@ Scope { // Scope description: "Opens left sidebar on press" onPressed: { - sidebarRoot.visible = true; - if(sidebarRoot.visible) Notifications.timeoutAll(); + sidebarLoader.active = true; + if(sidebarLoader.active) Notifications.timeoutAll(); } } @@ -230,7 +238,7 @@ Scope { // Scope description: "Closes left sidebar on press" onPressed: { - sidebarRoot.visible = false; + sidebarLoader.active = false; } } From f76da801fea2d2aeb2acaa8ee9846b12edc05ec4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:57:12 +0200 Subject: [PATCH 27/84] fix notif dismiss on right sidebar open --- .config/quickshell/modules/sidebarRight/SidebarRight.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index d9910a522..1feec0ec7 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -203,6 +203,7 @@ Scope { function toggle(): void { sidebarLoader.active = !sidebarLoader.active; + if(sidebarLoader.active) Notifications.timeoutAll(); } function close(): void { @@ -211,6 +212,7 @@ Scope { function open(): void { sidebarLoader.active = true; + Notifications.timeoutAll(); } } @@ -220,6 +222,7 @@ Scope { onPressed: { sidebarLoader.active = !sidebarLoader.active; + if(sidebarLoader.active) Notifications.timeoutAll(); } } GlobalShortcut { @@ -228,6 +231,7 @@ Scope { onPressed: { sidebarLoader.active = true; + Notifications.timeoutAll(); } } GlobalShortcut { From 4df645a025ce659929d091e86a06f4b7fa60a620 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 17 May 2025 23:57:32 +0200 Subject: [PATCH 28/84] notif popup: show only on focused screen --- .../notificationPopup/NotificationPopup.qml | 188 +++++++++--------- 1 file changed, 91 insertions(+), 97 deletions(-) diff --git a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml index 8d5b0549c..f970a09c6 100644 --- a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml +++ b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml @@ -7,111 +7,105 @@ import QtQuick.Controls import QtQuick.Layouts import Quickshell import Quickshell.Wayland +import Quickshell.Hyprland Scope { - id: screenCorners - readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + id: notificationPopup - Variants { - model: Quickshell.screens + LazyLoader { + loading: true + PanelWindow { + id: root + visible: (columnLayout.children.length > 0 || notificationWidgetList.length > 0) + screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) - LazyLoader { - property var modelData - loading: true - PanelWindow { - id: root - visible: (columnLayout.children.length > 0 || notificationWidgetList.length > 0) + property Component notifComponent: NotificationWidget {} + property list notificationWidgetList: [] - property Component notifComponent: NotificationWidget {} - property list notificationWidgetList: [] - - screen: modelData - WlrLayershell.namespace: "quickshell:notificationPopup" - WlrLayershell.layer: WlrLayer.Overlay - exclusiveZone: 0 - - anchors { - top: true - right: true - bottom: true - } - - mask: Region { - item: columnLayout - } - - color: "transparent" - implicitWidth: Appearance.sizes.notificationPopupWidth - - // Signal handlers to add/remove notifications - Connections { - target: Notifications - function onNotify(notification) { - if (GlobalStates.sidebarRightOpenCount > 0) { - return - } - // notificationRepeater.model = [notification, ...notificationRepeater.model] - const notif = root.notifComponent.createObject(columnLayout, { - notificationObject: notification, - popup: true - }); - notificationWidgetList.unshift(notif) - - // Remove stuff from t he column, add back - for (let i = 0; i < notificationWidgetList.length; i++) { - if (notificationWidgetList[i].parent === columnLayout) { - notificationWidgetList[i].parent = null; - } - } - - // Add notification widgets to the column - for (let i = 0; i < notificationWidgetList.length; i++) { - if (notificationWidgetList[i].parent === null) { - notificationWidgetList[i].parent = columnLayout; - } - } - } - function onDiscard(id) { - for (let i = notificationWidgetList.length - 1; i >= 0; i--) { - const widget = notificationWidgetList[i]; - if (widget && widget.notificationObject && widget.notificationObject.id === id) { - widget.destroyWithAnimation(); - notificationWidgetList.splice(i, 1); - } - } - } - function onTimeout(id) { - for (let i = notificationWidgetList.length - 1; i >= 0; i--) { - const widget = notificationWidgetList[i]; - if (widget && widget.notificationObject && widget.notificationObject.id === id) { - widget.destroyWithAnimation(); - notificationWidgetList.splice(i, 1); - } - } - } - function onDiscardAll() { - for (let i = notificationWidgetList.length - 1; i >= 0; i--) { - const widget = notificationWidgetList[i]; - if (widget && widget.notificationObject) { - widget.destroyWithAnimation(); - } - } - notificationWidgetList = []; - } - } - - ColumnLayout { // Scrollable window content - id: columnLayout - anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - Appearance.sizes.hyprlandGapsOut * 2 - spacing: 0 // The widgets themselves have margins for spacing - - // Notifications are added by the above signal handlers - } + WlrLayershell.namespace: "quickshell:notificationPopup" + WlrLayershell.layer: WlrLayer.Overlay + exclusiveZone: 0 + anchors { + top: true + right: true + bottom: true } - } + mask: Region { + item: columnLayout + } + + color: "transparent" + implicitWidth: Appearance.sizes.notificationPopupWidth + + // Signal handlers to add/remove notifications + Connections { + target: Notifications + function onNotify(notification) { + if (GlobalStates.sidebarRightOpenCount > 0) { + return + } + // notificationRepeater.model = [notification, ...notificationRepeater.model] + const notif = root.notifComponent.createObject(columnLayout, { + notificationObject: notification, + popup: true + }); + notificationWidgetList.unshift(notif) + + // Remove stuff from t he column, add back + for (let i = 0; i < notificationWidgetList.length; i++) { + if (notificationWidgetList[i].parent === columnLayout) { + notificationWidgetList[i].parent = null; + } + } + + // Add notification widgets to the column + for (let i = 0; i < notificationWidgetList.length; i++) { + if (notificationWidgetList[i].parent === null) { + notificationWidgetList[i].parent = columnLayout; + } + } + } + function onDiscard(id) { + for (let i = notificationWidgetList.length - 1; i >= 0; i--) { + const widget = notificationWidgetList[i]; + if (widget && widget.notificationObject && widget.notificationObject.id === id) { + widget.destroyWithAnimation(); + notificationWidgetList.splice(i, 1); + } + } + } + function onTimeout(id) { + for (let i = notificationWidgetList.length - 1; i >= 0; i--) { + const widget = notificationWidgetList[i]; + if (widget && widget.notificationObject && widget.notificationObject.id === id) { + widget.destroyWithAnimation(); + notificationWidgetList.splice(i, 1); + } + } + } + function onDiscardAll() { + for (let i = notificationWidgetList.length - 1; i >= 0; i--) { + const widget = notificationWidgetList[i]; + if (widget && widget.notificationObject) { + widget.destroyWithAnimation(); + } + } + notificationWidgetList = []; + } + } + + ColumnLayout { // Scrollable window content + id: columnLayout + anchors.horizontalCenter: parent.horizontalCenter + width: parent.width - Appearance.sizes.hyprlandGapsOut * 2 + spacing: 0 // The widgets themselves have margins for spacing + + // Notifications are added by the above signal handlers + } + + } } } From e0b883cc3e10ae5158c7194fe02ffb28c0e46d51 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:20:33 +0200 Subject: [PATCH 29/84] brightness osd: show only on focused screen --- .../OnScreenDisplayBrightness.qml | 136 +++++++++--------- 1 file changed, 70 insertions(+), 66 deletions(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index 4e3bca7c1..dfedbb946 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -12,6 +12,8 @@ import Quickshell.Wayland Scope { id: root property bool showOsdValues: false + property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen) function triggerOsd() { showOsdValues = true @@ -36,81 +38,83 @@ Scope { } } - Variants { - model: Quickshell.screens + Connections { + target: root.brightnessMonitor + enabled: !!root.brightnessMonitor + function onBrightnessChanged() { + if (!root.brightnessMonitor.ready) return + root.triggerOsd() + } + } - Loader { - id: osdLoader - property var modelData - active: showOsdValues - property var brightnessMonitor: Brightness.getMonitorForScreen(modelData) + Loader { + id: osdLoader + active: showOsdValues + + PanelWindow { + id: osdRoot Connections { - target: brightnessMonitor - function onBrightnessChanged() { - if (!brightnessMonitor.ready) return - root.triggerOsd() + target: osdLoader + function onFocusedScreenChanged() { + osdRoot.screen = root.focusedScreen } } - PanelWindow { - screen: modelData - exclusionMode: ExclusionMode.Normal - WlrLayershell.namespace: "quickshell:onScreenDisplay" - WlrLayershell.layer: WlrLayer.Overlay - color: "transparent" - - anchors { - top: true - } - mask: Region { - item: osdValuesWrapper - } - - implicitWidth: columnLayout.implicitWidth - implicitHeight: columnLayout.implicitHeight - visible: osdLoader.active - - ColumnLayout { - id: columnLayout - anchors.horizontalCenter: parent.horizontalCenter - Item { - height: 1 // Prevent Wayland protocol error - } - Item { - id: osdValuesWrapper - // Extra space for shadow - implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 - implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 - clip: true - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onEntered: root.showOsdValues = false - } - - Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.animation.menuDecel.duration - easing.type: Appearance.animation.menuDecel.type - } - } - - OsdValueIndicator { - id: osdValues - anchors.centerIn: parent - value: brightnessMonitor.brightness - icon: "light_mode" - rotateIcon: true - name: qsTr("Brightness") - } - } - } + exclusionMode: ExclusionMode.Normal + WlrLayershell.namespace: "quickshell:onScreenDisplay" + WlrLayershell.layer: WlrLayer.Overlay + color: "transparent" + anchors { + top: true } + mask: Region { + item: osdValuesWrapper + } + + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + visible: osdLoader.active + + ColumnLayout { + id: columnLayout + anchors.horizontalCenter: parent.horizontalCenter + Item { + height: 1 // Prevent Wayland protocol error + } + Item { + id: osdValuesWrapper + // Extra space for shadow + implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 + implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 + clip: true + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: root.showOsdValues = false + } + + Behavior on implicitHeight { + NumberAnimation { + duration: Appearance.animation.menuDecel.duration + easing.type: Appearance.animation.menuDecel.type + } + } + + OsdValueIndicator { + id: osdValues + anchors.centerIn: parent + value: root.brightnessMonitor.brightness + icon: "light_mode" + rotateIcon: true + name: qsTr("Brightness") + } + } + } + } - } IpcHandler { From 4fa53bb4fc3dd45d35941b31befe72359abed8af Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:25:24 +0200 Subject: [PATCH 30/84] osd anim: slide -> popin --- .config/hypr/hyprland/rules.conf | 1 - 1 file changed, 1 deletion(-) diff --git a/.config/hypr/hyprland/rules.conf b/.config/hypr/hyprland/rules.conf index 13bc45012..75adefcd4 100644 --- a/.config/hypr/hyprland/rules.conf +++ b/.config/hypr/hyprland/rules.conf @@ -109,7 +109,6 @@ layerrule = animation slide, quickshell:bar layerrule = animation fade, quickshell:screenCorners layerrule = animation slide right, quickshell:sidebarRight layerrule = animation slide left, quickshell:sidebarLeft -layerrule = animation slide top, quickshell:onScreenDisplay layerrule = blur, quickshell:session layerrule = noanim, quickshell:session # Launchers need to be FAST From 27ca14d208170e51125d6a598cc683939fd90662 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:26:42 +0200 Subject: [PATCH 31/84] volume osd: only show on focused screen --- .../onScreenDisplay/OnScreenDisplayVolume.qml | 120 +++++++++--------- 1 file changed, 61 insertions(+), 59 deletions(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 6bd83fa86..dfe4702ad 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -12,6 +12,7 @@ import Quickshell.Hyprland Scope { id: root property bool showOsdValues: false + property ShellScreen focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) function triggerOsd() { showOsdValues = true @@ -47,70 +48,71 @@ Scope { } } - Variants { - model: Quickshell.screens + Loader { + id: osdLoader + active: showOsdValues - Loader { - id: osdLoader - property var modelData - active: showOsdValues - PanelWindow { - screen: modelData - exclusionMode: ExclusionMode.Normal - WlrLayershell.namespace: "quickshell:onScreenDisplay" - WlrLayershell.layer: WlrLayer.Overlay - color: "transparent" + PanelWindow { + id: osdRoot - anchors { - top: true + Connections { + target: root + function onFocusedScreenChanged() { + osdRoot.screen = root.focusedScreen } - mask: Region { - item: osdValuesWrapper - } - - implicitWidth: columnLayout.implicitWidth - implicitHeight: columnLayout.implicitHeight - visible: osdLoader.active - - ColumnLayout { - id: columnLayout - anchors.horizontalCenter: parent.horizontalCenter - Item { - height: 1 // Prevent Wayland protocol error - } - Item { - id: osdValuesWrapper - // Extra space for shadow - implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 - implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 - clip: true - - MouseArea { - anchors.fill: parent - hoverEnabled: true - onEntered: root.showOsdValues = false - } - - Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.animation.menuDecel.duration - easing.type: Appearance.animation.menuDecel.type - } - } - - OsdValueIndicator { - id: osdValues - anchors.centerIn: parent - value: Audio.sink?.audio.volume ?? 0 - icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up" - name: qsTr("Volume") - } - } - } - } - } + exclusionMode: ExclusionMode.Normal + WlrLayershell.namespace: "quickshell:onScreenDisplay" + WlrLayershell.layer: WlrLayer.Overlay + color: "transparent" + + anchors.top: true + mask: Region { + item: osdValuesWrapper + } + + implicitWidth: columnLayout.implicitWidth + implicitHeight: columnLayout.implicitHeight + visible: osdLoader.active + + ColumnLayout { + id: columnLayout + anchors.horizontalCenter: parent.horizontalCenter + Item { + height: 1 // Prevent Wayland protocol error + } + Item { + id: osdValuesWrapper + // Extra space for shadow + implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 + implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 + clip: true + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onEntered: root.showOsdValues = false + } + + Behavior on implicitHeight { + NumberAnimation { + duration: Appearance.animation.menuDecel.duration + easing.type: Appearance.animation.menuDecel.type + } + } + + OsdValueIndicator { + id: osdValues + anchors.centerIn: parent + value: Audio.sink?.audio.volume ?? 0 + icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up" + name: qsTr("Volume") + } + } + } + + } } IpcHandler { From 853622e05e899bd85f547e2adc72cc09c9a84546 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:27:14 +0200 Subject: [PATCH 32/84] brightness osd: fix screen change connection target --- .../modules/onScreenDisplay/OnScreenDisplayBrightness.qml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index dfedbb946..ff9b17499 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -12,7 +12,7 @@ import Quickshell.Wayland Scope { id: root property bool showOsdValues: false - property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + property ShellScreen focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen) function triggerOsd() { @@ -55,7 +55,7 @@ Scope { id: osdRoot Connections { - target: osdLoader + target: root function onFocusedScreenChanged() { osdRoot.screen = root.focusedScreen } @@ -66,9 +66,7 @@ Scope { WlrLayershell.layer: WlrLayer.Overlay color: "transparent" - anchors { - top: true - } + anchors.top: true mask: Region { item: osdValuesWrapper } From 4626ab2bb290b7ff3b9e9166c09a7567a09eed20 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:31:38 +0200 Subject: [PATCH 33/84] fix cant assign undefined warning --- .../modules/onScreenDisplay/OnScreenDisplayBrightness.qml | 2 +- .../modules/onScreenDisplay/OnScreenDisplayVolume.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index ff9b17499..8fb145bc6 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -12,7 +12,7 @@ import Quickshell.Wayland Scope { id: root property bool showOsdValues: false - property ShellScreen focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) property var brightnessMonitor: Brightness.getMonitorForScreen(focusedScreen) function triggerOsd() { diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index dfe4702ad..d9c5b3bbf 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -12,7 +12,7 @@ import Quickshell.Hyprland Scope { id: root property bool showOsdValues: false - property ShellScreen focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) function triggerOsd() { showOsdValues = true From 5af7ede329b6f809c3e4c4dfaed2507a8ba5473c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:37:08 +0200 Subject: [PATCH 34/84] session menu: only show on focused monitor --- .../quickshell/modules/session/Session.qml | 386 +++++++++--------- 1 file changed, 182 insertions(+), 204 deletions(-) diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index 00c1d4a26..09d710437 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -4,198 +4,191 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell -import Quickshell.Hyprland import Quickshell.Io -import Quickshell.Wayland import Quickshell.Widgets +import Quickshell.Wayland +import Quickshell.Hyprland Scope { id: root - readonly property Toplevel activeWindow: ToplevelManager.activeToplevel + property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) - Variants { - id: sessionVariants - model: Quickshell.screens - - Loader { - id: sessionLoader - property var modelData - active: false - - PanelWindow { // Session menu - id: sessionRoot - visible: sessionLoader.active - - function hide() { - sessionLoader.active = false - } - - property string subtitle - - screen: modelData - exclusionMode: ExclusionMode.Ignore - WlrLayershell.namespace: "quickshell:session" - WlrLayershell.layer: WlrLayer.Overlay - WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: Appearance.transparentize(Appearance.m3colors.m3background, 0.3) - - anchors { - top: true - left: true - right: true - } - - implicitWidth: modelData.width - implicitHeight: modelData.height - - MouseArea { - id: sessionMouseArea - anchors.fill: parent - onClicked: { - sessionRoot.hide() - } - } - - ColumnLayout { // Content column - anchors.centerIn: parent - spacing: 15 - - Keys.onPressed: (event) => { - if (event.key === Qt.Key_Escape) { - sessionRoot.hide(); - } - } - - ColumnLayout { - Layout.alignment: Qt.AlignHCenter - spacing: 0 - StyledText { // Title - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - font.family: Appearance.font.family.title - font.pixelSize: Appearance.font.pixelSize.title - font.weight: Font.DemiBold - text: qsTr("Session") - } - - StyledText { // Small instruction - Layout.alignment: Qt.AlignHCenter - horizontalAlignment: Text.AlignHCenter - font.family: Appearance.font.family.title - font.pixelSize: Appearance.font.pixelSize.normal - text: qsTr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel") - } - } - - RowLayout { // First row of buttons - spacing: 15 - SessionActionButton { - id: sessionLock - focus: sessionRoot.visible - buttonIcon: "lock" - buttonText: qsTr("Lock") - onClicked: { Hyprland.dispatch("exec loginctl lock-session"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.right: sessionSleep - KeyNavigation.down: sessionHibernate - } - SessionActionButton { - id: sessionSleep - buttonIcon: "dark_mode" - buttonText: qsTr("Sleep") - onClicked: { Hyprland.dispatch("exec systemctl suspend || loginctl suspend"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.left: sessionLock - KeyNavigation.right: sessionLogout - KeyNavigation.down: sessionShutdown - } - SessionActionButton { - id: sessionLogout - buttonIcon: "logout" - buttonText: qsTr("Logout") - onClicked: { Hyprland.dispatch("exec pkill Hyprland"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.left: sessionSleep - KeyNavigation.right: sessionTaskManager - KeyNavigation.down: sessionReboot - } - SessionActionButton { - id: sessionTaskManager - buttonIcon: "browse_activity" - buttonText: qsTr("Task Manager") - onClicked: { Hyprland.dispatch("exec gnome-system-monitor & disown"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.left: sessionLogout - KeyNavigation.down: sessionFirmwareReboot - } - } - - RowLayout { // Second row of buttons - spacing: 15 - SessionActionButton { - id: sessionHibernate - buttonIcon: "downloading" - buttonText: qsTr("Hibernate") - onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.up: sessionLock - KeyNavigation.right: sessionShutdown - } - SessionActionButton { - id: sessionShutdown - buttonIcon: "power_settings_new" - buttonText: qsTr("Shutdown") - onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.left: sessionHibernate - KeyNavigation.right: sessionReboot - KeyNavigation.up: sessionSleep - } - SessionActionButton { - id: sessionReboot - buttonIcon: "restart_alt" - buttonText: qsTr("Reboot") - onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.left: sessionShutdown - KeyNavigation.right: sessionFirmwareReboot - KeyNavigation.up: sessionLogout - } - SessionActionButton { - id: sessionFirmwareReboot - buttonIcon: "settings_applications" - buttonText: qsTr("Reboot to firmware settings") - onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } - KeyNavigation.up: sessionTaskManager - KeyNavigation.left: sessionReboot - } - } - - Rectangle { - Layout.alignment: Qt.AlignHCenter - radius: Appearance.rounding.normal - implicitHeight: sessionSubtitle.implicitHeight + 10 * 2 - implicitWidth: sessionSubtitle.implicitWidth + 15 * 2 - color: Appearance.colors.colTooltip - clip: true - - Behavior on implicitWidth { - SmoothedAnimation { - velocity: Appearance.animation.elementMoveFast.velocity - } - } - - StyledText { - id: sessionSubtitle - anchors.centerIn: parent - color: Appearance.colors.colOnTooltip - text: sessionRoot.subtitle - } - } - } + Loader { + id: sessionLoader + active: false + PanelWindow { // Session menu + id: sessionRoot + visible: sessionLoader.active + property string subtitle + + function hide() { + sessionLoader.active = false } + + + exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "quickshell:session" + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive + color: Appearance.transparentize(Appearance.m3colors.m3background, 0.3) + + anchors { + top: true + left: true + right: true + } + + implicitWidth: root.focusedScreen?.width ?? 0 + implicitHeight: root.focusedScreen?.height ?? 0 + + MouseArea { + id: sessionMouseArea + anchors.fill: parent + onClicked: { + sessionRoot.hide() + } + } + + ColumnLayout { // Content column + anchors.centerIn: parent + spacing: 15 + + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape) { + sessionRoot.hide(); + } + } + + ColumnLayout { + Layout.alignment: Qt.AlignHCenter + spacing: 0 + StyledText { // Title + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.family: Appearance.font.family.title + font.pixelSize: Appearance.font.pixelSize.title + font.weight: Font.DemiBold + text: qsTr("Session") + } + + StyledText { // Small instruction + Layout.alignment: Qt.AlignHCenter + horizontalAlignment: Text.AlignHCenter + font.family: Appearance.font.family.title + font.pixelSize: Appearance.font.pixelSize.normal + text: qsTr("Arrow keys to navigate, Enter to select\nEsc or click anywhere to cancel") + } + } + + RowLayout { // First row of buttons + spacing: 15 + SessionActionButton { + id: sessionLock + focus: sessionRoot.visible + buttonIcon: "lock" + buttonText: qsTr("Lock") + onClicked: { Hyprland.dispatch("exec loginctl lock-session"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.right: sessionSleep + KeyNavigation.down: sessionHibernate + } + SessionActionButton { + id: sessionSleep + buttonIcon: "dark_mode" + buttonText: qsTr("Sleep") + onClicked: { Hyprland.dispatch("exec systemctl suspend || loginctl suspend"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.left: sessionLock + KeyNavigation.right: sessionLogout + KeyNavigation.down: sessionShutdown + } + SessionActionButton { + id: sessionLogout + buttonIcon: "logout" + buttonText: qsTr("Logout") + onClicked: { Hyprland.dispatch("exec pkill Hyprland"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.left: sessionSleep + KeyNavigation.right: sessionTaskManager + KeyNavigation.down: sessionReboot + } + SessionActionButton { + id: sessionTaskManager + buttonIcon: "browse_activity" + buttonText: qsTr("Task Manager") + onClicked: { Hyprland.dispatch("exec gnome-system-monitor & disown"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.left: sessionLogout + KeyNavigation.down: sessionFirmwareReboot + } + } + + RowLayout { // Second row of buttons + spacing: 15 + SessionActionButton { + id: sessionHibernate + buttonIcon: "downloading" + buttonText: qsTr("Hibernate") + onClicked: { Hyprland.dispatch("exec systemctl hibernate || loginctl hibernate"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.up: sessionLock + KeyNavigation.right: sessionShutdown + } + SessionActionButton { + id: sessionShutdown + buttonIcon: "power_settings_new" + buttonText: qsTr("Shutdown") + onClicked: { Hyprland.dispatch("exec systemctl poweroff || loginctl poweroff"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.left: sessionHibernate + KeyNavigation.right: sessionReboot + KeyNavigation.up: sessionSleep + } + SessionActionButton { + id: sessionReboot + buttonIcon: "restart_alt" + buttonText: qsTr("Reboot") + onClicked: { Hyprland.dispatch("exec reboot || loginctl reboot"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.left: sessionShutdown + KeyNavigation.right: sessionFirmwareReboot + KeyNavigation.up: sessionLogout + } + SessionActionButton { + id: sessionFirmwareReboot + buttonIcon: "settings_applications" + buttonText: qsTr("Reboot to firmware settings") + onClicked: { Hyprland.dispatch("exec systemctl reboot --firmware-setup || loginctl reboot --firmware-setup"); sessionRoot.hide() } + onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + KeyNavigation.up: sessionTaskManager + KeyNavigation.left: sessionReboot + } + } + + Rectangle { + Layout.alignment: Qt.AlignHCenter + radius: Appearance.rounding.normal + implicitHeight: sessionSubtitle.implicitHeight + 10 * 2 + implicitWidth: sessionSubtitle.implicitWidth + 15 * 2 + color: Appearance.colors.colTooltip + clip: true + + Behavior on implicitWidth { + SmoothedAnimation { + velocity: Appearance.animation.elementMoveFast.velocity + } + } + + StyledText { + id: sessionSubtitle + anchors.centerIn: parent + color: Appearance.colors.colOnTooltip + text: sessionRoot.subtitle + } + } + } + } } @@ -203,24 +196,15 @@ Scope { target: "session" function toggle(): void { - for (let i = 0; i < sessionVariants.instances.length; i++) { - let loader = sessionVariants.instances[i]; - loader.active = !loader.active; - } + sessionLoader.active = !sessionLoader.active; } function close(): void { - for (let i = 0; i < sessionVariants.instances.length; i++) { - let loader = sessionVariants.instances[i]; - loader.active = false; - } + sessionLoader.active = false; } function open(): void { - for (let i = 0; i < sessionVariants.instances.length; i++) { - let loader = sessionVariants.instances[i]; - loader.active = true; - } + sessionLoader.active = true; } } @@ -229,10 +213,7 @@ Scope { description: "Toggles session screen on press" onPressed: { - for (let i = 0; i < sessionVariants.instances.length; i++) { - let loader = sessionVariants.instances[i]; - loader.active = !loader.active; - } + sessionLoader.active = !sessionLoader.active; } } @@ -241,10 +222,7 @@ Scope { description: "Opens session screen on press" onPressed: { - for (let i = 0; i < sessionVariants.instances.length; i++) { - let loader = sessionVariants.instances[i]; - loader.active = !loader.active; - } + sessionLoader.active = true; } } From b9fe3da1e33ac0d0e4b9a5293e746432cfa2f8e6 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:48:34 +0200 Subject: [PATCH 35/84] brightness osd: fix no onBrightnessChanged signal warning --- .../modules/onScreenDisplay/OnScreenDisplayBrightness.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index 8fb145bc6..f37debb1d 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -39,8 +39,7 @@ Scope { } Connections { - target: root.brightnessMonitor - enabled: !!root.brightnessMonitor + target: Brightness function onBrightnessChanged() { if (!root.brightnessMonitor.ready) return root.triggerOsd() From 9385961fb889d123d50de1299b954081cd836fa0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 00:50:25 +0200 Subject: [PATCH 36/84] make osdvalueindicator more customizable (internally) --- .../modules/onScreenDisplay/OnScreenDisplayBrightness.qml | 1 + .../quickshell/modules/onScreenDisplay/OsdValueIndicator.qml | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index f37debb1d..952951584 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -106,6 +106,7 @@ Scope { value: root.brightnessMonitor.brightness icon: "light_mode" rotateIcon: true + scaleIcon: true name: qsTr("Brightness") } } diff --git a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml index 649d34bf6..dee30c397 100644 --- a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml +++ b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml @@ -14,6 +14,7 @@ Item { required property string icon required property string name property bool rotateIcon: false + property bool scaleIcon: false property real valueIndicatorVerticalPadding: 9 property real valueIndicatorLeftPadding: 10 @@ -47,7 +48,7 @@ Item { renderType: Text.QtRendering text: root.icon - iconSize: 20 + 10 * (root.rotateIcon ? value : 1) + iconSize: 20 + 10 * (root.scaleIcon ? value : 1) rotation: 180 * (root.rotateIcon ? value : 0) Behavior on iconSize { From b14df306ced80d629da80145280f54107b82257d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 01:50:12 +0200 Subject: [PATCH 37/84] refractor some animations --- .config/quickshell/modules/bar/Battery.qml | 13 ++----- .config/quickshell/modules/bar/Resource.qml | 6 +--- .config/quickshell/modules/bar/Workspaces.qml | 27 ++------------ .../modules/cheatsheet/Cheatsheet.qml | 6 +--- .../quickshell/modules/common/Appearance.qml | 27 ++++++++++++++ .../modules/common/widgets/DialogButton.qml | 12 ++----- .../modules/common/widgets/MenuButton.qml | 13 ++----- .../modules/common/widgets/NavRailButton.qml | 12 ++----- .../common/widgets/NotificationWidget.qml | 8 +---- .../modules/common/widgets/PrimaryTabBar.qml | 12 ++----- .../common/widgets/PrimaryTabButton.qml | 18 ++-------- .../modules/common/widgets/Revealer.qml | 12 ++----- .../common/widgets/SecondaryTabButton.qml | 18 ++-------- .../modules/common/widgets/StyledSwitch.qml | 36 ++++--------------- .../quickshell/modules/session/Session.qml | 4 +-- .../modules/session/SessionActionButton.qml | 7 +--- .../quickshell/modules/sidebarLeft/AiChat.qml | 24 ++++--------- .../quickshell/modules/sidebarLeft/Anime.qml | 30 +++++----------- .../modules/sidebarLeft/SidebarLeft.qml | 6 +--- .../aiChat/AiMessageControlButton.qml | 12 ++----- .../sidebarLeft/anime/BooruResponse.qml | 12 ++----- .../calendar/CalendarDayButton.qml | 14 ++------ .../calendar/CalendarHeaderButton.qml | 7 +--- .../notifications/NotificationList.qml | 6 +--- .../NotificationStatusButton.qml | 12 ++----- .../quickToggles/QuickToggleButton.qml | 12 ++----- .../modules/sidebarRight/todo/TaskList.qml | 6 +--- .../todo/TodoItemActionButton.qml | 7 +--- .../modules/sidebarRight/todo/TodoWidget.qml | 18 ++-------- .../volumeMixer/AudioDeviceSelectorButton.qml | 7 +--- 30 files changed, 94 insertions(+), 310 deletions(-) diff --git a/.config/quickshell/modules/bar/Battery.qml b/.config/quickshell/modules/bar/Battery.qml index 5267ceb16..e3baf5488 100644 --- a/.config/quickshell/modules/bar/Battery.qml +++ b/.config/quickshell/modules/bar/Battery.qml @@ -31,11 +31,7 @@ Rectangle { implicitWidth: (isCharging ? (boltIconLoader?.item?.width ?? 0) : 0) Behavior on implicitWidth { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } @@ -91,12 +87,7 @@ Rectangle { } Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/bar/Resource.qml b/.config/quickshell/modules/bar/Resource.qml index c13a3e0df..ae2d7167f 100644 --- a/.config/quickshell/modules/bar/Resource.qml +++ b/.config/quickshell/modules/bar/Resource.qml @@ -42,11 +42,7 @@ Item { } Behavior on x { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/bar/Workspaces.qml b/.config/quickshell/modules/bar/Workspaces.qml index fc6b7dc45..d3a701e8c 100644 --- a/.config/quickshell/modules/bar/Workspaces.qml +++ b/.config/quickshell/modules/bar/Workspaces.qml @@ -119,26 +119,14 @@ Item { opacity: (workspaceOccupied[index] && !(!activeWindow?.activated && monitor.activeWorkspace?.id === index+1)) ? 1 : 0 Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on radiusLeft { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on radiusRight { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } @@ -206,15 +194,6 @@ Item { (workspaceOccupied[index] ? Appearance.colors.colOnLayer1 : Appearance.colors.colOnLayer1Inactive) - Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - - } - Behavior on opacity { NumberAnimation { duration: Appearance.animation.elementMoveFast.duration diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 738fc621d..296ead290 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -113,11 +113,7 @@ Scope { // Scope Appearance.transparentize(Appearance.colors.colLayer0, 1) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } MaterialSymbol { diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index e15d264d7..4afc866c7 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -4,6 +4,7 @@ pragma Singleton pragma ComponentBehavior: Bound Singleton { + id: root property QtObject m3colors property QtObject animation property QtObject animationCurves @@ -189,12 +190,33 @@ Singleton { property int type: Easing.BezierSpline property list bezierCurve: animationCurves.emphasized property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMove.duration + easing.type: root.animation.elementMove.type + easing.bezierCurve: root.animation.elementMove.bezierCurve + } + } + property Component colorAnimation: Component { + ColorAnimation { + duration: root.animation.elementMove.duration + easing.type: root.animation.elementMove.type + easing.bezierCurve: root.animation.elementMove.bezierCurve + } + } } property QtObject elementMoveEnter: QtObject { property int duration: 400 property int type: Easing.BezierSpline property list bezierCurve: animationCurves.emphasizedDecel property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMoveEnter.duration + easing.type: root.animation.elementMoveEnter.type + easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve + } + } } property QtObject elementMoveExit: QtObject { property int duration: 200 @@ -207,6 +229,11 @@ Singleton { property int type: Easing.BezierSpline property list bezierCurve: animationCurves.standardDecel property int velocity: 850 + property Component colorAnimation: Component {ColorAnimation { + duration: root.animation.elementMoveFast.duration + easing.type: root.animation.elementMoveFast.type + easing.bezierCurve: root.animation.elementMoveFast.bezierCurve + }} } property QtObject scroll: QtObject { property int duration: 400 diff --git a/.config/quickshell/modules/common/widgets/DialogButton.qml b/.config/quickshell/modules/common/widgets/DialogButton.qml index d4310dbdf..2939c81c6 100644 --- a/.config/quickshell/modules/common/widgets/DialogButton.qml +++ b/.config/quickshell/modules/common/widgets/DialogButton.qml @@ -20,11 +20,7 @@ Button { color: (button.down && button.enabled) ? Appearance.colors.colLayer1Active : ((button.hovered && button.enabled) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } @@ -41,11 +37,7 @@ Button { color: button.enabled ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/common/widgets/MenuButton.qml b/.config/quickshell/modules/common/widgets/MenuButton.qml index f5de52c5e..f96ed3f67 100644 --- a/.config/quickshell/modules/common/widgets/MenuButton.qml +++ b/.config/quickshell/modules/common/widgets/MenuButton.qml @@ -21,12 +21,7 @@ Button { Appearance.transparentize(Appearance.m3colors.m3onSurface, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } @@ -42,11 +37,7 @@ Button { color: button.enabled ? Appearance.m3colors.m3onSurface : Appearance.m3colors.m3outline Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/common/widgets/NavRailButton.qml b/.config/quickshell/modules/common/widgets/NavRailButton.qml index 0c2ffbfdf..61972e165 100644 --- a/.config/quickshell/modules/common/widgets/NavRailButton.qml +++ b/.config/quickshell/modules/common/widgets/NavRailButton.qml @@ -33,11 +33,7 @@ Button { (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } MaterialSymbol { id: navRailButtonIcon @@ -48,11 +44,7 @@ Button { color: toggled ? Appearance.m3colors.m3onSecondaryContainer : Appearance.colors.colOnLayer1 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/common/widgets/NotificationWidget.qml b/.config/quickshell/modules/common/widgets/NotificationWidget.qml index 3f4acd275..bc472f318 100644 --- a/.config/quickshell/modules/common/widgets/NotificationWidget.qml +++ b/.config/quickshell/modules/common/widgets/NotificationWidget.qml @@ -425,14 +425,8 @@ Item { color: (expandButton.down) ? Appearance.colors.colLayer2Active : (expandButton.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } - } contentItem: MaterialSymbol { diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml index d8eff3378..de80d185b 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabBar.qml @@ -71,19 +71,11 @@ ColumnLayout { radius: Appearance.rounding.full Behavior on x { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on implicitWidth { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index c05f3e8fa..125b6d9e2 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -98,11 +98,7 @@ TabButton { } Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } Rectangle { @@ -133,11 +129,7 @@ TabButton { fill: selected ? 1 : 0 color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } StyledText { @@ -148,11 +140,7 @@ TabButton { color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 text: buttonText Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/common/widgets/Revealer.qml b/.config/quickshell/modules/common/widgets/Revealer.qml index e3afa680b..f919fb9f5 100644 --- a/.config/quickshell/modules/common/widgets/Revealer.qml +++ b/.config/quickshell/modules/common/widgets/Revealer.qml @@ -16,18 +16,10 @@ Item { Behavior on implicitWidth { enabled: !vertical - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation } Behavior on implicitHeight { enabled: vertical - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation } } diff --git a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml index eb6209121..2244ff42a 100644 --- a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -100,11 +100,7 @@ TabButton { } Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } Rectangle { @@ -143,11 +139,7 @@ TabButton { fill: selected ? 1 : 0 color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } @@ -158,11 +150,7 @@ TabButton { color: selected ? Appearance.m3colors.m3primary : Appearance.colors.colOnLayer1 text: buttonText Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/common/widgets/StyledSwitch.qml b/.config/quickshell/modules/common/widgets/StyledSwitch.qml index 8406a47d1..e12583b90 100644 --- a/.config/quickshell/modules/common/widgets/StyledSwitch.qml +++ b/.config/quickshell/modules/common/widgets/StyledSwitch.qml @@ -22,18 +22,10 @@ Switch { border.color: root.checked ? Appearance.m3colors.m3primary : Appearance.m3colors.m3outline Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.colorAnimation.createObject(this) } Behavior on border.color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.colorAnimation.createObject(this) } } @@ -48,32 +40,16 @@ Switch { anchors.leftMargin: root.checked ? (root.pressed ? (22 * root.scale) : 24 * root.scale) : (root.pressed ? (2 * root.scale) : 8 * root.scale) Behavior on anchors.leftMargin { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on width { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on height { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index 09d710437..0225587ca 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -175,9 +175,7 @@ Scope { clip: true Behavior on implicitWidth { - SmoothedAnimation { - velocity: Appearance.animation.elementMoveFast.velocity - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } StyledText { diff --git a/.config/quickshell/modules/session/SessionActionButton.qml b/.config/quickshell/modules/session/SessionActionButton.qml index b55c096b0..1d32018a0 100644 --- a/.config/quickshell/modules/session/SessionActionButton.qml +++ b/.config/quickshell/modules/session/SessionActionButton.qml @@ -37,12 +37,7 @@ Button { color: (button.down || button.keyboardDown) ? Appearance.colors.colLayer2Active : ((button.hovered || button.focus) ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMove.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index f8472cb79..9f05b6a3b 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -215,9 +215,9 @@ int main(int argc, char* argv[]) { Behavior on opacity { NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + duration: Appearance.animation.elementMoveEnter.duration + easing.type: Appearance.animation.elementMoveEnter.type + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve } } @@ -356,11 +356,7 @@ int main(int argc, char* argv[]) { border.width: 1 Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } RowLayout { // Input field and send button @@ -474,11 +470,7 @@ int main(int argc, char* argv[]) { Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } @@ -566,11 +558,7 @@ int main(int argc, char* argv[]) { Appearance.colors.colLayer2 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } onClicked: { diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index 5188ff3c9..818d236be 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -209,9 +209,9 @@ Item { Behavior on opacity { NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + duration: Appearance.animation.elementMoveEnter.duration + easing.type: Appearance.animation.elementMoveEnter.type + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve } } @@ -247,9 +247,9 @@ Item { Behavior on opacity { NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + duration: Appearance.animation.elementMoveEnter.duration + easing.type: Appearance.animation.elementMoveEnter.type + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve } } @@ -392,11 +392,7 @@ Item { border.width: 1 Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } RowLayout { // Input field and send button @@ -530,11 +526,7 @@ Item { Appearance.m3colors.m3primary) : Appearance.colors.colLayer2Disabled Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } @@ -666,11 +658,7 @@ Item { Appearance.colors.colLayer2 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } onClicked: { diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 6068d59e1..9c4547ee6 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -98,11 +98,7 @@ Scope { // Scope focus: sidebarRoot.visible Behavior on width { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Keys.onPressed: (event) => { diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml index 56de474dd..3b34c6e82 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml @@ -27,11 +27,7 @@ Button { Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } @@ -44,11 +40,7 @@ Button { Appearance.colors.colOnLayer1Inactive Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml index e9fe244ab..e62e5544f 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml @@ -120,18 +120,10 @@ Rectangle { } Behavior on height { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on implicitHeight { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } RowLayout { diff --git a/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml b/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml index 6977c6bc4..4d3585000 100644 --- a/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml +++ b/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml @@ -27,12 +27,7 @@ Button { Appearance.transparentize(Appearance.colors.colLayer1, 1) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } @@ -46,12 +41,7 @@ Button { Appearance.m3colors.m3outlineVariant Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } } diff --git a/.config/quickshell/modules/sidebarRight/calendar/CalendarHeaderButton.qml b/.config/quickshell/modules/sidebarRight/calendar/CalendarHeaderButton.qml index b0c394641..16d916b6c 100644 --- a/.config/quickshell/modules/sidebarRight/calendar/CalendarHeaderButton.qml +++ b/.config/quickshell/modules/sidebarRight/calendar/CalendarHeaderButton.qml @@ -26,12 +26,7 @@ Button { color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml b/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml index ff42a186e..085d7c5bf 100644 --- a/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml +++ b/.config/quickshell/modules/sidebarRight/notifications/NotificationList.qml @@ -142,11 +142,7 @@ Item { opacity: notificationWidgetList.length > 0 ? 1 : 0 visible: opacity > 0 Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarRight/notifications/NotificationStatusButton.qml b/.config/quickshell/modules/sidebarRight/notifications/NotificationStatusButton.qml index 0a1db2c43..568d7ad11 100644 --- a/.config/quickshell/modules/sidebarRight/notifications/NotificationStatusButton.qml +++ b/.config/quickshell/modules/sidebarRight/notifications/NotificationStatusButton.qml @@ -9,7 +9,7 @@ Button { property string buttonText: "" property string buttonIcon: "" - implicitHeight: 30 + // implicitHeight: 30 implicitWidth: contentRowLayout.implicitWidth + 10 * 2 Behavior on implicitWidth { SmoothedAnimation { @@ -25,19 +25,13 @@ Button { color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } contentItem: RowLayout { id: contentRowLayout - // anchors.centerIn: parent - anchors.right: parent.right + anchors.centerIn: parent spacing: 0 MaterialSymbol { text: buttonIcon diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml b/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml index ac17f34b1..e764494b5 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml @@ -23,11 +23,7 @@ Button { (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } @@ -39,11 +35,7 @@ Button { color: toggled ? Appearance.m3colors.m3onPrimary : Appearance.colors.colOnLayer1 Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarRight/todo/TaskList.qml b/.config/quickshell/modules/sidebarRight/todo/TaskList.qml index 5d688fae4..b727ab1cd 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TaskList.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TaskList.qml @@ -155,11 +155,7 @@ Item { anchors.fill: parent Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } ColumnLayout { diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml b/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml index d6ca97855..69e0882db 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml @@ -26,12 +26,7 @@ Button { color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1)) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index 3bbb4fc42..a480b2dbc 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -99,20 +99,12 @@ Item { Behavior on x { enabled: tabIndicator.enableIndicatorAnimation - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } Behavior on implicitWidth { enabled: tabIndicator.enableIndicatorAnimation - NumberAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } } } @@ -178,11 +170,7 @@ Item { color: (fabButton.down) ? Appearance.colors.colPrimaryContainerActive : (fabButton.hovered ? Appearance.colors.colPrimaryContainerHover : Appearance.m3colors.m3primaryContainer) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml b/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml index 14c4c3a37..643889048 100644 --- a/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml +++ b/.config/quickshell/modules/sidebarRight/volumeMixer/AudioDeviceSelectorButton.qml @@ -18,12 +18,7 @@ Button { color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2) Behavior on color { - ColorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve - } - + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } } From 611f8193736de08e99d642cfefc8d459213fbff9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 02:00:47 +0200 Subject: [PATCH 38/84] refractor elementMoveEnter anims --- .../quickshell/modules/common/Appearance.qml | 7 +++++ .../modules/common/widgets/Revealer.qml | 4 +-- .../common/widgets/StyledProgressBar.qml | 6 +--- .../onScreenDisplay/OsdValueIndicator.qml | 12 ++------ .../modules/overview/OverviewWindow.qml | 30 ++++--------------- .../quickshell/modules/sidebarLeft/AiChat.qml | 30 +++++++------------ .../quickshell/modules/sidebarLeft/Anime.qml | 24 +++++---------- .../modules/sidebarLeft/anime/BooruImage.qml | 6 +--- 8 files changed, 36 insertions(+), 83 deletions(-) diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 4afc866c7..b8fd1028a 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -223,6 +223,13 @@ Singleton { property int type: Easing.BezierSpline property list bezierCurve: animationCurves.emphasizedAccel property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementMoveExit.duration + easing.type: root.animation.elementMoveExit.type + easing.bezierCurve: root.animation.elementMoveExit.bezierCurve + } + } } property QtObject elementMoveFast: QtObject { property int duration: 200 diff --git a/.config/quickshell/modules/common/widgets/Revealer.qml b/.config/quickshell/modules/common/widgets/Revealer.qml index f919fb9f5..327d4aaa1 100644 --- a/.config/quickshell/modules/common/widgets/Revealer.qml +++ b/.config/quickshell/modules/common/widgets/Revealer.qml @@ -16,10 +16,10 @@ Item { Behavior on implicitWidth { enabled: !vertical - animation: Appearance.animation.elementMoveEnter.numberAnimation + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Behavior on implicitHeight { enabled: vertical - animation: Appearance.animation.elementMoveEnter.numberAnimation + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/common/widgets/StyledProgressBar.qml b/.config/quickshell/modules/common/widgets/StyledProgressBar.qml index 765f38d10..0a27fca99 100644 --- a/.config/quickshell/modules/common/widgets/StyledProgressBar.qml +++ b/.config/quickshell/modules/common/widgets/StyledProgressBar.qml @@ -15,11 +15,7 @@ ProgressBar { property real valueBarGap: 4 Behavior on value { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } background: Rectangle { diff --git a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml index dee30c397..c079f1dad 100644 --- a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml +++ b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml @@ -52,18 +52,10 @@ Item { rotation: 180 * (root.rotateIcon ? value : 0) Behavior on iconSize { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Behavior on rotation { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } } diff --git a/.config/quickshell/modules/overview/OverviewWindow.qml b/.config/quickshell/modules/overview/OverviewWindow.qml index d6526904e..0d6990eaf 100644 --- a/.config/quickshell/modules/overview/OverviewWindow.qml +++ b/.config/quickshell/modules/overview/OverviewWindow.qml @@ -47,32 +47,16 @@ Rectangle { // Window border.width : 1 Behavior on x { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Behavior on y { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Behavior on width { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Behavior on height { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } ColumnLayout { @@ -88,11 +72,7 @@ Rectangle { // Window implicitSize: Math.min(targetWindowWidth, targetWindowHeight) * (root.compactMode ? root.iconToWindowRatioCompact : root.iconToWindowRatio) Behavior on implicitSize { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } IconImage { diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index 9f05b6a3b..b8df45bb7 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -179,22 +179,18 @@ int main(int argc, char* argv[]) { } add: Transition { - NumberAnimation { - property: "opacity" - from: 0; to: 1 - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, { + property: "opacity", + from: 0, + to: 1 + })] } remove: Transition { - NumberAnimation { - property: "opacity" - from: 1; to: 0 - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, { + property: "opacity", + from: 1, + to: 0 + })] } model: ScriptModel { @@ -214,11 +210,7 @@ int main(int argc, char* argv[]) { anchors.fill: parent Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } ColumnLayout { diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index 818d236be..a1beca59d 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -174,13 +174,11 @@ Item { } add: Transition { - NumberAnimation { - property: "opacity" - from: 0; to: 1 - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animations: [Appearance.animation.elementMoveEnter.numberAnimation.createObject(this, { + property: "opacity", + from: 0, + to: 1 + })] } model: ScriptModel { @@ -208,11 +206,7 @@ Item { anchors.fill: parent Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } ColumnLayout { @@ -246,11 +240,7 @@ Item { visible: opacity > 0 Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } Rectangle { diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml index ca180271f..55e5361db 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml @@ -78,11 +78,7 @@ Button { } Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMoveEnter.duration - easing.type: Appearance.animation.elementMoveEnter.type - easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve - } + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } } From 68b233f4ef7dc542065a25f5acea774ae7d34ddb Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 11:08:36 +0200 Subject: [PATCH 39/84] keybinds: "user"/"custom" -> "extra" --- .config/hypr/custom/keybinds.conf | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.config/hypr/custom/keybinds.conf b/.config/hypr/custom/keybinds.conf index d3079441d..798742ee8 100644 --- a/.config/hypr/custom/keybinds.conf +++ b/.config/hypr/custom/keybinds.conf @@ -1,8 +1,10 @@ # See https://wiki.hyprland.org/Configuring/Binds/ #! -##! User keybinds -bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit custom keybinds +##! Extra keybinds +bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds # Add stuff here # Use #! to add an extra column on the cheatsheet # Use ##! to add a section in that column +# Add a comment after a bind to add a description, like above + From 69cd0fc447fefc71df66c158a111bd71868e1683 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 18:52:04 +0200 Subject: [PATCH 40/84] no more % on indicators --- .config/quickshell/modules/bar/Battery.qml | 2 +- .config/quickshell/modules/bar/Resource.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/bar/Battery.qml b/.config/quickshell/modules/bar/Battery.qml index e3baf5488..6a50eaf06 100644 --- a/.config/quickshell/modules/bar/Battery.qml +++ b/.config/quickshell/modules/bar/Battery.qml @@ -38,7 +38,7 @@ Rectangle { StyledText { Layout.alignment: Qt.AlignVCenter color: Appearance.colors.colOnLayer1 - text: `${Math.round(percentage * 100)}%` + text: `${Math.round(percentage * 100)}` } CircularProgress { diff --git a/.config/quickshell/modules/bar/Resource.qml b/.config/quickshell/modules/bar/Resource.qml index ae2d7167f..15f5fea26 100644 --- a/.config/quickshell/modules/bar/Resource.qml +++ b/.config/quickshell/modules/bar/Resource.qml @@ -38,7 +38,7 @@ Item { StyledText { Layout.alignment: Qt.AlignVCenter color: Appearance.colors.colOnLayer1 - text: `${Math.round(percentage * 100)}%` + text: `${Math.round(percentage * 100)}` } Behavior on x { From 1ce47424a67e69632bfa450942a0d3caf62c0c81 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 18:52:21 +0200 Subject: [PATCH 41/84] right sidebar: remove unecessary visibility hook --- .config/quickshell/modules/sidebarRight/SidebarRight.qml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index 1feec0ec7..d38de557e 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -29,10 +29,6 @@ Scope { visible: sidebarLoader.active focusable: true - onVisibleChanged: { - if (!visible) sidebarLoader.active = false - } - function hide() { sidebarLoader.active = false } From 931b276d60b0b1a09d9dc839f0b008682780eacb Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 18:53:25 +0200 Subject: [PATCH 42/84] tweak osd size --- .../quickshell/modules/common/Appearance.qml | 25 ++++++++++--------- .../OnScreenDisplayBrightness.qml | 14 +++++------ .../onScreenDisplay/OnScreenDisplayVolume.qml | 13 ++++------ .../onScreenDisplay/OsdValueIndicator.qml | 7 ++++-- 4 files changed, 29 insertions(+), 30 deletions(-) diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index b8fd1028a..8627835d9 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -260,18 +260,19 @@ Singleton { } sizes: QtObject { - property int barHeight: 40 - property int barCenterSideModuleWidth: 360 - property int barPreferredSideSectionWidth: 400 - property int sidebarWidth: 450 - property int sidebarWidthExtended: 750 - property int notificationPopupWidth: 410 - property int searchWidthCollapsed: 260 - property int searchWidth: 450 - property int hyprlandGapsOut: 5 - property int elevationMargin: 7 - property int fabShadowRadius: 5 - property int fabHoveredShadowRadius: 7 + property real barHeight: 40 + property real barCenterSideModuleWidth: 360 + property real barPreferredSideSectionWidth: 400 + property real sidebarWidth: 450 + property real sidebarWidthExtended: 750 + property real osdWidth: 200 + property real notificationPopupWidth: 410 + property real searchWidthCollapsed: 260 + property real searchWidth: 450 + property real hyprlandGapsOut: 5 + property real elevationMargin: 7 + property real fabShadowRadius: 5 + property real fabHoveredShadowRadius: 7 } syntaxHighlightingTheme: Appearance.m3colors.darkmode ? "Monokai" : "ayu Light" diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index 952951584..bec442a8a 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -70,21 +70,18 @@ Scope { item: osdValuesWrapper } - implicitWidth: columnLayout.implicitWidth + implicitWidth: Appearance.sizes.osdWidth implicitHeight: columnLayout.implicitHeight visible: osdLoader.active ColumnLayout { id: columnLayout anchors.horizontalCenter: parent.horizontalCenter - Item { - height: 1 // Prevent Wayland protocol error - } Item { id: osdValuesWrapper // Extra space for shadow - implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 - implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 + implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2 + implicitWidth: osdValues.implicitWidth clip: true MouseArea { @@ -102,8 +99,9 @@ Scope { OsdValueIndicator { id: osdValues - anchors.centerIn: parent - value: root.brightnessMonitor.brightness + anchors.fill: parent + anchors.margins: Appearance.sizes.elevationMargin + value: root.brightnessMonitor?.brightness ?? 50 icon: "light_mode" rotateIcon: true scaleIcon: true diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index d9c5b3bbf..3ecc01cde 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -72,21 +72,18 @@ Scope { item: osdValuesWrapper } - implicitWidth: columnLayout.implicitWidth + implicitWidth: Appearance.sizes.osdWidth implicitHeight: columnLayout.implicitHeight visible: osdLoader.active ColumnLayout { id: columnLayout anchors.horizontalCenter: parent.horizontalCenter - Item { - height: 1 // Prevent Wayland protocol error - } Item { id: osdValuesWrapper // Extra space for shadow - implicitHeight: true ? (osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2) : 0 - implicitWidth: osdValues.implicitWidth + Appearance.sizes.elevationMargin * 2 + implicitHeight: osdValues.implicitHeight + Appearance.sizes.elevationMargin * 2 + implicitWidth: osdValues.implicitWidth clip: true MouseArea { @@ -104,14 +101,14 @@ Scope { OsdValueIndicator { id: osdValues - anchors.centerIn: parent + anchors.fill: parent + anchors.margins: Appearance.sizes.elevationMargin value: Audio.sink?.audio.volume ?? 0 icon: Audio.sink?.audio.muted ? "volume_off" : "volume_up" name: qsTr("Volume") } } } - } } diff --git a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml index c079f1dad..36a9748b2 100644 --- a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml +++ b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml @@ -21,11 +21,12 @@ Item { property real valueIndicatorRightPadding: 20 // An icon is circle ish, a column isn't, hence the extra padding Layout.margins: Appearance.sizes.elevationMargin - implicitWidth: valueIndicator.implicitWidth + implicitWidth: Appearance.sizes.osdWidth implicitHeight: valueIndicator.implicitHeight WrapperRectangle { id: valueIndicator + anchors.fill: parent radius: Appearance.rounding.full color: Appearance.colors.colLayer0 implicitWidth: valueRow.implicitWidth @@ -33,9 +34,10 @@ Item { RowLayout { // Icon on the left, stuff on the right id: valueRow Layout.margins: 10 + anchors.fill: parent spacing: 10 - Item { + Item { implicitWidth: 30 implicitHeight: 30 Layout.alignment: Qt.AlignVCenter @@ -86,6 +88,7 @@ Item { StyledProgressBar { id: valueProgressBar + Layout.fillWidth: true value: root.value } } From 314a6c67b6bc0834a66c5f41cbc7c07aae43c194 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 18:54:28 +0200 Subject: [PATCH 43/84] feat: media controls --- .config/quickshell/modules/bar/Media.qml | 30 +- .../quickshell/modules/common/Appearance.qml | 2 + .../modules/common/functions/string_utils.js | 28 ++ .../modules/common/widgets/StyledSlider.qml | 12 +- .../modules/mediaControls/MediaControls.qml | 290 ++++++++++++++++++ .config/quickshell/shell.qml | 2 + 6 files changed, 347 insertions(+), 17 deletions(-) create mode 100644 .config/quickshell/modules/mediaControls/MediaControls.qml diff --git a/.config/quickshell/modules/bar/Media.qml b/.config/quickshell/modules/bar/Media.qml index ad6722806..ddcb458aa 100644 --- a/.config/quickshell/modules/bar/Media.qml +++ b/.config/quickshell/modules/bar/Media.qml @@ -1,29 +1,23 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/modules/common/functions/string_utils.js" as StringUtils import QtQuick import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Services.Mpris +import Quickshell.Hyprland Item { + id: root readonly property MprisPlayer activePlayer: MprisController.activePlayer - readonly property string cleanedTitle: activePlayer?.trackTitle.replace(/【[^】]*】/, "") || qsTr("No media") + readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || "No media" Layout.fillHeight: true implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 implicitHeight: 40 - // Background - Rectangle { - anchors.centerIn: parent - width: parent.width - implicitHeight: 32 - color: Appearance.colors.colLayer1 - radius: Appearance.rounding.small - } - Timer { running: activePlayer?.playbackState == MprisPlaybackState.Playing interval: 1000 @@ -33,7 +27,7 @@ Item { MouseArea { anchors.fill: parent - acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton + acceptedButtons: Qt.MiddleButton | Qt.BackButton | Qt.ForwardButton | Qt.RightButton | Qt.LeftButton onPressed: (event) => { if (event.button === Qt.MiddleButton) { activePlayer.togglePlaying(); @@ -41,11 +35,21 @@ Item { activePlayer.previous(); } else if (event.button === Qt.ForwardButton || event.button === Qt.RightButton) { activePlayer.next(); - } + } else if (event.button === Qt.LeftButton) { + Hyprland.dispatch("global quickshell:mediaControlsToggle") + } } } - RowLayout { + Rectangle { // Background + anchors.centerIn: parent + width: parent.width + implicitHeight: 32 + color: Appearance.colors.colLayer1 + radius: Appearance.rounding.small + } + + RowLayout { // Real content id: rowLayout spacing: 4 diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 8627835d9..38bea8b6b 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -266,6 +266,8 @@ Singleton { property real sidebarWidth: 450 property real sidebarWidthExtended: 750 property real osdWidth: 200 + property real mediaControlsWidth: 430 + property real mediaControlsHeight: 150 property real notificationPopupWidth: 410 property real searchWidthCollapsed: 260 property real searchWidth: 450 diff --git a/.config/quickshell/modules/common/functions/string_utils.js b/.config/quickshell/modules/common/functions/string_utils.js index 136355005..117b68f8e 100644 --- a/.config/quickshell/modules/common/functions/string_utils.js +++ b/.config/quickshell/modules/common/functions/string_utils.js @@ -108,4 +108,32 @@ function wordWrap(str, maxLen) { } if (current.length > 0) lines.push(current); return lines.join("\n"); +} + +function cleanMusicTitle(title) { + if (!title) return ""; + // Brackets + title = title.replace(/ *\([^)]*\) */g, " "); // Round brackets + title = title.replace(/ *\[[^\]]*\] */g, " "); // Square brackets + title = title.replace(/ *\{[^\}]*\} */g, " "); // Curly brackets + // Japenis brackets + title = title.replace(/【[^】]*】/, "") // Touhou + title = title.replace(/《[^》]*》/, "") // ?? + title = title.replace(/「[^」]*」/, "") // OP/ED + title = title.replace(/『[^』]*』/, "") // OP/ED + + return title; +} + +function friendlyTimeForSeconds(seconds) { + if (isNaN(seconds) || seconds < 0) return "0:00"; + seconds = Math.floor(seconds); + const h = Math.floor(seconds / 3600); + const m = Math.floor((seconds % 3600) / 60); + const s = seconds % 60; + if (h > 0) { + return `${h}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; + } else { + return `${m}:${s.toString().padStart(2, '0')}`; + } } \ No newline at end of file diff --git a/.config/quickshell/modules/common/widgets/StyledSlider.qml b/.config/quickshell/modules/common/widgets/StyledSlider.qml index b39a9a419..5a61182ac 100644 --- a/.config/quickshell/modules/common/widgets/StyledSlider.qml +++ b/.config/quickshell/modules/common/widgets/StyledSlider.qml @@ -16,8 +16,10 @@ Slider { property real handleWidth: (slider.pressed ? 3 : 5) * scale property real handleHeight: 44 * scale property real handleLimit: slider.backgroundDotMargins * scale + property real trackHeight: 15 * scale property real limitedHandleRangeWidth: (slider.availableWidth - handleWidth - slider.handleLimit * 2) + property string tooltipContent: `${Math.round(value * 100)}%` Layout.fillWidth: true from: 0 to: 1 @@ -44,13 +46,14 @@ Slider { background: Item { anchors.verticalCenter: parent.verticalCenter - implicitHeight: 12 // Somehow binding this makes it fill height. Must be set with a constant like this + implicitHeight: trackHeight // Fill left Rectangle { + anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left width: slider.handleLimit + slider.visualPosition * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2) - height: parent.height + height: trackHeight color: Appearance.m3colors.m3primary topLeftRadius: Appearance.rounding.full bottomLeftRadius: Appearance.rounding.full @@ -60,9 +63,10 @@ Slider { // Fill right Rectangle { + anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right width: slider.handleLimit + (1 - slider.visualPosition) * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2) - height: parent.height + height: trackHeight color: Appearance.m3colors.m3secondaryContainer topLeftRadius: Appearance.rounding.unsharpen bottomLeftRadius: Appearance.rounding.unsharpen @@ -101,7 +105,7 @@ Slider { StyledToolTip { extraVisibleCondition: slider.pressed - content: `${Math.round(slider.value * 100)}%` + content: slider.tooltipContent } } } \ No newline at end of file diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml new file mode 100644 index 000000000..01d9c84d3 --- /dev/null +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -0,0 +1,290 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/services" +import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/file_utils.js" as FileUtils +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Io +import Quickshell.Services.Mpris +import Quickshell.Widgets +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + required property var bar + property bool visible: false + readonly property MprisPlayer activePlayer: MprisController.activePlayer + readonly property real osdWidth: Appearance.sizes.osdWidth + readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth + readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight + property real contentPadding: 12 + property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 + property real artRounding: Appearance.rounding.verysmall + property string baseCoverArtDir: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/coverart`) + + Component.onCompleted: { + Hyprland.dispatch(`exec rm -rf ${baseCoverArtDir} && mkdir -p ${baseCoverArtDir}`) + } + + Loader { + id: mediaControlsLoader + active: false + + PanelWindow { + id: mediaControlsRoot + visible: mediaControlsLoader.active + + exclusiveZone: 0 + implicitWidth: ( + (mediaControlsRoot.screen.width / 2) // Middle of screen + - (osdWidth / 2) // Dodge OSD + - (widgetWidth / 2) // Account for widget width + ) * 2 + implicitHeight: playerColumnLayout.implicitHeight + color: "transparent" + WlrLayershell.namespace: "quickshell:mediaControls" + + anchors { + top: true + left: true + } + + ColumnLayout { + id: playerColumnLayout + anchors.top: parent.top + anchors.bottom: parent.bottom + x: (mediaControlsRoot.screen.width / 2) // Middle of screen + - (osdWidth / 2) // Dodge OSD + - (widgetWidth) // Account for widget width + + (Appearance.sizes.elevationMargin) // It's fine for shadows to overlap + + Item { // Player instance + id: playerController + property MprisPlayer player: root.activePlayer + + implicitWidth: widgetWidth + implicitHeight: widgetHeight + property string fileName: Qt.md5(activePlayer?.trackArtUrl) + ".jpg" + property string filePath: `${root.baseCoverArtDir}/${fileName}` + + Process { + id: downloadProcess + running: false + command: ["bash", "-c", `[ -f ${playerController.filePath} ] || curl '${playerController.player?.trackArtUrl}' -o '${playerController.filePath}'`] + onExited: (exitCode, exitStatus) => { + colorQuantizer.source = playerController.filePath + } + } + + ColorQuantizer { + id: colorQuantizer + depth: 1 // 2^1 colors + rescaleSize: 64 // Rescale to 64x64 for faster processing + } + + property QtObject blendedColors: QtObject { + // property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, colorQuantizer.colors[0], 0.5) + } + + Rectangle { + id: background + anchors.fill: parent + anchors.margins: Appearance.sizes.elevationMargin + color: Appearance.colors.colLayer0 + radius: root.popupRounding + + RowLayout { + anchors.fill: parent + anchors.margins: root.contentPadding + spacing: 10 + + Rectangle { // Art backgrounmd + Layout.fillHeight: true + implicitWidth: height + radius: root.artRounding + color: Appearance.colors.colLayer1 + + Image { // Art image + id: mediaArt + property int size: parent.height + anchors.fill: parent + + source: playerController.player?.trackArtUrl + fillMode: Image.PreserveAspectCrop + cache: false + antialiasing: true + asynchronous: true + + width: size + height: size + sourceSize.width: size + sourceSize.height: size + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: mediaArt.size + height: mediaArt.size + radius: root.artRounding + } + } + } + } + + ColumnLayout { // Info & controls + Layout.fillHeight: true + spacing: 2 + + StyledText { + id: trackTitle + Layout.fillWidth: true + font.pixelSize: Appearance.font.pixelSize.large + color: Appearance.colors.colOnLayer0 + elide: Text.ElideRight + text: StringUtils.cleanMusicTitle(playerController.player?.trackTitle) || "No media" + } + StyledText { + id: trackArtist + Layout.fillWidth: true + font.pixelSize: Appearance.font.pixelSize.smaller + color: Appearance.colors.colSubtext + elide: Text.ElideRight + text: playerController.player?.trackArtist + } + Item { Layout.fillHeight: true } + Item { + Layout.fillWidth: true + implicitHeight: trackTime.implicitHeight + slider.implicitHeight + + StyledText { + id: trackTime + anchors.bottom: slider.top + anchors.bottomMargin: -4 + anchors.left: parent.left + font.pixelSize: Appearance.font.pixelSize.small + color: Appearance.colors.colSubtext + elide: Text.ElideRight + text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}` + } + StyledSlider { + id: slider + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + bottomMargin: -8 + } + scale: 0.7 + value: playerController.player?.position / playerController.player?.length + onMoved: playerController.player.position = value * playerController.player.length + tooltipContent: StringUtils.friendlyTimeForSeconds(playerController.player?.position) + } + + Button { + id: playPauseButton + anchors.right: parent.right + anchors.bottom: slider.top + anchors.bottomMargin: -1 + implicitWidth: 44 + implicitHeight: 44 + onClicked: playerController.player.togglePlaying(); + + PointingHandInteraction {} + + background: Rectangle { + color: playerController.player?.isPlaying ? + (playPauseButton.pressed ? Appearance.colors.colPrimaryActive : + playPauseButton.hovered ? Appearance.colors.colPrimaryHover : + Appearance.m3colors.m3primary) : + (playPauseButton.pressed ? Appearance.colors.colSecondaryContainerActive : + playPauseButton.hovered ? Appearance.colors.colSecondaryContainerHover : + Appearance.m3colors.m3secondaryContainer) + radius: Appearance.rounding.full + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + + contentItem: MaterialSymbol { + iconSize: Appearance.font.pixelSize.huge + fill: 1 + horizontalAlignment: Text.AlignHCenter + color: playerController.player?.isPlaying ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer + text: playerController.player?.isPlaying ? "pause" : "play_arrow" + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + } + } + } + } + } + + DropShadow { + anchors.fill: background + source: background + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + } + } + } + } + } + + IpcHandler { + target: "mediaControls" + + function toggle(): void { + mediaControlsLoader.active = !mediaControlsLoader.active; + if(mediaControlsLoader.active) Notifications.timeoutAll(); + } + + function close(): void { + mediaControlsLoader.active = false; + } + + function open(): void { + mediaControlsLoader.active = true; + Notifications.timeoutAll(); + } + } + + GlobalShortcut { + name: "mediaControlsToggle" + description: "Toggles media controls on press" + + onPressed: { + mediaControlsLoader.active = !mediaControlsLoader.active; + if(mediaControlsLoader.active) Notifications.timeoutAll(); + } + } + GlobalShortcut { + name: "mediaControlsOpen" + description: "Opens media controls on press" + + onPressed: { + mediaControlsLoader.active = true; + Notifications.timeoutAll(); + } + } + GlobalShortcut { + name: "mediaControlsClose" + description: "Closes media controls on press" + + onPressed: { + mediaControlsLoader.active = false; + } + } + +} \ No newline at end of file diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index 2280482b7..3740c99f6 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -3,6 +3,7 @@ import "./modules/bar/" import "./modules/cheatsheet/" +import "./modules/mediaControls/" import "./modules/notificationPopup/" import "./modules/onScreenDisplay/" import "./modules/overview/" @@ -26,6 +27,7 @@ ShellRoot { Bar {} Cheatsheet {} + MediaControls {} NotificationPopup {} OnScreenDisplayBrightness {} OnScreenDisplayVolume {} From 73deae7ecea98a1a948660e8011370e880acbbfa Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 23:01:15 +0200 Subject: [PATCH 44/84] media controls: multi instance, colorize --- .../quickshell/modules/common/Appearance.qml | 6 +- .../modules/common/widgets/StyledSlider.qml | 11 +- .../modules/mediaControls/MediaControls.qml | 199 +++-------------- .../modules/mediaControls/PlayerControl.qml | 207 ++++++++++++++++++ 4 files changed, 243 insertions(+), 180 deletions(-) create mode 100644 .config/quickshell/modules/mediaControls/PlayerControl.qml diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 38bea8b6b..6e950b172 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -263,11 +263,11 @@ Singleton { property real barHeight: 40 property real barCenterSideModuleWidth: 360 property real barPreferredSideSectionWidth: 400 - property real sidebarWidth: 450 + property real sidebarWidth: 460 property real sidebarWidthExtended: 750 property real osdWidth: 200 - property real mediaControlsWidth: 430 - property real mediaControlsHeight: 150 + property real mediaControlsWidth: 440 + property real mediaControlsHeight: 160 property real notificationPopupWidth: 410 property real searchWidthCollapsed: 260 property real searchWidth: 450 diff --git a/.config/quickshell/modules/common/widgets/StyledSlider.qml b/.config/quickshell/modules/common/widgets/StyledSlider.qml index 5a61182ac..23a6179f4 100644 --- a/.config/quickshell/modules/common/widgets/StyledSlider.qml +++ b/.config/quickshell/modules/common/widgets/StyledSlider.qml @@ -17,6 +17,9 @@ Slider { property real handleHeight: 44 * scale property real handleLimit: slider.backgroundDotMargins * scale property real trackHeight: 15 * scale + property color highlightColor: Appearance.m3colors.m3primary + property color trackColor: Appearance.m3colors.m3secondaryContainer + property color handleColor: Appearance.m3colors.m3onSecondaryContainer property real limitedHandleRangeWidth: (slider.availableWidth - handleWidth - slider.handleLimit * 2) property string tooltipContent: `${Math.round(value * 100)}%` @@ -54,7 +57,7 @@ Slider { anchors.left: parent.left width: slider.handleLimit + slider.visualPosition * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2) height: trackHeight - color: Appearance.m3colors.m3primary + color: slider.highlightColor topLeftRadius: Appearance.rounding.full bottomLeftRadius: Appearance.rounding.full topRightRadius: Appearance.rounding.unsharpen @@ -67,7 +70,7 @@ Slider { anchors.right: parent.right width: slider.handleLimit + (1 - slider.visualPosition) * slider.limitedHandleRangeWidth - (slider.handleMargins + slider.handleWidth / 2) height: trackHeight - color: Appearance.m3colors.m3secondaryContainer + color: slider.trackColor topLeftRadius: Appearance.rounding.unsharpen bottomLeftRadius: Appearance.rounding.unsharpen topRightRadius: Appearance.rounding.full @@ -82,7 +85,7 @@ Slider { width: slider.backgroundDotSize height: slider.backgroundDotSize radius: Appearance.rounding.full - color: Appearance.m3colors.m3onSecondaryContainer + color: slider.handleColor } } @@ -93,7 +96,7 @@ Slider { implicitWidth: slider.handleWidth implicitHeight: slider.handleHeight radius: Appearance.rounding.full - color: Appearance.m3colors.m3onSecondaryContainer + color: slider.handleColor Behavior on implicitWidth { NumberAnimation { diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml index 01d9c84d3..6d640b4dd 100644 --- a/.config/quickshell/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -22,11 +22,25 @@ Scope { readonly property real osdWidth: Appearance.sizes.osdWidth readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight - property real contentPadding: 12 + property real contentPadding: 13 property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 property real artRounding: Appearance.rounding.verysmall property string baseCoverArtDir: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/coverart`) + // property bool hasPlasmaIntegration: true + function isRealPlayer(player) { + // return true + return ( + // Remove unecessary native buses from browsers if there's plasma integration + // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) && + // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) && + // playerctld just copies other buses and we don't need duplicates + !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && + // Non-instance mpd bus + !(player.dbusName?.endsWith('.mpd') && !player.busName.endsWith('MediaPlayer2.mpd')) + ); + } + Component.onCompleted: { Hyprland.dispatch(`exec rm -rf ${baseCoverArtDir} && mkdir -p ${baseCoverArtDir}`) } @@ -53,6 +67,9 @@ Scope { top: true left: true } + mask: Region { + item: playerColumnLayout + } ColumnLayout { id: playerColumnLayout @@ -62,180 +79,16 @@ Scope { - (osdWidth / 2) // Dodge OSD - (widgetWidth) // Account for widget width + (Appearance.sizes.elevationMargin) // It's fine for shadows to overlap + spacing: -Appearance.sizes.elevationMargin // Shadow overlap okay - Item { // Player instance - id: playerController - property MprisPlayer player: root.activePlayer - - implicitWidth: widgetWidth - implicitHeight: widgetHeight - property string fileName: Qt.md5(activePlayer?.trackArtUrl) + ".jpg" - property string filePath: `${root.baseCoverArtDir}/${fileName}` - - Process { - id: downloadProcess - running: false - command: ["bash", "-c", `[ -f ${playerController.filePath} ] || curl '${playerController.player?.trackArtUrl}' -o '${playerController.filePath}'`] - onExited: (exitCode, exitStatus) => { - colorQuantizer.source = playerController.filePath - } + Repeater { + model: { + // console.log(JSON.stringify(Mpris.players, null, 2)) + return Mpris.players.values.filter(player => isRealPlayer(player)) } - - ColorQuantizer { - id: colorQuantizer - depth: 1 // 2^1 colors - rescaleSize: 64 // Rescale to 64x64 for faster processing - } - - property QtObject blendedColors: QtObject { - // property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, colorQuantizer.colors[0], 0.5) - } - - Rectangle { - id: background - anchors.fill: parent - anchors.margins: Appearance.sizes.elevationMargin - color: Appearance.colors.colLayer0 - radius: root.popupRounding - - RowLayout { - anchors.fill: parent - anchors.margins: root.contentPadding - spacing: 10 - - Rectangle { // Art backgrounmd - Layout.fillHeight: true - implicitWidth: height - radius: root.artRounding - color: Appearance.colors.colLayer1 - - Image { // Art image - id: mediaArt - property int size: parent.height - anchors.fill: parent - - source: playerController.player?.trackArtUrl - fillMode: Image.PreserveAspectCrop - cache: false - antialiasing: true - asynchronous: true - - width: size - height: size - sourceSize.width: size - sourceSize.height: size - - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: mediaArt.size - height: mediaArt.size - radius: root.artRounding - } - } - } - } - - ColumnLayout { // Info & controls - Layout.fillHeight: true - spacing: 2 - - StyledText { - id: trackTitle - Layout.fillWidth: true - font.pixelSize: Appearance.font.pixelSize.large - color: Appearance.colors.colOnLayer0 - elide: Text.ElideRight - text: StringUtils.cleanMusicTitle(playerController.player?.trackTitle) || "No media" - } - StyledText { - id: trackArtist - Layout.fillWidth: true - font.pixelSize: Appearance.font.pixelSize.smaller - color: Appearance.colors.colSubtext - elide: Text.ElideRight - text: playerController.player?.trackArtist - } - Item { Layout.fillHeight: true } - Item { - Layout.fillWidth: true - implicitHeight: trackTime.implicitHeight + slider.implicitHeight - - StyledText { - id: trackTime - anchors.bottom: slider.top - anchors.bottomMargin: -4 - anchors.left: parent.left - font.pixelSize: Appearance.font.pixelSize.small - color: Appearance.colors.colSubtext - elide: Text.ElideRight - text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}` - } - StyledSlider { - id: slider - anchors { - bottom: parent.bottom - left: parent.left - right: parent.right - bottomMargin: -8 - } - scale: 0.7 - value: playerController.player?.position / playerController.player?.length - onMoved: playerController.player.position = value * playerController.player.length - tooltipContent: StringUtils.friendlyTimeForSeconds(playerController.player?.position) - } - - Button { - id: playPauseButton - anchors.right: parent.right - anchors.bottom: slider.top - anchors.bottomMargin: -1 - implicitWidth: 44 - implicitHeight: 44 - onClicked: playerController.player.togglePlaying(); - - PointingHandInteraction {} - - background: Rectangle { - color: playerController.player?.isPlaying ? - (playPauseButton.pressed ? Appearance.colors.colPrimaryActive : - playPauseButton.hovered ? Appearance.colors.colPrimaryHover : - Appearance.m3colors.m3primary) : - (playPauseButton.pressed ? Appearance.colors.colSecondaryContainerActive : - playPauseButton.hovered ? Appearance.colors.colSecondaryContainerHover : - Appearance.m3colors.m3secondaryContainer) - radius: Appearance.rounding.full - - Behavior on color { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } - } - - contentItem: MaterialSymbol { - iconSize: Appearance.font.pixelSize.huge - fill: 1 - horizontalAlignment: Text.AlignHCenter - color: playerController.player?.isPlaying ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer - text: playerController.player?.isPlaying ? "pause" : "play_arrow" - - Behavior on color { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } - } - } - } - } - } - } - - DropShadow { - anchors.fill: background - source: background - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow + delegate: PlayerControl { + required property MprisPlayer modelData + player: modelData } } } diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml new file mode 100644 index 000000000..00475a786 --- /dev/null +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -0,0 +1,207 @@ +import "root:/modules/common" +import "root:/modules/common/widgets" +import "root:/services" +import "root:/modules/common/functions/string_utils.js" as StringUtils +import Qt5Compat.GraphicalEffects +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import Quickshell.Io +import Quickshell.Services.Mpris +import Quickshell.Widgets +import Quickshell.Wayland +import Quickshell.Hyprland + +Item { // Player instance + id: playerController + required property MprisPlayer player + // property var artUrl: player?.metadata["xesam:url"] || player?.metadata["mpris:artUrl"] || player?.trackArtUrl + property var artUrl: player?.trackArtUrl + property string localArt + property color artDominantColor: "#00000000" + + implicitWidth: widgetWidth + implicitHeight: widgetHeight + + onArtUrlChanged: { + colorQuantizer.running = true + } + + Process { // Average Color Runner + id: colorQuantizer + command: [ "sh", "-c", `magick ${playerController.player.trackArtUrl} -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ] + stdout: SplitParser { + onRead: data => { + playerController.artDominantColor = "#" + data + } + } + } + + property QtObject blendedColors: QtObject { + property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.7) + property color colLayer1: Appearance.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) + property color colOnLayer0: Appearance.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7) + property color colOnLayer1: Appearance.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) + property color colSubtext: Appearance.mix(Appearance.colors.colSubtext, artDominantColor, 0.5) + property color colPrimary: Appearance.mix(Appearance.m3colors.m3primary, artDominantColor, 0.3) + property color colPrimaryHover: Appearance.mix(Appearance.colors.colPrimaryHover, artDominantColor, 0.3) + property color colPrimaryActive: Appearance.mix(Appearance.colors.colPrimaryActive, artDominantColor, 0.3) + property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.5) + property color colSecondaryContainerHover: Appearance.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) + property color colSecondaryContainerActive: Appearance.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3) + property color colOnPrimary: Appearance.mix(Appearance.colors.colOnPrimary, artDominantColor, 0.5) + property color colOnSecondaryContainer: Appearance.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2) + + } + + Rectangle { + id: background + anchors.fill: parent + anchors.margins: Appearance.sizes.elevationMargin + color: blendedColors.colLayer0 + radius: root.popupRounding + + RowLayout { + anchors.fill: parent + anchors.margins: root.contentPadding + spacing: 15 + + Rectangle { // Art backgrounmd + Layout.fillHeight: true + implicitWidth: height + radius: root.artRounding + color: blendedColors.colLayer1 + + Image { // Art image + id: mediaArt + property int size: parent.height + anchors.fill: parent + + source: playerController.artUrl + fillMode: Image.PreserveAspectCrop + cache: false + antialiasing: true + asynchronous: true + + width: size + height: size + sourceSize.width: size + sourceSize.height: size + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: mediaArt.size + height: mediaArt.size + radius: root.artRounding + } + } + } + } + + ColumnLayout { // Info & controls + Layout.fillHeight: true + spacing: 2 + + StyledText { + id: trackTitle + Layout.fillWidth: true + font.pixelSize: Appearance.font.pixelSize.large + color: blendedColors.colOnLayer0 + elide: Text.ElideRight + text: StringUtils.cleanMusicTitle(playerController.player?.trackTitle) || "Untitled" + } + StyledText { + id: trackArtist + Layout.fillWidth: true + font.pixelSize: Appearance.font.pixelSize.smaller + color: blendedColors.colSubtext + elide: Text.ElideRight + text: playerController.player?.trackArtist + } + Item { Layout.fillHeight: true } + Item { + Layout.fillWidth: true + implicitHeight: trackTime.implicitHeight + slider.implicitHeight + + StyledText { + id: trackTime + anchors.bottom: slider.top + anchors.bottomMargin: -4 + anchors.left: parent.left + font.pixelSize: Appearance.font.pixelSize.small + color: blendedColors.colSubtext + elide: Text.ElideRight + text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}` + } + StyledSlider { + id: slider + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + bottomMargin: -8 + } + highlightColor: blendedColors.colPrimary + trackColor: blendedColors.colSecondaryContainer + handleColor: blendedColors.colOnSecondaryContainer + scale: 0.6 + value: playerController.player?.position / playerController.player?.length + onMoved: playerController.player.position = value * playerController.player.length + tooltipContent: StringUtils.friendlyTimeForSeconds(playerController.player?.position) + } + + Button { + id: playPauseButton + anchors.right: parent.right + anchors.bottom: slider.top + anchors.bottomMargin: -1 + implicitWidth: 44 + implicitHeight: 44 + onClicked: playerController.player.togglePlaying(); + + PointingHandInteraction {} + + background: Rectangle { + color: playerController.player?.isPlaying ? + (playPauseButton.pressed ? blendedColors.colPrimaryActive : + playPauseButton.hovered ? blendedColors.colPrimaryHover : + blendedColors.colPrimary) : + (playPauseButton.pressed ? blendedColors.colSecondaryContainerActive : + playPauseButton.hovered ? blendedColors.colSecondaryContainerHover : + blendedColors.colSecondaryContainer) + radius: Appearance.rounding.full + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + + contentItem: MaterialSymbol { + iconSize: Appearance.font.pixelSize.huge + fill: 1 + horizontalAlignment: Text.AlignHCenter + color: playerController.player?.isPlaying ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer + text: playerController.player?.isPlaying ? "pause" : "play_arrow" + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + } + } + } + } + } + + DropShadow { + anchors.fill: background + source: background + horizontalOffset: 0 + verticalOffset: 2 + radius: Appearance.sizes.elevationMargin + samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs + color: Appearance.colors.colShadow + } +} \ No newline at end of file From 5479d66a66b297d52c808297285ca06aacf9ee81 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 18 May 2025 23:01:28 +0200 Subject: [PATCH 45/84] don't use qs' default reload popup --- .config/quickshell/shell.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/shell.qml b/.config/quickshell/shell.qml index 3740c99f6..af14d6b74 100644 --- a/.config/quickshell/shell.qml +++ b/.config/quickshell/shell.qml @@ -1,5 +1,6 @@ //@ pragma UseQApplication //@ pragma Env QT_QUICK_CONTROLS_STYLE=Basic +//@ pragma Env QS_NO_RELOAD_POPUP=1 import "./modules/bar/" import "./modules/cheatsheet/" From c9f1a80dc2b65aa30709d422855b5e394e656265 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 00:09:08 +0200 Subject: [PATCH 46/84] adjust media control colors --- .../quickshell/modules/common/Appearance.qml | 51 +++++++++++++++ .../common/widgets/StyledProgressBar.qml | 8 ++- .../modules/mediaControls/MediaControls.qml | 13 ++-- .../modules/mediaControls/PlayerControl.qml | 62 +++++++++++++------ 4 files changed, 106 insertions(+), 28 deletions(-) diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 6e950b172..be23fc2d9 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -14,6 +14,57 @@ Singleton { property QtObject sizes property string syntaxHighlightingTheme + function colorWithHueOf(color1, color2) { + // Convert both colors to HSV + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + // Helper to convert RGB to HSV + function rgb2hsv(c) { + var r = c.r, g = c.g, b = c.b; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + var d = max - min; + s = max === 0 ? 0 : d / max; + if (max === min) { + h = 0; + } else { + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return {h: h, s: s, v: v, a: c.a}; + } + + // Helper to convert HSV to RGB + function hsv2rgb(h, s, v, a) { + var r, g, b; + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + switch(i % 6){ + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + return Qt.rgba(r, g, b, a); + } + + var hsv1 = rgb2hsv(c1); + var hsv2 = rgb2hsv(c2); + + // Use hue from color2, saturation/value/alpha from color1 + return hsv2rgb(hsv2.h, hsv1.s, hsv1.v, hsv1.a); + } + function mix(color1, color2, percentage) { var c1 = Qt.color(color1); var c2 = Qt.color(color2); diff --git a/.config/quickshell/modules/common/widgets/StyledProgressBar.qml b/.config/quickshell/modules/common/widgets/StyledProgressBar.qml index 0a27fca99..7173af88b 100644 --- a/.config/quickshell/modules/common/widgets/StyledProgressBar.qml +++ b/.config/quickshell/modules/common/widgets/StyledProgressBar.qml @@ -13,6 +13,8 @@ ProgressBar { property real valueBarWidth: 120 property real valueBarHeight: 4 property real valueBarGap: 4 + property color highlightColor: Appearance.m3colors.m3primary + property color trackColor: Appearance.m3colors.m3secondaryContainer Behavior on value { animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) @@ -34,21 +36,21 @@ ProgressBar { width: root.visualPosition * parent.width height: parent.height radius: Appearance.rounding.full - color: Appearance.m3colors.m3primary + color: root.highlightColor } Rectangle { // Right remaining part fill anchors.right: parent.right width: (1 - root.visualPosition) * parent.width - valueBarGap height: parent.height radius: Appearance.rounding.full - color: Appearance.m3colors.m3secondaryContainer + color: root.trackColor } Rectangle { // Stop point anchors.right: parent.right width: valueBarGap height: valueBarGap radius: Appearance.rounding.full - color: Appearance.m3colors.m3primary + color: root.highlightColor } } } \ No newline at end of file diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml index 6d640b4dd..c5b3b87a2 100644 --- a/.config/quickshell/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -27,17 +27,17 @@ Scope { property real artRounding: Appearance.rounding.verysmall property string baseCoverArtDir: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/coverart`) - // property bool hasPlasmaIntegration: true + property bool hasPlasmaIntegration: false function isRealPlayer(player) { // return true return ( // Remove unecessary native buses from browsers if there's plasma integration - // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) && - // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) && + !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && + !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && // playerctld just copies other buses and we don't need duplicates !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && // Non-instance mpd bus - !(player.dbusName?.endsWith('.mpd') && !player.busName.endsWith('MediaPlayer2.mpd')) + !(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd')) ); } @@ -82,9 +82,8 @@ Scope { spacing: -Appearance.sizes.elevationMargin // Shadow overlap okay Repeater { - model: { - // console.log(JSON.stringify(Mpris.players, null, 2)) - return Mpris.players.values.filter(player => isRealPlayer(player)) + model: ScriptModel { + values: Mpris.players.values.filter(player => isRealPlayer(player)) } delegate: PlayerControl { required property MprisPlayer modelData diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 00475a786..37e417ffd 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -18,39 +18,51 @@ Item { // Player instance required property MprisPlayer player // property var artUrl: player?.metadata["xesam:url"] || player?.metadata["mpris:artUrl"] || player?.trackArtUrl property var artUrl: player?.trackArtUrl - property string localArt - property color artDominantColor: "#00000000" + property color artDominantColor: Appearance.m3colors.m3primaryFixed implicitWidth: widgetWidth implicitHeight: widgetHeight + Timer { // Force update for prevision + running: playerController.player?.playbackState == MprisPlaybackState.Playing + interval: 1000 + repeat: true + onTriggered: { + playerController.player.positionChanged() + } + } + onArtUrlChanged: { + if (playerController.artUrl.length == 0) return; + colorQuantizer.targetFile = playerController.artUrl // Yes this binding break is intentional colorQuantizer.running = true } Process { // Average Color Runner id: colorQuantizer - command: [ "sh", "-c", `magick ${playerController.player.trackArtUrl} -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ] + property string targetFile: playerController.artUrl + command: [ "sh", "-c", `magick '${targetFile}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo` ] stdout: SplitParser { onRead: data => { + // console.log("Color quantizer output:", data) playerController.artDominantColor = "#" + data } } } property QtObject blendedColors: QtObject { - property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.7) + property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.6) property color colLayer1: Appearance.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) property color colOnLayer0: Appearance.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7) property color colOnLayer1: Appearance.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) property color colSubtext: Appearance.mix(Appearance.colors.colSubtext, artDominantColor, 0.5) - property color colPrimary: Appearance.mix(Appearance.m3colors.m3primary, artDominantColor, 0.3) - property color colPrimaryHover: Appearance.mix(Appearance.colors.colPrimaryHover, artDominantColor, 0.3) - property color colPrimaryActive: Appearance.mix(Appearance.colors.colPrimaryActive, artDominantColor, 0.3) - property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.5) + property color colPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5) + property color colPrimaryHover: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) + property color colPrimaryActive: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) + property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.3) property color colSecondaryContainerHover: Appearance.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) property color colSecondaryContainerActive: Appearance.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3) - property color colOnPrimary: Appearance.mix(Appearance.colors.colOnPrimary, artDominantColor, 0.5) + property color colOnPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5) property color colOnSecondaryContainer: Appearance.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2) } @@ -62,6 +74,24 @@ Item { // Player instance color: blendedColors.colLayer0 radius: root.popupRounding + LinearGradient { + anchors.fill: parent + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: background.width + height: background.height + radius: root.popupRounding + } + } + start: Qt.point(0, 0) + end: Qt.point(background.width, background.height) + gradient: Gradient { + GradientStop { position: 0.0; color: Appearance.transparentize(artDominantColor, 0.6) } + GradientStop { position: 0.4; color: Appearance.transparentize(artDominantColor, 0.8) } + } + } + RowLayout { anchors.fill: parent anchors.margins: root.contentPadding @@ -128,35 +158,31 @@ Item { // Player instance StyledText { id: trackTime anchors.bottom: slider.top - anchors.bottomMargin: -4 + anchors.bottomMargin: 5 anchors.left: parent.left font.pixelSize: Appearance.font.pixelSize.small color: blendedColors.colSubtext elide: Text.ElideRight text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}` } - StyledSlider { + StyledProgressBar { id: slider anchors { bottom: parent.bottom left: parent.left right: parent.right - bottomMargin: -8 + bottomMargin: 5 } highlightColor: blendedColors.colPrimary trackColor: blendedColors.colSecondaryContainer - handleColor: blendedColors.colOnSecondaryContainer - scale: 0.6 value: playerController.player?.position / playerController.player?.length - onMoved: playerController.player.position = value * playerController.player.length - tooltipContent: StringUtils.friendlyTimeForSeconds(playerController.player?.position) } Button { id: playPauseButton anchors.right: parent.right anchors.bottom: slider.top - anchors.bottomMargin: -1 + anchors.bottomMargin: 20 implicitWidth: 44 implicitHeight: 44 onClicked: playerController.player.togglePlaying(); @@ -182,7 +208,7 @@ Item { // Player instance iconSize: Appearance.font.pixelSize.huge fill: 1 horizontalAlignment: Text.AlignHCenter - color: playerController.player?.isPlaying ? Appearance.m3colors.m3onPrimary : Appearance.m3colors.m3onSecondaryContainer + color: playerController.player?.isPlaying ? blendedColors.colOnPrimary : blendedColors.colOnSecondaryContainer text: playerController.player?.isPlaying ? "pause" : "play_arrow" Behavior on color { From a287b4524bf7d02111d90d944c9f23d5f995aaa4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 08:43:55 +0200 Subject: [PATCH 47/84] use filled icons on bar --- .config/quickshell/modules/bar/Battery.qml | 1 + .config/quickshell/modules/bar/Media.qml | 1 + .config/quickshell/modules/bar/Resource.qml | 1 + 3 files changed, 3 insertions(+) diff --git a/.config/quickshell/modules/bar/Battery.qml b/.config/quickshell/modules/bar/Battery.qml index 6a50eaf06..2c75d0597 100644 --- a/.config/quickshell/modules/bar/Battery.qml +++ b/.config/quickshell/modules/bar/Battery.qml @@ -52,6 +52,7 @@ Rectangle { MaterialSymbol { anchors.centerIn: parent + fill: 1 text: "battery_full" iconSize: Appearance.font.pixelSize.normal color: (isLow && !isCharging) ? batteryLowOnBackground : Appearance.m3colors.m3onSecondaryContainer diff --git a/.config/quickshell/modules/bar/Media.qml b/.config/quickshell/modules/bar/Media.qml index ddcb458aa..424cf860a 100644 --- a/.config/quickshell/modules/bar/Media.qml +++ b/.config/quickshell/modules/bar/Media.qml @@ -66,6 +66,7 @@ Item { MaterialSymbol { anchors.centerIn: parent + fill: 1 text: activePlayer?.isPlaying ? "pause" : "play_arrow" iconSize: Appearance.font.pixelSize.normal color: Appearance.m3colors.m3onSecondaryContainer diff --git a/.config/quickshell/modules/bar/Resource.qml b/.config/quickshell/modules/bar/Resource.qml index 15f5fea26..18fdcba42 100644 --- a/.config/quickshell/modules/bar/Resource.qml +++ b/.config/quickshell/modules/bar/Resource.qml @@ -28,6 +28,7 @@ Item { MaterialSymbol { anchors.centerIn: parent + fill: 1 text: iconName iconSize: Appearance.font.pixelSize.normal color: Appearance.m3colors.m3onSecondaryContainer From a0ed714199e5491d105af75ed9fc9db05f5366fa Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 08:44:32 +0200 Subject: [PATCH 48/84] media controls: next/prev track button --- .../modules/mediaControls/PlayerControl.qml | 62 ++++++++++++++++--- 1 file changed, 53 insertions(+), 9 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 37e417ffd..93cfa0587 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -23,6 +23,38 @@ Item { // Player instance implicitWidth: widgetWidth implicitHeight: widgetHeight + component TrackChangeButton: Button { + id: playPauseButton + implicitWidth: 24 + implicitHeight: 24 + + property var iconName + PointingHandInteraction {} + + background: Rectangle { + color: playPauseButton.pressed ? blendedColors.colSecondaryContainerActive : + playPauseButton.hovered ? blendedColors.colSecondaryContainerHover : + Appearance.transparentize(blendedColors.colSecondaryContainer, 1) + radius: Appearance.rounding.full + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + + contentItem: MaterialSymbol { + iconSize: Appearance.font.pixelSize.huge + fill: 1 + horizontalAlignment: Text.AlignHCenter + color: blendedColors.colOnSecondaryContainer + text: iconName + + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + } + } + Timer { // Force update for prevision running: playerController.player?.playbackState == MprisPlaybackState.Playing interval: 1000 @@ -153,11 +185,11 @@ Item { // Player instance Item { Layout.fillHeight: true } Item { Layout.fillWidth: true - implicitHeight: trackTime.implicitHeight + slider.implicitHeight + implicitHeight: trackTime.implicitHeight + sliderRow.implicitHeight StyledText { id: trackTime - anchors.bottom: slider.top + anchors.bottom: sliderRow.top anchors.bottomMargin: 5 anchors.left: parent.left font.pixelSize: Appearance.font.pixelSize.small @@ -165,24 +197,36 @@ Item { // Player instance elide: Text.ElideRight text: `${StringUtils.friendlyTimeForSeconds(playerController.player?.position)} / ${StringUtils.friendlyTimeForSeconds(playerController.player?.length)}` } - StyledProgressBar { - id: slider + RowLayout { + id: sliderRow anchors { bottom: parent.bottom left: parent.left right: parent.right bottomMargin: 5 } - highlightColor: blendedColors.colPrimary - trackColor: blendedColors.colSecondaryContainer - value: playerController.player?.position / playerController.player?.length + TrackChangeButton { + iconName: "skip_previous" + onClicked: playerController.player?.previous() + } + StyledProgressBar { + id: slider + Layout.fillWidth: true + highlightColor: blendedColors.colPrimary + trackColor: blendedColors.colSecondaryContainer + value: playerController.player?.position / playerController.player?.length + } + TrackChangeButton { + iconName: "skip_next" + onClicked: playerController.player?.next() + } } Button { id: playPauseButton anchors.right: parent.right - anchors.bottom: slider.top - anchors.bottomMargin: 20 + anchors.bottom: sliderRow.top + anchors.bottomMargin: 5 implicitWidth: 44 implicitHeight: 44 onClicked: playerController.player.togglePlaying(); From 29fb9a52689d4f3c1bd22c519ec207a6bb1d152c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 08:45:40 +0200 Subject: [PATCH 49/84] no more hacky focus grab thanks to hyprland's dfb841c303263208c2f8ac7a55fbdf4668594fb7 --- .../modules/cheatsheet/Cheatsheet.qml | 19 +----------- .../modules/sidebarLeft/SidebarLeft.qml | 29 ++++--------------- .../modules/sidebarRight/SidebarRight.qml | 19 +----------- 3 files changed, 8 insertions(+), 59 deletions(-) diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 296ead290..91a46852e 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -22,7 +22,6 @@ Scope { // Scope sourceComponent: PanelWindow { // Window id: cheatsheetRoot visible: cheatsheetLoader.active - focusable: true anchors { top: true @@ -49,28 +48,12 @@ Scope { // Scope HyprlandFocusGrab { // Click outside to close id: grab windows: [ cheatsheetRoot ] - active: false + active: cheatsheetRoot.visible onCleared: () => { if (!active) cheatsheetRoot.hide() } } - Connections { - target: cheatsheetRoot - function onVisibleChanged() { - delayedGrabTimer.start() - } - } - - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = cheatsheetRoot.visible - } - } - // Background Rectangle { id: cheatsheetBackground diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 9c4547ee6..bb5784100 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -27,7 +27,7 @@ Scope { // Scope PanelWindow { // Window id: sidebarRoot visible: sidebarLoader.active - focusable: true + property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab property bool extend: false property bool pin: false @@ -37,7 +37,10 @@ Scope { // Scope sidebarLoader.active = false } - exclusiveZone: pin ? sidebarWidth : 0 + exclusiveZone: { + console.log(sidebarRoot.pin ? sidebarWidth : 0) + return sidebarRoot.pin ? sidebarWidth : 0 + } implicitWidth: Appearance.sizes.sidebarWidthExtended WlrLayershell.namespace: "quickshell:sidebarLeft" // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close @@ -57,32 +60,12 @@ Scope { // Scope HyprlandFocusGrab { // Click outside to close id: grab windows: [ sidebarRoot ] - active: false + active: sidebarRoot.visible && !sidebarRoot.pin onCleared: () => { if (!active) sidebarRoot.hide() } } - Connections { - target: sidebarRoot - function onVisibleChanged() { - delayedGrabTimer.start() - swipeView.children[0].children[0].children[sidebarRoot.selectedTab].forceActiveFocus() - } - function onPinChanged() { - grab.active = !sidebarRoot.pin - } - } - - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = sidebarRoot.visible && !sidebarRoot.pin - } - } - // Background Rectangle { id: sidebarLeftBackground diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index d38de557e..f347b0698 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -27,7 +27,6 @@ Scope { PanelWindow { id: sidebarRoot visible: sidebarLoader.active - focusable: true function hide() { sidebarLoader.active = false @@ -49,28 +48,12 @@ Scope { HyprlandFocusGrab { id: grab windows: [ sidebarRoot ] - active: false + active: sidebarRoot.visible onCleared: () => { if (!active) sidebarRoot.hide() } } - Connections { - target: sidebarRoot - function onVisibleChanged() { - delayedGrabTimer.start() - } - } - - Timer { - id: delayedGrabTimer - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay - repeat: false - onTriggered: { - grab.active = sidebarRoot.visible - } - } - // Background Rectangle { id: sidebarRightBackground From 8b725202b381b9dd0d0a37a1429b17a9935148d1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 09:00:08 +0200 Subject: [PATCH 50/84] remove debug print --- .config/quickshell/modules/sidebarLeft/SidebarLeft.qml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index bb5784100..1a209e22f 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -37,10 +37,7 @@ Scope { // Scope sidebarLoader.active = false } - exclusiveZone: { - console.log(sidebarRoot.pin ? sidebarWidth : 0) - return sidebarRoot.pin ? sidebarWidth : 0 - } + exclusiveZone: sidebarRoot.pin ? sidebarWidth : 0 implicitWidth: Appearance.sizes.sidebarWidthExtended WlrLayershell.namespace: "quickshell:sidebarLeft" // Hyprland 0.49: OnDemand is Exclusive, Exclusive just breaks click-outside-to-close From e33b4fad58089cd98fcad5fcaa384d1e7585b73e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 09:00:21 +0200 Subject: [PATCH 51/84] dont open media controls when empty --- .config/quickshell/modules/mediaControls/MediaControls.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml index c5b3b87a2..204956114 100644 --- a/.config/quickshell/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -19,6 +19,7 @@ Scope { required property var bar property bool visible: false readonly property MprisPlayer activePlayer: MprisController.activePlayer + readonly property var realPlayers: Mpris.players.values.filter(player => isRealPlayer(player)) readonly property real osdWidth: Appearance.sizes.osdWidth readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth readonly property real widgetHeight: Appearance.sizes.mediaControlsHeight @@ -83,7 +84,7 @@ Scope { Repeater { model: ScriptModel { - values: Mpris.players.values.filter(player => isRealPlayer(player)) + values: root.realPlayers } delegate: PlayerControl { required property MprisPlayer modelData @@ -117,6 +118,9 @@ Scope { description: "Toggles media controls on press" onPressed: { + if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) { + return; + } mediaControlsLoader.active = !mediaControlsLoader.active; if(mediaControlsLoader.active) Notifications.timeoutAll(); } From 2b393708b74d815c25a8752524a4196bb0f0108b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 09:01:15 +0200 Subject: [PATCH 52/84] bar: util buttons: use filled icons, fix alignment --- .config/quickshell/modules/bar/UtilButtons.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/bar/UtilButtons.qml b/.config/quickshell/modules/bar/UtilButtons.qml index 93b34d8d9..62c1f45be 100644 --- a/.config/quickshell/modules/bar/UtilButtons.qml +++ b/.config/quickshell/modules/bar/UtilButtons.qml @@ -24,7 +24,8 @@ Rectangle { onClicked: Hyprland.dispatch("exec grimblast copy area") MaterialSymbol { - anchors.centerIn: parent + horizontalAlignment: Qt.AlignHCenter + fill: 1 text: "screenshot_region" iconSize: Appearance.font.pixelSize.normal color: Appearance.colors.colOnLayer2 @@ -37,7 +38,8 @@ Rectangle { onClicked: Hyprland.dispatch("exec hyprpicker -a") MaterialSymbol { - anchors.centerIn: parent + horizontalAlignment: Qt.AlignHCenter + fill: 1 text: "colorize" iconSize: Appearance.font.pixelSize.normal color: Appearance.colors.colOnLayer2 From 671d2463b8237a0680edb9c0e5d0b4e68a053aa3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 09:37:47 +0200 Subject: [PATCH 53/84] ai: add null check for gemini annotations --- .config/quickshell/services/Ai.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index 495f15efc..f47718f9c 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -318,14 +318,14 @@ Singleton { const dataJson = JSON.parse(requester.geminiBuffer); const responseContent = dataJson.candidates[0]?.content?.parts[0]?.text requester.message.content += responseContent; - const annotationSources = dataJson.candidates[0]?.groundingMetadata.groundingChunks?.map(chunk => { + const annotationSources = dataJson.candidates[0]?.groundingMetadata?.groundingChunks?.map(chunk => { return { "type": "url_citation", "text": chunk?.web?.title, "url": chunk?.web?.uri, } }); - const annotations = dataJson.candidates[0]?.groundingMetadata.groundingSupports?.map(citation => { + const annotations = dataJson.candidates[0]?.groundingMetadata?.groundingSupports?.map(citation => { return { "type": "url_citation", "start_index": citation.segment?.startIndex, @@ -403,7 +403,7 @@ Singleton { stdout: SplitParser { onRead: data => { - // console.log("RAW DATA: ", data); + console.log("RAW DATA: ", data); if (data.length === 0) return; // Handle response line From e6344a704d04547a3da6a893d2cb95e6dd21b374 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 09:38:06 +0200 Subject: [PATCH 54/84] music title cleaning: only clean brackets at start --- .../modules/common/functions/string_utils.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/.config/quickshell/modules/common/functions/string_utils.js b/.config/quickshell/modules/common/functions/string_utils.js index 117b68f8e..6becbc265 100644 --- a/.config/quickshell/modules/common/functions/string_utils.js +++ b/.config/quickshell/modules/common/functions/string_utils.js @@ -113,14 +113,14 @@ function wordWrap(str, maxLen) { function cleanMusicTitle(title) { if (!title) return ""; // Brackets - title = title.replace(/ *\([^)]*\) */g, " "); // Round brackets - title = title.replace(/ *\[[^\]]*\] */g, " "); // Square brackets - title = title.replace(/ *\{[^\}]*\} */g, " "); // Curly brackets + title = title.replace(/^ *\([^)]*\) */g, " "); // Round brackets + title = title.replace(/^ *\[[^\]]*\] */g, " "); // Square brackets + title = title.replace(/^ *\{[^\}]*\} */g, " "); // Curly brackets // Japenis brackets - title = title.replace(/【[^】]*】/, "") // Touhou - title = title.replace(/《[^》]*》/, "") // ?? - title = title.replace(/「[^」]*」/, "") // OP/ED - title = title.replace(/『[^』]*』/, "") // OP/ED + title = title.replace(/^ *【[^】]*】/, "") // Touhou + title = title.replace(/^ *《[^》]*》/, "") // ?? + title = title.replace(/^ *「[^」]*」/, "") // OP/ED + title = title.replace(/^ *『[^』]*』/, "") // OP/ED return title; } From d0c5b92fb9e7d985f24dbada990fcf4bec40384c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 11:03:52 +0200 Subject: [PATCH 55/84] media controls: adjust layout --- .config/quickshell/modules/mediaControls/PlayerControl.qml | 1 - 1 file changed, 1 deletion(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 93cfa0587..72226f01f 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -203,7 +203,6 @@ Item { // Player instance bottom: parent.bottom left: parent.left right: parent.right - bottomMargin: 5 } TrackChangeButton { iconName: "skip_previous" From d9b4de45e677af9b1f7ec3f59cafab253715a234 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 11:25:49 +0200 Subject: [PATCH 56/84] ags: no more pywal --- .../ags/modules/indicators/musiccontrols.js | 15 +++++++++---- .../ags/scripts/templates/wal/_musicwal.scss | 22 +++++++++++++++++++ .config/ags/scss/_music.scss | 3 +++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 .config/ags/scripts/templates/wal/_musicwal.scss diff --git a/.config/ags/modules/indicators/musiccontrols.js b/.config/ags/modules/indicators/musiccontrols.js index 6f07c7f50..0613ada1b 100644 --- a/.config/ags/modules/indicators/musiccontrols.js +++ b/.config/ags/modules/indicators/musiccontrols.js @@ -22,8 +22,8 @@ var lastCoverPath = ''; function isRealPlayer(player) { return ( // Remove unecessary native buses from browsers if there's plasma integration - !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) && - !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) && + // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.firefox')) && + // !(hasPlasmaIntegration && player.busName.startsWith('org.mpris.MediaPlayer2.chromium')) && // playerctld just copies other buses and we don't need duplicates !player.busName.startsWith('org.mpris.MediaPlayer2.playerctld') && // Non-instance mpd bus @@ -209,8 +209,15 @@ const CoverArt = ({ player, ...rest }) => { execAsync(['bash', '-c', `${App.configDir}/scripts/color_generation/generate_colors_material.py --path '${coverPath}' --mode ${darkMode.value ? 'dark' : 'light'} > ${GLib.get_user_state_dir()}/ags/scss/_musicmaterial.scss`]) .then(() => { - exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`) - exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`); + const dominantColor = `#${Utils.exec(`sh -c "magick '${coverPath}' -scale 1x1\\! -format '%[fx:int(255*r+.5)],%[fx:int(255*g+.5)],%[fx:int(255*b+.5)]' info: | sed 's/,/\\n/g' | xargs -L 1 printf '%02x' ; echo"`)}` + console.log(dominantColor); + // exec(`${App.configDir}/scripts/color_generation/pywal.sh -i "${player.coverPath}" -n -t -s -e -q ${darkMode.value ? '' : '-l'}`) + // exec(`cp ${GLib.get_user_cache_dir()}/wal/colors.scss ${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss`); + exec(`cp '${App.configDir}/scripts/templates/wal/_musicwal.scss' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`); + exec(`sed -i 's/{{dominantColor}}/${dominantColor}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`) + exec(`sed -i 's/{{backgroundColor}}/${darkMode.value ? "#0E1415" : "#EEF4F4"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`) + exec(`sed -i 's/{{foregroundColor}}/${darkMode.value ? "#EEF4F4" : "#0E1415"}/g' '${GLib.get_user_state_dir()}/ags/scss/_musicwal.scss'`) + exec(`sass -I "${GLib.get_user_state_dir()}/ags/scss" -I "${App.configDir}/scss/fallback" "${App.configDir}/scss/_music.scss" "${stylePath}"`); Utils.timeout(200, () => { // self.attribute.showImage(self, coverPath) diff --git a/.config/ags/scripts/templates/wal/_musicwal.scss b/.config/ags/scripts/templates/wal/_musicwal.scss new file mode 100644 index 000000000..cad4df3f5 --- /dev/null +++ b/.config/ags/scripts/templates/wal/_musicwal.scss @@ -0,0 +1,22 @@ +// Special +$background: {{backgroundColor}}; +$foreground: {{foregroundColor}}; +$cursor: {{foregroundColor}}; + +// Colors +$color0: {{dominantColor}}; +$color1: {{dominantColor}}; +$color2: {{dominantColor}}; +$color3: {{dominantColor}}; +$color4: {{dominantColor}}; +$color5: {{dominantColor}}; +$color6: {{dominantColor}}; +$color7: {{dominantColor}}; +$color8: {{dominantColor}}; +$color9: {{dominantColor}}; +$color10: {{dominantColor}}; +$color11: {{dominantColor}}; +$color12: {{dominantColor}}; +$color13: {{dominantColor}}; +$color14: {{dominantColor}}; +$color15: {{dominantColor}}; \ No newline at end of file diff --git a/.config/ags/scss/_music.scss b/.config/ags/scss/_music.scss index 473a855e8..7450c5041 100644 --- a/.config/ags/scss/_music.scss +++ b/.config/ags/scss/_music.scss @@ -20,8 +20,11 @@ $secondaryContainer: transparentize(mix(mix($background, $color2, 50%), $color6, $onSecondaryContainer: mix($color7, $color2, 90%); @if $darkmode == False { $onSecondaryContainer: mix($onSecondaryContainer, black, 50%); +} @else { + $onSecondaryContainer: mix($onSecondaryContainer, white, 50%); } + .osd-music { @include menu_decel; @include elevation2; From 7db6df769bd9ae27982a7ab7328460989da60c6e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 12:19:16 +0200 Subject: [PATCH 57/84] rename global state for sidebar open tracker --- .config/quickshell/GlobalStates.qml | 4 ++-- .config/quickshell/modules/bar/Bar.qml | 4 ++-- .../modules/notificationPopup/NotificationPopup.qml | 2 +- .config/quickshell/modules/sidebarLeft/SidebarLeft.qml | 2 +- .config/quickshell/modules/sidebarRight/SidebarRight.qml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.config/quickshell/GlobalStates.qml b/.config/quickshell/GlobalStates.qml index 470dfc4f8..b9e3362a6 100644 --- a/.config/quickshell/GlobalStates.qml +++ b/.config/quickshell/GlobalStates.qml @@ -8,8 +8,8 @@ pragma ComponentBehavior: Bound Singleton { id: root - property int sidebarLeftOpenCount: 0 - property int sidebarRightOpenCount: 0 + property bool sidebarLeftOpen: false + property bool sidebarRightOpen: false property bool overviewOpen: false property bool workspaceShowNumbers: false property bool superReleaseMightTrigger: true diff --git a/.config/quickshell/modules/bar/Bar.qml b/.config/quickshell/modules/bar/Bar.qml index c3365dd0f..516fa8a32 100644 --- a/.config/quickshell/modules/bar/Bar.qml +++ b/.config/quickshell/modules/bar/Bar.qml @@ -127,7 +127,7 @@ Scope { // Layout.fillHeight: true radius: Appearance.rounding.full - color: (barLeftSideMouseArea.pressed || GlobalStates.sidebarLeftOpenCount > 0) ? Appearance.colors.colLayer1Active : barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent" + color: (barLeftSideMouseArea.pressed || GlobalStates.sidebarLeftOpen) ? Appearance.colors.colLayer1Active : barLeftSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent" implicitWidth: distroIcon.width + 5*2 implicitHeight: distroIcon.height + 5*2 @@ -301,7 +301,7 @@ Scope { Layout.fillHeight: true implicitWidth: indicatorsRowLayout.implicitWidth + 10*2 radius: Appearance.rounding.full - color: (barRightSideMouseArea.pressed || GlobalStates.sidebarRightOpenCount > 0) ? Appearance.colors.colLayer1Active : barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent" + color: (barRightSideMouseArea.pressed || GlobalStates.sidebarRightOpen) ? Appearance.colors.colLayer1Active : barRightSideMouseArea.hovered ? Appearance.colors.colLayer1Hover : "transparent" RowLayout { id: indicatorsRowLayout anchors.centerIn: parent diff --git a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml index f970a09c6..79e855c2f 100644 --- a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml +++ b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml @@ -43,7 +43,7 @@ Scope { Connections { target: Notifications function onNotify(notification) { - if (GlobalStates.sidebarRightOpenCount > 0) { + if (GlobalStates.sidebarRightOpen) { return } // notificationRepeater.model = [notification, ...notificationRepeater.model] diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 1a209e22f..20c1b79eb 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -21,7 +21,7 @@ Scope { // Scope id: sidebarLoader active: false onActiveChanged: { - GlobalStates.sidebarLeftOpenCount += active ? 1 : -1 + GlobalStates.sidebarLeftOpen = sidebarLoader.active } PanelWindow { // Window diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index f347b0698..8d9941847 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -21,7 +21,7 @@ Scope { id: sidebarLoader active: false onActiveChanged: { - GlobalStates.sidebarRightOpenCount += active ? 1 : -1 + GlobalStates.sidebarRightOpen = sidebarLoader.active } PanelWindow { From 715496079f13648a555c71f3fb295dc7eaedd45d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 22:25:39 +0200 Subject: [PATCH 58/84] allow video wallpaper --- .config/quickshell/scripts/switchwall.sh | 97 ++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 7 deletions(-) diff --git a/.config/quickshell/scripts/switchwall.sh b/.config/quickshell/scripts/switchwall.sh index 32fb7c8a2..38f2684a3 100755 --- a/.config/quickshell/scripts/switchwall.sh +++ b/.config/quickshell/scripts/switchwall.sh @@ -27,6 +27,47 @@ post_process() { fi } +THUMBNAIL_DIR="/tmp/mpvpaper_thumbnails" +CUSTOM_DIR="$XDG_CONFIG_HOME/hypr/custom" +RESTORE_SCRIPT_DIR="$CUSTOM_DIR/scripts" +RESTORE_SCRIPT="$RESTORE_SCRIPT_DIR/__restore_video_wallpaper.sh" +VIDEO_OPTS="no-audio loop hwdec=auto scale=bilinear interpolation=no video-sync=display-resample panscan=1.0 video-scale-x=1.0 video-scale-y=1.0 video-align-x=0.5 video-align-y=0.5" + +is_video() { + local extension="${1##*.}" + [[ "$extension" == "mp4" || "$extension" == "mkv" || "$extension" == "webm" ]] && return 0 || return 1 +} + +kill_existing_mpvpaper() { + pkill -f -9 mpvpaper || true +} + +create_restore_script() { + local video_path=$1 + cat > "$RESTORE_SCRIPT.tmp" << EOF +#!/bin/bash +# Generated by switchwall.sh - Don't modify it by yourself. +# Time: $(date) + +pkill -f -9 mpvpaper + +for monitor in \$(hyprctl monitors -j | jq -r '.[] | .name'); do + mpvpaper -o "$VIDEO_OPTS" "\$monitor" "$video_path" & + sleep 0.1 +done +EOF + mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT" + chmod +x "$RESTORE_SCRIPT" +} + +remove_restore() { + cat > "$RESTORE_SCRIPT.tmp" << EOF +#!/bin/bash +# The content of this script will be generated by switchwall.sh - Don't modify it by yourself. +EOF + mv "$RESTORE_SCRIPT.tmp" "$RESTORE_SCRIPT" +} + switch() { imgpath="$1" mode_flag="$2" @@ -48,8 +89,54 @@ switch() { echo 'Aborted' exit 0 fi - matugen_args=(image "$imgpath") - generate_colors_material_args=(--path "$imgpath") + + kill_existing_mpvpaper + + if is_video "$imgpath"; then + mkdir -p "$THUMBNAIL_DIR" + + missing_deps=() + if ! command -v mpvpaper &> /dev/null; then + missing_deps+=("mpvpaper") + fi + if ! command -v ffmpeg &> /dev/null; then + missing_deps+=("ffmpeg") + fi + if [ ${#missing_deps[@]} -gt 0 ]; then + echo "Missing deps: ${missing_deps[*]}" + echo "Arch: yay -S ${missing_deps[*]}" + exit 0 + fi + + local video_path="$imgpath" + monitors=$(hyprctl monitors -j | jq -r '.[] | .name') + for monitor in $monitors; do + mpvpaper -o "$VIDEO_OPTS" "$monitor" "$video_path" & + sleep 0.1 + done + + # Extract first frame for color generation + thumbnail="$THUMBNAIL_DIR/$(basename "$imgpath").jpg" + ffmpeg -y -i "$imgpath" -vframes 1 "$thumbnail" 2>/dev/null + + if [ -f "$thumbnail" ]; then + matugen_args=(image "$thumbnail") + generate_colors_material_args=(--path "$thumbnail") + create_restore_script "$video_path" + else + echo "Cannot create image to colorgen" + remove_restore + exit 1 + fi + else + matugen_args=(image "$imgpath") + generate_colors_material_args=(--path "$imgpath") + # Set wallpaper with swww + swww img "$imgpath" --transition-step 100 --transition-fps 120 \ + --transition-type grow --transition-angle 30 --transition-duration 1 \ + --transition-pos "$cursorposx, $cursorposy_inverted" + remove_restore + fi fi # Determine mode if not set @@ -62,18 +149,14 @@ switch() { fi fi - # Dark/light mode, material scheme [[ -n "$mode_flag" ]] && matugen_args+=(--mode "$mode_flag") && generate_colors_material_args+=(--mode "$mode_flag") [[ -n "$type_flag" ]] && matugen_args+=(--type "$type_flag") && generate_colors_material_args+=(--scheme "$type_flag") - # Terminal scheme generate_colors_material_args+=(--termscheme "$terminalscheme" --blend_bg_fg) generate_colors_material_args+=(--cache "$STATE_DIR/user/color.txt") - + pre_process - # Generate with matugen matugen "${matugen_args[@]}" - # Use custom script for mixing (matugen can't D:) source "$(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate" python "$SCRIPT_DIR/generate_colors_material.py" "${generate_colors_material_args[@]}" \ > "$STATE_DIR"/user/generated/material_colors.scss From 80661d4f8615bd581a769aecf87227dcd1395773 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 22:40:06 +0200 Subject: [PATCH 59/84] wallpaper switcher: prompt video dep installation thru notif --- .config/quickshell/scripts/switchwall.sh | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/scripts/switchwall.sh b/.config/quickshell/scripts/switchwall.sh index 38f2684a3..f04ef44d5 100755 --- a/.config/quickshell/scripts/switchwall.sh +++ b/.config/quickshell/scripts/switchwall.sh @@ -104,7 +104,19 @@ switch() { fi if [ ${#missing_deps[@]} -gt 0 ]; then echo "Missing deps: ${missing_deps[*]}" - echo "Arch: yay -S ${missing_deps[*]}" + echo "Arch: sudo pacman -S ${missing_deps[*]}" + action=$(notify-send \ + -a "Wallpaper Switcher" \ + -c "im.error" \ + -A "install_arch=Install (Arch)" \ + "Can't switch to video wallpaper" \ + "Missing dependencies: ${missing_deps[*]}") + if [[ "$action" == "install_arch" ]]; then + foot sudo pacman -S "${missing_deps[*]}" + if command -v mpvpaper &>/dev/null && command -v ffmpeg &>/dev/null; then + notify-send 'Wallpaper switcher' 'Alright, try again!' + fi + fi exit 0 fi From 14f7542a21003cc3e4a21a726e05a499dab3d9d7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:13:17 +0200 Subject: [PATCH 60/84] move color funcs to their own file instead of appearance --- .../quickshell/modules/bar/SysTrayItem.qml | 3 +- .../modules/cheatsheet/Cheatsheet.qml | 3 +- .../quickshell/modules/common/Appearance.qml | 114 ++++----------- .../modules/common/functions/color_utils.js | 136 ++++++++++++++++++ .../modules/common/widgets/DialogButton.qml | 5 +- .../modules/common/widgets/MenuButton.qml | 7 +- .../modules/common/widgets/NavRailButton.qml | 3 +- .../common/widgets/NotificationWidget.qml | 7 +- .../common/widgets/PrimaryTabButton.qml | 3 +- .../common/widgets/SecondaryTabButton.qml | 3 +- .../modules/mediaControls/PlayerControl.qml | 35 ++--- .../modules/overview/OverviewWidget.qml | 3 +- .../modules/overview/OverviewWindow.qml | 3 +- .../modules/overview/SearchItem.qml | 5 +- .../quickshell/modules/session/Session.qml | 3 +- .../aiChat/AiMessageControlButton.qml | 5 +- .../sidebarLeft/aiChat/MessageThinkBlock.qml | 3 +- .../modules/sidebarLeft/anime/BooruImage.qml | 7 +- .../calendar/CalendarDayButton.qml | 3 +- .../quickToggles/QuickToggleButton.qml | 3 +- .../todo/TodoItemActionButton.qml | 3 +- .../modules/sidebarRight/todo/TodoWidget.qml | 3 +- 22 files changed, 229 insertions(+), 131 deletions(-) create mode 100644 .config/quickshell/modules/common/functions/color_utils.js diff --git a/.config/quickshell/modules/bar/SysTrayItem.qml b/.config/quickshell/modules/bar/SysTrayItem.qml index 6bef27ad3..900ef68f4 100644 --- a/.config/quickshell/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/modules/bar/SysTrayItem.qml @@ -1,4 +1,5 @@ import "root:/modules/common/" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Layouts import Quickshell @@ -57,7 +58,7 @@ MouseArea { ColorOverlay { anchors.fill: desaturatedIcon source: desaturatedIcon - color: Appearance.transparentize(Appearance.colors.colOnLayer0, 0.6) + color: ColorUtils.transparentize(Appearance.colors.colOnLayer0, 0.6) } } diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 91a46852e..eeeb4a692 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -93,7 +94,7 @@ Scope { // Scope radius: Appearance.rounding.full color: closeButton.pressed ? Appearance.colors.colLayer0Active : closeButton.hovered ? Appearance.colors.colLayer0Hover : - Appearance.transparentize(Appearance.colors.colLayer0, 1) + ColorUtils.transparentize(Appearance.colors.colLayer0, 1) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index be23fc2d9..0c26b342a 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -1,5 +1,6 @@ import QtQuick import Quickshell +import "root:/modules/common/functions/color_utils.js" as ColorUtils pragma Singleton pragma ComponentBehavior: Bound @@ -14,69 +15,6 @@ Singleton { property QtObject sizes property string syntaxHighlightingTheme - function colorWithHueOf(color1, color2) { - // Convert both colors to HSV - var c1 = Qt.color(color1); - var c2 = Qt.color(color2); - - // Helper to convert RGB to HSV - function rgb2hsv(c) { - var r = c.r, g = c.g, b = c.b; - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - var d = max - min; - s = max === 0 ? 0 : d / max; - if (max === min) { - h = 0; - } else { - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return {h: h, s: s, v: v, a: c.a}; - } - - // Helper to convert HSV to RGB - function hsv2rgb(h, s, v, a) { - var r, g, b; - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - switch(i % 6){ - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; - } - return Qt.rgba(r, g, b, a); - } - - var hsv1 = rgb2hsv(c1); - var hsv2 = rgb2hsv(c2); - - // Use hue from color2, saturation/value/alpha from color1 - return hsv2rgb(hsv2.h, hsv1.s, hsv1.v, hsv1.a); - } - - function mix(color1, color2, percentage) { - 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); - } - - // Transparentize - function transparentize(color, percentage) { - var c = Qt.color(color); - return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage)); - } - m3colors: QtObject { property bool darkmode: false property bool transparent: false @@ -160,37 +98,37 @@ Singleton { property color colSubtext: m3colors.m3outline property color colLayer0: m3colors.m3background property color colOnLayer0: m3colors.m3onBackground - property color colLayer0Hover: mix(colLayer0, colOnLayer0, 0.9) - property color colLayer0Active: mix(colLayer0, colOnLayer0, 0.8) + property color colLayer0Hover: ColorUtils.mix(colLayer0, colOnLayer0, 0.9) + property color colLayer0Active: ColorUtils.mix(colLayer0, colOnLayer0, 0.8) property color colLayer1: m3colors.m3surfaceContainerLow; property color colOnLayer1: m3colors.m3onSurfaceVariant; - property color colOnLayer1Inactive: mix(colOnLayer1, colLayer1, 0.45); - property color colLayer2: mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55); + property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45); + property color colLayer2: ColorUtils.mix(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 0.55); property color colOnLayer2: m3colors.m3onSurface; - property color colOnLayer2Disabled: mix(colOnLayer2, m3colors.m3background, 0.4); - property color colLayer3: mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96); + property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4); + property color colLayer3: ColorUtils.mix(m3colors.m3surfaceContainerHigh, m3colors.m3onSurface, 0.96); property color colOnLayer3: m3colors.m3onSurface; - property color colLayer1Hover: mix(colLayer1, colOnLayer1, 0.92); - property color colLayer1Active: mix(colLayer1, colOnLayer1, 0.85); - property color colLayer2Hover: mix(colLayer2, colOnLayer2, 0.90); - property color colLayer2Active: mix(colLayer2, colOnLayer2, 0.80); - property color colLayer2Disabled: mix(colLayer2, m3colors.m3background, 0.8); - property color colLayer3Hover: mix(colLayer3, colOnLayer3, 0.90); - property color colLayer3Active: mix(colLayer3, colOnLayer3, 0.80); - property color colPrimaryHover: mix(m3colors.m3primary, colLayer1Hover, 0.85) - property color colPrimaryActive: mix(m3colors.m3primary, colLayer1Active, 0.7) - property color colPrimaryContainerHover: mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7) - property color colPrimaryContainerActive: mix(m3colors.m3primaryContainer, colLayer1Active, 0.6) - property color colSecondaryHover: mix(m3colors.m3secondary, colLayer1Hover, 0.85) - property color colSecondaryActive: mix(m3colors.m3secondary, colLayer1Active, 0.4) - property color colSecondaryContainerHover: mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6) - property color colSecondaryContainerActive: mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54) - property color colSurfaceContainerHighestHover: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95) - property color colSurfaceContainerHighestActive: mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85) + property color colLayer1Hover: ColorUtils.mix(colLayer1, colOnLayer1, 0.92); + property color colLayer1Active: ColorUtils.mix(colLayer1, colOnLayer1, 0.85); + property color colLayer2Hover: ColorUtils.mix(colLayer2, colOnLayer2, 0.90); + property color colLayer2Active: ColorUtils.mix(colLayer2, colOnLayer2, 0.80); + property color colLayer2Disabled: ColorUtils.mix(colLayer2, m3colors.m3background, 0.8); + property color colLayer3Hover: ColorUtils.mix(colLayer3, colOnLayer3, 0.90); + property color colLayer3Active: ColorUtils.mix(colLayer3, colOnLayer3, 0.80); + property color colPrimaryHover: ColorUtils.mix(m3colors.m3primary, colLayer1Hover, 0.85) + property color colPrimaryActive: ColorUtils.mix(m3colors.m3primary, colLayer1Active, 0.7) + property color colPrimaryContainerHover: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Hover, 0.7) + property color colPrimaryContainerActive: ColorUtils.mix(m3colors.m3primaryContainer, colLayer1Active, 0.6) + property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85) + property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4) + property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Hover, 0.6) + property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54) + property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95) + property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85) property color colTooltip: "#3C4043" // m3colors.m3inverseSurface in the specs, but the m3 website actually uses this color property color colOnTooltip: "#F8F9FA" // m3colors.m3inverseOnSurface in the specs, but the m3 website actually uses this color - property color colScrim: transparentize(m3colors.m3scrim, 0.5) - property color colShadow: transparentize(m3colors.m3shadow, 0.75) + property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5) + property color colShadow: ColorUtils.transparentize(m3colors.m3shadow, 0.75) } rounding: QtObject { diff --git a/.config/quickshell/modules/common/functions/color_utils.js b/.config/quickshell/modules/common/functions/color_utils.js new file mode 100644 index 000000000..9fcce23ef --- /dev/null +++ b/.config/quickshell/modules/common/functions/color_utils.js @@ -0,0 +1,136 @@ +// Utility functions for color manipulation. + +/** + * Converts an RGB color object to HSV color space. + * + * @param {{r: number, g: number, b: number, a: number}} c - The color object with r, g, b, a properties (0-1). + * @returns {{h: number, s: number, v: number, a: number}} The HSV representation with alpha. + */ +function rgb2hsv(c) { + var r = c.r, g = c.g, b = c.b; + var max = Math.max(r, g, b), min = Math.min(r, g, b); + var h, s, v = max; + var d = max - min; + s = max === 0 ? 0 : d / max; + if (max === min) { + h = 0; + } else { + switch (max) { + case r: h = (g - b) / d + (g < b ? 6 : 0); break; + case g: h = (b - r) / d + 2; break; + case b: h = (r - g) / d + 4; break; + } + h /= 6; + } + return {h: h, s: s, v: v, a: c.a}; +} + +/** + * Converts an HSV color value to an RGBA color. + * + * @param {number} h - Hue component (0-1). + * @param {number} s - Saturation component (0-1). + * @param {number} v - Value component (0-1). + * @param {number} a - Alpha component (0-1). + * @returns {Qt.rgba} The resulting color as a Qt.rgba object. + */ +function hsv2rgb(h, s, v, a) { + var r, g, b; + var i = Math.floor(h * 6); + var f = h * 6 - i; + var p = v * (1 - s); + var q = v * (1 - f * s); + var t = v * (1 - (1 - f) * s); + switch(i % 6){ + case 0: r = v, g = t, b = p; break; + case 1: r = q, g = v, b = p; break; + case 2: r = p, g = v, b = t; break; + case 3: r = p, g = q, b = v; break; + case 4: r = t, g = p, b = v; break; + case 5: r = v, g = p, b = q; break; + } + return Qt.rgba(r, g, b, a); +} + +/** + * Returns a color with the hue of color2 and the saturation, value, and alpha of color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The color to take hue from. + * @returns {Qt.rgba} The resulting color. + */ +function colorWithHueOf(color1, color2) { + // Convert both colors to HSV + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + var hsv1 = rgb2hsv(c1); + var hsv2 = rgb2hsv(c2); + + // Use hue from color2, saturation/value/alpha from color1 + return hsv2rgb(hsv2.h, hsv1.s, hsv1.v, hsv1.a); +} + +/** + * Returns a color with the saturation of color2 and the hue/value/alpha of color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The color to take saturation from. + * @returns {Qt.rgba} The resulting color. + */ +function colorWithSaturationOf(color1, color2) { + // Convert both colors to HSV + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + var hsv1 = rgb2hsv(c1); + var hsv2 = rgb2hsv(c2); + + // Use hue from color2, saturation/value/alpha from color1 + return hsv2rgb(hsv1.h, hsv2.s, hsv1.v, hsv1.a); +} + +/** + * Adapts color1 to the accent (hue and saturation) of color2, keeping value and alpha from color1. + * + * @param {string} color1 - The base color (any Qt.color-compatible string). + * @param {string} color2 - The accent color. + * @returns {Qt.rgba} The resulting color. + */ +function adaptToAccent(color1, color2) { + // Convert both colors to HSV + var c1 = Qt.color(color1); + var c2 = Qt.color(color2); + + var hsv1 = rgb2hsv(c1); + var hsv2 = rgb2hsv(c2); + + // Use hue from color2, saturation/value/alpha from color1 + return hsv2rgb(hsv2.h, hsv2.s, hsv1.v, hsv1.a); +} + +/** + * Mixes two colors by a given percentage. + * + * @param {string} color1 - The first color (any Qt.color-compatible string). + * @param {string} color2 - The second color. + * @param {number} percentage - The mix ratio (0-1). 1 = all color1, 0 = all color2. + * @returns {Qt.rgba} The resulting mixed color. + */ +function mix(color1, color2, percentage) { + 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); +} + +/** + * Transparentizes a color by a given percentage. + * + * @param {string} color - The color (any Qt.color-compatible string). + * @param {number} percentage - The amount to transparentize (0-1). + * @returns {Qt.rgba} The resulting color. + */ +function transparentize(color, percentage) { + var c = Qt.color(color); + return Qt.rgba(c.r, c.g, c.b, c.a * (1 - percentage)); +} diff --git a/.config/quickshell/modules/common/widgets/DialogButton.qml b/.config/quickshell/modules/common/widgets/DialogButton.qml index 2939c81c6..c3ae39397 100644 --- a/.config/quickshell/modules/common/widgets/DialogButton.qml +++ b/.config/quickshell/modules/common/widgets/DialogButton.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -17,7 +18,9 @@ Button { background: Rectangle { anchors.fill: parent radius: Appearance.rounding.full - color: (button.down && button.enabled) ? Appearance.colors.colLayer1Active : ((button.hovered && button.enabled) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) + color: (button.down && button.enabled) ? Appearance.colors.colLayer1Active : + ((button.hovered && button.enabled) ? Appearance.colors.colLayer1Hover : + ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/common/widgets/MenuButton.qml b/.config/quickshell/modules/common/widgets/MenuButton.qml index f96ed3f67..f44c4c099 100644 --- a/.config/quickshell/modules/common/widgets/MenuButton.qml +++ b/.config/quickshell/modules/common/widgets/MenuButton.qml @@ -1,4 +1,5 @@ import "root:/modules/common" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -16,9 +17,9 @@ Button { background: Rectangle { anchors.fill: parent - color: (button.down && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.84) : - ((button.hovered && button.enabled) ? Appearance.transparentize(Appearance.m3colors.m3onSurface, 0.92) : - Appearance.transparentize(Appearance.m3colors.m3onSurface, 1)) + color: (button.down && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.84) : + ((button.hovered && button.enabled) ? ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 0.92) : + ColorUtils.transparentize(Appearance.m3colors.m3onSurface, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/common/widgets/NavRailButton.qml b/.config/quickshell/modules/common/widgets/NavRailButton.qml index 61972e165..1f55ad282 100644 --- a/.config/quickshell/modules/common/widgets/NavRailButton.qml +++ b/.config/quickshell/modules/common/widgets/NavRailButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -30,7 +31,7 @@ Button { radius: Appearance.rounding.full color: toggled ? (button.down ? Appearance.colors.colSecondaryContainerActive : button.hovered ? Appearance.colors.colSecondaryContainerHover : Appearance.m3colors.m3secondaryContainer) : - (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/common/widgets/NotificationWidget.qml b/.config/quickshell/modules/common/widgets/NotificationWidget.qml index bc472f318..a99fd1bf0 100644 --- a/.config/quickshell/modules/common/widgets/NotificationWidget.qml +++ b/.config/quickshell/modules/common/widgets/NotificationWidget.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/services" import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -187,7 +188,7 @@ Item { height: notificationColumnLayout.implicitHeight color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2 + ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2 radius: Appearance.rounding.normal Behavior on x { @@ -289,7 +290,7 @@ Item { } anchors.fill: parent color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) : + ColorUtils.mix(Appearance.m3colors.m3onSecondary, Appearance.m3colors.m3onSecondaryContainer, 0.1) : Appearance.m3colors.m3onSecondaryContainer iconSize: 27 horizontalAlignment: Text.AlignHCenter @@ -422,7 +423,7 @@ Item { background: Rectangle { anchors.fill: parent radius: Appearance.rounding.full - color: (expandButton.down) ? Appearance.colors.colLayer2Active : (expandButton.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1)) + color: (expandButton.down) ? Appearance.colors.colLayer2Active : (expandButton.hovered ? Appearance.colors.colLayer2Hover : ColorUtils.transparentize(Appearance.colors.colLayer2, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml index 125b6d9e2..58424032c 100644 --- a/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/PrimaryTabButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -87,7 +88,7 @@ TabButton { id: buttonBackground radius: Appearance.rounding.small implicitHeight: 50 - color: (button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + color: (button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)) layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { diff --git a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml index 2244ff42a..6f87e719d 100644 --- a/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml +++ b/.config/quickshell/modules/common/widgets/SecondaryTabButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls @@ -89,7 +90,7 @@ TabButton { id: buttonBackground radius: Appearance.rounding.small implicitHeight: 37 - color: (button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + color: (button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)) layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 72226f01f..ae199bc22 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -2,6 +2,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Layouts @@ -18,7 +19,7 @@ Item { // Player instance required property MprisPlayer player // property var artUrl: player?.metadata["xesam:url"] || player?.metadata["mpris:artUrl"] || player?.trackArtUrl property var artUrl: player?.trackArtUrl - property color artDominantColor: Appearance.m3colors.m3primaryFixed + property color artDominantColor: Appearance.m3colors.m3secondaryContainer implicitWidth: widgetWidth implicitHeight: widgetHeight @@ -34,7 +35,7 @@ Item { // Player instance background: Rectangle { color: playPauseButton.pressed ? blendedColors.colSecondaryContainerActive : playPauseButton.hovered ? blendedColors.colSecondaryContainerHover : - Appearance.transparentize(blendedColors.colSecondaryContainer, 1) + ColorUtils.transparentize(blendedColors.colSecondaryContainer, 1) radius: Appearance.rounding.full Behavior on color { @@ -83,19 +84,19 @@ Item { // Player instance } property QtObject blendedColors: QtObject { - property color colLayer0: Appearance.mix(Appearance.colors.colLayer0, artDominantColor, 0.6) - property color colLayer1: Appearance.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) - property color colOnLayer0: Appearance.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7) - property color colOnLayer1: Appearance.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) - property color colSubtext: Appearance.mix(Appearance.colors.colSubtext, artDominantColor, 0.5) - property color colPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5) - property color colPrimaryHover: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) - property color colPrimaryActive: Appearance.mix(Appearance.colorWithHueOf(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) - property color colSecondaryContainer: Appearance.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.3) - property color colSecondaryContainerHover: Appearance.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) - property color colSecondaryContainerActive: Appearance.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3) - property color colOnPrimary: Appearance.mix(Appearance.colorWithHueOf(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5) - property color colOnSecondaryContainer: Appearance.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2) + property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, 0.6) + property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) + property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7) + property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) + property color colSubtext: ColorUtils.mix(Appearance.colors.colSubtext, artDominantColor, 0.5) + property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5) + property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) + property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) + property color colSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, artDominantColor, 0.3) + property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) + property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.3) + property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5) + property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.2) } @@ -119,8 +120,8 @@ Item { // Player instance start: Qt.point(0, 0) end: Qt.point(background.width, background.height) gradient: Gradient { - GradientStop { position: 0.0; color: Appearance.transparentize(artDominantColor, 0.6) } - GradientStop { position: 0.4; color: Appearance.transparentize(artDominantColor, 0.8) } + GradientStop { position: 0.0; color: ColorUtils.transparentize(artDominantColor, 0.6) } + GradientStop { position: 0.4; color: ColorUtils.transparentize(artDominantColor, 0.8) } } } diff --git a/.config/quickshell/modules/overview/OverviewWidget.qml b/.config/quickshell/modules/overview/OverviewWidget.qml index 0b00fcb20..4de7beed6 100644 --- a/.config/quickshell/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/modules/overview/OverviewWidget.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Layouts @@ -78,7 +79,7 @@ Item { property int colIndex: index property int workspaceValue: root.workspaceGroup * workspacesShown + rowIndex * ConfigOptions.overview.numOfCols + colIndex + 1 property color defaultWorkspaceColor: Appearance.colors.colLayer1 // TODO: reconsider this color for a cleaner look - property color hoveredWorkspaceColor: Appearance.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) + property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) property color hoveredBorderColor: Appearance.colors.colLayer2Hover property color activeBorderColor: Appearance.m3colors.m3secondary property bool hoveredWhileDragging: false diff --git a/.config/quickshell/modules/overview/OverviewWindow.qml b/.config/quickshell/modules/overview/OverviewWindow.qml index 0d6990eaf..67faa9628 100644 --- a/.config/quickshell/modules/overview/OverviewWindow.qml +++ b/.config/quickshell/modules/overview/OverviewWindow.qml @@ -2,6 +2,7 @@ import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/icons.js" as Icons +import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Layouts @@ -42,7 +43,7 @@ Rectangle { // Window radius: Appearance.rounding.windowRounding * root.scale color: pressed ? Appearance.colors.colLayer2Active : hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2 - border.color : Appearance.transparentize(Appearance.m3colors.m3outline, 0.9) + border.color : ColorUtils.transparentize(Appearance.m3colors.m3outline, 0.9) border.pixelAligned : false border.width : 1 diff --git a/.config/quickshell/modules/overview/SearchItem.qml b/.config/quickshell/modules/overview/SearchItem.qml index ab6aaf8c5..1eb5bcac5 100644 --- a/.config/quickshell/modules/overview/SearchItem.qml +++ b/.config/quickshell/modules/overview/SearchItem.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -57,7 +58,9 @@ Button { anchors.leftMargin: root.horizontalMargin anchors.rightMargin: root.horizontalMargin radius: Appearance.rounding.normal - color: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) + color: (root.down || root.keyboardDown) ? Appearance.colors.colLayer1Active : + ((root.hovered || root.focus) ? Appearance.colors.colLayer1Hover : + ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHigh, 1)) } RowLayout { diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index 0225587ca..f5ed73588 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -31,7 +32,7 @@ Scope { WlrLayershell.namespace: "quickshell:session" WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - color: Appearance.transparentize(Appearance.m3colors.m3background, 0.3) + color: ColorUtils.transparentize(Appearance.m3colors.m3background, 0.3) anchors { top: true diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml index 3b34c6e82..96b967538 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessageControlButton.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -18,13 +19,13 @@ Button { background: Rectangle { radius: Appearance.rounding.small - color: !button.enabled ? Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1) : + color: !button.enabled ? ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1) : button.activated ? (button.down ? Appearance.colors.colPrimaryActive : button.hovered ? Appearance.colors.colPrimaryHover : Appearance.m3colors.m3primary) : (button.down ? Appearance.colors.colSurfaceContainerHighestActive : button.hovered ? Appearance.colors.colSurfaceContainerHighestHover : - Appearance.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1)) + ColorUtils.transparentize(Appearance.m3colors.m3surfaceContainerHighest, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml index f2f6b35d3..3f8f620b5 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml @@ -6,6 +6,7 @@ import "root:/modules/common/" import "root:/modules/common/widgets" import "../" import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -117,7 +118,7 @@ Item { radius: Appearance.rounding.full color: (headerMouseArea.pressed) ? Appearance.colors.colLayer2Active : (headerMouseArea.containsMouse ? Appearance.colors.colLayer2Hover - : Appearance.transparentize(Appearance.colors.colLayer2, 1)) + : ColorUtils.transparentize(Appearance.colors.colLayer2, 1)) Behavior on color { ColorAnimation { diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml index 55e5361db..c36a52ca5 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQml import Qt.labs.platform import QtQuick @@ -97,9 +98,9 @@ Button { } background: Rectangle { - color: menuButton.down ? Appearance.transparentize(Appearance.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.6), 0.1) : - menuButton.hovered ? Appearance.transparentize(Appearance.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.8), 0.2) : - Appearance.transparentize(Appearance.m3colors.m3surface, 0.3) + color: menuButton.down ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.6), 0.1) : + menuButton.hovered ? ColorUtils.transparentize(ColorUtils.mix(Appearance.m3colors.m3surface, Appearance.m3colors.m3onSurface, 0.8), 0.2) : + ColorUtils.transparentize(Appearance.m3colors.m3surface, 0.3) radius: Appearance.rounding.full } diff --git a/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml b/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml index 4d3585000..041c64a7a 100644 --- a/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml +++ b/.config/quickshell/modules/sidebarRight/calendar/CalendarDayButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -24,7 +25,7 @@ Button { Appearance.m3colors.m3primary) : (interactable && button.down) ? Appearance.colors.colLayer1Active : (interactable && button.hovered) ? Appearance.colors.colLayer1Hover : - Appearance.transparentize(Appearance.colors.colLayer1, 1) + ColorUtils.transparentize(Appearance.colors.colLayer1, 1) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml b/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml index e764494b5..0bb94cffb 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/QuickToggleButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import Quickshell.Io @@ -20,7 +21,7 @@ Button { radius: Appearance.rounding.full color: toggled ? (button.down ? Appearance.colors.colPrimaryActive : button.hovered ? Appearance.colors.colPrimaryHover : Appearance.m3colors.m3primary) : - (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : Appearance.transparentize(Appearance.colors.colLayer1Hover, 1)) + (button.down ? Appearance.colors.colLayer1Active : button.hovered ? Appearance.colors.colLayer1Hover : ColorUtils.transparentize(Appearance.colors.colLayer1Hover, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml b/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml index 69e0882db..1b1881776 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoItemActionButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -23,7 +24,7 @@ Button { background: Rectangle { anchors.fill: parent radius: Appearance.rounding.full - color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.transparentize(Appearance.colors.colLayer2, 1)) + color: (button.down) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : ColorUtils.transparentize(Appearance.colors.colLayer2, 1)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index a480b2dbc..b18e03fb7 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -1,6 +1,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/services" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -182,7 +183,7 @@ Item { verticalOffset: fabButton.hovered ? 4 : 2 radius: fabButton.hovered ? Appearance.sizes.fabHoveredShadowRadius : Appearance.sizes.fabShadowRadius samples: fabShadow.radius * 2 + 1 - color: Appearance.transparentize(Appearance.m3colors.m3shadow, 0.55) + color: Appearance.colors.colShadow z: fabBackground.z - 1 Behavior on verticalOffset { From 2da3d80a1445f17cfc7f326fa6b4b66b4fe0ded9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:16:04 +0200 Subject: [PATCH 61/84] sidebars: no more rememnber last used tab works inconsistently because of uhm swipeviews also it's weird... ai is more useful than anime grills and notifs are more useful than mixer imo... --- .../modules/common/PersistentStates.qml | 6 ------ .../modules/sidebarLeft/SidebarLeft.qml | 17 +++++++++-------- .../modules/sidebarRight/CenterWidgetGroup.qml | 18 +++++++----------- 3 files changed, 16 insertions(+), 25 deletions(-) diff --git a/.config/quickshell/modules/common/PersistentStates.qml b/.config/quickshell/modules/common/PersistentStates.qml index 4c9a7608c..cbf52e8e4 100644 --- a/.config/quickshell/modules/common/PersistentStates.qml +++ b/.config/quickshell/modules/common/PersistentStates.qml @@ -9,12 +9,6 @@ Singleton { } property QtObject sidebar: QtObject { - property QtObject leftSide: QtObject { - property int selectedTab: 0 - } - property QtObject centerGroup: QtObject { - property int selectedTab: 0 - } property QtObject bottomGroup: QtObject { property bool collapsed: false property int selectedTab: 0 diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 20c1b79eb..4c60bdac7 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -28,7 +28,7 @@ Scope { // Scope id: sidebarRoot visible: sidebarLoader.active - property int selectedTab: PersistentStates.sidebar.leftSide.selectedTab + property int selectedTab: 0 property bool extend: false property bool pin: false property real sidebarWidth: sidebarRoot.extend ? Appearance.sizes.sidebarWidthExtended : Appearance.sizes.sidebarWidth @@ -88,16 +88,16 @@ Scope { // Scope } if (event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_PageDown) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1)) + sidebarRoot.selectedTab = Math.min(sidebarRoot.selectedTab + 1, root.tabButtonList.length - 1) } else if (event.key === Qt.Key_PageUp) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", Math.max(sidebarRoot.selectedTab - 1, 0)) + sidebarRoot.selectedTab = Math.max(sidebarRoot.selectedTab - 1, 0) } else if (event.key === Qt.Key_Tab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab + 1) % root.tabButtonList.length); + sidebarRoot.selectedTab = (sidebarRoot.selectedTab + 1) % root.tabButtonList.length; } else if (event.key === Qt.Key_Backtab) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); + sidebarRoot.selectedTab = (sidebarRoot.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length; } else if (event.key === Qt.Key_O) { sidebarRoot.extend = !sidebarRoot.extend; @@ -120,7 +120,7 @@ Scope { // Scope tabButtonList: root.tabButtonList externalTrackedTab: sidebarRoot.selectedTab function onCurrentIndexChanged(currentIndex) { - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) + sidebarRoot.selectedTab = currentIndex } } @@ -130,10 +130,11 @@ Scope { // Scope Layout.fillWidth: true Layout.fillHeight: true spacing: 10 - currentIndex: sidebarRoot.selectedTab + + currentIndex: tabBar.externalTrackedTab onCurrentIndexChanged: { tabBar.enableIndicatorAnimation = true - PersistentStateManager.setState("sidebar.leftSide.selectedTab", currentIndex) + sidebarRoot.selectedTab = currentIndex } clip: true diff --git a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml index bb871993a..1b426da43 100644 --- a/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml +++ b/.config/quickshell/modules/sidebarRight/CenterWidgetGroup.qml @@ -16,27 +16,23 @@ Rectangle { radius: Appearance.rounding.normal color: Appearance.colors.colLayer1 - property int selectedTab: PersistentStates.sidebar.centerGroup.selectedTab + property int selectedTab: 0 property var tabButtonList: [{"icon": "notifications", "name": qsTr("Notifications")}, {"icon": "volume_up", "name": qsTr("Volume mixer")}] - onSelectedTabChanged: { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", selectedTab) - } - Keys.onPressed: (event) => { if (event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) { if (event.key === Qt.Key_PageDown) { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", Math.min(root.selectedTab + 1, root.tabButtonList.length - 1)) + root.selectedTab = Math.min(root.selectedTab + 1, root.tabButtonList.length - 1) } else if (event.key === Qt.Key_PageUp) { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", Math.max(root.selectedTab - 1, 0)) + root.selectedTab = Math.max(root.selectedTab - 1, 0) } event.accepted = true; } if (event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_Tab) { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", (root.selectedTab + 1) % root.tabButtonList.length); + root.selectedTab = (root.selectedTab + 1) % root.tabButtonList.length } else if (event.key === Qt.Key_Backtab) { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length); + root.selectedTab = (root.selectedTab - 1 + root.tabButtonList.length) % root.tabButtonList.length } event.accepted = true; } @@ -53,7 +49,7 @@ Rectangle { externalTrackedTab: root.selectedTab function onCurrentIndexChanged(currentIndex) { - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex) + root.selectedTab = currentIndex } } @@ -66,7 +62,7 @@ Rectangle { currentIndex: root.selectedTab onCurrentIndexChanged: { tabBar.enableIndicatorAnimation = true - PersistentStateManager.setState("sidebar.centerGroup.selectedTab", currentIndex) + root.selectedTab = currentIndex } clip: true From 912422bca5d24eb988c8a02637fd86733987b16f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:27:32 +0200 Subject: [PATCH 62/84] player control: default to accent when there's no cover --- .config/quickshell/modules/mediaControls/PlayerControl.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index ae199bc22..8302440df 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -66,7 +66,10 @@ Item { // Player instance } onArtUrlChanged: { - if (playerController.artUrl.length == 0) return; + if (playerController.artUrl.length == 0) { + playerController.artDominantColor = Appearance.m3colors.m3secondaryContainer + return; + } colorQuantizer.targetFile = playerController.artUrl // Yes this binding break is intentional colorQuantizer.running = true } From 2475a64bdc0ec4857f489dc6df86f44c4597d096 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:27:57 +0200 Subject: [PATCH 63/84] color utils: use qt's native color funcs --- .../modules/common/functions/color_utils.js | 75 +++---------------- 1 file changed, 12 insertions(+), 63 deletions(-) diff --git a/.config/quickshell/modules/common/functions/color_utils.js b/.config/quickshell/modules/common/functions/color_utils.js index 9fcce23ef..82653ed21 100644 --- a/.config/quickshell/modules/common/functions/color_utils.js +++ b/.config/quickshell/modules/common/functions/color_utils.js @@ -1,56 +1,4 @@ -// Utility functions for color manipulation. - -/** - * Converts an RGB color object to HSV color space. - * - * @param {{r: number, g: number, b: number, a: number}} c - The color object with r, g, b, a properties (0-1). - * @returns {{h: number, s: number, v: number, a: number}} The HSV representation with alpha. - */ -function rgb2hsv(c) { - var r = c.r, g = c.g, b = c.b; - var max = Math.max(r, g, b), min = Math.min(r, g, b); - var h, s, v = max; - var d = max - min; - s = max === 0 ? 0 : d / max; - if (max === min) { - h = 0; - } else { - switch (max) { - case r: h = (g - b) / d + (g < b ? 6 : 0); break; - case g: h = (b - r) / d + 2; break; - case b: h = (r - g) / d + 4; break; - } - h /= 6; - } - return {h: h, s: s, v: v, a: c.a}; -} - -/** - * Converts an HSV color value to an RGBA color. - * - * @param {number} h - Hue component (0-1). - * @param {number} s - Saturation component (0-1). - * @param {number} v - Value component (0-1). - * @param {number} a - Alpha component (0-1). - * @returns {Qt.rgba} The resulting color as a Qt.rgba object. - */ -function hsv2rgb(h, s, v, a) { - var r, g, b; - var i = Math.floor(h * 6); - var f = h * 6 - i; - var p = v * (1 - s); - var q = v * (1 - f * s); - var t = v * (1 - (1 - f) * s); - switch(i % 6){ - case 0: r = v, g = t, b = p; break; - case 1: r = q, g = v, b = p; break; - case 2: r = p, g = v, b = t; break; - case 3: r = p, g = q, b = v; break; - case 4: r = t, g = p, b = v; break; - case 5: r = v, g = p, b = q; break; - } - return Qt.rgba(r, g, b, a); -} +// This module provides high level utility functions for color manipulation. /** * Returns a color with the hue of color2 and the saturation, value, and alpha of color1. @@ -60,15 +8,16 @@ function hsv2rgb(h, s, v, a) { * @returns {Qt.rgba} The resulting color. */ function colorWithHueOf(color1, color2) { - // Convert both colors to HSV var c1 = Qt.color(color1); var c2 = Qt.color(color2); - var hsv1 = rgb2hsv(c1); - var hsv2 = rgb2hsv(c2); + // Qt.color hsvHue/hsvSaturation/hsvValue/alpha return 0-1 + var hue = c2.hsvHue; + var sat = c1.hsvSaturation; + var val = c1.hsvValue; + var alpha = c1.a; - // Use hue from color2, saturation/value/alpha from color1 - return hsv2rgb(hsv2.h, hsv1.s, hsv1.v, hsv1.a); + return Qt.hsva(hue, sat, val, alpha); } /** @@ -79,15 +28,15 @@ function colorWithHueOf(color1, color2) { * @returns {Qt.rgba} The resulting color. */ function colorWithSaturationOf(color1, color2) { - // Convert both colors to HSV var c1 = Qt.color(color1); var c2 = Qt.color(color2); - var hsv1 = rgb2hsv(c1); - var hsv2 = rgb2hsv(c2); + var hue = c1.hsvHue; + var sat = c2.hsvSaturation; + var val = c1.hsvValue; + var alpha = c1.a; - // Use hue from color2, saturation/value/alpha from color1 - return hsv2rgb(hsv1.h, hsv2.s, hsv1.v, hsv1.a); + return Qt.hsva(hue, sat, val, alpha); } /** From cd177a3fcd26fa8df777c4b981eb92f159ef8fa2 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:28:12 +0200 Subject: [PATCH 64/84] color utils: adapt to accent: use hsl --- .../modules/common/functions/color_utils.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/modules/common/functions/color_utils.js b/.config/quickshell/modules/common/functions/color_utils.js index 82653ed21..652b09ebb 100644 --- a/.config/quickshell/modules/common/functions/color_utils.js +++ b/.config/quickshell/modules/common/functions/color_utils.js @@ -40,22 +40,22 @@ function colorWithSaturationOf(color1, color2) { } /** - * Adapts color1 to the accent (hue and saturation) of color2, keeping value and alpha from color1. + * Adapts color1 to the accent (hue and saturation) of color2 using HSL, keeping lightness and alpha from color1. * * @param {string} color1 - The base color (any Qt.color-compatible string). * @param {string} color2 - The accent color. * @returns {Qt.rgba} The resulting color. */ function adaptToAccent(color1, color2) { - // Convert both colors to HSV var c1 = Qt.color(color1); var c2 = Qt.color(color2); - var hsv1 = rgb2hsv(c1); - var hsv2 = rgb2hsv(c2); + var hue = c2.hslHue; + var sat = c2.hslSaturation; + var light = c1.hslLightness; + var alpha = c1.a; - // Use hue from color2, saturation/value/alpha from color1 - return hsv2rgb(hsv2.h, hsv2.s, hsv1.v, hsv1.a); + return Qt.hsla(hue, sat, light, alpha); } /** From 26a5dbd91c356c489daadb83495e6376db590bcb Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 19 May 2025 23:36:17 +0200 Subject: [PATCH 65/84] services: null check for ConfigOptions for hackability/yoinkability --- .config/quickshell/services/Ai.qml | 2 +- .config/quickshell/services/Booru.qml | 11 ++--------- .config/quickshell/services/DateTime.qml | 2 +- .config/quickshell/services/MaterialThemeLoader.qml | 2 +- .../quickshell/services/PersistentStateManager.qml | 2 +- .config/quickshell/services/ResourceUsage.qml | 2 +- 6 files changed, 7 insertions(+), 14 deletions(-) diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index f47718f9c..04d2dbc82 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -14,7 +14,7 @@ Singleton { readonly property string interfaceRole: "interface" readonly property string apiKeyEnvVarName: "API_KEY" property Component aiMessageComponent: AiMessageData {} - property string systemPrompt: ConfigOptions.ai.systemPrompt ?? "" + property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? "" property var messages: [] readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {} readonly property var apiKeysLoaded: KeyringStorage.loaded diff --git a/.config/quickshell/services/Booru.qml b/.config/quickshell/services/Booru.qml index d0c5dceb2..8f0cb3f5d 100644 --- a/.config/quickshell/services/Booru.qml +++ b/.config/quickshell/services/Booru.qml @@ -13,17 +13,10 @@ Singleton { signal tagSuggestion(string query, var suggestions) - Connections { - target: ConfigOptions.sidebar.booru - function onAllowNsfwChanged() { - root.addSystemMessage(PersistentStates.booru.allowNsfw ? qsTr("Tiddies enabled") : qsTr("No horny")) - } - } - property string failMessage: qsTr("That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number") property var responses: [] property int runningRequests: 0 - property var defaultUserAgent: ConfigOptions.networking.userAgent + property var defaultUserAgent: ConfigOptions?.networking?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" property var providerList: ["yandere", "konachan", "zerochan", "danbooru", "gelbooru", "waifu.im"] property var providers: { "system": { "name": "System" }, @@ -347,7 +340,7 @@ Singleton { xhr.setRequestHeader("User-Agent", defaultUserAgent) } else if (currentProvider == "zerochan") { - const userAgent = ConfigOptions.sidebar.booru.zerochan.username ? `Desktop sidebar booru viewer - ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent + const userAgent = ConfigOptions?.sidebar?.booru?.zerochan?.username ? `Desktop sidebar booru viewer - ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent xhr.setRequestHeader("User-Agent", userAgent) } root.runningRequests++; diff --git a/.config/quickshell/services/DateTime.qml b/.config/quickshell/services/DateTime.qml index 32b89ff9c..ee5cf4b41 100644 --- a/.config/quickshell/services/DateTime.qml +++ b/.config/quickshell/services/DateTime.qml @@ -38,7 +38,7 @@ Singleton { if (hours > 0) formatted += `${formatted ? ", " : ""}${hours}h` if (minutes > 0 || !formatted) formatted += `${formatted ? ", " : ""}${minutes}m` uptime = formatted - interval = ConfigOptions.resources.updateInterval; + interval = ConfigOptions?.resources?.updateInterval ?? 3000 } } diff --git a/.config/quickshell/services/MaterialThemeLoader.qml b/.config/quickshell/services/MaterialThemeLoader.qml index 7eb974b1e..339a0d772 100644 --- a/.config/quickshell/services/MaterialThemeLoader.qml +++ b/.config/quickshell/services/MaterialThemeLoader.qml @@ -30,7 +30,7 @@ Singleton { Timer { id: delayedFileRead - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100 repeat: false running: false onTriggered: { diff --git a/.config/quickshell/services/PersistentStateManager.qml b/.config/quickshell/services/PersistentStateManager.qml index d961b41b6..7c3fec739 100644 --- a/.config/quickshell/services/PersistentStateManager.qml +++ b/.config/quickshell/services/PersistentStateManager.qml @@ -72,7 +72,7 @@ Singleton { Timer { id: delayedFileRead - interval: ConfigOptions.hacks.arbitraryRaceConditionDelay + interval: ConfigOptions?.hacks?.arbitraryRaceConditionDelay ?? 100 repeat: false running: false onTriggered: { diff --git a/.config/quickshell/services/ResourceUsage.qml b/.config/quickshell/services/ResourceUsage.qml index e30b36c84..39078fbde 100644 --- a/.config/quickshell/services/ResourceUsage.qml +++ b/.config/quickshell/services/ResourceUsage.qml @@ -50,7 +50,7 @@ Singleton { previousCpuStats = { total, idle } } - interval = ConfigOptions.resources.updateInterval + interval = ConfigOptions?.resources?.updateInterval ?? 3000 } } From a32edd387e92dcb67b48d17e964aead77e5637db Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 00:35:09 +0200 Subject: [PATCH 66/84] mfking chore: add qsTr() to strings for translations --- .config/quickshell/GlobalStates.qml | 2 +- .config/quickshell/modules/bar/Bar.qml | 4 ++-- .config/quickshell/modules/bar/Media.qml | 2 +- .../modules/cheatsheet/Cheatsheet.qml | 6 +++--- .../modules/common/ConfigOptions.qml | 4 ++-- .../common/widgets/NotificationWidget.qml | 2 +- .../modules/mediaControls/MediaControls.qml | 6 +++--- .../OnScreenDisplayBrightness.qml | 4 ++-- .../onScreenDisplay/OnScreenDisplayVolume.qml | 4 ++-- .../quickshell/modules/overview/Overview.qml | 12 +++++------ .../modules/overview/SearchItem.qml | 2 +- .../modules/overview/SearchWidget.qml | 18 +++++++--------- .../quickshell/modules/session/Session.qml | 4 ++-- .../modules/sidebarLeft/SidebarLeft.qml | 6 +++--- .../modules/sidebarLeft/aiChat/AiMessage.qml | 2 +- .../sidebarLeft/aiChat/MessageThinkBlock.qml | 2 +- .../modules/sidebarLeft/anime/BooruImage.qml | 4 ++-- .../sidebarLeft/anime/BooruResponse.qml | 4 +++- .../modules/sidebarRight/SidebarRight.qml | 9 ++++---- .../quickToggles/BluetoothToggle.qml | 6 ++++-- .../quickToggles/NetworkToggle.qml | 5 +++-- .config/quickshell/services/Ai.qml | 21 ++++++++++--------- .config/quickshell/services/Booru.qml | 4 ++-- .config/quickshell/services/Brightness.qml | 4 ++-- .config/quickshell/services/ConfigLoader.qml | 8 +++---- .../quickshell/services/KeyringStorage.qml | 5 +++-- 26 files changed, 78 insertions(+), 72 deletions(-) diff --git a/.config/quickshell/GlobalStates.qml b/.config/quickshell/GlobalStates.qml index b9e3362a6..1b879421b 100644 --- a/.config/quickshell/GlobalStates.qml +++ b/.config/quickshell/GlobalStates.qml @@ -31,7 +31,7 @@ Singleton { GlobalShortcut { name: "workspaceNumber" - description: "Hold to show workspace numbers, release to show icons" + description: qsTr("Hold to show workspace numbers, release to show icons") onPressed: { workspaceShowNumbersTimer.start() diff --git a/.config/quickshell/modules/bar/Bar.qml b/.config/quickshell/modules/bar/Bar.qml index 516fa8a32..ea5aa58ad 100644 --- a/.config/quickshell/modules/bar/Bar.qml +++ b/.config/quickshell/modules/bar/Bar.qml @@ -108,7 +108,7 @@ Scope { ScrollHint { reveal: barLeftSideMouseArea.hovered icon: "light_mode" - tooltipText: "Scroll to change brightness" + tooltipText: qsTr("Scroll to change brightness") side: "left" anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -283,7 +283,7 @@ Scope { ScrollHint { reveal: barRightSideMouseArea.hovered icon: "volume_up" - tooltipText: "Scroll to change volume" + tooltipText: qsTr("Scroll to change volume") side: "right" anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter diff --git a/.config/quickshell/modules/bar/Media.qml b/.config/quickshell/modules/bar/Media.qml index 424cf860a..2cebc4d35 100644 --- a/.config/quickshell/modules/bar/Media.qml +++ b/.config/quickshell/modules/bar/Media.qml @@ -12,7 +12,7 @@ import Quickshell.Hyprland Item { id: root readonly property MprisPlayer activePlayer: MprisController.activePlayer - readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || "No media" + readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media") Layout.fillHeight: true implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index eeeb4a692..2474430a5 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -156,7 +156,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetToggle" - description: "Toggles cheatsheet on press" + description: qsTr("Toggles cheatsheet on press") onPressed: { cheatsheetLoader.active = !cheatsheetLoader.active; @@ -165,7 +165,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetOpen" - description: "Opens cheatsheet on press" + description: qsTr("Opens cheatsheet on press") onPressed: { cheatsheetLoader.active = true; @@ -174,7 +174,7 @@ Scope { // Scope GlobalShortcut { name: "cheatsheetClose" - description: "Closes cheatsheet on press" + description: qsTr("Closes cheatsheet on press") onPressed: { cheatsheetLoader.active = false; diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index 57d6e5aa5..8b8b5f4e9 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -5,7 +5,7 @@ pragma ComponentBehavior: Bound Singleton { property QtObject ai: QtObject { - property string systemPrompt: "Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in bold to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`." + property string systemPrompt: qsTr("Use casual tone. No user knowledge is to be assumed except basic Linux literacy. Be brief and concise: When explaining concepts, use bullet points (prefer minus sign (-) over asterisk (*)) and highlight keywords in bold to pinpoint the main concepts instead of long paragraphs. You are also encouraged to split your response with h2 headers, each header title beginning with an emoji, like `## 🐧 Linux`.") } property QtObject appearance: QtObject { @@ -70,7 +70,7 @@ Singleton { property string defaultProvider: "yandere" property int limit: 20 property QtObject zerochan: QtObject { - property string username: "" + property string username: "[unset]" } } } diff --git a/.config/quickshell/modules/common/widgets/NotificationWidget.qml b/.config/quickshell/modules/common/widgets/NotificationWidget.qml index a99fd1bf0..fd7106ba7 100644 --- a/.config/quickshell/modules/common/widgets/NotificationWidget.qml +++ b/.config/quickshell/modules/common/widgets/NotificationWidget.qml @@ -127,7 +127,7 @@ Item { onPressAndHold: (mouse) => { if (mouse.button === Qt.LeftButton) { Hyprland.dispatch(`exec wl-copy '${StringUtils.shellSingleQuoteEscape(notificationObject.body)}'`) - notificationSummaryText.text = `${notificationObject.summary} (copied)` + notificationSummaryText.text = String.format(qsTr("{0} (copied)"), notificationObject.summary) } } onDragStartedChanged: () => { diff --git a/.config/quickshell/modules/mediaControls/MediaControls.qml b/.config/quickshell/modules/mediaControls/MediaControls.qml index 204956114..345bd3bb0 100644 --- a/.config/quickshell/modules/mediaControls/MediaControls.qml +++ b/.config/quickshell/modules/mediaControls/MediaControls.qml @@ -115,7 +115,7 @@ Scope { GlobalShortcut { name: "mediaControlsToggle" - description: "Toggles media controls on press" + description: qsTr("Toggles media controls on press") onPressed: { if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) { @@ -127,7 +127,7 @@ Scope { } GlobalShortcut { name: "mediaControlsOpen" - description: "Opens media controls on press" + description: qsTr("Opens media controls on press") onPressed: { mediaControlsLoader.active = true; @@ -136,7 +136,7 @@ Scope { } GlobalShortcut { name: "mediaControlsClose" - description: "Closes media controls on press" + description: qsTr("Closes media controls on press") onPressed: { mediaControlsLoader.active = false; diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml index bec442a8a..0bcc3615b 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayBrightness.qml @@ -131,7 +131,7 @@ Scope { GlobalShortcut { name: "osdBrightnessTrigger" - description: "Triggers brightness OSD on press" + description: qsTr("Triggers brightness OSD on press") onPressed: { root.triggerOsd() @@ -139,7 +139,7 @@ Scope { } GlobalShortcut { name: "osdBrightnessHide" - description: "Hides brightness OSD on press" + description: qsTr("Hides brightness OSD on press") onPressed: { root.showOsdValues = false diff --git a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml index 3ecc01cde..9a6c43272 100644 --- a/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml +++ b/.config/quickshell/modules/onScreenDisplay/OnScreenDisplayVolume.qml @@ -129,7 +129,7 @@ Scope { } GlobalShortcut { name: "osdVolumeTrigger" - description: "Triggers volume OSD on press" + description: qsTr("Triggers volume OSD on press") onPressed: { root.triggerOsd() @@ -137,7 +137,7 @@ Scope { } GlobalShortcut { name: "osdVolumeHide" - description: "Hides volume OSD on press" + description: qsTr("Hides volume OSD on press") onPressed: { root.showOsdValues = false diff --git a/.config/quickshell/modules/overview/Overview.qml b/.config/quickshell/modules/overview/Overview.qml index 9aa5da314..0b267699c 100644 --- a/.config/quickshell/modules/overview/Overview.qml +++ b/.config/quickshell/modules/overview/Overview.qml @@ -128,7 +128,7 @@ Scope { GlobalShortcut { name: "overviewToggle" - description: "Toggles overview on press" + description: qsTr("Toggles overview on press") onPressed: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen @@ -136,7 +136,7 @@ Scope { } GlobalShortcut { name: "overviewClose" - description: "Closes overview" + description: qsTr("Closes overview") onPressed: { GlobalStates.overviewOpen = false @@ -144,7 +144,7 @@ Scope { } GlobalShortcut { name: "overviewToggleRelease" - description: "Toggles overview on release" + description: qsTr("Toggles overview on release") onPressed: { GlobalStates.superReleaseMightTrigger = true @@ -160,9 +160,9 @@ Scope { } GlobalShortcut { name: "overviewToggleReleaseInterrupt" - description: "Interrupts possibility of overview being toggled on release. " + - "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + - "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + description: qsTr("Interrupts possibility of overview being toggled on release. ") + + qsTr("This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. ") + + qsTr("To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything.") onPressed: { GlobalStates.superReleaseMightTrigger = false diff --git a/.config/quickshell/modules/overview/SearchItem.qml b/.config/quickshell/modules/overview/SearchItem.qml index 1eb5bcac5..7643af80c 100644 --- a/.config/quickshell/modules/overview/SearchItem.qml +++ b/.config/quickshell/modules/overview/SearchItem.qml @@ -103,7 +103,7 @@ Button { StyledText { font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colSubtext - visible: root.itemType && root.itemType != "App" + visible: root.itemType && root.itemType != qsTr("App") text: root.itemType } StyledText { diff --git a/.config/quickshell/modules/overview/SearchWidget.qml b/.config/quickshell/modules/overview/SearchWidget.qml index c861b4549..887480de1 100644 --- a/.config/quickshell/modules/overview/SearchWidget.qml +++ b/.config/quickshell/modules/overview/SearchWidget.qml @@ -276,7 +276,7 @@ Item { // Wrapper nonAppResultsTimer.restart(); const mathResultObject = { name: root.mathResult, - clickActionName: "Copy", + clickActionName: qsTr("Copy"), type: qsTr("Math result"), fontType: "monospace", materialSymbol: 'calculate', @@ -286,7 +286,7 @@ Item { // Wrapper } const commandResultObject = { name: searchingText, - clickActionName: "Run", + clickActionName: qsTr("Run"), type: qsTr("Run command"), fontType: "monospace", materialSymbol: 'terminal', @@ -300,8 +300,8 @@ Item { // Wrapper if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) { return { name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString, - clickActionName: "Run", - type: "Action", + clickActionName: qsTr("Run"), + type: qsTr("Action"), materialSymbol: 'settings_suggest', execute: () => { action.execute(root.searchingText.split(" ").slice(1).join(" ")) @@ -319,8 +319,8 @@ Item { // Wrapper result = result.concat( AppSearch.fuzzyQuery(root.searchingText) .map((entry) => { - entry.clickActionName = "Launch"; - entry.type = "App" + entry.clickActionName = qsTr("Launch"); + entry.type = qsTr("App"); return entry; }) ); @@ -344,8 +344,8 @@ Item { // Wrapper // Web search result.push({ name: root.searchingText, - clickActionName: "Search", - type: "Search the web", + clickActionName: qsTr("Search"), + type: qsTr("Search the web"), materialSymbol: 'travel_explore', execute: () => { let url = ConfigOptions.search.engineBaseUrl + root.searchingText @@ -361,8 +361,6 @@ Item { // Wrapper } delegate: SearchItem { entry: modelData - // itemName: modelData.name - // itemIcon: modelData.icon } } diff --git a/.config/quickshell/modules/session/Session.qml b/.config/quickshell/modules/session/Session.qml index f5ed73588..190666bd0 100644 --- a/.config/quickshell/modules/session/Session.qml +++ b/.config/quickshell/modules/session/Session.qml @@ -209,7 +209,7 @@ Scope { GlobalShortcut { name: "sessionToggle" - description: "Toggles session screen on press" + description: qsTr("Toggles session screen on press") onPressed: { sessionLoader.active = !sessionLoader.active; @@ -218,7 +218,7 @@ Scope { GlobalShortcut { name: "sessionOpen" - description: "Opens session screen on press" + description: qsTr("Opens session screen on press") onPressed: { sessionLoader.active = true; diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 4c60bdac7..eff535d61 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -192,7 +192,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftToggle" - description: "Toggles left sidebar on press" + description: qsTr("Toggles left sidebar on press") onPressed: { sidebarLoader.active = !sidebarLoader.active; @@ -202,7 +202,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftOpen" - description: "Opens left sidebar on press" + description: qsTr("Opens left sidebar on press") onPressed: { sidebarLoader.active = true; @@ -212,7 +212,7 @@ Scope { // Scope GlobalShortcut { name: "sidebarLeftClose" - description: "Closes left sidebar on press" + description: qsTr("Closes left sidebar on press") onPressed: { sidebarLoader.active = false; diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml index 7418e31ab..4c412719a 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/AiMessage.qml @@ -151,7 +151,7 @@ Rectangle { color: Appearance.m3colors.m3onSecondaryContainer text: messageData.role == 'assistant' ? Ai.models[messageData.model].name : (messageData.role == 'user' && SystemInfo.username) ? SystemInfo.username : - (messageData.role == 'interface') ? qsTr("Interface") : qsTr("Unknown") + qsTr("Interface") } } } diff --git a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml index 3f8f620b5..e00615b06 100644 --- a/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml +++ b/.config/quickshell/modules/sidebarLeft/aiChat/MessageThinkBlock.qml @@ -99,7 +99,7 @@ Item { id: thinkBlockLanguage Layout.fillWidth: false Layout.alignment: Qt.AlignLeft - text: root.completed ? "Chain of Thought" : ("Thinking" + ".".repeat(Math.random() * 4)) + text: root.completed ? qsTr("Chain of Thought") : (qsTr("Thinking") + ".".repeat(Math.random() * 4)) } Item { Layout.fillWidth: true } Button { // Expand button diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml index c36a52ca5..3bc9b461d 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml @@ -94,7 +94,7 @@ Button { PointingHandInteraction {} StyledToolTip { - content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}\nClick for options` + content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}\n${qsTr("Click for options")}` } background: Rectangle { @@ -177,7 +177,7 @@ Button { MenuButton { id: downloadButton Layout.fillWidth: true - buttonText: "Download" + buttonText: qsTr("Download") onClicked: { root.showActions = false Hyprland.dispatch(`exec curl '${root.imageData.file_url}' -o '${root.imageData.is_nsfw ? root.nsfwPath : root.downloadPath}/${root.fileName}' && notify-send '${qsTr("Download complete")}' '${root.downloadPath}/${root.fileName}'`) diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml index e62e5544f..4822a2224 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruResponse.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/string_utils.js" as StringUtils import "../" import QtQuick import QtQuick.Controls @@ -93,7 +94,8 @@ Rectangle { anchors.centerIn: parent font.pixelSize: Appearance.font.pixelSize.smaller color: Appearance.colors.colOnLayer2 - text: `Page ${root.responseData.page}` + // text: `Page ${root.responseData.page}` + text: StringUtils.format(qsTr("Page {0}"), root.responseData.page) } } } diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index 8d9941847..e7dd4d9eb 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -2,6 +2,7 @@ import "root:/" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/string_utils.js" as StringUtils import "./quickToggles/" import QtQuick import QtQuick.Controls @@ -102,7 +103,7 @@ Scope { StyledText { font.pixelSize: Appearance.font.pixelSize.normal color: Appearance.colors.colOnLayer0 - text: `Uptime: ${DateTime.uptime}` + text: StringUtils.format(qsTr("Uptime: {0}"), DateTime.uptime) textFormat: Text.MarkdownText } @@ -197,7 +198,7 @@ Scope { GlobalShortcut { name: "sidebarRightToggle" - description: "Toggles right sidebar on press" + description: qsTr("Toggles right sidebar on press") onPressed: { sidebarLoader.active = !sidebarLoader.active; @@ -206,7 +207,7 @@ Scope { } GlobalShortcut { name: "sidebarRightOpen" - description: "Opens right sidebar on press" + description: qsTr("Opens right sidebar on press") onPressed: { sidebarLoader.active = true; @@ -215,7 +216,7 @@ Scope { } GlobalShortcut { name: "sidebarRightClose" - description: "Closes right sidebar on press" + description: qsTr("Closes right sidebar on press") onPressed: { sidebarLoader.active = false; diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml index d73a66076..fc432c090 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -2,6 +2,7 @@ import "../" import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" +import "root:/modules/common/functions/string_utils.js" as StringUtils import QtQuick import Quickshell import Quickshell.Io @@ -37,8 +38,9 @@ QuickToggleButton { } } StyledToolTip { - content: `${(Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ? - Bluetooth.bluetoothDeviceName : "Bluetooth"} | ${qsTr("Right-click to configure")}` + content: StringUtils.format(qsTr("{0} | Right-click to configure"), + (Bluetooth.bluetoothEnabled && Bluetooth.bluetoothDeviceName.length > 0) ? + Bluetooth.bluetoothDeviceName : qsTr("Bluetooth")) } } diff --git a/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml index 742a2f480..93a4aec99 100644 --- a/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -1,6 +1,7 @@ +import "root:/services" import "root:/modules/common" import "root:/modules/common/widgets" -import "root:/services" +import "root:/modules/common/functions/string_utils.js" as StringUtils import "../" import QtQuick import Quickshell @@ -42,6 +43,6 @@ QuickToggleButton { } } StyledToolTip { - content: `${Network.networkName} | Right-click to configure` + content: StringUtils.format(qsTr("{0} | Right-click to configure"), Network.networkName) } } diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index 04d2dbc82..4f1ac52e9 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -36,14 +36,14 @@ Singleton { "gemini-2.0-flash-search": { "name": "Gemini 2.0 Flash", "icon": "google-gemini-symbolic", - "description": "Online | Google's model\nGives up-to-date information with search.", + "description": qsTr("Online | Google's model\nGives up-to-date information with search."), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", "model": "gemini-2.0-flash", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key", + "key_get_description": qsTr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", "tools": [ { @@ -54,25 +54,26 @@ Singleton { "openrouter-llama4-maverick": { "name": "Llama 4 Maverick", "icon": "ollama-symbolic", - "description": "Online via OpenRouter | Meta's model", + "description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "Meta"), "homepage": "https://openrouter.ai/meta-llama/llama-4-maverick:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "meta-llama/llama-4-maverick:free", "requires_key": true, "key_id": "openrouter", "key_get_link": "https://openrouter.ai/settings/keys", - "key_get_description": "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key", + "key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), }, "openrouter-deepseek-r1": { "name": "DeepSeek R1", "icon": "deepseek-symbolic", - "description": "Online via OpenRouter | DeepSeek's reasoning model", + "description": StringUtils.format(qsTr("Online via {0} | {1}'s model"), "OpenRouter", "DeepSeek"), "homepage": "https://openrouter.ai/deepseek/deepseek-r1:free", "endpoint": "https://openrouter.ai/api/v1/chat/completions", "model": "deepseek/deepseek-r1:free", "requires_key": true, "key_id": "openrouter", "key_get_link": "https://openrouter.ai/settings/keys", + "key_get_description": qsTr("**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key"), }, } property var modelList: Object.keys(root.models) @@ -117,7 +118,7 @@ Singleton { root.models[model] = { "name": guessModelName(model), "icon": guessModelLogo(model), - "description": `Local Ollama model: ${model}`, + "description": StringUtils.format(qsTr("Local Ollama model | {0}"), model), "homepage": `https://ollama.com/library/${model}`, "endpoint": "http://localhost:11434/v1/chat/completions", "model": model, @@ -167,7 +168,7 @@ Singleton { modelId = modelId.toLowerCase() if (modelList.indexOf(modelId) !== -1) { PersistentStateManager.setState("ai.model", modelId); - if (feedback) root.addMessage("Model set to " + models[modelId].name, Ai.interfaceRole) + if (feedback) root.addMessage(StringUtils.format(StringUtils.format("Model set to {0}"), models[modelId].name, Ai.interfaceRole)) if (models[modelId].requires_key) { // If key not there show advice if (root.apiKeysLoaded && (!root.apiKeys[models[modelId].key_id] || root.apiKeys[models[modelId].key_id].length === 0)) { @@ -185,7 +186,7 @@ Singleton { function setApiKey(key) { const model = models[currentModelId]; if (!model.requires_key) { - root.addMessage(`${model.name} does not require an API key`, Ai.interfaceRole); + root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole); return; } if (!key || key.length === 0) { @@ -194,7 +195,7 @@ Singleton { return; } KeyringStorage.setNestedField(["apiKeys", model.key_id], key.trim()); - root.addMessage("API key set for " + model.name, Ai.interfaceRole); + root.addMessage(StringUtils.format(qsTr("API key set for {0}"), model.name, Ai.interfaceRole)); } function printApiKey() { @@ -207,7 +208,7 @@ Singleton { root.addMessage(StringUtils.format(qsTr("No API key set for {0}"), model.name), Ai.interfaceRole); } } else { - root.addMessage(`This model (${model.name}) does not require an API key`, Ai.interfaceRole); + root.addMessage(StringUtils.format(qsTr("{0} does not require an API key"), model.name), Ai.interfaceRole); } } diff --git a/.config/quickshell/services/Booru.qml b/.config/quickshell/services/Booru.qml index 8f0cb3f5d..216fc1f5e 100644 --- a/.config/quickshell/services/Booru.qml +++ b/.config/quickshell/services/Booru.qml @@ -19,7 +19,7 @@ Singleton { property var defaultUserAgent: ConfigOptions?.networking?.userAgent || "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36" property var providerList: ["yandere", "konachan", "zerochan", "danbooru", "gelbooru", "waifu.im"] property var providers: { - "system": { "name": "System" }, + "system": { "name": qsTr("System") }, "yandere": { "name": "yande.re", "url": "https://yande.re", @@ -340,7 +340,7 @@ Singleton { xhr.setRequestHeader("User-Agent", defaultUserAgent) } else if (currentProvider == "zerochan") { - const userAgent = ConfigOptions?.sidebar?.booru?.zerochan?.username ? `Desktop sidebar booru viewer - ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent + const userAgent = ConfigOptions?.sidebar?.booru?.zerochan?.username ? `Desktop sidebar booru viewer - username: ${ConfigOptions.sidebar.booru.zerochan.username}` : defaultUserAgent xhr.setRequestHeader("User-Agent", userAgent) } root.runningRequests++; diff --git a/.config/quickshell/services/Brightness.qml b/.config/quickshell/services/Brightness.qml index 7fc2a2b24..ddf25f8ec 100644 --- a/.config/quickshell/services/Brightness.qml +++ b/.config/quickshell/services/Brightness.qml @@ -134,13 +134,13 @@ Singleton { GlobalShortcut { name: "brightnessIncrease" - description: "Increase brightness" + description: qsTr("Increase brightness") onPressed: root.increaseBrightness() } GlobalShortcut { name: "brightnessDecrease" - description: "Decrease brightness" + description: qsTr("Decrease brightness") onPressed: root.decreaseBrightness() } } diff --git a/.config/quickshell/services/ConfigLoader.qml b/.config/quickshell/services/ConfigLoader.qml index c05b17341..6c330c68e 100644 --- a/.config/quickshell/services/ConfigLoader.qml +++ b/.config/quickshell/services/ConfigLoader.qml @@ -29,11 +29,11 @@ Singleton { if (root.firstLoad) { root.firstLoad = false; } else { - Hyprland.dispatch(`exec notify-send "Shell configuration reloaded" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration reloaded")}" "${root.filePath}"`) } } catch (e) { console.error("[ConfigLoader] Error reading file:", e); - Hyprland.dispatch(`exec notify-send "Shell configuration failed to load" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) return; } } @@ -66,9 +66,9 @@ Singleton { console.log("[ConfigLoader] File not found, creating new file.") const plainConfig = ObjectUtils.toPlainObject(ConfigOptions) configFileView.setText(JSON.stringify(plainConfig, null, 2)) - Hyprland.dispatch(`exec notify-send "Shell configuration created" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration created")}" "${root.filePath}"`) } else { - Hyprland.dispatch(`exec notify-send "Shell configuration failed to load" "${root.filePath}"`) + Hyprland.dispatch(`exec notify-send "${qsTr("Shell configuration failed to load")}" "${root.filePath}"`) } } } diff --git a/.config/quickshell/services/KeyringStorage.qml b/.config/quickshell/services/KeyringStorage.qml index 39632c6ca..0de619100 100644 --- a/.config/quickshell/services/KeyringStorage.qml +++ b/.config/quickshell/services/KeyringStorage.qml @@ -2,6 +2,7 @@ pragma Singleton pragma ComponentBehavior: Bound import "root:/modules/common" +import "root:/modules/common/functions/string_utils.js" as StringUtils import Quickshell; import Quickshell.Io; import Qt.labs.platform @@ -18,14 +19,14 @@ Singleton { property var properties: { "application": "illogical-impulse", - "explanation": "For storing API keys and other sensitive information", + "explanation": qsTr("For storing API keys and other sensitive information"), } property var propertiesAsArgs: Object.keys(root.properties).reduce( function(arr, key) { return arr.concat([key, root.properties[key]]); }, [] ) - property string keyringLabel: "illogical-impulse Safe Storage" + property string keyringLabel: StringUtils.format(qsTr("{0} Safe Storage"), "illogical-impulse") function setNestedField(path, value) { if (!root.keyringData) root.keyringData = {}; From 4715b02a453c9ee1dc72584bf7edac52ece9ecff Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 08:51:30 +0200 Subject: [PATCH 67/84] wallpaper switcher: prompt upscale --- .config/quickshell/scripts/switchwall.sh | 38 +++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/scripts/switchwall.sh b/.config/quickshell/scripts/switchwall.sh index f04ef44d5..4547be416 100755 --- a/.config/quickshell/scripts/switchwall.sh +++ b/.config/quickshell/scripts/switchwall.sh @@ -27,6 +27,41 @@ post_process() { fi } +check_and_prompt_upscale() { + local img="$1" + min_width_desired="$(hyprctl monitors -j | jq '([.[].width] | max)' | xargs)" # max monitor width + min_height_desired="$(hyprctl monitors -j | jq '([.[].height] | max)' | xargs)" # max monitor height + + if command -v identify &>/dev/null && [ -f "$img" ]; then + local img_width img_height + img_width=$(identify -format "%w" "$img" 2>/dev/null) + img_height=$(identify -format "%h" "$img" 2>/dev/null) + if [[ "$img_width" -lt "$min_width_desired" || "$img_height" -lt "$min_height_desired" ]]; then + action=$(notify-send "Upscale?" \ + "Image resolution (${img_width}x${img_height}) is lower than screen resolution (${min_width_desired}x${min_height_desired})" \ + -A "open_upscayl=Open Upscayl") + if [[ "$action" == "open_upscayl" ]]; then + if command -v upscayl &>/dev/null; then + nohup upscayl > /dev/null 2>&1 & + else + action2=$(notify-send \ + -a "Wallpaper switcher" \ + -c "im.error" \ + -A "install_upscayl=Install Upscayl (Arch)" \ + "Install Upscayl?" \ + "yay -S upscayl-bin") + if [[ "$action2" == "install_upscayl" ]]; then + foot yay -S upscayl-bin + if command -v upscayl &>/dev/null; then + nohup upscayl > /dev/null 2>&1 & + fi + fi + fi + fi + fi + fi +} + THUMBNAIL_DIR="/tmp/mpvpaper_thumbnails" CUSTOM_DIR="$XDG_CONFIG_HOME/hypr/custom" RESTORE_SCRIPT_DIR="$CUSTOM_DIR/scripts" @@ -90,6 +125,7 @@ switch() { exit 0 fi + check_and_prompt_upscale "$imgpath" & kill_existing_mpvpaper if is_video "$imgpath"; then @@ -106,7 +142,7 @@ switch() { echo "Missing deps: ${missing_deps[*]}" echo "Arch: sudo pacman -S ${missing_deps[*]}" action=$(notify-send \ - -a "Wallpaper Switcher" \ + -a "Wallpaper switcher" \ -c "im.error" \ -A "install_arch=Install (Arch)" \ "Can't switch to video wallpaper" \ From 0ab39c2c50151769f7d2ad6f4724598f5ca5c1fe Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:15:29 +0200 Subject: [PATCH 68/84] left sidebar: fix focus --- .config/quickshell/modules/sidebarLeft/Anime.qml | 6 ------ .config/quickshell/modules/sidebarLeft/SidebarLeft.qml | 3 +++ 2 files changed, 3 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index a1beca59d..ff529219d 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -114,12 +114,6 @@ Item { } } - Connections { - target: panelWindow - function onVisibleChanged(visible) { - tagInputField.forceActiveFocus() - } - } onFocusChanged: (focus) => { if (focus) { tagInputField.forceActiveFocus() diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index eff535d61..2f1bad740 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -58,6 +58,9 @@ Scope { // Scope id: grab windows: [ sidebarRoot ] active: sidebarRoot.visible && !sidebarRoot.pin + onActiveChanged: { // Focus the selected tab + if (active) swipeView.currentItem.forceActiveFocus() + } onCleared: () => { if (!active) sidebarRoot.hide() } From 981631474983dbadc19cd28fcedd13a8cf35cb87 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:16:21 +0200 Subject: [PATCH 69/84] ai use message ids to point to data i thought it would give cleaner updates... --- .../quickshell/modules/sidebarLeft/AiChat.qml | 13 +++-- .config/quickshell/services/Ai.qml | 47 ++++++++++++------- 2 files changed, 38 insertions(+), 22 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/AiChat.qml b/.config/quickshell/modules/sidebarLeft/AiChat.qml index b8df45bb7..37fa876ed 100644 --- a/.config/quickshell/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/modules/sidebarLeft/AiChat.qml @@ -18,7 +18,6 @@ Item { id: root property var panelWindow property var inputField: messageInputField - readonly property var messages: Ai.messages property string commandPrefix: "/" property string faviconDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/favicons`) @@ -31,7 +30,7 @@ Item { onFocusChanged: (focus) => { if (focus) { - messageInputField.forceActiveFocus() + root.inputField.forceActiveFocus() } } @@ -194,18 +193,22 @@ int main(int argc, char* argv[]) { } model: ScriptModel { - values: root.messages + values: Ai.messageIDs } delegate: AiMessage { + required property var modelData + required property int index messageIndex: index - messageData: modelData + messageData: { + Ai.messageByID[modelData] + } messageInputField: root.inputField faviconDownloadPath: root.faviconDownloadPath } } Item { // Placeholder when list is empty - opacity: root.messages.length === 0 ? 1 : 0 + opacity: Ai.messageIDs.length === 0 ? 1 : 0 visible: opacity > 0 anchors.fill: parent diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index 4f1ac52e9..949fa19cf 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -16,9 +16,20 @@ Singleton { property Component aiMessageComponent: AiMessageData {} property string systemPrompt: ConfigOptions?.ai?.systemPrompt ?? "" property var messages: [] + property var messageIDs: [] + property var messageByID: ({}) readonly property var apiKeys: KeyringStorage.keyringData?.apiKeys ?? {} readonly property var apiKeysLoaded: KeyringStorage.loaded + function idForMessage(message) { + // Generate a unique ID using timestamp and random value + return Date.now().toString(36) + Math.random().toString(36).substr(2, 8); + } + + function safeModelName(modelName) { + return modelName.replace(/:/g, "_").replace(/\./g, "_") + } + // Model properties: // - name: Name of the model // - icon: Icon name of the model @@ -115,7 +126,8 @@ Singleton { const dataJson = JSON.parse(data); root.modelList = [...root.modelList, ...dataJson]; dataJson.forEach(model => { - root.models[model] = { + const safeModelName = root.safeModelName(model); + root.models[safeModelName] = { "name": guessModelName(model), "icon": guessModelLogo(model), "description": StringUtils.format(qsTr("Local Ollama model | {0}"), model), @@ -142,13 +154,17 @@ Singleton { "thinking": false, "done": true, }); - root.messages = [...root.messages, aiMessage]; + const id = idForMessage(aiMessage); + root.messageIDs = [...root.messageIDs, id]; + root.messageByID[id] = aiMessage; } function removeMessage(index) { - if (index < 0 || index >= messages.length) return; - root.messages.splice(index, 1); - root.messages = [...root.messages]; + if (index < 0 || index >= messageIDs.length) return; + const id = root.messageIDs[index]; + root.messageIDs.splice(index, 1); + root.messageIDs = [...root.messageIDs]; + delete root.messageByID[id]; } function addApiKeyAdvice(model) { @@ -160,6 +176,7 @@ Singleton { } function getModel() { + console.log("MODEL:", currentModelId); return models[currentModelId]; } @@ -213,7 +230,8 @@ Singleton { } function clearMessages() { - messages = []; + root.messageIDs = []; + root.messageByID = ({}); } Process { @@ -275,7 +293,8 @@ Singleton { /* Build endpoint, request data */ const endpoint = (apiFormat === "gemini") ? buildGeminiEndpoint(model) : buildOpenAIEndpoint(model); - const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, root.messages) : buildOpenAIRequestData(model, root.messages); + const messageArray = root.messageIDs.map(id => root.messageByID[id]); + const data = (apiFormat === "gemini") ? buildGeminiRequestData(model, messageArray) : buildOpenAIRequestData(model, messageArray); let requestHeaders = { "Content-Type": "application/json", @@ -289,7 +308,9 @@ Singleton { "thinking": true, "done": false, }); - root.messages = [...root.messages, requester.message]; + const id = idForMessage(requester.message); + root.messageIDs = [...root.messageIDs, id]; + root.messageByID[id] = requester.message; /* Build header string for curl */ let headerString = Object.entries(requestHeaders) @@ -447,15 +468,7 @@ Singleton { function sendUserMessage(message) { if (message.length === 0) return; - - const userMessage = aiMessageComponent.createObject(root, { - "role": "user", - "content": message, - "thinking": false, - "done": true, - }); - root.messages = [...root.messages, userMessage]; - + root.addMessage(message, "user"); requester.makeRequest(); } From e57bf529cd6af1ebdb8a852d46e9446cf39399d4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:17:04 +0200 Subject: [PATCH 70/84] bar: introduce borderless mode --- .config/quickshell/modules/bar/Battery.qml | 3 ++- .config/quickshell/modules/bar/ClockWidget.qml | 3 ++- .config/quickshell/modules/bar/Media.qml | 3 ++- .config/quickshell/modules/bar/Resources.qml | 3 ++- .config/quickshell/modules/bar/UtilButtons.qml | 4 +++- .config/quickshell/modules/bar/Workspaces.qml | 3 ++- .config/quickshell/modules/common/ConfigOptions.qml | 1 + 7 files changed, 14 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/modules/bar/Battery.qml b/.config/quickshell/modules/bar/Battery.qml index 2c75d0597..5def8560e 100644 --- a/.config/quickshell/modules/bar/Battery.qml +++ b/.config/quickshell/modules/bar/Battery.qml @@ -8,6 +8,7 @@ import Quickshell.Services.UPower Rectangle { id: root + property bool borderless: ConfigOptions.bar.borderless readonly property var chargeState: UPower.displayDevice.state readonly property bool isCharging: chargeState == UPowerDeviceState.Charging readonly property bool isPluggedIn: isCharging || chargeState == UPowerDeviceState.PendingCharge @@ -18,7 +19,7 @@ Rectangle { implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 implicitHeight: 32 - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 radius: Appearance.rounding.small RowLayout { diff --git a/.config/quickshell/modules/bar/ClockWidget.qml b/.config/quickshell/modules/bar/ClockWidget.qml index e44ac7f5a..080dfb9b8 100644 --- a/.config/quickshell/modules/bar/ClockWidget.qml +++ b/.config/quickshell/modules/bar/ClockWidget.qml @@ -5,9 +5,10 @@ import QtQuick import QtQuick.Layouts Rectangle { + property bool borderless: ConfigOptions.bar.borderless implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 6 // idk, text seems nicer w/ more padding implicitHeight: 32 - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 radius: Appearance.rounding.small RowLayout { diff --git a/.config/quickshell/modules/bar/Media.qml b/.config/quickshell/modules/bar/Media.qml index 2cebc4d35..f4b8606b9 100644 --- a/.config/quickshell/modules/bar/Media.qml +++ b/.config/quickshell/modules/bar/Media.qml @@ -11,6 +11,7 @@ import Quickshell.Hyprland Item { id: root + property bool borderless: ConfigOptions.bar.borderless readonly property MprisPlayer activePlayer: MprisController.activePlayer readonly property string cleanedTitle: StringUtils.cleanMusicTitle(activePlayer?.trackTitle) || qsTr("No media") @@ -45,7 +46,7 @@ Item { anchors.centerIn: parent width: parent.width implicitHeight: 32 - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 radius: Appearance.rounding.small } diff --git a/.config/quickshell/modules/bar/Resources.qml b/.config/quickshell/modules/bar/Resources.qml index db7741b2c..3f82c9950 100644 --- a/.config/quickshell/modules/bar/Resources.qml +++ b/.config/quickshell/modules/bar/Resources.qml @@ -8,9 +8,10 @@ import Quickshell.Io import Quickshell.Services.Mpris Rectangle { + property bool borderless: ConfigOptions.bar.borderless implicitWidth: rowLayout.implicitWidth + rowLayout.anchors.leftMargin + rowLayout.anchors.rightMargin implicitHeight: 32 - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 radius: Appearance.rounding.small RowLayout { diff --git a/.config/quickshell/modules/bar/UtilButtons.qml b/.config/quickshell/modules/bar/UtilButtons.qml index 62c1f45be..1f0a09f91 100644 --- a/.config/quickshell/modules/bar/UtilButtons.qml +++ b/.config/quickshell/modules/bar/UtilButtons.qml @@ -7,10 +7,12 @@ import Quickshell.Io import Quickshell.Hyprland Rectangle { + id: root + property bool borderless: ConfigOptions.bar.borderless Layout.alignment: Qt.AlignVCenter implicitWidth: rowLayout.implicitWidth + rowLayout.spacing * 2 implicitHeight: 32 - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 radius: Appearance.rounding.small RowLayout { diff --git a/.config/quickshell/modules/bar/Workspaces.qml b/.config/quickshell/modules/bar/Workspaces.qml index d3a701e8c..b00738f61 100644 --- a/.config/quickshell/modules/bar/Workspaces.qml +++ b/.config/quickshell/modules/bar/Workspaces.qml @@ -15,6 +15,7 @@ import Qt5Compat.GraphicalEffects Item { required property var bar + property bool borderless: ConfigOptions.bar.borderless readonly property HyprlandMonitor monitor: Hyprland.monitorFor(bar.screen) readonly property Toplevel activeWindow: ToplevelManager.activeToplevel @@ -66,7 +67,7 @@ Item { implicitHeight: 32 implicitWidth: rowLayout.implicitWidth + widgetPadding * 2 radius: Appearance.rounding.small - color: Appearance.colors.colLayer1 + color: borderless ? "transparent" : Appearance.colors.colLayer1 } // Scroll to switch workspaces diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index 8b8b5f4e9..e2bc31b33 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -25,6 +25,7 @@ Singleton { property int batteryLowThreshold: 20 property string topLeftIcon: "spark" // Options: distro, spark property bool showBackground: true + property bool borderless: false property QtObject resources: QtObject { property bool alwaysShowSwap: true property bool alwaysShowCpu: false From 1adfc4e89f581af6367cc668384fa8aa3282b576 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:17:12 +0200 Subject: [PATCH 71/84] make bar util buttons cleaner --- .config/quickshell/modules/bar/CircleUtilButton.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/modules/bar/CircleUtilButton.qml b/.config/quickshell/modules/bar/CircleUtilButton.qml index 0651211c3..c4673e743 100644 --- a/.config/quickshell/modules/bar/CircleUtilButton.qml +++ b/.config/quickshell/modules/bar/CircleUtilButton.qml @@ -1,5 +1,6 @@ import "root:/modules/common" import "root:/modules/common/widgets/" +import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -21,7 +22,9 @@ Button { background: Rectangle { anchors.fill: parent radius: Appearance.rounding.full - color: (button.down || extraActiveCondition) ? Appearance.colors.colLayer2Active : (button.hovered ? Appearance.colors.colLayer2Hover : Appearance.colors.colLayer2) + color: (button.down || extraActiveCondition) ? Appearance.colors.colLayer1Active : + (button.hovered ? Appearance.colors.colLayer1Hover : + ColorUtils.transparentize(Appearance.colors.colLayer1, 1)) } From b0ed1e75b6711a63510bc63f20490ed28127dd9b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:43:45 +0200 Subject: [PATCH 72/84] media controls: use blurred cover art for bg --- .../modules/mediaControls/PlayerControl.qml | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 8302440df..3ee445bb8 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -103,28 +103,39 @@ Item { // Player instance } - Rectangle { + Item { id: background anchors.fill: parent anchors.margins: Appearance.sizes.elevationMargin - color: blendedColors.colLayer0 - radius: root.popupRounding - LinearGradient { + FastBlur { anchors.fill: parent + source: Image { + id: blurredArt + anchors.fill: parent + visible: false + source: playerController.artUrl + sourceSize.width: parent.width + sourceSize.height: parent.height + fillMode: Image.PreserveAspectCrop + cache: false + antialiasing: true + asynchronous: true + } + radius: 128 // Increase for more blur layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { - width: background.width - height: background.height + width: blurredArt.width + height: blurredArt.height radius: root.popupRounding } } - start: Qt.point(0, 0) - end: Qt.point(background.width, background.height) - gradient: Gradient { - GradientStop { position: 0.0; color: ColorUtils.transparentize(artDominantColor, 0.6) } - GradientStop { position: 0.4; color: ColorUtils.transparentize(artDominantColor, 0.8) } + layer.smooth: true + + Rectangle { + anchors.fill: parent + color: ColorUtils.transparentize(blendedColors.colLayer0, 0.25) } } From 0e0bcd4617d2d6895515fe37b888ab86fcdd6fb7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:45:47 +0200 Subject: [PATCH 73/84] media controls: fix rounding --- .config/quickshell/modules/mediaControls/PlayerControl.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 3ee445bb8..c65cd37f8 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -126,8 +126,8 @@ Item { // Player instance layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { - width: blurredArt.width - height: blurredArt.height + width: background.width + height: background.height radius: root.popupRounding } } From 1287fbebd77e0c67aa48ec260c03e8c89eef4268 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:51:03 +0200 Subject: [PATCH 74/84] adjust media control blur radius --- .config/quickshell/modules/mediaControls/PlayerControl.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index c65cd37f8..8756db553 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -122,7 +122,7 @@ Item { // Player instance antialiasing: true asynchronous: true } - radius: 128 // Increase for more blur + radius: 100 // Increase for more blur layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { From bece489ee95ea52f2e36ade6f43ffac52eeafa47 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 10:58:09 +0200 Subject: [PATCH 75/84] media controls: adjust colors --- .config/quickshell/modules/mediaControls/PlayerControl.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 8756db553..18c551abb 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -87,11 +87,11 @@ Item { // Player instance } property QtObject blendedColors: QtObject { - property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, 0.6) + property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, 0.5) property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) - property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.7) + property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.5) property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) - property color colSubtext: ColorUtils.mix(Appearance.colors.colSubtext, artDominantColor, 0.5) + property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.1) property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5) property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) From fcdf2dc7f6dc4e678f1bf568d0b2e396250fd117 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 12:58:39 +0200 Subject: [PATCH 76/84] use multieffect for shadows --- .../modules/cheatsheet/Cheatsheet.qml | 23 +++++++------ .../quickshell/modules/common/Appearance.qml | 2 +- .../common/widgets/NotificationWidget.qml | 25 +++++++------- .../notificationPopup/NotificationPopup.qml | 2 +- .../onScreenDisplay/OsdValueIndicator.qml | 24 +++++++------- .../modules/overview/OverviewWidget.qml | 23 +++++++------ .../modules/overview/SearchWidget.qml | 21 ++++++------ .../modules/sidebarLeft/SidebarLeft.qml | 22 ++++++------- .../modules/sidebarLeft/anime/BooruImage.qml | 33 +++++++------------ .../modules/sidebarRight/SidebarRight.qml | 22 ++++++------- .../modules/sidebarRight/todo/TodoWidget.qml | 28 +++++----------- 11 files changed, 100 insertions(+), 125 deletions(-) diff --git a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml index 2474430a5..587e47150 100644 --- a/.config/quickshell/modules/cheatsheet/Cheatsheet.qml +++ b/.config/quickshell/modules/cheatsheet/Cheatsheet.qml @@ -5,8 +5,8 @@ import "root:/modules/common/widgets" import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts -import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell import Quickshell.Widgets @@ -65,6 +65,16 @@ Scope { // Scope implicitWidth: cheatsheetColumnLayout.implicitWidth + padding * 2 implicitHeight: cheatsheetColumnLayout.implicitHeight + padding * 2 + layer.enabled: true + layer.effect: MultiEffect { + source: cheatsheetBackground + anchors.fill: cheatsheetBackground + shadowEnabled: true + shadowVerticalOffset: 1 + shadowColor: Appearance.colors.colShadow + shadowBlur: 0.5 + } + Keys.onPressed: (event) => { // Esc to close if (event.key === Qt.Key_Escape) { cheatsheetRoot.hide() @@ -124,17 +134,6 @@ Scope { // Scope } } - // Shadow - DropShadow { - anchors.fill: cheatsheetBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: cheatsheetBackground - } - } } diff --git a/.config/quickshell/modules/common/Appearance.qml b/.config/quickshell/modules/common/Appearance.qml index 0c26b342a..381bfcab7 100644 --- a/.config/quickshell/modules/common/Appearance.qml +++ b/.config/quickshell/modules/common/Appearance.qml @@ -261,7 +261,7 @@ Singleton { property real searchWidthCollapsed: 260 property real searchWidth: 450 property real hyprlandGapsOut: 5 - property real elevationMargin: 7 + property real elevationMargin: 8 property real fabShadowRadius: 5 property real fabHoveredShadowRadius: 7 } diff --git a/.config/quickshell/modules/common/widgets/NotificationWidget.qml b/.config/quickshell/modules/common/widgets/NotificationWidget.qml index fd7106ba7..cb94c95cd 100644 --- a/.config/quickshell/modules/common/widgets/NotificationWidget.qml +++ b/.config/quickshell/modules/common/widgets/NotificationWidget.qml @@ -5,6 +5,7 @@ import "root:/modules/common/functions/color_utils.js" as ColorUtils import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Io @@ -191,6 +192,16 @@ Item { ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, Appearance.colors.colLayer2, 0.35) : Appearance.colors.colLayer2 radius: Appearance.rounding.normal + layer.enabled: true + layer.effect: MultiEffect { + source: notificationBackground + anchors.fill: notificationBackground + shadowEnabled: popup + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + Behavior on x { enabled: enableAnimation NumberAnimation { @@ -208,20 +219,6 @@ Item { } } } - - Loader { - active: popup - anchors.fill: notificationBackground - sourceComponent: DropShadow { - id: notificationShadow - source: notificationBackground - radius: 5 - samples: radius * 2 + 1 - color: Appearance.colors.colShadow - verticalOffset: 2 - horizontalOffset: 0 - } - } } diff --git a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml index 79e855c2f..f4373f14d 100644 --- a/.config/quickshell/modules/notificationPopup/NotificationPopup.qml +++ b/.config/quickshell/modules/notificationPopup/NotificationPopup.qml @@ -99,7 +99,7 @@ Scope { ColumnLayout { // Scrollable window content id: columnLayout anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - Appearance.sizes.hyprlandGapsOut * 2 + width: parent.width - Appearance.sizes.elevationMargin * 2 spacing: 0 // The widgets themselves have margins for spacing // Notifications are added by the above signal handlers diff --git a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml index 36a9748b2..58d4ac5ca 100644 --- a/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml +++ b/.config/quickshell/modules/onScreenDisplay/OsdValueIndicator.qml @@ -3,10 +3,11 @@ import "root:/modules/common" import "root:/modules/common/widgets" import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Widgets -import Qt5Compat.GraphicalEffects +// import Qt5Compat.GraphicalEffects Item { id: root @@ -31,6 +32,16 @@ Item { color: Appearance.colors.colLayer0 implicitWidth: valueRow.implicitWidth + layer.enabled: true + layer.effect: MultiEffect { + source: valueIndicator + anchors.fill: valueIndicator + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + RowLayout { // Icon on the left, stuff on the right id: valueRow Layout.margins: 10 @@ -94,15 +105,4 @@ Item { } } } - - DropShadow { - id: valueShadow - anchors.fill: valueIndicator - source: valueIndicator - radius: Appearance.sizes.elevationMargin - samples: radius * 2 + 1 - color: Appearance.colors.colShadow - verticalOffset: 2 - horizontalOffset: 0 - } } \ No newline at end of file diff --git a/.config/quickshell/modules/overview/OverviewWidget.qml b/.config/quickshell/modules/overview/OverviewWidget.qml index 4de7beed6..f4498408e 100644 --- a/.config/quickshell/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/modules/overview/OverviewWidget.qml @@ -3,8 +3,8 @@ import "root:/services/" import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/color_utils.js" as ColorUtils -import Qt5Compat.GraphicalEffects import QtQuick +import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Io @@ -59,6 +59,16 @@ Item { color: Appearance.colors.colLayer0 radius: Appearance.rounding.screenRounding * root.scale + 5 * 2 + layer.enabled: true + layer.effect: MultiEffect { + source: overviewBackground + anchors.fill: overviewBackground + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + ColumnLayout { id: workspaceColumnLayout @@ -210,15 +220,4 @@ Item { } } } - - DropShadow { - z: -9999 - anchors.fill: overviewBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: radius * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: overviewBackground - } } diff --git a/.config/quickshell/modules/overview/SearchWidget.qml b/.config/quickshell/modules/overview/SearchWidget.qml index 887480de1..4320a96a1 100644 --- a/.config/quickshell/modules/overview/SearchWidget.qml +++ b/.config/quickshell/modules/overview/SearchWidget.qml @@ -7,6 +7,7 @@ import Qt5Compat.GraphicalEffects import Qt.labs.platform import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts import Quickshell import Quickshell.Io @@ -160,6 +161,15 @@ Item { // Wrapper implicitHeight: columnLayout.implicitHeight radius: Appearance.rounding.large color: Appearance.colors.colLayer0 + layer.enabled: true + layer.effect: MultiEffect { + source: searchWidgetContent + anchors.fill: searchWidgetContent + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } ColumnLayout { id: columnLayout @@ -366,15 +376,4 @@ Item { // Wrapper } } - - DropShadow { - id: searchWidgetShadow - anchors.fill: searchWidgetContent - source: searchWidgetContent - radius: Appearance.sizes.elevationMargin - samples: radius * 2 + 1 - color: Appearance.colors.colShadow - verticalOffset: 2 - horizontalOffset: 0 - } } \ No newline at end of file diff --git a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml index 2f1bad740..a17e7f582 100644 --- a/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml +++ b/.config/quickshell/modules/sidebarLeft/SidebarLeft.qml @@ -5,6 +5,7 @@ import "root:/modules/common/widgets" import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QtQuick.Effects import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell @@ -80,6 +81,16 @@ Scope { // Scope radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 focus: sidebarRoot.visible + layer.enabled: true + layer.effect: MultiEffect { + source: sidebarLeftBackground + anchors.fill: sidebarLeftBackground + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + Behavior on width { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } @@ -161,17 +172,6 @@ Scope { // Scope } } - // Shadow - DropShadow { - anchors.fill: sidebarLeftBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: sidebarLeftBackground - } - } } diff --git a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml index 3bc9b461d..4a754f813 100644 --- a/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/modules/sidebarLeft/anime/BooruImage.qml @@ -8,10 +8,11 @@ import Qt.labs.platform import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QtQuick.Effects +import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Io import Quickshell.Hyprland -import Qt5Compat.GraphicalEffects Button { id: root @@ -137,6 +138,16 @@ Button { implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2 implicitWidth: contextMenuColumnLayout.implicitWidth + layer.enabled: true + layer.effect: MultiEffect { + source: contextMenu + anchors.fill: contextMenu + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + Behavior on opacity { NumberAnimation { duration: Appearance.animation.elementMoveFast.duration @@ -185,26 +196,6 @@ Button { } } } - - DropShadow { - opacity: root.showActions ? 1 : 0 - visible: opacity > 0 - anchors.fill: contextMenu - source: contextMenu - radius: Appearance.sizes.elevationMargin - samples: radius * 2 + 1 - color: Appearance.colors.colShadow - verticalOffset: 2 - horizontalOffset: 0 - - Behavior on opacity { - NumberAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - } - } } } } diff --git a/.config/quickshell/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/modules/sidebarRight/SidebarRight.qml index e7dd4d9eb..a8013e87b 100644 --- a/.config/quickshell/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/modules/sidebarRight/SidebarRight.qml @@ -7,6 +7,7 @@ import "./quickToggles/" import QtQuick import QtQuick.Controls import QtQuick.Layouts +import QtQuick.Effects import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell @@ -65,6 +66,16 @@ Scope { color: Appearance.colors.colLayer0 radius: Appearance.rounding.screenRounding - Appearance.sizes.elevationMargin + 1 + layer.enabled: true + layer.effect: MultiEffect { + source: sidebarRightBackground + anchors.fill: sidebarRightBackground + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + Keys.onPressed: (event) => { if (event.key === Qt.Key_Escape) { sidebarRoot.hide(); @@ -164,17 +175,6 @@ Scope { } } - // Shadow - DropShadow { - anchors.fill: sidebarRightBackground - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - source: sidebarRightBackground - } - } } diff --git a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml index b18e03fb7..8b32fd855 100644 --- a/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml +++ b/.config/quickshell/modules/sidebarRight/todo/TodoWidget.qml @@ -4,8 +4,8 @@ import "root:/services" import "root:/modules/common/functions/color_utils.js" as ColorUtils import QtQuick import QtQuick.Controls +import QtQuick.Effects import QtQuick.Layouts -import Qt5Compat.GraphicalEffects Item { id: root @@ -173,25 +173,15 @@ Item { Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } - } - DropShadow { - id: fabShadow - anchors.fill: fabBackground - source: fabBackground - horizontalOffset: 0 - verticalOffset: fabButton.hovered ? 4 : 2 - radius: fabButton.hovered ? Appearance.sizes.fabHoveredShadowRadius : Appearance.sizes.fabShadowRadius - samples: fabShadow.radius * 2 + 1 - color: Appearance.colors.colShadow - z: fabBackground.z - 1 - - Behavior on verticalOffset { - NumberAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } + layer.enabled: true + layer.effect: MultiEffect { + source: fabBackground + anchors.fill: fabBackground + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowBlur: 0.6 + shadowVerticalOffset: fabButton.hovered ? 4 : 2 } } From d0c180f8fc98a05447f482655be130b6757a8eac Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 13:48:19 +0200 Subject: [PATCH 77/84] media controls: use multieffect instead of qt5 graphical effects --- .../modules/mediaControls/PlayerControl.qml | 79 ++++++++----------- 1 file changed, 35 insertions(+), 44 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index 18c551abb..d3d60a44f 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -3,8 +3,8 @@ import "root:/modules/common/widgets" import "root:/services" import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/color_utils.js" as ColorUtils -import Qt5Compat.GraphicalEffects import QtQuick +import QtQuick.Effects import QtQuick.Layouts import QtQuick.Controls import Quickshell @@ -91,7 +91,7 @@ Item { // Player instance property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, artDominantColor, 0.5) property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, artDominantColor, 0.5) property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) - property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.1) + property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, artDominantColor, 0.5) property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3primary, artDominantColor), artDominantColor, 0.5) property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, artDominantColor), artDominantColor, 0.3) property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, artDominantColor), artDominantColor, 0.3) @@ -103,39 +103,49 @@ Item { // Player instance } - Item { + ClippingRectangle { // Background id: background anchors.fill: parent anchors.margins: Appearance.sizes.elevationMargin + color: blendedColors.colLayer0 + radius: root.popupRounding - FastBlur { + layer.enabled: true + layer.effect: MultiEffect { + source: background + anchors.fill: background + shadowEnabled: true + shadowColor: Appearance.colors.colShadow + shadowVerticalOffset: 1 + shadowBlur: 0.5 + } + + Image { + id: blurredArt anchors.fill: parent - source: Image { - id: blurredArt - anchors.fill: parent - visible: false - source: playerController.artUrl - sourceSize.width: parent.width - sourceSize.height: parent.height - fillMode: Image.PreserveAspectCrop - cache: false - antialiasing: true - asynchronous: true - } - radius: 100 // Increase for more blur + visible: true + source: playerController.artUrl + sourceSize.width: background.width + sourceSize.height: background.height + fillMode: Image.PreserveAspectCrop + cache: false + antialiasing: true + asynchronous: true + layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: background.width - height: background.height - radius: root.popupRounding - } + layer.effect: MultiEffect { + source: blurredArt + anchors.fill: blurredArt + saturation: 0.2 + blurEnabled: true + blurMax: 100 + blur: 1 } - layer.smooth: true Rectangle { anchors.fill: parent color: ColorUtils.transparentize(blendedColors.colLayer0, 0.25) + radius: root.popupRounding } } @@ -144,7 +154,7 @@ Item { // Player instance anchors.margins: root.contentPadding spacing: 15 - Rectangle { // Art backgrounmd + ClippingRectangle { // Art backgrounmd Layout.fillHeight: true implicitWidth: height radius: root.artRounding @@ -165,15 +175,6 @@ Item { // Player instance height: size sourceSize.width: size sourceSize.height: size - - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: mediaArt.size - height: mediaArt.size - radius: root.artRounding - } - } } } @@ -278,14 +279,4 @@ Item { // Player instance } } } - - DropShadow { - anchors.fill: background - source: background - horizontalOffset: 0 - verticalOffset: 2 - radius: Appearance.sizes.elevationMargin - samples: Appearance.sizes.elevationMargin * 2 + 1 // Ideally should be 2 * radius + 1, see qt docs - color: Appearance.colors.colShadow - } } \ No newline at end of file From b1b6e837ba411899b8d9f467a2f27880425f9cae Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:03:34 +0200 Subject: [PATCH 78/84] media controls: go back to qt5 opacity mask to prevent crash --- .../modules/mediaControls/PlayerControl.qml | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/.config/quickshell/modules/mediaControls/PlayerControl.qml b/.config/quickshell/modules/mediaControls/PlayerControl.qml index d3d60a44f..7217e0b9f 100644 --- a/.config/quickshell/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/modules/mediaControls/PlayerControl.qml @@ -3,6 +3,7 @@ import "root:/modules/common/widgets" import "root:/services" import "root:/modules/common/functions/string_utils.js" as StringUtils import "root:/modules/common/functions/color_utils.js" as ColorUtils +import Qt5Compat.GraphicalEffects import QtQuick import QtQuick.Effects import QtQuick.Layouts @@ -103,7 +104,7 @@ Item { // Player instance } - ClippingRectangle { // Background + Rectangle { // Background id: background anchors.fill: parent anchors.margins: Appearance.sizes.elevationMargin @@ -111,13 +112,12 @@ Item { // Player instance radius: root.popupRounding layer.enabled: true - layer.effect: MultiEffect { - source: background - anchors.fill: background - shadowEnabled: true - shadowColor: Appearance.colors.colShadow - shadowVerticalOffset: 1 - shadowBlur: 0.5 + layer.effect: OpacityMask { + maskSource: Rectangle { + width: background.width + height: background.height + radius: background.radius + } } Image { @@ -154,12 +154,22 @@ Item { // Player instance anchors.margins: root.contentPadding spacing: 15 - ClippingRectangle { // Art backgrounmd + Rectangle { // Art background + id: artBackground Layout.fillHeight: true implicitWidth: height radius: root.artRounding color: blendedColors.colLayer1 + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: artBackground.width + height: artBackground.height + radius: artBackground.radius + } + } + Image { // Art image id: mediaArt property int size: parent.height From 2ad293221c6e92d3f49ce4070adda747a83e00ec Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:04:09 +0200 Subject: [PATCH 79/84] remove debug print --- .config/quickshell/services/Ai.qml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.config/quickshell/services/Ai.qml b/.config/quickshell/services/Ai.qml index 949fa19cf..7c33cd249 100644 --- a/.config/quickshell/services/Ai.qml +++ b/.config/quickshell/services/Ai.qml @@ -176,7 +176,6 @@ Singleton { } function getModel() { - console.log("MODEL:", currentModelId); return models[currentModelId]; } @@ -425,7 +424,7 @@ Singleton { stdout: SplitParser { onRead: data => { - console.log("RAW DATA: ", data); + // console.log("RAW DATA: ", data); if (data.length === 0) return; // Handle response line From e1ee645e87721ad995bb2cae38f1c34a711ad470 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:23:39 +0200 Subject: [PATCH 80/84] use better-control instead of gnome settings and blueberry --- .config/ags/modules/.configuration/default_options.jsonc | 6 +++--- .config/hypr/hyprland/keybinds.conf | 2 +- .config/quickshell/modules/common/ConfigOptions.qml | 6 +++--- arch-packages/illogical-impulse-gnome/PKGBUILD | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.config/ags/modules/.configuration/default_options.jsonc b/.config/ags/modules/.configuration/default_options.jsonc index e20975e1b..eaddb9eb1 100644 --- a/.config/ags/modules/.configuration/default_options.jsonc +++ b/.config/ags/modules/.configuration/default_options.jsonc @@ -46,10 +46,10 @@ "fakeScreenRounding": 2 // 0: None | 1: Always | 2: When not fullscreen }, "apps": { - "bluetooth": "blueberry", + "bluetooth": "better-control --bluetooth", "imageViewer": "loupe", - "network": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi", - "settings": "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center", + "network": "better-control --wifi", + "settings": "better-control", "taskManager": "gnome-usage", "terminal": "foot" // This is only for shell actions }, diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 496e9899e..4026e48d4 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -198,8 +198,8 @@ bind = Super+Alt, E, exec, thunar # [hidden] bind = Super, W, exec, zen-browser # [hidden] bind = Super+Shift, W, exec, wps # WPS Office bind = Ctrl+Super, V, exec, pavucontrol # Pavucontrol (volume mixer) +bind = Super, I, exec, better-control # Better Control (settings app) bind = Super, X, exec, gnome-text-editor --new-window # GNOME Text Editor -bind = Super, I, exec, XDG_CURRENT_DESKTOP="gnome" gnome-control-center # GNOME Settings bind = Ctrl+Shift, Escape, exec, gnome-system-monitor # GNOME System monitor # Cursed stuff diff --git a/.config/quickshell/modules/common/ConfigOptions.qml b/.config/quickshell/modules/common/ConfigOptions.qml index e2bc31b33..ce4ddc0f9 100644 --- a/.config/quickshell/modules/common/ConfigOptions.qml +++ b/.config/quickshell/modules/common/ConfigOptions.qml @@ -13,10 +13,10 @@ Singleton { } property QtObject apps: QtObject { - property string bluetooth: "blueberry" + property string bluetooth: "better-control --bluetooth" property string imageViewer: "loupe" - property string network: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center wifi" - property string settings: "XDG_CURRENT_DESKTOP=\"gnome\" gnome-control-center" + property string network: "better-control --wifi" + property string settings: "better-control" property string taskManager: "gnome-usage" property string terminal: "foot" // This is only for shell actions } diff --git a/arch-packages/illogical-impulse-gnome/PKGBUILD b/arch-packages/illogical-impulse-gnome/PKGBUILD index 33af4b8ba..3e773bdaf 100644 --- a/arch-packages/illogical-impulse-gnome/PKGBUILD +++ b/arch-packages/illogical-impulse-gnome/PKGBUILD @@ -7,6 +7,6 @@ license=(None) depends=( polkit-gnome gnome-keyring - gnome-control-center - blueberry networkmanager + networkmanager + better-control-git ) From 249ee0b55f1fb8e9a09fb8b87f90dfe7e9c135c7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:23:56 +0200 Subject: [PATCH 81/84] hyprland: media controls keybind --- .config/hypr/hyprland/keybinds.conf | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 4026e48d4..5482e9a79 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -23,6 +23,7 @@ bindd = Super, A, Toggle left sidebar, global, quickshell:sidebarLeftToggle # To bind = Super, O, global, quickshell:sidebarLeftToggle # [hidden] bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # Toggle right sidebar bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet +bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu bind = Ctrl+Alt, Delete, exec, qs ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] From b65b677839336b86160110cd68f54880ac2a9fa8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:24:37 +0200 Subject: [PATCH 82/84] dont let clipboard paste on select (very nasty...) --- .config/hypr/hyprland/keybinds.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 5482e9a79..e554838e6 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -47,7 +47,7 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi ##! Utilities # Screenshot, Record, OCR, Color picker, Clipboard history -bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel && exit || entry="$(cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode)"; [ -n "$entry" ] && wl-copy -- "$entry" && wtype -- "${entry//$'\n'/ }" # Clipboard history +bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel && exit || entry="$(cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode)"; [ -n "$entry" ] && wl-copy -- "$entry" # Clipboard history >> clipboard bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Emoji bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate From 44b5b25c0dac19d9e0176e57069bbaf7be678512 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:25:58 +0200 Subject: [PATCH 83/84] clipboard history: back to simple command that works --- .config/hypr/hyprland/keybinds.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index e554838e6..18a0ab955 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -47,7 +47,7 @@ bindd = Ctrl+Shift+Alt+Super, Delete, Shutdown, exec, systemctl poweroff || logi ##! Utilities # Screenshot, Record, OCR, Color picker, Clipboard history -bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel && exit || entry="$(cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode)"; [ -n "$entry" ] && wl-copy -- "$entry" # Clipboard history >> clipboard +bindd = Super, V, Copy clipboard history entry, exec, pkill fuzzel || cliphist list | fuzzel --match-mode fzf --dmenu | cliphist decode | wl-copy # Clipboard history >> clipboard bindd = Super, Period, Copy an emoji, exec, pkill fuzzel || ~/.local/bin/fuzzel-emoji # Emoji bindd = Super+Shift, S, Screen snip, exec, grimblast --freeze copy area # Screen snip >> clipboard bindd = Super+Shift+Alt, S, Screen snip and annotate, exec, grim -g "$(slurp)" - | swappy -f - # Screen snip and annotate From 0f8a48ed0d4373cdafe258400cf4673da9c04e83 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 20 May 2025 23:45:34 +0200 Subject: [PATCH 84/84] boorus: create homework folder --- .config/quickshell/modules/sidebarLeft/Anime.qml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/modules/sidebarLeft/Anime.qml b/.config/quickshell/modules/sidebarLeft/Anime.qml index ff529219d..fb1182f80 100644 --- a/.config/quickshell/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/modules/sidebarLeft/Anime.qml @@ -4,6 +4,7 @@ import "root:/modules/common" import "root:/modules/common/widgets" import "root:/modules/common/functions/fuzzysort.js" as Fuzzy import "root:/modules/common/functions/string_utils.js" as StringUtils +import "root:/modules/common/functions/file_utils.js" as FileUtils import "./anime/" import Qt.labs.platform import QtQuick @@ -19,9 +20,9 @@ Item { property var panelWindow property var inputField: tagInputField readonly property var responses: Booru.responses - property string previewDownloadPath: `${XdgDirectories.cache}/media/waifus`.replace("file://", "") - property string downloadPath: (XdgDirectories.pictures + "/homework").replace("file://", "") - property string nsfwPath: (XdgDirectories.pictures + "/homework/🌶️").replace("file://", "") + property string previewDownloadPath: FileUtils.trimFileProtocol(`${XdgDirectories.cache}/media/waifus`) + property string downloadPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework") + property string nsfwPath: FileUtils.trimFileProtocol(XdgDirectories.pictures + "/homework/🌶️") property string commandPrefix: "/" property real scrollOnNewResponse: 100 property int tagSuggestionDelay: 210 @@ -37,7 +38,8 @@ Item { } Component.onCompleted: { - Hyprland.dispatch(`exec rm -rf ${previewDownloadPath} && mkdir -p ${previewDownloadPath}`) + Hyprland.dispatch(`exec rm -rf '${previewDownloadPath}' && mkdir -p '${previewDownloadPath}'`) + Hyprland.dispatch(`exec mkdir -p '${downloadPath}' && mkdir -p '${downloadPath}'`) } property var allCommands: [