From e5033c3213adabb872f747a22a1678278230b059 Mon Sep 17 00:00:00 2001 From: Gwendolyn Page Date: Thu, 11 Sep 2025 17:19:05 -0500 Subject: [PATCH 01/60] feat(ai): Add auto-scroll functionality to AI chat MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add shouldAutoScroll property to track when user wants auto-scrolling - Auto-scroll when user sends a message to see the response - Auto-scroll during streaming when content height changes - Auto-scroll when new messages are added to the conversation - Stop auto-scroll when user manually scrolls up (preserves user intent) - Resume auto-scroll when user scrolls back to bottom 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../ii/modules/sidebarLeft/AiChat.qml | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 22a64412c..6c69603f1 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -209,6 +209,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) else { Ai.sendUserMessage(inputText); } + + // Always scroll to bottom when user sends a message + messageListView.shouldAutoScroll = true + messageListView.positionViewAtEnd() } Process { @@ -316,6 +320,28 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) property int lastResponseLength: 0 + // Simple auto-scroll state tracking (proven chat pattern) + property bool shouldAutoScroll: true + + // Track when user scrolls - simple and reliable + onContentYChanged: { + shouldAutoScroll = atYEnd + } + + // Auto-scroll when content height changes (during streaming) + onContentHeightChanged: { + if (shouldAutoScroll) { + positionViewAtEnd() + } + } + + // Auto-scroll when new messages are added + onCountChanged: { + if (shouldAutoScroll) { + positionViewAtEnd() + } + } + clip: true layer.enabled: true layer.effect: OpacityMask { From a719ca684cbb35057e171a74073efde1a1c808e3 Mon Sep 17 00:00:00 2001 From: Gwendolyn Page Date: Thu, 11 Sep 2025 17:49:10 -0500 Subject: [PATCH 02/60] fix(ai): Fix JSON injection vulnerability in primary-buffer-query.sh MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Fix critical JSON injection vulnerability by properly escaping clipboard content using jq - Add content length limiting (2000 chars) to prevent overflow attacks - Use proper JSON payload construction with jq to ensure safe API calls - Add silent curl flag and error handling for reliability This addresses a security issue where malicious clipboard content could break out of JSON strings and potentially execute arbitrary code. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- .../hypr/hyprland/scripts/ai/primary-buffer-query.sh | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh b/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh index 794414554..eac998afa 100755 --- a/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh +++ b/.config/hypr/hyprland/scripts/ai/primary-buffer-query.sh @@ -23,13 +23,15 @@ while [[ "$#" -gt 0 ]]; do done # Combine the system prompt with the clipboard content -content=$(wl-paste -p | tr '\n' ' ') -prompt="$SYSTEM_PROMPT $content" +content=$(wl-paste -p | tr '\n' ' ' | head -c 2000) # 2000 char limit to prevent overflow + +# Properly escape content for JSON using jq +prompt_json=$(jq -n --arg system_prompt "$SYSTEM_PROMPT" --arg content "$content" '$system_prompt + " " + $content') # Make the API call with the specified or default model -response=$(curl http://localhost:11434/api/generate -d \ - "{\"model\": \"$model\",\"prompt\": \"$prompt\",\"stream\": false}" \ - | jq -r '.response') +api_payload=$(jq -n --arg model "$model" --argjson prompt "$prompt_json" --argjson stream false \ + '{model: $model, prompt: $prompt, stream: $stream}') +response=$(curl -s http://localhost:11434/api/generate -d "$api_payload" | jq -r '.response' 2>/dev/null) # Check if content is a single line and no longer than 30 characters if [[ ${#content} -le 30 && "$content" != *$'\n'* ]]; then From 426804304c386e24e88a4d8d4bd84c42d3eefcec Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:22:47 +0200 Subject: [PATCH 03/60] wallpaper selector: add random button (#1997) --- .../WallpaperSelectorContent.qml | 29 ++++++++++++++++--- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index bc25ce669..209ac1ded 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -40,6 +40,13 @@ MouseArea { } } + function selectWallpaperPath(filePath) { + if (filePath && filePath.length > 0) { + Wallpapers.select(filePath, root.useDarkMode); + filterField.text = ""; + } + } + acceptedButtons: Qt.BackButton | Qt.ForwardButton onPressed: event => { if (event.button === Qt.BackButton) { @@ -267,8 +274,7 @@ MouseArea { function activateCurrent() { const filePath = grid.model.get(currentIndex, "filePath") - Wallpapers.select(filePath, root.useDarkMode); - filterField.text = ""; + root.selectWallpaperPath(filePath); } model: Wallpapers.folderModel @@ -287,8 +293,7 @@ MouseArea { } onActivated: { - Wallpapers.select(fileModelData.filePath, root.useDarkMode); - filterField.text = ""; + root.selectWallpaperPath(fileModelData.filePath); } } @@ -330,6 +335,22 @@ MouseArea { } } + ToolbarButton { + implicitWidth: height + onClicked: { + const randomIndex = Math.floor(Math.random() * Wallpapers.folderModel.count); + const filePath = Wallpapers.folderModel.get(randomIndex, "filePath"); + root.selectWallpaperPath(filePath); + } + contentItem: MaterialSymbol { + text: "ifl" + iconSize: Appearance.font.pixelSize.larger + } + StyledToolTip { + content: Translation.tr("Pick random from this folder") + } + } + ToolbarButton { implicitWidth: height onClicked: root.useDarkMode = !root.useDarkMode From 4a6fcb4f4cc8e94d013812701a2be92c59802592 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:30:30 +0200 Subject: [PATCH 04/60] wallpaper selector: hide Homework folder if not weeb, fix toolbar button alignment --- .../WallpaperSelectorContent.qml | 21 +++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index 209ac1ded..e5a5be372 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -171,7 +171,7 @@ MouseArea { { icon: "movie", name: "Videos", path: Directories.videos }, { icon: "", name: "---", path: "INTENTIONALLY_INVALID_DIR" }, { icon: "wallpaper", name: "Wallpapers", path: `${Directories.pictures}/Wallpapers` }, - { icon: "favorite", name: "Homework", path: `${Directories.pictures}/homework` }, + ...(Config.options.policies.weeb === 1 ? [{ icon: "favorite", name: "Homework", path: `${Directories.pictures}/homework` }] : []), ] delegate: RippleButton { id: quickDirButton @@ -327,6 +327,8 @@ MouseArea { Config.options.wallpaperSelector.useSystemFileDialog = true } contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter text: "open_in_new" iconSize: Appearance.font.pixelSize.larger } @@ -343,6 +345,8 @@ MouseArea { root.selectWallpaperPath(filePath); } contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter text: "ifl" iconSize: Appearance.font.pixelSize.larger } @@ -355,11 +359,13 @@ MouseArea { implicitWidth: height onClicked: root.useDarkMode = !root.useDarkMode contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter text: root.useDarkMode ? "dark_mode" : "light_mode" iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Click to toggle light/dark mode (applied when wallpaper is chosen)") + content: Translation.tr("Click to toggle light/dark mode\n(applied when wallpaper is chosen)") } } @@ -399,11 +405,18 @@ MouseArea { } ToolbarButton { + implicitWidth: height onClicked: { GlobalStates.wallpaperSelectorOpen = false; } - contentItem: StyledText { - text: "Cancel" + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: "cancel_presentation" + iconSize: Appearance.font.pixelSize.larger + } + StyledToolTip { + content: Translation.tr("Cancel wallpaper selection") } } } From cf4aa1256d8f4933fdab5e00cdfe56ef25afc051 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 16 Sep 2025 08:32:07 +0200 Subject: [PATCH 05/60] wallpaper selector: make quick dir icon filled when selected --- .../ii/modules/wallpaperSelector/WallpaperSelectorContent.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index e5a5be372..7e544d218 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -192,6 +192,7 @@ MouseArea { color: quickDirButton.toggled ? Appearance.colors.colOnSecondaryContainer : Appearance.colors.colOnLayer1 iconSize: Appearance.font.pixelSize.larger text: quickDirButton.modelData.icon + fill: quickDirButton.toggled ? 1 : 0 } StyledText { Layout.fillWidth: true From b984d6794eda473c4529ae397f874f0559bed521 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 17 Sep 2025 11:35:42 +0200 Subject: [PATCH 06/60] wallpaper selector: fix thumbnail generation (#1978, #1902) --- .config/quickshell/ii/modules/common/functions/FileUtils.qml | 4 +++- .config/quickshell/ii/services/Wallpapers.qml | 5 +++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/ii/modules/common/functions/FileUtils.qml b/.config/quickshell/ii/modules/common/functions/FileUtils.qml index 4ed8d8cb1..e7f5e6100 100644 --- a/.config/quickshell/ii/modules/common/functions/FileUtils.qml +++ b/.config/quickshell/ii/modules/common/functions/FileUtils.qml @@ -10,7 +10,9 @@ Singleton { * @returns {string} */ function trimFileProtocol(str) { - return str.startsWith("file://") ? str.slice(7) : str; + let s = str; + if (typeof s !== "string") s = str.toString(); // Convert to string if it's an url or whatever + return s.startsWith("file://") ? s.slice(7) : s; } /** diff --git a/.config/quickshell/ii/services/Wallpapers.qml b/.config/quickshell/ii/services/Wallpapers.qml index 4afffa1ff..07e984ff2 100644 --- a/.config/quickshell/ii/services/Wallpapers.qml +++ b/.config/quickshell/ii/services/Wallpapers.qml @@ -16,7 +16,7 @@ Singleton { id: root property string thumbgenScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/thumbgen.py` - property string generateThumbnailsMagicScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/generate-thumbnails-magick.sh` + property string generateThumbnailsMagickScriptPath: `${FileUtils.trimFileProtocol(Directories.scriptPath)}/thumbnails/generate-thumbnails-magick.sh` property alias directory: folderModel.folder readonly property string effectiveDirectory: FileUtils.trimFileProtocol(folderModel.folder.toString()) property url defaultFolder: Qt.resolvedUrl(`${Directories.pictures}/Wallpapers`) @@ -136,12 +136,13 @@ Singleton { // Thumbnail generation function generateThumbnail(size: string) { + // console.log("[Wallpapers] Updating thumbnails") if (!["normal", "large", "x-large", "xx-large"].includes(size)) throw new Error("Invalid thumbnail size"); thumbgenProc.directory = root.directory thumbgenProc.running = false thumbgenProc.command = [ "bash", "-c", - `${thumbgenScriptPath} --size ${size} --machine_progress -d ${root.directory} || ${generateThumbnailsMagicScriptPath} --size ${size} -d ${root.directory}`, + `${thumbgenScriptPath} --size ${size} --machine_progress -d ${FileUtils.trimFileProtocol(root.directory)} || ${generateThumbnailsMagickScriptPath} --size ${size} -d ${root.directory}`, ] root.thumbnailGenerationProgress = 0 thumbgenProc.running = true From c41364fb16c3929663d136cc9c97ee98aa6fefea Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 17 Sep 2025 23:39:56 +0200 Subject: [PATCH 07/60] lock: rename bg blur config option, no blur by default --- .../ii/modules/background/Background.qml | 17 ++++++++++------- .config/quickshell/ii/modules/common/Config.qml | 4 ++-- 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 8b054fc91..84eadd9db 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -57,7 +57,7 @@ Variants { property real clockX: (modelData.width / 2) property real clockY: (modelData.height / 2) property var textHorizontalAlignment: { - if (Config.options.background.blur.enable && Config.options.background.blur.centerClock && GlobalStates.screenLocked) + if (Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) return Text.AlignHCenter; if (clockX < screen.width / 3) return Text.AlignLeft; @@ -66,10 +66,13 @@ Variants { return Text.AlignHCenter; } // Colors + property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable) property color dominantColor: Appearance.colors.colPrimary property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 - property color colText: CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) - property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.blur.enable) + property color colText: (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colSecondary : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + Behavior on colText { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } // Layer props screen: modelData @@ -227,9 +230,9 @@ Variants { Loader { id: blurLoader - active: Config.options.background.blur.enable && (GlobalStates.screenLocked || scaleAnim.running) + active: Config.options.background.lockBlur.enable && (GlobalStates.screenLocked || scaleAnim.running) anchors.fill: wallpaper - scale: GlobalStates.screenLocked ? Config.options.background.blur.extraZoom : 1 + scale: GlobalStates.screenLocked ? Config.options.background.lockBlur.extraZoom : 1 Behavior on scale { NumberAnimation { id: scaleAnim @@ -240,7 +243,7 @@ Variants { } sourceComponent: GaussianBlur { source: wallpaper - radius: GlobalStates.screenLocked ? Config.options.background.blur.radius : 0 + radius: GlobalStates.screenLocked ? Config.options.background.lockBlur.radius : 0 samples: radius * 2 + 1 Rectangle { @@ -355,7 +358,7 @@ Variants { leftMargin: -5 rightMargin: -5 } - opacity: GlobalStates.screenLocked && (!Config.options.background.blur.enable || Config.options.background.blur.showLockedText) ? 1 : 0 + opacity: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) ? 1 : 0 visible: opacity > 0 Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index c95b148c7..2fef550c6 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -135,8 +135,8 @@ Singleton { property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property bool enableSidebar: true } - property JsonObject blur: JsonObject { - property bool enable: true + property JsonObject lockBlur: JsonObject { + property bool enable: false property int radius: 100 property bool centerClock: true property bool showLockedText: true From 103d349c5f79909564ad686473880cedffdd96dd Mon Sep 17 00:00:00 2001 From: 0blivi0nis <182329535+0blivi0nis@users.noreply.github.com> Date: Wed, 17 Sep 2025 23:49:45 +0000 Subject: [PATCH 08/60] feat(sideright): add Flatpak support for EasyEffects service and toggle --- .../sidebarRight/quickToggles/EasyEffectsToggle.qml | 2 +- .config/quickshell/ii/services/EasyEffects.qml | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml index 10b866007..af428c54b 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml @@ -21,7 +21,7 @@ QuickToggleButton { } altAction: () => { - Quickshell.execDetached(["easyeffects"]) + Quickshell.execDetached(["bash", "-c", "flatpak run com.github.wwmm.easyeffects || easyeffects"]) GlobalStates.sidebarRightOpen = false } diff --git a/.config/quickshell/ii/services/EasyEffects.qml b/.config/quickshell/ii/services/EasyEffects.qml index 8767a9d4e..4117e50c4 100644 --- a/.config/quickshell/ii/services/EasyEffects.qml +++ b/.config/quickshell/ii/services/EasyEffects.qml @@ -25,12 +25,12 @@ Singleton { function disable() { root.active = false - Quickshell.execDetached(["pkill", "easyeffects"]) + Quickshell.execDetached(["bash", "-c", "pkill easyeffects || flatpak pkill com.github.wwmm.easyeffects"]) } function enable() { root.active = true - Quickshell.execDetached(["easyeffects", "--gapplication-service"]) + Quickshell.execDetached(["bash", "-c", "easyeffects --gapplication-service || flatpak run com.github.wwmm.easyeffects --gapplication-service"]) } function toggle() { @@ -44,7 +44,7 @@ Singleton { Process { id: fetchAvailabilityProc running: true - command: ["bash", "-c", "command -v easyeffects"] + command: ["bash", "-c", "command -v easyeffects || flatpak info com.github.wwmm.easyeffects > /dev/null 2>&1"] onExited: (exitCode, exitStatus) => { root.available = exitCode === 0 } @@ -53,7 +53,7 @@ Singleton { Process { id: fetchActiveStateProc running: true - command: ["pidof", "easyeffects"] + command: ["bash", "-c", "pidof easyeffects || flatpak ps | grep com.github.wwmm.easyeffects > /dev/null 2>&1"] onExited: (exitCode, exitStatus) => { root.active = exitCode === 0 } From 47725ea4b502a9b545b70bb103fbaea1c54f49c5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 18 Sep 2025 07:52:08 +0200 Subject: [PATCH 09/60] bar: fix autohide's hover region not working on hyprland 0.51 (#1994) --- .config/quickshell/ii/modules/bar/Bar.qml | 4 ++-- .config/quickshell/ii/modules/common/Config.qml | 1 + .config/quickshell/ii/modules/verticalBar/VerticalBar.qml | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index f23c7eb9d..a9c97f8d2 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -93,8 +93,8 @@ Scope { id: hoverMaskRegion anchors { fill: barContent - topMargin: -1 - bottomMargin: -1 + topMargin: -Config.options.bar.autoHide.hoverRegionWidth + bottomMargin: -Config.options.bar.autoHide.hoverRegionWidth } } diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 2fef550c6..d80b2b89f 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -149,6 +149,7 @@ Singleton { property JsonObject bar: JsonObject { property JsonObject autoHide: JsonObject { property bool enable: false + property int hoverRegionWidth: 2 property bool pushWindows: false property JsonObject showWhenPressingSuper: JsonObject { property bool enable: true diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml index d23eb43be..3851f06d3 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBar.qml @@ -82,8 +82,8 @@ Scope { id: hoverMaskRegion anchors { fill: barContent - leftMargin: -1 - rightMargin: -1 + leftMargin: -Config.options.bar.autoHide.hoverRegionWidth + rightMargin: -Config.options.bar.autoHide.hoverRegionWidth } } From fc0b069ad27c44090ad7dcb09ca09e1cee9d4952 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 18 Sep 2025 08:12:52 +0200 Subject: [PATCH 10/60] media: progress update interval follows resources --- .config/quickshell/ii/modules/bar/Media.qml | 2 +- .config/quickshell/ii/modules/mediaControls/PlayerControl.qml | 4 ++-- .config/quickshell/ii/modules/verticalBar/VerticalMedia.qml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/Media.qml b/.config/quickshell/ii/modules/bar/Media.qml index 87151c6ec..63295d8d6 100644 --- a/.config/quickshell/ii/modules/bar/Media.qml +++ b/.config/quickshell/ii/modules/bar/Media.qml @@ -21,7 +21,7 @@ Item { Timer { running: activePlayer?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: activePlayer.positionChanged() } diff --git a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml index 33114fb55..01ca56068 100644 --- a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml @@ -46,9 +46,9 @@ Item { // Player instance } } - Timer { // Force update for prevision + Timer { // Force update for revision running: playerController.player?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: { playerController.player.positionChanged() diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml b/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml index f64792f2e..1be93d4a7 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalMedia.qml @@ -22,7 +22,7 @@ MouseArea { Timer { running: activePlayer?.playbackState == MprisPlaybackState.Playing - interval: 1000 + interval: Config.options.resources.updateInterval repeat: true onTriggered: activePlayer.positionChanged() } From 21303b24c8b755114c4335c3a00a875502b580b9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 18 Sep 2025 08:18:10 +0200 Subject: [PATCH 11/60] welcome app: add tip for window close keybind --- .config/quickshell/ii/welcome.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index fefdc7976..b92534039 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -109,6 +109,10 @@ ApplicationWindow { text: "close" iconSize: 20 } + + StyledToolTip { + content: Translation.tr("Tip: Close a window with Super+Q") + } } } } From 0859d752560ad6cb8b99de8bdccc034eeee9f988 Mon Sep 17 00:00:00 2001 From: nrand Date: Thu, 18 Sep 2025 15:03:09 +0300 Subject: [PATCH 12/60] bar: fixed kb layout+variant display in top right --- .../quickshell/ii/services/HyprlandXkb.qml | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/.config/quickshell/ii/services/HyprlandXkb.qml b/.config/quickshell/ii/services/HyprlandXkb.qml index cace0d29e..2d9d408e2 100644 --- a/.config/quickshell/ii/services/HyprlandXkb.qml +++ b/.config/quickshell/ii/services/HyprlandXkb.qml @@ -46,13 +46,24 @@ Singleton { if (!line.trim() || line.trim().startsWith('!')) return false; - // Match: key + whitespace + description - const match = line.match(/^\s*(\S+)\s+(.+)$/); - if (match && match[2] === targetDescription) { - root.cachedLayoutCodes[match[2]] = match[1]; - root.currentLayoutCode = match[1]; + // Match layout: (whitespace + ) key + whitespace + description + const matchLayout = line.match(/^\s*(\S+)\s+(.+)$/); + if (matchLayout && matchLayout[2] === targetDescription) { + root.cachedLayoutCodes[matchLayout[2]] = matchLayout[1]; + root.currentLayoutCode = matchLayout[1]; return true; } + + // Match variant: (whitespace + ) variant + whitespace + key + whitespace + description + const matchVariant = line.match(/^\s*(\S+)\s+(\S+)\s+(.+)$/); + if (matchVariant && matchVariant[3] === targetDescription) { + const complexLayout = matchVariant[2] + " " + matchVariant[1]; + root.cachedLayoutCodes[matchVariant[3]] = complexLayout; + root.currentLayourCode = complexLayout; + return true; + } + + return false; }); // console.log("[HyprlandXkb] Found line:", foundLine); // console.log("[HyprlandXkb] Layout:", root.currentLayoutName, "| Code:", root.currentLayoutCode); From f9d9df4bbfbca2aa9df87ef6c4fefe0cfc26e83a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 18 Sep 2025 14:07:21 +0200 Subject: [PATCH 13/60] lock screen blur: fix center clock option, add settings app toggles --- .../ii/modules/background/Background.qml | 2 +- .../modules/common/widgets/ConfigSwitch.qml | 5 ++- .../ii/modules/settings/InterfaceConfig.qml | 42 +++++++++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 84eadd9db..64e5936df 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -277,7 +277,7 @@ Variants { } states: State { name: "centered" - when: bgRoot.shouldBlur + when: bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock AnchorChanges { target: clockLoader anchors { diff --git a/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml b/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml index 9c0da8f98..349ee5737 100644 --- a/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml +++ b/.config/quickshell/ii/modules/common/widgets/ConfigSwitch.qml @@ -7,8 +7,11 @@ import QtQuick.Controls RippleButton { id: root property string buttonIcon + Layout.fillWidth: true implicitHeight: contentItem.implicitHeight + 8 * 2 + font.pixelSize: Appearance.font.pixelSize.small + onClicked: checked = !checked contentItem: RowLayout { @@ -21,7 +24,7 @@ RippleButton { id: labelWidget Layout.fillWidth: true text: root.text - font.pixelSize: Appearance.font.pixelSize.small + font: root.font color: Appearance.colors.colOnSecondaryContainer } StyledSwitch { diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 57074a34f..bbb6a5604 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -99,6 +99,48 @@ ContentPage { } } + ContentSection { + icon: "lock" + title: Translation.tr("Lock screen") + + ContentSubsection { + title: Translation.tr("Blurred style") + + ConfigSwitch { + font.pixelSize: Appearance.font.pixelSize.large + text: Translation.tr('Enable blur') + checked: Config.options.background.lockBlur.enable + onCheckedChanged: { + Config.options.background.lockBlur.enable = checked; + } + } + + ConfigRow { + uniform: true + + ConfigSwitch { + enabled: Config.options.background.lockBlur.enable + font.pixelSize: Appearance.font.pixelSize.large + text: Translation.tr('Center clock') + checked: Config.options.background.lockBlur.centerClock + onCheckedChanged: { + Config.options.background.lockBlur.centerClock = checked; + } + } + ConfigSwitch { + enabled: Config.options.background.lockBlur.enable + font.pixelSize: Appearance.font.pixelSize.large + text: Translation.tr('Show "Locked" text') + checked: Config.options.background.lockBlur.showLockedText + onCheckedChanged: { + Config.options.background.lockBlur.showLockedText = checked; + } + } + } + + } + } + ContentSection { icon: "side_navigation" title: Translation.tr("Sidebars") From ed56d03c0980c870c1ae67668b1bf45fd8aa4ffd Mon Sep 17 00:00:00 2001 From: nrand Date: Fri, 19 Sep 2025 07:58:12 +0300 Subject: [PATCH 14/60] bar: actually fixed it (there was a typo oopsie) --- .config/quickshell/ii/services/HyprlandXkb.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/services/HyprlandXkb.qml b/.config/quickshell/ii/services/HyprlandXkb.qml index 2d9d408e2..54c8f8f8b 100644 --- a/.config/quickshell/ii/services/HyprlandXkb.qml +++ b/.config/quickshell/ii/services/HyprlandXkb.qml @@ -59,7 +59,7 @@ Singleton { if (matchVariant && matchVariant[3] === targetDescription) { const complexLayout = matchVariant[2] + " " + matchVariant[1]; root.cachedLayoutCodes[matchVariant[3]] = complexLayout; - root.currentLayourCode = complexLayout; + root.currentLayoutCode = complexLayout; return true; } From 14778696e9f2ec5bf14130eaf8839481e569b032 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:30:17 +0200 Subject: [PATCH 15/60] custom system tray --- .../quickshell/ii/modules/bar/StyledPopup.qml | 16 +- .config/quickshell/ii/modules/bar/SysTray.qml | 52 ++++- .../quickshell/ii/modules/bar/SysTrayItem.qml | 39 +++- .../quickshell/ii/modules/bar/SysTrayMenu.qml | 218 ++++++++++++++++++ .../ii/modules/bar/SysTrayMenuEntry.qml | 128 ++++++++++ .../ii/modules/common/Appearance.qml | 28 ++- .../modules/common/widgets/RippleButton.qml | 2 +- .../common/widgets/StyledRadioButton.qml | 3 +- 8 files changed, 453 insertions(+), 33 deletions(-) create mode 100644 .config/quickshell/ii/modules/bar/SysTrayMenu.qml create mode 100644 .config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml diff --git a/.config/quickshell/ii/modules/bar/StyledPopup.qml b/.config/quickshell/ii/modules/bar/StyledPopup.qml index 9570da884..68ba609e2 100644 --- a/.config/quickshell/ii/modules/bar/StyledPopup.qml +++ b/.config/quickshell/ii/modules/bar/StyledPopup.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.widgets import qs.modules.common.functions import QtQuick import QtQuick.Effects @@ -26,6 +27,10 @@ LazyLoader { implicitWidth: popupBackground.implicitWidth + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin implicitHeight: popupBackground.implicitHeight + Appearance.sizes.hyprlandGapsOut * 2 + root.popupBackgroundMargin + mask: Region { + item: popupBackground + } + exclusionMode: ExclusionMode.Ignore exclusiveZone: 0 margins { @@ -49,15 +54,8 @@ LazyLoader { WlrLayershell.namespace: "quickshell:popup" WlrLayershell.layer: WlrLayer.Overlay - RectangularShadow { - property var target: popupBackground - anchors.fill: target - radius: target.radius - blur: 0.9 * Appearance.sizes.hyprlandGapsOut - offset: Qt.vector2d(0.0, 1.0) - spread: 0.7 - color: Appearance.colors.colShadow - cached: true + StyledRectangularShadow { + target: popupBackground } Rectangle { diff --git a/.config/quickshell/ii/modules/bar/SysTray.qml b/.config/quickshell/ii/modules/bar/SysTray.qml index 8eba7b6db..8da341192 100644 --- a/.config/quickshell/ii/modules/bar/SysTray.qml +++ b/.config/quickshell/ii/modules/bar/SysTray.qml @@ -3,6 +3,7 @@ import qs.modules.common.widgets import QtQuick import QtQuick.Layouts import Quickshell +import Quickshell.Hyprland import Quickshell.Services.SystemTray Item { @@ -14,20 +15,58 @@ Item { property bool trayOverflowOpen: false property bool showSeparator: true property bool showOverflowMenu: true + property var activeMenu: null property list itemsInUserList: SystemTray.items.values.filter(i => (Config.options.bar.tray.pinnedItems.includes(i.id) && i.status !== Status.Passive)) property list itemsNotInUserList: SystemTray.items.values.filter(i => (!Config.options.bar.tray.pinnedItems.includes(i.id) && i.status !== Status.Passive)) property bool invertPins: Config.options.bar.tray.invertPinnedItems property list pinnedItems: invertPins ? itemsNotInUserList : itemsInUserList property list unpinnedItems: invertPins ? itemsInUserList : itemsNotInUserList - onUnpinnedItemsChanged: if (unpinnedItems.length == 0) - root.trayOverflowOpen = false + onUnpinnedItemsChanged: { + if (unpinnedItems.length == 0) root.closeOverflowMenu(); + } + + function grabFocus() { + focusGrab.active = true; + } + + function setExtraWindowAndGrabFocus(window) { + root.activeMenu = window; + root.grabFocus(); + } + + function releaseFocus() { + focusGrab.active = false; + } + + function closeOverflowMenu() { + focusGrab.active = false; + } + + onTrayOverflowOpenChanged: { + if (root.trayOverflowOpen) { + root.grabFocus(); + } + } + + HyprlandFocusGrab { + id: focusGrab + active: false + windows: [trayOverflowLayout.QsWindow?.window, root.activeMenu] + onCleared: { + root.trayOverflowOpen = false; + if (root.activeMenu) { + root.activeMenu.close(); + root.activeMenu = null; + } + } + } GridLayout { id: gridLayout columns: root.vertical ? 1 : -1 anchors.fill: parent - rowSpacing: 6 + rowSpacing: 8 columnSpacing: 15 RippleButton { @@ -60,6 +99,7 @@ Item { } StyledPopup { + id: overflowPopup hoverTarget: trayOverflowButton active: root.trayOverflowOpen popupBackgroundMargin: 300 // This should be plenty... makes sure tooltips don't get cutoff (easily) @@ -79,6 +119,8 @@ Item { item: modelData Layout.fillHeight: !root.vertical Layout.fillWidth: root.vertical + onMenuClosed: root.releaseFocus(); + onMenuOpened: (qsWindow) => root.setExtraWindowAndGrabFocus(qsWindow); } } } @@ -95,6 +137,10 @@ Item { item: modelData Layout.fillHeight: !root.vertical Layout.fillWidth: root.vertical + onMenuClosed: root.releaseFocus(); + onMenuOpened: (qsWindow) => { + root.setExtraWindowAndGrabFocus(qsWindow); + } } } diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index f2bbc39d8..0307ad903 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -9,12 +9,13 @@ import Qt5Compat.GraphicalEffects MouseArea { id: root - - property var bar: root.QsWindow.window required property SystemTrayItem item property bool targetMenuOpen: false - hoverEnabled: true + signal menuOpened(qsWindow: var) + signal menuClosed() + + hoverEnabled: true acceptedButtons: Qt.LeftButton | Qt.RightButton implicitWidth: 20 implicitHeight: 20 @@ -36,16 +37,30 @@ MouseArea { if (Config.options.bar.tray.showItemId) tooltip.content += "\n[" + item.id + "]"; } - QsMenuAnchor { + Loader { id: menu - - menu: root.item.menu - anchor.window: bar - anchor.rect.x: root.x + (Config.options.bar.vertical ? 0 : bar?.width) - anchor.rect.y: root.y + (Config.options.bar.vertical ? bar?.height : 0) - anchor.rect.height: root.height - anchor.rect.width: root.width - anchor.edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + function open() { + menu.active = true; + } + active: false + sourceComponent: SysTrayMenu { + Component.onCompleted: this.open(); + trayItemMenuHandle: root.item.menu + anchor { + window: root.QsWindow.window + rect.x: root.x + (Config.options.bar.vertical ? 0 : QsWindow.window?.width) + rect.y: root.y + (Config.options.bar.vertical ? QsWindow.window?.height : 0) + rect.height: root.height + rect.width: root.width + edges: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + gravity: Config.options.bar.bottom ? (Edges.Top | Edges.Left) : (Edges.Bottom | Edges.Right) + } + onMenuOpened: (window) => root.menuOpened(window); + onMenuClosed: { + root.menuClosed(); + menu.active = false; + } + } } IconImage { diff --git a/.config/quickshell/ii/modules/bar/SysTrayMenu.qml b/.config/quickshell/ii/modules/bar/SysTrayMenu.qml new file mode 100644 index 000000000..6caffb41c --- /dev/null +++ b/.config/quickshell/ii/modules/bar/SysTrayMenu.qml @@ -0,0 +1,218 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Wayland + +PopupWindow { + id: root + required property QsMenuHandle trayItemMenuHandle + property real popupBackgroundMargin: 0 + + signal menuClosed + signal menuOpened(qsWindow: var) // Correct type is QsWindow, but QML does not like that + + color: "transparent" + property real padding: Appearance.sizes.elevationMargin + + implicitHeight: { + let result = 0; + for (let child of stackView.children) { + result = Math.max(child.implicitHeight, result); + } + return result + popupBackground.padding * 2 + root.padding * 2; + } + implicitWidth: { + let result = 0; + for (let child of stackView.children) { + result = Math.max(child.implicitWidth, result); + } + return result + popupBackground.padding * 2 + root.padding * 2; + } + + function open() { + root.visible = true; + root.menuOpened(root); + } + + function close() { + root.visible = false; + while (stackView.depth > 1) + stackView.pop(); + root.menuClosed(); + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.BackButton | Qt.RightButton + onClicked: event => { + if ((event.button === Qt.BackButton || event.button === Qt.RightButton) && stackView.depth > 1) + stackView.pop(); + } + + StyledRectangularShadow { + target: popupBackground + opacity: popupBackground.opacity + } + + Rectangle { + id: popupBackground + readonly property real padding: 4 + anchors { + left: parent.left + right: parent.right + verticalCenter: Config.options.bar.vertical ? parent.verticalCenter : undefined + top: Config.options.bar.vertical ? undefined : Config.options.bar.bottom ? undefined : parent.top + bottom: Config.options.bar.vertical ? undefined : Config.options.bar.bottom ? parent.bottom : undefined + margins: root.padding + } + + color: Appearance.colors.colLayer0 + radius: Appearance.rounding.windowRounding + border.width: 1 + border.color: Appearance.colors.colLayer0Border + clip: true + + opacity: 0 + Component.onCompleted: opacity = 1 + implicitWidth: stackView.implicitWidth + popupBackground.padding * 2 + implicitHeight: stackView.implicitHeight + popupBackground.padding * 2 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + + StackView { + id: stackView + anchors { + fill: parent + margins: popupBackground.padding + } + pushEnter: NoAnim {} + pushExit: NoAnim {} + popEnter: NoAnim {} + popExit: NoAnim {} + + implicitWidth: currentItem.implicitWidth + implicitHeight: currentItem.implicitHeight + + initialItem: SubMenu { + handle: root.trayItemMenuHandle + } + } + } + } + + component NoAnim: Transition { + NumberAnimation { + duration: 0 + } + } + + component SubMenu: ColumnLayout { + id: submenu + required property QsMenuHandle handle + property bool isSubMenu: false + property bool shown: false + opacity: shown ? 1 : 0 + + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + Component.onCompleted: shown = true + StackView.onActivating: shown = true + StackView.onDeactivating: shown = false + StackView.onRemoved: destroy() + + QsMenuOpener { + id: menuOpener + menu: submenu.handle + } + + spacing: 0 + + Loader { + Layout.fillWidth: true + visible: submenu.isSubMenu + active: visible + sourceComponent: RippleButton { + id: backButton + buttonRadius: popupBackground.radius - popupBackground.padding + horizontalPadding: 12 + implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + implicitHeight: 36 + + onClicked: stackView.pop() + + contentItem: RowLayout { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + leftMargin: backButton.horizontalPadding + rightMargin: backButton.horizontalPadding + } + spacing: 8 + MaterialSymbol { + iconSize: 20 + text: "chevron_left" + } + StyledText { + Layout.fillWidth: true + text: Translation.tr("Back") + } + } + } + } + + Repeater { + id: menuEntriesRepeater + property bool iconColumnNeeded: { + for (let i = 0; i < menuOpener.children.values.length; i++) { + if (menuOpener.children.values[i].icon.length > 0) + return true; + } + return false; + } + property bool specialInteractionColumnNeeded: { + for (let i = 0; i < menuOpener.children.values.length; i++) { + if (menuOpener.children.values[i].buttonType !== QsMenuButtonType.None) + return true; + } + return false; + } + model: menuOpener.children + delegate: SysTrayMenuEntry { + required property QsMenuEntry modelData + forceIconColumn: menuEntriesRepeater.iconColumnNeeded + forceSpecialInteractionColumn: menuEntriesRepeater.specialInteractionColumnNeeded + menuEntry: modelData + + buttonRadius: popupBackground.radius - popupBackground.padding + + onDismiss: root.close() + onOpenSubmenu: handle => { + stackView.push(subMenuComponent.createObject(null, { + handle: handle, + isSubMenu: true + })); + } + } + } + } + + Component { + id: subMenuComponent + SubMenu {} + } +} diff --git a/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml new file mode 100644 index 000000000..04d62087f --- /dev/null +++ b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml @@ -0,0 +1,128 @@ +pragma ComponentBehavior: Bound + +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Widgets + +RippleButton { + id: root + required property QsMenuEntry menuEntry + property bool forceIconColumn: false + property bool forceSpecialInteractionColumn: false + readonly property bool hasIcon: menuEntry.icon.length > 0 + readonly property bool hasSpecialInteraction: menuEntry.buttonType !== QsMenuButtonType.None + + signal dismiss() + signal openSubmenu(handle: QsMenuHandle) + + colBackground: menuEntry.isSeparator ? Appearance.m3colors.m3outlineVariant : ColorUtils.transparentize(Appearance.colors.colLayer0) + enabled: !menuEntry.isSeparator + opacity: 1 + + horizontalPadding: 12 + implicitWidth: contentItem.implicitWidth + horizontalPadding * 2 + implicitHeight: menuEntry.isSeparator ? 1 : 36 + Layout.topMargin: menuEntry.isSeparator ? 4 : 0 + Layout.bottomMargin: menuEntry.isSeparator ? 4 : 0 + Layout.fillWidth: true + + Component.onCompleted: { + if (menuEntry.isSeparator) { + root.buttonColor = root.colBackground; + } + } + + releaseAction: () => { + if (menuEntry.hasChildren) { + root.openSubmenu(root.menuEntry); + return; + } + menuEntry.triggered(); + root.dismiss(); + } + altAction: (event) => { // Not hog right-click + event.accepted = false; + } + + contentItem: RowLayout { + id: contentItem + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + right: parent.right + leftMargin: root.horizontalPadding + rightMargin: root.horizontalPadding + } + spacing: 8 + visible: !root.menuEntry.isSeparator + + // Interaction: checkbox or radio button + Item { + visible: root.hasSpecialInteraction || root.forceSpecialInteractionColumn + implicitWidth: 20 + implicitHeight: 20 + + Loader { + anchors.fill: parent + active: root.menuEntry.buttonType === QsMenuButtonType.RadioButton + + sourceComponent: StyledRadioButton { + padding: 0 + checked: root.menuEntry.checkState === Qt.Checked + onCheckedChanged: { + if (checked) root.clicked() + } + } + } + + Loader { + anchors.fill: parent + active: root.menuEntry.buttonType === QsMenuButtonType.CheckBox && root.menuEntry.checkState !== Qt.Unchecked + + sourceComponent: MaterialSymbol { + text: root.menuEntry.checkState === Qt.PartiallyChecked ? "check_indeterminate_small" : "check" + iconSize: 20 + } + } + } + + // Button icon + Item { + visible: root.hasIcon || root.forceIconColumn + implicitWidth: 20 + implicitHeight: 20 + + Loader { + anchors.centerIn: parent + active: root.menuEntry.icon.length > 0 + sourceComponent: IconImage { + asynchronous: true + source: root.menuEntry.icon + implicitSize: 20 + mipmap: true + } + } + } + + StyledText { + id: label + text: root.menuEntry.text + font.pixelSize: Appearance.font.pixelSize.smallie + Layout.fillWidth: true + } + + Loader { + active: root.menuEntry.hasChildren + + sourceComponent: MaterialSymbol { + text: "chevron_right" + iconSize: 20 + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/Appearance.qml b/.config/quickshell/ii/modules/common/Appearance.qml index 3bfdf530d..d63d04800 100644 --- a/.config/quickshell/ii/modules/common/Appearance.qml +++ b/.config/quickshell/ii/modules/common/Appearance.qml @@ -212,6 +212,7 @@ Singleton { property QtObject pixelSize: QtObject { property int smallest: 10 property int smaller: 12 + property int smallie: 13 property int small: 15 property int normal: 16 property int large: 17 @@ -254,14 +255,8 @@ Singleton { 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 @@ -275,6 +270,7 @@ Singleton { } } } + property QtObject elementMoveExit: QtObject { property int duration: 200 property int type: Easing.BezierSpline @@ -288,6 +284,7 @@ Singleton { } } } + property QtObject elementMoveFast: QtObject { property int duration: animationCurves.expressiveEffectsDuration property int type: Easing.BezierSpline @@ -304,6 +301,21 @@ Singleton { easing.bezierCurve: root.animation.elementMoveFast.bezierCurve }} } + + property QtObject elementResize: QtObject { + property int duration: 400 + property int type: Easing.BezierSpline + property list bezierCurve: animationCurves.emphasized + property int velocity: 650 + property Component numberAnimation: Component { + NumberAnimation { + duration: root.animation.elementResize.duration + easing.type: root.animation.elementResize.type + easing.bezierCurve: root.animation.elementResize.bezierCurve + } + } + } + property QtObject clickBounce: QtObject { property int duration: 200 property int type: Easing.BezierSpline @@ -315,11 +327,13 @@ Singleton { easing.bezierCurve: root.animation.clickBounce.bezierCurve }} } + property QtObject scroll: QtObject { property int duration: 200 property int type: Easing.BezierSpline property list bezierCurve: animationCurves.standardDecel } + property QtObject menuDecel: QtObject { property int duration: 350 property int type: Easing.OutExpo diff --git a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 07e6c5318..498bfc1cb 100644 --- a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -63,7 +63,7 @@ Button { acceptedButtons: Qt.LeftButton | Qt.RightButton | Qt.MiddleButton onPressed: (event) => { if(event.button === Qt.RightButton) { - if (root.altAction) root.altAction(); + if (root.altAction) root.altAction(event); return; } if(event.button === Qt.MiddleButton) { diff --git a/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml b/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml index a6a63b7b8..ac511ce57 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledRadioButton.qml @@ -10,7 +10,8 @@ import Quickshell.Services.Pipewire RadioButton { id: root - implicitHeight: contentItem.implicitHeight + 4 * 2 + padding: 4 + implicitHeight: contentItem.implicitHeight + padding * 2 property string description property color activeColor: Appearance?.colors.colPrimary ?? "#685496" property color inactiveColor: Appearance?.m3colors.m3onSurfaceVariant ?? "#45464F" From cf962a26f4cf34d5cc795a2ec3d24d05dba3b154 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:31:26 +0200 Subject: [PATCH 16/60] workspaces: fix some undefined thingy --- .config/quickshell/ii/modules/bar/Workspaces.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/bar/Workspaces.qml b/.config/quickshell/ii/modules/bar/Workspaces.qml index 775df18d7..5d0c9f62b 100644 --- a/.config/quickshell/ii/modules/bar/Workspaces.qml +++ b/.config/quickshell/ii/modules/bar/Workspaces.qml @@ -204,7 +204,7 @@ Item { rowSpacing: 0 anchors.fill: parent - implicitHeight: vertical ? Appearance.sizes.barWidth : Appearance.sizes.barHeight + implicitHeight: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.barHeight implicitWidth: vertical ? Appearance.sizes.verticalBarWidth : Appearance.sizes.verticalBarWidth Repeater { From 8b78d058050fb4b944560bb57e092b9d6b03759e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:49:34 +0200 Subject: [PATCH 17/60] background: fix clock position --- .config/quickshell/ii/modules/background/Background.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 64e5936df..3df6c445c 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -69,7 +69,7 @@ Variants { property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable) property color dominantColor: Appearance.colors.colPrimary property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 - property color colText: (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colSecondary : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + property color colText: (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) Behavior on colText { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } @@ -262,6 +262,7 @@ Variants { left: wallpaper.left top: wallpaper.top horizontalCenter: undefined + verticalCenter: undefined leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) topMargin: { if (bgRoot.shouldBlur) @@ -282,8 +283,9 @@ Variants { target: clockLoader anchors { left: undefined - horizontalCenter: wallpaper.horizontalCenter right: undefined + top: parent.top + horizontalCenter: parent.horizontalCenter } } } From 074aebbe5d946f0ab53832b999b68fa0efaa82af Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:49:57 +0200 Subject: [PATCH 18/60] tray: fix checkbuttons' clickability in menus --- .config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml index 04d62087f..59f3325c4 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayMenuEntry.qml @@ -72,11 +72,9 @@ RippleButton { active: root.menuEntry.buttonType === QsMenuButtonType.RadioButton sourceComponent: StyledRadioButton { + enabled: false padding: 0 checked: root.menuEntry.checkState === Qt.Checked - onCheckedChanged: { - if (checked) root.clicked() - } } } From 7711946cf5275475e2a0951f7d1e312e7022b330 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 09:50:15 +0200 Subject: [PATCH 19/60] bar: remove unused imports --- .config/quickshell/ii/modules/bar/Bar.qml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/Bar.qml b/.config/quickshell/ii/modules/bar/Bar.qml index a9c97f8d2..93587c398 100644 --- a/.config/quickshell/ii/modules/bar/Bar.qml +++ b/.config/quickshell/ii/modules/bar/Bar.qml @@ -1,11 +1,9 @@ import "./weather" import QtQuick -import QtQuick.Layouts import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland -import Quickshell.Services.UPower import qs import qs.services import qs.modules.common From cecd47caea23933c2034d4fd812b6f4aea808119 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 10:08:02 +0200 Subject: [PATCH 20/60] make filled symbols suck less --- .config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml index 126845313..44fabf136 100644 --- a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml +++ b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml @@ -6,6 +6,7 @@ StyledText { property real iconSize: Appearance?.font.pixelSize.small ?? 16 property real fill: 0 property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping + renderType: Text.CurveRendering font { hintingPreference: Font.PreferFullHinting family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded" From ed8e4b8766ca3a0eec86255f805c4bdf48d165d9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 10:11:35 +0200 Subject: [PATCH 21/60] move some files placed in the wrong folder --- .config/quickshell/ii/modules/common/{ => widgets}/NoticeBox.qml | 0 .../ii/modules/common/{ => widgets}/StyledBlurEffect.qml | 0 .../quickshell/ii/modules/common/{ => widgets}/ThumbnailImage.qml | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename .config/quickshell/ii/modules/common/{ => widgets}/NoticeBox.qml (100%) rename .config/quickshell/ii/modules/common/{ => widgets}/StyledBlurEffect.qml (100%) rename .config/quickshell/ii/modules/common/{ => widgets}/ThumbnailImage.qml (100%) diff --git a/.config/quickshell/ii/modules/common/NoticeBox.qml b/.config/quickshell/ii/modules/common/widgets/NoticeBox.qml similarity index 100% rename from .config/quickshell/ii/modules/common/NoticeBox.qml rename to .config/quickshell/ii/modules/common/widgets/NoticeBox.qml diff --git a/.config/quickshell/ii/modules/common/StyledBlurEffect.qml b/.config/quickshell/ii/modules/common/widgets/StyledBlurEffect.qml similarity index 100% rename from .config/quickshell/ii/modules/common/StyledBlurEffect.qml rename to .config/quickshell/ii/modules/common/widgets/StyledBlurEffect.qml diff --git a/.config/quickshell/ii/modules/common/ThumbnailImage.qml b/.config/quickshell/ii/modules/common/widgets/ThumbnailImage.qml similarity index 100% rename from .config/quickshell/ii/modules/common/ThumbnailImage.qml rename to .config/quickshell/ii/modules/common/widgets/ThumbnailImage.qml From b9ae5cafa0e46dfdc4f6549a4fac01b83ecdffa7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 10:37:24 +0200 Subject: [PATCH 22/60] brightness: delay setting for ddc monitors (#2022) --- .config/quickshell/ii/services/Brightness.qml | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/ii/services/Brightness.qml b/.config/quickshell/ii/services/Brightness.qml index 154bdffd4..42becc766 100644 --- a/.config/quickshell/ii/services/Brightness.qml +++ b/.config/quickshell/ii/services/Brightness.qml @@ -103,16 +103,27 @@ Singleton { } } - function setBrightness(value: real): void { - value = Math.max(0.01, Math.min(1, value)); - const rounded = Math.round(value * monitor.rawMaxBrightness); - if (Math.round(brightness * monitor.rawMaxBrightness) === rounded) - return; - brightness = value; + // We need a delay for DDC monitors because they can be quite slow and might act weird with rapid changes + property var setTimer: Timer { + id: setTimer + interval: monitor.isDdc ? 300 : 0 + onTriggered: { + syncBrightness(); + } + } + + function syncBrightness() { + const rounded = Math.round(monitor.brightness * monitor.rawMaxBrightness); setProc.command = isDdc ? ["ddcutil", "-b", busNum, "setvcp", "10", rounded] : ["brightnessctl", "s", rounded, "--quiet"]; setProc.startDetached(); } + function setBrightness(value: real): void { + value = Math.max(0.01, Math.min(1, value)); + monitor.brightness = value; + setTimer.restart(); + } + Component.onCompleted: { initialize(); } From 3a01dad945c749257fb0fa05066f3fd20b09bed9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 10:47:09 +0200 Subject: [PATCH 23/60] aloow configuration of notif timeout duration --- .config/quickshell/ii/modules/common/Config.qml | 4 ++++ .../ii/modules/settings/InterfaceConfig.qml | 16 ++++++++++++++++ .config/quickshell/ii/services/Notifications.qml | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index d80b2b89f..d5e0a440a 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -266,6 +266,10 @@ Singleton { property string 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 JsonObject notifications: JsonObject { + property int timeout: 7000 + } + property JsonObject osd: JsonObject { property int timeout: 1000 } diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index bbb6a5604..7df973622 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -141,6 +141,22 @@ ContentPage { } } + ContentSection { + icon: "notifications" + title: Translation.tr("Notifications") + + ConfigSpinBox { + text: Translation.tr("Timeout duration (if not defined by notification) (ms)") + value: Config.options.notifications.timeout + from: 1000 + to: 60000 + stepSize: 1000 + onValueChanged: { + Config.options.notifications.timeout = value; + } + } + } + ContentSection { icon: "side_navigation" title: Translation.tr("Sidebars") diff --git a/.config/quickshell/ii/services/Notifications.qml b/.config/quickshell/ii/services/Notifications.qml index f1f279672..00a87c3a7 100644 --- a/.config/quickshell/ii/services/Notifications.qml +++ b/.config/quickshell/ii/services/Notifications.qml @@ -60,7 +60,7 @@ Singleton { component NotifTimer: Timer { required property int notificationId - interval: 5000 + interval: 7000 running: true onTriggered: () => { root.timeoutNotification(notificationId); @@ -168,7 +168,7 @@ Singleton { if (notification.expireTimeout != 0) { newNotifObject.timer = notifTimerComponent.createObject(root, { "notificationId": newNotifObject.notificationId, - "interval": notification.expireTimeout < 0 ? 5000 : notification.expireTimeout, + "interval": notification.expireTimeout < 0 ? (Config?.options.notifications.timeout ?? 7000) : notification.expireTimeout, }); } } From 429cb50ff7aaecb445b48f59d6327357a52abf86 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 11:55:38 +0200 Subject: [PATCH 24/60] tooltips: use builtin text prop, fix crash (#1956) --- .../quickshell/ii/modules/bar/ScrollHint.qml | 2 +- .../quickshell/ii/modules/bar/SysTrayItem.qml | 6 +- .../ii/modules/common/widgets/AddressBar.qml | 2 +- .../common/widgets/ContentSubsection.qml | 2 +- .../modules/common/widgets/MaterialSymbol.qml | 2 +- .../common/widgets/NotificationGroup.qml | 2 +- .../common/widgets/NotificationItem.qml | 2 +- .../modules/common/widgets/StyledSlider.qml | 2 +- .../modules/common/widgets/StyledToolTip.qml | 57 ++++--------------- .../common/widgets/StyledToolTipContent.qml | 48 ++++++++++++++++ .../ii/modules/overview/OverviewWidget.qml | 2 +- .../ii/modules/overview/SearchItem.qml | 2 +- .../sessionScreen/SessionActionButton.qml | 2 +- .../ii/modules/settings/AdvancedConfig.qml | 6 +- .../ii/modules/settings/GeneralConfig.qml | 4 +- .../ii/modules/settings/InterfaceConfig.qml | 10 ++-- .../ii/modules/settings/QuickConfig.qml | 6 +- .../ii/modules/settings/ServicesConfig.qml | 2 +- .../ii/modules/sidebarLeft/AiChat.qml | 2 +- .../sidebarLeft/ApiInputBoxIndicator.qml | 2 +- .../modules/sidebarLeft/aiChat/AiMessage.qml | 10 ++-- .../sidebarLeft/aiChat/MessageCodeBlock.qml | 4 +- .../modules/sidebarLeft/anime/BooruImage.qml | 2 +- .../sidebarRight/SidebarRightContent.qml | 6 +- .../calendar/CalendarHeaderButton.qml | 2 +- .../quickToggles/BluetoothToggle.qml | 2 +- .../quickToggles/CloudflareWarp.qml | 2 +- .../quickToggles/EasyEffectsToggle.qml | 2 +- .../sidebarRight/quickToggles/GameMode.qml | 2 +- .../quickToggles/IdleInhibitor.qml | 2 +- .../quickToggles/NetworkToggle.qml | 2 +- .../sidebarRight/quickToggles/NightLight.qml | 2 +- .../todo/TodoItemActionButton.qml | 2 +- .../WallpaperSelectorContent.qml | 8 +-- .config/quickshell/ii/settings.qml | 2 +- .config/quickshell/ii/welcome.qml | 6 +- 36 files changed, 116 insertions(+), 103 deletions(-) create mode 100644 .config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml diff --git a/.config/quickshell/ii/modules/bar/ScrollHint.qml b/.config/quickshell/ii/modules/bar/ScrollHint.qml index a8e1c8dfe..9a338df48 100644 --- a/.config/quickshell/ii/modules/bar/ScrollHint.qml +++ b/.config/quickshell/ii/modules/bar/ScrollHint.qml @@ -24,7 +24,7 @@ Revealer { // Scroll hint // StyledToolTip { // extraVisibleCondition: tooltipText.length > 0 - // content: tooltipText + // text: tooltipText // } ColumnLayout { diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index 0307ad903..41389e85d 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -31,10 +31,10 @@ MouseArea { event.accepted = true; } onEntered: { - tooltip.content = item.tooltipTitle.length > 0 ? item.tooltipTitle + tooltip.text = item.tooltipTitle.length > 0 ? item.tooltipTitle : (item.title.length > 0 ? item.title : item.id); - if (item.tooltipDescription.length > 0) tooltip.content += " • " + item.tooltipDescription; - if (Config.options.bar.tray.showItemId) tooltip.content += "\n[" + item.id + "]"; + if (item.tooltipDescription.length > 0) tooltip.text += " • " + item.tooltipDescription; + if (Config.options.bar.tray.showItemId) tooltip.text += "\n[" + item.id + "]"; } Loader { diff --git a/.config/quickshell/ii/modules/common/widgets/AddressBar.qml b/.config/quickshell/ii/modules/common/widgets/AddressBar.qml index c965a536d..bf1cf2305 100644 --- a/.config/quickshell/ii/modules/common/widgets/AddressBar.qml +++ b/.config/quickshell/ii/modules/common/widgets/AddressBar.qml @@ -113,7 +113,7 @@ Rectangle { } StyledToolTip { - content: Translation.tr("Edit directory") + text: Translation.tr("Edit directory") } } } diff --git a/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml b/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml index b78f3aa81..6c3824e08 100644 --- a/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml +++ b/.config/quickshell/ii/modules/common/widgets/ContentSubsection.qml @@ -32,7 +32,7 @@ ColumnLayout { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: infoMouseArea.containsMouse - content: root.tooltip + text: root.tooltip } } } diff --git a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml index 44fabf136..75ff77d9b 100644 --- a/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml +++ b/.config/quickshell/ii/modules/common/widgets/MaterialSymbol.qml @@ -6,7 +6,7 @@ StyledText { property real iconSize: Appearance?.font.pixelSize.small ?? 16 property real fill: 0 property real truncatedFill: Math.round(fill * 100) / 100 // Reduce memory consumption spikes from constant font remapping - renderType: Text.CurveRendering + renderType: fill !== 0 ? Text.CurveRendering : Text.NativeRendering font { hintingPreference: Font.PreferFullHinting family: Appearance?.font.family.iconMaterial ?? "Material Symbols Rounded" diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index d4aa00234..e0736bb10 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -215,7 +215,7 @@ MouseArea { // Notification group area altAction: () => { root.toggleExpanded() } StyledToolTip { - content: Translation.tr("Tip: right-clicking a group\nalso expands it") + text: Translation.tr("Tip: right-clicking a group\nalso expands it") } } } diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml index 82367db17..94a7c71bf 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml @@ -209,7 +209,7 @@ Item { // Notification item area textFormat: Text.RichText text: { return `` + - `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` + `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` } onLinkActivated: (link) => { diff --git a/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml b/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml index e940f1a41..6f2fc2314 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml @@ -149,7 +149,7 @@ Slider { StyledToolTip { extraVisibleCondition: root.pressed - content: root.tooltipContent + text: root.tooltipContent } } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml b/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml index 813c9ed14..f0e40db40 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml @@ -6,55 +6,20 @@ import QtQuick.Layouts ToolTip { id: root - property string content property bool extraVisibleCondition: true property bool alternativeVisibleCondition: false - property bool internalVisibleCondition: { - const ans = (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition - return ans - } + readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition verticalPadding: 5 - horizontalPadding: 10 - opacity: internalVisibleCondition ? 1 : 0 - visible: opacity > 0 - - Behavior on opacity { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - + horizontalPadding: 10 background: null + + visible: internalVisibleCondition - contentItem: Item { - id: contentItemBackground - implicitWidth: tooltipTextObject.width + 2 * root.horizontalPadding - implicitHeight: tooltipTextObject.height + 2 * root.verticalPadding - - Rectangle { - id: backgroundRectangle - anchors.bottom: contentItemBackground.bottom - anchors.horizontalCenter: contentItemBackground.horizontalCenter - color: Appearance?.colors.colTooltip ?? "#3C4043" - radius: Appearance?.rounding.verysmall ?? 7 - width: internalVisibleCondition ? (tooltipTextObject.width + 2 * padding) : 0 - height: internalVisibleCondition ? (tooltipTextObject.height + 2 * padding) : 0 - clip: true - - Behavior on width { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on height { - animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) - } - - StyledText { - id: tooltipTextObject - anchors.centerIn: parent - text: content - font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14 - font.hintingPreference: Font.PreferNoHinting // Prevent shaky text - color: Appearance?.colors.colOnTooltip ?? "#FFFFFF" - wrapMode: Text.Wrap - } - } + contentItem: StyledToolTipContent { + id: contentItem + text: root.text + shown: root.internalVisibleCondition + horizontalPadding: root.horizontalPadding + verticalPadding: root.verticalPadding } -} \ No newline at end of file +} diff --git a/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml new file mode 100644 index 000000000..2d35cc6e6 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml @@ -0,0 +1,48 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts + +Item { + id: root + required property string text + property bool shown: true + property real horizontalPadding: 10 + property real verticalPadding: 5 + implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding + implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding + + Rectangle { + id: backgroundRectangle + anchors.bottom: root.bottom + anchors.horizontalCenter: root.horizontalCenter + color: Appearance?.colors.colTooltip ?? "#3C4043" + radius: Appearance?.rounding.verysmall ?? 7 + opacity: shown ? 1 : 0 + implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * padding) : 0 + implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * padding) : 0 + clip: true + + Behavior on implicitWidth { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on opacity { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + + StyledText { + id: tooltipTextObject + anchors.centerIn: parent + text: root.text + font.pixelSize: Appearance?.font.pixelSize.smaller ?? 14 + font.hintingPreference: Font.PreferNoHinting // Prevent shaky text + color: Appearance?.colors.colOnTooltip ?? "#FFFFFF" + wrapMode: Text.Wrap + } + } +} + diff --git a/.config/quickshell/ii/modules/overview/OverviewWidget.qml b/.config/quickshell/ii/modules/overview/OverviewWidget.qml index 2510a5642..13ec6370e 100644 --- a/.config/quickshell/ii/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/ii/modules/overview/OverviewWidget.qml @@ -235,7 +235,7 @@ Item { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active - content: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` + text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` } } } diff --git a/.config/quickshell/ii/modules/overview/SearchItem.qml b/.config/quickshell/ii/modules/overview/SearchItem.qml index 2e0afbfe8..f4fd20467 100644 --- a/.config/quickshell/ii/modules/overview/SearchItem.qml +++ b/.config/quickshell/ii/modules/overview/SearchItem.qml @@ -266,7 +266,7 @@ RippleButton { onClicked: modelData.execute() StyledToolTip { - content: modelData.name + text: modelData.name } } } diff --git a/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml b/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml index 199f2abed..000c4e201 100644 --- a/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml +++ b/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml @@ -52,7 +52,7 @@ RippleButton { } StyledToolTip { - content: buttonText + text: buttonText } } diff --git a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml index 9a8aa2a3b..1a400cddf 100644 --- a/.config/quickshell/ii/modules/settings/AdvancedConfig.qml +++ b/.config/quickshell/ii/modules/settings/AdvancedConfig.qml @@ -27,7 +27,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.enableQtApps = checked; } StyledToolTip { - content: Translation.tr("Shell & utilities theming must also be enabled") + text: Translation.tr("Shell & utilities theming must also be enabled") } } ConfigSwitch { @@ -38,7 +38,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.enableTerminal = checked; } StyledToolTip { - content: Translation.tr("Shell & utilities theming must also be enabled") + text: Translation.tr("Shell & utilities theming must also be enabled") } } ConfigRow { @@ -51,7 +51,7 @@ ContentPage { Config.options.appearance.wallpaperTheming.terminalGenerationProps.forceDarkMode= checked; } StyledToolTip { - content: Translation.tr("Ignored if terminal theming is not enabled") + text: Translation.tr("Ignored if terminal theming is not enabled") } } } diff --git a/.config/quickshell/ii/modules/settings/GeneralConfig.qml b/.config/quickshell/ii/modules/settings/GeneralConfig.qml index e520dab0b..5ba068f10 100644 --- a/.config/quickshell/ii/modules/settings/GeneralConfig.qml +++ b/.config/quickshell/ii/modules/settings/GeneralConfig.qml @@ -21,7 +21,7 @@ ContentPage { Config.options.audio.protection.enable = checked; } StyledToolTip { - content: Translation.tr("Prevents abrupt increments and restricts volume limit") + text: Translation.tr("Prevents abrupt increments and restricts volume limit") } } ConfigRow { @@ -85,7 +85,7 @@ ContentPage { Config.options.battery.automaticSuspend = checked; } StyledToolTip { - content: Translation.tr("Automatically suspends the system when battery is low") + text: Translation.tr("Automatically suspends the system when battery is low") } } ConfigSpinBox { diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 7df973622..2c14dc49f 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -168,7 +168,7 @@ ContentPage { Config.options.sidebar.keepRightSidebarLoaded = checked; } StyledToolTip { - content: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help") + text: Translation.tr("When enabled keeps the content of the right sidebar loaded to reduce the delay when opening,\nat the cost of around 15MB of consistent RAM usage. Delay significance depends on your system's performance.\nUsing a custom kernel like linux-cachyos might help") } } @@ -192,7 +192,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("When this is off you'll have to click") + text: Translation.tr("When this is off you'll have to click") } } } @@ -206,7 +206,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("Place the corners to trigger at the bottom") + text: Translation.tr("Place the corners to trigger at the bottom") } } ConfigSwitch { @@ -217,7 +217,7 @@ ContentPage { } StyledToolTip { - content: Translation.tr("Brightness and volume") + text: Translation.tr("Brightness and volume") } } } @@ -326,7 +326,7 @@ ContentPage { Config.options.screenshotTool.showContentRegions = checked; } StyledToolTip { - content: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.") + text: Translation.tr("Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.") } } } diff --git a/.config/quickshell/ii/modules/settings/QuickConfig.qml b/.config/quickshell/ii/modules/settings/QuickConfig.qml index d9a0a09cb..5b2de5112 100644 --- a/.config/quickshell/ii/modules/settings/QuickConfig.qml +++ b/.config/quickshell/ii/modules/settings/QuickConfig.qml @@ -99,14 +99,14 @@ ContentPage { konachanWallProc.running = true; } StyledToolTip { - content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") + text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { Layout.fillWidth: true materialIcon: "wallpaper" StyledToolTip { - content: Translation.tr("Pick wallpaper image on your system") + text: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached(`${Directories.wallpaperSwitchScriptPath}`); @@ -161,7 +161,7 @@ ContentPage { Config.options.appearance.transparency.enable = checked; } StyledToolTip { - content: Translation.tr("Might look ass. Unsupported.") + text: Translation.tr("Might look ass. Unsupported.") } } } diff --git a/.config/quickshell/ii/modules/settings/ServicesConfig.qml b/.config/quickshell/ii/modules/settings/ServicesConfig.qml index f28eab31a..dc7817cab 100644 --- a/.config/quickshell/ii/modules/settings/ServicesConfig.qml +++ b/.config/quickshell/ii/modules/settings/ServicesConfig.qml @@ -69,7 +69,7 @@ ContentPage { Config.options.search.sloppy = checked; } StyledToolTip { - content: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)") + text: Translation.tr("Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)") } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 22a64412c..f84972f3e 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -257,7 +257,7 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } StyledToolTip { - content: statusItem.description + text: statusItem.description extraVisibleCondition: false alternativeVisibleCondition: statusItem.containsMouse } diff --git a/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml b/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml index 878fba2b6..5cd969a24 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/ApiInputBoxIndicator.qml @@ -41,7 +41,7 @@ Item { // Model indicator id: toolTip extraVisibleCondition: false alternativeVisibleCondition: mouseArea.containsMouse // Show tooltip when hovered - content: root.tooltipText + text: root.tooltipText } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml b/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml index 87bfba710..2072a67ae 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/aiChat/AiMessage.qml @@ -164,7 +164,7 @@ Rectangle { text: "visibility_off" } StyledToolTip { - content: Translation.tr("Not visible to model") + text: Translation.tr("Not visible to model") } } @@ -191,7 +191,7 @@ Rectangle { } StyledToolTip { - content: Translation.tr("Copy") + text: Translation.tr("Copy") } } AiMessageControlButton { @@ -206,7 +206,7 @@ Rectangle { } } StyledToolTip { - content: root.editing ? Translation.tr("Save") : Translation.tr("Edit") + text: root.editing ? Translation.tr("Save") : Translation.tr("Edit") } } AiMessageControlButton { @@ -217,7 +217,7 @@ Rectangle { root.renderMarkdown = !root.renderMarkdown } StyledToolTip { - content: Translation.tr("View Markdown source") + text: Translation.tr("View Markdown source") } } AiMessageControlButton { @@ -227,7 +227,7 @@ Rectangle { Ai.removeMessage(root.messageIndex) } StyledToolTip { - content: Translation.tr("Delete") + text: Translation.tr("Delete") } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml index f8b0bac3e..dbe31d45d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/aiChat/MessageCodeBlock.qml @@ -84,7 +84,7 @@ ColumnLayout { } } StyledToolTip { - content: Translation.tr("Copy code") + text: Translation.tr("Copy code") } } AiMessageControlButton { @@ -114,7 +114,7 @@ ColumnLayout { } } StyledToolTip { - content: Translation.tr("Save to Downloads") + text: Translation.tr("Save to Downloads") } } } diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml index b2eef2939..8df196d9a 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruImage.qml @@ -41,7 +41,7 @@ Button { } StyledToolTip { - content: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}` + text: `${StringUtils.wordWrap(root.imageData.tags, root.maxTagStringLineLength)}` } padding: 0 diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml index 636d57729..ed2e4376f 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRightContent.qml @@ -88,7 +88,7 @@ Item { Quickshell.reload(true); } StyledToolTip { - content: Translation.tr("Reload Hyprland & Quickshell") + text: Translation.tr("Reload Hyprland & Quickshell") } } QuickToggleButton { @@ -99,7 +99,7 @@ Item { Quickshell.execDetached(["qs", "-p", root.settingsQmlPath]); } StyledToolTip { - content: Translation.tr("Settings") + text: Translation.tr("Settings") } } QuickToggleButton { @@ -109,7 +109,7 @@ Item { GlobalStates.sessionOpen = true; } StyledToolTip { - content: Translation.tr("Session") + text: Translation.tr("Session") } } } diff --git a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml index 6b5e5aa13..37a45db08 100644 --- a/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml +++ b/.config/quickshell/ii/modules/sidebarRight/calendar/CalendarHeaderButton.qml @@ -30,7 +30,7 @@ RippleButton { } StyledToolTip { - content: tooltipText + text: tooltipText extraVisibleCondition: tooltipText.length > 0 } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml index 1a4c053ed..af8a5cfc8 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/BluetoothToggle.qml @@ -21,7 +21,7 @@ QuickToggleButton { GlobalStates.sidebarRightOpen = false } StyledToolTip { - content: Translation.tr("%1 | Right-click to configure").arg( + text: Translation.tr("%1 | Right-click to configure").arg( (BluetoothStatus.firstActiveDevice?.name ?? Translation.tr("Bluetooth")) + (BluetoothStatus.activeDeviceCount > 1 ? ` +${BluetoothStatus.activeDeviceCount - 1}` : "") ) diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml index 39416ab22..1aebffa9a 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/CloudflareWarp.qml @@ -87,6 +87,6 @@ QuickToggleButton { } } StyledToolTip { - content: Translation.tr("Cloudflare WARP (1.1.1.1)") + text: Translation.tr("Cloudflare WARP (1.1.1.1)") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml index af428c54b..643764ff3 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/EasyEffectsToggle.qml @@ -26,6 +26,6 @@ QuickToggleButton { } StyledToolTip { - content: Translation.tr("EasyEffects | Right-click to configure") + text: Translation.tr("EasyEffects | Right-click to configure") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml index 1907080e8..e2b03ebef 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/GameMode.qml @@ -26,6 +26,6 @@ QuickToggleButton { } } StyledToolTip { - content: Translation.tr("Game mode") + text: Translation.tr("Game mode") } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml index 2b8f8b170..b6bce02f5 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/IdleInhibitor.qml @@ -10,7 +10,7 @@ QuickToggleButton { Idle.toggleInhibit() } StyledToolTip { - content: Translation.tr("Keep system awake") + text: Translation.tr("Keep system awake") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml index cb9e0d8e7..bb7d0969c 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NetworkToggle.qml @@ -18,6 +18,6 @@ QuickToggleButton { GlobalStates.sidebarRightOpen = false } StyledToolTip { - content: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) + text: Translation.tr("%1 | Right-click to configure").arg(Network.networkName) } } diff --git a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml index f0265126c..5fda831e1 100644 --- a/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/quickToggles/NightLight.qml @@ -23,6 +23,6 @@ QuickToggleButton { } StyledToolTip { - content: Translation.tr("Night Light | Right-click to toggle Auto mode") + text: Translation.tr("Night Light | Right-click to toggle Auto mode") } } diff --git a/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml b/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml index b0a6e7b9a..d2fe0abad 100644 --- a/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml +++ b/.config/quickshell/ii/modules/sidebarRight/todo/TodoItemActionButton.qml @@ -26,7 +26,7 @@ RippleButton { } StyledToolTip { - content: tooltipText + text: tooltipText extraVisibleCondition: tooltipText.length > 0 } } \ No newline at end of file diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index 7e544d218..1e714d39b 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -334,7 +334,7 @@ MouseArea { iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Use the system file picker instead\nRight-click to make this the default behavior") + text: Translation.tr("Use the system file picker instead\nRight-click to make this the default behavior") } } @@ -352,7 +352,7 @@ MouseArea { iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Pick random from this folder") + text: Translation.tr("Pick random from this folder") } } @@ -366,7 +366,7 @@ MouseArea { iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Click to toggle light/dark mode\n(applied when wallpaper is chosen)") + text: Translation.tr("Click to toggle light/dark mode\n(applied when wallpaper is chosen)") } } @@ -417,7 +417,7 @@ MouseArea { iconSize: Appearance.font.pixelSize.larger } StyledToolTip { - content: Translation.tr("Cancel wallpaper selection") + text: Translation.tr("Cancel wallpaper selection") } } } diff --git a/.config/quickshell/ii/settings.qml b/.config/quickshell/ii/settings.qml index 0f52b2382..d4724b534 100644 --- a/.config/quickshell/ii/settings.qml +++ b/.config/quickshell/ii/settings.qml @@ -177,7 +177,7 @@ ApplicationWindow { } StyledToolTip { - content: Translation.tr("Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json") + text: Translation.tr("Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json") } } diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index b92534039..a0699ac7c 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -111,7 +111,7 @@ ApplicationWindow { } StyledToolTip { - content: Translation.tr("Tip: Close a window with Super+Q") + text: Translation.tr("Tip: Close a window with Super+Q") } } } @@ -250,13 +250,13 @@ ApplicationWindow { konachanWallProc.running = true; } StyledToolTip { - content: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") + text: Translation.tr("Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers") } } RippleButtonWithIcon { materialIcon: "wallpaper" StyledToolTip { - content: Translation.tr("Pick wallpaper image on your system") + text: Translation.tr("Pick wallpaper image on your system") } onClicked: { Quickshell.execDetached([`${Directories.wallpaperSwitchScriptPath}`]); From d382358766ce1f43102d46640593fd71601c6f06 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:40:58 +0200 Subject: [PATCH 25/60] bar: tray: make action trigger on press --- .config/quickshell/ii/modules/bar/SysTrayItem.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index 41389e85d..44323c3c5 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -19,7 +19,7 @@ MouseArea { acceptedButtons: Qt.LeftButton | Qt.RightButton implicitWidth: 20 implicitHeight: 20 - onClicked: (event) => { + onPressed: (event) => { switch (event.button) { case Qt.LeftButton: item.activate(); From 72b5d006baa3fd58a7e46b81fedb5c10a57a9988 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 12:47:04 +0200 Subject: [PATCH 26/60] vertical bar: make tray tooltips not cut off --- .../quickshell/ii/modules/bar/SysTrayItem.qml | 3 +- .../modules/common/widgets/PopupToolTip.qml | 52 +++++++++++++++++++ .../common/widgets/StyledToolTipContent.qml | 14 +++-- 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100644 .config/quickshell/ii/modules/common/widgets/PopupToolTip.qml diff --git a/.config/quickshell/ii/modules/bar/SysTrayItem.qml b/.config/quickshell/ii/modules/bar/SysTrayItem.qml index 44323c3c5..1fe5df5f3 100644 --- a/.config/quickshell/ii/modules/bar/SysTrayItem.qml +++ b/.config/quickshell/ii/modules/bar/SysTrayItem.qml @@ -91,10 +91,11 @@ MouseArea { } } - StyledToolTip { + PopupToolTip { id: tooltip extraVisibleCondition: root.containsMouse alternativeVisibleCondition: extraVisibleCondition + anchorEdges: (!Config.options.bar.bottom && !Config.options.bar.vertical) ? Edges.Bottom : Edges.Top } } diff --git a/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml b/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml new file mode 100644 index 000000000..741b4556c --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/PopupToolTip.qml @@ -0,0 +1,52 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell + +Item { + id: root + property string text: "" + property bool extraVisibleCondition: true + property bool alternativeVisibleCondition: false + property real horizontalPadding: 10 + property real verticalPadding: 5 + + property var anchorEdges: Edges.Top + property var anchorGravity: anchorEdges + + readonly property bool internalVisibleCondition: (extraVisibleCondition && (parent.hovered === undefined || parent?.hovered)) || alternativeVisibleCondition + + Loader { + id: tooltipLoader + anchors.fill: parent + active: internalVisibleCondition + sourceComponent: PopupWindow { + visible: true + anchor { + window: root.QsWindow.window + item: root.parent + edges: root.anchorEdges + gravity: root.anchorGravity + } + mask: Region { + item: null + } + + color: "transparent" + implicitWidth: contentItem.implicitWidth + root.horizontalPadding * 2 + implicitHeight: contentItem.implicitHeight + root.verticalPadding * 2 + + StyledToolTipContent { + id: contentItem + anchors.centerIn: parent + text: root.text + shown: false + Component.onCompleted: shown = true + horizontalPadding: root.horizontalPadding + verticalPadding: root.verticalPadding + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml index 2d35cc6e6..9b08f9902 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledToolTipContent.qml @@ -7,21 +7,25 @@ import QtQuick.Layouts Item { id: root required property string text - property bool shown: true + property bool shown: false property real horizontalPadding: 10 property real verticalPadding: 5 implicitWidth: tooltipTextObject.implicitWidth + 2 * root.horizontalPadding implicitHeight: tooltipTextObject.implicitHeight + 2 * root.verticalPadding + property bool isVisible: backgroundRectangle.implicitHeight > 0 + Rectangle { id: backgroundRectangle - anchors.bottom: root.bottom - anchors.horizontalCenter: root.horizontalCenter + anchors { + bottom: root.bottom + horizontalCenter: root.horizontalCenter + } color: Appearance?.colors.colTooltip ?? "#3C4043" radius: Appearance?.rounding.verysmall ?? 7 opacity: shown ? 1 : 0 - implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * padding) : 0 - implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * padding) : 0 + implicitWidth: shown ? (tooltipTextObject.implicitWidth + 2 * root.horizontalPadding) : 0 + implicitHeight: shown ? (tooltipTextObject.implicitHeight + 2 * root.verticalPadding) : 0 clip: true Behavior on implicitWidth { From 5d1a3fe6a70c714866b3eeeea7f8346fbd04a3a1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:02:18 +0200 Subject: [PATCH 27/60] hyprland xkb layout indicator: fix weird placement on vertical bar --- .../quickshell/ii/modules/bar/BarContent.qml | 16 +++++++++++----- .../modules/verticalBar/VerticalBarContent.qml | 17 ++++++++++++----- .config/quickshell/ii/services/HyprlandXkb.qml | 2 +- 3 files changed, 24 insertions(+), 11 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/BarContent.qml b/.config/quickshell/ii/modules/bar/BarContent.qml index 7e1e885c0..b17fda1e9 100644 --- a/.config/quickshell/ii/modules/bar/BarContent.qml +++ b/.config/quickshell/ii/modules/bar/BarContent.qml @@ -293,11 +293,17 @@ Item { // Bar content region active: HyprlandXkb.layoutCodes.length > 1 visible: active Layout.rightMargin: indicatorsRowLayout.realSpacing - sourceComponent: StyledText { - text: HyprlandXkb.currentLayoutCode - font.pixelSize: Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true + sourceComponent: Item { + implicitWidth: layoutCodeText.implicitWidth + StyledText { + id: layoutCodeText + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: HyprlandXkb.currentLayoutCode.split(":").join("\n") + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smaller : Appearance.font.pixelSize.small + color: rightSidebarButton.colText + animateChange: true + } } } MaterialSymbol { diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml index bfcd0c6fb..5b4053e64 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml @@ -273,11 +273,18 @@ Item { // Bar content region active: HyprlandXkb.layoutCodes.length > 1 visible: active Layout.bottomMargin: indicatorsColumnLayout.realSpacing - sourceComponent: StyledText { - text: HyprlandXkb.currentLayoutCode - font.pixelSize: Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true + Layout.alignment: Qt.AlignHCenter + sourceComponent: Item { + implicitHeight: layoutCodeText.implicitHeight + StyledText { + id: layoutCodeText + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: HyprlandXkb.currentLayoutCode.split(":").join("\n") + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smaller : Appearance.font.pixelSize.small + color: rightSidebarButton.colText + animateChange: true + } } } MaterialSymbol { diff --git a/.config/quickshell/ii/services/HyprlandXkb.qml b/.config/quickshell/ii/services/HyprlandXkb.qml index 54c8f8f8b..1aa52ec54 100644 --- a/.config/quickshell/ii/services/HyprlandXkb.qml +++ b/.config/quickshell/ii/services/HyprlandXkb.qml @@ -57,7 +57,7 @@ Singleton { // Match variant: (whitespace + ) variant + whitespace + key + whitespace + description const matchVariant = line.match(/^\s*(\S+)\s+(\S+)\s+(.+)$/); if (matchVariant && matchVariant[3] === targetDescription) { - const complexLayout = matchVariant[2] + " " + matchVariant[1]; + const complexLayout = matchVariant[2] + matchVariant[1]; root.cachedLayoutCodes[matchVariant[3]] = complexLayout; root.currentLayoutCode = complexLayout; return true; From 06d6d3a9cac15668ab8ddc901f26f1b4a13bc26b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 13:05:23 +0200 Subject: [PATCH 28/60] bar: adjust xkb layout indicator font size --- .config/quickshell/ii/modules/bar/BarContent.qml | 2 +- .../quickshell/ii/modules/verticalBar/VerticalBarContent.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/BarContent.qml b/.config/quickshell/ii/modules/bar/BarContent.qml index b17fda1e9..d8c4318d1 100644 --- a/.config/quickshell/ii/modules/bar/BarContent.qml +++ b/.config/quickshell/ii/modules/bar/BarContent.qml @@ -300,7 +300,7 @@ Item { // Bar content region anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter text: HyprlandXkb.currentLayoutCode.split(":").join("\n") - font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smaller : Appearance.font.pixelSize.small + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small color: rightSidebarButton.colText animateChange: true } diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml index 5b4053e64..2c37f89f4 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml @@ -281,7 +281,7 @@ Item { // Bar content region anchors.centerIn: parent horizontalAlignment: Text.AlignHCenter text: HyprlandXkb.currentLayoutCode.split(":").join("\n") - font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smaller : Appearance.font.pixelSize.small + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small color: rightSidebarButton.colText animateChange: true } From 688bf0bef8fe1834fe2a9a044b191ee43b46b94c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Sep 2025 23:18:41 +0200 Subject: [PATCH 29/60] bar: refractor xkb layout indicator --- .../quickshell/ii/modules/bar/BarContent.qml | 17 ++---------- .../ii/modules/bar/HyprlandXkbIndicator.qml | 26 +++++++++++++++++++ .../verticalBar/VerticalBarContent.qml | 19 +++----------- 3 files changed, 31 insertions(+), 31 deletions(-) create mode 100644 .config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml diff --git a/.config/quickshell/ii/modules/bar/BarContent.qml b/.config/quickshell/ii/modules/bar/BarContent.qml index d8c4318d1..c0c5e31b8 100644 --- a/.config/quickshell/ii/modules/bar/BarContent.qml +++ b/.config/quickshell/ii/modules/bar/BarContent.qml @@ -289,22 +289,9 @@ Item { // Bar content region color: rightSidebarButton.colText } } - Loader { - active: HyprlandXkb.layoutCodes.length > 1 - visible: active + HyprlandXkbIndicator { + Layout.alignment: Qt.AlignVCenter Layout.rightMargin: indicatorsRowLayout.realSpacing - sourceComponent: Item { - implicitWidth: layoutCodeText.implicitWidth - StyledText { - id: layoutCodeText - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - text: HyprlandXkb.currentLayoutCode.split(":").join("\n") - font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true - } - } } MaterialSymbol { Layout.rightMargin: indicatorsRowLayout.realSpacing diff --git a/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml new file mode 100644 index 000000000..0c33c98e8 --- /dev/null +++ b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml @@ -0,0 +1,26 @@ +import QtQuick +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +Loader { + id: root + property bool vertical: false + + active: HyprlandXkb.layoutCodes.length > 1 + visible: active + + sourceComponent: Item { + implicitWidth: root.vertical ? undefined : layoutCodeText.implicitWidth + implicitHeight: root.vertical ? layoutCodeText.implicitHeight : undefined + StyledText { + id: layoutCodeText + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: HyprlandXkb.currentLayoutCode.split(":").join("\n") + font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small + color: rightSidebarButton.colText + animateChange: true + } + } +} diff --git a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml index 2c37f89f4..612510720 100644 --- a/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml +++ b/.config/quickshell/ii/modules/verticalBar/VerticalBarContent.qml @@ -269,23 +269,10 @@ Item { // Bar content region color: rightSidebarButton.colText } } - Loader { - active: HyprlandXkb.layoutCodes.length > 1 - visible: active - Layout.bottomMargin: indicatorsColumnLayout.realSpacing + Bar.HyprlandXkbIndicator { + vertical: true Layout.alignment: Qt.AlignHCenter - sourceComponent: Item { - implicitHeight: layoutCodeText.implicitHeight - StyledText { - id: layoutCodeText - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - text: HyprlandXkb.currentLayoutCode.split(":").join("\n") - font.pixelSize: text.includes("\n") ? Appearance.font.pixelSize.smallie : Appearance.font.pixelSize.small - color: rightSidebarButton.colText - animateChange: true - } - } + Layout.bottomMargin: indicatorsColumnLayout.realSpacing } MaterialSymbol { Layout.bottomMargin: indicatorsColumnLayout.realSpacing From b628ee0c3e62f813e715c767df30036854d408c7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 10:31:10 +0200 Subject: [PATCH 30/60] fix hyprland xkb indicator cant unassign undefined warning --- .config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml index 0c33c98e8..3e85b829b 100644 --- a/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml +++ b/.config/quickshell/ii/modules/bar/HyprlandXkbIndicator.qml @@ -11,8 +11,9 @@ Loader { visible: active sourceComponent: Item { - implicitWidth: root.vertical ? undefined : layoutCodeText.implicitWidth - implicitHeight: root.vertical ? layoutCodeText.implicitHeight : undefined + implicitWidth: root.vertical ? null : layoutCodeText.implicitWidth + implicitHeight: root.vertical ? layoutCodeText.implicitHeight : null + StyledText { id: layoutCodeText anchors.centerIn: parent From 5d95d20a325ca25eae86725499ed4e4f2e971203 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 10:31:21 +0200 Subject: [PATCH 31/60] sidebarright: remove unused imports --- .config/quickshell/ii/modules/sidebarRight/SidebarRight.qml | 5 ----- 1 file changed, 5 deletions(-) diff --git a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml index 4147c3abe..cdc861ce6 100644 --- a/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml +++ b/.config/quickshell/ii/modules/sidebarRight/SidebarRight.qml @@ -1,12 +1,7 @@ import qs import qs.services import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell import Quickshell.Wayland From cb3842f0bde70d086196f541c48b3700be00eaba Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 10:33:25 +0200 Subject: [PATCH 32/60] implement crosshair --- .config/quickshell/ii/GlobalStates.qml | 1 + .../quickshell/ii/modules/common/Config.qml | 5 + .../ii/modules/crosshair/Crosshair.qml | 57 +++++ .../ii/modules/crosshair/CrosshairContent.qml | 197 ++++++++++++++++++ .../ii/modules/settings/InterfaceConfig.qml | 31 +++ .config/quickshell/ii/shell.qml | 3 + 6 files changed, 294 insertions(+) create mode 100644 .config/quickshell/ii/modules/crosshair/Crosshair.qml create mode 100644 .config/quickshell/ii/modules/crosshair/CrosshairContent.qml diff --git a/.config/quickshell/ii/GlobalStates.qml b/.config/quickshell/ii/GlobalStates.qml index f2836c99b..207b23fe3 100644 --- a/.config/quickshell/ii/GlobalStates.qml +++ b/.config/quickshell/ii/GlobalStates.qml @@ -10,6 +10,7 @@ pragma ComponentBehavior: Bound Singleton { id: root property bool barOpen: true + property bool crosshairOpen: false property bool sidebarLeftOpen: false property bool sidebarRightOpen: false property bool mediaControlsOpen: false diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index d5e0a440a..878fdcd02 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -215,6 +215,11 @@ Singleton { property bool autoKillTrays: false } + property JsonObject crosshair: JsonObject { + // Valorant crosshair format. Use https://www.vcrdb.net/builder + property string code: "0;P;d;1;0l;10;0o;2;1b;0" + } + property JsonObject dock: JsonObject { property bool enable: false property bool monochromeIcons: true diff --git a/.config/quickshell/ii/modules/crosshair/Crosshair.qml b/.config/quickshell/ii/modules/crosshair/Crosshair.qml new file mode 100644 index 000000000..31c25f0dc --- /dev/null +++ b/.config/quickshell/ii/modules/crosshair/Crosshair.qml @@ -0,0 +1,57 @@ +import qs +import qs.modules.common +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + + Loader { + id: crosshairLoader + active: GlobalStates.crosshairOpen + sourceComponent: PanelWindow { + id: crosshairWindow + exclusionMode: ExclusionMode.Ignore + WlrLayershell.namespace: "quickshell:crosshair" + WlrLayershell.layer: WlrLayer.Overlay + visible: true + color: "transparent" + + mask: Region { // Crosshair should not block mouse input + item: null + } + + implicitWidth: crosshairContent.implicitWidth + implicitHeight: crosshairContent.implicitHeight + + CrosshairContent { + id: crosshairContent + anchors.centerIn: parent + } + } + } + + IpcHandler { + target: "sidebarRight" + + function toggle(): void { + GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; + } + } + + GlobalShortcut { + name: "crosshairToggle" + description: "Toggles crosshair on press" + + onPressed: { + GlobalStates.crosshairOpen = !GlobalStates.crosshairOpen; + } + } +} diff --git a/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml b/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml new file mode 100644 index 000000000..668a90867 --- /dev/null +++ b/.config/quickshell/ii/modules/crosshair/CrosshairContent.qml @@ -0,0 +1,197 @@ +pragma ComponentBehavior: Bound +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Item { + id: root + + // Keys to props + // f, 0f, 1f, m are irrelevant as they're firing error stuff + // 0 is irrelevant because it's some profile stuff + property var propertyMap: ({ + "c": "color", + "u": "colorCode", + "h": "outline", + "o": "outlineOpacity", + "t": "outlineThickness", + "d": "centerDot", + "a": "centerDotOpacity", + "z": "centerDotSize", + "0a": "innerLineOpacity", + "0l": "innerLineLength", + "0v": "innerLineVerticalLength", + "0g": "innerLineUnbindAxesLengths", + "0t": "innerLineThickness", + "0o": "innerLineOffset", + "1b": "outerLines", + "1a": "outerLineOpacity", + "1l": "outerLineLength", + "1v": "outerLineVerticalLength", + "1g": "outerLineUnbindAxesLengths", + "1t": "outerLineThickness", + "1o": "outerLineOffset", + }) + property var colorMap: ({ + 0: "#FFFFFF", + 1: "#00FF00", + 2: "#7FFF00", + 3: "#DFFF00", + 4: "#FFFF00", + 5: "#00FFFF", + 6: "#FF00FF", + 7: "#FF0000" + }) + + // Raw props + property int color: 0 + property string colorCode: "#FFFFFF" + property bool outline: true + property real outlineOpacity: 0.5 + property int outlineThickness: 1 + property bool centerDot: false + property real centerDotOpacity: 1 + property int centerDotSize: 2 + property bool innerLines: true + property real innerLineOpacity: 0.8 + property int innerLineLength: 6 + property int innerLineVerticalLength: innerLineLength + property bool innerLineUnbindAxesLengths: false + property int innerLineThickness: 2 + property int innerLineOffset: 3 + property bool outerLines: true + property real outerLineOpacity: 0.35 + property int outerLineLength: 2 + property int outerLineVerticalLength: outerLineLength + property bool outerLineUnbindAxesLengths: false + property int outerLineThickness: 2 + property int outerLineOffset: 10 + property string defaultCode: "c;0;u;FFFFFF;h;1;o;0.5;t;1;d;0;a;1;z;2;0a;0.8;0l;6;0v;6;0g;0;0t;2;0o;3;1b;1;1a;0.35;1l;2;1v;2;1g;0;1t;2;1o;10" + + function loadFromCode(code: string): void { + let args = code.split(";"); + for (let i = 0; i < args.length; i+= 2) { + let key = args[i]; + let value = args[i+1]; + let targetKey = root.propertyMap[key]; + let targetType = typeof root[targetKey]; + + if (targetKey === undefined) continue; + + if (targetType === "number") { + value = parseFloat(value); + } else if (targetType === "boolean") { + value = (value === "1"); + } + if (targetKey === "colorCode") { + value = "#" + value.slice(0, 6); + } + root[targetKey] = value; + } + + if (!root.innerLineUnbindAxesLengths) { + root.innerLineVerticalLength = root.innerLineLength; + } + if (!root.outerLineUnbindAxesLengths) { + root.outerLineVerticalLength = root.outerLineLength; + } + + } + + // Update values from code + property var code: Config.options.crosshair.code + Component.onCompleted: reloadFromCode(); + onCodeChanged: reloadFromCode(); + function reloadFromCode() { + root.loadFromCode(root.defaultCode); + root.loadFromCode(root.code); + } + + // Aggregated props + property color crosshairColor: { + if (colorMap[color] !== undefined) return root.colorMap[color]; + if (color === 8) return colorCode; + return "#FFFFFF"; + } + property int borderWidth: outline ? outlineThickness : 0 + property color borderColor: ColorUtils.transparentize("black", 1 - root.outlineOpacity) + property color innerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.innerLineOpacity) + property color outerLineColor: ColorUtils.transparentize(root.crosshairColor, 1 - root.outerLineOpacity) + property int innerLineTotalOffset: root.centerDotSize / 2 + 1 + root.innerLineOffset + property int outerLineTotalOffset: root.centerDotSize / 2 + 1 + root.outerLineOffset + property real centerDotTotalSize: root.centerDotSize + root.borderWidth * 2 + property real innerLineTotalSize: (innerLineTotalOffset + root.innerLineLength + root.borderWidth) * 2 + property real outerLineTotalSize: (outerLineTotalOffset + root.outerLineLength + root.borderWidth) * 2 + implicitWidth: Math.max(centerDotTotalSize, innerLineTotalSize, outerLineTotalSize) + 2 // 2 for pixel correction + implicitHeight: implicitWidth + // width: implicitWidth + // height: implicitHeight + + Rectangle { + id: centerDot + visible: root.centerDot + anchors.centerIn: parent + + color: root.crosshairColor + opacity: root.centerDotOpacity + width: centerDotTotalSize + height: width + + border.width: root.borderWidth + border.color: root.borderColor + } + + Repeater { + id: innerLines + model: 4 + Item { + id: innerHair + z: index % 2 // Vertical lines above horizontal lines + required property int index + property int pixelCorrection: (root.innerLineThickness % 2 === 1 && index > 1) ? 1 : 0 + property int hairLength: (innerHair.index % 2 === 0 ? root.innerLineLength : root.innerLineVerticalLength) + visible: root.innerLines && hairLength > 0 + anchors.fill: parent + rotation: index * 90 + Rectangle { + x: parent.width / 2 + root.innerLineTotalOffset - root.borderWidth + innerHair.pixelCorrection + y: parent.height / 2 - height / 2 + + color: root.innerLineColor + width: innerHair.hairLength + root.borderWidth * 2 + height: root.innerLineThickness + root.borderWidth * 2 + + border.width: root.borderWidth + border.color: root.borderColor + } + } + } + + Repeater { + id: outerLines + model: 4 + Item { + id: outerHair + z: index % 2 + 2 // Vertical lines above horizontal lines, above inner lines + required property int index + property int pixelCorrection: (root.outerLineThickness % 2 === 1 && index > 1) ? 1 : 0 + property int hairLength: (outerHair.index % 2 === 0 ? root.outerLineLength : root.outerLineVerticalLength) + visible: root.outerLines && hairLength > 0 + anchors.fill: parent + rotation: index * 90 + Rectangle { + x: parent.width / 2 + root.outerLineTotalOffset - root.borderWidth + outerHair.pixelCorrection + y: parent.height / 2 - height / 2 + + color: root.outerLineColor + width: hairLength + root.borderWidth * 2 + height: root.outerLineThickness + root.borderWidth * 2 + + border.width: root.borderWidth + border.color: root.borderColor + } + } + } + + +} diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 2c14dc49f..de1680609 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -61,6 +61,37 @@ ContentPage { } } + ContentSection { + icon: "point_scan" + title: Translation.tr("Crosshair") + + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Valorant crosshair code") + text: Config.options.crosshair.code + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.crosshair.code = text; + } + } + + RowLayout { + Item { Layout.fillWidth: true } + RippleButtonWithIcon { + id: editorButton + buttonRadius: Appearance.rounding.full + materialIcon: "open_in_new" + mainText: Translation.tr("Open editor") + onClicked: { + Qt.openUrlExternally(`https://www.vcrdb.net/builder?c=${Config.options.crosshair.code}`); + } + StyledToolTip { + text: "www.vcrdb.net" + } + } + } + } + ContentSection { icon: "call_to_action" title: Translation.tr("Dock") diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index f2062cc23..0abe0dca5 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -11,6 +11,7 @@ import "./modules/common/" import "./modules/background/" import "./modules/bar/" import "./modules/cheatsheet/" +import "./modules/crosshair/" import "./modules/dock/" import "./modules/lock/" import "./modules/mediaControls/" @@ -36,6 +37,7 @@ ShellRoot { property bool enableBar: true property bool enableBackground: true property bool enableCheatsheet: true + property bool enableCrosshair: true property bool enableDock: true property bool enableLock: true property bool enableMediaControls: true @@ -64,6 +66,7 @@ ShellRoot { LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} } LazyLoader { active: enableBackground; component: Background {} } LazyLoader { active: enableCheatsheet; component: Cheatsheet {} } + LazyLoader { active: enableCrosshair; component: Crosshair {} } LazyLoader { active: enableDock && Config.options.dock.enable; component: Dock {} } LazyLoader { active: enableLock; component: Lock {} } LazyLoader { active: enableMediaControls; component: MediaControls {} } From 9bb01bba304a6641511fc06b13ef7b2284a7aab8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 10:37:12 +0200 Subject: [PATCH 33/60] update hyprland config for crosshair --- .config/hypr/hyprland/keybinds.conf | 1 + .config/hypr/hyprland/rules.conf | 34 ++++++++++------------------- 2 files changed, 13 insertions(+), 22 deletions(-) diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 434e8ad31..4e3a94a8a 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -33,6 +33,7 @@ bindd = Super, N, Toggle right sidebar, global, quickshell:sidebarRightToggle # bindd = Super, Slash, Toggle cheatsheet, global, quickshell:cheatsheetToggle # Toggle cheatsheet bindd = Super, K, Toggle on-screen keyboard, global, quickshell:oskToggle # Toggle on-screen keyboard bindd = Super, M, Toggle media controls, global, quickshell:mediaControlsToggle # Toggle media controls +bind = Super, G, global, quickshell:crosshairToggle # Toggle crosshair bindd = Ctrl+Alt, Delete, Toggle session menu, global, quickshell:sessionToggle # Toggle session menu bindd = Super, J, Toggle bar, global, quickshell:barToggle # Toggle bar bind = Ctrl+Alt, Delete, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill wlogout || wlogout -p layer-shell # [hidden] Session menu (fallback) diff --git a/.config/hypr/hyprland/rules.conf b/.config/hypr/hyprland/rules.conf index f5a86a238..0589edb85 100644 --- a/.config/hypr/hyprland/rules.conf +++ b/.config/hypr/hyprland/rules.conf @@ -130,32 +130,22 @@ layerrule = blurpopups, quickshell:.* layerrule = blur, quickshell:.* layerrule = ignorealpha 0.79, quickshell:.* layerrule = animation slide, quickshell:bar -layerrule = animation slide, quickshell:verticalBar -layerrule = animation fade, quickshell:screenCorners -layerrule = animation slide right, quickshell:sidebarRight -layerrule = animation slide left, quickshell:sidebarLeft -layerrule = animation slide top, quickshell:wallpaperSelector -layerrule = animation slide bottom, quickshell:osk -layerrule = animation slide bottom, quickshell:dock layerrule = animation slide bottom, quickshell:cheatsheet +layerrule = noanim, quickshell:crosshair +layerrule = animation slide bottom, quickshell:dock +layerrule = animation popin 120%, quickshell:screenCorners +layerrule = noanim, quickshell:lockWindowPusher +layerrule = animation fade, quickshell:notificationPopup +layerrule = noanim, quickshell:overview +layerrule = animation slide bottom, quickshell:osk +layerrule = noanim, quickshell:screenshot layerrule = blur, quickshell:session layerrule = noanim, quickshell:session layerrule = ignorealpha 0, quickshell:session -layerrule = animation fade, quickshell:notificationPopup -layerrule = blur, quickshell:backgroundWidgets -layerrule = ignorealpha 0.05, quickshell:backgroundWidgets -layerrule = noanim, quickshell:screenshot -layerrule = animation popin 120%, quickshell:screenCorners -layerrule = noanim, quickshell:lockWindowPusher - +layerrule = animation slide right, quickshell:sidebarRight +layerrule = animation slide left, quickshell:sidebarLeft +layerrule = animation slide, quickshell:verticalBar +layerrule = animation slide top, quickshell:wallpaperSelector # Launchers need to be FAST -layerrule = noanim, quickshell:overview layerrule = noanim, gtk4-layer-shell -## outfoxxed's stuff -layerrule = blur, shell:bar -layerrule = ignorezero, shell:bar -layerrule = blur, shell:notifications -layerrule = ignorealpha 0.1, shell:notifications - - From b4cced68f1326edaa01b7dcfebba7120f8698ce1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 11:03:37 +0200 Subject: [PATCH 34/60] Change SCSS file path emacs theme thingy --- Extras/emacs/material-theme.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Extras/emacs/material-theme.el b/Extras/emacs/material-theme.el index 41c6bdcbc..a836f2ebf 100644 --- a/Extras/emacs/material-theme.el +++ b/Extras/emacs/material-theme.el @@ -23,7 +23,7 @@ ;; Define function to read SCSS variables (defun material-get-color-from-scss (var-name) "Extract color value for VAR-NAME from material_colors.scss file." - (let* ((scss-file (expand-file-name "~/.cache/ags/user/generated/material_colors.scss")) + (let* ((scss-file (expand-file-name "~/.local/state/quickshell/user/generated/material_colors.scss")) (scss-content (with-temp-buffer (insert-file-contents scss-file) (buffer-string))) From 16995d2ae5b6a8bda2e354285cb21f8a7533c02a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 12:08:09 +0200 Subject: [PATCH 35/60] wallpaper safety --- .../ii/modules/background/Background.qml | 146 +++++++++++------- .../quickshell/ii/modules/common/Config.qml | 11 +- .../modules/common/functions/StringUtils.qml | 46 +++++- 3 files changed, 140 insertions(+), 63 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 3df6c445c..c6cf9cd94 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -45,6 +45,10 @@ Variants { || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath + property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && ( + CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) && + CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords) + ) property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated @@ -57,7 +61,7 @@ Variants { property real clockX: (modelData.width / 2) property real clockY: (modelData.height / 2) property var textHorizontalAlignment: { - if (Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) + if ((Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered) return Text.AlignHCenter; if (clockX < screen.width / 3) return Text.AlignLeft; @@ -69,11 +73,61 @@ Variants { property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable) property color dominantColor: Appearance.colors.colPrimary property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 - property color colText: (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + property color colText: { + if (wallpaperSafetyTriggered) return CF.ColorUtils.mix(Appearance.colors.colOnLayer0, Appearance.colors.colPrimary, 0.75); + return (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + } Behavior on colText { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } + // Components + component ClockText: StyledText { + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: Font.DemiBold + } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: true + } + component ClockStatusText: RowLayout { + id: statusTextRow + property alias statusIcon: statusIconWidget.text + property alias statusText: statusTextWidget.text + property bool shown: true + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Layout.fillWidth: false + MaterialSymbol { + id: statusIconWidget + Layout.fillWidth: false + iconSize: Appearance.font.pixelSize.huge + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + ClockText { + id: statusTextWidget + Layout.fillWidth: false + color: bgRoot.colText + font { + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.large + weight: Font.Normal + } + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + } + // Layer props screen: modelData exclusionMode: ExclusionMode.Ignore @@ -86,7 +140,10 @@ Variants { left: true right: true } - color: "transparent" + color: CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75); + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } onWallpaperPathChanged: { bgRoot.updateZoomScale() @@ -206,7 +263,12 @@ Variants { property real effectiveValueY: Math.max(0, Math.min(1, valueY)) x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace - source: bgRoot.wallpaperPath + source: { + print("-----------------") + print("Safety triggered:", bgRoot.wallpaperSafetyTriggered); + print("Wallpaper path:", bgRoot.wallpaperPath); + return bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath + } fillMode: Image.PreserveAspectCrop Behavior on x { NumberAnimation { @@ -278,13 +340,14 @@ Variants { } states: State { name: "centered" - when: bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock + when: (bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock) || bgRoot.wallpaperSafetyTriggered AnchorChanges { target: clockLoader anchors { left: undefined right: undefined - top: parent.top + top: undefined + verticalCenter: parent.verticalCenter horizontalCenter: parent.horizontalCenter } } @@ -306,46 +369,27 @@ Variants { anchors.centerIn: parent spacing: 6 - StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 90 - weight: Font.Bold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow + ClockText { + font.pixelSize: 90 text: DateTime.time } - StyledText { - Layout.fillWidth: true + ClockText { Layout.topMargin: -5 - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow text: DateTime.date - animateChange: true } - StyledText { + StyledText { // Somehow gets fucked up if made a ClockText??? Layout.fillWidth: true horizontalAlignment: bgRoot.textHorizontalAlignment font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.normal + weight: 350 + italic: true } color: bgRoot.colText style: Text.Raised - visible: Config.options.background.quote !== "" styleColor: Appearance.colors.colShadow + // visible: Config.options.background.quote.length > 0 text: Config.options.background.quote } } @@ -356,31 +400,21 @@ Variants { left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined - topMargin: 5 - leftMargin: -5 - rightMargin: -5 - } - opacity: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) ? 1 : 0 - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + topMargin: 14 + leftMargin: -6 + rightMargin: -6 } + spacing: 16 Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } - MaterialSymbol { - text: "lock" - Layout.fillWidth: false - iconSize: Appearance.font.pixelSize.huge - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow + ClockStatusText { + shown: bgRoot.wallpaperSafetyTriggered + statusIcon: "hide_image" + statusText: qsTr("Wallpaper safety enforced") } - StyledText { - Layout.fillWidth: false - text: "Locked" - color: bgRoot.colText - font.pixelSize: Appearance.font.pixelSize.larger - style: Text.Raised - styleColor: Appearance.colors.colShadow + ClockStatusText { + shown: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) + statusIcon: "lock" + statusText: qsTr("Locked") } Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 878fdcd02..c3233a722 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -128,6 +128,8 @@ Singleton { property bool showClock: true property string wallpaperPath: "" property string thumbnailPath: "" + property string quote: "" + property bool hideWhenFullscreen: true property JsonObject parallax: JsonObject { property bool vertical: false property bool autoVertical: false @@ -142,8 +144,13 @@ Singleton { property bool showLockedText: true property real extraZoom: 1.1 } - property string quote: "" - property bool hideWhenFullscreen: true + property JsonObject wallpaperSafety: JsonObject { + property bool enable: true + property JsonObject triggerCondition: JsonObject { + property list wallpaperKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] + property list networkNameKeywords: ["guest", "public", "free", "airport"] + } + } } property JsonObject bar: JsonObject { diff --git a/.config/quickshell/ii/modules/common/functions/StringUtils.qml b/.config/quickshell/ii/modules/common/functions/StringUtils.qml index ea5e42df9..d3158a46c 100644 --- a/.config/quickshell/ii/modules/common/functions/StringUtils.qml +++ b/.config/quickshell/ii/modules/common/functions/StringUtils.qml @@ -8,7 +8,7 @@ Singleton { * Formats a string according to the args that are passed inc * @param { string } str * @param {...any} args - * @returns + * @returns { string } */ function format(str, ...args) { return str.replace(/{(\d+)}/g, (match, index) => typeof args[index] !== 'undefined' ? args[index] : match); @@ -35,10 +35,10 @@ Singleton { } /** - * Escapes single quotes in shell commands - * @param { string } str - * @returns { string } - */ + * Escapes single quotes in shell commands + * @param { string } str + * @returns { string } + */ function shellSingleQuoteEscape(str) { return String(str) // .replace(/\\/g, '\\\\') @@ -48,6 +48,7 @@ Singleton { /** * Splits markdown blocks into three different types: text, think, and code. * @param { string } markdown + * @returns {Array<{type: "text" | "think" | "code", content: string, lang?: string, completed?: boolean}>} */ function splitMarkdownBlocks(markdown) { const regex = /```(\w+)?\n([\s\S]*?)```|([\s\S]*?)<\/think>/g; @@ -182,6 +183,11 @@ Singleton { return lines.join("\n"); } + /** + * Cleans up a music title by removing bracketed and special characters. + * @param { string } title + * @returns { string } + */ function cleanMusicTitle(title) { if (!title) return ""; @@ -198,6 +204,11 @@ Singleton { return title.trim(); } + /** + * Converts seconds to a friendly time string (e.g. 1:23 or 1:02:03). + * @param { number } seconds + * @returns { string } + */ function friendlyTimeForSeconds(seconds) { if (isNaN(seconds) || seconds < 0) return "0:00"; @@ -212,13 +223,38 @@ Singleton { } } + /** + * Escapes HTML special characters in a string. + * @param { string } str + * @returns { string } + */ function escapeHtml(str) { if (typeof str !== 'string') return str; return str.replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"').replace(/'/g, '''); } + /** + * Cleans a cliphist entry by removing leading digits and tab. + * @param { string } str + * @returns { string } + */ function cleanCliphistEntry(str: string): string { return str.replace(/^\d+\t/, ""); } + + /** + * Checks if any substring in the list is contained in the string. + * @param { string } str + * @param { string[] } substrings + * @returns { boolean } + */ + function stringListContainsSubstring(str, substrings) { + for (let i = 0; i < substrings.length; ++i) { + if (str.includes(substrings[i])) { + return true; + } + } + return false; + } } From 800f5c7c649828dc1e4da59f4d242d3ff16a7ddf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 13:59:20 +0200 Subject: [PATCH 36/60] background: fix locked text spacing when there is no quote --- .config/quickshell/ii/modules/background/Background.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index c6cf9cd94..010f5850f 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -378,6 +378,7 @@ Variants { text: DateTime.date } StyledText { // Somehow gets fucked up if made a ClockText??? + visible: Config.options.background.quote.length > 0 Layout.fillWidth: true horizontalAlignment: bgRoot.textHorizontalAlignment font { @@ -389,7 +390,6 @@ Variants { color: bgRoot.colText style: Text.Raised styleColor: Appearance.colors.colShadow - // visible: Config.options.background.quote.length > 0 text: Config.options.background.quote } } From 9762a94138e27e7ac7d0f21d7533687f23ed6686 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:57:27 +0200 Subject: [PATCH 37/60] notifications: make icon guesses less strict --- .../ii/modules/common/widgets/notification_utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/common/widgets/notification_utils.js b/.config/quickshell/ii/modules/common/widgets/notification_utils.js index 9b151055c..7ab21c3bb 100644 --- a/.config/quickshell/ii/modules/common/widgets/notification_utils.js +++ b/.config/quickshell/ii/modules/common/widgets/notification_utils.js @@ -9,7 +9,7 @@ function findSuitableMaterialSymbol(summary = "") { const keywordsToTypes = { 'reboot': 'restart_alt', - 'recording': 'screen_record', + 'record': 'screen_record', 'battery': 'power', 'power': 'power', 'screenshot': 'screenshot_monitor', @@ -21,7 +21,7 @@ function findSuitableMaterialSymbol(summary = "") { 'update': 'update', 'ai response': 'neurology', 'control': 'settings', - 'upscale': 'compare', + 'upsca': 'compare', 'install': 'deployed_code_update', 'startswith:file': 'folder_copy', // Declarative startsWith check }; From 063399a6d61dfa868777b5d9c8be3b15bc220e11 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 18:57:38 +0200 Subject: [PATCH 38/60] settings: make crosshair code setting less misleading --- .config/quickshell/ii/modules/settings/InterfaceConfig.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index de1680609..9ff65f4b6 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -67,7 +67,7 @@ ContentPage { MaterialTextArea { Layout.fillWidth: true - placeholderText: Translation.tr("Valorant crosshair code") + placeholderText: Translation.tr("Crosshair code (in Valorant's format)") text: Config.options.crosshair.code wrapMode: TextEdit.Wrap onTextChanged: { From df7504d4a225fd9228f49856ed7cca1262af2764 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 19:51:15 +0200 Subject: [PATCH 39/60] remove useless ai comments --- .../ii/modules/sidebarLeft/AiChat.qml | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index 6c69603f1..be0ee350d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -211,7 +211,6 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) } // Always scroll to bottom when user sends a message - messageListView.shouldAutoScroll = true messageListView.positionViewAtEnd() } @@ -320,26 +319,13 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) property int lastResponseLength: 0 - // Simple auto-scroll state tracking (proven chat pattern) property bool shouldAutoScroll: true - - // Track when user scrolls - simple and reliable - onContentYChanged: { - shouldAutoScroll = atYEnd - } - - // Auto-scroll when content height changes (during streaming) + onContentYChanged: shouldAutoScroll = atYEnd onContentHeightChanged: { - if (shouldAutoScroll) { - positionViewAtEnd() - } + if (shouldAutoScroll) positionViewAtEnd(); } - - // Auto-scroll when new messages are added - onCountChanged: { - if (shouldAutoScroll) { - positionViewAtEnd() - } + onCountChanged: { // Auto-scroll when new messages are added + if (shouldAutoScroll) positionViewAtEnd(); } clip: true From b90cf142280b869783bee1ee839c74b876b68c9f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Sep 2025 20:21:34 +0200 Subject: [PATCH 40/60] add random wallpaper keybind --- .config/hypr/hyprland/keybinds.conf | 1 + .../ii/modules/background/Background.qml | 7 +------ .../wallpaperSelector/WallpaperSelector.qml | 16 +++++++++++++++- .../WallpaperSelectorContent.qml | 4 +--- .config/quickshell/ii/services/Wallpapers.qml | 10 ++++++++++ .config/quickshell/ii/shell.qml | 1 + 6 files changed, 29 insertions(+), 10 deletions(-) diff --git a/.config/hypr/hyprland/keybinds.conf b/.config/hypr/hyprland/keybinds.conf index 4e3a94a8a..a24470334 100644 --- a/.config/hypr/hyprland/keybinds.conf +++ b/.config/hypr/hyprland/keybinds.conf @@ -50,6 +50,7 @@ bindl = Alt ,XF86AudioMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidd bindl = ,XF86AudioMicMute, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindld = Super+Alt,M, Toggle mic, exec, wpctl set-mute @DEFAULT_SOURCE@ toggle # [hidden] bindd = Ctrl+Super, T, Toggle wallpaper selector, global, quickshell:wallpaperSelectorToggle # Wallpaper selector +bindd = Ctrl+Super+Alt, T, Select random wallpaper, global, quickshell:wallpaperSelectorRandom # Random wallpaper bindd = Ctrl+Super, T, Change wallpaper, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # [hidden] Change wallpaper (fallback) bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 010f5850f..24a1d0b82 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -263,12 +263,7 @@ Variants { property real effectiveValueY: Math.max(0, Math.min(1, valueY)) x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace - source: { - print("-----------------") - print("Safety triggered:", bgRoot.wallpaperSafetyTriggered); - print("Wallpaper path:", bgRoot.wallpaperPath); - return bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath - } + source: bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath fillMode: Image.PreserveAspectCrop Behavior on x { NumberAnimation { diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml index 43ebc31c2..197acbf2e 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelector.qml @@ -71,11 +71,25 @@ Scope { function toggle(): void { root.toggleWallpaperSelector(); } + + function random(): void { + Wallpapers.randomFromCurrentFolder(); + } } GlobalShortcut { name: "wallpaperSelectorToggle" description: "Toggle wallpaper selector" - onPressed: root.toggleWallpaperSelector(); + onPressed: { + root.toggleWallpaperSelector(); + } + } + + GlobalShortcut { + name: "wallpaperSelectorRandom" + description: "Select random wallpaper in current folder" + onPressed: { + Wallpapers.randomFromCurrentFolder(); + } } } diff --git a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml index 1e714d39b..9d89dfd7d 100644 --- a/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml +++ b/.config/quickshell/ii/modules/wallpaperSelector/WallpaperSelectorContent.qml @@ -341,9 +341,7 @@ MouseArea { ToolbarButton { implicitWidth: height onClicked: { - const randomIndex = Math.floor(Math.random() * Wallpapers.folderModel.count); - const filePath = Wallpapers.folderModel.get(randomIndex, "filePath"); - root.selectWallpaperPath(filePath); + Wallpapers.randomFromCurrentFolder(); } contentItem: MaterialSymbol { anchors.centerIn: parent diff --git a/.config/quickshell/ii/services/Wallpapers.qml b/.config/quickshell/ii/services/Wallpapers.qml index 07e984ff2..8ee9b7dae 100644 --- a/.config/quickshell/ii/services/Wallpapers.qml +++ b/.config/quickshell/ii/services/Wallpapers.qml @@ -33,6 +33,8 @@ Singleton { signal thumbnailGenerated(directory: string) signal thumbnailGeneratedFile(filePath: string) + function load () {} // For forcing initialization + // Executions Process { id: applyProc @@ -77,6 +79,14 @@ Singleton { selectProc.select(filePath, darkMode); } + function randomFromCurrentFolder(darkMode = Appearance.m3colors.darkmode) { + if (folderModel.count === 0) return; + const randomIndex = Math.floor(Math.random() * folderModel.count); + const filePath = folderModel.get(randomIndex, "filePath"); + print("Randomly selected wallpaper:", filePath); + root.select(filePath, darkMode); + } + Process { id: validateDirProc property string nicePath: "" diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 0abe0dca5..1088669ed 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -61,6 +61,7 @@ ShellRoot { FirstRunExperience.load() ConflictKiller.load() Cliphist.refresh() + Wallpapers.load() } LazyLoader { active: enableBar && Config.ready && !Config.options.bar.vertical; component: Bar {} } From 2237ec135c25f3a97c201b54ccc9d6179d1e6c4b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Sep 2025 20:25:46 +0200 Subject: [PATCH 41/60] background: add cookie clock style --- .../ii/modules/background/Background.qml | 577 ++++++++++-------- .../ii/modules/background/CookieClock.qml | 136 +++++ .../quickshell/ii/modules/common/Config.qml | 11 +- .../modules/common/widgets/MaterialCookie.qml | 50 ++ .../widgets/StyledRectangularShadow.qml | 2 +- .../ii/modules/settings/InterfaceConfig.qml | 26 +- 6 files changed, 524 insertions(+), 278 deletions(-) create mode 100644 .config/quickshell/ii/modules/background/CookieClock.qml create mode 100644 .config/quickshell/ii/modules/common/widgets/MaterialCookie.qml diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 24a1d0b82..fd1ee53dd 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -13,14 +13,14 @@ import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland - Variants { id: root - readonly property bool fixedClockPosition: Config.options.background.fixedClockPosition - readonly property real fixedClockX: Config.options.background.clockX - readonly property real fixedClockY: Config.options.background.clockY + readonly property bool fixedClockPosition: Config.options.background.clock.fixedPosition + readonly property real fixedClockX: Config.options.background.clock.x + readonly property real fixedClockY: Config.options.background.clock.y readonly property real clockSizePadding: 20 readonly property real screenSizePadding: 50 + readonly property string clockStyle: Config.options.background.clock.style model: Quickshell.screens PanelWindow { @@ -29,8 +29,8 @@ Variants { required property var modelData // Hide when fullscreen - property list workspacesForMonitor: Hyprland.workspaces.values.filter(workspace=>workspace.monitor && workspace.monitor.name == monitor.name) - property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace=>((workspace.toplevels.values.filter(window=>window.wayland?.fullscreen)[0] != undefined) && workspace.active))[0] + property list workspacesForMonitor: Hyprland.workspaces.values.filter(workspace => workspace.monitor && workspace.monitor.name == monitor.name) + property var activeWorkspaceWithFullscreen: workspacesForMonitor.filter(workspace => ((workspace.toplevels.values.filter(window => window.wayland?.fullscreen)[0] != undefined) && workspace.active))[0] visible: GlobalStates.screenLocked || (!(activeWorkspaceWithFullscreen != undefined)) || !Config?.options.background.hideWhenFullscreen // Workspaces @@ -39,16 +39,9 @@ Variants { property int firstWorkspaceId: relevantWindows[0]?.workspace.id || 1 property int lastWorkspaceId: relevantWindows[relevantWindows.length - 1]?.workspace.id || 10 // Wallpaper - property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") - || Config.options.background.wallpaperPath.endsWith(".webm") - || Config.options.background.wallpaperPath.endsWith(".mkv") - || Config.options.background.wallpaperPath.endsWith(".avi") - || Config.options.background.wallpaperPath.endsWith(".mov") + property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath - property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && ( - CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) && - CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords) - ) + property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) && CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords)) property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated @@ -74,60 +67,14 @@ Variants { property color dominantColor: Appearance.colors.colPrimary property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 property color colText: { - if (wallpaperSafetyTriggered) return CF.ColorUtils.mix(Appearance.colors.colOnLayer0, Appearance.colors.colPrimary, 0.75); - return (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)) + if (wallpaperSafetyTriggered) + return CF.ColorUtils.mix(Appearance.colors.colOnLayer0, Appearance.colors.colPrimary, 0.75); + return (GlobalStates.screenLocked && shouldBlur) ? Appearance.colors.colOnLayer0 : CF.ColorUtils.colorWithLightness(Appearance.colors.colPrimary, (dominantColorIsDark ? 0.8 : 0.12)); } Behavior on colText { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } - // Components - component ClockText: StyledText { - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - animateChange: true - } - component ClockStatusText: RowLayout { - id: statusTextRow - property alias statusIcon: statusIconWidget.text - property alias statusText: statusTextWidget.text - property bool shown: true - opacity: shown ? 1 : 0 - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Layout.fillWidth: false - MaterialSymbol { - id: statusIconWidget - Layout.fillWidth: false - iconSize: Appearance.font.pixelSize.huge - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - ClockText { - id: statusTextWidget - Layout.fillWidth: false - color: bgRoot.colText - font { - family: Appearance.font.family.main - pixelSize: Appearance.font.pixelSize.large - weight: Font.Normal - } - style: Text.Raised - styleColor: Appearance.colors.colShadow - } - } - // Layer props screen: modelData exclusionMode: ExclusionMode.Ignore @@ -140,45 +87,43 @@ Variants { left: true right: true } - color: CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75); + color: CF.ColorUtils.transparentize(CF.ColorUtils.mix(Appearance.colors.colLayer0, Appearance.colors.colPrimary, 0.75), (bgRoot.wallpaperIsVideo ? 1 : 0)) Behavior on color { animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) } onWallpaperPathChanged: { - bgRoot.updateZoomScale() + bgRoot.updateZoomScale(); // Clock position gets updated after zoom scale is updated } // Wallpaper zoom scale function updateZoomScale() { - getWallpaperSizeProc.path = bgRoot.wallpaperPath + getWallpaperSizeProc.path = bgRoot.wallpaperPath; getWallpaperSizeProc.running = true; } Process { id: getWallpaperSizeProc property string path: bgRoot.wallpaperPath - command: [ "magick", "identify", "-format", "%w %h", path ] + command: ["magick", "identify", "-format", "%w %h", path] stdout: StdioCollector { id: wallpaperSizeOutputCollector onStreamFinished: { - const output = wallpaperSizeOutputCollector.text + const output = wallpaperSizeOutputCollector.text; const [width, height] = output.split(" ").map(Number); const [screenWidth, screenHeight] = [bgRoot.screen.width, bgRoot.screen.height]; - bgRoot.wallpaperWidth = width - bgRoot.wallpaperHeight = height + bgRoot.wallpaperWidth = width; + bgRoot.wallpaperHeight = height; - if (width <= screenWidth || height <= screenHeight) { // Undersized/perfectly sized wallpapers + if (width <= screenWidth || height <= screenHeight) { + // Undersized/perfectly sized wallpapers bgRoot.effectiveWallpaperScale = Math.max(screenWidth / width, screenHeight / height); - } else { // Oversized = can be zoomed for parallax, yay - bgRoot.effectiveWallpaperScale = Math.min( - bgRoot.preferredWallpaperScale, - width / screenWidth, height / screenHeight - ); + } else { + // Oversized = can be zoomed for parallax, yay + bgRoot.effectiveWallpaperScale = Math.min(bgRoot.preferredWallpaperScale, width / screenWidth, height / screenHeight); } - - bgRoot.updateClockPosition() + bgRoot.updateClockPosition(); } } } @@ -186,11 +131,11 @@ Variants { // Clock positioning function updateClockPosition() { // Somehow all this manual setting is needed to make the proc correctly use the new values - leastBusyRegionProc.path = bgRoot.wallpaperPath - leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2 - leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2 - leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2 - leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2 + leastBusyRegionProc.path = bgRoot.wallpaperPath; + leastBusyRegionProc.contentWidth = clockLoader.implicitWidth + root.clockSizePadding * 2; + leastBusyRegionProc.contentHeight = clockLoader.implicitHeight + root.clockSizePadding * 2; + leastBusyRegionProc.horizontalPadding = bgRoot.movableXSpace + root.screenSizePadding * 2; + leastBusyRegionProc.verticalPadding = bgRoot.movableYSpace + root.screenSizePadding * 2; leastBusyRegionProc.running = false; leastBusyRegionProc.running = true; } @@ -201,220 +146,310 @@ Variants { property int contentHeight: 300 property int horizontalPadding: bgRoot.movableXSpace property int verticalPadding: bgRoot.movableYSpace - command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), - "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), - "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), - "--width", contentWidth, - "--height", contentHeight, - "--horizontal-padding", horizontalPadding, - "--vertical-padding", verticalPadding, - path, + command: [Quickshell.shellPath("scripts/images/least_busy_region.py"), "--screen-width", Math.round(bgRoot.screen.width / bgRoot.effectiveWallpaperScale), "--screen-height", Math.round(bgRoot.screen.height / bgRoot.effectiveWallpaperScale), "--width", contentWidth, "--height", contentHeight, "--horizontal-padding", horizontalPadding, "--vertical-padding", verticalPadding, path // "--visual-output", - ] + ,] stdout: StdioCollector { id: leastBusyRegionOutputCollector onStreamFinished: { - const output = leastBusyRegionOutputCollector.text + const output = leastBusyRegionOutputCollector.text; // console.log("[Background] Least busy region output:", output) - if (output.length === 0) return; - const parsedContent = JSON.parse(output) - bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale - bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale - bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary + if (output.length === 0) + return; + const parsedContent = JSON.parse(output); + bgRoot.clockX = parsedContent.center_x * bgRoot.effectiveWallpaperScale; + bgRoot.clockY = parsedContent.center_y * bgRoot.effectiveWallpaperScale; + bgRoot.dominantColor = parsedContent.dominant_color || Appearance.colors.colPrimary; } } } // Wallpaper - Image { - id: wallpaper - visible: opacity > 0 && !blurLoader.active - opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) - } - cache: false - asynchronous: true - retainWhileLoading: true - smooth: false - // Range = groups that workspaces span on - property int chunkSize: Config?.options.bar.workspaces.shown ?? 10; - property int lower: Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize; - property int upper: Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize; - property int range: upper - lower; - property real valueX: { - let result = 0.5; - if (Config.options.background.parallax.enableWorkspace && !bgRoot.verticalParallax) { - result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); - } - if (Config.options.background.parallax.enableSidebar) { - result += (0.15 * GlobalStates.sidebarRightOpen - 0.15 * GlobalStates.sidebarLeftOpen); - } - return result; - } - property real valueY: { - let result = 0.5; - if (Config.options.background.parallax.enableWorkspace && bgRoot.verticalParallax) { - result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); - } - return result; - } - property real effectiveValueX: Math.max(0, Math.min(1, valueX)) - property real effectiveValueY: Math.max(0, Math.min(1, valueY)) - x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace - y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace - source: bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath - fillMode: Image.PreserveAspectCrop - Behavior on x { - NumberAnimation { - duration: 600 - easing.type: Easing.OutCubic - } - } - Behavior on y { - NumberAnimation { - duration: 600 - easing.type: Easing.OutCubic - } - } - sourceSize { - width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale - height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale - } - width: bgRoot.wallpaperWidth / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale - height: bgRoot.wallpaperHeight / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale - } + Item { + anchors.fill: parent + clip: true - Loader { - id: blurLoader - active: Config.options.background.lockBlur.enable && (GlobalStates.screenLocked || scaleAnim.running) - anchors.fill: wallpaper - scale: GlobalStates.screenLocked ? Config.options.background.lockBlur.extraZoom : 1 - Behavior on scale { - NumberAnimation { - id: scaleAnim - duration: 400 - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + Image { + id: wallpaper + visible: opacity > 0 && !blurLoader.active + opacity: (status === Image.Ready && !bgRoot.wallpaperIsVideo) ? 1 : 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + cache: false + asynchronous: true + retainWhileLoading: true + smooth: false + // Range = groups that workspaces span on + property int chunkSize: Config?.options.bar.workspaces.shown ?? 10 + property int lower: Math.floor(bgRoot.firstWorkspaceId / chunkSize) * chunkSize + property int upper: Math.ceil(bgRoot.lastWorkspaceId / chunkSize) * chunkSize + property int range: upper - lower + property real valueX: { + let result = 0.5; + if (Config.options.background.parallax.enableWorkspace && !bgRoot.verticalParallax) { + result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); + } + if (Config.options.background.parallax.enableSidebar) { + result += (0.15 * GlobalStates.sidebarRightOpen - 0.15 * GlobalStates.sidebarLeftOpen); + } + return result; + } + property real valueY: { + let result = 0.5; + if (Config.options.background.parallax.enableWorkspace && bgRoot.verticalParallax) { + result = ((bgRoot.monitor.activeWorkspace?.id - lower) / range); + } + return result; + } + property real effectiveValueX: Math.max(0, Math.min(1, valueX)) + property real effectiveValueY: Math.max(0, Math.min(1, valueY)) + x: -(bgRoot.movableXSpace) - (effectiveValueX - 0.5) * 2 * bgRoot.movableXSpace + y: -(bgRoot.movableYSpace) - (effectiveValueY - 0.5) * 2 * bgRoot.movableYSpace + source: bgRoot.wallpaperSafetyTriggered ? "" : bgRoot.wallpaperPath + fillMode: Image.PreserveAspectCrop + Behavior on x { + NumberAnimation { + duration: 600 + easing.type: Easing.OutCubic + } + } + Behavior on y { + NumberAnimation { + duration: 600 + easing.type: Easing.OutCubic + } + } + sourceSize { + width: bgRoot.screen.width * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale + height: bgRoot.screen.height * bgRoot.effectiveWallpaperScale * bgRoot.monitor.scale + } + width: bgRoot.wallpaperWidth / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale + height: bgRoot.wallpaperHeight / bgRoot.wallpaperToScreenRatio * bgRoot.effectiveWallpaperScale } - sourceComponent: GaussianBlur { - source: wallpaper - radius: GlobalStates.screenLocked ? Config.options.background.lockBlur.radius : 0 - samples: radius * 2 + 1 - Rectangle { - opacity: GlobalStates.screenLocked ? 1 : 0 - anchors.fill: parent - color: CF.ColorUtils.transparentize(Appearance.colors.colLayer0, 0.7) + Loader { + id: blurLoader + active: Config.options.background.lockBlur.enable && (GlobalStates.screenLocked || scaleAnim.running) + anchors.fill: wallpaper + scale: GlobalStates.screenLocked ? Config.options.background.lockBlur.extraZoom : 1 + Behavior on scale { + NumberAnimation { + id: scaleAnim + duration: 400 + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animationCurves.expressiveDefaultSpatial + } } - } - } + sourceComponent: GaussianBlur { + source: wallpaper + radius: GlobalStates.screenLocked ? Config.options.background.lockBlur.radius : 0 + samples: radius * 2 + 1 - // The clock - Loader { - id: clockLoader - active: Config.options.background.showClock - anchors { - left: wallpaper.left - top: wallpaper.top - horizontalCenter: undefined - verticalCenter: undefined - leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - topMargin: { - if (bgRoot.shouldBlur) - return bgRoot.modelData.height / 3 - return bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) - } - Behavior on leftMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - Behavior on topMargin { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - } - states: State { - name: "centered" - when: (bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock) || bgRoot.wallpaperSafetyTriggered - AnchorChanges { - target: clockLoader - anchors { - left: undefined - right: undefined - top: undefined - verticalCenter: parent.verticalCenter - horizontalCenter: parent.horizontalCenter + Rectangle { + opacity: GlobalStates.screenLocked ? 1 : 0 + anchors.fill: parent + color: CF.ColorUtils.transparentize(Appearance.colors.colLayer0, 0.7) } } } - transitions: Transition { - AnchorAnimation { - duration: Appearance.animation.elementMove.duration - easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + + // The clock + Loader { + id: clockLoader + active: Config.options.background.clock.show + anchors { + left: wallpaper.left + top: wallpaper.top + horizontalCenter: undefined + verticalCenter: undefined + leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) + topMargin: { + if (bgRoot.shouldBlur) + return bgRoot.modelData.height / 3; + return bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2); + } + Behavior on leftMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + Behavior on topMargin { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } } - } - sourceComponent: Item { - id: clock - implicitWidth: clockColumn.implicitWidth - implicitHeight: clockColumn.implicitHeight - - ColumnLayout { - id: clockColumn - anchors.centerIn: parent - spacing: 6 - - ClockText { - font.pixelSize: 90 - text: DateTime.time - } - ClockText { - Layout.topMargin: -5 - text: DateTime.date - } - StyledText { // Somehow gets fucked up if made a ClockText??? - visible: Config.options.background.quote.length > 0 - Layout.fillWidth: true - horizontalAlignment: bgRoot.textHorizontalAlignment - font { - family: Appearance.font.family.main - pixelSize: Appearance.font.pixelSize.normal - weight: 350 - italic: true + states: State { + name: "centered" + when: (bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock) || bgRoot.wallpaperSafetyTriggered + AnchorChanges { + target: clockLoader + anchors { + left: undefined + right: undefined + top: undefined + verticalCenter: parent.verticalCenter + horizontalCenter: parent.horizontalCenter } - color: bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: Config.options.background.quote } } + transitions: Transition { + AnchorAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + } + } + sourceComponent: ColumnLayout { + id: clock + spacing: 8 - RowLayout { - anchors { - top: clockColumn.bottom - left: bgRoot.textHorizontalAlignment === Text.AlignLeft ? clockColumn.left : undefined - right: bgRoot.textHorizontalAlignment === Text.AlignRight ? clockColumn.right : undefined - horizontalCenter: bgRoot.textHorizontalAlignment === Text.AlignHCenter ? clockColumn.horizontalCenter : undefined - topMargin: 14 - leftMargin: -6 - rightMargin: -6 - } - spacing: 16 - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft; implicitWidth: 1 } - ClockStatusText { - shown: bgRoot.wallpaperSafetyTriggered - statusIcon: "hide_image" - statusText: qsTr("Wallpaper safety enforced") - } - ClockStatusText { - shown: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) - statusIcon: "lock" - statusText: qsTr("Locked") - } - Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight; implicitWidth: 1 } + Loader { + id: digitalClockLoader + visible: root.clockStyle === "digital" + active: visible + sourceComponent: ColumnLayout { + id: clockColumn + spacing: 6 + ClockText { + font.pixelSize: 90 + text: DateTime.time + } + ClockText { + Layout.topMargin: -5 + text: DateTime.date + } + StyledText { + // Somehow gets fucked up if made a ClockText??? + visible: Config.options.background.quote.length > 0 + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.normal + weight: 350 + italic: true + } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: Config.options.background.quote + } + } + } + + Loader { + id: cookieClockLoader + visible: root.clockStyle === "cookie" + active: visible + sourceComponent: CookieClock {} + } + + Item { + Layout.alignment: Qt.AlignHCenter + implicitWidth: statusTextBg.implicitWidth + implicitHeight: statusTextBg.implicitHeight + + StyledRectangularShadow { + target: statusTextBg + visible: statusTextBg.visible + opacity: statusTextBg.opacity + } + + Rectangle { + id: statusTextBg + opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 + visible: opacity > 0 + implicitHeight: statusTextRow.implicitHeight + 5 * 2 + implicitWidth: statusTextRow.implicitWidth + 10 * 2 + radius: Appearance.rounding.small + color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, cookieClockLoader.active ? 0 : 1) + + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + RowLayout { + id: statusTextRow + anchors.centerIn: parent + spacing: 16 + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft + implicitWidth: 1 + } + ClockStatusText { + id: safetyStatusText + shown: bgRoot.wallpaperSafetyTriggered + statusIcon: "hide_image" + statusText: qsTr("Wallpaper safety enforced") + } + ClockStatusText { + id: lockStatusText + shown: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) + statusIcon: "lock" + statusText: qsTr("Locked") + } + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight + implicitWidth: 1 + } + } + } + } } } } } + + // Components + component ClockText: StyledText { + Layout.fillWidth: true + horizontalAlignment: bgRoot.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: Font.DemiBold + } + color: bgRoot.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: true + } + component ClockStatusText: RowLayout { + id: statusTextRow + property alias statusIcon: statusIconWidget.text + property alias statusText: statusTextWidget.text + property bool shown: true + property color textColor: root.clockStyle === "cookie" ? Appearance.colors.colOnSecondaryContainer : bgRoot.colText + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Layout.fillWidth: false + MaterialSymbol { + id: statusIconWidget + Layout.fillWidth: false + iconSize: Appearance.font.pixelSize.huge + color: statusTextRow.textColor + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + ClockText { + id: statusTextWidget + Layout.fillWidth: false + color: statusTextRow.textColor + font { + family: Appearance.font.family.main + pixelSize: Appearance.font.pixelSize.large + weight: Font.Normal + } + style: Text.Raised + styleColor: Appearance.colors.colShadow + } + } } diff --git a/.config/quickshell/ii/modules/background/CookieClock.qml b/.config/quickshell/ii/modules/background/CookieClock.qml new file mode 100644 index 000000000..5a91ce649 --- /dev/null +++ b/.config/quickshell/ii/modules/background/CookieClock.qml @@ -0,0 +1,136 @@ +pragma ComponentBehavior: Bound + +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Layouts +import Quickshell +import Qt5Compat.GraphicalEffects + +Item { + id: root + + property real implicitSize: 230 + property real hourHandLength: 72 + property real hourHandWidth: 16 + property real minuteHandLength: 95 + property real minuteHandWidth: 8 + property real centerDotSize: 10 + property real hourDotSize: minuteHandWidth + property color colOnBackground: ColorUtils.mix(Appearance.colors.colPrimary, Appearance.colors.colSecondaryContainer, 0.5) + + property list clockNumbers: DateTime.time.split(/[: ]/) + property int clockHour: parseInt(clockNumbers[0]) % 12 + property int clockMinute: parseInt(clockNumbers[1]) + implicitWidth: implicitSize + implicitHeight: implicitSize + + DropShadow { + source: cookie + anchors.fill: source + horizontalOffset: 0 + verticalOffset: 2 + radius: 8 + samples: radius * 2 + 1 + color: Appearance.colors.colShadow + transparentBorder: true + } + + MaterialCookie { + id: cookie + z: 0 + implicitSize: root.implicitSize + amplitude: implicitSize / 70 + sides: 12 + color: Appearance.colors.colSecondaryContainer + + // 12 dots around the cookie + Repeater { + model: 12 + Item { + required property int index + rotation: 360 / 12 * index + anchors.fill: parent + Rectangle { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + leftMargin: 10 + } + implicitWidth: root.hourDotSize + implicitHeight: implicitWidth + radius: implicitWidth / 2 + color: root.colOnBackground + opacity: 0.5 + } + } + } + } + + Column { + id: timeIndicators + z: 1 + anchors.centerIn: cookie + spacing: -16 + + // Numbers + Repeater { + model: root.clockNumbers + delegate: StyledText { + required property string modelData + + anchors.horizontalCenter: parent.horizontalCenter + font { + pixelSize: modelData.match(/am|pm/i) ? 26 : 68 + family: Appearance.font.family.expressive + weight: Font.Bold + } + color: root.colOnBackground + text: modelData.padStart(2, "0") + } + } + } + + // Hour hand + Item { + anchors.fill: parent + z: 2 + rotation: -90 + (360 / 12) * (root.clockHour + root.clockMinute / 60) + Rectangle { + anchors.verticalCenter: parent.verticalCenter + x: parent.width / 2 - hourHandWidth / 2 + width: hourHandLength + height: hourHandWidth + radius: hourHandWidth / 2 + color: Appearance.colors.colPrimary + } + } + + // Minute hand + Item { + anchors.fill: parent + z: 3 + rotation: -90 + (360 / 60) * root.clockMinute + Rectangle { + anchors.verticalCenter: parent.verticalCenter + x: parent.width / 2 - minuteHandWidth / 2 + width: minuteHandLength + height: minuteHandWidth + radius: minuteHandWidth / 2 + color: Appearance.colors.colSecondary + } + } + + // Center dot + Rectangle { + z: 4 + color: Appearance.colors.colOnPrimary + anchors.centerIn: parent + implicitWidth: centerDotSize + implicitHeight: implicitWidth + radius: implicitWidth / 2 + } +} diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index c3233a722..05949c351 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -122,10 +122,13 @@ Singleton { } property JsonObject background: JsonObject { - property bool fixedClockPosition: false - property real clockX: -500 - property real clockY: -500 - property bool showClock: true + property JsonObject clock: JsonObject { + property bool fixedPosition: false + property real x: -500 + property real y: -500 + property bool show: true + property string style: "cookie" // Options: "cookie", "digital" + } property string wallpaperPath: "" property string thumbnailPath: "" property string quote: "" diff --git a/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml b/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml new file mode 100644 index 000000000..3f85cb294 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/MaterialCookie.qml @@ -0,0 +1,50 @@ +import QtQuick +import QtQuick.Shapes +import Quickshell + +Item { + id: root + + property int sides: 12 + property int implicitSize: 100 + property real amplitude: implicitSize / 50 + property int renderPoints: 360 + property color color: "#605790" + property alias strokeWidth: shapePath.strokeWidth + + implicitWidth: implicitSize + implicitHeight: implicitSize + + Shape { + id: shape + anchors.fill: parent + preferredRendererType: Shape.CurveRenderer + + ShapePath { + id: shapePath + strokeWidth: 0 + fillColor: root.color + pathHints: ShapePath.PathSolid & ShapePath.PathNonIntersecting + + PathPolyline { + property var pointsList: { + var points = [] + var cx = shape.width / 2 // center x + var cy = shape.height / 2 // center y + var steps = root.renderPoints + var radius = root.implicitSize / 2 - root.amplitude + for (var i = 0; i <= steps; i++) { + var angle = (i / steps) * 2 * Math.PI + var wave = Math.sin(angle * root.sides + Math.PI/2) * root.amplitude + var x = Math.cos(angle) * (radius + wave) + cx + var y = Math.sin(angle) * (radius + wave) + cy + points.push(Qt.point(x, y)) + } + return points + } + + path: pointsList + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml b/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml index a3c842c4b..a0c9f7ba3 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledRectangularShadow.qml @@ -5,7 +5,7 @@ import qs.modules.common RectangularShadow { required property var target anchors.fill: target - radius: target.radius + radius: 20 blur: 0.9 * Appearance.sizes.elevationMargin offset: Qt.vector2d(0.0, 1.0) spread: 1 diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 9ff65f4b6..253fff396 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -14,9 +14,31 @@ ContentPage { ConfigSwitch { text: Translation.tr("Show clock") - checked: Config.options.background.showClock + checked: Config.options.background.clock.show onCheckedChanged: { - Config.options.background.showClock = checked; + Config.options.background.clock.show = checked; + } + } + + ContentSubsection { + title: Translation.tr("Clock style") + ConfigSelectionArray { + currentValue: Config.options.background.clock.style + onSelected: newValue => { + Config.options.background.clock.style = newValue; + } + options: [ + { + displayName: Translation.tr("Simple digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Material cookie"), + icon: "cookie", + value: "cookie" + }, + ] } } From a67a8d746fb63777bf451d40729ede93c17831fd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:04:31 +0200 Subject: [PATCH 42/60] background: add scale config option for clock --- .../quickshell/ii/modules/background/Background.qml | 1 + .config/quickshell/ii/modules/common/Config.qml | 1 + .../ii/modules/settings/InterfaceConfig.qml | 11 +++++++++++ 3 files changed, 13 insertions(+) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index fd1ee53dd..fec906a3d 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -257,6 +257,7 @@ Variants { // The clock Loader { id: clockLoader + scale: Config.options.background.clock.scale active: Config.options.background.clock.show anchors { left: wallpaper.left diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 05949c351..05c1eb71c 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -128,6 +128,7 @@ Singleton { property real y: -500 property bool show: true property string style: "cookie" // Options: "cookie", "digital" + property real scale: 1 } property string wallpaperPath: "" property string thumbnailPath: "" diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 253fff396..9d66d0c65 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -20,6 +20,17 @@ ContentPage { } } + ConfigSpinBox { + text: Translation.tr("Scale (%)") + value: Config.options.background.clock.scale * 100 + from: 1 + to: 200 + stepSize: 2 + onValueChanged: { + Config.options.background.clock.scale = value / 100; + } + } + ContentSubsection { title: Translation.tr("Clock style") ConfigSelectionArray { From 036d8b485208f8fe0f61676f863f51b246d9032d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:50:25 +0200 Subject: [PATCH 43/60] lock: allow centering clock when not blurred --- .../ii/modules/background/Background.qml | 24 +++++---- .../ii/modules/background/CookieClock.qml | 2 +- .../quickshell/ii/modules/common/Config.qml | 17 ++++--- .../modules/common/widgets/ConfigSpinBox.qml | 1 - .../ii/modules/settings/InterfaceConfig.qml | 50 ++++++++++--------- 5 files changed, 53 insertions(+), 41 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index fec906a3d..9e91265f9 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -54,7 +54,7 @@ Variants { property real clockX: (modelData.width / 2) property real clockY: (modelData.height / 2) property var textHorizontalAlignment: { - if ((Config.options.background.lockBlur.enable && Config.options.background.lockBlur.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered) + if ((Config.options.lock.centerClock && GlobalStates.screenLocked) || wallpaperSafetyTriggered) return Text.AlignHCenter; if (clockX < screen.width / 3) return Text.AlignLeft; @@ -63,7 +63,7 @@ Variants { return Text.AlignHCenter; } // Colors - property bool shouldBlur: (GlobalStates.screenLocked && Config.options.background.lockBlur.enable) + property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable) property color dominantColor: Appearance.colors.colPrimary property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 property color colText: { @@ -230,9 +230,9 @@ Variants { Loader { id: blurLoader - active: Config.options.background.lockBlur.enable && (GlobalStates.screenLocked || scaleAnim.running) + active: Config.options.lock.blur.enable && (GlobalStates.screenLocked || scaleAnim.running) anchors.fill: wallpaper - scale: GlobalStates.screenLocked ? Config.options.background.lockBlur.extraZoom : 1 + scale: GlobalStates.screenLocked ? Config.options.lock.blur.extraZoom : 1 Behavior on scale { NumberAnimation { id: scaleAnim @@ -243,7 +243,7 @@ Variants { } sourceComponent: GaussianBlur { source: wallpaper - radius: GlobalStates.screenLocked ? Config.options.background.lockBlur.radius : 0 + radius: GlobalStates.screenLocked ? Config.options.lock.blur.radius : 0 samples: radius * 2 + 1 Rectangle { @@ -279,7 +279,7 @@ Variants { } states: State { name: "centered" - when: (bgRoot.shouldBlur && Config.options.background.lockBlur.centerClock) || bgRoot.wallpaperSafetyTriggered + when: (GlobalStates.screenLocked && Config.options.lock.centerClock) || bgRoot.wallpaperSafetyTriggered AnchorChanges { target: clockLoader anchors { @@ -345,13 +345,19 @@ Variants { } Item { - Layout.alignment: Qt.AlignHCenter + Layout.alignment: { + if (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") + return Qt.AlignHCenter; + return (bgRoot.textHorizontalAlignment === Text.AlignLeft ? Qt.AlignLeft : Qt.AlignRight) + } + Layout.leftMargin: -26 + Layout.rightMargin: -26 implicitWidth: statusTextBg.implicitWidth implicitHeight: statusTextBg.implicitHeight StyledRectangularShadow { target: statusTextBg - visible: statusTextBg.visible + visible: statusTextBg.visible && cookieClockLoader.active opacity: statusTextBg.opacity } @@ -390,7 +396,7 @@ Variants { } ClockStatusText { id: lockStatusText - shown: GlobalStates.screenLocked && (!Config.options.background.lockBlur.enable || Config.options.background.lockBlur.showLockedText) + shown: GlobalStates.screenLocked && Config.options.lock.showLockedText statusIcon: "lock" statusText: qsTr("Locked") } diff --git a/.config/quickshell/ii/modules/background/CookieClock.qml b/.config/quickshell/ii/modules/background/CookieClock.qml index 5a91ce649..b6c2f9655 100644 --- a/.config/quickshell/ii/modules/background/CookieClock.qml +++ b/.config/quickshell/ii/modules/background/CookieClock.qml @@ -33,7 +33,7 @@ Item { anchors.fill: source horizontalOffset: 0 verticalOffset: 2 - radius: 8 + radius: 12 samples: radius * 2 + 1 color: Appearance.colors.colShadow transparentBorder: true diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 05c1eb71c..625b4f4de 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -141,13 +141,6 @@ Singleton { property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property bool enableSidebar: true } - property JsonObject lockBlur: JsonObject { - property bool enable: false - property int radius: 100 - property bool centerClock: true - property bool showLockedText: true - property real extraZoom: 1.1 - } property JsonObject wallpaperSafety: JsonObject { property bool enable: true property JsonObject triggerCondition: JsonObject { @@ -273,6 +266,16 @@ Singleton { } } + property JsonObject lock: JsonObject { + property JsonObject blur: JsonObject { + property bool enable: false + property real radius: 100 + property real extraZoom: 1.1 + } + property bool centerClock: true + property bool showLockedText: true + } + property JsonObject media: JsonObject { // Attempt to remove dupes (the aggregator playerctl one and browsers' native ones when there's plasma browser integration) property bool filterDuplicatePlayers: true diff --git a/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml b/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml index 2c9d7eabb..33fb84057 100644 --- a/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml +++ b/.config/quickshell/ii/modules/common/widgets/ConfigSpinBox.qml @@ -24,7 +24,6 @@ RowLayout { id: labelWidget Layout.fillWidth: true text: root.text - font.pixelSize: Appearance.font.pixelSize.small color: Appearance.colors.colOnSecondaryContainer } } diff --git a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index 9d66d0c65..e79a40779 100644 --- a/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -171,37 +171,41 @@ ContentPage { title: Translation.tr("Blurred style") ConfigSwitch { - font.pixelSize: Appearance.font.pixelSize.large text: Translation.tr('Enable blur') - checked: Config.options.background.lockBlur.enable + checked: Config.options.lock.blur.enable onCheckedChanged: { - Config.options.background.lockBlur.enable = checked; + Config.options.lock.blur.enable = checked; } } - ConfigRow { - uniform: true - - ConfigSwitch { - enabled: Config.options.background.lockBlur.enable - font.pixelSize: Appearance.font.pixelSize.large - text: Translation.tr('Center clock') - checked: Config.options.background.lockBlur.centerClock - onCheckedChanged: { - Config.options.background.lockBlur.centerClock = checked; - } - } - ConfigSwitch { - enabled: Config.options.background.lockBlur.enable - font.pixelSize: Appearance.font.pixelSize.large - text: Translation.tr('Show "Locked" text') - checked: Config.options.background.lockBlur.showLockedText - onCheckedChanged: { - Config.options.background.lockBlur.showLockedText = checked; - } + ConfigSpinBox { + text: Translation.tr("Blur: Extra zoom (%)") + value: Config.options.lock.blur.extraZoom * 100 + from: 1 + to: 150 + stepSize: 2 + onValueChanged: { + Config.options.lock.blur.extraZoom = value / 100; } } + ConfigSwitch { + text: Translation.tr('Center clock') + checked: Config.options.lock.centerClock + onCheckedChanged: { + Config.options.lock.centerClock = checked; + } + } + + ConfigSwitch { + text: Translation.tr('Show "Locked" text') + checked: Config.options.lock.showLockedText + onCheckedChanged: { + Config.options.lock.showLockedText = checked; + } + } + + } } From f3120f1e0d40209122b5b7768eafb4218228ce95 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Sep 2025 21:54:15 +0200 Subject: [PATCH 44/60] background: fix cookie clock positioning when locked + wall safety --- .config/quickshell/ii/modules/background/Background.qml | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 9e91265f9..0f9900c2a 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -339,6 +339,7 @@ Variants { Loader { id: cookieClockLoader + Layout.alignment: Qt.AlignHCenter visible: root.clockStyle === "cookie" active: visible sourceComponent: CookieClock {} @@ -363,10 +364,11 @@ Variants { Rectangle { id: statusTextBg + clip: true opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 visible: opacity > 0 implicitHeight: statusTextRow.implicitHeight + 5 * 2 - implicitWidth: statusTextRow.implicitWidth + 10 * 2 + implicitWidth: statusTextRow.implicitWidth + 5 * 2 radius: Appearance.rounding.small color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, cookieClockLoader.active ? 0 : 1) @@ -383,7 +385,7 @@ Variants { RowLayout { id: statusTextRow anchors.centerIn: parent - spacing: 16 + spacing: 14 Item { Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft implicitWidth: 1 From e3bc2e5d84902c34d723dbc478bac1247f98b2ef Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:12:22 +0200 Subject: [PATCH 45/60] more wallpaper safety filters --- .config/quickshell/ii/modules/common/Config.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 625b4f4de..559148909 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -145,7 +145,7 @@ Singleton { property bool enable: true property JsonObject triggerCondition: JsonObject { property list wallpaperKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] - property list networkNameKeywords: ["guest", "public", "free", "airport"] + property list networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"] } } } From 769dafb4282050925a57971f2a5f8b1c17a02d0a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:13:00 +0200 Subject: [PATCH 46/60] move adapted material scheme to new file --- .../common/models/AdaptedMaterialScheme.qml | 27 +++++++++++++++++++ .../modules/mediaControls/PlayerControl.qml | 19 +++---------- 2 files changed, 30 insertions(+), 16 deletions(-) create mode 100644 .config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml diff --git a/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml b/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml new file mode 100644 index 000000000..6cc374c93 --- /dev/null +++ b/.config/quickshell/ii/modules/common/models/AdaptedMaterialScheme.qml @@ -0,0 +1,27 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +/** + * Material color scheme adapted to a given color. It's incomplete but enough for what we need... + */ +QtObject { + id: root + required property color color + readonly property bool colorIsDark: color.hslLightness < 0.5 + + property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, root.color, (colorIsDark && Appearance.m3colors.darkmode) ? 0.6 : 0.5) + property color colLayer1: ColorUtils.mix(Appearance.colors.colLayer1, root.color, 0.5) + property color colOnLayer0: ColorUtils.mix(Appearance.colors.colOnLayer0, root.color, 0.5) + property color colOnLayer1: ColorUtils.mix(Appearance.colors.colOnLayer1, root.color, 0.5) + property color colSubtext: ColorUtils.mix(Appearance.colors.colOnLayer1, root.color, 0.5) + property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimary, root.color), root.color, 0.5) + property color colPrimaryHover: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryHover, root.color), root.color, 0.3) + property color colPrimaryActive: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimaryActive, root.color), root.color, 0.3) + property color colSecondary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colSecondary, root.color), root.color, 0.5) + property color colSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3secondaryContainer, root.color, 0.15) + property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, root.color, 0.3) + property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, root.color, 0.5) + property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, root.color), root.color, 0.5) + property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, root.color, 0.5) +} diff --git a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml index 01ca56068..5be82f09e 100644 --- a/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml +++ b/.config/quickshell/ii/modules/mediaControls/PlayerControl.qml @@ -1,4 +1,5 @@ import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.services import qs.modules.common.functions @@ -82,22 +83,8 @@ Item { // Player instance rescaleSize: 1 // Rescale to 1x1 pixel for faster processing } - property bool backgroundIsDark: artDominantColor.hslLightness < 0.5 - property QtObject blendedColors: QtObject { - property color colLayer0: ColorUtils.mix(Appearance.colors.colLayer0, artDominantColor, (backgroundIsDark && Appearance.m3colors.darkmode) ? 0.6 : 0.5) - 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.5) - property color colPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.colors.colPrimary, 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.15) - property color colSecondaryContainerHover: ColorUtils.mix(Appearance.colors.colSecondaryContainerHover, artDominantColor, 0.3) - property color colSecondaryContainerActive: ColorUtils.mix(Appearance.colors.colSecondaryContainerActive, artDominantColor, 0.5) - property color colOnPrimary: ColorUtils.mix(ColorUtils.adaptToAccent(Appearance.m3colors.m3onPrimary, artDominantColor), artDominantColor, 0.5) - property color colOnSecondaryContainer: ColorUtils.mix(Appearance.m3colors.m3onSecondaryContainer, artDominantColor, 0.5) - + property QtObject blendedColors: AdaptedMaterialScheme { + color: artDominantColor } StyledRectangularShadow { From 1f700f33b2c6ff52f22e76d0758fc33c933ebf12 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:13:18 +0200 Subject: [PATCH 47/60] cookie clock: put color props to root --- .../ii/modules/background/Background.qml | 3 ++- .../ii/modules/background/CookieClock.qml | 24 ++++++++++++------- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 0f9900c2a..46fc3c62d 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -3,6 +3,7 @@ pragma ComponentBehavior: Bound import qs import qs.services import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.modules.common.functions as CF import QtQuick @@ -64,7 +65,7 @@ Variants { } // Colors property bool shouldBlur: (GlobalStates.screenLocked && Config.options.lock.blur.enable) - property color dominantColor: Appearance.colors.colPrimary + property color dominantColor: Appearance.colors.colPrimary // Default, to be changed property bool dominantColorIsDark: dominantColor.hslLightness < 0.5 property color colText: { if (wallpaperSafetyTriggered) diff --git a/.config/quickshell/ii/modules/background/CookieClock.qml b/.config/quickshell/ii/modules/background/CookieClock.qml index b6c2f9655..88ad5a90f 100644 --- a/.config/quickshell/ii/modules/background/CookieClock.qml +++ b/.config/quickshell/ii/modules/background/CookieClock.qml @@ -20,11 +20,17 @@ Item { property real minuteHandWidth: 8 property real centerDotSize: 10 property real hourDotSize: minuteHandWidth - property color colOnBackground: ColorUtils.mix(Appearance.colors.colPrimary, Appearance.colors.colSecondaryContainer, 0.5) - property list clockNumbers: DateTime.time.split(/[: ]/) - property int clockHour: parseInt(clockNumbers[0]) % 12 - property int clockMinute: parseInt(clockNumbers[1]) + property color colShadow: Appearance.colors.colShadow + property color colBackground: Appearance.colors.colSecondaryContainer + property color colOnBackground: ColorUtils.mix(Appearance.colors.colPrimary, Appearance.colors.colSecondaryContainer, 0.5) + property color colHourHand: Appearance.colors.colPrimary + property color colMinuteHand: Appearance.colors.colSecondary + property color colOnHourHand: Appearance.colors.colOnPrimary + + readonly property list clockNumbers: DateTime.time.split(/[: ]/) + readonly property int clockHour: parseInt(clockNumbers[0]) % 12 + readonly property int clockMinute: parseInt(clockNumbers[1]) implicitWidth: implicitSize implicitHeight: implicitSize @@ -35,7 +41,7 @@ Item { verticalOffset: 2 radius: 12 samples: radius * 2 + 1 - color: Appearance.colors.colShadow + color: root.colShadow transparentBorder: true } @@ -45,7 +51,7 @@ Item { implicitSize: root.implicitSize amplitude: implicitSize / 70 sides: 12 - color: Appearance.colors.colSecondaryContainer + color: root.colBackground // 12 dots around the cookie Repeater { @@ -105,7 +111,7 @@ Item { width: hourHandLength height: hourHandWidth radius: hourHandWidth / 2 - color: Appearance.colors.colPrimary + color: root.colHourHand } } @@ -120,14 +126,14 @@ Item { width: minuteHandLength height: minuteHandWidth radius: minuteHandWidth / 2 - color: Appearance.colors.colSecondary + color: root.colMinuteHand } } // Center dot Rectangle { z: 4 - color: Appearance.colors.colOnPrimary + color: root.colOnHourHand anchors.centerIn: parent implicitWidth: centerDotSize implicitHeight: implicitWidth From 2b47083c12c20f2460bcad2764841c46bb86be8b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:14:00 +0200 Subject: [PATCH 48/60] settings, welcome: change konachan btn icon, fix wallpaper desync --- .config/quickshell/ii/modules/settings/QuickConfig.qml | 3 ++- .config/quickshell/ii/welcome.qml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/modules/settings/QuickConfig.qml b/.config/quickshell/ii/modules/settings/QuickConfig.qml index 5b2de5112..ca8a0f73d 100644 --- a/.config/quickshell/ii/modules/settings/QuickConfig.qml +++ b/.config/quickshell/ii/modules/settings/QuickConfig.qml @@ -76,6 +76,7 @@ ContentPage { sourceSize.height: parent.implicitHeight fillMode: Image.PreserveAspectCrop source: Config.options.background.wallpaperPath + cache: false layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { @@ -93,7 +94,7 @@ ContentPage { visible: Config.options.policies.weeb === 1 Layout.fillWidth: true buttonRadius: Appearance.rounding.small - materialIcon: "wallpaper" + materialIcon: "ifl" mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { konachanWallProc.running = true; diff --git a/.config/quickshell/ii/welcome.qml b/.config/quickshell/ii/welcome.qml index a0699ac7c..1f7e0dc22 100644 --- a/.config/quickshell/ii/welcome.qml +++ b/.config/quickshell/ii/welcome.qml @@ -243,7 +243,7 @@ ApplicationWindow { visible: Config.options.policies.weeb === 1 Layout.alignment: Qt.AlignHCenter buttonRadius: Appearance.rounding.small - materialIcon: "wallpaper" + materialIcon: "ifl" mainText: konachanWallProc.running ? Translation.tr("Be patient...") : Translation.tr("Random: Konachan") onClicked: { console.log(konachanWallProc.command.join(" ")); From fc9bda9f7f5fdae255cf8caaad7b6ddf2517d654 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 11:14:34 +0200 Subject: [PATCH 49/60] add scroll edge fade to some scrolled windows --- .../ii/modules/background/CookieClock.qml | 2 +- .../common/widgets/NotificationItem.qml | 152 ++++++++++-------- .../modules/common/widgets/ScrollEdgeFade.qml | 59 +++++++ .../common/widgets/StyledFlickable.qml | 1 + .../ii/modules/sidebarLeft/AiChat.qml | 26 +-- .../ii/modules/sidebarLeft/Anime.qml | 25 +-- .../ii/modules/sidebarLeft/Translator.qml | 2 +- .../sidebarLeft/anime/BooruResponse.qml | 4 - 8 files changed, 178 insertions(+), 93 deletions(-) create mode 100644 .config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml diff --git a/.config/quickshell/ii/modules/background/CookieClock.qml b/.config/quickshell/ii/modules/background/CookieClock.qml index 88ad5a90f..0e1474543 100644 --- a/.config/quickshell/ii/modules/background/CookieClock.qml +++ b/.config/quickshell/ii/modules/background/CookieClock.qml @@ -88,7 +88,7 @@ Item { delegate: StyledText { required property string modelData - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent?.horizontalCenter font { pixelSize: modelData.match(/am|pm/i) ? 26 : 68 family: Appearance.font.family.expressive diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml index 94a7c71bf..4f87b4fdd 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml @@ -4,6 +4,7 @@ import qs.services import qs.modules.common.functions import QtQuick import QtQuick.Layouts +import Qt5Compat.GraphicalEffects import Quickshell import Quickshell.Hyprland import Quickshell.Services.Notifications @@ -220,91 +221,110 @@ Item { // Notification item area PointingHandLinkHover {} } - StyledFlickable { // Notification actions - id: actionsFlickable + Item { Layout.fillWidth: true - implicitHeight: actionRowLayout.implicitHeight - contentWidth: actionRowLayout.implicitWidth - clip: !onlyNotification + implicitWidth: actionsFlickable.implicitWidth + implicitHeight: actionsFlickable.implicitHeight - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on height { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - Behavior on implicitHeight { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: actionsFlickable.width + height: actionsFlickable.height + radius: Appearance.rounding.small + } } - RowLayout { - id: actionRowLayout - Layout.alignment: Qt.AlignBottom + ScrollEdgeFade { + target: actionsFlickable + vertical: false + } - NotificationActionButton { - Layout.fillWidth: true - buttonText: Translation.tr("Close") - urgency: notificationObject.urgency - implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : - (contentItem.implicitWidth + leftPadding + rightPadding) + StyledFlickable { // Notification actions + id: actionsFlickable + anchors.fill: parent + implicitHeight: actionRowLayout.implicitHeight + contentWidth: actionRowLayout.implicitWidth - onClicked: { - root.destroyWithAnimation() - } - - contentItem: MaterialSymbol { - iconSize: Appearance.font.pixelSize.large - horizontalAlignment: Text.AlignHCenter - color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface - text: "close" - } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on height { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + Behavior on implicitHeight { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } - Repeater { - id: actionRepeater - model: notificationObject.actions + RowLayout { + id: actionRowLayout + Layout.alignment: Qt.AlignBottom + NotificationActionButton { Layout.fillWidth: true - buttonText: modelData.text + buttonText: Translation.tr("Close") urgency: notificationObject.urgency + implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : + (contentItem.implicitWidth + leftPadding + rightPadding) + onClicked: { - Notifications.attemptInvokeAction(notificationObject.notificationId, modelData.identifier); + root.destroyWithAnimation() } - } - } - NotificationActionButton { - Layout.fillWidth: true - urgency: notificationObject.urgency - implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : - (contentItem.implicitWidth + leftPadding + rightPadding) - - onClicked: { - Quickshell.clipboardText = notificationObject.body - copyIcon.text = "inventory" - copyIconTimer.restart() - } - - Timer { - id: copyIconTimer - interval: 1500 - repeat: false - onTriggered: { - copyIcon.text = "content_copy" + contentItem: MaterialSymbol { + iconSize: Appearance.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + color: (notificationObject.urgency == NotificationUrgency.Critical) ? + Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface + text: "close" } } - contentItem: MaterialSymbol { - id: copyIcon - iconSize: Appearance.font.pixelSize.large - horizontalAlignment: Text.AlignHCenter - color: (notificationObject.urgency == NotificationUrgency.Critical) ? - Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface - text: "content_copy" + Repeater { + id: actionRepeater + model: notificationObject.actions + NotificationActionButton { + Layout.fillWidth: true + buttonText: modelData.text + urgency: notificationObject.urgency + onClicked: { + Notifications.attemptInvokeAction(notificationObject.notificationId, modelData.identifier); + } + } } + + NotificationActionButton { + Layout.fillWidth: true + urgency: notificationObject.urgency + implicitWidth: (notificationObject.actions.length == 0) ? ((actionsFlickable.width - actionRowLayout.spacing) / 2) : + (contentItem.implicitWidth + leftPadding + rightPadding) + + onClicked: { + Quickshell.clipboardText = notificationObject.body + copyIcon.text = "inventory" + copyIconTimer.restart() + } + + Timer { + id: copyIconTimer + interval: 1500 + repeat: false + onTriggered: { + copyIcon.text = "content_copy" + } + } + + contentItem: MaterialSymbol { + id: copyIcon + iconSize: Appearance.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + color: (notificationObject.urgency == NotificationUrgency.Critical) ? + Appearance.m3colors.m3onSurfaceVariant : Appearance.m3colors.m3onSurface + text: "content_copy" + } + } + } - } } } diff --git a/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml b/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml new file mode 100644 index 000000000..5c19f818a --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/ScrollEdgeFade.qml @@ -0,0 +1,59 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Item { + id: root + z: 99 + required property Item target + property real fadeSize: Appearance.m3colors.darkmode ? 40 : 20 + property color color: ColorUtils.transparentize(Appearance.colors.colShadow, Appearance.m3colors.darkmode ? 0 : 0.7) + property bool vertical: true + + anchors.fill: target + + EndGradient { + anchors { + top: parent.top + left: parent.left + right: vertical ? parent.right : undefined + bottom: vertical ? undefined : parent.bottom + } + shown: !(root.vertical ? root.target.atYBeginning : root.target.atXBeginning) + } + + EndGradient { + anchors { + bottom: parent.bottom + right: parent.right + left: vertical ? parent.left : undefined + top: vertical ? undefined : parent.top + } + shown: !(root.vertical ? root.target.atYEnd : root.target.atXEnd) + rotation: 180 + } + + component EndGradient: Rectangle { + required property bool shown + height: vertical ? root.fadeSize : parent.height + width: vertical ? parent.width : root.fadeSize + + opacity: shown ? 1 : 0 + visible: opacity > 0 + Behavior on opacity { + animation: Appearance?.animation.elementMoveFast.numberAnimation.createObject(this) + } + + gradient: Gradient { + orientation: root.vertical ? Gradient.Vertical : Gradient.Horizontal + GradientStop { + position: 0.0 + color: root.color + } + GradientStop { + position: 1.0 + color: ColorUtils.transparentize(root.color) + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml b/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml index 1f32326d9..10994ee00 100644 --- a/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml +++ b/.config/quickshell/ii/modules/common/widgets/StyledFlickable.qml @@ -50,4 +50,5 @@ Flickable { root.scrollTargetY = root.contentY; } } + } diff --git a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml index ef232fbaa..b746e9482 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/AiChat.qml @@ -308,6 +308,20 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) Item { // Messages Layout.fillWidth: true Layout.fillHeight: true + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } + } + + ScrollEdgeFade { + target: messageListView + vertical: true + } + StyledListView { // Message list id: messageListView anchors.fill: parent @@ -318,8 +332,8 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) mouseScrollFactor: Config.options.interactions.scrolling.mouseScrollFactor * 1.4 property int lastResponseLength: 0 - property bool shouldAutoScroll: true + onContentYChanged: shouldAutoScroll = atYEnd onContentHeightChanged: { if (shouldAutoScroll) positionViewAtEnd(); @@ -328,16 +342,6 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) if (shouldAutoScroll) positionViewAtEnd(); } - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small - } - } - add: null // Prevent function calls from being janky model: ScriptModel { diff --git a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml index 9e7bfb480..19b930958 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/Anime.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/Anime.qml @@ -141,6 +141,21 @@ Item { Item { Layout.fillWidth: true Layout.fillHeight: true + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: swipeView.width + height: swipeView.height + radius: Appearance.rounding.small + } + } + + ScrollEdgeFade { + target: booruResponseListView + vertical: true + } + StyledListView { // Booru responses id: booruResponseListView anchors.fill: parent @@ -151,16 +166,6 @@ Item { property int lastResponseLength: 0 - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: swipeView.width - height: swipeView.height - radius: Appearance.rounding.small - } - } - model: ScriptModel { values: { if(root.responses.length > booruResponseListView.lastResponseLength) { diff --git a/.config/quickshell/ii/modules/sidebarLeft/Translator.qml b/.config/quickshell/ii/modules/sidebarLeft/Translator.qml index e9e3d6fe9..b56681fb2 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/Translator.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/Translator.qml @@ -101,7 +101,7 @@ Item { ColumnLayout { anchors.fill: parent - Flickable { + StyledFlickable { Layout.fillWidth: true Layout.fillHeight: true contentHeight: contentColumn.implicitHeight diff --git a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml index 9e021e584..cfb2a7f9d 100644 --- a/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml +++ b/.config/quickshell/ii/modules/sidebarLeft/anime/BooruResponse.qml @@ -105,7 +105,6 @@ Rectangle { return true } implicitHeight: tagRowLayout.implicitHeight - // height: tagRowLayout.implicitHeight contentWidth: tagRowLayout.implicitWidth clip: true @@ -118,9 +117,6 @@ Rectangle { } } - Behavior on height { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } Behavior on implicitHeight { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } From 0498d7ea98c05e2424bfd78ceaa7b804d9cf294e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:36:09 +0200 Subject: [PATCH 50/60] overview: clearer numbers --- .../quickshell/ii/modules/overview/OverviewWidget.qml | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/.config/quickshell/ii/modules/overview/OverviewWidget.qml b/.config/quickshell/ii/modules/overview/OverviewWidget.qml index 13ec6370e..1c37386ee 100644 --- a/.config/quickshell/ii/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/ii/modules/overview/OverviewWidget.qml @@ -32,7 +32,7 @@ Item { ((monitor.height - monitorData?.reserved[1] - monitorData?.reserved[3]) * root.scale / monitor.scale) property real workspaceNumberMargin: 80 - property real workspaceNumberSize: Math.min(workspaceImplicitHeight, workspaceImplicitWidth) * monitor.scale + property real workspaceNumberSize: 250 * monitor.scale property int workspaceZ: 0 property int windowZ: 1 property int windowDraggingZ: 99999 @@ -97,8 +97,11 @@ Item { StyledText { anchors.centerIn: parent text: workspaceValue - font.pixelSize: root.workspaceNumberSize * root.scale - font.weight: Font.DemiBold + font { + pixelSize: root.workspaceNumberSize * root.scale + weight: Font.DemiBold + family: Appearance.font.family.expressive + } color: ColorUtils.transparentize(Appearance.colors.colOnLayer1, 0.8) horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter From 6707487d3ce98df3b42fa4c055753070a722e94d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 24 Sep 2025 08:29:04 +0200 Subject: [PATCH 51/60] background: fix clock movement jumping abruptly --- .../ii/modules/background/Background.qml | 120 +++++++++--------- 1 file changed, 59 insertions(+), 61 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 46fc3c62d..28ccdc5b2 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -266,11 +266,7 @@ Variants { horizontalCenter: undefined verticalCenter: undefined leftMargin: bgRoot.movableXSpace + ((root.fixedClockPosition ? root.fixedClockX : bgRoot.clockX * bgRoot.effectiveWallpaperScale) - implicitWidth / 2) - topMargin: { - if (bgRoot.shouldBlur) - return bgRoot.modelData.height / 3; - return bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2); - } + topMargin: bgRoot.movableYSpace + ((root.fixedClockPosition ? root.fixedClockY : bgRoot.clockY * bgRoot.effectiveWallpaperScale) - implicitHeight / 2) Behavior on leftMargin { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } @@ -300,7 +296,6 @@ Variants { } } sourceComponent: ColumnLayout { - id: clock spacing: 8 Loader { @@ -345,68 +340,71 @@ Variants { active: visible sourceComponent: CookieClock {} } + } - Item { - Layout.alignment: { - if (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") - return Qt.AlignHCenter; - return (bgRoot.textHorizontalAlignment === Text.AlignLeft ? Qt.AlignLeft : Qt.AlignRight) + Item { + anchors { + top: clockLoader.bottom + topMargin: 8 + horizontalCenter: (bgRoot.textHorizontalAlignment === Text.AlignHCenter || root.clockStyle === "cookie") ? clockLoader.horizontalCenter : undefined + left: (bgRoot.textHorizontalAlignment === Text.AlignLeft) ? clockLoader.left : undefined + right: (bgRoot.textHorizontalAlignment === Text.AlignRight) ? clockLoader.right : undefined + leftMargin: -26 + rightMargin: -26 + } + implicitWidth: statusTextBg.implicitWidth + implicitHeight: statusTextBg.implicitHeight + + StyledRectangularShadow { + target: statusTextBg + visible: statusTextBg.visible && root.clockStyle === "cookie" + opacity: statusTextBg.opacity + } + + Rectangle { + id: statusTextBg + anchors.centerIn: parent + clip: true + opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 + visible: opacity > 0 + implicitHeight: statusTextRow.implicitHeight + 5 * 2 + implicitWidth: statusTextRow.implicitWidth + 5 * 2 + radius: Appearance.rounding.small + color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, root.clockStyle === "cookie" ? 0 : 1) + + Behavior on implicitWidth { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) } - Layout.leftMargin: -26 - Layout.rightMargin: -26 - implicitWidth: statusTextBg.implicitWidth - implicitHeight: statusTextBg.implicitHeight - - StyledRectangularShadow { - target: statusTextBg - visible: statusTextBg.visible && cookieClockLoader.active - opacity: statusTextBg.opacity + Behavior on implicitHeight { + animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) } - Rectangle { - id: statusTextBg - clip: true - opacity: (safetyStatusText.shown || lockStatusText.shown) ? 1 : 0 - visible: opacity > 0 - implicitHeight: statusTextRow.implicitHeight + 5 * 2 - implicitWidth: statusTextRow.implicitWidth + 5 * 2 - radius: Appearance.rounding.small - color: CF.ColorUtils.transparentize(Appearance.colors.colSecondaryContainer, cookieClockLoader.active ? 0 : 1) - - Behavior on implicitWidth { - animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + RowLayout { + id: statusTextRow + anchors.centerIn: parent + spacing: 14 + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft + implicitWidth: 1 } - Behavior on implicitHeight { - animation: Appearance.animation.elementResize.numberAnimation.createObject(this) + ClockStatusText { + id: safetyStatusText + shown: bgRoot.wallpaperSafetyTriggered + statusIcon: "hide_image" + statusText: qsTr("Wallpaper safety enforced") } - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + ClockStatusText { + id: lockStatusText + shown: GlobalStates.screenLocked && Config.options.lock.showLockedText + statusIcon: "lock" + statusText: qsTr("Locked") } - - RowLayout { - id: statusTextRow - anchors.centerIn: parent - spacing: 14 - Item { - Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignLeft - implicitWidth: 1 - } - ClockStatusText { - id: safetyStatusText - shown: bgRoot.wallpaperSafetyTriggered - statusIcon: "hide_image" - statusText: qsTr("Wallpaper safety enforced") - } - ClockStatusText { - id: lockStatusText - shown: GlobalStates.screenLocked && Config.options.lock.showLockedText - statusIcon: "lock" - statusText: qsTr("Locked") - } - Item { - Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight - implicitWidth: 1 - } + Item { + Layout.fillWidth: bgRoot.textHorizontalAlignment !== Text.AlignRight + implicitWidth: 1 } } } From 7e491ce7d815a79851d9e3c029f1fd13aa66e1f8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:25:29 +0200 Subject: [PATCH 52/60] settings: adjust min window size --- .config/quickshell/ii/settings.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.config/quickshell/ii/settings.qml b/.config/quickshell/ii/settings.qml index d4724b534..3f3df0703 100644 --- a/.config/quickshell/ii/settings.qml +++ b/.config/quickshell/ii/settings.qml @@ -71,8 +71,8 @@ ApplicationWindow { MaterialThemeLoader.reapplyTheme() } - minimumWidth: 600 - minimumHeight: 400 + minimumWidth: 750 + minimumHeight: 500 width: 1100 height: 750 color: Appearance.m3colors.m3background From 263299b97d601018a43fa5ef44db9562ef672f30 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 24 Sep 2025 09:25:36 +0200 Subject: [PATCH 53/60] SearchItem: fix undefined warnings --- .../quickshell/ii/modules/overview/SearchItem.qml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/.config/quickshell/ii/modules/overview/SearchItem.qml b/.config/quickshell/ii/modules/overview/SearchItem.qml index f4fd20467..1abdb598d 100644 --- a/.config/quickshell/ii/modules/overview/SearchItem.qml +++ b/.config/quickshell/ii/modules/overview/SearchItem.qml @@ -16,11 +16,11 @@ RippleButton { property string query property bool entryShown: entry?.shown ?? true property string itemType: entry?.type ?? Translation.tr("App") - property string itemName: entry?.name + property string itemName: entry?.name ?? "" property string itemIcon: entry?.icon ?? "" property var itemExecute: entry?.execute property string fontType: entry?.fontType ?? "main" - property string itemClickActionName: entry?.clickActionName + property string itemClickActionName: entry?.clickActionName ?? "Open" property string bigText: entry?.bigText ?? "" property string materialSymbol: entry?.materialSymbol ?? "" property string cliphistRawString: entry?.cliphistRawString ?? "" @@ -233,8 +233,8 @@ RippleButton { delegate: RippleButton { id: actionButton required property var modelData - property string iconName: modelData.icon - property string materialIconName: modelData.materialIcon + property string iconName: modelData.icon ?? "" + property string materialIconName: modelData.materialIcon ?? "" implicitHeight: 34 implicitWidth: 34 @@ -246,7 +246,7 @@ RippleButton { anchors.centerIn: parent Loader { anchors.centerIn: parent - active: !(actionButton.iconName && actionButton.iconName !== "") || actionButton.materialIconName + active: !(actionButton.iconName !== "") || actionButton.materialIconName sourceComponent: MaterialSymbol { text: actionButton.materialIconName || "video_settings" font.pixelSize: Appearance.font.pixelSize.hugeass @@ -255,7 +255,7 @@ RippleButton { } Loader { anchors.centerIn: parent - active: !actionButton.materialIconName && actionButton.iconName && actionButton.iconName !== "" + active: actionButton.materialIconName.length == 0 && actionButton.iconName && actionButton.iconName !== "" sourceComponent: IconImage { source: Quickshell.iconPath(actionButton.iconName) implicitSize: 20 From 9eade626aa2a31e78559636cd5cf9ed545b895a8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 25 Sep 2025 09:49:22 +0200 Subject: [PATCH 54/60] overview: fix large vertical tooltip padding --- .config/quickshell/ii/modules/overview/OverviewWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/overview/OverviewWidget.qml b/.config/quickshell/ii/modules/overview/OverviewWidget.qml index 1c37386ee..d7942585a 100644 --- a/.config/quickshell/ii/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/ii/modules/overview/OverviewWidget.qml @@ -238,7 +238,7 @@ Item { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active - text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}\n` + text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}` } } } From 434c383fcdf2e794e67078cda97628c0003aca0b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 09:49:49 +0200 Subject: [PATCH 55/60] remove deprecated label from ags version, its totally unsupported now --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 56f3115eb..ce826254f 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ Widget system: Quickshell | Support: Yes | Window management | Weeb power | | image | image | -### illogical-impulseAGS (Deprecated) +### illogical-impulseAGS Widget system: AGS | Support: No From f324fc7c104cd99932e172caf435f4109725f805 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 10:21:19 +0200 Subject: [PATCH 56/60] readme: push unsupported stuff down --- README.md | 123 ++++++++++++++++++++++++++++-------------------------- 1 file changed, 64 insertions(+), 59 deletions(-) diff --git a/README.md b/README.md index ce826254f..982f10273 100644 --- a/README.md +++ b/README.md @@ -47,8 +47,8 @@ | [Hyprland](https://github.com/hyprwm/hyprland) | The compositor (manages and renders windows) | | [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, used for the status bar, sidebars, etc. | - - THERE IS NO WAYBAR - For the full list of dependencies, see the [arch-packages folder](https://github.com/end-4/dots-hyprland/tree/main/arch-packages) + - THERE IS NO WAYBAR STOP FUCKING CALLING EVERY BAR WAYBAR
@@ -62,20 +62,14 @@

