From e46c7c0d3d1d218c0446d3e9a25128ecbf747fe6 Mon Sep 17 00:00:00 2001 From: viridivn <42524580+viridivn@users.noreply.github.com> Date: Sun, 23 Nov 2025 15:04:49 -0500 Subject: [PATCH 001/154] add gemini 3 model --- dots/.config/quickshell/ii/services/Ai.qml | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index ccd237de8..389fb3e78 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -294,6 +294,19 @@ Singleton { "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", }), + "gemini-3-pro": aiModelComponent.createObject(this, { + "name": "Gemini 3.0 Pro", + "icon": "google-gemini-symbolic", + "description": Translation.tr("Online | Google's model\nGoogle's most intelligent model with SOTA reasoning and multimodal understanding."), + "homepage": "https://aistudio.google.com", + "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:streamGenerateContent", + "model": "gemini-3-pro-preview", + "requires_key": true, + "key_id": "gemini", + "key_get_link": "https://aistudio.google.com/app/apikey", + "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), + "api_format": "gemini", + }), "gemini-2.5-flash-lite": aiModelComponent.createObject(this, { "name": "Gemini 2.5 Flash-Lite", "icon": "google-gemini-symbolic", From 07f8a72d6db605d82f3fe2e5f4fd48029c24a857 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:35:43 +0100 Subject: [PATCH 002/154] wactioncenter: media controls --- .../ii/assets/icons/fluent/next-filled.svg | 1 + .../ii/assets/icons/fluent/next.svg | 1 + .../ii/assets/icons/fluent/pause-filled.svg | 1 + .../ii/assets/icons/fluent/pause.svg | 1 + .../ii/assets/icons/fluent/play-filled.svg | 1 + .../ii/assets/icons/fluent/play.svg | 1 + .../assets/icons/fluent/previous-filled.svg | 1 + .../ii/assets/icons/fluent/previous.svg | 1 + .../ii/mediaControls/MediaControls.qml | 25 +-- .../actionCenter/ActionCenterContent.qml | 61 +++++-- .../waffle/actionCenter/MediaPaneContent.qml | 156 ++++++++++++++++++ .../actionCenter/WaffleActionCenter.qml | 5 +- .../bluetooth/BluetoothControl.qml | 2 +- .../actionCenter/mainPage/MainPageFooter.qml | 4 +- .../waffle/actionCenter/wifi/WifiControl.qml | 2 +- .../ii/modules/waffle/bar/AppButton.qml | 2 +- .../waffle/looks/WBarAttachedPanelContent.qml | 54 ++---- ...FooterButton.qml => WBorderlessButton.qml} | 0 .../ii/modules/waffle/looks/WPane.qml | 58 +++++++ .../ii/modules/waffle/looks/WStackView.qml | 6 +- .../waffle/onScreenDisplay/OSDValue.qml | 65 ++++---- .../waffle/onScreenDisplay/WaffleOSD.qml | 5 +- .../ii/services/MprisController.qml | 25 +++ 23 files changed, 348 insertions(+), 130 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/next-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/next.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/pause-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/pause.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/play-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/play.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/previous-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/previous.svg create mode 100644 dots/.config/quickshell/ii/modules/waffle/actionCenter/MediaPaneContent.qml rename dots/.config/quickshell/ii/modules/waffle/looks/{WPanelFooterButton.qml => WBorderlessButton.qml} (100%) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/next-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/next-filled.svg new file mode 100644 index 000000000..d3741963d --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/next-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/next.svg b/dots/.config/quickshell/ii/assets/icons/fluent/next.svg new file mode 100644 index 000000000..26bdcb05e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/pause-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/pause-filled.svg new file mode 100644 index 000000000..595435fe7 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/pause-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/pause.svg b/dots/.config/quickshell/ii/assets/icons/fluent/pause.svg new file mode 100644 index 000000000..a334eb60d --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/pause.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/play-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/play-filled.svg new file mode 100644 index 000000000..ae2a12370 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/play-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/play.svg b/dots/.config/quickshell/ii/assets/icons/fluent/play.svg new file mode 100644 index 000000000..777cf8903 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/play.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/previous-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/previous-filled.svg new file mode 100644 index 000000000..b994af7e1 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/previous-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/previous.svg b/dots/.config/quickshell/ii/assets/icons/fluent/previous.svg new file mode 100644 index 000000000..bb61b7779 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/previous.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml b/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml index fa60e0474..3cd620834 100644 --- a/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml +++ b/dots/.config/quickshell/ii/modules/ii/mediaControls/MediaControls.qml @@ -16,7 +16,7 @@ Scope { id: root property bool visible: false readonly property MprisPlayer activePlayer: MprisController.activePlayer - readonly property var realPlayers: Mpris.players.values.filter(player => isRealPlayer(player)) + readonly property var realPlayers: MprisController.players readonly property var meaningfulPlayers: filterDuplicatePlayers(realPlayers) readonly property real osdWidth: Appearance.sizes.osdWidth readonly property real widgetWidth: Appearance.sizes.mediaControlsWidth @@ -24,27 +24,6 @@ Scope { property real popupRounding: Appearance.rounding.screenRounding - Appearance.sizes.hyprlandGapsOut + 1 property list visualizerPoints: [] - property bool hasPlasmaIntegration: false - Process { - id: plasmaIntegrationAvailabilityCheckProc - running: true - command: ["bash", "-c", "command -v plasma-browser-integration-host"] - onExited: (exitCode, exitStatus) => { - root.hasPlasmaIntegration = (exitCode === 0); - } - } - function isRealPlayer(player) { - if (!Config.options.media.filterDuplicatePlayers) { - return true; - } - return ( - // Remove unecessary native buses from browsers if there's plasma integration - !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && - // playerctld just copies other buses and we don't need duplicates - !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && - // Non-instance mpd bus - !(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd'))); - } function filterDuplicatePlayers(players) { let filtered = []; let used = new Set(); @@ -96,7 +75,7 @@ Scope { id: mediaControlsLoader active: GlobalStates.mediaControlsOpen onActiveChanged: { - if (!mediaControlsLoader.active && Mpris.players.values.filter(player => isRealPlayer(player)).length === 0) { + if (!mediaControlsLoader.active && root.realPlayers.length === 0) { GlobalStates.mediaControlsOpen = false; } } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml index 4d45df92e..284f72013 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -12,28 +13,52 @@ import qs.modules.waffle.actionCenter.mainPage WBarAttachedPanelContent { id: root - contentItem: WStackView { - id: stackView - anchors.fill: parent - implicitWidth: initItem.implicitWidth - implicitHeight: initItem.implicitHeight - - initialItem: PageColumn { - id: initItem - MainPageBody {} - Separator {} - MainPageFooter {} + readonly property bool barAtBottom: Config.options.waffles.bar.bottom + + contentItem: Column { + // This somewhat sophisticated anchoring is needed to make opening anim not jump abruptly when stuff appear + anchors { + left: parent.left + right: parent.right + top: root.barAtBottom ? undefined : parent.top + bottom: root.barAtBottom ? parent.bottom : undefined + margins: root.visualMargin } + spacing: 12 - Component.onCompleted: { - ActionCenterContext.stackView = this + WPane { + visible: MprisController.activePlayer != null && MprisController.isRealPlayer(MprisController.activePlayer) + anchors { + left: parent.left + right: parent.right + } + contentItem: MediaPaneContent {} } + WPane { + contentItem: WStackView { + id: stackView + anchors.fill: parent + implicitWidth: initItem.implicitWidth + implicitHeight: initItem.implicitHeight - MouseArea { - anchors.fill: parent - acceptedButtons: Qt.BackButton - onClicked: { - ActionCenterContext.back() + initialItem: PageColumn { + id: initItem + MainPageBody {} + Separator {} + MainPageFooter {} + } + + Component.onCompleted: { + ActionCenterContext.stackView = this; + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.BackButton + onClicked: { + ActionCenterContext.back(); + } + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/MediaPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/MediaPaneContent.qml new file mode 100644 index 000000000..5c4c6df97 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/MediaPaneContent.qml @@ -0,0 +1,156 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Rectangle { + id: root + implicitHeight: 176 + implicitWidth: 358 + color: Looks.colors.bgPanelBody + anchors.fill: parent + + readonly property var activePlayer: MprisController.activePlayer + + Column { + anchors { + fill: parent + leftMargin: 23 + rightMargin: 23 + topMargin: 16 + bottomMargin: 20 + } + spacing: 25 + + AppInfoRow { + anchors { + left: parent.left + right: parent.right + } + } + + TrackInfoRow { + anchors { + left: parent.left + right: parent.right + } + } + + ControlButtonsRow { + anchors.horizontalCenter: parent.horizontalCenter + } + } + + component AppInfoRow: RowLayout { + id: appInfo + spacing: 8 + + property var desktopEntry: { + const desktopEntryString = root.activePlayer?.desktopEntry ?? ""; + return DesktopEntries.byId(desktopEntryString); + } + + FluentIcon { + implicitSize: 20 + icon: appInfo.desktopEntry?.icon || "music-note-2" + monochrome: !appInfo.desktopEntry?.icon + } + + WText { + Layout.fillWidth: true + text: appInfo.desktopEntry?.name ?? Translation.tr("Media") + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + } + } + + component TrackInfoRow: RowLayout { + spacing: 16 + + ColumnLayout { + id: trackInfo + Layout.fillWidth: true + spacing: 0 + + WText { + Layout.fillWidth: true + font.weight: Looks.font.weight.strong + font.pixelSize: Looks.font.pixelSize.large + elide: Text.ElideRight + text: StringUtils.cleanMusicTitle(root.activePlayer?.trackTitle) || Translation.tr("Unknown Title") + } + + WText { + Layout.fillWidth: true + elide: Text.ElideRight + text: root.activePlayer?.trackArtist || Translation.tr("Unknown Artist") + } + } + + StyledImage { + id: artImage + Layout.preferredWidth: 58 + Layout.preferredHeight: trackInfo.implicitHeight + source: MprisController.activeTrack?.artUrl || "" + fillMode: Image.PreserveAspectFit + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Item { + width: artImage.width + height: artImage.height + Rectangle { + anchors.centerIn: parent + width: artImage.paintedWidth + height: artImage.paintedHeight + radius: Looks.radius.medium + } + } + } + } + } + + component ControlButtonsRow: RowLayout { + spacing: 26 + + MediaControlButton { + iconName: "previous" + enabled: root.activePlayer?.canGoPrevious ?? false + onClicked: root.activePlayer?.previous() + } + MediaControlButton { + readonly property bool playing: root.activePlayer?.isPlaying ?? false + iconName: playing ? "pause" : "play" + enabled: (playing && root.activePlayer?.canPause) || (!playing && root.activePlayer?.canPlay) + onClicked: root.activePlayer?.togglePlaying() + } + MediaControlButton { + iconName: "next" + enabled: root.activePlayer?.canGoNext ?? false + onClicked: root.activePlayer?.next() + } + } + + component MediaControlButton: WBorderlessButton { + id: controlButton + required property string iconName + implicitHeight: 40 + implicitWidth: 40 + + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: controlButton.iconName + monochrome: true + filled: true + implicitSize: 18 + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml index 174b7851f..8fe33fa47 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml @@ -35,8 +35,8 @@ Scope { right: true } - implicitWidth: content.implicitWidth + content.visualMargin * 2 - implicitHeight: content.implicitHeight + content.visualMargin * 2 + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight HyprlandFocusGrab { id: focusGrab @@ -55,7 +55,6 @@ Scope { ActionCenterContent { id: content anchors.fill: parent - anchors.margins: visualMargin focus: true Keys.onPressed: event => { // Esc to close diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml index c8d3a036a..e0e6327a6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml @@ -110,7 +110,7 @@ Item { Quickshell.execDetached(["bash", "-c", Config.options.apps.bluetooth]); } } - WPanelFooterButton { + WBorderlessButton { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 12 diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml index 1190bd38e..3d5c8d882 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageFooter.qml @@ -11,7 +11,7 @@ import qs.modules.waffle.actionCenter FooterRectangle { // Battery button - WPanelFooterButton { + WBorderlessButton { visible: Battery.available anchors.verticalCenter: parent.verticalCenter anchors.left: parent.left @@ -36,7 +36,7 @@ FooterRectangle { } // Settings button - WPanelFooterButton { + WBorderlessButton { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 12 diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml index 6f07462be..e6c91292a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml @@ -100,7 +100,7 @@ Item { Quickshell.execDetached(["bash", "-c", Config.options.apps.network]); } } - WPanelFooterButton { + WBorderlessButton { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 12 diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml index 9d8d323fe..20af517fe 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml @@ -54,7 +54,7 @@ BarButton { contentItem: Item { id: contentItem - anchors.centerIn: background + anchors.centerIn: root.background implicitHeight: iconWidget.implicitHeight implicitWidth: iconWidget.implicitWidth diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml index 3035a6511..657897ef1 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml @@ -12,8 +12,6 @@ Item { signal closed() - property alias border: borderRect - property alias borderColor: borderRect.border.color required property Item contentItem property real visualMargin: 12 property int closeAnimDuration: 150 @@ -24,32 +22,19 @@ Item { readonly property bool barAtBottom: Config.options.waffles.bar.bottom - implicitHeight: borderRect.implicitHeight - implicitWidth: borderRect.implicitWidth - - WRectangularShadow { - target: borderRect - } - - Rectangle { - id: borderRect - z: 1 - - color: "transparent" - radius: Looks.radius.large - border.color: Looks.colors.bg2Border - border.width: 1 - implicitWidth: contentItem.implicitWidth + border.width * 2 - implicitHeight: contentItem.implicitHeight + border.width * 2 + implicitHeight: contentItem.implicitHeight + visualMargin * 2 + implicitWidth: contentItem.implicitWidth + visualMargin * 2 + Item { + id: panelContent anchors { left: parent.left right: parent.right top: root.barAtBottom ? undefined : parent.top bottom: root.barAtBottom ? parent.bottom : undefined // Opening anim - bottomMargin: root.barAtBottom ? sourceEdgeMargin : 0 - topMargin: root.barAtBottom ? 0 : sourceEdgeMargin + bottomMargin: root.barAtBottom ? sourceEdgeMargin : root.visualMargin + topMargin: root.barAtBottom ? root.visualMargin : sourceEdgeMargin } Component.onCompleted: { @@ -59,9 +44,9 @@ Item { property real sourceEdgeMargin: -(implicitHeight + root.visualMargin) PropertyAnimation { id: openAnim - target: borderRect + target: panelContent property: "sourceEdgeMargin" - to: 0 + to: root.visualMargin duration: 200 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn @@ -69,7 +54,7 @@ Item { SequentialAnimation { id: closeAnim PropertyAnimation { - target: borderRect + target: panelContent property: "sourceEdgeMargin" to: -(implicitHeight + root.visualMargin) duration: root.closeAnimDuration @@ -82,23 +67,8 @@ Item { } } } - } - - Item { - id: contentArea - z: 0 - anchors.fill: borderRect - anchors.margins: borderRect.border.width - implicitWidth: contentItem.implicitWidth - implicitHeight: contentItem.implicitHeight - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: contentArea.width - height: contentArea.height - radius: borderRect.radius - borderRect.border.width - } - } + implicitWidth: root.contentItem.implicitWidth + implicitHeight: root.contentItem.implicitHeight children: [root.contentItem] - } + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelFooterButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/looks/WPanelFooterButton.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml new file mode 100644 index 000000000..806afcd37 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -0,0 +1,58 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks + +Item { + id: root + required property Item contentItem + property real radius: Looks.radius.large + property alias border: borderRect + property alias borderColor: borderRect.border.color + + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + + WRectangularShadow { + target: borderRect + } + + Rectangle { + id: borderRect + z: 1 + + color: "transparent" + radius: root.radius + border.color: Looks.colors.bg2Border + border.width: 1 + implicitWidth: contentItem.implicitWidth + border.width * 2 + implicitHeight: contentItem.implicitHeight + border.width * 2 + anchors.fill: contentRect + anchors.margins: -border.width + } + + Rectangle { + id: contentRect + anchors.centerIn: parent + z: 0 + + color: Looks.colors.bgPanelFooterBase + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + id: contentAreaMask + width: contentRect.width + height: contentRect.height + radius: root.radius - borderRect.border.width + } + } + children: [root.contentItem] + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WStackView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WStackView.qml index 218ec15fa..b566c91ab 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WStackView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WStackView.qml @@ -11,11 +11,7 @@ StackView { property list fadeBezierCurve: Looks.transition.easing.bezierCurve.easeInOut clip: true - property alias color: background.color - background: Rectangle { - id: background - color: Looks.colors.bgPanelFooterBase - } + background: null pushEnter: Transition { XAnimator { diff --git a/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/OSDValue.qml b/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/OSDValue.qml index 8d48a6f92..32db99479 100644 --- a/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/OSDValue.qml +++ b/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/OSDValue.qml @@ -13,7 +13,6 @@ WBarAttachedPanelContent { required property string iconName property real value property bool showNumber: true - borderColor: Looks.colors.ambientShadow property Timer timer: Timer { id: autoCloseTimer @@ -21,44 +20,48 @@ WBarAttachedPanelContent { interval: Config.options.osd.timeout repeat: false onTriggered: { - root.close() + root.close(); } } - contentItem: Rectangle { + contentItem: WPane { anchors.centerIn: parent - color: Looks.colors.bg1Base - radius: Looks.radius.medium - implicitWidth: root.showNumber ? 192 : 170 - implicitHeight: 46 - - RowLayout { - id: contentRow - anchors.fill: parent - anchors.margins: 12 + borderColor: Looks.colors.ambientShadow - spacing: 12 + contentItem: Item { + // color: Looks.colors.bg1Base + // radius: Looks.radius.medium + implicitWidth: root.showNumber ? 192 : 170 + implicitHeight: 46 - FluentIcon { - Layout.alignment: Qt.AlignVCenter - icon: root.iconName - implicitSize: 18 - } + RowLayout { + id: contentRow + anchors.fill: parent + anchors.margins: 12 - WProgressBar { - id: progressBar - value: root.value - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - Layout.rightMargin: root.showNumber ? 0 : 3 - } + spacing: 12 - WTextWithFixedWidth { - visible: root.showNumber - text: Math.round(root.value * 100) - // longestText: "100" - implicitWidth: 16 - horizontalAlignment: Text.AlignHCenter + FluentIcon { + Layout.alignment: Qt.AlignVCenter + icon: root.iconName + implicitSize: 18 + } + + WProgressBar { + id: progressBar + value: root.value + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + Layout.rightMargin: root.showNumber ? 0 : 3 + } + + WTextWithFixedWidth { + visible: root.showNumber + text: Math.round(root.value * 100) + // longestText: "100" + implicitWidth: 16 + horizontalAlignment: Text.AlignHCenter + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/WaffleOSD.qml b/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/WaffleOSD.qml index f2f06b2ae..569ed4f44 100644 --- a/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/WaffleOSD.qml +++ b/dots/.config/quickshell/ii/modules/waffle/onScreenDisplay/WaffleOSD.qml @@ -104,13 +104,12 @@ Scope { item: osdIndicatorLoader } - implicitWidth: osdIndicatorLoader.implicitWidth + osdIndicatorLoader.item.visualMargin * 2 - implicitHeight: osdIndicatorLoader.implicitHeight + osdIndicatorLoader.item.visualMargin * 2 + implicitWidth: osdIndicatorLoader.implicitWidth + implicitHeight: osdIndicatorLoader.implicitHeight Loader { id: osdIndicatorLoader anchors.fill: parent - anchors.margins: item.visualMargin source: root.indicators.find(i => i.id === root.currentIndicator)?.sourceUrl Connections { diff --git a/dots/.config/quickshell/ii/services/MprisController.qml b/dots/.config/quickshell/ii/services/MprisController.qml index 1dc42da2b..02151f1c1 100644 --- a/dots/.config/quickshell/ii/services/MprisController.qml +++ b/dots/.config/quickshell/ii/services/MprisController.qml @@ -9,12 +9,14 @@ import QtQuick import Quickshell import Quickshell.Io import Quickshell.Services.Mpris +import qs.modules.common /** * A service that provides easy access to the active Mpris player. */ Singleton { id: root; + property list players: Mpris.players.values.filter(player => isRealPlayer(player)); property MprisPlayer trackedPlayer: null; property MprisPlayer activePlayer: trackedPlayer ?? Mpris.players.values[0] ?? null; signal trackChanged(reverse: bool); @@ -23,6 +25,29 @@ Singleton { property var activeTrack; + property bool hasPlasmaIntegration: false + Process { + id: plasmaIntegrationAvailabilityCheckProc + running: true + command: ["bash", "-c", "command -v plasma-browser-integration-host"] + onExited: (exitCode, exitStatus) => { + root.hasPlasmaIntegration = (exitCode === 0); + } + } + function isRealPlayer(player) { + if (!Config.options.media.filterDuplicatePlayers) { + return true; + } + return ( + // Remove unecessary native buses from browsers if there's plasma integration + !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.firefox')) && !(hasPlasmaIntegration && player.dbusName.startsWith('org.mpris.MediaPlayer2.chromium')) && + // playerctld just copies other buses and we don't need duplicates + !player.dbusName?.startsWith('org.mpris.MediaPlayer2.playerctld') && + // Non-instance mpd bus + !(player.dbusName?.endsWith('.mpd') && !player.dbusName.endsWith('MediaPlayer2.mpd'))); + } + + // Original stuff from fox below Instantiator { model: Mpris.players; From 19ba7dac48f06848f168387d6011c1ab97677ba7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:38:50 +0100 Subject: [PATCH 003/154] overlay: recorder: fix open folder button missing text --- .../quickshell/ii/modules/ii/overlay/recorder/Recorder.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/ii/overlay/recorder/Recorder.qml b/dots/.config/quickshell/ii/modules/ii/overlay/recorder/Recorder.qml index 9efa37e60..812884127 100644 --- a/dots/.config/quickshell/ii/modules/ii/overlay/recorder/Recorder.qml +++ b/dots/.config/quickshell/ii/modules/ii/overlay/recorder/Recorder.qml @@ -3,6 +3,7 @@ import QtQuick import QtQuick.Layouts import Quickshell import qs +import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.ii.overlay From 25816662f8e744abb85f4b7f544e049c7c7d6260 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 23 Nov 2025 21:40:44 +0100 Subject: [PATCH 004/154] wactioncenter: fix wrong gap from bar --- .../ii/modules/waffle/actionCenter/ActionCenterContent.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml index 284f72013..d01502444 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml @@ -23,6 +23,7 @@ WBarAttachedPanelContent { top: root.barAtBottom ? undefined : parent.top bottom: root.barAtBottom ? parent.bottom : undefined margins: root.visualMargin + bottomMargin: 0 } spacing: 12 From 996579729d115b72c12ad50e7f763595d53f375d Mon Sep 17 00:00:00 2001 From: viridivn <42524580+viridivn@users.noreply.github.com> Date: Sun, 23 Nov 2025 16:03:56 -0500 Subject: [PATCH 005/154] no abbreviations --- dots/.config/quickshell/ii/services/Ai.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index 389fb3e78..d5ca44035 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -297,7 +297,7 @@ Singleton { "gemini-3-pro": aiModelComponent.createObject(this, { "name": "Gemini 3.0 Pro", "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nGoogle's most intelligent model with SOTA reasoning and multimodal understanding."), + "description": Translation.tr("Online | Google's model\nGoogle's most intelligent model with state-of-the-art reasoning and multimodal understanding."), "homepage": "https://aistudio.google.com", "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:streamGenerateContent", "model": "gemini-3-pro-preview", From 0703429393e17a834367722aa0d60c53dfb4f2a5 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 23 Nov 2025 22:27:17 +0100 Subject: [PATCH 006/154] Revert "Add Gemini 3 Pro to model list" --- dots/.config/quickshell/ii/services/Ai.qml | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index d5ca44035..ccd237de8 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -294,19 +294,6 @@ Singleton { "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", }), - "gemini-3-pro": aiModelComponent.createObject(this, { - "name": "Gemini 3.0 Pro", - "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nGoogle's most intelligent model with state-of-the-art reasoning and multimodal understanding."), - "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-pro-preview:streamGenerateContent", - "model": "gemini-3-pro-preview", - "requires_key": true, - "key_id": "gemini", - "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), - "api_format": "gemini", - }), "gemini-2.5-flash-lite": aiModelComponent.createObject(this, { "name": "Gemini 2.5 Flash-Lite", "icon": "google-gemini-symbolic", From e6f36114bd53c71448390626d6334ed6043c5b0c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:18:05 +0100 Subject: [PATCH 007/154] waffles: notif center: calendar header and focus --- .../ii/assets/icons/fluent/add-filled.svg | 1 + .../quickshell/ii/assets/icons/fluent/add.svg | 1 + .../ii/assets/icons/fluent/stop-filled.svg | 1 + .../ii/assets/icons/fluent/stop.svg | 1 + .../assets/icons/fluent/subtract-filled.svg | 1 + .../ii/assets/icons/fluent/subtract.svg | 1 + .../actionCenter/ActionCenterContent.qml | 15 ++-- .../actionCenter/WaffleActionCenter.qml | 13 +-- .../bluetooth/BluetoothControl.qml | 4 +- .../nightLight/NightLightControl.qml | 4 +- .../volumeControl/VolumeControl.qml | 8 +- .../waffle/actionCenter/wifi/WifiControl.qml | 4 +- .../{actionCenter => looks}/BodyRectangle.qml | 0 .../FooterMoreButton.qml | 0 .../FooterRectangle.qml | 2 +- .../ii/modules/waffle/looks/Looks.qml | 6 +- .../waffle/looks/WBarAttachedPanelContent.qml | 68 ++++++++++----- .../modules/waffle/looks/WBorderedButton.qml | 18 ++++ .../ii/modules/waffle/looks/WPane.qml | 4 +- .../WPanelPageColumn.qml} | 0 .../WPanelSeparator.qml} | 0 .../notificationCenter/CalendarHeader.qml | 51 +++++++++++ .../notificationCenter/CalendarView.qml | 23 +++++ .../waffle/notificationCenter/FocusFooter.qml | 87 +++++++++++++++++++ .../NotificationCenterContent.qml | 57 ++++++++++++ .../SmallBorderedIconButton.qml | 18 ++++ .../WaffleNotificationCenter.qml | 85 ++++++++++++++++++ .../quickshell/ii/services/DateTime.qml | 2 +- dots/.config/quickshell/ii/shell.qml | 4 +- 29 files changed, 420 insertions(+), 59 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/add-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/add.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/stop-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/stop.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/subtract-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/subtract.svg rename dots/.config/quickshell/ii/modules/waffle/{actionCenter => looks}/BodyRectangle.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/{actionCenter => looks}/FooterMoreButton.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/{actionCenter => looks}/FooterRectangle.qml (92%) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml rename dots/.config/quickshell/ii/modules/waffle/{actionCenter/PageColumn.qml => looks/WPanelPageColumn.qml} (100%) rename dots/.config/quickshell/ii/modules/waffle/{actionCenter/Separator.qml => looks/WPanelSeparator.qml} (100%) create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/WaffleNotificationCenter.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/add-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/add-filled.svg new file mode 100644 index 000000000..6b1a81835 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/add-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/add.svg b/dots/.config/quickshell/ii/assets/icons/fluent/add.svg new file mode 100644 index 000000000..c983f3518 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/add.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/stop-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/stop-filled.svg new file mode 100644 index 000000000..ef8a91225 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/stop-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/stop.svg b/dots/.config/quickshell/ii/assets/icons/fluent/stop.svg new file mode 100644 index 000000000..26eb3ea04 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/stop.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/subtract-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/subtract-filled.svg new file mode 100644 index 000000000..128a7f96b --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/subtract-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/subtract.svg b/dots/.config/quickshell/ii/assets/icons/fluent/subtract.svg new file mode 100644 index 000000000..3e1e2471c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/subtract.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml index d01502444..b69666660 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/ActionCenterContent.qml @@ -15,7 +15,7 @@ WBarAttachedPanelContent { readonly property bool barAtBottom: Config.options.waffles.bar.bottom - contentItem: Column { + contentItem: ColumnLayout { // This somewhat sophisticated anchoring is needed to make opening anim not jump abruptly when stuff appear anchors { left: parent.left @@ -28,24 +28,21 @@ WBarAttachedPanelContent { spacing: 12 WPane { - visible: MprisController.activePlayer != null && MprisController.isRealPlayer(MprisController.activePlayer) - anchors { - left: parent.left - right: parent.right - } + opacity: (MprisController.activePlayer != null && MprisController.isRealPlayer(MprisController.activePlayer)) ? 1 : 0 + Layout.fillWidth: true contentItem: MediaPaneContent {} } WPane { + Layout.fillWidth: true contentItem: WStackView { id: stackView - anchors.fill: parent implicitWidth: initItem.implicitWidth implicitHeight: initItem.implicitHeight - initialItem: PageColumn { + initialItem: WPanelPageColumn { id: initItem MainPageBody {} - Separator {} + WPanelSeparator {} MainPageFooter {} } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml index 8fe33fa47..56fbedda3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml @@ -15,12 +15,12 @@ Scope { target: GlobalStates function onSidebarLeftOpenChanged() { - if (GlobalStates.sidebarLeftOpen) barLoader.active = true; + if (GlobalStates.sidebarLeftOpen) panelLoader.active = true; } } Loader { - id: barLoader + id: panelLoader active: GlobalStates.sidebarLeftOpen sourceComponent: PanelWindow { id: panelWindow @@ -56,16 +56,9 @@ Scope { id: content anchors.fill: parent - focus: true - Keys.onPressed: event => { // Esc to close - if (event.key === Qt.Key_Escape) { - content.close() - } - } - onClosed: { GlobalStates.sidebarLeftOpen = false; - barLoader.active = false; + panelLoader.active = false; } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml index e0e6327a6..72f442b87 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml @@ -23,7 +23,7 @@ Item { Bluetooth.defaultAdapter.discovering = false; } - PageColumn { + WPanelPageColumn { anchors.fill: parent BodyRectangle { @@ -96,7 +96,7 @@ Item { } } - Separator {} + WPanelSeparator {} FooterRectangle { FooterMoreButton { diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/nightLight/NightLightControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/nightLight/NightLightControl.qml index 71630295c..591e56399 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/nightLight/NightLightControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/nightLight/NightLightControl.qml @@ -24,7 +24,7 @@ Item { Bluetooth.defaultAdapter.discovering = false; } - PageColumn { + WPanelPageColumn { anchors.fill: parent BodyRectangle { @@ -61,7 +61,7 @@ Item { } } - Separator {} + WPanelSeparator {} FooterRectangle {} } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml index b3e97a78e..52fe9ab3d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/volumeControl/VolumeControl.qml @@ -14,7 +14,7 @@ Item { id: root property bool output: true - PageColumn { + WPanelPageColumn { anchors.fill: parent BodyRectangle { @@ -48,7 +48,7 @@ Item { } } - Separator {} + WPanelSeparator {} FooterRectangle { WButton { @@ -103,7 +103,7 @@ Item { } } - Separator { + WPanelSeparator { visible: EasyEffects.available && root.output color: Looks.colors.bg2Hover } @@ -129,7 +129,7 @@ Item { onClicked: EasyEffects.enable() } - Separator { + WPanelSeparator { color: Looks.colors.bg2Hover } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml index e6c91292a..c90d06fc0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml @@ -19,7 +19,7 @@ Item { Network.rescanWifi(); } - PageColumn { + WPanelPageColumn { anchors.fill: parent BodyRectangle { @@ -86,7 +86,7 @@ Item { } } - Separator {} + WPanelSeparator {} FooterRectangle { FooterMoreButton { diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/looks/BodyRectangle.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/BodyRectangle.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/BodyRectangle.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterMoreButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/FooterMoreButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterMoreButton.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/FooterMoreButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/looks/FooterRectangle.qml similarity index 92% rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/FooterRectangle.qml index e3f7cd120..dcf4f519d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/FooterRectangle.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/FooterRectangle.qml @@ -12,6 +12,6 @@ Rectangle { Layout.fillWidth: true color: "transparent" - implicitWidth: 360 + implicitWidth: 358 implicitHeight: 47 } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 26ef9f101..0350f5004 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -20,7 +20,7 @@ Singleton { property real backgroundTransparency: 0.13 property real panelBackgroundTransparency: 0.12 property real panelLayerTransparency: root.dark ? 0.9 : 0.7 - property real contentTransparency: root.dark ? 0.9 : 0.5 + property real contentTransparency: root.dark ? 0.87 : 0.5 function applyBackgroundTransparency(col) { return ColorUtils.applyAlpha(col, 1 - root.backgroundTransparency) } @@ -41,8 +41,8 @@ Singleton { property color bg1Border: '#d7d7d7' property color bg2: "#FBFBFB" property color bg2Base: "#FBFBFB" - property color bg2Hover: "#FDFDFD" - property color bg2Active: "#FDFDFD" + property color bg2Hover: '#ffffff' + property color bg2Active: '#eeeeee' property color bg2Border: '#cdcdcd' property color subfg: "#5C5C5C" property color fg: "#000000" diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml index 657897ef1..97ee49d1e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBarAttachedPanelContent.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Qt5Compat.GraphicalEffects @@ -10,11 +11,13 @@ import qs.modules.waffle.looks Item { id: root - signal closed() + signal closed required property Item contentItem property real visualMargin: 12 property int closeAnimDuration: 150 + property bool revealFromSides: false + property bool revealFromLeft: true function close() { closeAnim.start(); @@ -25,16 +28,25 @@ Item { implicitHeight: contentItem.implicitHeight + visualMargin * 2 implicitWidth: contentItem.implicitWidth + visualMargin * 2 + focus: true + Keys.onPressed: event => { // Esc to close + if (event.key === Qt.Key_Escape) { + content.close(); + } + } + Item { id: panelContent anchors { - left: parent.left - right: parent.right - top: root.barAtBottom ? undefined : parent.top - bottom: root.barAtBottom ? parent.bottom : undefined + left: (root.revealFromSides && !root.revealFromLeft) ? undefined : parent.left + right: (root.revealFromSides && root.revealFromLeft) ? undefined : parent.right + top: (!root.revealFromSides && root.barAtBottom) ? undefined : parent.top + bottom: (!root.revealFromSides && !root.barAtBottom) ? undefined : parent.bottom // Opening anim - bottomMargin: root.barAtBottom ? sourceEdgeMargin : root.visualMargin - topMargin: root.barAtBottom ? root.visualMargin : sourceEdgeMargin + bottomMargin: (!root.revealFromSides && root.barAtBottom) ? sourceEdgeMargin : root.visualMargin + topMargin: (!root.revealFromSides && !root.barAtBottom) ? sourceEdgeMargin : root.visualMargin + leftMargin: (root.revealFromSides && root.revealFromLeft) ? sideEdgeMargin : root.visualMargin + rightMargin: (root.revealFromSides && !root.revealFromLeft) ? sideEdgeMargin : root.visualMargin } Component.onCompleted: { @@ -42,24 +54,22 @@ Item { } property real sourceEdgeMargin: -(implicitHeight + root.visualMargin) - PropertyAnimation { + property real sideEdgeMargin: -(implicitWidth + root.visualMargin) + OpenAnim { id: openAnim - target: panelContent - property: "sourceEdgeMargin" - to: root.visualMargin - duration: 200 - easing.type: Easing.BezierSpline - easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + properties: "sourceEdgeMargin, sideEdgeMargin" } SequentialAnimation { id: closeAnim - PropertyAnimation { - target: panelContent - property: "sourceEdgeMargin" - to: -(implicitHeight + root.visualMargin) - duration: root.closeAnimDuration - easing.type: Easing.BezierSpline - easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + ParallelAnimation { + CloseAnim { + property: "sourceEdgeMargin" + to: -(implicitHeight + root.visualMargin) + } + CloseAnim { + property: "sideEdgeMargin" + to: -(implicitWidth + root.visualMargin) + } } ScriptAction { script: { @@ -70,5 +80,19 @@ Item { implicitWidth: root.contentItem.implicitWidth implicitHeight: root.contentItem.implicitHeight children: [root.contentItem] - } + } + + component OpenAnim: PropertyAnimation { + target: panelContent + to: root.visualMargin + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + component CloseAnim: PropertyAnimation { + target: panelContent + duration: root.closeAnimDuration + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml new file mode 100644 index 000000000..74cb35f8b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Controls +import Quickshell +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WButton { + id: root + + colBackground: Looks.colors.bg2 + colBackgroundHover: Looks.colors.bg2Hover + colBackgroundActive: Looks.colors.bg2Active + border.color: Looks.colors.bg2Border + border.width: root.pressed ? 2 : 1 + + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index 806afcd37..68e836b66 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -15,8 +15,8 @@ Item { property alias border: borderRect property alias borderColor: borderRect.border.color - implicitWidth: contentItem.implicitWidth - implicitHeight: contentItem.implicitHeight + implicitWidth: borderRect.implicitWidth + implicitHeight: borderRect.implicitHeight WRectangularShadow { target: borderRect diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelPageColumn.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/PageColumn.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WPanelPageColumn.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelSeparator.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/actionCenter/Separator.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WPanelSeparator.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml new file mode 100644 index 000000000..79543846b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml @@ -0,0 +1,51 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +FooterRectangle { + id: root + + property bool collapsed + implicitWidth: 334 + + RowLayout { + anchors { + fill: parent + leftMargin: 16 + rightMargin: 16 + topMargin: 12 + bottomMargin: 12 + } + + WText { + Layout.fillWidth: true + font.pixelSize: Looks.font.pixelSize.large + text: DateTime.collapsedCalendarFormat + } + + WBorderedButton { + implicitWidth: 24 + implicitHeight: 24 + padding: 0 + onClicked: root.collapsed = !root.collapsed + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + implicitSize: 12 + icon: "chevron-down" + rotation: root.collapsed ? 180 : 0 + Behavior on rotation { + animation: Looks.transition.rotate.createObject(this) + } + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml new file mode 100644 index 000000000..335cecf5a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml @@ -0,0 +1,23 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +BodyRectangle { + id: root + + property bool collapsed + implicitHeight: collapsed ? 0 : 400 // For now + implicitWidth: 334 + + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml new file mode 100644 index 000000000..95fc529c2 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml @@ -0,0 +1,87 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +FooterRectangle { + Layout.fillWidth: true + implicitWidth: 334 + + RowLayout { + anchors { + fill: parent + leftMargin: 16 + rightMargin: 16 + topMargin: 12 + bottomMargin: 12 + } + spacing: 0 + + SmallBorderedIconButton { + visible: !TimerService.pomodoroRunning + icon.name: "subtract" + onClicked: Config.options.time.pomodoro.focus -= 300 // 5 mins + } + + WTextWithFixedWidth { + visible: !TimerService.pomodoroRunning + implicitWidth: 81 + horizontalAlignment: Text.AlignHCenter + color: Looks.colors.subfg + text: Translation.tr("%1 mins").arg(`${TimerService.focusTime / 60}`) + } + + SmallBorderedIconButton { + visible: !TimerService.pomodoroRunning + icon.name: "add" + onClicked: Config.options.time.pomodoro.focus += 300 // 5 mins + } + + WText { + visible: TimerService.pomodoroRunning + font.pixelSize: Looks.font.pixelSize.large + text: Translation.tr("Focusing") + } + + Item { + Layout.fillWidth: true + } + + SmallBorderedIconButton { + leftPadding: 12 + rightPadding: 12 + implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding + + onClicked: { + if (TimerService.pomodoroRunning) { + TimerService.togglePomodoro() + TimerService.resetPomodoro() + } else { + TimerService.togglePomodoro() + Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarRight", "toggle"]); + } + } + + contentItem: Row { + id: focusButtonContent + spacing: 4 + FluentIcon { + icon: TimerService.pomodoroRunning ? "stop" : "play" + filled: true + implicitSize: 14 + anchors.verticalCenter: parent.verticalCenter + } + WText { + anchors.verticalCenter: parent.verticalCenter + text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus") + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml new file mode 100644 index 000000000..44ceef6ba --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -0,0 +1,57 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Qt.labs.synchronizer +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WBarAttachedPanelContent { + id: root + + readonly property bool barAtBottom: Config.options.waffles.bar.bottom + revealFromSides: true + revealFromLeft: false + + property bool collapsed: false + + contentItem: Column { + anchors { + horizontalCenter: parent.horizontalCenter + top: root.barAtBottom ? undefined : parent.top + bottom: root.barAtBottom ? parent.bottom : undefined + } + spacing: 12 + + WPane { + contentItem: ColumnLayout { + spacing: 0 + CalendarHeader { + Layout.fillWidth: true + Synchronizer on collapsed { + property alias source: root.collapsed + } + } + + WPanelSeparator { visible: !root.collapsed } + + CalendarView { + Layout.fillWidth: true + Synchronizer on collapsed { + property alias source: root.collapsed + } + } + + WPanelSeparator {} + + FocusFooter { + Layout.fillWidth: true + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml new file mode 100644 index 000000000..1236779a7 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml @@ -0,0 +1,18 @@ +import QtQuick +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks + +WBorderedButton { + id: root + implicitWidth: 24 + implicitHeight: 24 + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + implicitSize: 12 + icon: root.icon.name + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WaffleNotificationCenter.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WaffleNotificationCenter.qml new file mode 100644 index 000000000..4caed7664 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WaffleNotificationCenter.qml @@ -0,0 +1,85 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +Scope { + id: root + + Connections { + target: GlobalStates + + function onSidebarRightOpenChanged() { + if (GlobalStates.sidebarRightOpen) panelLoader.active = true; + } + } + + Loader { + id: panelLoader + active: GlobalStates.sidebarRightOpen + sourceComponent: PanelWindow { + id: panelWindow + exclusiveZone: 0 + WlrLayershell.namespace: "quickshell:wNotificationCenter" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + bottom: true + top: true + right: true + } + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + HyprlandFocusGrab { + id: focusGrab + active: true + windows: [panelWindow] + onCleared: content.close(); + } + + Connections { + target: GlobalStates + function onSidebarRightOpenChanged() { + if (!GlobalStates.sidebarRightOpen) content.close(); + } + } + + NotificationCenterContent { + id: content + anchors.fill: parent + + onClosed: { + GlobalStates.sidebarRightOpen = false; + panelLoader.active = false; + } + } + } + } + + function toggleOpen() { + GlobalStates.sidebarRightOpen = !GlobalStates.sidebarRightOpen; + } + + IpcHandler { + target: "sidebarRight" + + function toggle() { + root.toggleOpen(); + } + } + + GlobalShortcut { + name: "sidebarRightToggle" + description: "Toggles notification center on press" + + onPressed: root.toggleOpen(); + } +} diff --git a/dots/.config/quickshell/ii/services/DateTime.qml b/dots/.config/quickshell/ii/services/DateTime.qml index 514da212b..e1a8300d4 100644 --- a/dots/.config/quickshell/ii/services/DateTime.qml +++ b/dots/.config/quickshell/ii/services/DateTime.qml @@ -22,7 +22,7 @@ Singleton { property string shortDate: Qt.locale().toString(clock.date, Config.options?.time.shortDateFormat ?? "dd/MM") property string date: Qt.locale().toString(clock.date, Config.options?.time.dateWithYearFormat ?? "dd/MM/yyyy") property string longDate: Qt.locale().toString(clock.date, Config.options?.time.dateFormat ?? "dddd, dd/MM") - property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dd MMMM yyyy") + property string collapsedCalendarFormat: Qt.locale().toString(clock.date, "dddd, MMMM dd") property string uptime: "0h, 0m" Timer { diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index b8193812f..6f401c90f 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -31,6 +31,7 @@ import qs.modules.ii.wallpaperSelector import qs.modules.waffle.actionCenter import qs.modules.waffle.background import qs.modules.waffle.bar +import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay import QtQuick @@ -79,6 +80,7 @@ ShellRoot { PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } PanelLoader { identifier: "wBar"; component: WaffleBar {} } PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } + PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } ReloadPopup {} @@ -92,7 +94,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", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiSidebarRight", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From cecb476ed3b164c4d648bf0c56460856c877de4f Mon Sep 17 00:00:00 2001 From: "Celestial.y" Date: Mon, 24 Nov 2025 23:38:03 +0800 Subject: [PATCH 008/154] Revert the "waybar" note modification 68c159f210b91f75113d454aab6c5866322334f4 --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index ff34ca061..b0ad4b9c4 100644 --- a/.github/README.md +++ b/.github/README.md @@ -50,7 +50,7 @@ | [Quickshell](https://quickshell.outfoxxed.me/) | A QtQuick-based widget system, used for the status bar, sidebars, etc. | | Others | See [deps-info.md](https://github.com/end-4/dots-hyprland/blob/main/sdata/deps-info.md) | - **Note: No Waybar here. Please stop calling every bar "Waybar".** + _THERE IS NO WAYBAR STOP FUCKING CALLING EVERY BAR WAYBAR_ From 1836a2ff1c7b648bedb433709dc523dcc5f30c84 Mon Sep 17 00:00:00 2001 From: Moeta Yuko Date: Mon, 24 Nov 2025 12:51:21 +0000 Subject: [PATCH 009/154] brightness: use connector name to distinguish different monitors One may have multiple monitors of the same model --- dots/.config/quickshell/ii/services/Brightness.qml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index 465d8729c..7c25f145c 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -58,7 +58,7 @@ Singleton { if (data.startsWith("Display ")) { const lines = data.split("\n").map(l => l.trim()); root.ddcMonitors.push({ - model: lines.find(l => l.startsWith("Monitor:")).split(":")[2], + name: lines.find(l => l.startsWith("DRM connector:")).split("-").slice(1).join('-'), busNum: lines.find(l => l.startsWith("I2C bus:")).split("/dev/i2c-")[1] }); } @@ -76,11 +76,11 @@ Singleton { required property ShellScreen screen readonly property bool isDdc: { - const match = root.ddcMonitors.find(m => m.model === screen.model && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); + const match = root.ddcMonitors.find(m => m.name === screen.name && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); return !!match; } readonly property string busNum: { - const match = root.ddcMonitors.find(m => m.model === screen.model && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); + const match = root.ddcMonitors.find(m => m.name === screen.name && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); return match?.busNum ?? ""; } property int rawMaxBrightness: 100 @@ -215,7 +215,7 @@ Singleton { Process { id: screenshotProc - command: ["bash", "-c", + command: ["bash", "-c", `mkdir -p '${StringUtils.shellSingleQuoteEscape(root.screenshotDir)}'` + ` && grim -o '${StringUtils.shellSingleQuoteEscape(screenScope.screenName)}' -` + ` | magick png:- -colorspace Gray -format "%[fx:mean*100]" info:` From 5242373db5e35198fe102482dc264fcfd970e89f Mon Sep 17 00:00:00 2001 From: Moeta Yuko Date: Mon, 24 Nov 2025 18:22:36 +0000 Subject: [PATCH 010/154] brightness: adjust DDCCI initialize timings * Delay DDCCI monitor initialization until after `ddcutil detect` to ensure `isDdc`, `busNum`, and `ddcMonitors` are properly set. * Run `ddcutil getvcp` sequentially to avoid failures from concurrent executions. --- .../quickshell/ii/services/Brightness.qml | 36 ++++++++++--------- 1 file changed, 19 insertions(+), 17 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Brightness.qml b/dots/.config/quickshell/ii/services/Brightness.qml index 7c25f145c..f2f4f0a7e 100644 --- a/dots/.config/quickshell/ii/services/Brightness.qml +++ b/dots/.config/quickshell/ii/services/Brightness.qml @@ -48,6 +48,16 @@ Singleton { ddcProc.running = true; } + function initializeMonitor(i: int): void { + if (i >= monitors.length) + return; + monitors[i].initialize(); + } + + function ddcDetectFinished(): void { + initializeMonitor(0); + } + Process { id: ddcProc @@ -64,7 +74,7 @@ Singleton { } } } - onExited: root.ddcMonitorsChanged() + onExited: root.ddcDetectFinished() } Process { @@ -75,14 +85,8 @@ Singleton { id: monitor required property ShellScreen screen - readonly property bool isDdc: { - const match = root.ddcMonitors.find(m => m.name === screen.name && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); - return !!match; - } - readonly property string busNum: { - const match = root.ddcMonitors.find(m => m.name === screen.name && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); - return match?.busNum ?? ""; - } + property bool isDdc + property string busNum property int rawMaxBrightness: 100 property real brightness property real brightnessMultiplier: 1.0 @@ -110,6 +114,9 @@ Singleton { function initialize() { monitor.ready = false; + const match = root.ddcMonitors.find(m => m.name === screen.name && !root.monitors.slice(0, root.monitors.indexOf(this)).some(mon => mon.busNum === m.busNum)); + isDdc = !!match; + busNum = match?.busNum ?? ""; initProc.command = isDdc ? ["ddcutil", "-b", busNum, "getvcp", "10", "--brief"] : ["sh", "-c", `echo "a b c $(brightnessctl g) $(brightnessctl m)"`]; initProc.running = true; } @@ -123,6 +130,9 @@ Singleton { monitor.ready = true; } } + onExited: (exitCode, exitStatus) => { + initializeMonitor(root.monitors.indexOf(monitor) + 1); + } } // We need a delay for DDC monitors because they can be quite slow and might act weird with rapid changes @@ -157,14 +167,6 @@ Singleton { function setBrightnessMultiplier(value: real): void { monitor.brightnessMultiplier = value; } - - Component.onCompleted: { - initialize(); - } - - onBusNumChanged: { - initialize(); - } } Component { From 90539274803dc3689bd418c1000ef40baed67efa Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 24 Nov 2025 22:45:54 +0100 Subject: [PATCH 011/154] wnotificationcenter: somewhat working calendar --- .../waffle/looks/WBorderlessButton.qml | 2 + .../ii/modules/waffle/looks/WButton.qml | 4 +- .../notificationCenter/CalendarHeader.qml | 2 +- .../notificationCenter/CalendarView.qml | 103 +++++++++++++++++- .../waffle/notificationCenter/FocusFooter.qml | 2 +- .../NotificationCenterContent.qml | 2 +- 6 files changed, 108 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml index 0f0d99ad9..7f3546a6b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderlessButton.qml @@ -25,8 +25,10 @@ Button { return root.colBackground } } + property alias radius: background.radius background: Rectangle { + id: background radius: Looks.radius.medium color: root.color } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index 69f255ad9..18fe0244b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -23,7 +23,7 @@ Button { if (root.checked) { if (root.down) { return root.colBackgroundToggledActive; - } else if (root.hovered && !root.down) { + } else if (root.hovered) { return root.colBackgroundToggledHover; } else { return root.colBackgroundToggled; @@ -31,7 +31,7 @@ Button { } if (root.down) { return root.colBackgroundActive; - } else if (root.hovered && !root.down) { + } else if (root.hovered) { return root.colBackgroundHover; } else { return root.colBackground; diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml index 79543846b..3e177f2fa 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml @@ -13,7 +13,7 @@ FooterRectangle { id: root property bool collapsed - implicitWidth: 334 + implicitWidth: 0 RowLayout { anchors { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml index 335cecf5a..07228590c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml @@ -12,12 +12,111 @@ import qs.modules.waffle.looks BodyRectangle { id: root + // State property bool collapsed - implicitHeight: collapsed ? 0 : 400 // For now - implicitWidth: 334 + + // Sizes + property int _rowsPerMonth: 6 + property real viewHeight: (_rowsPerMonth * buttonSize) + ((_rowsPerMonth - 1) * buttonSpacing) + property real buttonSize: 40 + property real buttonSpacing: 2 + property real spacePerExtraRow: buttonSize + buttonSpacing + + implicitWidth: currentMonthGrid.implicitWidth + implicitHeight: collapsed ? 0 : viewHeight + opacity: implicitHeight > 0 ? 1 : 0 Behavior on implicitHeight { animation: Looks.transition.enter.createObject(this) } + // Month stuff + property real targetMonthDiff: 0 + property real monthDiff: targetMonthDiff + property int focusedMonthDiff: monthDiff // whole part of monthDiff + property int currentMonth: DateTime.clock.date.getMonth() + 1 // 0-indexed -> 1-indexed + property int currentYear: DateTime.clock.date.getFullYear() + + clip: true + property list monthGrids: [previousPreviousMonthGrid, previousMonthGrid, currentMonthGrid, nextMonthGrid, nextNextMonthGrid] + ColumnLayout { + spacing: 0 + y: { + const origin = - currentMonthGrid.y; + const diff = root.monthDiff * root.viewHeight; + return origin + (-diff % root.viewHeight); + } + DiffMonthGrid { + id: previousPreviousMonthGrid + monthDiff: root.focusedMonthDiff - 2 + } + DiffMonthGrid { + id: previousMonthGrid + monthDiff: root.focusedMonthDiff - 1 + } + DiffMonthGrid { + id: currentMonthGrid + monthDiff: root.focusedMonthDiff + } + DiffMonthGrid { + id: nextMonthGrid + monthDiff: root.focusedMonthDiff + 1 + } + DiffMonthGrid { + id: nextNextMonthGrid + monthDiff: root.focusedMonthDiff + 2 + } + } + + MouseArea { + anchors.fill: parent + onWheel: wheel => { + root.targetMonthDiff += wheel.angleDelta.y / 120 * -0.333333; // Reverse cuz scrolling down should advance + } + } + + Behavior on monthDiff { + animation: Looks.transition.enter.createObject(this) + } + + component DiffMonthGrid: MonthGrid { + id: monthGrid + required property int monthDiff + property int index: root.monthGrids.indexOf(this) + month: ((root.currentMonth - 1) + monthDiff) % 12 // 1-indexed -> 0-indexed + year: root.currentYear + Math.floor((root.currentMonth - 1 + monthDiff) / 12) + + spacing: root.buttonSpacing + // background: Rectangle { + // color: Qt.rgba(Math.abs(Math.sin(month * 12.9898)) % 1, Math.abs(Math.sin(month * 78.233)) % 1, Math.abs(Math.sin(month * 45.164)) % 1, 1) + // } + delegate: MonthDayButton {} + } + + component MonthDayButton: WButton { + id: monthDayButton + required property var model + opacity: model.month == parent.parent.month || model.today ? 1 : 0 + checked: model.today + implicitWidth: root.buttonSize + implicitHeight: root.buttonSize + radius: height / 2 + + required property int index + + contentItem: Item { + WText { + anchors.centerIn: parent + text: monthDayButton.model.day + color: { + if (monthDayButton.model.today) + return Looks.colors.accentFg; + if (monthDayButton.model.month == root.currentMonth - 1) + return Looks.colors.fg; + return Looks.colors.subfg; + } + font.pixelSize: Looks.font.pixelSize.large + } + } + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml index 95fc529c2..c66925d04 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml @@ -11,7 +11,7 @@ import qs.modules.waffle.looks FooterRectangle { Layout.fillWidth: true - implicitWidth: 334 + implicitWidth: 0 RowLayout { anchors { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index 44ceef6ba..c2b97f6dc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -40,7 +40,7 @@ WBarAttachedPanelContent { WPanelSeparator { visible: !root.collapsed } CalendarView { - Layout.fillWidth: true + // Layout.fillWidth: true Synchronizer on collapsed { property alias source: root.collapsed } From 08739043f68f8d4bde6fafe4e89adf92366b195e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 25 Nov 2025 10:44:29 +0100 Subject: [PATCH 012/154] right sidebar: don't load all bottom group tabs --- .../ii/sidebarRight/BottomWidgetGroup.qml | 119 ++++------ .../modules/ii/sidebarRight/todo/TaskList.qml | 217 +++++++----------- .../notificationCenter/CalendarView.qml | 2 + 3 files changed, 137 insertions(+), 201 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml index f81e9db13..2adb5f503 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml @@ -16,9 +16,9 @@ Rectangle { property int selectedTab: Persistent.states.sidebar.bottomGroup.tab property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed property var tabs: [ - {"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": calendarWidget}, - {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": todoWidget}, - {"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": pomodoroWidget}, + {"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": "calendar/CalendarWidget.qml"}, + {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": "todo/TodoWidget.qml"}, + {"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": "pomodoro/PomodoroWidget.qml"}, ] Behavior on implicitHeight { @@ -170,83 +170,56 @@ Rectangle { } // Content area - StackLayout { + Loader { id: tabStack Layout.fillWidth: true - // Take the highest one, because the TODO list has no implicit height. This way the heigth of the calendar is used when it's initially loaded with the TODO list - height: Math.max(...tabStack.children.map(child => child.tabLoader?.implicitHeight || 0)) // TODO: make this less stupid - Layout.alignment: Qt.AlignVCenter - property int realIndex: root.selectedTab - property int animationDuration: Appearance.animation.elementMoveFast.duration * 1.5 - currentIndex: root.selectedTab + Layout.fillHeight: true + source: root.tabs[root.selectedTab].widget - // Switch the tab on halfway of the anim duration - Connections { - target: root - function onSelectedTabChanged() { - delayedStackSwitch.start() - tabStack.realIndex = root.selectedTab - } - } - Timer { - id: delayedStackSwitch - interval: tabStack.animationDuration / 2 - repeat: false - onTriggered: { - tabStack.currentIndex = root.selectedTab - } - } + Behavior on source { + id: switchBehavior - Repeater { - model: tabs - Item { // TODO: make behavior on y also act for the item that's switched to - id: tabItem - property int tabIndex: index - property string tabType: modelData.type - property int animDistance: 5 - property var tabLoader: tabLoader - // Opacity: show up only when being animated to - opacity: (tabStack.currentIndex === tabItem.tabIndex && tabStack.realIndex === tabItem.tabIndex) ? 1 : 0 - // Y: starts animating when user selects a different tab - y: (tabStack.realIndex === tabItem.tabIndex) ? 0 : (tabStack.realIndex < tabItem.tabIndex) ? animDistance : -animDistance - Behavior on opacity { NumberAnimation { duration: tabStack.animationDuration / 2; easing.type: Easing.OutCubic } } - Behavior on y { NumberAnimation { duration: tabStack.animationDuration; easing.type: Easing.OutExpo } } - Loader { - id: tabLoader - anchors.fill: parent - sourceComponent: modelData.widget - focus: root.selectedTab === tabItem.tabIndex + SequentialAnimation { + id: switchAnim + ParallelAnimation { + PropertyAnimation { + target: tabStack.item + properties: "opacity" + to: 0 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + PropertyAnimation { + target: tabStack.item + properties: "y" + from: 0 + to: 20 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.InExpo + } + } + PropertyAction {} // The source change happens here + ParallelAnimation { + PropertyAnimation { + target: tabStack.item + properties: "opacity" + to: 1 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + PropertyAnimation { + target: tabStack.item + properties: "y" + from: 20 + to: 0 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.OutExpo + } } } } } } - - // Calendar component - Component { - id: calendarWidget - - CalendarWidget { - anchors.fill: parent - anchors.margins: 5 - } - } - - // To Do component - Component { - id: todoWidget - TodoWidget { - anchors.fill: parent - anchors.margins: 5 - } - } - - // Pomodoro component - Component { - id: pomodoroWidget - PomodoroWidget { - anchors.fill: parent - anchors.margins: 5 - } - } } diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TaskList.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TaskList.qml index 4f6a4304d..b48352f31 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TaskList.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TaskList.qml @@ -9,147 +9,108 @@ import Quickshell Item { id: root - required property var taskList; + required property var taskList property string emptyPlaceholderIcon property string emptyPlaceholderText property int todoListItemSpacing: 5 property int todoListItemPadding: 8 property int listBottomPadding: 80 - StyledFlickable { - id: flickable + StyledListView { + id: listView anchors.fill: parent - contentHeight: columnLayout.height - - clip: true - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Rectangle { - width: flickable.width - height: flickable.height - radius: Appearance.rounding.small - } + spacing: root.todoListItemSpacing + animateAppearance: false + model: ScriptModel { + values: root.taskList } + delegate: Item { + id: todoItem + required property var modelData + property bool pendingDoneToggle: false + property bool pendingDelete: false + property bool enableHeightAnimation: false - ColumnLayout { - id: columnLayout - width: parent.width - spacing: 0 - Repeater { - model: ScriptModel { - values: taskList + implicitHeight: todoItemRectangle.implicitHeight + width: ListView.view.width + clip: true + + Behavior on implicitHeight { + enabled: enableHeightAnimation + NumberAnimation { + duration: Appearance.animation.elementMoveFast.duration + easing.type: Appearance.animation.elementMoveFast.type + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve } - delegate: Item { - id: todoItem - property bool pendingDoneToggle: false - property bool pendingDelete: false - property bool enableHeightAnimation: false - - Layout.fillWidth: true - implicitHeight: todoItemRectangle.implicitHeight + todoListItemSpacing - height: implicitHeight - clip: true - - Behavior on implicitHeight { - enabled: enableHeightAnimation - NumberAnimation { - duration: Appearance.animation.elementMoveFast.duration - easing.type: Appearance.animation.elementMoveFast.type - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - } - - function startAction() { - enableHeightAnimation = true - todoItem.implicitHeight = 0 - actionTimer.start() - } - - Timer { - id: actionTimer - interval: Appearance.animation.elementMoveFast.duration - repeat: false - onTriggered: { - if (todoItem.pendingDelete) { - Todo.deleteItem(modelData.originalIndex) - } else if (todoItem.pendingDoneToggle) { - if (!modelData.done) Todo.markDone(modelData.originalIndex) - else Todo.markUnfinished(modelData.originalIndex) - } - } - } - - Rectangle { - id: todoItemRectangle - anchors.left: parent.left - anchors.right: parent.right - anchors.bottom: parent.bottom - implicitHeight: todoContentRowLayout.implicitHeight - color: Appearance.colors.colLayer2 - radius: Appearance.rounding.small - ColumnLayout { - id: todoContentRowLayout - anchors.left: parent.left - anchors.right: parent.right - - StyledText { - Layout.fillWidth: true // Needed for wrapping - Layout.leftMargin: 10 - Layout.rightMargin: 10 - Layout.topMargin: todoListItemPadding - id: todoContentText - text: modelData.content - wrapMode: Text.Wrap - } - RowLayout { - Layout.leftMargin: 10 - Layout.rightMargin: 10 - Layout.bottomMargin: todoListItemPadding - Item { - Layout.fillWidth: true - } - TodoItemActionButton { - Layout.fillWidth: false - onClicked: { - todoItem.pendingDoneToggle = true - todoItem.startAction() - } - contentItem: MaterialSymbol { - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - text: modelData.done ? "remove_done" : "check" - iconSize: Appearance.font.pixelSize.larger - color: Appearance.colors.colOnLayer1 - } - } - TodoItemActionButton { - Layout.fillWidth: false - onClicked: { - todoItem.pendingDelete = true - todoItem.startAction() - } - contentItem: MaterialSymbol { - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - text: "delete_forever" - iconSize: Appearance.font.pixelSize.larger - color: Appearance.colors.colOnLayer1 - } - } - } - } - } - } - } - // Bottom padding - Item { - implicitHeight: listBottomPadding + + Rectangle { + id: todoItemRectangle + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + implicitHeight: todoContentRowLayout.implicitHeight + color: Appearance.colors.colLayer2 + radius: Appearance.rounding.small + + ColumnLayout { + id: todoContentRowLayout + anchors.left: parent.left + anchors.right: parent.right + + StyledText { + id: todoContentText + Layout.fillWidth: true // Needed for wrapping + Layout.leftMargin: 10 + Layout.rightMargin: 10 + Layout.topMargin: todoListItemPadding + text: todoItem.modelData.content + wrapMode: Text.Wrap + } + RowLayout { + Layout.leftMargin: 10 + Layout.rightMargin: 10 + Layout.bottomMargin: todoListItemPadding + Item { + Layout.fillWidth: true + } + TodoItemActionButton { + Layout.fillWidth: false + onClicked: { + if (!todoItem.modelData.done) + Todo.markDone(todoItem.modelData.originalIndex); + else + Todo.markUnfinished(todoItem.modelData.originalIndex); + } + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: todoItem.modelData.done ? "remove_done" : "check" + iconSize: Appearance.font.pixelSize.larger + color: Appearance.colors.colOnLayer1 + } + } + TodoItemActionButton { + Layout.fillWidth: false + onClicked: { + Todo.deleteItem(todoItem.modelData.originalIndex); + } + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + text: "delete_forever" + iconSize: Appearance.font.pixelSize.larger + color: Appearance.colors.colOnLayer1 + } + } + } + } } } } - - Item { // Placeholder when list is empty + + Item { + // Placeholder when list is empty visible: opacity > 0 opacity: taskList.length === 0 ? 1 : 0 anchors.fill: parent @@ -177,4 +138,4 @@ Item { } } } -} \ No newline at end of file +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml index 07228590c..b0ff57684 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml @@ -9,6 +9,8 @@ import qs.modules.common import qs.modules.common.functions import qs.modules.waffle.looks +// TODO: The overlaps are crazy, but the positioning approach works. +// This could work well if we do it week by week instead of month by month. BodyRectangle { id: root From b4920a7cb610c026ec8683f73909400f39097340 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 25 Nov 2025 13:12:46 +0100 Subject: [PATCH 013/154] right sidebar: slightly nicer bottom group tab switch anim --- .../ii/sidebarRight/BottomWidgetGroup.qml | 197 ++++++++++++------ 1 file changed, 128 insertions(+), 69 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml index 2adb5f503..638e2d56f 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import qs.modules.common import qs.modules.common.widgets import qs.services @@ -12,32 +13,47 @@ Rectangle { radius: Appearance.rounding.normal color: Appearance.colors.colLayer1 clip: true - implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : bottomWidgetGroupRow.implicitHeight + implicitHeight: collapsed ? collapsedBottomWidgetGroupRow.implicitHeight : 350 property int selectedTab: Persistent.states.sidebar.bottomGroup.tab + property int previousIndex: -1 property bool collapsed: Persistent.states.sidebar.bottomGroup.collapsed property var tabs: [ - {"type": "calendar", "name": Translation.tr("Calendar"), "icon": "calendar_month", "widget": "calendar/CalendarWidget.qml"}, - {"type": "todo", "name": Translation.tr("To Do"), "icon": "done_outline", "widget": "todo/TodoWidget.qml"}, - {"type": "timer", "name": Translation.tr("Timer"), "icon": "schedule", "widget": "pomodoro/PomodoroWidget.qml"}, + { + "type": "calendar", + "name": Translation.tr("Calendar"), + "icon": "calendar_month", + "widget": "calendar/CalendarWidget.qml" + }, + { + "type": "todo", + "name": Translation.tr("To Do"), + "icon": "done_outline", + "widget": "todo/TodoWidget.qml" + }, + { + "type": "timer", + "name": Translation.tr("Timer"), + "icon": "schedule", + "widget": "pomodoro/PomodoroWidget.qml" + }, ] Behavior on implicitHeight { NumberAnimation { duration: Appearance.animation.elementMove.duration easing.type: Appearance.animation.elementMove.type - easing.bezierCurve: Appearance.animation.elementMove.bezierCurve + easing.bezierCurve: Appearance.animation.elementMove.bezierCurve } } function setCollapsed(state) { - Persistent.states.sidebar.bottomGroup.collapsed = state + Persistent.states.sidebar.bottomGroup.collapsed = state; if (collapsed) { - bottomWidgetGroupRow.opacity = 0 + bottomWidgetGroupRow.opacity = 0; + } else { + collapsedBottomWidgetGroupRow.opacity = 0; } - else { - collapsedBottomWidgetGroupRow.opacity = 0 - } - collapseCleanFadeTimer.start() + collapseCleanFadeTimer.start(); } Timer { @@ -45,18 +61,19 @@ Rectangle { interval: Appearance.animation.elementMove.duration / 2 repeat: false onTriggered: { - if(collapsed) collapsedBottomWidgetGroupRow.opacity = 1 - else bottomWidgetGroupRow.opacity = 1 + if (collapsed) + collapsedBottomWidgetGroupRow.opacity = 1; + else + bottomWidgetGroupRow.opacity = 1; } } - Keys.onPressed: (event) => { - if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) - && event.modifiers === Qt.ControlModifier) { + Keys.onPressed: event => { + if ((event.key === Qt.Key_PageDown || event.key === Qt.Key_PageUp) && event.modifiers === Qt.ControlModifier) { if (event.key === Qt.Key_PageDown) { - root.selectedTab = Math.min(root.selectedTab + 1, root.tabs.length - 1) + root.selectedTab = Math.min(root.selectedTab + 1, root.tabs.length - 1); } else if (event.key === Qt.Key_PageUp) { - root.selectedTab = Math.max(root.selectedTab - 1, 0) + root.selectedTab = Math.max(root.selectedTab - 1, 0); } event.accepted = true; } @@ -77,13 +94,13 @@ Rectangle { } spacing: 15 - + CalendarHeaderButton { Layout.margins: 10 Layout.rightMargin: 0 forceCircle: true downAction: () => { - root.setCollapsed(false) + root.setCollapsed(false); } contentItem: MaterialSymbol { text: "keyboard_arrow_up" @@ -94,7 +111,7 @@ Rectangle { } StyledText { - property int remainingTasks: Todo.list.filter(task => !task.done).length; + property int remainingTasks: Todo.list.filter(task => !task.done).length Layout.margins: 10 Layout.leftMargin: 0 // text: `${DateTime.collapsedCalendarFormat} • ${remainingTasks} task${remainingTasks > 1 ? "s" : ""}` @@ -119,10 +136,10 @@ Rectangle { } } - anchors.fill: parent - height: tabStack.height + anchors.fill: parent + // implicitHeight: tabStack.implicitHeight spacing: 10 - + // Navigation rail Item { Layout.fillHeight: true @@ -141,13 +158,15 @@ Rectangle { Repeater { model: root.tabs NavigationRailButton { + required property int index + required property var modelData showToggledHighlight: false toggled: root.selectedTab == index buttonText: modelData.name buttonIcon: modelData.icon onPressed: { - root.selectedTab = index - Persistent.states.sidebar.bottomGroup.tab = index + root.selectedTab = index; + Persistent.states.sidebar.bottomGroup.tab = index; } } } @@ -158,7 +177,7 @@ Rectangle { anchors.top: parent.top forceCircle: true downAction: () => { - root.setCollapsed(true) + root.setCollapsed(true); } contentItem: MaterialSymbol { text: "keyboard_arrow_down" @@ -170,56 +189,96 @@ Rectangle { } // Content area - Loader { - id: tabStack + Item { Layout.fillWidth: true Layout.fillHeight: true - source: root.tabs[root.selectedTab].widget + // implicitHeight: tabStack.implicitHeight - Behavior on source { - id: switchBehavior + Loader { + id: tabStack + anchors.fill: parent + anchors.bottomMargin: -anchors.topMargin - SequentialAnimation { - id: switchAnim - ParallelAnimation { - PropertyAnimation { - target: tabStack.item - properties: "opacity" - to: 0 - duration: Appearance.animation.elementMoveFast.duration - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - PropertyAnimation { - target: tabStack.item - properties: "y" - from: 0 - to: 20 - duration: Appearance.animation.elementMoveFast.duration - easing.type: Easing.InExpo - } + Component.onCompleted: { + tabStack.source = root.tabs[root.selectedTab].widget; + } + + Connections { + target: root + function onSelectedTabChanged() { + if (root.currentTab > root.previousIndex) + tabSwitchBehavior.animation.down = true; + else if (root.currentTab < root.previousIndex) + tabSwitchBehavior.animation.down = false; + tabStack.source = root.tabs[root.selectedTab].widget; } - PropertyAction {} // The source change happens here - ParallelAnimation { - PropertyAnimation { - target: tabStack.item - properties: "opacity" - to: 1 - duration: Appearance.animation.elementMoveFast.duration - easing.type: Easing.BezierSpline - easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve - } - PropertyAnimation { - target: tabStack.item - properties: "y" - from: 20 - to: 0 - duration: Appearance.animation.elementMoveFast.duration - easing.type: Easing.OutExpo - } + } + + Behavior on source { + id: tabSwitchBehavior + animation: TabSwitchAnim { + id: upAnim + down: true } } } } } + + component TabSwitchAnim: SequentialAnimation { + id: switchAnim + property bool down: false + // ScriptAction { + // script: { + // switchAnim.down = root.selectedTab > root.previousIndex; + // } + // } + ParallelAnimation { + PropertyAnimation { + target: tabStack + properties: "opacity" + to: 0 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + PropertyAnimation { + target: tabStack.anchors + properties: "topMargin" + to: 10 * (switchAnim.down ? -1 : 1) + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveFast.bezierCurve + } + } + PropertyAction { + target: tabStack + property: "source" + value: root.tabs[root.selectedTab].widget + } // The source change happens here + ParallelAnimation { + PropertyAnimation { + target: tabStack.anchors + properties: "topMargin" + from: 10 * -(switchAnim.down ? -1 : 1) + to: 0 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve + } + PropertyAnimation { + target: tabStack + properties: "opacity" + to: 1 + duration: Appearance.animation.elementMoveFast.duration + easing.type: Easing.BezierSpline + easing.bezierCurve: Appearance.animation.elementMoveEnter.bezierCurve + } + } + ScriptAction { + script: { + root.previousIndex = root.selectedTab; + } + } + } } From e4b5718833eb891f1a7a6b74f997bbfe365303ba Mon Sep 17 00:00:00 2001 From: wooze-pao Date: Tue, 25 Nov 2025 23:08:31 -0600 Subject: [PATCH 014/154] Fix: finger print keep running when use password login --- dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml index b242cdc05..753044afa 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml @@ -70,7 +70,7 @@ Scope { } function stopFingerPam() { - if (fingerPam.running) { + if (fingerPam.active) { fingerPam.abort(); } } From 9ba8723a5dfe51c364042fa20130cac9483683d3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 26 Nov 2025 19:40:19 +0100 Subject: [PATCH 015/154] waffles: map media controls bind to action center --- .../ii/sidebarRight/BottomWidgetGroup.qml | 5 ---- .../sidebarRight/calendar/calendar_layout.js | 6 ++--- .../actionCenter/WaffleActionCenter.qml | 27 ++++++++++++++++--- dots/.config/quickshell/ii/shell.qml | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml index 638e2d56f..4c6ea6dd9 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/BottomWidgetGroup.qml @@ -228,11 +228,6 @@ Rectangle { component TabSwitchAnim: SequentialAnimation { id: switchAnim property bool down: false - // ScriptAction { - // script: { - // switchAnim.down = root.selectedTab > root.previousIndex; - // } - // } ParallelAnimation { PropertyAnimation { target: tabStack diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/calendar/calendar_layout.js b/dots/.config/quickshell/ii/modules/ii/sidebarRight/calendar/calendar_layout.js index 7f750b411..e2042bff7 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/calendar/calendar_layout.js +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/calendar/calendar_layout.js @@ -74,7 +74,7 @@ function getCalendarLayout(dateObject, highlight) { // Fill var monthDiff = (weekdayOfMonthFirst == 0 ? 0 : -1); var toFill, dim; - if(weekdayOfMonthFirst == 0) { + if (weekdayOfMonthFirst == 0) { toFill = 1; dim = daysInMonth; } @@ -88,8 +88,7 @@ function getCalendarLayout(dateObject, highlight) { calendar[i][j] = { "day": toFill, "today": ((toFill == day && monthDiff == 0 && highlight) ? 1 : ( - monthDiff == 0 ? 0 : - -1 + monthDiff == 0 ? 0 : -1 )) }; // Increment @@ -112,4 +111,3 @@ function getCalendarLayout(dateObject, highlight) { } return calendar; } - diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml index 56fbedda3..48e81bbc2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/WaffleActionCenter.qml @@ -15,7 +15,8 @@ Scope { target: GlobalStates function onSidebarLeftOpenChanged() { - if (GlobalStates.sidebarLeftOpen) panelLoader.active = true; + if (GlobalStates.sidebarLeftOpen) + panelLoader.active = true; } } @@ -42,13 +43,14 @@ Scope { id: focusGrab active: true windows: [panelWindow] - onCleared: content.close(); + onCleared: content.close() } Connections { target: GlobalStates function onSidebarLeftOpenChanged() { - if (!GlobalStates.sidebarLeftOpen) content.close(); + if (!GlobalStates.sidebarLeftOpen) + content.close(); } } @@ -80,6 +82,23 @@ Scope { name: "sidebarLeftToggle" description: "Toggles left sidebar on press" - onPressed: root.toggleOpen(); + onPressed: root.toggleOpen() + } + + IpcHandler { + target: "mediaControls" + + function toggle(): void { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } + } + + GlobalShortcut { + name: "mediaControlsToggle" + description: "Toggles media controls on press" + + onPressed: { + GlobalStates.sidebarLeftOpen = !GlobalStates.sidebarLeftOpen; + } } } diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 6f401c90f..3f57c0c28 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -94,7 +94,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", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From a786f0353ebf4a9c9395bd25b5e70aa27f3fc02b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:55:09 +0100 Subject: [PATCH 016/154] listview anims always run to end --- .../quickshell/ii/modules/common/Appearance.qml | 17 +++++++++++------ .../modules/common/widgets/StyledListView.qml | 1 + 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index d528e6bd5..9c5045014 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -278,6 +278,7 @@ Singleton { property int velocity: 650 property Component numberAnimation: Component { NumberAnimation { + alwaysRunToEnd: true duration: root.animation.elementMoveEnter.duration easing.type: root.animation.elementMoveEnter.type easing.bezierCurve: root.animation.elementMoveEnter.bezierCurve @@ -292,6 +293,7 @@ Singleton { property int velocity: 650 property Component numberAnimation: Component { NumberAnimation { + alwaysRunToEnd: true duration: root.animation.elementMoveExit.duration easing.type: root.animation.elementMoveExit.type easing.bezierCurve: root.animation.elementMoveExit.bezierCurve @@ -310,9 +312,10 @@ Singleton { easing.bezierCurve: root.animation.elementMoveFast.bezierCurve }} property Component numberAnimation: Component { NumberAnimation { - duration: root.animation.elementMoveFast.duration - easing.type: root.animation.elementMoveFast.type - easing.bezierCurve: root.animation.elementMoveFast.bezierCurve + alwaysRunToEnd: true + duration: root.animation.elementMoveFast.duration + easing.type: root.animation.elementMoveFast.type + easing.bezierCurve: root.animation.elementMoveFast.bezierCurve }} } @@ -323,6 +326,7 @@ Singleton { property int velocity: 650 property Component numberAnimation: Component { NumberAnimation { + alwaysRunToEnd: true duration: root.animation.elementResize.duration easing.type: root.animation.elementResize.type easing.bezierCurve: root.animation.elementResize.bezierCurve @@ -336,9 +340,10 @@ Singleton { property list bezierCurve: animationCurves.expressiveDefaultSpatial property int velocity: 850 property Component numberAnimation: Component { NumberAnimation { - duration: root.animation.clickBounce.duration - easing.type: root.animation.clickBounce.type - easing.bezierCurve: root.animation.clickBounce.bezierCurve + alwaysRunToEnd: true + duration: root.animation.clickBounce.duration + easing.type: root.animation.clickBounce.type + easing.bezierCurve: root.animation.clickBounce.bezierCurve }} } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml index 2db1f094a..26518cfb1 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml @@ -54,6 +54,7 @@ ListView { Behavior on contentY { NumberAnimation { id: scrollAnim + alwaysRunToEnd: true duration: Appearance.animation.scroll.duration easing.type: Appearance.animation.scroll.type easing.bezierCurve: Appearance.animation.scroll.bezierCurve From d27fbede2ac9f02d5ae4b8daf3806d37f069df78 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 26 Nov 2025 22:57:40 +0100 Subject: [PATCH 017/154] waffles: more continuous infinite scrolling calendar --- .../ii/modules/common/functions/DateUtils.qml | 38 +++++ .../modules/common/widgets/CalendarView.qml | 118 ++++++++++++++++ .../ii/modules/common/widgets/WeekRow.qml | 43 ++++++ .../ii/modules/waffle/looks/Looks.qml | 8 ++ .../ii/modules/waffle/looks/WButton.qml | 7 +- .../notificationCenter/CalendarView.qml | 124 ---------------- .../notificationCenter/CalendarWidget.qml | 132 ++++++++++++++++++ .../{CalendarHeader.qml => DateHeader.qml} | 6 +- .../waffle/notificationCenter/FocusFooter.qml | 1 + .../NotificationCenterContent.qml | 6 +- 10 files changed, 354 insertions(+), 129 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml delete mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml rename dots/.config/quickshell/ii/modules/waffle/notificationCenter/{CalendarHeader.qml => DateHeader.qml} (87%) diff --git a/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml new file mode 100644 index 000000000..a7527bcb8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml @@ -0,0 +1,38 @@ +pragma Singleton +import Quickshell + +Singleton { + id: root + + function getMonday(date, american = false) { + const d = new Date(date); // Copy + const day = d.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday + + // Calculate difference to Monday + if (american) { + // Week starts on Sunday + d.setDate(d.getDate() - day); + } else { + // Week starts on Monday + const diff = day === 0 ? -6 : 1 - day; + d.setDate(d.getDate() + diff); + } + + return d; + } + + function sameDate(d1, d2) { + return ( + d1.getFullYear() === d2.getFullYear() && + d1.getMonth() === d2.getMonth() && + d1.getDate() === d2.getDate() + ); + } + + function getIthDayDateOfSameWeek(date, i, american = false) { + const monday = root.getMonday(date, american); + const targetDate = new Date(monday); + targetDate.setDate(monday.getDate() + i); + return targetDate; + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml new file mode 100644 index 000000000..56997ecba --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml @@ -0,0 +1,118 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +Item { + id: root + + // Expose delegate + property Component delegate: Text { + required property var model + text: model.day + } + + // Configuration + property int paddingWeeks: 2 // 1 should be sufficient with proper clipping and no padding + property bool american: false // 🍔🦅 = Sunday first + + // Scrolling + function scrollMonthsAndSnap(x) { // Scroll x months and snap to month + const focusedDate = root.focusedDate; + const focusedMonth = focusedDate.getMonth(); + const focusedYear = focusedDate.getFullYear(); + const targetMonth = focusedMonth + x; + const targetDate = new Date(focusedYear, targetMonth, 1); + const currentFirstShownDate = new Date(root.dateInFirstWeek.getTime() + (root.paddingWeeks * root.millisPerWeek)); + const diffMillis = targetDate.getTime() - currentFirstShownDate.getTime(); + const diffWeeks = Math.round(diffMillis / root.millisPerWeek); + root.targetWeekDiff += diffWeeks; + } + property int weeksPerScroll: 1 + property real targetWeekDiff: 0 + property real weekDiff: targetWeekDiff + property int contentWeekDiff: weekDiff // whole part of weekDiff + property bool scrolling: false + + Behavior on weekDiff { + id: weekScrollBehavior + animation: Looks.transition.scroll.createObject(this) + } + Timer { + id: scrollAnimationCheckTimer + interval: 30 // Should be plenty for 60fps + onTriggered: root.scrolling = false; + } + onWeekDiffChanged: { + scrolling = true; + scrollAnimationCheckTimer.restart(); + } + + MouseArea { + anchors.fill: parent + onWheel: wheel => { + root.targetWeekDiff += wheel.angleDelta.y / 120 * -root.weeksPerScroll; // Reverse cuz scrolling down should advance + } + } + + // Date calculations + readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000 + readonly property int totalWeeks: 6 + (paddingWeeks * 2) + readonly property int focusedWeekIndex: 2 // The third row, 0-indexed + readonly property int focusDayOfWeekIndex: 6 // Non-American + property date dateInFirstWeek: { + const currentDate = new Date(); + const currentMonth = currentDate.getMonth(); + const currentYear = currentDate.getFullYear(); + const firstDayThisMonth = new Date(currentYear, currentMonth, 1); + return new Date(firstDayThisMonth.getTime() - (paddingWeeks * millisPerWeek) + contentWeekDiff * millisPerWeek); + } + property date focusedDate: { + // The last day of 3rd week shown is considered the focused month + const addedTime = (root.paddingWeeks + root.focusedWeekIndex) * root.millisPerWeek + const dateInTargetWeek = new Date(root.dateInFirstWeek.getTime() + addedTime); + return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - (1 * root.american), root.american); // 4 = Thursday + } + property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed + + // Sizes + property real verticalPadding: 0 + property real buttonSize: 40 + property real buttonSpacing: 2 + implicitHeight: (6 * buttonSize) + (5 * buttonSpacing) + (2 * verticalPadding) + implicitWidth: weeksColumn.implicitWidth + clip: true + + ColumnLayout { + id: weeksColumn + anchors { + left: parent.left + right: parent.right + } + y: { + const spacePerExtraRow = root.buttonSize + root.buttonSpacing; + const origin = -(spacePerExtraRow * root.paddingWeeks); + const diff = root.weekDiff * spacePerExtraRow; + return origin + (-diff % spacePerExtraRow) + root.verticalPadding; + } + + spacing: root.buttonSpacing + Repeater { + model: root.totalWeeks + WeekRow { + required property int index + date: new Date(root.dateInFirstWeek.getTime() + (index * root.millisPerWeek)) + Layout.fillWidth: true + spacing: root.buttonSpacing + delegate: root.delegate + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml b/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml new file mode 100644 index 000000000..2863f8896 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml @@ -0,0 +1,43 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common.functions + +RowLayout { + id: root + + // Pls supply + required property date date + property bool sundayFirst: false + + // Expose model and delegate for flexibility + property list model: { + // Should expose props like here: https://doc.qt.io/qt-6/qml-qtquick-controls-monthgrid.html#delegate-prop + // (except weekNumber because i'm lazy and it's not so important) + const firstDayOfWeek = DateUtils.getMonday(root.date, root.sundayFirst); + const weekDates = []; + for (let i = 0; i < 7; i++) { + const dayDate = new Date(firstDayOfWeek); + dayDate.setDate(firstDayOfWeek.getDate() + i); + weekDates.push({ + date: dayDate, + day: dayDate.getDate(), + month: dayDate.getMonth() + 1, + year: dayDate.getFullYear(), + today: DateUtils.sameDate(dayDate, DateTime.clock.date) + }); + } + return weekDates; + } + property Component delegate: Text { + required property var model + text: model.day + } + + // Obvious + Repeater { + model: root.model + delegate: root.delegate + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 0350f5004..e55f840cb 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -231,5 +231,13 @@ Singleton { easing.bezierCurve: transition.easing.bezierCurve.easeIn } } + + property Component scroll: Component { + NumberAnimation { + duration: 250 + easing.type: Easing.BezierSpline + easing.bezierCurve: [0.0, 0.0, 0.25, 1.0, 1, 1] + } + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index 18fe0244b..6b2bc4ecb 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -17,6 +17,7 @@ Button { property color colBackgroundToggledActive: Looks.colors.accentActive property color colForeground: Looks.colors.fg property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) property alias backgroundOpacity: backgroundRect.opacity property color color: { if (!root.enabled) return colBackground; @@ -37,7 +38,11 @@ Button { return root.colBackground; } } - property color fgColor: root.checked ? root.colForegroundToggled : root.colForeground + property color fgColor: { + if (root.checked) return root.colForegroundToggled + if (root.enabled) return root.colForeground + return root.colForegroundDisabled + } property alias horizontalAlignment: buttonText.horizontalAlignment font { family: Looks.font.family.ui diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml deleted file mode 100644 index b0ff57684..000000000 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarView.qml +++ /dev/null @@ -1,124 +0,0 @@ -pragma ComponentBehavior: Bound -import QtQuick -import QtQuick.Controls -import QtQuick.Layouts -import Quickshell -import qs -import qs.services -import qs.modules.common -import qs.modules.common.functions -import qs.modules.waffle.looks - -// TODO: The overlaps are crazy, but the positioning approach works. -// This could work well if we do it week by week instead of month by month. -BodyRectangle { - id: root - - // State - property bool collapsed - - // Sizes - property int _rowsPerMonth: 6 - property real viewHeight: (_rowsPerMonth * buttonSize) + ((_rowsPerMonth - 1) * buttonSpacing) - property real buttonSize: 40 - property real buttonSpacing: 2 - property real spacePerExtraRow: buttonSize + buttonSpacing - - implicitWidth: currentMonthGrid.implicitWidth - implicitHeight: collapsed ? 0 : viewHeight - opacity: implicitHeight > 0 ? 1 : 0 - - Behavior on implicitHeight { - animation: Looks.transition.enter.createObject(this) - } - - // Month stuff - property real targetMonthDiff: 0 - property real monthDiff: targetMonthDiff - property int focusedMonthDiff: monthDiff // whole part of monthDiff - property int currentMonth: DateTime.clock.date.getMonth() + 1 // 0-indexed -> 1-indexed - property int currentYear: DateTime.clock.date.getFullYear() - - clip: true - property list monthGrids: [previousPreviousMonthGrid, previousMonthGrid, currentMonthGrid, nextMonthGrid, nextNextMonthGrid] - ColumnLayout { - spacing: 0 - y: { - const origin = - currentMonthGrid.y; - const diff = root.monthDiff * root.viewHeight; - return origin + (-diff % root.viewHeight); - } - DiffMonthGrid { - id: previousPreviousMonthGrid - monthDiff: root.focusedMonthDiff - 2 - } - DiffMonthGrid { - id: previousMonthGrid - monthDiff: root.focusedMonthDiff - 1 - } - DiffMonthGrid { - id: currentMonthGrid - monthDiff: root.focusedMonthDiff - } - DiffMonthGrid { - id: nextMonthGrid - monthDiff: root.focusedMonthDiff + 1 - } - DiffMonthGrid { - id: nextNextMonthGrid - monthDiff: root.focusedMonthDiff + 2 - } - } - - MouseArea { - anchors.fill: parent - onWheel: wheel => { - root.targetMonthDiff += wheel.angleDelta.y / 120 * -0.333333; // Reverse cuz scrolling down should advance - } - } - - Behavior on monthDiff { - animation: Looks.transition.enter.createObject(this) - } - - component DiffMonthGrid: MonthGrid { - id: monthGrid - required property int monthDiff - property int index: root.monthGrids.indexOf(this) - month: ((root.currentMonth - 1) + monthDiff) % 12 // 1-indexed -> 0-indexed - year: root.currentYear + Math.floor((root.currentMonth - 1 + monthDiff) / 12) - - spacing: root.buttonSpacing - // background: Rectangle { - // color: Qt.rgba(Math.abs(Math.sin(month * 12.9898)) % 1, Math.abs(Math.sin(month * 78.233)) % 1, Math.abs(Math.sin(month * 45.164)) % 1, 1) - // } - delegate: MonthDayButton {} - } - - component MonthDayButton: WButton { - id: monthDayButton - required property var model - opacity: model.month == parent.parent.month || model.today ? 1 : 0 - checked: model.today - implicitWidth: root.buttonSize - implicitHeight: root.buttonSize - radius: height / 2 - - required property int index - - contentItem: Item { - WText { - anchors.centerIn: parent - text: monthDayButton.model.day - color: { - if (monthDayButton.model.today) - return Looks.colors.accentFg; - if (monthDayButton.model.month == root.currentMonth - 1) - return Looks.colors.fg; - return Looks.colors.subfg; - } - font.pixelSize: Looks.font.pixelSize.large - } - } - } -} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml new file mode 100644 index 000000000..23fc7c6eb --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -0,0 +1,132 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +BodyRectangle { + id: root + + // State + property bool collapsed + + implicitHeight: collapsed ? 0 : contentColumn.implicitHeight + implicitWidth: contentColumn.implicitWidth + + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } + + clip: true + ColumnLayout { + id: contentColumn + spacing: 12 + CalendarHeader { + Layout.topMargin: 10 + Layout.fillWidth: true + } + ColumnLayout { + Layout.fillWidth: true + Layout.leftMargin: 5 + Layout.rightMargin: 5 + spacing: 1 + DayOfWeekRow { + Layout.fillWidth: true + locale: Qt.locale("en-GB") + spacing: calendarView.buttonSpacing + implicitHeight: calendarView.buttonSize + delegate: Item { + id: dayOfWeekItem + required property var model + implicitHeight: calendarView.buttonSize + implicitWidth: calendarView.buttonSize + WText { + anchors.centerIn: parent + text: dayOfWeekItem.model.shortName.substring(0,2) + color: Looks.colors.fg + font.pixelSize: Looks.font.pixelSize.large + } + } + } + CalendarView { + id: calendarView + verticalPadding: 2 + buttonSize: 41 // ??? + buttonSpacing: 1 + Layout.fillWidth: true + delegate: DayButton {} + } + } + } + + component DayButton: WButton { + id: dayButton + required property var model + checked: model.today + enabled: hovered || calendarView.scrolling || checked || model.month === calendarView.focusedMonth + implicitWidth: calendarView.buttonSize + implicitHeight: calendarView.buttonSize + radius: height / 2 + + required property int index + + contentItem: Item { + WText { + anchors.centerIn: parent + text: dayButton.model.day + color: dayButton.fgColor + font.pixelSize: Looks.font.pixelSize.large + } + } + } + + component CalendarHeader: RowLayout { + Layout.leftMargin: 8 + Layout.rightMargin: 8 + spacing: 8 + + WBorderlessButton { + Layout.fillWidth: true + implicitHeight: 34 + contentItem: Item { + WText { + anchors.fill: parent + horizontalAlignment: Text.AlignLeft + text: Qt.locale().toString(calendarView.dateInFirstWeek, "MMMM yyyy") + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + } + } + ScrollMonthButton { + scrollDown: false + } + ScrollMonthButton { + scrollDown: true + } + } + + component ScrollMonthButton: WBorderlessButton { + id: scrollMonthButton + required property bool scrollDown + Layout.alignment: Qt.AlignVCenter + + onClicked: { + calendarView.scrollMonthsAndSnap(scrollDown ? 1 : -1); + } + implicitWidth: 32 + implicitHeight: 34 + + contentItem: FluentIcon { + filled: true + implicitSize: 12 + icon: scrollMonthButton.scrollDown ? "caret-down" : "caret-up" + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/DateHeader.qml similarity index 87% rename from dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml rename to dots/.config/quickshell/ii/modules/waffle/notificationCenter/DateHeader.qml index 3e177f2fa..99e0666ed 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarHeader.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/DateHeader.qml @@ -12,8 +12,12 @@ import qs.modules.waffle.looks FooterRectangle { id: root - property bool collapsed implicitWidth: 0 + property bool collapsed + color: ColorUtils.transparentize(Looks.colors.bgPanelBody, collapsed ? 0 : 1) + Behavior on color { + animation: Looks.transition.color.createObject(this) + } RowLayout { anchors { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml index c66925d04..c222e41c4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml @@ -12,6 +12,7 @@ import qs.modules.waffle.looks FooterRectangle { Layout.fillWidth: true implicitWidth: 0 + color: Looks.colors.bgPanelBody RowLayout { anchors { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index c2b97f6dc..ed6677854 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -30,7 +30,7 @@ WBarAttachedPanelContent { WPane { contentItem: ColumnLayout { spacing: 0 - CalendarHeader { + DateHeader { Layout.fillWidth: true Synchronizer on collapsed { property alias source: root.collapsed @@ -39,8 +39,8 @@ WBarAttachedPanelContent { WPanelSeparator { visible: !root.collapsed } - CalendarView { - // Layout.fillWidth: true + CalendarWidget { + Layout.fillWidth: true Synchronizer on collapsed { property alias source: root.collapsed } From d40df98aa585894b9ce246119301d5c54c465c08 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 27 Nov 2025 08:14:15 +0100 Subject: [PATCH 018/154] =?UTF-8?q?waffles:=20make=20calendar=20more=20?= =?UTF-8?q?=F0=9F=8D=94=F0=9F=A6=85-friendly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit do we care about somalia and ethiopia --- dots/.config/quickshell/ii/modules/common/Config.qml | 6 +++++- .../quickshell/ii/modules/common/widgets/CalendarView.qml | 7 ++++++- .../modules/waffle/notificationCenter/CalendarWidget.qml | 7 ++++++- 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 610a4959d..e5285d416 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -282,6 +282,10 @@ Singleton { property int suspend: 3 } + property JsonObject calendar: JsonObject { + property bool forceMondayWeekStart: true + } + property JsonObject cheatsheet: JsonObject { // Use a nerdfont to see the icons // 0: 󰖳 | 1: 󰌽 | 2: 󰘳 | 3:  | 4: 󰨡 @@ -581,7 +585,7 @@ Singleton { } property JsonObject waffles: JsonObject { - // Animations on Windoes are kinda janky. Setting the following to + // Some spots are kinda janky/awkward. Setting the following to // false will make (some) stuff also be like that for accuracy. // Example: the right-click menu of the Start button property JsonObject tweaks: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml index 56997ecba..44d64efd5 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml @@ -1,4 +1,5 @@ pragma ComponentBehavior: Bound +import QtQml import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -21,7 +22,8 @@ Item { // Configuration property int paddingWeeks: 2 // 1 should be sufficient with proper clipping and no padding - property bool american: false // 🍔🦅 = Sunday first + property var locale: Qt.locale() // Should be of type Locale but QML is being funny + property bool american: locale.firstDayOfWeek == Locale.Sunday // Scrolling function scrollMonthsAndSnap(x) { // Scroll x months and snap to month @@ -104,10 +106,13 @@ Item { } spacing: root.buttonSpacing + Repeater { model: root.totalWeeks + WeekRow { required property int index + sundayFirst: root.american date: new Date(root.dateInFirstWeek.getTime() + (index * root.millisPerWeek)) Layout.fillWidth: true spacing: root.buttonSpacing diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 23fc7c6eb..5b595de31 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -1,4 +1,5 @@ pragma ComponentBehavior: Bound +import QtQml import QtQuick import QtQuick.Controls import QtQuick.Layouts @@ -16,6 +17,9 @@ BodyRectangle { // State property bool collapsed + // Locale + property var locale: Config.options.calendar.forceMondayWeekStart ? Qt.locale("en-GB") : Qt.locale() + implicitHeight: collapsed ? 0 : contentColumn.implicitHeight implicitWidth: contentColumn.implicitWidth @@ -38,7 +42,7 @@ BodyRectangle { spacing: 1 DayOfWeekRow { Layout.fillWidth: true - locale: Qt.locale("en-GB") + locale: root.locale spacing: calendarView.buttonSpacing implicitHeight: calendarView.buttonSize delegate: Item { @@ -56,6 +60,7 @@ BodyRectangle { } CalendarView { id: calendarView + locale: root.locale verticalPadding: 2 buttonSize: 41 // ??? buttonSpacing: 1 From 0700e024d9742776fdc5d6cd4dbdf7ca631e2488 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 27 Nov 2025 13:17:24 +0100 Subject: [PATCH 019/154] waffles: calendar: generalize first day of week --- .../quickshell/ii/modules/common/Config.qml | 5 +++- .../ii/modules/common/functions/DateUtils.qml | 29 ++++++------------- .../modules/common/widgets/CalendarView.qml | 7 ++--- .../ii/modules/common/widgets/WeekRow.qml | 6 ++-- .../notificationCenter/CalendarWidget.qml | 10 +++++-- 5 files changed, 26 insertions(+), 31 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index e5285d416..1716aa2ef 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -283,7 +283,7 @@ Singleton { } property JsonObject calendar: JsonObject { - property bool forceMondayWeekStart: true + property string locale: "en-GB" } property JsonObject cheatsheet: JsonObject { @@ -599,6 +599,9 @@ Singleton { property JsonObject actionCenter: JsonObject { property list toggles: [ "network", "bluetooth", "easyEffects", "powerProfile", "idleInhibitor", "nightLight", "darkMode", "antiFlashbang", "cloudflareWarp", "mic", "musicRecognition", "notifications", "onScreenKeyboard", "gameMode", "screenSnip", "colorPicker" ] } + property JsonObject calendar: JsonObject { + property bool force2CharDayOfWeek: true + } } } } diff --git a/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml index a7527bcb8..3cbae5bee 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/DateUtils.qml @@ -4,35 +4,24 @@ import Quickshell Singleton { id: root - function getMonday(date, american = false) { + function getFirstDayOfWeek(date, firstDay = 1) { const d = new Date(date); // Copy const day = d.getDay(); // 0 = Sunday, 1 = Monday, ..., 6 = Saturday - // Calculate difference to Monday - if (american) { - // Week starts on Sunday - d.setDate(d.getDate() - day); - } else { - // Week starts on Monday - const diff = day === 0 ? -6 : 1 - day; - d.setDate(d.getDate() + diff); - } - + // Calculate difference to firstDay + const diff = (day - firstDay + 7) % 7; + d.setDate(d.getDate() - diff); return d; } function sameDate(d1, d2) { - return ( - d1.getFullYear() === d2.getFullYear() && - d1.getMonth() === d2.getMonth() && - d1.getDate() === d2.getDate() - ); + return (d1.getFullYear() === d2.getFullYear() && d1.getMonth() === d2.getMonth() && d1.getDate() === d2.getDate()); } - function getIthDayDateOfSameWeek(date, i, american = false) { - const monday = root.getMonday(date, american); - const targetDate = new Date(monday); - targetDate.setDate(monday.getDate() + i); + function getIthDayDateOfSameWeek(date, i, firstDay = 1) { + const firstDayDate = root.getFirstDayOfWeek(date, firstDay); + const targetDate = new Date(firstDayDate); + targetDate.setDate(firstDayDate.getDate() + i); return targetDate; } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml index 44d64efd5..648da466a 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml @@ -23,7 +23,6 @@ Item { // Configuration property int paddingWeeks: 2 // 1 should be sufficient with proper clipping and no padding property var locale: Qt.locale() // Should be of type Locale but QML is being funny - property bool american: locale.firstDayOfWeek == Locale.Sunday // Scrolling function scrollMonthsAndSnap(x) { // Scroll x months and snap to month @@ -68,7 +67,7 @@ Item { readonly property int millisPerWeek: 7 * 24 * 60 * 60 * 1000 readonly property int totalWeeks: 6 + (paddingWeeks * 2) readonly property int focusedWeekIndex: 2 // The third row, 0-indexed - readonly property int focusDayOfWeekIndex: 6 // Non-American + readonly property int focusDayOfWeekIndex: 6 property date dateInFirstWeek: { const currentDate = new Date(); const currentMonth = currentDate.getMonth(); @@ -80,7 +79,7 @@ Item { // The last day of 3rd week shown is considered the focused month const addedTime = (root.paddingWeeks + root.focusedWeekIndex) * root.millisPerWeek const dateInTargetWeek = new Date(root.dateInFirstWeek.getTime() + addedTime); - return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - (1 * root.american), root.american); // 4 = Thursday + return DateUtils.getIthDayDateOfSameWeek(dateInTargetWeek, root.focusDayOfWeekIndex - root.locale.firstDayOfWeek, root.locale.firstdayOfWeek); // 4 = Thursday } property int focusedMonth: focusedDate.getMonth() + 1 // 0-indexed -> 1-indexed @@ -112,7 +111,7 @@ Item { WeekRow { required property int index - sundayFirst: root.american + locale: root.locale date: new Date(root.dateInFirstWeek.getTime() + (index * root.millisPerWeek)) Layout.fillWidth: true spacing: root.buttonSpacing diff --git a/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml b/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml index 2863f8896..93fc536d2 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/WeekRow.qml @@ -8,14 +8,14 @@ RowLayout { id: root // Pls supply - required property date date - property bool sundayFirst: false + required property date date // Any date within the week + property var locale // Expose model and delegate for flexibility property list model: { // Should expose props like here: https://doc.qt.io/qt-6/qml-qtquick-controls-monthgrid.html#delegate-prop // (except weekNumber because i'm lazy and it's not so important) - const firstDayOfWeek = DateUtils.getMonday(root.date, root.sundayFirst); + const firstDayOfWeek = DateUtils.getFirstDayOfWeek(root.date, root.locale.firstDayOfWeek); const weekDates = []; for (let i = 0; i < 7; i++) { const dayDate = new Date(firstDayOfWeek); diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 5b595de31..0ac48d13b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -18,7 +18,7 @@ BodyRectangle { property bool collapsed // Locale - property var locale: Config.options.calendar.forceMondayWeekStart ? Qt.locale("en-GB") : Qt.locale() + property var locale: Qt.locale(Config.options.calendar.locale) implicitHeight: collapsed ? 0 : contentColumn.implicitHeight implicitWidth: contentColumn.implicitWidth @@ -52,7 +52,11 @@ BodyRectangle { implicitWidth: calendarView.buttonSize WText { anchors.centerIn: parent - text: dayOfWeekItem.model.shortName.substring(0,2) + text: { + var result = dayOfWeekItem.model.shortName; + if (Config.options.waffles.calendar.force2CharDayOfWeek) result = result.substring(0,2); + return result; + } color: Looks.colors.fg font.pixelSize: Looks.font.pixelSize.large } @@ -103,7 +107,7 @@ BodyRectangle { WText { anchors.fill: parent horizontalAlignment: Text.AlignLeft - text: Qt.locale().toString(calendarView.dateInFirstWeek, "MMMM yyyy") + text: Qt.locale().toString(calendarView.focusedDate, "MMMM yyyy") font.pixelSize: Looks.font.pixelSize.large font.weight: Looks.font.weight.strong } From 9cb5cc1416ca9d98118c464ffcc5c9536a9f4207 Mon Sep 17 00:00:00 2001 From: jwihardi Date: Thu, 27 Nov 2025 15:29:30 -0500 Subject: [PATCH 020/154] added gentoo uninstall --- sdata/dist-gentoo/uninstall-deps.sh | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 sdata/dist-gentoo/uninstall-deps.sh diff --git a/sdata/dist-gentoo/uninstall-deps.sh b/sdata/dist-gentoo/uninstall-deps.sh new file mode 100644 index 000000000..280433946 --- /dev/null +++ b/sdata/dist-gentoo/uninstall-deps.sh @@ -0,0 +1,8 @@ +# This script is meant to be sourced. +# It's not for directly running. + +for i in illogical-impulse-{quickshell-git,audio,backlight,basic,bibata-modern-classic-bin,fonts-themes,hyprland,kde,microtex-git,oneui4-icons-git,portal,python,screencapture,toolkit,widgets}; do + v sudo emerge --unmerge $i +done + +v sudo emerge --depclean From 2fd25af353fd963c13ef35d07325b9143f3b4a48 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 27 Nov 2025 23:25:59 +0100 Subject: [PATCH 021/154] waffles: notifications, kind of --- .../ii/assets/icons/fluent/apps-filled.svg | 4 + .../ii/assets/icons/fluent/apps.svg | 4 + .../icons/fluent/more-horizontal-filled.svg | 4 + .../assets/icons/fluent/more-horizontal.svg | 4 + .../common/functions/NotificationUtils.qml | 87 +++++++++++++++++ .../common/widgets/NotificationAppIcon.qml | 2 +- .../common/widgets/NotificationGroup.qml | 7 +- .../common/widgets/notification_utils.js | 82 ---------------- .../modules/waffle/looks/WBorderedButton.qml | 6 +- .../ii/modules/waffle/looks/WPane.qml | 1 + .../waffle/notificationCenter/FocusFooter.qml | 28 ++---- .../NotificationCenterContent.qml | 34 ++++++- .../NotificationHeaderButton.qml | 25 +++++ .../NotificationPaneContent.qml | 81 ++++++++++++++++ .../SmallBorderedIconAndTextButton.qml | 34 +++++++ .../SmallBorderedIconButton.qml | 1 + .../WNotificationAppIcon.qml | 30 ++++++ .../notificationCenter/WNotificationGroup.qml | 93 +++++++++++++++++++ .../WSingleNotification.qml | 59 ++++++++++++ 19 files changed, 470 insertions(+), 116 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/apps-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/apps.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal.svg create mode 100644 dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml delete mode 100644 dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationAppIcon.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/apps-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/apps-filled.svg new file mode 100644 index 000000000..88214527a --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/apps-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/apps.svg b/dots/.config/quickshell/ii/assets/icons/fluent/apps.svg new file mode 100644 index 000000000..5eb188422 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/apps.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal-filled.svg new file mode 100644 index 000000000..5b5a33c9f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal.svg b/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal.svg new file mode 100644 index 000000000..57e2ee132 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/more-horizontal.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml new file mode 100644 index 000000000..8a336e8ad --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml @@ -0,0 +1,87 @@ +pragma Singleton +import Quickshell + +Singleton { + id: root + /** + * @param { string } summary + * @returns { string } + */ + function findSuitableMaterialSymbol(summary = "") { + const defaultType = 'chat'; + if (summary.length === 0) return defaultType; + + const keywordsToTypes = { + 'reboot': 'restart_alt', + 'record': 'screen_record', + 'battery': 'power', + 'power': 'power', + 'screenshot': 'screenshot_monitor', + 'welcome': 'waving_hand', + 'time': 'scheduleb', + 'installed': 'download', + 'configuration reloaded': 'reset_wrench', + 'unable': 'question_mark', + "couldn't": 'question_mark', + 'config': 'reset_wrench', + 'update': 'update', + 'ai response': 'neurology', + 'control': 'settings', + 'upsca': 'compare', + 'music': 'queue_music', + 'install': 'deployed_code_update', + 'input': 'keyboard_alt', + 'preedit': 'keyboard_alt', + 'startswith:file': 'folder_copy', // Declarative startsWith check + }; + + const lowerSummary = summary.toLowerCase(); + + for (const [keyword, type] of Object.entries(keywordsToTypes)) { + if (keyword.startsWith('startswith:')) { + const startsWithKeyword = keyword.replace('startswith:', ''); + if (lowerSummary.startsWith(startsWithKeyword)) { + return type; + } + } else if (lowerSummary.includes(keyword)) { + return type; + } + } + + return defaultType; + } + + /** + * @param { number | string | Date } timestamp + * @returns { string } + */ + function getFriendlyNotifTimeString(timestamp) { + if (!timestamp) return ''; + const messageTime = new Date(timestamp); + const now = new Date(); + const diffMs = now.getTime() - messageTime.getTime(); + + // Less than 1 minute + if (diffMs < 60000) + return 'Now'; + + // Same day - show relative time + if (messageTime.toDateString() === now.toDateString()) { + const diffMinutes = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + + if (diffHours > 0) { + return `${diffHours}h`; + } else { + return `${diffMinutes}m`; + } + } + + // Yesterday + if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString()) + return 'Yesterday'; + + // Older dates + return Qt.formatDateTime(messageTime, "MMMM dd"); + } +} diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationAppIcon.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationAppIcon.qml index b590f22ac..ad250d9b6 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationAppIcon.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationAppIcon.qml @@ -1,5 +1,5 @@ import qs.modules.common -import "notification_utils.js" as NotificationUtils +import qs.modules.common.functions import Qt5Compat.GraphicalEffects import QtQuick import Quickshell diff --git a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml index b20b470fa..fc612dcb1 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/NotificationGroup.qml @@ -1,7 +1,6 @@ import qs.services import qs.modules.common import qs.modules.common.functions -import "notification_utils.js" as NotificationUtils import QtQuick import QtQuick.Layouts import Quickshell @@ -136,7 +135,7 @@ MouseArea { // Notification group area } clip: true - implicitHeight: expanded ? + implicitHeight: root.expanded ? row.implicitHeight + padding * 2 : Math.min(80, row.implicitHeight + padding * 2) @@ -157,8 +156,8 @@ MouseArea { // Notification group area Layout.alignment: Qt.AlignTop Layout.fillWidth: false image: root?.multipleNotifications ? "" : notificationGroup?.notifications[0]?.image ?? "" - appIcon: notificationGroup?.appIcon - summary: notificationGroup?.notifications[root.notificationCount - 1]?.summary + appIcon: root.notificationGroup?.appIcon + summary: root.notificationGroup?.notifications[root.notificationCount - 1]?.summary urgency: root.notifications.some(n => n.urgency === NotificationUrgency.Critical.toString()) ? NotificationUrgency.Critical : NotificationUrgency.Normal } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js b/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js deleted file mode 100644 index 1f879baa2..000000000 --- a/dots/.config/quickshell/ii/modules/common/widgets/notification_utils.js +++ /dev/null @@ -1,82 +0,0 @@ - -/** - * @param { string } summary - * @returns { string } - */ -function findSuitableMaterialSymbol(summary = "") { - const defaultType = 'chat'; - if(summary.length === 0) return defaultType; - - const keywordsToTypes = { - 'reboot': 'restart_alt', - 'record': 'screen_record', - 'battery': 'power', - 'power': 'power', - 'screenshot': 'screenshot_monitor', - 'welcome': 'waving_hand', - 'time': 'scheduleb', - 'installed': 'download', - 'configuration reloaded': 'reset_wrench', - 'unable': 'question_mark', - "couldn't": 'question_mark', - 'config': 'reset_wrench', - 'update': 'update', - 'ai response': 'neurology', - 'control': 'settings', - 'upsca': 'compare', - 'music': 'queue_music', - 'install': 'deployed_code_update', - 'input': 'keyboard_alt', - 'preedit': 'keyboard_alt', - 'startswith:file': 'folder_copy', // Declarative startsWith check - }; - - const lowerSummary = summary.toLowerCase(); - - for (const [keyword, type] of Object.entries(keywordsToTypes)) { - if (keyword.startsWith('startswith:')) { - const startsWithKeyword = keyword.replace('startswith:', ''); - if (lowerSummary.startsWith(startsWithKeyword)) { - return type; - } - } else if (lowerSummary.includes(keyword)) { - return type; - } - } - - return defaultType; -} - -/** - * @param { number | string | Date } timestamp - * @returns { string } - */ -const getFriendlyNotifTimeString = (timestamp) => { - if (!timestamp) return ''; - const messageTime = new Date(timestamp); - const now = new Date(); - const diffMs = now.getTime() - messageTime.getTime(); - - // Less than 1 minute - if (diffMs < 60000) - return 'Now'; - - // Same day - show relative time - if (messageTime.toDateString() === now.toDateString()) { - const diffMinutes = Math.floor(diffMs / 60000); - const diffHours = Math.floor(diffMs / 3600000); - - if (diffHours > 0) { - return `${diffHours}h`; - } else { - return `${diffMinutes}m`; - } - } - - // Yesterday - if (messageTime.toDateString() === new Date(now.getTime() - 86400000).toDateString()) - return 'Yesterday'; - - // Older dates - return Qt.formatDateTime(messageTime, "MMMM dd"); -}; \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml index 74cb35f8b..b3377cc64 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WBorderedButton.qml @@ -11,8 +11,8 @@ WButton { colBackground: Looks.colors.bg2 colBackgroundHover: Looks.colors.bg2Hover colBackgroundActive: Looks.colors.bg2Active - border.color: Looks.colors.bg2Border + property color colBorder: Looks.colors.bg2Border + property color colBorderToggled: Looks.colors.accent + border.color: checked ? colBorderToggled : colBorder border.width: root.pressed ? 2 : 1 - - } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index 68e836b66..be75cc30a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -14,6 +14,7 @@ Item { property real radius: Looks.radius.large property alias border: borderRect property alias borderColor: borderRect.border.color + property alias borderWidth: borderRect.border.width implicitWidth: borderRect.implicitWidth implicitHeight: borderRect.implicitHeight diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml index c222e41c4..2f2b788a4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/FocusFooter.qml @@ -54,35 +54,19 @@ FooterRectangle { Layout.fillWidth: true } - SmallBorderedIconButton { - leftPadding: 12 - rightPadding: 12 - implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding + SmallBorderedIconAndTextButton { + iconName: TimerService.pomodoroRunning ? "stop" : "play" + text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus") onClicked: { if (TimerService.pomodoroRunning) { - TimerService.togglePomodoro() - TimerService.resetPomodoro() + TimerService.togglePomodoro(); + TimerService.resetPomodoro(); } else { - TimerService.togglePomodoro() + TimerService.togglePomodoro(); Quickshell.execDetached(["qs", "-p", Quickshell.shellPath(""), "ipc", "call", "sidebarRight", "toggle"]); } } - - contentItem: Row { - id: focusButtonContent - spacing: 4 - FluentIcon { - icon: TimerService.pomodoroRunning ? "stop" : "play" - filled: true - implicitSize: 14 - anchors.verticalCenter: parent.verticalCenter - } - WText { - anchors.verticalCenter: parent.verticalCenter - text: TimerService.pomodoroRunning ? Translation.tr("End session") : Translation.tr("Focus") - } - } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index ed6677854..797cee28a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -19,16 +19,40 @@ WBarAttachedPanelContent { property bool collapsed: false - contentItem: Column { + contentItem: ColumnLayout { anchors { horizontalCenter: parent.horizontalCenter - top: root.barAtBottom ? undefined : parent.top - bottom: root.barAtBottom ? parent.bottom : undefined + top: parent.top + bottom: parent.bottom } spacing: 12 + Item { + id: notificationArea + Layout.fillHeight: true + implicitWidth: notificationPane.implicitWidth + + WPane { + id: notificationPane + anchors { + bottom: parent.bottom + left: parent.left + right: parent.right + } + contentItem: NotificationPaneContent { + implicitWidth: calendarColumnLayout.implicitWidth + implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230 + + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } + } + } + } + WPane { contentItem: ColumnLayout { + id: calendarColumnLayout spacing: 0 DateHeader { Layout.fillWidth: true @@ -37,7 +61,9 @@ WBarAttachedPanelContent { } } - WPanelSeparator { visible: !root.collapsed } + WPanelSeparator { + visible: !root.collapsed + } CalendarWidget { Layout.fillWidth: true diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml new file mode 100644 index 000000000..860451fc3 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml @@ -0,0 +1,25 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +WBorderlessButton { + id: headerButton + Layout.fillWidth: false + implicitWidth: 16 + implicitHeight: 16 + color: "transparent" + + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + implicitSize: 16 + icon: headerButton.icon.name + color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1 + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml new file mode 100644 index 000000000..368829865 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml @@ -0,0 +1,81 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +BodyRectangle { + id: root + anchors.fill: parent + implicitHeight: 230 + + ColumnLayout { + id: contentLayout + anchors.fill: parent + anchors.margins: 4 + + spacing: 12 + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 12 + Layout.rightMargin: 12 + Layout.topMargin: 8 + + spacing: 8 + + WText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + text: Translation.tr("Notifications") + font.pixelSize: Looks.font.pixelSize.large + } + + SmallBorderedIconButton { + icon.name: "alert-snooze" + checked: Notifications.silent + onClicked: { + Notifications.silent = !Notifications.silent; + } + } + + SmallBorderedIconAndTextButton { + visible: Notifications.list.length > 0 + iconVisible: false + text: Translation.tr("Clear all") + onClicked: { + Notifications.discardAllNotifications(); + } + } + } + + StyledListView { + Layout.fillWidth: true + Layout.fillHeight: true + animateAppearance: false + clip: true + + model: Notifications.appNameList + delegate: WNotificationGroup { + required property int index + required property var modelData + width: ListView.view.width + notificationGroup: Notifications.groupsByAppName[modelData] + } + + EmptyPlaceholder { + visible: Notifications.list.length === 0 + anchors.centerIn: parent + } + } + } + + component EmptyPlaceholder: WText { + horizontalAlignment: Text.AlignHCenter + text: Translation.tr("No new notifications") + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml new file mode 100644 index 000000000..c4331a7dc --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml @@ -0,0 +1,34 @@ +import QtQuick +import qs +import qs.services +import qs.modules.common +import qs.modules.waffle.looks + +SmallBorderedIconButton { + id: root + + property bool iconVisible: true + property string iconName: "" + property bool iconFilled: true + + leftPadding: 12 + rightPadding: 12 + implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding + + contentItem: Row { + id: focusButtonContent + spacing: 4 + + FluentIcon { + visible: root.iconVisible + icon: root.iconName + filled: root.iconFilled + implicitSize: 14 + anchors.verticalCenter: parent.verticalCenter + } + WText { + anchors.verticalCenter: parent.verticalCenter + text: root.text + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml index 1236779a7..f7006ca7f 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconButton.qml @@ -13,6 +13,7 @@ WBorderedButton { anchors.centerIn: parent implicitSize: 12 icon: root.icon.name + color: root.fgColor } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationAppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationAppIcon.qml new file mode 100644 index 000000000..a658695d1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationAppIcon.qml @@ -0,0 +1,30 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import org.kde.kirigami as Kirigami +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +Item { + id: root + + property string icon: "" + property real implicitSize: 16 + implicitWidth: implicitSize + implicitHeight: implicitSize + + Kirigami.Icon { + anchors.fill: parent + implicitWidth: root.implicitSize + implicitHeight: root.implicitSize + + source: root.icon || fallback + fallback: `${Looks.iconsPath}/apps.svg` + roundToIconSize: false + isMask: !root.icon + color: Looks.colors.fg + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml new file mode 100644 index 000000000..837c7c016 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -0,0 +1,93 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +MouseArea { + id: root + + required property var notificationGroup + readonly property var notifications: notificationGroup?.notifications ?? [] + + implicitWidth: contentLayout.implicitWidth + implicitHeight: contentLayout.implicitHeight + + ColumnLayout { + id: contentLayout + anchors.fill: parent + spacing: 4 + + GroupHeader { + id: notifHeader + Layout.fillWidth: true + Layout.margins: 11 + } + + ListView { + Layout.fillWidth: true + implicitWidth: notifHeader.implicitWidth + implicitHeight: contentHeight + interactive: false + spacing: 4 + model: ScriptModel { + values: root.notifications.slice().reverse() + } + delegate: WSingleNotification { + required property var modelData + width: ListView.view.width + notification: modelData + } + } + } + + component GroupHeader: MouseArea { + id: headerMouseArea + hoverEnabled: true + acceptedButtons: Qt.NoButton + + implicitWidth: appHeader.implicitWidth + implicitHeight: appHeader.implicitHeight + + RowLayout { + id: appHeader + anchors.fill: parent + spacing: 7 + + WNotificationAppIcon { + Layout.alignment: Qt.AlignVCenter + icon: root.notificationGroup?.appIcon ?? "" + } + + WText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + text: root.notificationGroup?.appName ?? "" + } + + // NotificationHeaderButton { // TODO: More notification functionality needed so we can have this button + // visible: headerMouseArea.containsMouse + // Layout.leftMargin: 25 + // Layout.rightMargin: 25 + // icon.name: "more-horizontal" + // } + + NotificationHeaderButton { + visible: headerMouseArea.containsMouse + Layout.rightMargin: 3 + icon.name: "dismiss" + onClicked: { + root.notifications.forEach(notif => { + Qt.callLater(() => { + Notifications.discardNotification(notif.notificationId); + }); + }); + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml new file mode 100644 index 000000000..42a832adb --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -0,0 +1,59 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import Quickshell.Services.Notifications +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +MouseArea { + id: root + + required property var notification + property bool expanded: false + + implicitHeight: contentItem.implicitHeight + implicitWidth: contentItem.implicitWidth + + Rectangle { + id: contentItem + anchors.fill: parent + color: Looks.colors.bgPanelBody + radius: Looks.radius.medium + property real padding: 12 + implicitHeight: notificationContent.implicitHeight + padding * 2 + implicitWidth: notificationContent.implicitWidth + padding * 2 + border.width: 1 + border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow) + + ColumnLayout { + id: notificationContent + anchors.fill: parent + anchors.margins: contentItem.padding + + RowLayout { + Layout.fillWidth: true + WText { + text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) + } + } + + ColumnLayout { + Layout.fillWidth: true + WText { + Layout.fillWidth: true + elide: Text.ElideRight + text: root.notification.summary + } + WText { + Layout.fillWidth: true + elide: Text.ElideRight + wrapMode: Text.Wrap + maximumLineCount: root.expanded ? 100 : 1 + } + } + } + } +} From 2a1aaa9b7e34b11413b0bbfc4dec6f0ba087f81c Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Fri, 28 Nov 2025 14:07:58 +0800 Subject: [PATCH 022/154] config: use KDE network manager GUI for WiFi settings Replace nmtui (terminal UI) with kcmshell6 kcm_networkmanagement for network settings to match the Bluetooth settings UI style and provide a more consistent graphical user experience. --- dots/.config/quickshell/ii/modules/common/Config.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 1716aa2ef..0cf28653a 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -153,7 +153,7 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" - property string network: "kitty -1 fish -c nmtui" + property string network: "kcmshell6 kcm_networkmanagement" property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string taskManager: "plasma-systemmonitor --page-name Processes" property string terminal: "kitty -1" // This is only for shell actions From b214993c161b995cc214d320bd8dc5cf51ae1c43 Mon Sep 17 00:00:00 2001 From: eren <98850034+EisregenHaha@users.noreply.github.com> Date: Fri, 28 Nov 2025 12:51:19 +0100 Subject: [PATCH 023/154] Add Qalculate as Dependency for Fedora fixes math --- sdata/dist-fedora/feddeps.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml index f21169a1b..2a1bf2df5 100644 --- a/sdata/dist-fedora/feddeps.toml +++ b/sdata/dist-fedora/feddeps.toml @@ -181,6 +181,7 @@ packages = [ "hyprpicker", "songrec", "translate-shell", + "qalculate", "wlogout" ] @@ -191,4 +192,4 @@ packages = [ "plasma-systemmonitor", "unzip" ] -install_opts = ["--setopt=install_weak_deps=False"] \ No newline at end of file +install_opts = ["--setopt=install_weak_deps=False"] From 04f73e67c8eee60ccf1fdf553e46c246c6948c01 Mon Sep 17 00:00:00 2001 From: clsty Date: Fri, 28 Nov 2025 21:07:39 +0800 Subject: [PATCH 024/154] Add qalc dep for dist-arch and deps-info (#2591) --- sdata/deps-info.md | 3 +++ sdata/dist-arch/illogical-impulse-widgets/PKGBUILD | 3 ++- sdata/dist-fedora/feddeps.toml | 1 + 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/sdata/deps-info.md b/sdata/deps-info.md index fc6f315a3..9a1e4e826 100644 --- a/sdata/deps-info.md +++ b/sdata/deps-info.md @@ -191,6 +191,9 @@ Tips: - Used in Quickshell config. - `wlogout` - Used in Hyprland config. +- `libqalculate` + - Used in Quickshell config, providing math ability in searchbar. + - Note that `qalc` is the needed executable. In Arch Linux [libqalculate](https://archlinux.org/packages/extra/x86_64/libqalculate) provides it, but in Fedora [qalculate](https://packages.fedoraproject.org/pkgs/libqalculate/qalculate/fedora-43.html#files) does and [libqalculate](https://packages.fedoraproject.org/pkgs/libqalculate/libqalculate/fedora-43.html#files) does not. # Actual packages diff --git a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD index 76f71d43f..fa9d4df02 100644 --- a/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD +++ b/sdata/dist-arch/illogical-impulse-widgets/PKGBUILD @@ -1,6 +1,6 @@ pkgname=illogical-impulse-widgets pkgver=1.0 -pkgrel=5 +pkgrel=6 pkgdesc='Illogical Impulse Widget Dependencies' arch=(any) license=(None) @@ -14,4 +14,5 @@ depends=( songrec translate-shell wlogout + libqalculate ) diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml index 2a1bf2df5..367626ced 100644 --- a/sdata/dist-fedora/feddeps.toml +++ b/sdata/dist-fedora/feddeps.toml @@ -193,3 +193,4 @@ packages = [ "unzip" ] install_opts = ["--setopt=install_weak_deps=False"] + From b4038dafa900509e4784eb0980aa7e78878d06f4 Mon Sep 17 00:00:00 2001 From: clsty Date: Fri, 28 Nov 2025 22:42:47 +0800 Subject: [PATCH 025/154] Update dist-nix - Not use NixGL but let home-manager automatically handle it (since hm 25.11) - Add libqalculate --- sdata/dist-nix/README.md | 34 ++++++++++++++++++++-- sdata/dist-nix/home-manager/flake.nix | 19 +++++++----- sdata/dist-nix/home-manager/home.nix | 19 ++++++++---- sdata/dist-nix/home-manager/quickshell.nix | 10 +++++-- sdata/dist-nix/install-deps.sh | 5 ++-- 5 files changed, 67 insertions(+), 20 deletions(-) diff --git a/sdata/dist-nix/README.md b/sdata/dist-nix/README.md index 3a8120e04..9aca1d13f 100644 --- a/sdata/dist-nix/README.md +++ b/sdata/dist-nix/README.md @@ -37,8 +37,38 @@ As [commented](https://github.com/end-4/dots-hyprland/issues/1061#issuecomment-3 See also [caelestia-dots/shell#668](https://github.com/caelestia-dots/shell/issues/668). -### NixGL -On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. `nixGL` should be used to address the problem. +### GPU +On non-NixOS distros, packages installed via home-manager have problem accessing GPU, especially Hyprland because it requires GPU acceleration to launch. + +~~`nixGL` should be used to address the problem.~~ + +Since home-manager 25.11, for non-NixOS just set the following: +```nix +targets.genericLinux.enable = true; +``` +Then during building, home-manager will show a message to tell you running a command manually to configure GPU, like: +```bash +sudo /nix/store/-non-nixos-gpu/bin/non-nixos-gpu-setup +``` +It runs a bash script with following content: +``` +#!/nix/store/-bash-/bin/bash + +set -e + +# Install the systemd service file and ensure that the store path won't be +# garbage-collected as long as it's installed. +unit_path=/etc/systemd/system/non-nixos-gpu.service +ln -sf /nix/store/-non-nixos-gpu/resources/non-nixos-gpu.service "$unit_path" +ln -sf "$unit_path" "/nix/var/nix"/gcroots/non-nixos-gpu.service + +systemctl daemon-reload +systemctl enable non-nixos-gpu.service +systemctl restart non-nixos-gpu.service +``` +_Note: it uses `systemctl`, maybe won't work for OpenRC..._ + +See [gpu-non-nixos](https://nix-community.github.io/home-manager/index.xhtml#sec-usage-gpu-non-nixos). # Handling dot files ## Status diff --git a/sdata/dist-nix/home-manager/flake.nix b/sdata/dist-nix/home-manager/flake.nix index d88daad6e..97a50c466 100644 --- a/sdata/dist-nix/home-manager/flake.nix +++ b/sdata/dist-nix/home-manager/flake.nix @@ -3,23 +3,24 @@ description = "illogical-impulse"; inputs = { - # Qt 6.10 is not yet available from released version of nixpkgs. - #nixpkgs.url = "nixpkgs/nixos-25.05"; - nixpkgs.url = "nixpkgs/nixos-unstable"; + nixpkgs.url = "nixpkgs/nixos-25.11"; + #nixpkgs.url = "nixpkgs/nixos-unstable"; home-manager = { - #url = "github:nix-community/home-manager/release-25.05"; - url = "github:nix-community/home-manager/master"; + url = "github:nix-community/home-manager/release-25.11"; + #url = "github:nix-community/home-manager/master"; inputs.nixpkgs.follows = "nixpkgs"; }; - nixgl.url = "github:nix-community/nixGL"; + #nixgl.url = "github:nix-community/nixGL"; quickshell = { url = "github:quickshell-mirror/quickshell/db1777c20b936a86528c1095cbcb1ebd92801402"; inputs.nixpkgs.follows = "nixpkgs"; }; }; - outputs = { nixpkgs, home-manager, nixgl, quickshell, ... }: + outputs = { nixpkgs, home-manager, + #nixgl, + quickshell, ... }: let home_attrs = rec { username = import ./username.nix; @@ -36,7 +37,9 @@ homeConfigurations = { illogical_impulse = home-manager.lib.homeManagerConfiguration { inherit pkgs; - extraSpecialArgs = { inherit home_attrs nixgl quickshell; }; + extraSpecialArgs = { inherit home_attrs + #nixgl + quickshell; }; modules = [ ./home.nix ]; diff --git a/sdata/dist-nix/home-manager/home.nix b/sdata/dist-nix/home-manager/home.nix index 596430395..b1381481d 100644 --- a/sdata/dist-nix/home-manager/home.nix +++ b/sdata/dist-nix/home-manager/home.nix @@ -1,8 +1,13 @@ -{ config, lib, pkgs, nixgl, quickshell, home_attrs, ... }: +{ config, lib, pkgs, +#nixgl, +quickshell, home_attrs, ... }: { programs.home-manager.enable = true; - nixGL.packages = nixgl.packages; - nixGL.defaultWrapper = "mesa"; + + # Necessary for non-NixOS to handle GPU (since home-manager version 25.11) + targets.genericLinux.enable = true; + #nixGL.packages = nixgl.packages; + #nixGL.defaultWrapper = "mesa"; xdg.portal = { enable = true; @@ -27,7 +32,8 @@ systemd.enable = false; plugins = []; settings = {}; extraConfig = ""; enable = true; ## Use NixGL - package = config.lib.nixGL.wrap pkgs.hyprland; + #package = config.lib.nixGL.wrap pkgs.hyprland; + package = pkgs.hyprland; }; home = { @@ -167,6 +173,7 @@ songrec #songrec translate-shell #translate-shell wlogout #wlogout + libqalculate #libqalculate ] ++ [ @@ -174,7 +181,9 @@ ### illogical-impulse-quickshell-git #(config.lib.nixGL.wrap quickshell.packages.x86_64-linux.default) - (import ./quickshell.nix { inherit pkgs quickshell; nixGLWrap = config.lib.nixGL.wrap; }) + (import ./quickshell.nix { inherit pkgs quickshell; + #nixGLWrap = config.lib.nixGL.wrap; + }) ]; }//home_attrs; } diff --git a/sdata/dist-nix/home-manager/quickshell.nix b/sdata/dist-nix/home-manager/quickshell.nix index 0fb7b57fe..fb81af848 100644 --- a/sdata/dist-nix/home-manager/quickshell.nix +++ b/sdata/dist-nix/home-manager/quickshell.nix @@ -1,10 +1,14 @@ -{ pkgs, quickshell, nixGLWrap, ... }: +{ pkgs, quickshell, +#nixGLWrap, +... }: let - qs = nixGLWrap quickshell.packages.x86_64-linux.default; + #qs = nixGLWrap quickshell.packages.x86_64-linux.default; + qs = quickshell.packages.x86_64-linux.default; in pkgs.stdenv.mkDerivation { name = "illogical-impulse-quickshell-wrapper"; meta = with pkgs.lib; { - description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage"; + #description = "Quickshell wrapped with NixGL + bundled Qt deps for home-manager usage"; + description = "Quickshell bundled Qt deps for home-manager usage"; license = licenses.gpl3Only; }; diff --git a/sdata/dist-nix/install-deps.sh b/sdata/dist-nix/install-deps.sh index 357bb498f..2049efb82 100644 --- a/sdata/dist-nix/install-deps.sh +++ b/sdata/dist-nix/install-deps.sh @@ -32,8 +32,8 @@ function install_home-manager(){ try source $HOME/.nix-profile/etc/profile.d/hm-session-vars.sh command -v $cmd && return - x nix-channel --add https://nixos.org/channels/nixos-25.05 nixpkgs-home - x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.05.tar.gz home-manager + x nix-channel --add https://nixos.org/channels/nixos-25.11 nixpkgs-home + x nix-channel --add https://github.com/nix-community/home-manager/archive/release-25.11.tar.gz home-manager x nix-channel --update x env NIX_PATH="nixpkgs=$HOME/.nix-defexpr/channels/nixpkgs-home" nix-shell '' -A install @@ -56,6 +56,7 @@ function hm_deps(){ x home-manager switch --flake .#illogical_impulse \ --extra-experimental-features nix-command \ --extra-experimental-features flakes + x sudo /nix/store/*-non-nixos-gpu/bin/non-nixos-gpu-setup cd $REPO_ROOT x git rm -f "${SETUP_USERNAME_NIXFILE}" } From 533b9c01f53b0e1ec7112057d746013ca4147801 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Fri, 28 Nov 2025 23:10:33 +0300 Subject: [PATCH 026/154] digital clock: several improvements --- .../quickshell/ii/modules/common/Config.qml | 5 + .../modules/common/widgets/ConfigSlider.qml | 42 +++++ .../modules/common/widgets/StyledSlider.qml | 3 +- .../ii/modules/common/widgets/StyledText.qml | 2 +- .../ii/background/widgets/clock/ClockText.qml | 18 +++ .../background/widgets/clock/ClockWidget.qml | 43 +----- .../background/widgets/clock/DigitalClock.qml | 74 +++++++++ .../ii/modules/settings/BackgroundConfig.qml | 146 ++++++++++++------ 8 files changed, 246 insertions(+), 87 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml create mode 100644 dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml create mode 100644 dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 1716aa2ef..eb62d0955 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -186,7 +186,12 @@ Singleton { property bool useSineCookie: false } property JsonObject digital: JsonObject { + property bool adaptiveAlignment: true + property bool showDate: true property bool animateChange: true + property bool vertical: false + property real weight: 350 + property real size: 90 } property JsonObject quote: JsonObject { property bool enable: false diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml new file mode 100644 index 000000000..b53a5f7c0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml @@ -0,0 +1,42 @@ +import qs.modules.common.widgets +import qs.modules.common +import QtQuick +import QtQuick.Layouts + +RowLayout { + id: root + spacing: 10 + Layout.leftMargin: 8 + Layout.rightMargin: 8 + + property string text: "" + property string buttonIcon: "" + property alias value: slider.value + property bool usePercentTooltip: true + property real from: slider.from + property real to: slider.to + + RowLayout { + spacing: 10 + OptionalMaterialSymbol { + id: iconWidget + icon: root.buttonIcon + iconSize: Appearance.font.pixelSize.larger + } + StyledText { + id: labelWidget + text: root.text + color: Appearance.colors.colOnSecondaryContainer + } + } + + StyledSlider { + id: slider + Layout.fillWidth: true + configuration: StyledSlider.Configuration.XS + usePercentTooltip: root.usePercentTooltip + value: root.value + from: root.from + to: root.to + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml index 98b8eebf3..971a1eb10 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledSlider.qml @@ -46,7 +46,8 @@ Slider { property real handleWidth: root.pressed ? handlePressedWidth : handleDefaultWidth property real handleMargins: 4 property real trackDotSize: 3 - property string tooltipContent: `${Math.round(value * 100)}%` + property bool usePercentTooltip: true + property string tooltipContent: usePercentTooltip ? `${Math.round(((value - from) / (to - from)) * 100)}%` : `${Math.round(value)}` property bool wavy: configuration === StyledSlider.Configuration.Wavy // If true, the progress bar will have a wavy fill effect property bool animateWave: true property real waveAmplitudeMultiplier: wavy ? 0.5 : 0 diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml index 14f6a9c23..f705fc2a4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml @@ -16,7 +16,7 @@ Text { hintingPreference: Font.PreferDefaultHinting family: defaultFont pixelSize: Appearance?.font.pixelSize.small ?? 15 - variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main + //variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main } color: Appearance?.m3colors.m3onBackground ?? "black" linkColor: Appearance?.m3colors.m3primary diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml new file mode 100644 index 000000000..b3f44fbd8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -0,0 +1,18 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Layouts + +StyledText { + Layout.fillWidth: true + horizontalAlignment: root.textHorizontalAlignment + font { + family: Appearance.font.family.expressive + pixelSize: 20 + weight: 350 + } + color: root.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + animateChange: Config.options.background.widgets.clock.digital.animateChange +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml index 97e1e468a..82556a3ed 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml @@ -26,7 +26,7 @@ AbstractBackgroundWidget { visibleWhenLocked: true property var textHorizontalAlignment: { - if (root.forceCenter) + if (!Config.options.background.widgets.clock.digital.adaptiveAlignment || root.forceCenter) return Text.AlignHCenter; if (root.x < root.scaledScreenWidth / 3) return Text.AlignLeft; @@ -63,33 +63,7 @@ AbstractBackgroundWidget { anchors.horizontalCenter: parent.horizontalCenter shown: root.clockStyle === "digital" && (root.shouldShow) fade: false - sourceComponent: ColumnLayout { - id: clockColumn - spacing: 6 - - ClockText { - font.pixelSize: 90 - text: DateTime.time - } - ClockText { - Layout.topMargin: -5 - text: DateTime.longDate - } - StyledText { - // Somehow gets fucked up if made a ClockText??? - visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 - Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment - font { - pixelSize: Appearance.font.pixelSize.normal - weight: 350 - } - color: root.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - text: Config.options.background.widgets.clock.quote.text - } - } + sourceComponent: DigitalClock {} } StatusRow { anchors.horizontalCenter: parent.horizontalCenter @@ -154,19 +128,6 @@ AbstractBackgroundWidget { } } - component ClockText: StyledText { - Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment - font { - family: Appearance.font.family.expressive - pixelSize: 20 - weight: Font.DemiBold - } - color: root.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - animateChange: Config.options.background.widgets.clock.digital.animateChange - } component ClockStatusText: Row { id: statusTextRow property alias statusIcon: statusIconWidget.text diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml new file mode 100644 index 000000000..1d4e92f21 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -0,0 +1,74 @@ +pragma ComponentBehavior: Bound + +//TODO: fix imports to only what is necessary +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell.Io + +ColumnLayout { + id: clockColumn + spacing: 6 + + property bool isVertical: Config.options.background.widgets.clock.digital.vertical + + Item { + Layout.fillWidth: true + implicitHeight: timeTextTop.font.pixelSize + (clockColumn.isVertical ? timeTextBottom.font.pixelSize + 10 : 0) + implicitWidth: Math.max(timeTextTop.paintedWidth, timeTextBottom.paintedWidth) + + ClockText { + id: timeTextTop + text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + } + font { + pixelSize: Config.options.background.widgets.clock.digital.size + weight: Config.options.background.widgets.clock.digital.weight + } + } + ClockText { + id: timeTextBottom + text: clockColumn.isVertical ? DateTime.time.substring(3, 5) : "" + visible: clockColumn.isVertical + + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + font { + pixelSize: Config.options.background.widgets.clock.digital.size + weight: Config.options.background.widgets.clock.digital.weight + } + } + } + + ClockText { + visible: Config.options.background.widgets.clock.digital.showDate + Layout.topMargin: clockColumn.isVertical ? -10 : 0 + text: DateTime.longDate + } + StyledText { + // Somehow gets fucked up if made a ClockText??? + visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 + Layout.fillWidth: true + horizontalAlignment: root.textHorizontalAlignment + font { + pixelSize: Appearance.font.pixelSize.normal + weight: 350 + } + color: root.colText + style: Text.Raised + styleColor: Appearance.colors.colShadow + text: Config.options.background.widgets.clock.quote.text + } +} + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index 66630cc24..18c30853e 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -120,48 +120,52 @@ ContentPage { } } - ContentSubsection { - visible: !Config.options.background.widgets.clock.showOnlyWhenLocked - title: Translation.tr("Clock style") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.style - onSelected: newValue => { - Config.options.background.widgets.clock.style = newValue; - } - options: [ - { - displayName: Translation.tr("Digital"), - icon: "timer_10", - value: "digital" - }, - { - displayName: Translation.tr("Cookie"), - icon: "cookie", - value: "cookie" + ConfigRow { + ContentSubsection { + visible: !Config.options.background.widgets.clock.showOnlyWhenLocked + title: Translation.tr("Clock style") + Layout.fillWidth: true + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.style + onSelected: newValue => { + Config.options.background.widgets.clock.style = newValue; } - ] + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } } - } - ContentSubsection { - title: Translation.tr("Clock style (locked)") - ConfigSelectionArray { - currentValue: Config.options.background.widgets.clock.styleLocked - onSelected: newValue => { - Config.options.background.widgets.clock.styleLocked = newValue; - } - options: [ - { - displayName: Translation.tr("Digital"), - icon: "timer_10", - value: "digital" - }, - { - displayName: Translation.tr("Cookie"), - icon: "cookie", - value: "cookie" + ContentSubsection { + title: Translation.tr("Clock style (locked)") + Layout.fillWidth: false + ConfigSelectionArray { + currentValue: Config.options.background.widgets.clock.styleLocked + onSelected: newValue => { + Config.options.background.widgets.clock.styleLocked = newValue; } - ] + options: [ + { + displayName: Translation.tr("Digital"), + icon: "timer_10", + value: "digital" + }, + { + displayName: Translation.tr("Cookie"), + icon: "cookie", + value: "cookie" + } + ] + } } } @@ -169,12 +173,66 @@ ContentPage { visible: settingsClock.digitalPresent title: Translation.tr("Digital clock settings") - ConfigSwitch { - buttonIcon: "animation" - text: Translation.tr("Animate time change") - checked: Config.options.background.widgets.clock.digital.animateChange - onCheckedChanged: { - Config.options.background.widgets.clock.digital.animateChange = checked; + ConfigRow { + ConfigSwitch { + buttonIcon: "vertical_distribute" + text: Translation.tr("Vertical") + checked: Config.options.background.widgets.clock.digital.vertical + onCheckedChanged: { + Config.options.background.widgets.clock.digital.vertical = checked; + } + } + ConfigSwitch { + buttonIcon: "animation" + text: Translation.tr("Animate time change") + checked: Config.options.background.widgets.clock.digital.animateChange + onCheckedChanged: { + Config.options.background.widgets.clock.digital.animateChange = checked; + } + } + } + + ConfigRow { + ConfigSwitch { + buttonIcon: "date_range" + text: Translation.tr("Show date") + checked: Config.options.background.widgets.clock.digital.showDate + onCheckedChanged: { + Config.options.background.widgets.clock.digital.showDate = checked; + } + } + ConfigSwitch { + buttonIcon: "activity_zone" + text: Translation.tr("Use adaptive alignment") + checked: Config.options.background.widgets.clock.digital.adaptiveAlignment + onCheckedChanged: { + Config.options.background.widgets.clock.digital.adaptiveAlignment = checked; + } + StyledToolTip { + text: Translation.tr("Aligns the date and quote to left, center or right depending on its position on the screen.") + } + } + } + ConfigSlider { + text: Translation.tr("Font weight") + value: Config.options.background.widgets.clock.digital.weight + usePercentTooltip: false + buttonIcon: "format_bold" + from: 1 + to: 1000 + onValueChanged: { + Config.options.background.widgets.clock.digital.weight = value; + } + } + ConfigSlider { + text: Translation.tr("Font Size") + value: Config.options.background.widgets.clock.digital.size + usePercentTooltip: false + buttonIcon: "format_size" + from: 70 + to: 150 + onValueChanged: { + Config.options.background.widgets.clock.digital.size = value; } } } From fd209851d94378205c65e11d54db8a86d8169195 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Fri, 28 Nov 2025 23:21:58 +0300 Subject: [PATCH 027/154] digital clock: use clocktext instead of styledtext --- .../ii/modules/common/widgets/StyledText.qml | 2 +- .../ii/background/widgets/clock/DigitalClock.qml | 14 +++----------- 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml index f705fc2a4..db6df827c 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml @@ -16,7 +16,7 @@ Text { hintingPreference: Font.PreferDefaultHinting family: defaultFont pixelSize: Appearance?.font.pixelSize.small ?? 15 - //variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main + //variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main // Disabled for now because it causes some fonts to not display their weight properly } color: Appearance?.m3colors.m3onBackground ?? "black" linkColor: Appearance?.m3colors.m3primary diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 1d4e92f21..ac94d254f 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -54,19 +54,11 @@ ColumnLayout { Layout.topMargin: clockColumn.isVertical ? -10 : 0 text: DateTime.longDate } - StyledText { - // Somehow gets fucked up if made a ClockText??? + ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 - Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment - font { - pixelSize: Appearance.font.pixelSize.normal - weight: 350 - } - color: root.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow + font.pixelSize: Appearance.font.pixelSize.normal text: Config.options.background.widgets.clock.quote.text + animateChange: false } } From 21f2a9c65d228a457d3038ce526040f24c7de279 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Fri, 28 Nov 2025 23:48:06 +0300 Subject: [PATCH 028/154] fix: find a better solution on variableAxes --- .../.config/quickshell/ii/modules/common/widgets/StyledText.qml | 2 +- .../ii/modules/ii/background/widgets/clock/ClockText.qml | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml index db6df827c..14f6a9c23 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledText.qml @@ -16,7 +16,7 @@ Text { hintingPreference: Font.PreferDefaultHinting family: defaultFont pixelSize: Appearance?.font.pixelSize.small ?? 15 - //variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main // Disabled for now because it causes some fonts to not display their weight properly + variableAxes: shouldUseNumberFont ? ({}) : Appearance.font.variableAxes.main } color: Appearance?.m3colors.m3onBackground ?? "black" linkColor: Appearance?.m3colors.m3primary diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml index b3f44fbd8..d16103ed7 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -10,6 +10,7 @@ StyledText { family: Appearance.font.family.expressive pixelSize: 20 weight: 350 + variableAxes: ({}) } color: root.colText style: Text.Raised From 6b90e37b0f16678bd90858e3339b03161996849a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:23:19 +0100 Subject: [PATCH 029/154] waffles: expandable notifs --- .../ii/modules/waffle/bar/BarButton.qml | 29 +---- .../ii/modules/waffle/looks/AcrylicButton.qml | 42 +++++++ .../modules/waffle/looks/AcrylicRectangle.qml | 9 +- .../ii/modules/waffle/looks/Looks.qml | 3 + .../ii/modules/waffle/looks/WText.qml | 3 + .../NotificationHeaderButton.qml | 18 ++- .../SmallBorderedIconAndTextButton.qml | 11 +- .../notificationCenter/WNotificationGroup.qml | 14 ++- .../WSingleNotification.qml | 106 ++++++++++++++++-- 9 files changed, 185 insertions(+), 50 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml index 53a37cba0..4502b22f7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/BarButton.qml @@ -5,17 +5,12 @@ import qs.modules.common import qs.modules.common.functions import qs.modules.waffle.looks -WButton { +AcrylicButton { id: root property var altAction: () => {} property var middleClickAction: () => {} - colBackground: ColorUtils.transparentize(Looks.colors.bg1) - colBackgroundHover: Looks.colors.bg1Hover - colBackgroundActive: Looks.colors.bg1Active - property color colBackgroundBorder - property color color Layout.fillHeight: true topInset: 4 bottomInset: 4 @@ -23,16 +18,7 @@ WButton { rightInset: 0 horizontalPadding: 8 - colBackgroundBorder: ColorUtils.transparentize(Looks.colors.bg1Border, (root.checked || root.hovered) ? Looks.backgroundTransparency : 1) - color: { - if (root.down) { - return root.colBackgroundActive - } else if ((root.hovered && !root.down) || root.checked) { - return root.colBackgroundHover - } else { - return root.colBackground - } - } + colBackground: ColorUtils.transparentize(Looks.colors.bg1) MouseArea { anchors.fill: parent @@ -50,15 +36,4 @@ WButton { } } - background: AcrylicRectangle { - shiny: ((root.hovered && !root.down) || root.checked) - color: root.color - radius: Looks.radius.medium - border.width: 1 - border.color: root.colBackgroundBorder - - Behavior on border.color { - animation: Looks.transition.color.createObject(this) - } - } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml new file mode 100644 index 000000000..ef5c0747a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicButton.qml @@ -0,0 +1,42 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WButton { + id: root + + colBackground: Looks.colors.bg1 + colBackgroundHover: Looks.colors.bg1Hover + colBackgroundActive: Looks.colors.bg1Active + property color colBackgroundBorder + property color color + property alias border: background.border + property alias shinyColor: background.borderColor + + colBackgroundBorder: ColorUtils.transparentize(color, (root.checked || root.hovered) ? Looks.backgroundTransparency : 0) + color: { + if (root.down) { + return root.colBackgroundActive + } else if ((root.hovered && !root.down) || root.checked) { + return root.colBackgroundHover + } else { + return root.colBackground + } + } + + background: AcrylicRectangle { + id: background + shiny: ((root.hovered && !root.down) || root.checked) + color: root.color + radius: Looks.radius.medium + border.width: 1 + border.color: root.colBackgroundBorder + + Behavior on border.color { + animation: Looks.transition.color.createObject(this) + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml index 7e041d111..7fecaa068 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/AcrylicRectangle.qml @@ -9,16 +9,17 @@ Rectangle { id: root property bool shiny: true // Top border - property color borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, shiny ? 0.5 : 1) + property color borderColor: ColorUtils.transparentize(Looks.colors.bg1Hover, 0.7) + property color internalBorderColor: ColorUtils.transparentize(borderColor, shiny ? 0.0 : 1) color: Looks.colors.bg1Hover radius: Looks.radius.medium Behavior on color { animation: Looks.transition.color.createObject(this) } - Behavior on borderColor { + Behavior on internalBorderColor { animation: Looks.transition.color.createObject(this) } - onBorderColorChanged: { + onInternalBorderColorChanged: { borderCanvas.requestPaint(); } @@ -32,7 +33,7 @@ Rectangle { var ctx = getContext("2d"); ctx.clearRect(0, 0, width, height); - var borderColor = root.borderColor; + var borderColor = root.internalBorderColor; var r = root.radius; var fadeLength = Math.max(1, r); diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index e55f840cb..a321fa98e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -53,6 +53,7 @@ Singleton { property color controlBgHover: '#57575B' property color controlFg: "#FFFFFF" property color accentUnfocused: "#848484" + property color link: "#235CCF" } darkColors: QtObject { id: darkColors @@ -80,6 +81,7 @@ Singleton { property color controlBgHover: "#CFCED1" property color controlFg: "#454545" property color accentUnfocused: "#989898" + property color link: "#A7C9FC" } colors: QtObject { id: colors @@ -110,6 +112,7 @@ Singleton { 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 link: root.dark ? root.darkColors.link : root.lightColors.link property color danger: "#C42B1C" property color dangerActive: "#B62D1F" property color warning: "#FF9900" diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml index 240fd63be..0da156893 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml @@ -8,8 +8,11 @@ Text { color: Looks.colors.fg font { + hintingPreference: Font.PreferFullHinting family: Looks.font.family.ui pixelSize: Looks.font.pixelSize.normal weight: Looks.font.weight.regular } + + linkColor: Looks.colors.link } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml index 860451fc3..9aa20a690 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationHeaderButton.qml @@ -8,18 +8,24 @@ import qs.modules.common.functions import qs.modules.waffle.looks WBorderlessButton { - id: headerButton + id: root Layout.fillWidth: false - implicitWidth: 16 - implicitHeight: 16 + property real implicitSize: 16 + implicitWidth: implicitSize + implicitHeight: implicitSize color: "transparent" + colForeground: root.hovered && !root.pressed ? Looks.colors.fg : Looks.colors.fg1 + + Behavior on colForeground { + animation: Looks.transition.color.createObject(this) + } contentItem: Item { FluentIcon { anchors.centerIn: parent - implicitSize: 16 - icon: headerButton.icon.name - color: headerButton.hovered && !headerButton.pressed ? Looks.colors.fg : Looks.colors.fg1 + implicitSize: root.implicitSize + icon: root.icon.name + color: root.colForeground } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml index c4331a7dc..faab4d90d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/SmallBorderedIconAndTextButton.qml @@ -2,18 +2,27 @@ import QtQuick import qs import qs.services import qs.modules.common +import qs.modules.common.functions import qs.modules.waffle.looks -SmallBorderedIconButton { +AcrylicButton { id: root property bool iconVisible: true property string iconName: "" property bool iconFilled: true + colBackground: Looks.colors.bg2 + colBackgroundHover: Looks.colors.bg2Hover + colBackgroundActive: Looks.colors.bg2Active + property color colBorder: Looks.colors.bg2Border + property color colBorderToggled: Looks.colors.accent + border.color: checked ? colBorderToggled : colBorder + leftPadding: 12 rightPadding: 12 implicitWidth: focusButtonContent.implicitWidth + leftPadding + rightPadding + implicitHeight: 24 contentItem: Row { id: focusButtonContent diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 837c7c016..21ef2c544 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -12,6 +12,7 @@ MouseArea { required property var notificationGroup readonly property var notifications: notificationGroup?.notifications ?? [] + property bool expanded: false implicitWidth: contentLayout.implicitWidth implicitHeight: contentLayout.implicitHeight @@ -34,12 +35,23 @@ MouseArea { interactive: false spacing: 4 model: ScriptModel { - values: root.notifications.slice().reverse() + values: root.expanded ? root.notifications.slice().reverse() : root.notifications.slice(-1) + objectProp: "notificationId" } delegate: WSingleNotification { + required property int index required property var modelData width: ListView.view.width notification: modelData + groupExpandControlMessage: { + if (root.notifications.length === 0) return ""; + if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1); + if (index === root.notifications.length - 1) return Translation.tr("See fewer"); + return ""; + } + onGroupExpandToggle: { + root.expanded = !root.expanded; + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 42a832adb..95780133c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -13,10 +13,17 @@ MouseArea { required property var notification property bool expanded: false + property string groupExpandControlMessage: "" + signal groupExpandToggle + hoverEnabled: true implicitHeight: contentItem.implicitHeight implicitWidth: contentItem.implicitWidth + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } + Rectangle { id: contentItem anchors.fill: parent @@ -26,32 +33,109 @@ MouseArea { implicitHeight: notificationContent.implicitHeight + padding * 2 implicitWidth: notificationContent.implicitWidth + padding * 2 border.width: 1 - border.color: Looks.applyContentTransparency(Looks.colors.ambientShadow) + border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1) ColumnLayout { id: notificationContent anchors.fill: parent anchors.margins: contentItem.padding + spacing: 19 RowLayout { Layout.fillWidth: true - WText { - text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) + + ExpandButton { + Layout.topMargin: -2 + } + + Item { + Layout.fillWidth: true + } + + NotificationHeaderButton { + Layout.rightMargin: 4 + opacity: root.containsMouse ? 1 : 0 + icon.name: "dismiss" + implicitSize: 12 + onClicked: { + Qt.callLater(() => { + Notifications.discardNotification(root.notification?.notificationId); + }); + } } } ColumnLayout { Layout.fillWidth: true - WText { - Layout.fillWidth: true - elide: Text.ElideRight - text: root.notification.summary + spacing: 3 + + SummaryText {} + BodyText {} + } + + AcrylicButton { + id: groupExpandButton + visible: root.groupExpandControlMessage !== "" + Layout.bottomMargin: 2 + horizontalPadding: 10 + implicitHeight: 24 + implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2 + onClicked: root.groupExpandToggle() + contentItem: Item { + WText { + id: expandButtonText + anchors.centerIn: parent + text: root.groupExpandControlMessage + } } + } + } + } + + component SummaryText: WText { + Layout.fillWidth: true + elide: Text.ElideRight + text: root.notification?.summary + font.pixelSize: Looks.font.pixelSize.large + } + + component BodyText: WText { + Layout.fillWidth: true + Layout.fillHeight: true + elide: Text.ElideRight + verticalAlignment: Text.AlignTop + wrapMode: Text.Wrap + maximumLineCount: root.expanded ? 100 : 1 + text: root.notification?.body + color: Looks.colors.subfg + } + + component ExpandButton: NotificationHeaderButton { + id: expandButton + implicitWidth: expandButtonContent.implicitWidth + onClicked: root.expanded = !root.expanded + + contentItem: Item { + id: expandButtonContent + implicitWidth: expandButtonRow.implicitWidth + implicitHeight: expandButtonRow.implicitHeight + RowLayout { + id: expandButtonRow + anchors.centerIn: parent + spacing: 8 WText { - Layout.fillWidth: true - elide: Text.ElideRight - wrapMode: Text.Wrap - maximumLineCount: root.expanded ? 100 : 1 + color: expandButton.colForeground + text: NotificationUtils.getFriendlyNotifTimeString(root.notification?.time) + } + FluentIcon { + Layout.rightMargin: 12 + icon: "chevron-down" + implicitSize: 18 + rotation: root.expanded ? -180 : 0 + color: expandButton.colForeground + Behavior on rotation { + animation: Looks.transition.rotate.createObject(this) + } } } } From d2c019f8de66e1cbd268878e626c4d1f3a237241 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 29 Nov 2025 00:37:15 +0100 Subject: [PATCH 030/154] fix "+0 notifications" --- .../ii/modules/waffle/notificationCenter/WNotificationGroup.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 21ef2c544..122469771 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -44,7 +44,7 @@ MouseArea { width: ListView.view.width notification: modelData groupExpandControlMessage: { - if (root.notifications.length === 0) return ""; + if (root.notifications.length <= 1) return ""; if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1); if (index === root.notifications.length - 1) return Translation.tr("See fewer"); return ""; From 3cf14671ad5ffcb984c1e176e5839b2ca885f8fe Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 29 Nov 2025 02:40:28 +0300 Subject: [PATCH 031/154] fix: get gif size properly --- .../modules/common/utils/ImageDownloaderProcess.qml | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/utils/ImageDownloaderProcess.qml b/dots/.config/quickshell/ii/modules/common/utils/ImageDownloaderProcess.qml index 11ff92a6d..3a86b2fe1 100644 --- a/dots/.config/quickshell/ii/modules/common/utils/ImageDownloaderProcess.qml +++ b/dots/.config/quickshell/ii/modules/common/utils/ImageDownloaderProcess.qml @@ -18,14 +18,19 @@ Process { running: true command: ["bash", "-c", - `mkdir -p $(dirname '${processFilePath(filePath)}'); [ -f '${processFilePath(filePath)}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath(filePath)}' && magick identify -format '%w %h' '${processFilePath(filePath)}'[0]` + `mkdir -p $(dirname '${processFilePath()}'); [ -f '${processFilePath()}' ] || curl -sSL '${sourceUrl}' -o '${processFilePath()}' && file '${processFilePath()}'` ] stdout: StdioCollector { id: imageSizeOutputCollector onStreamFinished: { const output = imageSizeOutputCollector.text.trim(); - const [width, height] = output.split(" ").map(Number); - root.done(root.filePath, width, height); + const match = output.match(/(\d+)\s*x\s*(\d+)/); + + if (match) { + const width = Number(match[1]); + const height = Number(match[2]); + root.done(root.filePath, width, height); + } } } } From c11814b1f8a9a5bd54fdfdd360628386c6c6c1f7 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 29 Nov 2025 14:35:52 +0300 Subject: [PATCH 032/154] fix: weight property not properly applied (styleName conflict) --- .../ii/modules/ii/background/widgets/clock/ClockText.qml | 1 + 1 file changed, 1 insertion(+) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml index d16103ed7..39267b83d 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -11,6 +11,7 @@ StyledText { pixelSize: 20 weight: 350 variableAxes: ({}) + styleName: "" } color: root.colText style: Text.Raised From 677fa06b06934174109e00206b2661ed2d686468 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 29 Nov 2025 12:51:31 +0100 Subject: [PATCH 033/154] waffles: notifications: image support --- .../common/functions/NotificationUtils.qml | 24 +++ .../common/widgets/NotificationItem.qml | 31 +--- .../ii/modules/waffle/bar/StartButton.qml | 1 + .../NotificationCenterContent.qml | 19 ++- .../WSingleNotification.qml | 157 ++++++++++++++---- 5 files changed, 172 insertions(+), 60 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml index 8a336e8ad..d6adcc0f6 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/NotificationUtils.qml @@ -84,4 +84,28 @@ Singleton { // Older dates return Qt.formatDateTime(messageTime, "MMMM dd"); } + + function processNotificationBody(body, appName) { + let processedBody = body + + // Clean Chromium-based browsers notifications - remove first line + if (appName) { + const lowerApp = appName.toLowerCase() + const chromiumBrowsers = [ + "brave", "chrome", "chromium", "vivaldi", "opera", "microsoft edge" + ] + + if (chromiumBrowsers.some(name => lowerApp.includes(name))) { + const lines = body.split('\n\n') + + if (lines.length > 1 && lines[0].startsWith(' lowerApp.includes(name))) { - const lines = body.split('\n\n') - - if (lines.length > 1 && lines[0].startsWith('") + return NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
") } } } ColumnLayout { // Expanded content + id: expandedContentColumn Layout.fillWidth: true opacity: root.expanded ? 1 : 0 visible: opacity > 0 @@ -218,8 +197,8 @@ Item { // Notification item area elide: Text.ElideRight textFormat: Text.RichText text: { - return `` + - `${processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` + return `` + + `${NotificationUtils.processNotificationBody(notificationObject.body, notificationObject.appName || notificationObject.summary).replace(/\n/g, "
")}` } onLinkActivated: (link) => { @@ -293,6 +272,8 @@ Item { // Notification item area id: actionRepeater model: notificationObject.actions NotificationActionButton { + id: notifAction + required property var modelData Layout.fillWidth: true buttonText: modelData.text urgency: notificationObject.urgency diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml index f4a15cc00..5ec96eeb4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml @@ -7,6 +7,7 @@ import qs.services import qs.modules.common import qs.modules.waffle.looks +// TODO: Replace the icon with QMLized svg (with /usr/lib/qt6/bin/svgtoqml) for proper micro-animation AppButton { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index 797cee28a..47ad2f92d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -20,6 +20,7 @@ WBarAttachedPanelContent { property bool collapsed: false contentItem: ColumnLayout { + id: contentLayout anchors { horizontalCenter: parent.horizontalCenter top: parent.top @@ -41,9 +42,24 @@ WBarAttachedPanelContent { } contentItem: NotificationPaneContent { implicitWidth: calendarColumnLayout.implicitWidth - implicitHeight: Notifications.list.length > 0 ? (notificationArea.height - notificationPane.borderWidth * 2) : 230 + implicitHeight: { + if (Notifications.list.length > 0) { + return ((contentLayout.height - calendarPane.height - contentLayout.spacing) - notificationPane.borderWidth * 2) + } + return 230; + } + Timer { + id: enableTimer + interval: Config.options.hacks.arbitraryRaceConditionDelay + onTriggered: heightBehavior.enabled = true; + } Behavior on implicitHeight { + id: heightBehavior + enabled: false + Component.onCompleted: { + enableTimer.restart(); + } animation: Looks.transition.enter.createObject(this) } } @@ -51,6 +67,7 @@ WBarAttachedPanelContent { } WPane { + id: calendarPane contentItem: ColumnLayout { id: calendarColumnLayout spacing: 0 diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 95780133c..856ddb160 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell @@ -41,52 +42,115 @@ MouseArea { anchors.margins: contentItem.padding spacing: 19 - RowLayout { + // Header + SingleNotificationHeader { Layout.fillWidth: true + } - ExpandButton { - Layout.topMargin: -2 + // Content + Item { + id: actualContent + Layout.fillWidth: true + Layout.fillHeight: true + property real spacing: 16 + implicitHeight: Math.max(contentColumn.implicitHeight, imageLoader.height) + implicitWidth: contentColumn.implicitWidth + + Loader { + id: imageLoader + active: root.notification.image != "" + sourceComponent: StyledImage { + width: 48 + height: 48 + sourceSize.width: width + sourceSize.height: height + source: root.notification.image + } } - Item { - Layout.fillWidth: true - } + ColumnLayout { + id: contentColumn + anchors { + top: parent.top + left: parent.left + right: parent.right + } + spacing: 3 - NotificationHeaderButton { - Layout.rightMargin: 4 - opacity: root.containsMouse ? 1 : 0 - icon.name: "dismiss" - implicitSize: 12 - onClicked: { - Qt.callLater(() => { - Notifications.discardNotification(root.notification?.notificationId); - }); + SummaryText { + id: summaryText + Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0 + } + BodyText { + Layout.leftMargin: imageLoader.active ? imageLoader.width + actualContent.spacing : 0 + // onLineLaidOut: (line) => { + // if (!imageLoader.active) return; + // const dodgeDistance = imageLoader.width + actualContent.spacing; + // // print(line.y, dodgeDistance) + // if (summaryText.height + line.y > dodgeDistance) { + // line.x -= dodgeDistance; + // line.width += dodgeDistance; + // } + // } } } } - ColumnLayout { + // Actions + ActionsRow { Layout.fillWidth: true - spacing: 3 - - SummaryText {} - BodyText {} } - AcrylicButton { - id: groupExpandButton - visible: root.groupExpandControlMessage !== "" + // "+1 notifications" button + GroupExpandButton { Layout.bottomMargin: 2 - horizontalPadding: 10 - implicitHeight: 24 - implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2 - onClicked: root.groupExpandToggle() - contentItem: Item { - WText { - id: expandButtonText - anchors.centerIn: parent - text: root.groupExpandControlMessage - } + } + } + } + + component SingleNotificationHeader: RowLayout { + ExpandButton { + Layout.topMargin: -2 + } + + Item { + Layout.fillWidth: true + } + + NotificationHeaderButton { + Layout.rightMargin: 4 + opacity: root.containsMouse ? 1 : 0 + icon.name: "dismiss" + implicitSize: 12 + onClicked: { + Qt.callLater(() => { + Notifications.discardNotification(root.notification?.notificationId); + }); + } + } + } + + component ActionsRow: RowLayout { + visible: root.expanded && root.notification.actions.length > 0 + uniformCellSizes: true + Repeater { + id: actionRepeater + model: root.notification.actions + delegate: WBorderedButton { + id: actionButton + Layout.fillHeight: true + required property var modelData + Layout.fillWidth: true + verticalPadding: 16 + horizontalPadding: 12 + text: modelData.text + implicitHeight: actionButtonText.implicitHeight + verticalPadding * 2 + contentItem: WText { + id: actionButtonText + text: actionButton.text + font.pixelSize: Looks.font.pixelSize.large + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.Wrap } } } @@ -106,8 +170,17 @@ MouseArea { verticalAlignment: Text.AlignTop wrapMode: Text.Wrap maximumLineCount: root.expanded ? 100 : 1 - text: root.notification?.body + text: { + if (root.expanded) + return `` + `${NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "
")}`; + return NotificationUtils.processNotificationBody(root.notification.body, root.notification.appName || root.notification.summary).replace(/\n/g, "
"); + } color: Looks.colors.subfg + textFormat: root.expanded ? Text.RichText : Text.StyledText + onLinkActivated: link => { + Qt.openUrlExternally(link); + GlobalStates.sidebarRightOpen = false; + } } component ExpandButton: NotificationHeaderButton { @@ -140,4 +213,20 @@ MouseArea { } } } + + component GroupExpandButton: AcrylicButton { + id: groupExpandButton + visible: root.groupExpandControlMessage !== "" + horizontalPadding: 10 + implicitHeight: 24 + implicitWidth: expandButtonText.implicitWidth + horizontalPadding * 2 + onClicked: root.groupExpandToggle() + contentItem: Item { + WText { + id: expandButtonText + anchors.centerIn: parent + text: root.groupExpandControlMessage + } + } + } } From 889298287423dc3469d967142c02b3968b2694c7 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 29 Nov 2025 14:57:38 +0300 Subject: [PATCH 034/154] feat: refactor font settings and add width config option --- .../quickshell/ii/modules/common/Config.qml | 7 +++++-- .../background/widgets/clock/DigitalClock.qml | 14 ++++++++++---- .../ii/modules/settings/BackgroundConfig.qml | 19 +++++++++++++++---- 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index eb62d0955..89169fbdf 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -190,8 +190,11 @@ Singleton { property bool showDate: true property bool animateChange: true property bool vertical: false - property real weight: 350 - property real size: 90 + property JsonObject font: JsonObject { + property real weight: 350 + property real width: 100 + property real size: 90 + } } property JsonObject quote: JsonObject { property bool enable: false diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index ac94d254f..90a65a0dd 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -29,8 +29,11 @@ ColumnLayout { horizontalCenter: parent.horizontalCenter } font { - pixelSize: Config.options.background.widgets.clock.digital.size - weight: Config.options.background.widgets.clock.digital.weight + pixelSize: Config.options.background.widgets.clock.digital.font.size + weight: Config.options.background.widgets.clock.digital.font.weight + variableAxes: ({ + "wdth": Config.options.background.widgets.clock.digital.font.width, + }) } } ClockText { @@ -43,8 +46,11 @@ ColumnLayout { horizontalCenter: parent.horizontalCenter } font { - pixelSize: Config.options.background.widgets.clock.digital.size - weight: Config.options.background.widgets.clock.digital.weight + pixelSize: Config.options.background.widgets.clock.digital.font.size + weight: Config.options.background.widgets.clock.digital.font.weight + variableAxes: ({ + "wdth": Config.options.background.widgets.clock.digital.font.width, + }) } } } diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index 18c30853e..bb08ccb0c 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -215,24 +215,35 @@ ContentPage { } ConfigSlider { text: Translation.tr("Font weight") - value: Config.options.background.widgets.clock.digital.weight + value: Config.options.background.widgets.clock.digital.font.weight usePercentTooltip: false buttonIcon: "format_bold" from: 1 to: 1000 onValueChanged: { - Config.options.background.widgets.clock.digital.weight = value; + Config.options.background.widgets.clock.digital.font.weight = value; + } + } + ConfigSlider { + text: Translation.tr("Font Width") + value: Config.options.background.widgets.clock.digital.font.width + usePercentTooltip: false + buttonIcon: "fit_width" + from: 25 + to: 125 + onValueChanged: { + Config.options.background.widgets.clock.digital.font.width = value; } } ConfigSlider { text: Translation.tr("Font Size") - value: Config.options.background.widgets.clock.digital.size + value: Config.options.background.widgets.clock.digital.font.size usePercentTooltip: false buttonIcon: "format_size" from: 70 to: 150 onValueChanged: { - Config.options.background.widgets.clock.digital.size = value; + Config.options.background.widgets.clock.digital.font.size = value; } } } From f5fea85334b738186d75bd680b94e44f9ec6439e Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 29 Nov 2025 15:05:07 +0300 Subject: [PATCH 035/154] fix: capital letters --- .../quickshell/ii/modules/settings/BackgroundConfig.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index bb08ccb0c..eddc06fa9 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -225,7 +225,7 @@ ContentPage { } } ConfigSlider { - text: Translation.tr("Font Width") + text: Translation.tr("Font width") value: Config.options.background.widgets.clock.digital.font.width usePercentTooltip: false buttonIcon: "fit_width" @@ -236,7 +236,7 @@ ContentPage { } } ConfigSlider { - text: Translation.tr("Font Size") + text: Translation.tr("Font size") value: Config.options.background.widgets.clock.digital.font.size usePercentTooltip: false buttonIcon: "format_size" From 44422004791e8d651bf3094bfb6d51e7f1dee737 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 29 Nov 2025 23:23:58 +0100 Subject: [PATCH 036/154] waffles: start menu base --- dots/.config/hypr/hyprland/keybinds.conf | 32 ++--- dots/.config/quickshell/ii/GlobalStates.qml | 1 + .../ii/assets/icons/fluent/power-filled.svg | 4 + .../ii/assets/icons/fluent/power.svg | 4 + .../fluent/system-search-checked-dark.svg | 14 +-- .../fluent/system-search-checked-light.svg | 16 +-- .../ii/modules/common/Directories.qml | 4 + .../ii/modules/common/widgets/StyledImage.qml | 10 ++ .../modules/common/widgets/StyledToolTip.qml | 1 + .../ii/modules/ii/overview/Overview.qml | 22 ++-- .../ii/modules/waffle/bar/AppButton.qml | 2 +- .../ii/modules/waffle/bar/StartButton.qml | 3 +- .../ii/modules/waffle/bar/WidgetsButton.qml | 2 +- .../waffle/bar/tasks/WindowPreview.qml | 2 +- .../ii/modules/waffle/looks/Looks.qml | 6 +- .../{bar/AppIcon.qml => looks/WAppIcon.qml} | 1 - .../ii/modules/waffle/looks/WButton.qml | 3 +- .../ii/modules/waffle/looks/WTextInput.qml | 18 +++ .../NotificationCenterContent.qml | 3 +- .../WSingleNotification.qml | 16 ++- .../ii/modules/waffle/startMenu/SearchBar.qml | 84 +++++++++++++ .../waffle/startMenu/SearchPageContent.qml | 16 +++ .../waffle/startMenu/StartMenuContent.qml | 39 ++++++ .../waffle/startMenu/StartPageContent.qml | 98 +++++++++++++++ .../waffle/startMenu/WaffleStartMenu.qml | 119 ++++++++++++++++++ dots/.config/quickshell/ii/shell.qml | 5 +- 26 files changed, 464 insertions(+), 61 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/power.svg rename dots/.config/quickshell/ii/modules/waffle/{bar/AppIcon.qml => looks/WAppIcon.qml} (94%) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 020fe582d..6032ab65a 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -4,27 +4,27 @@ #! ##! Shell # These absolutely need to be on top, or they won't work consistently -bindid = Super, Super_L, Toggle overview, global, quickshell:overviewToggleRelease # Toggle overview/launcher -bindid = Super, Super_R, Toggle overview, global, quickshell:overviewToggleRelease # [hidden] Toggle overview/launcher +bindid = Super, Super_L, Toggle search, global, quickshell:searchToggleRelease # Toggle search +bindid = Super, Super_R, Toggle search, global, quickshell:searchToggleRelease # [hidden] Toggle search bind = Super, Super_L, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback) bind = Super, Super_R, exec, qs -c $qsConfig ipc call TEST_ALIVE || pkill fuzzel || fuzzel # [hidden] Launcher (fallback) -binditn = Super, catchall, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Ctrl, Super_L, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Ctrl, Super_R, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:272, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:273, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:274, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:275, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:276, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse:277, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse_up, global, quickshell:overviewToggleReleaseInterrupt # [hidden] -bind = Super, mouse_down,global, quickshell:overviewToggleReleaseInterrupt # [hidden] +binditn = Super, catchall, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Ctrl, Super_L, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Ctrl, Super_R, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:272, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:273, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:274, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:275, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:276, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse:277, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse_up, global, quickshell:searchToggleReleaseInterrupt # [hidden] +bind = Super, mouse_down,global, quickshell:searchToggleReleaseInterrupt # [hidden] bindit = ,Super_L, global, quickshell:workspaceNumber # [hidden] bindit = ,Super_R, global, quickshell:workspaceNumber # [hidden] +bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # Toggle overview bindd = Super, V, Clipboard history >> clipboard, global, quickshell:overviewClipboardToggle # Clipboard history >> clipboard bindd = Super, Period, Emoji >> clipboard, global, quickshell:overviewEmojiToggle # Emoji >> clipboard -bind = Super, Tab, global, quickshell:overviewWorkspacesToggle # [hidden] Toggle overview/launcher (alt) bind = Super, A, global, quickshell:sidebarLeftToggle # Toggle left sidebar bind = Super+Alt, A, global, quickshell:sidebarLeftToggleDetach # [hidden] bind = Super, B, global, quickshell:sidebarLeftToggle # [hidden] @@ -218,8 +218,8 @@ submap = global #! # Testing -bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account image and Discord icon. Oh and here is a random image in your Pictures folder: \"Testing" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden] -bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your Pictures folder and Discord icon.\nFlick right to dismiss!" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Open profile image" -A "action2=Useless button" -A "action3=Cry more"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden] +bind = Super+Alt, f11, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification with body image" "This notification should contain your user account image and Discord icon. Oh and here is a random image in your Pictures folder: \"Testing" -a "Hyprland keybind" -p -h "string:image-path:/var/lib/AccountsService/icons/$USER" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Open the random image" -A "action3=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"; [[ $ACTION == *action2 ]] && xdg-open \"$RANDOM_IMAGE\"' # [hidden] +bind = Super+Alt, f12, exec, bash -c 'RANDOM_IMAGE=$(find ~/Pictures -type f | grep -v -i "nipple" | grep -v -i "pussy" | shuf -n 1); ACTION=$(notify-send "Test notification" "This notification should contain a random image in your Pictures folder and Discord icon.\nFlick right to dismiss!" -a "Discord (fake)" -p -h "string:image-path:$RANDOM_IMAGE" -t 6000 -i "discord" -A "openImage=Profile image" -A "action2=Useless button"); [[ $ACTION == *openImage ]] && xdg-open "/var/lib/AccountsService/icons/$USER"' # [hidden] bind = Super+Alt, Equal, exec, notify-send "Urgent notification" "Ah hell no" -u critical -a 'Hyprland keybind' # [hidden] ##! Session diff --git a/dots/.config/quickshell/ii/GlobalStates.qml b/dots/.config/quickshell/ii/GlobalStates.qml index 972495c64..85a0414d6 100644 --- a/dots/.config/quickshell/ii/GlobalStates.qml +++ b/dots/.config/quickshell/ii/GlobalStates.qml @@ -20,6 +20,7 @@ Singleton { property bool overlayOpen: false property bool overviewOpen: false property bool regionSelectorOpen: false + property bool searchOpen: false property bool screenLocked: false property bool screenLockContainsCharacters: false property bool screenUnlockFailed: false diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg new file mode 100644 index 000000000..2cfa6dba7 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/power-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/power.svg b/dots/.config/quickshell/ii/assets/icons/fluent/power.svg new file mode 100644 index 000000000..5c28fe986 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/power.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg index af58d933f..84f42c500 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-dark.svg @@ -26,8 +26,8 @@ inkscape:zoom="4.65625" inkscape:cx="32" inkscape:cy="32" - inkscape:window-width="1197" - inkscape:window-height="1020" + inkscape:window-width="1595" + inkscape:window-height="664" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -48,10 +48,10 @@ diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg index 8d0e69fce..76af86e67 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/system-search-checked-light.svg @@ -23,10 +23,10 @@ inkscape:pagecheckerboard="0" inkscape:deskcolor="#d1d1d1" inkscape:zoom="6.5849319" - inkscape:cx="26.95548" + inkscape:cx="27.031411" inkscape:cy="26.423963" - inkscape:window-width="1257" - inkscape:window-height="1020" + inkscape:window-width="1621" + inkscape:window-height="820" inkscape:window-x="0" inkscape:window-y="0" inkscape:window-maximized="1" @@ -35,10 +35,10 @@ id="defs2"> diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 56f647684..9afbed44b 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -1,6 +1,7 @@ pragma Singleton pragma ComponentBehavior: Bound +import qs.services import qs.modules.common.functions import QtCore import QtQuick @@ -46,6 +47,9 @@ Singleton { property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`) property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`) property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`) + property string userAvatarPathAccountsService: FileUtils.trimFileProtocol(`/var/lib/AccountsService/icons/${SystemInfo.username}`) + property string userAvatarPathRicersAndWeirdSystems: FileUtils.trimFileProtocol(`${Directories.home}.face`) + property string userAvatarPathRicersAndWeirdSystems2: FileUtils.trimFileProtocol(`${Directories.home}.face.icon`) // Cleanup on init Component.onCompleted: { Quickshell.execDetached(["mkdir", "-p", `${shellConfig}`]) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml index c360b536c..17dfc56c4 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledImage.qml @@ -12,4 +12,14 @@ Image { Behavior on opacity { animation: Appearance.animation.elementMoveEnter.numberAnimation.createObject(this) } + + property list fallbacks: [] + property int currentFallbackIndex: 0 + + onStatusChanged: { + if (status === Image.Error && currentFallbackIndex < fallbacks.length) { + source = fallbacks[currentFallbackIndex]; + currentFallbackIndex += 1; + } + } } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml index 53797fb66..4688b29be 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledToolTip.qml @@ -20,6 +20,7 @@ ToolTip { hintingPreference: Font.PreferNoHinting // Prevent shaky text } + delay: 0 visible: internalVisibleCondition contentItem: StyledToolTipContent { diff --git a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml index 248b46b56..c435f7f8a 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/Overview.qml @@ -162,7 +162,7 @@ Scope { } IpcHandler { - target: "overview" + target: "search" function toggle() { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; @@ -185,8 +185,8 @@ Scope { } GlobalShortcut { - name: "overviewToggle" - description: "Toggles overview on press" + name: "searchToggle" + description: "Toggles search on press" onPressed: { GlobalStates.overviewOpen = !GlobalStates.overviewOpen; @@ -201,16 +201,8 @@ Scope { } } GlobalShortcut { - name: "overviewClose" - description: "Closes overview" - - onPressed: { - GlobalStates.overviewOpen = false; - } - } - GlobalShortcut { - name: "overviewToggleRelease" - description: "Toggles overview on release" + name: "searchToggleRelease" + description: "Toggles search on release" onPressed: { GlobalStates.superReleaseMightTrigger = true; @@ -225,8 +217,8 @@ Scope { } } GlobalShortcut { - name: "overviewToggleReleaseInterrupt" - description: "Interrupts possibility of overview being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + name: "searchToggleReleaseInterrupt" + description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." onPressed: { GlobalStates.superReleaseMightTrigger = false; diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml index 20af517fe..440695a2e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/AppButton.qml @@ -66,7 +66,7 @@ BarButton { } } - AppIcon { + WAppIcon { id: iconWidget anchors.centerIn: parent iconName: root.iconName diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml index 5ec96eeb4..a92a85578 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml @@ -14,8 +14,9 @@ AppButton { leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0 iconName: down ? "start-here-pressed" : "start-here" + checked: GlobalStates.searchOpen onClicked: { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... + GlobalStates.searchOpen = !GlobalStates.searchOpen; } BarToolTip { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml index 51a3175bc..c1c16096b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/WidgetsButton.qml @@ -42,7 +42,7 @@ AppButton { } spacing: 6 - AppIcon { + WAppIcon { id: iconWidget anchors.verticalCenter: parent.verticalCenter iconName: root.iconName diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml index 2839a6747..9f114609f 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml @@ -43,7 +43,7 @@ Button { Layout.fillHeight: false spacing: 8 - AppIcon { + WAppIcon { id: appIcon Layout.leftMargin: Looks.radius.large - root.padding + 2 Layout.alignment: Qt.AlignVCenter diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index a321fa98e..609c9877b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -54,6 +54,7 @@ Singleton { property color controlFg: "#FFFFFF" property color accentUnfocused: "#848484" property color link: "#235CCF" + property color inputBg: ColorUtils.transparentize(bg0, 0.4) } darkColors: QtObject { id: darkColors @@ -71,7 +72,7 @@ Singleton { property color bg2: '#8a8a8a' property color bg2Hover: '#b1b1b1' property color bg2Active: '#919191' - property color bg2Border: '#c4c4c4' + property color bg2Border: '#bdbdbd' property color subfg: "#CED1D7" property color fg: "#FFFFFF" property color fg1: "#D1D1D1" @@ -82,6 +83,7 @@ Singleton { property color controlFg: "#454545" property color accentUnfocused: "#989898" property color link: "#A7C9FC" + property color inputBg: ColorUtils.transparentize(darkColors.bg0, 0.5) } colors: QtObject { id: colors @@ -112,6 +114,7 @@ Singleton { 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" @@ -121,6 +124,7 @@ Singleton { property color accentActive: Appearance.colors.colPrimaryActive property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000" + property color selection: Appearance.colors.colPrimaryContainer } radius: QtObject { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml similarity index 94% rename from dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml index 48ff26104..6f71c65bb 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/AppIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml @@ -2,7 +2,6 @@ import QtQuick import org.kde.kirigami as Kirigami import qs.services import qs.modules.common -import qs.modules.waffle.looks Kirigami.Icon { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml index 6b2bc4ecb..ceed470ba 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WButton.qml @@ -53,10 +53,11 @@ Button { // Hover stuff signal hoverTimedOut property bool shouldShowTooltip: false + ToolTip.delay: 400 property Timer hoverTimer: Timer { id: hoverTimer running: root.hovered - interval: 400 + interval: root.ToolTip.delay onTriggered: { root.hoverTimedOut(); } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml new file mode 100644 index 000000000..a3f3e8a40 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml @@ -0,0 +1,18 @@ +import QtQuick +import QtQuick.Controls + +TextInput { + id: root + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: Looks.colors.fg + + font { + hintingPreference: Font.PreferFullHinting + family: Looks.font.family.ui + pixelSize: Looks.font.pixelSize.large + weight: Looks.font.weight.regular + } + + selectionColor: Looks.colors.selection +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml index 47ad2f92d..f49477988 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationCenterContent.qml @@ -68,9 +68,8 @@ WBarAttachedPanelContent { WPane { id: calendarPane - contentItem: ColumnLayout { + contentItem: WPanelPageColumn { id: calendarColumnLayout - spacing: 0 DateHeader { Layout.fillWidth: true Synchronizer on collapsed { diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 856ddb160..895ab6892 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -13,7 +13,7 @@ MouseArea { id: root required property var notification - property bool expanded: false + property bool expanded: notification.actions.length > 0 property string groupExpandControlMessage: "" signal groupExpandToggle hoverEnabled: true @@ -58,13 +58,19 @@ MouseArea { Loader { id: imageLoader + anchors { + top: parent.top + left: parent.left + } active: root.notification.image != "" sourceComponent: StyledImage { - width: 48 - height: 48 - sourceSize.width: width - sourceSize.height: height + readonly property int size: 48 + width: size + height: size + sourceSize.width: size + sourceSize.height: size source: root.notification.image + fillMode: Image.PreserveAspectFit } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml new file mode 100644 index 000000000..6bd23ae9b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml @@ -0,0 +1,84 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +FooterRectangle { + id: root + + property bool searching: text.length > 0 + property alias text: searchInput.text + + Component.onCompleted: searchInput.forceActiveFocus() + + focus: true + color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter + + implicitWidth: 832 // TODO: Make sizes naturally inferred + implicitHeight: 63 + + Rectangle { + id: outline + anchors { + fill: parent + leftMargin: 32 + rightMargin: 32 + topMargin: 16 + bottomMargin: 15 + } + color: "transparent" + radius: height / 2 + border.width: 1 + border.color: Looks.colors.bg2Border + } + + Rectangle { + id: searchInputBg + anchors.fill: outline + anchors.margins: 1 + radius: height / 2 + color: Looks.colors.inputBg + + RowLayout { + anchors.fill: parent + spacing: 11 + + WAppIcon { + Layout.leftMargin: 14 + iconName: "system-search-checked" + separateLightDark: true + implicitSize: 18 + } + + WTextInput { + id: searchInput + focus: true + Layout.fillWidth: true + + WText { + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + color: Looks.colors.accentUnfocused + text: Translation.tr("Search for apps") // should also have "", settings, and documents" but we don't have those + visible: searchInput.text.length === 0 + font.pixelSize: Looks.font.pixelSize.large + } + } + } + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.IBeamCursor + acceptedButtons: Qt.NoButton + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml new file mode 100644 index 000000000..cdbb7d3b8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml @@ -0,0 +1,16 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +BodyRectangle { + id: root + + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml new file mode 100644 index 000000000..6e97f88be --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -0,0 +1,39 @@ +pragma ComponentBehavior: Bound +import Qt.labs.synchronizer +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +WBarAttachedPanelContent { + id: root + + property bool searching: false + property string searchText: "" + + contentItem: WPane { + contentItem: WPanelPageColumn { + SearchBar { + focus: true + Layout.fillWidth: true + Synchronizer on searching { + property alias target: root.searching + } + Synchronizer on text { + property alias source: root.searchText + } + } + Loader { + id: pageContentLoader + Layout.fillWidth: true + source: root.searching ? "SearchPageContent.qml" : "StartPageContent.qml" + } + } + } + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml new file mode 100644 index 000000000..36e605187 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -0,0 +1,98 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import org.kde.kirigami as Kirigami +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WPanelPageColumn { + id: root + + WPanelSeparator {} + + BodyRectangle { + implicitHeight: 736 // TODO: Make sizes naturally inferred + } + + WPanelSeparator {} + + StartFooter { + Layout.fillWidth: true + } + + component StartFooter: FooterRectangle { + implicitHeight: 63 + + UserButton { + anchors { + left: parent.left + leftMargin: 52 + bottom: parent.bottom + bottomMargin: 12 + } + } + + PowerButton { + anchors { + right: parent.right + rightMargin: 52 + bottom: parent.bottom + bottomMargin: 12 + } + } + } + + component UserButton: WBorderlessButton { + id: userButton + implicitWidth: userButtonRow.implicitWidth + 12 * 2 + implicitHeight: 40 + + contentItem: Item { + RowLayout { + id: userButtonRow + anchors.centerIn: parent + spacing: 12 + + StyledImage { + id: avatar + // Use this for free fallback because I'm lazy + Layout.alignment: Qt.AlignTop + sourceSize: Qt.size(32, 32) + source: Directories.userAvatarPathAccountsService + fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Circle { + diameter: avatar.height + } + } + } + WText { + Layout.alignment: Qt.AlignVCenter + text: SystemInfo.username + } + } + } + } + + component PowerButton: WBorderlessButton { + implicitWidth: 40 + implicitHeight: 40 + + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: "power" + implicitSize: 20 + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml new file mode 100644 index 000000000..9b59fb0c8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml @@ -0,0 +1,119 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets + +Scope { + id: root + + Connections { + target: GlobalStates + + function onSearchOpenChanged() { + if (GlobalStates.searchOpen) + panelLoader.active = true; + } + } + + Loader { + id: panelLoader + active: GlobalStates.searchOpen + sourceComponent: PanelWindow { + id: panelWindow + exclusiveZone: 0 + WlrLayershell.namespace: "quickshell:wStartMenu" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + color: "transparent" + + anchors { + bottom: Config.options.waffles.bar.bottom + top: !Config.options.waffles.bar.bottom + left: Config.options.waffles.bar.leftAlignApps + } + + implicitWidth: content.implicitWidth + implicitHeight: content.implicitHeight + + HyprlandFocusGrab { + id: focusGrab + active: true + windows: [panelWindow] + onCleared: content.close() + } + + Connections { + target: GlobalStates + function onSearchOpenChanged() { + if (!GlobalStates.searchOpen) + content.close(); + } + } + + StartMenuContent { + id: content + anchors.fill: parent + focus: true + + onClosed: { + GlobalStates.searchOpen = false; + panelLoader.active = false; + } + } + } + } + + IpcHandler { + target: "search" + + function toggle() { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + function close() { + GlobalStates.searchOpen = false; + } + function open() { + GlobalStates.searchOpen = true; + } + function toggleReleaseInterrupt() { + GlobalStates.superReleaseMightTrigger = false; + } + } + + GlobalShortcut { + name: "searchToggle" + description: "Toggles search on press" + + onPressed: { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + } + GlobalShortcut { + name: "searchToggleRelease" + description: "Toggles search on release" + + onPressed: { + GlobalStates.superReleaseMightTrigger = true; + } + + onReleased: { + if (!GlobalStates.superReleaseMightTrigger) { + GlobalStates.superReleaseMightTrigger = true; + return; + } + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + } + GlobalShortcut { + name: "searchToggleReleaseInterrupt" + description: "Interrupts possibility of search being toggled on release. " + "This is necessary because GlobalShortcut.onReleased in quickshell triggers whether or not you press something else while holding the key. " + "To make sure this works consistently, use binditn = MODKEYS, catchall in an automatically triggered submap that includes everything." + + onPressed: { + GlobalStates.superReleaseMightTrigger = false; + } + } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 3f57c0c28..7bb565d11 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -33,6 +33,7 @@ import qs.modules.waffle.background import qs.modules.waffle.bar import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay +import qs.modules.waffle.startMenu import QtQuick import QtQuick.Window @@ -77,11 +78,13 @@ ShellRoot { PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} } PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} } PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} } + PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } PanelLoader { identifier: "wBar"; component: WaffleBar {} } PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } + PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } ReloadPopup {} component PanelLoader: LazyLoader { @@ -94,7 +97,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", "wNotificationCenter", "wOnScreenDisplay", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From b45f2dd235f1e4e868d5748688ea4e23088c4f30 Mon Sep 17 00:00:00 2001 From: jwihardi Date: Sun, 30 Nov 2025 15:31:02 -0500 Subject: [PATCH 037/154] added libqalculate gentoo --- ...ets-1.0-r3.ebuild => illogical-impulse-widgets-1.0-r4.ebuild} | 1 + 1 file changed, 1 insertion(+) rename sdata/dist-gentoo/illogical-impulse-widgets/{illogical-impulse-widgets-1.0-r3.ebuild => illogical-impulse-widgets-1.0-r4.ebuild} (95%) diff --git a/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild b/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild similarity index 95% rename from sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild rename to sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild index 95ffeffba..9ffceea09 100644 --- a/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r3.ebuild +++ b/sdata/dist-gentoo/illogical-impulse-widgets/illogical-impulse-widgets-1.0-r4.ebuild @@ -21,6 +21,7 @@ RDEPEND=" app-misc/songrec app-i18n/translate-shell gui-apps/wlogout + sci-libs/libqalculate " ##### CUSTOM EBUILDS # app-misc/songrec From 8d2c8bd38ee48e96c7751ffaf3595e5056a460e3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:27:01 +0100 Subject: [PATCH 038/154] waffles: power menu and user account menu --- .../ii/assets/icons/fluent/corporation.svg | 17 ++ .../icons/fluent/start-here-pressed.svg | 142 +++++++++++++--- .../ii/assets/icons/fluent/start-here.svg | 142 +++++++++++++--- .../quickshell/ii/modules/common/Config.qml | 1 + .../ii/modules/waffle/looks/WIcons.qml | 4 + .../ii/modules/waffle/looks/WMenu.qml | 83 +++++++++ .../ii/modules/waffle/looks/WMenuItem.qml | 93 +++++++++++ .../ii/modules/waffle/looks/WPane.qml | 2 +- .../ii/modules/waffle/looks/WToolTip.qml | 2 + .../modules/waffle/looks/WToolTipContent.qml | 1 + .../ii/modules/waffle/looks/WUserAvatar.qml | 27 +++ .../notificationCenter/WNotificationGroup.qml | 1 + .../waffle/startMenu/StartPageContent.qml | 157 ++++++++++++++++-- 13 files changed, 611 insertions(+), 61 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg new file mode 100644 index 000000000..0ed1a0319 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/corporation.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg index e6b950eca..2efc81c45 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here-pressed.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg index 708d5a71b..9f7b4a177 100644 --- a/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg +++ b/dots/.config/quickshell/ii/assets/icons/fluent/start-here.svg @@ -1,24 +1,120 @@ - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 1716aa2ef..6331df0b1 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -153,6 +153,7 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" + property string manageUser: "kcmshell6 kcm_users" property string network: "kitty -1 fish -c nmtui" property string networkEthernet: "kcmshell6 kcm_networkmanagement" property string taskManager: "plasma-systemmonitor --page-name Processes" diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml index 1d24170bc..7fe121165 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml @@ -7,6 +7,10 @@ import qs.services Singleton { id: root + function pathForName(iconName) { + return Quickshell.shellPath(`assets/icons/fluent/${iconName}.svg`); + } + function wifiIconForStrength(strength) { if (strength > 75) return "wifi-1"; diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml new file mode 100644 index 000000000..a208f355b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -0,0 +1,83 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +Menu { + id: root + + property bool downDirection: false + property bool hasIcons: false // TODO: implement + + implicitWidth: background.implicitWidth + root.padding * 2 + implicitHeight: background.implicitHeight + root.padding * 2 + padding: 3 + property real sourceEdgeMargin: -implicitHeight + clip: true + + enter: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: -root.implicitHeight + to: root.padding + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + exit: Transition { + NumberAnimation { + property: "sourceEdgeMargin" + from: root.padding + to: -root.implicitHeight + duration: 150 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + } + + background: WPane { + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + contentItem: Rectangle { + color: Looks.colors.bg1Base + implicitWidth: menuListView.implicitWidth + root.padding * 2 + implicitHeight: root.contentItem.implicitHeight + root.padding * 2 + } + } + + contentItem: ListView { + id: menuListView + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.padding * 2 + topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding + bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin + } + implicitHeight: contentHeight + implicitWidth: Array.from({ + length: count + }, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b) + + model: root.contentModel + } + + delegate: WMenuItem { + id: menuItemDelegate + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml new file mode 100644 index 000000000..3030bc122 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -0,0 +1,93 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Hyprland +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +MenuItem { + id: root + + 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 colForeground: Looks.colors.fg + property color colForegroundToggled: Looks.colors.accentFg + property color colForegroundDisabled: ColorUtils.transparentize(Looks.colors.subfg, 0.4) + property color color: { + if (!root.enabled) + return colBackground; + if (root.checked) { + if (root.down) { + return root.colBackgroundToggledActive; + } else if (root.hovered) { + return root.colBackgroundToggledHover; + } else { + return root.colBackgroundToggled; + } + } + if (root.down) { + return root.colBackgroundActive; + } else if (root.hovered) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + property color fgColor: { + if (root.checked) + return root.colForegroundToggled; + if (root.enabled) + return root.colForeground; + return root.colForegroundDisabled; + } + + property real inset: 2 + topInset: inset + bottomInset: inset + leftInset: inset + rightInset: inset + horizontalPadding: 11 + + background: Rectangle { + id: backgroundRect + radius: Looks.radius.medium + color: root.color + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + } + + 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 + } + 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 + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index be75cc30a..f281a8b7c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -10,7 +10,7 @@ import qs.modules.waffle.looks Item { id: root - required property Item contentItem + property Item contentItem property real radius: Looks.radius.large property alias border: borderRect property alias borderColor: borderRect.border.color diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml index 3c8d20d26..7368836cc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTip.qml @@ -25,6 +25,8 @@ StyledToolTip { verticalPadding: 8 horizontalPadding: 10 + delay: 400 + contentItem: WToolTipContent { id: tooltipContent realContentItem: root.realContentItem diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml index 13c78fc7e..b0a400df0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WToolTipContent.qml @@ -6,6 +6,7 @@ Item { id: root anchors.centerIn: parent required property Item realContentItem + property alias radius: realContent.radius property real verticalPadding: 8 property real horizontalPadding: 10 implicitWidth: realContent.implicitWidth + 2 * 2 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml new file mode 100644 index 000000000..07499d51b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WUserAvatar.qml @@ -0,0 +1,27 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +StyledImage { + id: avatar + Layout.alignment: Qt.AlignTop + sourceSize: Qt.size(32, 32) + source: Directories.userAvatarPathAccountsService + fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Circle { + diameter: avatar.height + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 122469771..658bc03ae 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -7,6 +7,7 @@ import qs.modules.common.widgets import qs.modules.common.functions import qs.modules.waffle.looks +// TODO: Swipe to dismiss MouseArea { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml index 36e605187..eb3c96a80 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -4,7 +4,6 @@ import QtQuick.Controls import QtQuick.Layouts import Qt5Compat.GraphicalEffects import Quickshell -import org.kde.kirigami as Kirigami import qs import qs.services import qs.modules.common @@ -60,20 +59,8 @@ WPanelPageColumn { anchors.centerIn: parent spacing: 12 - StyledImage { - id: avatar - // Use this for free fallback because I'm lazy - Layout.alignment: Qt.AlignTop + WUserAvatar { sourceSize: Qt.size(32, 32) - source: Directories.userAvatarPathAccountsService - fallbacks: [Directories.userAvatarPathRicersAndWeirdSystems, Directories.userAvatarPathRicersAndWeirdSystems2] - - layer.enabled: true - layer.effect: OpacityMask { - maskSource: Circle { - diameter: avatar.height - } - } } WText { Layout.alignment: Qt.AlignVCenter @@ -81,9 +68,116 @@ WPanelPageColumn { } } } + + onClicked: { + userMenu.open(); + } + + WToolTip { + text: SystemInfo.username + } + + Popup { + id: userMenu + x: -51 + y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10 + + background: null + + WToolTipContent { + id: popupContent + horizontalPadding: 10 + verticalPadding: 7 + radius: Looks.radius.large + realContentItem: Item { + implicitWidth: userMenuContentLayout.implicitWidth + implicitHeight: userMenuContentLayout.implicitHeight + + ColumnLayout { + id: userMenuContentLayout + anchors { + fill: parent + leftMargin: popupContent.horizontalPadding + rightMargin: popupContent.horizontalPadding + topMargin: popupContent.verticalPadding + bottomMargin: popupContent.verticalPadding + } + spacing: 5 + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 6 + FluentIcon { + Layout.alignment: Qt.AlignVCenter + implicitSize: 22 + icon: "corporation" + monochrome: false + } + WText { + Layout.alignment: Qt.AlignVCenter + text: "Megahard" + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + Item { Layout.fillWidth: true } + WBorderlessButton { + Layout.alignment: Qt.AlignVCenter + implicitHeight: 36 + implicitWidth: textItem.implicitWidth + 10 * 2 + contentItem: WText { + id: textItem + text: Translation.tr("Sign out") + font.pixelSize: Looks.font.pixelSize.large + } + onClicked: Session.logout() + } + } + Item { // Force min width 360 (using min on the item somehow doesn't work) + implicitWidth: 334 + } + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 7 + Layout.leftMargin: 6 + spacing: 12 + WUserAvatar { + sourceSize: Qt.size(58, 58) + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 2 + WText { + text: SystemInfo.username + font.pixelSize: Looks.font.pixelSize.larger + font.weight: Looks.font.weight.strong + } + WText { + color: Looks.colors.fg1 + text: Translation.tr("Local account") + } + WText { + color: Looks.colors.accent + text: Translation.tr("Manage my account") + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) + GlobalStates.searchOpen = false; + } + } + } + } + } + } + } + } + } } component PowerButton: WBorderlessButton { + id: powerButton implicitWidth: 40 implicitHeight: 40 @@ -94,5 +188,40 @@ WPanelPageColumn { implicitSize: 20 } } + + WToolTip { + extraVisibleCondition: !powerMenu.visible + text: qsTr("Power") + } + + onClicked: { + powerMenu.open() + } + + WMenu { + id: powerMenu + x: -powerMenu.implicitWidth / 2 + powerButton.implicitWidth / 2 + y: -powerMenu.implicitHeight - 4 + Action { + icon.name: "lock-closed" + text: Translation.tr("Lock") + onTriggered: Session.lock() + } + Action { + icon.name: "weather-moon" + text: Translation.tr("Sleep") + onTriggered: Session.suspend() + } + Action { + icon.name: "power" + text: Translation.tr("Shut down") + onTriggered: Session.poweroff() + } + Action { + icon.name: "arrow-counterclockwise" + text: Translation.tr("Restart") + onTriggered: Session.reboot() + } + } } } From 4cad401ea6ceccb6f83426c9ed9198908d67d4b6 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 1 Dec 2025 13:28:41 +0100 Subject: [PATCH 039/154] add layerrules for more waffles stuff --- dots/.config/hypr/hyprland/rules.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index 17a4d5af9..cfb1e3f7b 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -159,7 +159,9 @@ layerrule = animation slide right, quickshell:sidebarRight layerrule = animation slide left, quickshell:sidebarLeft layerrule = animation slide, quickshell:verticalBar layerrule = animation slide top, quickshell:wallpaperSelector +layerrule = noanim, quickshell:wNotificationCenter layerrule = noanim, quickshell:wOnScreenDisplay +layerrule = noanim, quickshell:wStartMenu # Launchers need to be FAST layerrule = noanim, gtk4-layer-shell From 4ce4645749255f26ce642877d3470d277a2a98a0 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 1 Dec 2025 22:00:36 +0100 Subject: [PATCH 040/154] move launcher search logic to a service --- .../ii/modules/ii/overview/SearchBar.qml | 2 +- .../ii/modules/ii/overview/SearchWidget.qml | 268 +---------------- .../quickshell/ii/services/LauncherSearch.qml | 278 ++++++++++++++++++ 3 files changed, 285 insertions(+), 263 deletions(-) create mode 100644 dots/.config/quickshell/ii/services/LauncherSearch.qml diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml index 5cd7b9ac5..6a5de6a7e 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchBar.qml @@ -81,7 +81,7 @@ RowLayout { } } - onTextChanged: root.searchingText = text + onTextChanged: LauncherSearch.query = text onAccepted: { if (appResults.count > 0) { diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index 03ebfce1b..c0ca28d62 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -14,81 +14,11 @@ import Quickshell.Io Item { // Wrapper id: root readonly property string xdgConfigHome: Directories.config - property string searchingText: "" + property string searchingText: LauncherSearch.query property bool showResults: searchingText != "" implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 - property string mathResult: "" - property bool clipboardWorkSafetyActive: { - const enabled = Config.options.workSafety.enable.clipboard; - const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) - return enabled && sensitiveNetwork; - } - - property var searchActions: [ - { - action: "accentcolor", - execute: args => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); - } - }, - { - action: "dark", - execute: () => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]); - } - }, - { - action: "konachanwallpaper", - execute: () => { - Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]); - } - }, - { - action: "light", - execute: () => { - Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); - } - }, - { - action: "superpaste", - execute: args => { - if (!/^(\d+)/.test(args.trim())) { // Invalid if doesn't start with numbers - Quickshell.execDetached([ - "notify-send", - Translation.tr("Superpaste"), - Translation.tr("Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries").arg(Config.options.search.prefix.action), - "-a", "Shell" - ]); - return; - } - const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim()); - const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1; - const isImage = !!syntaxMatch[2]; - Cliphist.superpaste(count, isImage); - } - }, - { - action: "todo", - execute: args => { - Todo.addTask(args); - } - }, - { - action: "wallpaper", - execute: () => { - GlobalStates.wallpaperSelectorOpen = true; - } - }, - { - action: "wipeclipboard", - execute: () => { - Cliphist.wipe(); - } - }, - ] - function focusFirstItem() { appResults.currentIndex = 0; } @@ -103,13 +33,13 @@ Item { // Wrapper function cancelSearch() { searchBar.searchInput.selectAll(); - root.searchingText = ""; + LauncherSearch.query = ""; searchBar.animateWidth = true; } function setSearchingText(text) { searchBar.searchInput.text = text; - root.searchingText = text; + LauncherSearch.query = text; } function containsUnsafeLink(entry) { @@ -118,34 +48,6 @@ Item { // Wrapper return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); } - Timer { - id: nonAppResultsTimer - interval: Config.options.search.nonAppResultDelay - onTriggered: { - let expr = root.searchingText; - if (expr.startsWith(Config.options.search.prefix.math)) { - expr = expr.slice(Config.options.search.prefix.math.length); - } - mathProcess.calculateExpression(expr); - } - } - - Process { - id: mathProcess - property list baseCommand: ["qalc", "-t"] - function calculateExpression(expression) { - mathProcess.running = false; - mathProcess.command = baseCommand.concat(expression); - mathProcess.running = true; - } - stdout: SplitParser { - onRead: data => { - root.mathResult = data; - root.focusFirstItem(); - } - } - } - Keys.onPressed: event => { // Prevent Esc and Backspace from registering if (event.key === Qt.Key_Escape) @@ -285,167 +187,9 @@ Item { // Wrapper model: ScriptModel { id: model objectProp: "key" - values: { - // Search results are handled here - ////////////////// Skip? ////////////////// - if (root.searchingText == "") - return []; - - ///////////// Special cases /////////////// - if (root.searchingText.startsWith(Config.options.search.prefix.clipboard)) { - // Clipboard - const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.clipboard); - return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { - const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; - let shouldBlurImage = mightBlurImage; - if (mightBlurImage) { - shouldBlurImage = shouldBlurImage && (containsUnsafeLink(array[index - 1]) || containsUnsafeLink(array[index + 1])); - } - const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}` - return { - key: type, - cliphistRawString: entry, - name: StringUtils.cleanCliphistEntry(entry), - clickActionName: "", - type: type, - execute: () => { - Cliphist.copy(entry) - }, - actions: [ - { - name: "Copy", - materialIcon: "content_copy", - execute: () => { - Cliphist.copy(entry); - } - }, - { - name: "Delete", - materialIcon: "delete", - execute: () => { - Cliphist.deleteEntry(entry); - } - } - ], - blurImage: shouldBlurImage, - blurImageText: Translation.tr("Work safety") - }; - }).filter(Boolean); - } - else if (root.searchingText.startsWith(Config.options.search.prefix.emojis)) { - // Clipboard - const searchString = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.emojis); - return Emojis.fuzzyQuery(searchString).map(entry => { - const emoji = entry.match(/^\s*(\S+)/)?.[1] || "" - return { - key: emoji, - cliphistRawString: entry, - bigText: emoji, - name: entry.replace(/^\s*\S+\s+/, ""), - clickActionName: "", - type: "Emoji", - execute: () => { - Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]; - } - }; - }).filter(Boolean); - } - - ////////////////// Init /////////////////// - nonAppResultsTimer.restart(); - const mathResultObject = { - key: `Math result: ${root.mathResult}`, - name: root.mathResult, - clickActionName: Translation.tr("Copy"), - type: Translation.tr("Math result"), - fontType: "monospace", - materialSymbol: 'calculate', - execute: () => { - Quickshell.clipboardText = root.mathResult; - } - }; - const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.app)).map(entry => { - entry.clickActionName = Translation.tr("Launch"); - entry.type = Translation.tr("App"); - entry.key = entry.execute - return entry; - }) - const commandResultObject = { - key: `cmd ${root.searchingText}`, - name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.shellCommand).replace("file://", ""), - clickActionName: Translation.tr("Run"), - type: Translation.tr("Run command"), - fontType: "monospace", - materialSymbol: 'terminal', - execute: () => { - let cleanedCommand = root.searchingText.replace("file://", ""); - cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand); - if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) { - cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length); - } - Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); - } - }; - const webSearchResultObject = { - key: `website ${root.searchingText}`, - name: StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch), - clickActionName: Translation.tr("Search"), - type: Translation.tr("Search the web"), - materialSymbol: 'travel_explore', - execute: () => { - let query = StringUtils.cleanPrefix(root.searchingText, Config.options.search.prefix.webSearch); - let url = Config.options.search.engineBaseUrl + query; - for (let site of Config.options.search.excludedSites) { - url += ` -site:${site}`; - } - Qt.openUrlExternally(url); - } - } - const launcherActionObjects = root.searchActions.map(action => { - const actionString = `${Config.options.search.prefix.action}${action.action}`; - if (actionString.startsWith(root.searchingText) || root.searchingText.startsWith(actionString)) { - return { - key: `Action ${actionString}`, - name: root.searchingText.startsWith(actionString) ? root.searchingText : actionString, - clickActionName: Translation.tr("Run"), - type: Translation.tr("Action"), - materialSymbol: 'settings_suggest', - execute: () => { - action.execute(root.searchingText.split(" ").slice(1).join(" ")); - } - }; - } - return null; - }).filter(Boolean); - - //////// Prioritized by prefix ///////// - let result = []; - const startsWithNumber = /^\d/.test(root.searchingText); - const startsWithMathPrefix = root.searchingText.startsWith(Config.options.search.prefix.math); - const startsWithShellCommandPrefix = root.searchingText.startsWith(Config.options.search.prefix.shellCommand); - const startsWithWebSearchPrefix = root.searchingText.startsWith(Config.options.search.prefix.webSearch); - if (startsWithNumber || startsWithMathPrefix) { - result.push(mathResultObject); - } else if (startsWithShellCommandPrefix) { - result.push(commandResultObject); - } else if (startsWithWebSearchPrefix) { - result.push(webSearchResultObject); - } - - //////////////// Apps ////////////////// - result = result.concat(appResultObjects); - - ////////// Launcher actions //////////// - result = result.concat(launcherActionObjects); - - /// Math result, command, web search /// - if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) { - if (!startsWithShellCommandPrefix) result.push(commandResultObject); - if (!startsWithNumber && !startsWithMathPrefix) result.push(mathResultObject); - if (!startsWithWebSearchPrefix) result.push(webSearchResultObject); - } - - return result; + values: LauncherSearch.results + onValuesChanged: { + root.focusFirstItem(); } } diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml new file mode 100644 index 000000000..bf23c1c48 --- /dev/null +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -0,0 +1,278 @@ +pragma Singleton + +import qs.modules.common +import qs.modules.common.models +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + property string query: "" + property var searchActions: [ + { + action: "accentcolor", + execute: args => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--noswitch", "--color", ...(args != '' ? [`${args}`] : [])]); + } + }, + { + action: "dark", + execute: () => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "dark", "--noswitch"]); + } + }, + { + action: "konachanwallpaper", + execute: () => { + Quickshell.execDetached([Quickshell.shellPath("scripts/colors/random/random_konachan_wall.sh")]); + } + }, + { + action: "light", + execute: () => { + Quickshell.execDetached([Directories.wallpaperSwitchScriptPath, "--mode", "light", "--noswitch"]); + } + }, + { + action: "superpaste", + execute: args => { + if (!/^(\d+)/.test(args.trim())) { + // Invalid if doesn't start with numbers + Quickshell.execDetached(["notify-send", Translation.tr("Superpaste"), Translation.tr("Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries").arg(Config.options.search.prefix.action), "-a", "Shell"]); + return; + } + const syntaxMatch = /^(?:(\d+)(i)?)/.exec(args.trim()); + const count = syntaxMatch[1] ? parseInt(syntaxMatch[1]) : 1; + const isImage = !!syntaxMatch[2]; + Cliphist.superpaste(count, isImage); + } + }, + { + action: "todo", + execute: args => { + Todo.addTask(args); + } + }, + { + action: "wallpaper", + execute: () => { + GlobalStates.wallpaperSelectorOpen = true; + } + }, + { + action: "wipeclipboard", + execute: () => { + Cliphist.wipe(); + } + }, + ] + + property string mathResult: "" + property bool clipboardWorkSafetyActive: { + const enabled = Config.options.workSafety.enable.clipboard; + const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + return enabled && sensitiveNetwork; + } + + function containsUnsafeLink(entry) { + if (entry == undefined) return false; + const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; + return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); + } + + Timer { + id: nonAppResultsTimer + interval: Config.options.search.nonAppResultDelay + onTriggered: { + let expr = root.query; + if (expr.startsWith(Config.options.search.prefix.math)) { + expr = expr.slice(Config.options.search.prefix.math.length); + } + mathProc.calculateExpression(expr); + } + } + + Process { + id: mathProc + property list baseCommand: ["qalc", "-t"] + function calculateExpression(expression) { + mathProc.running = false; + mathProc.command = baseCommand.concat(expression); + mathProc.running = true; + } + stdout: SplitParser { + onRead: data => { + root.mathResult = data; + } + } + } + + property list results: { + // Search results are handled here + ////////////////// Skip? ////////////////// + if (root.query == "") + return []; + + ///////////// Special cases /////////////// + if (root.query.startsWith(Config.options.search.prefix.clipboard)) { + // Clipboard + const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.clipboard); + return Cliphist.fuzzyQuery(searchString).map((entry, index, array) => { + const mightBlurImage = Cliphist.entryIsImage(entry) && root.clipboardWorkSafetyActive; + let shouldBlurImage = mightBlurImage; + if (mightBlurImage) { + shouldBlurImage = shouldBlurImage && (root.containsUnsafeLink(array[index - 1]) || root.containsUnsafeLink(array[index + 1])); + } + const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`; + return { + key: type, + cliphistRawString: entry, + name: StringUtils.cleanCliphistEntry(entry), + clickActionName: "", + type: type, + execute: () => { + Cliphist.copy(entry); + }, + actions: [ + { + name: "Copy", + materialIcon: "content_copy", + execute: () => { + Cliphist.copy(entry); + } + }, + { + name: "Delete", + materialIcon: "delete", + execute: () => { + Cliphist.deleteEntry(entry); + } + } + ], + blurImage: shouldBlurImage, + blurImageText: Translation.tr("Work safety") + }; + }).filter(Boolean); + } else if (root.query.startsWith(Config.options.search.prefix.emojis)) { + // Clipboard + const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.emojis); + return Emojis.fuzzyQuery(searchString).map(entry => { + const emoji = entry.match(/^\s*(\S+)/)?.[1] || ""; + return { + key: emoji, + cliphistRawString: entry, + bigText: emoji, + name: entry.replace(/^\s*\S+\s+/, ""), + clickActionName: "", + type: "Emoji", + execute: () => { + Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]; + } + }; + }).filter(Boolean); + } + + ////////////////// Init /////////////////// + nonAppResultsTimer.restart(); + const mathResultObject = { + key: `Math result: ${root.mathResult}`, + name: root.mathResult, + clickActionName: Translation.tr("Copy"), + type: Translation.tr("Math result"), + fontType: "monospace", + materialSymbol: 'calculate', + execute: () => { + Quickshell.clipboardText = root.mathResult; + } + }; + const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => { + entry.clickActionName = Translation.tr("Launch"); + entry.type = Translation.tr("App"); + entry.key = entry.execute; + return entry; + }); + const commandResultObject = { + key: `cmd ${root.query}`, + name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""), + clickActionName: Translation.tr("Run"), + type: Translation.tr("Run command"), + fontType: "monospace", + materialSymbol: 'terminal', + execute: () => { + let cleanedCommand = root.query.replace("file://", ""); + cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand); + if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) { + cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length); + } + Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); + } + }; + const webSearchResultObject = { + key: `website ${root.query}`, + name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch), + clickActionName: Translation.tr("Search"), + type: Translation.tr("Search the web"), + materialSymbol: 'travel_explore', + execute: () => { + let query = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch); + let url = Config.options.search.engineBaseUrl + query; + for (let site of Config.options.search.excludedSites) { + url += ` -site:${site}`; + } + Qt.openUrlExternally(url); + } + }; + const launcherActionObjects = root.searchActions.map(action => { + const actionString = `${Config.options.search.prefix.action}${action.action}`; + if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) { + return { + key: `Action ${actionString}`, + name: root.query.startsWith(actionString) ? root.query : actionString, + clickActionName: Translation.tr("Run"), + type: Translation.tr("Action"), + materialSymbol: 'settings_suggest', + execute: () => { + action.execute(root.query.split(" ").slice(1).join(" ")); + } + }; + } + return null; + }).filter(Boolean); + + //////// Prioritized by prefix ///////// + let result = []; + const startsWithNumber = /^\d/.test(root.query); + const startsWithMathPrefix = root.query.startsWith(Config.options.search.prefix.math); + const startsWithShellCommandPrefix = root.query.startsWith(Config.options.search.prefix.shellCommand); + const startsWithWebSearchPrefix = root.query.startsWith(Config.options.search.prefix.webSearch); + if (startsWithNumber || startsWithMathPrefix) { + result.push(mathResultObject); + } else if (startsWithShellCommandPrefix) { + result.push(commandResultObject); + } else if (startsWithWebSearchPrefix) { + result.push(webSearchResultObject); + } + + //////////////// Apps ////////////////// + result = result.concat(appResultObjects); + + ////////// Launcher actions //////////// + result = result.concat(launcherActionObjects); + + /// Math result, command, web search /// + if (Config.options.search.prefix.showDefaultActionsWithoutPrefix) { + if (!startsWithShellCommandPrefix) + result.push(commandResultObject); + if (!startsWithNumber && !startsWithMathPrefix) + result.push(mathResultObject); + if (!startsWithWebSearchPrefix) + result.push(webSearchResultObject); + } + + return result; + } +} From f08bbc0d67b7d52614b73da3bcf13553d783f690 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 2 Dec 2025 21:28:18 +0800 Subject: [PATCH 041/154] Update env.conf --- dots/.config/hypr/hyprland/env.conf | 8 -------- 1 file changed, 8 deletions(-) diff --git a/dots/.config/hypr/hyprland/env.conf b/dots/.config/hypr/hyprland/env.conf index bd45f9f7e..acc363cc6 100644 --- a/dots/.config/hypr/hyprland/env.conf +++ b/dots/.config/hypr/hyprland/env.conf @@ -1,11 +1,3 @@ -# ######### Input method ########## -# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland -#env = QT_IM_MODULE, fcitx -#env = XMODIFIERS, @im=fcitx -#env = SDL_IM_MODULE, fcitx -#env = GLFW_IM_MODULE, ibus -#env = INPUT_METHOD, fcitx - # ############ Wayland ############# env = ELECTRON_OZONE_PLATFORM_HINT,auto From 1d40360cf241d3925ae6c9e0581218b9e500beb2 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 2 Dec 2025 21:28:41 +0800 Subject: [PATCH 042/154] Update env.conf --- dots/.config/hypr/custom/env.conf | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/dots/.config/hypr/custom/env.conf b/dots/.config/hypr/custom/env.conf index fcd59ec86..3215877be 100644 --- a/dots/.config/hypr/custom/env.conf +++ b/dots/.config/hypr/custom/env.conf @@ -1,2 +1,10 @@ # You can put extra environment variables here # https://wiki.hyprland.org/Configuring/Environment-variables/ + +# ######### Input method ########## +# See https://fcitx-im.org/wiki/Using_Fcitx_5_on_Wayland +#env = QT_IM_MODULE, fcitx +#env = XMODIFIERS, @im=fcitx +#env = SDL_IM_MODULE, fcitx +#env = GLFW_IM_MODULE, ibus +#env = INPUT_METHOD, fcitx From cce06cc2f0e30df1e4749e45e33a7145cbb0bd1d Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 2 Dec 2025 21:29:11 +0800 Subject: [PATCH 043/154] Update execs.conf --- dots/.config/hypr/hyprland/execs.conf | 3 --- 1 file changed, 3 deletions(-) diff --git a/dots/.config/hypr/hyprland/execs.conf b/dots/.config/hypr/hyprland/execs.conf index bb5dd7225..2cad1308e 100644 --- a/dots/.config/hypr/hyprland/execs.conf +++ b/dots/.config/hypr/hyprland/execs.conf @@ -3,9 +3,6 @@ exec-once = ~/.config/hypr/hyprland/scripts/start_geoclue_agent.sh exec-once = qs -c $qsConfig & exec-once = ~/.config/hypr/custom/scripts/__restore_video_wallpaper.sh -# Input method -# exec-once = fcitx5 - # Core components (authentication, lock screen, notification daemon) exec-once = gnome-keyring-daemon --start --components=secrets exec-once = hypridle From 90f9467871ec932559831dd8dcd32935abc301b2 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Tue, 2 Dec 2025 21:29:37 +0800 Subject: [PATCH 044/154] Update execs.conf --- dots/.config/hypr/custom/execs.conf | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dots/.config/hypr/custom/execs.conf b/dots/.config/hypr/custom/execs.conf index cae4ef6c5..66bb2b8b5 100644 --- a/dots/.config/hypr/custom/execs.conf +++ b/dots/.config/hypr/custom/execs.conf @@ -1,2 +1,6 @@ # You can make apps auto-start here # Relevant Hyprland wiki section: https://wiki.hyprland.org/Configuring/Keywords/#executing + +# Input method +# exec-once = fcitx5 + From dd07a62dc02200e3c7c3ff6b56a5703125622492 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Wed, 3 Dec 2025 10:04:17 +0800 Subject: [PATCH 045/154] Skip fish/conf.d when syncing to preserve user customizations --- sdata/subcmd-install/3.files-exp.yaml | 1 + sdata/subcmd-install/3.files-legacy.sh | 6 +++++- sdata/subcmd-install/3.files.sh | 16 ++++++++++++++++ 3 files changed, 22 insertions(+), 1 deletion(-) diff --git a/sdata/subcmd-install/3.files-exp.yaml b/sdata/subcmd-install/3.files-exp.yaml index 5282e0594..63a0cad1f 100644 --- a/sdata/subcmd-install/3.files-exp.yaml +++ b/sdata/subcmd-install/3.files-exp.yaml @@ -20,6 +20,7 @@ patterns: - from: "dots/.config/fish" to: "$XDG_CONFIG_HOME/fish" mode: "sync" + excludes: ["conf.d"] condition: type: "shell" value: "fish" diff --git a/sdata/subcmd-install/3.files-legacy.sh b/sdata/subcmd-install/3.files-legacy.sh index 6ba8cefc2..98d792be8 100644 --- a/sdata/subcmd-install/3.files-legacy.sh +++ b/sdata/subcmd-install/3.files-legacy.sh @@ -30,7 +30,11 @@ esac case "${SKIP_FISH}" in true) sleep 0;; *) - install_dir__sync dots/.config/fish "$XDG_CONFIG_HOME"/fish + # Use rsync with exclude to preserve user's custom conf.d directory + if [ -d "$XDG_CONFIG_HOME"/fish ];then + warning_overwrite + fi + rsync_dir__sync_exclude dots/.config/fish "$XDG_CONFIG_HOME"/fish "conf.d" ;; esac diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index 41e9972d0..49490a49f 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -67,6 +67,22 @@ rsync_dir__sync(){ x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" rsync -a --delete --out-format='%i %n' "$1"/ "$2"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}" } +rsync_dir__sync_exclude(){ + # NOTE: This function is only for using in other functions + # Same as rsync_dir__sync but with exclude patterns support + # Usage: rsync_dir__sync_exclude [ ...] + local src="$1" + local dest_dir="$2" + shift 2 + local excludes=() + for pattern in "$@"; do + excludes+=(--exclude "$pattern") + done + x mkdir -p "$dest_dir" + local dest="$(realpath -se $dest_dir)" + x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" + rsync -a --delete "${excludes[@]}" --out-format='%i %n' "$src"/ "$dest_dir"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}" +} function install_file(){ # NOTE: Do not add prefix `v` or `x` when using this function local s=$1 From 8a9f105e75d4c080ccaf4202c99d9cb6ea55812e Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Wed, 3 Dec 2025 10:17:42 +0800 Subject: [PATCH 046/154] Eliminate repeated sudo password prompts during installation --- sdata/lib/functions.sh | 50 ++++++++++++++++++++++++++++++++++++++++++ setup | 10 +++++++++ 2 files changed, 60 insertions(+) diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index b6dcae99d..ecaf1fcc1 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -80,6 +80,56 @@ function prevent_sudo_or_root(){ root) echo -e "${STY_RED}[$0]: This script is NOT to be executed with sudo or as root. Aborting...${STY_RST}";exit 1;; esac } + +# Initialize sudo session and keep it alive in background +# Store PID in a global variable that can be accessed by trap +declare -g SUDO_KEEPALIVE_PID="" + +function sudo_init_keepalive(){ + # Check if sudo is available + if ! command -v sudo >/dev/null 2>&1; then + return 0 + fi + + # Only initialize for install-related commands that need sudo + case "${SUBCMD_NAME}" in + install|install-deps|install-setups) ;; + *) return 0 ;; + esac + + # Skip if already initialized + if [[ -n "$SUDO_KEEPALIVE_PID" ]] && kill -0 "$SUDO_KEEPALIVE_PID" 2>/dev/null; then + return 0 + fi + + # Prompt for sudo password once at the beginning + echo -e "${STY_CYAN}[$0]: Requesting sudo privileges for installation...${STY_RST}" + if ! sudo -v; then + echo -e "${STY_RED}[$0]: Failed to obtain sudo privileges. Aborting...${STY_RST}" + exit 1 + fi + + # Start background process to keep sudo session alive + # This updates the sudo timestamp every 60 seconds + ( + while true; do + sleep 60 + sudo -v 2>/dev/null || exit 0 + done + ) & + SUDO_KEEPALIVE_PID=$! + + echo -e "${STY_GREEN}[$0]: Sudo session initialized and will be kept alive (PID: $SUDO_KEEPALIVE_PID)${STY_RST}" +} + +# Stop the sudo keepalive background process +function sudo_stop_keepalive(){ + if [[ -n "$SUDO_KEEPALIVE_PID" ]] && kill -0 "$SUDO_KEEPALIVE_PID" 2>/dev/null; then + kill "$SUDO_KEEPALIVE_PID" 2>/dev/null + wait "$SUDO_KEEPALIVE_PID" 2>/dev/null + SUDO_KEEPALIVE_PID="" + fi +} function git_auto_unshallow(){ # We need this function for latest_commit_hash to work properly if [[ -f "$(git rev-parse --git-dir)/shallow" ]]; then diff --git a/setup b/setup index 8a6e2d82f..226a1ba85 100755 --- a/setup +++ b/setup @@ -8,6 +8,10 @@ source ./sdata/lib/package-installers.sh source ./sdata/lib/dist-determine.sh prevent_sudo_or_root + +# Trap to ensure sudo keepalive is stopped on exit +trap sudo_stop_keepalive EXIT INT TERM + set -e ##################################################################################### @@ -71,6 +75,8 @@ case ${SUBCMD_NAME} in $function done pause + # Initialize sudo keepalive for the entire install process + sudo_init_keepalive if [[ "${SKIP_ALLGREETING}" != true ]]; then source ${SUBCMD_DIR}/0.greeting.sh fi @@ -89,6 +95,8 @@ case ${SUBCMD_NAME} in $function done pause + # Initialize sudo keepalive for dependency installation + sudo_init_keepalive if [[ "${SKIP_ALLDEPS}" != true ]]; then source ${SUBCMD_DIR}/1.deps-router.sh fi @@ -98,6 +106,8 @@ case ${SUBCMD_NAME} in $function done pause + # Initialize sudo keepalive for setup steps + sudo_init_keepalive if [[ "${SKIP_ALLSETUPS}" != true ]]; then source ${SUBCMD_DIR}/2.setups.sh fi From ffd01741d9ac412525772c993cf72ceff0dcaecd Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Wed, 3 Dec 2025 13:58:49 +0800 Subject: [PATCH 047/154] Remove repeated code --- sdata/subcmd-install/3.files.sh | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index 49490a49f..2bd2e1190 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -62,10 +62,7 @@ rsync_dir__sync(){ # `--delete' for rsync to make sure that # original dotfiles and new ones in the SAME DIRECTORY # (eg. in ~/.config/hypr) won't be mixed together - x mkdir -p "$2" - local dest="$(realpath -se $2)" - x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" - rsync -a --delete --out-format='%i %n' "$1"/ "$2"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}" + rsync_dir__sync_exclude "$1" "$2" } rsync_dir__sync_exclude(){ # NOTE: This function is only for using in other functions From d3c1ae14b859d345d0446a7a240b72075dbca518 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Wed, 3 Dec 2025 20:58:45 +0800 Subject: [PATCH 048/154] Move trap to each needed subcommand --- setup | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/setup b/setup index 226a1ba85..9174eeb93 100755 --- a/setup +++ b/setup @@ -8,10 +8,6 @@ source ./sdata/lib/package-installers.sh source ./sdata/lib/dist-determine.sh prevent_sudo_or_root - -# Trap to ensure sudo keepalive is stopped on exit -trap sudo_stop_keepalive EXIT INT TERM - set -e ##################################################################################### @@ -77,6 +73,8 @@ case ${SUBCMD_NAME} in pause # Initialize sudo keepalive for the entire install process sudo_init_keepalive + # Set trap to cleanup when this subcommand exits + trap sudo_stop_keepalive EXIT INT TERM if [[ "${SKIP_ALLGREETING}" != true ]]; then source ${SUBCMD_DIR}/0.greeting.sh fi @@ -97,6 +95,8 @@ case ${SUBCMD_NAME} in pause # Initialize sudo keepalive for dependency installation sudo_init_keepalive + # Set trap to cleanup when this subcommand exits + trap sudo_stop_keepalive EXIT INT TERM if [[ "${SKIP_ALLDEPS}" != true ]]; then source ${SUBCMD_DIR}/1.deps-router.sh fi @@ -108,6 +108,8 @@ case ${SUBCMD_NAME} in pause # Initialize sudo keepalive for setup steps sudo_init_keepalive + # Set trap to cleanup when this subcommand exits + trap sudo_stop_keepalive EXIT INT TERM if [[ "${SKIP_ALLSETUPS}" != true ]]; then source ${SUBCMD_DIR}/2.setups.sh fi From ed9e510c32fd7f0f656878350ffed7fc0b41d349 Mon Sep 17 00:00:00 2001 From: "Celestial.y" Date: Wed, 3 Dec 2025 21:35:36 +0800 Subject: [PATCH 049/154] Remove determine subcommand install* --- sdata/lib/functions.sh | 6 ------ 1 file changed, 6 deletions(-) diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index ecaf1fcc1..0fe24f0ed 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -91,12 +91,6 @@ function sudo_init_keepalive(){ return 0 fi - # Only initialize for install-related commands that need sudo - case "${SUBCMD_NAME}" in - install|install-deps|install-setups) ;; - *) return 0 ;; - esac - # Skip if already initialized if [[ -n "$SUDO_KEEPALIVE_PID" ]] && kill -0 "$SUDO_KEEPALIVE_PID" 2>/dev/null; then return 0 From 20d60a11f7ad1f803a05903e1b4cff12fa71d2ca Mon Sep 17 00:00:00 2001 From: tmcnulty387 Date: Wed, 3 Dec 2025 13:23:41 -0500 Subject: [PATCH 050/154] Set PACMAN_AUTH to prevent repeated sudo password prompts --- sdata/dist-arch/install-deps.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/sdata/dist-arch/install-deps.sh b/sdata/dist-arch/install-deps.sh index 3cd43d6ba..ecbb3b907 100644 --- a/sdata/dist-arch/install-deps.sh +++ b/sdata/dist-arch/install-deps.sh @@ -43,6 +43,11 @@ if ! command -v pacman >/dev/null 2>&1; then exit 1 fi +# Keep makepkg from resetting sudo credentials +if [[ -z "${PACMAN_AUTH:-}" ]]; then + export PACMAN_AUTH="sudo" +fi + showfun remove_deprecated_dependencies v remove_deprecated_dependencies From 05aae36e82c0f0f1b581abc1da39429fec3edcdd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:05:55 +0100 Subject: [PATCH 051/154] overviewwidget: fix undefined warning --- .../quickshell/ii/modules/ii/overview/OverviewWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index 18501b082..f0e40d56e 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -278,7 +278,7 @@ Item { StyledToolTip { extraVisibleCondition: false alternativeVisibleCondition: dragArea.containsMouse && !window.Drag.active - text: `${windowData.title}\n[${windowData.class}] ${windowData.xwayland ? "[XWayland] " : ""}` + text: `${windowData?.title}\n[${windowData?.class}] ${windowData?.xwayland ? "[XWayland] " : ""}` } } } From 4055ad48fa776608521cf52dddaf78e5feb8e3e8 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 3 Dec 2025 22:07:06 +0100 Subject: [PATCH 052/154] launcher search service: use proper type instead of json clumps --- .../quickshell/ii/modules/common/Config.qml | 3 +- .../common/models/LauncherSearchResult.qml | 29 ++++ .../ii/modules/ii/overview/SearchItem.qml | 55 ++++--- .../ii/modules/ii/overview/SearchWidget.qml | 6 - .../quickshell/ii/services/LauncherSearch.qml | 150 +++++++++++------- 5 files changed, 161 insertions(+), 82 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index d26f04d65..b0b767a44 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -590,8 +590,9 @@ Singleton { // false will make (some) stuff also be like that for accuracy. // Example: the right-click menu of the Start button property JsonObject tweaks: JsonObject { - property bool smootherMenuAnimations: true property bool switchHandlePositionFix: true + property bool smootherMenuAnimations: true + property bool smootherSearchBar: true } property JsonObject bar: JsonObject { property bool bottom: true diff --git a/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml b/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml new file mode 100644 index 000000000..15beef12e --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml @@ -0,0 +1,29 @@ +import QtQuick +import Quickshell + +QtObject { + enum IconType { Material, Text, System, None } + enum FontType { Normal, Monospace } + + // General stuff + property string type: "" + property var fontType: LauncherSearchResult.FontType.Normal + property string name: "" + property string rawValue: "" + property string iconName: "" + property var iconType: LauncherSearchResult.IconType.None + property string verb: "" + property bool blurImage: false + property var execute: () => { + print("Not implemented"); + } + property var actions: [] + + // Stuff needed for DesktopEntry objects + property bool shown: true + property string comment: "" + property bool runInTerminal: false + property string genericName: "" + property list keywords: [] + +} diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchItem.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchItem.qml index 8f72db56a..a0f9b248d 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchItem.qml @@ -2,6 +2,7 @@ import qs import qs.services import qs.modules.common +import qs.modules.common.models import qs.modules.common.widgets import qs.modules.common.functions import QtQuick @@ -12,20 +13,27 @@ import Quickshell.Hyprland RippleButton { id: root - property var entry + property LauncherSearchResult entry property string query property bool entryShown: entry?.shown ?? true property string itemType: entry?.type ?? Translation.tr("App") property string itemName: entry?.name ?? "" - property string itemIcon: entry?.icon ?? "" + property var iconType: entry?.iconType + property string iconName: entry?.iconName ?? "" property var itemExecute: entry?.execute - property string fontType: entry?.fontType ?? "main" - property string itemClickActionName: entry?.clickActionName ?? "Open" - property string bigText: entry?.bigText ?? "" - property string materialSymbol: entry?.materialSymbol ?? "" - property string cliphistRawString: entry?.cliphistRawString ?? "" + property var fontType: switch(entry?.fontType) { + case LauncherSearchResult.FontType.Monospace: + return "monospace" + case LauncherSearchResult.FontType.Normal: + return "main" + default: + return "main" + } + property string itemClickActionName: entry?.verb ?? "Open" + property string bigText: entry?.iconType === LauncherSearchResult.IconType.Text ? entry?.iconName ?? "" : "" + property string materialSymbol: entry.iconType === LauncherSearchResult.IconType.Material ? entry?.iconName ?? "" : "" + property string cliphistRawString: entry?.rawValue ?? "" property bool blurImage: entry?.blurImage ?? false - property string blurImageText: entry?.blurImageText ?? "Image hidden" visible: root.entryShown property int horizontalMargin: 10 @@ -97,7 +105,7 @@ RippleButton { } Keys.onPressed: (event) => { if (event.key === Qt.Key_Delete && event.modifiers === Qt.ShiftModifier) { - const deleteAction = root.entry.actions.find(action => action.name == "Delete"); + const deleteAction = root.entry.actions.find(action => action.name == Translation.tr("Delete")); if (deleteAction) { deleteAction.execute() @@ -126,16 +134,24 @@ RippleButton { Loader { id: iconLoader active: true - sourceComponent: root.materialSymbol !== "" ? materialSymbolComponent : - root.bigText ? bigTextComponent : - root.itemIcon !== "" ? iconImageComponent : - null + sourceComponent: switch(root.iconType) { + case LauncherSearchResult.IconType.Material: + return materialSymbolComponent + case LauncherSearchResult.IconType.Text: + return bigTextComponent + case LauncherSearchResult.IconType.System: + return iconImageComponent + case LauncherSearchResult.IconType.None: + return null + default: + return null + } } Component { id: iconImageComponent IconImage { - source: Quickshell.iconPath(root.itemIcon, "image-missing") + source: Quickshell.iconPath(root.iconName, "image-missing") width: 35 height: 35 } @@ -217,7 +233,6 @@ RippleButton { maxWidth: contentColumn.width maxHeight: 140 blur: root.blurImage - blurText: root.blurImageText } } } @@ -243,8 +258,8 @@ RippleButton { delegate: RippleButton { id: actionButton required property var modelData - property string iconName: modelData.icon ?? "" - property string materialIconName: modelData.materialIcon ?? "" + property var iconType: modelData.iconType + property string iconName: modelData.iconName ?? "" implicitHeight: 34 implicitWidth: 34 @@ -256,16 +271,16 @@ RippleButton { anchors.centerIn: parent Loader { anchors.centerIn: parent - active: !(actionButton.iconName !== "") || actionButton.materialIconName + active: actionButton.iconType === LauncherSearchResult.IconType.Material || actionButton.iconName === "" sourceComponent: MaterialSymbol { - text: actionButton.materialIconName || "video_settings" + text: actionButton.iconName || "video_settings" font.pixelSize: Appearance.font.pixelSize.hugeass color: Appearance.m3colors.m3onSurface } } Loader { anchors.centerIn: parent - active: actionButton.materialIconName.length == 0 && actionButton.iconName && actionButton.iconName !== "" + active: actionButton.iconType === LauncherSearchResult.IconType.System && actionButton.iconName !== "" sourceComponent: IconImage { source: Quickshell.iconPath(actionButton.iconName) implicitSize: 20 diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index c0ca28d62..6b982c4d2 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -42,12 +42,6 @@ Item { // Wrapper LauncherSearch.query = text; } - function containsUnsafeLink(entry) { - if (entry == undefined) return false; - const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; - return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); - } - Keys.onPressed: event => { // Prevent Esc and Backspace from registering if (event.key === Qt.Key_Escape) diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index bf23c1c48..89d9e0056 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -4,7 +4,6 @@ import qs.modules.common import qs.modules.common.models import qs.modules.common.functions import QtQuick -import QtQuick.Controls import Quickshell import Quickshell.Io @@ -12,6 +11,15 @@ Singleton { id: root property string query: "" + + function ensurePrefix(prefix) { + if ([Config.options.search.prefix.action, Config.options.search.prefix.app, Config.options.search.prefix.clipboard, Config.options.search.prefix.emojis, Config.options.search.prefix.math, Config.options.search.prefix.shellCommand, Config.options.search.prefix.webSearch,].some(i => root.query.startsWith(i))) { + root.query = prefix + root.query.slice(1); + } else { + root.query = prefix + root.query; + } + } + property var searchActions: [ { action: "accentcolor", @@ -74,12 +82,13 @@ Singleton { property string mathResult: "" property bool clipboardWorkSafetyActive: { const enabled = Config.options.workSafety.enable.clipboard; - const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)) + const sensitiveNetwork = (StringUtils.stringListContainsSubstring(Network.networkName.toLowerCase(), Config.options.workSafety.triggerCondition.networkNameKeywords)); return enabled && sensitiveNetwork; } function containsUnsafeLink(entry) { - if (entry == undefined) return false; + if (entry == undefined) + return false; const unsafeKeywords = Config.options.workSafety.triggerCondition.linkKeywords; return StringUtils.stringListContainsSubstring(entry.toLowerCase(), unsafeKeywords); } @@ -128,95 +137,121 @@ Singleton { shouldBlurImage = shouldBlurImage && (root.containsUnsafeLink(array[index - 1]) || root.containsUnsafeLink(array[index + 1])); } const type = `#${entry.match(/^\s*(\S+)/)?.[1] || ""}`; - return { - key: type, - cliphistRawString: entry, + return resultComp.createObject(null, { + rawValue: entry, name: StringUtils.cleanCliphistEntry(entry), - clickActionName: "", + verb: "", type: type, execute: () => { Cliphist.copy(entry); }, - actions: [ - { - name: "Copy", - materialIcon: "content_copy", + actions: [resultComp.createObject(null, { + name: Translation.tr("Copy"), + iconName: "content_copy", + iconType: LauncherSearchResult.IconType.Material, execute: () => { Cliphist.copy(entry); } - }, - { - name: "Delete", - materialIcon: "delete", + }), resultComp.createObject(null, { + name: Translation.tr("Delete"), + iconName: "delete", + iconType: LauncherSearchResult.IconType.Material, execute: () => { Cliphist.deleteEntry(entry); } - } - ], - blurImage: shouldBlurImage, - blurImageText: Translation.tr("Work safety") - }; + })], + blurImage: shouldBlurImage + }); }).filter(Boolean); } else if (root.query.startsWith(Config.options.search.prefix.emojis)) { // Clipboard const searchString = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.emojis); return Emojis.fuzzyQuery(searchString).map(entry => { const emoji = entry.match(/^\s*(\S+)/)?.[1] || ""; - return { - key: emoji, - cliphistRawString: entry, - bigText: emoji, + return resultComp.createObject(null, { + rawValue: entry, name: entry.replace(/^\s*\S+\s+/, ""), - clickActionName: "", - type: "Emoji", + iconName: emoji, + iconType: LauncherSearchResult.IconType.Text, + verb: Translation.tr("Copy"), + type: Translation.tr("Emoji"), execute: () => { Quickshell.clipboardText = entry.match(/^\s*(\S+)/)?.[1]; } - }; + }); }).filter(Boolean); } ////////////////// Init /////////////////// nonAppResultsTimer.restart(); - const mathResultObject = { - key: `Math result: ${root.mathResult}`, + const mathResultObject = resultComp.createObject(null, { name: root.mathResult, - clickActionName: Translation.tr("Copy"), + verb: Translation.tr("Copy"), type: Translation.tr("Math result"), - fontType: "monospace", - materialSymbol: 'calculate', + fontType: LauncherSearchResult.FontType.Monospace, + iconName: 'calculate', + iconType: LauncherSearchResult.IconType.Material, execute: () => { Quickshell.clipboardText = root.mathResult; } - }; - const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => { - entry.clickActionName = Translation.tr("Launch"); - entry.type = Translation.tr("App"); - entry.key = entry.execute; - return entry; }); - const commandResultObject = { - key: `cmd ${root.query}`, + const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => { + return resultComp.createObject(null, { + type: Translation.tr("App"), + name: entry.name, + iconName: entry.icon, + iconType: LauncherSearchResult.IconType.System, + verb: Translation.tr("Launch"), + execute: () => { + if (!entry.runInTerminal) + entry.execute(); + else { + // Probably needs more proper escaping, but this will do for now + Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(entry.command.join(' '))}'`]); + } + }, + comment: entry.comment, + runInTerminal: entry.runInTerminal, + genericName: entry.genericName, + keywords: entry.keywords, + actions: entry.actions.map(action => { + return resultComp.createObject(null, { + name: action.name, + iconName: action.icon, + iconType: LauncherSearchResult.IconType.System, + execute: () => { + if (!action.runInTerminal) + action.execute(); + else { + Quickshell.execDetached(["bash", '-c', `${Config.options.apps.terminal} -e '${StringUtils.shellSingleQuoteEscape(action.command.join(' '))}'`]); + } + } + }); + }) + }); + }); + const commandResultObject = resultComp.createObject(null, { name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""), - clickActionName: Translation.tr("Run"), + verb: Translation.tr("Run"), type: Translation.tr("Run command"), - fontType: "monospace", - materialSymbol: 'terminal', + fontType: LauncherSearchResult.FontType.Monospace, + iconName: 'terminal', + iconType: LauncherSearchResult.IconType.Material, execute: () => { let cleanedCommand = root.query.replace("file://", ""); cleanedCommand = StringUtils.cleanPrefix(cleanedCommand, Config.options.search.prefix.shellCommand); if (cleanedCommand.startsWith(Config.options.search.prefix.shellCommand)) { cleanedCommand = cleanedCommand.slice(Config.options.search.prefix.shellCommand.length); } - Quickshell.execDetached(["bash", "-c", searchingText.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); + Quickshell.execDetached(["bash", "-c", root.query.startsWith('sudo') ? `${Config.options.apps.terminal} fish -C '${cleanedCommand}'` : cleanedCommand]); } - }; - const webSearchResultObject = { - key: `website ${root.query}`, + }); + const webSearchResultObject = resultComp.createObject(null, { name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch), - clickActionName: Translation.tr("Search"), + verb: Translation.tr("Search"), type: Translation.tr("Search the web"), - materialSymbol: 'travel_explore', + iconName: 'travel_explore', + iconType: LauncherSearchResult.IconType.Material, execute: () => { let query = StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch); let url = Config.options.search.engineBaseUrl + query; @@ -225,20 +260,20 @@ Singleton { } Qt.openUrlExternally(url); } - }; + }); const launcherActionObjects = root.searchActions.map(action => { const actionString = `${Config.options.search.prefix.action}${action.action}`; if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) { - return { - key: `Action ${actionString}`, + return resultComp.createObject(null, { name: root.query.startsWith(actionString) ? root.query : actionString, - clickActionName: Translation.tr("Run"), + verb: Translation.tr("Run"), type: Translation.tr("Action"), - materialSymbol: 'settings_suggest', + iconName: 'settings_suggest', + iconType: LauncherSearchResult.IconType.Material, execute: () => { action.execute(root.query.split(" ").slice(1).join(" ")); } - }; + }); } return null; }).filter(Boolean); @@ -275,4 +310,9 @@ Singleton { return result; } + + Component { + id: resultComp + LauncherSearchResult {} + } } From 71c1fbe1dd3a8c01308fd4f920756ce118e48c36 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 3 Dec 2025 23:23:08 +0100 Subject: [PATCH 053/154] waffles: start: more search progress --- .../ii/modules/waffle/looks/Looks.qml | 3 +- .../ii/modules/waffle/looks/WChoiceButton.qml | 1 + .../ii/modules/waffle/looks/WMenu.qml | 83 ++++++++++------- .../modules/waffle/looks/WPanelIconButton.qml | 13 ++- .../ii/modules/waffle/looks/WTextInput.qml | 1 + .../ii/modules/waffle/startMenu/SearchBar.qml | 24 +++-- .../waffle/startMenu/SearchPageContent.qml | 22 ++++- .../waffle/startMenu/SearchResults.qml | 60 ++++++++++++ .../waffle/startMenu/StartMenuContent.qml | 31 +++++-- .../waffle/startMenu/StartPageContent.qml | 2 +- .../ii/modules/waffle/startMenu/TagStrip.qml | 92 +++++++++++++++++++ .../waffle/startMenu/WSearchResultButton.qml | 81 ++++++++++++++++ 12 files changed, 356 insertions(+), 57 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 609c9877b..b9cbc00bd 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -64,7 +64,7 @@ Singleton { property color bg0: "#1C1C1C" property color bg0Border: "#404040" property color bg1Base: "#2C2C2C" - property color bg1: "#a8a8a8" + property color bg1: '#9f9f9f' property color bg1Hover: "#b3b3b3" property color bg1Active: '#727272' property color bg1Border: '#bebebe' @@ -125,6 +125,7 @@ Singleton { property color accentUnfocused: root.dark ? root.darkColors.accentUnfocused : root.lightColors.accentUnfocused property color accentFg: ColorUtils.isDark(accent) ? "#FFFFFF" : "#000000" property color selection: Appearance.colors.colPrimaryContainer + property color selectionFg: Appearance.colors.colOnPrimaryContainer } radius: QtObject { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml index eff776601..14691c716 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WChoiceButton.qml @@ -44,6 +44,7 @@ WButton { radius: Looks.radius.medium color: root.color Behavior on color { + enabled: root.animateChoiceHighlight animation: Looks.transition.color.createObject(this) } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index a208f355b..a18c59ec9 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -14,17 +14,18 @@ Menu { property bool downDirection: false property bool hasIcons: false // TODO: implement - implicitWidth: background.implicitWidth + root.padding * 2 - implicitHeight: background.implicitHeight + root.padding * 2 + implicitWidth: background.implicitWidth + margins * 2 + implicitHeight: background.implicitHeight + margins * 2 + margins: 10 padding: 3 property real sourceEdgeMargin: -implicitHeight clip: true - + enter: Transition { NumberAnimation { property: "sourceEdgeMargin" from: -root.implicitHeight - to: root.padding + to: root.margins duration: 200 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn @@ -33,7 +34,7 @@ Menu { exit: Transition { NumberAnimation { property: "sourceEdgeMargin" - from: root.padding + from: root.margins to: -root.implicitHeight duration: 150 easing.type: Easing.BezierSpline @@ -41,43 +42,55 @@ Menu { } } - background: WPane { - anchors { - left: parent.left - right: parent.right - top: root.downDirection ? parent.top : undefined - bottom: root.downDirection ? undefined : parent.bottom - margins: root.padding - topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding - bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin - } - contentItem: Rectangle { - color: Looks.colors.bg1Base - implicitWidth: menuListView.implicitWidth + root.padding * 2 - implicitHeight: root.contentItem.implicitHeight + root.padding * 2 + background: Item { + id: bgItem + implicitWidth: bgPane.implicitWidth + implicitHeight: bgPane.implicitHeight + WPane { + id: bgPane + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.margins + topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins + bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin + } + contentItem: Rectangle { + color: Looks.colors.bg1Base + implicitWidth: menuListView.implicitWidth + root.padding * 2 + implicitHeight: root.contentItem.implicitHeight + root.padding * 2 + } + } } - contentItem: ListView { - id: menuListView - anchors { - left: parent.left - right: parent.right - top: root.downDirection ? parent.top : undefined - bottom: root.downDirection ? undefined : parent.bottom - margins: root.padding * 2 - topMargin: root.downDirection ? root.sourceEdgeMargin : root.padding - bottomMargin: root.downDirection ? root.padding : root.sourceEdgeMargin - } - implicitHeight: contentHeight - implicitWidth: Array.from({ - length: count - }, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b) + contentItem: Item { + implicitWidth: menuListView.implicitWidth + implicitHeight: menuListView.implicitHeight + ListView { + id: menuListView + anchors { + left: parent.left + right: parent.right + top: root.downDirection ? parent.top : undefined + bottom: root.downDirection ? undefined : parent.bottom + margins: root.margins // ???? + topMargin: root.downDirection ? root.sourceEdgeMargin : root.margins + bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin + } + implicitHeight: contentHeight + implicitWidth: Array.from({ + length: count + }, (_, i) => itemAtIndex(i)?.implicitWidth ?? 0).reduce((a, b) => a > b ? a : b) - model: root.contentModel + model: root.contentModel + } } delegate: WMenuItem { id: menuItemDelegate + width: ListView.view?.width } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml index e0e966022..3cdb3abfd 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPanelIconButton.qml @@ -10,14 +10,17 @@ WButton { id: root property alias iconName: iconContent.icon + property alias iconSize: iconContent.implicitSize property alias monochrome: iconContent.monochrome implicitWidth: 40 implicitHeight: 40 - contentItem: FluentIcon { - id: iconContent - anchors.centerIn: parent - implicitSize: 18 - icon: root.iconName + contentItem: Item { + FluentIcon { + id: iconContent + anchors.centerIn: parent + implicitSize: 18 + icon: root.iconName + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml index a3f3e8a40..84bed180d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextInput.qml @@ -15,4 +15,5 @@ TextInput { } selectionColor: Looks.colors.selection + selectedTextColor: Looks.colors.selectionFg } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml index 6bd23ae9b..ce5652329 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml @@ -12,26 +12,36 @@ import qs.modules.waffle.looks FooterRectangle { id: root + property real horizontalPadding: 32 + property real verticalPadding: 16 property bool searching: text.length > 0 property alias text: searchInput.text + implicitHeight: outline.implicitHeight + verticalPadding * 2 Component.onCompleted: searchInput.forceActiveFocus() focus: true color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter - implicitWidth: 832 // TODO: Make sizes naturally inferred - implicitHeight: 63 + Behavior on horizontalPadding { + enabled: Config.options.waffles.tweaks.smootherSearchBar + animation: Looks.transition.move.createObject(this) + } + Behavior on verticalPadding { + enabled: Config.options.waffles.tweaks.smootherSearchBar + animation: Looks.transition.move.createObject(this) + } Rectangle { id: outline anchors { - fill: parent - leftMargin: 32 - rightMargin: 32 - topMargin: 16 - bottomMargin: 15 + left: parent.left + right: parent.right + leftMargin: root.horizontalPadding + rightMargin: root.horizontalPadding + verticalCenter: parent.verticalCenter } + implicitHeight: 32 color: "transparent" radius: height / 2 border.width: 1 diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml index cdbb7d3b8..a294950fd 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml @@ -12,5 +12,25 @@ import qs.modules.waffle.looks BodyRectangle { id: root - + property string searchText: LauncherSearch.query + + ColumnLayout { + anchors { + fill: parent + topMargin: 2 + leftMargin: 24 + rightMargin: 24 + } + spacing: 12 + + TagStrip { + Layout.fillWidth: true + Layout.fillHeight: false + } + + SearchResults { + Layout.fillWidth: true + Layout.fillHeight: true + } + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml new file mode 100644 index 000000000..5f3d93411 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml @@ -0,0 +1,60 @@ +import qs.modules.waffle.looks +import qs.modules.common.functions +import qs.modules.common +import qs.services +import qs +import Quickshell +import QtQuick.Layouts +import QtQuick.Controls +import QtQuick +pragma ComponentBehavior: Bound + +RowLayout { + id: root + + function focusFirstItem() { + resultList.currentIndex = 0; + } + + ResultList { + id: resultList + Layout.fillHeight: true + Layout.fillWidth: true + } + ResultPreview { + Layout.preferredWidth: 386 + Layout.leftMargin: 1 + Layout.rightMargin: 1 + } + + component ResultList: ListView { + section { + criteria: ViewSection.FullString + property: "type" + } + clip: true + spacing: 4 + model: ScriptModel { + values: { + // TODO: categorize and have max per category + LauncherSearch.results.slice(0, 10) + } + onValuesChanged: { + root.focusFirstItem(); + } + } + delegate: WSearchResultButton { + required property int index + required property var modelData + entry: modelData + firstEntry: index === 0 + width: ListView.view?.width + } + } + + component ResultPreview: Rectangle { + Layout.fillHeight: true + color: Looks.colors.bg1 + radius: Looks.radius.large + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index 6e97f88be..09de017ee 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -14,26 +14,43 @@ WBarAttachedPanelContent { id: root property bool searching: false - property string searchText: "" + property string searchText: LauncherSearch.query contentItem: WPane { contentItem: WPanelPageColumn { SearchBar { focus: true Layout.fillWidth: true + implicitWidth: 832 // TODO: Make sizes naturally inferred + horizontalPadding: root.searching ? 24 : 32 + // verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel Synchronizer on searching { property alias target: root.searching } - Synchronizer on text { - property alias source: root.searchText + text: root.searchText + onTextChanged: { + LauncherSearch.query = text; } } - Loader { - id: pageContentLoader + Item { + implicitHeight: root.searching ? 736 : 736 // TODO: Make sizes naturally inferred Layout.fillWidth: true - source: root.searching ? "SearchPageContent.qml" : "StartPageContent.qml" + Loader { + id: pageContentLoader + anchors.fill: parent + sourceComponent: root.searching ? searchPageComp : startPageComp + } } } } - + + Component { + id: searchPageComp + SearchPageContent {} + } + + Component { + id: startPageComp + StartPageContent {} + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml index eb3c96a80..f2f863cd2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -17,7 +17,7 @@ WPanelPageColumn { WPanelSeparator {} BodyRectangle { - implicitHeight: 736 // TODO: Make sizes naturally inferred + Layout.fillHeight: true } WPanelSeparator {} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml new file mode 100644 index 000000000..10448b5cd --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml @@ -0,0 +1,92 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +RowLayout { + WPanelIconButton { + implicitWidth: 36 + implicitHeight: 36 + iconSize: 24 + iconName: "arrow-left" + onClicked: LauncherSearch.query = "" + } + ListView { + id: tagListView + Layout.fillWidth: true + Layout.fillHeight: true + orientation: Qt.Horizontal + spacing: 4 + model: [ + { + name: Translation.tr("All"), + prefix: "" + }, + { + name: Translation.tr("Apps"), + prefix: Config.options.search.prefix.app + }, + { + name: Translation.tr("Actions"), + prefix: Config.options.search.prefix.action + }, + { + name: Translation.tr("Clipboard"), + prefix: Config.options.search.prefix.clipboard + }, + { + name: Translation.tr("Emojis"), + prefix: Config.options.search.prefix.emojis + }, + { + name: Translation.tr("Math"), + prefix: Config.options.search.prefix.math + }, + { + name: Translation.tr("Commands"), + prefix: Config.options.search.prefix.shellCommand + }, + { + name: Translation.tr("Web"), + prefix: Config.options.search.prefix.webSearch + }, + ] + delegate: WBorderedButton { + id: tagButton + required property var modelData + border.width: 1 + radius: height / 2 + implicitWidth: tagButtonText.implicitWidth + 12 * 2 + implicitHeight: 32 + checked: { + if (modelData.prefix != "") { + return LauncherSearch.query.startsWith(modelData.prefix); + } else { + return !tagListView.model.some(i => (i.prefix != "" && LauncherSearch.query.startsWith(i.prefix))) + } + } + contentItem: Item { + WText { + id: tagButtonText + anchors.centerIn: parent + color: tagButton.fgColor + text: tagButton.modelData.name + font.pixelSize: Looks.font.pixelSize.large + } + } + onClicked: LauncherSearch.ensurePrefix(tagButton.modelData.prefix) + } + } + WPanelIconButton { + implicitWidth: 36 + implicitHeight: 36 + iconSize: 24 + iconName: "more-horizontal" + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml new file mode 100644 index 000000000..1aca104b0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml @@ -0,0 +1,81 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.models +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WChoiceButton { + id: root + + required property LauncherSearchResult entry + property bool firstEntry: false + + checked: focus + animateChoiceHighlight: false + implicitWidth: contentLayout.implicitWidth + leftPadding + rightPadding + implicitHeight: contentLayout.implicitHeight + topPadding + bottomPadding + + onClicked: { + GlobalStates.searchOpen = false + root.entry.execute() + } + + contentItem: RowLayout { + id: contentLayout + spacing: 8 + + EntryIcon {} + EntryNameColumn { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + } + } + + component EntryIcon: Item { + implicitWidth: 24 + implicitHeight: 24 + Loader { + anchors.centerIn: parent + active: root.entry.iconType === LauncherSearchResult.IconType.System + sourceComponent: WAppIcon { + implicitSize: 24 + tryCustomIcon: false + iconName: root.entry.iconName + } + } + Loader { + anchors.centerIn: parent + active: root.entry.iconType === LauncherSearchResult.IconType.Text + sourceComponent: WText { + text: root.entry.iconName + font.pixelSize: 24 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + } + + component EntryNameColumn: ColumnLayout { + spacing: 4 + + WText { + Layout.fillWidth: true + wrapMode: Text.Wrap + text: root.entry.name + font.pixelSize: Looks.font.pixelSize.large + maximumLineCount: 2 + } + + WText { + Layout.fillWidth: true + visible: root.firstEntry + text: root.entry.type + color: Looks.colors.accentUnfocused + } + } +} From 8203115a24eea64790500e231bb17f8d2e6af148 Mon Sep 17 00:00:00 2001 From: LIghtJUNction Date: Thu, 4 Dec 2025 15:17:24 +0800 Subject: [PATCH 054/154] Move the comments from the default configuration to the custom configuration and add more comments. (#2621) --- dots/.config/hypr/custom/env.conf | 12 ++++++++++++ dots/.config/hypr/custom/general.conf | 6 +++++- dots/.config/hypr/custom/keybinds.conf | 13 +++++++++++++ dots/.config/hypr/custom/rules.conf | 8 ++++++++ dots/.config/hypr/hyprland/env.conf | 6 ------ dots/.config/hypr/hyprland/general.conf | 4 ---- dots/.config/hypr/hyprland/rules.conf | 6 +----- 7 files changed, 39 insertions(+), 16 deletions(-) diff --git a/dots/.config/hypr/custom/env.conf b/dots/.config/hypr/custom/env.conf index 3215877be..4e59a32c2 100644 --- a/dots/.config/hypr/custom/env.conf +++ b/dots/.config/hypr/custom/env.conf @@ -8,3 +8,15 @@ #env = SDL_IM_MODULE, fcitx #env = GLFW_IM_MODULE, ibus #env = INPUT_METHOD, fcitx + +# ######## Wayland ######### +# Tearing +# env = WLR_DRM_NO_ATOMIC, 1 +# ? +# env = WLR_NO_HARDWARE_CURSORS, 1 + +# ######## EDITOR ######### +#https://wiki.archlinux.org/title/Category:Text_editors +# for example: vi nano nvim ... + +#env = EDITOR, vim diff --git a/dots/.config/hypr/custom/general.conf b/dots/.config/hypr/custom/general.conf index ded706ae7..aa3fd4f83 100644 --- a/dots/.config/hypr/custom/general.conf +++ b/dots/.config/hypr/custom/general.conf @@ -1,2 +1,6 @@ # Put general config stuff here -# Here's a list of every variable: https://wiki.hyprland.org/Configuring/Variables/ \ No newline at end of file +# Here's a list of every variable: https://wiki.hyprland.org/Configuring/Variables/ + +# monitor=,addreserved, 0, 0, 0, 0 # Custom reserved area + +# HDMI port: mirror display. To see device name, use `hyprctl monitors` diff --git a/dots/.config/hypr/custom/keybinds.conf b/dots/.config/hypr/custom/keybinds.conf index f2a568394..8885a2175 100644 --- a/dots/.config/hypr/custom/keybinds.conf +++ b/dots/.config/hypr/custom/keybinds.conf @@ -4,6 +4,19 @@ bind = Ctrl+Super, Slash, exec, xdg-open ~/.config/illogical-impulse/config.json # Edit shell config bind = Ctrl+Super+Alt, Slash, exec, xdg-open ~/.config/hypr/custom/keybinds.conf # Edit extra keybinds +##! Apps +# bind = Super, Return, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # Terminal +# bind = Super, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (alt) +# bind = Ctrl+Alt, T, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "${TERMINAL}" "kitty -1" "foot" "alacritty" "wezterm" "konsole" "kgx" "uxterm" "xterm" # [hidden] (terminal) (for Ubuntu people) +# bind = Super, E, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "dolphin" "nautilus" "nemo" "thunar" "${TERMINAL}" "kitty -1 fish -c yazi" # File manager +# bind = Super, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "google-chrome-stable" "zen-browser" "firefox" "brave" "chromium" "microsoft-edge-stable" "opera" "librewolf" # Browser +# bind = Super, C, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "code" "codium" "cursor" "zed" "zedit" "zeditor" "kate" "gnome-text-editor" "emacs" "command -v nvim && kitty -1 nvim" "command -v micro && kitty -1 micro" # Code editor +# bind = Ctrl+Super+Shift+Alt, W, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "wps" "onlyoffice-desktopeditors" "libreoffice" # Office software +# bind = Super, X, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "kate" "gnome-text-editor" "emacs" # Text editor +# bind = Ctrl+Super, V, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "pavucontrol-qt" "pavucontrol" # Volume mixer +# bind = Super, I, exec, XDG_CURRENT_DESKTOP=gnome ~/.config/hypr/hyprland/scripts/launch_first_available.sh "qs -p ~/.config/quickshell/$qsConfig/settings.qml" "systemsettings" "gnome-control-center" "better-control" # Settings app +# bind = Ctrl+Shift, Escape, exec, ~/.config/hypr/hyprland/scripts/launch_first_available.sh "gnome-system-monitor" "plasma-systemmonitor --page-name Processes" "command -v btop && kitty -1 fish -c btop" # Task manager + # Add stuff here # Use #! to add an extra column on the cheatsheet # Use ##! to add a section in that column diff --git a/dots/.config/hypr/custom/rules.conf b/dots/.config/hypr/custom/rules.conf index 159de0915..b253bc8d1 100644 --- a/dots/.config/hypr/custom/rules.conf +++ b/dots/.config/hypr/custom/rules.conf @@ -1,3 +1,11 @@ # You can put custom rules here # Window/layer rules: https://wiki.hyprland.org/Configuring/Window-Rules/ # Workspace rules: https://wiki.hyprland.org/Configuring/Workspace-Rules/ + +# ######## Window rules ######## + +# Uncomment to apply global transparency to all windows: +# windowrulev2 = opacity 0.89 override 0.89 override, class:.* + +# Disable blur for all xwayland apps +# windowrulev2 = noblur, xwayland:1 \ No newline at end of file diff --git a/dots/.config/hypr/hyprland/env.conf b/dots/.config/hypr/hyprland/env.conf index acc363cc6..affd1ca82 100644 --- a/dots/.config/hypr/hyprland/env.conf +++ b/dots/.config/hypr/hyprland/env.conf @@ -9,12 +9,6 @@ env = QT_QPA_PLATFORM, wayland env = QT_QPA_PLATFORMTHEME, kde env = XDG_MENU_PREFIX, plasma- -# ######## Wayland ######### -# Tearing -# env = WLR_DRM_NO_ATOMIC, 1 -# ? -# env = WLR_NO_HARDWARE_CURSORS, 1 - # ######## Virtual envrionment ######### env = ILLOGICAL_IMPULSE_VIRTUAL_ENV, ~/.local/state/quickshell/.venv diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index c4c54bc28..77580552d 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -1,9 +1,5 @@ # MONITOR CONFIG monitor=,preferred,auto,1 -# monitor=,addreserved, 0, 0, 0, 0 # Custom reserved area - -# HDMI port: mirror display. To see device name, use `hyprctl monitors` -# monitor=HDMI-A-1,1920x1080@60,1920x0,1,mirror,eDP-1 gesture = 3, swipe, move, gesture = 3, pinch, float diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index cfb1e3f7b..a6e4d4e48 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -1,12 +1,8 @@ # ######## Window rules ######## -# Uncomment to apply global transparency to all windows: -# windowrulev2 = opacity 0.89 override 0.89 override, class:.* - # Disable blur for xwayland context menus windowrulev2 = noblur,class:^()$,title:^()$ -# Disable blur for all xwayland apps -# windowrulev2 = noblur, xwayland:1 + # Disable blur for every window windowrulev2 = noblur, class:.* From 7d9a405146b6ad6a80ba022b1cd840bb2ac6fde9 Mon Sep 17 00:00:00 2001 From: Madjid Taha <1833954+madjidtaha@users.noreply.github.com> Date: Thu, 4 Dec 2025 08:20:21 +0100 Subject: [PATCH 055/154] Fix CPU frequency detection for non-English locales (#2629) * fix: ensure lscpu is using en_US locale for LANG * fix: use environment in Process --- dots/.config/quickshell/ii/services/ResourceUsage.qml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/dots/.config/quickshell/ii/services/ResourceUsage.qml b/dots/.config/quickshell/ii/services/ResourceUsage.qml index df823adf7..c513b90ca 100644 --- a/dots/.config/quickshell/ii/services/ResourceUsage.qml +++ b/dots/.config/quickshell/ii/services/ResourceUsage.qml @@ -102,6 +102,10 @@ Singleton { Process { id: findCpuMaxFreqProc + environment: ({ + LANG: "C", + LC_ALL: "C" + }) command: ["bash", "-c", "lscpu | grep 'CPU max MHz' | awk '{print $4}'"] running: true stdout: StdioCollector { From 8cfb3be4cd36689d39eafb1ae1c5cf0b067b31f4 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Thu, 4 Dec 2025 17:06:26 +0800 Subject: [PATCH 056/154] Use a public API to wrap rsync_* function --- sdata/subcmd-install/3.files-legacy.sh | 6 +----- sdata/subcmd-install/3.files.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/sdata/subcmd-install/3.files-legacy.sh b/sdata/subcmd-install/3.files-legacy.sh index 98d792be8..0a3bef85f 100644 --- a/sdata/subcmd-install/3.files-legacy.sh +++ b/sdata/subcmd-install/3.files-legacy.sh @@ -30,11 +30,7 @@ esac case "${SKIP_FISH}" in true) sleep 0;; *) - # Use rsync with exclude to preserve user's custom conf.d directory - if [ -d "$XDG_CONFIG_HOME"/fish ];then - warning_overwrite - fi - rsync_dir__sync_exclude dots/.config/fish "$XDG_CONFIG_HOME"/fish "conf.d" + install_dir__sync_exclude dots/.config/fish "$XDG_CONFIG_HOME"/fish "conf.d" ;; esac diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index 2bd2e1190..aa3099ab0 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -137,6 +137,18 @@ function install_dir__skip_existed(){ v rsync_dir $s $t fi } +function install_dir__sync_exclude(){ + # NOTE: Do not add prefix `v` or `x` when using this function + # Sync directory with exclude patterns + # Usage: install_dir__sync_exclude [ ...] + local s=$1 + local t=$2 + shift 2 + if [ -d $t ];then + warning_overwrite + fi + rsync_dir__sync_exclude $s $t "$@" +} function install_google_sans_flex(){ local font_name="Google Sans Flex" local src_name="google-sans-flex" From e9b5e7d7d2ecc204a9730046f7d8dc75a3f892db Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 4 Dec 2025 16:29:49 +0100 Subject: [PATCH 057/154] add placeholder when volume mixer is empty --- .../ii/sidebarRight/volumeMixer/VolumeDialogContent.qml | 6 ++++++ dots/.config/quickshell/ii/services/Audio.qml | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml index f7c2dc9f8..dab92df5f 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/volumeMixer/VolumeDialogContent.qml @@ -30,6 +30,12 @@ ColumnLayout { required property var modelData node: modelData } + PagePlaceholder { + icon: "widgets" + title: Translation.tr("No applications") + shown: !root.hasApps + shape: MaterialShape.Shape.Cookie7Sided + } } StyledComboBox { diff --git a/dots/.config/quickshell/ii/services/Audio.qml b/dots/.config/quickshell/ii/services/Audio.qml index 4b45701a9..33c49cc30 100644 --- a/dots/.config/quickshell/ii/services/Audio.qml +++ b/dots/.config/quickshell/ii/services/Audio.qml @@ -18,6 +18,12 @@ Singleton { readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so... property string audioTheme: Config.options.sounds.theme property real value: sink?.audio.volume ?? 0 + property bool micBeingAccessed: Pipewire.links.values.filter(link => + !link.source.isStream && !link.source.isSink && link.target.isStream + ).length > 0 + onMicBeingAccessedChanged: { + print(micBeingAccessed) + } function friendlyDeviceName(node) { return (node.nickname || node.description || Translation.tr("Unknown")); } From 8e704e4009933f44d85a69635daf43f9316e8f06 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 4 Dec 2025 20:40:33 +0100 Subject: [PATCH 058/154] lock: ctrl+enter to also turn on idle inhibitor --- .../quickshell/ii/modules/ii/lock/Lock.qml | 8 ++++++- .../ii/modules/ii/lock/LockContext.qml | 4 +++- .../ii/modules/ii/lock/LockSurface.qml | 16 ++++++++++++-- .../ii/regionSelector/RegionSelection.qml | 2 +- dots/.config/quickshell/ii/services/Idle.qml | 22 +++++++++++-------- 5 files changed, 38 insertions(+), 14 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index e942f4fda..3d6506c54 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -72,7 +72,7 @@ Scope { } // Unlock the keyring if configured to do so - if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); + if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async // Unlock the screen before exiting, or the compositor will display a // fallback lock you can't interact with. @@ -83,6 +83,12 @@ Scope { // Reset lockContext.reset(); + + // Post-unlock actions + if (lockContext.alsoInhibitIdle) { + lockContext.alsoInhibitIdle = false; + Idle.toggleInhibit(true); + } } } diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml index b242cdc05..e6454b476 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml @@ -21,6 +21,7 @@ Scope { property bool showFailure: false property bool fingerprintsConfigured: false property var targetAction: LockContext.ActionEnum.Unlock + property bool alsoInhibitIdle: false function resetTargetAction() { root.targetAction = LockContext.ActionEnum.Unlock; @@ -58,7 +59,8 @@ Scope { passwordClearTimer.restart(); } - function tryUnlock() { + function tryUnlock(alsoInhibitIdle = false) { + root.alsoInhibitIdle = alsoInhibitIdle; root.unlockInProgress = true; pam.start(); } diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml index da3e17627..e44eecacb 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml @@ -59,10 +59,20 @@ MouseArea { } // Key presses + property bool ctrlHeld: false Keys.onPressed: event => { root.context.resetClearTimer(); + if (event.key === Qt.Key_Control) { + root.ctrlHeld = true; + } if (event.key === Qt.Key_Escape) { // Esc to clear root.context.currentText = ""; + } + forceFieldFocus(); + } + Keys.onReleased: event => { + if (event.key === Qt.Key_Control) { + root.ctrlHeld = false; } forceFieldFocus(); } @@ -133,7 +143,9 @@ MouseArea { // Synchronizing (across monitors) and unlocking onTextChanged: root.context.currentText = this.text - onAccepted: root.context.tryUnlock() + onAccepted: { + root.context.tryUnlock(ctrlHeld); + } Connections { target: root.context function onCurrentTextChanged() { @@ -202,7 +214,7 @@ MouseArea { iconSize: 24 text: { if (root.context.targetAction === LockContext.ActionEnum.Unlock) { - return "arrow_right_alt"; + return root.ctrlHeld ? "emoji_food_beverage" : "arrow_right_alt"; } else if (root.context.targetAction === LockContext.ActionEnum.Poweroff) { return "power_settings_new"; } else if (root.context.targetAction === LockContext.ActionEnum.Reboot) { diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml index a52471ba6..8f484591b 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml @@ -26,7 +26,7 @@ PanelWindow { bottom: true } - // TODO: Ask: sidebar AI; Ocr: tesseract + // TODO: Ask: sidebar AI enum SnipAction { Copy, Edit, Search, CharRecognition, Record, RecordWithSound } enum SelectionMode { RectCorners, Circle } property var action: RegionSelection.SnipAction.Copy diff --git a/dots/.config/quickshell/ii/services/Idle.qml b/dots/.config/quickshell/ii/services/Idle.qml index ad938f3c3..a2b804772 100644 --- a/dots/.config/quickshell/ii/services/Idle.qml +++ b/dots/.config/quickshell/ii/services/Idle.qml @@ -1,8 +1,8 @@ +pragma Singleton import qs.modules.common import QtQuick import Quickshell import Quickshell.Wayland -pragma Singleton /** * A nice wrapper for date and time strings. @@ -17,21 +17,26 @@ Singleton { target: Persistent function onReadyChanged() { if (!Persistent.isNewHyprlandInstance) { - root.inhibit = Persistent.states.idle.inhibit + root.inhibit = Persistent.states.idle.inhibit; } else { - Persistent.states.idle.inhibit = root.inhibit + Persistent.states.idle.inhibit = root.inhibit; } } } - function toggleInhibit() { - root.inhibit = !root.inhibit - Persistent.states.idle.inhibit = root.inhibit + function toggleInhibit(active = null) { + if (active !== null) { + root.inhibit = active; + } else { + root.inhibit = !root.inhibit; + } + Persistent.states.idle.inhibit = root.inhibit; } IdleInhibitor { id: idleInhibitor - window: PanelWindow { // Inhibitor requires a "visible" surface + window: PanelWindow { + // Inhibitor requires a "visible" surface // Actually not lol implicitWidth: 0 implicitHeight: 0 @@ -46,6 +51,5 @@ Singleton { item: null } } - } - + } } From 15481c646a98ba3d9b634d429a9a90d826ecb25f Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Thu, 4 Dec 2025 23:07:03 +0300 Subject: [PATCH 059/154] digital clock: add roundness slider and fix settings layout --- .../quickshell/ii/modules/common/Config.qml | 1 + .../modules/common/widgets/ConfigSlider.qml | 9 +++- .../background/widgets/clock/DigitalClock.qml | 2 + .../ii/modules/settings/BackgroundConfig.qml | 45 ++++++++++++++----- 4 files changed, 44 insertions(+), 13 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 89169fbdf..3518b3dbd 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -194,6 +194,7 @@ Singleton { property real weight: 350 property real width: 100 property real size: 90 + property real roundness: 0 } } property JsonObject quote: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml index b53a5f7c0..edc63191a 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/ConfigSlider.qml @@ -2,6 +2,7 @@ import qs.modules.common.widgets import qs.modules.common import QtQuick import QtQuick.Layouts +import qs.services RowLayout { id: root @@ -12,12 +13,16 @@ RowLayout { property string text: "" property string buttonIcon: "" property alias value: slider.value + property alias stopIndicatorValues: slider.stopIndicatorValues property bool usePercentTooltip: true property real from: slider.from property real to: slider.to - + property real textWidth: 120 + RowLayout { + id: row spacing: 10 + OptionalMaterialSymbol { id: iconWidget icon: root.buttonIcon @@ -25,6 +30,7 @@ RowLayout { } StyledText { id: labelWidget + Layout.preferredWidth: root.textWidth text: root.text color: Appearance.colors.colOnSecondaryContainer } @@ -32,7 +38,6 @@ RowLayout { StyledSlider { id: slider - Layout.fillWidth: true configuration: StyledSlider.Configuration.XS usePercentTooltip: root.usePercentTooltip value: root.value diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 90a65a0dd..233cad7b1 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -33,6 +33,7 @@ ColumnLayout { weight: Config.options.background.widgets.clock.digital.font.weight variableAxes: ({ "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness }) } } @@ -50,6 +51,7 @@ ColumnLayout { weight: Config.options.background.widgets.clock.digital.font.weight variableAxes: ({ "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness }) } } diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index eddc06fa9..697c40274 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -172,8 +172,10 @@ ContentPage { ContentSubsection { visible: settingsClock.digitalPresent title: Translation.tr("Digital clock settings") + tooltip: Translation.tr("Font width and roundness settings are only available for some fonts like 'Google Sans Flex'") ConfigRow { + uniform: true ConfigSwitch { buttonIcon: "vertical_distribute" text: Translation.tr("Vertical") @@ -193,6 +195,9 @@ ContentPage { } ConfigRow { + uniform: true + + ConfigSwitch { buttonIcon: "date_range" text: Translation.tr("Show date") @@ -213,6 +218,8 @@ ContentPage { } } } + + ConfigSlider { text: Translation.tr("Font weight") value: Config.options.background.widgets.clock.digital.font.weight @@ -220,21 +227,12 @@ ContentPage { buttonIcon: "format_bold" from: 1 to: 1000 + stopIndicatorValues: [350] onValueChanged: { Config.options.background.widgets.clock.digital.font.weight = value; } } - ConfigSlider { - text: Translation.tr("Font width") - value: Config.options.background.widgets.clock.digital.font.width - usePercentTooltip: false - buttonIcon: "fit_width" - from: 25 - to: 125 - onValueChanged: { - Config.options.background.widgets.clock.digital.font.width = value; - } - } + ConfigSlider { text: Translation.tr("Font size") value: Config.options.background.widgets.clock.digital.font.size @@ -242,10 +240,35 @@ ContentPage { buttonIcon: "format_size" from: 70 to: 150 + stopIndicatorValues: [90] onValueChanged: { Config.options.background.widgets.clock.digital.font.size = value; } } + + ConfigSlider { + text: Translation.tr("Font width") + value: Config.options.background.widgets.clock.digital.font.width + usePercentTooltip: false + buttonIcon: "fit_width" + from: 25 + to: 125 + stopIndicatorValues: [100] + onValueChanged: { + Config.options.background.widgets.clock.digital.font.width = value; + } + } + ConfigSlider { + text: Translation.tr("Font roundness") + value: Config.options.background.widgets.clock.digital.font.roundness + usePercentTooltip: false + buttonIcon: "line_curve" + from: 0 + to: 100 + onValueChanged: { + Config.options.background.widgets.clock.digital.font.roundness = value; + } + } } ContentSubsection { From f62ab85a19858b4480650e06dc48cdd6c646108e Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Thu, 4 Dec 2025 15:37:52 -0500 Subject: [PATCH 060/154] audio: protection: fix weird volume limit behavior --- dots/.config/quickshell/ii/services/Audio.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Audio.qml b/dots/.config/quickshell/ii/services/Audio.qml index 33c49cc30..d4bfa5932 100644 --- a/dots/.config/quickshell/ii/services/Audio.qml +++ b/dots/.config/quickshell/ii/services/Audio.qml @@ -111,9 +111,9 @@ Singleton { if (newVolume - lastVolume > maxAllowedIncrease) { sink.audio.volume = lastVolume; root.sinkProtectionTriggered(Translation.tr("Illegal increment")); - } else if (newVolume > maxAllowed || newVolume > root.hardMaxValue) { + } else if (Math.round(newVolume * 100) / 100 > maxAllowed || newVolume > root.hardMaxValue) { root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed")); - sink.audio.volume = Math.min(lastVolume, maxAllowed); + sink.audio.volume = maxAllowed; } lastVolume = sink.audio.volume; } From 9043ae7bf61d51167969c659ab8c7acb5c8b1f1b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:19:21 +0100 Subject: [PATCH 061/154] waffles: functioning search --- .../icons/fluent/app-generic-filled.svg | 4 + .../ii/assets/icons/fluent/app-generic.svg | 4 + .../icons/fluent/arrow-enter-left-filled.svg | 4 + .../assets/icons/fluent/arrow-enter-left.svg | 4 + .../assets/icons/fluent/calculator-filled.svg | 4 + .../ii/assets/icons/fluent/calculator.svg | 4 + .../ii/assets/icons/fluent/desktop-filled.svg | 4 + .../ii/assets/icons/fluent/desktop.svg | 4 + .../icons/fluent/globe-search-filled.svg | 4 + .../ii/assets/icons/fluent/globe-search.svg | 4 + .../assets/icons/fluent/image-copy-filled.svg | 4 + .../ii/assets/icons/fluent/image-copy.svg | 4 + .../ii/assets/icons/fluent/image-filled.svg | 4 + .../ii/assets/icons/fluent/image.svg | 4 + .../ii/assets/icons/fluent/library-filled.svg | 4 + .../ii/assets/icons/fluent/library.svg | 4 + .../ii/assets/icons/fluent/news-filled.svg | 4 + .../ii/assets/icons/fluent/news.svg | 4 + .../ii/assets/icons/fluent/open-filled.svg | 4 + .../ii/assets/icons/fluent/open.svg | 4 + .../ii/assets/icons/fluent/people-filled.svg | 4 + .../icons/fluent/people-team-filled.svg | 4 + .../ii/assets/icons/fluent/people-team.svg | 4 + .../ii/assets/icons/fluent/people.svg | 4 + .../ii/assets/icons/fluent/record-filled.svg | 4 + .../ii/assets/icons/fluent/record.svg | 1 + .../ii/assets/icons/fluent/server-filled.svg | 4 + .../ii/assets/icons/fluent/server.svg | 4 + .../icons/fluent/store-microsoft-filled.svg | 4 + .../assets/icons/fluent/store-microsoft.svg | 4 + .../ii/assets/icons/fluent/wand-filled.svg | 4 + .../ii/assets/icons/fluent/wand.svg | 4 + .../common/models/LauncherSearchResult.qml | 5 +- .../common/widgets/StyledScrollBar.qml | 1 + .../ii/modules/waffle/looks/Looks.qml | 1 + .../ii/modules/waffle/looks/WIcons.qml | 61 ++++- .../ii/modules/waffle/looks/WListView.qml | 10 + .../ii/modules/waffle/looks/WScrollBar.qml | 25 +++ .../ii/modules/waffle/startMenu/SearchBar.qml | 12 +- .../waffle/startMenu/SearchEntryIcon.qml | 48 ++++ .../waffle/startMenu/SearchPageContent.qml | 4 + .../waffle/startMenu/SearchResults.qml | 208 ++++++++++++++++-- .../waffle/startMenu/StartMenuContent.qml | 72 +++++- .../waffle/startMenu/StartMenuContext.qml | 64 ++++++ .../ii/modules/waffle/startMenu/TagStrip.qml | 38 +--- .../waffle/startMenu/WSearchResultButton.qml | 50 ++--- .../waffle/startMenu/WaffleStartMenu.qml | 5 +- .../quickshell/ii/services/LauncherSearch.qml | 7 +- 48 files changed, 648 insertions(+), 88 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/app-generic-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/app-generic.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/calculator-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/calculator.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/desktop-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/desktop.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/globe-search-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/globe-search.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image-copy-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image-copy.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/image.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/library-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/library.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/news-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/news.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/open-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/open.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people-team-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people-team.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/record-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/record.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/server-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/server.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/wand-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/wand.svg create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContext.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/app-generic-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/app-generic-filled.svg new file mode 100644 index 000000000..87c7b6c41 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/app-generic-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/app-generic.svg b/dots/.config/quickshell/ii/assets/icons/fluent/app-generic.svg new file mode 100644 index 000000000..cd0c7b516 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/app-generic.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left-filled.svg new file mode 100644 index 000000000..f183cc835 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left.svg new file mode 100644 index 000000000..c9ffd3eda --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-enter-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calculator-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calculator-filled.svg new file mode 100644 index 000000000..92ad06215 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calculator-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/calculator.svg b/dots/.config/quickshell/ii/assets/icons/fluent/calculator.svg new file mode 100644 index 000000000..0dc9d3056 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/calculator.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/desktop-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-filled.svg new file mode 100644 index 000000000..d09661aec --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/desktop-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/desktop.svg b/dots/.config/quickshell/ii/assets/icons/fluent/desktop.svg new file mode 100644 index 000000000..979d35cc3 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/desktop.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/globe-search-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/globe-search-filled.svg new file mode 100644 index 000000000..933c6282d --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/globe-search-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/globe-search.svg b/dots/.config/quickshell/ii/assets/icons/fluent/globe-search.svg new file mode 100644 index 000000000..caa2c94ff --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/globe-search.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-copy-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-copy-filled.svg new file mode 100644 index 000000000..f01cd9872 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-copy-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-copy.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-copy.svg new file mode 100644 index 000000000..a40940693 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-copy.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image-filled.svg new file mode 100644 index 000000000..d35954106 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/image.svg b/dots/.config/quickshell/ii/assets/icons/fluent/image.svg new file mode 100644 index 000000000..379a1cd14 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/image.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/library-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/library-filled.svg new file mode 100644 index 000000000..7afac3d5e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/library-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/library.svg b/dots/.config/quickshell/ii/assets/icons/fluent/library.svg new file mode 100644 index 000000000..79432ebdd --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/library.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/news-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/news-filled.svg new file mode 100644 index 000000000..1d07cf802 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/news-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/news.svg b/dots/.config/quickshell/ii/assets/icons/fluent/news.svg new file mode 100644 index 000000000..9e0c3a2fe --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/news.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/open-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/open-filled.svg new file mode 100644 index 000000000..95ae58d39 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/open-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/open.svg b/dots/.config/quickshell/ii/assets/icons/fluent/open.svg new file mode 100644 index 000000000..561ed5ec2 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/open.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people-filled.svg new file mode 100644 index 000000000..96e98b863 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people-team-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people-team-filled.svg new file mode 100644 index 000000000..84364ec4e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people-team-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people-team.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people-team.svg new file mode 100644 index 000000000..8507e971c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people-team.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people.svg new file mode 100644 index 000000000..967192214 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/record-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/record-filled.svg new file mode 100644 index 000000000..164d37e87 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/record-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/record.svg b/dots/.config/quickshell/ii/assets/icons/fluent/record.svg new file mode 100644 index 000000000..31e957588 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/record.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/server-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/server-filled.svg new file mode 100644 index 000000000..2b0869653 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/server-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/server.svg b/dots/.config/quickshell/ii/assets/icons/fluent/server.svg new file mode 100644 index 000000000..1b876a172 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/server.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft-filled.svg new file mode 100644 index 000000000..6673448f3 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft.svg b/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft.svg new file mode 100644 index 000000000..affc6cc6e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/store-microsoft.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/wand-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/wand-filled.svg new file mode 100644 index 000000000..fffb70e7f --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/wand-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/wand.svg b/dots/.config/quickshell/ii/assets/icons/fluent/wand.svg new file mode 100644 index 000000000..a5033c274 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/wand.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml b/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml index 15beef12e..c47fc4f39 100644 --- a/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml +++ b/dots/.config/quickshell/ii/modules/common/models/LauncherSearchResult.qml @@ -19,11 +19,14 @@ QtObject { } property var actions: [] - // Stuff needed for DesktopEntry objects + // Stuff needed for DesktopEntry + property string id: "" property bool shown: true property string comment: "" property bool runInTerminal: false property string genericName: "" property list keywords: [] + // Extra stuff to allow for more flexibility + property string category: type } diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledScrollBar.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledScrollBar.qml index 7b677c426..f37203250 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledScrollBar.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledScrollBar.qml @@ -9,6 +9,7 @@ ScrollBar { policy: ScrollBar.AsNeeded topPadding: Appearance.rounding.normal bottomPadding: Appearance.rounding.normal + active: hovered || pressed contentItem: Rectangle { implicitWidth: 4 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index b9cbc00bd..af656d8d6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -152,6 +152,7 @@ Singleton { property real normal: 11 property real large: 13 property real larger: 15 + property real xlarger: 17 } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml index 7fe121165..b804aa3d9 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WIcons.qml @@ -48,7 +48,7 @@ Singleton { } property string batteryLevelIcon: { - const discreteLevel = Math.ceil(Battery.percentage * 10) + const discreteLevel = Math.ceil(Battery.percentage * 10); return `battery-${discreteLevel > 9 ? "full" : discreteLevel}`; } @@ -107,7 +107,8 @@ Singleton { function audioAppIcon(node) { let icon; icon = AppSearch.guessIcon(node?.properties["application.icon-name"] ?? ""); - if (AppSearch.iconExists(icon)) return icon; + if (AppSearch.iconExists(icon)) + return icon; icon = AppSearch.guessIcon(node?.properties["node.name"] ?? ""); return icon; } @@ -127,4 +128,60 @@ Singleton { return "bluetooth"; } + function fluentFromMaterial(icon) { + switch (icon) { + case "calculate": + return "calculator"; + case "keyboard_return": + return "arrow-enter-left"; + case "open_in_new": + return "open"; + case "settings_suggest": + return "wand"; + case "terminal": + return "app-generic"; + case "travel_explore": + return "globe-search"; + case "keep": + return "pin"; + case "keep_off": + return "pin-off"; + default: + return "apps"; + } + } + + function guessIconForName(name) { + const lowerName = name.toLowerCase(); + if (lowerName.includes("app") || lowerName.includes("desktop")) + return "apps"; + if (lowerName.includes("news")) + return "news"; + if (lowerName.includes("new") || lowerName.includes("create") || lowerName.includes("add")) + return "add"; + if (lowerName.includes("open")) + return "open"; + if (lowerName.includes("friends") || lowerName.includes("contact") || lowerName.includes("family")) + return "people"; + if (lowerName.includes("community")) + return "people-team"; + if (lowerName.includes("library")) + return "library"; + if (lowerName.includes("setting")) + return "settings"; + if (lowerName.includes("gallery")) + return "image-copy"; + if (lowerName.includes("server")) + return "server"; + if (lowerName.includes("picture") || lowerName.includes("photo") || lowerName.includes("image")) + return "image"; + if (lowerName.includes("store") || lowerName.includes("shop")) + return "store-microsoft"; + if (lowerName.includes("record") || lowerName.includes("capture")) + return "record"; + if (lowerName.includes("screen") || lowerName.includes("display") || lowerName.includes("monitor") || lowerName.includes("desktop")) + return "desktop"; + + return "apps"; + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml new file mode 100644 index 000000000..bc567762a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml @@ -0,0 +1,10 @@ +import qs.modules.common +import qs.modules.common.widgets +import QtQuick +import QtQuick.Controls + +ListView { + id: root + + ScrollBar.vertical: WScrollBar {} +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml new file mode 100644 index 000000000..bdc962806 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WScrollBar.qml @@ -0,0 +1,25 @@ +import QtQuick +import QtQuick.Controls +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions + +ScrollBar { + id: root + + policy: ScrollBar.AsNeeded + active: hovered || pressed + property color color: Looks.colors.controlBg + + contentItem: Rectangle { + implicitWidth: root.active ? 4 : 2 + implicitHeight: root.visualSize + radius: 9999 + color: root.color + + opacity: root.policy === ScrollBar.AlwaysOn || (root.active && root.size < 1.0) ? 0.5 : 0 + Behavior on opacity { + animation: Looks.transition.opacity.createObject(this) + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml index ce5652329..d38bc8d98 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchBar.qml @@ -15,10 +15,16 @@ FooterRectangle { property real horizontalPadding: 32 property real verticalPadding: 16 property bool searching: text.length > 0 + property alias searchInput: searchInput property alias text: searchInput.text implicitHeight: outline.implicitHeight + verticalPadding * 2 - Component.onCompleted: searchInput.forceActiveFocus() + signal accepted() + + Component.onCompleted: forceFocus() + function forceFocus() { + searchInput.forceActiveFocus(); + } focus: true color: searching ? Looks.colors.bgPanelBody : Looks.colors.bgPanelFooter @@ -81,6 +87,10 @@ FooterRectangle { visible: searchInput.text.length === 0 font.pixelSize: Looks.font.pixelSize.large } + + onAccepted: { + root.accepted(); + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml new file mode 100644 index 000000000..5f94870b7 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml @@ -0,0 +1,48 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.models +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Item { + id: root + required property LauncherSearchResult entry + property int iconSize: 24 + implicitWidth: Math.max(iconSize, textIconLoader.implicitWidth) + implicitHeight: iconSize + Loader { + anchors.centerIn: parent + active: root.entry.iconType === LauncherSearchResult.IconType.System && root.entry.iconName !== "" + sourceComponent: WAppIcon { + implicitSize: root.iconSize + iconName: root.entry.iconName + tryCustomIcon: false + animated: false + } + } + Loader { + id: textIconLoader + anchors.centerIn: parent + active: root.entry.iconType === LauncherSearchResult.IconType.Text + sourceComponent: WText { + text: root.entry.iconName + font.pixelSize: root.iconSize + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + } + } + Loader { + anchors.centerIn: parent + active: root.entry.iconType === LauncherSearchResult.IconType.Material || root.entry.iconType === LauncherSearchResult.IconType.None || root.entry.iconName === "" + sourceComponent: FluentIcon { + icon: root.entry.iconName ? WIcons.fluentFromMaterial(root.entry.iconName) : WIcons.guessIconForName(root.entry.name) + implicitSize: root.iconSize + animated: false + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml index a294950fd..b6f4a4e94 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml @@ -12,7 +12,9 @@ import qs.modules.waffle.looks BodyRectangle { id: root + property alias context: searchResults.context property string searchText: LauncherSearch.query + property alias currentIndex: searchResults.currentIndex ColumnLayout { anchors { @@ -24,11 +26,13 @@ BodyRectangle { spacing: 12 TagStrip { + context: root.context Layout.fillWidth: true Layout.fillHeight: false } SearchResults { + id: searchResults Layout.fillWidth: true Layout.fillHeight: true } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml index 5f3d93411..14627e54a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml @@ -1,19 +1,44 @@ +pragma ComponentBehavior: Bound +import qs +import qs.services +import qs.modules.common import qs.modules.waffle.looks import qs.modules.common.functions -import qs.modules.common -import qs.services -import qs +import qs.modules.common.models import Quickshell import QtQuick.Layouts import QtQuick.Controls import QtQuick -pragma ComponentBehavior: Bound RowLayout { id: root + property int maxResultsPerCategory: 4 + property StartMenuContext context + property int currentIndex: context.currentIndex + onCurrentIndexChanged: { + forceCurrentIndex(currentIndex); + } function focusFirstItem() { - resultList.currentIndex = 0; + forceCurrentIndex(0); + } + function forceCurrentIndex(index) { + context.currentIndex = index; + // Somehow this hack is needed + if (index === 0) { + resultList.incrementCurrentIndex(); + resultList.decrementCurrentIndex(); + } else { + resultList.decrementCurrentIndex(); + resultList.incrementCurrentIndex(); + } + } + + Connections { + target: context + function onAccepted() { + resultList.currentItem?.execute(); + } } ResultList { @@ -25,23 +50,74 @@ RowLayout { Layout.preferredWidth: 386 Layout.leftMargin: 1 Layout.rightMargin: 1 + entry: resultList.model[resultList.currentIndex] ?? searchResultComp.createObject() } - component ResultList: ListView { + component ResultList: WListView { + id: resultListView section { criteria: ViewSection.FullString - property: "type" + property: "category" // This is "type" with tweaks to make it match more closely + labelPositioning: ViewSection.InlineLabels + delegate: Item { + id: sectionButton + required property string section + implicitHeight: sectionChoiceButton.implicitHeight + resultListView.spacing + width: ListView.view?.width + WChoiceButton { + id: sectionChoiceButton + anchors { + left: parent.left + right: parent.right + top: parent.top + } + implicitHeight: 38 + contentItem: WText { + text: sectionButton.section + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + onClicked: { + root.context.selectCategory(sectionButton.section); + } + } + } } clip: true spacing: 4 - model: ScriptModel { - values: { - // TODO: categorize and have max per category - LauncherSearch.results.slice(0, 10) - } - onValuesChanged: { - root.focusFirstItem(); + currentIndex: root.currentIndex + + // We can't use a ScriptModel here because it would mess up sections + model: { + const allResults = LauncherSearch.results; + // Find categories + var categories = new Set(); + for (let i = 0; i < allResults.length; i++) { + categories.add(allResults[i].type); } + + // Collect max 4 per category + var categorizedResults = []; + categories.forEach(category => { + let count = 0; + for (let i = 0; i < allResults.length; i++) { + if (allResults[i].type === category) { + const entry = allResults[i]; + const tweakedEntry = searchResultComp.createObject(null, Object.assign({}, entry)); + tweakedEntry.category = categorizedResults.length === 0 ? Translation.tr("Best match") : entry.type + categorizedResults.push(tweakedEntry); // Section header + count++; + if (count >= root.maxResultsPerCategory) { + break; + } + } + } + }); + // print(JSON.stringify(categorizedResults, null, 2)); + return categorizedResults; + } + onModelChanged: { + root.focusFirstItem(); } delegate: WSearchResultButton { required property int index @@ -53,8 +129,112 @@ RowLayout { } component ResultPreview: Rectangle { + id: resultPreview + + property LauncherSearchResult entry // LauncherSearchResult + Layout.fillHeight: true color: Looks.colors.bg1 radius: Looks.radius.large + + ColumnLayout { + anchors.fill: parent + anchors.margins: 22 + spacing: 13 + + ColumnLayout { + id: mainInfoColumn + Layout.alignment: Qt.AlignHCenter + SearchEntryIcon { + Layout.alignment: Qt.AlignHCenter + Layout.topMargin: 10 + Layout.bottomMargin: 12 + entry: resultPreview.entry + iconSize: 64 + } + WText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + wrapMode: Text.Wrap + maximumLineCount: 2 + text: resultPreview.entry?.name || "" + font.pixelSize: Looks.font.pixelSize.xlarger + } + WText { + Layout.alignment: Qt.AlignHCenter + text: resultPreview.entry?.type || "" + color: Looks.colors.accentUnfocused + font.pixelSize: Looks.font.pixelSize.normal + } + } + Rectangle { + id: resultSeparator + implicitHeight: 2 + Layout.topMargin: 16 + Layout.fillWidth: true + color: Looks.colors.bg2Hover + } + WListView { + id: actionsColumn + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + spacing: 2 + model: { + const isAppEntry = resultPreview.entry.type === Translation.tr("App"); + const appId = isAppEntry ? resultPreview.entry.id : ""; + const pinned = isAppEntry ? (Config.options.dock.pinnedApps.includes(appId)) : false; + var result = [ + searchResultComp.createObject(null, { + name: resultPreview.entry.verb, + iconName: isAppEntry ? "open_in_new" : "keyboard_return", + iconType: LauncherSearchResult.IconType.Material, + execute: () => { + resultPreview.entry.execute(); + } + }), + ...(isAppEntry ? [ + searchResultComp.createObject(null, { + name: pinned ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar"), + iconName: pinned ? "keep_off" : "keep", + iconType: LauncherSearchResult.IconType.Material, + execute: () => { + TaskbarApps.togglePin(appId); + } + }) + ] : []) + ]; + result = result.concat(resultPreview.entry.actions); + return result; + } + delegate: WButton { + id: actionButton + required property var modelData + width: ListView.view?.width + icon.name: modelData.iconName + text: modelData.name + onClicked: modelData.execute(); + + contentItem: RowLayout { + spacing: 11 + SearchEntryIcon { + entry: actionButton.modelData + iconSize: 16 + } + WText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + text: actionButton.text + } + } + } + } + } + } + + Component { + id: searchResultComp + LauncherSearchResult {} } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index 09de017ee..2f76ca0ca 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -16,10 +16,72 @@ WBarAttachedPanelContent { property bool searching: false property string searchText: LauncherSearch.query + StartMenuContext { + id: context + } + + Keys.onPressed: event => { + // Prevent Esc and Backspace from registering + if (event.key === Qt.Key_Escape) + return; + + // Handle Backspace: focus and delete character if not focused + if (event.key === Qt.Key_Backspace) { + searchBar.forceFocus(); + if (event.modifiers & Qt.ControlModifier) { + // Delete word before cursor + let text = searchBar.text; + let pos = searchBar.searchInput.cursorPosition; + if (pos > 0) { + // Find the start of the previous word + let left = text.slice(0, pos); + let match = left.match(/(\s*\S+)\s*$/); + let deleteLen = match ? match[0].length : 1; + searchBar.text = text.slice(0, pos - deleteLen) + text.slice(pos); + searchBar.searchInput.cursorPosition = pos - deleteLen; + } + } else { + // Delete character before cursor if any + if (searchBar.searchInput.cursorPosition > 0) { + searchBar.text = searchBar.text.slice(0, searchBar.searchInput.cursorPosition - 1) + searchBar.text.slice(searchBar.searchInput.cursorPosition); + searchBar.searchInput.cursorPosition -= 1; + } + } + // Always move cursor to end after programmatic edit + searchBar.searchInput.cursorPosition = searchBar.text.length; + event.accepted = true; + // If already focused, let TextField handle it + return; + } + + // Only handle visible printable characters (ignore control chars, arrows, etc.) + if (event.text && event.text.length === 1 && event.key !== Qt.Key_Enter && event.key !== Qt.Key_Return && event.key !== Qt.Key_Delete && event.text.charCodeAt(0) >= 0x20) // ignore control chars like Backspace, Tab, etc. + { + if (!searchBar.searchInput.activeFocus) { + searchBar.forceFocus(); + // Insert the character at the cursor position + searchBar.text = searchBar.text.slice(0, searchBar.searchInput.cursorPosition) + event.text + searchBar.text.slice(searchBar.searchInput.cursorPosition); + searchBar.searchInput.cursorPosition += 1; + event.accepted = true; + context.setCurrentIndex(0); + } + } + + // Arrow keys for item navigation + if (event.key === Qt.Key_Down) { + let maxIndex = Math.max(0, LauncherSearch.results.length - 1); + context.setCurrentIndex(Math.min(context.currentIndex + 1, maxIndex)); + event.accepted = true; + } else if (event.key === Qt.Key_Up) { + context.setCurrentIndex(Math.max(context.currentIndex - 1, 0)); + event.accepted = true; + } + } + contentItem: WPane { contentItem: WPanelPageColumn { SearchBar { - focus: true + id: searchBar Layout.fillWidth: true implicitWidth: 832 // TODO: Make sizes naturally inferred horizontalPadding: root.searching ? 24 : 32 @@ -27,10 +89,14 @@ WBarAttachedPanelContent { Synchronizer on searching { property alias target: root.searching } + focus: true text: root.searchText onTextChanged: { LauncherSearch.query = text; } + onAccepted: { + context.accepted(); + } } Item { implicitHeight: root.searching ? 736 : 736 // TODO: Make sizes naturally inferred @@ -46,7 +112,9 @@ WBarAttachedPanelContent { Component { id: searchPageComp - SearchPageContent {} + SearchPageContent { + context: context + } } Component { diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContext.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContext.qml new file mode 100644 index 000000000..ff2289da0 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContext.qml @@ -0,0 +1,64 @@ +import QtQuick +import Quickshell +import Quickshell.Io +import qs +import qs.modules.common +import qs.services + +Scope { + id: root + + signal accepted + + property int currentIndex: 0 + function setCurrentIndex(index) { + if (index == currentIndex) + return; + currentIndex = index; + } + + function selectCategory(category) { + for (let i = 0; i < root.categories.length; i++) { + const thisCategoryName = root.categories[i].name; + if (thisCategoryName.startsWith(category) || category.startsWith(thisCategoryName)) { + LauncherSearch.ensurePrefix(root.categories[i].prefix); + return; + } + } + } + property list categories: [ + { + name: Translation.tr("All"), + prefix: "" + }, + { + name: Translation.tr("Apps"), + prefix: Config.options.search.prefix.app + }, + { + name: Translation.tr("Actions"), + prefix: Config.options.search.prefix.action + }, + { + name: Translation.tr("Clipboard"), + prefix: Config.options.search.prefix.clipboard + }, + { + name: Translation.tr("Emojis"), + prefix: Config.options.search.prefix.emojis + }, + { + name: Translation.tr("Math"), + prefix: Config.options.search.prefix.math + }, + { + name: Translation.tr("Commands"), + prefix: Config.options.search.prefix.shellCommand + }, + { + name: Translation.tr("Web"), + prefix: Config.options.search.prefix.webSearch + }, + ] + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml index 10448b5cd..ce7475815 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml @@ -10,6 +10,9 @@ import qs.modules.common.functions import qs.modules.waffle.looks RowLayout { + id: root + property StartMenuContext context + WPanelIconButton { implicitWidth: 36 implicitHeight: 36 @@ -23,40 +26,7 @@ RowLayout { Layout.fillHeight: true orientation: Qt.Horizontal spacing: 4 - model: [ - { - name: Translation.tr("All"), - prefix: "" - }, - { - name: Translation.tr("Apps"), - prefix: Config.options.search.prefix.app - }, - { - name: Translation.tr("Actions"), - prefix: Config.options.search.prefix.action - }, - { - name: Translation.tr("Clipboard"), - prefix: Config.options.search.prefix.clipboard - }, - { - name: Translation.tr("Emojis"), - prefix: Config.options.search.prefix.emojis - }, - { - name: Translation.tr("Math"), - prefix: Config.options.search.prefix.math - }, - { - name: Translation.tr("Commands"), - prefix: Config.options.search.prefix.shellCommand - }, - { - name: Translation.tr("Web"), - prefix: Config.options.search.prefix.webSearch - }, - ] + model: root.context.categories delegate: WBorderedButton { id: tagButton required property var modelData diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml index 1aca104b0..4cd28fa9a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml @@ -11,7 +11,7 @@ import qs.modules.waffle.looks WChoiceButton { id: root - + required property LauncherSearchResult entry property bool firstEntry: false @@ -21,45 +21,28 @@ WChoiceButton { implicitHeight: contentLayout.implicitHeight + topPadding + bottomPadding onClicked: { - GlobalStates.searchOpen = false - root.entry.execute() + execute(); } - + + function execute() { + GlobalStates.searchOpen = false; + root.entry.execute(); + } + contentItem: RowLayout { id: contentLayout spacing: 8 - - EntryIcon {} + + SearchEntryIcon { + entry: root.entry + iconSize: 24 + } EntryNameColumn { Layout.fillWidth: true Layout.alignment: Qt.AlignVCenter } } - component EntryIcon: Item { - implicitWidth: 24 - implicitHeight: 24 - Loader { - anchors.centerIn: parent - active: root.entry.iconType === LauncherSearchResult.IconType.System - sourceComponent: WAppIcon { - implicitSize: 24 - tryCustomIcon: false - iconName: root.entry.iconName - } - } - Loader { - anchors.centerIn: parent - active: root.entry.iconType === LauncherSearchResult.IconType.Text - sourceComponent: WText { - text: root.entry.iconName - font.pixelSize: 24 - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - } - } - } - component EntryNameColumn: ColumnLayout { spacing: 4 @@ -78,4 +61,11 @@ WChoiceButton { color: Looks.colors.accentUnfocused } } + + MouseArea { + anchors.fill: parent + // hoverEnabled: true + acceptedButtons: Qt.NoButton + cursorShape: Qt.PointingHandCursor + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml index 9b59fb0c8..15ffc6f43 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml @@ -15,8 +15,10 @@ Scope { target: GlobalStates function onSearchOpenChanged() { - if (GlobalStates.searchOpen) + if (GlobalStates.searchOpen) { + LauncherSearch.query = ""; panelLoader.active = true; + } } } @@ -62,6 +64,7 @@ Scope { onClosed: { GlobalStates.searchOpen = false; panelLoader.active = false; + LauncherSearch.query = ""; } } } diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index 89d9e0056..49de2158e 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -198,10 +198,11 @@ Singleton { const appResultObjects = AppSearch.fuzzyQuery(StringUtils.cleanPrefix(root.query, Config.options.search.prefix.app)).map(entry => { return resultComp.createObject(null, { type: Translation.tr("App"), + id: entry.id, name: entry.name, iconName: entry.icon, iconType: LauncherSearchResult.IconType.System, - verb: Translation.tr("Launch"), + verb: Translation.tr("Open"), execute: () => { if (!entry.runInTerminal) entry.execute(); @@ -233,7 +234,7 @@ Singleton { const commandResultObject = resultComp.createObject(null, { name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.shellCommand).replace("file://", ""), verb: Translation.tr("Run"), - type: Translation.tr("Run command"), + type: Translation.tr("Command"), fontType: LauncherSearchResult.FontType.Monospace, iconName: 'terminal', iconType: LauncherSearchResult.IconType.Material, @@ -249,7 +250,7 @@ Singleton { const webSearchResultObject = resultComp.createObject(null, { name: StringUtils.cleanPrefix(root.query, Config.options.search.prefix.webSearch), verb: Translation.tr("Search"), - type: Translation.tr("Search the web"), + type: Translation.tr("Web search"), iconName: 'travel_explore', iconType: LauncherSearchResult.IconType.Material, execute: () => { From 7a2e21ac7c50092d93231712b0ab75a53883e053 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 5 Dec 2025 00:19:37 +0100 Subject: [PATCH 062/154] add service for privacy indicators --- dots/.config/quickshell/ii/services/Audio.qml | 7 +------ dots/.config/quickshell/ii/services/Privacy.qml | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 6 deletions(-) create mode 100644 dots/.config/quickshell/ii/services/Privacy.qml diff --git a/dots/.config/quickshell/ii/services/Audio.qml b/dots/.config/quickshell/ii/services/Audio.qml index 33c49cc30..68ebc3ae0 100644 --- a/dots/.config/quickshell/ii/services/Audio.qml +++ b/dots/.config/quickshell/ii/services/Audio.qml @@ -18,12 +18,7 @@ Singleton { readonly property real hardMaxValue: 2.00 // People keep joking about setting volume to 5172% so... property string audioTheme: Config.options.sounds.theme property real value: sink?.audio.volume ?? 0 - property bool micBeingAccessed: Pipewire.links.values.filter(link => - !link.source.isStream && !link.source.isSink && link.target.isStream - ).length > 0 - onMicBeingAccessedChanged: { - print(micBeingAccessed) - } + function friendlyDeviceName(node) { return (node.nickname || node.description || Translation.tr("Unknown")); } diff --git a/dots/.config/quickshell/ii/services/Privacy.qml b/dots/.config/quickshell/ii/services/Privacy.qml new file mode 100644 index 000000000..a14da7689 --- /dev/null +++ b/dots/.config/quickshell/ii/services/Privacy.qml @@ -0,0 +1,16 @@ +pragma Singleton +pragma ComponentBehavior: Bound +import qs.modules.common +import QtQuick +import Quickshell +import Quickshell.Services.Pipewire + +/** + * Screensharing and mic activity. + */ +Singleton { + id: root + + property bool screenSharing: Pipewire.linkGroups.values.filter(pwlg => pwlg.source.type === PwNodeType.VideoSource).map(pwlg => pwlg.target) + property bool micActive: Pipewire.linkGroups.values.filter(pwlg => pwlg.source.type === PwNodeType.AudioSource && pwlg.target.type === PwNodeType.AudioInStream).map(pwlg => pwlg.target) +} From ed89ad882f80da8de72c923eb4a8f803ca26a874 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 5 Dec 2025 12:20:50 +0100 Subject: [PATCH 063/154] waffles: start menu: add the right arrow thingy --- .../icons/fluent/people-settings-filled.svg | 1 + .../assets/icons/fluent/people-settings.svg | 1 + .../ii/modules/waffle/looks/Looks.qml | 4 +- .../waffle/startMenu/SearchResults.qml | 4 ++ .../ii/modules/waffle/startMenu/TagStrip.qml | 23 ++++++- .../waffle/startMenu/WSearchResultButton.qml | 64 +++++++++++++++++-- 6 files changed, 86 insertions(+), 11 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people-settings-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/people-settings.svg diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people-settings-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people-settings-filled.svg new file mode 100644 index 000000000..0bcd7d12d --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people-settings-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/people-settings.svg b/dots/.config/quickshell/ii/assets/icons/fluent/people-settings.svg new file mode 100644 index 000000000..8ff1df32a --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/people-settings.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index af656d8d6..802add786 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -17,8 +17,8 @@ Singleton { property string iconsPath: `${Directories.assetsPath}/icons/fluent` property bool dark: Appearance.m3colors.darkmode - property real backgroundTransparency: 0.13 - property real panelBackgroundTransparency: 0.12 + property real backgroundTransparency: 0.16 + property real panelBackgroundTransparency: 0.14 property real panelLayerTransparency: root.dark ? 0.9 : 0.7 property real contentTransparency: root.dark ? 0.87 : 0.5 function applyBackgroundTransparency(col) { diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml index 14627e54a..a9e83929e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml @@ -125,6 +125,10 @@ RowLayout { entry: modelData firstEntry: index === 0 width: ListView.view?.width + checked: resultListView.currentIndex === index + onRequestFocus: { + root.forceCurrentIndex(index); + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml index ce7475815..a1deb0027 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml @@ -12,7 +12,7 @@ import qs.modules.waffle.looks RowLayout { id: root property StartMenuContext context - + WPanelIconButton { implicitWidth: 36 implicitHeight: 36 @@ -27,6 +27,7 @@ RowLayout { orientation: Qt.Horizontal spacing: 4 model: root.context.categories + clip: true delegate: WBorderedButton { id: tagButton required property var modelData @@ -38,7 +39,7 @@ RowLayout { if (modelData.prefix != "") { return LauncherSearch.query.startsWith(modelData.prefix); } else { - return !tagListView.model.some(i => (i.prefix != "" && LauncherSearch.query.startsWith(i.prefix))) + return !tagListView.model.some(i => (i.prefix != "" && LauncherSearch.query.startsWith(i.prefix))); } } contentItem: Item { @@ -54,9 +55,27 @@ RowLayout { } } WPanelIconButton { + id: optionsButton implicitWidth: 36 implicitHeight: 36 iconSize: 24 iconName: "more-horizontal" + + onClicked: accountsMenu.open() + + WMenu { + id: accountsMenu + x: -accountsMenu.implicitWidth + optionsButton.implicitWidth + y: optionsButton.height + 10 + downDirection: true + Action { + icon.name: "people-settings" + text: Translation.tr("Manage accounts") + onTriggered: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) + GlobalStates.searchOpen = false; + } + } + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml index 4cd28fa9a..b7fb1df14 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml @@ -15,6 +15,8 @@ WChoiceButton { required property LauncherSearchResult entry property bool firstEntry: false + signal requestFocus() + checked: focus animateChoiceHighlight: false implicitWidth: contentLayout.implicitWidth + leftPadding + rightPadding @@ -29,17 +31,65 @@ WChoiceButton { root.entry.execute(); } + horizontalPadding: 0 + verticalPadding: 0 + contentItem: RowLayout { id: contentLayout - spacing: 8 + spacing: 0 - SearchEntryIcon { - entry: root.entry - iconSize: 24 - } - EntryNameColumn { + WButton { + id: launchButton Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter + Layout.fillHeight: true + horizontalPadding: 10 + verticalPadding: 11 + implicitHeight: root.firstEntry ? 62 : 36 + implicitWidth: entryContentRow.implicitWidth + leftPadding + rightPadding + topRightRadius: 0 + bottomRightRadius: 0 + onClicked: root.click() + contentItem: Item { + RowLayout { + id: entryContentRow + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + spacing: 8 + + SearchEntryIcon { + entry: root.entry + iconSize: 24 + } + EntryNameColumn { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + } + } + } + } + Rectangle { + id: separator + opacity: (root.hovered && !root.checked) ? 1 : 0 + Layout.fillHeight: true + implicitWidth: 1 + color: ColorUtils.transparentize(Looks.colors.fg, 0.75) + } + WButton { + visible: !root.checked + Layout.fillHeight: true + implicitWidth: 47 + topLeftRadius: 0 + bottomLeftRadius: 0 + onClicked: root.requestFocus() + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + icon: "chevron-right" + implicitSize: 14 + } + } } } From 255006172f9079ddcd8e884edb69b12f47243cf4 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 6 Dec 2025 00:22:13 +0300 Subject: [PATCH 064/154] fix: make thumbnail generator script actually work --- .../quickshell/ii/scripts/thumbnails/thumbgen-venv.sh | 5 ++--- dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh index fc7d8729e..a27b5ca1d 100755 --- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh +++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh @@ -2,6 +2,5 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate -"$SCRIPT_DIR/thumbgen.py" "$@" -deactivate - +GIO_USE_VFS=local "$SCRIPT_DIR/thumbgen.py" "$@" +deactivate \ No newline at end of file diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py index 69f630721..92dd126f6 100755 --- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py +++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen.py @@ -15,7 +15,7 @@ import gi from loguru import logger from tqdm import tqdm -gi.require_version("GnomeDesktop", "3.0") +gi.require_version("GnomeDesktop", "4.0") from gi.repository import Gio, GnomeDesktop # isort:skip thumbnail_size_map = { From fa47c6778cc370b25b4056fdf9ba7f4e253cd3f3 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sat, 6 Dec 2025 00:26:36 +0300 Subject: [PATCH 065/154] wallpaper selector: add thumbnail loading indicator --- .../ii/wallpaperSelector/WallpaperDirectoryItem.qml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml index 23f7915ef..f0a53bf39 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml @@ -11,6 +11,7 @@ MouseArea { required property var fileModelData property bool isDirectory: fileModelData.fileIsDir property bool useThumbnail: Images.isValidImageByName(fileModelData.fileName) + property bool showLoadingIndicator: false property alias colBackground: background.color property alias colText: wallpaperItemName.color @@ -69,6 +70,8 @@ MouseArea { sourceSize.width: wallpaperItemColumnLayout.width sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height + onVisibleChanged: root.showLoadingIndicator = !thumbnailImage.visible + Connections { target: Wallpapers function onThumbnailGenerated(directory) { @@ -106,6 +109,15 @@ MouseArea { sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height } } + + FadeLoader { + id: loadingIndicatorLoader + shown: root.showLoadingIndicator + anchors.centerIn: parent + sourceComponent: MaterialLoadingIndicator { + loading: true + } + } } StyledText { From 80a7804adedf6f7bf6e3f14641d36461a379ba7b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 6 Dec 2025 13:17:29 +0100 Subject: [PATCH 066/154] waffles: start menu apps --- .../quickshell/ii/modules/common/Config.qml | 4 + .../mainPage/MainPageBodyToggles.qml | 58 +--- .../ii/modules/waffle/looks/Looks.qml | 4 +- .../waffle/looks/VerticalPageIndicator.qml | 73 ++++ .../startMenu/AggregatedAppCategoryModel.qml | 7 + .../modules/waffle/startMenu/AllAppsGrid.qml | 84 +++++ .../waffle/startMenu/AppCategoryGrid.qml | 311 ++++++++++++++++++ .../modules/waffle/startMenu/BigAppGrid.qml | 39 +++ .../waffle/startMenu/SearchResults.qml | 15 + .../waffle/startMenu/StartAppButton.qml | 46 +++ .../waffle/startMenu/StartMenuContent.qml | 2 +- .../waffle/startMenu/StartPageApps.qml | 74 +++++ .../waffle/startMenu/StartPageContent.qml | 132 +------- .../waffle/startMenu/StartUserButton.qml | 140 ++++++++ .../ii/modules/waffle/startMenu/TagStrip.qml | 4 +- dots/.config/quickshell/ii/services/Audio.qml | 4 +- .../quickshell/ii/services/LauncherSearch.qml | 11 + 17 files changed, 822 insertions(+), 186 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/VerticalPageIndicator.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index b0b767a44..462ee4dfb 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -346,6 +346,10 @@ Singleton { } } + property JsonObject launcher: JsonObject { + property list pinnedApps: [ "org.kde.dolphin", "kitty", "cmake-gui"] + } + property JsonObject light: JsonObject { property JsonObject night: JsonObject { property bool automatic: true diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml index 88dbc9e03..64298b712 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/mainPage/MainPageBodyToggles.qml @@ -87,38 +87,16 @@ Item { } } - Column { + VerticalPageIndicator { anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 6 - spacing: 6 - - NavigationArrow { - down: false - } - - Repeater { - model: root.pages - delegate: MouseArea { - id: pageIndicator - required property int index - hoverEnabled: true - onClicked: root.currentPage = index - anchors.horizontalCenter: parent.horizontalCenter - implicitWidth: 6 - implicitHeight: 6 - - Circle { - anchors.centerIn: parent - diameter: (index === root.currentPage || pageIndicator.containsMouse) && !pageIndicator.pressed ? 6 : 4 - color: pageIndicator.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg - } - } - } - - NavigationArrow { - down: true - } + + currentIndex: root.currentPage + count: root.pages + onClicked: (index) => root.currentPage = index + onIncreasePage: root.increasePage(); + onDecreasePage: root.decreasePage(); } FocusedScrollMouseArea { @@ -126,25 +104,7 @@ Item { anchors.fill: parent acceptedButtons: Qt.NoButton hoverEnabled: false - onScrollUp: decreasePage(); - onScrollDown: increasePage(); - } - - component NavigationArrow: FluentIcon { - id: navArrow - required property bool down - anchors.horizontalCenter: parent.horizontalCenter - implicitHeight: 12 - implicitWidth: 12 - (2 * upArea.containsPress) - icon: down ? "caret-down" : "caret-up" - color: upArea.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg - filled: true - opacity: ((down && root.currentPage < root.pages - 1) || (!down && root.currentPage > 0)) ? 1 : 0 - MouseArea { - id: upArea - anchors.fill: parent - hoverEnabled: true - onClicked: navArrow.down ? root.increasePage() : root.decreasePage(); - } + onScrollUp: root.decreasePage(); + onScrollDown: root.increasePage(); } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 802add786..b018b0629 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -96,7 +96,7 @@ Singleton { property color bg0Opaque: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 property color bg0: ColorUtils.transparentize(bg0Opaque, root.backgroundTransparency) property color bg0Border: ColorUtils.transparentize(root.dark ? root.darkColors.bg0Border : root.lightColors.bg0Border, root.backgroundTransparency) - property color bg1Base: ColorUtils.transparentize(root.dark ? root.darkColors.bg1Base : root.lightColors.bg1Base, 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) @@ -146,7 +146,7 @@ Singleton { property int thin: Font.Normal property int regular: Font.Medium property int strong: Font.DemiBold - property int stronger: Font.Bold + property int stronger: (Font.DemiBold + 2*Font.Bold) / 3 } property QtObject pixelSize: QtObject { property real normal: 11 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/VerticalPageIndicator.qml b/dots/.config/quickshell/ii/modules/waffle/looks/VerticalPageIndicator.qml new file mode 100644 index 000000000..bb46bdc1c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/VerticalPageIndicator.qml @@ -0,0 +1,73 @@ +pragma ComponentBehavior: Bound +import Qt.labs.synchronizer +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +Column { + id: root + + property bool showArrows: true + property int currentIndex: 0 + property int count: 1 + signal clicked(int index) + signal increasePage() + signal decreasePage() + + visible: count > 1 + spacing: 6 + + NavigationArrow { + visible: root.showArrows + down: false + } + + Repeater { + model: root.count + delegate: MouseArea { + id: pageIndicator + required property int index + hoverEnabled: true + onClicked: root.clicked(index); + anchors.horizontalCenter: parent.horizontalCenter + implicitWidth: 6 + implicitHeight: 6 + + Circle { + anchors.centerIn: parent + diameter: (index === root.currentIndex || pageIndicator.containsMouse) && !pageIndicator.pressed ? 6 : 4 + color: pageIndicator.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg + } + } + } + + NavigationArrow { + visible: root.showArrows + down: true + } + + component NavigationArrow: FluentIcon { + id: navArrow + required property bool down + anchors.horizontalCenter: parent.horizontalCenter + implicitHeight: 12 + implicitWidth: 12 - (2 * upArea.containsPress) + icon: down ? "caret-down" : "caret-up" + color: upArea.containsMouse ? Looks.colors.controlBgHover : Looks.colors.controlBg + filled: true + opacity: ((down && root.currentIndex < root.count - 1) || (!down && root.currentIndex > 0)) ? 1 : 0 + MouseArea { + id: upArea + anchors.fill: parent + hoverEnabled: true + onClicked: navArrow.down ? root.increasePage() : root.decreasePage(); + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml new file mode 100644 index 000000000..e3886235c --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml @@ -0,0 +1,7 @@ +import QtQuick +import qs.services + +QtObject { + property string name + property list categories +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml new file mode 100644 index 000000000..aa1321eb8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml @@ -0,0 +1,84 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +GridLayout { + id: root + + columns: 4 + + Component { + id: aggAppCatComp + AggregatedAppCategoryModel {} + } + property list aggregatedCategories: [ + aggAppCatComp.createObject(null, { + name: Translation.tr("Productivity"), + categories: ["Development", "Education", "Network", "Office"] + }), aggAppCatComp.createObject(null, { + name: Translation.tr("Utilities & Tools"), + categories: ["Utility", "Science"] + }), 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"] + }) + ] + + Repeater { + model: root.aggregatedCategories + delegate: AppCategory { + required property var modelData + aggregatedCategory: modelData + } + } + + columnSpacing: 27 + rowSpacing: 12 + component AppCategory: Item { + id: categoryItem + property AggregatedAppCategoryModel aggregatedCategory + implicitWidth: categoryLayout.implicitWidth + implicitHeight: categoryLayout.implicitHeight + ColumnLayout { + id: categoryLayout + anchors.fill: parent + spacing: 4 + + AppCategoryGrid { + id: categoryGrid + Layout.fillWidth: true + aggregatedCategory: categoryItem.aggregatedCategory + } + + WButton { + id: categoryButton + Layout.fillWidth: true + implicitHeight: 32 + + contentItem: WText { + id: categoryButtonText + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + elide: Text.ElideRight + text: categoryItem.aggregatedCategory.name + } + onClicked: { + categoryGrid.openCategoryFolder(); + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml new file mode 100644 index 000000000..38c8d8d41 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml @@ -0,0 +1,311 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Rectangle { + id: root + property AggregatedAppCategoryModel aggregatedCategory + property list desktopEntries: DesktopEntries.applications.values.filter(app => { + const appCategories = app.categories; + const gridCategories = root.aggregatedCategory.categories; + return appCategories.some(cat => gridCategories.indexOf(cat) !== -1); + }) + + property Item windowRootItem: { + var item = root; + // print("FINDING ROOT") + while (item.parent != null) { + if (item.parent.toString().includes("ProxyWindow")) + break; + item = item.parent; + } + // print(item.width, item.height) + return item; + } + function openCategoryFolder() { + categoryFolderPopup.open(); + } + + radius: Looks.radius.large + color: Looks.colors.bg1 + border.width: 1 + border.color: ColorUtils.transparentize(Looks.colors.ambientShadow, 0.7) + implicitWidth: 156 + implicitHeight: 156 + + GridLayout { + id: categoryAppsGrid + anchors.fill: parent + anchors.margins: 10 + columns: 2 + rows: 2 + columnSpacing: 0 + rowSpacing: 0 + uniformCellHeights: true + uniformCellWidths: true + + Repeater { + model: ScriptModel { + values: root.desktopEntries.slice(0, 3) + } + delegate: SmallGridAppButton { + required property DesktopEntry modelData + desktopEntry: modelData + } + } + Loader { + id: categoryOpenButtonLoader + // It's like this on the real thing - you get an invisible button if there's not enough items + opacity: root.desktopEntries.length > 3 ? 1 : 0 + active: true + sourceComponent: CategoryOpenButton { + aggregatedCategory: root.aggregatedCategory + } + } + } + + Popup { + id: categoryFolderPopup + // I don't even know what the fuck is going on at this point + // I hate point mapping + property point originPoint: categoryOpenButtonLoader.mapToItem(root, categoryOpenButtonLoader.width / 2, categoryOpenButtonLoader.height / 2) + property point windowCenterPoint: { + const rootContentItem = root.windowRootItem; + const canvasPosInRoot = root.mapFromItem(rootContentItem, rootContentItem.width / 2, rootContentItem.height / 2); + const sectionItem = root.parent.parent.parent; + const positionInSection = sectionItem.mapFromItem(categoryOpenButtonLoader, categoryOpenButtonLoader.x, categoryOpenButtonLoader.y); + const targetY = Math.max(-positionInSection.y + 212, canvasPosInRoot.y); + return Qt.point(canvasPosInRoot.x, targetY); + } + + enter: Transition { + NumberAnimation { + target: categoryFolderPopup + property: "x" + from: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 3 / 2 + to: categoryFolderPopup.windowCenterPoint.x - categoryFolderPopup.width / 2 + duration: 300 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + NumberAnimation { + target: categoryFolderPopup + property: "y" + from: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height / 2 + to: categoryFolderPopup.windowCenterPoint.y - categoryFolderPopup.height / 2 + duration: 300 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + NumberAnimation { + target: categoryFolderPopup + property: "scale" + from: 0 + to: 1 + duration: 300 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + + exit: Transition { + NumberAnimation { + target: categoryFolderPopup + property: "x" + to: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 3 / 2 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + NumberAnimation { + target: categoryFolderPopup + property: "y" + to: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height / 2 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + NumberAnimation { + target: categoryFolderPopup + property: "scale" + from: 1 + to: 0 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut + } + } + + background: null + + Loader { + active: categoryFolderPopup.visible + sourceComponent: CategoryFolderContent { + title: root.aggregatedCategory.name + desktopEntries: root.desktopEntries + } + } + } + + component CategoryFolderContent: WToolTipContent { + id: categoryFolderContent + property string title + property list desktopEntries: root.desktopEntries + horizontalPadding: 0 + verticalPadding: 0 + radius: Looks.radius.large + realContentItem: Item { + implicitWidth: 448 + implicitHeight: 376 + ColumnLayout { + anchors { + fill: parent + leftMargin: 32 + rightMargin: 32 + topMargin: 40 + bottomMargin: 32 + } + spacing: 28 + WText { + Layout.fillWidth: true + text: categoryFolderContent.title + font.pixelSize: Looks.font.pixelSize.xlarger + font.weight: Looks.font.weight.stronger + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + + SwipeView { + id: categoryFolderSwipeView + anchors.fill: parent + orientation: Qt.Vertical + clip: true + + Repeater { + model: Math.ceil(root.desktopEntries.length / 12) + delegate: Item { + id: folderPage + required property int index + width: SwipeView.view.width + height: SwipeView.view.height + BigAppGrid { + anchors { + top: parent.top + left: parent.left + } + columns: 4 + rows: 3 + desktopEntries: root.desktopEntries.slice(folderPage.index * 12, (folderPage.index + 1) * 12) + } + } + } + } + VerticalPageIndicator { + anchors.verticalCenter: parent.verticalCenter + anchors.right: categoryFolderSwipeView.right + anchors.rightMargin: -19 + + showArrows: false + currentIndex: categoryFolderSwipeView.currentIndex + count: Math.ceil(root.desktopEntries.length / 12) + onClicked: index => categoryFolderSwipeView.currentIndex = index + } + } + } + FocusedScrollMouseArea { + z: 999 + anchors.fill: parent + acceptedButtons: Qt.NoButton + hoverEnabled: false + onScrollUp: categoryFolderSwipeView.decrementCurrentIndex() + onScrollDown: categoryFolderSwipeView.incrementCurrentIndex() + } + } + } + + component CategoryOpenButton: SmallGridButton { + id: categoryOpenButton + property AggregatedAppCategoryModel aggregatedCategory + + onClicked: root.openCategoryFolder() + contentItem: Item { + Behavior on scale { + NumberAnimation { + id: scaleAnim + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + GridLayout { + anchors.centerIn: parent + rows: 2 + columns: 2 + rowSpacing: 2 + columnSpacing: 2 + + Repeater { + model: root.desktopEntries.slice(3, 7) + delegate: WAppIcon { + required property DesktopEntry modelData + tryCustomIcon: false + iconName: modelData.icon + implicitSize: 16 + } + } + } + } + } + + component SmallGridAppButton: SmallGridButton { + id: smallGridAppButton + property DesktopEntry desktopEntry + + onClicked: { + GlobalStates.searchOpen = false; + desktopEntry.execute(); + } + + contentItem: Item { + Behavior on scale { + NumberAnimation { + id: scaleAnim + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + WAppIcon { + anchors.centerIn: parent + tryCustomIcon: false + iconName: smallGridAppButton.desktopEntry.icon + implicitSize: 34 + } + } + + WToolTip { + text: smallGridAppButton.desktopEntry.name + } + } + + component SmallGridButton: WButton { + id: root + implicitWidth: 68 + implicitHeight: 68 + + property real pressedScale: 5 / 6 + + onDownChanged: { + contentItem.scale = root.down ? root.pressedScale : 1; // If/When we do dragging, the scale is 1.25 + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml new file mode 100644 index 000000000..3142466b9 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml @@ -0,0 +1,39 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +GridLayout { + id: root + + property list desktopEntries: [] + + columnSpacing: 0 + rowSpacing: 0 + + uniformCellHeights: true + uniformCellWidths: true + + Repeater { + model: ScriptModel { + values: root.desktopEntries + } + delegate: StartAppButton { + id: pinnedAppButton + required property var modelData + desktopEntry: modelData + onClicked: { + GlobalStates.searchOpen = false; + desktopEntry.execute(); + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml index a9e83929e..918534b78 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml @@ -189,6 +189,7 @@ RowLayout { const isAppEntry = resultPreview.entry.type === Translation.tr("App"); const appId = isAppEntry ? resultPreview.entry.id : ""; const pinned = isAppEntry ? (Config.options.dock.pinnedApps.includes(appId)) : false; + const startPinned = isAppEntry ? (Config.options.launcher.pinnedApps.includes(appId)) : false; var result = [ searchResultComp.createObject(null, { name: resultPreview.entry.verb, @@ -207,6 +208,20 @@ RowLayout { TaskbarApps.togglePin(appId); } }) + ] : []), + ...(isAppEntry ? [ + searchResultComp.createObject(null, { + name: startPinned ? Translation.tr("Unpin from start") : Translation.tr("Pin to start"), + iconName: startPinned ? "keep_off" : "keep", + iconType: LauncherSearchResult.IconType.Material, + execute: () => { + if (Config.options.launcher.pinnedApps.indexOf(appId) !== -1) { + Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.filter(id => id !== appId) + } else { + Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.concat([appId]) + } + } + }) ] : []) ]; result = result.concat(resultPreview.entry.actions); diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml new file mode 100644 index 000000000..4b27b58bd --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml @@ -0,0 +1,46 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WButton { + id: root + required property DesktopEntry desktopEntry + implicitWidth: 96 + implicitHeight: 84 + horizontalPadding: 0 + verticalPadding: 0 + contentItem: ColumnLayout { + spacing: 3 + WAppIcon { + Layout.topMargin: 12 + Layout.alignment: Qt.AlignHCenter + iconName: root.desktopEntry.icon + implicitSize: 34 + tryCustomIcon: false + } + WText { + Layout.fillHeight: true + Layout.fillWidth: true + Layout.leftMargin: 8 + Layout.rightMargin: 8 + text: root.desktopEntry.name + wrapMode: Text.Wrap + elide: Text.ElideRight + maximumLineCount: 2 + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + } + } + WToolTip { + text: root.desktopEntry.name + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index 2f76ca0ca..2364295fc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -99,7 +99,7 @@ WBarAttachedPanelContent { } } Item { - implicitHeight: root.searching ? 736 : 736 // TODO: Make sizes naturally inferred + implicitHeight: root.searching ? 800 : 800 // TODO: Make sizes naturally inferred Layout.fillWidth: true Loader { id: pageContentLoader diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml new file mode 100644 index 000000000..6de65027b --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml @@ -0,0 +1,74 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +BodyRectangle { + id: root + + ColumnLayout { + anchors { + fill: parent + leftMargin: 32 + rightMargin: 32 + topMargin: 25 + bottomMargin: 30 + } + spacing: 26 + + PinnedApps { + Layout.fillWidth: true + } + + AllApps { + implicitHeight: 300 // for now + } + } + + component PinnedApps: PageSection { + title: Translation.tr("Pinned") + + BigAppGrid { + Layout.fillWidth: true + columns: 8 + desktopEntries: Config.options.launcher.pinnedApps.map(appId => DesktopEntries.byId(appId)) + } + } + + component AllApps: PageSection { + title: Translation.tr("All") + AllAppsGrid { + Layout.fillWidth: true + Layout.fillHeight: true + Layout.leftMargin: 32 + Layout.rightMargin: 32 + } + } + + component PageSection: ColumnLayout { + id: pageSection + required property string title + default property alias data: pageSectionContentArea.data + + spacing: 16 + + WText { + Layout.leftMargin: 32 + text: pageSection.title + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.stronger + } + + ColumnLayout { + id: pageSectionContentArea + Layout.fillWidth: true + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml index f2f863cd2..3d0eabb8a 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml @@ -16,7 +16,7 @@ WPanelPageColumn { WPanelSeparator {} - BodyRectangle { + StartPageApps { Layout.fillHeight: true } @@ -29,7 +29,7 @@ WPanelPageColumn { component StartFooter: FooterRectangle { implicitHeight: 63 - UserButton { + StartUserButton { anchors { left: parent.left leftMargin: 52 @@ -48,134 +48,6 @@ WPanelPageColumn { } } - component UserButton: WBorderlessButton { - id: userButton - implicitWidth: userButtonRow.implicitWidth + 12 * 2 - implicitHeight: 40 - - contentItem: Item { - RowLayout { - id: userButtonRow - anchors.centerIn: parent - spacing: 12 - - WUserAvatar { - sourceSize: Qt.size(32, 32) - } - WText { - Layout.alignment: Qt.AlignVCenter - text: SystemInfo.username - } - } - } - - onClicked: { - userMenu.open(); - } - - WToolTip { - text: SystemInfo.username - } - - Popup { - id: userMenu - x: -51 - y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10 - - background: null - - WToolTipContent { - id: popupContent - horizontalPadding: 10 - verticalPadding: 7 - radius: Looks.radius.large - realContentItem: Item { - implicitWidth: userMenuContentLayout.implicitWidth - implicitHeight: userMenuContentLayout.implicitHeight - - ColumnLayout { - id: userMenuContentLayout - anchors { - fill: parent - leftMargin: popupContent.horizontalPadding - rightMargin: popupContent.horizontalPadding - topMargin: popupContent.verticalPadding - bottomMargin: popupContent.verticalPadding - } - spacing: 5 - - RowLayout { - Layout.fillWidth: true - Layout.leftMargin: 6 - FluentIcon { - Layout.alignment: Qt.AlignVCenter - implicitSize: 22 - icon: "corporation" - monochrome: false - } - WText { - Layout.alignment: Qt.AlignVCenter - text: "Megahard" - font.pixelSize: Looks.font.pixelSize.large - font.weight: Looks.font.weight.strong - } - Item { Layout.fillWidth: true } - WBorderlessButton { - Layout.alignment: Qt.AlignVCenter - implicitHeight: 36 - implicitWidth: textItem.implicitWidth + 10 * 2 - contentItem: WText { - id: textItem - text: Translation.tr("Sign out") - font.pixelSize: Looks.font.pixelSize.large - } - onClicked: Session.logout() - } - } - Item { // Force min width 360 (using min on the item somehow doesn't work) - implicitWidth: 334 - } - RowLayout { - Layout.fillWidth: true - Layout.bottomMargin: 7 - Layout.leftMargin: 6 - spacing: 12 - WUserAvatar { - sourceSize: Qt.size(58, 58) - } - ColumnLayout { - Layout.fillWidth: true - Layout.alignment: Qt.AlignVCenter - spacing: 2 - WText { - text: SystemInfo.username - font.pixelSize: Looks.font.pixelSize.larger - font.weight: Looks.font.weight.strong - } - WText { - color: Looks.colors.fg1 - text: Translation.tr("Local account") - } - WText { - color: Looks.colors.accent - text: Translation.tr("Manage my account") - MouseArea { - anchors.fill: parent - cursorShape: Qt.PointingHandCursor - onClicked: { - Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) - GlobalStates.searchOpen = false; - } - } - } - } - } - } - } - } - } - } - component PowerButton: WBorderlessButton { id: powerButton implicitWidth: 40 diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml new file mode 100644 index 000000000..274883c72 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml @@ -0,0 +1,140 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WBorderlessButton { + id: userButton + implicitWidth: userButtonRow.implicitWidth + 12 * 2 + implicitHeight: 40 + + contentItem: Item { + RowLayout { + id: userButtonRow + anchors.centerIn: parent + spacing: 12 + + WUserAvatar { + sourceSize: Qt.size(32, 32) + } + WText { + Layout.alignment: Qt.AlignVCenter + text: SystemInfo.username + } + } + } + + onClicked: { + userMenu.open(); + } + + WToolTip { + text: SystemInfo.username + } + + Popup { + id: userMenu + x: -51 + y: -userMenu.implicitHeight + userButton.implicitHeight / 2 - 10 + + background: null + + WToolTipContent { + id: popupContent + horizontalPadding: 10 + verticalPadding: 7 + radius: Looks.radius.large + realContentItem: Item { + implicitWidth: userMenuContentLayout.implicitWidth + implicitHeight: userMenuContentLayout.implicitHeight + + ColumnLayout { + id: userMenuContentLayout + anchors { + fill: parent + leftMargin: popupContent.horizontalPadding + rightMargin: popupContent.horizontalPadding + topMargin: popupContent.verticalPadding + bottomMargin: popupContent.verticalPadding + } + spacing: 5 + + RowLayout { + Layout.fillWidth: true + Layout.leftMargin: 6 + FluentIcon { + Layout.alignment: Qt.AlignVCenter + implicitSize: 22 + icon: "corporation" + monochrome: false + } + WText { + Layout.alignment: Qt.AlignVCenter + text: "Megahard" + font.pixelSize: Looks.font.pixelSize.large + font.weight: Looks.font.weight.strong + } + Item { Layout.fillWidth: true } + WBorderlessButton { + Layout.alignment: Qt.AlignVCenter + implicitHeight: 36 + implicitWidth: textItem.implicitWidth + 10 * 2 + contentItem: WText { + id: textItem + text: Translation.tr("Sign out") + font.pixelSize: Looks.font.pixelSize.large + } + onClicked: Session.logout() + } + } + Item { // Force min width 360 (using min on the item somehow doesn't work) + implicitWidth: 334 + } + RowLayout { + Layout.fillWidth: true + Layout.bottomMargin: 7 + Layout.leftMargin: 6 + spacing: 12 + WUserAvatar { + sourceSize: Qt.size(58, 58) + } + ColumnLayout { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + spacing: 2 + WText { + text: SystemInfo.username + font.pixelSize: Looks.font.pixelSize.larger + font.weight: Looks.font.weight.strong + } + WText { + color: Looks.colors.fg1 + text: Translation.tr("Local account") + } + WText { + color: Looks.colors.accent + text: Translation.tr("Manage my account") + MouseArea { + anchors.fill: parent + cursorShape: Qt.PointingHandCursor + onClicked: { + Quickshell.execDetached(["bash", "-c", Config.options.apps.manageUser]) + GlobalStates.searchOpen = false; + } + } + } + } + } + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml index a1deb0027..f1ea82ba1 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml @@ -65,8 +65,8 @@ RowLayout { WMenu { id: accountsMenu - x: -accountsMenu.implicitWidth + optionsButton.implicitWidth - y: optionsButton.height + 10 + x: -accountsMenu.implicitWidth + optionsButton.implicitWidth + 10 + y: optionsButton.height downDirection: true Action { icon.name: "people-settings" diff --git a/dots/.config/quickshell/ii/services/Audio.qml b/dots/.config/quickshell/ii/services/Audio.qml index 29b0a2f68..68ebc3ae0 100644 --- a/dots/.config/quickshell/ii/services/Audio.qml +++ b/dots/.config/quickshell/ii/services/Audio.qml @@ -106,9 +106,9 @@ Singleton { if (newVolume - lastVolume > maxAllowedIncrease) { sink.audio.volume = lastVolume; root.sinkProtectionTriggered(Translation.tr("Illegal increment")); - } else if (Math.round(newVolume * 100) / 100 > maxAllowed || newVolume > root.hardMaxValue) { + } else if (newVolume > maxAllowed || newVolume > root.hardMaxValue) { root.sinkProtectionTriggered(Translation.tr("Exceeded max allowed")); - sink.audio.volume = maxAllowed; + sink.audio.volume = Math.min(lastVolume, maxAllowed); } lastVolume = sink.audio.volume; } diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index 49de2158e..f5a18bec9 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -20,6 +20,17 @@ Singleton { } } + // https://specifications.freedesktop.org/menu/latest/category-registry.html + property list mainRegisteredCategories: ["AudioVideo", "Development", "Education", "Game", "Graphics", "Network", "Office", "Science", "Settings", "System", "Utility"] + property list appCategories: DesktopEntries.applications.values.reduce((acc, entry) => { + for (const category of entry.categories) { + if (!acc.includes(category) && mainRegisteredCategories.includes(category)) { + acc.push(category); + } + } + return acc; + }, []).sort() + property var searchActions: [ { action: "accentcolor", From 13968db31c1be9c1952da29e29687b276ef49aaf Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 6 Dec 2025 23:14:08 +0100 Subject: [PATCH 067/154] waffles: ctrl alt del menu --- .../quickshell/ii/modules/common/Config.qml | 1 + .../ii/modules/common/functions/Session.qml | 4 + .../ii/sessionScreen/SessionScreen.qml | 179 +++++++++-------- .../bluetooth/BluetoothControl.qml | 2 +- .../waffle/actionCenter/wifi/WifiControl.qml | 2 +- .../ii/modules/waffle/looks/WMenu.qml | 4 + .../waffle/looks/WRectangularShadowThis.qml | 15 ++ .../{FooterMoreButton.qml => WTextButton.qml} | 0 .../sessionScreen/SessionScreenContent.qml | 189 ++++++++++++++++++ .../WSessionScreenTextButton.qml | 54 +++++ .../sessionScreen/WaffleSessionScreen.qml | 117 +++++++++++ .../waffle/startMenu/StartMenuContent.qml | 2 + .../waffle/startMenu/WaffleStartMenu.qml | 31 +++ .../{ => searchPage}/SearchEntryIcon.qml | 0 .../{ => searchPage}/SearchPageContent.qml | 0 .../SearchResultButton.qml} | 0 .../{ => searchPage}/SearchResults.qml | 3 +- .../startMenu/{ => searchPage}/TagStrip.qml | 1 + .../AggregatedAppCategoryModel.qml | 0 .../startMenu/{ => startPage}/AllAppsGrid.qml | 0 .../{ => startPage}/AppCategoryGrid.qml | 17 +- .../startMenu/{ => startPage}/BigAppGrid.qml | 0 .../{ => startPage}/StartAppButton.qml | 0 .../{ => startPage}/StartPageApps.qml | 2 + .../{ => startPage}/StartPageContent.qml | 0 .../{ => startPage}/StartUserButton.qml | 0 .../ii/services/SessionWarnings.qml | 39 ++++ .../quickshell/ii/services/Updates.qml | 1 + dots/.config/quickshell/ii/shell.qml | 5 +- 29 files changed, 578 insertions(+), 90 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WRectangularShadowThis.qml rename dots/.config/quickshell/ii/modules/waffle/looks/{FooterMoreButton.qml => WTextButton.qml} (100%) create mode 100644 dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => searchPage}/SearchEntryIcon.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => searchPage}/SearchPageContent.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{WSearchResultButton.qml => searchPage/SearchResultButton.qml} (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => searchPage}/SearchResults.qml (99%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => searchPage}/TagStrip.qml (98%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/AggregatedAppCategoryModel.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/AllAppsGrid.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/AppCategoryGrid.qml (96%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/BigAppGrid.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/StartAppButton.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/StartPageApps.qml (93%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/StartPageContent.qml (100%) rename dots/.config/quickshell/ii/modules/waffle/startMenu/{ => startPage}/StartUserButton.qml (100%) create mode 100644 dots/.config/quickshell/ii/services/SessionWarnings.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 462ee4dfb..859e336e3 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -153,6 +153,7 @@ Singleton { property JsonObject apps: JsonObject { property string bluetooth: "kcmshell6 kcm_bluetooth" + property string changePassword: "kitty -1 --hold=yes fish -i -c 'passwd'" property string network: "kcmshell6 kcm_networkmanagement" property string manageUser: "kcmshell6 kcm_users" property string networkEthernet: "kcmshell6 kcm_networkmanagement" diff --git a/dots/.config/quickshell/ii/modules/common/functions/Session.qml b/dots/.config/quickshell/ii/modules/common/functions/Session.qml index bbb9932c3..184bd34c3 100644 --- a/dots/.config/quickshell/ii/modules/common/functions/Session.qml +++ b/dots/.config/quickshell/ii/modules/common/functions/Session.qml @@ -12,6 +12,10 @@ Singleton { }); } + function changePassword() { + Quickshell.execDetached(["bash", "-c", `${Config.options.apps.changePassword}`]); + } + function lock() { Quickshell.execDetached(["loginctl", "lock-session"]); } diff --git a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml index 8899c3e75..6350d18a9 100644 --- a/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml +++ b/dots/.config/quickshell/ii/modules/ii/sessionScreen/SessionScreen.qml @@ -14,61 +14,13 @@ import Quickshell.Hyprland Scope { id: root property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) - property bool packageManagerRunning: false - property bool downloadRunning: false - - component DescriptionLabel: Rectangle { - id: descriptionLabel - property string text - property color textColor: Appearance.colors.colOnTooltip - color: Appearance.colors.colTooltip - clip: true - radius: Appearance.rounding.normal - implicitHeight: descriptionLabelText.implicitHeight + 10 * 2 - implicitWidth: descriptionLabelText.implicitWidth + 15 * 2 - - Behavior on implicitWidth { - animation: Appearance.animation.elementMove.numberAnimation.createObject(this) - } - - StyledText { - id: descriptionLabelText - anchors.centerIn: parent - color: descriptionLabel.textColor - text: descriptionLabel.text - } - } - - function detectRunningStuff() { - packageManagerRunning = false; - downloadRunning = false; - detectPackageManagerProc.running = false; - detectPackageManagerProc.running = true; - detectDownloadProc.running = false; - detectDownloadProc.running = true; - } - - Process { - id: detectPackageManagerProc - command: ["bash", "-c", "pidof pacman yay paru dnf zypper apt apx xbps flatpak snap apk yum epsi pikman"] - onExited: (exitCode, exitStatus) => { - root.packageManagerRunning = (exitCode === 0); - } - } - - Process { - id: detectDownloadProc - command: ["bash", "-c", "pidof curl wget aria2c yt-dlp || ls ~/Downloads | grep -E '\.crdownload$|\.part$'"] - onExited: (exitCode, exitStatus) => { - root.downloadRunning = (exitCode === 0); - } - } Loader { id: sessionLoader active: GlobalStates.sessionOpen onActiveChanged: { - if (sessionLoader.active) root.detectRunningStuff(); + if (sessionLoader.active) + SessionWarnings.refresh(); } Connections { @@ -84,7 +36,7 @@ Scope { id: sessionRoot visible: sessionLoader.active property string subtitle - + function hide() { GlobalStates.sessionOpen = false; } @@ -110,7 +62,7 @@ Scope { id: sessionMouseArea anchors.fill: parent onClicked: { - sessionRoot.hide() + sessionRoot.hide(); } } @@ -119,7 +71,7 @@ Scope { anchors.centerIn: parent spacing: 15 - Keys.onPressed: (event) => { + Keys.onPressed: event => { if (event.key === Qt.Key_Escape) { sessionRoot.hide(); } @@ -128,7 +80,8 @@ Scope { ColumnLayout { Layout.alignment: Qt.AlignHCenter spacing: 0 - StyledText { // Title + StyledText { + // Title Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter font { @@ -139,7 +92,8 @@ Scope { text: Translation.tr("Session") } - StyledText { // Small instruction + StyledText { + // Small instruction Layout.alignment: Qt.AlignHCenter horizontalAlignment: Text.AlignHCenter font.pixelSize: Appearance.font.pixelSize.normal @@ -157,8 +111,14 @@ Scope { focus: sessionRoot.visible buttonIcon: "lock" buttonText: Translation.tr("Lock") - onClicked: { Session.lock(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.lock(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.right: sessionSleep KeyNavigation.down: sessionHibernate } @@ -166,8 +126,14 @@ Scope { id: sessionSleep buttonIcon: "dark_mode" buttonText: Translation.tr("Sleep") - onClicked: { Session.suspend(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.suspend(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.left: sessionLock KeyNavigation.right: sessionLogout KeyNavigation.down: sessionShutdown @@ -176,8 +142,14 @@ Scope { id: sessionLogout buttonIcon: "logout" buttonText: Translation.tr("Logout") - onClicked: { Session.logout(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.logout(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.left: sessionSleep KeyNavigation.right: sessionTaskManager KeyNavigation.down: sessionReboot @@ -186,8 +158,14 @@ Scope { id: sessionTaskManager buttonIcon: "browse_activity" buttonText: Translation.tr("Task Manager") - onClicked: { Session.launchTaskManager(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.launchTaskManager(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.left: sessionLogout KeyNavigation.down: sessionFirmwareReboot } @@ -196,8 +174,14 @@ Scope { id: sessionHibernate buttonIcon: "downloading" buttonText: Translation.tr("Hibernate") - onClicked: { Session.hibernate(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.hibernate(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.up: sessionLock KeyNavigation.right: sessionShutdown } @@ -205,8 +189,14 @@ Scope { id: sessionShutdown buttonIcon: "power_settings_new" buttonText: Translation.tr("Shutdown") - onClicked: { Session.poweroff(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.poweroff(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.left: sessionHibernate KeyNavigation.right: sessionReboot KeyNavigation.up: sessionSleep @@ -215,8 +205,14 @@ Scope { id: sessionReboot buttonIcon: "restart_alt" buttonText: Translation.tr("Reboot") - onClicked: { Session.reboot(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.reboot(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.left: sessionShutdown KeyNavigation.right: sessionFirmwareReboot KeyNavigation.up: sessionLogout @@ -225,8 +221,14 @@ Scope { id: sessionFirmwareReboot buttonIcon: "settings_applications" buttonText: Translation.tr("Reboot to firmware settings") - onClicked: { Session.rebootToFirmware(); sessionRoot.hide() } - onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } + onClicked: { + Session.rebootToFirmware(); + sessionRoot.hide(); + } + onFocusChanged: { + if (focus) + sessionRoot.subtitle = buttonText; + } KeyNavigation.up: sessionTaskManager KeyNavigation.left: sessionReboot } @@ -247,7 +249,7 @@ Scope { spacing: 10 Loader { - active: root.packageManagerRunning + active: SessionWarnings.packageManagerRunning visible: active sourceComponent: DescriptionLabel { text: Translation.tr("Your package manager is running") @@ -256,7 +258,7 @@ Scope { } } Loader { - active: root.downloadRunning + active: SessionWarnings.downloadRunning visible: active sourceComponent: DescriptionLabel { text: Translation.tr("There might be a download in progress") @@ -268,6 +270,28 @@ Scope { } } + component DescriptionLabel: Rectangle { + id: descriptionLabel + property string text + property color textColor: Appearance.colors.colOnTooltip + color: Appearance.colors.colTooltip + clip: true + radius: Appearance.rounding.normal + implicitHeight: descriptionLabelText.implicitHeight + 10 * 2 + implicitWidth: descriptionLabelText.implicitWidth + 15 * 2 + + Behavior on implicitWidth { + animation: Appearance.animation.elementMove.numberAnimation.createObject(this) + } + + StyledText { + id: descriptionLabelText + anchors.centerIn: parent + color: descriptionLabel.textColor + text: descriptionLabel.text + } + } + IpcHandler { target: "session" @@ -276,11 +300,11 @@ Scope { } function close(): void { - GlobalStates.sessionOpen = false + GlobalStates.sessionOpen = false; } function open(): void { - GlobalStates.sessionOpen = true + GlobalStates.sessionOpen = true; } } @@ -298,7 +322,7 @@ Scope { description: "Opens session screen on press" onPressed: { - GlobalStates.sessionOpen = true + GlobalStates.sessionOpen = true; } } @@ -307,8 +331,7 @@ Scope { description: "Closes session screen on press" onPressed: { - GlobalStates.sessionOpen = false + GlobalStates.sessionOpen = false; } } - } diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml index 72f442b87..05e89dafe 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/bluetooth/BluetoothControl.qml @@ -99,7 +99,7 @@ Item { WPanelSeparator {} FooterRectangle { - FooterMoreButton { + WTextButton { anchors { verticalCenter: parent.verticalCenter left: parent.left diff --git a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml index c90d06fc0..061c74154 100644 --- a/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml +++ b/dots/.config/quickshell/ii/modules/waffle/actionCenter/wifi/WifiControl.qml @@ -89,7 +89,7 @@ Item { WPanelSeparator {} FooterRectangle { - FooterMoreButton { + WTextButton { anchors { verticalCenter: parent.verticalCenter left: parent.left diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index a18c59ec9..3b0d2cd42 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -66,6 +66,10 @@ Menu { } } + Component.onCompleted: { + menuListView.itemAtIndex(0)?.forceActiveFocus(); + } + contentItem: Item { implicitWidth: menuListView.implicitWidth implicitHeight: menuListView.implicitHeight diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WRectangularShadowThis.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WRectangularShadowThis.qml new file mode 100644 index 000000000..5ad46de29 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WRectangularShadowThis.qml @@ -0,0 +1,15 @@ +import QtQuick +import QtQuick.Effects +import qs.modules.common +import qs.modules.common.widgets + +Item { + default property Item contentItem + property Item shadow: WRectangularShadow { + target: contentItem + } + implicitWidth: contentItem.implicitWidth + implicitHeight: contentItem.implicitHeight + + children: [shadow, contentItem] +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/FooterMoreButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/looks/FooterMoreButton.qml rename to dots/.config/quickshell/ii/modules/waffle/looks/WTextButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml new file mode 100644 index 000000000..ddadb373a --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml @@ -0,0 +1,189 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +Item { + id: root + + Component.onCompleted: { + lockButton.forceActiveFocus(); + } + + ColumnLayout { + anchors.centerIn: parent + spacing: 4 + + WSessionScreenTextButton { + id: lockButton + focus: true + text: Translation.tr("Lock") + onClicked: { + GlobalStates.sessionOpen = false; + Session.lock(); + } + KeyNavigation.up: powerButton + KeyNavigation.down: signOutButton + } + WSessionScreenTextButton { + id: signOutButton + focus: true + text: Translation.tr("Sign out") + onClicked: { + GlobalStates.sessionOpen = false; + Session.logout(); + } + KeyNavigation.up: lockButton + KeyNavigation.down: changePasswordButton + } + + WSessionScreenTextButton { + id: changePasswordButton + focus: true + text: Translation.tr("Change password") + onClicked: { + GlobalStates.sessionOpen = false; + Session.changePassword(); + } + KeyNavigation.up: signOutButton + KeyNavigation.down: taskManagerButton + } + + WSessionScreenTextButton { + id: taskManagerButton + focus: true + text: Translation.tr("Task Manager") + onClicked: { + GlobalStates.sessionOpen = false; + Session.launchTaskManager(); + } + KeyNavigation.up: signOutButton + KeyNavigation.down: cancelButton + } + + CancelButton { + id: cancelButton + Layout.fillWidth: true + Layout.leftMargin: 5 + Layout.rightMargin: 5 + Layout.topMargin: 38 + onClicked: GlobalStates.sessionOpen = false + KeyNavigation.up: taskManagerButton + KeyNavigation.down: powerButton + } + } + + RowLayout { + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: 21 + rightMargin: 31 + } + PowerButton { + id: powerButton + KeyNavigation.up: cancelButton + KeyNavigation.down: lockButton + } + } + + component PowerButton: WSessionScreenTextButton { + id: root + implicitWidth: 40 + implicitHeight: 40 + focusRingRadius: Looks.radius.large + colBackgroundHover: Looks.colors.bg2Hover + colBackgroundActive: Looks.colors.bg2Active + property color color: { + if (root.down) { + return root.colBackgroundActive; + } else if (root.hovered) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + background: Rectangle { + id: background + radius: Looks.radius.medium + color: root.color + } + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + implicitSize: 20 + icon: "power" + } + } + + onClicked: { + powerMenu.visible = !powerMenu.visible; + } + + WMenu { + id: powerMenu + x: -powerMenu.implicitWidth / 2 + root.implicitWidth / 2 + y: -powerMenu.implicitHeight + + Action { + icon.name: "power" + text: Translation.tr("Shut down") + onTriggered: Session.poweroff() + } + Action { + icon.name: "arrow-counterclockwise" + text: Translation.tr("Restart") + onTriggered: Session.reboot() + } + } + } + + component CancelButton: WBorderlessButton { + id: root + implicitHeight: 32 + colBackground: Looks.colors.bg1Base + colBackgroundHover: Qt.lighter(Looks.colors.bg1Base, 1.2) + colBackgroundActive: Qt.lighter(Looks.colors.bg1Base, 1.1) + + property bool keyboardDown: false + + Keys.onPressed: event => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + keyboardDown = true; + event.accepted = true; + } + } + Keys.onReleased: event => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + keyboardDown = false; + root.clicked(); + event.accepted = true; + } + } + + contentItem: WText { + text: Translation.tr("Cancel") + horizontalAlignment: Text.AlignHCenter + font.pixelSize: Looks.font.pixelSize.large + } + + Rectangle { + visible: cancelButton.focus + anchors { + fill: parent + margins: -3 + } + radius: cancelButton.background.radius + 4 + color: "transparent" + border.width: 2 + border.color: "#ffffff" + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml new file mode 100644 index 000000000..ec8b58487 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml @@ -0,0 +1,54 @@ +pragma ComponentBehavior: Bound +import QtQuick +import qs +import qs.modules.waffle.looks + +WTextButton { + id: root + + implicitWidth: 135 + implicitHeight: 40 + horizontalPadding: 5 + + property bool keyboardDown: false + property alias focusRingRadius: focusRing.radius + + Keys.onPressed: event => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + keyboardDown = true; + event.accepted = true; + } + } + Keys.onReleased: event => { + if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { + keyboardDown = false; + root.clicked(); + event.accepted = true; + } + } + + contentItem: Item { + id: contentItem + implicitWidth: buttonText.implicitWidth + + WText { + id: buttonText + anchors.fill: parent + color: (root.pressed || root.keyboardDown) ? Looks.colors.fg1 : Looks.colors.fg + text: root.text + font.pixelSize: Looks.font.pixelSize.large + } + } + + Rectangle { + id: focusRing + visible: root.focus + anchors { + fill: parent + margins: -4 + } + color: "transparent" + border.width: 2 + border.color: "#ffffff" + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml new file mode 100644 index 000000000..41c52b151 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml @@ -0,0 +1,117 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + property var focusedScreen: Quickshell.screens.find(s => s.name === Hyprland.focusedMonitor?.name) + + Loader { + id: sessionLoader + active: GlobalStates.sessionOpen + onActiveChanged: { + if (sessionLoader.active) SessionWarnings.refresh(); + } + + Connections { + target: GlobalStates + function onScreenLockedChanged() { + if (GlobalStates.screenLocked) { + GlobalStates.sessionOpen = false; + } + } + } + + sourceComponent: PanelWindow { // Session menu + id: sessionRoot + visible: sessionLoader.active + property string subtitle + + function hide() { + GlobalStates.sessionOpen = false; + } + + exclusionMode: ExclusionMode.Ignore + 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: "#000000" + + anchors { + top: true + left: true + right: true + bottom: true + } + + Item { + anchors.fill: parent + focus: true + Keys.onPressed: (event) => { + if (event.key === Qt.Key_Escape) { + sessionRoot.hide(); + } + } + + SessionScreenContent { + anchors.fill: parent + } + } + } + } + + IpcHandler { + target: "session" + + function toggle(): void { + GlobalStates.sessionOpen = !GlobalStates.sessionOpen; + } + + function close(): void { + GlobalStates.sessionOpen = false + } + + function open(): void { + GlobalStates.sessionOpen = true + } + } + + GlobalShortcut { + name: "sessionToggle" + description: "Toggles session screen on press" + + onPressed: { + GlobalStates.sessionOpen = !GlobalStates.sessionOpen; + } + } + + GlobalShortcut { + name: "sessionOpen" + description: "Opens session screen on press" + + onPressed: { + GlobalStates.sessionOpen = true + } + } + + GlobalShortcut { + name: "sessionClose" + description: "Closes session screen on press" + + onPressed: { + GlobalStates.sessionOpen = false + } + } + +} diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index 2364295fc..fb506dd49 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -9,6 +9,8 @@ import qs.services import qs.modules.common import qs.modules.common.functions import qs.modules.waffle.looks +import qs.modules.waffle.startMenu.startPage +import qs.modules.waffle.startMenu.searchPage WBarAttachedPanelContent { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml index 15ffc6f43..62bdc65bf 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/WaffleStartMenu.qml @@ -70,6 +70,19 @@ Scope { } } + function toggleClipboard() { + if (LauncherSearch.query.startsWith(Config.options.search.prefix.clipboard) || !GlobalStates.searchOpen) { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + LauncherSearch.ensurePrefix(Config.options.search.prefix.clipboard); + } + function toggleEmojis() { + if (LauncherSearch.query.startsWith(Config.options.search.prefix.emojis) || !GlobalStates.searchOpen) { + GlobalStates.searchOpen = !GlobalStates.searchOpen; + } + LauncherSearch.ensurePrefix(Config.options.search.prefix.emojis); + } + IpcHandler { target: "search" @@ -119,4 +132,22 @@ Scope { GlobalStates.superReleaseMightTrigger = false; } } + + GlobalShortcut { + name: "overviewClipboardToggle" + description: "Toggle clipboard query on overview widget" + + onPressed: { + root.toggleClipboard(); + } + } + + GlobalShortcut { + name: "overviewEmojiToggle" + description: "Toggle emoji query on overview widget" + + onPressed: { + root.toggleEmojis(); + } + } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchEntryIcon.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/SearchEntryIcon.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchEntryIcon.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchPageContent.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/SearchPageContent.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchPageContent.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/WSearchResultButton.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResultButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml similarity index 99% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml index 918534b78..e02d56a50 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml @@ -5,6 +5,7 @@ import qs.modules.common import qs.modules.waffle.looks import qs.modules.common.functions import qs.modules.common.models +import qs.modules.waffle.startMenu import Quickshell import QtQuick.Layouts import QtQuick.Controls @@ -119,7 +120,7 @@ RowLayout { onModelChanged: { root.focusFirstItem(); } - delegate: WSearchResultButton { + delegate: SearchResultButton { required property int index required property var modelData entry: modelData diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/TagStrip.qml similarity index 98% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/TagStrip.qml index f1ea82ba1..33aef1906 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/TagStrip.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/TagStrip.qml @@ -8,6 +8,7 @@ import qs.services import qs.modules.common import qs.modules.common.functions import qs.modules.waffle.looks +import qs.modules.waffle.startMenu RowLayout { id: root diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AggregatedAppCategoryModel.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/AggregatedAppCategoryModel.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AggregatedAppCategoryModel.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/AllAppsGrid.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AllAppsGrid.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml similarity index 96% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml index 38c8d8d41..e1ab25c54 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/AppCategoryGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml @@ -90,7 +90,7 @@ Rectangle { NumberAnimation { target: categoryFolderPopup property: "x" - from: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 3 / 2 + from: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 5 / 2 to: categoryFolderPopup.windowCenterPoint.x - categoryFolderPopup.width / 2 duration: 300 easing.type: Easing.BezierSpline @@ -99,7 +99,7 @@ Rectangle { NumberAnimation { target: categoryFolderPopup property: "y" - from: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height / 2 + from: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height * 3 / 2 to: categoryFolderPopup.windowCenterPoint.y - categoryFolderPopup.height / 2 duration: 300 easing.type: Easing.BezierSpline @@ -120,7 +120,7 @@ Rectangle { NumberAnimation { target: categoryFolderPopup property: "x" - to: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 3 / 2 + to: categoryFolderPopup.originPoint.x - categoryOpenButtonLoader.width * 5 / 2 duration: 200 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut @@ -128,7 +128,7 @@ Rectangle { NumberAnimation { target: categoryFolderPopup property: "y" - to: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height / 2 + to: categoryFolderPopup.originPoint.y - categoryOpenButtonLoader.height * 3 / 2 duration: 200 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeOut @@ -147,10 +147,13 @@ Rectangle { background: null Loader { + id: folderContentLoader active: categoryFolderPopup.visible - sourceComponent: CategoryFolderContent { - title: root.aggregatedCategory.name - desktopEntries: root.desktopEntries + sourceComponent: WRectangularShadowThis { + CategoryFolderContent { + title: root.aggregatedCategory.name + desktopEntries: root.desktopEntries + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/BigAppGrid.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/StartAppButton.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartPageApps.qml similarity index 93% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartPageApps.qml index 6de65027b..b4539b317 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageApps.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartPageApps.qml @@ -44,6 +44,8 @@ BodyRectangle { component AllApps: PageSection { title: Translation.tr("All") + // TODO: Do we wanna also implement list view and grid view? + // (instead of only category view) AllAppsGrid { Layout.fillWidth: true Layout.fillHeight: true diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartPageContent.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/StartPageContent.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartPageContent.qml diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartUserButton.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/waffle/startMenu/StartUserButton.qml rename to dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartUserButton.qml diff --git a/dots/.config/quickshell/ii/services/SessionWarnings.qml b/dots/.config/quickshell/ii/services/SessionWarnings.qml new file mode 100644 index 000000000..c698d28c7 --- /dev/null +++ b/dots/.config/quickshell/ii/services/SessionWarnings.qml @@ -0,0 +1,39 @@ +pragma Singleton + +import qs.modules.common +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Io + +Singleton { + id: root + + property bool packageManagerRunning: false + property bool downloadRunning: false + + function refresh() { + packageManagerRunning = false; + downloadRunning = false; + detectPackageManagerProc.running = false; + detectPackageManagerProc.running = true; + detectDownloadProc.running = false; + detectDownloadProc.running = true; + } + + Process { + id: detectPackageManagerProc + command: ["bash", "-c", "pidof pacman yay paru dnf zypper apt apx xbps snap apk yum epsi pikman"] + onExited: (exitCode, exitStatus) => { + root.packageManagerRunning = (exitCode === 0); + } + } + + Process { + id: detectDownloadProc + command: ["bash", "-c", "pidof curl wget aria2c yt-dlp || ls ~/Downloads | grep -E '\.crdownload$|\.part$'"] + onExited: (exitCode, exitStatus) => { + root.downloadRunning = (exitCode === 0); + } + } +} diff --git a/dots/.config/quickshell/ii/services/Updates.qml b/dots/.config/quickshell/ii/services/Updates.qml index 58b8be892..48549ac3c 100644 --- a/dots/.config/quickshell/ii/services/Updates.qml +++ b/dots/.config/quickshell/ii/services/Updates.qml @@ -13,6 +13,7 @@ Singleton { id: root property bool available: false + property alias checking: checkUpdatesProc.running property int count: 0 readonly property bool updateAdvised: available && count > Config.options.updates.adviseUpdateThreshold diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 7bb565d11..55dc7bce9 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -6,7 +6,6 @@ // Adjust this to make the shell smaller or larger //@ pragma Env QT_SCALE_FACTOR=1 - import qs.modules.common import qs.modules.ii.background import qs.modules.ii.bar @@ -34,6 +33,7 @@ import qs.modules.waffle.bar import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.startMenu +import qs.modules.waffle.sessionScreen import QtQuick import QtQuick.Window @@ -85,6 +85,7 @@ ShellRoot { PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } + PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } ReloadPopup {} component PanelLoader: LazyLoader { @@ -97,7 +98,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", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "iiSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From 6c460b209cd397e3094f1870c615023d1a147835 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 7 Dec 2025 00:04:24 +0100 Subject: [PATCH 068/154] waffles: context menus for app buttons --- .../icons/fluent/arrow-right-filled.svg | 4 ++ .../ii/assets/icons/fluent/arrow-right.svg | 4 ++ .../icons/fluent/arrow-up-left-filled.svg | 4 ++ .../ii/assets/icons/fluent/arrow-up-left.svg | 4 ++ .../ii/modules/waffle/looks/WMenu.qml | 1 - .../ii/modules/waffle/looks/WMenuItem.qml | 3 ++ .../sessionScreen/WaffleSessionScreen.qml | 1 - .../startMenu/searchPage/SearchResults.qml | 24 ++++----- .../startMenu/startPage/AppCategoryGrid.qml | 27 ++++++++++ .../waffle/startMenu/startPage/BigAppGrid.qml | 4 +- .../startMenu/startPage/StartAppButton.qml | 52 +++++++++++++++++++ .../quickshell/ii/services/LauncherApps.qml | 41 +++++++++++++++ .../quickshell/ii/services/TaskbarApps.qml | 6 ++- 13 files changed, 155 insertions(+), 20 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-right-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-right.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left.svg create mode 100644 dots/.config/quickshell/ii/services/LauncherApps.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right-filled.svg new file mode 100644 index 000000000..0e4e5daaa --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right.svg new file mode 100644 index 000000000..db97a6e38 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-right.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left-filled.svg new file mode 100644 index 000000000..dd5011358 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left.svg b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left.svg new file mode 100644 index 000000000..8b6683c69 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/arrow-up-left.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index 3b0d2cd42..3a42e666d 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -95,6 +95,5 @@ Menu { delegate: WMenuItem { id: menuItemDelegate - width: ListView.view?.width } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml index 3030bc122..731b0d704 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenuItem.qml @@ -55,6 +55,9 @@ MenuItem { rightInset: inset horizontalPadding: 11 + width: ListView.view?.width + height: visible ? implicitHeight : 0 + background: Rectangle { id: backgroundRect radius: Looks.radius.medium diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml index 41c52b151..7f0d1a5d7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WaffleSessionScreen.qml @@ -57,7 +57,6 @@ Scope { Item { anchors.fill: parent - focus: true Keys.onPressed: (event) => { if (event.key === Qt.Key_Escape) { sessionRoot.hide(); 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 e02d56a50..aeaa4748c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/searchPage/SearchResults.qml @@ -200,6 +200,16 @@ RowLayout { resultPreview.entry.execute(); } }), + ...(isAppEntry ? [ + searchResultComp.createObject(null, { + name: startPinned ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start"), + iconName: startPinned ? "keep_off" : "keep", + iconType: LauncherSearchResult.IconType.Material, + execute: () => { + LauncherApps.togglePin(appId); + } + }) + ] : []), ...(isAppEntry ? [ searchResultComp.createObject(null, { name: pinned ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar"), @@ -210,20 +220,6 @@ RowLayout { } }) ] : []), - ...(isAppEntry ? [ - searchResultComp.createObject(null, { - name: startPinned ? Translation.tr("Unpin from start") : Translation.tr("Pin to start"), - iconName: startPinned ? "keep_off" : "keep", - iconType: LauncherSearchResult.IconType.Material, - execute: () => { - if (Config.options.launcher.pinnedApps.indexOf(appId) !== -1) { - Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.filter(id => id !== appId) - } else { - Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.concat([appId]) - } - } - }) - ] : []) ]; result = result.concat(resultPreview.entry.actions); return result; diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml index e1ab25c54..bc584def3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml @@ -274,6 +274,9 @@ Rectangle { id: smallGridAppButton property DesktopEntry desktopEntry + property bool pinnedStart: LauncherApps.isPinned(smallGridAppButton.desktopEntry.id); + property bool pinnedTaskbar: TaskbarApps.isPinned(smallGridAppButton.desktopEntry.id); + onClicked: { GlobalStates.searchOpen = false; desktopEntry.execute(); @@ -298,6 +301,30 @@ Rectangle { WToolTip { text: smallGridAppButton.desktopEntry.name } + + altAction: () => { + appMenu.popup(); + } + + WMenu { + id: appMenu + downDirection: true + + WMenuItem { + icon.name: smallGridAppButton.pinnedStart ? "pin-off" : "pin" + text: smallGridAppButton.pinnedStart ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start") + onTriggered: { + LauncherApps.togglePin(smallGridAppButton.desktopEntry.id); + } + } + WMenuItem { + icon.name: smallGridAppButton.pinnedTaskbar ? "pin-off" : "pin" + text: smallGridAppButton.pinnedTaskbar ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar") + onTriggered: { + TaskbarApps.togglePin(smallGridAppButton.desktopEntry.id); + } + } + } } component SmallGridButton: WButton { diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml index 3142466b9..dbc44adf8 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/BigAppGrid.qml @@ -23,9 +23,7 @@ GridLayout { uniformCellWidths: true Repeater { - model: ScriptModel { - values: root.desktopEntries - } + model: root.desktopEntries delegate: StartAppButton { id: pinnedAppButton required property var modelData diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml index 4b27b58bd..8df30bd85 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/StartAppButton.qml @@ -14,6 +14,10 @@ import qs.modules.waffle.looks WButton { id: root required property DesktopEntry desktopEntry + + property bool pinnedStart: LauncherApps.isPinned(root.desktopEntry.id); + property bool pinnedTaskbar: TaskbarApps.isPinned(root.desktopEntry.id); + implicitWidth: 96 implicitHeight: 84 horizontalPadding: 0 @@ -43,4 +47,52 @@ WButton { WToolTip { text: root.desktopEntry.name } + + altAction: () => { + appMenu.popup() + } + + WMenu { + id: appMenu + downDirection: true + + WMenuItem { + visible: root.pinnedStart + icon.name: "arrow-up-left" + text: Translation.tr("Move to front") + onTriggered: { + LauncherApps.moveToFront(root.desktopEntry.id); + } + } + WMenuItem { + visible: root.pinnedStart + icon.name: "arrow-left" + text: Translation.tr("Move left") + onTriggered: { + LauncherApps.moveLeft(root.desktopEntry.id); + } + } + WMenuItem { + visible: root.pinnedStart + icon.name: "arrow-right" + text: Translation.tr("Move right") + onTriggered: { + LauncherApps.moveRight(root.desktopEntry.id); + } + } + WMenuItem { + icon.name: root.pinnedStart ? "pin-off" : "pin" + text: root.pinnedStart ? Translation.tr("Unpin from Start") : Translation.tr("Pin to Start") + onTriggered: { + LauncherApps.togglePin(root.desktopEntry.id); + } + } + WMenuItem { + icon.name: root.pinnedTaskbar ? "pin-off" : "pin" + text: root.pinnedTaskbar ? Translation.tr("Unpin from taskbar") : Translation.tr("Pin to taskbar") + onTriggered: { + TaskbarApps.togglePin(root.desktopEntry.id); + } + } + } } diff --git a/dots/.config/quickshell/ii/services/LauncherApps.qml b/dots/.config/quickshell/ii/services/LauncherApps.qml new file mode 100644 index 000000000..0017f3812 --- /dev/null +++ b/dots/.config/quickshell/ii/services/LauncherApps.qml @@ -0,0 +1,41 @@ +pragma Singleton + +import qs.modules.common +import QtQuick +import Quickshell + +Singleton { + id: root + + function isPinned(appId) { + return Config.options.launcher.pinnedApps.indexOf(appId) !== -1; + } + + function togglePin(appId) { + if (root.isPinned(appId)) { + Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.filter(id => id !== appId) + } else { + Config.options.launcher.pinnedApps = Config.options.launcher.pinnedApps.concat([appId]) + } + } + + function moveToFront(appId) { + if (!root.isPinned(appId)) return; + const pinnedApps = Config.options.launcher.pinnedApps; + Config.options.launcher.pinnedApps = [appId].concat(pinnedApps.filter(id => id !== appId)); + } + + function moveLeft(appId) { + const pinnedApps = Config.options.launcher.pinnedApps; + const index = pinnedApps.indexOf(appId); + if (index === -1 || index === 0) return; + Config.options.launcher.pinnedApps = pinnedApps.slice(0, index - 1).concat([appId]).concat(pinnedApps[index - 1]).concat(pinnedApps.slice(index + 1)); + } + + function moveRight(appId) { + const pinnedApps = Config.options.launcher.pinnedApps; + const index = pinnedApps.indexOf(appId); + if (index === -1 || index === pinnedApps.length - 1) return; + Config.options.launcher.pinnedApps = pinnedApps.slice(0, index).concat(pinnedApps[index + 1]).concat([appId]).concat(pinnedApps.slice(index + 2)); + } +} diff --git a/dots/.config/quickshell/ii/services/TaskbarApps.qml b/dots/.config/quickshell/ii/services/TaskbarApps.qml index 052abcaec..6351896b8 100644 --- a/dots/.config/quickshell/ii/services/TaskbarApps.qml +++ b/dots/.config/quickshell/ii/services/TaskbarApps.qml @@ -8,8 +8,12 @@ import Quickshell.Wayland Singleton { id: root + function isPinned(appId) { + return Config.options.dock.pinnedApps.indexOf(appId) !== -1; + } + function togglePin(appId) { - if (Config.options.dock.pinnedApps.indexOf(appId) !== -1) { + if (root.isPinned(appId)) { Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.filter(id => id !== appId) } else { Config.options.dock.pinnedApps = Config.options.dock.pinnedApps.concat([appId]) From 8b1f0fc1d4ec8dc21322f5e4a25c8ef372077b1d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:58:28 +0100 Subject: [PATCH 069/154] waffles: bar: make start and search button more accurate --- .../.config/quickshell/ii/modules/waffle/bar/SearchButton.qml | 4 ++-- dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml index 3e8dd6282..6bb48f8fc 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/SearchButton.qml @@ -12,9 +12,9 @@ AppButton { iconName: checked ? "system-search-checked" : "system-search" separateLightDark: true - checked: GlobalStates.overviewOpen + checked: GlobalStates.searchOpen && LauncherSearch.query !== "" onClicked: { - GlobalStates.overviewOpen = !GlobalStates.overviewOpen; // For now... + GlobalStates.searchOpen = !GlobalStates.searchOpen; // For now... } BarToolTip { diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml index a92a85578..7fa716b07 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/StartButton.qml @@ -14,7 +14,7 @@ AppButton { leftInset: Config.options.waffles.bar.leftAlignApps ? 12 : 0 iconName: down ? "start-here-pressed" : "start-here" - checked: GlobalStates.searchOpen + checked: GlobalStates.searchOpen && LauncherSearch.query === "" onClicked: { GlobalStates.searchOpen = !GlobalStates.searchOpen; } From 1c8339df108d52a1c76a9e7a09293c5d5738900e Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 7 Dec 2025 20:58:49 +0100 Subject: [PATCH 070/154] waffles: start: sort items alphabetically in each category --- .../ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml index bc584def3..fa38e53f3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/startPage/AppCategoryGrid.qml @@ -13,11 +13,11 @@ import qs.modules.waffle.looks Rectangle { id: root property AggregatedAppCategoryModel aggregatedCategory - property list desktopEntries: DesktopEntries.applications.values.filter(app => { + property list desktopEntries: [...DesktopEntries.applications.values.filter(app => { const appCategories = app.categories; const gridCategories = root.aggregatedCategory.categories; return appCategories.some(cat => gridCategories.indexOf(cat) !== -1); - }) + })].sort((a, b) => a.name.localeCompare(b.name)); property Item windowRootItem: { var item = root; From 064520080702a983cb35e832ad30e4f35a41b3a9 Mon Sep 17 00:00:00 2001 From: Eric <51763643+EricL521@users.noreply.github.com> Date: Sun, 7 Dec 2025 21:44:32 -0500 Subject: [PATCH 071/154] Allow persistant accent color --- .../quickshell/ii/modules/common/Config.qml | 1 + .../quickshell/ii/scripts/colors/switchwall.sh | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 859e336e3..a9b193ccf 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -138,6 +138,7 @@ Singleton { } property JsonObject palette: JsonObject { property string type: "auto" // Allowed: auto, scheme-content, scheme-expressive, scheme-fidelity, scheme-fruit-salad, scheme-monochrome, scheme-neutral, scheme-rainbow, scheme-tonal-spot + property string accentColor: "" } } diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 31f260760..1be85e021 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -319,6 +319,12 @@ main() { get_type_from_config() { jq -r '.appearance.palette.type' "$SHELL_CONFIG_FILE" 2>/dev/null || echo "auto" } + get_accent_color_from_config() { + jq -r '.appearance.palette.accentColor' "$SHELL_CONFIG_FILE" 2>/dev/null || echo "" + } + set_accent_color_in_config() { + jq --arg c "$1" '.appearance.palette.accentColor = $c' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE" + } detect_scheme_type_from_image() { local img="$1" @@ -338,12 +344,11 @@ main() { shift 2 ;; --color) - color_flag="1" if [[ "$2" =~ ^#?[A-Fa-f0-9]{6}$ ]]; then - color="$2" + set_accent_color_in_config "$2" shift 2 else - color=$(hyprpicker --no-fancy) + set_accent_color_in_config $(hyprpicker --no-fancy) shift fi ;; @@ -365,6 +370,13 @@ main() { esac done + # If accentColor is set in config, use it + config_color="$(get_accent_color_from_config)" + if [[ "$config_color" =~ ^#?[A-Fa-f0-9]{6}$ ]]; then + color_flag="1" + color="$config_color" + fi + # If type_flag is not set, get it from config if [[ -z "$type_flag" ]]; then type_flag="$(get_type_from_config)" From 34b589237460c8caa9aaa5022f7127494bd193be Mon Sep 17 00:00:00 2001 From: Eric <51763643+EricL521@users.noreply.github.com> Date: Sun, 7 Dec 2025 22:50:46 -0500 Subject: [PATCH 072/154] Polish script changes a little --- .../quickshell/ii/scripts/colors/switchwall.sh | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh index 1be85e021..430114d5b 100755 --- a/dots/.config/quickshell/ii/scripts/colors/switchwall.sh +++ b/dots/.config/quickshell/ii/scripts/colors/switchwall.sh @@ -322,8 +322,9 @@ main() { get_accent_color_from_config() { jq -r '.appearance.palette.accentColor' "$SHELL_CONFIG_FILE" 2>/dev/null || echo "" } - set_accent_color_in_config() { - jq --arg c "$1" '.appearance.palette.accentColor = $c' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE" + set_accent_color() { + local color="$1" + jq --arg color "$color" '.appearance.palette.accentColor = $color' "$SHELL_CONFIG_FILE" > "$SHELL_CONFIG_FILE.tmp" && mv "$SHELL_CONFIG_FILE.tmp" "$SHELL_CONFIG_FILE" } detect_scheme_type_from_image() { @@ -345,10 +346,13 @@ main() { ;; --color) if [[ "$2" =~ ^#?[A-Fa-f0-9]{6}$ ]]; then - set_accent_color_in_config "$2" + set_accent_color "$2" + shift 2 + elif [[ "$2" == "clear" ]]; then + set_accent_color "" shift 2 else - set_accent_color_in_config $(hyprpicker --no-fancy) + set_accent_color $(hyprpicker --no-fancy) shift fi ;; From 8b8ac448525aa52c6b646ceb79525d9624f1f1c1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:34:05 +0100 Subject: [PATCH 073/154] waffles: polkit --- .../ii/assets/icons/fluent/shield-filled.svg | 4 + .../icons/fluent/shield-lock-filled.svg | 4 + .../ii/assets/icons/fluent/shield-lock.svg | 4 + .../ii/assets/icons/fluent/shield.svg | 4 + .../icons/fluent/window-shield-filled.svg | 4 + .../ii/assets/icons/fluent/window-shield.svg | 4 + .../common/widgets/FullscreenPolkitWindow.qml | 44 ++++ .../ii/modules/ii/polkit/Polkit.qml | 33 +-- .../ii/modules/ii/polkit/PolkitContent.qml | 13 +- .../waffle/bar/tasks/WindowPreview.qml | 38 +--- .../ii/modules/waffle/looks/CloseButton.qml | 48 ++++ .../ii/modules/waffle/looks/Looks.qml | 3 +- .../ii/modules/waffle/looks/WAppIcon.qml | 2 + .../ii/modules/waffle/looks/WText.qml | 2 +- .../ii/modules/waffle/looks/WTextField.qml | 27 +++ .../modules/waffle/polkit/WPolkitContent.qml | 208 ++++++++++++++++++ .../ii/modules/waffle/polkit/WafflePolkit.qml | 15 ++ .../quickshell/ii/services/PolkitService.qml | 12 + dots/.config/quickshell/ii/shell.qml | 4 +- 19 files changed, 394 insertions(+), 79 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/shield-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/shield-lock-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/shield-lock.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/shield.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/window-shield-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/window-shield.svg create mode 100644 dots/.config/quickshell/ii/modules/common/widgets/FullscreenPolkitWindow.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/CloseButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/polkit/WafflePolkit.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/shield-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/shield-filled.svg new file mode 100644 index 000000000..e639be9bf --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/shield-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock-filled.svg new file mode 100644 index 000000000..b916cb4aa --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock.svg b/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock.svg new file mode 100644 index 000000000..af0ed6eaa --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/shield-lock.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/shield.svg b/dots/.config/quickshell/ii/assets/icons/fluent/shield.svg new file mode 100644 index 000000000..cc53bbc8c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/shield.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/window-shield-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/window-shield-filled.svg new file mode 100644 index 000000000..4709d3d1e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/window-shield-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/window-shield.svg b/dots/.config/quickshell/ii/assets/icons/fluent/window-shield.svg new file mode 100644 index 000000000..023ae50bd --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/window-shield.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/common/widgets/FullscreenPolkitWindow.qml b/dots/.config/quickshell/ii/modules/common/widgets/FullscreenPolkitWindow.qml new file mode 100644 index 000000000..e4c6ef725 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/widgets/FullscreenPolkitWindow.qml @@ -0,0 +1,44 @@ +pragma ComponentBehavior: Bound +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Wayland + +Scope { + id: root + required property Component contentComponent + + Loader { + active: PolkitService.active + sourceComponent: Variants { + model: Quickshell.screens + delegate: PanelWindow { + id: panelWindow + required property var modelData + screen: modelData + + anchors { + top: true + left: true + right: true + bottom: true + } + + color: "transparent" + WlrLayershell.namespace: "quickshell:polkit" + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand + WlrLayershell.layer: WlrLayer.Overlay + exclusionMode: ExclusionMode.Ignore + + Loader { + anchors.fill: parent + sourceComponent: root.contentComponent + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/polkit/Polkit.qml b/dots/.config/quickshell/ii/modules/ii/polkit/Polkit.qml index e1c54a42f..50f04f675 100644 --- a/dots/.config/quickshell/ii/modules/ii/polkit/Polkit.qml +++ b/dots/.config/quickshell/ii/modules/ii/polkit/Polkit.qml @@ -6,37 +6,10 @@ import qs.modules.common.functions import QtQuick import Quickshell import Quickshell.Wayland -import Quickshell.Hyprland -Scope { +FullscreenPolkitWindow { id: root - - Loader { - active: PolkitService.active - sourceComponent: Variants { - model: Quickshell.screens - delegate: PanelWindow { - id: panelWindow - required property var modelData - screen: modelData - - anchors { - top: true - left: true - right: true - bottom: true - } - - color: "transparent" - WlrLayershell.namespace: "quickshell:polkit" - WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand - WlrLayershell.layer: WlrLayer.Overlay - exclusionMode: ExclusionMode.Ignore - - PolkitContent { - anchors.fill: parent - } - } - } + contentComponent: Component { + PolkitContent {} } } diff --git a/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml b/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml index baef7f0b5..a78c9ce93 100644 --- a/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml @@ -66,12 +66,7 @@ Item { WindowDialogParagraph { Layout.fillWidth: true horizontalAlignment: Text.AlignLeft - text: { - if (!PolkitService.flow) return; - return PolkitService.flow.message.endsWith(".") - ? PolkitService.flow.message.slice(0, -1) - : PolkitService.flow.message - } + text: PolkitService.cleanMessage } MaterialTextField { @@ -79,11 +74,7 @@ Item { Layout.fillWidth: true focus: true enabled: PolkitService.interactionAvailable - placeholderText: { - const inputPrompt = PolkitService.flow?.inputPrompt.trim() ?? ""; - const cleanedInputPrompt = inputPrompt.endsWith(":") ? inputPrompt.slice(0, -1) : inputPrompt; - return cleanedInputPrompt || (root.usePasswordChars ? Translation.tr("Password") : Translation.tr("Input")) - } + placeholderText: PolkitService.cleanPrompt echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal onAccepted: root.submit(); diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml index 9f114609f..764d91ca7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/WindowPreview.qml @@ -67,7 +67,7 @@ Button { } } - CloseButton { + WindowCloseButton { id: closeButton } } @@ -91,46 +91,14 @@ Button { } } - component CloseButton: Button { - id: reusableCloseButton + component WindowCloseButton: CloseButton { visible: root.hovered Layout.leftMargin: 4 implicitHeight: 30 implicitWidth: 30 + radius: Looks.radius.large - root.padding onClicked: { root.toplevel.close(); } - - Rectangle { - z: 0 - color: "transparent" - anchors.fill: closeButtonBg - anchors.margins: -1 - opacity: closeButtonBg.opacity - border.width: 1 - radius: closeButtonBg.radius + 1 - border.color: Looks.colors.bg2Border - } - - background: Rectangle { - id: closeButtonBg - z: 1 - opacity: reusableCloseButton.hovered ? 1 : 0 - radius: Looks.radius.large - root.padding - color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger - Behavior on opacity { - animation: Looks.transition.opacity.createObject(this) - } - Behavior on color { - animation: Looks.transition.color.createObject(this) - } - } - - contentItem: FluentIcon { - z: 2 - anchors.centerIn: parent - icon: "dismiss" - implicitSize: 10 - } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/CloseButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/CloseButton.qml new file mode 100644 index 000000000..3345d0cc8 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/CloseButton.qml @@ -0,0 +1,48 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks +import qs.modules.waffle.bar +import Quickshell + +Button { + id: reusableCloseButton + implicitHeight: 30 + implicitWidth: 30 + property alias radius: closeButtonBg.radius + + Rectangle { + z: 0 + color: "transparent" + anchors.fill: closeButtonBg + anchors.margins: -1 + opacity: closeButtonBg.opacity + border.width: 1 + radius: closeButtonBg.radius + 1 + border.color: Looks.colors.bg2Border + } + + background: Rectangle { + id: closeButtonBg + z: 1 + opacity: reusableCloseButton.hovered ? 1 : 0 + color: reusableCloseButton.pressed ? Looks.colors.dangerActive : Looks.colors.danger + Behavior on opacity { + animation: Looks.transition.opacity.createObject(this) + } + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + } + + contentItem: FluentIcon { + z: 2 + anchors.centerIn: parent + icon: "dismiss" + implicitSize: 10 + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index b018b0629..b0d8b91f8 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -101,7 +101,7 @@ Singleton { 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: ColorUtils.transparentize(root.dark ? root.darkColors.bg2Base : root.lightColors.bg2Base, root.backgroundTransparency) + 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) @@ -147,6 +147,7 @@ Singleton { property int regular: Font.Medium property int strong: Font.DemiBold property int stronger: (Font.DemiBold + 2*Font.Bold) / 3 + property int strongest: Font.Bold } property QtObject pixelSize: QtObject { property real normal: 11 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml index 6f71c65bb..bd0f2fce4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WAppIcon.qml @@ -17,4 +17,6 @@ Kirigami.Icon { roundToIconSize: false fallback: root.iconName source: tryCustomIcon ? `${Looks.iconsPath}/${root.iconName}${!root.separateLightDark ? "" : Looks.dark ? "-dark" : "-light"}.svg` : fallback + + color: Looks.colors.fg } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml index 0da156893..b9bc558d7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml @@ -8,7 +8,7 @@ Text { color: Looks.colors.fg font { - hintingPreference: Font.PreferFullHinting + hintingPreference: Font.PreferDefaultHinting family: Looks.font.family.ui pixelSize: Looks.font.pixelSize.normal weight: Looks.font.weight.regular diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml new file mode 100644 index 000000000..a666cec50 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml @@ -0,0 +1,27 @@ +import qs.modules.common +import QtQuick +import QtQuick.Controls.FluentWinUI3 +import QtQuick.Controls + +TextField { + id: root + + clip: true + renderType: Text.NativeRendering + verticalAlignment: Text.AlignVCenter + color: Looks.colors.fg + + font { + hintingPreference: Font.PreferDefaultHinting + family: Looks.font.family.ui + pixelSize: Looks.font.pixelSize.normal + weight: Looks.font.weight.regular + } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + hoverEnabled: true + cursorShape: Qt.IBeamCursor + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml new file mode 100644 index 000000000..1218efb69 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml @@ -0,0 +1,208 @@ +import QtQuick +import QtQuick.Layouts +import Quickshell +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Rectangle { + id: root + + color: "#000000" + readonly property bool usePasswordChars: !PolkitService.flow?.responseVisible ?? true + + Keys.onPressed: event => { // Esc to close + if (event.key === Qt.Key_Escape) { + PolkitService.cancel(); + } + } + + StyledImage { + anchors.fill: parent + source: Config.options.background.wallpaperPath + fillMode: Image.PreserveAspectCrop + + Rectangle { + anchors.fill: parent + color: ColorUtils.transparentize("#000000", 0.31) + + PolkitDialog { + id: dialog + DragHandler { + target: null + property real startX: dialog.x + property real startY: dialog.y + onActiveChanged: { + if (!active) return; + startX = dialog.x; + startY = dialog.y; + } + xAxis.onActiveValueChanged: { + dialog.x = Math.round(startX + xAxis.activeValue); + } + yAxis.onActiveValueChanged: { + dialog.y = Math.round(startY + yAxis.activeValue); + } + } + x: Math.round((parent.width - width) / 2) + y: Math.round((parent.height - height) / 2) + } + } + } + + component PolkitDialog: WPane { + borderColor: Looks.colors.ambientShadow + + contentItem: WPanelPageColumn { + PolkitDialogHeader { + Layout.fillWidth: true + } + BodyRectangle { + id: dialogBody + implicitHeight: bodyContent.implicitHeight + 48 + implicitWidth: 434 + color: Looks.colors.bg1Base + + ColumnLayout { + id: bodyContent + anchors.fill: parent + anchors.margins: 24 + spacing: 20 + + RowLayout { + Layout.fillWidth: true + spacing: 15 + + WAppIcon { + iconName: PolkitService.flow?.iconName ?? "window-shield" + fallback: PolkitService.flow?.iconName == "" ? `${Looks.iconsPath}/window-shield` : PolkitService.flow.iconName + isMask: PolkitService.flow?.iconName === "" + tryCustomIcon: false + } + WText { + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + font.pixelSize: Looks.font.pixelSize.larger + font.weight: Looks.font.weight.strongest + text: { + const iconName = PolkitService.flow?.iconName ?? ""; + if (iconName === "") + return Translation.tr("Command-line-invoked Action"); + const desktopEntry = DesktopEntries.applications.values.find(entry => { + return entry.icon == iconName; + }); + return desktopEntry ? desktopEntry.name : Translation.tr("Unknown Application"); + } + } + } + + WText { + Layout.fillWidth: true + wrapMode: Text.Wrap + horizontalAlignment: Text.AlignLeft + text: PolkitService.cleanMessage + } + + WTextField { + id: inputField + Layout.fillWidth: true + focus: true + enabled: PolkitService.interactionAvailable + placeholderText: PolkitService.cleanPrompt + echoMode: root.usePasswordChars ? TextInput.Password : TextInput.Normal + onAccepted: PolkitService.submit(inputField.text) + + Keys.onPressed: event => { // Esc to close + if (event.key === Qt.Key_Escape) { + PolkitService.cancel(); + } + } + + Component.onCompleted: forceActiveFocus() + Connections { + target: PolkitService + function onInteractionAvailableChanged() { + if (!PolkitService.interactionAvailable) + return; + inputField.text = ""; + inputField.forceActiveFocus(); + } + } + } + } + } + BodyRectangle { + implicitHeight: 80 + color: Looks.colors.bgPanelFooterBase + RowLayout { + anchors.fill: parent + anchors.margins: 24 + spacing: 8 + uniformCellSizes: true + + WButton { + Layout.fillWidth: true + implicitHeight: 32 + colBackground: Looks.colors.bg1 + horizontalAlignment: Text.AlignHCenter + text: Translation.tr("Yes") + onClicked: PolkitService.submit(inputField.text) + } + WButton { + Layout.fillWidth: true + implicitHeight: 32 + horizontalAlignment: Text.AlignHCenter + checked: true + text: Translation.tr("No") + onClicked: PolkitService.cancel() + } + } + } + } + } + + component PolkitDialogHeader: BodyRectangle { + implicitHeight: headerContent.implicitHeight + color: Looks.colors.bg2Base + + CloseButton { + anchors { + top: parent.top + right: parent.right + } + radius: 0 + implicitWidth: 32 + implicitHeight: 32 + + onClicked: { + PolkitService.cancel(); + } + } + + ColumnLayout { + id: headerContent + anchors.fill: parent + anchors.leftMargin: 24 + anchors.rightMargin: 24 + spacing: 18 + + WText { + Layout.topMargin: 20 + Layout.fillWidth: true + horizontalAlignment: Text.AlignLeft + text: Translation.tr("Polkit") + } + WText { + Layout.fillWidth: true + Layout.bottomMargin: 12 + horizontalAlignment: Text.AlignLeft + wrapMode: Text.Wrap + text: Translation.tr("Do you want to allow this app to make changes to your device?") + font.pixelSize: Looks.font.pixelSize.xlarger + font.weight: Looks.font.weight.strongest + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/polkit/WafflePolkit.qml b/dots/.config/quickshell/ii/modules/waffle/polkit/WafflePolkit.qml new file mode 100644 index 000000000..8aa7d5672 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/polkit/WafflePolkit.qml @@ -0,0 +1,15 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Wayland + +FullscreenPolkitWindow { + id: root + contentComponent: Component { + WPolkitContent {} + } +} diff --git a/dots/.config/quickshell/ii/services/PolkitService.qml b/dots/.config/quickshell/ii/services/PolkitService.qml index 56576f4f4..758f0b0b8 100644 --- a/dots/.config/quickshell/ii/services/PolkitService.qml +++ b/dots/.config/quickshell/ii/services/PolkitService.qml @@ -11,6 +11,18 @@ Singleton { property alias active: polkitAgent.isActive property alias flow: polkitAgent.flow property bool interactionAvailable: false + property string cleanMessage: { + if (!root.flow) return ""; + return root.flow.message.endsWith(".") + ? root.flow.message.slice(0, -1) + : root.flow.message + } + property string cleanPrompt: { + const inputPrompt = PolkitService.flow?.inputPrompt.trim() ?? ""; + const cleanedInputPrompt = inputPrompt.endsWith(":") ? inputPrompt.slice(0, -1) : inputPrompt; + const usePasswordChars = !PolkitService.flow?.responseVisible ?? true + return cleanedInputPrompt || (usePasswordChars ? Translation.tr("Password") : Translation.tr("Input")) + } function cancel() { root.flow.cancelAuthenticationRequest() diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 55dc7bce9..eebd15e22 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.notificationCenter import qs.modules.waffle.onScreenDisplay +import qs.modules.waffle.polkit import qs.modules.waffle.startMenu import qs.modules.waffle.sessionScreen @@ -84,6 +85,7 @@ ShellRoot { PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } + PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } ReloadPopup {} @@ -98,7 +100,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", "wNotificationCenter", "wOnScreenDisplay", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiPolkit", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From bfe97c1c053fccaaef4d93df723bf02d1fc06871 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 8 Dec 2025 14:48:44 +0100 Subject: [PATCH 074/154] refractor lock screen --- .../panels}/lock/LockContext.qml | 0 .../modules/common/panels/lock/LockScreen.qml | 157 ++++++++++++++++ .../panels}/lock/pam/fprintd.conf | 0 .../quickshell/ii/modules/ii/lock/Lock.qml | 172 ++---------------- .../ii/modules/ii/lock/LockSurface.qml | 1 + dots/.config/quickshell/ii/shell.qml | 2 +- 6 files changed, 177 insertions(+), 155 deletions(-) rename dots/.config/quickshell/ii/modules/{ii => common/panels}/lock/LockContext.qml (100%) create mode 100644 dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml rename dots/.config/quickshell/ii/modules/{ii => common/panels}/lock/pam/fprintd.conf (100%) diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml similarity index 100% rename from dots/.config/quickshell/ii/modules/ii/lock/LockContext.qml rename to dots/.config/quickshell/ii/modules/common/panels/lock/LockContext.qml diff --git a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml new file mode 100644 index 000000000..9e4b9bd94 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml @@ -0,0 +1,157 @@ +pragma ComponentBehavior: Bound +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import QtQuick +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: root + + required property Component lockSurface + property alias context: lockContext + property Component sessionLockSurface: WlSessionLockSurface { + id: sessionLockSurface + color: "transparent" + Loader { + active: GlobalStates.screenLocked + anchors.fill: parent + opacity: active ? 1 : 0 + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) + } + sourceComponent: root.lockSurface + } + } + + Process { + id: unlockKeyringProc + onExited: (exitCode, exitStatus) => { + KeyringStorage.fetchKeyringData(); + } + } + function unlockKeyring() { + unlockKeyringProc.exec({ + environment: ({ + "UNLOCK_PASSWORD": lockContext.currentText + }), + command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")] + }) + } + + // This stores all the information shared between the lock surfaces on each screen. + // https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen + LockContext { + id: lockContext + + Connections { + target: GlobalStates + function onScreenLockedChanged() { + if (GlobalStates.screenLocked) { + lockContext.reset(); + lockContext.tryFingerUnlock(); + } + } + } + + onUnlocked: (targetAction) => { + // Perform the target action if it's not just unlocking + if (targetAction == LockContext.ActionEnum.Poweroff) { + Session.poweroff(); + return; + } else if (targetAction == LockContext.ActionEnum.Reboot) { + Session.reboot(); + return; + } + + // Unlock the keyring if configured to do so + if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async + + // Unlock the screen before exiting, or the compositor will display a + // fallback lock you can't interact with. + GlobalStates.screenLocked = false; + + // Refocus last focused window on unlock (hack) + Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`]) + + // Reset + lockContext.reset(); + + // Post-unlock actions + if (lockContext.alsoInhibitIdle) { + lockContext.alsoInhibitIdle = false; + Idle.toggleInhibit(true); + } + } + } + + WlSessionLock { + id: lock + locked: GlobalStates.screenLocked + + surface: root.sessionLockSurface + } + + function lock() { + if (Config.options.lock.useHyprlock) { + Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]); + return; + } + GlobalStates.screenLocked = true; + } + + IpcHandler { + target: "lock" + + function activate(): void { + root.lock(); + } + function focus(): void { + lockContext.shouldReFocus(); + } + } + + GlobalShortcut { + name: "lock" + description: "Locks the screen" + + onPressed: { + root.lock() + } + } + + GlobalShortcut { + name: "lockFocus" + description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason" + + "decides to keyboard-unfocus the lock screen" + + onPressed: { + lockContext.shouldReFocus(); + } + } + + function initIfReady() { + if (!Config.ready || !Persistent.ready) return; + if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) { + root.lock(); + } else { + KeyringStorage.fetchKeyringData(); + } + } + Connections { + target: Config + function onReadyChanged() { + root.initIfReady(); + } + } + Connections { + target: Persistent + function onReadyChanged() { + root.initIfReady(); + } + } +} diff --git a/dots/.config/quickshell/ii/modules/ii/lock/pam/fprintd.conf b/dots/.config/quickshell/ii/modules/common/panels/lock/pam/fprintd.conf similarity index 100% rename from dots/.config/quickshell/ii/modules/ii/lock/pam/fprintd.conf rename to dots/.config/quickshell/ii/modules/common/panels/lock/pam/fprintd.conf diff --git a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml index 3d6506c54..3aae6ef7b 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/Lock.qml @@ -3,116 +3,39 @@ import qs import qs.services import qs.modules.common import qs.modules.common.functions +import qs.modules.common.panels.lock import QtQuick import Quickshell import Quickshell.Io import Quickshell.Wayland import Quickshell.Hyprland -Scope { +LockScreen { id: root - Process { - id: unlockKeyringProc - onExited: (exitCode, exitStatus) => { - KeyringStorage.fetchKeyringData(); - } - } - function unlockKeyring() { - unlockKeyringProc.exec({ - environment: ({ - "UNLOCK_PASSWORD": lockContext.currentText - }), - command: ["bash", "-c", Quickshell.shellPath("scripts/keyring/unlock.sh")] - }) + lockSurface: LockSurface { + context: root.context } + // Push everything down property var windowData: [] function saveWindowPositionAndTile() { - Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]) - root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id)) + Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "true"]); + root.windowData = HyprlandData.windowList.filter(w => (w.floating && w.workspace.id === HyprlandData.activeWorkspace.id)); root.windowData.forEach(w => { - Hyprland.dispatch(`pseudo address:${w.address}`) - Hyprland.dispatch(`settiled address:${w.address}`) - Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`) - }) + Hyprland.dispatch(`pseudo address:${w.address}`); + Hyprland.dispatch(`settiled address:${w.address}`); + Hyprland.dispatch(`movetoworkspacesilent ${w.workspace.id},address:${w.address}`); + }); } function restoreWindowPositionAndTile() { root.windowData.forEach(w => { - Hyprland.dispatch(`setfloating address:${w.address}`) - Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`) - Hyprland.dispatch(`pseudo address:${w.address}`) - }) - Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]) + Hyprland.dispatch(`setfloating address:${w.address}`); + Hyprland.dispatch(`movewindowpixel exact ${w.at[0]} ${w.at[1]}, address:${w.address}`); + Hyprland.dispatch(`pseudo address:${w.address}`); + }); + Quickshell.execDetached(["hyprctl", "keyword", "dwindle:pseudotile", "false"]); } - - // This stores all the information shared between the lock surfaces on each screen. - // https://github.com/quickshell-mirror/quickshell-examples/tree/master/lockscreen - LockContext { - id: lockContext - - Connections { - target: GlobalStates - function onScreenLockedChanged() { - if (GlobalStates.screenLocked) { - lockContext.reset(); - lockContext.tryFingerUnlock(); - } - } - } - - onUnlocked: (targetAction) => { - // Perform the target action if it's not just unlocking - if (targetAction == LockContext.ActionEnum.Poweroff) { - Session.poweroff(); - return; - } else if (targetAction == LockContext.ActionEnum.Reboot) { - Session.reboot(); - return; - } - - // Unlock the keyring if configured to do so - if (Config.options.lock.security.unlockKeyring) root.unlockKeyring(); // Async - - // Unlock the screen before exiting, or the compositor will display a - // fallback lock you can't interact with. - GlobalStates.screenLocked = false; - - // Refocus last focused window on unlock (hack) - Quickshell.execDetached(["bash", "-c", `sleep 0.2; hyprctl --batch "dispatch togglespecialworkspace; dispatch togglespecialworkspace"`]) - - // Reset - lockContext.reset(); - - // Post-unlock actions - if (lockContext.alsoInhibitIdle) { - lockContext.alsoInhibitIdle = false; - Idle.toggleInhibit(true); - } - } - } - - WlSessionLock { - id: lock - locked: GlobalStates.screenLocked - - WlSessionLockSurface { - color: "transparent" - Loader { - active: GlobalStates.screenLocked - anchors.fill: parent - opacity: active ? 1 : 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.colorAnimation.createObject(this) - } - sourceComponent: LockSurface { - context: lockContext - } - } - } - } - - // Blur layer hack Variants { model: Quickshell.screens delegate: Scope { @@ -124,71 +47,12 @@ Scope { onShouldPushChanged: { if (shouldPush) { root.saveWindowPositionAndTile(); - Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]) + Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, ${verticalMovementDistance}, ${-verticalMovementDistance}, ${horizontalSqueeze}, ${horizontalSqueeze}`]); } else { - Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]) + Quickshell.execDetached(["bash", "-c", `hyprctl keyword monitor ${targetMonitorName}, addreserved, 0, 0, 0, 0`]); root.restoreWindowPositionAndTile(); } } } } - - function lock() { - if (Config.options.lock.useHyprlock) { - Quickshell.execDetached(["bash", "-c", "pidof hyprlock || hyprlock"]); - return; - } - GlobalStates.screenLocked = true; - } - - IpcHandler { - target: "lock" - - function activate(): void { - root.lock(); - } - function focus(): void { - lockContext.shouldReFocus(); - } - } - - GlobalShortcut { - name: "lock" - description: "Locks the screen" - - onPressed: { - root.lock() - } - } - - GlobalShortcut { - name: "lockFocus" - description: "Re-focuses the lock screen. This is because Hyprland after waking up for whatever reason" - + "decides to keyboard-unfocus the lock screen" - - onPressed: { - lockContext.shouldReFocus(); - } - } - - function initIfReady() { - if (!Config.ready || !Persistent.ready) return; - if (Config.options.lock.launchOnStartup && Persistent.isNewHyprlandInstance) { - root.lock(); - } else { - KeyringStorage.fetchKeyringData(); - } - } - Connections { - target: Config - function onReadyChanged() { - root.initIfReady(); - } - } - Connections { - target: Persistent - function onReadyChanged() { - root.initIfReady(); - } - } } diff --git a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml index e44eecacb..b2482132d 100644 --- a/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml +++ b/dots/.config/quickshell/ii/modules/ii/lock/LockSurface.qml @@ -7,6 +7,7 @@ import qs.services import qs.modules.common import qs.modules.common.widgets import qs.modules.common.functions +import qs.modules.common.panels.lock import qs.modules.ii.bar as Bar import Quickshell import Quickshell.Services.SystemTray diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index eebd15e22..6ed6480a6 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -100,7 +100,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", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "wSessionScreen", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From fce229cdc338e63e7fdc42c08ed6566feb0562c1 Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Tue, 9 Dec 2025 07:55:24 -0500 Subject: [PATCH 075/154] translations: improve & complete zh_CN translation --- .../quickshell/ii/translations/zh_CN.json | 425 ++++++++++++++---- 1 file changed, 326 insertions(+), 99 deletions(-) diff --git a/dots/.config/quickshell/ii/translations/zh_CN.json b/dots/.config/quickshell/ii/translations/zh_CN.json index 432fdd38d..bb61c85eb 100644 --- a/dots/.config/quickshell/ii/translations/zh_CN.json +++ b/dots/.config/quickshell/ii/translations/zh_CN.json @@ -9,7 +9,7 @@ "%1 characters": "%1 个字符", "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**价格**:免费。数据用于训练。\n\n**说明**:登录 Google 账户,允许 AI Studio 创建 Google Cloud 项目或其他要求,然后返回并点击获取 API 密钥", "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", - ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": "。Zerochan 注意事项:\n- 您需要指定一个颜色\n- 请在 `sidebar.booru.zerochan.username` 配置项内填写您的 Zerochan 用户名。如果不这样做[将可能会被封禁](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", "No further instruction provided": "未提供进一步说明", "API key set for %1": "已为 %1 设置 API 密钥", "API key:\n\n```txt\n%1\n```": "API 密钥:\n\n```txt\n%1\n```", @@ -26,10 +26,9 @@ "Bluetooth": "蓝牙", "Brightness": "亮度", "Cancel": "取消", - "Cheat sheet": "快捷键表", + "Cheat sheet": "快捷键指南", "Choose model": "选择模型", "Clean stuff | Excellent quality, no NSFW": "清洁内容 | 优秀质量,无 NSFW", - "Clear": "清除", "Clear chat history": "清除聊天记录", "Clear the current list of images": "清除当前图片列表", "Close": "关闭", @@ -51,12 +50,12 @@ "Go to source (%1)": "转到源 (%1)", "Hibernate": "休眠", "Input": "输入", - "Intelligence": "智能体", + "Intelligence": "智能", "Interface": "界面", "Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。", "Jump to current month": "跳转到当前月份", "Keep system awake": "保持系统唤醒", - "Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW", + "Large images | God tier quality, no NSFW.": "高清图片 | 顶级质量,无 NSFW", "Large language models": "大语言模型", "Launch": "启动", "Local Ollama model | %1": "本地 Ollama 模型 | %1", @@ -65,9 +64,7 @@ "Markdown test": "Markdown 测试", "Math result": "数学结果", "No API key set for %1": "未为 %1 设置 API 密钥", - "No audio source": "无音频源", "No media": "无媒体", - "No notifications": "无通知", "Not visible to model": "对模型不可见", "Nothing here!": "这里什么都没有!", "Notifications": "通知", @@ -77,7 +74,7 @@ "Page %1": "第 %1 页", "Reboot": "重启", "Reboot to firmware settings": "重启到固件设置", - "Reload Hyprland & Quickshell": "重新加载 Hyprland 和 Quickshell", + "Reload Hyprland & Quickshell": "重新加载 Hyprland 与 Quickshell", "Run": "运行", "Run command": "运行命令", "Save": "保存", @@ -97,13 +94,13 @@ "Task Manager": "任务管理器", "Task description": "任务描述", "Temperature must be between 0 and 2": "温度必须在 0 到 2 之间", - "Temperature set to %1": "温度设置为 %1", + "Temperature set to %1": "温度已设置为 %1", "Temperature: %1": "温度:%1", "The hentai one | Great quantity, a lot of NSFW, quality varies wildly": "成人向 | 数量巨大,大量 NSFW,质量参差不齐", "The popular one | Best quantity, but quality can vary wildly": "最受欢迎 | 数量最多,但质量参差不齐", "Thinking": "思考中", "Translation goes here...": "翻译结果会显示在这里...", - "Translator": "翻译器", + "Translator": "翻译", "Unfinished": "未完成", "Unknown": "未知", "Unknown Album": "未知专辑", @@ -112,28 +109,25 @@ "Unknown function call: %1": "未知函数调用:%1", "View Markdown source": "查看 Markdown 源码", "Volume": "音量", - "Volume mixer": "音量混合器", + "Volume mixer": "音量合成器", "Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限", - "Waiting for response...": "等待响应...", "Workspace": "工作区", "%1 Safe Storage": "%1 安全存储", "%1 does not require an API key": "%1 不需要 API 密钥", - "%1 queries pending": "%1 个查询等待中", - "%1 | Right-click to configure": "%1 | 右键点击进行配置", - "Invalid API provider. Supported: \n-": "无效的 API 提供商。支持的:\n-", - "Unknown command:": "未知命令:", - "Type /key to get started with online models\nCtrl+O to expand the sidebar\nCtrl+P to detach sidebar into a window": "输入 /key 开始使用在线模型\nCtrl+O 展开侧边栏\nCtrl+P 将侧边栏分离为窗口", - "Provider set to": "提供商设置为", - "Invalid model. Supported: \n```": "无效模型。支持的:\n```", + "%1 | Right-click to configure": "%1 | 右键以配置", + "Invalid API provider. Supported: \n- ": "无效的 API 提供商。支持的有:\n- ", + "Unknown command: ": "未知命令:", + "Provider set to ": "提供商已设置为 ", + "Invalid model. Supported: \n```\n": "无效模型。支持的有:\n```\n", "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", - "Enter tags, or \"%1\" for commands": "输入标签,或 \"%1\" 查看命令", - "Online via %1 | %2's model": "通过 %1 在线 | %2 的模型", - "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果没有想到标签,请输入页码", + "Enter tags, or \"%1\" for commands": "输入标签,或 “%1” 以查看命令", + "Online via %1 | %2's model": "在线 | 通过 %1 | %2 的模型", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果还没想到标签,可以直接输入页码", "Settings": "设置", - "Save chat": "保存对话", - "Load chat": "加载对话", + "Save chat": "保存聊天记录", + "Load chat": "加载聊天记录", "or": "或", - "Set the system prompt for the model.": "为模型设置系统提示。", + "Set the system prompt for the model.": "为模型设置系统提示词。", "To Do": "待办", "Calendar": "日历", "Advanced": "高级", @@ -141,11 +135,11 @@ "Services": "服务", "Light": "浅色", "Dark": "深色", - "Fidelity": "保真度", + "Fidelity": "保真", "Fruit Salad": "水果沙拉", "When not fullscreen": "非全屏时", "Choose file": "选择文件", - "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "随机 Konachan SFW 动漫壁纸\n图片保存到 ~/图片/Wallpapers", + "Random SFW Anime wallpaper from Konachan\nImage is saved to ~/Pictures/Wallpapers": "随机 Konachan SFW 动漫壁纸\n图片会保存到 ~/Pictures/Wallpapers", "Be patient...": "请耐心等待...", "Tonal Spot": "色调点", "Auto": "自动", @@ -162,7 +156,7 @@ "Pick wallpaper image on your system": "在系统中选择壁纸图片", "No": "否", "AI": "AI", - "Local only": "仅本地", + "Local only": "仅限本地", "Policies": "策略", "Weeb": "二次元", "Closet": "隐藏", @@ -173,14 +167,14 @@ "Style & wallpaper": "样式与壁纸", "Configuration": "配置", "Keybinds": "快捷键", - "Float": "浮动", + "Float": "悬浮", "Hug": "贴合", "illogical-impulse Welcome": "illogical-impulse 欢迎页", "Info": "信息", "Volume limit": "音量限制", "Prevents abrupt increments and restricts volume limit": "防止骤增并限制音量", "Resources": "资源", - "12h am/pm": "12小时 上午/下午", + "12h am/pm": "12小时制 am/pm", "Base URL": "基础 URL", "Audio": "声音", "Networking": "网络", @@ -200,24 +194,22 @@ "24h": "24小时制", "Use Levenshtein distance-based algorithm instead of fuzzy": "使用 Levenshtein 距离算法替代模糊匹配", "System prompt": "系统提示词", - "12h AM/PM": "12小时 AM/PM", - "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "如果你经常打错字可能更好用,但结果可能很奇怪,并且可能无法匹配缩写(如 \"GIMP\" 可能搜不到绘图程序)", + "12h AM/PM": "12小时制 AM/PM", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "如果您经常打错字可能更好用,但结果可能会奇怪,并且可能无法匹配缩写(如 “GIMP” 可能搜不到绘图程序)", "Critical warning": "临界警告", "User agent (for services that require it)": "用户代理(部分服务需要)", - "Such regions could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "这些区域可能是图片或屏幕中具有一定包容性的部分。\n可能并不总是准确。\n这是通过本地运行的图像处理算法实现的,没有使用 AI。", "Workspaces shown": "显示的工作区数", "Dark/Light toggle": "深浅色切换", "Dock": "停靠栏", "Weather": "天气", "Pinned on startup": "启动时固定", - "Always show numbers": "总是显示数字", + "Always show numbers": "始终显示数字", "Keyboard toggle": "键盘切换", - "Scale (%)": "缩放比例(%)", + "Scale (%)": "缩放比例(%)", "Overview": "概览", "Rows": "行数", - "Screenshot tool": "截图工具", - "Number show delay when pressing Super (ms)": "按下 Super 时数字显示延迟(ms)", - "Timeout (ms)": "超时时间(ms)", + "Number show delay when pressing Super (ms)": "按下 Super 时的数字显示延迟(毫秒)", + "Timeout (ms)": "显示时间(毫秒)", "Show app icons": "显示应用图标", "Workspaces": "工作区", "Columns": "列数", @@ -226,7 +218,6 @@ "Mic toggle": "麦克风切换", "Hover to reveal": "悬停显示", "Bar": "条栏", - "Show regions of potential interest": "显示可能感兴趣的区域", "Color picker": "取色器", "Help & Support": "帮助与支持", "Discussions": "讨论区", @@ -242,12 +233,11 @@ "Terminal": "终端", "Shell & utilities": "Shell 与工具", "Qt apps": "Qt 应用", - "Force dark mode in terminal": "终端强制使用深色模式", + "Force dark mode in terminal": "强制终端使用深色模式", "Report a Bug": "报告问题", "Issues": "问题追踪", - "Drag or click a region • LMB: Copy • RMB: Edit": "拖动或点击一个区域 • 鼠标左键:复制 • 鼠标右键:编辑", "Current model: %1\nSet it with %2model MODEL": "当前模型:%1\n使用 %2model MODEL 设置", - "Message the model... \"%1\" for commands": "与模型对话... \"%1\" 查看命令", + "Message the model... \"%1\" for commands": "向模型发送消息... “%1” 以查看命令", "The current system prompt is\n\n---\n\n%1": "当前系统提示词为\n\n---\n\n%1", "Model set to %1": "模型已设置为 %1", "Loaded the following system prompt\n\n---\n\n%1": "已加载以下系统提示词\n\n---\n\n%1", @@ -255,16 +245,13 @@ "Save chat to %1": "保存聊天记录到 %1", "Load chat from %1": "从 %1 加载聊天记录", "Load prompt from %1": "从 %1 加载提示词", - "Select output device": "选择输出设备", - "%1 • %2 tasks": "%1 • %2 个任务", - "Online models disallowed\n\nControlled by `policies.ai` config option": "禁止在线模型\n\n由 `policies.ai` 配置项控制", - "Select input device": "选择输入设备", + "%1 • %2 tasks": "%1 • %2 项任务", + "Online models disallowed\n\nControlled by `policies.ai` config option": "已禁止在线模型\n\n由 `policies.ai` 配置项控制", "Low battery": "电量低", "Registration failed. Please inspect manually with the warp-cli command": "注册失败。请使用 warp-cli 命令手动检查", "Code saved to file": "代码已保存到文件", - "Consider plugging in your device": "请考虑连接您的设备", + "Consider plugging in your device": "请考虑为您的设备充电", "Weather Service": "天气服务", - "Please charge!\nAutomatic suspend triggers at %1": "请充电!\n自动挂起将在 %1 时触发", "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", "Cloudflare WARP": "Cloudflare WARP", "Download complete": "下载完成", @@ -288,43 +275,40 @@ "Fully charged": "已充满电", "Charging:": "充电功率:", "Discharging:": "放电功率:", - "No pending tasks": "没有待办任务", + "No pending tasks": "没有要做的任务", "... and %1 more": "... 还有 %1 个", "Used:": "已用:", "Free:": "可用:", "Total:": "总计:", "Load:": "负载:", - "High": "高", - "Medium": "中", - "Low": "低", + "Medium": "适中", "Tint icons": "图标着色", - "Performance Profile toggle": "性能配置文件切换", - "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**说明**:登录 Mistral 账户,在侧边栏中选择 Keys,点击创建新密钥", + "Performance Profile toggle": "性能配置切换", + "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**说明**:登录 Mistral 账户,在侧边栏中选择 Keys,点击 Create new key", "Invalid arguments. Must provide `command`.": "参数无效。必须提供 `command`。", "Thought": "思考", - "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "在线 | Google 模型\n针对成本效益和高吞吐量优化的 Gemini 2.5 Flash 模型。", - "Online | Google's model\nFast, can perform searches for up-to-date information": "在线 | Google 模型\n速度快,可搜索最新信息", + "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "在线 | Google 的模型\n针对成本效益和高吞吐量优化的 Gemini 2.5 Flash 模型。", + "Online | Google's model\nFast, can perform searches for up-to-date information": "在线 | Google 的模型\n速度快,可搜索最新信息", "Your package manager is running": "您的包管理器正在运行", "Gives the model search capabilities (immediately)": "为模型提供搜索功能(即时)", "Set the tool to use for the model.": "设置模型使用的工具。", "Night Light | Right-click to toggle Auto mode": "夜间模式 | 右键切换自动模式", "Online | %1's model | Delivers fast, responsive and well-formatted answers. Disadvantages: not very eager to do stuff; might make up unknown function calls": "在线 | %1 的模型 | 提供快速、响应迅速且格式良好的答案。缺点:不太积极主动;可能编造未知的函数调用", - "Depends on workspace": "取决于工作区", + "Depends on workspace": "随工作区移动", "Usage: %1tool TOOL_NAME": "用法:%1tool 工具名称", "Tray": "托盘", "Usage: %1save CHAT_NAME": "用法:%1save 聊天名称", "Approve": "批准", - "Depends on sidebars": "取决于侧边栏", - "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "命令、编辑配置、搜索。\n如果需要,会额外执行一次切换到搜索模式", + "Depends on sidebars": "随侧边栏移动", + "Commands, edit configs, search.\nTakes an extra turn to switch to search mode if that's needed": "执行命令、编辑配置、搜索。\n如果需要,会额外执行一次切换到搜索模式", "Up %1": "运行 %1", - "Tool set to: %1": "工具设置为:%1", - "Wallpaper parallax": "壁纸视差", - "Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.": "在线 | Google 模型\nGoogle 最先进的多用途模型,在编程和复杂推理任务方面表现卓越。", + "Tool set to: %1": "工具已设置为 %1", + "Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks.": "在线 | Google 的模型\nGoogle 最先进的多用途模型,在编程和复杂推理任务方面表现卓越。", "Tint app icons": "应用图标着色", - "Preferred wallpaper zoom (%)": "首选壁纸缩放比例 (%)", - "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "要设置 API 密钥,请使用 %4 命令传递\n\n要查看密钥,请在命令中传递 \"get\"
\n\n### 对于 %1:\n\n**链接**:%2\n\n%3", + "Preferred wallpaper zoom (%)": "首选壁纸缩放比例(%)", + "To set an API key, pass it with the %4 command\n\nTo view the key, pass \"get\" with the command
\n\n### For %1:\n\n**Link**: %2\n\n%3": "要设置 API 密钥,请使用 %4 命令传递\n\n要查看密钥,请在命令中传递 “get”
\n\n### 对于 %1:\n\n**链接**:%2\n\n%3", "No API key\nSet it with /key YOUR_API_KEY": "无 API 密钥\n使用 /key YOUR_API_KEY 设置", - "Total token count\nInput: %1\nOutput: %2": "总令牌数\n输入:%1\n输出:%2", + "Total token count\nInput: %1\nOutput: %2": "总词元数\n输入:%1\n输出:%2", "Disable tools": "禁用工具", "API key is set\nChange with /key YOUR_API_KEY": "API 密钥已设置\n使用 /key YOUR_API_KEY 更改", "Usage: %1load CHAT_NAME": "用法:%1load 聊天名称", @@ -332,17 +316,17 @@ "Temperature\nChange with /temp VALUE": "温度\n使用 /temp VALUE 更改", "Current tool: %1\nSet it with %2tool TOOL": "当前工具:%1\n使用 %2tool TOOL 设置", "There might be a download in progress": "可能有下载正在进行", - "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "在线 | Google 模型\n比前代模型更慢但应该提供更高质量答案的新模型", - "EasyEffects | Right-click to configure": "EasyEffects | 右键配置", + "Online | Google's model\nNewer model that's slower than its predecessor but should deliver higher quality answers": "在线 | Google 的模型\n比前代模型更慢但应该提供更高质量答案的新模型", + "EasyEffects | Right-click to configure": "EasyEffects | 右键以配置", "Command rejected by user": "用户拒绝了命令", "Invalid tool. Supported tools:\n- %1": "无效工具。支持的工具:\n- %1", "Keep right sidebar loaded": "保持右侧边栏加载", "Reject": "拒绝", "Enter password": "输入密码", "Automatically hide": "自动隐藏", - "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**定价**:提供免费层,速率有限。详情见 https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**说明**:生成一个具有 Models 权限的 GitHub 个人访问令牌,并在此处将其设置为 API 密钥\n\n**注意**:使用此提供商时需要将温度参数设置为 1", + "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**定价**:提供免费层级,速率有限。详情见 https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**说明**:生成一个具有 Models 权限的 GitHub 个人访问令牌,并在此处将其设置为 API 密钥\n\n**注意**:使用此提供商时需要将温度参数设置为 1", "Conflicts with the shell's system tray implementation": "与 Shell 的系统托盘实现冲突", - "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "祝您愉快!随时可以按 Super+Shift+Alt+/ 重新打开欢迎应用。要打开设置应用,请按 Super+I", + "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "祝您愉快!可以随时按 Super+Shift+Alt+/ 重新打开欢迎应用。要打开设置应用,请按 Super+I", "Reset": "重置", "☕ Break: %1 minutes": "☕ 休息:%1 分钟", "Pomodoro": "番茄钟", @@ -356,12 +340,11 @@ "Config file": "配置文件", "Stopwatch": "秒表", "Break": "休息", - "Shell conflicts killer": "终止冲突程序", + "Shell conflicts killer": "冲突终止程序", "Vertical": "垂直", "🔴 Focus: %1 minutes": "🔴 专注:%1 分钟", "System uptime:": "系统运行时间:", "Focus": "专注", - "Open the shell config file.\nIf the button doesn't work or doesn't open in your favorite editor,\nyou can manually open ~/.config/illogical-impulse/config.json": "打开 Shell 配置文件。\n如果按钮无法工作或没有在您喜欢的编辑器中打开,\n可以手动打开 ~/.config/illogical-impulse/config.json", "Attach a file. Only works with Gemini.": "附加文件。仅适用于 Gemini。", "🌿 Long break: %1 minutes": "🌿 长休息:%1 分钟", "Always": "始终", @@ -370,19 +353,19 @@ "Timer": "计时器", "Conflicts with the shell's notification implementation": "与 Shell 的通知实现冲突", "Pause": "暂停", - "Feels like %1": "体感温度:%1", + "Feels like %1": "体感温度 %1", "Lap": "计时", "Welcome app": "欢迎应用", "Corner style": "角落样式", "Language": "语言", - "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "选择用户界面的语言。\n\"自动\" 将使用系统语言环境。", + "Select the language for the user interface.\n\"Auto\" will use your system's locale.": "选择用户界面的语言。\n“自动”将使用系统区域设置。", "Auto (System)": "自动(系统)", "Interface Language": "界面语言", "Paired": "已配对", - "Hit \"/\" to search": "按 \"/\" 搜索", + "Hit \"/\" to search": "按 “/” 以搜索", "Region width": "区域宽度", "Math": "数学", - "When this is off you'll have to click": "关闭后需要点击", + "When this is off you'll have to click": "若关闭则需要点击来打开", "Edit directory": "编辑目录", "Pills": "胶囊", "No active player": "无活动播放器", @@ -395,7 +378,6 @@ "Bluetooth devices": "蓝牙设备", "Wallpaper & Colors": "壁纸与配色", "Details": "详细信息", - "Show clock": "显示时钟", "Connected": "已连接", "Open network portal": "打开网络门户", "Bar style": "条栏样式", @@ -404,32 +386,32 @@ "Value scroll": "滚动调整数值", "Line-separated": "线条分隔", "Region height": "区域高度", - "Pick a wallpaper": "选择壁纸", - "Visualize region": "可视化区域", + "Pick a wallpaper": "挑选壁纸", + "Visualize region": "显示区域", "Bottom": "底部", - "Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries": "用法:%1superpaste 条目数[i]\n需要图片时加上 i\n示例:\n%1superpaste 4i 获取最近 4 张图片\n%1superpaste 7 获取最近 7 条记录", - "Terminal: Harmony (%)": "终端:协调度 (%)", + "Usage: %1superpaste NUM_OF_ENTRIES[i]\nSupply i when you want images\nExamples:\n%1superpaste 4i for the last 4 images\n%1superpaste 7 for the last 7 entries": "用法:%1superpaste 条目数[i]\n需要图片时加上 i\n示例:\n%1superpaste 4i 粘贴最近 4 张图片\n%1superpaste 7 粘贴最近 7 个条目", + "Terminal: Harmony (%)": "终端:协调度(%)", "Forget": "忘记", "Background": "背景", "Top": "顶部", - "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "并非所有选项都在此应用中提供。您还应点击左上角的“配置文件”按钮或手动打开 %1 查看配置文件。", - "General": "常规", + "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "并非所有选项都在此应用中提供。您还应点击左上角的“配置文件”按钮,或手动打开 %1 以查看配置文件。", + "General": "通用", "Right": "右侧", "Utility buttons": "工具按钮", "Quick": "快速", - "Terminal: Foreground boost (%)": "终端:前景增强 (%)", + "Terminal: Foreground boost (%)": "终端:前景增强(%)", "Left": "左侧", - "Tip: right-clicking a group\nalso expands it": "提示:右键点击一个分组\n也可展开", + "Tip: right-clicking a group\nalso expands it": "提示:右键点击一个分组\n也可将其展开", "Change any time later with /dark, /light, /wallpaper in the launcher\nIf the shell's colors aren't changing:\n 1. Open the right sidebar with Super+N\n 2. Click \"Reload Hyprland & Quickshell\" in the top-right corner": "稍后可在启动器中通过 /dark、/light、/wallpaper 随时更改\n如果 Shell 的配色没有变化:\n 1. 用 Super+N 打开右侧边栏\n 2. 点击右上角的“重新加载 Hyprland 与 Quickshell”", "Place at bottom": "放置在底部", - "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "无论条栏位置如何,都允许通过点击或悬停屏幕角落打开侧边栏", + "Allows you to open sidebars by clicking or hovering screen corners regardless of bar position": "无论条栏位置,都允许通过点击或悬停在屏幕角落来打开侧边栏", "Positioning": "位置", "Disconnect": "断开连接", "Unknown device": "未知设备", "Make icons pinned by default": "默认固定图标", "Bar & screen": "条栏与屏幕", "Corner open": "角落打开", - "Hover to trigger": "悬停触发", + "Hover to trigger": "悬停以触发", "Terminal: Harmonize threshold": "终端:协调阈值", "Bar position": "条栏位置", "Place the corners to trigger at the bottom": "将触发角落放置在底部", @@ -439,34 +421,279 @@ "Launch on startup": "启动时锁屏", "Also unlock keyring": "同时解锁密钥环", "Tip: Close a window with Super+Q": "提示: 使用 Super+Q 关闭窗口", - "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "请记住,大多数设备仍可以长按电源键进行强制关机\n此选项只会降低误触的可能性而已", - "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "这通常是安全的,并且对浏览器和 AI 侧边栏也是必要的\n主要对那些使用“开机自动锁屏”而不是显示管理器(如 GDM、SDDM 等)执行锁屏的用户有用", - "Show \"Locked\" text": "显示“已锁定”文字", + "Remember that on most devices one can always hold the power button to force shutdown\nThis only makes it a tiny bit harder for accidents to happen": "请记住,大多数设备仍可以通过长按电源键强制关机\n此选项只会略微降低误触的可能性而已", + "This is usually safe and needed for your browser and AI sidebar anyway\nMostly useful for those who use lock on startup instead of a display manager that does it (GDM, SDDM, etc.)": "这通常是安全的,并且对浏览器和 AI 侧边栏也是必要的\n主要对使用“启动时锁屏”而非显示管理器(如 GDM、SDDM 等)执行锁屏的用户有用", + "Show \"Locked\" text": "显示“已锁定”字样", "at": "在", - "Simple digital": "简洁数字", "Style: general": "样式:通用", - "Pick random from this folder": "从此文件随机选择", + "Pick random from this folder": "从此文件夹随机选择", "Back": "返回", "Cancel wallpaper selection": "取消壁纸选择", - "Timeout duration (if not defined by notification) (ms)": "延时时间 (若通知内未定义)(毫秒)", + "Timeout duration (if not defined by notification) (ms)": "显示时间(若通知未指定)(毫秒)", "Enable blur": "启用模糊", - "Material cookie": "Material 曲奇", "Click to toggle light/dark mode\n(applied when wallpaper is chosen)": "点击来切换浅色/深色模式\n(仅在选择壁纸后生效)", - "Use the system file picker instead\nRight-click to make this the default behavior": "改为使用系统文件选择器\n右键点击可设为默认行为", + "Use the system file picker instead\nRight-click to make this the default behavior": "改为使用系统文件选择器\n右键以将此设为默认行为", "Center clock": "居中时钟", - "Press Super+G to toggle appearance": "按下 Super+G 切换准星显示", "Lock screen": "锁屏", - "Crosshair code (in Valorant's format)": "准星代码 (瓦罗兰特格式)", + "Crosshair code (in Valorant's format)": "准星代码(瓦罗兰特格式)", "Random: osu! seasonal": "随机:osu! 季节性壁纸", "Work safety": "安全模式", "Require password to power off/restart": "需要密码来关机或重启", "Random osu! seasonal background\nImage is saved to ~/Pictures/Wallpapers": "随机 osu! 季节性壁纸\n图片会保存到 ~/Pictures/Wallpapers", "Open editor": "打开编辑器", - "Extra wallpaper zoom (%)": "额外壁纸缩放 (%)", + "Extra wallpaper zoom (%)": "额外壁纸缩放(%)", "Security": "安全", "Clock style": "时钟样式", "Style: Blurred": "样式:模糊", - "Crosshair overlay": "射击准星叠加", "Locked": "已锁定", - "Wallpaper safety enforced": "已启用壁纸安全模式" + "Wallpaper safety enforced": "已启用壁纸安全模式", + "Hour hand": "时针", + "Automatic": "自动", + "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "想要的语言不在列表内或翻译不完整?\n您可以选择使用 Gemini 为其生成翻译。\n1. 按 Super+A 打开左侧边栏,将模型设置为 Gemini(如果不已经是了的话)\n2. 输入 /key,按 Enter 然后跟随说明\n3. 输入 /key YOUR_API_KEY\n4. 在下方输入您的语言代码并按下生成", + "Auto styling with Gemini": "使用 Gemini 自动决定样式", + "Audio output | Right-click for volume mixer & device selector": "音频输出 | 右键打开音量合成器与设备选择器", + "Most busy": "最繁杂处", + "Title font": "标题字体", + "Nerd font icons": "Nerd Font 图标", + "Could be images or parts of the screen that have some containment.\nMight not always be accurate.\nThis is done with an image processing algorithm run locally and no AI is used.": "可能是带有一定边界的图片或屏幕部分。结果不一定总是准确。\n这是通过本地的图片处理算法完成的,过程没有 AI 参与。", + "Hollow": "空心", + "Turn on from sunset to sunrise": "在日落到日出期间开启", + "Notes": "笔记", + "It may take a few seconds to update": "可能需要几秒钟来更新", + "Google Lens": "Google 智能镜头", + "Monospace font": "等宽字体", + "Auto, ": "自动,", + "Classic": "经典", + "Font family name (e.g., JetBrains Mono NF)": "字体名称(如 JetBrains Mono NF)", + "Clear all": "全部清除", + "Show aim lines": "显示瞄准线", + "System sound": "系统声音", + "Search for apps": "搜索应用", + "Circle to Search": "圈定即搜", + "Thin": "纤细", + "Content region": "内容区域", + "Bubble": "气泡", + "Enable opening zoom animation": "启用打开时的缩放动画", + "Secured": "安全", + "Wi-Fi": "Wi-Fi", + "Manage my account": "管理我的账户", + "Regenerate": "重新生成", + "%1\nInternet access": "%1\nInternet 访问", + "Font family name (e.g., Space Grotesk)": "字体名称(如 Space Grotesk)", + "Inactive": "未启用", + "Least busy": "最空旷处", + "Constantly rotate": "持续旋转", + "Anti-flashbang (experimental)": "防高亮保护(实验性)", + "Bold": "加粗", + "Open the shell config file\nAlternatively right-click to copy path": "打开 Shell 配置文件\n或右键以复制路径", + "Description font size": "描述字体大小", + "Dot": "圆点", + "Second hand": "秒针", + "Generate translation with Gemini": "使用 Gemini 生成翻译", + "Enable GPS based location": "启用基于 GPS 的位置", + "Microphone": "麦克风", + "Virtual Keyboard": "屏幕键盘", + "Shut down": "关机", + "Digits in the middle": "在中心显示数字", + "If you want to somehow use fingerprint unlock...": "如果您想想办法用指纹解锁的话...", + "Couldn't recognize music": "未识别到歌曲", + "Split buttons": "拆分按键", + "Number style": "数字样式", + "Write something here...\nUse '-' to create copyable bullet points, like this:\n\nSheep fricker\n- 4x Slab\n- 1x Boat\n- 4x Redstone Dust\n- 1x Sticky Piston\n- 1x End Rod\n- 4x Redstone Repeater\n- 1x Redstone Torch\n- 1x Sheep": "在这里写些什么...\n使用 '-' 来创建可复制的列表项,比如这样:\n\n羊羊快♂乐机\n- 4x 半砖\n- 1x 船\n- 4x 红石粉\n- 1x 黏性活塞\n- 1x 末地烛\n- 4x 红石中继器\n- 1x 红石火把\n- 1x 绵羊", + "Network": "网络", + "Overlay: Floating Image": "叠加面板:悬浮图像", + "Unpin from taskbar": "从任务栏取消固定", + "Sound input": "声音输入", + "Not connected": "未连接", + "Use macOS-like symbols for mods keys": "为修饰键使用 macOS 风格的图标", + "Use symbols for mouse": "使用图标表示鼠标键", + "Fonts": "字体", + "Circle": "圈选", + "Wallpaper selector": "壁纸选择器", + "(Plugged in)": "(电源已接通)", + "Sound effects": "声音效果", + "Intensity": "强度", + "Close window": "关闭窗口", + "Video Recording Path": "视频录制路径", + "Speakers (%1): %2": "扬声器 (%1): %2", + "Perhaps what you're listening to is too niche": "也许您听的音乐太小众了", + "Animate time change": "时间变化动画", + "Set FPS limit": "设置帧数限制", + "Identify Music": "识别音乐", + "Focusing": "Focusing", + "Sound output": "声音输出", + "Type /key to get started with online models\nCtrl+O to expand sidebar\nCtrl+P to pin sidebar\nCtrl+D to detach sidebar": "输入 /key 来开始使用在线模型\nCtrl+O 拓宽侧边栏\nCtrl+P 固定侧边栏\nCtrl+D 分离侧边栏", + "Parallax": "视差效果", + "EasyEffects": "EasyEffects", + "Show only when locked": "仅在锁屏时显示", + "Date style": "日期样式", + "Font family name (e.g., Google Sans Flex)": "字体名称(如 Google Sans Flex)", + "+%1 notifications": "+%1 个通知", + "Hide clipboard images copied from sussy sources": "隐藏剪贴板中来自奇奇怪怪来源的图片", + "Use Hyprlock (instead of Quickshell)": "使用 Hyprlock(而非 Quickshell)", + "Darken screen": "屏幕变暗", + "Generating...\nDon't close this window!": "生成中...\n请勿关闭此窗口!", + "Dial style": "表盘样式", + "Anti-flashbang": "防高亮保护", + "Please charge!\nAutomatic suspend triggers at %1%": "请充电!\n将在电量为 %1% 时自动挂起", + "Battery full": "电量已充满", + "More Bluetooth settings": "更多蓝牙设置", + "Center icons": "图标居中", + "Generate\nTypically takes 2 minutes": "生成\n通常需要 2 分钟", + "Overlay: Crosshair": "叠加面板:准星", + "You can also manually edit cheatsheet.superKey": "您也可以手动编辑 cheatsheet.superKey 配置项", + "Layers": "显示层", + "Sign out": "注销", + "Android": "安卓", + "Recognize music | Right-click to toggle source": "识别音乐 | 右键以切换源", + "Why this is cool:\nFor non-0 values, it won't trigger when you reach the\nscreen corner along the horizontal edge, but it will when\nyou do along the vertical edge": "为什么这很酷:\n对于非 0 的数值,当你沿着水平边缘抵达屏幕角时,它不会触发;\n但当你沿着垂直边缘抵达屏幕角时,它就会触发", + "Locale code, e.g. fr_FR, de_DE, zh_CN...": "语言代码,如 fr_FR、de_DE、zh_CN 等", + "of %1": "共 %1", + "Clock style (locked)": "时钟样式(锁屏时)", + "City name": "城市名", + "Make sure you have songrec installed": "请确保您已安装 songrec", + "Windows": "窗口", + "Power Profile": "电源模式", + "Used for code and terminal": "用于代码和终端", + "Select language": "选择语言", + "File Explorer": "文件资源管理器", + "Saved ": "已保存 ", + "Not secured": "不安全", + "Overlay: General": "叠加面板:通用", + "Keybind font size": "快捷键字体大小", + "Nothing": "空空如也", + "Main font": "主字体", + "End session": "结束专注", + "Listening...": "正在听取...", + "Font family name (e.g., Readex Pro)": "字体名称(如 Readex Pro)", + "Fill": "填充", + "Dark Mode": "深色模式", + "Restart": "重启", + "Dots": "圆点", + "%1 mins": "%1 分钟", + "Font used for Nerd Font icons": "用于 Nerd Font 图标的字体", + "Region selector (screen snipping/Google Lens)": "区域选择器(屏幕截图与 Google 智能镜头)", + "Battery: %1%2": "电池状态:%1%2", + "Click to cycle through power profiles": "点击以循环切换电源模式", + "When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll": "当上一个选项为关闭且此项开启时,您仍然\n可以通过悬停在角落的末端来打开侧边栏,\n剩余的区域将可用于滚动调节音量与亮度", + "Widgets": "小组件", + "On-screen keyboard": "屏幕键盘", + "Used for general UI text": "用于通用 UI 文本", + "Line": "线条", + "Replace 󱕐 for \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"LMB\", R󰍽 \"RMB\", 󱕒 \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"": "如用 󱕐 来表示 “Scroll ↓”,󱕑 “Scroll ↑”,L󰍽 “LMB”,R󰍽 “RMB”,以及 󱕒 “Scroll ↑/↓” 和 ⇞/⇟ 来表示 “Page_↑/↓”", + "Unmuted": "已打开", + "Path copied": "路径已复制", + "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "使用 Gemini 对壁纸进行分类,然后根据分类选择一个预设。\n您需要先在左侧边栏设置 Gemini API 密钥。\n图片会被降低分辨率以提高性能, 但为了安全起见,\n请勿选择包含敏感信息的壁纸。", + "Unread indicator: show count": "未读指示器:显示数量", + "RAM": "内存", + "Saving...": "保存中...", + "Illegal increment": "超过最大增量限制", + "\nLMB to enable/disable\nRMB to toggle size\nScroll to swap position": "左键以启用/禁用\n右键以切换尺寸\n滚动以交换位置", + "Health:": "电池健康:", + "Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \"󰘴 + A\" instead of \"󰘴 A\")": "使用多个“键帽”显示修饰键和按键(如显示为 “Ctrl + A” 而非 “Ctrl A”,或 “󰘴 + A” 而非 “󰘴 A”)", + "Cookie clock settings": "曲奇时钟设置", + "Eye protection": "护眼选项", + "Tooltips": "悬停提示", + "See fewer": "查看更少", + "Click to show": "点击以显示", + "Circle selection": "圈定选区", + "Enter a valid number": "请输入有效的数字", + "Music Recognition": "音乐识别", + "Sounds": "提示音", + "Input device": "输入设备", + "On": "开", + "Hide sussy/anime wallpapers": "隐藏可疑或动漫壁纸", + "Full": "完整", + "Image source": "图像来源", + "Night Light": "夜间模式", + "Digital clock settings": "数字时钟设置", + "e.g. 󰘴 for Ctrl, 󰘵 for Alt, 󰘶 for Shift, etc": "如用 󰘴 来表示 Ctrl,󰘵 来表示 Alt,󰘶 来表示 Shift 等", + "Use system file picker": "使用系统文件选择器", + "Show hidden icons": "显示隐藏的图标", + "Exceeded max allowed": "已超过最高限制", + "Please unplug the charger": "请拔掉充电器", + "Numbers": "数字", + "Example use case: eroge on one workspace, dark Discord window on another": "使用示例:在一个工作区玩小黄游,另一个工作区开着深色的 Discord 窗口", + "Enabled": "已打开", + "Recognize music": "识别音乐", + "Digital": "数字", + "Audio input | Right-click for volume mixer & device selector": "音频输入 | 右键打开音量合成器与设备选择器", + "Use old sine wave cookie implementation": "使用旧版正弦波形曲奇实现", + "Used for displaying numbers": "用于显示数字", + "Music Recognized": "识别到歌曲", + "Numbers font": "数字字体", + "Media": "媒体", + "Quick toggles": "快捷设置", + "Copy path": "复制路径", + "Screenshot Path (leave empty to just copy)": "屏幕截图路径(留空则只复制)", + "Draggable": "可移动", + "Off": "关", + "Super key symbol": "Super 键图标", + "Normal": "正常", + "Scroll to Bottom": "滚动到底部", + "Audio output": "音频输出", + "Use varying shapes for password characters": "使用多样形状显示密码字符", + "Hour marks": "时标", + "Night Light | Right-click to configure": "夜间模式 | 右键以配置", + "Edit quick toggles": "编辑快捷设置", + "Total duration timeout (s)": "总持续时长(秒)", + "Font family name": "字体名称", + "Rectangular selection": "矩形选区", + "Sides": "边数", + "Stroke width": "笔画粗细", + "Widget: Clock": "小组件:时钟", + "Audio input": "音频输入", + "Polling interval (s)": "轮询间隔(秒)", + "Used for decorative/expressive text": "用于装饰性或富有表现力的文字", + "Enable translator": "启用翻译", + "Pin to taskbar": "固定到任务栏", + "Active": "已启用", + "Used for headings and titles": "用于标题和副标题", + "More volume settings": "更多音量设置", + "Minute hand": "分针", + "Enable if you want clocks to show seconds accurately": "启用以让时钟精准显示秒数", + "Keep awake": "保持唤醒", + "Local account": "本地账户", + "Save paths": "保存路径", + "Open recordings folder": "打开录像文件夹", + "Muted": "已静音", + "Sliders": "滑块", + "CPU": "CPU", + "Second precision": "精确显秒", + "Border": "内圈", + "Reading font": "阅读字体", + "Press Super+G to open the overlay and pin the crosshair": "按 Super+G 来打开叠加面板,然后固定准星", + "Show": "显示", + "More Internet settings": "更多 Internet 设置", + "Get the latest features and security improvements with\nthe newest feature update.\n\n%1 packages": "通过安装更新获取最新的功能和\n安全改进。\n\n%1 个软件包", + "Quote": "语录", + "Widget: Weather": "小组件:天气", + "Used for reading large blocks of text": "用于阅读大段文字", + "with vertical offset": "使用垂直偏移", + "Han chars": "汉字", + "e.g. 󱊫 for F1, 󱊶 for F12": "如用 󱊫 来表示 F1,󱊶 来表示 F12 等", + "Internet": "网络", + "Show notifications": "显示通知", + "Force hover open at absolute corner": "强制在绝对角落悬停打开", + "Use symbols for function keys": "使用符号表示功能键", + "Record": "屏幕录制", + "Authentication": "身份验证", + "Hint target regions": "建议目标区域", + "Enable now": "现在启用", + "You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.": "您需要先输入 Gemini API 密钥。\n在侧边栏输入 /key 以获取说明。", + "Output device": "输出设备", + "Swap": "虚拟内存", + "Full warning": "满电警告", + "Padding": "额外边距", + "Expressive font": "表现力字体", + "Balance brightness based on content": "根据内容更改亮度", + "Cookie": "曲奇", + "Fahrenheit unit": "华氏度单位", + "Roman": "罗马", + "Polling interval (m)": "轮询间隔(分钟)", + "Close all windows": "关闭所有窗口", + "Adjust the color temperature": "调整色温", + "Task View": "任务视图", + "More comfortable viewing at night": "夜间浏览更舒适", + "No new notifications": "没有新通知" } \ No newline at end of file From 95c6fcab01f80d68a8f094ae9422d7432b35308c Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Tue, 9 Dec 2025 08:44:00 -0500 Subject: [PATCH 076/154] translations: zh-CN another update & remove unused --- .../quickshell/ii/translations/zh_CN.json | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/translations/zh_CN.json b/dots/.config/quickshell/ii/translations/zh_CN.json index bb61c85eb..93be8e4b4 100644 --- a/dots/.config/quickshell/ii/translations/zh_CN.json +++ b/dots/.config/quickshell/ii/translations/zh_CN.json @@ -57,7 +57,6 @@ "Keep system awake": "保持系统唤醒", "Large images | God tier quality, no NSFW.": "高清图片 | 顶级质量,无 NSFW", "Large language models": "大语言模型", - "Launch": "启动", "Local Ollama model | %1": "本地 Ollama 模型 | %1", "Lock": "锁定", "Logout": "注销", @@ -76,11 +75,9 @@ "Reboot to firmware settings": "重启到固件设置", "Reload Hyprland & Quickshell": "重新加载 Hyprland 与 Quickshell", "Run": "运行", - "Run command": "运行命令", "Save": "保存", "Save to Downloads": "保存到下载文件夹", "Search": "搜索", - "Search the web": "在网络上搜索", "Search, calculate or run": "搜索、计算或运行", "Select Language": "选择语言", "Session": "会话", @@ -283,7 +280,7 @@ "Load:": "负载:", "Medium": "适中", "Tint icons": "图标着色", - "Performance Profile toggle": "性能配置切换", + "Performance Profile toggle": "性能配置切换", "**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key": "**说明**:登录 Mistral 账户,在侧边栏中选择 Keys,点击 Create new key", "Invalid arguments. Must provide `command`.": "参数无效。必须提供 `command`。", "Thought": "思考", @@ -588,7 +585,7 @@ "RAM": "内存", "Saving...": "保存中...", "Illegal increment": "超过最大增量限制", - "\nLMB to enable/disable\nRMB to toggle size\nScroll to swap position": "左键以启用/禁用\n右键以切换尺寸\n滚动以交换位置", + "\nLMB to enable/disable\nRMB to toggle size\nScroll to swap position": "\n左键以启用/禁用\n右键以切换尺寸\n滚动以交换位置", "Health:": "电池健康:", "Display modifiers and keys in multiple keycap (e.g., \"Ctrl + A\" instead of \"Ctrl A\" or \"󰘴 + A\" instead of \"󰘴 A\")": "使用多个“键帽”显示修饰键和按键(如显示为 “Ctrl + A” 而非 “Ctrl A”,或 “󰘴 + A” 而非 “󰘴 A”)", "Cookie clock settings": "曲奇时钟设置", @@ -695,5 +692,31 @@ "Adjust the color temperature": "调整色温", "Task View": "任务视图", "More comfortable viewing at night": "夜间浏览更舒适", - "No new notifications": "没有新通知" + "No new notifications": "没有新通知", + "All": "全部", + "Move to front": "移到前面", + "Emoji": "表情符号", + "Best match": "最佳匹配", + "Other": "其他", + "Unpin from Start": "从“开始”屏幕取消固定", + "Polkit": "Polkit", + "Productivity": "效率", + "Web": "网页", + "Apps": "应用", + "Manage accounts": "管理账户", + "Commands": "命令", + "Do you want to allow this app to make changes to your device?": "你要允许此应用对你的设备进行更改吗?", + "Actions": "操作", + "Open": "打开", + "Pinned": "已固定", + "Move right": "右移", + "Command": "命令", + "Utilities & Tools": "实用工具", + "Change password": "更改密码", + "Command-line-invoked Action": "由命令行执行的操作", + "Unknown Application": "未知应用", + "No applications": "没有应用", + "Creativity": "创意", + "Move left": "左移", + "Pin to Start": "固定到“开始”屏幕" } \ No newline at end of file From bb65137415b6a5d72e8de5b0776fdff061938b9e Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Tue, 9 Dec 2025 10:35:22 -0500 Subject: [PATCH 077/154] translations: update zh_CN based on feedback --- .../quickshell/ii/translations/zh_CN.json | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dots/.config/quickshell/ii/translations/zh_CN.json b/dots/.config/quickshell/ii/translations/zh_CN.json index 93be8e4b4..6796a29cc 100644 --- a/dots/.config/quickshell/ii/translations/zh_CN.json +++ b/dots/.config/quickshell/ii/translations/zh_CN.json @@ -8,8 +8,8 @@ "Su": "日/*keep*/", "%1 characters": "%1 个字符", "**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key": "**价格**:免费。数据用于训练。\n\n**说明**:登录 Google 账户,允许 AI Studio 创建 Google Cloud 项目或其他要求,然后返回并点击获取 API 密钥", - "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于您的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", - ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": "。Zerochan 注意事项:\n- 您需要指定一个颜色\n- 请在 `sidebar.booru.zerochan.username` 配置项内填写您的 Zerochan 用户名。如果不这样做[将可能会被封禁](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", + "**Pricing**: free. Data use policy varies depending on your OpenRouter account settings.\n\n**Instructions**: Log into OpenRouter account, go to Keys on the topright menu, click Create API Key": "**价格**:免费。数据使用政策取决于你的 OpenRouter 账户设置。\n\n**说明**:登录 OpenRouter 账户,在右上角菜单中选择 Keys,点击创建 API 密钥", + ". Notes for Zerochan:\n- You must enter a color\n- Set your zerochan username in `sidebar.booru.zerochan.username` config option. You [might be banned for not doing so](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!": "。Zerochan 注意事项:\n- 你需要指定一个颜色\n- 请在 `sidebar.booru.zerochan.username` 配置项内填写你的 Zerochan 用户名。如果不这样做[将可能会被封禁](https://www.zerochan.net/api#:~:text=The%20request%20may%20still%20be%20completed%20successfully%20without%20this%20custom%20header%2C%20but%20your%20project%20may%20be%20banned%20for%20being%20anonymous.)!", "No further instruction provided": "未提供进一步说明", "API key set for %1": "已为 %1 设置 API 密钥", "API key:\n\n```txt\n%1\n```": "API 密钥:\n\n```txt\n%1\n```", @@ -55,7 +55,7 @@ "Invalid arguments. Must provide `key` and `value`.": "参数无效。必须提供 `key` 和 `value`。", "Jump to current month": "跳转到当前月份", "Keep system awake": "保持系统唤醒", - "Large images | God tier quality, no NSFW.": "高清图片 | 顶级质量,无 NSFW", + "Large images | God tier quality, no NSFW.": "大尺寸图片 | 顶级质量,无 NSFW", "Large language models": "大语言模型", "Local Ollama model | %1": "本地 Ollama 模型 | %1", "Lock": "锁定", @@ -119,7 +119,7 @@ "Switched to search mode. Continue with the user's request.": "已切换到搜索模式。继续处理用户请求。", "Enter tags, or \"%1\" for commands": "输入标签,或 “%1” 以查看命令", "Online via %1 | %2's model": "在线 | 通过 %1 | %2 的模型", - "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查您的标签和 NSFW 设置\n- 如果还没想到标签,可以直接输入页码", + "That didn't work. Tips:\n- Check your tags and NSFW settings\n- If you don't have a tag in mind, type a page number": "没有找到结果。提示:\n- 检查你的标签和 NSFW 设置\n- 如果还没想到标签,可以直接输入页码", "Settings": "设置", "Save chat": "保存聊天记录", "Load chat": "加载聊天记录", @@ -192,7 +192,7 @@ "Use Levenshtein distance-based algorithm instead of fuzzy": "使用 Levenshtein 距离算法替代模糊匹配", "System prompt": "系统提示词", "12h AM/PM": "12小时制 AM/PM", - "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "如果您经常打错字可能更好用,但结果可能会奇怪,并且可能无法匹配缩写(如 “GIMP” 可能搜不到绘图程序)", + "Could be better if you make a ton of typos,\nbut results can be weird and might not work with acronyms\n(e.g. \"GIMP\" might not give you the paint program)": "如果你经常打错字可能更好用,但结果可能会奇怪,并且可能无法匹配缩写(如 “GIMP” 可能搜不到绘图程序)", "Critical warning": "临界警告", "User agent (for services that require it)": "用户代理(部分服务需要)", "Workspaces shown": "显示的工作区数", @@ -247,7 +247,7 @@ "Low battery": "电量低", "Registration failed. Please inspect manually with the warp-cli command": "注册失败。请使用 warp-cli 命令手动检查", "Code saved to file": "代码已保存到文件", - "Consider plugging in your device": "请考虑为您的设备充电", + "Consider plugging in your device": "请考虑为你的设备充电", "Weather Service": "天气服务", "Cloudflare WARP (1.1.1.1)": "Cloudflare WARP (1.1.1.1)", "Cloudflare WARP": "Cloudflare WARP", @@ -286,7 +286,7 @@ "Thought": "思考", "Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput.": "在线 | Google 的模型\n针对成本效益和高吞吐量优化的 Gemini 2.5 Flash 模型。", "Online | Google's model\nFast, can perform searches for up-to-date information": "在线 | Google 的模型\n速度快,可搜索最新信息", - "Your package manager is running": "您的包管理器正在运行", + "Your package manager is running": "你的包管理器正在运行", "Gives the model search capabilities (immediately)": "为模型提供搜索功能(即时)", "Set the tool to use for the model.": "设置模型使用的工具。", "Night Light | Right-click to toggle Auto mode": "夜间模式 | 右键切换自动模式", @@ -323,7 +323,7 @@ "Automatically hide": "自动隐藏", "**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1": "**定价**:提供免费层级,速率有限。详情见 https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**说明**:生成一个具有 Models 权限的 GitHub 个人访问令牌,并在此处将其设置为 API 密钥\n\n**注意**:使用此提供商时需要将温度参数设置为 1", "Conflicts with the shell's system tray implementation": "与 Shell 的系统托盘实现冲突", - "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "祝您愉快!可以随时按 Super+Shift+Alt+/ 重新打开欢迎应用。要打开设置应用,请按 Super+I", + "Enjoy! You can reopen the welcome app any time with Super+Shift+Alt+/. To open the settings app, hit Super+I": "祝你愉快!可以随时按 Super+Shift+Alt+/ 重新打开欢迎应用。要打开设置应用,请按 Super+I", "Reset": "重置", "☕ Break: %1 minutes": "☕ 休息:%1 分钟", "Pomodoro": "番茄钟", @@ -368,7 +368,7 @@ "No active player": "无活动播放器", "Search wallpapers": "搜索壁纸", "Rect": "矩形", - "Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "请确保您的播放器支持 MPRIS\n或尝试关闭重复播放器过滤", + "Make sure your player has MPRIS support\nor try turning off duplicate player filtering": "请确保你的播放器支持 MPRIS\n或尝试关闭重复播放器过滤", "Shell command": "Shell 命令", "Screen round corner": "屏幕圆角", "Password": "密码", @@ -391,7 +391,7 @@ "Forget": "忘记", "Background": "背景", "Top": "顶部", - "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "并非所有选项都在此应用中提供。您还应点击左上角的“配置文件”按钮,或手动打开 %1 以查看配置文件。", + "Not all options are available in this app. You should also check the config file by hitting the \"Config file\" button on the topleft corner or opening %1 manually.": "并非所有选项都在此应用中提供。你还应点击左上角的“配置文件”按钮,或手动打开 %1 以查看配置文件。", "General": "通用", "Right": "右侧", "Utility buttons": "工具按钮", @@ -446,7 +446,7 @@ "Wallpaper safety enforced": "已启用壁纸安全模式", "Hour hand": "时针", "Automatic": "自动", - "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "想要的语言不在列表内或翻译不完整?\n您可以选择使用 Gemini 为其生成翻译。\n1. 按 Super+A 打开左侧边栏,将模型设置为 Gemini(如果不已经是了的话)\n2. 输入 /key,按 Enter 然后跟随说明\n3. 输入 /key YOUR_API_KEY\n4. 在下方输入您的语言代码并按下生成", + "Language not listed or incomplete translations?\nYou can choose to generate translations for it with Gemini.\n1. Open the left sidebar with Super+A, set model to Gemini (if it isn't already)\n2. Type /key, hit Enter and follow the instructions\n3. Type /key YOUR_API_KEY\n4. Type the locale of your language below and press Generate": "想要的语言不在列表内或翻译不完整?\n你可以选择使用 Gemini 为其生成翻译。\n1. 按 Super+A 打开左侧边栏,将模型设置为 Gemini(如果不已经是了的话)\n2. 输入 /key,按 Enter 然后跟随说明\n3. 输入 /key YOUR_API_KEY\n4. 在下方输入你的语言代码并按下生成", "Auto styling with Gemini": "使用 Gemini 自动决定样式", "Audio output | Right-click for volume mixer & device selector": "音频输出 | 右键打开音量合成器与设备选择器", "Most busy": "最繁杂处", @@ -492,7 +492,7 @@ "Virtual Keyboard": "屏幕键盘", "Shut down": "关机", "Digits in the middle": "在中心显示数字", - "If you want to somehow use fingerprint unlock...": "如果您想想办法用指纹解锁的话...", + "If you want to somehow use fingerprint unlock...": "如果你想想办法用指纹解锁的话...", "Couldn't recognize music": "未识别到歌曲", "Split buttons": "拆分按键", "Number style": "数字样式", @@ -513,7 +513,7 @@ "Close window": "关闭窗口", "Video Recording Path": "视频录制路径", "Speakers (%1): %2": "扬声器 (%1): %2", - "Perhaps what you're listening to is too niche": "也许您听的音乐太小众了", + "Perhaps what you're listening to is too niche": "也许你听的音乐太小众了", "Animate time change": "时间变化动画", "Set FPS limit": "设置帧数限制", "Identify Music": "识别音乐", @@ -538,7 +538,7 @@ "Center icons": "图标居中", "Generate\nTypically takes 2 minutes": "生成\n通常需要 2 分钟", "Overlay: Crosshair": "叠加面板:准星", - "You can also manually edit cheatsheet.superKey": "您也可以手动编辑 cheatsheet.superKey 配置项", + "You can also manually edit cheatsheet.superKey": "你也可以手动编辑 cheatsheet.superKey 配置项", "Layers": "显示层", "Sign out": "注销", "Android": "安卓", @@ -548,7 +548,7 @@ "of %1": "共 %1", "Clock style (locked)": "时钟样式(锁屏时)", "City name": "城市名", - "Make sure you have songrec installed": "请确保您已安装 songrec", + "Make sure you have songrec installed": "请确保你已安装 songrec", "Windows": "窗口", "Power Profile": "电源模式", "Used for code and terminal": "用于代码和终端", @@ -572,7 +572,7 @@ "Region selector (screen snipping/Google Lens)": "区域选择器(屏幕截图与 Google 智能镜头)", "Battery: %1%2": "电池状态:%1%2", "Click to cycle through power profiles": "点击以循环切换电源模式", - "When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll": "当上一个选项为关闭且此项开启时,您仍然\n可以通过悬停在角落的末端来打开侧边栏,\n剩余的区域将可用于滚动调节音量与亮度", + "When the previous option is off and this is on,\nyou can still hover the corner's end to open sidebar,\nand the remaining area can be used for volume/brightness scroll": "当上一个选项为关闭且此项开启时,你仍然\n可以通过悬停在角落的末端来打开侧边栏,\n剩余的区域将可用于滚动调节音量与亮度", "Widgets": "小组件", "On-screen keyboard": "屏幕键盘", "Used for general UI text": "用于通用 UI 文本", @@ -580,7 +580,7 @@ "Replace 󱕐 for \"Scroll ↓\", 󱕑 \"Scroll ↑\", L󰍽 \"LMB\", R󰍽 \"RMB\", 󱕒 \"Scroll ↑/↓\" and ⇞/⇟ for \"Page_↑/↓\"": "如用 󱕐 来表示 “Scroll ↓”,󱕑 “Scroll ↑”,L󰍽 “LMB”,R󰍽 “RMB”,以及 󱕒 “Scroll ↑/↓” 和 ⇞/⇟ 来表示 “Page_↑/↓”", "Unmuted": "已打开", "Path copied": "路径已复制", - "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "使用 Gemini 对壁纸进行分类,然后根据分类选择一个预设。\n您需要先在左侧边栏设置 Gemini API 密钥。\n图片会被降低分辨率以提高性能, 但为了安全起见,\n请勿选择包含敏感信息的壁纸。", + "Uses Gemini to categorize the wallpaper then picks a preset based on it.\nYou'll need to set Gemini API key on the left sidebar first.\nImages are downscaled for performance, but just to be safe,\ndo not select wallpapers with sensitive information.": "使用 Gemini 对壁纸进行分类,然后根据分类选择一个预设。\n你需要先在左侧边栏设置 Gemini API 密钥。\n图片会被降低分辨率以提高性能, 但为了安全起见,\n请勿选择包含敏感信息的壁纸。", "Unread indicator: show count": "未读指示器:显示数量", "RAM": "内存", "Saving...": "保存中...", @@ -677,7 +677,7 @@ "Authentication": "身份验证", "Hint target regions": "建议目标区域", "Enable now": "现在启用", - "You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.": "您需要先输入 Gemini API 密钥。\n在侧边栏输入 /key 以获取说明。", + "You'll need to enter your Gemini API key first.\nType /key on the sidebar for instructions.": "你需要先输入 Gemini API 密钥。\n在侧边栏输入 /key 以获取说明。", "Output device": "输出设备", "Swap": "虚拟内存", "Full warning": "满电警告", From 3cb61c4267205b420e65edefd019a839853d2afd Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Tue, 9 Dec 2025 10:50:27 -0500 Subject: [PATCH 078/154] translations: update zh_CN based on feedback 2 --- dots/.config/quickshell/ii/translations/zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/translations/zh_CN.json b/dots/.config/quickshell/ii/translations/zh_CN.json index 6796a29cc..a30b03dc7 100644 --- a/dots/.config/quickshell/ii/translations/zh_CN.json +++ b/dots/.config/quickshell/ii/translations/zh_CN.json @@ -106,7 +106,7 @@ "Unknown function call: %1": "未知函数调用:%1", "View Markdown source": "查看 Markdown 源码", "Volume": "音量", - "Volume mixer": "音量合成器", + "Volume mixer": "音量混合器", "Waifus only | Excellent quality, limited quantity": "仅限角色 | 优秀质量,数量有限", "Workspace": "工作区", "%1 Safe Storage": "%1 安全存储", From fdbe39d74474a9c5e438cbaf86b9e2fecacdd40c Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:05:48 +0100 Subject: [PATCH 079/154] waffles: add lock screen, fix ctrl alt del screen for light mode --- .../ii/assets/icons/fluent/eye-filled.svg | 4 + .../ii/assets/icons/fluent/eye-off-filled.svg | 4 + .../ii/assets/icons/fluent/eye-off.svg | 4 + .../quickshell/ii/assets/icons/fluent/eye.svg | 4 + .../ii/modules/ii/polkit/PolkitContent.qml | 2 +- .../ii/modules/waffle/lock/WaffleLock.qml | 324 ++++++++++++++++++ .../ii/modules/waffle/looks/Looks.qml | 5 + .../ii/modules/waffle/looks/WMenu.qml | 5 +- .../ii/modules/waffle/looks/WText.qml | 1 + .../ii/modules/waffle/looks/WTextField.qml | 4 + .../waffle/sessionScreen/PowerButton.qml | 76 ++++ .../sessionScreen/SessionScreenContent.qml | 60 +--- .../WSessionScreenTextButton.qml | 3 +- dots/.config/quickshell/ii/shell.qml | 4 +- 14 files changed, 441 insertions(+), 59 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/eye-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/eye-off-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/eye-off.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/eye.svg create mode 100644 dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/sessionScreen/PowerButton.qml diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/eye-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/eye-filled.svg new file mode 100644 index 000000000..fe959b74c --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/eye-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/eye-off-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/eye-off-filled.svg new file mode 100644 index 000000000..d3c53b85e --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/eye-off-filled.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/eye-off.svg b/dots/.config/quickshell/ii/assets/icons/fluent/eye-off.svg new file mode 100644 index 000000000..b409256f5 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/eye-off.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/eye.svg b/dots/.config/quickshell/ii/assets/icons/fluent/eye.svg new file mode 100644 index 000000000..55be66814 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/eye.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml b/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml index a78c9ce93..7dfd045db 100644 --- a/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml +++ b/dots/.config/quickshell/ii/modules/ii/polkit/PolkitContent.qml @@ -86,7 +86,7 @@ Item { } WindowDialogButtonRow { - + Layout.bottomMargin: 10 // I honestly don't know why this is necessary Item { Layout.fillWidth: true } diff --git a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml new file mode 100644 index 000000000..1b4f1d0fd --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml @@ -0,0 +1,324 @@ +pragma ComponentBehavior: Bound +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.common.panels.lock +import qs.modules.waffle.looks +import qs.modules.waffle.sessionScreen as SessionScreen + +LockScreen { + id: root + + property bool passwordView: false + + lockSurface: Item { + id: lockSurfaceItem + + Component.onCompleted: { + root.passwordView = false; + lockSurfaceItem.forceActiveFocus(); + } + + Keys.onPressed: { + root.passwordView = true; + } + + Image { + id: bg + z: 0 + anchors.fill: parent + sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height) + source: Config.options.background.wallpaperPath + fillMode: Image.PreserveAspectCrop + } + + GaussianBlur { + z: 1 + anchors.fill: parent + source: bg + radius: 100 + samples: radius * 2 + 1 + scale: root.passwordView ? 1.1 : 1 + opacity: root.passwordView ? 1 : 0 + + Behavior on opacity { + animation: Looks.transition.opacity.createObject(this) + } + + Behavior on scale { + NumberAnimation { + duration: 400 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + } + } + + Interactables { + z: 2 + anchors.fill: parent + } + } + + component Interactables: Rectangle { + id: interactables + color: ColorUtils.transparentize("#000000", 0.8) + // Button { + // onClicked: { + // root.context.unlocked(LockContext.ActionEnum.Unlock); + // GlobalStates.screenLocked = false; + // } + // text: "woah it doesnt work let me out pls uwu colon three" + // } + + ClockTextGroup { + visible: !root.passwordView + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: interactables.height * 0.1 + } + } + + PasswordGroup { + visible: root.passwordView + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } + } + + RowLayout { + visible: !root.passwordView + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: 21 + rightMargin: 31 + } + IconIndicator { + baseIcon: "wifi-1" + icon: WIcons.internetIcon + } + IconIndicator { + baseIcon: WIcons.batteryIcon + icon: WIcons.batteryLevelIcon + } + } + + RowLayout { + visible: root.passwordView + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: 21 + rightMargin: 31 + } + SessionScreen.PowerButton { + id: powerButton + } + } + } + + component IconIndicator: Item { + id: iconIndicator + required property string baseIcon + required property string icon + default property alias data: iconWidget.data + implicitWidth: 40 + implicitHeight: 40 + FluentIcon { + id: iconWidget + anchors.centerIn: parent + icon: iconIndicator.baseIcon + color: Looks.darkColors.inactiveIcon + implicitSize: 20 + FluentIcon { + anchors.fill: parent + icon: iconIndicator.icon + } + } + } + + component ClockTextGroup: Column { + id: clockTextGroup + spacing: -3 + + WText { + anchors.horizontalCenter: parent.horizontalCenter + color: Looks.darkColors.fg + font.pixelSize: 133 + font.weight: Looks.font.weight.strong + text: { + // Don't take am/pm + // Match groups of digits separated by non-digit chars (e.g., "12:34", "12.34", "12-34") + let match = DateTime.time.match(/(\d{1,2})\D+(\d{2})/); + return match ? `${match[1]}${DateTime.time.match(/\D+/)[0]}${match[2]}` : DateTime.time; + } + } + + WText { + id: dateLabel + color: Looks.darkColors.fg + anchors.horizontalCenter: parent.horizontalCenter + font.pixelSize: 28 + font.weight: Looks.font.weight.strong + text: DateTime.collapsedCalendarFormat + } + } + + component PasswordGroup: ColumnLayout { + id: passwordGroup + spacing: 15 + + WUserAvatar { + Layout.alignment: Qt.AlignHCenter + sourceSize: Qt.size(192, 192) + } + + WText { + Layout.alignment: Qt.AlignHCenter + text: SystemInfo.username + color: Looks.darkColors.fg + font.pixelSize: 26 + font.weight: Looks.font.weight.strong + } + + Rectangle { + id: passwordInputWrapper + Layout.topMargin: 10 + Layout.alignment: Qt.AlignHCenter + Layout.bottomMargin: 132 + color: "transparent" + implicitWidth: 296 + implicitHeight: 36 + border.width: 2 + border.color: Looks.applyContentTransparency(Looks.darkColors.bg1Border) + radius: Looks.radius.medium + + Rectangle { + id: passwordInputBackground + anchors.fill: parent + anchors.margins: 2 + radius: Looks.radius.small + 1 + color: passwordInput.focus ? Looks.applyBackgroundTransparency(Looks.darkColors.bg1Base) : Looks.applyContentTransparency(Looks.darkColors.bg1) + + RowLayout { + anchors.fill: parent + anchors.margins: 6 + spacing: 3 + + WTextInput { + id: passwordInput + Layout.fillHeight: true + Layout.fillWidth: true + verticalAlignment: TextInput.AlignVCenter + inputMethodHints: Qt.ImhSensitiveData + echoMode: passwordVisibilityButton.pressed ? TextInput.Normal : TextInput.Password + color: Looks.darkColors.fg + + font.pixelSize: 12 + WText { + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + visible: passwordInput.text.length === 0 + text: Translation.tr("Password") + font.pixelSize: Looks.font.pixelSize.large + color: Looks.darkColors.fg + opacity: 0.8 + } + + onTextChanged: root.context.currentText = this.text + onAccepted: { + root.context.tryUnlock(); + } + Connections { + target: root.context + function onCurrentTextChanged() { + passwordInput.text = root.context.currentText; + } + } + Connections { + target: root + function onPasswordViewChanged() { + passwordInput.forceActiveFocus(); + } + } + + Keys.onPressed: event => { + root.context.resetClearTimer(); + } + } + + PasswordBoxButton { + id: passwordVisibilityButton + property bool passwordVisible: false + onPressed: passwordVisible = true + onReleased: passwordVisible = false + icon.name: passwordVisible ? "eye-off" : "eye" + } + + PasswordBoxButton { + onClicked: { + root.context.tryUnlock(); + } + icon.name: "arrow-right" + } + } + } + Rectangle { + id: activeIndicatorLine + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + implicitHeight: 2 + color: passwordInput.focus ? Looks.colors.accent : Looks.applyContentTransparency(Looks.darkColors.bg2Border) + } + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: passwordInputWrapper.width + height: passwordInputWrapper.height + radius: passwordInputWrapper.radius + } + } + } + + Item {} + } + + component PasswordBoxButton: WButton { + id: pwBoxBtn + implicitWidth: 28 + implicitHeight: 22 + + property color colBackground: ColorUtils.transparentize(Looks.darkColors.bg1) + property color colBackgroundHover: ColorUtils.transparentize(Looks.darkColors.bg2Hover) + property color colBackgroundActive: ColorUtils.transparentize(Looks.darkColors.bg2Active) + fgColor: Looks.darkColors.fg + + checked: hovered + + contentItem: Item { + FluentIcon { + color: pwBoxBtn.fgColor + anchors.centerIn: parent + icon: pwBoxBtn.icon.name + implicitSize: 16 + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index b0d8b91f8..51e7fe40c 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -155,6 +155,11 @@ Singleton { property real larger: 15 property real xlarger: 17 } + property QtObject variableAxes: QtObject { + property var ui: ({ + "wdth": 25 + }) + } } transition: QtObject { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml index 3a42e666d..9f4f7f340 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMenu.qml @@ -14,6 +14,9 @@ Menu { property bool downDirection: false property bool hasIcons: false // TODO: implement + property color color: Looks.colors.bg1Base + property alias backgroundPane: bgPane + implicitWidth: background.implicitWidth + margins * 2 implicitHeight: background.implicitHeight + margins * 2 margins: 10 @@ -58,7 +61,7 @@ Menu { bottomMargin: root.downDirection ? root.margins : root.sourceEdgeMargin } contentItem: Rectangle { - color: Looks.colors.bg1Base + color: root.color implicitWidth: menuListView.implicitWidth + root.padding * 2 implicitHeight: root.contentItem.implicitHeight + root.padding * 2 } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml index b9bc558d7..ab6dd60b7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WText.qml @@ -12,6 +12,7 @@ Text { family: Looks.font.family.ui pixelSize: Looks.font.pixelSize.normal weight: Looks.font.weight.regular + variableAxes: Looks.font.variableAxes.ui } linkColor: Looks.colors.link diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml index a666cec50..a3cc9dbc0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WTextField.qml @@ -11,6 +11,10 @@ TextField { verticalAlignment: Text.AlignVCenter color: Looks.colors.fg + palette { + active: Looks.colors.accent + } + font { hintingPreference: Font.PreferDefaultHinting family: Looks.font.family.ui diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/PowerButton.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/PowerButton.qml new file mode 100644 index 000000000..d3bd07ad3 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/PowerButton.qml @@ -0,0 +1,76 @@ +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io + +WSessionScreenTextButton { + id: root + implicitWidth: 40 + implicitHeight: 40 + focusRingRadius: Looks.radius.large + colBackground: ColorUtils.transparentize(Looks.darkColors.bg2) + colBackgroundHover: Looks.applyContentTransparency(Looks.darkColors.bg2Hover) + colBackgroundActive: Looks.applyContentTransparency(Looks.darkColors.bg2Active) + property color color: { + if (root.down) { + return root.colBackgroundActive; + } else if (root.hovered) { + return root.colBackgroundHover; + } else { + return root.colBackground; + } + } + background: Rectangle { + id: background + radius: Looks.radius.medium + color: root.color + } + contentItem: Item { + FluentIcon { + anchors.centerIn: parent + implicitSize: 20 + icon: "power" + color: root.fgColor + } + } + + onClicked: { + powerMenu.visible = !powerMenu.visible; + } + + WMenu { + id: powerMenu + x: -powerMenu.implicitWidth / 2 + root.implicitWidth / 2 + y: -powerMenu.implicitHeight + + color: Looks.darkColors.bg1Base + Component.onCompleted: { + powerMenu.backgroundPane.borderColor = Looks.applyContentTransparency(Looks.darkColors.bg2Border); + } + delegate: WMenuItem { + id: menuItemDelegate + colBackground: ColorUtils.transparentize(Looks.darkColors.bg1Base) + colBackgroundHover: Looks.applyContentTransparency(Looks.darkColors.bg2Hover) + colBackgroundActive: Looks.applyContentTransparency(Looks.darkColors.bg2Active) + colForeground: Looks.darkColors.fg + } + + Action { + icon.name: "power" + text: Translation.tr("Shut down") + onTriggered: Session.poweroff() + } + Action { + icon.name: "arrow-counterclockwise" + text: Translation.tr("Restart") + onTriggered: Session.reboot() + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml index ddadb373a..6a9d0ef31 100644 --- a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/SessionScreenContent.qml @@ -8,7 +8,6 @@ import QtQuick import QtQuick.Controls import QtQuick.Layouts import Quickshell -import Quickshell.Io Item { id: root @@ -94,63 +93,13 @@ Item { } } - component PowerButton: WSessionScreenTextButton { - id: root - implicitWidth: 40 - implicitHeight: 40 - focusRingRadius: Looks.radius.large - colBackgroundHover: Looks.colors.bg2Hover - colBackgroundActive: Looks.colors.bg2Active - property color color: { - if (root.down) { - return root.colBackgroundActive; - } else if (root.hovered) { - return root.colBackgroundHover; - } else { - return root.colBackground; - } - } - background: Rectangle { - id: background - radius: Looks.radius.medium - color: root.color - } - contentItem: Item { - FluentIcon { - anchors.centerIn: parent - implicitSize: 20 - icon: "power" - } - } - - onClicked: { - powerMenu.visible = !powerMenu.visible; - } - - WMenu { - id: powerMenu - x: -powerMenu.implicitWidth / 2 + root.implicitWidth / 2 - y: -powerMenu.implicitHeight - - Action { - icon.name: "power" - text: Translation.tr("Shut down") - onTriggered: Session.poweroff() - } - Action { - icon.name: "arrow-counterclockwise" - text: Translation.tr("Restart") - onTriggered: Session.reboot() - } - } - } - component CancelButton: WBorderlessButton { id: root implicitHeight: 32 - colBackground: Looks.colors.bg1Base - colBackgroundHover: Qt.lighter(Looks.colors.bg1Base, 1.2) - colBackgroundActive: Qt.lighter(Looks.colors.bg1Base, 1.1) + colBackground: Looks.darkColors.bg1Base + colBackgroundHover: Qt.lighter(Looks.darkColors.bg1Base, 1.2) + colBackgroundActive: Qt.lighter(Looks.darkColors.bg1Base, 1.1) + colForeground: Looks.darkColors.fg property bool keyboardDown: false @@ -172,6 +121,7 @@ Item { text: Translation.tr("Cancel") horizontalAlignment: Text.AlignHCenter font.pixelSize: Looks.font.pixelSize.large + color: root.colForeground } Rectangle { diff --git a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml index ec8b58487..875ca03b4 100644 --- a/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml +++ b/dots/.config/quickshell/ii/modules/waffle/sessionScreen/WSessionScreenTextButton.qml @@ -12,6 +12,7 @@ WTextButton { property bool keyboardDown: false property alias focusRingRadius: focusRing.radius + fgColor: (root.pressed || root.keyboardDown) ? Looks.darkColors.fg1 : Looks.darkColors.fg Keys.onPressed: event => { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { @@ -34,7 +35,7 @@ WTextButton { WText { id: buttonText anchors.fill: parent - color: (root.pressed || root.keyboardDown) ? Looks.colors.fg1 : Looks.colors.fg + color: root.fgColor text: root.text font.pixelSize: Looks.font.pixelSize.large } diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index 6ed6480a6..b8fa41c3f 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -30,6 +30,7 @@ import qs.modules.ii.wallpaperSelector import qs.modules.waffle.actionCenter import qs.modules.waffle.background import qs.modules.waffle.bar +import qs.modules.waffle.lock import qs.modules.waffle.notificationCenter import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.polkit @@ -83,6 +84,7 @@ ShellRoot { PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } PanelLoader { identifier: "wBar"; component: WaffleBar {} } PanelLoader { identifier: "wBackground"; component: WaffleBackground {} } + PanelLoader { identifier: "wLock"; component: WaffleLock {} } PanelLoader { identifier: "wNotificationCenter"; component: WaffleNotificationCenter {} } PanelLoader { identifier: "wOnScreenDisplay"; component: WaffleOSD {} } PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } @@ -100,7 +102,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", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiLock", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From d9b1d0261d192dace0a83db05f98e111ab3fee02 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:26:43 +0100 Subject: [PATCH 080/154] waffles: fix lock colors in some places for light mode --- .../ii/modules/waffle/lock/WaffleLock.qml | 100 +++++++++++------- 1 file changed, 60 insertions(+), 40 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml index 1b4f1d0fd..581ce9152 100644 --- a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml +++ b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml @@ -30,7 +30,7 @@ LockScreen { } Keys.onPressed: { - root.passwordView = true; + interactables.switchToFocusedView(); } Image { @@ -65,13 +65,14 @@ LockScreen { } Interactables { + id: interactables z: 2 anchors.fill: parent } } component Interactables: Rectangle { - id: interactables + id: interactablesComponent color: ColorUtils.transparentize("#000000", 0.8) // Button { // onClicked: { @@ -81,51 +82,63 @@ LockScreen { // text: "woah it doesnt work let me out pls uwu colon three" // } - ClockTextGroup { + function switchToFocusedView() { + root.passwordView = true; + } + + Item { + id: unfocusedContent + anchors.fill: parent visible: !root.passwordView - anchors { - horizontalCenter: parent.horizontalCenter - top: parent.top - topMargin: interactables.height * 0.1 + ClockTextGroup { + anchors { + horizontalCenter: parent.horizontalCenter + top: parent.top + topMargin: interactablesComponent.height * 0.1 + } + } + RowLayout { + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: 21 + rightMargin: 31 + } + IconIndicator { + baseIcon: "wifi-1" + icon: WIcons.internetIcon + } + IconIndicator { + baseIcon: WIcons.batteryIcon + icon: WIcons.batteryLevelIcon + } } } - PasswordGroup { + Item { + id: focusedContent + anchors.fill: parent visible: root.passwordView - anchors { - horizontalCenter: parent.horizontalCenter - verticalCenter: parent.verticalCenter - } - } - RowLayout { - visible: !root.passwordView - anchors { - bottom: parent.bottom - right: parent.right - bottomMargin: 21 - rightMargin: 31 + PasswordGroup { + visible: root.passwordView + anchors { + horizontalCenter: parent.horizontalCenter + verticalCenter: parent.verticalCenter + } } - IconIndicator { - baseIcon: "wifi-1" - icon: WIcons.internetIcon - } - IconIndicator { - baseIcon: WIcons.batteryIcon - icon: WIcons.batteryLevelIcon - } - } - RowLayout { - visible: root.passwordView - anchors { - bottom: parent.bottom - right: parent.right - bottomMargin: 21 - rightMargin: 31 - } - SessionScreen.PowerButton { - id: powerButton + RowLayout { + visible: root.passwordView + anchors { + bottom: parent.bottom + right: parent.right + bottomMargin: 21 + rightMargin: 31 + } + SessionScreen.PowerButton { + id: powerButton + } } } } @@ -258,11 +271,18 @@ LockScreen { Keys.onPressed: event => { root.context.resetClearTimer(); } + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.NoButton + cursorShape: Qt.IBeamCursor + } } PasswordBoxButton { id: passwordVisibilityButton property bool passwordVisible: false + visible: passwordInput.text.length > 0 onPressed: passwordVisible = true onReleased: passwordVisible = false icon.name: passwordVisible ? "eye-off" : "eye" @@ -308,7 +328,7 @@ LockScreen { property color colBackground: ColorUtils.transparentize(Looks.darkColors.bg1) property color colBackgroundHover: ColorUtils.transparentize(Looks.darkColors.bg2Hover) property color colBackgroundActive: ColorUtils.transparentize(Looks.darkColors.bg2Active) - fgColor: Looks.darkColors.fg + fgColor: checked ? Looks.colors.accentFg : Looks.darkColors.fg checked: hovered From 0cc521aef558ff409155bd6c4faa8a4423862e2b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:27:23 +0100 Subject: [PATCH 081/154] option for satty instead of swappy (#2585) --- dots/.config/quickshell/ii/modules/common/Config.qml | 3 +++ .../ii/modules/ii/regionSelector/RegionSelection.qml | 3 ++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 859e336e3..ac4dfc96b 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -437,6 +437,9 @@ Singleton { property int strokeWidth: 6 property int padding: 10 } + property JsonObject annotation: JsonObject { + property bool useSatty: false + } } property JsonObject resources: JsonObject { diff --git a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml index 8f484591b..59dc186e7 100644 --- a/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml +++ b/dots/.config/quickshell/ii/modules/ii/regionSelector/RegionSelection.qml @@ -261,6 +261,7 @@ PanelWindow { 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 === "") { @@ -282,7 +283,7 @@ PanelWindow { break; case RegionSelection.SnipAction.Edit: - snipProc.command = ["bash", "-c", `${cropToStdout} | swappy -f - && ${cleanup}`] + 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}`] From 1a3cc9b4d33445096d62a4d831e11882d05b0681 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 9 Dec 2025 23:50:28 +0100 Subject: [PATCH 082/154] bar: make battery indicator more aligned --- .../quickshell/ii/modules/ii/bar/BatteryIndicator.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml b/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml index 36799fefd..176f3a065 100644 --- a/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml +++ b/dots/.config/quickshell/ii/modules/ii/bar/BatteryIndicator.qml @@ -30,7 +30,11 @@ MouseArea { height: batteryProgress.valueBarHeight RowLayout { - anchors.centerIn: parent + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + bottomMargin: (parent.height - height) / 2 + } spacing: 0 MaterialSymbol { From bcd7fb1c1bbd5af82f5e4196078e8c1bfd1ee193 Mon Sep 17 00:00:00 2001 From: Fengzi <41763546+F-fengzi@users.noreply.github.com> Date: Tue, 9 Dec 2025 19:30:46 -0500 Subject: [PATCH 083/154] translations: missed zh_CN string This is one of the cases where the translation is not accurate, but I'll use the exact translation in Windows for strings that only appear in waffles --- dots/.config/quickshell/ii/translations/zh_CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/translations/zh_CN.json b/dots/.config/quickshell/ii/translations/zh_CN.json index a30b03dc7..3b4c17d69 100644 --- a/dots/.config/quickshell/ii/translations/zh_CN.json +++ b/dots/.config/quickshell/ii/translations/zh_CN.json @@ -517,7 +517,7 @@ "Animate time change": "时间变化动画", "Set FPS limit": "设置帧数限制", "Identify Music": "识别音乐", - "Focusing": "Focusing", + "Focusing": "专注", "Sound output": "声音输出", "Type /key to get started with online models\nCtrl+O to expand sidebar\nCtrl+P to pin sidebar\nCtrl+D to detach sidebar": "输入 /key 来开始使用在线模型\nCtrl+O 拓宽侧边栏\nCtrl+P 固定侧边栏\nCtrl+D 分离侧边栏", "Parallax": "视差效果", From b966e2d53930b17712d23152548257a347f35d3e Mon Sep 17 00:00:00 2001 From: stewart86 Date: Wed, 10 Dec 2025 18:10:15 +0800 Subject: [PATCH 084/154] feat: add support for custom user action scripts Add ability to define custom actions by placing executable scripts in ~/.config/illogical-impulse/actions/ - Script filename becomes the action name (extension stripped) - Use /script-name in search bar to execute - Arguments are passed to the script - Uses FolderListModel for auto-reload when scripts are added/removed - Follows existing pattern of user-customizable directories (ai/prompts) --- .../ii/modules/common/Directories.qml | 2 ++ .../quickshell/ii/services/LauncherSearch.qml | 34 ++++++++++++++++++- 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Directories.qml b/dots/.config/quickshell/ii/modules/common/Directories.qml index 9afbed44b..78b0a9edb 100644 --- a/dots/.config/quickshell/ii/modules/common/Directories.qml +++ b/dots/.config/quickshell/ii/modules/common/Directories.qml @@ -44,6 +44,7 @@ Singleton { property string wallpaperSwitchScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/colors/switchwall.sh`) property string defaultAiPrompts: Quickshell.shellPath("defaults/ai/prompts") property string userAiPrompts: FileUtils.trimFileProtocol(`${Directories.shellConfig}/ai/prompts`) + property string userActions: FileUtils.trimFileProtocol(`${Directories.shellConfig}/actions`) property string aiChats: FileUtils.trimFileProtocol(`${Directories.state}/user/ai/chats`) property string aiTranslationScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/ai/gemini-translate.sh`) property string recordScriptPath: FileUtils.trimFileProtocol(`${Directories.scriptPath}/videos/record.sh`) @@ -59,6 +60,7 @@ Singleton { Quickshell.execDetached(["bash", "-c", `rm -rf '${latexOutput}'; mkdir -p '${latexOutput}'`]) Quickshell.execDetached(["bash", "-c", `rm -rf '${cliphistDecode}'; mkdir -p '${cliphistDecode}'`]) Quickshell.execDetached(["mkdir", "-p", `${aiChats}`]) + Quickshell.execDetached(["mkdir", "-p", `${userActions}`]) Quickshell.execDetached(["rm", "-rf", `${tempImages}`]) } } diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index f5a18bec9..d8f7af59a 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -4,6 +4,7 @@ import qs.modules.common import qs.modules.common.models import qs.modules.common.functions import QtQuick +import Qt.labs.folderlistmodel import Quickshell import Quickshell.Io @@ -31,6 +32,34 @@ Singleton { return acc; }, []).sort() + // Load user action scripts from ~/.config/illogical-impulse/actions/ + // Uses FolderListModel to auto-reload when scripts are added/removed + property var userActionScripts: { + const actions = []; + for (let i = 0; i < userActionsFolder.count; i++) { + const fileName = userActionsFolder.get(i, "fileName"); + const filePath = userActionsFolder.get(i, "filePath"); + if (fileName && filePath) { + const actionName = fileName.replace(/\.[^/.]+$/, ""); // strip extension + actions.push({ + action: actionName, + execute: ((path) => (args) => { + Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]); + })(filePath.toString().replace("file://", "")) + }); + } + } + return actions; + } + + FolderListModel { + id: userActionsFolder + folder: `file://${Directories.userActions}` + showDirs: false + showHidden: false + sortField: FolderListModel.Name + } + property var searchActions: [ { action: "accentcolor", @@ -90,6 +119,9 @@ Singleton { }, ] + // Combined built-in and user actions + property var allActions: searchActions.concat(userActionScripts) + property string mathResult: "" property bool clipboardWorkSafetyActive: { const enabled = Config.options.workSafety.enable.clipboard; @@ -273,7 +305,7 @@ Singleton { Qt.openUrlExternally(url); } }); - const launcherActionObjects = root.searchActions.map(action => { + const launcherActionObjects = root.allActions.map(action => { const actionString = `${Config.options.search.prefix.action}${action.action}`; if (actionString.startsWith(root.query) || root.query.startsWith(actionString)) { return resultComp.createObject(null, { From 70c0adb8e5f94054cdb6e6b3d1d802918d981018 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 10 Dec 2025 22:38:07 +0100 Subject: [PATCH 085/154] waffles: make calendar spacing more accurate --- .../quickshell/ii/modules/common/widgets/CalendarView.qml | 7 ++++--- .../modules/waffle/notificationCenter/CalendarWidget.qml | 5 +++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml index 648da466a..0e1bd13fc 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/CalendarView.qml @@ -87,7 +87,8 @@ Item { property real verticalPadding: 0 property real buttonSize: 40 property real buttonSpacing: 2 - implicitHeight: (6 * buttonSize) + (5 * buttonSpacing) + (2 * verticalPadding) + property real buttonVerticalSpacing: buttonSpacing + implicitHeight: (6 * buttonSize) + (5 * buttonVerticalSpacing) + (2 * verticalPadding) implicitWidth: weeksColumn.implicitWidth clip: true @@ -98,13 +99,13 @@ Item { right: parent.right } y: { - const spacePerExtraRow = root.buttonSize + root.buttonSpacing; + const spacePerExtraRow = root.buttonSize + root.buttonVerticalSpacing; const origin = -(spacePerExtraRow * root.paddingWeeks); const diff = root.weekDiff * spacePerExtraRow; return origin + (-diff % spacePerExtraRow) + root.verticalPadding; } - spacing: root.buttonSpacing + spacing: root.buttonVerticalSpacing Repeater { model: root.totalWeeks diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml index 0ac48d13b..7c01a0b01 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/CalendarWidget.qml @@ -67,7 +67,8 @@ BodyRectangle { locale: root.locale verticalPadding: 2 buttonSize: 41 // ??? - buttonSpacing: 1 + buttonSpacing: 6 + buttonVerticalSpacing: 1 Layout.fillWidth: true delegate: DayButton {} } @@ -90,7 +91,7 @@ BodyRectangle { anchors.centerIn: parent text: dayButton.model.day color: dayButton.fgColor - font.pixelSize: Looks.font.pixelSize.large + font.pixelSize: Looks.font.pixelSize.larger } } } From e874a6a3e0ee55fc34353113bfe7af90300c5f96 Mon Sep 17 00:00:00 2001 From: reakjra Date: Thu, 11 Dec 2025 22:00:03 +0100 Subject: [PATCH 086/154] proper error handling --- .../quickshell/ii/services/ai/GeminiApiStrategy.qml | 8 ++++++++ .../quickshell/ii/services/ai/MistralApiStrategy.qml | 11 ++++++++++- .../quickshell/ii/services/ai/OpenAiApiStrategy.qml | 11 ++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml index b610c3d44..9f04f6364 100644 --- a/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml +++ b/dots/.config/quickshell/ii/services/ai/GeminiApiStrategy.qml @@ -111,6 +111,14 @@ ApiStrategy { return ({}) } + // Error response handling + if (dataJson.error) { + const errorMsg = `**Error ${dataJson.error.code}**: ${dataJson.error.message}`; + message.rawContent += errorMsg; + message.content += errorMsg; + return { finished: true }; + } + // No candidates? if (!dataJson.candidates) return {}; diff --git a/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml index 14cd1a17a..9a37df096 100644 --- a/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml +++ b/dots/.config/quickshell/ii/services/ai/MistralApiStrategy.qml @@ -58,8 +58,17 @@ ApiStrategy { // Real stuff try { const dataJson = JSON.parse(cleanData); + + // Error response handling + if (dataJson.error) { + const errorMsg = `**Error**: ${dataJson.error.message || JSON.stringify(dataJson.error)}`; + message.rawContent += errorMsg; + message.content += errorMsg; + return { finished: true }; + } + let newContent = ""; - + const responseContent = dataJson.choices[0]?.delta?.content || dataJson.message?.content; const responseReasoning = dataJson.choices[0]?.delta?.reasoning || dataJson.choices[0]?.delta?.reasoning_content; diff --git a/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml b/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml index 1178c837f..a049abf30 100644 --- a/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml +++ b/dots/.config/quickshell/ii/services/ai/OpenAiApiStrategy.qml @@ -49,8 +49,17 @@ ApiStrategy { // Real stuff try { const dataJson = JSON.parse(cleanData); + + // Error response handling + if (dataJson.error) { + const errorMsg = `**Error**: ${dataJson.error.message || JSON.stringify(dataJson.error)}`; + message.rawContent += errorMsg; + message.content += errorMsg; + return { finished: true }; + } + let newContent = ""; - + const responseContent = dataJson.choices[0]?.delta?.content || dataJson.message?.content; const responseReasoning = dataJson.choices[0]?.delta?.reasoning || dataJson.choices[0]?.delta?.reasoning_content; From 044221be9313f472501a26bd6c848672637d1764 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 11 Dec 2025 23:29:42 +0100 Subject: [PATCH 087/154] waffles: task view: workspaces --- dots/.config/hypr/hyprland/general.conf | 8 +- dots/.config/hypr/hyprland/rules.conf | 2 + .../ii/modules/ii/overview/OverviewWidget.qml | 4 +- .../modules/waffle/looks/WMouseAreaButton.qml | 45 +++++++++ .../waffle/taskView/TaskViewContent.qml | 96 ++++++++++++++++++ .../waffle/taskView/TaskViewWorkspace.qml | 97 +++++++++++++++++++ .../waffle/taskView/WaffleTaskView.qml | 84 ++++++++++++++++ dots/.config/quickshell/ii/shell.qml | 4 +- 8 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/looks/WMouseAreaButton.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index 77580552d..e2f02ad75 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -56,10 +56,10 @@ decoration { size = 10 passes = 3 brightness = 1 - noise = 0.15 - contrast = 0.2 - vibrancy = 0.8 - vibrancy_darkness = 0.8 + noise = 0.05 + contrast = 0.89 + vibrancy = 0.5 + vibrancy_darkness = 0.5 popups = false popups_ignorealpha = 0.6 input_methods = true diff --git a/dots/.config/hypr/hyprland/rules.conf b/dots/.config/hypr/hyprland/rules.conf index a6e4d4e48..3a3a01ef9 100644 --- a/dots/.config/hypr/hyprland/rules.conf +++ b/dots/.config/hypr/hyprland/rules.conf @@ -158,6 +158,8 @@ layerrule = animation slide top, quickshell:wallpaperSelector layerrule = noanim, quickshell:wNotificationCenter layerrule = noanim, quickshell:wOnScreenDisplay layerrule = noanim, quickshell:wStartMenu +layerrule = ignorealpha 0, quickshell:wTaskView +layerrule = noanim, quickshell:wTaskView # Launchers need to be FAST layerrule = noanim, gtk4-layer-shell diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index f0e40d56e..db424e0e6 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -159,12 +159,12 @@ Item { model: ScriptModel { values: { // console.log(JSON.stringify(ToplevelManager.toplevels.values.map(t => t), null, 2)) - return [...ToplevelManager.toplevels.values.filter((toplevel) => { + return ToplevelManager.toplevels.values.filter((toplevel) => { const address = `0x${toplevel.HyprlandToplevel?.address}` var win = windowByAddress[address] const inWorkspaceGroup = (root.workspaceGroup * root.workspacesShown < win?.workspace?.id && win?.workspace?.id <= (root.workspaceGroup + 1) * root.workspacesShown) return inWorkspaceGroup; - })].reverse() + }) } } delegate: OverviewWindow { diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WMouseAreaButton.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WMouseAreaButton.qml new file mode 100644 index 000000000..181ce6047 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WMouseAreaButton.qml @@ -0,0 +1,45 @@ +import QtQuick +import qs +import qs.modules.common +import qs.modules.common.functions +import qs.modules.waffle.looks + +MouseArea { + id: root + + property real radius: Looks.radius.medium + hoverEnabled: true + + property color colBackground: ColorUtils.transparentize(Looks.colors.bg2) + property color colBackgroundHover: Looks.colors.bg2Hover + property color colBackgroundActive: Looks.colors.bg2Active + property color colBorder: ColorUtils.transparentize(Looks.colors.bg2Border) + property color colBorderHover: Looks.colors.bg2Border + + property color color: { + if (containsMouse) { + return pressed ? colBackgroundActive : colBackgroundHover; + } else { + return colBackground; + } + } + + property color borderColor: { + if (containsMouse) { + return colBorderHover; + } else { + return colBorder; + } + } + + property Item background: Rectangle { + id: bgRect + parent: root + anchors.fill: parent + color: root.color + radius: root.radius + + border.color: root.borderColor + border.width: 1 + } +} \ No newline at end of file diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml new file mode 100644 index 000000000..7c7153fb9 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -0,0 +1,96 @@ +import QtQuick +import Quickshell +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +Rectangle { + id: root + + color: ColorUtils.transparentize(Looks.colors.bg1Base, 0.5) + property real openProgress: 0 + + Component.onCompleted: { + openAnim.start(); + } + + PropertyAnimation { + id: openAnim + target: root + property: "openProgress" + to: 1 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + PropertyAnimation { + id: closeAnim + target: root + property: "openProgress" + to: 0 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + + // Workspaces + Rectangle { + id: wsBorder + property real sourceEdgeMargin: -(height + 8) + root.openProgress * (height + 16) + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + leftMargin: 8 + rightMargin: 8 + topMargin: sourceEdgeMargin + bottomMargin: sourceEdgeMargin + } + border.color: Looks.colors.bg2Border + border.width: 1 + radius: Looks.radius.large + color: "transparent" + + implicitHeight: wsBg.implicitHeight + border.width * 2 + + Rectangle { + id: wsBg + anchors.fill: parent + anchors.margins: wsBorder.border.width + radius: wsBorder.radius - wsBorder.border.width + color: Looks.colors.bgPanelFooterBase + + implicitHeight: 174 + + ListView { + anchors { + top: parent.top + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + topMargin: 5 + bottomMargin: 5 + } + width: Math.min(contentWidth + leftMargin + rightMargin, parent.width) + leftMargin: 5 + rightMargin: 5 + clip: true + orientation: ListView.Horizontal + spacing: 4 + + model: ScriptModel { + values: { + const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id)) + return Array(maxWorkspaceId) + } + } + delegate: TaskViewWorkspace { + required property int index + workspace: index + 1 + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml new file mode 100644 index 000000000..987511f05 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -0,0 +1,97 @@ +import QtQuick +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks + +WMouseAreaButton { + id: root + + required property int workspace + + readonly property real screenWidth: QsWindow.window.width + readonly property real screenHeight: QsWindow.window.height + readonly property real screenAspectRatio: screenWidth / screenHeight + readonly property real screenScale: QsWindow.window.devicePixelRatio + readonly property real scale: 0.1148148148 + + height: ListView.view.height + implicitWidth: 244 // for now + + onClicked: { + GlobalStates.overviewOpen = false; + Hyprland.dispatch(`workspace ${root.workspace}`); + } + + ColumnLayout { + anchors { + fill: parent + leftMargin: 12 + rightMargin: 12 + topMargin: 9 + bottomMargin: 8 + } + spacing: 8 + + WText { + Layout.fillWidth: true + Layout.fillHeight: false + horizontalAlignment: Text.AlignLeft + elide: Text.ElideRight + text: Translation.tr("Desktop %1").arg(root.workspace) + } + + Rectangle { + id: wsBg + height: 124 + Layout.fillHeight: true + Layout.fillWidth: true + color: Looks.colors.bg1Base + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: Rectangle { + width: wsBg.width + height: wsBg.height + radius: Looks.radius.medium + } + } + + StyledImage { + anchors.fill: parent + cache: true + sourceSize: Qt.size(root.screenAspectRatio * 124, 124) + source: Config.options.background.wallpaperPath + fillMode: Image.PreserveAspectCrop + + Repeater { + model: ScriptModel { + values: ToplevelManager.toplevels.values.filter(toplevel => { + const address = `0x${toplevel.HyprlandToplevel?.address}`; + var win = HyprlandData.windowByAddress[address]; + const inWorkspace = win?.workspace?.id === root.workspace; + return inWorkspace; + }) + } + delegate: ScreencopyView { + required property var modelData + readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`] + captureSource: modelData + live: true + width: hyprlandWindowData?.size[0] * root.scale + height: hyprlandWindowData?.size[1] * root.scale + x: hyprlandWindowData?.at[0] * root.scale + y: hyprlandWindowData?.at[1] * root.scale + } + } + } + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml new file mode 100644 index 000000000..75e71ccb1 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml @@ -0,0 +1,84 @@ +pragma ComponentBehavior: Bound +import qs +import qs.services +import qs.modules.common +import qs.modules.common.widgets +import Qt.labs.synchronizer +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Quickshell +import Quickshell.Io +import Quickshell.Wayland +import Quickshell.Hyprland + +Scope { + id: overviewScope + property bool dontAutoCancelSearch: false + + Variants { + id: overviewVariants + model: Quickshell.screens + + Loader { + id: panelLoader + required property var modelData + active: GlobalStates.overviewOpen + sourceComponent: PanelWindow { + id: root + property string searchingText: "" + readonly property HyprlandMonitor monitor: Hyprland.monitorFor(root.screen) + property bool monitorIsFocused: (Hyprland.focusedMonitor?.id == monitor?.id) + screen: panelLoader.modelData + + WlrLayershell.namespace: "quickshell:wTaskView" + WlrLayershell.layer: WlrLayer.Overlay + // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + color: "transparent" + + anchors { + top: true + bottom: true + left: true + right: true + } + + TaskViewContent { + anchors.fill: parent + } + } + } + } + + IpcHandler { + target: "search" + + function toggle() { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + function workspacesToggle() { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + function close() { + GlobalStates.overviewOpen = false; + } + function open() { + GlobalStates.overviewOpen = true; + } + function toggleReleaseInterrupt() { + GlobalStates.superReleaseMightTrigger = false; + } + function clipboardToggle() { + overviewScope.toggleClipboard(); + } + } + + GlobalShortcut { + name: "overviewWorkspacesToggle" + description: "Toggles overview on press" + + onPressed: { + GlobalStates.overviewOpen = !GlobalStates.overviewOpen; + } + } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index b8fa41c3f..feced64cd 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -36,6 +36,7 @@ import qs.modules.waffle.onScreenDisplay import qs.modules.waffle.polkit import qs.modules.waffle.startMenu import qs.modules.waffle.sessionScreen +import qs.modules.waffle.taskView import QtQuick import QtQuick.Window @@ -90,6 +91,7 @@ ShellRoot { PanelLoader { identifier: "wPolkit"; component: WafflePolkit {} } PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } + PanelLoader { identifier: "wTaskView"; component: WaffleTaskView {} } ReloadPopup {} component PanelLoader: LazyLoader { @@ -102,7 +104,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", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], + "waffle": ["wActionCenter", "wBar", "wBackground", "wLock", "wNotificationCenter", "wOnScreenDisplay", "wTaskView", "wPolkit", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiNotificationPopup", "iiOnScreenKeyboard", "iiOverlay", "iiRegionSelector", "iiWallpaperSelector"], }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) From eaae89c904b2eb72cc56d2adda7777573a8eee24 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 11 Dec 2025 23:29:54 +0100 Subject: [PATCH 088/154] make touchpad scrolling less slow --- dots/.config/hypr/hyprland/general.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index e2f02ad75..3f5fa2cf8 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -127,7 +127,7 @@ input { natural_scroll = yes disable_while_typing = true clickfinger_behavior = true - scroll_factor = 0.5 + scroll_factor = 0.7 } } From d83733bd8655a44ee8c2cd129a440f0de461cf77 Mon Sep 17 00:00:00 2001 From: Perdixky <3293789706@qq.com> Date: Fri, 12 Dec 2025 08:42:45 +0800 Subject: [PATCH 089/154] Restore original rsync_dir__sync() implementation --- sdata/subcmd-install/3.files.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index aa3099ab0..d0e537aa3 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -62,7 +62,10 @@ rsync_dir__sync(){ # `--delete' for rsync to make sure that # original dotfiles and new ones in the SAME DIRECTORY # (eg. in ~/.config/hypr) won't be mixed together - rsync_dir__sync_exclude "$1" "$2" + x mkdir -p "$2" + local dest="$(realpath -se $2)" + x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" + rsync -a --delete --out-format='%i %n' "$1"/ "$2"/ | awk -v d="$dest" '$1 ~ /^>/{ sub(/^[^ ]+ /,""); printf d "/" $0 "\n" }' >> "${INSTALLED_LISTFILE}" } rsync_dir__sync_exclude(){ # NOTE: This function is only for using in other functions From 39a3a0c484371945414b0bf91bc41a6d6f5ac0f3 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 12 Dec 2025 09:18:55 +0100 Subject: [PATCH 090/154] waffles: taskview: indicate active ws --- .../ii/modules/waffle/looks/Looks.qml | 2 +- .../waffle/taskView/TaskViewContent.qml | 11 +++++---- .../waffle/taskView/TaskViewWorkspace.qml | 24 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 51e7fe40c..85728c754 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -177,7 +177,7 @@ Singleton { property Component color: Component { ColorAnimation { - duration: 120 + duration: 70 easing.type: Easing.BezierSpline easing.bezierCurve: transition.easing.bezierCurve.easeIn } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index 7c7153fb9..79f08fc79 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -64,8 +64,9 @@ Rectangle { color: Looks.colors.bgPanelFooterBase implicitHeight: 174 - + ListView { + id: workspaceListView anchors { top: parent.top bottom: parent.bottom @@ -73,17 +74,19 @@ Rectangle { topMargin: 5 bottomMargin: 5 } + flickableDirection: Flickable.HorizontalFlick + orientation: ListView.Horizontal + interactive: width == parent.width width: Math.min(contentWidth + leftMargin + rightMargin, parent.width) leftMargin: 5 rightMargin: 5 clip: true - orientation: ListView.Horizontal spacing: 4 model: ScriptModel { values: { - const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id)) - return Array(maxWorkspaceId) + const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id)); + return Array(Math.max(maxWorkspaceId, 1)); } } delegate: TaskViewWorkspace { diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml index 987511f05..b285d91e0 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -16,6 +16,7 @@ WMouseAreaButton { required property int workspace + readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace readonly property real screenWidth: QsWindow.window.width readonly property real screenHeight: QsWindow.window.height readonly property real screenAspectRatio: screenWidth / screenHeight @@ -30,6 +31,12 @@ WMouseAreaButton { Hyprland.dispatch(`workspace ${root.workspace}`); } + colBackground: ColorUtils.transparentize(Looks.colors.bg2, isActiveWorkspace ? 0 : 1) + Behavior on color { + animation: Looks.transition.color.createObject(this) + } + + // Content ColumnLayout { anchors { fill: parent @@ -94,4 +101,21 @@ WMouseAreaButton { } } } + + // Active indicator + WFadeLoader { + anchors { + horizontalCenter: parent.horizontalCenter + bottom: parent.bottom + } + shown: root.isActiveWorkspace + + sourceComponent: Rectangle { + id: activeIndicator + implicitWidth: 32 + implicitHeight: 3 + color: Looks.colors.accent + radius: height / 2 + } + } } From f71ed855e5fe5cf6755bee0f5bddf2e2dc7d33fe Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:45:35 +0100 Subject: [PATCH 091/154] waffles: taskview: show windows --- .../quickshell/ii/assets/icons/fluent/.svg | 35 +++++ .../assets/icons/fluent/checkmark-filled.svg | 1 + .../ii/assets/icons/fluent/checkmark.svg | 1 + .../ii/assets/icons/fluent/empty.svg | 35 +++++ .../ii/modules/common/models/IndexModel.qml | 6 + .../ii/modules/waffle/looks/Looks.qml | 2 +- .../ii/modules/waffle/looks/WListView.qml | 2 + .../waffle/taskView/TaskViewContent.qml | 109 ++++++++++++++- .../waffle/taskView/TaskViewWindow.qml | 127 ++++++++++++++++++ .../waffle/taskView/TaskViewWorkspace.qml | 78 +++++++---- .../modules/waffle/taskView/window-layout.js | 36 +++++ .../quickshell/ii/services/HyprlandData.qml | 25 ++++ 12 files changed, 423 insertions(+), 34 deletions(-) create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg create mode 100644 dots/.config/quickshell/ii/assets/icons/fluent/empty.svg create mode 100644 dots/.config/quickshell/ii/modules/common/models/IndexModel.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml create mode 100644 dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/.svg b/dots/.config/quickshell/ii/assets/icons/fluent/.svg new file mode 100644 index 000000000..c047f0c03 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/.svg @@ -0,0 +1,35 @@ + + + + + diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg new file mode 100644 index 000000000..966863066 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark-filled.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg new file mode 100644 index 000000000..07380988b --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/checkmark.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg b/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg new file mode 100644 index 000000000..c047f0c03 --- /dev/null +++ b/dots/.config/quickshell/ii/assets/icons/fluent/empty.svg @@ -0,0 +1,35 @@ + + + + + diff --git a/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml b/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml new file mode 100644 index 000000000..0cf30cdea --- /dev/null +++ b/dots/.config/quickshell/ii/modules/common/models/IndexModel.qml @@ -0,0 +1,6 @@ +import Quickshell + +ScriptModel { + required property int count + values: Array(count).map((_, i) => i) +} diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 85728c754..8120aa85e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -177,7 +177,7 @@ Singleton { property Component color: Component { ColorAnimation { - duration: 70 + duration: 80 easing.type: Easing.BezierSpline easing.bezierCurve: transition.easing.bezierCurve.easeIn } diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml index bc567762a..e2ea3cda3 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml @@ -6,5 +6,7 @@ import QtQuick.Controls ListView { id: root + boundsBehavior: Flickable.DragOverBounds + ScrollBar.vertical: WScrollBar {} } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index 79f08fc79..f767ee435 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -1,11 +1,16 @@ import QtQuick +import QtQuick.Layouts import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland import qs import qs.services import qs.modules.common import qs.modules.common.functions +import qs.modules.common.models import qs.modules.common.widgets import qs.modules.waffle.looks +import "window-layout.js" as WindowLayout Rectangle { id: root @@ -36,6 +41,90 @@ Rectangle { easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn } + // Windows + property real maxWindowHeight: 290 + property real maxWindowWidth: 738 + property real padding: 52 + property real spacing: 25 + readonly property list toplevels: ToplevelManager.toplevels.values.filter(t => { + const client = HyprlandData.clientForToplevel(t); + return client && client.workspace.id === HyprlandData.activeWorkspace?.id; + }) + readonly property list arrangedToplevels: { + const maxRowWidth = width - padding * 2; + const count = toplevels.length; + const resultLayout = []; + + var i = 0; + while (i < count) { + var row = []; + var rowWidth = 0; + var j = i; + + while (j < count) { + const toplevel = toplevels[j]; + const client = HyprlandData.clientForToplevel(toplevel); + const scaledSize = WindowLayout.scaleWindow(client, maxWindowWidth, maxWindowHeight); + + if (rowWidth + scaledSize.width <= maxRowWidth || row.length === 0) { + row.push(toplevel); + rowWidth += scaledSize.width; + j++; + } else { + break; + } + } + + resultLayout.push(row); + i = j; + } + return resultLayout; + } + + // Windows + WListView { + anchors { + left: parent.left + right: parent.right + top: parent.top + topMargin: (root.height - (wsBorder.height + 16) - height) / 2 + } + spacing: root.spacing + topMargin: root.padding + bottomMargin: root.padding + leftMargin: root.padding + rightMargin: root.padding + height: Math.min(contentHeight + topMargin + bottomMargin, root.height - (wsBorder.height + 16)) + + interactive: height < contentHeight + + clip: true + + model: IndexModel { + count: arrangedToplevels.length + } + delegate: RowLayout { + id: clientRow + required property int index + spacing: root.spacing + anchors.horizontalCenter: parent.horizontalCenter + + Repeater { + model: IndexModel { + count: root.arrangedToplevels[clientRow.index].length + } + delegate: TaskViewWindow { + id: client + required property int index + Layout.alignment: Qt.AlignTop + maxHeight: root.maxWindowHeight + maxWidth: root.maxWindowWidth + toplevel: root.arrangedToplevels[clientRow.index][index] + } + } + } + } + // Workspaces Rectangle { id: wsBorder @@ -65,7 +154,7 @@ Rectangle { implicitHeight: 174 - ListView { + WListView { id: workspaceListView anchors { top: parent.top @@ -83,15 +172,27 @@ Rectangle { clip: true spacing: 4 - model: ScriptModel { - values: { + function reposition() { + positionViewAtIndex(HyprlandData.activeWorkspace.id - 1, ListView.Contain); + } + + Connections { + target: HyprlandData + function onActiveWorkspaceChanged() { + workspaceListView.reposition(); + } + } + model: IndexModel { + id: workspaceIndexModel + count: { const maxWorkspaceId = Math.max.apply(null, HyprlandData.workspaces.map(ws => ws.id)); - return Array(Math.max(maxWorkspaceId, 1)); + return Math.max(maxWorkspaceId, 1) + 1; } } delegate: TaskViewWorkspace { required property int index workspace: index + 1 + newWorkspace: index == workspaceIndexModel.count - 1 } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml new file mode 100644 index 000000000..e738ac6d5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -0,0 +1,127 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Qt5Compat.GraphicalEffects +import Quickshell +import Quickshell.Wayland +import Quickshell.Hyprland +import qs +import qs.services +import qs.modules.common +import qs.modules.common.functions +import qs.modules.common.widgets +import qs.modules.waffle.looks +import "window-layout.js" as WindowLayout + +WMouseAreaButton { + id: root + + required property var toplevel + required property int maxHeight + required property int maxWidth + + property var hyprlandClient: HyprlandData.clientForToplevel(root.toplevel) + + property string iconName: AppSearch.guessIcon(hyprlandClient?.class) + + color: containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase + borderColor: Looks.colors.bg2Border + radius: Looks.radius.xLarge + + property size size: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight) + implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138) + implicitHeight: Math.round(contentItem.implicitHeight) + + layer.enabled: true + layer.effect: OpacityMask { + maskSource: root.background + } + scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1 + Behavior on scale { + NumberAnimation { + id: scaleAnim + duration: 300 + easing.type: Easing.OutExpo + } + } + + function closeWindow() { + Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`) + } + + acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton + onClicked: (event) => { + if (event.button === Qt.LeftButton) { + GlobalStates.overviewOpen = false + Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`) + GlobalStates.overviewOpen = false; + } else if (event.button === Qt.MiddleButton) { + root.closeWindow(); + event.accepted = true; + } else if (event.button === Qt.RightButton) { + if (!windowMenu.visible) windowMenu.popup(); + else windowMenu.close(); + } + } + + ColumnLayout { + id: contentItem + z: 2 + anchors.fill: parent + anchors.margins: 1 + spacing: 0 + + RowLayout { + spacing: 8 + WAppIcon { + Layout.leftMargin: 10 + Layout.alignment: Qt.AlignVCenter + iconName: root.iconName + implicitSize: 16 + tryCustomIcon: false + } + WText { + Layout.fillWidth: true + Layout.alignment: Qt.AlignVCenter + elide: Text.ElideRight + text: root.hyprlandClient?.title ?? "" + } + CloseButton { + implicitWidth: 38 + implicitHeight: 38 + padding: 8 + onClicked: root.closeWindow() + } + } + + ScreencopyView { + Layout.fillHeight: true + Layout.alignment: Qt.AlignHCenter + implicitWidth: Math.round(root.size.width) + implicitHeight: Math.round(root.size.height) + + captureSource: root.toplevel ?? null + live: true + } + } + + WMenu { + id: windowMenu + downDirection: true + + Action { + enabled: root.hyprlandClient?.floating + property bool isPinned: root.hyprlandClient?.pinned + icon.name: isPinned ? "checkmark" : "empty" + text: Translation.tr("Show this window on all desktops") + onTriggered: { + Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`) + } + } + Action { + icon.name: "empty" + text: Translation.tr("Close") + onTriggered: root.closeWindow() + } + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml index b285d91e0..cb3976997 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -15,13 +15,15 @@ WMouseAreaButton { id: root required property int workspace + property bool newWorkspace: false readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace readonly property real screenWidth: QsWindow.window.width readonly property real screenHeight: QsWindow.window.height readonly property real screenAspectRatio: screenWidth / screenHeight - readonly property real screenScale: QsWindow.window.devicePixelRatio - readonly property real scale: 0.1148148148 + readonly property real windowScale: wallpaperHeight / screenHeight + + property real wallpaperHeight: 124 height: ListView.view.height implicitWidth: 244 // for now @@ -36,8 +38,18 @@ WMouseAreaButton { animation: Looks.transition.color.createObject(this) } + scale: root.containsPress ? 0.95 : 1 + Behavior on scale { + NumberAnimation { + id: scaleAnim + duration: 300 + easing.type: Easing.OutExpo + } + } + // Content ColumnLayout { + id: contentItem anchors { fill: parent leftMargin: 12 @@ -52,15 +64,15 @@ WMouseAreaButton { Layout.fillHeight: false horizontalAlignment: Text.AlignLeft elide: Text.ElideRight - text: Translation.tr("Desktop %1").arg(root.workspace) + text: root.newWorkspace ? Translation.tr("New desktop") : Translation.tr("Desktop %1").arg(root.workspace) } Rectangle { id: wsBg - height: 124 + height: root.wallpaperHeight Layout.fillHeight: true Layout.fillWidth: true - color: Looks.colors.bg1Base + color: Looks.colors.bg1 layer.enabled: true layer.effect: OpacityMask { @@ -71,34 +83,42 @@ WMouseAreaButton { } } - StyledImage { + // Workspace content + Loader { anchors.fill: parent - cache: true - sourceSize: Qt.size(root.screenAspectRatio * 124, 124) - source: Config.options.background.wallpaperPath - fillMode: Image.PreserveAspectCrop + active: !root.newWorkspace + sourceComponent: StyledImage { + cache: true + sourceSize: Qt.size(root.screenAspectRatio * root.wallpaperHeight, root.wallpaperHeight) + source: Config.options.background.wallpaperPath + fillMode: Image.PreserveAspectCrop - Repeater { - model: ScriptModel { - values: ToplevelManager.toplevels.values.filter(toplevel => { - const address = `0x${toplevel.HyprlandToplevel?.address}`; - var win = HyprlandData.windowByAddress[address]; - const inWorkspace = win?.workspace?.id === root.workspace; - return inWorkspace; - }) - } - delegate: ScreencopyView { - required property var modelData - readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`] - captureSource: modelData - live: true - width: hyprlandWindowData?.size[0] * root.scale - height: hyprlandWindowData?.size[1] * root.scale - x: hyprlandWindowData?.at[0] * root.scale - y: hyprlandWindowData?.at[1] * root.scale + Repeater { + model: ScriptModel { + values: HyprlandData.toplevelsForWorkspace(root.workspace) + } + delegate: ScreencopyView { + required property var modelData + readonly property var hyprlandWindowData: HyprlandData.windowByAddress[`0x${modelData.HyprlandToplevel?.address}`] + captureSource: modelData + live: true + width: hyprlandWindowData?.size[0] * root.windowScale + height: hyprlandWindowData?.size[1] * root.windowScale + x: hyprlandWindowData?.at[0] * root.windowScale + y: hyprlandWindowData?.at[1] * root.windowScale + } } } } + + // New plus icon + Loader { + anchors.centerIn: parent + active: root.newWorkspace + sourceComponent: FluentIcon { + icon: "add" + } + } } } @@ -109,7 +129,7 @@ WMouseAreaButton { bottom: parent.bottom } shown: root.isActiveWorkspace - + sourceComponent: Rectangle { id: activeIndicator implicitWidth: 32 diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js b/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js new file mode 100644 index 000000000..d5548bdb5 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/window-layout.js @@ -0,0 +1,36 @@ +function scaleWindow(hyprlandClient, maxWindowWidth, maxWindowHeight) { + const [width, height] = hyprlandClient.size; + const [xScale, yScale] = [maxWindowWidth / width, maxWindowHeight / height]; + const scale = Math.min(xScale, yScale); + return Qt.size(width * scale, height * scale) +} + +function arrangedClients(hyprlandClients, maxRowWidth, maxWindowWidth, maxWindowHeight) { + const count = hyprlandClients.length; + const resultLayout = []; + + var i = 0; + while (i < count) { + var row = []; + var rowWidth = 0; + var j = i; + + while (j < count) { + const client = hyprlandClients[j]; + const scaledSize = scaleWindow(client, maxWindowWidth, maxWindowHeight); + + if (rowWidth + scaledSize.width <= maxRowWidth || row.length === 0) { + row.push(client); + rowWidth += scaledSize.width; + j++; + } else { + break; + } + } + + resultLayout.push(row); + i = j; + } + + return resultLayout; +} diff --git a/dots/.config/quickshell/ii/services/HyprlandData.qml b/dots/.config/quickshell/ii/services/HyprlandData.qml index abbaaf577..7bb437c84 100644 --- a/dots/.config/quickshell/ii/services/HyprlandData.qml +++ b/dots/.config/quickshell/ii/services/HyprlandData.qml @@ -4,6 +4,7 @@ pragma ComponentBehavior: Bound import QtQuick import Quickshell import Quickshell.Io +import Quickshell.Wayland import Quickshell.Hyprland /** @@ -21,6 +22,30 @@ Singleton { property var monitors: [] property var layers: ({}) + // Convenient stuff + + function toplevelsForWorkspace(workspace) { + return ToplevelManager.toplevels.values.filter(toplevel => { + const address = `0x${toplevel.HyprlandToplevel?.address}`; + var win = HyprlandData.windowByAddress[address]; + return win?.workspace?.id === workspace; + }) + } + + function hyprlandClientsForWorkspace(workspace) { + return root.windowList.filter(win => win.workspace.id === workspace); + } + + function clientForToplevel(toplevel) { + if (!toplevel || !toplevel.HyprlandToplevel) { + return null; + } + const address = `0x${toplevel?.HyprlandToplevel?.address}`; + return root.windowByAddress[address]; + } + + // Internals + function updateWindowList() { getClients.running = true; } From 136f4a3e486380a4f510425d261f43b7dd803c1a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 00:50:55 +0100 Subject: [PATCH 092/154] waffles: taskview: fix unintended window transparency --- .../ii/modules/waffle/taskView/TaskViewWindow.qml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml index e738ac6d5..e46a124fa 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -34,7 +34,11 @@ WMouseAreaButton { layer.enabled: true layer.effect: OpacityMask { - maskSource: root.background + maskSource: Rectangle { + width: root.width + height: root.height + radius: root.radius + } } scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1 Behavior on scale { From 91a2a520b0143a69fa255bf71b88fd5c63620e93 Mon Sep 17 00:00:00 2001 From: nrand Date: Sat, 13 Dec 2025 16:43:34 +0200 Subject: [PATCH 093/154] overview: custom workspace order --- .../quickshell/ii/modules/common/Config.qml | 2 + .../ii/modules/ii/overview/OverviewWidget.qml | 29 +++++++++++--- .../ii/modules/settings/InterfaceConfig.qml | 39 +++++++++++++++++++ 3 files changed, 64 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index f9a9a8654..4fed1b6fb 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -418,6 +418,8 @@ Singleton { property real scale: 0.18 // Relative to screen size property real rows: 2 property real columns: 5 + property bool orderRightLeft: false + property bool orderBottomUp: false property bool centerIcons: true } diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index db424e0e6..f6891fb31 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -50,6 +50,24 @@ Item { property Component windowComponent: OverviewWindow {} property list windowWidgets: [] + + function getWsRow(ws) + { + // 1-indexed workspace, 0-indexed row + var normalRow = Math.floor((ws - 1) / Config.options.overview.columns) % Config.options.overview.rows; + return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - normalRow - 1 : normalRow); + } + function getWsColumn(ws) + { + // 1-indexed workspace, 0-indexed column + var normalCol = (ws - 1) % Config.options.overview.columns; + return (Config.options.overview.orderRightLeft ? Config.options.overview.columns - normalCol - 1 : normalCol); + } + function getWsInCell(ri, ci) + { + // 1-indexed workspace, 0-indexed row and column index + return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - ri - 1 : ri) * Config.options.overview.columns + (Config.options.overview.orderRightLeft ? Config.options.overview.columns - ci - 1 : ci) + 1 + } StyledRectangularShadow { target: overviewBackground @@ -85,7 +103,7 @@ Item { id: workspace required property int index property int colIndex: index - property int workspaceValue: root.workspaceGroup * root.workspacesShown + row.index * Config.options.overview.columns + colIndex + 1 + 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 hoveredWorkspaceColor: ColorUtils.mix(defaultWorkspaceColor, Appearance.colors.colLayer1Hover, 0.1) property color hoveredBorderColor: Appearance.colors.colLayer2Hover @@ -182,8 +200,8 @@ Item { property bool atInitPosition: (initX == x && initY == y) // Offset on the canvas - property int workspaceColIndex: (windowData?.workspace.id - 1) % Config.options.overview.columns - property int workspaceRowIndex: Math.floor((windowData?.workspace.id - 1) % root.workspacesShown / Config.options.overview.columns) + property int workspaceColIndex: getWsColumn(windowData?.workspace.id) + property int workspaceRowIndex: getWsRow(windowData?.workspace.id) xOffset: (root.workspaceImplicitWidth + workspaceSpacing) * workspaceColIndex yOffset: (root.workspaceImplicitHeight + workspaceSpacing) * workspaceRowIndex property real xWithinWorkspaceWidget: Math.max((windowData?.at[0] - (monitor?.x ?? 0) - monitorData?.reserved[0]) * root.scale, 0) @@ -286,9 +304,8 @@ Item { Rectangle { // Focused workspace indicator id: focusedWorkspaceIndicator - property int activeWorkspaceInGroup: monitor.activeWorkspace?.id - (root.workspaceGroup * root.workspacesShown) - property int rowIndex: Math.floor((activeWorkspaceInGroup - 1) / Config.options.overview.columns) - property int colIndex: (activeWorkspaceInGroup - 1) % Config.options.overview.columns + property int rowIndex: getWsRow(monitor.activeWorkspace?.id) + property int colIndex: getWsColumn(monitor.activeWorkspace?.id) x: (root.workspaceImplicitWidth + workspaceSpacing) * colIndex y: (root.workspaceImplicitHeight + workspaceSpacing) * rowIndex z: root.windowZ diff --git a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml index b99e291b0..f94f1e811 100644 --- a/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/InterfaceConfig.qml @@ -739,6 +739,45 @@ ContentPage { } } } + ConfigRow { + uniform: true + ConfigSelectionArray { + currentValue: Config.options.overview.orderRightLeft + onSelected: newValue => { + Config.options.overview.orderRightLeft = newValue + } + options: [ + { + displayName: Translation.tr("Left to right"), + icon: "arrow_forward", + value: 0 + }, + { + displayName: Translation.tr("Right to left"), + icon: "arrow_back", + value: 1 + } + ] + } + ConfigSelectionArray { + currentValue: Config.options.overview.orderBottomUp + onSelected: newValue => { + Config.options.overview.orderBottomUp = newValue + } + options: [ + { + displayName: Translation.tr("Top-down"), + icon: "arrow_downward", + value: 0 + }, + { + displayName: Translation.tr("Bottom-up"), + icon: "arrow_upward", + value: 1 + } + ] + } + } } ContentSection { From d54ad65b50904aded25151e89bc232c3a3febd2d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:07:47 +0100 Subject: [PATCH 094/154] waffles: taskview: fix sizes not getting updated --- .../waffle/taskView/TaskViewContent.qml | 30 ++++++++++++------- .../waffle/taskView/TaskViewWindow.qml | 7 +++-- .../waffle/taskView/TaskViewWorkspace.qml | 6 ++-- 3 files changed, 26 insertions(+), 17 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index f767ee435..842395cd9 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -100,26 +100,34 @@ Rectangle { clip: true - model: IndexModel { - count: arrangedToplevels.length + model: ScriptModel { + values: root.arrangedToplevels } delegate: RowLayout { id: clientRow - required property int index + required property var modelData spacing: root.spacing anchors.horizontalCenter: parent.horizontalCenter Repeater { - model: IndexModel { - count: root.arrangedToplevels[clientRow.index].length + model: ScriptModel { + values: clientRow.modelData } - delegate: TaskViewWindow { - id: client + delegate: Item { + id: clientGridArea required property int index - Layout.alignment: Qt.AlignTop - maxHeight: root.maxWindowHeight - maxWidth: root.maxWindowWidth - toplevel: root.arrangedToplevels[clientRow.index][index] + required property var modelData + implicitWidth: windowItem.implicitWidth + implicitHeight: windowItem.implicitHeight + + TaskViewWindow { + id: windowItem + z: 9999 + Layout.alignment: Qt.AlignTop + maxHeight: root.maxWindowHeight + maxWidth: root.maxWindowWidth + toplevel: clientGridArea.modelData + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml index e46a124fa..d8bf4b45b 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -21,6 +21,7 @@ WMouseAreaButton { required property int maxWidth property var hyprlandClient: HyprlandData.clientForToplevel(root.toplevel) + property string address: hyprlandClient?.address property string iconName: AppSearch.guessIcon(hyprlandClient?.class) @@ -35,9 +36,9 @@ WMouseAreaButton { layer.enabled: true layer.effect: OpacityMask { maskSource: Rectangle { - width: root.width - height: root.height - radius: root.radius + width: root.background.width + height: root.background.height + radius: root.background.radius } } scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1 diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml index cb3976997..56451a4e7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -18,14 +18,14 @@ WMouseAreaButton { property bool newWorkspace: false readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace - readonly property real screenWidth: QsWindow.window.width - readonly property real screenHeight: QsWindow.window.height + readonly property real screenWidth: QsWindow.window?.width ?? 0 + readonly property real screenHeight: QsWindow.window?.height ?? 0 readonly property real screenAspectRatio: screenWidth / screenHeight readonly property real windowScale: wallpaperHeight / screenHeight property real wallpaperHeight: 124 - height: ListView.view.height + height: ListView.view?.height ?? 100 implicitWidth: 244 // for now onClicked: { From f0926b6ce3eec4b1525da52a25a36c5677415941 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 22:08:30 +0100 Subject: [PATCH 095/154] waffles: bar: use listview for tasks --- .../ii/modules/waffle/bar/tasks/Tasks.qml | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml index f33c6e12a..a2670118e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/bar/tasks/Tasks.qml @@ -9,8 +9,8 @@ MouseArea { id: root Layout.fillHeight: true - implicitHeight: row.implicitHeight - implicitWidth: row.implicitWidth + implicitHeight: appRow.implicitHeight + implicitWidth: appRow.implicitWidth hoverEnabled: true function showPreviewPopup(appEntry, button) { @@ -21,31 +21,31 @@ MouseArea { animation: Looks.transition.move.createObject(this) } - // Apps row - RowLayout { - id: row + WListView { + id: appRow anchors { top: parent.top bottom: parent.bottom } + orientation: Qt.Horizontal spacing: 0 + implicitWidth: contentWidth + clip: true + interactive: false + // TODO: Include only apps (and windows) in current workspace only | wait, does that even make sense in a Hyprland workflow? + model: ScriptModel { + objectProp: "appId" + values: TaskbarApps.apps.filter(app => app.appId !== "SEPARATOR") + } + delegate: TaskAppButton { + required property var modelData + appEntry: modelData - Repeater { - // TODO: Include only apps (and windows) in current workspace only | wait, does that even make sense in a Hyprland workflow? - model: ScriptModel { - objectProp: "appId" - values: TaskbarApps.apps.filter(app => app.appId !== "SEPARATOR") + onHoverPreviewRequested: { + root.showPreviewPopup(appEntry, this); } - delegate: TaskAppButton { - required property var modelData - appEntry: modelData - - onHoverPreviewRequested: { - root.showPreviewPopup(appEntry, this) - } - onHoverPreviewDismissed: { - previewPopup.close() - } + onHoverPreviewDismissed: { + previewPopup.close(); } } } @@ -56,5 +56,4 @@ MouseArea { tasksHovered: root.containsMouse anchor.window: root.QsWindow.window } - } From 93bc4d935c0361e3320b5fecc76d4a895e18d32f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:07:26 +0100 Subject: [PATCH 096/154] waffles: notif center: drag to dismiss --- .../modules/common/widgets/StyledListView.qml | 2 +- .../ii/modules/waffle/looks/WListView.qml | 16 ++++- .../NotificationPaneContent.qml | 3 +- .../WNotificationDismissAnim.qml | 32 ++++++++++ .../notificationCenter/WNotificationGroup.qml | 59 +++++++++++++++---- .../WSingleNotification.qml | 40 +++++++++++-- 6 files changed, 132 insertions(+), 20 deletions(-) create mode 100644 dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml index 26518cfb1..4267e7bcd 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledListView.qml @@ -100,7 +100,7 @@ ListView { to: 1, }), ] : [] - } + } move: Transition { animations: root.animateMovement ? [ diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml index e2ea3cda3..a46a03fe5 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml @@ -7,6 +7,20 @@ ListView { id: root boundsBehavior: Flickable.DragOverBounds - + ScrollBar.vertical: WScrollBar {} + + displaced: Transition { + animations: [Looks.transition.enter.createObject(this, { + property: "y" + })] + } + + remove: Transition { + ParallelAnimation { + NumberAnimation { property: "opacity"; to: 0; duration: 1000 } + NumberAnimation { properties: "x,y"; to: 100; duration: 1000 } + } + } + } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml index 368829865..5e9582bf1 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/NotificationPaneContent.qml @@ -53,10 +53,9 @@ BodyRectangle { } } - StyledListView { + WListView { Layout.fillWidth: true Layout.fillHeight: true - animateAppearance: false clip: true model: Notifications.appNameList diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml new file mode 100644 index 000000000..8d3972c40 --- /dev/null +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationDismissAnim.qml @@ -0,0 +1,32 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets +import qs.modules.common.functions +import qs.modules.waffle.looks + +SequentialAnimation { + id: root + + required property var target + + PropertyAction { + target: root.target + property: "ListView.delayRemove" + value: true + } + NumberAnimation { + target: root.target + property: "x" + to: root.target.width + duration: 250 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + PropertyAction { + target: root.target + property: "ListView.delayRemove" + value: false + } +} diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml index 658bc03ae..88c44136e 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WNotificationGroup.qml @@ -1,3 +1,4 @@ +pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import Quickshell @@ -18,10 +19,44 @@ MouseArea { implicitWidth: contentLayout.implicitWidth implicitHeight: contentLayout.implicitHeight + function dismissAll() { + root.notifications.forEach(notif => { + Qt.callLater(() => { + Notifications.discardNotification(notif.notificationId); + }); + }); + removeAnimation.start(); + } + + WNotificationDismissAnim { + id: removeAnimation + target: root + } + + property real dragDismissThreshold: 100 + drag { + axis: Drag.XAxis + target: contentLayout + minimumX: 0 + onActiveChanged: { + if (drag.active) + return; + if (contentLayout.x > root.dragDismissThreshold) { + root.dismissAll(); + } else { + contentLayout.x = 0; + } + } + } + ColumnLayout { id: contentLayout - anchors.fill: parent spacing: 4 + width: root.width + + Behavior on x { + animation: Looks.transition.enter.createObject(this) + } GroupHeader { id: notifHeader @@ -29,7 +64,9 @@ MouseArea { Layout.margins: 11 } - ListView { + WListView { + Layout.leftMargin: -Math.min(35, contentLayout.x) + Layout.rightMargin: -Layout.leftMargin Layout.fillWidth: true implicitWidth: notifHeader.implicitWidth implicitHeight: contentHeight @@ -40,14 +77,20 @@ MouseArea { objectProp: "notificationId" } delegate: WSingleNotification { + id: singleNotif required property int index required property var modelData + width: ListView.view.width notification: modelData + groupExpandControlMessage: { - if (root.notifications.length <= 1) return ""; - if (!root.expanded) return Translation.tr("+%1 notifications").arg(root.notifications.length - 1); - if (index === root.notifications.length - 1) return Translation.tr("See fewer"); + if (root.notifications.length <= 1) + return ""; + if (!root.expanded) + return Translation.tr("+%1 notifications").arg(root.notifications.length - 1); + if (index === root.notifications.length - 1) + return Translation.tr("See fewer"); return ""; } onGroupExpandToggle: { @@ -94,11 +137,7 @@ MouseArea { Layout.rightMargin: 3 icon.name: "dismiss" onClicked: { - root.notifications.forEach(notif => { - Qt.callLater(() => { - Notifications.discardNotification(notif.notificationId); - }); - }); + root.dismissAll(); } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml index 895ab6892..350e7b0ab 100644 --- a/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml +++ b/dots/.config/quickshell/ii/modules/waffle/notificationCenter/WSingleNotification.qml @@ -18,6 +18,18 @@ MouseArea { signal groupExpandToggle hoverEnabled: true + function dismiss() { + Qt.callLater(() => { + Notifications.discardNotification(root.notification?.notificationId); + }); + removeAnimation.start(); + } + + WNotificationDismissAnim { + id: removeAnimation + target: root + } + implicitHeight: contentItem.implicitHeight implicitWidth: contentItem.implicitWidth @@ -25,9 +37,25 @@ MouseArea { animation: Looks.transition.enter.createObject(this) } + property real dragDismissThreshold: 100 + drag { + axis: Drag.XAxis + target: contentItem + minimumX: 0 + onActiveChanged: { + if (drag.active) + return; + if (contentItem.x > root.dragDismissThreshold) { + root.dismiss(); + } else { + contentItem.x = 0; + } + } + } + Rectangle { id: contentItem - anchors.fill: parent + width: parent.width color: Looks.colors.bgPanelBody radius: Looks.radius.medium property real padding: 12 @@ -36,6 +64,10 @@ MouseArea { border.width: 1 border.color: ColorUtils.applyAlpha(Looks.colors.ambientShadow, 0.1) + Behavior on x { + animation: Looks.transition.enter.createObject(this) + } + ColumnLayout { id: notificationContent anchors.fill: parent @@ -128,11 +160,7 @@ MouseArea { opacity: root.containsMouse ? 1 : 0 icon.name: "dismiss" implicitSize: 12 - onClicked: { - Qt.callLater(() => { - Notifications.discardNotification(root.notification?.notificationId); - }); - } + onClicked: root.dismiss() } } From 29c4a6a15f3c85540e189df2855507ebcdbb551f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 13 Dec 2025 23:18:07 +0100 Subject: [PATCH 097/154] wait why is this here --- .../quickshell/ii/modules/waffle/looks/WListView.qml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml index a46a03fe5..3e506ef99 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WListView.qml @@ -16,11 +16,4 @@ ListView { })] } - remove: Transition { - ParallelAnimation { - NumberAnimation { property: "opacity"; to: 0; duration: 1000 } - NumberAnimation { properties: "x,y"; to: 100; duration: 1000 } - } - } - } From 536c1ab4657909bec035176334b9408e311117d7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 09:43:59 +0100 Subject: [PATCH 098/154] Revert "wallpaper selector: add thumbnail loading indicator" This reverts commit fa47c6778cc370b25b4056fdf9ba7f4e253cd3f3. --- .../ii/wallpaperSelector/WallpaperDirectoryItem.qml | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml index f0a53bf39..23f7915ef 100644 --- a/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml +++ b/dots/.config/quickshell/ii/modules/ii/wallpaperSelector/WallpaperDirectoryItem.qml @@ -11,7 +11,6 @@ MouseArea { required property var fileModelData property bool isDirectory: fileModelData.fileIsDir property bool useThumbnail: Images.isValidImageByName(fileModelData.fileName) - property bool showLoadingIndicator: false property alias colBackground: background.color property alias colText: wallpaperItemName.color @@ -70,8 +69,6 @@ MouseArea { sourceSize.width: wallpaperItemColumnLayout.width sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height - onVisibleChanged: root.showLoadingIndicator = !thumbnailImage.visible - Connections { target: Wallpapers function onThumbnailGenerated(directory) { @@ -109,15 +106,6 @@ MouseArea { sourceSize.height: wallpaperItemColumnLayout.height - wallpaperItemColumnLayout.spacing - wallpaperItemName.height } } - - FadeLoader { - id: loadingIndicatorLoader - shown: root.showLoadingIndicator - anchors.centerIn: parent - sourceComponent: MaterialLoadingIndicator { - loading: true - } - } } StyledText { From abd657ed7608887fc8b577024d03109158394c24 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 09:53:37 +0100 Subject: [PATCH 099/154] fix braces --- .../quickshell/ii/modules/ii/overview/OverviewWidget.qml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml index f6891fb31..8b791f44a 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/OverviewWidget.qml @@ -51,20 +51,17 @@ Item { property Component windowComponent: OverviewWindow {} property list windowWidgets: [] - function getWsRow(ws) - { + function getWsRow(ws) { // 1-indexed workspace, 0-indexed row var normalRow = Math.floor((ws - 1) / Config.options.overview.columns) % Config.options.overview.rows; return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - normalRow - 1 : normalRow); } - function getWsColumn(ws) - { + function getWsColumn(ws) { // 1-indexed workspace, 0-indexed column var normalCol = (ws - 1) % Config.options.overview.columns; return (Config.options.overview.orderRightLeft ? Config.options.overview.columns - normalCol - 1 : normalCol); } - function getWsInCell(ri, ci) - { + function getWsInCell(ri, ci) { // 1-indexed workspace, 0-indexed row and column index return (Config.options.overview.orderBottomUp ? Config.options.overview.rows - ri - 1 : ri) * Config.options.overview.columns + (Config.options.overview.orderRightLeft ? Config.options.overview.columns - ci - 1 : ci) + 1 } From e206c4334c5623ff6ed66c874e69c41c434edc36 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 10:20:54 +0100 Subject: [PATCH 100/154] waffles: taskview: draggable windows --- .../ii/modules/waffle/taskView/TaskViewContent.qml | 12 ++++++++++++ .../ii/scripts/thumbnails/thumbgen-venv.sh | 3 ++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index 842395cd9..cbb61b2fe 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -123,6 +123,18 @@ Rectangle { TaskViewWindow { id: windowItem z: 9999 + drag { + target: this + onActiveChanged: { + if (drag.active) { + parent = root; + } else { + parent = clientGridArea; + x = 0; + y = 0; + } + } + } Layout.alignment: Qt.AlignTop maxHeight: root.maxWindowHeight maxWidth: root.maxWindowWidth diff --git a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh index a27b5ca1d..5b24f16f9 100755 --- a/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh +++ b/dots/.config/quickshell/ii/scripts/thumbnails/thumbgen-venv.sh @@ -3,4 +3,5 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source $(eval echo $ILLOGICAL_IMPULSE_VIRTUAL_ENV)/bin/activate GIO_USE_VFS=local "$SCRIPT_DIR/thumbgen.py" "$@" -deactivate \ No newline at end of file +deactivate + From 5f51b97b6d343d5fbed35dd4e3de43df08715211 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 10:38:12 +0100 Subject: [PATCH 101/154] replace manual `file://` replacement with util functions --- dots/.config/quickshell/ii/services/LauncherSearch.qml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/services/LauncherSearch.qml b/dots/.config/quickshell/ii/services/LauncherSearch.qml index d8f7af59a..52e783e28 100644 --- a/dots/.config/quickshell/ii/services/LauncherSearch.qml +++ b/dots/.config/quickshell/ii/services/LauncherSearch.qml @@ -45,7 +45,7 @@ Singleton { action: actionName, execute: ((path) => (args) => { Quickshell.execDetached([path, ...(args ? args.split(" ") : [])]); - })(filePath.toString().replace("file://", "")) + })(FileUtils.trimFileProtocol(filePath.toString())) }); } } @@ -54,7 +54,7 @@ Singleton { FolderListModel { id: userActionsFolder - folder: `file://${Directories.userActions}` + folder: Qt.resolvedUrl(Directories.userActions) showDirs: false showHidden: false sortField: FolderListModel.Name From f1fdb941e1e3d71900d7af0f9b619c0c034a5bd1 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 10:47:53 +0100 Subject: [PATCH 102/154] persistent cheatsheet tabs (closes #2681) --- .../quickshell/ii/modules/common/Persistent.qml | 4 ++++ .../ii/modules/ii/cheatsheet/Cheatsheet.qml | 11 +++++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 478cadd10..247884028 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -63,6 +63,10 @@ Singleton { property real temperature: 0.5 } + property JsonObject cheatsheet: JsonObject { + property int tabIndex: 0 + } + property JsonObject sidebar: JsonObject { property JsonObject bottomGroup: JsonObject { property bool collapsed: false diff --git a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml index d133fa654..f7fb68f68 100644 --- a/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml +++ b/dots/.config/quickshell/ii/modules/ii/cheatsheet/Cheatsheet.qml @@ -4,6 +4,7 @@ import qs.modules.common.widgets import QtQuick import QtQuick.Controls import QtQuick.Layouts +import Qt.labs.synchronizer import Qt5Compat.GraphicalEffects import Quickshell.Io import Quickshell @@ -135,7 +136,10 @@ Scope { // Scope ToolbarTabBar { id: tabBar tabButtonList: root.tabButtonList - currentIndex: swipeView.currentIndex + + Synchronizer on currentIndex { + property alias source: swipeView.currentIndex + } } } @@ -144,8 +148,11 @@ Scope { // Scope Layout.topMargin: 5 Layout.fillWidth: true Layout.fillHeight: true - currentIndex: tabBar.currentIndex spacing: 10 + currentIndex: Persistent.states.cheatsheet.tabIndex + onCurrentIndexChanged: { + Persistent.states.cheatsheet.tabIndex = currentIndex; + } implicitWidth: Math.max.apply(null, contentChildren.map(child => child.implicitWidth || 0)) implicitHeight: Math.max.apply(null, contentChildren.map(child => child.implicitHeight || 0)) From 0e3b3eceb664f183e9cae63f838c37a60ed11e87 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sun, 14 Dec 2025 13:48:37 +0300 Subject: [PATCH 103/154] digital clock: add font.family option --- dots/.config/quickshell/ii/modules/common/Config.qml | 1 + .../ii/background/widgets/clock/DigitalClock.qml | 2 ++ .../ii/modules/settings/BackgroundConfig.qml | 10 ++++++++++ 3 files changed, 13 insertions(+) diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 3518b3dbd..3dfc6dd30 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -191,6 +191,7 @@ Singleton { property bool animateChange: true property bool vertical: false property JsonObject font: JsonObject { + property string family: "Google Sans Flex" property real weight: 350 property real width: 100 property real size: 90 diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 233cad7b1..aff44ebd2 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -31,6 +31,7 @@ ColumnLayout { font { pixelSize: Config.options.background.widgets.clock.digital.font.size weight: Config.options.background.widgets.clock.digital.font.weight + family: Config.options.background.widgets.clock.digital.font.family variableAxes: ({ "wdth": Config.options.background.widgets.clock.digital.font.width, "ROND": Config.options.background.widgets.clock.digital.font.roundness @@ -49,6 +50,7 @@ ColumnLayout { font { pixelSize: Config.options.background.widgets.clock.digital.font.size weight: Config.options.background.widgets.clock.digital.font.weight + family: Config.options.background.widgets.clock.digital.font.family variableAxes: ({ "wdth": Config.options.background.widgets.clock.digital.font.width, "ROND": Config.options.background.widgets.clock.digital.font.roundness diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index 697c40274..e6aa0aaa7 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -219,6 +219,16 @@ ContentPage { } } + MaterialTextArea { + Layout.fillWidth: true + placeholderText: Translation.tr("Font family (e.g., Google Sans Flex)") + text: Config.options.background.widgets.clock.digital.font.family + wrapMode: TextEdit.Wrap + onTextChanged: { + Config.options.background.widgets.clock.digital.font.family = text; + } + } + ConfigSlider { text: Translation.tr("Font weight") From 2ef8342187c48a59a3ef01aae59aeae768d77cb0 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sun, 14 Dec 2025 13:51:38 +0300 Subject: [PATCH 104/154] fix(digital clock): properly define clock color --- .../ii/modules/ii/background/widgets/clock/ClockWidget.qml | 4 +++- .../ii/modules/ii/background/widgets/clock/DigitalClock.qml | 5 +++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml index 82556a3ed..4b41634a4 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml @@ -63,7 +63,9 @@ AbstractBackgroundWidget { anchors.horizontalCenter: parent.horizontalCenter shown: root.clockStyle === "digital" && (root.shouldShow) fade: false - sourceComponent: DigitalClock {} + sourceComponent: DigitalClock { + colText: root.colText + } } StatusRow { anchors.horizontalCenter: parent.horizontalCenter diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index aff44ebd2..9e2613ee5 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -15,6 +15,7 @@ ColumnLayout { spacing: 6 property bool isVertical: Config.options.background.widgets.clock.digital.vertical + property color colText: Appearance.colors.colOnSecondaryContainer Item { Layout.fillWidth: true @@ -24,6 +25,7 @@ ColumnLayout { ClockText { id: timeTextTop text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time + color: clockColumn.colText anchors { top: parent.top horizontalCenter: parent.horizontalCenter @@ -42,6 +44,7 @@ ColumnLayout { id: timeTextBottom text: clockColumn.isVertical ? DateTime.time.substring(3, 5) : "" visible: clockColumn.isVertical + color: clockColumn.colText anchors { bottom: parent.bottom @@ -63,12 +66,14 @@ ColumnLayout { visible: Config.options.background.widgets.clock.digital.showDate Layout.topMargin: clockColumn.isVertical ? -10 : 0 text: DateTime.longDate + color: clockColumn.colText } ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 font.pixelSize: Appearance.font.pixelSize.normal text: Config.options.background.widgets.clock.quote.text animateChange: false + color: clockColumn.colText } } From 607d6056c1f55f3e9e2e24d39d00569330c90b82 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sun, 14 Dec 2025 14:07:01 +0300 Subject: [PATCH 105/154] fix(digital clock): layout and handle properties better --- .../ii/background/widgets/clock/ClockText.qml | 2 - .../background/widgets/clock/ClockWidget.qml | 2 + .../background/widgets/clock/DigitalClock.qml | 80 +++++++++---------- 3 files changed, 38 insertions(+), 46 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml index 39267b83d..6175c98a5 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -5,7 +5,6 @@ import QtQuick.Layouts StyledText { Layout.fillWidth: true - horizontalAlignment: root.textHorizontalAlignment font { family: Appearance.font.family.expressive pixelSize: 20 @@ -13,7 +12,6 @@ StyledText { variableAxes: ({}) styleName: "" } - color: root.colText style: Text.Raised styleColor: Appearance.colors.colShadow animateChange: Config.options.background.widgets.clock.digital.animateChange diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml index 4b41634a4..964755269 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockWidget.qml @@ -65,6 +65,7 @@ AbstractBackgroundWidget { fade: false sourceComponent: DigitalClock { colText: root.colText + textHorizontalAlignment: root.textHorizontalAlignment } } StatusRow { @@ -153,6 +154,7 @@ AbstractBackgroundWidget { ClockText { id: statusTextWidget color: statusTextRow.textColor + horizontalAlignment: root.textHorizontalAlignment anchors.verticalCenter: statusTextRow.verticalCenter font { pixelSize: Appearance.font.pixelSize.large diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 9e2613ee5..9709789d8 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -12,68 +12,60 @@ import Quickshell.Io ColumnLayout { id: clockColumn - spacing: 6 + spacing: 4 property bool isVertical: Config.options.background.widgets.clock.digital.vertical property color colText: Appearance.colors.colOnSecondaryContainer + property string textHorizontalAlignment: Text.AlignHCenter - Item { - Layout.fillWidth: true - implicitHeight: timeTextTop.font.pixelSize + (clockColumn.isVertical ? timeTextBottom.font.pixelSize + 10 : 0) - implicitWidth: Math.max(timeTextTop.paintedWidth, timeTextBottom.paintedWidth) - - ClockText { - id: timeTextTop - text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time - color: clockColumn.colText - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - } - font { - pixelSize: Config.options.background.widgets.clock.digital.font.size - weight: Config.options.background.widgets.clock.digital.font.weight - family: Config.options.background.widgets.clock.digital.font.family - variableAxes: ({ - "wdth": Config.options.background.widgets.clock.digital.font.width, - "ROND": Config.options.background.widgets.clock.digital.font.roundness - }) - } + ClockText { + id: timeTextTop + text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time + color: root.colText + horizontalAlignment: Text.AlignHCenter + font { + pixelSize: Config.options.background.widgets.clock.digital.font.size + weight: Config.options.background.widgets.clock.digital.font.weight + family: Config.options.background.widgets.clock.digital.font.family + variableAxes: ({ + "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness + }) } - ClockText { - id: timeTextBottom - text: clockColumn.isVertical ? DateTime.time.substring(3, 5) : "" - visible: clockColumn.isVertical - color: clockColumn.colText + } - anchors { - bottom: parent.bottom - horizontalCenter: parent.horizontalCenter - } - font { - pixelSize: Config.options.background.widgets.clock.digital.font.size - weight: Config.options.background.widgets.clock.digital.font.weight - family: Config.options.background.widgets.clock.digital.font.family - variableAxes: ({ - "wdth": Config.options.background.widgets.clock.digital.font.width, - "ROND": Config.options.background.widgets.clock.digital.font.roundness - }) - } + ClockText { + id: timeTextBottom + text: DateTime.time.substring(3, 5) + visible: clockColumn.isVertical + color: root.colText + Layout.topMargin: -40 + horizontalAlignment: Text.AlignHCenter + font { + pixelSize: Config.options.background.widgets.clock.digital.font.size + weight: Config.options.background.widgets.clock.digital.font.weight + family: Config.options.background.widgets.clock.digital.font.family + variableAxes: ({ + "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness + }) } } ClockText { visible: Config.options.background.widgets.clock.digital.showDate - Layout.topMargin: clockColumn.isVertical ? -10 : 0 + Layout.topMargin: -20 text: DateTime.longDate - color: clockColumn.colText + color: root.colText + horizontalAlignment: root.textHorizontalAlignment } ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 font.pixelSize: Appearance.font.pixelSize.normal text: Config.options.background.widgets.clock.quote.text animateChange: false - color: clockColumn.colText + color: root.colText + horizontalAlignment: root.textHorizontalAlignment } } From c92832ff9513aebc809d578129cf409cc7e07cc6 Mon Sep 17 00:00:00 2001 From: vaguesyntax Date: Sun, 14 Dec 2025 14:18:03 +0300 Subject: [PATCH 106/154] digital clock: fix properties (again) --- .../ii/background/widgets/clock/DigitalClock.qml | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 9709789d8..6506b3e1c 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -16,12 +16,12 @@ ColumnLayout { property bool isVertical: Config.options.background.widgets.clock.digital.vertical property color colText: Appearance.colors.colOnSecondaryContainer - property string textHorizontalAlignment: Text.AlignHCenter + property var textHorizontalAlignment: Text.AlignHCenter ClockText { id: timeTextTop text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time - color: root.colText + color: clockColumn.colText horizontalAlignment: Text.AlignHCenter font { pixelSize: Config.options.background.widgets.clock.digital.font.size @@ -38,7 +38,7 @@ ColumnLayout { id: timeTextBottom text: DateTime.time.substring(3, 5) visible: clockColumn.isVertical - color: root.colText + color: clockColumn.colText Layout.topMargin: -40 horizontalAlignment: Text.AlignHCenter font { @@ -56,16 +56,16 @@ ColumnLayout { visible: Config.options.background.widgets.clock.digital.showDate Layout.topMargin: -20 text: DateTime.longDate - color: root.colText - horizontalAlignment: root.textHorizontalAlignment + color: clockColumn.colText + horizontalAlignment: clockColumn.textHorizontalAlignment } ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 font.pixelSize: Appearance.font.pixelSize.normal text: Config.options.background.widgets.clock.quote.text animateChange: false - color: root.colText - horizontalAlignment: root.textHorizontalAlignment + color: clockColumn.colText + horizontalAlignment: clockColumn.textHorizontalAlignment } } From 663eb1896ab1ab894c3825c4b50274d2b4c9fac9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 23:40:32 +0100 Subject: [PATCH 107/154] waffles: overview: more proper anims and dragging functionality ITS FUCKING WORKING AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA --- .../waffle/taskView/TaskViewContent.qml | 125 +++++++++++++++--- .../waffle/taskView/TaskViewWindow.qml | 51 +++++-- .../waffle/taskView/TaskViewWorkspace.qml | 16 ++- .../waffle/taskView/WaffleTaskView.qml | 18 ++- .../quickshell/ii/services/HyprlandData.qml | 1 + 5 files changed, 168 insertions(+), 43 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index cbb61b2fe..31d0a0945 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -16,29 +16,43 @@ Rectangle { id: root color: ColorUtils.transparentize(Looks.colors.bg1Base, 0.5) + property bool draggingWindow: false property real openProgress: 0 + property Item hoveredWorkspace: null + signal closed Component.onCompleted: { openAnim.start(); } + function close() { + closeAnim.start(); + } PropertyAnimation { id: openAnim target: root property: "openProgress" to: 1 - duration: 200 + duration: 250 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn } - PropertyAnimation { + SequentialAnimation { id: closeAnim - target: root - property: "openProgress" - to: 0 - duration: 200 - easing.type: Easing.BezierSpline - easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + + PropertyAnimation { + target: root + property: "openProgress" + to: 0 + duration: 200 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + ScriptAction { + script: { + root.closed(); + } + } } // Windows @@ -81,8 +95,18 @@ Rectangle { return resultLayout; } + MouseArea { + z: 0 + anchors.fill: parent + onClicked: { + GlobalStates.overviewOpen = false; + } + } + // Windows WListView { + id: windowListView + z: root.openProgress == 1 ? 2 : 1 anchors { left: parent.left right: parent.right @@ -96,9 +120,8 @@ Rectangle { rightMargin: root.padding height: Math.min(contentHeight + topMargin + bottomMargin, root.height - (wsBorder.height + 16)) - interactive: height < contentHeight - - clip: true + interactive: (height < contentHeight) && !root.draggingWindow + clip: root.openProgress > 0.99 && !root.draggingWindow model: ScriptModel { values: root.arrangedToplevels @@ -107,7 +130,7 @@ Rectangle { id: clientRow required property var modelData spacing: root.spacing - anchors.horizontalCenter: parent.horizontalCenter + anchors.horizontalCenter: parent?.horizontalCenter ?? undefined Repeater { model: ScriptModel { @@ -117,24 +140,61 @@ Rectangle { id: clientGridArea required property int index required property var modelData - implicitWidth: windowItem.implicitWidth - implicitHeight: windowItem.implicitHeight + implicitWidth: windowItem.openedSize.width + implicitHeight: windowItem.openedSize.height + windowItem.titleBarImplicitHeight TaskViewWindow { id: windowItem - z: 9999 - drag { - target: this + z: Drag.active ? 2 : 1 + opacity: openAnim.running ? root.openProgress : 1 + + property int mappedX: { + // print("AAAWAWAAWAWWA: ", -(clientRow.x + clientGridArea.x + root.padding)); + var rootPosToThis = -(clientRow.x + clientGridArea.x + root.padding); + return rootPosToThis + hyprlandClient.at[0]; + } + property int mappedY: { + // print("AAAWAWAAWAWWA YYYY YUIUSDFOIU: ", clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight) + var rootPosToThis = -(clientRow.y + windowListView.y + root.padding + windowItem.titleBarImplicitHeight); + return rootPosToThis + hyprlandClient.at[1]; + } + property int openedX: 0 + property int openedY: 0 + // property int openedX: Drag.active ? (dragHandler.xAxis.activeValue) : 0 + // property int openedY: Drag.active ? (dragHandler.yAxis.activeValue) : 0 + scaleSize: (root.openProgress > 0 && !closeAnim.running) + x: mappedX + (openedX - mappedX) * root.openProgress + y: mappedY + (openedY - mappedY) * root.openProgress + + droppable: root.hoveredWorkspace !== null + Drag.active: dragHandler.active + Drag.hotSpot.x: mouseX + Drag.hotSpot.y: mouseY + + DragHandler { + id: dragHandler + target: null + xAxis.onActiveValueChanged: { + windowItem.openedX = dragHandler.xAxis.activeValue; + } + yAxis.onActiveValueChanged: { + windowItem.openedY = dragHandler.yAxis.activeValue; + } onActiveChanged: { - if (drag.active) { - parent = root; + if (active) { + root.draggingWindow = true; } else { - parent = clientGridArea; - x = 0; - y = 0; + root.draggingWindow = false; + if (root.hoveredWorkspace !== null && root.hoveredWorkspace.workspace !== windowItem.hyprlandClient.workspace.id) { + Hyprland.dispatch(`movetoworkspacesilent ${root.hoveredWorkspace.workspace}, address:${windowItem.hyprlandClient.address}`); + } else { + windowItem.openedX = 0; + windowItem.openedY = 0; + } } } } + Layout.alignment: Qt.AlignTop maxHeight: root.maxWindowHeight maxWidth: root.maxWindowWidth @@ -148,6 +208,7 @@ Rectangle { // Workspaces Rectangle { id: wsBorder + z: root.openProgress == 1 ? 1 : 2 property real sourceEdgeMargin: -(height + 8) + root.openProgress * (height + 16) anchors { left: parent.left @@ -210,9 +271,29 @@ Rectangle { } } delegate: TaskViewWorkspace { + id: workspaceItem required property int index workspace: index + 1 newWorkspace: index == workspaceIndexModel.count - 1 + + droppable: root.hoveredWorkspace === workspaceItem + DropArea { + anchors.fill: parent + onEntered: drag => { + root.hoveredWorkspace = workspaceItem; + } + onExited: { + if (root.hoveredWorkspace === workspaceItem) { + root.hoveredWorkspace = null; + } + } + } + + onClicked: { + GlobalStates.overviewOpen = false; + root.closed(); // Close immediately to avoid weird animations + Hyprland.dispatch(`workspace ${workspaceItem.workspace}`); + } } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml index d8bf4b45b..0cab91fe5 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -25,47 +25,60 @@ WMouseAreaButton { property string iconName: AppSearch.guessIcon(hyprlandClient?.class) - color: containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase - borderColor: Looks.colors.bg2Border + color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase) + borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0) radius: Looks.radius.xLarge - property size size: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight) + property real titleBarImplicitHeight: titleBar.implicitHeight + property bool scaleSize: true + property size openedSize: WindowLayout.scaleWindow(hyprlandClient, maxWidth, maxHeight); + property size fullSize: Qt.size(hyprlandClient?.size[0] ?? maxWidth, hyprlandClient?.size[1] ?? maxHeight) + property size size: scaleSize ? openedSize : fullSize implicitWidth: Math.max(Math.round(contentItem.implicitWidth), 138) implicitHeight: Math.round(contentItem.implicitHeight) layer.enabled: true layer.effect: OpacityMask { - maskSource: Rectangle { + maskSource: Item { width: root.background.width height: root.background.height - radius: root.background.radius + Rectangle { + radius: root.background.radius + anchors { + fill: parent + topMargin: root.drag.active ? root.titleBarImplicitHeight : 0 + } + } } } - scale: (root.pressedButtons & Qt.LeftButton) ? 0.95 : 1 + property bool droppable: false + scale: (root.pressedButtons & Qt.LeftButton || root.Drag.active) ? (droppable ? 0.4 : 0.95) : 1 Behavior on scale { NumberAnimation { id: scaleAnim - duration: 300 + duration: 200 easing.type: Easing.OutExpo } } function closeWindow() { - Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`) + Hyprland.dispatch(`closewindow address:${root.hyprlandClient?.address}`); } acceptedButtons: Qt.LeftButton | Qt.MiddleButton | Qt.RightButton - onClicked: (event) => { + onClicked: event => { if (event.button === Qt.LeftButton) { - GlobalStates.overviewOpen = false - Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`) + GlobalStates.overviewOpen = false; + Hyprland.dispatch(`focuswindow address:${root.hyprlandClient?.address}`); GlobalStates.overviewOpen = false; } else if (event.button === Qt.MiddleButton) { root.closeWindow(); event.accepted = true; } else if (event.button === Qt.RightButton) { - if (!windowMenu.visible) windowMenu.popup(); - else windowMenu.close(); + if (!windowMenu.visible) + windowMenu.popup(); + else + windowMenu.close(); } } @@ -77,6 +90,8 @@ WMouseAreaButton { spacing: 0 RowLayout { + id: titleBar + opacity: root.drag.active ? 0 : 1 spacing: 8 WAppIcon { Layout.leftMargin: 10 @@ -104,6 +119,14 @@ WMouseAreaButton { Layout.alignment: Qt.AlignHCenter implicitWidth: Math.round(root.size.width) implicitHeight: Math.round(root.size.height) + constraintSize: Qt.size(Math.round(root.size.width), Math.round(root.size.height)) + + Behavior on implicitWidth { + animation: Looks.transition.enter.createObject(this) + } + Behavior on implicitHeight { + animation: Looks.transition.enter.createObject(this) + } captureSource: root.toplevel ?? null live: true @@ -120,7 +143,7 @@ WMouseAreaButton { icon.name: isPinned ? "checkmark" : "empty" text: Translation.tr("Show this window on all desktops") onTriggered: { - Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`) + Hyprland.dispatch(`pin address:${root.hyprlandClient?.address}`); } } Action { diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml index 56451a4e7..8fe3f35bd 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWorkspace.qml @@ -16,6 +16,7 @@ WMouseAreaButton { required property int workspace property bool newWorkspace: false + property bool droppable: false readonly property bool isActiveWorkspace: HyprlandData.activeWorkspace?.id === root.workspace readonly property real screenWidth: QsWindow.window?.width ?? 0 @@ -28,12 +29,7 @@ WMouseAreaButton { height: ListView.view?.height ?? 100 implicitWidth: 244 // for now - onClicked: { - GlobalStates.overviewOpen = false; - Hyprland.dispatch(`workspace ${root.workspace}`); - } - - colBackground: ColorUtils.transparentize(Looks.colors.bg2, isActiveWorkspace ? 0 : 1) + colBackground: ColorUtils.transparentize(Looks.colors.bg2, (isActiveWorkspace || droppable) ? 0 : 1) Behavior on color { animation: Looks.transition.color.createObject(this) } @@ -119,6 +115,14 @@ WMouseAreaButton { icon: "add" } } + + Rectangle { + z: 2 + visible: root.droppable && !root.newWorkspace + anchors.fill: parent + color: Looks.colors.accent + opacity: 0.2 + } } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml index 75e71ccb1..fc145f221 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml @@ -23,7 +23,14 @@ Scope { Loader { id: panelLoader required property var modelData - active: GlobalStates.overviewOpen + active: false + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (GlobalStates.overviewOpen) + panelLoader.active = true; + } + } sourceComponent: PanelWindow { id: root property string searchingText: "" @@ -44,7 +51,16 @@ Scope { } TaskViewContent { + id: taskViewContent anchors.fill: parent + + Connections { + target: GlobalStates + function onOverviewOpenChanged() { + if (!GlobalStates.overviewOpen) taskViewContent.close(); + } + } + onClosed: panelLoader.active = false; } } } diff --git a/dots/.config/quickshell/ii/services/HyprlandData.qml b/dots/.config/quickshell/ii/services/HyprlandData.qml index 7bb437c84..5ec7bd68d 100644 --- a/dots/.config/quickshell/ii/services/HyprlandData.qml +++ b/dots/.config/quickshell/ii/services/HyprlandData.qml @@ -88,6 +88,7 @@ Singleton { function onRawEvent(event) { // console.log("Hyprland raw event:", event.name); + if (["openlayer", "closelayer", "screencast"].includes(event.name)) return; updateAll() } } From 70363ab886cc880bf61a89043dab6748a9c66fa9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sun, 14 Dec 2025 23:46:29 +0100 Subject: [PATCH 108/154] make panel family cycle keybind less misclickable --- dots/.config/hypr/hyprland/keybinds.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/hypr/hyprland/keybinds.conf b/dots/.config/hypr/hyprland/keybinds.conf index 6032ab65a..0cca263f1 100644 --- a/dots/.config/hypr/hyprland/keybinds.conf +++ b/dots/.config/hypr/hyprland/keybinds.conf @@ -53,7 +53,7 @@ bindd = Ctrl+Super, T, Toggle wallpaper selector, global, quickshell:wallpaperSe bindd = Ctrl+Super+Alt, T, Select random wallpaper, global, quickshell:wallpaperSelectorRandom # Random wallpaper bindd = Ctrl+Super, T, Change wallpaper, exec, qs -c $qsConfig ipc call TEST_ALIVE || ~/.config/quickshell/$qsConfig/scripts/colors/switchwall.sh # [hidden] Change wallpaper (fallback) bind = Ctrl+Super, R, exec, killall ags agsv1 gjs ydotool qs quickshell; qs -c $qsConfig & # Restart widgets -bind = Super+Alt, W, global, quickshell:panelFamilyCycle # Cycle panel family +bind = Ctrl+Super, P, global, quickshell:panelFamilyCycle # Cycle panel family ##! Utilities # Screenshot, Record, OCR, Color picker, Clipboard history From 60b2225cc65ba735014ce1ccd0f8ab0d2d0d424f Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 15 Dec 2025 00:31:10 +0100 Subject: [PATCH 109/154] waffles: taskview: esc to close --- .../modules/waffle/taskView/WaffleTaskView.qml | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml index fc145f221..05a2ccf40 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/WaffleTaskView.qml @@ -40,7 +40,7 @@ Scope { WlrLayershell.namespace: "quickshell:wTaskView" WlrLayershell.layer: WlrLayer.Overlay - // WlrLayershell.keyboardFocus: GlobalStates.overviewOpen ? WlrKeyboardFocus.OnDemand : WlrKeyboardFocus.None + WlrLayershell.keyboardFocus: WlrKeyboardFocus.OnDemand color: "transparent" anchors { @@ -54,13 +54,23 @@ Scope { id: taskViewContent anchors.fill: parent + Component.onCompleted: { + taskViewContent.forceActiveFocus(); + } + Keys.onPressed: event => { + if (event.key === Qt.Key_Escape) { + GlobalStates.overviewOpen = false; + } + } + Connections { target: GlobalStates function onOverviewOpenChanged() { - if (!GlobalStates.overviewOpen) taskViewContent.close(); + if (!GlobalStates.overviewOpen) + taskViewContent.close(); } } - onClosed: panelLoader.active = false; + onClosed: panelLoader.active = false } } } From 36ff18bfe350b7565497c297cdfa1727e89e52dc Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 15 Dec 2025 00:31:22 +0100 Subject: [PATCH 110/154] waffles: lock: open anim --- .../modules/common/panels/lock/LockScreen.qml | 1 - .../ii/modules/waffle/lock/WaffleLock.qml | 48 +++++++++++++++++-- 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml index 9e4b9bd94..5a1b24d7e 100644 --- a/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml +++ b/dots/.config/quickshell/ii/modules/common/panels/lock/LockScreen.qml @@ -92,7 +92,6 @@ Scope { WlSessionLock { id: lock locked: GlobalStates.screenLocked - surface: root.sessionLockSurface } diff --git a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml index 581ce9152..b1ff6f353 100644 --- a/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml +++ b/dots/.config/quickshell/ii/modules/waffle/lock/WaffleLock.qml @@ -36,15 +36,34 @@ LockScreen { Image { id: bg z: 0 - anchors.fill: parent + width: parent.width + height: parent.height + onStatusChanged: { + if (status === Image.Ready) { + print("Lock wallpaper loaded"); + print(lockSurfaceItem.height); + y = -lockSurfaceItem.height; + openAnim.restart(); + } + } sourceSize: Qt.size(lockSurfaceItem.width, lockSurfaceItem.height) source: Config.options.background.wallpaperPath fillMode: Image.PreserveAspectCrop + + PropertyAnimation { + id: openAnim + target: bg + property: "y" + to: 0 + duration: 350 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } } GaussianBlur { z: 1 - anchors.fill: parent + anchors.fill: bg source: bg radius: 100 samples: radius * 2 + 1 @@ -67,7 +86,7 @@ LockScreen { Interactables { id: interactables z: 2 - anchors.fill: parent + anchors.fill: bg } } @@ -83,12 +102,31 @@ LockScreen { // } function switchToFocusedView() { - root.passwordView = true; + switchToPasswordViewAnim.restart(); + } + + SequentialAnimation { + id: switchToPasswordViewAnim + PropertyAnimation { + target: unfocusedContent + property: "y" + from: 0 + to: -height * 1.1 + duration: 250 + easing.type: Easing.BezierSpline + easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn + } + ScriptAction { + script: { + root.passwordView = true; + } + } } Item { id: unfocusedContent - anchors.fill: parent + width: parent.width + height: parent.height visible: !root.passwordView ClockTextGroup { anchors { From 409fc94d6e769be3d14b941e309fcb94968b793d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:18:28 +0100 Subject: [PATCH 111/154] waffle: start menu: fix unconsistency that's fixed in the real thing no way i intentionally make it weird so it matches and then they make it not weird lol --- .../quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml index fb506dd49..64ad321c6 100644 --- a/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/startMenu/StartMenuContent.qml @@ -86,7 +86,7 @@ WBarAttachedPanelContent { id: searchBar Layout.fillWidth: true implicitWidth: 832 // TODO: Make sizes naturally inferred - horizontalPadding: root.searching ? 24 : 32 + horizontalPadding: 32 // verticalPadding: root.searching ? 32 : 16 // TODO: make this not nuke the panel Synchronizer on searching { property alias target: root.searching From f65f805fe2308ad4d22cb08655801e56384f0dff Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 15 Dec 2025 09:18:55 +0100 Subject: [PATCH 112/154] waffles: taskview: fix weird close anim trajectory --- .../quickshell/ii/modules/waffle/taskView/TaskViewContent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index 31d0a0945..4dc6f5bf7 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -44,7 +44,7 @@ Rectangle { target: root property: "openProgress" to: 0 - duration: 200 + duration: 250 easing.type: Easing.BezierSpline easing.bezierCurve: Looks.transition.easing.bezierCurve.easeIn } From 2b9e5b1a6ff79c0b13a77ff487cd9dbd549637cb Mon Sep 17 00:00:00 2001 From: jwihardi Date: Mon, 15 Dec 2025 19:15:42 -0500 Subject: [PATCH 113/154] added hyprland-qtutils patch --- .../hyprland-qtutils-private.patch | 33 ++++++++++++ sdata/dist-gentoo/keywords-user | 53 +++++++++++++++++++ sdata/dist-gentoo/qt-keywords-user | 27 ++++++++++ 3 files changed, 113 insertions(+) create mode 100644 sdata/dist-gentoo/hyprland-qtutils-private.patch create mode 100644 sdata/dist-gentoo/keywords-user create mode 100644 sdata/dist-gentoo/qt-keywords-user diff --git a/sdata/dist-gentoo/hyprland-qtutils-private.patch b/sdata/dist-gentoo/hyprland-qtutils-private.patch new file mode 100644 index 000000000..6ca55104f --- /dev/null +++ b/sdata/dist-gentoo/hyprland-qtutils-private.patch @@ -0,0 +1,33 @@ +--- a/utils/dialog/CMakeLists.txt ++++ b/utils/dialog/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) +--- a/utils/donate-screen/CMakeLists.txt ++++ b/utils/donate-screen/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) +--- a/utils/update-screen/CMakeLists.txt ++++ b/utils/update-screen/CMakeLists.txt +@@ -8,7 +8,7 @@ + set(CMAKE_CXX_STANDARD 23) + set(CMAKE_CXX_STANDARD_REQUIRED ON) + +-find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient) ++find_package(Qt6 6.5 REQUIRED COMPONENTS Widgets Quick QuickControls2 WaylandClient WaylandClientPrivate) + find_package(PkgConfig REQUIRED) + + pkg_check_modules(hyprutils REQUIRED IMPORTED_TARGET hyprutils) diff --git a/sdata/dist-gentoo/keywords-user b/sdata/dist-gentoo/keywords-user new file mode 100644 index 000000000..d4fcf1b4e --- /dev/null +++ b/sdata/dist-gentoo/keywords-user @@ -0,0 +1,53 @@ +app-misc/illogical-impulse-audio ~amd64 +app-misc/illogical-impulse-backlight ~amd64 +app-misc/illogical-impulse-basic ~amd64 +app-misc/illogical-impulse-bibata-modern-classic-bin ~amd64 +app-misc/illogical-impulse-fonts-themes ~amd64 +app-misc/illogical-impulse-hyprland ~amd64 +app-misc/illogical-impulse-kde ~amd64 +app-misc/illogical-impulse-microtex-git ~amd64 +app-misc/illogical-impulse-oneui4-icons-git ~amd64 +app-misc/illogical-impulse-portal ~amd64 +app-misc/illogical-impulse-python ~amd64 +app-misc/illogical-impulse-quickshell-git ~amd64 +app-misc/illogical-impulse-screencapture ~amd64 +app-misc/illogical-impulse-toolkit ~amd64 +app-misc/illogical-impulse-widgets ~amd64 +x11-misc/matugen ~amd64 +media-fonts/twemoji ~amd64 +app-misc/brightnessctl ~amd64 +app-misc/cliphist ~amd64 +gui-apps/hypridle ~amd64 +gui-apps/hyprlock ~amd64 +gui-apps/hyprpicker ~amd64 +gui-apps/hyprsunset ~amd64 +gui-libs/xdg-desktop-portal-hyprland ~amd64 +gui-apps/hyprshot ~amd64 +gui-apps/wf-recorder ~amd64 +gui-apps/wtype ~amd64 +gui-apps/fuzzel ~amd64 +gui-apps/quickshell ** ~amd64 +gui-apps/wlogout ~amd64 +dev-cpp/sdbus-c++ ~amd64 +dev-libs/hyprland-protocols ~amd64 +gui-libs/aquamarine ~amd64 +gui-libs/hyprutils ~amd64 +dev-util/hyprwayland-scanner ~amd64 +dev-libs/hyprlang ~amd64 +dev-libs/hyprgraphics ~amd64 +gui-libs/hyprcursor ~amd64 +gui-libs/hyprland-qt-support ~amd64 +gui-libs/hyprland-qtutils ~amd64 +gui-wm/hyprland ~amd64 +x11-libs/libxkbcommon ~amd64 +dev-util/breakpad ~amd64 +dev-libs/linux-syscall-support ~amd64 +dev-embedded/libdisasm ~amd64 +kde-plasma/breeze-plus ~amd64 +x11-themes/darkly ~amd64 +x11-themes/adw-gtk3 ~amd64 +media-fonts/space-grotesk ~amd64 +media-fonts/material-symbols-variable ** ~amd64 +media-fonts/readex-pro ~amd64 +media-fonts/rubik-vf ~amd64 +app-misc/songrec ~amd64 diff --git a/sdata/dist-gentoo/qt-keywords-user b/sdata/dist-gentoo/qt-keywords-user new file mode 100644 index 000000000..9fadb29db --- /dev/null +++ b/sdata/dist-gentoo/qt-keywords-user @@ -0,0 +1,27 @@ +dev-qt/qtbase ~amd64 +dev-qt/qtdeclarative ~amd64 +dev-qt/qtlanguageserver ~amd64 +dev-qt/qtsvg ~amd64 +dev-qt/qtshadertools ~amd64 +dev-qt/qttranslations ~amd64 +dev-qt/qttools ~amd64 +dev-qt/qt5compat ~amd64 +dev-qt/qtcore ~amd64 +dev-qt/qtdbus ~amd64 +dev-qt/qtgraphicaleffects ~amd64 +dev-qt/qtgui ~amd64 +dev-qt/qtimageformats ~amd64 +dev-qt/qtlocation ~amd64 +dev-qt/qtmultimedia ~amd64 +dev-qt/qtnetwork ~amd64 +dev-qt/qtpositioning ~amd64 +dev-qt/qtquick3d ~amd64 +dev-qt/qtquickcontrols2 ~amd64 +dev-qt/qtquicktimeline ~amd64 +dev-qt/qtsensors ~amd64 +dev-qt/qtspeech ~amd64 +dev-qt/qttest ~amd64 +dev-qt/qtvirtualkeyboard ~amd64 +dev-qt/qtwayland ~amd64 +dev-qt/qtwayland ~amd64 +dev-qt/qtwidgets ~amd64 From 0cff92d02c42245497ec7ca99f78885d48243de0 Mon Sep 17 00:00:00 2001 From: jwihardi <84292598+jwihardi@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:30:10 -0500 Subject: [PATCH 114/154] Document hyprland-qtutils emerging issues and fixes Added troubleshooting steps for emerging hyprland-qtutils, including a patch suggestion. --- sdata/dist-gentoo/README.md | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/sdata/dist-gentoo/README.md b/sdata/dist-gentoo/README.md index 630a3a178..df06b41fc 100644 --- a/sdata/dist-gentoo/README.md +++ b/sdata/dist-gentoo/README.md @@ -52,3 +52,28 @@ end - The Hyprland live ebuild sometimes has linkage issues, deleting _Hyprland_ and _hyprland_ from `/usr/bin/` and then re-emerging usually fixes this. - When emerging Hyprland if you get an issue relating to `undefined reference to ``Hyprutils::Math::Vector2D::˜Vector2D()`` ` - Clear the cache folder (`rm -fr /var/tmp/portage/gui-wm/hyprland*`) then try again +- If emerging ``hyprland-qtutils`` fails and gives you something like this... + ```cmake + CMake Error at utils/dialog/CMakeLists.txt:26 (target_link_libraries): + Target "hyprland-dialog" links to: + Qt6::WaylandClientPrivate + but the target was not found. Possible reasons include: + * There is a typo in the target name. + * A find_package call is missing for an IMPORTED target. + * An ALIAS target is missing. + CMake Error at utils/update-screen/CMakeLists.txt:34 (target_link_libraries): + Target "hyprland-update-screen" links to: + Qt6::WaylandClientPrivate + but the target was not found. Possible reasons include: + * There is a typo in the target name. + * A find_package call is missing for an IMPORTED target. + * An ALIAS target is missing. + CMake Error at utils/donate-screen/CMakeLists.txt:32 (target_link_libraries): + Target "hyprland-donate-screen" links to: + Qt6::WaylandClientPrivate + but the target was not found. Possible reasons include: + * There is a typo in the target name. + * A find_package call is missing for an IMPORTED target. + * An ALIAS target is missing. + ``` + Try putting ``sdata/dist-gentoo/hyprland-qtutils-private.patch`` into ``/etc/portage/patches/gui-libs/hyprland-qtutils/``. From 4b1e02dda683e3aceb9b01bcec9874d4250a2ec0 Mon Sep 17 00:00:00 2001 From: jwihardi Date: Mon, 15 Dec 2025 19:45:03 -0500 Subject: [PATCH 115/154] removed install prints --- sdata/dist-gentoo/install-deps.sh | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/sdata/dist-gentoo/install-deps.sh b/sdata/dist-gentoo/install-deps.sh index 0aadbf81b..9d79c157a 100644 --- a/sdata/dist-gentoo/install-deps.sh +++ b/sdata/dist-gentoo/install-deps.sh @@ -1,23 +1,12 @@ printf "${STY_YELLOW}" printf "============WARNING/NOTE (1)============\n" -printf "GCC in use: $(which gcc)\n" -printf "GCC version info: $(gcc --version | grep gcc)\n" -printf "GCC version number: $(gcc --version | grep gcc | awk '{print $3}')\n" -printf "GCC-15>= is required for Hyprland\n" -printf "If you have GCC-15>= and it's currently set then you can safely ignore this\n" -printf "If not, you must ensure you are using the correct GCC version and set it (gcc-config )\n" -printf "It is heavily recommended to re-emerge @world with an empty tree after changing GCC version (emerge -e @world)\n\n" -printf "${STY_RST}" -pause - -printf "${STY_YELLOW}" -printf "============WARNING/NOTE (2)============\n" printf "Ensure you have a global use flag for elogind or systemd in your make.conf for simplicity\n" printf "Or you can manually add the use flags for each package that requires it\n" printf "${STY_RST}" pause printf "${STY_YELLOW}" +printf "============WARNING/NOTE (2)============\n" printf "https://github.com/end-4/dots-hyprland/blob/main/sdata/dist-gentoo/README.md\n" printf "Checkout the above README for potential bug fixes or additional information\n\n" printf "${STY_RST}" From dd00908026b0a95ec61e561ce05b8a67cb4abb9e Mon Sep 17 00:00:00 2001 From: jwihardi <84292598+jwihardi@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:56:23 -0500 Subject: [PATCH 116/154] Update README with patch credit information Added patch credit for hyprland-qtutils. --- sdata/dist-gentoo/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sdata/dist-gentoo/README.md b/sdata/dist-gentoo/README.md index df06b41fc..84153c779 100644 --- a/sdata/dist-gentoo/README.md +++ b/sdata/dist-gentoo/README.md @@ -77,3 +77,4 @@ end * An ALIAS target is missing. ``` Try putting ``sdata/dist-gentoo/hyprland-qtutils-private.patch`` into ``/etc/portage/patches/gui-libs/hyprland-qtutils/``. + - Patch Credit: fedeliallalinea on https://forums.gentoo.org/viewtopic-p-8874098.html From 61f0f0dc97bf2a1ebb35796fb7bbf713e9d0f71f Mon Sep 17 00:00:00 2001 From: jwihardi Date: Mon, 15 Dec 2025 20:00:49 -0500 Subject: [PATCH 117/154] removed user files, accidentally commited them earlier --- sdata/dist-gentoo/keywords-user | 53 ------------------------------ sdata/dist-gentoo/qt-keywords-user | 27 --------------- 2 files changed, 80 deletions(-) delete mode 100644 sdata/dist-gentoo/keywords-user delete mode 100644 sdata/dist-gentoo/qt-keywords-user diff --git a/sdata/dist-gentoo/keywords-user b/sdata/dist-gentoo/keywords-user deleted file mode 100644 index d4fcf1b4e..000000000 --- a/sdata/dist-gentoo/keywords-user +++ /dev/null @@ -1,53 +0,0 @@ -app-misc/illogical-impulse-audio ~amd64 -app-misc/illogical-impulse-backlight ~amd64 -app-misc/illogical-impulse-basic ~amd64 -app-misc/illogical-impulse-bibata-modern-classic-bin ~amd64 -app-misc/illogical-impulse-fonts-themes ~amd64 -app-misc/illogical-impulse-hyprland ~amd64 -app-misc/illogical-impulse-kde ~amd64 -app-misc/illogical-impulse-microtex-git ~amd64 -app-misc/illogical-impulse-oneui4-icons-git ~amd64 -app-misc/illogical-impulse-portal ~amd64 -app-misc/illogical-impulse-python ~amd64 -app-misc/illogical-impulse-quickshell-git ~amd64 -app-misc/illogical-impulse-screencapture ~amd64 -app-misc/illogical-impulse-toolkit ~amd64 -app-misc/illogical-impulse-widgets ~amd64 -x11-misc/matugen ~amd64 -media-fonts/twemoji ~amd64 -app-misc/brightnessctl ~amd64 -app-misc/cliphist ~amd64 -gui-apps/hypridle ~amd64 -gui-apps/hyprlock ~amd64 -gui-apps/hyprpicker ~amd64 -gui-apps/hyprsunset ~amd64 -gui-libs/xdg-desktop-portal-hyprland ~amd64 -gui-apps/hyprshot ~amd64 -gui-apps/wf-recorder ~amd64 -gui-apps/wtype ~amd64 -gui-apps/fuzzel ~amd64 -gui-apps/quickshell ** ~amd64 -gui-apps/wlogout ~amd64 -dev-cpp/sdbus-c++ ~amd64 -dev-libs/hyprland-protocols ~amd64 -gui-libs/aquamarine ~amd64 -gui-libs/hyprutils ~amd64 -dev-util/hyprwayland-scanner ~amd64 -dev-libs/hyprlang ~amd64 -dev-libs/hyprgraphics ~amd64 -gui-libs/hyprcursor ~amd64 -gui-libs/hyprland-qt-support ~amd64 -gui-libs/hyprland-qtutils ~amd64 -gui-wm/hyprland ~amd64 -x11-libs/libxkbcommon ~amd64 -dev-util/breakpad ~amd64 -dev-libs/linux-syscall-support ~amd64 -dev-embedded/libdisasm ~amd64 -kde-plasma/breeze-plus ~amd64 -x11-themes/darkly ~amd64 -x11-themes/adw-gtk3 ~amd64 -media-fonts/space-grotesk ~amd64 -media-fonts/material-symbols-variable ** ~amd64 -media-fonts/readex-pro ~amd64 -media-fonts/rubik-vf ~amd64 -app-misc/songrec ~amd64 diff --git a/sdata/dist-gentoo/qt-keywords-user b/sdata/dist-gentoo/qt-keywords-user deleted file mode 100644 index 9fadb29db..000000000 --- a/sdata/dist-gentoo/qt-keywords-user +++ /dev/null @@ -1,27 +0,0 @@ -dev-qt/qtbase ~amd64 -dev-qt/qtdeclarative ~amd64 -dev-qt/qtlanguageserver ~amd64 -dev-qt/qtsvg ~amd64 -dev-qt/qtshadertools ~amd64 -dev-qt/qttranslations ~amd64 -dev-qt/qttools ~amd64 -dev-qt/qt5compat ~amd64 -dev-qt/qtcore ~amd64 -dev-qt/qtdbus ~amd64 -dev-qt/qtgraphicaleffects ~amd64 -dev-qt/qtgui ~amd64 -dev-qt/qtimageformats ~amd64 -dev-qt/qtlocation ~amd64 -dev-qt/qtmultimedia ~amd64 -dev-qt/qtnetwork ~amd64 -dev-qt/qtpositioning ~amd64 -dev-qt/qtquick3d ~amd64 -dev-qt/qtquickcontrols2 ~amd64 -dev-qt/qtquicktimeline ~amd64 -dev-qt/qtsensors ~amd64 -dev-qt/qtspeech ~amd64 -dev-qt/qttest ~amd64 -dev-qt/qtvirtualkeyboard ~amd64 -dev-qt/qtwayland ~amd64 -dev-qt/qtwayland ~amd64 -dev-qt/qtwidgets ~amd64 From 252a1055c234ac04589cb42e93408b62d871abdd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Wed, 17 Dec 2025 23:39:44 +0100 Subject: [PATCH 118/154] make install script proceed prompt less confusing --- sdata/lib/functions.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdata/lib/functions.sh b/sdata/lib/functions.sh index 0fe24f0ed..423f45b81 100644 --- a/sdata/lib/functions.sh +++ b/sdata/lib/functions.sh @@ -64,7 +64,7 @@ function showfun(){ function pause(){ if [ ! "$ask" == "false" ];then printf "${STY_FAINT}${STY_SLANT}" - local p; read -p "(Ctrl-C to abort, others to proceed)" p + local p; read -p "(Ctrl-C to abort, Enter to proceed)" p printf "${STY_RST}" fi } From 28d3f6a94a40c836e643a5d3f4ee1f0c8d7760a2 Mon Sep 17 00:00:00 2001 From: Elysia <1391048988@qq.com> Date: Sat, 29 Nov 2025 01:42:44 +0800 Subject: [PATCH 119/154] chore: remove redundant packages. --- sdata/dist-fedora/install-deps.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index 20e1d1328..e98ec049f 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -34,7 +34,7 @@ v sudo dnf versionlock delete quickshell-git 2>/dev/null v sudo dnf install yq -y # Development-tools -r v sudo dnf install @development-tools fedora-packager rpmdevtools fonts-rpm-macros qt6-rpm-macros -y +r v sudo dnf install @development-tools fedora-packager -y # COPR repositories v sudo dnf copr enable ririko66z/dots-hyprland -y From 34991dd32e98bd4f0c0c5ee7e1cadb196ccb71fb Mon Sep 17 00:00:00 2001 From: Elysia <1391048988@qq.com> Date: Mon, 15 Dec 2025 15:51:26 +0800 Subject: [PATCH 120/154] chore: fine-tune installation script. (ref #2566) --- sdata/dist-fedora/feddeps.toml | 11 ++++ sdata/dist-fedora/install-deps.sh | 88 ++++++++++++++++++------------- 2 files changed, 62 insertions(+), 37 deletions(-) diff --git a/sdata/dist-fedora/feddeps.toml b/sdata/dist-fedora/feddeps.toml index 367626ced..def14e066 100644 --- a/sdata/dist-fedora/feddeps.toml +++ b/sdata/dist-fedora/feddeps.toml @@ -1,3 +1,13 @@ +# COPR repositories list +[copr] +repos = [ + "ririko66z/dots-hyprland", + "solopasha/hyprland", + "deltacopy/darkly", + "alternateved/eza", + "atim/starship" +] + # Fedora dependencies list # Audio @@ -17,6 +27,7 @@ packages = [ "brightnessctl", "ddcutil" ] +install_opts = ["--setopt=install_weak_deps=False"] # Basic [groups.basic] diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index e98ec049f..7e43ff0f4 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -1,9 +1,16 @@ # This script is meant to be sourced. # It's not for directly running. +# ------------------------- +# CONFIG +# ------------------------- +user_config="${REPO_ROOT}/sdata/dist-fedora/user_data.yaml" +rpmbuildroot="${REPO_ROOT}/cache/rpmbuild" +deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml" -# Initialize the user configuration file -user_config=${REPO_ROOT}/sdata/dist-fedora/user_data.yaml +# ------------------------- +# FUNCTIONS +# ------------------------- # Recording DNF Transaction ID function r() { @@ -16,6 +23,36 @@ function r() { [ "$original_id" == "$last_id" ] || yq -i ".dnf.transaction_ids += [ $last_id ]" "$user_config" || : } +# Start building and install the missing RPM package locally. +function install_RPMS() { + local local_specs local_rpms + rpmbuildroot="${rpmbuildroot:-${REPO_ROOT}/cache/rpmbuild}" + + x mkdir -p "$rpmbuildroot"/{BUILD,RPMS,SOURCES} + x cp -r "${REPO_ROOT}/sdata/dist-fedora/SPECS" "$rpmbuildroot/" + + x cd $rpmbuildroot/SPECS + + mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) + for spec_file in ${local_specs[@]}; do + # Download sources + x spectool -g -C "$rpmbuildroot/SOURCES" "$spec_file" + # Install build dependencies + r x sudo dnf builddep -y "$spec_file" + # Build the RPM with proper quoting preserved + x rpmbuild -bb --define "_topdir $rpmbuildroot" "$spec_file" + done + + mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) + echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" + r x sudo dnf install "${local_rpms[@]}" -y + x cd ${REPO_ROOT} +} + +# ------------------------- +# MAIN +# ------------------------- + if ! command -v dnf >/dev/null 2>&1; then printf "${STY_RED}[$0]: dnf not found, it seems that the system is not Fedora 42 or later distros. Aborting...${STY_RST}\n" exit 1 @@ -33,61 +70,38 @@ v sudo dnf versionlock delete quickshell-git 2>/dev/null # Install yq for parsing config files v sudo dnf install yq -y -# Development-tools +# Install development tools r v sudo dnf install @development-tools fedora-packager -y -# COPR repositories -v sudo dnf copr enable ririko66z/dots-hyprland -y -v sudo dnf copr enable solopasha/hyprland -y -v sudo dnf copr enable deltacopy/darkly -y -v sudo dnf copr enable alternateved/eza -y -v sudo dnf copr enable atim/starship -y - -# Start building and install the missing RPM package locally. -install_RPMS() { - rpmbuildroot=${REPO_ROOT}/cache/rpmbuild - x mkdir -p $rpmbuildroot/{BUILD,RPMS,SOURCES} - x cp -r ${REPO_ROOT}/sdata/dist-fedora/SPECS $rpmbuildroot/ - x cd $rpmbuildroot/SPECS - mapfile -t -d '' local_specs < <(find "$rpmbuildroot/SPECS" -maxdepth 1 -type f -name "*.spec" -print0) - for spec_file in ${local_specs[@]}; do - x spectool -g -C "$rpmbuildroot/SOURCES" $spec_file - r x sudo dnf builddep -y $spec_file - x rpmbuild -bb --define "_topdir $rpmbuildroot" $spec_file - done - mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) - echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" - r x sudo dnf install "${local_rpms[@]}" -y - x cd ${REPO_ROOT} -} +# Install COPR repositories +copr_repos_json=$(yq -o=j '.copr.repos // []' "$deps_data_file") +eval "$(jq -r '@sh "copr_repos_array+=(\(.[]))"' <<<"$copr_repos_json")" # Fedora distro contains jq +for copr in ${copr_repos_array[@]}; do + v sudo dnf copr enable "$copr" -y +done +# Build and install locally RPMS showfun install_RPMS v install_RPMS -deps_data_file="${REPO_ROOT}/sdata/dist-fedora/feddeps.toml" +# Install packages from toml file deps_data=$(yq -o=j '.' "$deps_data_file") echo "Starting to install packages from $deps_data_file ..." while IFS= read -r deps_list_key; do - echo "Installing package list: $deps_list_key" + install_opts=$(echo $deps_data | yq ".groups.\"$deps_list_key\" | select(has(\"install_opts\")) | .install_opts[]") package_list=$(echo $deps_data | yq ".groups.\"$deps_list_key\".packages | unique | .[]") r v sudo dnf install -y $install_opts $package_list 0) -') +done < <(echo "$deps_data" | yq '.groups | keys[]? | select(length > 0)') # Add back versionlock at the end v sudo dnf versionlock add quickshell-git echo -e "\n========================================" -echo "All installations are complete." +echo "All installations are completed." echo "========================================" - From 17a3874ab1e9bf647b04febb1eec4c33434289ec Mon Sep 17 00:00:00 2001 From: Elysia <1391048988@qq.com> Date: Thu, 18 Dec 2025 17:54:50 +0800 Subject: [PATCH 121/154] chore: Added a fallback mechanism for build failures. (ref #2706) --- sdata/dist-fedora/SPECS/quickshell-git.spec | 2 -- sdata/dist-fedora/install-deps.sh | 18 ++++++++++++------ 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/sdata/dist-fedora/SPECS/quickshell-git.spec b/sdata/dist-fedora/SPECS/quickshell-git.spec index e90bdebe2..5167a1ae3 100644 --- a/sdata/dist-fedora/SPECS/quickshell-git.spec +++ b/sdata/dist-fedora/SPECS/quickshell-git.spec @@ -63,8 +63,6 @@ Wayland and X11. %endif -DBUILD_SHARED_LIBS=OFF \ -DCMAKE_BUILD_TYPE=Release \ - -DDISTRIBUTOR="Fedora COPR (errornointernet/quickshell)" \ - -DDISTRIBUTOR_DEBUGINFO_AVAILABLE=YES \ -DGIT_REVISION=%{commit} \ -DINSTALL_QML_PREFIX=%{_lib}/qt6/qml %cmake_build diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index 7e43ff0f4..ffa54e60f 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -1,6 +1,6 @@ # This script is meant to be sourced. # It's not for directly running. - +set -ex # ------------------------- # CONFIG # ------------------------- @@ -39,13 +39,19 @@ function install_RPMS() { x spectool -g -C "$rpmbuildroot/SOURCES" "$spec_file" # Install build dependencies r x sudo dnf builddep -y "$spec_file" - # Build the RPM with proper quoting preserved - x rpmbuild -bb --define "_topdir $rpmbuildroot" "$spec_file" + # Build the RPM package locally. If it fails, download it from COPR. + if ! rpmbuild -bb --define "_topdir $rpmbuildroot" --define "debug_package %{nil}" "$spec_file"; then + printf "${STY_RED}Local build encountered an issue. Downloading $(basename "$spec_file" .spec) from COPR. Report the issue to Discussions pls.${STY_RST}\n" + sudo dnf install -y $(basename "$spec_file" .spec) + nolock_qs=true + fi done mapfile -t -d '' local_rpms < <(find "$rpmbuildroot/RPMS" -maxdepth 2 -type f -name '*.rpm' -not -name '*debug*' -print0) - echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" - r x sudo dnf install "${local_rpms[@]}" -y + if [[ ${#local_rpms[@]} -ge 1 ]]; then + echo -e "${STY_BLUE}Next command:${STY_RST} sudo dnf install ${local_rpms[@]} -y" + r x sudo dnf install "${local_rpms[@]}" -y + fi x cd ${REPO_ROOT} } @@ -100,7 +106,7 @@ while IFS= read -r deps_list_key; do done < <(echo "$deps_data" | yq '.groups | keys[]? | select(length > 0)') # Add back versionlock at the end -v sudo dnf versionlock add quickshell-git +[ -n $nolock_qs ] || v sudo dnf versionlock add quickshell-git || true echo -e "\n========================================" echo "All installations are completed." From a38c725d3d3dea24bcbc5b2872d0446f2a4fdb46 Mon Sep 17 00:00:00 2001 From: "Celestial.y" Date: Thu, 18 Dec 2025 21:25:11 +0800 Subject: [PATCH 122/154] Remove redundant set -e; not using set -x --- sdata/dist-fedora/install-deps.sh | 1 - 1 file changed, 1 deletion(-) diff --git a/sdata/dist-fedora/install-deps.sh b/sdata/dist-fedora/install-deps.sh index ffa54e60f..18da73e06 100644 --- a/sdata/dist-fedora/install-deps.sh +++ b/sdata/dist-fedora/install-deps.sh @@ -1,6 +1,5 @@ # This script is meant to be sourced. # It's not for directly running. -set -ex # ------------------------- # CONFIG # ------------------------- From ed6a0204b42c3780172fc42cea7d77b93ae5c19e Mon Sep 17 00:00:00 2001 From: clsty Date: Thu, 18 Dec 2025 21:31:27 +0800 Subject: [PATCH 123/154] Fix arg of install_google_sans_flex (closes #2702) --- sdata/subcmd-install/3.files.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdata/subcmd-install/3.files.sh b/sdata/subcmd-install/3.files.sh index d0e537aa3..57c8568e1 100644 --- a/sdata/subcmd-install/3.files.sh +++ b/sdata/subcmd-install/3.files.sh @@ -170,7 +170,7 @@ function install_google_sans_flex(){ x fc-cache -fv x cd $REPO_ROOT x mkdir -p "$(dirname ${INSTALLED_LISTFILE})" - realpath -se "$2" >> "${INSTALLED_LISTFILE}" + realpath -se "$target_dir" >> "${INSTALLED_LISTFILE}" } ##################################################################################### From 3eec36d20d0b9de54be54aca0a85f693969f62a4 Mon Sep 17 00:00:00 2001 From: Bernat Felip Date: Thu, 18 Dec 2025 16:26:34 +0100 Subject: [PATCH 124/154] 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 125/154] 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 126/154] 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 127/154] 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 128/154] 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 129/154] 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 130/154] 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 131/154] 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 132/154] 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 133/154] 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 c580b050e44e12f5c42981506d0222abad81b59a Mon Sep 17 00:00:00 2001 From: reakjra Date: Mon, 22 Dec 2025 23:34:52 +0100 Subject: [PATCH 134/154] Fix background clock widget's color being obnoxious --- .../modules/ii/background/widgets/AbstractBackgroundWidget.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml index b24c33aaa..6d8c14598 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/AbstractBackgroundWidget.qml @@ -59,7 +59,8 @@ AbstractWidget { function onReadyChanged() { refreshPlacementIfNeeded() } } function refreshPlacementIfNeeded() { - if (!Config.ready || (root.placementStrategy === "free" && root.needsColText)) return; + if (!Config.ready) return; + if (root.placementStrategy === "free" && !root.needsColText) return; leastBusyRegionProc.wallpaperPath = root.wallpaperPath; leastBusyRegionProc.running = false; leastBusyRegionProc.running = true; 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 135/154] 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 136/154] 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 137/154] 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) From a658aced0dd16a584848180ca382d7755ad1ce2a Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Thu, 25 Dec 2025 21:21:40 +0100 Subject: [PATCH 138/154] fix transparent combobox --- .../quickshell/ii/modules/common/widgets/StyledComboBox.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml index 18add2cc3..55504acb2 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/StyledComboBox.qml @@ -192,7 +192,7 @@ ComboBox { id: popupBackground anchors.fill: parent radius: Appearance.rounding.normal - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh } } From a34ead004f349ff3eb2860809f253a6ecb5e5d18 Mon Sep 17 00:00:00 2001 From: KemonoNeco Date: Fri, 26 Dec 2025 12:24:28 -0800 Subject: [PATCH 139/154] feat: Removed deprecated Gemini and GitHub AI models, and added Gemini 3 Flash. --- dots/.config/quickshell/ii/services/Ai.qml | 49 +++------------------- 1 file changed, 5 insertions(+), 44 deletions(-) diff --git a/dots/.config/quickshell/ii/services/Ai.qml b/dots/.config/quickshell/ii/services/Ai.qml index ccd237de8..580b3cfdb 100644 --- a/dots/.config/quickshell/ii/services/Ai.qml +++ b/dots/.config/quickshell/ii/services/Ai.qml @@ -255,19 +255,6 @@ Singleton { // - api_format: The API format of the model. Can be "openai" or "gemini". Default is "openai". // - extraParams: Extra parameters to be passed to the model. This is a JSON object. property var models: Config.options.policies.ai === 2 ? {} : { - "gemini-2.0-flash": aiModelComponent.createObject(this, { - "name": "Gemini 2.0 Flash", - "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nFast, can perform searches for up-to-date information"), - "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:streamGenerateContent", - "model": "gemini-2.0-flash", - "requires_key": true, - "key_id": "gemini", - "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), - "api_format": "gemini", - }), "gemini-2.5-flash": aiModelComponent.createObject(this, { "name": "Gemini 2.5 Flash", "icon": "google-gemini-symbolic", @@ -281,26 +268,13 @@ Singleton { "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), "api_format": "gemini", }), - "gemini-2.5-flash-pro": aiModelComponent.createObject(this, { - "name": "Gemini 2.5 Pro", + "gemini-3-flash": aiModelComponent.createObject(this, { + "name": "Gemini 3 Flash", "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nGoogle's state-of-the-art multipurpose model that excels at coding and complex reasoning tasks."), + "description": Translation.tr("Online | Google's model\nPro-level intelligence at the speed and pricing of Flash."), "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-pro:streamGenerateContent", - "model": "gemini-2.5-pro", - "requires_key": true, - "key_id": "gemini", - "key_get_link": "https://aistudio.google.com/app/apikey", - "key_get_description": Translation.tr("**Pricing**: free. Data used for training.\n\n**Instructions**: Log into Google account, allow AI Studio to create Google Cloud project or whatever it asks, go back and click Get API key"), - "api_format": "gemini", - }), - "gemini-2.5-flash-lite": aiModelComponent.createObject(this, { - "name": "Gemini 2.5 Flash-Lite", - "icon": "google-gemini-symbolic", - "description": Translation.tr("Online | Google's model\nA Gemini 2.5 Flash model optimized for cost-efficiency and high throughput."), - "homepage": "https://aistudio.google.com", - "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash-lite:streamGenerateContent", - "model": "gemini-2.5-flash-lite", + "endpoint": "https://generativelanguage.googleapis.com/v1beta/models/gemini-3-flash-preview:streamGenerateContent", + "model": "gemini-3-flash-preview", "requires_key": true, "key_id": "gemini", "key_get_link": "https://aistudio.google.com/app/apikey", @@ -320,19 +294,6 @@ Singleton { "key_get_description": Translation.tr("**Instructions**: Log into Mistral account, go to Keys on the sidebar, click Create new key"), "api_format": "mistral", }), - "github-gpt-5-nano": aiModelComponent.createObject(this, { - "name": "GPT-5 Nano (GH Models)", - "icon": "github-symbolic", - "api_format": "openai", - "description": Translation.tr("Online via %1 | %2's model").arg("GitHub Models").arg("OpenAI"), - "homepage": "https://github.com/marketplace/models", - "endpoint": "https://models.inference.ai.azure.com/chat/completions", - "model": "gpt-5-nano", - "requires_key": true, - "key_id": "github", - "key_get_link": "https://github.com/settings/tokens", - "key_get_description": Translation.tr("**Pricing**: Free tier available with limited rates. See https://docs.github.com/en/billing/concepts/product-billing/github-models\n\n**Instructions**: Generate a GitHub personal access token with Models permission, then set as API key here\n\n**Note**: To use this you will have to set the temperature parameter to 1"), - }), "openrouter-deepseek-r1": aiModelComponent.createObject(this, { "name": "DeepSeek R1", "icon": "deepseek-symbolic", From 1e2a9727476691e3afd1d6e15bf37d5bf1d771ec Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Dec 2025 21:42:55 +0100 Subject: [PATCH 140/154] fix translator lang selector transparency --- .../quickshell/ii/modules/common/widgets/SelectionDialog.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml b/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml index 68d67a32c..6100c0864 100644 --- a/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml +++ b/dots/.config/quickshell/ii/modules/common/widgets/SelectionDialog.qml @@ -32,7 +32,7 @@ Item { Rectangle { // The dialog id: dialog - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh radius: Appearance.rounding.normal anchors.fill: parent anchors.margins: dialogMargin From db60f8775dc288c2361ca31e3b23e956874ce0d4 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Fri, 26 Dec 2025 22:06:18 +0100 Subject: [PATCH 141/154] ai: default to gemini 2.5 --- dots/.config/quickshell/ii/modules/common/Persistent.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Persistent.qml b/dots/.config/quickshell/ii/modules/common/Persistent.qml index 247884028..8367692e3 100644 --- a/dots/.config/quickshell/ii/modules/common/Persistent.qml +++ b/dots/.config/quickshell/ii/modules/common/Persistent.qml @@ -59,7 +59,7 @@ Singleton { property string hyprlandInstanceSignature: "" property JsonObject ai: JsonObject { - property string model + property string model: "gemini-2.5-flash" property real temperature: 0.5 } From 13a827c0f443701b5d52c5d8dde8abd2d7dc80cd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 27 Dec 2025 09:58:14 +0100 Subject: [PATCH 142/154] group panels more properly --- .../quickshell/ii/modules/common/Config.qml | 5 +- .../ii/modules/waffle/looks/Looks.qml | 7 +- .../ii/modules/waffle/looks/WPane.qml | 3 +- .../modules/waffle/polkit/WPolkitContent.qml | 2 +- .../screenSnip/WRectangularSelection.qml | 2 +- .../waffle/taskView/TaskViewContent.qml | 2 +- .../waffle/taskView/TaskViewWindow.qml | 2 +- .../panelFamilies/IllogicalImpulseFamily.qml | 45 ++++++++ .../ii/panelFamilies/PanelLoader.qml | 9 ++ .../ii/panelFamilies/WaffleFamily.qml | 44 ++++++++ dots/.config/quickshell/ii/shell.qml | 106 ++++-------------- 11 files changed, 133 insertions(+), 94 deletions(-) create mode 100644 dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml create mode 100644 dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml create mode 100644 dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml diff --git a/dots/.config/quickshell/ii/modules/common/Config.qml b/dots/.config/quickshell/ii/modules/common/Config.qml index 4fed1b6fb..9a29c53f3 100644 --- a/dots/.config/quickshell/ii/modules/common/Config.qml +++ b/dots/.config/quickshell/ii/modules/common/Config.qml @@ -78,10 +78,7 @@ Singleton { JsonAdapter { id: configOptionsJsonAdapter - property list enabledPanels: [ - "iiBar", "iiBackground", "iiCheatsheet", "iiDock", "iiLock", "iiMediaControls", "iiNotificationPopup", "iiOnScreenDisplay", "iiOnScreenKeyboard", "iiOverlay", "iiOverview", "iiPolkit", "iiRegionSelector", "iiReloadPopup", "iiScreenCorners", "iiSessionScreen", "iiSidebarLeft", "iiSidebarRight", "iiVerticalBar", "iiWallpaperSelector" - ] - property string panelFamily: "ii" // "ii", "w" + property string panelFamily: "ii" // "ii", "waffle" property JsonObject policies: JsonObject { property int ai: 1 // 0: No | 1: Yes | 2: Local diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml index 779835837..7bad59e84 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/Looks.qml @@ -89,10 +89,11 @@ Singleton { // 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.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency) - property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBase, root.panelLayerTransparency) + property color bgPanelFooterBase: root.dark ? root.darkColors.bg0 : root.lightColors.bg0 + property color bgPanelFooterBackground: ColorUtils.transparentize(root.dark ? root.darkColors.bg0 : root.lightColors.bg0, root.panelBackgroundTransparency) + property color bgPanelFooter: ColorUtils.transparentize(bgPanelFooterBackground, 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 bgPanelBody: ColorUtils.solveOverlayColor(bgPanelFooterBackground,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 diff --git a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml index f281a8b7c..01f20d8d2 100644 --- a/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml +++ b/dots/.config/quickshell/ii/modules/waffle/looks/WPane.qml @@ -12,6 +12,7 @@ Item { id: root property Item contentItem property real radius: Looks.radius.large + property alias color: contentRect.color property alias border: borderRect property alias borderColor: borderRect.border.color property alias borderWidth: borderRect.border.width @@ -42,7 +43,7 @@ Item { anchors.centerIn: parent z: 0 - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground implicitWidth: contentItem.implicitWidth implicitHeight: contentItem.implicitHeight layer.enabled: true diff --git a/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml index 1218efb69..1fcb25072 100644 --- a/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/polkit/WPolkitContent.qml @@ -135,7 +135,7 @@ Rectangle { } BodyRectangle { implicitHeight: 80 - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground RowLayout { anchors.fill: parent anchors.margins: 24 diff --git a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml index a0ec287b8..1de501613 100644 --- a/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml +++ b/dots/.config/quickshell/ii/modules/waffle/screenSnip/WRectangularSelection.qml @@ -18,7 +18,7 @@ Item { Component.onCompleted: overlayColor = ColorUtils.transparentize("#000000", 0.4) Behavior on overlayColor { ColorAnimation { - duration: 250 + duration: 150 easing.type: Easing.InOutQuad } } diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml index 4dc6f5bf7..6828098cf 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewContent.qml @@ -231,7 +231,7 @@ Rectangle { anchors.fill: parent anchors.margins: wsBorder.border.width radius: wsBorder.radius - wsBorder.border.width - color: Looks.colors.bgPanelFooterBase + color: Looks.colors.bgPanelFooterBackground implicitHeight: 174 diff --git a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml index 0cab91fe5..b91655097 100644 --- a/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml +++ b/dots/.config/quickshell/ii/modules/waffle/taskView/TaskViewWindow.qml @@ -25,7 +25,7 @@ WMouseAreaButton { property string iconName: AppSearch.guessIcon(hyprlandClient?.class) - color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBase) + color: drag.active ? ColorUtils.transparentize(Looks.colors.bg1Base) : (containsMouse ? Looks.colors.bg1Base : Looks.colors.bgPanelFooterBackground) borderColor: ColorUtils.transparentize(Looks.colors.bg2Border, drag.active ? 1 : 0) radius: Looks.radius.xLarge diff --git a/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml b/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml new file mode 100644 index 000000000..f4ffda651 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/IllogicalImpulseFamily.qml @@ -0,0 +1,45 @@ +import QtQuick +import Quickshell + +import qs.modules.common +import qs.modules.ii.background +import qs.modules.ii.bar +import qs.modules.ii.cheatsheet +import qs.modules.ii.dock +import qs.modules.ii.lock +import qs.modules.ii.mediaControls +import qs.modules.ii.notificationPopup +import qs.modules.ii.onScreenDisplay +import qs.modules.ii.onScreenKeyboard +import qs.modules.ii.overview +import qs.modules.ii.polkit +import qs.modules.ii.regionSelector +import qs.modules.ii.screenCorners +import qs.modules.ii.sessionScreen +import qs.modules.ii.sidebarLeft +import qs.modules.ii.sidebarRight +import qs.modules.ii.overlay +import qs.modules.ii.verticalBar +import qs.modules.ii.wallpaperSelector + +Scope { + PanelLoader { extraCondition: !Config.options.bar.vertical; component: Bar {} } + PanelLoader { component: Background {} } + PanelLoader { component: Cheatsheet {} } + PanelLoader { extraCondition: Config.options.dock.enable; component: Dock {} } + PanelLoader { component: Lock {} } + PanelLoader { component: MediaControls {} } + PanelLoader { component: NotificationPopup {} } + PanelLoader { component: OnScreenDisplay {} } + PanelLoader { component: OnScreenKeyboard {} } + PanelLoader { component: Overlay {} } + PanelLoader { component: Overview {} } + PanelLoader { component: Polkit {} } + PanelLoader { component: RegionSelector {} } + PanelLoader { component: ScreenCorners {} } + PanelLoader { component: SessionScreen {} } + PanelLoader { component: SidebarLeft {} } + PanelLoader { component: SidebarRight {} } + PanelLoader { extraCondition: Config.options.bar.vertical; component: VerticalBar {} } + PanelLoader { component: WallpaperSelector {} } +} diff --git a/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml b/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml new file mode 100644 index 000000000..1e7b52f42 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/PanelLoader.qml @@ -0,0 +1,9 @@ +import QtQuick +import Quickshell + +import qs.modules.common + +LazyLoader { + property bool extraCondition: true + active: Config.ready && extraCondition +} diff --git a/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml b/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml new file mode 100644 index 000000000..67d35de55 --- /dev/null +++ b/dots/.config/quickshell/ii/panelFamilies/WaffleFamily.qml @@ -0,0 +1,44 @@ +import QtQuick +import Quickshell + +import qs.modules.common +import qs.modules.waffle.actionCenter +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.overlay +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 + +// Fallbacks +import qs.modules.ii.cheatsheet +import qs.modules.ii.onScreenKeyboard +import qs.modules.ii.overlay +import qs.modules.ii.wallpaperSelector + +Scope { + PanelLoader { component: WaffleActionCenter {} } + PanelLoader { component: WaffleBar {} } + PanelLoader { component: WaffleBackground {} } + PanelLoader { component: WaffleLock {} } + PanelLoader { component: WaffleNotificationCenter {} } + PanelLoader { component: WaffleNotificationPopup {} } + PanelLoader { component: WaffleOSD {} } + // PanelLoader { component: WaffleOverlay {} } + PanelLoader { component: WafflePolkit {} } + PanelLoader { component: WScreenSnip {} } + PanelLoader { component: WaffleStartMenu {} } + PanelLoader { component: WaffleSessionScreen {} } + PanelLoader { component: WaffleTaskView {} } + + PanelLoader { component: Cheatsheet {} } + PanelLoader { component: OnScreenKeyboard {} } + PanelLoader { component: Overlay {} } + PanelLoader { component: WallpaperSelector {} } +} diff --git a/dots/.config/quickshell/ii/shell.qml b/dots/.config/quickshell/ii/shell.qml index b0fe37a4f..a1cb57baa 100644 --- a/dots/.config/quickshell/ii/shell.qml +++ b/dots/.config/quickshell/ii/shell.qml @@ -6,51 +6,22 @@ // Adjust this to make the shell smaller or larger //@ pragma Env QT_SCALE_FACTOR=1 -import qs.modules.common -import qs.modules.ii.background -import qs.modules.ii.bar -import qs.modules.ii.cheatsheet -import qs.modules.ii.dock -import qs.modules.ii.lock -import qs.modules.ii.mediaControls -import qs.modules.ii.notificationPopup -import qs.modules.ii.onScreenDisplay -import qs.modules.ii.onScreenKeyboard -import qs.modules.ii.overview -import qs.modules.ii.polkit -import qs.modules.ii.regionSelector -import qs.modules.ii.screenCorners -import qs.modules.ii.sessionScreen -import qs.modules.ii.sidebarLeft -import qs.modules.ii.sidebarRight -import qs.modules.ii.overlay -import qs.modules.ii.verticalBar -import qs.modules.ii.wallpaperSelector - -import qs.modules.waffle.actionCenter -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 -import qs.modules.waffle.startMenu -import qs.modules.waffle.sessionScreen -import qs.modules.waffle.taskView +import "modules/common" +import "services" +import "panelFamilies" import QtQuick import QtQuick.Window import Quickshell import Quickshell.Io import Quickshell.Hyprland -import qs.services ShellRoot { id: root - // Force initialization of some singletons + // Stuff for every panel family + ReloadPopup {} + Component.onCompleted: { MaterialThemeLoader.reapplyTheme() Hyprsunset.load() @@ -61,62 +32,33 @@ ShellRoot { Updates.load() } - // Load enabled stuff - // Well, these loaders only *allow* them to be loaded, to always load or not is defined in each component - // The media controls for example is not loaded if it's not opened - PanelLoader { identifier: "iiBar"; extraCondition: !Config.options.bar.vertical; component: Bar {} } - PanelLoader { identifier: "iiBackground"; component: Background {} } - PanelLoader { identifier: "iiCheatsheet"; component: Cheatsheet {} } - PanelLoader { identifier: "iiDock"; extraCondition: Config.options.dock.enable; component: Dock {} } - PanelLoader { identifier: "iiLock"; component: Lock {} } - PanelLoader { identifier: "iiMediaControls"; component: MediaControls {} } - PanelLoader { identifier: "iiNotificationPopup"; component: NotificationPopup {} } - PanelLoader { identifier: "iiOnScreenDisplay"; component: OnScreenDisplay {} } - PanelLoader { identifier: "iiOnScreenKeyboard"; component: OnScreenKeyboard {} } - PanelLoader { identifier: "iiOverlay"; component: Overlay {} } - PanelLoader { identifier: "iiOverview"; component: Overview {} } - PanelLoader { identifier: "iiPolkit"; component: Polkit {} } - PanelLoader { identifier: "iiRegionSelector"; component: RegionSelector {} } - PanelLoader { identifier: "iiScreenCorners"; component: ScreenCorners {} } - PanelLoader { identifier: "iiSessionScreen"; component: SessionScreen {} } - PanelLoader { identifier: "iiSidebarLeft"; component: SidebarLeft {} } - PanelLoader { identifier: "iiSidebarRight"; component: SidebarRight {} } - PanelLoader { identifier: "iiVerticalBar"; extraCondition: Config.options.bar.vertical; component: VerticalBar {} } - PanelLoader { identifier: "iiWallpaperSelector"; component: WallpaperSelector {} } - - PanelLoader { identifier: "wActionCenter"; component: WaffleActionCenter {} } - PanelLoader { identifier: "wBar"; component: WaffleBar {} } - 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 {} } - PanelLoader { identifier: "wStartMenu"; component: WaffleStartMenu {} } - PanelLoader { identifier: "wSessionScreen"; component: WaffleSessionScreen {} } - PanelLoader { identifier: "wTaskView"; component: WaffleTaskView {} } - ReloadPopup {} - - component PanelLoader: LazyLoader { - required property string identifier - property bool extraCondition: true - active: Config.ready && Config.options.enabledPanels.includes(identifier) && extraCondition - } // Panel families 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", "wNotificationPopup", "wOnScreenDisplay", "wTaskView", "wPolkit", "wScreenSnip", "wSessionScreen", "wStartMenu", "iiCheatsheet", "iiOnScreenKeyboard", "iiOverlay", "iiWallpaperSelector"], - }) function cyclePanelFamily() { const currentIndex = families.indexOf(Config.options.panelFamily) const nextIndex = (currentIndex + 1) % families.length Config.options.panelFamily = families[nextIndex] - Config.options.enabledPanels = panelFamilies[Config.options.panelFamily] } + component PanelFamilyLoader: LazyLoader { + required property string identifier + property bool extraCondition: true + active: Config.ready && Config.options.panelFamily === identifier && extraCondition + } + + PanelFamilyLoader { + identifier: "ii" + component: IllogicalImpulseFamily {} + } + + PanelFamilyLoader { + identifier: "waffle" + component: WaffleFamily {} + } + + + // Shortcuts IpcHandler { target: "panelFamily" From c5aaf721c802848a2776b4ce5c59d9a094058059 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 27 Dec 2025 11:24:19 +0100 Subject: [PATCH 143/154] hyprland: tune shadows and borders --- dots/.config/hypr/hyprland/general.conf | 23 +++++++++++-------- .../matugen/templates/hyprland/colors.conf | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/dots/.config/hypr/hyprland/general.conf b/dots/.config/hypr/hyprland/general.conf index 4b12fc84f..145cbb77c 100644 --- a/dots/.config/hypr/hyprland/general.conf +++ b/dots/.config/hypr/hyprland/general.conf @@ -22,7 +22,7 @@ general { gaps_workspaces = 50 border_size = 1 - col.active_border = rgba(0DB7D4FF) + col.active_border = rgba(0DB7D455) col.inactive_border = rgba(31313600) resize_on_border = true @@ -31,10 +31,10 @@ general { allow_tearing = true # This just allows the `immediate` window rule to work snap { - enabled = true - window_gap = 4 - monitor_gap = 5 - respect_gaps = true + enabled = true + window_gap = 4 + monitor_gap = 5 + respect_gaps = true } } @@ -46,6 +46,9 @@ dwindle { } decoration { + # 2 = circle, higher = squircle, 4 = very obvious squircle + # Clear squircles look really off; we use only extra .4 here to make the rounding feel more continuous + rounding_power = 2.4 rounding = 18 blur { @@ -69,15 +72,15 @@ decoration { shadow { enabled = true ignore_window = true - range = 30 - offset = 0 2 - render_power = 4 - color = rgba(00000010) + range = 50 + offset = 0 4 + render_power = 10 + color = rgba(00000027) } # Dim dim_inactive = true - dim_strength = 0.025 + dim_strength = 0.05 dim_special = 0.07 } diff --git a/dots/.config/matugen/templates/hyprland/colors.conf b/dots/.config/matugen/templates/hyprland/colors.conf index d0002e9e7..b8e8a819a 100644 --- a/dots/.config/matugen/templates/hyprland/colors.conf +++ b/dots/.config/matugen/templates/hyprland/colors.conf @@ -1,6 +1,6 @@ general { - col.active_border = rgba({{colors.outline.default.hex_stripped}}AA) - col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}AA) + col.active_border = rgba({{colors.outline.default.hex_stripped}}77) + col.inactive_border = rgba({{colors.outline_variant.default.hex_stripped}}55) } misc { From e1f30bf85cf51c025b25871a8387de8eeaf9d5ef Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:02:08 +0100 Subject: [PATCH 144/154] less transparent light mode --- dots/.config/quickshell/ii/modules/common/Appearance.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/common/Appearance.qml b/dots/.config/quickshell/ii/modules/common/Appearance.qml index aca8bfa4f..d64253476 100644 --- a/dots/.config/quickshell/ii/modules/common/Appearance.qml +++ b/dots/.config/quickshell/ii/modules/common/Appearance.qml @@ -28,7 +28,7 @@ Singleton { property real autoBackgroundTransparency: { // y = 0.5768x^2 - 0.759x + 0.2896 let x = wallpaperVibrancy let y = 0.5768 * (x * x) - 0.759 * (x) + 0.2896 - return Math.max(0, Math.min(0.22, y)) + return Math.max(0, Math.min(0.22, y)) - 0.12 * (m3colors.darkmode ? 0 : 1) } 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 From 5c141e0361adabdb7ea3575392309bec3a592af9 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 27 Dec 2025 12:02:27 +0100 Subject: [PATCH 145/154] fix transparent booru image menu --- .../quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml index e403417ba..feeeeb05b 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarLeft/anime/BooruImage.qml @@ -126,7 +126,7 @@ Button { opacity: root.showActions ? 1 : 0 visible: opacity > 0 radius: Appearance.rounding.small - color: Appearance.colors.colSurfaceContainer + color: Appearance.m3colors.m3surfaceContainer implicitHeight: contextMenuColumnLayout.implicitHeight + radius * 2 implicitWidth: contextMenuColumnLayout.implicitWidth From d22822e734193ca36758b056b5ecc431d1471a56 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Sat, 27 Dec 2025 20:14:57 +0100 Subject: [PATCH 146/154] fix transparent todo list add dialog --- .../quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml index 36c885523..8341887fd 100644 --- a/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/sidebarRight/todo/TodoWidget.qml @@ -139,7 +139,7 @@ Item { anchors.margins: root.dialogMargins implicitHeight: dialogColumnLayout.implicitHeight - color: Appearance.colors.colSurfaceContainerHigh + color: Appearance.m3colors.m3surfaceContainerHigh radius: Appearance.rounding.normal function addTask() { From d178dafac6508ec3152c707a4941ea745c2d7080 Mon Sep 17 00:00:00 2001 From: Bishoy Ehab Date: Mon, 29 Dec 2025 05:45:59 +0200 Subject: [PATCH 147/154] fix(exp-update): build packages in /tmp with auto-cleanup --- sdata/subcmd-exp-update/0.run.sh | 169 +++++++++++++++++-------------- 1 file changed, 95 insertions(+), 74 deletions(-) diff --git a/sdata/subcmd-exp-update/0.run.sh b/sdata/subcmd-exp-update/0.run.sh index 2ab21379c..72c11e770 100644 --- a/sdata/subcmd-exp-update/0.run.sh +++ b/sdata/subcmd-exp-update/0.run.sh @@ -31,9 +31,7 @@ # set -euo pipefail -# TODO: For Arch(-Linux) specific part please check if pacman exists first, if not it should be skipped. - -# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. +# Note: The detect_repo_structure function below auto-detects the folder layout # Try to find the packages directory (different names in different versions) if which pacman &>/dev/null; then if [[ -d "${REPO_ROOT}/dist-arch" ]]; then @@ -56,7 +54,6 @@ declare -a IGNORE_SUBSTRING_PATTERNS=() # Track created directories to avoid redundant mkdir calls declare -A CREATED_DIRS -# TODO: Is this really needed? `git pull` should do a full upgrade, not partially, which means this script will be updated along with the folder structure together. # Auto-detect repository structure detect_repo_structure() { local found_dirs=() @@ -644,23 +641,41 @@ build_packages() { log_info "Building package: $pkg_name" if [[ "$DRY_RUN" == true ]]; then - log_info "[DRY-RUN] Would build package in directory: $pkg_dir" + log_info "[DRY-RUN] Would build package in temp directory and clean up after" continue fi - cd "$pkg_dir" || { - log_error "Failed to change to package directory: $pkg_dir" + # Create temp build directory to avoid polluting the repo + local build_tmp_dir + build_tmp_dir=$(mktemp -d "/tmp/pkgbuild-${pkg_name}-XXXXXX") + + # Copy package files to temp directory (using /. to include hidden files) + cp -r "$pkg_dir"/. "$build_tmp_dir/" || { + log_error "Failed to copy package files to temp directory" + rm -rf "$build_tmp_dir" continue } - if makepkg -si --noconfirm; then + cd "$build_tmp_dir" || { + log_error "Failed to change to temp build directory: $build_tmp_dir" + rm -rf "$build_tmp_dir" + continue + } + + if makepkg -sCi --noconfirm; then log_success "Successfully built and installed $pkg_name" - ((rebuilt_packages++)) + ((rebuilt_packages++)) || true else log_error "Failed to build package $pkg_name" fi + # Clean up temp build directory cd "$REPO_ROOT" || log_die "Failed to return to repository directory" + rm -rf "$build_tmp_dir" + log_info "Cleaned up temp build directory" + + # Also clean any old build artifacts in the original package directory + rm -rf "${pkg_dir}/src" "${pkg_dir}/pkg" "${pkg_dir}"/*.pkg.tar.* 2>/dev/null || true done if [[ $rebuilt_packages -eq 0 ]]; then @@ -852,8 +867,12 @@ if git remote get-url origin &>/dev/null; then fi fi else - log_warning "Failed to pull changes from remote. Continuing with local repository..." - log_info "You may need to resolve conflicts manually later." + log_warning "Failed to pull changes from remote." + log_warning "This could be due to:" + log_warning " - Network issues" + log_warning " - Uncommitted local changes (use 'git stash' first)" + log_warning " - Diverged history (may need 'git pull --rebase')" + log_info "Continuing with local repository state..." fi fi else @@ -879,68 +898,70 @@ if [[ "$CHECK_PACKAGES" == true ]]; then if [[ "$PKG_TOOLS_AVAILABLE" == true ]]; then if [[ ! -d "$ARCH_PACKAGES_DIR" ]]; then - log_warning "No packages directory found (tried: dist-arch, arch-packages, sdata/dist-arch). Skipping package management." + log_warning "No packages directory found (tried: dist-arch, arch-packages, sdata/dist-arch)." + log_warning "Skipping package management." else - changed_pkgbuilds=() - for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do - if [[ -f "${pkg_dir}/PKGBUILD" ]]; then - pkg_name=$(basename "$pkg_dir") - if check_pkgbuild_changed "$pkg_dir"; then - changed_pkgbuilds+=("$pkg_name") - fi - fi - done - - if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then - log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}" - echo - echo "Package build options:" - echo "1) Build only packages with changed PKGBUILDs" - echo "2) List all packages and select which to build" - echo "3) Build all packages" - echo "4) Skip package building" - echo - - if [[ "$NON_INTERACTIVE" == true ]]; then - pkg_choice="1" - log_info "Non-interactive mode: Using default package option: $pkg_choice" - elif safe_read "Choose an option (1-4): " pkg_choice "1"; then - if [[ "$VERBOSE" == true ]]; then - log_info "User selected package option: $pkg_choice" - fi - else - log_warning "Failed to read input. Skipping package building." - pkg_choice="" - fi - - if [[ -n "$pkg_choice" ]]; then - case $pkg_choice in - 1) build_packages "changed" ;; - 2) - if list_packages; then - build_packages "select" + # Scan for changed PKGBUILDs + changed_pkgbuilds=() + for pkg_dir in "$ARCH_PACKAGES_DIR"/*/; do + if [[ -f "${pkg_dir}/PKGBUILD" ]]; then + pkg_name=$(basename "$pkg_dir") + if check_pkgbuild_changed "$pkg_dir"; then + changed_pkgbuilds+=("$pkg_name") fi - ;; - 3) build_packages "all" ;; - 4 | *) log_info "Skipping package building" ;; - esac - fi - else - log_info "No PKGBUILDs have changed since last update." - echo - if [[ "$NON_INTERACTIVE" == true ]]; then - check_anyway="N" - log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway" - elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then - if [[ "$VERBOSE" == true ]]; then - log_info "User chose to check packages anyway: $check_anyway" + fi + done + + if [[ ${#changed_pkgbuilds[@]} -gt 0 ]]; then + log_info "Found ${#changed_pkgbuilds[@]} package(s) with changed PKGBUILDs: ${changed_pkgbuilds[*]}" + echo + echo "Package build options:" + echo "1) Build only packages with changed PKGBUILDs" + echo "2) List all packages and select which to build" + echo "3) Build all packages" + echo "4) Skip package building" + echo + + if [[ "$NON_INTERACTIVE" == true ]]; then + pkg_choice="1" + log_info "Non-interactive mode: Using default package option: $pkg_choice" + elif safe_read "Choose an option (1-4): " pkg_choice "1"; then + if [[ "$VERBOSE" == true ]]; then + log_info "User selected package option: $pkg_choice" + fi + else + log_warning "Failed to read input. Skipping package building." + pkg_choice="" + fi + + if [[ -n "$pkg_choice" ]]; then + case $pkg_choice in + 1) build_packages "changed" ;; + 2) + if list_packages; then + build_packages "select" + fi + ;; + 3) build_packages "all" ;; + 4|*) log_info "Skipping package building" ;; + esac fi else - log_warning "Failed to read input. Skipping package management." - check_anyway="" - fi + log_info "No PKGBUILDs have changed since last update." + echo + if [[ "$NON_INTERACTIVE" == true ]]; then + check_anyway="N" + log_info "Non-interactive mode: Using default for check packages anyway: $check_anyway" + elif safe_read "Do you want to check and build packages anyway? (y/N): " check_anyway "N"; then + if [[ "$VERBOSE" == true ]]; then + log_info "User chose to check packages anyway: $check_anyway" + fi + else + log_warning "Failed to read input. Skipping package management." + check_anyway="" + fi - if [[ -n "$check_anyway" && "$check_anyway" =~ ^[Yy]$ ]]; then + if [[ -n "$check_anyway" && "$check_anyway" =~ ^[Yy]$ ]]; then if list_packages; then echo echo "Package build options:" @@ -950,9 +971,9 @@ if [[ "$CHECK_PACKAGES" == true ]]; then if safe_read "Choose an option (1-3): " build_choice "3"; then case $build_choice in - 1) build_packages "select" ;; - 2) build_packages "all" ;; - 3 | *) log_info "Skipping package building" ;; + 1) build_packages "select" ;; + 2) build_packages "all" ;; + 3|*) log_info "Skipping package building" ;; esac else log_info "Skipping package building" @@ -963,10 +984,10 @@ if [[ "$CHECK_PACKAGES" == true ]]; then fi fi fi - else - log_header "Package Management" - log_info "Package checking disabled. Use -p or --packages flag to enable package management." fi +else + log_header "Package Management" + log_info "Package checking disabled. Use -p or --packages flag to enable package management." fi # Step 3: Update configuration files From b820bdc65451c1612253d4fcb8d898dc3981c549 Mon Sep 17 00:00:00 2001 From: radicalgale <79670095+radicalgale@users.noreply.github.com> Date: Mon, 29 Dec 2025 19:39:15 +0700 Subject: [PATCH 148/154] Fixed SearchWidget.qml to use proper implicitHeight calculation The `SearchWidget.qml` has the wrong `implicitHeight` value. Instead of using `searchWidgetContent.implicitHeight` it uses `searchBar.implicitHeight`. This causes the search bar to ignore mouse clicks and only allows the user to launch apps by pressing Return (Enter). This issue isn't present if Overview is enabled, because it expands `columnLayout.implicitHeight`, then gets hidden if search isn't equal to "", allowing the mouse clicks to go through. So, to reproduce this issue, try turning off Overview and launching any app in search results by using mouse. --- dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml index 6b982c4d2..3dd88aef3 100644 --- a/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml +++ b/dots/.config/quickshell/ii/modules/ii/overview/SearchWidget.qml @@ -17,7 +17,7 @@ Item { // Wrapper property string searchingText: LauncherSearch.query property bool showResults: searchingText != "" implicitWidth: searchWidgetContent.implicitWidth + Appearance.sizes.elevationMargin * 2 - implicitHeight: searchBar.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 + implicitHeight: searchWidgetContent.implicitHeight + searchBar.verticalPadding * 2 + Appearance.sizes.elevationMargin * 2 function focusFirstItem() { appResults.currentIndex = 0; From b21699b8a08480e5bd4bbee7bba80a5fb68274fd Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:25:49 +0100 Subject: [PATCH 149/154] fix weird vertical clock with 12h format --- .../background/widgets/clock/DigitalClock.qml | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 6506b3e1c..518d9797c 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -18,9 +18,10 @@ ColumnLayout { property color colText: Appearance.colors.colOnSecondaryContainer property var textHorizontalAlignment: Text.AlignHCenter + // Time ClockText { id: timeTextTop - text: clockColumn.isVertical ? DateTime.time.substring(0, 2) : DateTime.time + text: clockColumn.isVertical ? DateTime.time.split(":")[0].padStart(2, "0") : DateTime.time color: clockColumn.colText horizontalAlignment: Text.AlignHCenter font { @@ -36,22 +37,20 @@ ColumnLayout { ClockText { id: timeTextBottom - text: DateTime.time.substring(3, 5) + text: DateTime.time.split(":")[1].split(" ")[0].padStart(2, "0") visible: clockColumn.isVertical color: clockColumn.colText Layout.topMargin: -40 horizontalAlignment: Text.AlignHCenter font { - pixelSize: Config.options.background.widgets.clock.digital.font.size - weight: Config.options.background.widgets.clock.digital.font.weight - family: Config.options.background.widgets.clock.digital.font.family - variableAxes: ({ - "wdth": Config.options.background.widgets.clock.digital.font.width, - "ROND": Config.options.background.widgets.clock.digital.font.roundness - }) + pixelSize: timeTextTop.font.pixelSize + weight: timeTextTop.font.weight + family: timeTextTop.font.family + variableAxes: timeTextTop.font.variableAxes } } + // Date ClockText { visible: Config.options.background.widgets.clock.digital.showDate Layout.topMargin: -20 @@ -59,6 +58,7 @@ ColumnLayout { color: clockColumn.colText horizontalAlignment: clockColumn.textHorizontalAlignment } + ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 font.pixelSize: Appearance.font.pixelSize.normal From 953427692f5db7d28f4a621862e757f9e8a325c7 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:31:05 +0100 Subject: [PATCH 150/154] remove unnecessary imports --- .../ii/modules/ii/background/widgets/clock/DigitalClock.qml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 518d9797c..104bc24cb 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -1,14 +1,9 @@ pragma ComponentBehavior: Bound -//TODO: fix imports to only what is necessary import qs.services import qs.modules.common -import qs.modules.common.widgets -import qs.modules.common.functions import QtQuick import QtQuick.Layouts -import Qt5Compat.GraphicalEffects -import Quickshell.Io ColumnLayout { id: clockColumn @@ -59,6 +54,7 @@ ColumnLayout { horizontalAlignment: clockColumn.textHorizontalAlignment } + // Quote ClockText { visible: Config.options.background.widgets.clock.quote.enable && Config.options.background.widgets.clock.quote.text.length > 0 font.pixelSize: Appearance.font.pixelSize.normal From 61feb958ecc11a14a3ca426b36ef8915cdcbc00b Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:37:55 +0100 Subject: [PATCH 151/154] add comment for empty style name assignment --- .../ii/modules/ii/background/widgets/clock/ClockText.qml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml index 6175c98a5..133a2759c 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/ClockText.qml @@ -9,8 +9,9 @@ StyledText { family: Appearance.font.family.expressive pixelSize: 20 weight: 350 - variableAxes: ({}) + // Set empty to prevent conflicts, not meaningless styleName: "" + variableAxes: ({}) } style: Text.Raised styleColor: Appearance.colors.colShadow From 281943306ed92cbb0d66f8d26d789f8f3c79e66d Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:42:03 +0100 Subject: [PATCH 152/154] bg clock: put bottom time text in a loader --- .../background/widgets/clock/DigitalClock.qml | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml index 104bc24cb..d2a6f95d0 100644 --- a/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml +++ b/dots/.config/quickshell/ii/modules/ii/background/widgets/clock/DigitalClock.qml @@ -24,27 +24,30 @@ ColumnLayout { weight: Config.options.background.widgets.clock.digital.font.weight family: Config.options.background.widgets.clock.digital.font.family variableAxes: ({ - "wdth": Config.options.background.widgets.clock.digital.font.width, - "ROND": Config.options.background.widgets.clock.digital.font.roundness - }) + "wdth": Config.options.background.widgets.clock.digital.font.width, + "ROND": Config.options.background.widgets.clock.digital.font.roundness + }) } } - ClockText { - id: timeTextBottom - text: DateTime.time.split(":")[1].split(" ")[0].padStart(2, "0") - visible: clockColumn.isVertical - color: clockColumn.colText + Loader { Layout.topMargin: -40 - horizontalAlignment: Text.AlignHCenter - font { - pixelSize: timeTextTop.font.pixelSize - weight: timeTextTop.font.weight - family: timeTextTop.font.family - variableAxes: timeTextTop.font.variableAxes + active: clockColumn.isVertical + visible: active + sourceComponent: ClockText { + id: timeTextBottom + text: DateTime.time.split(":")[1].split(" ")[0].padStart(2, "0") + color: clockColumn.colText + horizontalAlignment: Text.AlignHCenter + font { + pixelSize: timeTextTop.font.pixelSize + weight: timeTextTop.font.weight + family: timeTextTop.font.family + variableAxes: timeTextTop.font.variableAxes + } } } - + // Date ClockText { visible: Config.options.background.widgets.clock.digital.showDate @@ -64,6 +67,3 @@ ColumnLayout { horizontalAlignment: clockColumn.textHorizontalAlignment } } - - - \ No newline at end of file From 7763a26e03365840ac9433b33fdaaf2ae56936ba Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Mon, 29 Dec 2025 18:47:15 +0100 Subject: [PATCH 153/154] backgroundconfig: format, adjust texts --- .../quickshell/ii/modules/settings/BackgroundConfig.qml | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml index e6aa0aaa7..b67e10409 100644 --- a/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/BackgroundConfig.qml @@ -53,9 +53,9 @@ ContentPage { } ContentSection { + id: settingsClock icon: "clock_loader_40" title: Translation.tr("Widget: Clock") - id: settingsClock function stylePresent(styleName) { if (!Config.options.background.widgets.clock.showOnlyWhenLocked && Config.options.background.widgets.clock.style === styleName) { @@ -172,7 +172,7 @@ ContentPage { ContentSubsection { visible: settingsClock.digitalPresent title: Translation.tr("Digital clock settings") - tooltip: Translation.tr("Font width and roundness settings are only available for some fonts like 'Google Sans Flex'") + tooltip: Translation.tr("Font width and roundness settings are only available for some fonts like Google Sans Flex") ConfigRow { uniform: true @@ -196,7 +196,6 @@ ContentPage { ConfigRow { uniform: true - ConfigSwitch { buttonIcon: "date_range" @@ -221,7 +220,7 @@ ContentPage { MaterialTextArea { Layout.fillWidth: true - placeholderText: Translation.tr("Font family (e.g., Google Sans Flex)") + placeholderText: Translation.tr("Font family") text: Config.options.background.widgets.clock.digital.font.family wrapMode: TextEdit.Wrap onTextChanged: { @@ -229,7 +228,6 @@ ContentPage { } } - ConfigSlider { text: Translation.tr("Font weight") value: Config.options.background.widgets.clock.digital.font.weight From 91f6f954608775be6b362825952b479001112905 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 30 Dec 2025 08:54:30 +0100 Subject: [PATCH 154/154] remove "transparency looks ass" notice --- dots/.config/quickshell/ii/modules/settings/QuickConfig.qml | 3 --- 1 file changed, 3 deletions(-) diff --git a/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml b/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml index c9c540afa..8a31d7728 100644 --- a/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml +++ b/dots/.config/quickshell/ii/modules/settings/QuickConfig.qml @@ -225,9 +225,6 @@ ContentPage { onCheckedChanged: { Config.options.appearance.transparency.enable = checked; } - StyledToolTip { - text: Translation.tr("Might look ass. Unsupported.") - } } }