Warning, /plasma/plasma-mobile/look-and-feel/contents/lockscreen/PasswordBar.qml is written in an unsupported language. File is not indexed.
0001 /* 0002 * SPDX-FileCopyrightText: 2020-2022 Devin Lin <espidev@gmail.com> 0003 * SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 import QtQuick 2.12 0007 import QtQuick.Controls 2.1 0008 import QtQuick.Layouts 1.1 0009 import QtGraphicalEffects 1.12 0010 0011 import org.kde.plasma.core 2.0 as PlasmaCore 0012 import org.kde.plasma.workspace.keyboardlayout 1.0 0013 import org.kde.plasma.workspace.keyboardlayout 1.0 as Keyboards 0014 0015 import org.kde.kirigami 2.12 as Kirigami 0016 0017 Rectangle { 0018 id: root 0019 implicitHeight: PlasmaCore.Units.gridUnit * 2.5 0020 0021 required property var lockScreenState 0022 0023 property alias textField: textField 0024 0025 // toggle between pin and password mode 0026 property bool isPinMode: true 0027 0028 // for displaying temporary number in pin dot display 0029 property int previewCharIndex: -2 0030 0031 property string pinLabel: qsTr("Enter PIN") 0032 0033 property bool keypadOpen 0034 0035 readonly property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255}) 0036 readonly property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255}) 0037 0038 // model for shown dots 0039 // we need to use a listmodel to avoid all delegates from reloading 0040 ListModel { 0041 id: dotDisplayModel 0042 } 0043 0044 Connections { 0045 target: root.lockScreenState 0046 0047 function onUnlockSucceeded() { 0048 root.pinLabel = qsTr("Logging in..."); 0049 } 0050 0051 function onUnlockFailed() { 0052 root.pinLabel = qsTr("Wrong PIN"); 0053 } 0054 0055 function onPasswordChanged() { 0056 while (root.lockScreenState.password.length < dotDisplayModel.count) { 0057 dotDisplayModel.remove(dotDisplayModel.count - 1); 0058 } 0059 while (root.lockScreenState.password.length > dotDisplayModel.count) { 0060 dotDisplayModel.append({"char": root.lockScreenState.password.charAt(dotDisplayModel.count)}); 0061 } 0062 } 0063 } 0064 0065 // keypad functions 0066 function backspace() { 0067 if (!lockScreenState.waitingForAuth) { 0068 root.previewCharIndex = -2; 0069 lockScreenState.password = lockScreenState.password.substr(0, lockScreenState.password.length - 1); 0070 } 0071 } 0072 0073 function clear() { 0074 if (!lockScreenState.waitingForAuth) { 0075 root.previewCharIndex = -2; 0076 lockScreenState.resetPassword(); 0077 } 0078 } 0079 0080 function enter() { 0081 lockScreenState.tryPassword(); 0082 0083 if (keypadOpen && !isPinMode) { 0084 // make sure keyboard doesn't close 0085 openKeyboardTimer.restart(); 0086 } 0087 } 0088 0089 function keyPress(data) { 0090 if (!lockScreenState.waitingForAuth) { 0091 0092 if (root.pinLabel !== qsTr("Enter PIN")) { 0093 root.pinLabel = qsTr("Enter PIN"); 0094 } 0095 0096 root.previewCharIndex = lockScreenState.password.length; 0097 lockScreenState.password += data 0098 0099 // trigger turning letter into dot later 0100 letterTimer.restart(); 0101 } 0102 } 0103 0104 // HACK: we have to open the virtual keyboard after a certain amount of time or else it will close anyway 0105 Timer { 0106 id: openKeyboardTimer 0107 interval: 10 0108 running: false 0109 repeat: false 0110 onTriggered: Keyboards.KWinVirtualKeyboard.active = true 0111 } 0112 0113 // trigger turning letter into dot after 500 milliseconds 0114 Timer { 0115 id: letterTimer 0116 interval: 500 0117 running: false 0118 repeat: false 0119 onTriggered: { 0120 root.previewCharIndex = -2; 0121 } 0122 } 0123 0124 // hidden textfield so that the virtual keyboard shows up 0125 TextField { 0126 id: textField 0127 visible: false 0128 focus: keypadOpen && !isPinMode 0129 z: 1 0130 inputMethodHints: Qt.ImhNoPredictiveText 0131 0132 onFocusChanged: { 0133 if (focus) { 0134 Keyboards.KWinVirtualKeyboard.active = true; 0135 } 0136 } 0137 0138 property bool externalEdit: false 0139 property string prevText: "" 0140 0141 Connections { 0142 target: root.lockScreenState 0143 0144 function onPasswordChanged() { 0145 if (textField.text != root.lockScreenState.password) { 0146 textField.externalEdit = true; 0147 textField.text = root.lockScreenState.password; 0148 } 0149 } 0150 } 0151 0152 onEditingFinished: { 0153 if (textField.focus) { 0154 root.enter(); 0155 } 0156 } 0157 0158 onTextChanged: { 0159 if (!externalEdit) { 0160 if (prevText.length > text.length) { // backspace 0161 for (let i = 0; i < (prevText.length - text.length); i++) { 0162 root.backspace(); 0163 } 0164 } else if (text.length > 0) { // key enter 0165 root.keyPress(text.charAt(text.length - 1)); 0166 } 0167 prevText = text; 0168 } 0169 externalEdit = false; 0170 } 0171 } 0172 0173 MouseArea { 0174 anchors.fill: parent 0175 onClicked: { 0176 // clicking on rectangle opens keyboard if not already open 0177 if (!isPinMode) { 0178 Keyboards.KWinVirtualKeyboard.active = true; 0179 } 0180 } 0181 0182 // toggle between showing keypad and not 0183 ToolButton { 0184 anchors.right: parent.right 0185 anchors.top: parent.top 0186 anchors.bottom: parent.bottom 0187 anchors.margins: PlasmaCore.Units.smallSpacing 0188 implicitWidth: height 0189 icon.name: root.isPinMode ? "input-keyboard-virtual-symbolic" : "input-dialpad-symbolic" 0190 onClicked: { 0191 root.isPinMode = !root.isPinMode; 0192 if (!root.isPinMode) { 0193 Keyboards.KWinVirtualKeyboard.active = true; 0194 } 0195 } 0196 } 0197 0198 // label ("wrong pin", "enter pin") 0199 Label { 0200 opacity: root.lockScreenState.password.length === 0 ? 1 : 0 0201 anchors.centerIn: parent 0202 text: root.pinLabel 0203 font.pointSize: 12 0204 color: root.headerTextColor 0205 0206 Behavior on opacity { 0207 NumberAnimation { duration: 200 } 0208 } 0209 } 0210 0211 // pin dot display 0212 ColumnLayout { 0213 anchors.fill: parent 0214 0215 ListView { 0216 id: dotDisplay 0217 property int dotWidth: Math.round(PlasmaCore.Units.gridUnit * 0.35) 0218 0219 Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter 0220 Layout.bottomMargin: Math.round(dotWidth / 2) 0221 orientation: ListView.Horizontal 0222 implicitWidth: count * dotWidth + spacing * (count - 1) 0223 spacing: 8 0224 model: dotDisplayModel 0225 0226 Behavior on implicitWidth { 0227 NumberAnimation { duration: 50 } 0228 } 0229 0230 delegate: Item { 0231 implicitWidth: dotDisplay.dotWidth 0232 implicitHeight: dotDisplay.dotWidth 0233 property bool showChar: index === root.previewCharIndex 0234 0235 Component.onCompleted: { 0236 if (showChar) { 0237 charAnimation.to = 1; 0238 charAnimation.duration = 75; 0239 charAnimation.restart(); 0240 } else { 0241 dotAnimation.to = 1; 0242 dotAnimation.restart(); 0243 } 0244 } 0245 0246 onShowCharChanged: { 0247 if (!showChar) { 0248 charAnimation.to = 0; 0249 charAnimation.duration = 50; 0250 charAnimation.restart(); 0251 dotAnimation.to = 1; 0252 dotAnimation.start(); 0253 } 0254 } 0255 0256 Rectangle { // dot 0257 id: dot 0258 scale: 0 0259 anchors.fill: parent 0260 radius: width 0261 color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth 0262 0263 PropertyAnimation { 0264 id: dotAnimation 0265 target: dot; 0266 property: "scale"; 0267 duration: 50 0268 } 0269 } 0270 0271 Label { // number/letter 0272 id: charLabel 0273 scale: 0 0274 anchors.centerIn: parent 0275 color: lockScreenState.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth 0276 text: model.char 0277 font.pointSize: 12 0278 0279 PropertyAnimation { 0280 id: charAnimation 0281 target: charLabel; 0282 property: "scale"; 0283 } 0284 } 0285 } 0286 } 0287 } 0288 } 0289 }