From 3eec36d20d0b9de54be54aca0a85f693969f62a4 Mon Sep 17 00:00:00 2001 From: Bernat Felip Date: Thu, 18 Dec 2025 16:26:34 +0100 Subject: [PATCH 01/13] fix(overview): correct 4 finger vertical swipes --- dots/.config/hypr/hyprland/general.conf | 4 ++-- .../quickshell/ii/modules/ii/overview/Overview.qml | 8 ++++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index 3f5fa2cf8..4b12fc84f 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -4,8 +4,8 @@ monitor=,preferred,auto,1 gesture = 3, swipe, move, gesture = 3, pinch, float gesture = 4, horizontal, workspace -gesture = 4, up, dispatcher, global, quickshell:overviewToggle -gesture = 4, down, dispatcher, global, quickshell:overviewClose +gesture = 4, up, dispatcher, global, quickshell:overviewWorkspacesToggle +gesture = 4, down, dispatcher, global, quickshell:overviewWorkspacesClose gestures { workspace_swipe_distance = 700 workspace_swipe_cancel_ratio = 0.2 diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index c435f7f8a..bf8706eb0 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -192,6 +192,14 @@ Scope { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; } } + GlobalShortcut { + name: "overviewWorkspacesClose" + description: "Closes overview on press" + + onPressed: { + GlobalStates.overviewOpen = false; + } + } GlobalShortcut { name: "overviewWorkspacesToggle" description: "Toggles overview on press" From e499f4f8f100d64d8ac070c046e4b9ffc70aa8be Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Dec 2025 11:45:10 +0100 Subject: [PATCH 02/13] waffles: start: limit clipboard results to prevent lag, adjust btn --- .../searchPage/SearchResultButton.qml | 5 ++++- .../startMenu/searchPage/SearchResults.qml | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml index b7fb1df14..db45480f4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml @@ -44,7 +44,7 @@ WChoiceButton { Layout.fillHeight: true horizontalPadding: 10 verticalPadding: 11 - implicitHeight: root.firstEntry ? 62 : 36 + implicitHeight: Math.max(root.firstEntry ? 62 : 36, entryContentRow.implicitHeight + 8 * 2) implicitWidth: entryContentRow.implicitWidth + leftPadding + rightPadding topRightRadius: 0 bottomRightRadius: 0 @@ -54,6 +54,7 @@ WChoiceButton { id: entryContentRow anchors { left: parent.left + right: parent.right verticalCenter: parent.verticalCenter } spacing: 8 @@ -102,6 +103,7 @@ WChoiceButton { text: root.entry.name font.pixelSize: Looks.font.pixelSize.large maximumLineCount: 2 + elide: Text.ElideRight } WText { @@ -109,6 +111,7 @@ WChoiceButton { visible: root.firstEntry text: root.entry.type color: Looks.colors.accentUnfocused + elide: Text.ElideRight } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml index aeaa4748c..fc4adc684 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml @@ -15,6 +15,7 @@ RowLayout { id: root property int maxResultsPerCategory: 4 + property int resultLimit: 20 property StartMenuContext context property int currentIndex: context.currentIndex onCurrentIndexChanged: { @@ -99,21 +100,33 @@ RowLayout { // Collect max 4 per category var categorizedResults = []; - categories.forEach(category => { + let categoriesArray = Array.from(categories); + let totalCount = 0; + for (let c = 0; c < categoriesArray.length; c++) { + let category = categoriesArray[c]; let count = 0; for (let i = 0; i < allResults.length; i++) { if (allResults[i].type === category) { + if (totalCount >= root.resultLimit) { + break; + } const entry = allResults[i]; const tweakedEntry = searchResultComp.createObject(null, Object.assign({}, entry)); - tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type + tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type; + categorizedResults.push(tweakedEntry); // Section header count++; + totalCount++; if (count >= root.maxResultsPerCategory) { break; } } } - }); + if (totalCount >= root.resultLimit) { + break; + } + } + // print(JSON.stringify(categorizedResults, null, 2)); return categorizedResults; } From 169b24bea5f8ce9d75502037ad6a34676317a858 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Dec 2025 11:45:39 +0100 Subject: [PATCH 03/13] region selector: add region hover fade anim --- .../quickshell/ii/modules/ii/regionSelector/TargetRegion.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml index a1ecbcd0f..d3763e0ab 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/TargetRegion.qml @@ -25,6 +25,10 @@ Rectangle { border.width: targeted ? 4 : 2 radius: 4 + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + visible: opacity > 0 Behavior on opacity { animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) From 8842df6340915b2a7e3aad17c8f7875e19e9ab6f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 20 Dec 2025 11:46:07 +0100 Subject: [PATCH 04/13] waffles: screen snip --- .../icons/fluent/calendar-add-filled.svg | 4 + .../ii/assets/icons/fluent/calendar-add.svg | 4 + .../ii/assets/icons/fluent/camera-filled.svg | 1 + .../ii/assets/icons/fluent/camera.svg | 1 + .../ii/assets/icons/fluent/crop-filled.svg | 4 + .../ii/assets/icons/fluent/crop.svg | 4 + .../assets/icons/fluent/image-edit-filled.svg | 1 + .../ii/assets/icons/fluent/image-edit.svg | 1 + .../assets/icons/fluent/scan-text-filled.svg | 1 + .../ii/assets/icons/fluent/scan-text.svg | 1 + .../icons/fluent/search-visual-filled.svg | 4 + .../ii/assets/icons/fluent/search-visual.svg | 4 + .../ii/assets/icons/fluent/video-filled.svg | 1 + .../ii/assets/icons/fluent/video.svg | 1 + .../modules/common/utils/ScreenshotAction.qml | 81 ++++ .../common/utils/TempScreenshotProcess.qml | 14 + .../modules/common/widgets/DashedBorder.qml | 28 ++ .../ii/modules/common/widgets/DragManager.qml | 8 +- .../modules/common/widgets/ToolbarTabBar.qml | 38 +- .../ii/regionSelector/RegionSelection.qml | 104 ++--- .../ii/modules/waffle/looks/Looks.qml | 4 +- .../ii/modules/waffle/looks/WButton.qml | 3 +- .../ii/modules/waffle/looks/WMenu.qml | 4 +- .../ii/modules/waffle/looks/WMenuItem.qml | 79 ++-- .../ii/modules/waffle/looks/WToolbar.qml | 38 ++ .../modules/waffle/looks/WToolbarButton.qml | 8 + .../waffle/looks/WToolbarIconButton.qml | 16 + .../waffle/looks/WToolbarIconTabButton.qml | 21 + .../waffle/looks/WToolbarSeparator.qml | 11 + .../modules/waffle/looks/WToolbarTabBar.qml | 53 +++ .../screenSnip/WRectangularSelection.qml | 62 +++ .../screenSnip/WRegionSelectionPanel.qml | 375 ++++++++++++++++++ .../modules/waffle/screenSnip/WScreenSnip.qml | 106 +++++ dots/.config/quickshell/ii/shell.qml | 4 +- 34 files changed, 976 insertions(+), 113 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/camera.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/crop.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/video.svg create mode 100644 dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml create mode 100644 dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg new file mode 100644 index 000000000..9b12e58e6 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg new file mode 100644 index 000000000..fe0ff7263 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calendar-add.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg new file mode 100644 index 000000000..643964740 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/camera-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg b/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg new file mode 100644 index 000000000..40fa6d1f2 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/camera.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg new file mode 100644 index 000000000..f86a4e42c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/crop-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg b/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg new file mode 100644 index 000000000..1447e7c1f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/crop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg new file mode 100644 index 000000000..661466195 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg new file mode 100644 index 000000000..6f751b3ab --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-edit.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg new file mode 100644 index 000000000..3a8c786b8 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg new file mode 100644 index 000000000..beaf70538 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/scan-text.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg new file mode 100644 index 000000000..1ab1d25b0 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg new file mode 100644 index 000000000..167f228d0 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/search-visual.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg new file mode 100644 index 000000000..b3d6843ea --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/video-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/video.svg b/dots/.config/quickshell/ii/assets/icons/fluent/video.svg new file mode 100644 index 000000000..021cbd863 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/video.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml b/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml new file mode 100644 index 000000000..831834bd5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/utils/ScreenshotAction.qml @@ -0,0 +1,81 @@ +pragma ComponentBehavior: Bound +pragma Singleton +import qs.modules.common +import qs.modules.common.utils +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import Qt.labs.synchronizer +import Quickshell + +Singleton { + id: root + + enum Action { + Copy, + Edit, + Search, + CharRecognition, + Record, + RecordWithSound + } + + property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl + property string fileUploadApiEndpoint: "https://uguu.se/upload" + + function getCommand(x, y, width, height, screenshotPath, action, saveDir = "") { + // Set command for action + const rx = Math.round(x); + const ry = Math.round(y); + const rw = Math.round(width); + const rh = Math.round(height); + const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(screenshotPath)} ` + + `-crop ${rw}x${rh}+${rx}+${ry}` + const cropToStdout = `${cropBase} -` + const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'` + const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'` + const slurpRegion = `${rx},${ry} ${rw}x${rh}` + const uploadAndGetUrl = (filePath) => { + return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'` + } + const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`; + switch (action) { + case ScreenshotAction.Action.Copy: + if (saveDir === "") { + // not saving the screenshot, just copy to clipboard + return ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] + break; + } + return [ + "bash", "-c", + `mkdir -p '${StringUtils.shellSingleQuoteEscape(saveDir)}' && \ + saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \ + savePath="${saveDir}/$saveFileName" && \ + ${cropToStdout} | tee >(wl-copy) > "$savePath" && \ + ${cleanup}` + ] + + break; + case ScreenshotAction.Action.Edit: + return ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`] + break; + case ScreenshotAction.Action.Search: + return ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(screenshotPath)})" && ${cleanup}`] + break; + case ScreenshotAction.Action.CharRecognition: + return ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`] + break; + case ScreenshotAction.Action.Record: + return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`] + break; + case ScreenshotAction.Action.RecordWithSound: + return ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`] + break; + default: + console.warn("[Region Selector] Unknown snip action, skipping snip."); + return; + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml b/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml new file mode 100644 index 000000000..40c40fb34 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/utils/TempScreenshotProcess.qml @@ -0,0 +1,14 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs.modules.common +import qs.modules.common.functions + +Process { + id: screenshotProc + running: true + property string screenshotDir: Directories.screenshotTemp + required property ShellScreen screen + property string screenshotPath: `${screenshotDir}/image-${screen.name}` + command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(screen.name)}' '${StringUtils.shellSingleQuoteEscape(screenshotPath)}'`] +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml b/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml new file mode 100644 index 000000000..1d992ffab --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/DashedBorder.qml @@ -0,0 +1,28 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions + +Canvas { + id: root + property color color: "#ffffff" + property int dashLength: 6 + property int gapLength: 4 + property int borderWidth: 1 + + onDashLengthChanged: requestPaint() + onGapLengthChanged: requestPaint() + onWidthChanged: requestPaint() + onHeightChanged: requestPaint() + onPaint: { + var ctx = getContext("2d"); + ctx.clearRect(0, 0, width, height); + ctx.save(); + ctx.strokeStyle = root.color; + ctx.lineWidth = root.borderWidth; + if (root.gapLength > 0) { + ctx.setLineDash([root.dashLength, root.gapLength]); // Set dash pattern + } + ctx.strokeRect(root.borderWidth / 2, root.borderWidth / 2, width - root.borderWidth, height - root.borderWidth); // Draw it + ctx.restore(); + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml b/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml index 9a430d93b..4d6225400 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/DragManager.qml @@ -14,12 +14,16 @@ MouseArea { property bool automaticallyReset: true readonly property real dragDiffX: _dragDiffX readonly property real dragDiffY: _dragDiffY + property real startX: 0 + property real startY: 0 + property real regionTopLeftX: Math.min(startX, startX + _dragDiffX) + property real regionTopLeftY: Math.min(startY, startY + _dragDiffY) + property real regionWidth: Math.abs(_dragDiffX) + property real regionHeight: Math.abs(_dragDiffY) signal dragPressed(diffX: real, diffY: real) signal dragReleased(diffX: real, diffY: real) - property real startX: 0 - property real startY: 0 property bool dragging: false property real _dragDiffX: 0 property real _dragDiffY: 0 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml index 1bca18463..708b30bd7 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ToolbarTabBar.qml @@ -12,19 +12,30 @@ Item { required property var tabButtonList function incrementCurrentIndex() { - tabBar.incrementCurrentIndex() + tabBar.incrementCurrentIndex(); } function decrementCurrentIndex() { - tabBar.decrementCurrentIndex() + tabBar.decrementCurrentIndex(); } function setCurrentIndex(index) { - tabBar.setCurrentIndex(index) + tabBar.setCurrentIndex(index); } Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter implicitWidth: contentItem.implicitWidth implicitHeight: 40 + property Component delegate: ToolbarTabButton { + required property int index + required property var modelData + current: index == root.currentIndex + text: modelData.name + materialSymbol: modelData.icon + onClicked: { + root.setCurrentIndex(index); + } + } + Row { id: contentItem z: 1 @@ -33,16 +44,7 @@ Item { Repeater { model: root.tabButtonList - delegate: ToolbarTabButton { - required property int index - required property var modelData - current: index == root.currentIndex - text: modelData.name - materialSymbol: modelData.icon - onClicked: { - root.setCurrentIndex(index) - } - } + delegate: root.delegate } } @@ -76,23 +78,23 @@ Item { z: 2 acceptedButtons: Qt.NoButton cursorShape: Qt.PointingHandCursor - onWheel: (event) => { + onWheel: event => { if (event.angleDelta.y < 0) { root.incrementCurrentIndex(); - } - else { + } else { root.decrementCurrentIndex(); } } } - // TabBar doesn't allow tabs to be of different sizes. Literally unusable. + // TabBar doesn't allow tabs to be of different sizes. That's what I thought... // We use it only for the logic and draw stuff manually TabBar { id: tabBar z: -1 background: null - Repeater { // This is to fool the TabBar that it has tabs so it does the indices properly + Repeater { + // This is to fool the TabBar that it has tabs so it does the indices properly model: root.tabButtonList.length delegate: TabButton { background: null diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml index 59dc186e7..920e69b56 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml @@ -1,5 +1,6 @@ pragma ComponentBehavior: Bound import qs.modules.common +import qs.modules.common.utils import qs.modules.common.functions import qs.modules.common.widgets import qs.services @@ -32,14 +33,8 @@ PanelWindow { property var action: RegionSelection.SnipAction.Copy property var selectionMode: RegionSelection.SelectionMode.RectCorners signal dismiss() - - property string saveScreenshotDir: Config.options.screenSnip.savePath !== "" - ? Config.options.screenSnip.savePath - : "" property string screenshotDir: Directories.screenshotTemp - property string imageSearchEngineBaseUrl: Config.options.search.imageSearch.imageSearchEngineBaseUrl - property string fileUploadApiEndpoint: "https://uguu.se/upload" property color overlayColor: "#88111111" property color brightText: Appearance.m3colors.darkmode ? Appearance.colors.colOnLayer0 : Appearance.colors.colLayer0 property color brightSecondary: Appearance.m3colors.darkmode ? Appearance.colors.colSecondary : Appearance.colors.colOnSecondary @@ -180,10 +175,12 @@ PanelWindow { property real regionX: Math.min(dragStartX, draggingX) property real regionY: Math.min(dragStartY, draggingY) - Process { + TempScreenshotProcess { id: screenshotProc running: true - command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}' && grim -o '${StringUtils.shellSingleQuoteEscape(root.screen.name)}' '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'`] + screen: root.screen + screenshotDir: root.screenshotDir + screenshotPath: root.screenshotPath onExited: (exitCode, exitStatus) => { if (root.enableContentRegions) imageDetectionProcess.running = true; root.preparationDone = !checkRecordingProc.running; @@ -229,6 +226,27 @@ PanelWindow { } } + function getScreenshotAction() { + switch(root.action) { + case RegionSelection.SnipAction.Copy: + return ScreenshotAction.Action.Copy; + case RegionSelection.SnipAction.Edit: + return ScreenshotAction.Action.Edit; + case RegionSelection.SnipAction.Search: + return ScreenshotAction.Action.Search; + case RegionSelection.SnipAction.CharRecognition: + return ScreenshotAction.Action.CharRecognition; + case RegionSelection.SnipAction.Record: + return ScreenshotAction.Action.Record; + case RegionSelection.SnipAction.RecordWithSound: + return ScreenshotAction.Action.RecordWithSound; + default: + console.warn("[Region Selector] Unknown snip action, skipping snip."); + root.dismiss(); + return; + } + } + function snip() { // Validity check if (root.regionWidth <= 0 || root.regionHeight <= 0) { @@ -246,62 +264,20 @@ PanelWindow { if (root.action === RegionSelection.SnipAction.Copy || root.action === RegionSelection.SnipAction.Edit) { root.action = root.mouseButton === Qt.RightButton ? RegionSelection.SnipAction.Edit : RegionSelection.SnipAction.Copy; } - - // Set command for action - const rx = Math.round(root.regionX * root.monitorScale); - const ry = Math.round(root.regionY * root.monitorScale); - const rw = Math.round(root.regionWidth * root.monitorScale); - const rh = Math.round(root.regionHeight * root.monitorScale); - const cropBase = `magick ${StringUtils.shellSingleQuoteEscape(root.screenshotPath)} ` - + `-crop ${rw}x${rh}+${rx}+${ry}` - const cropToStdout = `${cropBase} -` - const cropInPlace = `${cropBase} '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'` - const cleanup = `rm '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}'` - const slurpRegion = `${rx},${ry} ${rw}x${rh}` - const uploadAndGetUrl = (filePath) => { - return `curl -sF files[]=@'${StringUtils.shellSingleQuoteEscape(filePath)}' ${root.fileUploadApiEndpoint} | jq -r '.files[0].url'` - } - const annotationCommand = `${Config.options.regionSelector.annotation.useSatty ? "satty" : "swappy"} -f -`; - switch (root.action) { - case RegionSelection.SnipAction.Copy: - if (saveScreenshotDir === "") { - // not saving the screenshot, just copy to clipboard - snipProc.command = ["bash", "-c", `${cropToStdout} | wl-copy && ${cleanup}`] - break; - } - - const savePathBase = root.saveScreenshotDir - - snipProc.command = [ - "bash", "-c", - `mkdir -p '${StringUtils.shellSingleQuoteEscape(savePathBase)}' && \ - saveFileName="screenshot-$(date '+%Y-%m-%d_%H.%M.%S').png" && \ - savePath="${savePathBase}/$saveFileName" && \ - ${cropToStdout} | tee >(wl-copy) > "$savePath" && \ - ${cleanup}` - ] - - break; - case RegionSelection.SnipAction.Edit: - snipProc.command = ["bash", "-c", `${cropToStdout} | ${annotationCommand} && ${cleanup}`] - break; - case RegionSelection.SnipAction.Search: - snipProc.command = ["bash", "-c", `${cropInPlace} && xdg-open "${root.imageSearchEngineBaseUrl}$(${uploadAndGetUrl(root.screenshotPath)})" && ${cleanup}`] - break; - case RegionSelection.SnipAction.CharRecognition: - snipProc.command = ["bash", "-c", `${cropInPlace} && tesseract '${StringUtils.shellSingleQuoteEscape(root.screenshotPath)}' stdout -l $(tesseract --list-langs | awk 'NR>1{print $1}' | tr '\\n' '+' | sed 's/\\+$/\\n/') | wl-copy && ${cleanup}`] - break; - case RegionSelection.SnipAction.Record: - snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}'`] - break; - case RegionSelection.SnipAction.RecordWithSound: - snipProc.command = ["bash", "-c", `${Directories.recordScriptPath} --region '${slurpRegion}' --sound`] - break; - default: - console.warn("[Region Selector] Unknown snip action, skipping snip."); - root.dismiss(); - return; - } + + const screenshotDir = Config.options.screenSnip.savePath !== "" ? // + Config.options.screenSnip.savePath : ""; + var screenshotAction = root.getScreenshotAction(); + const command = ScreenshotAction.getCommand( + root.regionX * root.monitorScale, // + root.regionY * root.monitorScale, // + root.regionWidth * root.monitorScale,// + root.regionHeight * root.monitorScale, // + root.screenshotPath, // + screenshotAction, // + screenshotDir + ) + snipProc.command = command; // Image post-processing snipProc.startDetached(); diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 8120aa85e..44075570f 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -93,8 +93,8 @@ Singleton { property color bgPanelFooter: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelLayerTransparency) property color bgPanelBody: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody, root.panelLayerTransparency) property color bgPanelSeparator: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, root.backgroundTransparency) - property color bg0Opaque: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 - property color bg0: ColorUtils.transparentize(bg0Opaque, root.backgroundTransparency) + property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 + property color bg0: ColorUtils.transparentize(bg0Base, root.backgroundTransparency) property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency) property color bg1Base: root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base property color bg1: ColorUtils.transparentize(root.dark ? root.darkColors.bg1 : root.lightColors.bg1, root.contentTransparency) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index ceed470ba..d2cef0634 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -39,9 +39,10 @@ Button { } } property color fgColor: { + if (!root.enabled) return root.colForegroundDisabled if (root.checked) return root.colForegroundToggled if (root.enabled) return root.colForeground - return root.colForegroundDisabled + return root.colForeground } property alias horizontalAlignment: buttonText.horizontalAlignment font { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index 9f4f7f340..1d30576ec 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -76,8 +76,9 @@ Menu { contentItem: Item { implicitWidth: menuListView.implicitWidth implicitHeight: menuListView.implicitHeight - ListView { + WListView { id: menuListView + interactive: contentHeight > height anchors { left: parent.left right: parent.right @@ -87,6 +88,7 @@ Menu { topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin } + clip: true implicitHeight: contentHeight implicitWidth: Array.from({ length: count diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml index 731b0d704..0a27c6cb3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -6,6 +6,7 @@ import Quickshell import Quickshell.Hyprland import qs.modules.common import qs.modules.common.functions +import qs.modules.common.widgets import qs.modules.waffle.looks MenuItem { @@ -14,11 +15,11 @@ MenuItem { property color colBackground: ColorUtils.transparentize(Looks.colors.bg1) property color colBackgroundHover: Looks.colors.bg2Hover property color colBackgroundActive: Looks.colors.bg2Active - property color colBackgroundToggled: Looks.colors.accent - property color colBackgroundToggledHover: Looks.colors.accentHover - property color colBackgroundToggledActive: Looks.colors.accentActive + property color colBackgroundToggled: Looks.colors.bg2Hover + property color colBackgroundToggledHover: Looks.colors.bg2Active + property color colBackgroundToggledActive: Looks.colors.bg2Hover property color colForeground: Looks.colors.fg - property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundToggled: Looks.colors.fg property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) property color color: { if (!root.enabled) @@ -70,27 +71,57 @@ MenuItem { implicitHeight: Math.max(28, contentItem.implicitHeight) + topInset + bottomInset implicitWidth: contentItem.implicitWidth + leftInset + rightInset + leftPadding + rightPadding - contentItem: RowLayout { - id: contentLayout - spacing: 12 - FluentIcon { - id: buttonIcon - monochrome: true - implicitSize: 20 - Layout.fillWidth: false - Layout.alignment: Qt.AlignVCenter - color: root.fgColor - visible: root.icon.name !== ""; - icon: root.icon.name + contentItem: Item { + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + + RowLayout { + id: contentLayout + anchors.fill: parent + spacing: 12 + FluentIcon { + id: buttonIcon + monochrome: true + implicitSize: 20 + Layout.fillWidth: false + Layout.alignment: Qt.AlignVCenter + color: root.fgColor + visible: root.icon.name !== "" + icon: root.icon.name + } + WText { + id: buttonText + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft + text: root.text + horizontalAlignment: Text.AlignLeft + font.pixelSize: Looks.font.pixelSize.large + color: root.fgColor + } } - WText { - id: buttonText - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter | Qt.AlignLeft - text: root.text - horizontalAlignment: Text.AlignLeft - font.pixelSize: Looks.font.pixelSize.large - color: root.fgColor + + WFadeLoader { + anchors { + verticalCenter: parent.verticalCenter + left: parent.left + leftMargin: -root.leftPadding + width + } + shown: root.checked + sourceComponent: Rectangle { + implicitWidth: 3 + implicitHeight: 3 + radius: width / 2 + color: Looks.colors.accent + property bool forceZeroHeight: true + height: forceZeroHeight ? 0 : Math.max(root.down ? 10 : 16, root.background.height - 18 * 2) + Component.onCompleted: { + forceZeroHeight = false; + } + + Behavior on height { + animation: Looks.transition.resize.createObject(this) + } + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml new file mode 100644 index 000000000..6d58e8f7c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbar.qml @@ -0,0 +1,38 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets + +Item { + id: root + + property real padding: 9 + property alias colBackground: background.color + property alias spacing: toolbarLayout.spacing + property alias radius: background.radius + default property alias data: toolbarLayout.data + + implicitWidth: background.implicitWidth + implicitHeight: background.implicitHeight + + Rectangle { + id: background + anchors.fill: parent + implicitHeight: 50 + implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 + radius: Looks.radius.large + color: Looks.colors.bg0Base + + border.width: 1 + border.color: Looks.colors.bg1Border + + RowLayout { + id: toolbarLayout + spacing: 4 + anchors { + fill: parent + margins: root.padding + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml new file mode 100644 index 000000000..59dc5ee99 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarButton.qml @@ -0,0 +1,8 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +WButton { + implicitHeight: 32 + radius: Looks.radius.medium +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml new file mode 100644 index 000000000..4a3644735 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconButton.qml @@ -0,0 +1,16 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +WToolbarButton { + id: root + implicitWidth: height + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: root.icon.name + implicitSize: 18 + color: root.fgColor + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml new file mode 100644 index 000000000..0dc6ae208 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarIconTabButton.qml @@ -0,0 +1,21 @@ +import QtQuick +import QtQuick.Controls +import qs.modules.common + +TabButton { + id: root + + implicitWidth: 38 + implicitHeight: 32 + padding: 0 + + background: null + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: root.icon.name + color: root.icon.color + implicitSize: 18 + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml new file mode 100644 index 000000000..d67c4b767 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarSeparator.qml @@ -0,0 +1,11 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +Rectangle { + Layout.leftMargin: 4 + Layout.rightMargin: 4 + implicitHeight: 24 + implicitWidth: 1 + color: Looks.colors.bg0Border +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml new file mode 100644 index 000000000..025ee53a4 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolbarTabBar.qml @@ -0,0 +1,53 @@ +import QtQuick +import QtQuick.Controls +import qs.modules.common +import qs.modules.common.functions + +TabBar { + id: root + implicitHeight: 32 + + background: Rectangle { + radius: Looks.radius.medium + color: Looks.colors.bgPanelFooter + border.color: ColorUtils.transparentize(Looks.colors.bg0Border, 0.7) + border.width: 1 + + // Indicator + Rectangle { + anchors { + top: parent.top + bottom: parent.bottom + left: parent.left + leftMargin: root.currentIndex * (root.width / root.count) + Behavior on leftMargin { + animation: Looks.transition.resize.createObject(this) + } + } + radius: Looks.radius.medium + color: Looks.colors.bg2Base + border.color: Looks.colors.bg0Border + border.width: 1 + width: root.width / root.count + + Rectangle { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: 1 + } + implicitWidth: pressDetector.containsPress ? 16 : 12 + implicitHeight: 3 + radius: height / 2 + color: Looks.colors.accent + } + } + } + + MouseArea { + id: pressDetector + z: 9999 + anchors.fill: parent + acceptedButtons: Qt.LeftButton + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml new file mode 100644 index 000000000..a0ec287b8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml @@ -0,0 +1,62 @@ +import QtQuick +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Item { + id: root + + required property int regionX + required property int regionY + required property int regionWidth + required property int regionHeight + + property bool dashed: true + property color borderColor: "#ffffff" + property color overlayColor: ColorUtils.transparentize("#000000", 1) + Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4) + Behavior on overlayColor { + ColorAnimation { + duration: 250 + easing.type: Easing.InOutQuad + } + } + + // Overlay to darken screen + // Base dark overlay around region + Rectangle { + id: darkenOverlay + z: 1 + anchors { + left: parent.left + top: parent.top + leftMargin: root.regionX - darkenOverlay.border.width + topMargin: root.regionY - darkenOverlay.border.width + } + width: root.regionWidth + darkenOverlay.border.width * 2 + height: root.regionHeight + darkenOverlay.border.width * 2 + color: "transparent" + border.color: root.overlayColor + border.width: Math.max(root.width, root.height) + } + + // Selection border + DashedBorder { + id: border + z: 2 + visible: root.regionWidth > 0 && root.regionHeight > 0 + anchors { + left: parent.left + top: parent.top + leftMargin: Math.round(root.regionX - borderWidth) + topMargin: Math.round(root.regionY - borderWidth) + } + width: Math.round(root.regionWidth + borderWidth * 2) + height: Math.round(root.regionHeight + borderWidth * 2) + color: root.borderColor + dashLength: 4 + gapLength: root.dashed ? 3 : 0 + borderWidth: 1 + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml new file mode 100644 index 000000000..318887928 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRegionSelectionPanel.qml @@ -0,0 +1,375 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt.labs.synchronizer +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.utils +import qs.modules.common.widgets +import qs.modules.waffle.looks + +PanelWindow { + id: root + + enum MediaType { + Image, + Video + } + enum ImageAction { + Copy, + Menu, + CharRecognition, + Search + } + enum VideoAction { + Record, + RecordWithSound + } + enum SelectionMode { + Rect, + Window + } + + signal closed + function close() { + root.closed(); + } + + property var mediaType: WRegionSelectionPanel.MediaType.Image + property var imageAction: WRegionSelectionPanel.ImageAction.Copy + property var selectionMode: WRegionSelectionPanel.SelectionMode.Rect + + visible: false + color: "transparent" + WlrLayershell.namespace: "quickshell:regionSelector" + WlrLayershell.layer: WlrLayer.Overlay + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + exclusionMode: ExclusionMode.Ignore + anchors { + left: true + right: true + top: true + bottom: true + } + + // Hyprland stuff + readonly property HyprlandMonitor hyprlandMonitor: Hyprland.monitorFor(screen) + readonly property real monitorScale: hyprlandMonitor.scale + readonly property var windows: [...HyprlandData.windowList].sort((a, b) => { + // Sort floating=true windows before others + if (a.floating === b.floating) + return 0; + return a.floating ? -1 : 1; + }) + + property string screenshotDir: Directories.screenshotTemp + property string screenshotPath: `${root.screenshotDir}/image-${screen.name}` + TempScreenshotProcess { + id: screenshotProc + running: true + screen: root.screen + screenshotDir: root.screenshotDir + screenshotPath: root.screenshotPath + onExited: (exitCode, exitStatus) => { + root.preparationDone = true; + } + } + property bool preparationDone: false + onPreparationDoneChanged: { + if (!preparationDone) + return; + root.visible = true; + } + + function getScreenshotAction() { + switch (root.mediaType) { + case WRegionSelectionPanel.MediaType.Image: + switch (root.imageAction) { + case WRegionSelectionPanel.ImageAction.Copy: + return ScreenshotAction.Action.Copy; + case WRegionSelectionPanel.ImageAction.Menu: + return ScreenshotAction.Action.Edit; + case WRegionSelectionPanel.ImageAction.CharRecognition: + return ScreenshotAction.Action.CharRecognition; + case WRegionSelectionPanel.ImageAction.Search: + return ScreenshotAction.Action.Search; + default: + return ScreenshotAction.Action.Copy; + } + break; + case WRegionSelectionPanel.MediaType.Video: + switch (root.videoAction) { + case WRegionSelectionPanel.VideoAction.Record: + return ScreenshotAction.Action.Record; + case WRegionSelectionPanel.VideoAction.RecordWithSound: + return ScreenshotAction.Action.RecordWithSound; + } + } + } + + Process { + id: snipProc + } + + ScreencopyView { + id: screencopyView + anchors.fill: parent + live: false + captureSource: root.screen + + focus: root.visible + Keys.onPressed: event => { // Esc to close + if (event.key === Qt.Key_Escape) { + root.close(); + } else if (event.key === Qt.Key_E && event.modifiers & Qt.ControlModifier) { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.imageAction = WRegionSelectionPanel.ImageAction.Menu; + } + } + } + + DragManager { + id: dragArea + anchors.fill: parent + hoverEnabled: true + acceptedButtons: Qt.LeftButton | Qt.RightButton + cursorShape: Qt.CrossCursor + + property bool isWindowSelection: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window + property var hoveredWindow: root.windows.find(w => { + const inCurrentWorkspace = w.workspace.id === HyprlandData.activeWorkspace.id; + const withinXRange = w.at[0] <= dragArea.mouseX && dragArea.mouseX <= w.at[0] + w.size[0]; + const withinYRange = w.at[1] <= dragArea.mouseY && dragArea.mouseY <= w.at[1] + w.size[1]; + return inCurrentWorkspace && withinXRange && withinYRange; + }) + property int winPadding: 1 + property int selectionX: isWindowSelection ? ((hoveredWindow?.at[0] ?? 0) - winPadding) : regionTopLeftX + property int selectionY: isWindowSelection ? ((hoveredWindow?.at[1] ?? 0) - winPadding) : regionTopLeftY + property int selectionWidth: isWindowSelection ? ((hoveredWindow?.size[0] ?? 0) + winPadding * 2) : regionWidth + property int selectionHeight: isWindowSelection ? ((hoveredWindow?.size[1] ?? 0) + winPadding * 2) : regionHeight + + onDragReleased: (diffX, diffY) => { + if (selectionWidth === 0 || selectionHeight === 0) { + return; + } + const screenshotDir = Config.options.screenSnip.savePath !== "" ? Config.options.screenSnip.savePath : ""; + const screenshotAction = root.getScreenshotAction(); + const command = ScreenshotAction.getCommand(dragArea.selectionX * root.monitorScale // + , dragArea.selectionY * root.monitorScale // + , dragArea.selectionWidth * root.monitorScale// + , dragArea.selectionHeight * root.monitorScale // + , root.screenshotPath // + , screenshotAction // + , screenshotDir); // yo wtf is this formatting qmlls do be funnie + snipProc.command = command; + + // Image post-processing + snipProc.startDetached(); + root.close(); + } + + WRectangularSelection { + id: rectangularSelection + anchors.fill: parent + regionX: dragArea.selectionX + regionY: dragArea.selectionY + regionWidth: dragArea.selectionWidth + regionHeight: dragArea.selectionHeight + dashed: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect + } + + RegionSelectionOptionsToolbar { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: 12 + } + } + } + } + + component RegionSelectionOptionsToolbar: WToolbar { + // Image/video + WToolbarTabBar { + currentIndex: switch (root.mediaType) { + case WRegionSelectionPanel.MediaType.Image: + return 0; + case WRegionSelectionPanel.MediaType.Video: + return 1; + default: + return 0; + } + WToolbarIconTabButton { + icon.name: "camera" + icon.color: Looks.colors.fg + } + WToolbarIconTabButton { + icon.name: "video" + icon.color: Looks.colors.fg + } + onCurrentIndexChanged: { + switch (currentIndex) { + case 0: + root.mediaType = WRegionSelectionPanel.MediaType.Image; + break; + case 1: + root.mediaType = WRegionSelectionPanel.MediaType.Video; + break; + } + } + + WToolTip { + text: Translation.tr("Snip") + } + } + + // Selection type + WToolbarButton { + id: selectionTypeBtn + implicitWidth: selectionTypeBtnRow.implicitWidth + 11 * 2 + leftPadding: 11 + rightPadding: 11 + onClicked: { + selectionTypeMenu.visible = !selectionTypeMenu.visible; + } + contentItem: Row { + id: selectionTypeBtnRow + spacing: 4 + FluentIcon { + anchors.verticalCenter: parent.verticalCenter + icon: switch (root.selectionMode) { + case WRegionSelectionPanel.SelectionMode.Rect: + return "crop"; + case WRegionSelectionPanel.SelectionMode.Window: + return "calendar-add"; + default: + return "crop"; + } + implicitSize: 18 + } + FluentIcon { + anchors { + top: parent.top + topMargin: (parent.height - height) / 2 + (selectionTypeBtn.down ? 2 : 0) + Behavior on topMargin { + animation: Looks.transition.enter.createObject(this) + } + } + icon: "chevron-down" + implicitSize: 12 + } + } + + WMenu { + id: selectionTypeMenu + onClosed: screencopyView.focus = true + x: -margins + y: -margins - (selectionTypeBtn.parent.height - selectionTypeBtn.height) - 16 + topMargin: -6 + height: implicitHeight + sourceEdgeMargin + + color: Looks.colors.bg1Base + + Action { + icon.name: "crop" + text: Translation.tr("Rectangle") + checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Rect + onTriggered: { + root.selectionMode = WRegionSelectionPanel.SelectionMode.Rect; + } + } + Action { + icon.name: "calendar-add" + text: Translation.tr("Window") + checked: root.selectionMode === WRegionSelectionPanel.SelectionMode.Window + onTriggered: { + root.selectionMode = WRegionSelectionPanel.SelectionMode.Window; + } + } + } + + WToolTip { + text: Translation.tr("Snipping area") + } + } + + // Markup + WToolbarIconButton { + icon.name: "image-edit" + enabled: root.mediaType === WRegionSelectionPanel.MediaType.Image + checked: root.imageAction === WRegionSelectionPanel.ImageAction.Menu + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Menu) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.imageAction = WRegionSelectionPanel.ImageAction.Menu; + } + } + WToolTip { + text: Translation.tr("Quick markup (Ctrl+E)") + } + } + + WToolbarSeparator {} + + // Tools + WToolbarIconButton { + icon.name: "search-visual" + checked: root.imageAction === WRegionSelectionPanel.ImageAction.Search + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.Search && root.mediaType === WRegionSelectionPanel.MediaType.Image) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.mediaType = WRegionSelectionPanel.MediaType.Image; + root.imageAction = WRegionSelectionPanel.ImageAction.Search; + } + } + WToolTip { + text: Translation.tr("Image search") + } + } + WToolbarIconButton { + icon.name: "eyedropper" + onClicked: { + Quickshell.execDetached(["bash", "-c", "sleep 0.2; hyprpicker -a"]); + root.closed(); + } + WToolTip { + text: Translation.tr("Color picker") + } + } + WToolbarIconButton { + icon.name: "scan-text" + checked: root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition + onClicked: { + if (root.imageAction === WRegionSelectionPanel.ImageAction.CharRecognition && root.mediaType === WRegionSelectionPanel.MediaType.Image) { + root.imageAction = WRegionSelectionPanel.ImageAction.Copy; + } else { + root.mediaType = WRegionSelectionPanel.MediaType.Image; + root.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition; + } + } + WToolTip { + text: Translation.tr("Text extractor") + } + } + + WToolbarSeparator {} + + WToolbarIconButton { + icon.name: "dismiss" + onClicked: root.close() + WToolTip { + text: Translation.tr("Close (Esc)") + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml new file mode 100644 index 000000000..c462f9e7b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WScreenSnip.qml @@ -0,0 +1,106 @@ +pragma ComponentBehavior: Bound +import qs +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.services +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Widgets +import Quickshell.Hyprland + +Scope { + id: root + + function dismiss() { + GlobalStates.regionSelectorOpen = false; + } + + Loader { + id: regionSelectorLoader + active: GlobalStates.regionSelectorOpen + + sourceComponent: WRegionSelectionPanel { + onClosed: root.dismiss() + } + } + + function screenshot() { + GlobalStates.regionSelectorOpen = true; + } + + function ocr() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image; + regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.CharRecognition; + } + + function record() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video; + regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.Record; + } + + function recordWithSound() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Video; + regionSelectorLoader.item.videoAction = WRegionSelectionPanel.VideoAction.RecordWithSound; + } + + function search() { + GlobalStates.regionSelectorOpen = true; + regionSelectorLoader.item.mediaType = WRegionSelectionPanel.MediaType.Image; + regionSelectorLoader.item.imageAction = WRegionSelectionPanel.ImageAction.Search; + } + + IpcHandler { + target: "region" + + function screenshot() { + root.screenshot(); + } + function ocr() { + root.ocr(); + } + function record() { + root.record(); + } + function recordWithSound() { + root.recordWithSound(); + } + function search() { + root.search(); + } + } + + GlobalShortcut { + name: "regionScreenshot" + description: "Takes a screenshot of the selected region" + onPressed: root.screenshot() + } + GlobalShortcut { + name: "regionSearch" + description: "Searches the selected region" + onPressed: root.search() + } + GlobalShortcut { + name: "regionOcr" + description: "Recognizes text in the selected region" + onPressed: root.ocr() + } + GlobalShortcut { + name: "regionRecord" + description: "Records the selected region" + onPressed: root.record() + } + GlobalShortcut { + name: "regionRecordWithSound" + description: "Records the selected region with sound" + onPressed: root.recordWithSound() + } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index feced64cd..93541c5bf 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -34,6 +34,7 @@ import qs.modules.waffle.lock import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.polkit +import qs.modules.waffle.screenSnip import qs.modules.waffle.startMenu import qs.modules.waffle.sessionScreen import qs.modules.waffle.taskView @@ -89,6 +90,7 @@ ShellRoot { PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } + PanelLoader { identifier: "wScreenSnip"; component: WScreenSnip {} } PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } PanelLoader { identifier: "wTaskView"; component: WaffleTaskView {} } @@ -104,7 +106,7 @@ ShellRoot { property list families: ["ii", "waffle"] property var panelFamilies: ({ "ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], - "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wTaskView", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wTaskView", "wPolkit", "wScreenSnip", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From 4041310b4dfdeb52a8e72530645ff62304df87b5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:29:32 +0100 Subject: [PATCH 05/13] make transparency not ass --- .../ii/modules/common/Appearance.qml | 64 ++++++++++++------- .../modules/common/functions/ColorUtils.qml | 34 ++++++++++ .../ii/modules/ii/overview/OverviewWidget.qml | 2 +- 3 files changed, 75 insertions(+), 25 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index 9c5045014..0e53d511e 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -31,12 +31,13 @@ Singleton { return Math.max(0, Math.min(0.22, y)) } property real autoContentTransparency: { // y = -10.1734x^2 + 3.4457x + 0.1872 - let x = autoBackgroundTransparency - let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872 - return Math.max(0, Math.min(0.6, y)) + // let x = autoBackgroundTransparency + // let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872 + // return Math.max(0, Math.min(0.6, y)) + return 0.9; } property real backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0 - property real contentTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency : 0 + property real contentTransparency: Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency m3colors: QtObject { property bool darkmode: true @@ -114,30 +115,41 @@ Singleton { colors: QtObject { property color colSubtext: m3colors.m3outline - property color colLayer0: ColorUtils.mix(ColorUtils.transparentize(m3colors.m3background, root.backgroundTransparency), m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1) + // Layer 0 + property color colLayer0Base: ColorUtils.mix(m3colors.m3background, m3colors.m3primary, Config.options.appearance.extraBackgroundTint ? 0.99 : 1) + property color colLayer0: ColorUtils.transparentize(colLayer0Base, root.backgroundTransparency) property color colOnLayer0: m3colors.m3onBackground property color colLayer0Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.9, root.contentTransparency)) property color colLayer0Active: ColorUtils.transparentize(ColorUtils.mix(colLayer0, colOnLayer0, 0.8, root.contentTransparency)) property color colLayer0Border: ColorUtils.mix(root.m3colors.m3outlineVariant, colLayer0, 0.4) - property color colLayer1: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency); + // Layer 1 + property color colLayer1Base: m3colors.m3surfaceContainerLow + property color colLayer1: ColorUtils.solveOverlayColor(colLayer0Base, colLayer1Base, 1 - root.contentTransparency); property color colOnLayer1: m3colors.m3onSurfaceVariant; property color colOnLayer1Inactive: ColorUtils.mix(colOnLayer1, colLayer1, 0.45); - property color colLayer2: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency) - property color colOnLayer2: m3colors.m3onSurface; - property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4); property color colLayer1Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.92), root.contentTransparency) property color colLayer1Active: ColorUtils.transparentize(ColorUtils.mix(colLayer1, colOnLayer1, 0.85), root.contentTransparency); - property color colLayer2Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.90), root.contentTransparency) - property color colLayer2Active: ColorUtils.transparentize(ColorUtils.mix(colLayer2, colOnLayer2, 0.80), root.contentTransparency); - property color colLayer2Disabled: ColorUtils.transparentize(ColorUtils.mix(colLayer2, m3colors.m3background, 0.8), root.contentTransparency); - property color colLayer3: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency) + // Layer 2 + property color colLayer2Base: m3colors.m3surfaceContainer + property color colLayer2: ColorUtils.solveOverlayColor(colLayer1Base, colLayer2Base, 1 - root.contentTransparency) + property color colLayer2Hover: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.90), 1 - root.contentTransparency) + property color colLayer2Active: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, colOnLayer2, 0.80), 1 - root.contentTransparency); + property color colLayer2Disabled: ColorUtils.solveOverlayColor(colLayer1Base, ColorUtils.mix(colLayer2Base, m3colors.m3background, 0.8), 1 - root.contentTransparency); + property color colOnLayer2: m3colors.m3onSurface; + property color colOnLayer2Disabled: ColorUtils.mix(colOnLayer2, m3colors.m3background, 0.4); + // Layer 3 + property color colLayer3Base: m3colors.m3surfaceContainerHigh + property color colLayer3: ColorUtils.solveOverlayColor(colLayer2Base, colLayer3Base, 1 - root.contentTransparency) + property color colLayer3Hover: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.90), 1 - root.contentTransparency) + property color colLayer3Active: ColorUtils.solveOverlayColor(colLayer2Base, ColorUtils.mix(colLayer3Base, colOnLayer3, 0.80), 1 - root.contentTransparency); property color colOnLayer3: m3colors.m3onSurface; - property color colLayer3Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.90), root.contentTransparency) - property color colLayer3Active: ColorUtils.transparentize(ColorUtils.mix(colLayer3, colOnLayer3, 0.80), root.contentTransparency); - property color colLayer4: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency) + // Layer 4 + property color colLayer4Base: m3colors.m3surfaceContainerHighest + property color colLayer4: ColorUtils.solveOverlayColor(colLayer3Base, colLayer4Base, 1 - root.contentTransparency) + property color colLayer4Hover: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.90), 1 - root.contentTransparency) + property color colLayer4Active: ColorUtils.solveOverlayColor(colLayer3Base, ColorUtils.mix(colLayer4Base, colOnLayer4, 0.80), 1 - root.contentTransparency); property color colOnLayer4: m3colors.m3onSurface; - property color colLayer4Hover: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.90), root.contentTransparency) - property color colLayer4Active: ColorUtils.transparentize(ColorUtils.mix(colLayer4, colOnLayer4, 0.80), root.contentTransparency); + // Primary property color colPrimary: m3colors.m3primary property color colOnPrimary: m3colors.m3onPrimary property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87) @@ -146,13 +158,16 @@ Singleton { property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.9) property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.8) property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer + // Secondary property color colSecondary: m3colors.m3secondary - property color colOnSecondary: m3colors.m3onSecondary property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85) property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4) + property color colOnSecondary: m3colors.m3onSecondary property color colSecondaryContainer: m3colors.m3secondaryContainer property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90) property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.54) + property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer + // Tertiary property color colTertiary: m3colors.m3tertiary property color colTertiaryHover: ColorUtils.mix(m3colors.m3tertiary, colLayer1Hover, 0.85) property color colTertiaryActive: ColorUtils.mix(m3colors.m3tertiary, colLayer1Active, 0.4) @@ -161,16 +176,17 @@ Singleton { property color colTertiaryContainerActive: ColorUtils.mix(m3colors.m3tertiaryContainer, colLayer1Active, 0.54) property color colOnTertiary: m3colors.m3onTertiary property color colOnTertiaryContainer: m3colors.m3onTertiaryContainer - property color colOnSecondaryContainer: m3colors.m3onSecondaryContainer - property color colSurfaceContainerLow: ColorUtils.transparentize(m3colors.m3surfaceContainerLow, root.contentTransparency) - property color colSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.contentTransparency) + // Surface property color colBackgroundSurfaceContainer: ColorUtils.transparentize(m3colors.m3surfaceContainer, root.backgroundTransparency) - property color colSurfaceContainerHigh: ColorUtils.transparentize(m3colors.m3surfaceContainerHigh, root.contentTransparency) - property color colSurfaceContainerHighest: ColorUtils.transparentize(m3colors.m3surfaceContainerHighest, root.contentTransparency) + property color colSurfaceContainerLow: ColorUtils.solveOverlayColor(m3colors.m3background, m3colors.m3surfaceContainerLow, 1 - root.contentTransparency) + property color colSurfaceContainer: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerLow, m3colors.m3surfaceContainer, 1 - root.contentTransparency) + property color colSurfaceContainerHigh: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainer, m3colors.m3surfaceContainerHigh, 1 - root.contentTransparency) + property color colSurfaceContainerHighest: ColorUtils.solveOverlayColor(m3colors.m3surfaceContainerHigh, m3colors.m3surfaceContainerHighest, 1 - root.contentTransparency) property color colSurfaceContainerHighestHover: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.95) property color colSurfaceContainerHighestActive: ColorUtils.mix(m3colors.m3surfaceContainerHighest, m3colors.m3onSurface, 0.85) property color colOnSurface: m3colors.m3onSurface property color colOnSurfaceVariant: m3colors.m3onSurfaceVariant + // Misc property color colTooltip: m3colors.m3inverseSurface property color colOnTooltip: m3colors.m3inverseOnSurface property color colScrim: ColorUtils.transparentize(m3colors.m3scrim, 0.5) diff --git a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml index 165f27754..74305c8fa 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/ColorUtils.qml @@ -135,4 +135,38 @@ Singleton { var c = Qt.color(color); return c.hslLightness < 0.5; } + + /** + * Clamps a value to the inclusive range [0, 1]. + * + * @param {number} x - The value to clamp. + * @returns {number} The clamped value in the range [0, 1]. + */ + function clamp01(x) { + return Math.min(1, Math.max(0, x)); + } + + /** + * Solves for the solid overlay color that, when composited over a base color + * with a given opacity, yields the target color. + * + * The compositing equation is: + * result = overlay * overlayOpacity + base * (1 - overlayOpacity) + * + * This function algebraically inverts that equation per channel. + * + * @param {Qt.rgba} baseColor - The base (background) color. + * @param {Qt.rgba} targetColor - The resulting color after compositing. + * @param {number} overlayOpacity - The overlay opacity (0-1). + * @returns {Qt.rgba} The solved overlay color + */ + function solveOverlayColor(baseColor, targetColor, overlayOpacity) { + let invA = 1.0 - overlayOpacity; + + let r = (targetColor.r - baseColor.r * invA) / overlayOpacity; + let g = (targetColor.g - baseColor.g * invA) / overlayOpacity; + let b = (targetColor.b - baseColor.b * invA) / overlayOpacity; + + return Qt.rgba(clamp01(r), clamp01(g), clamp01(b), overlayOpacity); + } } diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index 8b791f44a..f1ea1b331 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -101,7 +101,7 @@ Item { required property int index property int colIndex: index property int workspaceValue: root.workspaceGroup * root.workspacesShown + getWsInCell(row.index, colIndex) - property color defaultWorkspaceColor: ColorUtils.mix(Appearance.colors.colBackgroundSurfaceContainer, Appearance.colors.colSurfaceContainerHigh, 0.8) + property color defaultWorkspaceColor: Appearance.colors.colSurfaceContainerLow property color hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) property color hoveredBorderColor: Appearance.colors.colLayer2Hover property bool hoveredWhileDragging: false From 41f007a771b95e22ef0fdfae58055178286ff006 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:38:49 +0100 Subject: [PATCH 06/13] increase session screen transparency --- dots/.config/quickshell/ii/modules/common/Appearance.qml | 7 +------ .../ii/modules/ii/sessionScreen/SessionScreen.qml | 4 +--- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index 0e53d511e..aca8bfa4f 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -30,12 +30,7 @@ Singleton { let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896 return Math.max(0, Math.min(0.22, y)) } - property real autoContentTransparency: { // y = -10.1734x^2 + 3.4457x + 0.1872 - // let x = autoBackgroundTransparency - // let y = -10.1734 * (x * x) + 3.4457 * (x) + 0.1872 - // return Math.max(0, Math.min(0.6, y)) - return 0.9; - } + property real autoContentTransparency: 0.9 property real backgroundTransparency: Config?.options.appearance.transparency.enable ? Config?.options.appearance.transparency.automatic ? autoBackgroundTransparency : Config?.options.appearance.transparency.backgroundTransparency : 0 property real contentTransparency: Config?.options.appearance.transparency.automatic ? autoContentTransparency : Config?.options.appearance.transparency.contentTransparency diff --git a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml index 6350d18a9..ee8714793 100644 --- a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml +++ b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml @@ -45,9 +45,7 @@ Scope { WlrLayershell.namespace: "quickshell:session" WlrLayershell.layer: WlrLayer.Overlay WlrLayershell.keyboardFocus: WlrKeyboardFocus.Exclusive - // This is a big surface so we needa carefully choose the transparency, - // or we'll get a large scary rgb blob - color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.04 : 0.12) + color: ColorUtils.transparentize(Appearance.m3colors.m3background, Appearance.m3colors.darkmode ? 0.05 : 0.12) anchors { top: true From 8d7dd0d6aefa52db206abf408be390efb56ad1f3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 21 Dec 2025 23:52:01 +0100 Subject: [PATCH 07/13] overlay: fix weird transparency --- .../quickshell/ii/modules/ii/overlay/OverlayBackground.qml | 2 +- .../quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml index d91e4cbba..205307cab 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/OverlayBackground.qml @@ -4,5 +4,5 @@ import qs.modules.common Rectangle { id: contentItem anchors.fill: parent - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer } diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml index 949cd2380..2cae47e9e 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml @@ -190,7 +190,7 @@ AbstractOverlayWidget { fill: parent margins: root.resizeMargin } - color: ColorUtils.transparentize(Appearance.colors.colLayer1, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1) + color: ColorUtils.transparentize(Appearance.colors.colLayer1Base, (root.fancyBorders && GlobalStates.overlayOpen) ? 0 : 1) radius: root.radius border.color: ColorUtils.transparentize(Appearance.colors.colOutlineVariant, GlobalStates.overlayOpen ? 0 : 1) border.width: 1 From 896aa977011519c661ce365c15b399a89e83e701 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Dec 2025 00:16:19 +0100 Subject: [PATCH 08/13] fix wrong notif and overlay crosshair titlebar color --- .../quickshell/ii/modules/common/widgets/NotificationGroup.qml | 2 +- .../quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index fc612dcb1..e3d64027b 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -121,7 +121,7 @@ MouseArea { // Notification group area id: background anchors.left: parent.left width: parent.width - color: popup ? ColorUtils.applyAlpha(Appearance.colors.colLayer2, 1 - Appearance.backgroundTransparency) : Appearance.colors.colLayer2 + color: popup ? Appearance.colors.colBackgroundSurfaceContainer : Appearance.colors.colLayer2 radius: Appearance.rounding.normal anchors.leftMargin: root.xOffset diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml index 2cae47e9e..b3cf1e74a 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/StyledOverlayWidget.qml @@ -217,7 +217,7 @@ AbstractOverlayWidget { Layout.fillWidth: true implicitWidth: titleBarRow.implicitWidth + root.padding * 2 implicitHeight: titleBarRow.implicitHeight + root.padding * 2 - color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1 + color: root.fancyBorders ? "transparent" : Appearance.colors.colLayer1Base // border.color: Appearance.colors.colOutlineVariant // border.width: 1 From 29c8001785b5d329381cfb277fbecd3990acbc79 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Dec 2025 23:01:00 +0100 Subject: [PATCH 09/13] waffle: start: move Other category to last --- .../ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml index aa1321eb8..4a704909d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml @@ -28,13 +28,13 @@ GridLayout { }), aggAppCatComp.createObject(null, { name: Translation.tr("Creativity"), categories: ["AudioVideo", "Graphics"] - }), aggAppCatComp.createObject(null, { - name: Translation.tr("Other"), - categories: ["Game"] }), aggAppCatComp.createObject(null, { name: Translation.tr("System"), categories: ["Settings", "System"] - }) + }), aggAppCatComp.createObject(null, { + name: Translation.tr("Other"), + categories: ["Game"] + }), ] Repeater { From d7ae6014ed43cd8e766aade5604179dbfc55b23c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 22 Dec 2025 23:01:42 +0100 Subject: [PATCH 10/13] waffles: popup notifs --- .../WSingleNotification.qml | 11 ++-- .../WaffleNotificationPopup.qml | 66 +++++++++++++++++++ .../quickshell/ii/services/Notifications.qml | 4 +- dots/.config/quickshell/ii/shell.qml | 4 +- 4 files changed, 78 insertions(+), 7 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 350e7b0ab..0b7bfc8b9 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -15,6 +15,9 @@ MouseArea { required property var notification property bool expanded: notification.actions.length > 0 property string groupExpandControlMessage: "" + + readonly property bool isPopup: notification?.popup ?? false + signal groupExpandToggle hoverEnabled: true @@ -56,8 +59,8 @@ MouseArea { Rectangle { id: contentItem width: parent.width - color: Looks.colors.bgPanelBody - radius: Looks.radius.medium + color: root.isPopup ? Looks.colors.bg0 : Looks.colors.bgPanelBody + radius: root.isPopup ? Looks.radius.large : Looks.radius.medium property real padding: 12 implicitHeight: notificationContent.implicitHeight + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2 @@ -157,9 +160,9 @@ MouseArea { NotificationHeaderButton { Layout.rightMargin: 4 - opacity: root.containsMouse ? 1 : 0 + opacity: (root.containsMouse || root.isPopup) ? 1 : 0 icon.name: "dismiss" - implicitSize: 12 + implicitSize: 14 onClicked: root.dismiss() } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml new file mode 100644 index 000000000..7deb54c30 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationPopup/WaffleNotificationPopup.qml @@ -0,0 +1,66 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.waffle.looks +import qs.modules.waffle.notificationCenter + +Scope { + id: notificationPopup + + PanelWindow { + id: root + visible: (Notifications.popupList.length > 0) && !GlobalStates.screenLocked + screen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) ?? null + + WlrLayershell.namespace: "quickshell:notificationPopup" + WlrLayershell.layer: WlrLayer.Overlay + exclusiveZone: 0 + + anchors { + top: true + right: true + bottom: true + } + + mask: Region { + item: listview.contentItem + } + + color: "transparent" + implicitWidth: listview.implicitWidth + + WListView { + id: listview + anchors { + bottom: parent.bottom + right: parent.right + left: parent.left + } + leftMargin: 16 + rightMargin: 16 + topMargin: 16 + bottomMargin: 16 + + height: Math.min(contentItem.height + topMargin + bottomMargin, parent.height) + width: parent.width - Appearance.sizes.elevationMargin * 2 + + implicitWidth: 396 + spacing:12 + + model: ScriptModel { + values: Notifications.popupList + } + delegate: WSingleNotification { + required property var modelData + notification: modelData + width: ListView.view.width - ListView.view.leftMargin - ListView.view.rightMargin + } + } + } +} diff --git a/dots/.config/quickshell/ii/services/Notifications.qml b/dots/.config/quickshell/ii/services/Notifications.qml index 702da0f8a..a45b86007 100644 --- a/dots/.config/quickshell/ii/services/Notifications.qml +++ b/dots/.config/quickshell/ii/services/Notifications.qml @@ -135,8 +135,8 @@ Singleton { property var groupsByAppName: groupsForList(root.list) property var popupGroupsByAppName: groupsForList(root.popupList) - property var appNameList: appNameListForGroups(root.groupsByAppName) - property var popupAppNameList: appNameListForGroups(root.popupGroupsByAppName) + property list appNameList: appNameListForGroups(root.groupsByAppName) + property list popupAppNameList: appNameListForGroups(root.popupGroupsByAppName) // Quickshell's notification IDs starts at 1 on each run, while saved notifications // can already contain higher IDs. This is for avoiding id collisions diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 93541c5bf..b0fe37a4f 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -32,6 +32,7 @@ import qs.modules.waffle.background import qs.modules.waffle.bar import qs.modules.waffle.lock import qs.modules.waffle.notificationCenter +import qs.modules.waffle.notificationPopup import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.polkit import qs.modules.waffle.screenSnip @@ -88,6 +89,7 @@ ShellRoot { PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } PanelLoader { identifier: "wLock"; component: WaffleLock {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } + PanelLoader { identifier: "wNotificationPopup"; component: WaffleNotificationPopup {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } PanelLoader { identifier: "wScreenSnip"; component: WScreenSnip {} } @@ -106,7 +108,7 @@ ShellRoot { property list families: ["ii", "waffle"] property var panelFamilies: ({ "ii": ["iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector"], - "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wTaskView", "wPolkit", "wScreenSnip", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wNotificationPopup", "wOnScreenDisplay", "wTaskView", "wPolkit", "wScreenSnip", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiOnScreenKeyboard", "iiOverlay", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From cfb8b44d7a8b609e3ce5618aa64a021fe2b16735 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:05:41 +0100 Subject: [PATCH 11/13] sidebar: fix unintended transparent statusbar --- dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml index 820626b51..3dc181fe4 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/AiChat.qml @@ -314,7 +314,10 @@ Inline w/ backslash and round brackets \\(e^{i\\pi} + 1 = 0\\) implicitWidth: statusRowLayout.implicitWidth + 10 * 2 implicitHeight: Math.max(statusRowLayout.implicitHeight, 38) radius: Appearance.rounding.normal - root.padding - color: Appearance.colors.colLayer2 + color: messageListView.atYBeginning ? Appearance.colors.colLayer2 : Appearance.colors.colLayer2Base + Behavior on color { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } RowLayout { id: statusRowLayout anchors.centerIn: parent From ec7d6fd66b4069f75dd4c64931495dd10756eeec Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Dec 2025 09:11:06 +0100 Subject: [PATCH 12/13] waffles: make bar not hide when locked --- dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml index 2326a47f7..b69b7c8e2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WaffleBar.qml @@ -13,7 +13,7 @@ Scope { LazyLoader { id: barLoader - active: GlobalStates.barOpen && !GlobalStates.screenLocked + active: GlobalStates.barOpen component: Variants { model: Quickshell.screens delegate: PanelWindow { // Bar window From 76ca889eecd5ad85a3bbb26c9413d68390067c51 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 23 Dec 2025 10:31:05 +0100 Subject: [PATCH 13/13] waffles: adjust colors --- .../ii/modules/waffle/looks/Looks.qml | 73 ++++++++++--------- .../NotificationPaneContent.qml | 2 +- .../WSingleNotification.qml | 2 +- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 44075570f..779835837 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -17,8 +17,9 @@ Singleton { property string iconsPath: `${Directories.assetsPath}/icons/fluent` property bool dark: Appearance.m3colors.darkmode - property real backgroundTransparency: 0.16 - property real panelBackgroundTransparency: 0.14 + readonly property bool transparencyEnabled: Config.options.appearance.transparency.enable + property real backgroundTransparency: transparencyEnabled ? 0.16 : 0 + property real panelBackgroundTransparency: transparencyEnabled ? 0.14 : 0 property real panelLayerTransparency: root.dark ? 0.9 : 0.7 property real contentTransparency: root.dark ? 0.87 : 0.5 function applyBackgroundTransparency(col) { @@ -27,23 +28,22 @@ Singleton { function applyContentTransparency(col) { return ColorUtils.applyAlpha(col, 1 - root.contentTransparency) } - lightColors: QtObject { // TODO: figure out transparency + lightColors: QtObject { id: lightColors - property color bgPanelFooter: "#EEEEEE" property color bgPanelBody: "#F2F2F2" property color bgPanelSeparator: "#E0E0E0" property color bg0: "#EEEEEE" - property color bg0Border: '#adadad' - property color bg1: "#F7F7F7" + property color bg0Border: '#BEBEBE' property color bg1Base: "#F7F7F7" + property color bg1: "#F7F7F7" property color bg1Hover: "#F7F7F7" property color bg1Active: '#EFEFEF' - property color bg1Border: '#d7d7d7' + property color bg1Border: '#E9E9E9' property color bg2: "#FBFBFB" property color bg2Base: "#FBFBFB" property color bg2Hover: '#ffffff' property color bg2Active: '#eeeeee' - property color bg2Border: '#cdcdcd' + property color bg2Border: '#E0E0E0' property color subfg: "#5C5C5C" property color fg: "#000000" property color fg1: "#626262" @@ -58,21 +58,20 @@ Singleton { } darkColors: QtObject { id: darkColors - property color bgPanelFooter: "#1C1C1C" - property color bgPanelBody: '#616161' + property color bgPanelBody: '#242424' property color bgPanelSeparator: "#191919" property color bg0: "#1C1C1C" property color bg0Border: "#404040" - property color bg1Base: "#2C2C2C" - property color bg1: '#9f9f9f' - property color bg1Hover: "#b3b3b3" - property color bg1Active: '#727272' + property color bg1Base: '#2C2C2C' + property color bg1: '#2C2C2C' + property color bg1Hover: "#292929" + property color bg1Active: '#252525' property color bg1Border: '#bebebe' property color bg2Base: "#313131" - property color bg2: '#8a8a8a' - property color bg2Hover: '#b1b1b1' - property color bg2Active: '#919191' - property color bg2Border: '#bdbdbd' + property color bg2: '#313131' + property color bg2Hover: '#363636' + property color bg2Active: '#2B2B2B' + property color bg2Border: '#404040' property color subfg: "#CED1D7" property color fg: "#FFFFFF" property color fg1: "#D1D1D1" @@ -87,38 +86,46 @@ Singleton { } colors: QtObject { id: colors + // Special property color shadow: ColorUtils.transparentize('#161616', 0.62) property color ambientShadow: ColorUtils.transparentize("#000000", 0.75) - property color bgPanelFooterBase: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelBackgroundTransparency) - property color bgPanelFooter: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelFooter : root.lightColors.bgPanelFooter, root.panelLayerTransparency) - property color bgPanelBody: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody, root.panelLayerTransparency) - property color bgPanelSeparator: ColorUtils.transparentize(root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, root.backgroundTransparency) + property color bgPanelFooterBase: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency) + property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBase, root.panelLayerTransparency) + property color bgPanelBodyBase: root.dark ? root.darkColors.bgPanelBody : root.lightColors.bgPanelBody + property color bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBase,bgPanelBodyBase, 1 - root.panelLayerTransparency) + property color bgPanelSeparator: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bgPanelSeparator : root.lightColors.bgPanelSeparator, 1 - root.panelBackgroundTransparency) + // Layer 0 property color bg0Base: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 property color bg0: ColorUtils.transparentize(bg0Base, root.backgroundTransparency) property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency) - property color bg1Base: root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base - property color bg1: ColorUtils.transparentize(root.dark ? root.darkColors.bg1 : root.lightColors.bg1, root.contentTransparency) - property color bg1Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, root.contentTransparency) - property color bg1Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, root.contentTransparency) - property color bg1Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, root.contentTransparency) - property color bg2Base: root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base - property color bg2: ColorUtils.transparentize(root.dark ? root.darkColors.bg2 : root.lightColors.bg2, root.contentTransparency) - property color bg2Hover: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, root.contentTransparency) - property color bg2Active: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, root.contentTransparency) - property color bg2Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, root.contentTransparency) + // Layer 1 + property color bg1Base: root.dark ? root.darkColors.bg1 : root.lightColors.bg1 + property color bg1: ColorUtils.solveOverlayColor(bg0Base, bg1Base, 1 - root.contentTransparency) + property color bg1Hover: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Hover : root.lightColors.bg1Hover, 1 - root.contentTransparency) + property color bg1Active: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Active : root.lightColors.bg1Active, 1 - root.contentTransparency) + property color bg1Border: ColorUtils.solveOverlayColor(bg0Base, root.dark ? root.darkColors.bg1Border : root.lightColors.bg1Border, 1 - root.contentTransparency) + // Layer 2 + property color bg2Base: root.dark ? root.darkColors.bg2 : root.lightColors.bg2 + property color bg2: ColorUtils.solveOverlayColor(bgPanelBodyBase, bg2Base, 1 - root.contentTransparency) + property color bg2Hover: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Hover : root.lightColors.bg2Hover, 1 - root.contentTransparency) + property color bg2Active: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Active : root.lightColors.bg2Active, 1 - root.contentTransparency) + property color bg2Border: ColorUtils.solveOverlayColor(bgPanelBodyBase, root.dark ? root.darkColors.bg2Border : root.lightColors.bg2Border, 1 - root.contentTransparency) + // Foreground / Text property color subfg: root.dark ? root.darkColors.subfg : root.lightColors.subfg property color fg: root.dark ? root.darkColors.fg : root.lightColors.fg property color fg1: root.dark ? root.darkColors.fg1 : root.lightColors.fg1 property color inactiveIcon: root.dark ? root.darkColors.inactiveIcon : root.lightColors.inactiveIcon + property color link: root.dark ? root.darkColors.link : root.lightColors.link + // Controls property color controlBgInactive: root.dark ? root.darkColors.controlBgInactive : root.lightColors.controlBgInactive property color controlBg: root.dark ? root.darkColors.controlBg : root.lightColors.controlBg property color controlBgHover: root.dark ? root.darkColors.controlBgHover : root.lightColors.controlBgHover property color controlFg: root.dark ? root.darkColors.controlFg : root.lightColors.controlFg property color inputBg: root.dark ? root.darkColors.inputBg : root.lightColors.inputBg - property color link: root.dark ? root.darkColors.link : root.lightColors.link property color danger: "#C42B1C" property color dangerActive: "#B62D1F" property color warning: "#FF9900" + // Accent property color accent: Appearance.colors.colPrimary property color accentHover: Appearance.colors.colPrimaryHover property color accentActive: Appearance.colors.colPrimaryActive diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml index 5e9582bf1..96669e353 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml @@ -7,7 +7,7 @@ import qs.modules.common.widgets import qs.modules.common.functions import qs.modules.waffle.looks -BodyRectangle { +FooterRectangle { id: root anchors.fill: parent implicitHeight: 230 diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 0b7bfc8b9..e7fb58a12 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -65,7 +65,7 @@ MouseArea { implicitHeight: notificationContent.implicitHeight + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2 border.width: 1 - border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1) + border.color: root.isPopup ? Looks.colors.bg2Border : Looks.colors.bgPanelSeparator Behavior on x { animation: Looks.transition.enter.createObject(this)