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] 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)