feat: aggiungi tema SDDM costruttivista
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Controls 2.15 as QQC2
|
||||
|
||||
// Selettore a tendina etichettato in stile costruttivista.
|
||||
Column {
|
||||
id: comboWrap
|
||||
property string label: ""
|
||||
property alias model: combo.model
|
||||
property alias textRole: combo.textRole
|
||||
property alias currentIndex: combo.currentIndex
|
||||
property alias currentText: combo.currentText
|
||||
signal activated(int index)
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: comboWrap.label
|
||||
color: "#f0dfba"
|
||||
font.pixelSize: 12
|
||||
font.bold: true
|
||||
font.letterSpacing: 2
|
||||
}
|
||||
|
||||
QQC2.ComboBox {
|
||||
id: combo
|
||||
width: comboWrap.width
|
||||
height: 40
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
onActivated: comboWrap.activated(index)
|
||||
|
||||
background: Rectangle {
|
||||
color: "#cc101012"
|
||||
border.color: combo.hovered ? "#d83a28" : "#66f0dfba"
|
||||
border.width: 1
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
leftPadding: 10
|
||||
rightPadding: 10
|
||||
text: combo.displayText
|
||||
color: "#f0dfba"
|
||||
font: combo.font
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
elide: Text.ElideRight
|
||||
}
|
||||
|
||||
indicator: Item { width: 0; height: 0 }
|
||||
|
||||
delegate: QQC2.ItemDelegate {
|
||||
width: combo.width
|
||||
height: 34
|
||||
highlighted: combo.highlightedIndex === index
|
||||
|
||||
contentItem: Text {
|
||||
text: combo.textRole ? model[combo.textRole] : modelData
|
||||
color: combo.highlightedIndex === index ? "#f6f1e5" : "#f0dfba"
|
||||
font: combo.font
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
leftPadding: 10
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: combo.highlightedIndex === index ? "#d83a28" : "#e6101012"
|
||||
}
|
||||
}
|
||||
|
||||
popup: QQC2.Popup {
|
||||
y: combo.height + 2
|
||||
width: combo.width
|
||||
implicitHeight: contentItem.implicitHeight
|
||||
padding: 1
|
||||
|
||||
contentItem: ListView {
|
||||
clip: true
|
||||
implicitHeight: contentHeight
|
||||
model: combo.popup.visible ? combo.delegateModel : null
|
||||
currentIndex: combo.highlightedIndex
|
||||
}
|
||||
|
||||
background: Rectangle {
|
||||
color: "#e6101012"
|
||||
border.color: "#66f0dfba"
|
||||
border.width: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
// Campo di input etichettato in stile costruttivista.
|
||||
Column {
|
||||
id: field
|
||||
property string label: ""
|
||||
property alias text: input.text
|
||||
property alias input: input
|
||||
property bool password: false
|
||||
property bool focusOnStart: false
|
||||
signal accepted()
|
||||
width: parent.width
|
||||
spacing: 4
|
||||
|
||||
Text {
|
||||
text: field.label
|
||||
color: "#f0dfba"
|
||||
font.pixelSize: 12
|
||||
font.bold: true
|
||||
font.letterSpacing: 2
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: field.width
|
||||
height: 40
|
||||
color: "#cc101012"
|
||||
border.color: input.activeFocus ? "#d83a28" : "#66f0dfba"
|
||||
border.width: input.activeFocus ? 2 : 1
|
||||
|
||||
Behavior on border.color { ColorAnimation { duration: 150 } }
|
||||
|
||||
TextInput {
|
||||
id: input
|
||||
anchors.fill: parent
|
||||
anchors.leftMargin: 10
|
||||
anchors.rightMargin: 10
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
color: "#f0dfba"
|
||||
font.pixelSize: 16
|
||||
font.bold: true
|
||||
echoMode: field.password ? TextInput.Password : TextInput.Normal
|
||||
selectByMouse: true
|
||||
focus: field.focusOnStart
|
||||
onAccepted: field.accepted()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,391 @@
|
||||
/*
|
||||
Costruttivista — tema SDDM
|
||||
Schermata di accesso in stile costruttivista sovietico per SDDM.
|
||||
Autosufficiente: solo QtQuick 2.15 e QtQuick.Controls 2.15, nessuna dipendenza esterna.
|
||||
*/
|
||||
|
||||
import QtQuick 2.15
|
||||
import QtQuick.Layouts 1.15
|
||||
|
||||
Rectangle {
|
||||
id: root
|
||||
color: "#101012"
|
||||
width: 1920
|
||||
height: 1080
|
||||
|
||||
readonly property color red: "#d83a28"
|
||||
readonly property color cream: "#f0dfba"
|
||||
readonly property color dark: "#181719"
|
||||
readonly property color panelBg: "#d9151517"
|
||||
readonly property color paper: "#f6f1e5"
|
||||
|
||||
// Utente selezionato dalla lista (quando il modello espone utenti).
|
||||
property string selectedUser: userModel.lastUser || ""
|
||||
|
||||
// Data in italiano senza dipendere dalla locale del greeter.
|
||||
function italianDate(d) {
|
||||
var days = ["DOMENICA", "LUNEDÌ", "MARTEDÌ", "MERCOLEDÌ", "GIOVEDÌ", "VENERDÌ", "SABATO"]
|
||||
var months = ["GENNAIO", "FEBBRAIO", "MARZO", "APRILE", "MAGGIO", "GIUGNO", "LUGLIO", "AGOSTO", "SETTEMBRE", "OTTOBRE", "NOVEMBRE", "DICEMBRE"]
|
||||
return days[d.getDay()] + " " + d.getDate() + " " + months[d.getMonth()] + " " + d.getFullYear()
|
||||
}
|
||||
|
||||
function tryLogin() {
|
||||
var username = userModel.count > 0 ? root.selectedUser : userField.text
|
||||
if (username.length === 0) {
|
||||
errorText.text = "INSERISCI L'UTENTE"
|
||||
shake.start()
|
||||
return
|
||||
}
|
||||
errorText.text = ""
|
||||
sddm.login(username, passField.text, sessionCombo.currentIndex)
|
||||
}
|
||||
|
||||
Item {
|
||||
id: ui
|
||||
anchors.fill: parent
|
||||
opacity: 0
|
||||
|
||||
// ---------- sfondo ----------
|
||||
Image {
|
||||
id: background
|
||||
anchors.fill: parent
|
||||
source: "assets/sfondo_costruttivista_fhd.png"
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
cache: true
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: "#12000000"
|
||||
opacity: overlayFade.value
|
||||
}
|
||||
|
||||
// ---------- accenti geometrici ----------
|
||||
Item {
|
||||
id: accents
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
id: redBar
|
||||
x: parent.width * 0.07
|
||||
y: parent.height * 0.15
|
||||
width: Math.max(5, parent.width * 0.008)
|
||||
height: parent.height * 0.27
|
||||
color: root.red
|
||||
opacity: 0.9
|
||||
transform: Translate { id: redShift; y: -root.height * 0.10 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: creamLine
|
||||
x: parent.width * 0.07
|
||||
y: parent.height * 0.44
|
||||
width: parent.width * 0.20
|
||||
height: Math.max(2, parent.height * 0.004)
|
||||
color: root.cream
|
||||
opacity: 0.85
|
||||
transform: Translate { id: lineShift; x: -root.width * 0.12 }
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
id: darkBar
|
||||
x: parent.width * 0.82
|
||||
y: parent.height * 0.60
|
||||
width: Math.max(4, parent.width * 0.005)
|
||||
height: parent.height * 0.20
|
||||
color: root.dark
|
||||
opacity: 0.75
|
||||
transform: Translate { id: darkShift; y: root.height * 0.09 }
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- orologio ----------
|
||||
Rectangle {
|
||||
id: clockBackdrop
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: Math.max(36, root.height * 0.055)
|
||||
width: clockBlock.width + Math.max(36, root.width * 0.02)
|
||||
height: clockBlock.height + Math.max(22, root.height * 0.022)
|
||||
color: "#66101012"
|
||||
border.color: "#33f0dfba"
|
||||
border.width: 1
|
||||
|
||||
Column {
|
||||
id: clockBlock
|
||||
anchors.centerIn: parent
|
||||
spacing: Math.max(2, root.height * 0.004)
|
||||
|
||||
Text {
|
||||
id: clockText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: Qt.formatTime(new Date(), "HH:mm")
|
||||
color: root.cream
|
||||
font.pixelSize: Math.max(44, Math.min(root.width * 0.05, 88))
|
||||
font.bold: true
|
||||
font.letterSpacing: 4
|
||||
}
|
||||
|
||||
Text {
|
||||
id: dateText
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: root.italianDate(new Date())
|
||||
color: "#ef6a55"
|
||||
font.pixelSize: Math.max(13, Math.min(root.width * 0.014, 22))
|
||||
font.bold: true
|
||||
font.letterSpacing: 2
|
||||
}
|
||||
|
||||
Timer {
|
||||
interval: 1000
|
||||
repeat: true
|
||||
running: true
|
||||
onTriggered: {
|
||||
clockText.text = Qt.formatTime(new Date(), "HH:mm")
|
||||
dateText.text = root.italianDate(new Date())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- pannello di accesso ----------
|
||||
Item {
|
||||
id: panelHost
|
||||
anchors.centerIn: parent
|
||||
width: panel.width
|
||||
height: panel.height
|
||||
transform: Translate { id: panelShift; x: 0 }
|
||||
|
||||
Rectangle {
|
||||
id: panel
|
||||
width: Math.min(root.width * 0.40, 500)
|
||||
height: panelCol.implicitHeight + Math.max(52, root.height * 0.056)
|
||||
color: root.panelBg
|
||||
border.color: "#66f0dfba"
|
||||
border.width: Math.max(1, root.height * 0.002)
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(8, panel.width * 0.02)
|
||||
anchors.left: parent.left
|
||||
anchors.top: parent.top
|
||||
anchors.bottom: parent.bottom
|
||||
color: root.red
|
||||
}
|
||||
|
||||
Column {
|
||||
id: panelCol
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
anchors.top: parent.top
|
||||
anchors.leftMargin: Math.max(26, root.height * 0.028)
|
||||
anchors.rightMargin: Math.max(26, root.height * 0.028)
|
||||
anchors.topMargin: Math.max(26, root.height * 0.028)
|
||||
spacing: Math.max(14, root.height * 0.016)
|
||||
|
||||
// intestazione: marca + titolo
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Math.max(16, root.width * 0.011)
|
||||
|
||||
Mark {
|
||||
Layout.preferredWidth: Math.max(58, Math.min(panel.width * 0.15, 96))
|
||||
Layout.preferredHeight: Layout.preferredWidth
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
}
|
||||
|
||||
Column {
|
||||
Layout.fillWidth: true
|
||||
Layout.alignment: Qt.AlignVCenter
|
||||
spacing: 2
|
||||
|
||||
Text {
|
||||
text: "ACCESSO"
|
||||
color: root.cream
|
||||
font.pixelSize: Math.max(20, Math.min(root.width * 0.024, 34))
|
||||
font.bold: true
|
||||
font.letterSpacing: 3
|
||||
}
|
||||
|
||||
Text {
|
||||
text: "INSERISCI LE CREDENZIALI"
|
||||
color: "#e85a48"
|
||||
font.pixelSize: Math.max(10, Math.min(root.width * 0.011, 15))
|
||||
font.bold: true
|
||||
font.letterSpacing: 1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lista utenti con avatar (solo se il modello espone utenti)
|
||||
Item {
|
||||
width: parent.width
|
||||
height: userListRow.height
|
||||
|
||||
UserList {
|
||||
id: userListRow
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
onUserSelected: root.selectedUser = name
|
||||
}
|
||||
}
|
||||
|
||||
// campo utente (solo se non ci sono utenti da selezionare)
|
||||
Field {
|
||||
id: userField
|
||||
visible: userModel.count === 0
|
||||
label: "UTENTE"
|
||||
text: userModel.lastUser || ""
|
||||
}
|
||||
|
||||
// campo password
|
||||
Field {
|
||||
id: passField
|
||||
label: "PASSWORD"
|
||||
password: true
|
||||
onAccepted: root.tryLogin()
|
||||
}
|
||||
|
||||
// selettore sessione
|
||||
Combo {
|
||||
id: sessionCombo
|
||||
label: "SESSIONE"
|
||||
model: sessionModel
|
||||
textRole: "name"
|
||||
currentIndex: sessionModel.lastIndex >= 0 ? sessionModel.lastIndex : 0
|
||||
}
|
||||
|
||||
// messaggio di errore
|
||||
Text {
|
||||
id: errorText
|
||||
width: parent.width
|
||||
text: ""
|
||||
color: root.red
|
||||
font.bold: true
|
||||
font.letterSpacing: 1
|
||||
font.pixelSize: Math.max(11, Math.min(root.width * 0.012, 16))
|
||||
visible: text.length > 0
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
// pulsante di accesso
|
||||
Rectangle {
|
||||
id: loginButton
|
||||
width: parent.width
|
||||
height: Math.max(42, root.height * 0.05)
|
||||
color: root.red
|
||||
border.color: root.cream
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: "ENTRA"
|
||||
color: root.paper
|
||||
font.bold: true
|
||||
font.letterSpacing: 3
|
||||
font.pixelSize: Math.max(14, Math.min(root.width * 0.015, 20))
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: root.tryLogin()
|
||||
onPressed: loginButton.color = Qt.darker(root.red, 1.3)
|
||||
onReleased: loginButton.color = root.red
|
||||
}
|
||||
}
|
||||
|
||||
// separatore
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 2
|
||||
color: "#66f0dfba"
|
||||
}
|
||||
|
||||
// pulsanti di sistema
|
||||
RowLayout {
|
||||
width: parent.width
|
||||
spacing: Math.max(8, root.width * 0.006)
|
||||
|
||||
PowerButton {
|
||||
Layout.fillWidth: true
|
||||
label: "SOSPENDI"
|
||||
enabled: sddm.canSuspend
|
||||
onClicked: sddm.suspend()
|
||||
}
|
||||
PowerButton {
|
||||
Layout.fillWidth: true
|
||||
label: "RIAVVIA"
|
||||
enabled: sddm.canReboot
|
||||
onClicked: sddm.reboot()
|
||||
}
|
||||
PowerButton {
|
||||
Layout.fillWidth: true
|
||||
label: "SPEGNI"
|
||||
enabled: sddm.canPowerOff
|
||||
onClicked: sddm.powerOff()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- animazioni ----------
|
||||
QtObject { id: overlayFade; property real value: 0.0 }
|
||||
|
||||
ParallelAnimation {
|
||||
id: intro
|
||||
NumberAnimation { target: ui; property: "opacity"; from: 0; to: 1; duration: 700; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: overlayFade; property: "value"; from: 0; to: 1; duration: 900 }
|
||||
NumberAnimation { target: redShift; property: "y"; to: 0; duration: 850; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: lineShift; property: "x"; to: 0; duration: 950; easing.type: Easing.OutCubic }
|
||||
NumberAnimation { target: darkShift; property: "y"; to: 0; duration: 1050; easing.type: Easing.OutCubic }
|
||||
}
|
||||
|
||||
SequentialAnimation {
|
||||
id: shake
|
||||
NumberAnimation { target: panelShift; property: "x"; to: -14; duration: 45 }
|
||||
NumberAnimation { target: panelShift; property: "x"; to: 14; duration: 45 }
|
||||
NumberAnimation { target: panelShift; property: "x"; to: -10; duration: 45 }
|
||||
NumberAnimation { target: panelShift; property: "x"; to: 10; duration: 45 }
|
||||
NumberAnimation { target: panelShift; property: "x"; to: 0; duration: 45 }
|
||||
}
|
||||
|
||||
NumberAnimation {
|
||||
id: leave
|
||||
target: root
|
||||
property: "opacity"
|
||||
to: 0
|
||||
duration: 500
|
||||
easing.type: Easing.InOutQuad
|
||||
}
|
||||
|
||||
Component.onCompleted: {
|
||||
intro.start()
|
||||
focusTimer.start()
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: focusTimer
|
||||
interval: 200
|
||||
onTriggered: {
|
||||
if (userModel.count > 0) {
|
||||
passField.input.forceActiveFocus()
|
||||
} else {
|
||||
userField.input.forceActiveFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: sddm
|
||||
function onLoginFailed() {
|
||||
errorText.text = "ACCESSO NEGATO"
|
||||
passField.text = ""
|
||||
shake.start()
|
||||
}
|
||||
function onLoginSucceeded() {
|
||||
leave.start()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
// Marca geometrica rotante — stessa identità visiva dello splash costruttivista.
|
||||
Item {
|
||||
id: mark
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
anchors.margins: mark.width * 0.09
|
||||
radius: width / 2
|
||||
color: "transparent"
|
||||
border.color: "#f0dfba"
|
||||
border.width: Math.max(4, mark.width * 0.075)
|
||||
opacity: 0.92
|
||||
}
|
||||
|
||||
Item {
|
||||
id: rotor
|
||||
anchors.fill: parent
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * 0.48
|
||||
height: Math.max(10, parent.height * 0.16)
|
||||
anchors.right: parent.right
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
color: "#d83a28"
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: Math.max(9, parent.width * 0.14)
|
||||
height: parent.height * 0.42
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
color: "#f0dfba"
|
||||
}
|
||||
|
||||
RotationAnimator on rotation {
|
||||
from: 0
|
||||
to: 360
|
||||
duration: 1200
|
||||
loops: Animation.Infinite
|
||||
running: true
|
||||
}
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: parent.width * 0.25
|
||||
height: width
|
||||
anchors.centerIn: parent
|
||||
color: "#171719"
|
||||
border.color: "#d83a28"
|
||||
border.width: Math.max(3, width * 0.15)
|
||||
rotation: 45
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
// Pulsante di sistema (sospendi / riavvia / spegni).
|
||||
Rectangle {
|
||||
id: pbtn
|
||||
property string label: ""
|
||||
property bool enabled: true
|
||||
property bool hovered: false
|
||||
signal clicked()
|
||||
height: 34
|
||||
color: !pbtn.enabled ? "#77101012" : (pbtn.hovered ? "#33d83a28" : "#cc101012")
|
||||
border.color: pbtn.enabled ? "#99f0dfba" : "#55f0dfba"
|
||||
border.width: 1
|
||||
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: pbtn.label
|
||||
color: pbtn.enabled ? "#f0dfba" : "#aaf0dfba"
|
||||
font.pixelSize: 13
|
||||
font.bold: true
|
||||
font.letterSpacing: 1
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
enabled: pbtn.enabled
|
||||
hoverEnabled: true
|
||||
onClicked: pbtn.clicked()
|
||||
onEntered: pbtn.hovered = true
|
||||
onExited: pbtn.hovered = false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
# Costruttivista — tema SDDM
|
||||
|
||||
Schermata di accesso in stile costruttivista sovietico per SDDM, gemella del tema splash KDE `org.enne2.costruttivista.splash`. Stessa identità visiva: palette rosso/crema/nero, accenti geometrici, marca rotante e pannello con bordo crema.
|
||||
|
||||
Autosufficiente: solo QtQuick 2.15 e QtQuick.Controls 2.15, nessuna dipendenza esterna (niente Kirigami, Plasma components o rete). L'immagine di sfondo è inclusa nel pacchetto.
|
||||
|
||||
## Funzionalità
|
||||
|
||||
- Lista utenti con avatar (dal modello `userModel` di SDDM, con fallback geometrico se manca l'avatar)
|
||||
- Campo password con focus automatico
|
||||
- Selettore sessione
|
||||
- Orologio e data in italiano su pannello scuro
|
||||
- Pulsanti di sistema: sospendi / riavvia / spegni
|
||||
- Animazione di ingresso, scossa su errore, dissolvenza al successo
|
||||
|
||||
## Struttura
|
||||
|
||||
```
|
||||
org.enne2.costruttivista.sddm/
|
||||
├── theme.conf # metadati del tema
|
||||
├── metadata.desktop # metadati (compatibilità)
|
||||
├── Main.qml # schermata principale
|
||||
├── UserList.qml # lista utenti con avatar
|
||||
├── Field.qml # campo di input etichettato
|
||||
├── Combo.qml # selettore a tendina etichettato
|
||||
├── PowerButton.qml # pulsante di sistema
|
||||
├── Mark.qml # marca geometrica rotante
|
||||
├── assets/
|
||||
│ └── sfondo_costruttivista_fhd.png
|
||||
└── dist/
|
||||
└── org.enne2.costruttivista.sddm.tar.gz
|
||||
```
|
||||
|
||||
## Installazione
|
||||
|
||||
Per l'utente corrente (consigliato per la prova):
|
||||
|
||||
```sh
|
||||
mkdir -p ~/.local/share/sddm/themes
|
||||
cp -r org.enne2.costruttivista.sddm ~/.local/share/sddm/themes/
|
||||
```
|
||||
|
||||
Per tutti gli utenti:
|
||||
|
||||
```sh
|
||||
sudo cp -r org.enne2.costruttivista.sddm /usr/share/sddm/themes/
|
||||
```
|
||||
|
||||
## Attivazione
|
||||
|
||||
Impostare il tema in `/etc/sddm.conf` (o in un drop-in in `/etc/sddm.conf.d/`):
|
||||
|
||||
```ini
|
||||
[Theme]
|
||||
Current=org.enne2.costruttivista.sddm
|
||||
```
|
||||
|
||||
In alternativa, dal KCM **Impostazioni di sistema → Sessione → Schermata di accesso (SDDM)** selezionare **Costruttivista**.
|
||||
|
||||
Il pacchetto non viene installato né attivato automaticamente.
|
||||
@@ -0,0 +1,97 @@
|
||||
import QtQuick 2.15
|
||||
|
||||
// Lista orizzontale di utenti con avatar, in stile costruttivista.
|
||||
// Usa il modello `userModel` esposto da SDDM (ruoli: name, realName, icon, isCurrent).
|
||||
Row {
|
||||
id: userList
|
||||
property int currentIndex: userModel.lastIndex >= 0 ? userModel.lastIndex : 0
|
||||
signal userSelected(string name)
|
||||
spacing: Math.max(8, parent.width * 0.02)
|
||||
|
||||
Repeater {
|
||||
id: repeater
|
||||
model: userModel
|
||||
|
||||
delegate: Rectangle {
|
||||
id: card
|
||||
required property int index
|
||||
required property string name
|
||||
required property string icon
|
||||
|
||||
width: 80
|
||||
height: 100
|
||||
color: userList.currentIndex === index ? "#33d83a28" : "#cc101012"
|
||||
border.color: userList.currentIndex === index ? "#d83a28" : "#66f0dfba"
|
||||
border.width: userList.currentIndex === index ? 2 : 1
|
||||
|
||||
// cornice circolare con avatar
|
||||
Rectangle {
|
||||
id: avatarFrame
|
||||
width: 54
|
||||
height: 54
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: 7
|
||||
radius: width / 2
|
||||
color: "#181719"
|
||||
border.color: "#f0dfba"
|
||||
border.width: 2
|
||||
clip: true
|
||||
|
||||
Image {
|
||||
id: avatarImage
|
||||
anchors.fill: parent
|
||||
anchors.margins: 2
|
||||
source: card.icon
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
sourceSize: Qt.size(108, 108)
|
||||
asynchronous: true
|
||||
}
|
||||
|
||||
// fallback geometrico se l'avatar non è disponibile
|
||||
Text {
|
||||
anchors.centerIn: parent
|
||||
text: card.name.length > 0 ? card.name.charAt(0).toUpperCase() : "?"
|
||||
color: "#f0dfba"
|
||||
font.pixelSize: 22
|
||||
font.bold: true
|
||||
visible: avatarImage.status !== Image.Ready
|
||||
}
|
||||
}
|
||||
|
||||
// nome utente
|
||||
Text {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: 6
|
||||
width: card.width - 6
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
elide: Text.ElideRight
|
||||
text: card.name.toUpperCase()
|
||||
color: userList.currentIndex === index ? "#f6f1e5" : "#f0dfba"
|
||||
font.pixelSize: 11
|
||||
font.bold: true
|
||||
font.letterSpacing: 1
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
onClicked: userList.select(index)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Seleziona l'utente all'indice i (click o tastiera).
|
||||
function select(i) {
|
||||
if (i < 0 || i >= userModel.count) return
|
||||
currentIndex = i
|
||||
var item = repeater.itemAt(i)
|
||||
if (item) {
|
||||
userSelected(item.name)
|
||||
}
|
||||
}
|
||||
|
||||
Keys.onLeftPressed: select(currentIndex - 1)
|
||||
Keys.onRightPressed: select(currentIndex + 1)
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.2 MiB |
BIN
Binary file not shown.
@@ -0,0 +1,4 @@
|
||||
[Desktop Entry]
|
||||
Type=X-SDDM-Theme
|
||||
Name=Costruttivista
|
||||
Comment=Schermata di accesso costruttivista per SDDM
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 348 KiB |
@@ -0,0 +1,3 @@
|
||||
[General]
|
||||
Name=Costruttivista
|
||||
Comment=Schermata di accesso costruttivista per SDDM
|
||||
Reference in New Issue
Block a user