From 79e7f262a7348fae9b0ee4153c7aee692b8be457 Mon Sep 17 00:00:00 2001 From: end-4 <97237370+end-4@users.noreply.github.com> Date: Tue, 26 Aug 2025 17:59:37 +0700 Subject: [PATCH] lock screen: redesign password prompt --- .../ii/modules/background/Background.qml | 20 ---- .../ii/modules/common/Appearance.qml | 6 +- .../ii/modules/common/functions/Session.qml | 50 ++++++++ .../modules/common/widgets/RippleButton.qml | 4 +- .../ii/modules/common/widgets/Toolbar.qml | 39 +++++++ .../modules/common/widgets/ToolbarButton.qml | 10 ++ .../common/widgets/ToolbarTextField.qml | 30 +++++ .../common/widgets/VibrantToolbarButton.qml | 10 ++ .../ii/modules/lock/LockSurface.qml | 109 ++++++++++-------- .../SessionActionButton.qml | 0 .../SessionScreen.qml} | 22 ++-- .config/quickshell/ii/shell.qml | 6 +- 12 files changed, 215 insertions(+), 91 deletions(-) create mode 100644 .config/quickshell/ii/modules/common/functions/Session.qml create mode 100644 .config/quickshell/ii/modules/common/widgets/Toolbar.qml create mode 100644 .config/quickshell/ii/modules/common/widgets/ToolbarButton.qml create mode 100644 .config/quickshell/ii/modules/common/widgets/ToolbarTextField.qml create mode 100644 .config/quickshell/ii/modules/common/widgets/VibrantToolbarButton.qml rename .config/quickshell/ii/modules/{session => sessionScreen}/SessionActionButton.qml (100%) rename .config/quickshell/ii/modules/{session/Session.qml => sessionScreen/SessionScreen.qml} (89%) diff --git a/.config/quickshell/ii/modules/background/Background.qml b/.config/quickshell/ii/modules/background/Background.qml index 9ccb58a17..06735f3a1 100644 --- a/.config/quickshell/ii/modules/background/Background.qml +++ b/.config/quickshell/ii/modules/background/Background.qml @@ -315,25 +315,5 @@ Variants { } } - - // Password prompt - StyledText { - anchors { - horizontalCenter: parent.horizontalCenter - bottom: parent.bottom - bottomMargin: 30 - } - opacity: (GlobalStates.screenLocked && !GlobalStates.screenLockContainsCharacters) ? 1 : 0 - scale: opacity - visible: opacity > 0 - Behavior on opacity { - animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) - } - text: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password") - color: GlobalStates.screenUnlockFailed ? Appearance.colors.colError : bgRoot.colText - style: Text.Raised - styleColor: Appearance.colors.colShadow - font.pixelSize: Appearance.font.pixelSize.normal - } } } diff --git a/.config/quickshell/ii/modules/common/Appearance.qml b/.config/quickshell/ii/modules/common/Appearance.qml index 609217c67..cb18a411c 100644 --- a/.config/quickshell/ii/modules/common/Appearance.qml +++ b/.config/quickshell/ii/modules/common/Appearance.qml @@ -146,15 +146,15 @@ Singleton { property color colPrimaryHover: ColorUtils.mix(colors.colPrimary, colLayer1Hover, 0.87) property color colPrimaryActive: ColorUtils.mix(colors.colPrimary, colLayer1Active, 0.7) property color colPrimaryContainer: m3colors.m3primaryContainer - property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Hover, 0.7) - property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colLayer1Active, 0.6) + property color colPrimaryContainerHover: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.9) + property color colPrimaryContainerActive: ColorUtils.mix(colors.colPrimaryContainer, colors.colOnPrimaryContainer, 0.8) property color colOnPrimaryContainer: m3colors.m3onPrimaryContainer property color colSecondary: m3colors.m3secondary property color colSecondaryHover: ColorUtils.mix(m3colors.m3secondary, colLayer1Hover, 0.85) property color colSecondaryActive: ColorUtils.mix(m3colors.m3secondary, colLayer1Active, 0.4) property color colSecondaryContainer: m3colors.m3secondaryContainer property color colSecondaryContainerHover: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.90) - property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, colLayer1Active, 0.54) + property color colSecondaryContainerActive: ColorUtils.mix(m3colors.m3secondaryContainer, m3colors.m3onSecondaryContainer, 0.54) property color colTertiary: m3colors.m3tertiary property color colTertiaryHover: ColorUtils.mix(m3colors.m3tertiary, colLayer1Hover, 0.85) property color colTertiaryActive: ColorUtils.mix(m3colors.m3tertiary, colLayer1Active, 0.4) diff --git a/.config/quickshell/ii/modules/common/functions/Session.qml b/.config/quickshell/ii/modules/common/functions/Session.qml new file mode 100644 index 000000000..1afc45817 --- /dev/null +++ b/.config/quickshell/ii/modules/common/functions/Session.qml @@ -0,0 +1,50 @@ +pragma Singleton +import Quickshell +import qs.services +import qs.modules.common + +Singleton { + id: root + + function closeAllWindows() { + HyprlandData.windowList.map(w => w.pid).forEach(pid => { + Quickshell.execDetached(["kill", pid]); + }); + } + + function lock() { + Quickshell.execDetached(["loginctl", "lock-session"]); + } + + function suspend() { + Quickshell.execDetached(["bash", "-c", "systemctl suspend || loginctl suspend"]); + } + + function logout() { + closeAllWindows(); + Quickshell.execDetached(["pkill", "Hyprland"]); + } + + function launchTaskManager() { + Quickshell.execDetached(["bash", "-c", `${Config.options.apps.taskManager}`]); + } + + function hibernate() { + Quickshell.execDetached(["bash", "-c", `systemctl hibernate || loginctl hibernate`]); + } + + function poweroff() { + closeAllWindows(); + Quickshell.execDetached(["bash", "-c", `systemctl poweroff || loginctl poweroff`]); + } + + function reboot() { + closeAllWindows(); + Quickshell.execDetached(["bash", "-c", `reboot || loginctl reboot`]); + } + + function rebootToFirmware() { + closeAllWindows(); + Quickshell.execDetached(["bash", "-c", `systemctl reboot --firmware-setup || loginctl reboot --firmware-setup`]); + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml index 0308fa974..b90c5e291 100644 --- a/.config/quickshell/ii/modules/common/widgets/RippleButton.qml +++ b/.config/quickshell/ii/modules/common/widgets/RippleButton.qml @@ -30,11 +30,11 @@ Button { property color colRippleToggled: Appearance?.colors.colPrimaryActive ?? "#D6CEE2" opacity: root.enabled ? 1 : 0.4 - property color buttonColor: root.enabled ? (root.toggled ? + property color buttonColor: ColorUtils.transparentize(root.toggled ? (root.hovered ? colBackgroundToggledHover : colBackgroundToggled) : (root.hovered ? colBackgroundHover : - colBackground)) : colBackground + colBackground), root.enabled ? 0 : 1) property color rippleColor: root.toggled ? colRippleToggled : colRipple function startRipple(x, y) { diff --git a/.config/quickshell/ii/modules/common/widgets/Toolbar.qml b/.config/quickshell/ii/modules/common/widgets/Toolbar.qml new file mode 100644 index 000000000..6826802ab --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/Toolbar.qml @@ -0,0 +1,39 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.widgets + +/** + * Material 3 expressive style toolbar. + * https://m3.material.io/components/toolbars + */ +Item { + id: root + + property real padding: 6 + property alias colBackground: background.color + default property alias data: toolbarLayout.data + implicitWidth: background.implicitWidth + implicitHeight: background.implicitHeight + + StyledRectangularShadow { + target: background + } + + Rectangle { + id: background + anchors.centerIn: parent + color: Appearance.colors.colLayer2 + implicitHeight: toolbarLayout.implicitHeight + root.padding * 2 + implicitWidth: toolbarLayout.implicitWidth + root.padding * 2 + radius: Appearance.rounding.full + + RowLayout { + id: toolbarLayout + anchors { + fill: parent + margins: root.padding + } + } + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/ToolbarButton.qml b/.config/quickshell/ii/modules/common/widgets/ToolbarButton.qml new file mode 100644 index 000000000..286c4edba --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/ToolbarButton.qml @@ -0,0 +1,10 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common + +RippleButton { + Layout.fillHeight: true + Layout.topMargin: 2 + Layout.bottomMargin: 2 + buttonRadius: Appearance.rounding.full +} diff --git a/.config/quickshell/ii/modules/common/widgets/ToolbarTextField.qml b/.config/quickshell/ii/modules/common/widgets/ToolbarTextField.qml new file mode 100644 index 000000000..fea574e96 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/ToolbarTextField.qml @@ -0,0 +1,30 @@ +import QtQuick +import QtQuick.Layouts +import QtQuick.Controls +import qs.modules.common +import qs.modules.common.widgets + +TextField { + id: filterField + + property alias colBackground: background.color + + Layout.fillHeight: true + Layout.topMargin: 2 + Layout.bottomMargin: 2 + implicitWidth: 200 + padding: 10 + + placeholderTextColor: Appearance.colors.colSubtext + color: Appearance.colors.colOnLayer1 + font.pixelSize: Appearance.font.pixelSize.small + renderType: Text.NativeRendering + selectedTextColor: Appearance.colors.colOnSecondaryContainer + selectionColor: Appearance.colors.colSecondaryContainer + + background: Rectangle { + id: background + color: Appearance.colors.colLayer1 + radius: Appearance.rounding.full + } +} diff --git a/.config/quickshell/ii/modules/common/widgets/VibrantToolbarButton.qml b/.config/quickshell/ii/modules/common/widgets/VibrantToolbarButton.qml new file mode 100644 index 000000000..c6e58f352 --- /dev/null +++ b/.config/quickshell/ii/modules/common/widgets/VibrantToolbarButton.qml @@ -0,0 +1,10 @@ +import QtQuick +import QtQuick.Layouts +import qs.modules.common +import qs.modules.common.functions + +ToolbarButton { + colBackground: ColorUtils.transparentize(Appearance.colors.colPrimaryContainer) + colBackgroundHover: Appearance.colors.colPrimaryContainerHover + colRipple: Appearance.colors.colPrimaryContainerActive +} diff --git a/.config/quickshell/ii/modules/lock/LockSurface.qml b/.config/quickshell/ii/modules/lock/LockSurface.qml index a0b28445d..a8ad410b8 100644 --- a/.config/quickshell/ii/modules/lock/LockSurface.qml +++ b/.config/quickshell/ii/modules/lock/LockSurface.qml @@ -1,6 +1,7 @@ import QtQuick import QtQuick.Layouts import Qt5Compat.GraphicalEffects +import qs import qs.services import qs.modules.common import qs.modules.common.widgets @@ -27,23 +28,20 @@ MouseArea { } } - Keys.onPressed: (event) => { // Esc to clear - // console.log("KEY!!") + Keys.onPressed: event => { // Esc to clear if (event.key === Qt.Key_Escape) { - root.context.currentText = "" + root.context.currentText = ""; } forceFieldFocus(); } hoverEnabled: true acceptedButtons: Qt.LeftButton - onPressed: (mouse) => { + onPressed: mouse => { forceFieldFocus(); - // console.log("Pressed") } - onPositionChanged: (mouse) => { + onPositionChanged: mouse => { forceFieldFocus(); - // console.log(JSON.stringify(mouse)) } anchors.fill: parent @@ -63,38 +61,57 @@ MouseArea { // } // } - // Password entry - Rectangle { - id: passwordBoxContainer + // Controls + Toolbar { anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom - bottomMargin: root.showInputField ? 20 : -height + bottomMargin: 20 } Behavior on anchors.bottomMargin { animation: Appearance.animation.elementMove.numberAnimation.createObject(this) } - radius: Appearance.rounding.full - color: Appearance.m3colors.m3surfaceContainer - implicitWidth: 160 - implicitHeight: 44 - StyledTextInput { + scale: 0.9 + opacity: 0 + Component.onCompleted: { + scale = 1 + opacity = 1 + } + Behavior on scale { + NumberAnimation { + duration: Appearance.animation.elementMove.duration + easing.type: Appearance.animation.elementMove.type + easing.bezierCurve: Appearance.animationCurves.expressiveFastSpatial + } + } + Behavior on opacity { + animation: Appearance.animation.elementMoveFast.numberAnimation.createObject(this) + } + + ToolbarButton { + id: sleepButton + implicitWidth: height + + onClicked: Session.suspend() + + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + iconSize: 24 + text: "dark_mode" + color: Appearance.colors.colOnPrimaryContainer + } + } + + ToolbarTextField { id: passwordBox + placeholderText: GlobalStates.screenUnlockFailed ? Translation.tr("Incorrect password") : Translation.tr("Enter password") - anchors { - fill: parent - margins: 10 - } + // Style clip: true - horizontalAlignment: TextInput.AlignHCenter - verticalAlignment: TextInput.AlignVCenter - focus: true - onFocusChanged: root.forceFieldFocus(); - color: Appearance.colors.colOnLayer2 - font { - pixelSize: 10 - } + font.pixelSize: Appearance.font.pixelSize.small // Password enabled: !root.context.unlockInProgress @@ -111,30 +128,24 @@ MouseArea { } } } - } - RippleButton { - anchors { - verticalCenter: passwordBoxContainer.verticalCenter - left: passwordBoxContainer.right - leftMargin: 5 - } + ToolbarButton { + id: confirmButton + implicitWidth: height + toggled: true + enabled: !root.context.unlockInProgress && root.context.currentText.length > 0 + colBackgroundToggled: Appearance.colors.colPrimary - visible: opacity > 0 - implicitHeight: passwordBoxContainer.implicitHeight - 12 - implicitWidth: implicitHeight - toggled: true - buttonRadius: passwordBoxContainer.radius - colBackground: Appearance.colors.colLayer2 - onClicked: root.context.tryUnlock() + onClicked: root.context.tryUnlock() - contentItem: MaterialSymbol { - anchors.centerIn: parent - horizontalAlignment: Text.AlignHCenter - verticalAlignment: Text.AlignVCenter - iconSize: 24 - text: "arrow_right_alt" - color: Appearance.colors.colOnPrimary + contentItem: MaterialSymbol { + anchors.centerIn: parent + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + iconSize: 24 + text: "arrow_right_alt" + color: confirmButton.enabled ? Appearance.colors.colOnPrimary : Appearance.colors.colSubtext + } } } } diff --git a/.config/quickshell/ii/modules/session/SessionActionButton.qml b/.config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml similarity index 100% rename from .config/quickshell/ii/modules/session/SessionActionButton.qml rename to .config/quickshell/ii/modules/sessionScreen/SessionActionButton.qml diff --git a/.config/quickshell/ii/modules/session/Session.qml b/.config/quickshell/ii/modules/sessionScreen/SessionScreen.qml similarity index 89% rename from .config/quickshell/ii/modules/session/Session.qml rename to .config/quickshell/ii/modules/sessionScreen/SessionScreen.qml index 6df45cd1b..d5b075670 100644 --- a/.config/quickshell/ii/modules/session/Session.qml +++ b/.config/quickshell/ii/modules/sessionScreen/SessionScreen.qml @@ -39,12 +39,6 @@ Scope { } } - function closeAllWindows() { - HyprlandData.windowList.map(w => w.pid).forEach((pid) => { - Quickshell.execDetached(["kill", pid]); - }); - } - function detectRunningStuff() { packageManagerRunning = false; downloadRunning = false; @@ -160,7 +154,7 @@ Scope { focus: sessionRoot.visible buttonIcon: "lock" buttonText: Translation.tr("Lock") - onClicked: { Quickshell.execDetached(["loginctl", "lock-session"]); sessionRoot.hide() } + onClicked: { Session.lock(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.right: sessionSleep KeyNavigation.down: sessionHibernate @@ -169,7 +163,7 @@ Scope { id: sessionSleep buttonIcon: "dark_mode" buttonText: Translation.tr("Sleep") - onClicked: { Quickshell.execDetached(["bash", "-c", "systemctl suspend || loginctl suspend"]); sessionRoot.hide() } + onClicked: { Session.suspend(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionLock KeyNavigation.right: sessionLogout @@ -179,7 +173,7 @@ Scope { id: sessionLogout buttonIcon: "logout" buttonText: Translation.tr("Logout") - onClicked: { root.closeAllWindows(); Quickshell.execDetached(["pkill", "Hyprland"]); sessionRoot.hide() } + onClicked: { Session.logout(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionSleep KeyNavigation.right: sessionTaskManager @@ -189,7 +183,7 @@ Scope { id: sessionTaskManager buttonIcon: "browse_activity" buttonText: Translation.tr("Task Manager") - onClicked: { Quickshell.execDetached(["bash", "-c", `${Config.options.apps.taskManager}`]); sessionRoot.hide() } + onClicked: { Session.launchTaskManager(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionLogout KeyNavigation.down: sessionFirmwareReboot @@ -199,7 +193,7 @@ Scope { id: sessionHibernate buttonIcon: "downloading" buttonText: Translation.tr("Hibernate") - onClicked: { Quickshell.execDetached(["bash", "-c", `systemctl hibernate || loginctl hibernate`]); sessionRoot.hide() } + onClicked: { Session.hibernate(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.up: sessionLock KeyNavigation.right: sessionShutdown @@ -208,7 +202,7 @@ Scope { id: sessionShutdown buttonIcon: "power_settings_new" buttonText: Translation.tr("Shutdown") - onClicked: { root.closeAllWindows(); Quickshell.execDetached(["bash", "-c", `systemctl poweroff || loginctl poweroff`]); sessionRoot.hide() } + onClicked: { Session.poweroff(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionHibernate KeyNavigation.right: sessionReboot @@ -218,7 +212,7 @@ Scope { id: sessionReboot buttonIcon: "restart_alt" buttonText: Translation.tr("Reboot") - onClicked: { root.closeAllWindows(); Quickshell.execDetached(["bash", "-c", `reboot || loginctl reboot`]); sessionRoot.hide() } + onClicked: { Session.reboot(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.left: sessionShutdown KeyNavigation.right: sessionFirmwareReboot @@ -228,7 +222,7 @@ Scope { id: sessionFirmwareReboot buttonIcon: "settings_applications" buttonText: Translation.tr("Reboot to firmware settings") - onClicked: { root.closeAllWindows(); Quickshell.execDetached(["bash", "-c", `systemctl reboot --firmware-setup || loginctl reboot --firmware-setup`]); sessionRoot.hide() } + onClicked: { Session.rebootToFirmware(); sessionRoot.hide() } onFocusChanged: { if (focus) sessionRoot.subtitle = buttonText } KeyNavigation.up: sessionTaskManager KeyNavigation.left: sessionReboot diff --git a/.config/quickshell/ii/shell.qml b/.config/quickshell/ii/shell.qml index 4877bd449..34114d5d7 100644 --- a/.config/quickshell/ii/shell.qml +++ b/.config/quickshell/ii/shell.qml @@ -19,7 +19,7 @@ import "./modules/onScreenDisplay/" import "./modules/onScreenKeyboard/" import "./modules/overview/" import "./modules/screenCorners/" -import "./modules/session/" +import "./modules/sessionScreen/" import "./modules/sidebarLeft/" import "./modules/sidebarRight/" import "./modules/verticalBar/" @@ -45,7 +45,7 @@ ShellRoot { property bool enableOverview: true property bool enableReloadPopup: true property bool enableScreenCorners: true - property bool enableSession: true + property bool enableSessionScreen: true property bool enableSidebarLeft: true property bool enableSidebarRight: true property bool enableVerticalBar: true @@ -72,7 +72,7 @@ ShellRoot { LazyLoader { active: enableOverview; component: Overview {} } LazyLoader { active: enableReloadPopup; component: ReloadPopup {} } LazyLoader { active: enableScreenCorners; component: ScreenCorners {} } - LazyLoader { active: enableSession; component: Session {} } + LazyLoader { active: enableSessionScreen; component: SessionScreen {} } LazyLoader { active: enableSidebarLeft; component: SidebarLeft {} } LazyLoader { active: enableSidebarRight; component: SidebarRight {} } LazyLoader { active: enableVerticalBar && Config.ready && Config.options.bar.vertical; component: VerticalBar {} }