import QtQuick.Controls 1.1 import QtQuick.Controls 2.12 as QQC2 import org.kde.plasma.core 2.0 as PlasmaCore import org.kde.plasma.components 3.0 as PlasmaComponents3 import org.kde.plasma.extras 2.0 as PlasmaExtras import QtGraphicalEffects 1.0 import QtQuick.Layouts 1.1 import QtQuick 2.8 import SddmComponents 2.0 import QtMultimedia 5.7 import "components" Rectangle { // Main Container id: container LayoutMirroring.enabled: Qt.locale().textDirection == Qt.RightToLeft LayoutMirroring.childrenInherit: true property int sessionIndex: session.index // Inherited from SDDMComponents TextConstants { id: textConstants } // Set SDDM actions Connections { target: sddm onLoginSucceeded: { } onLoginFailed: { error_message.color = "#dc322f" error_message.text = textConstants.loginFailed } } // Set Font FontLoader { id: textFont; name: config.displayFont } // Background Fill Rectangle { anchors.fill: parent color: "black" } // Set Background Image Image { id: image1 anchors.fill: parent //source: config.background fillMode: Image.PreserveAspectCrop } // Set Background Video1 MediaPlayer { id: mediaplayer1 autoPlay: true; muted: true playlist: Playlist { id: playlist1 playbackMode: Playlist.Random onLoaded: { mediaplayer1.play() } } } VideoOutput { id: video1 fillMode: VideoOutput.PreserveAspectCrop anchors.fill: parent; source: mediaplayer1 MouseArea { id: mouseArea1 anchors.fill: parent; //onPressed: {playlist1.shuffle(); playlist1.next();} onPressed: { fader1.state = fader1.state == "off" ? "on" : "off" ; if (config.autofocusInput == "true") { if (username_input_box.text == "") username_input_box.focus = true else password_input_box.focus = true } } } Keys.onPressed: { fader1.state = "on"; if (username_input_box.text == "") username_input_box.focus = true else password_input_box.focus = true } } WallpaperFader { id: fader1 visible: true anchors.fill: parent state: "off" source: video1 mainStack: login_container footer: login_container } // Set Background Video2 MediaPlayer { id: mediaplayer2 autoPlay: true; muted: true playlist: Playlist { id: playlist2; playbackMode: Playlist.Random //onLoaded: { mediaplayer2.play() } } } VideoOutput { id: video2 fillMode: VideoOutput.PreserveAspectCrop anchors.fill: parent; source: mediaplayer2 opacity: 0 MouseArea { id: mouseArea2 enabled: false anchors.fill: parent; onPressed: { fader1.state = fader1.state == "off" ? "on" : "off" ; if (config.autofocusInput == "true") { if (username_input_box.text == "") username_input_box.focus = true else password_input_box.focus = true } } } Behavior on opacity { enabled: true NumberAnimation { easing.type: Easing.InOutQuad; duration: 3000 } } Keys.onPressed: { fader2.state = "on"; if (username_input_box.text == "") username_input_box.focus = true else password_input_box.focus = true } } WallpaperFader { id: fader2 visible: true anchors.fill: parent state: "off" source: video2 mainStack: login_container footer: login_container } property MediaPlayer currentPlayer: mediaplayer1 // Timer event to handle fade between videos Timer { interval: 1000; running: true; repeat: true onTriggered: { if (currentPlayer.duration != -1 && currentPlayer.position > currentPlayer.duration - 10000) { // pre load the 2nd player if (video2.opacity == 0) { // toogle opacity mediaplayer2.play() } else mediaplayer1.play() } if (currentPlayer.duration != -1 && currentPlayer.position > currentPlayer.duration - 3000) { // initiate transition if (video2.opacity == 0) { // toogle opacity mouseArea1.enabled = false currentPlayer = mediaplayer2 video2.opacity = 1 triggerTimer.start() mouseArea2.enabled = true } else { mouseArea2.enabled = false currentPlayer = mediaplayer1 video2.opacity = 0 triggerTimer.start() mouseArea1.enabled = true } } } } Timer { // this timer waits for fade to stop and stops the video id: triggerTimer interval: 4000; running: false; repeat: false onTriggered: { if (video2.opacity == 1) mediaplayer1.stop() else mediaplayer2.stop() } } // Clock and Login Area Rectangle { id: rectangle anchors.fill: parent color: "transparent" MouseArea { id: loginScreenRoot anchors.fill: parent property bool uiVisible: true property bool blockUI: mainStack.depth > 1 || userListComponent.mainPasswordBox.text.length > 0 || inputPanel.keyboardActive || config.type !== "image" 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.onPressed: { uiVisible = true; event.accepted = false; } //takes one full minute for the ui to disappear Timer { id: fadeoutTimer running: true interval: 60000 onTriggered: { if (!loginScreenRoot.blockUI) { loginScreenRoot.uiVisible = false; } } } QQC2.StackView { id: mainStack anchors.fill: parent height: root.height + PlasmaCore.Units.gridUnit * 3 // If true (depends on the style and environment variables), hover events are always accepted // and propagation stopped. This means the parent MouseArea won't get them and the UI won't be shown. // Disable capturing those events while the UI is hidden to avoid that, while still passing events otherwise. // One issue is that while the UI is visible, mouse activity won't keep resetting the timer, but when it // finally expires, the next event should immediately set uiVisible = true again. hoverEnabled: loginScreenRoot.uiVisible ? undefined : false 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 loginScreenUiVisible: loginScreenRoot.uiVisible userListCurrentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0 lastUserName: userModel.lastUser showUserList: { if ( !userListModel.hasOwnProperty("count") || !userListModel.hasOwnProperty("disableAvatarsThreshold")) return (userList.y + mainStack.y) > 0 if ( userListModel.count === 0 ) return false if ( userListModel.hasOwnProperty("containsAllUsers") && !userListModel.containsAllUsers ) return false return userListModel.count <= userListModel.disableAvatarsThreshold && (userList.y + mainStack.y) > 0 } 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 } onLoginRequest: { root.notificationMessage = "" sddm.login(username, password, sessionButton.currentIndex) } } Behavior on opacity { OpacityAnimator { duration: PlasmaCore.Units.longDuration } } readonly property real zoomFactor: 3 popEnter: Transition { ScaleAnimator { from: mainStack.zoomFactor to: 1 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } OpacityAnimator { from: 0 to: 1 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } } popExit: Transition { ScaleAnimator { from: 1 to: 0 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } OpacityAnimator { from: 1 to: 0 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } } pushEnter: Transition { ScaleAnimator { from: 0 to: 1 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } OpacityAnimator { from: 0 to: 1 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } } pushExit: Transition { ScaleAnimator { from: 1 to: mainStack.zoomFactor duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } OpacityAnimator { from: 1 to: 0 duration: PlasmaCore.Units.longDuration * (mainStack.zoomFactor / 2) easing.type: Easing.OutCubic } } } Loader { id: inputPanel state: "hidden" property bool keyboardActive: item ? item.active : false onKeyboardActiveChanged: { if (keyboardActive) { state = "visible" // Otherwise the password field loses focus and virtual keyboard // keystrokes get eaten userListComponent.mainPasswordBox.forceActiveFocus(); } else { state = "hidden"; } } source: Qt.platform.pluginName.includes("wayland") ? "components/VirtualKeyboard_wayland.qml" : "components/VirtualKeyboard.qml" anchors { left: parent.left right: parent.right } function showHide() { state = state == "hidden" ? "visible" : "hidden"; } states: [ State { name: "visible" PropertyChanges { target: mainStack y: Math.min(0, root.height - inputPanel.height - userListComponent.visibleBoundary) } PropertyChanges { target: inputPanel y: root.height - inputPanel.height opacity: 1 } }, State { name: "hidden" PropertyChanges { target: mainStack y: 0 } PropertyChanges { target: inputPanel y: root.height - root.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: PlasmaCore.Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: PlasmaCore.Units.longDuration easing.type: Easing.OutQuad } OpacityAnimator { target: inputPanel duration: PlasmaCore.Units.longDuration easing.type: Easing.OutQuad } } } }, Transition { from: "visible" to: "hidden" SequentialAnimation { ParallelAnimation { NumberAnimation { target: mainStack property: "y" duration: PlasmaCore.Units.longDuration easing.type: Easing.InOutQuad } NumberAnimation { target: inputPanel property: "y" duration: PlasmaCore.Units.longDuration easing.type: Easing.InQuad } OpacityAnimator { target: inputPanel duration: PlasmaCore.Units.longDuration easing.type: Easing.InQuad } } ScriptAction { script: { inputPanel.item.activated = false; Qt.inputMethod.hide(); } } } } ] } Component { id: userPromptComponent Login { showUsernamePrompt: true notificationMessage: root.notificationMessage loginScreenUiVisible: loginScreenRoot.uiVisible fontSize: parseInt(config.fontSize) + 2 // using a model rather than a QObject list to avoid QTBUG-75900 userListModel: ListModel { ListElement { name: "" iconSource: "" } Component.onCompleted: { // as we can't bind inside ListElement setProperty(0, "name", i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Type in Username and Password")); } } onLoginRequest: { root.notificationMessage = "" sddm.login(username, password, sessionButton.currentIndex) } } } DropShadow { id: logoShadow anchors.fill: logo source: logo visible: !softwareRendering && config.showlogo == "shown" horizontalOffset: 1 verticalOffset: 1 radius: 6 samples: 14 spread: 0.3 color : "black" // shadows should always be black opacity: loginScreenRoot.uiVisible ? 0 : 1 Behavior on opacity { //OpacityAnimator when starting from 0 is buggy (it shows one frame with opacity 1)" NumberAnimation { duration: PlasmaCore.Units.longDuration easing.type: Easing.InOutQuad } } } Image { id: logo visible: config.showlogo == "shown" source: config.logo anchors.horizontalCenter: parent.horizontalCenter anchors.bottom: footer.top anchors.bottomMargin: PlasmaCore.Units.largeSpacing asynchronous: true sourceSize.height: height opacity: loginScreenRoot.uiVisible ? 0 : 1 fillMode: Image.PreserveAspectFit height: Math.round(PlasmaCore.Units.gridUnit * 3.5) Behavior on opacity { // OpacityAnimator when starting from 0 is buggy (it shows one frame with opacity 1)" NumberAnimation { duration: PlasmaCore.Units.longDuration easing.type: Easing.InOutQuad } } } //Footer RowLayout { id: footer anchors { bottom: parent.bottom left: parent.left right: parent.right margins: PlasmaCore.Units.smallSpacing } Behavior on opacity { OpacityAnimator { duration: PlasmaCore.Units.longDuration } } PlasmaComponents3.ToolButton { text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to show/hide virtual keyboard", "Virtual Keyboard") font.pointSize: config.fontSize icon.name: inputPanel.keyboardActive ? "input-keyboard-virtual-on" : "input-keyboard-virtual-off" onClicked: inputPanel.showHide() visible: inputPanel.status == Loader.Ready } KeyboardButton { font.pointSize: config.fontSize } SessionButton { id: sessionButton font.pointSize: config.fontSize } Item { Layout.fillWidth: true } Battery { fontSize: config.fontSize } } } Clock { id: clock y: parent.height * config.relativePositionY - clock.height / 2 x: parent.width * config.relativePositionX - clock.width / 2 color: "white" timeFont.family: textFont.name dateFont.family: textFont.name } Rectangle { id: login_container //y: parent.height * 0.8 y: clock.y + clock.height + 30 width: clock.width height: parent.height * 0.08 color: "transparent" anchors.left: clock.left Rectangle { id: username_row height: parent.height * 0.36 color: "transparent" anchors.left: parent.left anchors.leftMargin: 0 anchors.right: parent.right anchors.rightMargin: 0 transformOrigin: Item.Center anchors.margins: 10 Text { id: username_label width: parent.width * 0.27 height: parent.height * 0.66 horizontalAlignment: Text.AlignLeft font.family: textFont.name font.bold: true font.pixelSize: 16 color: "white" text: "Username" anchors.verticalCenter: parent.verticalCenter } TextBox { id: username_input_box height: parent.height text: userModel.lastUser anchors.verticalCenter: parent.verticalCenter anchors.left: username_label.right anchors.leftMargin: config.usernameLeftMargin anchors.right: parent.right anchors.rightMargin: 0 font: textFont.name color: "#25000000" borderColor: "transparent" textColor: "white" Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { sddm.login(username_input_box.text, password_input_box.text, session.index) event.accepted = true } } KeyNavigation.backtab: password_input_box KeyNavigation.tab: password_input_box } } Rectangle { id: password_row y: username_row.height + 10 height: parent.height * 0.36 color: "transparent" anchors.right: parent.right anchors.rightMargin: 0 anchors.left: parent.left anchors.leftMargin: 0 Text { id: password_label width: parent.width * 0.27 text: textConstants.password anchors.verticalCenter: parent.verticalCenter horizontalAlignment: Text.AlignLeft font.family: textFont.name font.bold: true font.pixelSize: 16 color: "white" } PasswordBox { id: password_input_box height: parent.height font: textFont.name color: "#25000000" anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: parent.height // this sets button width, this way its a square anchors.left: password_label.right anchors.leftMargin: config.passwordLeftMargin borderColor: "transparent" textColor: "white" tooltipBG: "#25000000" tooltipFG: "#dc322f" image: "components/resources/warning_red.png" onTextChanged: { if (password_input_box.text == "") { clear_passwd_button.visible = false } if (password_input_box.text != "" && config.showClearPasswordButton != "false") { clear_passwd_button.visible = true } } Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { sddm.login(username_input_box.text, password_input_box.text, session.index) event.accepted = true } } KeyNavigation.backtab: username_input_box KeyNavigation.tab: login_button } Button { id: clear_passwd_button height: parent.height width: parent.height color: "transparent" text: "x" font: textFont.name border.color: "transparent" border.width: 0 anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.leftMargin: 0 anchors.rightMargin: parent.height disabledColor: "#dc322f" activeColor: "#393939" pressedColor: "#2aa198" onClicked: { password_input_box.text='' password_input_box.focus = true } } Button { id: login_button height: parent.height color: "#393939" text: ">" border.color: "#00000000" anchors.verticalCenter: parent.verticalCenter anchors.left: password_input_box.right anchors.right: parent.right disabledColor: "#dc322f" activeColor: "#268bd2" pressedColor: "#2aa198" textColor: "white" font: textFont.name onClicked: sddm.login(username_input_box.text, password_input_box.text, session.index) KeyNavigation.backtab: password_input_box KeyNavigation.tab: reboot_button } Text { id: error_message height: parent.height font.family: textFont.name font.pixelSize: 12 color: "white" anchors.top: password_input_box.bottom anchors.left: password_input_box.left anchors.leftMargin: 0 } } } } Component.onCompleted: { // Set Focus /* if (username_input_box.text == "") */ /* username_input_box.focus = true */ /* else */ /* password_input_box.focus = true */ video1.focus = true // load and randomize playlist var time = parseInt(new Date().toLocaleTimeString(Qt.locale(),'h')) if ( time >= 5 && time <= 17 ) { playlist1.load(Qt.resolvedUrl(config.background_vid_day), 'm3u') playlist2.load(Qt.resolvedUrl(config.background_vid_day), 'm3u') image1.source = config.background_img_day } else { playlist1.load(Qt.resolvedUrl(config.background_vid_night), 'm3u') playlist2.load(Qt.resolvedUrl(config.background_vid_night), 'm3u') image1.source = config.background_img_night } for (var k = 0; k < Math.ceil(Math.random() * 10) ; k++) { playlist1.shuffle() playlist2.shuffle() } if (config.showLoginButton == "false") { login_button.visible = false password_input_box.anchors.rightMargin = 0 clear_passwd_button.anchors.rightMargin = 0 } clear_passwd_button.visible = false } }