diff --git a/lookandfeel/contents/lockscreen/VirtualKeyboard.qml b/lookandfeel/contents/components/VirtualKeyboard.qml similarity index 100% rename from lookandfeel/contents/lockscreen/VirtualKeyboard.qml rename to lookandfeel/contents/components/VirtualKeyboard.qml diff --git a/lookandfeel/contents/lockscreen/LockScreenUi.qml b/lookandfeel/contents/lockscreen/LockScreenUi.qml index 0bbefe6d3..9bc551e34 100644 --- a/lookandfeel/contents/lockscreen/LockScreenUi.qml +++ b/lookandfeel/contents/lockscreen/LockScreenUi.qml @@ -1,297 +1,297 @@ /******************************************************************** This file is part of the KDE project. Copyright (C) 2014 Aleix Pol Gonzalez 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 . *********************************************************************/ import QtQuick 2.5 import QtQuick.Controls 1.1 import QtQuick.Layouts 1.1 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 { 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: true } PlasmaCore.DataSource { id: keystateSource engine: "keystate" connectedSources: "Caps Lock" } Loader { id: changeSessionComponent active: false source: "ChangeSession.qml" visible: false } Item { id: lockScreenRoot x: parent.x y: parent.y width: parent.width height: parent.height 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: 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() } } } ColumnLayout { anchors.top: parent.top anchors.bottom: parent.verticalCenter anchors.horizontalCenter: parent.horizontalCenter Clock { Layout.alignment: Qt.AlignBaseline } Item { Layout.maximumHeight: units.gridUnit * 13 Layout.fillHeight: true } } ListModel { id: users Component.onCompleted: { users.append({name: kscreenlocker_userName, realName: kscreenlocker_userName, icon: kscreenlocker_userImage, }) } } ColumnLayout { anchors.fill: parent StackView { id: mainStack Layout.fillWidth: true Layout.fillHeight: true 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 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: mainStack.push(switchSessionPage) // the current session isn't listed in the model, hence a check for greater than zero, not one visible: (sessionsModel.count > 0 || sessionsModel.canStartNewSession) && sessionsModel.canSwitchUser } ] Loader { Layout.fillWidth: true Layout.preferredHeight: item ? item.implicitHeight : 0 active: true // TODO configurable source: "MediaControls.qml" } } } Loader { id: inputPanel property bool keyboardActive: item ? item.active : false Layout.fillWidth: true Layout.preferredHeight: item ? (item.active ? item.implicitHeight : 0) : 0 function showHide() { if (Qt.inputMethod.visible) { Qt.inputMethod.hide(); } else { item.activated = true; Qt.inputMethod.show(); } } - Component.onCompleted: inputPanel.source = "VirtualKeyboard.qml" + Component.onCompleted: inputPanel.source = "../components/VirtualKeyboard.qml" } } 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() PlasmaComponents.Button { Layout.fillWidth: true // the magic "-1" vtNumber indicates the "New Session" entry text: userListCurrentModelData.vtNumber === -1 ? i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Start New Session") : i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Switch Session") onClicked: initSwitchSession() } actionItems: [ ActionButton { iconSource: "go-previous" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") onClicked: mainStack.pop() } ] } } Loader { active: root.viewVisible source: "LockOsd.qml" anchors { horizontalCenter: parent.horizontalCenter bottom: parent.bottom } } 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; } } } diff --git a/sddm-theme/Main.qml b/sddm-theme/Main.qml index 030aa31e0..c6e6098f3 100644 --- a/sddm-theme/Main.qml +++ b/sddm-theme/Main.qml @@ -1,240 +1,269 @@ /* * Copyright 2016 David Edmundson * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 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 Library General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ import QtQuick 2.2 import QtQuick.Layouts 1.1 import QtQuick.Controls 1.1 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 "components" PlasmaCore.ColorScope { id: root colorGroup: PlasmaCore.Theme.ComplementaryColorGroup width: 1600 height: 900 property string notificationMessage LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft LayoutMirroring.childrenInherit: true PlasmaCore.DataSource { id: keystateSource engine: "keystate" connectedSources: "Caps Lock" } Repeater { model: screenModel Background { x: geometry.x; y: geometry.y; width: geometry.width; height: geometry.height sceneBackgroundType: config.type sceneBackgroundColor: config.color sceneBackgroundImage: config.background } } Clock { visible: y > 0 y: (userListComponent.userList.y + mainStack.y)/2 - height/2 anchors.horizontalCenter: parent.horizontalCenter } - StackView { - id: mainStack + ColumnLayout { anchors { top: parent.top bottom: footer.top left: parent.left right: parent.right topMargin: footer.height // effectively centre align within the view } - focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it - - Timer { - //SDDM has a bug in 0.13 where even though we set the focus on the right item within the window, the window doesn't have focus - //it is fixed in 6d5b36b28907b16280ff78995fef764bb0c573db which will be 0.14 - //we need to call "window->activate()" *After* it's been shown. We can't control that in QML so we use a shoddy timer - //it's been this way for all Plasma 5.x without a huge problem - running: true - repeat: false - interval: 200 - onTriggered: mainStack.forceActiveFocus() - } + StackView { + id: mainStack + Layout.fillWidth: true + Layout.fillHeight: true + focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it + + Timer { + //SDDM has a bug in 0.13 where even though we set the focus on the right item within the window, the window doesn't have focus + //it is fixed in 6d5b36b28907b16280ff78995fef764bb0c573db which will be 0.14 + //we need to call "window->activate()" *After* it's been shown. We can't control that in QML so we use a shoddy timer + //it's been this way for all Plasma 5.x without a huge problem + running: true + repeat: false + interval: 200 + onTriggered: mainStack.forceActiveFocus() + } - initialItem: Login { - id: userListComponent - userListModel: userModel - userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0 - lastUserName: userModel.lastUser - showUserList: { - if ( !userListModel.hasOwnProperty("count") - || !userListModel.hasOwnProperty("disableAvatarsThreshold")) - return true + initialItem: Login { + id: userListComponent + userListModel: userModel + userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0 + lastUserName: userModel.lastUser + showUserList: { + if ( !userListModel.hasOwnProperty("count") + || !userListModel.hasOwnProperty("disableAvatarsThreshold")) + return true - if ( userListModel.count == 0 ) return false + if ( userListModel.count == 0 ) return false - return userListModel.count <= userListModel.disableAvatarsThreshold - } + return userListModel.count <= userListModel.disableAvatarsThreshold + } - notificationMessage: { - var text = "" - if (keystateSource.data["Caps Lock"]["Locked"]) { - text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") - if (root.notificationMessage) { - text += " • " + notificationMessage: { + var text = "" + if (keystateSource.data["Caps Lock"]["Locked"]) { + text += i18nd("plasma_lookandfeel_org.kde.lookandfeel","Caps Lock is on") + if (root.notificationMessage) { + text += " • " + } } + text += root.notificationMessage + return text } - text += root.notificationMessage - return text - } - actionItems: [ - ActionButton { - iconSource: "system-suspend" - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Suspend") - onClicked: sddm.suspend() - enabled: sddm.canSuspend - }, - ActionButton { - iconSource: "system-reboot" - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") - onClicked: sddm.reboot() - enabled: sddm.canReboot - }, - ActionButton { - iconSource: "system-shutdown" - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shutdown") - onClicked: sddm.powerOff() - enabled: sddm.canPowerOff - }, - ActionButton { - iconSource: "system-search" - text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Different User") - onClicked: mainStack.push(userPromptComponent) - enabled: true - visible: !userListComponent.showUsernamePrompt + actionItems: [ + ActionButton { + iconSource: "system-suspend" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Suspend") + onClicked: sddm.suspend() + enabled: sddm.canSuspend + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: "system-reboot" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") + onClicked: sddm.reboot() + enabled: sddm.canReboot + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: "system-shutdown" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shutdown") + onClicked: sddm.powerOff() + enabled: sddm.canPowerOff + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: "system-search" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Different User") + onClicked: mainStack.push(userPromptComponent) + enabled: true + visible: !userListComponent.showUsernamePrompt && !inputPanel.keyboardActive + } + ] + + onLoginRequest: { + root.notificationMessage = "" + sddm.login(username, password, sessionButton.currentIndex) } - ] + } - onLoginRequest: { - root.notificationMessage = "" - sddm.login(username, password, sessionButton.currentIndex) + Behavior on opacity { + OpacityAnimator { + duration: units.longDuration + } } } - Behavior on opacity { - OpacityAnimator { - duration: units.longDuration + Loader { + id: inputPanel + property bool keyboardActive: item ? item.active : false + source: "components/VirtualKeyboard.qml" + Layout.fillWidth: true + Layout.preferredHeight: (item && item.active) ? item.implicitHeight : 0 + function showHide() { + if (Qt.inputMethod.visible) { + Qt.inputMethod.hide(); + } else { + item.activated = true; + Qt.inputMethod.show(); + } } } - } Component { id: userPromptComponent Login { showUsernamePrompt: true notificationMessage: root.notificationMessage userListModel: QtObject { property string name: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Login as different user") property string iconSource: "" } onLoginRequest: { root.notificationMessage = "" sddm.login(username, password, sessionButton.currentIndex) } actionItems: [ ActionButton { iconSource: "go-previous" text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Back") onClicked: mainStack.pop() } ] } } //Footer RowLayout { id: footer anchors { bottom: parent.bottom left: parent.left right: parent.right margins: units.smallSpacing } Behavior on opacity { OpacityAnimator { duration: units.longDuration } } + 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 + } + KeyboardButton { } SessionButton { id: sessionButton } Item { Layout.fillWidth: true } Battery { } } Connections { target: sddm onLoginFailed: { notificationMessage = i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Login Failed") } onLoginSucceeded: { //note SDDM will kill the greeter at some random point after this //there is no certainty any transition will finish, it depends on the time it //takes to complete the init mainStack.opacity = 0 footer.opacity = 0 } } onNotificationMessageChanged: { if (notificationMessage) { notificationResetTimer.start(); } } Timer { id: notificationResetTimer interval: 3000 onTriggered: notificationMessage = "" } }