lock: input field + separate pam

This commit is contained in:
2 * r + 2 * t 2025-08-10 19:22:27 +10:00
parent ba1c6fda43
commit 56ca1632c5
6 changed files with 303 additions and 85 deletions

View file

@ -5,23 +5,6 @@ JsonObject {
property Sizes sizes: Sizes {}
component Sizes: JsonObject {
property int clockWidth: 800
property int clockHeight: 200
property int inputWidth: 600
property int inputHeight: 200
property int faceSize: 200
property int weatherWidth: 400
property int weatherHeight: 100
property int mediaWidth: 600
property int mediaWidthSmall: 450
property int mediaHeight: 170
property int mediaHeightSmall: 150
property int mediaCoverSize: 150
property int mediaCoverSizeSmall: 120
property int mediaCoverBorder: 3
property int largeScreenWidth: 2560
property int smallScreenWidth: 1080
property int buttonsWidth: 400
property int buttonsWidthSmall: 300
property int centerWidth: 600
}
}

214
modules/lock/Center.qml Normal file
View file

@ -0,0 +1,214 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.images
import qs.services
import qs.config
import qs.utils
import Quickshell
import QtQuick
import QtQuick.Layouts
ColumnLayout {
id: root
required property var lock
readonly property list<string> timeComponents: Time.format(Config.services.useTwelveHourClock ? "hh:mm:A" : "hh:mm").split(":")
Layout.fillWidth: true
Layout.fillHeight: true
Layout.minimumWidth: Config.lock.sizes.centerWidth
spacing: Appearance.spacing.large * 2
RowLayout {
Layout.alignment: Qt.AlignHCenter
Layout.bottomMargin: Appearance.spacing.large
spacing: Appearance.spacing.small
StyledText {
Layout.alignment: Qt.AlignVCenter
text: root.timeComponents[0]
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: ":"
color: Colours.palette.m3primary
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
}
StyledText {
Layout.alignment: Qt.AlignVCenter
text: root.timeComponents[1]
color: Colours.palette.m3secondary
font.pointSize: Appearance.font.size.extraLarge * 3
font.bold: true
}
Loader {
Layout.leftMargin: Appearance.spacing.normal
Layout.alignment: Qt.AlignVCenter
asynchronous: true
active: Config.services.useTwelveHourClock
visible: active
sourceComponent: StyledText {
text: root.timeComponents[2] ?? ""
color: Colours.palette.m3primary
font.pointSize: Appearance.font.size.extraLarge * 2
font.bold: true
}
}
}
StyledClippingRect {
Layout.alignment: Qt.AlignHCenter
implicitWidth: Config.lock.sizes.centerWidth / 2
implicitHeight: Config.lock.sizes.centerWidth / 2
color: Colours.tPalette.m3surfaceContainer
radius: Appearance.rounding.full
MaterialIcon {
anchors.centerIn: parent
text: "person"
fill: 1
grade: 200
font.pointSize: Math.floor(Config.lock.sizes.centerWidth / 4)
}
CachingImage {
id: pfp
anchors.fill: parent
path: `${Paths.stringify(Paths.home)}/.face`
}
}
StyledRect {
Layout.alignment: Qt.AlignHCenter
implicitWidth: Config.lock.sizes.centerWidth * 0.8
implicitHeight: input.implicitHeight + Appearance.padding.small * 2
color: Colours.palette.m3surfaceContainer
radius: Appearance.rounding.full
focus: true
onActiveFocusChanged: {
if (!activeFocus)
forceActiveFocus();
}
Keys.onPressed: event => root.lock.pam.handleKey(event)
RowLayout {
id: input
anchors.fill: parent
anchors.margins: Appearance.padding.small
spacing: Appearance.spacing.normal
MaterialIcon {
Layout.leftMargin: Appearance.padding.smaller
text: "lock"
}
ListView {
id: passwordList
Layout.fillWidth: true
implicitHeight: Appearance.font.size.normal
orientation: Qt.Horizontal
clip: true
spacing: Appearance.spacing.small / 2
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 0
preferredHighlightEnd: count ? width - implicitHeight * 2 : 0
currentIndex: count - 1
model: ScriptModel {
values: root.lock.pam.buffer
}
delegate: StyledRect {
implicitWidth: implicitHeight
implicitHeight: passwordList.implicitHeight
color: Colours.palette.m3onSurface
radius: Appearance.rounding.small / 2
}
add: Transition {
Anim {
property: "scale"
from: 0
to: 1
duration: Appearance.anim.durations.expressiveFastSpatial
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial
}
}
remove: Transition {
Anim {
property: "scale"
to: 0.5
}
Anim {
property: "opacity"
to: 0
}
}
highlightFollowsCurrentItem: false
highlight: Item {
x: passwordList.currentItem?.x ?? 0
Behavior on x {
Anim {}
}
}
}
StyledRect {
implicitWidth: implicitHeight
implicitHeight: enterIcon.implicitHeight + Appearance.padding.small * 2
color: root.lock.pam.buffer ? Colours.palette.m3primary : Colours.layer(Colours.palette.m3surfaceContainerHigh, 2)
radius: Appearance.rounding.full
StateLayer {
color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
function onClicked(): void {
root.lock.pam.start();
}
}
MaterialIcon {
id: enterIcon
anchors.centerIn: parent
text: "arrow_forward"
color: root.lock.pam.buffer ? Colours.palette.m3onPrimary : Colours.palette.m3onSurface
font.weight: 500
}
}
}
}
component Anim: NumberAnimation {
duration: Appearance.anim.durations.normal
easing.type: Easing.BezierSpline
easing.bezierCurve: Appearance.anim.curves.standard
}
}