- - - - - -
- illogical-impulse logo - - latest and only style that I actively use. Other past styles are still there for your viewing pleasure and not actual use, but code is still available, see below. -
- +
+ illogical-impulse logo +
### illogical-impulseQuickshell +This is the latest and only supported style. Other stuff are still there mostly for viewing pleasure and not actual use, but code is still available, see below. + Widget system: Quickshell | Support: Yes [Showcase video](https://www.youtube.com/watch?v=RPwovTInagE) @@ -86,52 +80,7 @@ Widget system: Quickshell | Support: Yes | Window management | Weeb power | | image | image | -### illogical-impulseAGS - -Widget system: AGS | Support: No - -| AI | Common widgets | -|:---|:---------------| -| ![image](https://github.com/user-attachments/assets/9d7af13f-89ef-470d-ba78-d2288b79cf60) | ![image](https://github.com/end-4/dots-hyprland/assets/97237370/406b72b6-fa38-4f0d-a6c4-4d7d5d5ddcb7) | -| Window management | Weeb power | -| ![image](https://github.com/user-attachments/assets/02983b9b-79ba-4c25-8717-90bef2357ae5) | ![image](https://github.com/user-attachments/assets/bbb332ec-962a-4e88-a95b-486d0bd8ce76) | - -### Very old stuff - -- Not likely to work, but the source is still available in the [`archive`](https://github.com/end-4/dots-hyprland/tree/archive) branch. Extremely spaghetti. -- Click image for a presentation video - -#### m3ww - - Widget system: EWW | Support: No, dead - - - Material Eww! - - -#### NovelKnock - - Widget system: EWW | Support: No, dead - - - Desktop Preview - - -#### Hybrid - - Widget system: EWW | Support: No, dead - - - click the circles! - - -#### Windoes - - Widget system: EWW | Support: No, dead - - - Desktop Preview - +### Other styles: Available at the end of the readme.

