mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-02 12:47:49 +02:00
181 lines
6.0 KiB
QML
181 lines
6.0 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import qs.Commons
|
|
import qs.Ui
|
|
|
|
Item {
|
|
id: root
|
|
|
|
property string backgroundPath: ""
|
|
property int backgroundVersion: 0
|
|
property bool fingerprintConfigured: false
|
|
property bool authenticatingPassword: false
|
|
property string failureMessage: ""
|
|
property int failedAttempts: 0
|
|
property bool inputEnabled: true
|
|
property bool loadBackground: true
|
|
property string passwordText: ""
|
|
property bool syncingPasswordText: false
|
|
|
|
readonly property string placeholderText: "Enter Password"
|
|
readonly property int fieldWidth: 381
|
|
readonly property int fieldHeight: 67
|
|
readonly property int outlineThickness: 3
|
|
readonly property int fieldFontSize: Math.round(Style.font.heading * 1.125)
|
|
readonly property int passwordDotFontSize: Math.round(Style.font.heading * 1.33)
|
|
readonly property int passwordDotLetterSpacing: Math.round(Style.font.heading * 0.19)
|
|
readonly property bool showPasswordCursor: inputEnabled && !authenticatingPassword && failureMessage.length === 0
|
|
readonly property bool errorState: failureMessage.length > 0
|
|
readonly property var inputBorderSpec: errorState
|
|
? Border.surfaceSpec("lock", "border-error", Color.lock.borderError, root.outlineThickness, "border-alpha")
|
|
: Border.surfaceSpec("lock", "border-active", Color.lock.borderActive, root.outlineThickness, "border-alpha")
|
|
|
|
signal submitPassword(string password)
|
|
signal passwordTextEdited(string password)
|
|
signal clearFailureRequested()
|
|
signal wakeRequested()
|
|
|
|
// Cache-busts the lock background by appending `?v=`. Adding a query
|
|
// string keeps Image's loader happy while forcing it to reload when the
|
|
// user picks a new background mid-session.
|
|
function fileUrl(path) {
|
|
if (!path) return ""
|
|
var encoded = String(path).split("/").map(encodeURIComponent).join("/")
|
|
return "file://" + encoded + "?v=" + backgroundVersion
|
|
}
|
|
|
|
function forcePasswordFocus() {
|
|
passwordInput.forceActiveFocus()
|
|
}
|
|
|
|
function clearPassword() {
|
|
passwordTextEdited("")
|
|
}
|
|
|
|
function syncPasswordText() {
|
|
if (passwordInput.text === passwordText) return
|
|
syncingPasswordText = true
|
|
passwordInput.text = passwordText
|
|
syncingPasswordText = false
|
|
}
|
|
|
|
onPasswordTextChanged: syncPasswordText()
|
|
onInputEnabledChanged: {
|
|
if (inputEnabled) Qt.callLater(forcePasswordFocus)
|
|
}
|
|
Component.onCompleted: {
|
|
syncPasswordText()
|
|
if (inputEnabled) Qt.callLater(forcePasswordFocus)
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Color.background
|
|
|
|
Image {
|
|
id: wallpaper
|
|
anchors.fill: parent
|
|
source: root.loadBackground ? root.fileUrl(root.backgroundPath) : ""
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
cache: false
|
|
sourceSize.width: width
|
|
sourceSize.height: height
|
|
}
|
|
|
|
MultiEffect {
|
|
anchors.fill: wallpaper
|
|
source: wallpaper
|
|
autoPaddingEnabled: false
|
|
blurEnabled: root.loadBackground && wallpaper.status === Image.Ready
|
|
blur: 1.0
|
|
blurMax: 128
|
|
blurMultiplier: 1.25
|
|
contrast: -0.08
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
onClicked: { root.wakeRequested(); root.forcePasswordFocus() }
|
|
onPositionChanged: root.wakeRequested()
|
|
}
|
|
|
|
BorderSurface {
|
|
id: inputField
|
|
width: root.fieldWidth
|
|
height: root.fieldHeight
|
|
anchors.centerIn: parent
|
|
color: Color.lock.background
|
|
borderSpec: root.inputBorderSpec
|
|
radius: Style.cornerRadius
|
|
clip: true
|
|
|
|
TextInput {
|
|
id: passwordInput
|
|
anchors.fill: parent
|
|
anchors.topMargin: inputField.borderTop
|
|
anchors.rightMargin: inputField.borderRight + 18
|
|
anchors.bottomMargin: inputField.borderBottom
|
|
anchors.leftMargin: inputField.borderLeft + 18
|
|
verticalAlignment: TextInput.AlignVCenter
|
|
horizontalAlignment: TextInput.AlignHCenter
|
|
activeFocusOnPress: true
|
|
clip: true
|
|
enabled: root.inputEnabled && !root.authenticatingPassword
|
|
readOnly: root.authenticatingPassword
|
|
echoMode: TextInput.Password
|
|
passwordCharacter: "\u25CF"
|
|
passwordMaskDelay: 0
|
|
color: Color.lock.text
|
|
selectionColor: Color.lock.selection
|
|
selectedTextColor: Color.lock.text
|
|
font.family: Style.font.family
|
|
font.pixelSize: text.length > 0 ? root.passwordDotFontSize : root.fieldFontSize
|
|
font.letterSpacing: text.length > 0 ? root.passwordDotLetterSpacing : 0
|
|
cursorVisible: activeFocus && root.showPasswordCursor && text.length > 0
|
|
cursorDelegate: Rectangle {
|
|
width: 2
|
|
color: Color.lock.text
|
|
visible: passwordInput.cursorVisible
|
|
}
|
|
|
|
onTextChanged: {
|
|
if (!root.syncingPasswordText) root.passwordTextEdited(text)
|
|
if (text.length > 0) {
|
|
root.wakeRequested()
|
|
}
|
|
if (text.length > 0 && root.failureMessage.length > 0) root.clearFailureRequested()
|
|
}
|
|
|
|
onAccepted: {
|
|
var submitted = root.passwordText
|
|
root.passwordTextEdited("")
|
|
if (submitted.length > 0) root.submitPassword(submitted)
|
|
}
|
|
|
|
Keys.onPressed: function(event) {
|
|
root.wakeRequested()
|
|
if (event.key === Qt.Key_Escape || (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_U)) {
|
|
root.passwordTextEdited("")
|
|
event.accepted = true
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
anchors.fill: passwordInput
|
|
text: root.authenticatingPassword ? "Checking…" : (root.failureMessage.length > 0 ? root.failureMessage : root.placeholderText)
|
|
visible: passwordInput.text.length === 0
|
|
color: root.authenticatingPassword ? Color.lock.text : (root.failureMessage.length > 0 ? Color.lock.textError : Color.lock.placeholder)
|
|
font.family: Style.font.family
|
|
font.pixelSize: root.fieldFontSize
|
|
font.italic: !root.authenticatingPassword && root.failureMessage.length > 0
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|