/* 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() } } }