Skip to content

Commit 2fa0ee1

Browse files
derrylclaude
andcommitted
Redesign login form and eliminate preview/theme duplication
LoginForm: remove username field (uses defaultUsername directly), add password + small icon login button in a horizontal row, restyle password field with dark slate inset background. Main.qml: move PowerBar from footer to directly below login form, centered horizontally. Preview architecture: inject mock SDDM context properties (sddm, config, userModel, sessionModel, keyboard) from Python via setContextProperty(), matching how the real SDDM greeter works. This lets the Loader load the theme's Main.qml directly — eliminating ThemeLayout.qml and the duplicated layout logic that had to be kept in sync. - preview-host.py: add MockSddm, MockConfig (reads theme.conf), MockUserModel, MockSessionModel, MockKeyboard classes - Preview.qml: reduced to thin window shell + Loader + overlay - preview.sh: also symlinks Main.qml, passes theme dir to Python host - lint-qml.sh: use Python host for runtime type-error check - Delete preview/ThemeLayout.qml 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent b3661c9 commit 2fa0ee1

8 files changed

Lines changed: 481 additions & 390 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# Symlinks created by preview/lint scripts at runtime
2+
preview/Main.qml
23
preview/components
34
preview/assets
45
preview/.reload-signal

preview/Preview.qml

Lines changed: 14 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,13 @@
11
import QtQuick
22
import QtQuick.Window
3-
import QtQuick.Layouts
4-
import QtQuick.Controls
53

