diff --git a/parch/Main.qml b/parch/Main.qml
new file mode 100644
index 0000000..3dde394
--- /dev/null
+++ b/parch/Main.qml
@@ -0,0 +1,55 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Window 2.12
+import "components"
+
+Item {
+ id: root
+
+ height: Screen.height
+ width: Screen.width
+
+ Image {
+ id: background
+
+ anchors.fill: parent
+ height: parent.height
+ width: parent.width
+ fillMode: Image.PreserveAspectCrop
+ source: config.Background
+ asynchronous: false
+ cache: true
+ mipmap: true
+ clip: true
+ }
+
+ Item {
+ id: contentPanel
+
+ anchors {
+ fill: parent
+ topMargin: config.Padding
+ rightMargin: config.Padding
+ bottomMargin: config.Padding
+ leftMargin: config.Padding
+ }
+
+ DateTimePanel {
+ id: dateTimePanel
+
+ anchors {
+ top: parent.top
+ right: parent.right
+ }
+
+ }
+
+ LoginPanel {
+ id: loginPanel
+
+ anchors.fill: parent
+ }
+
+ }
+
+}
diff --git a/parch/backgrounds/glacier.png b/parch/backgrounds/glacier.png
new file mode 100644
index 0000000..81a502a
Binary files /dev/null and b/parch/backgrounds/glacier.png differ
diff --git a/parch/backgrounds/leaves.png b/parch/backgrounds/leaves.png
new file mode 100644
index 0000000..7de6db1
Binary files /dev/null and b/parch/backgrounds/leaves.png differ
diff --git a/parch/components/DateTimePanel.qml b/parch/components/DateTimePanel.qml
new file mode 100644
index 0000000..8522249
--- /dev/null
+++ b/parch/components/DateTimePanel.qml
@@ -0,0 +1,53 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Column {
+ spacing: 0
+ Component.onCompleted: {
+ timeLabel.updateTime();
+ dateLabel.updateDate();
+ }
+
+ Text {
+ id: dateLabel
+
+ function updateDate() {
+ text = new Date().toLocaleDateString(Qt.locale(), config.DateFormat);
+ }
+
+ anchors.right: parent.right
+ opacity: config.DateOpacity
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.DateSize
+ font.bold: config.DateIsBold == "true" ? true : false
+ color: config.DateColor
+ }
+
+ Text {
+ id: timeLabel
+
+ function updateTime() {
+ text = new Date().toLocaleTimeString(Qt.locale(), config.TimeFormat);
+ }
+
+ anchors.right: parent.right
+ opacity: config.TimeOpacity
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.TimeSize
+ font.bold: config.TimeIsBold == "true" ? true : false
+ color: config.TimeColor
+ }
+
+ Timer {
+ interval: 1000
+ repeat: true
+ running: true
+ onTriggered: {
+ timeLabel.updateTime();
+ dateLabel.updateDate();
+ }
+ }
+
+}
diff --git a/parch/components/LoginPanel.qml b/parch/components/LoginPanel.qml
new file mode 100644
index 0000000..9872237
--- /dev/null
+++ b/parch/components/LoginPanel.qml
@@ -0,0 +1,180 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+import QtQuick.Window 2.12
+
+Item {
+ property var user: userPanel.username
+ property var password: passwordField.text
+ property var session: sessionPanel.session
+ property var inputHeight: Screen.height * config.UIScale * 0.25
+ property var inputWidth: Screen.width * config.UIScale
+
+ Column {
+ spacing: 8
+
+ anchors {
+ bottom: parent.bottom
+ left: parent.left
+ }
+
+ PowerPanel {
+ id: powerPanel
+ }
+
+ SessionPanel {
+ id: sessionPanel
+ }
+
+ }
+
+ Column {
+ spacing: 8
+ width: inputWidth
+
+ anchors {
+ bottom: parent.bottom
+ right: parent.right
+ }
+
+ UserPanel {
+ id: userPanel
+ }
+
+ PasswordPanel {
+ id: passwordField
+
+ height: inputHeight
+ width: parent.width
+ onAccepted: loginButton.clicked()
+ }
+
+ Button {
+ id: loginButton
+
+ height: inputHeight
+ width: parent.width
+ enabled: user != "" && password != "" ? true : false
+ hoverEnabled: true
+ text: "Login!!"
+ onClicked: {
+ sddm.login(user, password, session);
+ }
+ states: [
+ State {
+ name: "pressed"
+ when: loginButton.down
+
+ PropertyChanges {
+ target: buttonBackground
+ color: Qt.darker(config.LoginButtonBg, 1.4)
+ opacity: 1
+ }
+
+ PropertyChanges {
+ target: buttonText
+ opacity: 1
+ }
+
+ },
+ State {
+ name: "hovered"
+ when: loginButton.hovered
+
+ PropertyChanges {
+ target: buttonBackground
+ color: Qt.darker(config.LoginButtonBg, 1.2)
+ opacity: 1
+ }
+
+ PropertyChanges {
+ target: buttonText
+ opacity: 1
+ }
+
+ },
+ State {
+ name: "enabled"
+ when: loginButton.enabled
+
+ PropertyChanges {
+ target: buttonBackground
+ opacity: 1
+ }
+
+ PropertyChanges {
+ target: buttonText
+ opacity: 1
+ }
+
+ }
+ ]
+
+ Rectangle {
+ id: loginAnim
+
+ radius: parent.width / 2
+ anchors.centerIn: loginButton
+ color: "black"
+ opacity: 1
+
+ NumberAnimation {
+ id: coverScreen
+
+ target: loginAnim
+ properties: "height, width"
+ from: 0
+ to: root.width * 2
+ duration: 1000
+ easing.type: Easing.InExpo
+ }
+
+ }
+
+ contentItem: Text {
+ id: buttonText
+
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: config.LoginButtonTextColor
+ opacity: 0.5
+ text: config.LoginButtonText
+ }
+
+ background: Rectangle {
+ id: buttonBackground
+
+ color: config.LoginButtonBg
+ opacity: 0.5
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color, opacity"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ }
+
+ Connections {
+ function onLoginSucceeded() {
+ coverScreen.start();
+ }
+
+ function onLoginFailed() {
+ passwordField.text = "";
+ passwordField.focus = true;
+ }
+
+ target: sddm
+ }
+
+}
diff --git a/parch/components/PasswordPanel.qml b/parch/components/PasswordPanel.qml
new file mode 100644
index 0000000..a6c0ba5
--- /dev/null
+++ b/parch/components/PasswordPanel.qml
@@ -0,0 +1,61 @@
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+TextField {
+ id: passwordField
+
+ focus: true
+ selectByMouse: true
+ placeholderText: config.PassFieldBgText
+ echoMode: TextInput.Password
+ passwordCharacter: "•"
+ passwordMaskDelay: 1000
+ selectionColor: config.FieldText
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ color: config.FieldText
+ horizontalAlignment: TextInput.AlignHCenter
+ states: [
+ State {
+ name: "focused"
+ when: passwordField.activeFocus
+
+ PropertyChanges {
+ target: passFieldBg
+ color: Qt.darker(config.FieldBackground, 1.2)
+ border.width: config.FieldBorderWidth
+ }
+
+ },
+ State {
+ name: "hovered"
+ when: passwordField.hovered
+
+ PropertyChanges {
+ target: passFieldBg
+ color: Qt.darker(config.FieldBackground, 1.2)
+ }
+
+ }
+ ]
+
+ background: Rectangle {
+ id: passFieldBg
+
+ color: config.FieldBackground
+ border.color: config.FieldBorderColor
+ border.width: 0
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color, border.width"
+ duration: 150
+ }
+
+ }
+
+}
diff --git a/parch/components/PowerPanel.qml b/parch/components/PowerPanel.qml
new file mode 100644
index 0000000..a73f5bb
--- /dev/null
+++ b/parch/components/PowerPanel.qml
@@ -0,0 +1,236 @@
+import QtGraphicalEffects 1.12
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Item {
+ implicitHeight: powerButton.height
+ implicitWidth: powerButton.width
+
+ ListModel {
+ id: powerModel
+
+ ListElement {
+ name: "Sleep"
+ }
+
+ ListElement {
+ name: "Restart"
+ }
+
+ ListElement {
+ name: "Shut\nDown"
+ }
+
+ }
+
+ Button {
+ id: powerButton
+
+ height: inputHeight
+ width: inputHeight
+ hoverEnabled: true
+ icon.source: Qt.resolvedUrl("../icons/power.svg")
+ icon.height: height
+ icon.width: width
+ icon.color: config.PowerIconColor
+ onClicked: {
+ powerPopup.visible ? powerPopup.close() : powerPopup.open();
+ powerButton.state = "pressed";
+ }
+ states: [
+ State {
+ name: "pressed"
+ when: powerButton.down
+
+ PropertyChanges {
+ target: powerButtonBg
+ color: Qt.darker(config.PowerButtonBg, 1.2)
+ }
+
+ },
+ State {
+ name: "hovered"
+ when: powerButton.hovered
+
+ PropertyChanges {
+ target: powerButtonBg
+ color: Qt.darker(config.PowerButtonBg, 1.2)
+ }
+
+ },
+ State {
+ name: "selection"
+ when: powerPopup.visible
+
+ PropertyChanges {
+ target: powerButtonBg
+ color: Qt.darker(config.PowerButtonBg, 1.2)
+ }
+
+ }
+ ]
+
+ background: Rectangle {
+ id: powerButtonBg
+
+ color: config.PowerButtonBg
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ Popup {
+ id: powerPopup
+
+ height: inputHeight * 2.2 + padding * 2
+ x: powerButton.width + powerList.spacing
+ y: -height + powerButton.height
+ padding: 15
+
+ background: Rectangle {
+ radius: config.Radius * 1.8
+ color: config.PopupBackground
+ }
+
+ contentItem: ListView {
+ id: powerList
+
+ implicitWidth: contentWidth
+ spacing: 8
+ orientation: Qt.Horizontal
+ clip: true
+ model: powerModel
+
+ delegate: ItemDelegate {
+ id: powerEntry
+
+ height: inputHeight * 2.2
+ width: inputHeight * 2.2
+ display: AbstractButton.TextUnderIcon
+ states: [
+ State {
+ name: "hovered"
+ when: powerEntry.hovered
+
+ PropertyChanges {
+ target: powerEntryBg
+ color: Qt.darker(config.PopupHighlight, 1.2)
+ }
+
+ PropertyChanges {
+ target: iconOverlay
+ color: Qt.darker(config.PopupHighlight, 1.2)
+ }
+
+ PropertyChanges {
+ target: powerText
+ opacity: 1
+ }
+
+ }
+ ]
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ powerPopup.close();
+ index == 0 ? sddm.suspend() : (index == 1 ? sddm.reboot() : sddm.powerOff());
+ }
+ }
+
+ contentItem: Item {
+ Image {
+ id: powerIcon
+
+ anchors.centerIn: parent
+ source: index == 0 ? Qt.resolvedUrl("../icons/sleep.svg") : (index == 1 ? Qt.resolvedUrl("../icons/restart.svg") : Qt.resolvedUrl("../icons/power.svg"))
+ sourceSize: Qt.size(powerEntry.width * 0.5, powerEntry.height * 0.5)
+ }
+
+ ColorOverlay {
+ id: iconOverlay
+
+ anchors.fill: powerIcon
+ source: powerIcon
+ color: config.PopupBackground
+ }
+
+ Text {
+ id: powerText
+
+ anchors.centerIn: parent
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ color: config.PopupBackground
+ text: name
+ opacity: 0
+ }
+
+ }
+
+ background: Rectangle {
+ id: powerEntryBg
+
+ color: config.PopupHighlight
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color, opacity"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ }
+
+ enter: Transition {
+ ParallelAnimation {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 400
+ easing.type: Easing.OutExpo
+ }
+
+ NumberAnimation {
+ property: "x"
+ from: powerPopup.x - (inputWidth * 0.1)
+ to: powerPopup.x
+ duration: 500
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+ exit: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: 300
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+}
diff --git a/parch/components/SessionPanel.qml b/parch/components/SessionPanel.qml
new file mode 100644
index 0000000..190590f
--- /dev/null
+++ b/parch/components/SessionPanel.qml
@@ -0,0 +1,195 @@
+import QtQml.Models 2.12
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Item {
+ property var session: sessionList.currentIndex
+
+ implicitHeight: sessionButton.height
+ implicitWidth: sessionButton.width
+
+ DelegateModel {
+ id: sessionWrapper
+
+ model: sessionModel
+
+ delegate: ItemDelegate {
+ id: sessionEntry
+
+ height: inputHeight
+ width: parent.width
+ highlighted: sessionList.currentIndex == index
+ states: [
+ State {
+ name: "hovered"
+ when: sessionEntry.hovered
+
+ PropertyChanges {
+ target: sessionEntryBg
+ color: highlighted ? Qt.darker(config.PopupHighlight, 1.2) : Qt.darker(config.PopupBackground, 1.2)
+ }
+
+ }
+ ]
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ sessionList.currentIndex = index;
+ sessionPopup.close();
+ }
+ }
+
+ contentItem: Text {
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: highlighted ? config.PopupHighlightText : config.PopupHighlight
+ text: name
+ }
+
+ background: Rectangle {
+ id: sessionEntryBg
+
+ color: highlighted ? config.PopupHighlight : config.PopupBackground
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ property: "color"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ }
+
+ Button {
+ id: sessionButton
+
+ height: inputHeight
+ width: inputHeight
+ hoverEnabled: true
+ icon.source: Qt.resolvedUrl("../icons/settings.svg")
+ icon.height: height * 0.6
+ icon.width: width * 0.6
+ icon.color: config.SessionIconColor
+ onClicked: {
+ sessionPopup.visible ? sessionPopup.close() : sessionPopup.open();
+ sessionButton.state = "pressed";
+ }
+ states: [
+ State {
+ name: "pressed"
+ when: sessionButton.down
+
+ PropertyChanges {
+ target: sessionButtonBg
+ color: Qt.darker(config.SessionButtonBg, 1.2)
+ }
+
+ },
+ State {
+ name: "hovered"
+ when: sessionButton.hovered
+
+ PropertyChanges {
+ target: sessionButtonBg
+ color: Qt.darker(config.SessionButtonBg, 1.2)
+ }
+
+ },
+ State {
+ name: "selection"
+ when: sessionPopup.visible
+
+ PropertyChanges {
+ target: sessionButtonBg
+ color: Qt.darker(config.SessionButtonBg, 1.2)
+ }
+
+ }
+ ]
+
+ background: Rectangle {
+ id: sessionButtonBg
+
+ color: config.SessionButtonBg
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ Popup {
+ id: sessionPopup
+
+ width: inputWidth + padding * 2
+ x: sessionButton.width + sessionList.spacing
+ y: -(contentHeight + padding * 2) + sessionButton.height
+ padding: 15
+
+ background: Rectangle {
+ radius: config.Radius * 1.8
+ color: config.PopupBackground
+ }
+
+ contentItem: ListView {
+ id: sessionList
+
+ implicitHeight: contentHeight
+ spacing: 8
+ model: sessionWrapper
+ currentIndex: sessionModel.lastIndex
+ clip: true
+ }
+
+ enter: Transition {
+ ParallelAnimation {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 400
+ easing.type: Easing.OutExpo
+ }
+
+ NumberAnimation {
+ property: "x"
+ from: sessionPopup.x - (inputWidth * 0.1)
+ to: sessionPopup.x
+ duration: 500
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+ exit: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: 300
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+}
diff --git a/parch/components/UserFieldPanel.qml b/parch/components/UserFieldPanel.qml
new file mode 100644
index 0000000..2644f80
--- /dev/null
+++ b/parch/components/UserFieldPanel.qml
@@ -0,0 +1,62 @@
+import QtGraphicalEffects 1.12
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+TextField {
+ id: usernameField
+
+ height: inputHeight
+ width: inputWidth
+ selectByMouse: true
+ echoMode: TextInput.Normal
+ selectionColor: config.FieldText
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ color: config.FieldText
+ horizontalAlignment: Text.AlignHCenter
+ placeholderText: config.UserFieldBgText
+ text: userModel.lastUser
+ states: [
+ State {
+ name: "focused"
+ when: usernameField.activeFocus
+
+ PropertyChanges {
+ target: userFieldBackground
+ color: Qt.darker(config.FieldBackground, 1.2)
+ border.width: config.FieldBorderWidth
+ }
+
+ },
+ State {
+ name: "hovered"
+ when: usernameField.hovered
+
+ PropertyChanges {
+ target: userFieldBackground
+ color: Qt.darker(config.FieldBackground, 1.2)
+ }
+
+ }
+ ]
+
+ background: Rectangle {
+ id: userFieldBackground
+
+ color: config.FieldBackground
+ border.color: config.FieldBorderColor
+ border.width: 0
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "color, border.width"
+ duration: 150
+ }
+
+ }
+
+}
diff --git a/parch/components/UserPanel.qml b/parch/components/UserPanel.qml
new file mode 100644
index 0000000..73dec16
--- /dev/null
+++ b/parch/components/UserPanel.qml
@@ -0,0 +1,323 @@
+import QtGraphicalEffects 1.12
+import QtQml.Models 2.12
+import QtQuick 2.12
+import QtQuick.Controls 2.12
+
+Column {
+ property var username: usernameField.text
+
+ spacing: 30
+ Component.onCompleted: userPicture.source = userWrapper.items.get(userList.currentIndex).model.icon
+
+ DelegateModel {
+ id: userWrapper
+
+ model: userModel
+
+ delegate: ItemDelegate {
+ id: userEntry
+
+ height: inputHeight
+ width: parent.width
+ highlighted: userList.currentIndex == index
+ states: [
+ State {
+ name: "hovered"
+ when: userEntry.hovered
+
+ PropertyChanges {
+ target: userEntryBg
+ color: highlighted ? Qt.darker(config.PopupHighlight, 1.2) : Qt.darker(config.PopupBackground, 1.2)
+ }
+
+ }
+ ]
+
+ MouseArea {
+ anchors.fill: parent
+ onClicked: {
+ userList.currentIndex = index;
+ usernameField.text = userWrapper.items.get(index).model.name;
+ userPicture.source = userWrapper.items.get(index).model.icon;
+ userPopup.close();
+ }
+ }
+
+ contentItem: Text {
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: highlighted ? config.PopupHighlightText : config.PopupHighlight
+ text: name
+ }
+
+ background: Rectangle {
+ id: userEntryBg
+
+ color: highlighted ? config.PopupHighlight : config.PopupBackground
+ radius: config.Radius
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ property: "color"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ }
+
+ Popup {
+ id: userPopup
+
+ width: inputWidth
+ padding: 15
+
+ background: Rectangle {
+ radius: config.Radius * 1.8
+ color: config.PopupBackground
+ }
+
+ contentItem: ListView {
+ id: userList
+
+ implicitHeight: contentHeight
+ spacing: 8
+ model: userWrapper
+ currentIndex: userModel.lastIndex
+ clip: true
+ }
+
+ enter: Transition {
+ ParallelAnimation {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 400
+ easing.type: Easing.OutExpo
+ }
+
+ NumberAnimation {
+ property: "y"
+ from: (inputWidth / 3) - userPopup.padding - (inputHeight * userList.count * 0.5) - (userList.spacing * (userList.count - 1) * 0.5) + (inputWidth * 0.1)
+ to: (inputWidth / 3) - userPopup.padding - (inputHeight * userList.count * 0.5) - (userList.spacing * (userList.count - 1) * 0.5)
+ duration: 500
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+ exit: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: 300
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+ Item {
+ width: inputWidth
+ implicitHeight: pictureBorder.height
+
+ Rectangle {
+ id: pictureBorder
+
+ anchors.centerIn: userPicture
+ height: inputWidth / 1.5 + (border.width * 2)
+ width: inputWidth / 1.5 + (border.width * 2)
+ radius: height / 2
+ border.width: config.UAPBorderWidth
+ border.color: config.UAPBorderColor
+ color: config.UAPColor
+ states: [
+ State {
+ name: "pressed"
+
+ PropertyChanges {
+ target: pictureBorder
+ border.color: Qt.darker(config.UAPBorderColor, 1.2)
+ color: Qt.darker(config.UAPColor, 1.2)
+ }
+
+ },
+ State {
+ name: "hovered"
+
+ PropertyChanges {
+ target: pictureBorder
+ border.color: Qt.darker(config.UAPBorderColor, 1.4)
+ color: Qt.darker(config.UAPColor, 1.4)
+ }
+
+ },
+ State {
+ name: "unhovered"
+
+ PropertyChanges {
+ target: pictureBorder
+ border.color: config.UAPBorderColor
+ color: config.UAPColor
+ }
+
+ }
+ ]
+
+ MouseArea {
+ id: roundMouseArea
+
+ anchors.fill: parent
+ hoverEnabled: true
+ onClicked: userPopup.open()
+ onHoveredChanged: {
+ if (containsMouse)
+ pictureBorder.state = "hovered";
+ else
+ pictureBorder.state = "unhovered";
+ }
+ onPressedChanged: {
+ if (containsPress)
+ pictureBorder.state = "pressed";
+ else if (containsMouse)
+ pictureBorder.state = "hovered";
+ else
+ pictureBorder.state = "unhovered";
+ }
+ }
+
+ transitions: Transition {
+ PropertyAnimation {
+ properties: "border.color, color"
+ duration: 150
+ }
+
+ }
+
+ }
+
+ Image {
+ id: userPicture
+
+ source: ""
+ height: inputWidth / 1.5
+ width: inputWidth / 1.5
+ anchors.horizontalCenter: parent.horizontalCenter
+ fillMode: Image.PreserveAspectCrop
+ layer.enabled: true
+
+ Rectangle {
+ id: mask
+
+ anchors.fill: parent
+ radius: inputWidth / 3
+ visible: false
+ }
+
+ layer.effect: OpacityMask {
+ maskSource: mask
+ }
+
+ }
+
+ Popup {
+ id: incorrectPopup
+
+ height: incorrectText.paintedHeight * 2
+ width: inputWidth
+ y: (pictureBorder.height - height) / 2
+ onOpened: incorrectTimer.start()
+
+ Timer {
+ id: incorrectTimer
+
+ interval: 3000
+ onTriggered: incorrectPopup.close()
+ }
+
+ background: Rectangle {
+ radius: config.Radius
+ color: config.PopupBackground
+ }
+
+ contentItem: Text {
+ id: incorrectText
+
+ renderType: Text.NativeRendering
+ font.family: config.Font
+ font.pointSize: config.FontSize
+ font.bold: true
+ horizontalAlignment: Text.AlignHCenter
+ verticalAlignment: Text.AlignVCenter
+ color: config.PopupHighlight
+ text: "Incorrect username\nor password!"
+ }
+
+ enter: Transition {
+ ParallelAnimation {
+ NumberAnimation {
+ property: "opacity"
+ from: 0
+ to: 1
+ duration: 400
+ easing.type: Easing.OutExpo
+ }
+
+ NumberAnimation {
+ property: "x"
+ from: incorrectPopup.x - (inputWidth * 0.1)
+ to: incorrectPopup.x
+ duration: 500
+ easing.type: Easing.OutElastic
+ }
+
+ }
+
+ }
+
+ exit: Transition {
+ NumberAnimation {
+ property: "opacity"
+ from: 1
+ to: 0
+ duration: 300
+ easing.type: Easing.OutExpo
+ }
+
+ }
+
+ }
+
+ }
+
+ UserFieldPanel {
+ id: usernameField
+
+ height: inputHeight
+ width: inputWidth
+ }
+
+ Connections {
+ function onLoginSucceeded() {
+ }
+
+ function onLoginFailed() {
+ incorrectPopup.open();
+ }
+
+ target: sddm
+ }
+
+}
diff --git a/parch/icons/power.svg b/parch/icons/power.svg
new file mode 100644
index 0000000..a45c9d1
--- /dev/null
+++ b/parch/icons/power.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/parch/icons/restart.svg b/parch/icons/restart.svg
new file mode 100644
index 0000000..2ff075a
--- /dev/null
+++ b/parch/icons/restart.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/parch/icons/settings.svg b/parch/icons/settings.svg
new file mode 100644
index 0000000..1813987
--- /dev/null
+++ b/parch/icons/settings.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/parch/icons/sleep.svg b/parch/icons/sleep.svg
new file mode 100644
index 0000000..a538384
--- /dev/null
+++ b/parch/icons/sleep.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/parch/theme.conf b/parch/theme.conf
new file mode 100644
index 0000000..c81af97
--- /dev/null
+++ b/parch/theme.conf
@@ -0,0 +1,128 @@
+[General]
+
+### GENERAL
+#
+# Background: wallpaper path, absolute or relative. can be placed
+# in the backgrounds/ folder (as seen below) for extra convenience.
+#
+# Font: please name the font family!
+#
+# FontSize: this size is used for everything *except* the date and time.
+#
+# Padding: specify how far stuff should be from the screen edges.
+#
+# Radius: set to 0 to disable rounded corners on UI elements.
+#
+# UIScale: if UI elements are too big or small, try to adjust this value.
+# you should probably keep it below 1, though.
+
+Background="backgrounds/parch.png"
+Font="Atkinson Hyperlegible"
+FontSize="9"
+Padding="50"
+Radius="5"
+UIScale="0.175"
+
+### USER ACCOUNT PICTURE (UAP)
+#
+# UAPBorderWidth: set to 0 to disable the border around the picture.
+#
+# UAPBorderColor: remember to include the "#" when specifying color! will not
+# show when border width is 0.
+#
+# UAPColor: color of the default, blank avatar. only visible if you
+# don't have your own picture.
+
+UAPBorderWidth="5"
+UAPBorderColor="#b4befe"
+UAPColor="#cdd6f4"
+
+### USERNAME, PASSWORD FIELDS
+#
+# FieldBackground: background color of the input fields.
+#
+# FieldText: color of the typed text.
+#
+# FieldBorderWidth: border width of the currently selected field. set to 0
+# to disable the border.
+#
+# FieldBorderColor: border color of selected field. not visible if width is 0.
+#
+# UserFieldBgText: placeholder text shown when user field is empty.
+#
+# PassFieldBgText: placeholder text shown when password field is empty.
+
+FieldBackground="#1e1e2e"
+FieldText="#cdd6f4"
+FieldBorderWidth="3"
+FieldBorderColor="#b4befe"
+UserFieldBgText="enter username"
+PassFieldBgText="enter password"
+
+### LOGIN BUTTON
+#
+# LoginButtonTextColor: text color of login button.
+#
+# LoginButtonText: text displayed on the login button.
+#
+# LoginButtonBg: login button background color.
+
+LoginButtonTextColor="#1e1e2e"
+LoginButtonText="Login!!"
+LoginButtonBg="#b4befe"
+
+### POWER, SESSION, USER SELECTION POPUPS
+#
+# PopupBackground: background color of popup window.
+#
+# PopupHighlight: color of the currently selected entry.
+#
+# PopupHighlightText: text color inside the currently selected entry. this is
+# provided in case the highlight clashes with the text.
+
+PopupBackground="#b4befe"
+PopupHighlight="#1e1e2e"
+PopupHighlightText="#cdd6f4"
+
+### SESSION, POWER BUTTONS
+#
+# SessionButtonBg: session button background color.
+#
+# SessionIconColor: session icon color inside session button.
+#
+# PowerButtonBg: power button background color.
+#
+# PowerIconColor: power icon color inside power button.
+
+SessionButtonBg="#b4befe"
+SessionIconColor="#1e1e2e"
+PowerButtonBg="#b4befe"
+PowerIconColor="#1e1e2e"
+
+### DATE, TIME
+#
+# DateColor: date text color.
+#
+# DateSize: font size for the date.
+#
+# DateIsBold: whether date text should be bolded. accepts "true" or "false"
+#
+# DateOpacity: date text opacity value between 0.0 (completely transparent)
+# and 1.0 (fully solid).
+#
+# DateFormat: here, you can create a custom date format.
+#
+# of course, equivalent options exist for the time.
+
+DateColor="#cdd6f4"
+DateSize="36"
+DateIsBold="false"
+DateOpacity="1.0"
+DateFormat="dddd, MMMM d"
+
+TimeColor="#cdd6f4"
+TimeSize="48"
+TimeIsBold="true"
+TimeOpacity="1.0"
+TimeFormat="hh:mm AP"
+