commit 636e786f88d2e9838c8096465d8b4e9b3b0914aa Author: Henk Kalkwater Date: Fri May 15 01:12:55 2020 +0200 Initial commit diff --git a/Main.qml b/Main.qml new file mode 100644 index 0000000..d527702 --- /dev/null +++ b/Main.qml @@ -0,0 +1,89 @@ +import QtQuick 2.0 +import QtGamepad 1.0 +import SddmComponents 2.0 + +import "components" +import "." + +Rectangle { + id: root + color: "black" + property LoginPane _userSelector + property string _user + property string _password + + Repeater { + model: screenModel + Image { + x: geometry.x + y: geometry.y + width: geometry.width + height: geometry.height + source: Style.background + Loader { + Component.onCompleted: console.log("%1, %2, %3 %4".arg(geometry.x).arg(geometry.y).arg(geometry.width).arg(geometry.height)) + anchors.fill: parent + source: screenModel.primary == index ? "./components/LoginPane.qml" : "" + onLoaded: { + if (screenModel.primary == index) { + _userSelector = item + _userSelector.userSelected.connect(root.userSelected) + _userSelector.closed.connect(root.userClosed) + _userSelector.hostname = sddm.hostname + console.log("Opening userSelect") + //startupAnim.start() + } + } + onStatusChanged: { + if (status == Loader.Error) { + console.log("Something went wrong!") + } + } + } + } + } + + Dialog { + id: errorDialog + anchors.fill: parent + message: qsTr("Invalid password") + onClosed: _userSelector.open() + } + + SequentialAnimation { + id: startupAnim + running: true + ColorAnimation { + target: root + property: "color" + from: "black" + to: "white" + easing.type: Easing.InOutQuad + duration: 250 + } + PauseAnimation { + duration: 300 + } + ScriptAction { + script: _userSelector.open() + } + } + + Connections { + target: sddm + onLoginFailed: { + errorDialog.show() + } + } + + function userSelected(name, password) { + _user = name + _password = password + _userSelector.close() + } + + function userClosed() { + //errorDialog.show() + sddm.login(_user, _password, _userSelector.sessionIndex) + } +} diff --git a/Style.qml b/Style.qml new file mode 100644 index 0000000..5acef8f --- /dev/null +++ b/Style.qml @@ -0,0 +1,27 @@ +pragma Singleton +import QtQuick 2.0 + +QtObject { + property color backgroundColor: "#FF444444" + property color backgroundColor2: "#FF888888" + property color foregroundColor: "white" + + property color overlayColor: "#cc000000" + + property color highlightColor: "#FFAAAAFF" + property color highlightColor2: "blue" + + property real highlightGap: 1 + property int highlightBorderSize: 8 + + property real smallPadding: 16 + property real mediumPadding: 32 + property real horizontalPadding: 32 + property real horizontalContentPadding: 64 + + property int iconSize: 32 + property int fontTitleSize: 32 + property int fontNormalSize: 24 + + property string background: config.background ? config.background : Qt.resolvedUrl("bg.png") +} diff --git a/bg.jpg b/bg.jpg new file mode 100644 index 0000000..93ac5b3 Binary files /dev/null and b/bg.jpg differ diff --git a/bg.png b/bg.png new file mode 100644 index 0000000..047673f Binary files /dev/null and b/bg.png differ diff --git a/components/Cursor.qml b/components/Cursor.qml new file mode 100644 index 0000000..6a6095a --- /dev/null +++ b/components/Cursor.qml @@ -0,0 +1,30 @@ +import QtQuick 2.6 +import ".." + +Rectangle { + id: highlightItem + property Item target + + anchors.centerIn: parent + implicitWidth: target.width + Style.highlightGap + implicitHeight: target.height + Style.highlightGap + color: "#00000000" + radius: 3 + border.width: Style.highlightBorderSize - 2 * Style.highlightGap + border.color: Style.highlightColor + SequentialAnimation on border.color { + loops: Animation.Infinite + ColorAnimation { + from: Style.highlightColor + to: Style.highlightColor2 + easing.type: Easing.InQuad + duration: 3000 + } + ColorAnimation { + from: Style.highlightColor2 + to: Style.highlightColor + easing.type: Easing.OutQuad + duration: 3000 + } + } +} diff --git a/components/Dialog.qml b/components/Dialog.qml new file mode 100644 index 0000000..166bc2b --- /dev/null +++ b/components/Dialog.qml @@ -0,0 +1,91 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.6 + +import ".." + +FocusScope { + id: dialogRoot + + visible: false + enabled: visible + property alias message: messageText.text + signal closed() + + opacity: visible ? 1 : 0 + Behavior on opacity { NumberAnimation {} } + + Rectangle { + anchors.fill: parent + color: Style.overlayColor + } + + Rectangle { + anchors.centerIn: parent + width: 600 + height: column.height + color: Style.backgroundColor + + Column { + id: column + width: parent.width + + Text { + id: messageText + width: parent.width + padding: Style.horizontalPadding + font.pixelSize: Style.fontNormalSize + color: Style.foregroundColor + } + + Button { + id: button + flat: true + focus: true + width: parent.width + leftPadding: Style.horizontalPadding + rightPadding: leftPadding + topPadding: Style.smallPadding + bottomPadding: topPadding + font.pixelSize: Style.fontNormalSize + palette.buttonText: Style.highlightColor + palette.brightText: Style.highlightColor + palette.windowText: Style.highlightColor + palette.button: Style.backgroundColor2 + palette.mid: Style.backgroundColor2 + onClicked: { + hide() + } + indicator: Cursor { + target: button.background + visible: button.visualFocus + } + text: qsTr("Close") + } + } + } + + SequentialAnimation { + id: hideAnimation + NumberAnimation { + target: dialogRoot + property: "opacity" + to: 0 + } + ScriptAction { + script: { + dialogRoot.visible = false; + dialogRoot.closed() + } + } + } + + function show () { + console.log("ErrorDialog open") + visible = true; + dialogRoot.focus = true; + } + + function hide () { + hideAnimation.start() + } +} diff --git a/components/Divider.qml b/components/Divider.qml new file mode 100644 index 0000000..bb1ee30 --- /dev/null +++ b/components/Divider.qml @@ -0,0 +1,10 @@ +import QtQuick 2.0 +import ".." + +Rectangle { + anchors.left: parent.left + anchors.leftMargin: Style.horizontalPadding + anchors.right: parent.right + anchors.rightMargin: Style.horizontalPadding + height: 3 +} diff --git a/components/LoginPane.qml b/components/LoginPane.qml new file mode 100644 index 0000000..e47b2b5 --- /dev/null +++ b/components/LoginPane.qml @@ -0,0 +1,230 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.6 +import QtGraphicalEffects 1.6 + +import QtQuick.Layouts 1.6 + +import ".." +import "." +Item { + // This rectangle spans the screen + id: loginPaneRoot + // Emitted when an user is selected + signal userSelected(string user, string password) + signal closed() + property string hostname + property int sessionIndex + property bool _open: false + property string _user + + // Gives a blur effect to the background + Item { + id: background + opacity: 0 + anchors.fill: parent + Image { + id: effectSource + source: Style.background //refers to the root in Main.qml + anchors.fill: parent + visible: false + } + GaussianBlur { + id: blur + anchors.fill: effectSource + source: effectSource + radius: 16 + samples: 16 + } + } + + // The actual pane + Rectangle { + id: userSelector + anchors.bottom: parent.bottom + anchors.bottomMargin: -height + width: parent.width + height: content.height + color: Style.backgroundColor + Column { + id: content + width: parent.width + Title { + text: "Who is using %1?".arg(hostname) + anchors.left: parent.left + anchors.leftMargin: Style.horizontalContentPadding + } + + Divider {} + Item { height: Style.mediumPadding; width: 1; } + FocusScope { + width: parent.width + height: 220 + Row { + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + spacing: Style.highlightBorderSize + Style.highlightGap * 2 + /*ListModel { + id: userModel + ListElement { realName: "test1"; icon: "file:///home/chris/.face.icon";} + ListElement { realName: "test2"; icon: "file:///home/chris/.face.icon";} + ListElement { realName: "test3"; icon: "file:///home/chris/.face.icon";} + }*/ + Repeater { + model: userModel + UserDelegate { + focus: true + text: model.realName + icon.source: model.icon + onClicked: { + _user = model.realName + showPasswordOverlay() + } + } + } + } + } + Item { height: Style.mediumPadding; width: 1; } + Divider {} + //Item { height: Style.mediumPadding; width: 1; } + RowLayout { + height: Style.iconSize + Style.mediumPadding + anchors.left: parent.left + anchors.leftMargin: Style.horizontalContentPadding + anchors.right: parent.right + anchors.rightMargin: anchors.leftMargin + TintedImage { + Layout.preferredWidth: Style.iconSize + Layout.preferredHeight: Style.iconSize + + color: Style.foregroundColor + source: "../icons/keyboard.svg" + } + ComboBox { + Layout.fillHeight: true + //height: parent.height + Layout.preferredWidth: 400 + model: keyboard.layouts + flat: true + font.pixelSize: Style.fontNormalSize + + palette.buttonText: Style.foregroundColor + palette.button: Style.backgroundColor + palette.mid: Style.backgroundColor2 + palette.dark: Style.foregroundColor + palette.light: Style.backgroundColor + palette.window: Style.backgroundColor + + textRole: "longName" + currentIndex: keyboard.currentLayout + onCurrentIndexChanged: keyboard.currentLayout = currentIndex + } + Item { + Layout.fillWidth: true + Layout.fillHeight: true + } + ComboBox { + id: sessionIndex + Layout.fillHeight: true + //height: parent.height + Layout.preferredWidth: 400 + model: sessionModel + flat: true + font.pixelSize: Style.fontNormalSize + + palette.buttonText: Style.foregroundColor + palette.button: Style.backgroundColor + palette.mid: Style.backgroundColor2 + palette.dark: Style.foregroundColor + palette.light: Style.backgroundColor + palette.window: Style.backgroundColor + + textRole: "name" + currentIndex: sessionModel.lastIndex + onCurrentIndexChanged: loginPaneRoot.sessionIndex = currentIndex + } + + } + //Item { height: Style.mediumPadding; width: 1; } + } + + ParallelAnimation { + id: userSelectorShow + NumberAnimation { + target: background + property: "opacity" + from: 0 + to: 1 + duration: 100 + } + NumberAnimation { + target: userSelector + property: "anchors.bottomMargin" + from: -userSelector.height + to: 0 + duration: 150 + easing.type: Easing.OutQuad + } + } + SequentialAnimation { + id: userSelectorHide + ParallelAnimation { + NumberAnimation { + target: userSelector + property: "anchors.bottomMargin" + from: 0 + to: -userSelector.height + duration: 150 + easing.type: Easing.InQuad + } + NumberAnimation { + target: background + property: "opacity" + from: 1 + to: 0 + duration: 100 + } + } + ScriptAction { + script: closed() + } + } + } + + PasswordOverlay { + id: passwordOverlay + enabled: false + anchors.fill: parent + opacity: enabled ? 1 : 0 + Behavior on opacity { NumberAnimation {} } + + username: _user + + onPasswordEntered: { + hidePasswordOverlay() + userSelected(_user, password) + } + onCancelled: hidePasswordOverlay() + } + + function showPasswordOverlay() { + userSelector.enabled = false; + passwordOverlay.enabled = true; + passwordOverlay.focus = true; + } + + function hidePasswordOverlay() { + userSelector.enabled = true; + passwordOverlay.enabled = false; + } + + function open() { + if (!_open) userSelectorShow.start() + } + + function close() { + console.log("Closing pane") + /*if (_open)*/ userSelectorHide.start() + } + +} diff --git a/components/PasswordOverlay.qml b/components/PasswordOverlay.qml new file mode 100644 index 0000000..3b2259e --- /dev/null +++ b/components/PasswordOverlay.qml @@ -0,0 +1,63 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.6 + +import ".." + +FocusScope { + id: overlay + + property string username + + signal passwordEntered(string password) + signal cancelled() + + Connections { + target: Qt.inputMethod + onVisibleChanged: { + if (!Qt.inputMethod.visible) cancelled() + } + } + + + Rectangle { + anchors.fill: parent + color: Style.overlayColor + + TextField { + focus: true + //anchors.top: parent.top + property real _vkeyboardOffset: Qt.inputMethod.visible + ? root.height - Qt.inputMethod.keyboardRectangle.top : 0 + y: (parent.height - _vkeyboardOffset) / 2 - height / 2 + Behavior on y { NumberAnimation { duration: 100 }} + + anchors.left: parent.left + anchors.right: parent.right + //anchors.topMargin: Style.horizontalPadding + anchors.leftMargin: Style.horizontalContentPadding + anchors.rightMargin: Style.horizontalContentPadding + + padding: Style.mediumPadding + color: Style.foregroundColor + echoMode: TextInput.Password + + placeholderText: "Enter the password for %1".arg(username) + //color: Style.foregroundColor + font.pixelSize: Style.fontTitleSize + + background: Rectangle { + color: "#00000000" + border.width: Style.highlightBorderSize + border.color: Style.backgroundColor2 + } + + Keys.onEscapePressed: cancelled() + + + onAccepted: { + passwordEntered(text) + text = "" + } + } + } +} diff --git a/components/TintedImage.qml b/components/TintedImage.qml new file mode 100644 index 0000000..4c2b549 --- /dev/null +++ b/components/TintedImage.qml @@ -0,0 +1,23 @@ +import QtQuick 2.6 +import QtGraphicalEffects 1.6 + +Item { + property alias source: image.source + property alias color: colorOverlay.color + + // I thought this should be standard + implicitWidth: image.implicitWidth + implicitHeight: image.implicitHeight + Image { + id: image + visible: false + anchors.fill: parent + smooth: true + } + + ColorOverlay { + id: colorOverlay + anchors.fill: parent + source: image + } +} diff --git a/components/Title.qml b/components/Title.qml new file mode 100644 index 0000000..09ef916 --- /dev/null +++ b/components/Title.qml @@ -0,0 +1,9 @@ +import QtQuick 2.6 +import ".." + +Text { + color: Style.foregroundColor + font.pixelSize: Style.fontTitleSize + topPadding: Style.smallPadding + bottomPadding: Style.smallPadding +} diff --git a/components/UserDelegate.qml b/components/UserDelegate.qml new file mode 100644 index 0000000..ae5e411 --- /dev/null +++ b/components/UserDelegate.qml @@ -0,0 +1,71 @@ +import QtQuick 2.6 +import QtQuick.Controls 2.6 +import QtGraphicalEffects 1.0 +import ".." + +AbstractButton { + id: userDelegateRoot + //property alias icon: userPic.source + //property alias name: userName.text + focusPolicy: Qt.StrongFocus + readonly property real _innerWidth: 180 + readonly property real _innerHeight: 220 + + //cursorShape: Qt.PointingHandCursor + + indicator: Cursor { + target: backgroundItem + visible: userDelegateRoot.visualFocus + } + background: Item { + id: backgroundItem + anchors.centerIn: parent + width: contentItem.width + 2 * Style.highlightBorderSize + height: contentItem.height + 2 * Style.highlightBorderSize + + Rectangle { + id: backgroundItemInner + anchors.centerIn: parent + color: userDelegateRoot.down ? Style.backgroundColor : Style.backgroundColor2 + implicitWidth: parent.width - 2 * Style.highlightBorderSize + implicitHeight: parent.height - 2 * Style.highlightBorderSize + visible: false + } + + DropShadow { + anchors.fill: backgroundItemInner + horizontalOffset: 3 + verticalOffset: 3 + radius: 8.0 + samples: 17 + color: "#80000000" + source: backgroundItemInner + } + } + contentItem: Item { + id: contentItem + implicitWidth: _innerWidth + implicitHeight: _innerHeight + + Image { + id: userPic + anchors.top: parent.top + anchors.left: parent.left + anchors.right: parent.right + width: parent.width + height: width + source: userDelegateRoot.icon.source + } + + Text { + id: userName + anchors.top: userPic.bottom + anchors.bottom: parent.bottom + anchors.horizontalCenter: parent.horizontalCenter + + text: userDelegateRoot.text + font.pixelSize: Style.fontNormalSize + color: Style.foregroundColor + } + } +} diff --git a/icons/keyboard.svg b/icons/keyboard.svg new file mode 100644 index 0000000..acef629 --- /dev/null +++ b/icons/keyboard.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/icons/mouse.svg b/icons/mouse.svg new file mode 100644 index 0000000..cda9188 --- /dev/null +++ b/icons/mouse.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/metadata.desktop b/metadata.desktop new file mode 100644 index 0000000..5f5348b --- /dev/null +++ b/metadata.desktop @@ -0,0 +1,14 @@ +[SddmGreeterTheme] +Name=NX Theme +Namep[nl]=NX-Thema + +Description=A theme inspired by the Nintendo Switch +Description[nl]=Een theme geinspireerd door de Nintendo Switch +Preview=screenshot.png +MainScript=Main.qml +Author=Chris Josten +License=lgpl-v3 +Type=sddm-theme +ConfigFile=theme.conf +Theme-Id=nx-sddm +Theme-API=2.0 diff --git a/qmldir b/qmldir new file mode 100644 index 0000000..87b898e --- /dev/null +++ b/qmldir @@ -0,0 +1 @@ +singleton Style 1.0 Style.qml diff --git a/screenshot.png b/screenshot.png new file mode 100644 index 0000000..c792ef1 Binary files /dev/null and b/screenshot.png differ diff --git a/theme.conf b/theme.conf new file mode 100644 index 0000000..09f78d3 --- /dev/null +++ b/theme.conf @@ -0,0 +1,3 @@ +[General] + +background=