64
/*
75
* Preview.qml - Development harness for the SDDM theme.
86
*
9-
* Provides mock objects in place of the SDDM context properties, then loads
10-
* the theme layout through a Loader. A file-watcher timer detects changes
11-
* (via a signal file touched by entr) and reloads the Loader in-place,
12-
* keeping the window open for a true live-preview experience.
7+
* A thin window shell that loads the theme's Main.qml via a Loader.
8+
* Mock SDDM context properties (sddm, config, userModel, sessionModel,
9+
* keyboard) are injected by preview-host.py as engine context properties,
10+
* so Main.qml works identically here and under the real SDDM greeter.
1311
*
1412
* Usage: ./scripts/preview.sh [-theme <name>]
1513
*/
@@ -21,113 +19,26 @@ Window {
2119
visible: true
2220
title: "KDE Lockscreen Builder — Preview"
2321

24-
// ═══════════════════════════════════════════════════════════════
25-
// Mock objects
26-
// ═══════════════════════════════════════════════════════════════
27-
28-
property QtObject mockConfig: QtObject {
29-
property string background: Qt.resolvedUrl("assets/background.jpg")
30-
property string type: "image"
31-
property string color: "#1a1a2e"
32-
property string primaryColor: "#ffffff"
33-
property string accentColor: "#4a9eff"
34-
property string backgroundOverlayColor: "#000000"
35-
property real backgroundOverlayOpacity: 0.3
36-
property string fontFamily: ""
37-
property int fontPointSize: 12
38-
property string clockVisible: "true"
39-
property string clockFormat: "hh:mm"
40-
property string dateFormat: "dddd, MMMM d"
41-
property int screenWidth: previewWindow.width
42-
property int screenHeight: previewWindow.height
43-
}
44-
45-
property QtObject mockSddm: QtObject {
46-
property string hostname: "preview-host"
47-
property bool canPowerOff: true
48-
property bool canReboot: true
49-
property bool canSuspend: true
50-
property bool canHibernate: true
51-
property bool canHybridSleep: false
52-
53-
signal loginFailed()
54-
signal loginSucceeded()
55-
56-
function login(user, password, sessionIndex) {
57-
console.log("[mock] sddm.login:", user, "session:", sessionIndex)
58-
if (password === "test") {
59-
console.log("[mock] Login succeeded")
60-
loginSucceeded()
61-
} else {
62-
console.log("[mock] Login failed (use 'test' as password)")
63-
loginFailed()
64-
}
65-
}
66-
function powerOff() { console.log("[mock] sddm.powerOff()") }
67-
function reboot() { console.log("[mock] sddm.reboot()") }
68-
function suspend() { console.log("[mock] sddm.suspend()") }
69-
function hibernate() { console.log("[mock] sddm.hibernate()") }
70-
function hybridSleep() { console.log("[mock] sddm.hybridSleep()") }
71-
}
72-
73-
property ListModel mockUserModel: ListModel {
74-
property string lastUser: "user"
75-
property int lastIndex: 0
76-
property int disableAvatarsThreshold: 7
77-
property bool containsAllUsers: true
78-
79-
ListElement { name: "user"; realName: "User"; icon: ""; needsPassword: true }
80-
ListElement { name: "guest"; realName: "Guest User"; icon: ""; needsPassword: true }
81-
}
82-
83-
property ListModel mockSessionModel: ListModel {
84-
property int lastIndex: 0
85-
86-
ListElement { name: "Plasma (Wayland)"; comment: "" }
87-
ListElement { name: "Plasma (X11)"; comment: "" }
88-
ListElement { name: "GNOME"; comment: "" }
89-
}
90-
91-
property QtObject mockKeyboard: QtObject {
92-
property bool capsLock: false
93-
property bool numLock: true
94-
}
95-
96-
// ═══════════════════════════════════════════════════════════════
97-
// Derived properties (mirrors Main.qml logic)
98-
// ═══════════════════════════════════════════════════════════════
99-
100-
property string notificationMessage: ""
101-
property int baseFontSize: mockConfig.fontPointSize || 12
102-
property string fontFamily: mockConfig.fontFamily || ""
103-
property color primaryColor: mockConfig.primaryColor || "#ffffff"
104-
property color accentColor: mockConfig.accentColor || "#4a9eff"
22+
// Keep config.screenWidth/Height in sync with this window's dimensions
23+
onWidthChanged: config.screenWidth = width
24+
onHeightChanged: config.screenHeight = height
10525

10626
// ═══════════════════════════════════════════════════════════════
10727
// Hot-reload Loader
10828
// ═══════════════════════════════════════════════════════════════
10929

110-
// Hot-reload: `hotReloader` is a context property injected by
111-
// preview-host.py. Reload is a 3-phase process orchestrated by Python:
112-
// 1. unloadRequested → QML destroys all component instances
113-
// 2. Python clears the now-unreferenced component cache
114-
// 3. reloadRequested → QML reloads components from disk
115-
//
116-
// When hotReloader is not present (e.g. launched via plain qml6),
117-
// the theme loads once without hot-reload.
118-
11930
Loader {
12031
id: themeLoader
12132
anchors.fill: parent
12233

12334
onStatusChanged: {
12435
if (status === Loader.Error)
125-
console.log("[hot-reload] Error loading ThemeLayout.qml — check for syntax errors")
36+
console.log("[hot-reload] Error loading Main.qml — check for syntax errors")
12637
}
12738
}
12839

12940
function loadTheme() {
130-
themeLoader.setSource("ThemeLayout.qml", { "preview": previewWindow })
41+
themeLoader.setSource("Main.qml")
13142
}
13243

13344
function unloadTheme() {
@@ -140,7 +51,11 @@ Window {
14051
function onReloadRequested() { previewWindow.loadTheme() }
14152
}
14253

143-
Component.onCompleted: loadTheme()
54+
Component.onCompleted: {
55+
config.screenWidth = width
56+
config.screenHeight = height
57+
loadTheme()
58+
}
14459

14560
// ── Preview overlay ──────────────────────────────────────────
14661
Rectangle {

preview/ThemeLayout.qml

Lines changed: 0 additions & 148 deletions
This file was deleted.

scripts/lint-qml.sh

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,20 +122,23 @@ fi
122122
export QT_QUICK_CONTROLS_STYLE=Basic
123123

124124
STDERR_LOG=$(mktemp)
125-
trap 'rm -f "$STDERR_LOG" "$PROJECT_DIR/preview/components" "$PROJECT_DIR/preview/assets"' EXIT
125+
trap 'rm -f "$STDERR_LOG" "$PROJECT_DIR/preview/components" "$PROJECT_DIR/preview/assets" "$PROJECT_DIR/preview/Main.qml"' EXIT
126126

127127
# Test with the default theme (or the specified one).
128128
RUNTIME_THEME="${THEME_DIRS[0]}"
129129
RUNTIME_THEME_DIR="$PROJECT_DIR/$RUNTIME_THEME"
130-
# Symlink the theme's components and assets into preview/ so QML resolves them.
131-
ln -sfn "$RUNTIME_THEME_DIR/components" "$PROJECT_DIR/preview/components"
132-
ln -sfn "$RUNTIME_THEME_DIR/assets" "$PROJECT_DIR/preview/assets"
130+
# Symlink the theme's Main.qml, components, and assets into preview/ so QML resolves them.
131+
ln -sfn "$RUNTIME_THEME_DIR/Main.qml" "$PROJECT_DIR/preview/Main.qml"
132+
ln -sfn "$RUNTIME_THEME_DIR/components" "$PROJECT_DIR/preview/components"
133+
ln -sfn "$RUNTIME_THEME_DIR/assets" "$PROJECT_DIR/preview/assets"
133134

134135
# Run preview for 3 seconds under a virtual framebuffer, capture stderr.
136+
# Use the Python preview host so SDDM context properties are injected.
137+
PREVIEW_CMD="python3 $SCRIPT_DIR/preview-host.py $RUNTIME_THEME_DIR"
135138
if command -v xvfb-run &>/dev/null; then
136-
timeout 3 xvfb-run -a "$QML_RUNTIME" preview/Preview.qml 2>"$STDERR_LOG" || true
139+
timeout 3 xvfb-run -a $PREVIEW_CMD 2>"$STDERR_LOG" || true
137140
elif [ -n "${DISPLAY:-}" ] || [ -n "${WAYLAND_DISPLAY:-}" ]; then
138-
timeout 3 "$QML_RUNTIME" preview/Preview.qml 2>"$STDERR_LOG" || true
141+
timeout 3 $PREVIEW_CMD 2>"$STDERR_LOG" || true
139142
else
140143
echo "SKIP: no display and xvfb-run not available."
141144
echo ""

0 commit comments

Comments
 (0)