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