View file

@ -1,14 +1,10 @@
pragma ComponentBehavior: Bound
import qs.components
import qs.components.images
import qs.services
import qs.config
import qs.utils
import QtQuick
import QtQuick.Layouts
GridLayout {
RowLayout {
id: root
required property var lock
@ -16,85 +12,52 @@ GridLayout {
anchors.fill: parent
anchors.margins: Appearance.padding.large
rowSpacing: Appearance.spacing.large
columnSpacing: Appearance.spacing.large
spacing: Appearance.spacing.large
rows: 2
columns: 3
StyledRect {
Layout.row: 0
Layout.column: 0
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: Appearance.spacing.normal
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
StyledRect {
Layout.fillWidth: true
Layout.fillHeight: true
StyledRect {
Layout.row: 1
Layout.column: 0
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
StyledClippingRect {
Layout.row: 0
Layout.column: 1
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
implicitWidth: Config.lock.sizes.faceSize
implicitHeight: Config.lock.sizes.faceSize
radius: Appearance.rounding.large
color: Colours.tPalette.m3surfaceContainer
MaterialIcon {
anchors.centerIn: parent
text: "person"
fill: 1
grade: 200
font.pointSize: Math.floor(Config.lock.sizes.faceSize / 2)
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
CachingImage {
id: pfp
StyledRect {
Layout.fillWidth: true
Layout.fillHeight: true
anchors.fill: parent
path: `${Paths.stringify(Paths.home)}/.face`
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
}
Input {
Layout.row: 1
Layout.column: 1
Center {
lock: root.lock
}
StyledRect {
Layout.row: 0
Layout.column: 2
ColumnLayout {
Layout.fillWidth: true
Layout.fillHeight: true
spacing: Appearance.spacing.normal
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
StyledRect {
Layout.fillWidth: true
Layout.fillHeight: true
StyledRect {
Layout.row: 1
Layout.column: 2
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
StyledRect {
Layout.fillWidth: true
Layout.fillHeight: true
radius: Appearance.rounding.small
color: Colours.tPalette.m3surfaceContainer
}
}
component Anim: NumberAnimation {

View file

@ -14,9 +14,16 @@ Scope {
LockSurface {
lock: lock
pam: pam
}
}
Pam {
id: pam
lock: lock
}
CustomShortcut {
name: "lock"
description: "Lock the current session"

View file

@ -12,6 +12,7 @@ WlSessionLockSurface {
id: root
required property WlSessionLock lock
required property Pam pam
property bool locked

50
modules/lock/Pam.qml Normal file
View file

@ -0,0 +1,50 @@
import Quickshell.Wayland
import Quickshell.Services.Pam
import QtQuick
PamContext {
id: root
required property WlSessionLock lock
property string state: "none"
property string buffer: ""
function handleKey(event: KeyEvent): void {
if (active)
return;
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
start();
} else if (event.key === Qt.Key_Backspace) {
if (event.modifiers & Qt.ControlModifier) {
buffer = "";
} else {
buffer = buffer.slice(0, -1);
}
} else if (" abcdefghijklmnopqrstuvwxyz1234567890`~!@#$%^&*()-_=+[{]}\\|;:'\",<.>/?".includes(event.text.toLowerCase())) {
// No illegal characters (you are insane if you use unicode in your password)
buffer += event.text;
}
}
onResponseRequiredChanged: {
if (!responseRequired)
return;
respond(buffer);
buffer = "";
}
onCompleted: res => {
if (res === PamResult.Success)
return lock.unlock();
if (res === PamResult.Error)
state = "error";
else if (res === PamResult.MaxTries)
state = "max";
else if (res === PamResult.Failed)
state = "fail";
}
}