aboutsummaryrefslogtreecommitdiffstats
path: root/theme/Main.qml
diff options
context:
space:
mode:
authorDanilo M. <danix@danix.xyz>2026-09-17 13:26:07 +0200
committerDanilo M. <danix@danix.xyz>2026-09-17 13:26:07 +0200
commit96841b1cef108130bcf1ab21c34cf89b92f1b35c (patch)
tree29ecfe725930f6e27e72574d7a8d43172370da7b /theme/Main.qml
parent8f749d8b1fe5c2740d8829a78fbfbb3c739dade9 (diff)
downloadsddm-theme-udt-96841b1cef108130bcf1ab21c34cf89b92f1b35c.tar.gz
sddm-theme-udt-96841b1cef108130bcf1ab21c34cf89b92f1b35c.zip
fix: pin the login card per SDDM screen window
Diffstat (limited to 'theme/Main.qml')
-rw-r--r--theme/Main.qml118
1 files changed, 55 insertions, 63 deletions
diff --git a/theme/Main.qml b/theme/Main.qml
index e5c529f..a813d6c 100644
--- a/theme/Main.qml
+++ b/theme/Main.qml
@@ -1,9 +1,11 @@
// 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.
+// UDT SDDM greeter. SDDM creates one window per screen, each sized to its own
+// screen and carrying a one-row ScreenModel, so this root item is exactly one
+// screen. The wallpaper fills it; the login card draws only on the screen
+// named by config uiScreen, else on the primary screen. Input wakes the card
+// after it fades.
import QtQuick
import "components"
@@ -11,81 +13,68 @@ import "components"
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
+ // SDDM creates one window per screen and sizes it to that screen. The
+ // model holds exactly one row and geometry(index) ignores index, returning
+ // this window's own screen at origin (0,0).
+ readonly property var windowGeometry: screenModel.geometry(0)
+ width: windowGeometry.width
+ height: windowGeometry.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")
+ // The screen the card belongs on: config uiScreen if non-empty, else the
+ // primary screen. `primaryScreen` is a per-window context property SDDM
+ // sets true only on the window showing the primary screen.
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
+ property string thisScreenName: ""
+ readonly property bool cardScreen: wantedScreen === ""
+ ? primaryScreen
+ : (thisScreenName === wantedScreen)
+
+ // The wallpaper fills this window's single screen. The frosted card
+ // samples this image.
+ Image {
+ id: wallpaperImage
+ anchors.fill: parent
+ source: root.wallpaperUrl
+ fillMode: Image.PreserveAspectCrop
+ sourceSize.width: width
+ sourceSize.height: height
+ smooth: true
+ asynchronous: true
+ }
- // 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
+ // Scrim so the card and text read on any wallpaper.
+ Rectangle {
+ anchors.fill: parent
+ color: root.baseColor
+ opacity: 0.45
+ }
+ // Non-visual: reads the model's single row to learn this screen's name.
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)
+ visible: false
+ Component.onCompleted: root.thisScreenName = name
}
}
- // 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
+ // While faded the card is disabled, which clears its focus; take focus on
+ // the root so the first key press wakes the card instead of vanishing.
+ onUiVisibleChanged: {
+ if (!uiVisible)
+ root.forceActiveFocus()
+ }
+
Timer {
id: idle
interval: config.intValue("fadeoutMs") > 0 ? config.intValue("fadeoutMs") : 60000
@@ -95,7 +84,10 @@ Item {
}
function wake() {
- root.uiVisible = true
+ if (!root.uiVisible) {
+ root.uiVisible = true
+ card.restoreFocus()
+ }
idle.restart()
}
@@ -113,12 +105,12 @@ Item {
LoginCard {
id: card
- x: root.cardGeometry.x + (root.cardGeometry.width - width) / 2 + card.shakeOffset
- y: root.cardGeometry.y + (root.cardGeometry.height - height) / 2
- backdropSource: root.cardScreenImage
- visible: opacity > 0
+ anchors.centerIn: parent
+ visible: root.cardScreen
+ enabled: root.uiVisible
opacity: root.uiVisible ? 1 : 0
Behavior on opacity { NumberAnimation { duration: 260; easing.type: Easing.OutCubic } }
+ backdropSource: wallpaperImage
onWake: root.wake()
}
}