mirror of
https://github.com/arthur-pbty/arthur-os.git
synced 2026-08-04 20:28:23 +02:00
153 lines
4.6 KiB
QML
153 lines
4.6 KiB
QML
import QtQuick
|
|
import QtQuick.Effects
|
|
import qs.Commons
|
|
|
|
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 real scaleFactor: 1
|
|
property bool hasTyped: false
|
|
|
|
readonly property string fingerprintGlyph: "\uDB80\uDE37"
|
|
readonly property string placeholderText: fingerprintConfigured ? "Enter Password " + fingerprintGlyph : "Enter Password"
|
|
readonly property real effectiveScale: Math.max(1, scaleFactor)
|
|
readonly property int fieldWidth: Math.round(650 / effectiveScale)
|
|
readonly property int fieldHeight: Math.round(100 / effectiveScale)
|
|
readonly property int outlineThickness: Math.max(1, Math.round(4 / effectiveScale))
|
|
readonly property int fieldFontSize: Style.font.heading
|
|
|
|
signal submitPassword(string password)
|
|
|
|
function withAlpha(color, alpha) {
|
|
return Qt.rgba(color.r, color.g, color.b, alpha)
|
|
}
|
|
|
|
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() {
|
|
passwordInput.text = ""
|
|
hasTyped = false
|
|
}
|
|
|
|
onInputEnabledChanged: {
|
|
hasTyped = false
|
|
if (inputEnabled) Qt.callLater(forcePasswordFocus)
|
|
}
|
|
Component.onCompleted: {
|
|
hasTyped = false
|
|
if (inputEnabled) Qt.callLater(forcePasswordFocus)
|
|
}
|
|
|
|
Rectangle {
|
|
anchors.fill: parent
|
|
color: Color.background
|
|
|
|
Image {
|
|
id: wallpaper
|
|
anchors.fill: parent
|
|
source: root.fileUrl(root.backgroundPath)
|
|
fillMode: Image.PreserveAspectCrop
|
|
asynchronous: true
|
|
cache: false
|
|
sourceSize.width: width
|
|
sourceSize.height: height
|
|
}
|
|
|
|
MultiEffect {
|
|
anchors.fill: wallpaper
|
|
source: wallpaper
|
|
blurEnabled: wallpaper.status === Image.Ready
|
|
blur: 1.0
|
|
blurMax: 64
|
|
blurMultiplier: 1.0
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
onClicked: root.forcePasswordFocus()
|
|
}
|
|
|
|
Rectangle {
|
|
id: inputField
|
|
width: root.fieldWidth
|
|
height: root.fieldHeight
|
|
anchors.centerIn: parent
|
|
color: root.withAlpha(Color.background, 0.8)
|
|
border.color: root.failureMessage.length > 0 ? Color.urgent : (root.authenticatingPassword ? Color.accent : Color.foreground)
|
|
border.width: root.outlineThickness
|
|
radius: Style.cornerRadius
|
|
clip: true
|
|
|
|
TextInput {
|
|
id: passwordInput
|
|
anchors.fill: parent
|
|
anchors.leftMargin: root.outlineThickness + 18
|
|
anchors.rightMargin: root.outlineThickness + 18
|
|
verticalAlignment: TextInput.AlignVCenter
|
|
horizontalAlignment: TextInput.AlignHCenter
|
|
activeFocusOnPress: true
|
|
clip: true
|
|
enabled: root.inputEnabled && !root.authenticatingPassword
|
|
readOnly: root.authenticatingPassword
|
|
echoMode: TextInput.Password
|
|
passwordCharacter: "\u2022"
|
|
passwordMaskDelay: 0
|
|
color: Color.foreground
|
|
selectionColor: root.withAlpha(Color.accent, 0.45)
|
|
selectedTextColor: Color.foreground
|
|
font.family: "monospace"
|
|
font.pixelSize: root.fieldFontSize
|
|
cursorVisible: activeFocus && !root.authenticatingPassword && root.hasTyped
|
|
|
|
onTextChanged: {
|
|
if (text.length > 0) root.hasTyped = true
|
|
if (text.length > 0 && root.failureMessage.length > 0) root.failureMessage = ""
|
|
}
|
|
|
|
onAccepted: {
|
|
var submitted = text
|
|
text = ""
|
|
root.hasTyped = false
|
|
if (submitted.length > 0) root.submitPassword(submitted)
|
|
}
|
|
|
|
Keys.onPressed: function(event) {
|
|
if (event.key === Qt.Key_Escape || (event.modifiers & Qt.ControlModifier && event.key === Qt.Key_U)) {
|
|
text = ""
|
|
root.hasTyped = false
|
|
event.accepted = true
|
|
}
|
|
}
|
|
}
|
|
|
|
Text {
|
|
anchors.fill: passwordInput
|
|
text: root.failureMessage.length > 0 ? root.failureMessage : root.placeholderText
|
|
visible: passwordInput.text.length === 0
|
|
color: root.failureMessage.length > 0 ? Color.urgent : Color.foreground
|
|
font.family: "monospace"
|
|
font.pixelSize: root.fieldFontSize
|
|
font.italic: root.failureMessage.length > 0
|
|
horizontalAlignment: Text.AlignHCenter
|
|
verticalAlignment: Text.AlignVCenter
|
|
elide: Text.ElideRight
|
|
}
|
|
}
|
|
}
|
|
}
|