• thank you •

@@ -162,4 +111,60 @@ Widget system: AGS | Support: No
- Inspiration: osu!lazer, Windows 11, Material Design 3, AvdanOS (concept) - - Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (unless you do weird stuff and violate the license). (this note is here because some people actually asked) + - Copying: The license allows you to. Personally I have absolutely no problem with others redistributing/recreating my work. There's no "stealing" (maybe unless you loudly do weird stuff and violate the license) (some people actually had to ask smh) + +--- + +--- + +
+

• old, UNSUPPORTED stuff •

+

+
+ +- Source for illogical-impulse AGS available in the `ii-ags` branch, others in the `archive` branch. +- The list goes from newest to the oldest, and the code quality is worse the older a style is +- No bug fix or official support will be provided. + +### illogical-impulseAGS + +Widget system: AGS | Support: No + +| AI | Common widgets | +|:---|:---------------| +| ![image](https://github.com/user-attachments/assets/9d7af13f-89ef-470d-ba78-d2288b79cf60) | ![image](https://github.com/end-4/dots-hyprland/assets/97237370/406b72b6-fa38-4f0d-a6c4-4d7d5d5ddcb7) | +| Window management | Weeb power | +| ![image](https://github.com/user-attachments/assets/02983b9b-79ba-4c25-8717-90bef2357ae5) | ![image](https://github.com/user-attachments/assets/bbb332ec-962a-4e88-a95b-486d0bd8ce76) | + +#### m3ww + + Widget system: EWW | Support: No, dead + + + Material Eww! + + +#### NovelKnock + + Widget system: EWW | Support: No + + + Desktop Preview + + +#### Hybrid + + Widget system: EWW | Support: No + + + click the circles! + + +#### Windoes + + Widget system: EWW | Support: No + + + Desktop Preview + + From 6394fe049d2cdee7c8c329509070dca5db2ecc5e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 22:55:14 +0200 Subject: [PATCH 57/60] notifications: prevent summaries from spilling --- .../ii/modules/common/widgets/NotificationItem.qml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml index 4f87b4fdd..3aafdc400 100644 --- a/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml +++ b/.config/quickshell/ii/modules/common/widgets/NotificationItem.qml @@ -16,6 +16,7 @@ Item { // Notification item area property bool onlyNotification: false property real fontSize: Appearance.font.pixelSize.small property real padding: onlyNotification ? 0 : 8 + property real summaryElideRatio: 0.85 property real dragConfirmThreshold: 70 // Drag further to discard notification property real dismissOvershoot: notificationIcon.implicitWidth + 20 // Account for gaps and bouncy animations @@ -58,6 +59,12 @@ Item { // Notification item area destroyAnimation.running = true; } + TextMetrics { + id: summaryTextMetrics + font.pixelSize: root.fontSize + text: root.notificationObject.summary || "" + } + SequentialAnimation { // Drag finish animation id: destroyAnimation running: false @@ -164,9 +171,9 @@ Item { // Notification item area visible: !root.onlyNotification || !root.expanded Layout.fillWidth: true implicitHeight: summaryText.implicitHeight - // Layout.fillWidth: true StyledText { id: summaryText + Layout.fillWidth: summaryTextMetrics.width >= summaryRow.width * root.summaryElideRatio visible: !root.onlyNotification font.pixelSize: root.fontSize color: Appearance.colors.colOnLayer3 From 01815d04dcb9d861f8163bb1db5cfe2505acfcbf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:05:34 +0200 Subject: [PATCH 58/60] remove unused shaders --- .../hypr/shaders/chromatic_abberation.frag | 24 - .config/hypr/shaders/crt.frag | 511 ------------------ .config/hypr/shaders/drugs.frag | 42 -- .config/hypr/shaders/extradark.frag | 21 - .config/hypr/shaders/invert.frag | 13 - .config/hypr/shaders/solarized.frag | 41 -- 6 files changed, 652 deletions(-) delete mode 100644 .config/hypr/shaders/chromatic_abberation.frag delete mode 100644 .config/hypr/shaders/crt.frag delete mode 100644 .config/hypr/shaders/drugs.frag delete mode 100644 .config/hypr/shaders/extradark.frag delete mode 100644 .config/hypr/shaders/invert.frag delete mode 100644 .config/hypr/shaders/solarized.frag diff --git a/.config/hypr/shaders/chromatic_abberation.frag b/.config/hypr/shaders/chromatic_abberation.frag deleted file mode 100644 index 5389241cd..000000000 --- a/.config/hypr/shaders/chromatic_abberation.frag +++ /dev/null @@ -1,24 +0,0 @@ -// vim: set ft=glsl: - -precision highp float; -varying highp vec2 v_texcoord; -uniform highp sampler2D tex; - -#define STRENGTH 0.0027 - -void main() { - vec2 center = vec2(0.5, 0.5); - vec2 offset = (v_texcoord - center) * STRENGTH; - - float rSquared = dot(offset, offset); - float distortion = 1.0 + 1.0 * rSquared; - vec2 distortedOffset = offset * distortion; - - vec2 redOffset = vec2(distortedOffset.x, distortedOffset.y); - vec2 blueOffset = vec2(distortedOffset.x, distortedOffset.y); - - vec4 redColor = texture2D(tex, v_texcoord + redOffset); - vec4 blueColor = texture2D(tex, v_texcoord + blueOffset); - - gl_FragColor = vec4(redColor.r, texture2D(tex, v_texcoord).g, blueColor.b, 1.0); -} diff --git a/.config/hypr/shaders/crt.frag b/.config/hypr/shaders/crt.frag deleted file mode 100644 index a37ff9fb2..000000000 --- a/.config/hypr/shaders/crt.frag +++ /dev/null @@ -1,511 +0,0 @@ -#version 100 -precision highp float; -varying highp vec2 v_texcoord; -varying highp vec3 v_pos; -uniform highp sampler2D tex; -uniform lowp float time; - -#define BORDER_COLOR vec4(vec3(0.0, 0.0, 0.0), 1.0) // black border -#define BORDER_RADIUS 1.0 // larger vignette radius -#define BORDER_SIZE 0.01 // small border size -#define CHROMATIC_ABERRATION_STRENGTH 0.00 -#define DENOISE_INTENSITY 0.0001 // -#define DISTORTION_AMOUNT 0.00 // moderate distortion amount -#define HDR_BLOOM 0.75 // bloom intensity -#define HDR_BRIGHTNESS 0.011 // brightness -#define HDR_CONTRAST 0.011 // contrast -#define HDR_SATURATION 1.0// saturation -#define LENS_DISTORTION_AMOUNT 0.0 -#define NOISE_THRESHOLD 0.0001 -#define PHOSPHOR_BLUR_AMOUNT 0.77 // Amount of blur for phosphor glow -#define PHOSPHOR_GLOW_AMOUNT 0.77 // Amount of phosphor glow -#define SAMPLING_RADIUS 0.0001 -#define SCANLINE_FREQUENCY 540.0 -#define SCANLINE_THICKNESS 0.0507 -#define SCANLINE_TIME time * 471.24 -#define SHARPNESS 0.25 -#define SUPERSAMPLING_SAMPLES 16.0 -#define VIGNETTE_RADIUS 0.0 // larger vignette radius -#define PI 3.14159265359 -#define TWOPI 6.28318530718 - -vec2 applyBarrelDistortion(vec2 coord, float amt) { - vec2 p = coord.xy / vec2(1.0); - vec2 v = p * 2.0 - vec2(1.0); - float r = dot(v, v); - float k = 1.0 + pow(r, 2.0) * pow(amt, 2.0); - vec2 result = v * k; - return vec2(0.5, 0.5) + 0.5 * result.xy; -} - -vec4 applyColorCorrection(vec4 color) { - color.rgb *= vec3(1.0, 0.79, 0.89); - return vec4(color.rgb, 1.0); -} - -vec4 applyBorder(vec2 tc, vec4 color, float borderSize, vec4 borderColor) { - float dist_x = min(tc.x, 1.0 - tc.x); - float dist_y = min(tc.y, 1.0 - tc.y); - float dist = min(dist_x, dist_y) * -1.0; - float border = smoothstep(borderSize, 0.0, dist); - border += smoothstep(borderSize, 0.0, dist); - return mix(color, borderColor, border); -} - -vec4 applyFakeHDR(vec4 color, float brightness, float contrast, float saturation, float bloom) { - color.rgb = (color.rgb - vec3(0.5)) * exp2(brightness) + vec3(0.5); - vec3 crtfactor = vec3(1.05, 0.92, 1.0); - color.rgb = pow(color.rgb, crtfactor); - // // NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); - - // // BT.709 - // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); - - // BT.2020 - vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); - - // // Warm NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); - - float luminance = dot(color.rgb, lumCoeff); - luminance = pow(luminance, 2.2); - color.rgb = mix(vec3(luminance), color.rgb, saturation); - color.rgb = mix(color.rgb, vec3(1.0), pow(max(0.0, luminance - 1.0 + bloom), 4.0)); - return color; -} - -vec4 applyVignette(vec4 color) { - vec2 center = vec2(0.5, 0.5); // center of screen - float radius = VIGNETTE_RADIUS; // radius of vignette effect - float softness = 1.0; // softness of vignette effect - float intensity = 0.7; // intensity of vignette effect - vec2 offset = v_texcoord - center; // offset from center of screen - float distance = length(offset); // distance from center of screen - float alpha = smoothstep(radius, radius - radius * softness, distance) * intensity; // calculate alpha value for vignette effect - return mix(vec4(0.0, 0.0, 0.0, alpha), color, alpha); // mix black with color using calculated alpha value -} - -vec4 applyPhosphorGlow(vec2 tc, vec4 color, sampler2D tex) { - // Calculate average color value of the texture - vec4 texelColor = color; - float averageColor = (texelColor.r + texelColor.g + texelColor.b) / 3.0; - - // Determine brightness-dependent color factor - float factor = mix( - mix(0.09, - mix(0.005, 0.0075, (averageColor - 0.1) / 0.1), - step(0.01, averageColor)), 0.0005, - step(0.02, averageColor)); - // Apply phosphor glow effect - vec4 sum = vec4(0.0); - vec4 pixels[9]; - pixels[0] = texture2D(tex, tc - vec2(0.001, 0.001)); - pixels[1] = texture2D(tex, tc - vec2(0.001, 0.0)); - pixels[2] = texture2D(tex, tc - vec2(0.001, -0.001)); - pixels[3] = texture2D(tex, tc - vec2(0.0, 0.001)); - pixels[4] = texture2D(tex, tc); - pixels[5] = texture2D(tex, tc + vec2(0.001, 0.001)); - pixels[6] = texture2D(tex, tc + vec2(0.001, 0.0)); - pixels[7] = texture2D(tex, tc + vec2(0.001, -0.001)); - pixels[8] = texture2D(tex, tc + vec2(0.0, 0.001)); - -// Perform operations on input pixels in parallel - sum = pixels[0] - + pixels[1] - + pixels[2] - + pixels[3] - + pixels[4] - + pixels[5] - + pixels[6] - + pixels[7] - + pixels[8]; - sum /= 9.0; - sum += texture2D(tex, tc - vec2(0.01, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.0, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(-0.01, 0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.01, 0.0)) * 0.001; - sum += color * PHOSPHOR_BLUR_AMOUNT; - sum += texture2D(tex, tc - vec2(-0.01, 0.0)) * 0.001; - sum += texture2D(tex, tc - vec2(0.01, -0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(0.0, -0.01)) * 0.001; - sum += texture2D(tex, tc - vec2(-0.01, -0.01)) * 0.001; - sum *= PHOSPHOR_GLOW_AMOUNT; - - // Initialize sum_sum_factor to zero - vec4 sum_sum_factor = vec4(0.0); - // Compute sum_j for i = -1 - vec4 sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, -1) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - - // Compute sum_j for i = 0 - sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - - // Compute sum_j for i = 1 - sum_j = vec4(0.0); - sum_j += texture2D(tex, tc + vec2(-1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 0) * 0.01); - sum_j += texture2D(tex, tc + vec2(-1, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(0, 1) * 0.01); - sum_j += texture2D(tex, tc + vec2(1, 1) * 0.01); - sum_sum_factor += sum_j * vec4(0.011); - color += mix(sum_sum_factor * sum_sum_factor * vec4(factor), sum, 0.5); - return color; -} - -vec4 applyAdaptiveSharpen(vec2 tc, vec4 color, sampler2D tex) { - vec4 color_tl = texture2D(tex, tc + vec2(-1.0, -1.0) * 0.5 / 2160.0); - vec4 color_tr = texture2D(tex, tc + vec2(1.0, -1.0) * 0.5 / 2160.0); - vec4 color_bl = texture2D(tex, tc + vec2(-1.0, 1.0) * 0.5 / 2160.0); - vec4 color_br = texture2D(tex, tc + vec2(1.0, 1.0) * 0.5 / 2160.0); - float sharpness = SHARPNESS; - vec3 color_no_alpha = color.rgb; - vec3 color_tl_no_alpha = color_tl.rgb; - vec3 color_tr_no_alpha = color_tr.rgb; - vec3 color_bl_no_alpha = color_bl.rgb; - vec3 color_br_no_alpha = color_br.rgb; - float delta = (dot(color_no_alpha, vec3(0.333333)) + dot(color_tl_no_alpha, vec3(0.333333)) + dot(color_tr_no_alpha, vec3(0.333333)) + dot(color_bl_no_alpha, vec3(0.333333)) + dot(color_br_no_alpha, vec3(0.333333))) * 0.2 - dot(color_no_alpha, vec3(0.333333)); - vec3 sharp_color_no_alpha = color_no_alpha + min(vec3(0.0), vec3(delta * sharpness)); - vec4 sharp_color = vec4(sharp_color_no_alpha, color.a); - return sharp_color; -} - -vec4 applyScanlines(vec2 tc, vec4 color) { - float scanline = (cos(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME) * - sin(tc.y * SCANLINE_FREQUENCY + SCANLINE_TIME)) * SCANLINE_THICKNESS; - float alpha = clamp(1.0 - abs(scanline), 0.0, 1.0); - return vec4(color.rgb * alpha, color.a); -} - -vec4 applyChromaticAberration(vec2 uv, vec4 color) { - vec2 center = vec2(0.5, 0.5); // center of the screen - vec2 offset = (uv - center) * CHROMATIC_ABERRATION_STRENGTH; // calculate the offset from the center - - // apply lens distortion - float rSquared = dot(offset, offset); - float distortion = 1.0 + LENS_DISTORTION_AMOUNT * rSquared; - vec2 distortedOffset = offset * distortion; - - // apply chromatic aberration - vec2 redOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); - vec2 blueOffset = vec2(distortedOffset.x * 1.00, distortedOffset.y * 1.00); - - vec4 redColor = texture2D(tex, uv + redOffset); - vec4 blueColor = texture2D(tex, uv + blueOffset); - - vec4 result = vec4(redColor.r, color.g, blueColor.b, color.a); - - return result; -} - -vec4 reduceGlare(vec4 color) { - // Calculate the intensity of the color by taking the average of the RGB components - float intensity = (color.r + color.g + color.b) / 3.0; - // Set the maximum intensity that can be considered for glare - float maxIntensity = 0.98; - // Use smoothstep to create a smooth transition from no glare to full glare - // based on the intensity of the color and the maximum intensity - float glareIntensity = smoothstep(maxIntensity - 0.02, maxIntensity, intensity); - // Set the amount of glare to apply to the color - float glareAmount = 0.02; - // Mix the original color with the reduced color that has glare applied to it - vec3 reducedColor = mix(color.rgb, vec3(glareIntensity), glareAmount); - // Return the reduced color with the original alpha value - return vec4(reducedColor, color.a); -} - -// Apply a fake HDR effect to the input color. -// Parameters: -// - inputColor: the color to apply the effect to. -// - brightness: the brightness of the image. Should be a value between 0 and 1. -// - contrast: the contrast of the image. Should be a value between 0 and 1. -// - saturation: the saturation of the image. Should be a value between 0 and 2. -// - bloom: the intensity of the bloom effect. Should be a value between 0 and 1. -vec4 applyFakeHDREffect(vec4 inputColor, float brightness, float contrast, float saturation, float bloom) { - const float minBrightness = 0.0; - const float maxBrightness = 1.0; - const float minContrast = 0.0; - const float maxContrast = 1.0; - const float minSaturation = 0.0; - const float maxSaturation = 2.0; - const float minBloom = 0.0; - const float maxBloom = 1.0; - - // Check input parameters for validity - if (brightness < minBrightness || brightness > maxBrightness) { - return vec4(0.0, 0.0, 0.0, 1.0); // Return black with alpha of 1.0 to indicate error - } - if (contrast < minContrast || contrast > maxContrast) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - if (saturation < minSaturation || saturation > maxSaturation) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - if (bloom < minBloom || bloom > maxBloom) { - return vec4(0.0, 0.0, 0.0, 1.0); - } - - // Apply brightness and contrast - vec3 color = inputColor.rgb; - color = (color - vec3(0.5)) * exp2(brightness * 10.0) + vec3(0.5); - color = mix(vec3(0.5), color, pow(contrast * 4.0 + 1.0, 2.0)); - - // // NTSC - // vec3 lumCoeff = vec3(0.2125, 0.7154, 0.0721); - - // // BT.709 - // vec3 lumCoeff = vec3(0.299, 0.587, 0.114); - - // // BT.2020 - // vec3 lumCoeff = vec3(0.2627, 0.6780, 0.0593); - - // Warm NTSC - vec3 lumCoeff = vec3(0.2125, 0.7010, 0.0865); - - // Apply saturation - float luminance = dot(color, lumCoeff); - vec3 grey = vec3(luminance); - color = mix(grey, color, saturation); - - // Apply bloom effect - float threshold = 1.0 - bloom; - vec3 bloomColor = max(color - threshold, vec3(0.0)); - bloomColor = pow(bloomColor, vec3(2.0)); - bloomColor = mix(vec3(0.0), bloomColor, pow(min(luminance, threshold), 4.0)); - color += bloomColor; - - return vec4(color, inputColor.a); -} - -vec4 bilateralFilter(sampler2D tex, vec2 uv, vec4 color, float sampleRadius, float noiseThreshold, float intensity) { - vec4 filteredColor = vec4(0.0); - float totalWeight = 0.0; - - // Top-left pixel - vec4 sample = texture2D(tex, uv + vec2(-1.0, -1.0)); - float dist = length(vec2(-1.0, -1.0)); - float colorDist = length(sample - color); - float weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Top pixel - sample = texture2D(tex, uv + vec2(0.0, -1.0)); - dist = length(vec2(0.0, -1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Top-right pixel - sample = texture2D(tex, uv + vec2(1.0, -1.0)); - dist = length(vec2(1.0, -1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Left pixel - sample = texture2D(tex, uv + vec2(-1.0, 0.0)); - dist = length(vec2(-1.0, 0.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Center pixel - sample = texture2D(tex, uv); - dist = 0.0; - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Right pixel - sample = texture2D(tex, uv + vec2(1.0, 0.0)); - dist = length(vec2(1.0, 0.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - // Bottom-left pixel - sample = texture2D(tex, uv + vec2(-1.0, 1.0)); - dist = length(vec2(-1.0, 1.0)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - -// Bottom pixel - sample = texture2D(tex, uv + vec2(0.0, sampleRadius)); - dist = length(vec2(0.0, sampleRadius)); - colorDist = length(sample - color); - weight = exp(-0.5 * (dist * dist + colorDist * colorDist * intensity) / (sampleRadius * sampleRadius)); - filteredColor += sample * weight; - totalWeight += weight; - - filteredColor /= totalWeight; - return mix(color, filteredColor, step(noiseThreshold, length(filteredColor - color))); -} - -vec4 supersample(sampler2D tex, vec2 uv, float sampleRadius, float noiseThreshold, float intensity) { - float radiusSq = sampleRadius * sampleRadius; - vec2 poissonDisk; - vec4 color = vec4(0.0); - - float r1_0 = sqrt(0.0 / 16.0); - float r2_0 = fract(1.0 / 3.0); - float theta_0 = TWOPI * r2_0; - poissonDisk = vec2(r1_0 * cos(theta_0), r1_0 * sin(theta_0)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_1 = sqrt(1.0 / 16.0); - float r2_1 = fract(2.0 / 3.0); - float theta_1 = TWOPI * r2_1; - poissonDisk = vec2(r1_1 * cos(theta_1), r1_1 * sin(theta_1)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_2 = sqrt(2.0 / 16.0); - float r2_2 = fract(3.0 / 3.0); - float theta_2 = TWOPI * r2_2; - poissonDisk = vec2(r1_2 * cos(theta_2), r1_2 * sin(theta_2)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_3 = sqrt(3.0 / 16.0); - float r2_3 = fract(4.0 / 3.0); - float theta_3 = TWOPI * r2_3; - poissonDisk = vec2(r1_3 * cos(theta_3), r1_3 * sin(theta_3)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_4 = sqrt(4.0 / 16.0); - float r2_4 = fract(5.0 / 3.0); - float theta_4 = TWOPI * r2_4; - poissonDisk = vec2(r1_4 * cos(theta_4), r1_4 * sin(theta_4)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_5 = sqrt(5.0 / 16.0); - float r2_5 = fract(6.0 / 3.0); - float theta_5 = TWOPI * r2_5; - poissonDisk = vec2(r1_5 * cos(theta_5), r1_5 * sin(theta_5)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_6 = sqrt(6.0 / 16.0); - float r2_6 = fract(7.0 / 3.0); - float theta_6 = TWOPI * r2_6; - poissonDisk = vec2(r1_6 * cos(theta_6), r1_6 * sin(theta_6)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_7 = sqrt(7.0 / 16.0); - float r2_7 = fract(8.0 / 3.0); - float theta_7 = TWOPI * r2_7; - poissonDisk = vec2(r1_7 * cos(theta_7), r1_7 * sin(theta_7)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_8 = sqrt(8.0 / 16.0); - float r2_8 = fract(9.0 / 3.0); - float theta_8 = TWOPI * r2_8; - poissonDisk = vec2(r1_8 * cos(theta_8), r1_8 * sin(theta_8)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_9 = sqrt(9.0 / 16.0); - float r2_9 = fract(10.0 / 3.0); - float theta_9 = TWOPI * r2_9; - poissonDisk = vec2(r1_9 * cos(theta_9), r1_9 * sin(theta_9)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_10 = sqrt(10.0 / 16.0); - float r2_10 = fract(11.0 / 3.0); - float theta_10 = TWOPI * r2_10; - poissonDisk = vec2(r1_10 * cos(theta_10), r1_10 * sin(theta_10)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_11 = sqrt(11.0 / 16.0); - float r2_11 = fract(12.0 / 3.0); - float theta_11 = TWOPI * r2_11; - poissonDisk = vec2(r1_11 * cos(theta_11), r1_11 * sin(theta_11)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_12 = sqrt(12.0 / 16.0); - float r2_12 = fract(13.0 / 3.0); - float theta_12 = TWOPI * r2_12; - poissonDisk = vec2(r1_12 * cos(theta_12), r1_12 * sin(theta_12)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_13 = sqrt(13.0 / 16.0); - float r2_13 = fract(14.0 / 3.0); - float theta_13 = TWOPI * r2_13; - poissonDisk = vec2(r1_13 * cos(theta_13), r1_13 * sin(theta_13)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_14 = sqrt(14.0 / 16.0); - float r2_14 = fract(15.0 / 3.0); - float theta_14 = TWOPI * r2_14; - poissonDisk = vec2(r1_14 * cos(theta_14), r1_14 * sin(theta_14)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - float r1_15 = sqrt(15.0 / 16.0); - float r2_15 = fract(16.0 / 3.0); - float theta_15 = TWOPI * r2_15; - poissonDisk = vec2(r1_15 * cos(theta_15), r1_15 * sin(theta_15)); - color += texture2D(tex, uv + poissonDisk * sampleRadius); - - return bilateralFilter(tex, uv, color, sampleRadius, noiseThreshold, intensity); -} -void main() { - vec2 tc_no_dist = v_texcoord; - - vec2 tc = applyBarrelDistortion(tc_no_dist, DISTORTION_AMOUNT); - - // [-1, 1] - vec2 tc_no_dist_symmetric = tc_no_dist * 2.0 - 1.0; - - // [0,1] - vec2 tc_no_dist_normalized = (tc_no_dist_symmetric + 1.0) / 2.0; - - // vec4 color = texture2D(tex, tc); - vec4 color = supersample(tex, tc, SAMPLING_RADIUS, NOISE_THRESHOLD, DENOISE_INTENSITY); - - color = applyAdaptiveSharpen(tc, color, tex); - - color = applyPhosphorGlow(tc, color, tex); - - color = reduceGlare(color); - - color = mix(applyFakeHDREffect(color, HDR_BRIGHTNESS, HDR_CONTRAST, HDR_SATURATION, HDR_BLOOM), color, 0.5); - - color = applyColorCorrection(color); - - color /= SUPERSAMPLING_SAMPLES; - - color = mix(applyChromaticAberration(tc, color), color, 0.25); - - color = mix(color, applyVignette(color), 0.37); - - color = applyBorder(tc_no_dist_normalized, color, 1.0 - BORDER_SIZE * BORDER_RADIUS, BORDER_COLOR); - - color = mix(applyBorder(tc, color, BORDER_SIZE, BORDER_COLOR), color, 0.05); - - color = applyScanlines(tc, color); - - gl_FragColor = color; - gl_FragColor.a = 1.0; -} - diff --git a/.config/hypr/shaders/drugs.frag b/.config/hypr/shaders/drugs.frag deleted file mode 100644 index 1190ed03c..000000000 --- a/.config/hypr/shaders/drugs.frag +++ /dev/null @@ -1,42 +0,0 @@ - -precision highp float; -varying vec2 v_texcoord; -uniform sampler2D tex; -uniform float time; - -void warpco(inout vec2 tc) { - tc -= 0.5; - tc *= length(tc) * 2.0; - tc += 0.5; -} - -float rand1d(float seed) { - return sin(seed*1454.0); -} - -float rand2d(vec2 co) -{ - return fract(sin(dot(co.xy, vec2(12.9898,78.233))) * 43758.5453); -} - -vec3 rgb(in vec2 tc, float freq, float amp, inout vec4 centre) { - vec2 off = vec2(1.0/800.0, 0.0) * sin(tc.t * freq + time) * amp; - vec2 off2 = vec2(1.0/800.0, 0.0) * sin(tc.t * freq - time * 1.5) * amp; - centre = texture2D(tex, tc); - return vec3(texture2D(tex, tc-off).r, centre.g, texture2D(tex, tc+off2).b); -} - -void main() { - // vec2 px = 1.0 / textureSize(tex, 0).st; - vec2 tc = v_texcoord; - warpco(tc); - tc = mix(v_texcoord, tc, sin(time * 2.0)*0.07); - tc.x += rand2d(floor(tc * 20.0 + floor(time * 2.5))) * 0.01; - tc.x += rand1d(floor(tc.x * 40.0)) * 0.005 * rand1d(time * 0.001); - tc.y += sin(tc.x + time) * 0.02; - vec4 centre; - vec3 bent = rgb(tc, 100.0, 5.0, centre); - vec3 col = mix(centre.rgb, bent, sin(time)); - gl_FragColor = vec4(col, centre.a); - // gl_FragColor = vec4(texture2D(tex, v_texcoord)); -} \ No newline at end of file diff --git a/.config/hypr/shaders/extradark.frag b/.config/hypr/shaders/extradark.frag deleted file mode 100644 index 089ee8149..000000000 --- a/.config/hypr/shaders/extradark.frag +++ /dev/null @@ -1,21 +0,0 @@ -// vim: set ft=glsl: -// blue light filter shader -// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux - -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -void main() { - - vec4 pixColor = texture2D(tex, v_texcoord); - - // red - pixColor[0] *= 0.7; - // green - pixColor[1] *= 0.6; - // blue - pixColor[2] *= 0.5; - - gl_FragColor = pixColor; -} diff --git a/.config/hypr/shaders/invert.frag b/.config/hypr/shaders/invert.frag deleted file mode 100644 index 864f7188c..000000000 --- a/.config/hypr/shaders/invert.frag +++ /dev/null @@ -1,13 +0,0 @@ -// vim: set ft=glsl: -// blue light filter shader -// values from https://reshade.me/forum/shader-discussion/3673-blue-light-filter-similar-to-f-lux - -precision mediump float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -void main() { - vec4 pixColor = texture2D(tex, v_texcoord); - pixColor.rgb = 1.0 - pixColor.rgb; - gl_FragColor = pixColor; -} \ No newline at end of file diff --git a/.config/hypr/shaders/solarized.frag b/.config/hypr/shaders/solarized.frag deleted file mode 100644 index 1c37f2cc0..000000000 --- a/.config/hypr/shaders/solarized.frag +++ /dev/null @@ -1,41 +0,0 @@ -// -*- mode:c -*- -precision lowp float; -varying vec2 v_texcoord; -uniform sampler2D tex; - -float distanceSquared(vec3 pixColor, vec3 solarizedColor) { - vec3 distanceVector = pixColor - solarizedColor; - return dot(distanceVector, distanceVector); -} - -void main() { - vec3 solarized[16]; - solarized[0] = vec3(0.,0.169,0.212); - solarized[1] = vec3(0.027,0.212,0.259); - solarized[2] = vec3(0.345,0.431,0.459); - solarized[3] = vec3(0.396,0.482,0.514); - solarized[4] = vec3(0.514,0.58,0.588); - solarized[5] = vec3(0.576,0.631,0.631); - solarized[6] = vec3(0.933,0.91,0.835); - solarized[7] = vec3(0.992,0.965,0.89); - solarized[8] = vec3(0.71,0.537,0.); - solarized[9] = vec3(0.796,0.294,0.086); - solarized[10] = vec3(0.863,0.196,0.184); - solarized[11] = vec3(0.827,0.212,0.51); - solarized[12] = vec3(0.424,0.443,0.769); - solarized[13] = vec3(0.149,0.545,0.824); - solarized[14] = vec3(0.165,0.631,0.596); - solarized[15] = vec3(0.522,0.6,0.); - - vec3 pixColor = vec3(texture2D(tex, v_texcoord)); - int closest = 0; - float closestDistanceSquared = distanceSquared(pixColor, solarized[0]); - for (int i = 1; i < 15; i++) { - float newDistanceSquared = distanceSquared(pixColor, solarized[i]); - if (newDistanceSquared < closestDistanceSquared) { - closest = i; - closestDistanceSquared = newDistanceSquared; - } - } - gl_FragColor = vec4(solarized[closest], 1.); -} From 1e175e4e8268b81bdd44cb44cf1f978caf52a160 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:56:13 +0200 Subject: [PATCH 59/60] work safety for clipboard images copied from browser --- .../ii/modules/background/Background.qml | 7 +- .../quickshell/ii/modules/common/Config.qml | 19 ++-- .../modules/common/widgets/CliphistImage.qml | 93 +++++++++++++------ .../ii/modules/overview/SearchItem.qml | 4 + .../ii/modules/overview/SearchWidget.qml | 50 ++++++---- 5 files changed, 120 insertions(+), 53 deletions(-) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 28ccdc5b2..4ed1055d7 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -42,7 +42,12 @@ Variants { // Wallpaper property bool wallpaperIsVideo: Config.options.background.wallpaperPath.endsWith(".mp4") || Config.options.background.wallpaperPath.endsWith(".webm") || Config.options.background.wallpaperPath.endsWith(".mkv") || Config.options.background.wallpaperPath.endsWith(".avi") || Config.options.background.wallpaperPath.endsWith(".mov") property string wallpaperPath: wallpaperIsVideo ? Config.options.background.thumbnailPath : Config.options.background.wallpaperPath - property bool wallpaperSafetyTriggered: Config.options.background.wallpaperSafety.enable && (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.wallpaperKeywords) && CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.background.wallpaperSafety.triggerCondition.networkNameKeywords)) + property bool wallpaperSafetyTriggered: { + const enabled = Config.options.workSafety.enable.wallpaper + const sensitiveWallpaper = (CF.StringUtils.stringListContainsSubstring(wallpaperPath.toLowerCase(), Config.options.workSafety.triggerCondition.fileKeywords)) + const sensitiveNetwork = (CF.StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveWallpaper && sensitiveNetwork; + } property real wallpaperToScreenRatio: Math.min(wallpaperWidth / screen.width, wallpaperHeight / screen.height) property real preferredWallpaperScale: Config.options.background.parallax.workspaceZoom property real effectiveWallpaperScale: 1 // Some reasonable init value, to be updated diff --git a/.config/quickshell/ii/modules/common/Config.qml b/.config/quickshell/ii/modules/common/Config.qml index 559148909..e7b595d44 100644 --- a/.config/quickshell/ii/modules/common/Config.qml +++ b/.config/quickshell/ii/modules/common/Config.qml @@ -141,13 +141,6 @@ Singleton { property real workspaceZoom: 1.07 // Relative to your screen, not wallpaper size property bool enableSidebar: true } - property JsonObject wallpaperSafety: JsonObject { - property bool enable: true - property JsonObject triggerCondition: JsonObject { - property list wallpaperKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] - property list networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"] - } - } } property JsonObject bar: JsonObject { @@ -379,6 +372,18 @@ Singleton { property JsonObject screenshotTool: JsonObject { property bool showContentRegions: true } + + property JsonObject workSafety: JsonObject { + property JsonObject enable: JsonObject { + property bool wallpaper: true + property bool clipboard: true + } + property JsonObject triggerCondition: JsonObject { + property list networkNameKeywords: ["airport", "cafe", "college", "company", "eduroam", "free", "guest", "public", "school", "university"] + property list fileKeywords: ["anime", "ecchi", "hentai", "yande.re", "konachan", "breast", "nipples", "pussy", "nsfw", "spoiler", "girl"] + property list linkKeywords: ["hentai", "porn", "sukebei", "hitomi.la", "rule34", "gelbooru", "fanbox", "dlsite"] + } + } } } } diff --git a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml index e72954622..339cd4e6c 100644 --- a/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml +++ b/.config/quickshell/ii/modules/common/widgets/CliphistImage.qml @@ -12,6 +12,8 @@ Rectangle { property string entry property real maxWidth property real maxHeight + property bool blur: false + property string blurText: "Image hidden" property string imageDecodePath: Directories.cliphistDecode property string imageDecodeFileName: `${entryNumber}` @@ -19,26 +21,25 @@ Rectangle { property string source property int entryNumber: { - if (!root.entry) return 0 - const match = root.entry.match(/^(\d+)\t/) - return match ? parseInt(match[1]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/^(\d+)\t/); + return match ? parseInt(match[1]) : 0; } property int imageWidth: { - if (!root.entry) return 0 - const match = root.entry.match(/(\d+)x(\d+)/) - return match ? parseInt(match[1]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/(\d+)x(\d+)/); + return match ? parseInt(match[1]) : 0; } property int imageHeight: { - if (!root.entry) return 0 - const match = root.entry.match(/(\d+)x(\d+)/) - return match ? parseInt(match[2]) : 0 + if (!root.entry) + return 0; + const match = root.entry.match(/(\d+)x(\d+)/); + return match ? parseInt(match[2]) : 0; } property real scale: { - return Math.min( - root.maxWidth / imageWidth, - root.maxHeight / imageHeight, - 1 - ) + return Math.min(root.maxWidth / imageWidth, root.maxHeight / imageHeight, 1); } color: Appearance.colors.colLayer1 @@ -47,26 +48,33 @@ Rectangle { implicitWidth: imageWidth * scale Component.onCompleted: { - decodeImageProcess.running = true + decodeImageProcess.running = true; } Process { id: decodeImageProcess - command: ["bash", "-c", - `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'` - ] + command: ["bash", "-c", `[ -f ${imageDecodeFilePath} ] || echo '${StringUtils.shellSingleQuoteEscape(root.entry)}' | ${Cliphist.cliphistBinary} decode > '${imageDecodeFilePath}'`] onExited: (exitCode, exitStatus) => { if (exitCode === 0) { - root.source = imageDecodeFilePath + root.source = imageDecodeFilePath; } else { - console.error("[CliphistImage] Failed to decode image for entry:", root.entry) - root.source = "" + console.error("[CliphistImage] Failed to decode image for entry:", root.entry); + root.source = ""; } } } Component.onDestruction: { - Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`]) + Quickshell.execDetached(["bash", "-c", `[ -f '${imageDecodeFilePath}' ] && rm -f '${imageDecodeFilePath}'`]); + } + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: image.width + height: image.height + radius: root.radius + } } Image { @@ -82,15 +90,42 @@ Rectangle { height: root.imageHeight * root.scale sourceSize.width: width sourceSize.height: height + } - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: image.width - height: image.height - radius: root.radius + Loader { + id: blurLoader + active: root.blur + anchors.fill: image + sourceComponent: GaussianBlur { + source: image + radius: 35 + samples: radius * 2 + 1 + + Rectangle { + anchors.fill: parent + color: ColorUtils.transparentize(Appearance.colors.colLayer0, 0.5) + + Column { + anchors { + left: parent.left + right: parent.right + verticalCenter: parent.verticalCenter + } + MaterialSymbol { + visible: width <= image.width + anchors.horizontalCenter: parent.horizontalCenter + text: "visibility_off" + font.pixelSize: 28 + } + StyledText { + visible: width <= image.width + anchors.horizontalCenter: parent.horizontalCenter + text: root.blurText + color: Appearance.colors.colOnSurface + font.pixelSize: Appearance.font.pixelSize.smallie + } + } } } } } - diff --git a/.config/quickshell/ii/modules/overview/SearchItem.qml b/.config/quickshell/ii/modules/overview/SearchItem.qml index 1abdb598d..46b470de6 100644 --- a/.config/quickshell/ii/modules/overview/SearchItem.qml +++ b/.config/quickshell/ii/modules/overview/SearchItem.qml @@ -24,6 +24,8 @@ RippleButton { property string bigText: entry?.bigText ?? "" property string materialSymbol: entry?.materialSymbol ?? "" property string cliphistRawString: entry?.cliphistRawString ?? "" + property bool blurImage: entry?.blurImage ?? false + property string blurImageText: entry?.blurImageText ?? "Image hidden" visible: root.entryShown property int horizontalMargin: 10 @@ -208,6 +210,8 @@ RippleButton { entry: root.cliphistRawString maxWidth: contentColumn.width maxHeight: 140 + blur: root.blurImage + blurText: root.blurImageText } } } diff --git a/.config/quickshell/ii/modules/overview/SearchWidget.qml b/.config/quickshell/ii/modules/overview/SearchWidget.qml index 85b7d58cb..c857c0676 100644 --- a/.config/quickshell/ii/modules/overview/SearchWidget.qml +++ b/.config/quickshell/ii/modules/overview/SearchWidget.qml @@ -20,20 +20,10 @@ Item { // Wrapper implicitHeight: searchWidgetContent.implicitHeight + Appearance.sizes.elevationMargin * 2 property string mathResult: "" - - function disableExpandAnimation() { - searchWidthBehavior.enabled = false; - } - - function cancelSearch() { - searchInput.selectAll(); - root.searchingText = ""; - searchWidthBehavior.enabled = true; - } - - function setSearchingText(text) { - searchInput.text = text; - root.searchingText = text; + property bool clipboardWorkSafetyActive: { + const enabled = Config.options.workSafety.enable.clipboard; + const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveNetwork; } property var searchActions: [ @@ -97,6 +87,27 @@ Item { // Wrapper appResults.currentIndex = 0; } + function disableExpandAnimation() { + searchWidthBehavior.enabled = false; + } + + function cancelSearch() { + searchInput.selectAll(); + root.searchingText = ""; + searchWidthBehavior.enabled = true; + } + + function setSearchingText(text) { + searchInput.text = text; + root.searchingText = text; + } + + function containsUnsafeLink(entry) { + if (entry == undefined) return false; + const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; + return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); + } + Timer { id: nonAppResultsTimer interval: Config.options.search.nonAppResultDelay @@ -311,7 +322,12 @@ Item { // Wrapper if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) { // Clipboard const searchString = root.searchingText.slice(Config.options.search.prefix.clipboard.length); - return Cliphist.fuzzyQuery(searchString).map(entry => { + return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { + const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; + let shouldBlurImage = mightBlurImage; + if (mightBlurImage) { + shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1])); + } return { cliphistRawString: entry, name: StringUtils.cleanCliphistEntry(entry), @@ -335,7 +351,9 @@ Item { // Wrapper Cliphist.deleteEntry(entry); } } - ] + ], + blurImage: shouldBlurImage, + blurImageText: Translation.tr("Work safety") }; }).filter(Boolean); } From 83386bcdbf64c99c181b37bc13a4fa96443a9352 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:56:31 +0200 Subject: [PATCH 60/60] overviewwidget: fix undefined warning --- .config/quickshell/ii/modules/overview/OverviewWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.config/quickshell/ii/modules/overview/OverviewWidget.qml b/.config/quickshell/ii/modules/overview/OverviewWidget.qml index d7942585a..05bf29e82 100644 --- a/.config/quickshell/ii/modules/overview/OverviewWidget.qml +++ b/.config/quickshell/ii/modules/overview/OverviewWidget.qml @@ -149,7 +149,7 @@ Item { values: { // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2)) return ToplevelManager.toplevels.values.filter((toplevel) => { - const address = `0x${toplevel.HyprlandToplevel.address}` + const address = `0x${toplevel.HyprlandToplevel?.address}` var win = windowByAddress[address] const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) return inWorkspaceGroup;