feat(sddm): add fws login theme for kde xfce and hyprland

Introduce a new SDDM theme "fws" designed for FWS with support for KDE, XFCE, and Hyprland desktop environments.

The theme is built using pure QtQuick and QtQuick.Controls (Basic style) with no additional dependencies beyond what SDDM already requires. It includes:

- Main.qml: Custom login screen with clock, date, user/password fields, session selector, and power controls
- metadata.desktop: Theme metadata and configuration
- theme.conf: Default configuration with customizable accent color and gradient colors

The design uses a dark theme with accent colors and supports capslock detection and login error handling.
This commit is contained in:
2026-07-04 00:00:05 +02:00
parent 5d6d1c6db8
commit 24370461ea
3 changed files with 273 additions and 0 deletions
@@ -0,0 +1,249 @@
// Thème SDDM « fws » — écran de connexion FWS (KDE / XFCE / Hyprland).
//
// VOLONTAIREMENT en pur QtQuick + QtQuick.Controls (style Basic) : ces
// modules arrivent avec qt6-declarative, dépendance de sddm lui-même →
// AUCUN paquet supplémentaire requis (pas de Breeze/plasma-workspace, pas
// de QtGraphicalEffects — supprimé en Qt6). Le greeter fournit en contexte
// les objets sddm, userModel, sessionModel, keyboard et config (theme.conf,
// surchargé par theme.conf.user écrit par le %post : accent selon le DE).
import QtQuick
import QtQuick.Controls.Basic
Rectangle {
id: root
width: 1280
height: 800
readonly property color accent: config.accent || "#3daee9"
readonly property color fieldBg: "#2a2a44"
gradient: Gradient {
GradientStop { position: 0.0; color: config.colorTop || "#1a1a2e" }
GradientStop { position: 1.0; color: config.colorBottom || "#16213e" }
}
Connections {
target: sddm
function onLoginFailed() {
errorLabel.text = "Échec de la connexion — vérifiez le mot de passe"
passwordField.text = ""
passwordField.forceActiveFocus()
}
function onLoginSucceeded() {
errorLabel.text = ""
}
}
function doLogin() {
errorLabel.text = ""
sddm.login(userField.text, passwordField.text, sessionBox.currentIndex)
}
// --- Horloge -----------------------------------------------------------
Timer {
interval: 1000
running: true
repeat: true
onTriggered: {
var now = new Date()
clockLabel.text = Qt.formatTime(now, "hh:mm")
dateLabel.text = Qt.formatDate(now, "dddd d MMMM yyyy")
}
Component.onCompleted: triggered()
}
Column {
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: parent.height * 0.10
spacing: 4
Text {
id: clockLabel
anchors.horizontalCenter: parent.horizontalCenter
color: "#f0f0f5"
font.pixelSize: 84
font.weight: Font.Light
}
Text {
id: dateLabel
anchors.horizontalCenter: parent.horizontalCenter
color: "#a0a0b8"
font.pixelSize: 20
font.capitalization: Font.Capitalize
}
}
// --- Carte de connexion --------------------------------------------------
Rectangle {
id: card
anchors.centerIn: parent
anchors.verticalCenterOffset: parent.height * 0.08
width: 400
height: cardContent.implicitHeight + 56
radius: 14
color: "#232338"
border.color: Qt.rgba(1, 1, 1, 0.10)
border.width: 1
Column {
id: cardContent
anchors.centerIn: parent
width: parent.width - 56
spacing: 14
Text {
anchors.horizontalCenter: parent.horizontalCenter
text: config.greeting || "Bienvenue sur FWS"
color: "#f0f0f5"
font.pixelSize: 22
font.weight: Font.Medium
}
Rectangle { width: 44; height: 3; radius: 1.5; color: accent
anchors.horizontalCenter: parent.horizontalCenter }
TextField {
id: userField
width: parent.width
text: userModel.lastUser
placeholderText: "Utilisateur"
placeholderTextColor: "#7d7d95"
color: "#f0f0f5"
font.pixelSize: 15
leftPadding: 14
background: Rectangle {
radius: 8
color: fieldBg
border.color: userField.activeFocus ? accent : "transparent"
border.width: 2
}
onAccepted: passwordField.forceActiveFocus()
}
TextField {
id: passwordField
width: parent.width
placeholderText: "Mot de passe"
placeholderTextColor: "#7d7d95"
color: "#f0f0f5"
font.pixelSize: 15
leftPadding: 14
echoMode: TextInput.Password
focus: true
background: Rectangle {
radius: 8
color: fieldBg
border.color: passwordField.activeFocus ? accent : "transparent"
border.width: 2
}
onAccepted: doLogin()
}
Text {
visible: keyboard.capsLock
anchors.horizontalCenter: parent.horizontalCenter
text: "⚠ Verrouillage majuscules activé"
color: "#e6c07b"
font.pixelSize: 13
}
Text {
id: errorLabel
width: parent.width
horizontalAlignment: Text.AlignHCenter
wrapMode: Text.WordWrap
color: "#e06c75"
font.pixelSize: 13
}
Button {
id: loginButton
width: parent.width
height: 44
onClicked: doLogin()
contentItem: Text {
text: "Se connecter"
color: "#ffffff"
font.pixelSize: 16
font.weight: Font.Medium
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 8
color: loginButton.down ? Qt.darker(accent, 1.25)
: loginButton.hovered ? Qt.lighter(accent, 1.10)
: accent
}
}
ComboBox {
id: sessionBox
width: parent.width
model: sessionModel
currentIndex: sessionModel.lastIndex
textRole: "name"
font.pixelSize: 13
contentItem: Text {
leftPadding: 14
text: sessionBox.displayText
color: "#a0a0b8"
font.pixelSize: 13
verticalAlignment: Text.AlignVCenter
}
background: Rectangle {
radius: 8
color: "transparent"
border.color: Qt.rgba(1, 1, 1, 0.15)
border.width: 1
}
}
}
}
// --- Boutons d'alimentation ----------------------------------------------
// PAS de Repeater avec des fonctions dans un modèle JS : en Qt6 elles ne
// survivent pas toujours à la conversion QVariant (clic silencieusement
// inopérant). Deux boutons explicites via un composant inline.
component PowerButton: Button {
id: powerButton
property string label: ""
height: 36
contentItem: Text {
text: powerButton.label
color: powerButton.hovered ? "#f0f0f5" : "#a0a0b8"
font.pixelSize: 14
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
leftPadding: 14
rightPadding: 14
}
background: Rectangle {
radius: 8
color: powerButton.hovered ? Qt.rgba(1, 1, 1, 0.08) : "transparent"
border.color: Qt.rgba(1, 1, 1, 0.15)
border.width: 1
}
}
Row {
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 24
spacing: 10
PowerButton { label: "Redémarrer"; onClicked: sddm.reboot() }
PowerButton { label: "Éteindre"; onClicked: sddm.powerOff() }
}
// Nom de la machine, discret, en bas à gauche.
Text {
anchors.left: parent.left
anchors.bottom: parent.bottom
anchors.margins: 24
text: sddm.hostName
color: "#5d5d78"
font.pixelSize: 13
}
}
@@ -0,0 +1,17 @@
[SddmGreeterTheme]
Name=FWS
Description=Écran de connexion FWS
Author=FWS
Copyright=FWS
License=GPL-2.0-or-later
Type=sddm-theme
Version=1.0
Website=
Screenshot=
MainScript=Main.qml
ConfigFile=theme.conf
TranslationsDirectory=
Email=
Theme-Id=fws
Theme-API=2.0
QtVersion=6
@@ -0,0 +1,7 @@
# Thème SDDM « fws » — valeurs par défaut, surchargées par theme.conf.user
# (écrit par le %post du kickstart : accent différent selon le bureau choisi).
[General]
accent=#3daee9
colorTop=#1a1a2e
colorBottom=#16213e
greeting=Bienvenue sur FWS