lock: add placeholder + manual focus

This commit is contained in:
2 * r + 2 * t 2025-08-10 19:37:34 +10:00
parent 56ca1632c5
commit 31cc51b6e9
2 changed files with 104 additions and 58 deletions

View file

@ -110,6 +110,15 @@ ColumnLayout {
Keys.onPressed: event => root.lock.pam.handleKey(event) Keys.onPressed: event => root.lock.pam.handleKey(event)
StateLayer {
hoverEnabled: false
cursorShape: Qt.IBeamCursor
function onClicked(): void {
parent.forceActiveFocus();
}
}
RowLayout { RowLayout {
id: input id: input
@ -122,58 +131,81 @@ ColumnLayout {
text: "lock" text: "lock"
} }
ListView { Item {
id: passwordList
Layout.fillWidth: true Layout.fillWidth: true
implicitHeight: Appearance.font.size.normal Layout.fillHeight: true
orientation: Qt.Horizontal ListView {
clip: true id: passwordList
spacing: Appearance.spacing.small / 2
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 0
preferredHighlightEnd: count ? width - implicitHeight * 2 : 0
currentIndex: count - 1
model: ScriptModel { anchors.left: parent.left
values: root.lock.pam.buffer anchors.right: parent.right
} anchors.verticalCenter: parent.verticalCenter
implicitHeight: Appearance.font.size.normal
delegate: StyledRect { orientation: Qt.Horizontal
implicitWidth: implicitHeight clip: true
implicitHeight: passwordList.implicitHeight interactive: false
spacing: Appearance.spacing.small / 2
highlightRangeMode: ListView.StrictlyEnforceRange
preferredHighlightBegin: 0
preferredHighlightEnd: count ? width - implicitHeight * 2 : 0
currentIndex: count - 1
color: Colours.palette.m3onSurface model: ScriptModel {
radius: Appearance.rounding.small / 2 values: root.lock.pam.buffer
} }
add: Transition { delegate: StyledRect {
Anim { implicitWidth: implicitHeight
property: "scale" implicitHeight: passwordList.implicitHeight
from: 0
to: 1 color: Colours.palette.m3onSurface
duration: Appearance.anim.durations.expressiveFastSpatial radius: Appearance.rounding.small / 2
easing.bezierCurve: Appearance.anim.curves.expressiveFastSpatial }
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 {}
}
} }
} }
remove: Transition { StyledText {
Anim { anchors.centerIn: parent
property: "scale"
to: 0.5
}
Anim {
property: "opacity"
to: 0
}
}
highlightFollowsCurrentItem: false text: qsTr("Enter your password")
highlight: Item { color: Colours.palette.m3outline
x: passwordList.currentItem?.x ?? 0 font.pointSize: Appearance.font.size.normal
font.family: Appearance.font.family.mono
Behavior on x { opacity: root.lock.pam.buffer ? 0 : 1
Behavior on opacity {
Anim {} Anim {}
} }
} }

View file

@ -1,8 +1,9 @@
import Quickshell
import Quickshell.Wayland import Quickshell.Wayland
import Quickshell.Services.Pam import Quickshell.Services.Pam
import QtQuick import QtQuick
PamContext { Scope {
id: root id: root
required property WlSessionLock lock required property WlSessionLock lock
@ -11,11 +12,11 @@ PamContext {
property string buffer: "" property string buffer: ""
function handleKey(event: KeyEvent): void { function handleKey(event: KeyEvent): void {
if (active) if (passwd.active)
return; return;
if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) { if (event.key === Qt.Key_Enter || event.key === Qt.Key_Return) {
start(); passwd.start();
} else if (event.key === Qt.Key_Backspace) { } else if (event.key === Qt.Key_Backspace) {
if (event.modifiers & Qt.ControlModifier) { if (event.modifiers & Qt.ControlModifier) {
buffer = ""; buffer = "";
@ -28,23 +29,36 @@ PamContext {
} }
} }
onResponseRequiredChanged: { PamContext {
if (!responseRequired) id: passwd
return;
respond(buffer); onResponseRequiredChanged: {
buffer = ""; if (!responseRequired)
return;
respond(root.buffer);
root.buffer = "";
}
onCompleted: res => {
if (res === PamResult.Success)
return root.lock.unlock();
if (res === PamResult.Error)
root.state = "error";
else if (res === PamResult.MaxTries)
root.state = "max";
else if (res === PamResult.Failed)
root.state = "fail";
stateReset.restart();
}
} }
onCompleted: res => { Timer {
if (res === PamResult.Success) id: stateReset
return lock.unlock();
if (res === PamResult.Error) interval: 4000
state = "error"; onTriggered: root.state = "none"
else if (res === PamResult.MaxTries)
state = "max";
else if (res === PamResult.Failed)
state = "fail";
} }
} }