You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
526 lines
19 KiB
526 lines
19 KiB
/******************************************************************** |
|
This file is part of the KDE project. |
|
|
|
Copyright (C) 2014 Aleix Pol Gonzalez <aleixpol@blue-systems.com> |
|
|
|
This program is free software; you can redistribute it and/or modify |
|
it under the terms of the GNU General Public License as published by |
|
the Free Software Foundation; either version 2 of the License, or |
|
(at your option) any later version. |
|
|
|
This program is distributed in the hope that it will be useful, |
|
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
GNU General Public License for more details. |
|
|
|
You should have received a copy of the GNU General Public License |
|
along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
*********************************************************************/ |
|
|
|
import QtQuick 2.8 |
|
import QtQuick.Controls 1.1 |
|
import QtQuick.Layouts 1.1 |
|
import QtGraphicalEffects 1.0 |
|
|
|
import org.kde.plasma.core 2.0 as PlasmaCore |
|
import org.kde.plasma.components 2.0 as PlasmaComponents |
|
|
|
import org.kde.plasma.private.sessions 2.0 |
|
import "../components" |
|
|
|
PlasmaCore.ColorScope { |
|
|
|
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 |
|
readonly property bool lightBackground: Math.max(PlasmaCore.ColorScope.backgroundColor.r, PlasmaCore.ColorScope.backgroundColor.g, PlasmaCore.ColorScope.backgroundColor.b) > 0.5 |
|
|
|
colorGroup: PlasmaCore.Theme.ComplementaryColorGroup |
|
|
|
Connections { |
|
target: authenticator |
|
onFailed: { |
|
root.notification = i18nd("plasma_lookandfeel_org.kde.lookandfeel","Unlocking failed"); |
|
} |
|
onGraceLockedChanged: { |
|
if (!authenticator.graceLocked) { |
|
root.notification = ""; |
|
root.clearPassword(); |
|
} |
|
} |
|
onMessage: { |
|
root.notification = msg; |
|
} |
|
onError: { |
|
root.notification = err; |
|
} |
|
} |
|
|
|
SessionsModel { |
|
id: sessionsModel |
|
showNewSessionEntry: false |
|
} |
|
|
|
PlasmaCore.DataSource { |
|
id: keystateSource |
|
engine: "keystate" |
|
connectedSources: "Caps Lock" |
|
} |
|
|
|
Loader { |
|
id: changeSessionComponent |
|
active: false |
|
source: "ChangeSession.qml" |
|
visible: false |
|
} |
|
|
|
MouseArea { |
|
id: lockScreenRoot |
|
|
|
property bool uiVisible: false |
|
property bool blockUI: mainStack.depth > 1 || mainBlock.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive |
|
|
|
x: parent.x |
|
y: parent.y |
|
width: parent.width |
|
height: parent.height |
|
hoverEnabled: true |
|
drag.filterChildren: true |
|
onPressed: uiVisible = true; |
|
onPositionChanged: uiVisible = true; |
|
onUiVisibleChanged: { |
|
if (blockUI) { |
|
fadeoutTimer.running = false; |
|
} else if (uiVisible) { |
|
fadeoutTimer.restart(); |
|
} |
|
} |
|
onBlockUIChanged: { |
|
if (blockUI) { |
|
fadeoutTimer.running = false; |
|
uiVisible = true; |
|
} else { |
|
fadeoutTimer.restart(); |
|
} |
|
} |
|
Keys.onEscapePressed: { |
|
uiVisible = !uiVisible; |
|
if (inputPanel.keyboardActive) { |
|
inputPanel.showHide(); |
|
} |
|
if (!uiVisible) { |
|
mainBlock.mainPasswordBox.text = ""; |
|
} |
|
} |
|
Keys.onPressed: { |
|
uiVisible = true; |
|
event.accepted = false; |
|
} |
|
Timer { |
|
id: fadeoutTimer |
|
interval: 10000 |
|
onTriggered: { |
|
if (!lockScreenRoot.blockUI) { |
|
lockScreenRoot.uiVisible = false; |
|
} |
|
} |
|
} |
|
|
|
Component.onCompleted: PropertyAnimation { id: launchAnimation; target: lockScreenRoot; property: "opacity"; from: 0; to: 1; duration: 1000 } |
|
|
|
states: [ |
|
State { |
|
name: "onOtherSession" |
|
// for slide out animation |
|
PropertyChanges { target: lockScreenRoot; y: lockScreenRoot.height } |
|
// we also change the opacity just to be sure it's not visible even on unexpected screen dimension changes with possible race conditions |
|
PropertyChanges { target: lockScreenRoot; opacity: 0 } |
|
} |
|
] |
|
|
|
transitions: |
|
Transition { |
|
// we only animate switchting to another session, because kscreenlocker doesn't get notified when |
|
// coming from another session back and so we wouldn't know when to trigger the animation exactly |
|
from: "" |
|
to: "onOtherSession" |
|
|
|
PropertyAnimation { id: stateChangeAnimation; properties: "y"; duration: 300; easing.type: Easing.InQuad} |
|
PropertyAnimation { properties: "opacity"; duration: 300} |
|
|
|
onRunningChanged: { |
|
// after the animation has finished switch session: since we only animate the transition TO state "onOtherSession" |
|
// and not the other way around, we don't have to check the state we transitioned into |
|
if (/* lockScreenRoot.state == "onOtherSession" && */ !running) { |
|
mainStack.currentItem.switchSession() |
|
} |
|
} |
|
} |
|
|
|
WallpaperFader { |
|
anchors.fill: parent |
|
state: lockScreenRoot.uiVisible ? "on" : "off" |
|
source: wallpaper |
|
mainStack: mainStack |
|
footer: footer |
|
clock: clock |
|
} |
|
|
|
DropShadow { |
|
id: clockShadow |
|
anchors.fill: clock |
|
source: clock |
|
visible: !softwareRendering |
|
horizontalOffset: 1 |
|
verticalOffset: 1 |
|
radius: 6 |
|
samples: 14 |
|
spread: 0.3 |
|
color: lockScreenUi.lightBackground ? PlasmaCore.ColorScope.backgroundColor : "black" // black matches Breeze window decoration and desktopcontainment |
|
Behavior on opacity { |
|
OpacityAnimator { |
|
duration: 1000 |
|
easing.type: Easing.InOutQuad |
|
} |
|
} |
|
} |
|
|
|
Clock { |
|
id: clock |
|
property Item shadow: clockShadow |
|
anchors.horizontalCenter: parent.horizontalCenter |
|
y: (mainBlock.userList.y + mainStack.y)/2 - height/2 |
|
visible: y > 0 |
|
Layout.alignment: Qt.AlignBaseline |
|
} |
|
|
|
ListModel { |
|
id: users |
|
|
|
Component.onCompleted: { |
|
users.append({name: kscreenlocker_userName, |
|
realName: kscreenlocker_userName, |
|
icon: kscreenlocker_userImage, |
|
|
|
}) |
|
} |
|
} |
|
|
|
StackView { |
|
id: mainStack |
|
anchors { |
|
left: parent.left |
|
right: parent.right |
|
} |
|
height: lockScreenRoot.height + units.gridUnit * 3 |
|
focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it |
|
|
|
initialItem: MainBlock { |
|
id: mainBlock |
|
lockScreenUiVisible: lockScreenRoot.uiVisible |
|
|
|
showUserList: userList.y + mainStack.y > 0 |
|
|
|
Stack.onStatusChanged: { |
|
// prepare for presenting again to the user |
|
if (Stack.status == Stack.Activating) { |
|
mainPasswordBox.remove(0, mainPasswordBox.length) |
|
mainPasswordBox.focus = true |
|
} |
|
} |
|
userListModel: users |
|
notificationMessage: { |
|
var text = "" |
|
if (keystateSource.data["Caps Lock"]["Locked"]) { |
|
text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") |
|
if (root.notification) { |
|
text += " • " |
|
} |
|
} |
|
text += root.notification |
|
return text |
|
} |
|
|
|
onLoginRequest: { |
|
root.notification = "" |
|
authenticator.tryUnlock(password) |
|
} |
|
|
|
actionItems: [ |
|
ActionButton { |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch User") |
|
iconSource: "system-switch-user" |
|
onClicked: { |
|
// If there are no existing sessions to switch to, create a new one instead |
|
if (((sessionsModel.showNewSessionEntry && sessionsModel.count === 1) || |
|
(!sessionsModel.showNewSessionEntry && sessionsModel.count === 0)) && |
|
sessionsModel.canSwitchUser) { |
|
mainStack.pop({immediate:true}) |
|
sessionsModel.startNewSession(true /* lock the screen too */) |
|
lockScreenRoot.state = '' |
|
} else { |
|
mainStack.push(switchSessionPage) |
|
} |
|
} |
|
visible: sessionsModel.canStartNewSession && sessionsModel.canSwitchUser |
|
//Button gets cut off on smaller displays without this. |
|
anchors{ |
|
verticalCenter: parent.top |
|
} |
|
} |
|
] |
|
|
|
Loader { |
|
Layout.fillWidth: true |
|
Layout.preferredHeight: item ? item.implicitHeight : 0 |
|
active: config.showMediaControls |
|
source: "MediaControls.qml" |
|
} |
|
} |
|
|
|
Component.onCompleted: { |
|
if (defaultToSwitchUser) { //context property |
|
// If we are in the only session, then going to the session switcher is |
|
// a pointless extra step; instead create a new session immediately |
|
if (((sessionsModel.showNewSessionEntry && sessionsModel.count === 1) || |
|
(!sessionsModel.showNewSessionEntry && sessionsModel.count === 0)) && |
|
sessionsModel.canStartNewSession) { |
|
sessionsModel.startNewSession(true /* lock the screen too */) |
|
} else { |
|
mainStack.push({ |
|
item: switchSessionPage, |
|
immediate: true}); |
|
} |
|
} |
|
} |
|
} |
|
|
|
Loader { |
|
id: inputPanel |
|
state: "hidden" |
|
readonly property bool keyboardActive: item ? item.active : false |
|
anchors { |
|
left: parent.left |
|
right: parent.right |
|
} |
|
function showHide() { |
|
state = state == "hidden" ? "visible" : "hidden"; |
|
} |
|
Component.onCompleted: inputPanel.source = "../components/VirtualKeyboard.qml" |
|
|
|
onKeyboardActiveChanged: { |
|
if (keyboardActive) { |
|
state = "visible"; |
|
} else { |
|
state = "hidden"; |
|
} |
|
} |
|
|
|
states: [ |
|
State { |
|
name: "visible" |
|
PropertyChanges { |
|
target: mainStack |
|
y: Math.min(0, lockScreenRoot.height - inputPanel.height - mainBlock.visibleBoundary) |
|
} |
|
PropertyChanges { |
|
target: inputPanel |
|
y: lockScreenRoot.height - inputPanel.height |
|
opacity: 1 |
|
} |
|
}, |
|
State { |
|
name: "hidden" |
|
PropertyChanges { |
|
target: mainStack |
|
y: 0 |
|
} |
|
PropertyChanges { |
|
target: inputPanel |
|
y: lockScreenRoot.height - lockScreenRoot.height/4 |
|
opacity: 0 |
|
} |
|
} |
|
] |
|
transitions: [ |
|
Transition { |
|
from: "hidden" |
|
to: "visible" |
|
SequentialAnimation { |
|
ScriptAction { |
|
script: { |
|
inputPanel.item.activated = true; |
|
Qt.inputMethod.show(); |
|
} |
|
} |
|
ParallelAnimation { |
|
NumberAnimation { |
|
target: mainStack |
|
property: "y" |
|
duration: units.longDuration |
|
easing.type: Easing.InOutQuad |
|
} |
|
NumberAnimation { |
|
target: inputPanel |
|
property: "y" |
|
duration: units.longDuration |
|
easing.type: Easing.OutQuad |
|
} |
|
OpacityAnimator { |
|
target: inputPanel |
|
duration: units.longDuration |
|
easing.type: Easing.OutQuad |
|
} |
|
} |
|
} |
|
}, |
|
Transition { |
|
from: "visible" |
|
to: "hidden" |
|
SequentialAnimation { |
|
ParallelAnimation { |
|
NumberAnimation { |
|
target: mainStack |
|
property: "y" |
|
duration: units.longDuration |
|
easing.type: Easing.InOutQuad |
|
} |
|
NumberAnimation { |
|
target: inputPanel |
|
property: "y" |
|
duration: units.longDuration |
|
easing.type: Easing.InQuad |
|
} |
|
OpacityAnimator { |
|
target: inputPanel |
|
duration: units.longDuration |
|
easing.type: Easing.InQuad |
|
} |
|
} |
|
ScriptAction { |
|
script: { |
|
Qt.inputMethod.hide(); |
|
} |
|
} |
|
} |
|
} |
|
] |
|
} |
|
|
|
Component { |
|
id: switchSessionPage |
|
SessionManagementScreen { |
|
property var switchSession: finalSwitchSession |
|
|
|
Stack.onStatusChanged: { |
|
if (Stack.status == Stack.Activating) { |
|
focus = true |
|
} |
|
} |
|
|
|
userListModel: sessionsModel |
|
|
|
// initiating animation of lockscreen for session switch |
|
function initSwitchSession() { |
|
lockScreenRoot.state = 'onOtherSession' |
|
} |
|
|
|
// initiating session switch and preparing lockscreen for possible return of user |
|
function finalSwitchSession() { |
|
mainStack.pop({immediate:true}) |
|
sessionsModel.switchUser(userListCurrentModelData.vtNumber) |
|
lockScreenRoot.state = '' |
|
} |
|
|
|
Keys.onLeftPressed: userList.decrementCurrentIndex() |
|
Keys.onRightPressed: userList.incrementCurrentIndex() |
|
Keys.onEnterPressed: initSwitchSession() |
|
Keys.onReturnPressed: initSwitchSession() |
|
Keys.onEscapePressed: mainStack.pop() |
|
|
|
ColumnLayout { |
|
Layout.fillWidth: true |
|
spacing: units.largeSpacing |
|
|
|
PlasmaComponents.Button { |
|
Layout.fillWidth: true |
|
font.pointSize: theme.defaultFont.pointSize + 1 |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch to This Session") |
|
onClicked: initSwitchSession() |
|
visible: sessionsModel.count > 0 |
|
} |
|
|
|
PlasmaComponents.Button { |
|
Layout.fillWidth: true |
|
font.pointSize: theme.defaultFont.pointSize + 1 |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Start New Session") |
|
onClicked: { |
|
mainStack.pop({immediate:true}) |
|
sessionsModel.startNewSession(true /* lock the screen too */) |
|
lockScreenRoot.state = '' |
|
} |
|
} |
|
} |
|
|
|
|
|
actionItems: [ |
|
ActionButton { |
|
iconSource: "go-previous" |
|
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") |
|
onClicked: mainStack.pop() |
|
//Button gets cut off on smaller displays without this. |
|
anchors{ |
|
verticalCenter: parent.top |
|
} |
|
} |
|
] |
|
} |
|
} |
|
|
|
Loader { |
|
active: root.viewVisible |
|
source: "LockOsd.qml" |
|
anchors { |
|
horizontalCenter: parent.horizontalCenter |
|
bottom: parent.bottom |
|
bottomMargin: units.largeSpacing |
|
} |
|
} |
|
|
|
RowLayout { |
|
id: footer |
|
anchors { |
|
bottom: parent.bottom |
|
left: parent.left |
|
right: parent.right |
|
margins: units.smallSpacing |
|
} |
|
|
|
PlasmaComponents.ToolButton { |
|
text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard") |
|
iconName: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off" |
|
onClicked: inputPanel.showHide() |
|
|
|
visible: inputPanel.status == Loader.Ready |
|
} |
|
|
|
KeyboardLayoutButton { |
|
} |
|
|
|
Item { |
|
Layout.fillWidth: true |
|
} |
|
|
|
Battery {} |
|
} |
|
} |
|
|
|
Component.onCompleted: { |
|
// version support checks |
|
if (root.interfaceVersion < 1) { |
|
// ksmserver of 5.4, with greeter of 5.5 |
|
root.viewVisible = true; |
|
} |
|
} |
|
}
|
|
|