diff options
| author | Danilo M. <danix@danix.xyz> | 2026-09-17 14:37:07 +0200 |
|---|---|---|
| committer | Danilo M. <danix@danix.xyz> | 2026-09-17 14:37:07 +0200 |
| commit | 27ef16187a672acb96e8d930e0ab4bf893ad8f6a (patch) | |
| tree | 9ce5a7b3c697bf267db0569101b22dca2283ffe9 | |
| parent | 8ead58fb5baa8d781159cf443c0f7ffd68cb6775 (diff) | |
| download | sddm-theme-udt-27ef16187a672acb96e8d930e0ab4bf893ad8f6a.tar.gz sddm-theme-udt-27ef16187a672acb96e8d930e0ab4bf893ad8f6a.zip | |
fix: quote dateFormat and gate hidden credential field focus
Quote the comma-bearing dateFormat default; QSettings reads an
unquoted comma as a QStringList and SDDM's stringValue() then returns
the empty string, leaving the value inert. Note the caveat in the
README Panel section.
Set activeFocusOnTab per credential stage so Tab cannot reach the
offscreen, transparent field and silently append to the submitted
value. forceActiveFocus() ignores activeFocusOnTab, so focusStage()
still lands focus in each stage.
Apply the remaining review fixes: compare the PowerBar fallback as a
string, animate the back chevron width, give both opacity behaviours
an OutCubic easing, gate the clock timer on panel visibility, clamp
panelWidth to 1, and reword stale "card" prose to "panel".
| -rw-r--r-- | README.md | 6 | ||||
| -rw-r--r-- | theme/Main.qml | 18 | ||||
| -rw-r--r-- | theme/components/Backdrop.qml | 8 | ||||
| -rw-r--r-- | theme/components/Credentials.qml | 7 | ||||
| -rw-r--r-- | theme/components/Panel.qml | 2 | ||||
| -rw-r--r-- | theme/components/PowerBar.qml | 2 | ||||
| -rw-r--r-- | theme/theme.conf | 6 |
7 files changed, 29 insertions, 20 deletions
@@ -12,7 +12,7 @@ its colours from the UDT palette, including the wallpaper-snapped accent. SDDM merges into the theme config at every greeter start. Wallpaper and accent need no root re-run; only theme code changes do. -Set `uiScreen` in the root-owned `theme/theme.conf` to pin the login card to a +Set `uiScreen` in the root-owned `theme/theme.conf` to pin the login panel to a named screen; leave it empty for the primary screen. It does not belong in the generated `theme.conf.user`, which is overwritten on every wallpaper change. @@ -24,6 +24,10 @@ From top to bottom it shows the clock (`timeFormat`) and date (`dateFormat`), the `greeting`, the avatar, one credential slot, the power row, and the session selector at the bottom. +If you override `dateFormat`, quote any value containing a comma (for example +`dateFormat="dddd, d MMMM"`): SDDM reads the config through QSettings, which +treats an unquoted comma as a list, and the date then comes out empty. + The credential slot is staged: type the username, confirm, and it slides out to reveal the password; confirm again to log in. Esc or the back chevron returns to the username. diff --git a/theme/Main.qml b/theme/Main.qml index a5acac9..0a7ce3d 100644 --- a/theme/Main.qml +++ b/theme/Main.qml @@ -3,8 +3,8 @@ // // 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 +// screen. The wallpaper fills it; the login panel draws only on the screen +// named by config uiScreen, else on the primary screen. Input wakes the panel // after it fades. import QtQuick @@ -27,7 +27,7 @@ Item { ? "" : "file://" + config.stringValue("background") - // The screen the card belongs on: config uiScreen if non-empty, else the + // The screen the panel 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") @@ -36,7 +36,7 @@ Item { ? primaryScreen : (thisScreenName === wantedScreen) - // The wallpaper fills this window's single screen. The frosted card + // The wallpaper fills this window's single screen. The frosted panel // samples this image. Image { id: wallpaperImage @@ -49,7 +49,7 @@ Item { asynchronous: true } - // Scrim so the card and text read on any wallpaper. + // Scrim so the panel and text read on any wallpaper. Rectangle { anchors.fill: parent color: root.baseColor @@ -65,11 +65,11 @@ Item { } } - // Idle: fade the card after fadeoutMs, restore on any input. + // Idle: fade the panel 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. + // While faded the panel is disabled, which clears its focus; take focus on + // the root so the first key press wakes the panel instead of vanishing. onUiVisibleChanged: { if (!uiVisible) root.forceActiveFocus() @@ -107,7 +107,7 @@ Item { // key is missing or malformed. readonly property real panelWidth: { var v = parseFloat(config.stringValue("panelWidth")) - return v > 0 ? v : 0.30 + return v > 0 ? Math.min(v, 1) : 0.30 } Panel { diff --git a/theme/components/Backdrop.qml b/theme/components/Backdrop.qml index c13b766..de39bc2 100644 --- a/theme/components/Backdrop.qml +++ b/theme/components/Backdrop.qml @@ -1,9 +1,9 @@ // SPDX-License-Identifier: GPL-2.0-only // Copyright (C) 2026 Danilo M. <danix@danix.xyz> // -// A frosted panel. It snapshots the wallpaper image the card sits on, blurs it -// and masks it to the card's rounded rect, then tints it for text contrast. -// When there is no source, only the tint draws, so the card still reads. +// A frosted panel. It snapshots the wallpaper image the panel sits on, blurs it +// and masks it to the panel's rounded rect, then tints it for text contrast. +// When there is no source, only the tint draws, so the panel still reads. import QtQuick import QtQuick.Effects @@ -25,7 +25,7 @@ Item { if (backdrop.sourceItem === null) return Qt.rect(0, 0, 0, 0) // Guard reads: mapToItem() hides its reads in C++, so the binding - // would not otherwise re-run when the card or image moves (e.g. + // would not otherwise re-run when the panel or image moves (e.g. // the failed-login shake). Reading the coordinates registers them. var _guard = backdrop.x + backdrop.y + backdrop.parent.x + backdrop.parent.y + backdrop.sourceItem.x + backdrop.sourceItem.y diff --git a/theme/components/Credentials.qml b/theme/components/Credentials.qml index c2aba29..2bfbaf5 100644 --- a/theme/components/Credentials.qml +++ b/theme/components/Credentials.qml @@ -56,6 +56,7 @@ Item { height: 44 clip: true visible: width > 0 + Behavior on width { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } Rectangle { anchors.fill: parent @@ -122,6 +123,7 @@ Item { height: slot.height text: userModel.lastUser !== undefined ? userModel.lastUser : "" placeholderText: "Username" + activeFocusOnTab: !creds.passwordStage font.family: "Noto Sans" color: config.stringValue("text") palette.base: config.stringValue("surface0") @@ -135,7 +137,7 @@ Item { x: creds.passwordStage ? -slot.width : 0 opacity: creds.passwordStage ? 0 : 1 Behavior on x { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } - Behavior on opacity { NumberAnimation { duration: 220 } } + Behavior on opacity { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } onAccepted: creds.confirm() onTextChanged: creds.wake() Keys.onPressed: event => { @@ -150,6 +152,7 @@ Item { height: slot.height echoMode: TextInput.Password placeholderText: "Password" + activeFocusOnTab: creds.passwordStage font.family: "Noto Sans" color: config.stringValue("text") palette.base: config.stringValue("surface0") @@ -163,7 +166,7 @@ Item { x: creds.passwordStage ? 0 : slot.width opacity: creds.passwordStage ? 1 : 0 Behavior on x { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } - Behavior on opacity { NumberAnimation { duration: 220 } } + Behavior on opacity { NumberAnimation { duration: 220; easing.type: Easing.OutCubic } } onAccepted: creds.confirm() Keys.onPressed: event => { creds.wake() diff --git a/theme/components/Panel.qml b/theme/components/Panel.qml index 4df989b..eac2c58 100644 --- a/theme/components/Panel.qml +++ b/theme/components/Panel.qml @@ -36,7 +36,7 @@ Item { property date now: new Date() Timer { interval: 1000 - running: true + running: panel.visible repeat: true onTriggered: panel.now = new Date() } diff --git a/theme/components/PowerBar.qml b/theme/components/PowerBar.qml index 1ce3e76..3d6b2c5 100644 --- a/theme/components/PowerBar.qml +++ b/theme/components/PowerBar.qml @@ -49,7 +49,7 @@ Row { source: modelData.icon !== "" ? modelData.icon : modelData.fallback visible: false onStatusChanged: { - if (status === Image.Error && source !== modelData.fallback) + if (status === Image.Error && String(source) !== modelData.fallback) source = modelData.fallback } } diff --git a/theme/theme.conf b/theme/theme.conf index 2db45a1..33e6bd3 100644 --- a/theme/theme.conf +++ b/theme/theme.conf @@ -26,7 +26,7 @@ blue=#8aadf4 lavender=#b7bdf8 accent=#b7bdf8 -# Empty uiScreen pins the card to the primary screen. Set a screen name here, +# Empty uiScreen pins the panel to the primary screen. Set a screen name here, # in this root-owned file: theme.conf.user is regenerated on every wallpaper # change and is not the place for uiScreen. uiScreen= @@ -41,7 +41,9 @@ panelWidth=0.30 # Clock and greeting. Qt date/time format strings. The date follows the # greeter's locale, which may be C. timeFormat=HH:mm -dateFormat=dddd, d MMMM +# Quote any format containing a comma, here and in theme.conf.user: QSettings +# reads an unquoted comma as a list, and SDDM's stringValue() then returns "". +dateFormat="dddd, d MMMM" greeting=hello there # Power button icons. udt-accent writes absolute paths here from the icon theme |
