diff --git a/kde/sddm/DropdownMenuStyle.qml b/kde/sddm/DropdownMenuStyle.qml new file mode 100644 index 0000000..08c632f --- /dev/null +++ b/kde/sddm/DropdownMenuStyle.qml @@ -0,0 +1,26 @@ +import QtQuick 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore + +import QtQuick.Controls.Styles 1.4 as QQCS +import QtQuick.Controls 1.3 as QQC + +QQCS.MenuStyle { + frame: Rectangle { + color: "#1E2326" + border.color: "#0C0E15" + border.width: 1 + } + itemDelegate.label: QQC.Label { + height: contentHeight * 2 + verticalAlignment: Text.AlignVCenter + color: config.highlight_color + font.pointSize: config.fontSize + font.family: config.font + text: styleData.text + } + itemDelegate.background: Rectangle { + visible: styleData.selected + color: config.selected_color + } +} diff --git a/kde/sddm/KeyboardButton.qml b/kde/sddm/KeyboardButton.qml new file mode 100644 index 0000000..2a83f89 --- /dev/null +++ b/kde/sddm/KeyboardButton.qml @@ -0,0 +1,38 @@ +import QtQuick 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +import QtQuick.Controls 1.3 as QQC + +PlasmaComponents.ToolButton { + id: keyboardButton + + property int currentIndex: -1 + + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Keyboard Layout: %1", instantiator.objectAt(currentIndex).shortName) + implicitWidth: minimumWidth + font.pointSize: config.fontSize + + visible: menu.items.length > 1 + + Component.onCompleted: currentIndex = Qt.binding(function() {return keyboard.currentLayout}); + + menu: QQC.Menu { + id: keyboardMenu + style: DropdownMenuStyle {} + Instantiator { + id: instantiator + model: keyboard.layouts + onObjectAdded: keyboardMenu.insertItem(index, object) + onObjectRemoved: keyboardMenu.removeItem( object ) + delegate: QQC.MenuItem { + text: modelData.longName + property string shortName: modelData.shortName + onTriggered: { + keyboard.currentLayout = model.index + } + } + } + } +} diff --git a/kde/sddm/Login.qml b/kde/sddm/Login.qml new file mode 100644 index 0000000..d35499f --- /dev/null +++ b/kde/sddm/Login.qml @@ -0,0 +1,139 @@ +import "components" + +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Styles 1.4 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +SessionManagementScreen { + id: root + property Item mainPasswordBox: passwordBox + + property bool showUsernamePrompt: !showUserList + + property string lastUserName + property bool loginScreenUiVisible: false + + //the y position that should be ensured visible when the on screen keyboard is visible + property int visibleBoundary: mapFromItem(loginButton, 0, 0).y + onHeightChanged: visibleBoundary = mapFromItem(loginButton, 0, 0).y + loginButton.height + units.smallSpacing + + signal loginRequest(string username, string password) + + onShowUsernamePromptChanged: { + if (!showUsernamePrompt) { + lastUserName = "" + } + } + + /* + * Login has been requested with the following username and password + * If username field is visible, it will be taken from that, otherwise from the "name" property of the currentIndex + */ + function startLogin() { + var username = showUsernamePrompt ? userNameInput.text : userList.selectedUser + var password = passwordBox.text + + //this is partly because it looks nicer + //but more importantly it works round a Qt bug that can trigger if the app is closed with a TextField focused + //DAVE REPORT THE FRICKING THING AND PUT A LINK + loginButton.forceActiveFocus(); + loginRequest(username, password); + } + + Input { + id: userNameInput + Layout.fillWidth: true + text: lastUserName + visible: showUsernamePrompt + focus: showUsernamePrompt && !lastUserName //if there's a username prompt it gets focus first, otherwise password does + placeholderText: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Username") + + onAccepted: + if (root.loginScreenUiVisible) { + passwordBox.forceActiveFocus() + } + } + + Input { + id: passwordBox + placeholderText: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Password") + focus: !showUsernamePrompt || lastUserName + echoMode: TextInput.Password + + Layout.fillWidth: true + + onAccepted: { + if (root.loginScreenUiVisible) { + startLogin(); + } + } + + Keys.onEscapePressed: { + mainStack.currentItem.forceActiveFocus(); + } + + //if empty and left or right is pressed change selection in user switch + //this cannot be in keys.onLeftPressed as then it doesn't reach the password box + Keys.onPressed: { + if (event.key == Qt.Key_Left && !text) { + userList.decrementCurrentIndex(); + event.accepted = true + } + if (event.key == Qt.Key_Right && !text) { + userList.incrementCurrentIndex(); + event.accepted = true + } + } + + Connections { + target: sddm + onLoginFailed: { + passwordBox.selectAll() + passwordBox.forceActiveFocus() + } + } + } + Button { + id: loginButton + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Log In") + enabled: passwordBox.text != "" + + Layout.topMargin: 20 + Layout.fillWidth: true + + font.pointSize: config.fontSize + font.family: config.font + + contentItem: Text { + text: loginButton.text + font: loginButton.font + opacity: enabled ? 1.0 : 0.3 + color: "#ffffff" + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + } + + background: Rectangle { + id: buttonBackground + width:30 + height: 270 + radius: width / 2 + rotation: -90 + anchors.centerIn: parent + + gradient: Gradient { + GradientStop { position: 0.0; color: "#F9D423" } + GradientStop { position: 0.33; color: "#FF4E50" } + GradientStop { position: 1.0; color: "#8A2387" } + } + } + + onClicked: startLogin(); + } + +} diff --git a/kde/sddm/Main.qml b/kde/sddm/Main.qml new file mode 100644 index 0000000..3aaf58f --- /dev/null +++ b/kde/sddm/Main.qml @@ -0,0 +1,445 @@ +import QtQuick 2.8 + +import QtQuick.Layouts 1.1 +import QtQuick.Controls 1.1 +import QtGraphicalEffects 1.0 + +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 + + readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software + + colorGroup: PlasmaCore.Theme.ComplementaryColorGroup + + width: 1600 + height: 900 + + property string notificationMessage + property string clock_color: "#fff" + + LayoutMirroring.enabled: Qt.application.layoutDirection === Qt.RightToLeft + LayoutMirroring.childrenInherit: true + + PlasmaCore.DataSource { + id: keystateSource + engine: "keystate" + connectedSources: "Caps Lock" + } + + Image { + id: wallpaper + height: parent.height + width: parent.width + source: config.background || config.Background + asynchronous: true + cache: true + clip: true + } + + 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; + } + } + } + + Clock { + id: clock + visible: true + x: parent.width - width - 10 + y: parent.height - height - 10 + } + + + StackView { + id: mainStack + anchors.left: parent.left + height: root.height + width: parent.width / 3 + + 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 + + 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: Qt.resolvedUrl("assets/suspend.svgz") + text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep") + onClicked: sddm.suspend() + enabled: sddm.canSuspend + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: Qt.resolvedUrl("assets/restart.svgz") + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") + onClicked: sddm.reboot() + enabled: sddm.canReboot + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: Qt.resolvedUrl("assets/shutdown.svgz") + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down") + onClicked: sddm.powerOff() + enabled: sddm.canPowerOff + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: Qt.resolvedUrl("assets/change_user.svgz") + 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) + } + } + + Behavior on opacity { + OpacityAnimator { + duration: units.longDuration + } + } + } + + Loader { + id: inputPanel + state: "hidden" + property bool keyboardActive: item ? item.active : false + onKeyboardActiveChanged: { + if (keyboardActive) { + state = "visible" + } else { + state = "hidden"; + } + } + source: "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: units.longDuration + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: inputPanel + property: "y" + duration: units.longDuration + easing.type: Easing.OutQuad + } + OpacityAnimator { + target: inputPanel + duration: units.longDuration + easing.type: Easing.OutQuad + } + } + } + }, + Transition { + from: "visible" + to: "hidden" + SequentialAnimation { + ParallelAnimation { + NumberAnimation { + target: mainStack + property: "y" + duration: units.longDuration + easing.type: Easing.InOutQuad + } + NumberAnimation { + target: inputPanel + property: "y" + duration: units.longDuration + easing.type: Easing.InQuad + } + OpacityAnimator { + target: inputPanel + duration: units.longDuration + easing.type: Easing.InQuad + } + } + ScriptAction { + script: { + Qt.inputMethod.hide(); + } + } + } + } + ] + } + + + Component { + id: userPromptComponent + Login { + showUsernamePrompt: true + notificationMessage: root.notificationMessage + loginScreenUiVisible: loginScreenRoot.uiVisible + + // 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: Qt.resolvedUrl("assets/suspend.svgz") + text: i18ndc("plasma_lookandfeel_org.kde.lookandfeel","Suspend to RAM","Sleep") + onClicked: sddm.suspend() + enabled: sddm.canSuspend + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: Qt.resolvedUrl("assets/restart.svgz") + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Restart") + onClicked: sddm.reboot() + enabled: sddm.canReboot + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: Qt.resolvedUrl("assets/shutdown.svgz") + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Shut Down") + onClicked: sddm.powerOff() + enabled: sddm.canPowerOff + visible: !inputPanel.keyboardActive + }, + ActionButton { + iconSource: "go-previous" + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","List Users") + onClicked: mainStack.pop() + visible: !inputPanel.keyboardActive + } + ] + } + } + + Rectangle { + id: formBg + anchors.fill: mainStack + anchors.centerIn: mainStack + color: "#401E2326" + z:-1 + } + + ShaderEffectSource { + id: blurArea + sourceItem: wallpaper + width: formBg.width + height: formBg.height + anchors.centerIn: formBg + sourceRect: Qt.rect(x,y,width,height) + visible: true + z:-2 + } + + GaussianBlur { + id: blur + height: formBg.height + width: formBg.width + source: blurArea + radius: 50 + samples: 50 * 2 + 1 + cached: true + anchors.centerIn: formBg + visible: true + z:-2 + } + + //Footer + RowLayout { + id: footer + anchors { + bottom: parent.bottom + left: parent.left + 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 + } + } + } + + 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 = "" + } +} diff --git a/kde/sddm/SessionButton.qml b/kde/sddm/SessionButton.qml new file mode 100644 index 0000000..dc76fd9 --- /dev/null +++ b/kde/sddm/SessionButton.qml @@ -0,0 +1,40 @@ +import QtQuick 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +import QtQuick.Controls 1.3 as QQC + +PlasmaComponents.ToolButton { + id: root + property int currentIndex: -1 + + implicitWidth: minimumWidth + + visible: menu.items.length > 1 + + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel", "Desktop Session: %1", instantiator.objectAt(currentIndex).text || "") + + font.pointSize: config.fontSize + + Component.onCompleted: { + currentIndex = sessionModel.lastIndex + } + + menu: QQC.Menu { + id: menu + style: DropdownMenuStyle {} + Instantiator { + id: instantiator + model: sessionModel + onObjectAdded: menu.insertItem(index, object) + onObjectRemoved: menu.removeItem( object ) + delegate: QQC.MenuItem { + text: model.name + onTriggered: { + root.currentIndex = model.index + } + } + } + } +} diff --git a/kde/sddm/assets/bg.jpg b/kde/sddm/assets/bg.jpg new file mode 100644 index 0000000..89c6164 Binary files /dev/null and b/kde/sddm/assets/bg.jpg differ diff --git a/kde/sddm/assets/change_user.svgz b/kde/sddm/assets/change_user.svgz new file mode 100644 index 0000000..0b56e17 Binary files /dev/null and b/kde/sddm/assets/change_user.svgz differ diff --git a/kde/sddm/assets/restart.svgz b/kde/sddm/assets/restart.svgz new file mode 100644 index 0000000..7ff29c0 Binary files /dev/null and b/kde/sddm/assets/restart.svgz differ diff --git a/kde/sddm/assets/shutdown.svgz b/kde/sddm/assets/shutdown.svgz new file mode 100644 index 0000000..9228762 Binary files /dev/null and b/kde/sddm/assets/shutdown.svgz differ diff --git a/kde/sddm/assets/suspend.svgz b/kde/sddm/assets/suspend.svgz new file mode 100644 index 0000000..cff7f6b Binary files /dev/null and b/kde/sddm/assets/suspend.svgz differ diff --git a/kde/sddm/components/ActionButton.qml b/kde/sddm/components/ActionButton.qml new file mode 100644 index 0000000..830ca30 --- /dev/null +++ b/kde/sddm/components/ActionButton.qml @@ -0,0 +1,72 @@ +import QtQuick 2.2 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: root + property alias text: label.text + property alias iconSource: icon.source + property alias containsMouse: mouseArea.containsMouse + property alias font: label.font + signal clicked + + activeFocusOnTab: true + + property int iconSize: units.gridUnit * 2.5 + + implicitWidth: Math.max(iconSize + units.largeSpacing * 2, label.contentWidth) + implicitHeight: iconSize + units.smallSpacing + label.implicitHeight + + opacity: activeFocus || containsMouse ? 1.5 : 0.97 + Behavior on opacity { + PropertyAnimation { // OpacityAnimator makes it turn black at random intervals + duration: units.longDuration * 2 + easing.type: Easing.InOutQuad + } + } + + + PlasmaCore.IconItem { + id: icon + anchors { + top: parent.top + horizontalCenter: parent.horizontalCenter + } + width: iconSize + height: iconSize + + colorGroup: PlasmaCore.ColorScope.colorGroup + active: mouseArea.containsMouse || root.activeFocus + } + PlasmaComponents.Label { + id: label + anchors { + top: icon.bottom + topMargin: units.smallSpacing + left: parent.left + right: parent.right + } + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignTop + wrapMode: Text.WordWrap + font.underline: root.activeFocus + font.pointSize: config.fontSize + font.family: config.font + color:activeFocus || containsMouse ? config.highlight_color : config.color + } + + MouseArea { + id: mouseArea + hoverEnabled: true + onClicked: root.clicked() + anchors.fill: parent + } + + Keys.onEnterPressed: clicked() + Keys.onReturnPressed: clicked() + Keys.onSpacePressed: clicked() + + Accessible.onPressAction: clicked() + Accessible.role: Accessible.Button + Accessible.name: label.text +} diff --git a/kde/sddm/components/Battery.qml b/kde/sddm/components/Battery.qml new file mode 100644 index 0000000..7685b39 --- /dev/null +++ b/kde/sddm/components/Battery.qml @@ -0,0 +1,33 @@ +import QtQuick 2.2 + +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents +import org.kde.plasma.workspace.components 2.0 as PW + +Row { + spacing: units.smallSpacing + visible: pmSource.data["Battery"]["Has Cumulative"] + + PlasmaCore.DataSource { + id: pmSource + engine: "powermanagement" + connectedSources: ["Battery", "AC Adapter"] + } + + PW.BatteryIcon { + id: battery + hasBattery: pmSource.data["Battery"]["Has Battery"] || false + percent: pmSource.data["Battery"]["Percent"] || 0 + pluggedIn: pmSource.data["AC Adapter"] ? pmSource.data["AC Adapter"]["Plugged in"] : false + + height: batteryLabel.height + width: height + } + + PlasmaComponents.Label { + id: batteryLabel + height: undefined + text: i18nd("plasma_lookandfeel_org.kde.lookandfeel","%1%", battery.percent) + Accessible.name: i18nd("plasma_lookandfeel_org.kde.lookandfeel","Battery at %1%", battery.percent) + } +} diff --git a/kde/sddm/components/Clock.qml b/kde/sddm/components/Clock.qml new file mode 100644 index 0000000..6b6ec0a --- /dev/null +++ b/kde/sddm/components/Clock.qml @@ -0,0 +1,35 @@ +import QtQuick 2.8 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.5 +import org.kde.plasma.core 2.0 + +ColumnLayout { + readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software + + Label { + text: Qt.formatTime(timeSource.data["Local"]["DateTime"]) + color: root.clock_color + style: softwareRendering ? Text.Outline : Text.Normal + styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" //no outline, doesn't matter + font.pointSize: 34 + Layout.alignment: Qt.AlignHCenter + font.family: config.font + + } + Label { + text: Qt.formatDate(timeSource.data["Local"]["DateTime"], Qt.DefaultLocaleLongDate) + color: root.clock_color + style: softwareRendering ? Text.Outline : Text.Normal + styleColor: softwareRendering ? ColorScope.backgroundColor : "transparent" //no outline, doesn't matter + font.pointSize: 17 + Layout.alignment: Qt.AlignHCenter + font.family: config.font + + } + DataSource { + id: timeSource + engine: "time" + connectedSources: ["Local"] + interval: 1000 + } +} diff --git a/kde/sddm/components/Input.qml b/kde/sddm/components/Input.qml new file mode 100644 index 0000000..3ac4e4d --- /dev/null +++ b/kde/sddm/components/Input.qml @@ -0,0 +1,18 @@ +import QtQuick 2.2 +import QtQuick.Layouts 1.2 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Styles 1.4 + +TextField { + placeholderTextColor: config.color + palette.text: config.color + font.pointSize: config.fontSize + font.family: config.font + background: Rectangle { + color: "#701E2326" + radius: parent.width / 2 + height: 30 + width: 270 + anchors.centerIn: parent + } +} \ No newline at end of file diff --git a/kde/sddm/components/KeyboardLayoutButton.qml b/kde/sddm/components/KeyboardLayoutButton.qml new file mode 100644 index 0000000..a5e35a9 --- /dev/null +++ b/kde/sddm/components/KeyboardLayoutButton.qml @@ -0,0 +1,29 @@ +import QtQuick 2.1 +import QtQuick.Controls 1.1 as QQC + +import org.kde.plasma.components 2.0 as PlasmaComponents + +import org.kde.plasma.workspace.keyboardlayout 1.0 + +PlasmaComponents.ToolButton { + id: kbLayoutButton + + iconName: "input-keyboard" + implicitWidth: minimumWidth + text: layout.currentLayoutDisplayName + + Accessible.name: i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Button to change keyboard layout", "Switch layout") + + visible: layout.layouts.length > 1 + + onClicked: layout.nextLayout() + + KeyboardLayout { + id: layout + function nextLayout() { + var layouts = layout.layouts; + var index = (layouts.indexOf(layout.currentLayout)+1) % layouts.length; + layout.currentLayout = layouts[index]; + } + } +} diff --git a/kde/sddm/components/SessionManagementScreen.qml b/kde/sddm/components/SessionManagementScreen.qml new file mode 100644 index 0000000..0749b26 --- /dev/null +++ b/kde/sddm/components/SessionManagementScreen.qml @@ -0,0 +1,99 @@ +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 + +Item { + id: root + + /* + * Any message to be displayed to the user, visible above the text fields + */ + property alias notificationMessage: notificationsLabel.text + + /* + * A list of Items (typically ActionButtons) to be shown in a Row beneath the prompts + */ + property alias actionItems: actionItemsLayout.children + + /* + * A model with a list of users to show in the view + * The following roles should exist: + * - name + * - iconSource + * + * The following are also handled: + * - vtNumber + * - displayNumber + * - session + * - isTty + */ + property alias userListModel: userListView.model + + /* + * Self explanatory + */ + property alias userListCurrentIndex: userListView.currentIndex + property var userListCurrentModelData: userListView.currentItem === null ? [] : userListView.currentItem.m + property bool showUserList: true + + property alias userList: userListView + + default property alias _children: innerLayout.children + + UserList { + id: userListView + visible: showUserList && y > 0 + anchors { + bottom: parent.verticalCenter + left: parent.left + right: parent.right + } + } + + //goal is to show the prompts, in ~16 grid units high, then the action buttons + //but collapse the space between the prompts and actions if there's no room + //ui is constrained to 16 grid units wide, or the screen + ColumnLayout { + id: prompts + anchors.top: parent.verticalCenter + anchors.topMargin: units.gridUnit * 0.5 + anchors.left: parent.left + anchors.right: parent.right + anchors.bottom: parent.bottom + PlasmaComponents.Label { + id: notificationsLabel + Layout.maximumWidth: units.gridUnit * 16 + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + horizontalAlignment: Text.AlignHCenter + wrapMode: Text.WordWrap + font.italic: true + } + ColumnLayout { + Layout.minimumHeight: implicitHeight + Layout.maximumHeight: units.gridUnit * 10 + Layout.maximumWidth: units.gridUnit * 16 + Layout.alignment: Qt.AlignHCenter + ColumnLayout { + id: innerLayout + Layout.alignment: Qt.AlignHCenter + Layout.fillWidth: true + } + Item { + Layout.fillHeight: true + } + } + Row { //deliberately not rowlayout as I'm not trying to resize child items + id: actionItemsLayout + spacing: units.smallSpacing + Layout.alignment: Qt.AlignHCenter + } + Item { + Layout.fillHeight: true + } + } +} diff --git a/kde/sddm/components/UserDelegate.qml b/kde/sddm/components/UserDelegate.qml new file mode 100644 index 0000000..18d9bcb --- /dev/null +++ b/kde/sddm/components/UserDelegate.qml @@ -0,0 +1,175 @@ +import QtQuick 2.8 +import org.kde.plasma.core 2.0 as PlasmaCore +import org.kde.plasma.components 2.0 as PlasmaComponents + +Item { + id: wrapper + + // If we're using software rendering, draw outlines instead of shadows + // See https://bugs.kde.org/show_bug.cgi?id=398317 + readonly property bool softwareRendering: GraphicsInfo.api === GraphicsInfo.Software + + property bool isCurrent: true + + readonly property var m: model + property string name + property string userName + property string avatarPath + property string iconSource + property bool constrainText: true + property alias nameFontSize: usernameDelegate.font.pointSize + property int fontSize: config.fontSize - 1 + signal clicked() + + property real faceSize: Math.min(width, height - usernameDelegate.height - units.smallSpacing) + + opacity: isCurrent ? 1.0 : 0.5 + + Behavior on opacity { + OpacityAnimator { + duration: units.longDuration + } + } + + // Draw a translucent background circle under the user picture + Rectangle { + anchors.centerIn: imageSource + width: imageSource.width + 5 // Subtract to prevent fringing + height: width + radius: width / 2 + + gradient: Gradient { + GradientStop { position: 0.0; color: "#FEAC5E" } + GradientStop { position: 0.33; color: "#C779D0" } + GradientStop { position: 1.0; color: "#4BC0C8" } + } + } + + Item { + id: imageSource + anchors { + bottom: usernameDelegate.top + bottomMargin: units.largeSpacing + horizontalCenter: parent.horizontalCenter + } + Behavior on width { + PropertyAnimation { + from: faceSize + duration: units.longDuration * 2; + } + } + width: isCurrent ? faceSize : faceSize - units.largeSpacing + height: width + + //Image takes priority, taking a full path to a file, if that doesn't exist we show an icon + Image { + id: face + source: wrapper.avatarPath + sourceSize: Qt.size(faceSize, faceSize) + fillMode: Image.PreserveAspectCrop + anchors.fill: parent + } + + PlasmaCore.IconItem { + id: faceIcon + source: iconSource + visible: (face.status == Image.Error || face.status == Image.Null) + anchors.fill: parent + anchors.margins: units.gridUnit * 0.5 // because mockup says so... + colorGroup: PlasmaCore.ColorScope.colorGroup + } + } + + ShaderEffect { + anchors { + bottom: usernameDelegate.top + bottomMargin: units.largeSpacing + horizontalCenter: parent.horizontalCenter + } + + width: imageSource.width + height: imageSource.height + + supportsAtlasTextures: true + + property var source: ShaderEffectSource { + sourceItem: imageSource + // software rendering is just a fallback so we can accept not having a rounded avatar here + hideSource: wrapper.GraphicsInfo.api !== GraphicsInfo.Software + live: true // otherwise the user in focus will show a blurred avatar + } + + property var colorBorder: "#00000000" + + //draw a circle with an antialised border + //innerRadius = size of the inner circle with contents + //outerRadius = size of the border + //blend = area to blend between two colours + //all sizes are normalised so 0.5 == half the width of the texture + + //if copying into another project don't forget to connect themeChanged to update() + //but in SDDM that's a bit pointless + fragmentShader: " + varying highp vec2 qt_TexCoord0; + uniform highp float qt_Opacity; + uniform lowp sampler2D source; + + uniform lowp vec4 colorBorder; + highp float blend = 0.01; + highp float innerRadius = 0.47; + highp float outerRadius = 0.49; + lowp vec4 colorEmpty = vec4(0.0, 0.0, 0.0, 0.0); + + void main() { + lowp vec4 colorSource = texture2D(source, qt_TexCoord0.st); + + highp vec2 m = qt_TexCoord0 - vec2(0.5, 0.5); + highp float dist = sqrt(m.x * m.x + m.y * m.y); + + if (dist < innerRadius) + gl_FragColor = colorSource; + else if (dist < innerRadius + blend) + gl_FragColor = mix(colorSource, colorBorder, ((dist - innerRadius) / blend)); + else if (dist < outerRadius) + gl_FragColor = colorBorder; + else if (dist < outerRadius + blend) + gl_FragColor = mix(colorBorder, colorEmpty, ((dist - outerRadius) / blend)); + else + gl_FragColor = colorEmpty ; + + gl_FragColor = gl_FragColor * qt_Opacity; + } + " + } + + PlasmaComponents.Label { + id: usernameDelegate + font.pointSize: Math.max(fontSize + 2,theme.defaultFont.pointSize + 2) + anchors { + bottom: parent.bottom + horizontalCenter: parent.horizontalCenter + } + height: implicitHeight // work around stupid bug in Plasma Components that sets the height + width: constrainText ? parent.width : implicitWidth + text: wrapper.name + style: softwareRendering ? Text.Outline : Text.Normal + styleColor: softwareRendering ? PlasmaCore.ColorScope.backgroundColor : "transparent" //no outline, doesn't matter + elide: Text.ElideRight + horizontalAlignment: Text.AlignHCenter + color: config.color + //make an indication that this has active focus, this only happens when reached with keyboard navigation + font.underline: wrapper.activeFocus + font.family: config.font + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + + onClicked: wrapper.clicked(); + } + + Accessible.name: name + Accessible.role: Accessible.Button + function accessiblePressAction() { wrapper.clicked() } +} diff --git a/kde/sddm/components/UserList.qml b/kde/sddm/components/UserList.qml new file mode 100644 index 0000000..720a67f --- /dev/null +++ b/kde/sddm/components/UserList.qml @@ -0,0 +1,74 @@ +import QtQuick 2.2 + +ListView { + id: view + readonly property string selectedUser: currentItem ? currentItem.userName : "" + readonly property int userItemWidth: units.gridUnit * 8 + readonly property int userItemHeight: units.gridUnit * 8 + + implicitHeight: userItemHeight + + activeFocusOnTab : true + + /* + * Signals that a user was explicitly selected + */ + signal userSelected; + + orientation: ListView.Horizontal + highlightRangeMode: ListView.StrictlyEnforceRange + + //centre align selected item (which implicitly centre aligns the rest + preferredHighlightBegin: width/2 - userItemWidth/2 + preferredHighlightEnd: preferredHighlightBegin + + delegate: UserDelegate { + avatarPath: model.icon || "" + iconSource: model.iconName || "user-identity" + + name: { + var displayName = model.realName || model.name + + if (model.vtNumber === undefined || model.vtNumber < 0) { + return displayName + } + + if (!model.session) { + return i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Nobody logged in on that session", "Unused") + } + + + var location = "" + if (model.isTty) { + location = i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "User logged in on console number", "TTY %1", model.vtNumber) + } else if (model.displayNumber) { + location = i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "User logged in on console (X display number)", "on TTY %1 (Display %2)", model.vtNumber, model.displayNumber) + } + + if (location) { + return i18ndc("plasma_lookandfeel_org.kde.lookandfeel", "Username (location)", "%1 (%2)", displayName, location) + } + + return displayName + } + + userName: model.name + + width: userItemWidth + height: userItemHeight + + //if we only have one delegate, we don't need to clip the text as it won't be overlapping with anything + constrainText: ListView.view.count > 1 + + isCurrent: ListView.isCurrentItem + + onClicked: { + ListView.view.currentIndex = index; + ListView.view.userSelected(); + } + } + + Keys.onEscapePressed: view.userSelected() + Keys.onEnterPressed: view.userSelected() + Keys.onReturnPressed: view.userSelected() +} diff --git a/kde/sddm/components/VirtualKeyboard.qml b/kde/sddm/components/VirtualKeyboard.qml new file mode 100644 index 0000000..0c295d3 --- /dev/null +++ b/kde/sddm/components/VirtualKeyboard.qml @@ -0,0 +1,10 @@ +import QtQuick 2.5 +import QtQuick.VirtualKeyboard 2.1 + +InputPanel { + id: inputPanel + property bool activated: false + active: activated && Qt.inputMethod.visible + visible: active + width: parent.width +} diff --git a/kde/sddm/components/artwork/logout_primary.svgz b/kde/sddm/components/artwork/logout_primary.svgz new file mode 100644 index 0000000..6a9423f Binary files /dev/null and b/kde/sddm/components/artwork/logout_primary.svgz differ diff --git a/kde/sddm/components/artwork/restart_primary.svgz b/kde/sddm/components/artwork/restart_primary.svgz new file mode 100644 index 0000000..e2312bd Binary files /dev/null and b/kde/sddm/components/artwork/restart_primary.svgz differ diff --git a/kde/sddm/components/artwork/shutdown_primary.svgz b/kde/sddm/components/artwork/shutdown_primary.svgz new file mode 100644 index 0000000..1c60152 Binary files /dev/null and b/kde/sddm/components/artwork/shutdown_primary.svgz differ diff --git a/kde/sddm/faces/.face.icon b/kde/sddm/faces/.face.icon new file mode 100644 index 0000000..ecee991 --- /dev/null +++ b/kde/sddm/faces/.face.icon @@ -0,0 +1,14 @@ + + + + + + diff --git a/kde/sddm/metadata.desktop b/kde/sddm/metadata.desktop new file mode 100644 index 0000000..ef6ce98 --- /dev/null +++ b/kde/sddm/metadata.desktop @@ -0,0 +1,16 @@ +[SddmGreeterTheme] +Name=Sweet +Description=Sweet sddm theme +Author=Eliver Lara +Copyright=(c) 2019, Eliver Lara +License=GPL 3+ +Type=sddm-theme +Version=0.1 +Website=https://github.com/EliverLara/Sweet/tree/nova/kde/sddm +Screenshot=preview.png +MainScript=Main.qml +ConfigFile=theme.conf +TranslationsDirectory=translations +Email=eliverlara@gmail.com +Theme-Id=Sweet +Theme-API=2.0 diff --git a/kde/sddm/preview.png b/kde/sddm/preview.png new file mode 100644 index 0000000..df00a1b Binary files /dev/null and b/kde/sddm/preview.png differ diff --git a/kde/sddm/theme.conf b/kde/sddm/theme.conf new file mode 100644 index 0000000..4eafaf1 --- /dev/null +++ b/kde/sddm/theme.conf @@ -0,0 +1,8 @@ +[General] +type=image +color=#C3C7D1 +highlight_color=#ffffff +selected_color=#c50ed2 +fontSize=10 +Background="assets/bg.jpg" +font="Cantarell"