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 01/18] 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 02/18] 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 03/18] 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 04/18] 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 05/18] 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 06/18] 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 07/18] 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 08/18] 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 09/18] 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 10/18] 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 11/18] 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 12/18] 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 13/18] 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 14/18] 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 15/18] 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 16/18] 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 17/18] 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 18/18] 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": "视差效果",