Files
kde-sddm-costruttivista/Main.qml
T

405 lines
14 KiB
QML

/*
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
import "fauxcyrillic.js" as Faux
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"
// Font pseudo-cirillico (Russo One, OFL) incluso nel tema.
FontLoader {
id: themeFont
source: "assets/fonts/RussoOne-Regular.ttf"
}
// 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 = Faux.apply("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.family: themeFont.name
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: Faux.apply(root.italianDate(new Date()))
color: "#ef6a55"
font.family: themeFont.name
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 = Faux.apply(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: Faux.apply("ACCESSO")
color: root.cream
font.family: themeFont.name
font.pixelSize: Math.max(20, Math.min(root.width * 0.024, 34))
font.bold: true
font.letterSpacing: 3
}
Text {
text: Faux.apply("INSERISCI LE CREDENZIALI")
color: "#e85a48"
font.family: themeFont.name
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.family: themeFont.name
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: Faux.apply("ENTRA")
color: root.paper
font.bold: true
font.letterSpacing: 3
font.family: themeFont.name
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 = Faux.apply("ACCESSO NEGATO")
passField.text = ""
shake.start()
}
function onLoginSucceeded() {
leave.start()
}
}
}