aerothemeplasma/plasma/shells/org.kde.plasma.desktop/contents/lockscreen/LockScreenUi.qml

589 lines
19 KiB
QML
Raw Normal View History

2024-01-20 02:08:06 +00:00
/*
SPDX-FileCopyrightText: 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
SPDX-License-Identifier: GPL-2.0-or-later
*/
import QtQml 2.15
import QtQuick 2.8
2024-08-09 01:20:25 +00:00
import QtQuick.Controls 2.15
2024-01-20 02:08:06 +00:00
import QtQuick.Layouts 1.1
2024-08-09 01:20:25 +00:00
import Qt5Compat.GraphicalEffects
import QtCore
2024-01-20 02:08:06 +00:00
import org.kde.plasma.core 2.0 as PlasmaCore
2024-08-09 01:20:25 +00:00
import org.kde.plasma.private.keyboardindicator as KeyboardIndicator
2024-01-20 02:08:06 +00:00
import org.kde.plasma.components 3.0 as PlasmaComponents3
import org.kde.plasma.workspace.components 2.0 as PW
2024-08-09 01:20:25 +00:00
import org.kde.kirigami 2.20 as Kirigami
import org.kde.kscreenlocker 1.0 as ScreenLocker
import org.kde.kirigamiaddons.sounds
import QtMultimedia
import org.kde.plasma.plasma5support as Plasma5Support
//import org.kde.breeze.components
2024-01-20 02:08:06 +00:00
import org.kde.plasma.private.sessions 2.0
import "../components"
2024-08-09 01:20:25 +00:00
Item {
2024-01-20 02:08:06 +00:00
id: lockScreenUi
// If we're using software rendering, draw outlines instead of shadows
// See https://bugs.kde.org/show_bug.cgi?id=398317
readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
property bool hadPrompt: false;
property int currentPage: 0;
2024-08-09 01:20:25 +00:00
Kirigami.Theme.colorSet: Kirigami.Theme.Complementary
Kirigami.Theme.inherit: false
//colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
2024-01-20 02:08:06 +00:00
2024-08-09 01:20:25 +00:00
Rectangle {
id: blackRect
anchors.fill: parent
color: "black"
z: 99
opacity: 0
Behavior on opacity {
NumberAnimation { duration: 600 }
}
}
Timer {
id: graceLockTimer
interval: 3000
onTriggered: {
root.clearPassword();
authenticator.startAuthenticating();
}
}
Timer {
id: successTimer
interval: 800
onTriggered: {
Qt.quit();
}
}
2024-01-20 02:08:06 +00:00
function setWrongPasswordScreen(msg) {
root.clearPassword();
currentMessage.text = msg;
currentMessageIcon.source = "dialog-error";
currentPage = 2;
dismissButton.focus = true;
2024-08-09 01:20:25 +00:00
//graceLockTimer.restart();
}
// This is probably the worst code I've ever written, just so that I can play a themed sound file slightly earlier, on time, instead of letting Plasma decide,
// because Plasma plays the sounds too early/too late for this to be accurate, the biggest offender being the sound that plays when the user successfully logs
// back into the session. Plasma plays it right as kscreenlocker closes, which is too late and sounds jarring as a result.
// It literally executes a kreadconfig to read kdeglobals to extract the sound theme because I cannot for the life of me find the appropriate API calls
// and then it manually *searches* for the appropriate sound file, because the SoundsModel component provided by kirigamiaddons (the only thing I could)
// actually find at all, does not have a standard way of representing these sounds at all.
Plasma5Support.DataSource {
id: executable
engine: "executable"
connectedSources: []
onNewData: (sourceName, data) => {
var stdout = data["stdout"]
exited(stdout)
disconnectSource(sourceName) // cmd finished
}
function exec(cmd) {
if (cmd) {
connectSource(cmd)
}
}
signal exited(string stdout)
2024-01-20 02:08:06 +00:00
}
2024-08-09 01:20:25 +00:00
Connections {
target: executable
function onExited(stdout) {
soundsModel.theme = stdout.trim() ? stdout.trim() : "ocean";
for(var i = 0; i < soundsModel.rowCount(); i++) {
var str = soundsModel.initialSourceUrl(i);
if(str.includes("desktop-login") && !str.endsWith(".license")) {
lockSuccess.source = str;
break;
}
/*if(str.includes("desktop-logout") && !str.endsWith(".license")) {
lockSound.source = str;
lockSound.play();
}*/
}
}
}
SoundsModel {
id: soundsModel
}
MediaPlayer {
id: lockSuccess
audioOutput: AudioOutput {}
}
/*MediaPlayer {
id: lockSound
audioOutput: AudioOutput {}
}*/
2024-01-20 02:08:06 +00:00
Connections {
target: authenticator
2024-08-09 01:20:25 +00:00
function onFailed(kind) {
if (kind != 0) { // if this is coming from the noninteractive authenticators
return;
}
2024-01-20 02:08:06 +00:00
if (root.notification) {
root.notification += "\n"
}
setWrongPasswordScreen("The user name or password is incorrect.");
lockScreenUi.hadPrompt = false;
}
function onSucceeded() {
if (lockScreenUi.hadPrompt) {
2024-08-09 01:20:25 +00:00
blackRect.opacity = 1;
lockSuccess.play();
successTimer.start();
2024-01-20 02:08:06 +00:00
} else {
currentPage = 4;
noPasswordArea.forceActiveFocus();
}
}
2024-08-09 01:20:25 +00:00
function onInfoMessageChanged() {
2024-01-20 02:08:06 +00:00
root.clearPassword();
2024-08-09 01:20:25 +00:00
currentMessage.text = authenticator.infoMessage;
2024-01-20 02:08:06 +00:00
currentMessageIcon.source = "dialog-info";
currentPage = 2;
dismissButton.focus = true;
}
2024-08-09 01:20:25 +00:00
function onErrorMessageChanged() {
console.log("ERROR " + authenticator.errorMessage);
2024-01-20 02:08:06 +00:00
}
2024-08-09 01:20:25 +00:00
function onPromptChanged() {
root.notification = authenticator.prompt;
2024-01-20 02:08:06 +00:00
passwordArea.mainPasswordBox.forceActiveFocus();
lockScreenUi.hadPrompt = true;
}
2024-08-09 01:20:25 +00:00
function onPromptForSecretChanged() {
2024-01-20 02:08:06 +00:00
passwordArea.mainPasswordBox.forceActiveFocus();
lockScreenUi.hadPrompt = true;
}
}
SessionManagement {
id: sessionManagement
}
Connections {
target: sessionManagement
function onAboutToSuspend() {
root.clearPassword();
}
}
SessionsModel {
id: sessionsModel
showNewSessionEntry: false
}
2024-08-09 01:20:25 +00:00
KeyboardIndicator.KeyState {
id: capsLockState
key: Qt.Key_CapsLock
2024-01-20 02:08:06 +00:00
}
Loader {
id: changeSessionComponent
active: false
source: "ChangeSession.qml"
visible: false
}
WallpaperFader {
anchors.fill: parent
source: wallpaper
}
Loader {
id: inputPanel
state: "hidden"
readonly property bool keyboardActive: item ? item.active : false
anchors {
left: parent.left
right: parent.right
bottom: lockScreenUi.bottom
}
function showHide() {
state = state == "hidden" ? "visible" : "hidden";
}
Component.onCompleted: {
inputPanel.source = Qt.platform.pluginName.includes("wayland") ? "../components/VirtualKeyboard_wayland.qml" : "../components/VirtualKeyboard.qml"
}
onKeyboardActiveChanged: {
if (keyboardActive) {
inputPanel.z = 99;
state = "visible";
} else {
state = "hidden";
}
}
states: [
State {
name: "visible"
PropertyChanges {
target: lockScreenRoot
height: lockScreenUi.height - inputPanel.height;
//y: 0 - inputPanel.height + passwordArea.height - (switchuserButton.height + switchuserButton.anchors.topMargin);
}
PropertyChanges {
target: inputPanel
y: lockScreenRoot.height - inputPanel.height
}
},
State {
name: "hidden"
PropertyChanges {
target: lockScreenRoot
height: lockScreenUi.height;
}
PropertyChanges {
target: inputPanel
y: lockScreenRoot.height - lockScreenRoot.height/4
}
}
]
transitions: [
Transition {
from: "hidden"
to: "visible"
SequentialAnimation {
ScriptAction {
script: {
inputPanel.item.activated = true;
Qt.inputMethod.show();
}
}
ParallelAnimation {
NumberAnimation {
target: lockScreenRoot
property: "height"
2024-08-09 01:20:25 +00:00
duration: Kirigami.Units.longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: inputPanel
property: "y"
2024-08-09 01:20:25 +00:00
duration: Kirigami.Units.longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing.OutQuad
}
}
}
},
Transition {
from: "visible"
to: "hidden"
SequentialAnimation {
ParallelAnimation {
NumberAnimation {
target: lockScreenRoot
property: "height"
2024-08-09 01:20:25 +00:00
duration: Kirigami.Units.longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing.InOutQuad
}
NumberAnimation {
target: inputPanel
property: "y"
2024-08-09 01:20:25 +00:00
duration: Kirigami.Units.longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing.InQuad
}
OpacityAnimator {
target: inputPanel
2024-08-09 01:20:25 +00:00
duration: Kirigami.Units.longDuration
2024-01-20 02:08:06 +00:00
easing.type: Easing.InQuad
}
}
ScriptAction {
script: {
inputPanel.item.activated = false;
Qt.inputMethod.hide();
}
}
}
}
]
}
MouseArea {
id: lockScreenRoot
property bool calledUnlock: false
Component.onCompleted: {
2024-08-09 01:20:25 +00:00
executable.exec("kreadconfig6 --file ~/.config/kdeglobals --group Sounds --key Theme");
2024-01-20 02:08:06 +00:00
if (!calledUnlock) {
2024-08-09 01:20:25 +00:00
calledUnlock = true;
authenticator.startAuthenticating();
graceLockTimer.restart();
2024-01-20 02:08:06 +00:00
}
}
x: parent.x
y: parent.y
width: parent.width
height: parent.height
hoverEnabled: true
drag.filterChildren: true
Keys.onEscapePressed: {
if (inputPanel.keyboardActive) {
inputPanel.showHide();
}
}
2024-08-09 01:20:25 +00:00
Keys.onPressed: (event) => {
2024-01-20 02:08:06 +00:00
event.accepted = false;
}
GenericButton {
id: switchLayoutButton
anchors {
top: parent.top
topMargin: 5
left: parent.left
leftMargin: 7
}
implicitWidth: 35
implicitHeight: 28
label.font.pointSize: 9
label.font.capitalization: Font.AllUppercase
focusPolicy: Qt.TabFocus
Accessible.description: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to change keyboard layout", "Switch layout")
PW.KeyboardLayoutSwitcher {
id: keyboardLayoutSwitcher
anchors.fill: parent
acceptedButtons: Qt.NoButton
}
text: keyboardLayoutSwitcher.layoutNames.shortName
onClicked: keyboardLayoutSwitcher.keyboardLayout.switchToNextLayout()
visible: keyboardLayoutSwitcher.hasMultipleKeyboardLayouts
}
ListModel {
id: users
Component.onCompleted: {
users.append({
name: kscreenlocker_userName,
realName: kscreenlocker_userName,
icon: kscreenlocker_userImage,
})
}
}
MainBlock {
id: passwordArea
anchors.centerIn: parent
visible: currentPage == 0
focus: true
2024-08-09 01:20:25 +00:00
//enabled: !authenticator.busy
enabled: !graceLockTimer.running
onPasswordResult: (password) => {
2024-01-20 02:08:06 +00:00
// Switch to the 'Welcome' screen
currentPage = 1;
2024-08-09 01:20:25 +00:00
authenticator.startAuthenticating();
authenticator.respond(password);
2024-01-20 02:08:06 +00:00
}
notificationMessage: {
2024-08-09 01:20:25 +00:00
if (capsLockState.locked) {
2024-01-20 02:08:06 +00:00
return i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Caps Lock is on");
} else {
return "";
}
}
}
NoPasswordUnlock {
id: noPasswordArea
anchors.horizontalCenter: parent.horizontalCenter
anchors.bottom: switchuserButton.top
anchors.bottomMargin: 52
visible: currentPage == 4
onClicked: {
Qt.quit();
}
}
GenericButton {
id: switchuserButton
visible: currentPage == 0 || currentPage == 4
label.font.pointSize: 11
implicitWidth: 108
implicitHeight: 28
focusPolicy: Qt.TabFocus
Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User")
PlasmaComponents3.Label {
font.pointSize: 11
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User")
anchors.fill: parent
2024-08-09 01:20:25 +00:00
anchors.bottomMargin: Kirigami.Units.smallSpacing / 2
2024-01-20 02:08:06 +00:00
verticalAlignment: Text.AlignVCenter
horizontalAlignment: Text.AlignHCenter
2024-08-09 01:20:25 +00:00
renderType: Text.NativeRendering
font.hintingPreference: Font.PreferFullHinting
font.kerning: false
2024-01-20 02:08:06 +00:00
elide: Text.ElideRight
layer.enabled: true
layer.effect: DropShadow {
//visible: !softwareRendering
horizontalOffset: 0
verticalOffset: 1
radius: 6
samples: 14
spread: 0.0001
color: "#bf000000"
}
}
onClicked: {
2024-08-09 01:20:25 +00:00
sessionManagement.switchUser();
//sessionsModel.startNewSession(true /* lock the screen too */)
2024-01-20 02:08:06 +00:00
lockScreenRoot.state = ''
passwordArea.mainPasswordBox.forceActiveFocus();
}
anchors {
top: passwordArea.bottom
topMargin: (currentPage == 4 ? 36 : 40) / (inputPanel.keyboardActive ? 4 : 1) // for some reason, Microsoft offset Windows 7's Switch User button a bit when in no password lock
horizontalCenter: parent.horizontalCenter
}
}
RowLayout {
visible: currentPage == 0 || currentPage == 4
id: footer
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
margins: 34
}
EoAButton {
}
OSKButton {
onClicked: {
// Otherwise the password field loses focus and virtual keyboard
// keystrokes get eaten
passwordArea.mainPasswordBox.forceActiveFocus();
inputPanel.showHide()
}
visible: inputPanel.status == Loader.Ready
}
Item {
Layout.fillWidth: true
}
}
Item {
id: welcomePage
visible: currentPage == 1
anchors.fill: parent
Status {
id: statusText
anchors.centerIn: parent
anchors.verticalCenterOffset: -36
statusText: i18nd("okular", "Welcome")
speen: welcomePage.visible
}
}
ColumnLayout {
id: messagePage
visible: currentPage == 2
anchors {
bottom: switchuserButton.bottom
horizontalCenter: parent.horizontalCenter
}
spacing: 0
RowLayout {
spacing: 10
Kirigami.Icon {
id: currentMessageIcon
implicitHeight: 32
implicitWidth: 32
}
Label {
id: currentMessage
Layout.alignment: Qt.AlignHCenter
font.pointSize: 9
width: implicitWidth
color: "white"
horizontalAlignment: Text.AlignCenter
layer.enabled: true
layer.effect: DropShadow {
//visible: !softwareRendering
horizontalOffset: 0
verticalOffset: 1
radius: 6
samples: 14
spread: 0.0001
color: "#bf000000"
}
}
}
Item {
height: 40
}
GenericButton {
id: dismissButton
Layout.alignment: Qt.AlignHCenter
font.pointSize: 11
implicitWidth: 93
implicitHeight: 28
focusPolicy: Qt.TabFocus
Accessible.name: "OK"
text: "OK"
onClicked: {
2024-08-09 01:20:25 +00:00
authenticator.startAuthenticating();
2024-01-20 02:08:06 +00:00
currentPage = 0;
passwordArea.mainPasswordBox.forceActiveFocus();
}
}
}
RowLayout {
anchors {
bottom: parent.bottom
left: parent.left
right: parent.right
}
height: 96
Rectangle { Layout.fillWidth: true }
Image {
id: watermark
source: "../images/watermark.png"
opacity: !inputPanel.keyboardActive
}
Rectangle { Layout.fillWidth: true }
}
Loader {
active: true
source: "LockOsd.qml"
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
2024-08-09 01:20:25 +00:00
topMargin: Kirigami.Units.largeSpacing
2024-01-20 02:08:06 +00:00
}
}
}
}