543 lines
19 KiB
QML
543 lines
19 KiB
QML
/********************************************************************
|
|
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.extras 2.0 as PlasmaExtras
|
|
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
SessionManagement {
|
|
id: sessionManagement
|
|
}
|
|
|
|
Connections {
|
|
target: sessionManagement
|
|
onAboutToSuspend: {
|
|
mainBlock.mainPasswordBox.text = "";
|
|
}
|
|
}
|
|
|
|
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
|
|
z: -3
|
|
}
|
|
|
|
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.centerIn: parent
|
|
|
|
height: lockScreenRoot.height + units.gridUnit * 3
|
|
width: parent.width / 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
|
|
z: -2
|
|
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;
|
|
}
|
|
}
|
|
}
|