mirror of
https://invent.kde.org/marcoa/shift-shell.git
synced 2026-08-03 10:14:45 +00:00
Add keyboard option to lockscreen #72
This commit is contained in:
parent
8852ad01a9
commit
07142481bf
3 changed files with 185 additions and 118 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
SPDX-FileCopyrightText: 2020 Devin Lin <espidev@gmail.com>
|
SPDX-FileCopyrightText: 2020-2021 Devin Lin <espidev@gmail.com>
|
||||||
|
|
||||||
SPDX-License-Identifier: GPL-2.0-or-later
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
*/
|
*/
|
||||||
|
|
@ -24,7 +24,7 @@ Rectangle {
|
||||||
property string pinLabel: qsTr("Enter PIN")
|
property string pinLabel: qsTr("Enter PIN")
|
||||||
|
|
||||||
// for displaying temporary number in pin dot display
|
// for displaying temporary number in pin dot display
|
||||||
property int indexWithNumber: -2
|
property int previewCharIndex: -2
|
||||||
|
|
||||||
// if waiting for result of auth
|
// if waiting for result of auth
|
||||||
property bool waitingForAuth: false
|
property bool waitingForAuth: false
|
||||||
|
|
@ -35,11 +35,17 @@ Rectangle {
|
||||||
property color buttonTextColor: PlasmaCore.Theme.textColor
|
property color buttonTextColor: PlasmaCore.Theme.textColor
|
||||||
property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2)
|
property color dropShadowColor: Qt.darker(PlasmaCore.Theme.backgroundColor, 1.2)
|
||||||
property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
|
property color headerBackgroundColor: Qt.lighter(PlasmaCore.Theme.backgroundColor, 1.3)
|
||||||
property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255})
|
|
||||||
property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255})
|
|
||||||
|
|
||||||
opacity: Math.sin((Math.PI / 2) * swipeProgress + 1.5 * Math.PI) + 1
|
opacity: Math.sin((Math.PI / 2) * swipeProgress + 1.5 * Math.PI) + 1
|
||||||
|
|
||||||
|
implicitHeight: passwordBar.isPinMode ? PlasmaCore.Units.gridUnit * 17 : passwordBar.implicitHeight
|
||||||
|
Behavior on implicitHeight {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Kirigami.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
signal passwordChanged()
|
signal passwordChanged()
|
||||||
|
|
||||||
// keypad functions
|
// keypad functions
|
||||||
|
|
@ -52,7 +58,7 @@ Rectangle {
|
||||||
|
|
||||||
function backspace() {
|
function backspace() {
|
||||||
if (!keypadRoot.waitingForAuth) {
|
if (!keypadRoot.waitingForAuth) {
|
||||||
keypadRoot.indexWithNumber = -2;
|
keypadRoot.previewCharIndex = -2;
|
||||||
root.password = root.password.substr(0, root.password.length - 1);
|
root.password = root.password.substr(0, root.password.length - 1);
|
||||||
passwordChanged();
|
passwordChanged();
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +66,7 @@ Rectangle {
|
||||||
|
|
||||||
function clear() {
|
function clear() {
|
||||||
if (!keypadRoot.waitingForAuth) {
|
if (!keypadRoot.waitingForAuth) {
|
||||||
keypadRoot.indexWithNumber = -2;
|
keypadRoot.previewCharIndex = -2;
|
||||||
root.password = "";
|
root.password = "";
|
||||||
passwordChanged();
|
passwordChanged();
|
||||||
}
|
}
|
||||||
|
|
@ -82,7 +88,7 @@ Rectangle {
|
||||||
if (keypadRoot.pinLabel !== qsTr("Enter PIN")) {
|
if (keypadRoot.pinLabel !== qsTr("Enter PIN")) {
|
||||||
keypadRoot.pinLabel = qsTr("Enter PIN");
|
keypadRoot.pinLabel = qsTr("Enter PIN");
|
||||||
}
|
}
|
||||||
keypadRoot.indexWithNumber = root.password.length;
|
keypadRoot.previewCharIndex = root.password.length;
|
||||||
root.password += data
|
root.password += data
|
||||||
passwordChanged();
|
passwordChanged();
|
||||||
|
|
||||||
|
|
@ -131,13 +137,13 @@ Rectangle {
|
||||||
running: false
|
running: false
|
||||||
repeat: false
|
repeat: false
|
||||||
onTriggered: {
|
onTriggered: {
|
||||||
keypadRoot.indexWithNumber = -2;
|
keypadRoot.previewCharIndex = -2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RectangularGlow {
|
RectangularGlow {
|
||||||
anchors.topMargin: 1
|
anchors.topMargin: 1
|
||||||
anchors.fill: topTextDisplay
|
anchors.fill: passwordBar
|
||||||
cached: true
|
cached: true
|
||||||
glowRadius: 4
|
glowRadius: 4
|
||||||
spread: 0.2
|
spread: 0.2
|
||||||
|
|
@ -145,126 +151,36 @@ Rectangle {
|
||||||
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
|
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// rectangle "bar" on the top of the keypad
|
// pin display and bar
|
||||||
Rectangle {
|
PasswordBar {
|
||||||
id: topTextDisplay
|
id: passwordBar
|
||||||
anchors.top: parent.top
|
anchors.top: parent.top
|
||||||
anchors.left: parent.left
|
anchors.left: parent.left
|
||||||
anchors.right: parent.right
|
anchors.right: parent.right
|
||||||
color: keypadRoot.headerBackgroundColor
|
color: keypadRoot.headerBackgroundColor
|
||||||
implicitHeight: units.gridUnit * 2.5
|
|
||||||
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
|
opacity: (Math.sin(2*((Math.PI / 2) * keypadRoot.swipeProgress + 1.5 * Math.PI)) + 1)
|
||||||
|
|
||||||
// label ("wrong pin", "enter pin")
|
keypadOpen: swipeProgress === 1
|
||||||
Label {
|
password: root.password
|
||||||
opacity: root.password.length === 0 ? 1 : 0
|
previewCharIndex: keypadRoot.previewCharIndex
|
||||||
anchors.centerIn: parent
|
pinLabel: keypadRoot.pinLabel
|
||||||
text: keypadRoot.pinLabel
|
|
||||||
font.pointSize: 12
|
|
||||||
color: keypadRoot.headerTextColor
|
|
||||||
|
|
||||||
Behavior on opacity {
|
|
||||||
NumberAnimation { duration: 200 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// we need to use a listmodel to avoid all delegates from reloading
|
|
||||||
ListModel {
|
|
||||||
id: dotDisplayModel
|
|
||||||
}
|
|
||||||
onPasswordChanged: {
|
|
||||||
while (root.password.length < dotDisplayModel.count) {
|
|
||||||
dotDisplayModel.remove(dotDisplayModel.count - 1);
|
|
||||||
}
|
|
||||||
while (root.password.length > dotDisplayModel.count) {
|
|
||||||
dotDisplayModel.append({"char": root.password.charAt(dotDisplayModel.count)});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// pin dot display
|
|
||||||
ColumnLayout {
|
|
||||||
anchors.fill: topTextDisplay
|
|
||||||
ListView {
|
|
||||||
id: dotDisplay
|
|
||||||
property int dotWidth: Math.round(units.gridUnit * 0.35)
|
|
||||||
|
|
||||||
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
|
||||||
Layout.bottomMargin: Math.round(dotWidth / 2)
|
|
||||||
orientation: ListView.Horizontal
|
|
||||||
implicitWidth: count * dotWidth + spacing * (count - 1)
|
|
||||||
spacing: 8
|
|
||||||
model: dotDisplayModel
|
|
||||||
|
|
||||||
Behavior on implicitWidth {
|
|
||||||
NumberAnimation { duration: 50 }
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate: Item {
|
|
||||||
implicitWidth: dotDisplay.dotWidth
|
|
||||||
implicitHeight: dotDisplay.dotWidth
|
|
||||||
property bool showChar: index === indexWithNumber
|
|
||||||
|
|
||||||
Component.onCompleted: {
|
|
||||||
if (showChar) {
|
|
||||||
charAnimation.to = 1;
|
|
||||||
charAnimation.duration = 75;
|
|
||||||
charAnimation.restart();
|
|
||||||
} else {
|
|
||||||
dotAnimation.to = 1;
|
|
||||||
dotAnimation.restart();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onShowCharChanged: {
|
|
||||||
if (!showChar) {
|
|
||||||
charAnimation.to = 0;
|
|
||||||
charAnimation.duration = 50;
|
|
||||||
charAnimation.restart();
|
|
||||||
dotAnimation.to = 1;
|
|
||||||
dotAnimation.start();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Rectangle { // dot
|
|
||||||
id: dot
|
|
||||||
scale: 0
|
|
||||||
anchors.fill: parent
|
|
||||||
radius: width
|
|
||||||
color: keypadRoot.waitingForAuth ? keypadRoot.headerTextInactiveColor : keypadRoot.headerTextColor // dim when waiting for auth
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
id: dotAnimation
|
|
||||||
target: dot;
|
|
||||||
property: "scale";
|
|
||||||
duration: 50
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Label { // number/letter
|
|
||||||
id: charLabel
|
|
||||||
scale: 0
|
|
||||||
anchors.centerIn: parent
|
|
||||||
color: keypadRoot.waitingForAuth ? keypadRoot.headerTextInactiveColor : keypadRoot.headerTextColor // dim when waiting for auth
|
|
||||||
text: model.char
|
|
||||||
font.pointSize: 12
|
|
||||||
|
|
||||||
PropertyAnimation {
|
|
||||||
id: charAnimation
|
|
||||||
target: charLabel;
|
|
||||||
property: "scale";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// actual number keys
|
// actual number keys
|
||||||
ColumnLayout {
|
ColumnLayout {
|
||||||
|
visible: opacity > 0
|
||||||
|
opacity: passwordBar.isPinMode ? 1 : 0
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation {
|
||||||
|
duration: Kirigami.Units.longDuration
|
||||||
|
easing.type: Easing.InOutQuad
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
anchors {
|
anchors {
|
||||||
left: parent.left
|
left: parent.left
|
||||||
right: parent.right
|
right: parent.right
|
||||||
top: topTextDisplay.bottom
|
top: passwordBar.bottom
|
||||||
bottom: parent.bottom
|
bottom: parent.bottom
|
||||||
topMargin: units.gridUnit
|
topMargin: units.gridUnit
|
||||||
bottomMargin: units.gridUnit
|
bottomMargin: units.gridUnit
|
||||||
|
|
|
||||||
|
|
@ -278,8 +278,6 @@ PlasmaCore.ColorScope {
|
||||||
focus: passwordFlickable.contentY === passwordFlickable.columnHeight
|
focus: passwordFlickable.contentY === passwordFlickable.columnHeight
|
||||||
swipeProgress: passwordFlickable.contentY / passwordFlickable.columnHeight
|
swipeProgress: passwordFlickable.contentY / passwordFlickable.columnHeight
|
||||||
Layout.fillWidth: true
|
Layout.fillWidth: true
|
||||||
Layout.minimumHeight: units.gridUnit * 17
|
|
||||||
Layout.maximumWidth: root.width
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
153
look-and-feel/contents/lockscreen/PasswordBar.qml
Normal file
153
look-and-feel/contents/lockscreen/PasswordBar.qml
Normal file
|
|
@ -0,0 +1,153 @@
|
||||||
|
/*
|
||||||
|
SPDX-FileCopyrightText: 2020-2021 Devin Lin <espidev@gmail.com>
|
||||||
|
|
||||||
|
SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
import QtQuick 2.12
|
||||||
|
import QtQuick.Controls 2.1
|
||||||
|
import QtQuick.Layouts 1.1
|
||||||
|
import QtGraphicalEffects 1.12
|
||||||
|
import org.kde.plasma.components 2.0 as PlasmaComponents
|
||||||
|
import org.kde.plasma.core 2.0 as PlasmaCore
|
||||||
|
import org.kde.plasma.workspace.keyboardlayout 1.0
|
||||||
|
import org.kde.kirigami 2.12 as Kirigami
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
implicitHeight: units.gridUnit * 2.5
|
||||||
|
|
||||||
|
// toggle between pin and password mode
|
||||||
|
property bool isPinMode: true
|
||||||
|
|
||||||
|
property string password
|
||||||
|
property int previewCharIndex
|
||||||
|
property string pinLabel
|
||||||
|
property bool keypadOpen
|
||||||
|
|
||||||
|
property color headerTextColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.75*255})
|
||||||
|
property color headerTextInactiveColor: Kirigami.ColorUtils.adjustColor(PlasmaCore.Theme.textColor, {"alpha": 0.4*255})
|
||||||
|
|
||||||
|
// we need to use a listmodel to avoid all delegates from reloading
|
||||||
|
ListModel {
|
||||||
|
id: dotDisplayModel
|
||||||
|
}
|
||||||
|
onPasswordChanged: {
|
||||||
|
while (password.length < dotDisplayModel.count) {
|
||||||
|
dotDisplayModel.remove(dotDisplayModel.count - 1);
|
||||||
|
}
|
||||||
|
while (password.length > dotDisplayModel.count) {
|
||||||
|
dotDisplayModel.append({"char": password.charAt(dotDisplayModel.count)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hidden textfield so that the virtual keyboard shows up
|
||||||
|
TextField {
|
||||||
|
visible: false
|
||||||
|
id: textField
|
||||||
|
focus: keypadOpen && !isPinMode
|
||||||
|
z: 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// toggle between showing keypad and not
|
||||||
|
ToolButton {
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.top: parent.top
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: units.smallSpacing
|
||||||
|
implicitWidth: height
|
||||||
|
icon.name: "input-keyboard-virtual-symbolic"
|
||||||
|
onClicked: root.isPinMode = !root.isPinMode
|
||||||
|
}
|
||||||
|
|
||||||
|
// label ("wrong pin", "enter pin")
|
||||||
|
Label {
|
||||||
|
opacity: password.length === 0 ? 1 : 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: root.pinLabel
|
||||||
|
font.pointSize: 12
|
||||||
|
color: root.headerTextColor
|
||||||
|
|
||||||
|
Behavior on opacity {
|
||||||
|
NumberAnimation { duration: 200 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pin dot display
|
||||||
|
ColumnLayout {
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
ListView {
|
||||||
|
id: dotDisplay
|
||||||
|
property int dotWidth: Math.round(units.gridUnit * 0.35)
|
||||||
|
|
||||||
|
Layout.alignment: Qt.AlignHCenter | Qt.AlignVCenter
|
||||||
|
Layout.bottomMargin: Math.round(dotWidth / 2)
|
||||||
|
orientation: ListView.Horizontal
|
||||||
|
implicitWidth: count * dotWidth + spacing * (count - 1)
|
||||||
|
spacing: 8
|
||||||
|
model: dotDisplayModel
|
||||||
|
|
||||||
|
Behavior on implicitWidth {
|
||||||
|
NumberAnimation { duration: 50 }
|
||||||
|
}
|
||||||
|
|
||||||
|
delegate: Item {
|
||||||
|
implicitWidth: dotDisplay.dotWidth
|
||||||
|
implicitHeight: dotDisplay.dotWidth
|
||||||
|
property bool showChar: index === root.previewCharIndex
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
if (showChar) {
|
||||||
|
charAnimation.to = 1;
|
||||||
|
charAnimation.duration = 75;
|
||||||
|
charAnimation.restart();
|
||||||
|
} else {
|
||||||
|
dotAnimation.to = 1;
|
||||||
|
dotAnimation.restart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onShowCharChanged: {
|
||||||
|
if (!showChar) {
|
||||||
|
charAnimation.to = 0;
|
||||||
|
charAnimation.duration = 50;
|
||||||
|
charAnimation.restart();
|
||||||
|
dotAnimation.to = 1;
|
||||||
|
dotAnimation.start();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle { // dot
|
||||||
|
id: dot
|
||||||
|
scale: 0
|
||||||
|
anchors.fill: parent
|
||||||
|
radius: width
|
||||||
|
color: keypadRoot.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: dotAnimation
|
||||||
|
target: dot;
|
||||||
|
property: "scale";
|
||||||
|
duration: 50
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Label { // number/letter
|
||||||
|
id: charLabel
|
||||||
|
scale: 0
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: keypadRoot.waitingForAuth ? root.headerTextInactiveColor : root.headerTextColor // dim when waiting for auth
|
||||||
|
text: model.char
|
||||||
|
font.pointSize: 12
|
||||||
|
|
||||||
|
PropertyAnimation {
|
||||||
|
id: charAnimation
|
||||||
|
target: charLabel;
|
||||||
|
property: "scale";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue