723 lines
27 KiB
QML

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
}
actionItems: [
ActionButton {
iconSource: "system-suspend"
text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.suspend()
enabled: sddm.canSuspend
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-reboot"
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.reboot()
enabled: sddm.canReboot
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-shutdown"
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.powerOff()
enabled: sddm.canPowerOff
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-user-prompt"
text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "For switching to a username and password prompt", "Other…")
fontSize: parseInt(config.fontSize) + 1
onClicked: mainStack.push(userPromptComponent)
enabled: true
visible: !userListComponent.showUsernamePrompt && !inputPanel.keyboardActive
}]
onLoginRequest: {
//root.notificationMessage = ""
sddm.login("sven", 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)
}
actionItems: [
ActionButton {
iconSource: "system-suspend"
text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.suspend()
enabled: sddm.canSuspend
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-reboot"
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.reboot()
enabled: sddm.canReboot
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-shutdown"
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down")
fontSize: parseInt(config.fontSize) + 1
onClicked: sddm.powerOff()
enabled: sddm.canPowerOff
visible: !inputPanel.keyboardActive
},
ActionButton {
iconSource: "system-user-list"
text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","List Users")
fontSize: parseInt(config.fontSize) + 1
onClicked: mainStack.pop()
visible: !inputPanel.keyboardActive
}
]
}
}
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
}
}
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
}
}