From 26d68e697c4807843cb837ad31f90bc30c200d6d Mon Sep 17 00:00:00 2001 From: Derryl Carter Date: Thu, 26 Feb 2026 16:16:00 -0800 Subject: [PATCH] Move theme files into themes/default/ and add -theme flag to scripts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Theme files (Main.qml, metadata.desktop, theme.conf, assets/, components/, faces/) now live under themes/default/ instead of the project root. This enables a multi-theme workflow where users copy themes/default/ to create new themes and preview them independently. Scripts accept an optional -theme flag (defaulting to "default"): ./scripts/preview.sh -theme foobar ./scripts/test-sddm.sh -theme foobar ./scripts/lint-qml.sh -theme foobar Preview and lint scripts create temporary symlinks (preview/components, preview/assets) pointing to the selected theme so QML relative imports resolve correctly. Symlinks are cleaned up on exit and gitignored. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .gitignore | 3 + preview/Preview.qml | 10 ++-- scripts/lint-qml.sh | 54 +++++++++++++++++- scripts/preview.sh | 49 +++++++++++++++- scripts/test-sddm.sh | 45 ++++++++++++++- Main.qml => themes/default/Main.qml | 0 .../default/assets}/background.jpg | Bin .../default/components}/Clock.qml | 0 .../default/components}/LoginForm.qml | 0 .../default/components}/PowerBar.qml | 0 .../default/components}/SessionSelector.qml | 0 .../default/metadata.desktop | 0 theme.conf => themes/default/theme.conf | 0 13 files changed, 150 insertions(+), 11 deletions(-) create mode 100644 .gitignore rename Main.qml => themes/default/Main.qml (100%) rename {assets => themes/default/assets}/background.jpg (100%) rename {components => themes/default/components}/Clock.qml (100%) rename {components => themes/default/components}/LoginForm.qml (100%) rename {components => themes/default/components}/PowerBar.qml (100%) rename {components => themes/default/components}/SessionSelector.qml (100%) rename metadata.desktop => themes/default/metadata.desktop (100%) rename theme.conf => themes/default/theme.conf (100%) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..03368e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +# Symlinks created by preview/lint scripts at runtime +preview/components +preview/assets diff --git a/preview/Preview.qml b/preview/Preview.qml index 7286446..ed86396 100644 --- a/preview/Preview.qml +++ b/preview/Preview.qml @@ -3,7 +3,7 @@ import QtQuick.Window import QtQuick.Layouts import QtQuick.Controls -import "../components" +import "components" /* * Preview.qml - Development harness for the SDDM theme. @@ -15,8 +15,10 @@ import "../components" * Components receive their dependencies as explicit properties, so they * work identically whether driven by SDDM or by this preview. * - * Usage: qml6 preview/Preview.qml - * or: qmlscene6 preview/Preview.qml + * The preview script symlinks components/ and assets/ into preview/ + * so that relative imports and asset paths resolve to the selected theme. + * + * Usage: ./scripts/preview.sh [-theme ] */ Window { @@ -34,7 +36,7 @@ Window { id: mockConfig // Set to "" to use the solid fallback color, or provide a path // to an image (e.g. drop your own into assets/background.jpg). - property string background: Qt.resolvedUrl("../assets/background.jpg") + property string background: Qt.resolvedUrl("assets/background.jpg") property string type: "image" property string color: "#1a1a2e" property string primaryColor: "#ffffff" diff --git a/scripts/lint-qml.sh b/scripts/lint-qml.sh index b969643..5f03881 100755 --- a/scripts/lint-qml.sh +++ b/scripts/lint-qml.sh @@ -5,7 +5,10 @@ # Runs qmllint (static) on all QML source files, then launches the preview # under xvfb for a few seconds to catch runtime type-assignment warnings. # -# Usage: ./scripts/lint-qml.sh +# Usage: ./scripts/lint-qml.sh [-theme ] +# +# Options: +# -theme Lint a specific theme (default: lint all themes) # # Exit codes: # 0 All checks passed @@ -18,6 +21,30 @@ PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" cd "$PROJECT_DIR" +# ── Parse arguments ───────────────────────────────────────────── +THEME_NAME="" + +while [[ $# -gt 0 ]]; do + case "$1" in + -theme) + THEME_NAME="${2:?Error: -theme requires a name}" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [-theme ]" + echo "" + echo "Options:" + echo " -theme Lint a specific theme (default: lint all themes)" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + echo "Usage: $0 [-theme ]" >&2 + exit 1 + ;; + esac +done + # ── Locate Qt 6 tools ──────────────────────────────────────────── # Binary names and paths vary across distros: # Arch: qmllint, qml6 (on PATH) @@ -41,13 +68,27 @@ QML_RUNTIME=$(find_tool qml6 qml) FAILED=0 +# Build list of theme directories to check. +if [[ -n "$THEME_NAME" ]]; then + THEME_DIRS=("themes/$THEME_NAME") + if [[ ! -d "${THEME_DIRS[0]}" ]]; then + echo "Error: theme directory not found: ${THEME_DIRS[0]}" >&2 + exit 1 + fi +else + THEME_DIRS=() + for d in themes/*/; do + [[ -d "$d" ]] && THEME_DIRS+=("${d%/}") + done +fi + # ── Static analysis with qmllint ────────────────────────────────── echo "=== qmllint: static analysis ===" if [[ -z "$QMLLINT" ]]; then echo "SKIP: qmllint not found." else - QML_FILES=$(find . -name '*.qml' -not -path './.git/*') + QML_FILES=$(find "${THEME_DIRS[@]}" preview -name '*.qml' -not -path './.git/*') echo "Checking: $QML_FILES" echo "" @@ -81,7 +122,14 @@ fi export QT_QUICK_CONTROLS_STYLE=Basic STDERR_LOG=$(mktemp) -trap 'rm -f "$STDERR_LOG"' EXIT +trap 'rm -f "$STDERR_LOG" "$PROJECT_DIR/preview/components" "$PROJECT_DIR/preview/assets"' EXIT + +# Test with the default theme (or the specified one). +RUNTIME_THEME="${THEME_DIRS[0]}" +RUNTIME_THEME_DIR="$PROJECT_DIR/$RUNTIME_THEME" +# Symlink the theme's components and assets into preview/ so QML resolves them. +ln -sfn "$RUNTIME_THEME_DIR/components" "$PROJECT_DIR/preview/components" +ln -sfn "$RUNTIME_THEME_DIR/assets" "$PROJECT_DIR/preview/assets" # Run preview for 3 seconds under a virtual framebuffer, capture stderr. if command -v xvfb-run &>/dev/null; then diff --git a/scripts/preview.sh b/scripts/preview.sh index d9c95e8..d8914e5 100755 --- a/scripts/preview.sh +++ b/scripts/preview.sh @@ -5,7 +5,10 @@ # Watches all QML, conf, and image files. On any change, kills the # running qml6 process and relaunches the Preview.qml harness. # -# Usage: ./scripts/preview.sh +# Usage: ./scripts/preview.sh [-theme ] +# +# Options: +# -theme Preview the theme in themes// (default: "default") # set -euo pipefail @@ -13,11 +16,49 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# ── Parse arguments ───────────────────────────────────────────── +THEME_NAME="default" + +while [[ $# -gt 0 ]]; do + case "$1" in + -theme) + THEME_NAME="${2:?Error: -theme requires a name}" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [-theme ]" + echo "" + echo "Options:" + echo " -theme Preview the theme in themes// (default: \"default\")" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + echo "Usage: $0 [-theme ]" >&2 + exit 1 + ;; + esac +done + +THEME_DIR="$PROJECT_DIR/themes/$THEME_NAME" + +if [[ ! -d "$THEME_DIR" ]]; then + echo "Error: theme directory not found: $THEME_DIR" >&2 + echo "" >&2 + echo "Available themes:" >&2 + for d in "$PROJECT_DIR"/themes/*/; do + [[ -d "$d" ]] && echo " $(basename "$d")" >&2 + done + exit 1 +fi + +# ── Setup ─────────────────────────────────────────────────────── ENTR_PID="" cleanup() { trap - SIGINT SIGTERM # prevent re-entry [[ -n "$ENTR_PID" ]] && kill "$ENTR_PID" 2>/dev/null && wait "$ENTR_PID" 2>/dev/null + rm -f "$PROJECT_DIR/preview/components" "$PROJECT_DIR/preview/assets" echo "" echo "Preview stopped." exit 0 @@ -29,8 +70,14 @@ trap cleanup SIGINT SIGTERM # unavailable outside a full Plasma session. export QT_QUICK_CONTROLS_STYLE=Basic +# Symlink the theme's components and assets into preview/ so QML's +# relative imports and asset paths resolve to the selected theme. +ln -sfn "$THEME_DIR/components" "$PROJECT_DIR/preview/components" +ln -sfn "$THEME_DIR/assets" "$PROJECT_DIR/preview/assets" + echo "=== KDE Lockscreen Builder — Live Preview ===" echo "Project: $PROJECT_DIR" +echo "Theme: $THEME_NAME ($THEME_DIR)" echo "Press Ctrl+C to stop" echo "" diff --git a/scripts/test-sddm.sh b/scripts/test-sddm.sh index e76702d..254e180 100755 --- a/scripts/test-sddm.sh +++ b/scripts/test-sddm.sh @@ -6,7 +6,10 @@ # project directory. Shows real user list, session list, and keyboard # state. Login/power actions won't actually execute in test mode. # -# Usage: ./scripts/test-sddm.sh +# Usage: ./scripts/test-sddm.sh [-theme ] +# +# Options: +# -theme Test the theme in themes// (default: "default") # set -euo pipefail @@ -14,8 +17,44 @@ set -euo pipefail SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)" +# ── Parse arguments ───────────────────────────────────────────── +THEME_NAME="default" + +while [[ $# -gt 0 ]]; do + case "$1" in + -theme) + THEME_NAME="${2:?Error: -theme requires a name}" + shift 2 + ;; + -h|--help) + echo "Usage: $0 [-theme ]" + echo "" + echo "Options:" + echo " -theme Test the theme in themes// (default: \"default\")" + exit 0 + ;; + *) + echo "Unknown option: $1" >&2 + echo "Usage: $0 [-theme ]" >&2 + exit 1 + ;; + esac +done + +THEME_DIR="$PROJECT_DIR/themes/$THEME_NAME" + +if [[ ! -d "$THEME_DIR" ]]; then + echo "Error: theme directory not found: $THEME_DIR" >&2 + echo "" >&2 + echo "Available themes:" >&2 + for d in "$PROJECT_DIR"/themes/*/; do + [[ -d "$d" ]] && echo " $(basename "$d")" >&2 + done + exit 1 +fi + echo "=== KDE Lockscreen Builder — SDDM Test Mode ===" -echo "Theme: $PROJECT_DIR" +echo "Theme: $THEME_NAME ($THEME_DIR)" echo "" -sddm-greeter-qt6 --test-mode --theme "$PROJECT_DIR" +sddm-greeter-qt6 --test-mode --theme "$THEME_DIR" diff --git a/Main.qml b/themes/default/Main.qml similarity index 100% rename from Main.qml rename to themes/default/Main.qml diff --git a/assets/background.jpg b/themes/default/assets/background.jpg similarity index 100% rename from assets/background.jpg rename to themes/default/assets/background.jpg diff --git a/components/Clock.qml b/themes/default/components/Clock.qml similarity index 100% rename from components/Clock.qml rename to themes/default/components/Clock.qml diff --git a/components/LoginForm.qml b/themes/default/components/LoginForm.qml similarity index 100% rename from components/LoginForm.qml rename to themes/default/components/LoginForm.qml diff --git a/components/PowerBar.qml b/themes/default/components/PowerBar.qml similarity index 100% rename from components/PowerBar.qml rename to themes/default/components/PowerBar.qml diff --git a/components/SessionSelector.qml b/themes/default/components/SessionSelector.qml similarity index 100% rename from components/SessionSelector.qml rename to themes/default/components/SessionSelector.qml diff --git a/metadata.desktop b/themes/default/metadata.desktop similarity index 100% rename from metadata.desktop rename to themes/default/metadata.desktop diff --git a/theme.conf b/themes/default/theme.conf similarity index 100% rename from theme.conf rename to themes/default/theme.conf