lock: glance-first touch surface, keypad behind swipe-to-unlock
PIN pad hidden until swipe up / tap hint / hardware key; retreats after 25s idle with nothing typed. shouldReFocus no longer implies reveal -- hypridle fires it on every wake and wake must land on glance.
This commit is contained in:
parent
4fe27d9ef5
commit
401a6609ea
1 changed files with 82 additions and 2 deletions
|
|
@ -29,9 +29,19 @@ MouseArea {
|
|||
readonly property int keySize: 96
|
||||
readonly property int keySpacing: 18
|
||||
|
||||
// Two-stage lock: glance (ambient cards + swipe hint) and PIN. The pad
|
||||
// is not always up — a swipe up (or any hardware key) reveals it, and it
|
||||
// retreats after sitting idle with nothing typed. Credentials machinery
|
||||
// is untouched; this is purely which stage is presented.
|
||||
property bool pinRevealed: false
|
||||
property real pressY: 0
|
||||
|
||||
function forceFieldFocus() {
|
||||
root.forceActiveFocus();
|
||||
}
|
||||
// Note: shouldReFocus does NOT reveal the pad — hypridle's after_sleep_cmd
|
||||
// fires it on every wake to fix Hyprland's keyboard-focus loss, and wake
|
||||
// must land on glance, not the keypad. Typing reveals via Keys below.
|
||||
Connections {
|
||||
target: root.context
|
||||
function onShouldReFocus() {
|
||||
|
|
@ -39,7 +49,26 @@ MouseArea {
|
|||
}
|
||||
}
|
||||
Component.onCompleted: forceFieldFocus()
|
||||
onPressed: forceFieldFocus()
|
||||
onPressed: mouse => {
|
||||
root.pressY = mouse.y;
|
||||
forceFieldFocus();
|
||||
}
|
||||
onPositionChanged: mouse => {
|
||||
if (!root.pinRevealed && root.pressY - mouse.y > 60)
|
||||
root.pinRevealed = true;
|
||||
else if (root.pinRevealed && mouse.y - root.pressY > 80
|
||||
&& root.context.currentText.length === 0
|
||||
&& !root.context.unlockInProgress)
|
||||
root.pinRevealed = false;
|
||||
}
|
||||
|
||||
// Retreat to glance when the pad sits unused and empty.
|
||||
Timer {
|
||||
interval: 25000
|
||||
running: root.pinRevealed && root.context.currentText.length === 0
|
||||
&& !root.context.unlockInProgress
|
||||
onTriggered: root.pinRevealed = false
|
||||
}
|
||||
|
||||
// Souveraine-owned ambient content. Credentials stay below this host, so
|
||||
// phone, laptop, and desktop can rearrange the same cards later without
|
||||
|
|
@ -59,9 +88,11 @@ MouseArea {
|
|||
root.context.currentText += d;
|
||||
}
|
||||
|
||||
// Hardware keyboard entry (USB-C keyboard); mirrors the desktop surface
|
||||
// Hardware keyboard entry (USB-C keyboard); mirrors the desktop surface.
|
||||
// Any key is also a reveal gesture, so typing works from glance.
|
||||
focus: true
|
||||
Keys.onPressed: event => {
|
||||
root.pinRevealed = true;
|
||||
root.context.resetClearTimer();
|
||||
if (event.key === Qt.Key_Backspace) {
|
||||
root.context.currentText = (event.modifiers & Qt.ControlModifier)
|
||||
|
|
@ -75,6 +106,40 @@ MouseArea {
|
|||
}
|
||||
}
|
||||
|
||||
// Glance-stage hint; tapping it is an alternative to the swipe.
|
||||
ColumnLayout {
|
||||
id: swipeHint
|
||||
anchors {
|
||||
horizontalCenter: parent.horizontalCenter
|
||||
bottom: parent.bottom
|
||||
bottomMargin: 64
|
||||
}
|
||||
z: 2
|
||||
spacing: 2
|
||||
visible: opacity > 0
|
||||
opacity: root.pinRevealed ? 0 : 1
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 180 }
|
||||
}
|
||||
|
||||
MaterialSymbol {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: "keyboard_arrow_up"
|
||||
iconSize: 34
|
||||
color: Appearance.colors.colOnSurfaceVariant
|
||||
}
|
||||
StyledText {
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
text: Translation.tr("Swipe up to unlock")
|
||||
color: Appearance.colors.colSubtext
|
||||
font.pixelSize: Appearance.font.pixelSize.normal
|
||||
}
|
||||
|
||||
TapHandler {
|
||||
onTapped: root.pinRevealed = true
|
||||
}
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
id: padColumn
|
||||
anchors {
|
||||
|
|
@ -84,6 +149,21 @@ MouseArea {
|
|||
}
|
||||
z: 2
|
||||
spacing: 20
|
||||
visible: opacity > 0
|
||||
opacity: root.pinRevealed ? 1 : 0
|
||||
Behavior on opacity {
|
||||
NumberAnimation { duration: 200 }
|
||||
}
|
||||
// Slide up from below the screen edge as it reveals.
|
||||
transform: Translate {
|
||||
y: root.pinRevealed ? 0 : padColumn.height + 48
|
||||
Behavior on y {
|
||||
NumberAnimation {
|
||||
duration: 260
|
||||
easing.type: Easing.OutCubic
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Entered-PIN dots, with the empty-state hint behind them.
|
||||
// In a ColumnLayout so ErrorShakeAnimation's Layout.leftMargin works.
|
||||
|
|
|
|||
Loading…
Reference in a new issue