diff options
Diffstat (limited to 'theme/Main.qml')
| -rw-r--r-- | theme/Main.qml | 126 |
1 files changed, 126 insertions, 0 deletions
diff --git a/theme/Main.qml b/theme/Main.qml new file mode 100644 index 0000000..f1b91ee --- /dev/null +++ b/theme/Main.qml @@ -0,0 +1,126 @@ +// SPDX-License-Identifier: GPL-2.0-only +// Copyright (C) 2026 Danilo M. <danix@danix.xyz> +// +// UDT SDDM greeter. The root item spans every screen. The wallpaper is drawn +// once per screen; the login card is pinned to one screen (config uiScreen, +// else the primary screen); input wakes the card after it fades. + +import QtQuick + +Item { + id: root + + // SDDM sizes the greeter to the union of every screen. + readonly property var union: screenModel.geometry(-1) + width: union.width + height: union.height + + // Palette. Values come from theme.conf, overridden live by theme.conf.user. + readonly property string baseColor: config.stringValue("base") + readonly property int cardRadius: 10 + readonly property string wallpaperUrl: + config.stringValue("background") === "" + ? "" + : "file://" + config.stringValue("background") + + readonly property string wantedScreen: config.stringValue("uiScreen") + property var primaryGeometry: screenModel.geometry(screenModel.primary >= 0 ? screenModel.primary : 0) + property var namedGeometry: primaryGeometry + readonly property var cardGeometry: wantedScreen === "" ? primaryGeometry : namedGeometry + + // The Image delegate of the screen the card sits on. The frosted card + // samples it. Null until the model has populated. + property Item cardScreenImage: null + + Repeater { + id: screens + model: screenModel + delegate: Item { + x: geometry.x + y: geometry.y + width: geometry.width + height: geometry.height + + Image { + id: screenImage + anchors.fill: parent + source: root.wallpaperUrl + fillMode: Image.PreserveAspectCrop + sourceSize.width: parent.width + sourceSize.height: parent.height + smooth: true + asynchronous: true + } + + // Scrim so the card and text read on any wallpaper. + Rectangle { + anchors.fill: parent + color: root.baseColor + opacity: 0.45 + } + + readonly property string screenName: name + + Component.onCompleted: root.registerScreen(index, screenName, screenImage, geometry) + } + } + + // Filled as the model populates: the primary screen's name, and the + // wallpaper image plus geometry of whichever screen owns the card. + property string primaryName: "" + + function registerScreen(index, screenName, image, geom) { + if (index === screenModel.primary) + root.primaryName = screenName + if (screenName === root.cardScreenName()) { + root.cardScreenImage = image + root.namedGeometry = geom + } + } + + function cardScreenName() { + return root.wantedScreen !== "" ? root.wantedScreen : root.primaryName + } + + // Idle: fade the card after fadeoutMs, restore on any input. + property bool uiVisible: true + + Timer { + id: idle + interval: config.intValue("fadeoutMs") > 0 ? config.intValue("fadeoutMs") : 60000 + running: root.uiVisible + repeat: false + onTriggered: root.uiVisible = false + } + + function wake() { + root.uiVisible = true + idle.restart() + } + + MouseArea { + anchors.fill: parent + hoverEnabled: true + onPressed: root.wake() + onPositionChanged: root.wake() + } + + Keys.onPressed: event => { + root.wake() + event.accepted = false + } + + // Placeholder card. Task 2 replaces this with components/LoginCard.qml. + Rectangle { + id: card + x: root.cardGeometry.x + (root.cardGeometry.width - width) / 2 + y: root.cardGeometry.y + (root.cardGeometry.height - height) / 2 + width: 380 + height: 220 + radius: root.cardRadius + color: config.stringValue("mantle") + opacity: root.uiVisible ? 0.9 : 0 + visible: opacity > 0 + Behavior on opacity { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } } + } +} |
