Warning, /plasma/plasma-workspace/lookandfeel/sddm-theme/Main.qml is written in an unsupported language. File is not indexed.

0001 /*
0002     SPDX-FileCopyrightText: 2016 David Edmundson <davidedmundson@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 import QtQuick 2.15
0008 import QtQuick.Layouts 1.15
0009 import QtQuick.Controls 2.15 as QQC2
0010 import QtGraphicalEffects 1.15
0011 
0012 import org.kde.plasma.core 2.0 as PlasmaCore
0013 import org.kde.plasma.components 3.0 as PlasmaComponents3
0014 import org.kde.plasma.extras 2.0 as PlasmaExtras
0015 
0016 import "components"
0017 import "components/animation"
0018 
0019 // TODO: Once SDDM 0.19 is released and we are setting the font size using the
0020 // SDDM KCM's syncing feature, remove the `config.fontSize` overrides here and
0021 // the fontSize properties in various components, because the theme's default
0022 // font size will be correctly propagated to the login screen
0023 
0024 PlasmaCore.ColorScope {
0025     id: root
0026 
0027     // If we're using software rendering, draw outlines instead of shadows
0028     // See https://bugs.kde.org/show_bug.cgi?id=398317
0029     readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software
0030 
0031     colorGroup: PlasmaCore.Theme.ComplementaryColorGroup
0032 
0033     width: 1600
0034     height: 900
0035 
0036     property string notificationMessage
0037 
0038     LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft
0039     LayoutMirroring.childrenInherit: true
0040 
0041     PlasmaCore.DataSource {
0042         id: keystateSource
0043         engine: "keystate"
0044         connectedSources: "Caps Lock"
0045     }
0046 
0047     Item {
0048         id: wallpaper
0049         anchors.fill: parent
0050         Repeater {
0051             model: screenModel
0052 
0053             Background {
0054                 x: geometry.x; y: geometry.y; width: geometry.width; height: geometry.height
0055                 sceneBackgroundType: config.type
0056                 sceneBackgroundColor: config.color
0057                 sceneBackgroundImage: config.background
0058             }
0059         }
0060     }
0061 
0062     RejectPasswordAnimation {
0063         id: rejectPasswordAnimation
0064         target: mainStack
0065     }
0066 
0067     MouseArea {
0068         id: loginScreenRoot
0069         anchors.fill: parent
0070 
0071         property bool uiVisible: true
0072         property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive || config.type !== "image"
0073 
0074         hoverEnabled: true
0075         drag.filterChildren: true
0076         onPressed: uiVisible = true;
0077         onPositionChanged: uiVisible = true;
0078         onUiVisibleChanged: {
0079             if (blockUI) {
0080                 fadeoutTimer.running = false;
0081             } else if (uiVisible) {
0082                 fadeoutTimer.restart();
0083             }
0084         }
0085         onBlockUIChanged: {
0086             if (blockUI) {
0087                 fadeoutTimer.running = false;
0088                 uiVisible = true;
0089             } else {
0090                 fadeoutTimer.restart();
0091             }
0092         }
0093 
0094         Keys.onPressed: {
0095             uiVisible = true;
0096             event.accepted = false;
0097         }
0098 
0099         //takes one full minute for the ui to disappear
0100         Timer {
0101             id: fadeoutTimer
0102             running: true
0103             interval: 60000
0104             onTriggered: {
0105                 if (!loginScreenRoot.blockUI) {
0106                     userListComponent.mainPasswordBox.showPassword = false;
0107                     loginScreenRoot.uiVisible = false;
0108                 }
0109             }
0110         }
0111         WallpaperFader {
0112             visible: config.type === "image"
0113             anchors.fill: parent
0114             state: loginScreenRoot.uiVisible ? "on" : "off"
0115             source: wallpaper
0116             mainStack: mainStack
0117             footer: footer
0118             clock: clock
0119         }
0120 
0121         DropShadow {
0122             id: clockShadow
0123             anchors.fill: clock
0124             source: clock
0125             visible: !softwareRendering
0126             radius: 6
0127             samples: 14
0128             spread: 0.3
0129             color : "black" // shadows should always be black
0130             Behavior on opacity {
0131                 OpacityAnimator {
0132                     duration: PlasmaCore.Units.veryLongDuration * 2
0133                     easing.type: Easing.InOutQuad
0134                 }
0135             }
0136         }
0137 
0138         Clock {
0139             id: clock
0140             property Item shadow: clockShadow
0141             visible: y > 0
0142             anchors.horizontalCenter: parent.horizontalCenter
0143             y: (userListComponent.userList.y + mainStack.y)/2 - height/2
0144             Layout.alignment: Qt.AlignBaseline
0145         }
0146 
0147         QQC2.StackView {
0148             id: mainStack
0149             anchors {
0150                 left: parent.left
0151                 right: parent.right
0152             }
0153             height: root.height + PlasmaCore.Units.gridUnit * 3
0154 
0155             // If true (depends on the style and environment variables), hover events are always accepted
0156             // and propagation stopped. This means the parent MouseArea won't get them and the UI won't be shown.
0157             // Disable capturing those events while the UI is hidden to avoid that, while still passing events otherwise.
0158             // One issue is that while the UI is visible, mouse activity won't keep resetting the timer, but when it
0159             // finally expires, the next event should immediately set uiVisible = true again.
0160             hoverEnabled: loginScreenRoot.uiVisible ? undefined : false
0161 
0162             focus: true //StackView is an implicit focus scope, so we need to give this focus so the item inside will have it
0163 
0164             Timer {
0165                 //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
0166                 //it is fixed in 6d5b36b28907b16280ff78995fef764bb0c573db which will be 0.14
0167                 //we need to call "window->activate()" *After* it's been shown. We can't control that in QML so we use a shoddy timer
0168                 //it's been this way for all Plasma 5.x without a huge problem
0169                 running: true
0170                 repeat: false
0171                 interval: 200
0172                 onTriggered: mainStack.forceActiveFocus()
0173             }
0174 
0175             initialItem: Login {
0176                 id: userListComponent
0177                 userListModel: userModel
0178                 loginScreenUiVisible: loginScreenRoot.uiVisible
0179                 userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0
0180                 lastUserName: userModel.lastUser
0181                 showUserList: {
0182                     if (!userListModel.hasOwnProperty("count")
0183                         || !userListModel.hasOwnProperty("disableAvatarsThreshold")) {
0184                         return false
0185                     }
0186 
0187                     if (userListModel.count === 0 ) {
0188                         return false
0189                     }
0190 
0191                     if (userListModel.hasOwnProperty("containsAllUsers") && !userListModel.containsAllUsers) {
0192                         return false
0193                     }
0194 
0195                     return userListModel.count <= userListModel.disableAvatarsThreshold
0196                 }
0197 
0198                 notificationMessage: {
0199                     const parts = [];
0200                     if (keystateSource.data["Caps Lock"]["Locked"]) {
0201                         parts.push(i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Caps Lock is on"));
0202                     }
0203                     if (root.notificationMessage) {
0204                         parts.push(root.notificationMessage);
0205                     }
0206                     return parts.join(" • ");
0207                 }
0208 
0209                 actionItemsVisible: !inputPanel.keyboardActive
0210                 actionItems: [
0211                     ActionButton {
0212                         iconSource: "system-suspend"
0213                         text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Suspend to RAM", "Sleep")
0214                         fontSize: parseInt(config.fontSize) + 1
0215                         onClicked: sddm.suspend()
0216                         enabled: sddm.canSuspend
0217                     },
0218                     ActionButton {
0219                         iconSource: "system-reboot"
0220                         text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Restart")
0221                         fontSize: parseInt(config.fontSize) + 1
0222                         onClicked: sddm.reboot()
0223                         enabled: sddm.canReboot
0224                     },
0225                     ActionButton {
0226                         iconSource: "system-shutdown"
0227                         text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Shut Down")
0228                         fontSize: parseInt(config.fontSize) + 1
0229                         onClicked: sddm.powerOff()
0230                         enabled: sddm.canPowerOff
0231                     },
0232                     ActionButton {
0233                         iconSource: "system-user-prompt"
0234                         text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "For switching to a username and password prompt", "Other…")
0235                         fontSize: parseInt(config.fontSize) + 1
0236                         onClicked: mainStack.push(userPromptComponent)
0237                         enabled: true
0238                         visible: !userListComponent.showUsernamePrompt
0239                     }]
0240 
0241                 onLoginRequest: {
0242                     root.notificationMessage = ""
0243                     sddm.login(username, password, sessionButton.currentIndex)
0244                 }
0245             }
0246 
0247             Behavior on opacity {
0248                 OpacityAnimator {
0249                     duration: PlasmaCore.Units.longDuration
0250                 }
0251             }
0252 
0253             readonly property real zoomFactor: 3
0254 
0255             popEnter: Transition {
0256                 ScaleAnimator {
0257                     from: mainStack.zoomFactor
0258                     to: 1
0259                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0260                     easing.type: Easing.OutCubic
0261                 }
0262                 OpacityAnimator {
0263                     from: 0
0264                     to: 1
0265                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0266                     easing.type: Easing.OutCubic
0267                 }
0268             }
0269 
0270             popExit: Transition {
0271                 ScaleAnimator {
0272                     from: 1
0273                     to: 0
0274                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0275                     easing.type: Easing.OutCubic
0276                 }
0277                 OpacityAnimator {
0278                     from: 1
0279                     to: 0
0280                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0281                     easing.type: Easing.OutCubic
0282                 }
0283             }
0284 
0285             pushEnter: Transition {
0286                 ScaleAnimator {
0287                     from: 0
0288                     to: 1
0289                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0290                     easing.type: Easing.OutCubic
0291                 }
0292                 OpacityAnimator {
0293                     from: 0
0294                     to: 1
0295                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0296                     easing.type: Easing.OutCubic
0297                 }
0298             }
0299 
0300             pushExit: Transition {
0301                 ScaleAnimator {
0302                     from: 1
0303                     to: mainStack.zoomFactor
0304                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0305                     easing.type: Easing.OutCubic
0306                 }
0307                 OpacityAnimator {
0308                     from: 1
0309                     to: 0
0310                     duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2)
0311                     easing.type: Easing.OutCubic
0312                 }
0313             }
0314         }
0315 
0316         Loader {
0317             id: inputPanel
0318             state: "hidden"
0319             property bool keyboardActive: item ? item.active : false
0320             onKeyboardActiveChanged: {
0321                 if (keyboardActive) {
0322                     state = "visible"
0323                     // Otherwise the password field loses focus and virtual keyboard
0324                     // keystrokes get eaten
0325                     userListComponent.mainPasswordBox.forceActiveFocus();
0326                 } else {
0327                     state = "hidden";
0328                 }
0329             }
0330 
0331             source: Qt.platform.pluginName.includes("wayland") ? "components/VirtualKeyboard_wayland.qml" : "components/VirtualKeyboard.qml"
0332             anchors {
0333                 left: parent.left
0334                 right: parent.right
0335             }
0336 
0337             function showHide() {
0338                 state = state === "hidden" ? "visible" : "hidden";
0339             }
0340 
0341             states: [
0342                 State {
0343                     name: "visible"
0344                     PropertyChanges {
0345                         target: mainStack
0346                         y: Math.min(0, root.height - inputPanel.height - userListComponent.visibleBoundary)
0347                     }
0348                     PropertyChanges {
0349                         target: inputPanel
0350                         y: root.height - inputPanel.height
0351                         opacity: 1
0352                     }
0353                 },
0354                 State {
0355                     name: "hidden"
0356                     PropertyChanges {
0357                         target: mainStack
0358                         y: 0
0359                     }
0360                     PropertyChanges {
0361                         target: inputPanel
0362                         y: root.height - root.height/4
0363                         opacity: 0
0364                     }
0365                 }
0366             ]
0367             transitions: [
0368                 Transition {
0369                     from: "hidden"
0370                     to: "visible"
0371                     SequentialAnimation {
0372                         ScriptAction {
0373                             script: {
0374                                 inputPanel.item.activated = true;
0375                                 Qt.inputMethod.show();
0376                             }
0377                         }
0378                         ParallelAnimation {
0379                             NumberAnimation {
0380                                 target: mainStack
0381                                 property: "y"
0382                                 duration: PlasmaCore.Units.longDuration
0383                                 easing.type: Easing.InOutQuad
0384                             }
0385                             NumberAnimation {
0386                                 target: inputPanel
0387                                 property: "y"
0388                                 duration: PlasmaCore.Units.longDuration
0389                                 easing.type: Easing.OutQuad
0390                             }
0391                             OpacityAnimator {
0392                                 target: inputPanel
0393                                 duration: PlasmaCore.Units.longDuration
0394                                 easing.type: Easing.OutQuad
0395                             }
0396                         }
0397                     }
0398                 },
0399                 Transition {
0400                     from: "visible"
0401                     to: "hidden"
0402                     SequentialAnimation {
0403                         ParallelAnimation {
0404                             NumberAnimation {
0405                                 target: mainStack
0406                                 property: "y"
0407                                 duration: PlasmaCore.Units.longDuration
0408                                 easing.type: Easing.InOutQuad
0409                             }
0410                             NumberAnimation {
0411                                 target: inputPanel
0412                                 property: "y"
0413                                 duration: PlasmaCore.Units.longDuration
0414                                 easing.type: Easing.InQuad
0415                             }
0416                             OpacityAnimator {
0417                                 target: inputPanel
0418                                 duration: PlasmaCore.Units.longDuration
0419                                 easing.type: Easing.InQuad
0420                             }
0421                         }
0422                         ScriptAction {
0423                             script: {
0424                                 inputPanel.item.activated = false;
0425                                 Qt.inputMethod.hide();
0426                             }
0427                         }
0428                     }
0429                 }
0430             ]
0431         }
0432 
0433         Component {
0434             id: userPromptComponent
0435             Login {
0436                 showUsernamePrompt: true
0437                 notificationMessage: root.notificationMessage
0438                 loginScreenUiVisible: loginScreenRoot.uiVisible
0439                 fontSize: parseInt(config.fontSize) + 2
0440 
0441                 // using a model rather than a QObject list to avoid QTBUG-75900
0442                 userListModel: ListModel {
0443                     ListElement {
0444                         name: ""
0445                         iconSource: ""
0446                     }
0447                     Component.onCompleted: {
0448                         // as we can't bind inside ListElement
0449                         setProperty(0, "name", i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Type in Username and Password"));
0450                     }
0451                 }
0452 
0453                 onLoginRequest: {
0454                     root.notificationMessage = ""
0455                     sddm.login(username, password, sessionButton.currentIndex)
0456                 }
0457 
0458                 actionItemsVisible: !inputPanel.keyboardActive
0459                 actionItems: [
0460                     ActionButton {
0461                         iconSource: "system-suspend"
0462                         text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Suspend to RAM", "Sleep")
0463                         fontSize: parseInt(config.fontSize) + 1
0464                         onClicked: sddm.suspend()
0465                         enabled: sddm.canSuspend
0466                     },
0467                     ActionButton {
0468                         iconSource: "system-reboot"
0469                         text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Restart")
0470                         fontSize: parseInt(config.fontSize) + 1
0471                         onClicked: sddm.reboot()
0472                         enabled: sddm.canReboot
0473                     },
0474                     ActionButton {
0475                         iconSource: "system-shutdown"
0476                         text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Shut Down")
0477                         fontSize: parseInt(config.fontSize) + 1
0478                         onClicked: sddm.powerOff()
0479                         enabled: sddm.canPowerOff
0480                     },
0481                     ActionButton {
0482                         iconSource: "system-user-list"
0483                         text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "List Users")
0484                         fontSize: parseInt(config.fontSize) + 1
0485                         onClicked: mainStack.pop()
0486                     }
0487                 ]
0488             }
0489         }
0490 
0491         DropShadow {
0492             id: logoShadow
0493             anchors.fill: logo
0494             source: logo
0495             visible: !softwareRendering && config.showlogo === "shown"
0496             horizontalOffset: 1
0497             verticalOffset: 1
0498             radius: 6
0499             samples: 14
0500             spread: 0.3
0501             color : "black" // shadows should always be black
0502             opacity: loginScreenRoot.uiVisible ? 0 : 1
0503             Behavior on opacity {
0504                 //OpacityAnimator when starting from 0 is buggy (it shows one frame with opacity 1)"
0505                 NumberAnimation {
0506                     duration: PlasmaCore.Units.longDuration
0507                     easing.type: Easing.InOutQuad
0508                 }
0509             }
0510         }
0511 
0512         Image {
0513             id: logo
0514             visible: config.showlogo === "shown"
0515             source: config.logo
0516             anchors.horizontalCenter: parent.horizontalCenter
0517             anchors.bottom: footer.top
0518             anchors.bottomMargin: PlasmaCore.Units.largeSpacing
0519             asynchronous: true
0520             sourceSize.height: height
0521             opacity: loginScreenRoot.uiVisible ? 0 : 1
0522             fillMode: Image.PreserveAspectFit
0523             height: Math.round(PlasmaCore.Units.gridUnit * 3.5)
0524             Behavior on opacity {
0525                 // OpacityAnimator when starting from 0 is buggy (it shows one frame with opacity 1)"
0526                 NumberAnimation {
0527                     duration: PlasmaCore.Units.longDuration
0528                     easing.type: Easing.InOutQuad
0529                 }
0530             }
0531         }
0532 
0533         //Footer
0534         RowLayout {
0535             id: footer
0536             anchors {
0537                 bottom: parent.bottom
0538                 left: parent.left
0539                 right: parent.right
0540                 margins: PlasmaCore.Units.smallSpacing
0541             }
0542 
0543             Behavior on opacity {
0544                 OpacityAnimator {
0545                     duration: PlasmaCore.Units.longDuration
0546                 }
0547             }
0548 
0549             PlasmaComponents3.ToolButton {
0550                 text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard")
0551                 font.pointSize: config.fontSize
0552                 icon.name: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off"
0553                 onClicked: {
0554                     // Otherwise the password field loses focus and virtual keyboard
0555                     // keystrokes get eaten
0556                     userListComponent.mainPasswordBox.forceActiveFocus();
0557                     inputPanel.showHide()
0558                 }
0559                 visible: inputPanel.status === Loader.Ready
0560             }
0561 
0562             KeyboardButton {
0563                 font.pointSize: config.fontSize
0564 
0565                 onKeyboardLayoutChanged: {
0566                     // Otherwise the password field loses focus and virtual keyboard
0567                     // keystrokes get eaten
0568                     userListComponent.mainPasswordBox.forceActiveFocus();
0569                 }
0570             }
0571 
0572             SessionButton {
0573                 id: sessionButton
0574                 font.pointSize: config.fontSize
0575 
0576                 onSessionChanged: {
0577                     // Otherwise the password field loses focus and virtual keyboard
0578                     // keystrokes get eaten
0579                     userListComponent.mainPasswordBox.forceActiveFocus();
0580                 }
0581             }
0582 
0583             Item {
0584                 Layout.fillWidth: true
0585             }
0586 
0587             Battery {
0588                 fontSize: config.fontSize
0589             }
0590         }
0591     }
0592 
0593     Connections {
0594         target: sddm
0595         function onLoginFailed() {
0596             notificationMessage = i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Login Failed")
0597             footer.enabled = true
0598             mainStack.enabled = true
0599             userListComponent.userList.opacity = 1
0600             rejectPasswordAnimation.start()
0601         }
0602         function onLoginSucceeded() {
0603             //note SDDM will kill the greeter at some random point after this
0604             //there is no certainty any transition will finish, it depends on the time it
0605             //takes to complete the init
0606             mainStack.opacity = 0
0607             footer.opacity = 0
0608         }
0609     }
0610 
0611     onNotificationMessageChanged: {
0612         if (notificationMessage) {
0613             notificationResetTimer.start();
0614         }
0615     }
0616 
0617     Timer {
0618         id: notificationResetTimer
0619         interval: 3000
0620         onTriggered: notificationMessage = ""
0621     }
0622 }