pragma ComponentBehavior: Bound import QtQuick import QtQuick.Layouts import qs.HyprChrome.Theme // Headless visual core of the login greeter; Greeter.qml owns greetd. Item { id: root property string hostName: "" property string sessionName: "" property date now: new Date() // Label over the secret field. Empty means the plain password step; set // when PAM asks for something else (OTP, PIN). property string inputPrompt: "" property bool responseVisible: false property string message: "" property bool messageIsError: false property bool failed: false property bool busy: false property alias user: userInput.text property alias response: secretInput.text signal submitted(string user, string response) signal userEdited signal powerRequested(string action) function focusSecret() { secretInput.forceActiveFocus(); } function focusUser() { userInput.forceActiveFocus(); } function clearSecret() { secretInput.text = ""; } implicitWidth: 640 implicitHeight: panel.implicitHeight function submit() { if (root.busy) return; if (userInput.text.trim() === "") { root.focusUser(); return; } root.submitted(userInput.text.trim(), secretInput.text); } component MicroText: Text { color: Theme.muted font.family: Theme.microFont font.pixelSize: 8 font.letterSpacing: 0.9 elide: Text.ElideRight } component Field: Rectangle { id: field property bool active: false property bool alert: false property string glyph: "" default property alias input: slot.data Layout.fillWidth: true implicitHeight: 36 color: Theme.selection border.width: 1 border.color: field.alert ? Theme.hot : field.active ? Theme.accentAlpha(0.7) : Theme.hair Behavior on border.color { ColorAnimation { duration: 120 } } // Focus tick on the left edge. Rectangle { width: 2 height: parent.height color: Theme.accent visible: field.active } Text { id: glyphText x: 12 anchors.verticalCenter: parent.verticalCenter text: field.glyph color: field.active ? Theme.accent : Theme.disabled font.family: Theme.displayFont font.pixelSize: 14 font.bold: true } Item { id: slot anchors.left: glyphText.right anchors.leftMargin: 10 anchors.right: parent.right anchors.rightMargin: 12 anchors.top: parent.top anchors.bottom: parent.bottom } } component PowerChip: Rectangle { id: chip property string label: "" property string action: "" width: chipLabel.implicitWidth + 16 height: chipLabel.implicitHeight + 8 color: chipMouse.containsMouse ? Theme.accent : "transparent" border.width: 1 border.color: chipMouse.containsMouse ? Theme.accent : Theme.disabled Text { id: chipLabel anchors.centerIn: parent text: chip.label color: chipMouse.containsMouse ? Theme.surface : Theme.muted font.family: Theme.microFont font.pixelSize: 9 font.letterSpacing: 0.8 } MouseArea { id: chipMouse anchors.fill: parent hoverEnabled: true cursorShape: Qt.PointingHandCursor onClicked: root.powerRequested(chip.action) } } LoginPanel { id: panel width: root.width panelId: "LGN" title: "SESSION // " + root.hostName.toUpperCase() meta: "GREETD" busy: root.busy ColumnLayout { width: parent.width spacing: 16 RowLayout { Layout.fillWidth: true spacing: 20 // ---- clock column ---- ColumnLayout { Layout.preferredWidth: 190 Layout.alignment: Qt.AlignTop spacing: 4 Text { text: Qt.formatTime(root.now, "HH:mm") color: Theme.text font.family: Theme.displayFont font.pixelSize: 54 font.letterSpacing: 2 } MicroText { text: Qt.formatDate(root.now, "ddd dd.MM.yyyy").toUpperCase() color: Theme.accent font.pixelSize: 10 } // Seconds as a segment meter: one cell per five seconds. Row { Layout.topMargin: 10 spacing: 3 Repeater { model: 12 Rectangle { required property int index width: 11 height: 5 color: index < Math.floor(root.now.getSeconds() / 5) + 1 ? Theme.accent : Theme.raised } } } MicroText { Layout.topMargin: 10 text: "SESSION " + root.sessionName.toUpperCase() } } Rectangle { Layout.fillHeight: true implicitWidth: 1 color: Theme.textAlpha(0.12) } // ---- credentials ---- ColumnLayout { Layout.fillWidth: true Layout.alignment: Qt.AlignTop spacing: 6 MicroText { text: "OPERATOR" } Field { glyph: "@" active: userInput.activeFocus TextInput { id: userInput anchors.fill: parent verticalAlignment: TextInput.AlignVCenter enabled: !root.busy && root.inputPrompt === "" color: Theme.text selectionColor: Theme.accent selectedTextColor: Theme.surface font.family: Theme.displayFont font.pixelSize: 14 font.letterSpacing: 1 clip: true onTextEdited: root.userEdited() onAccepted: root.focusSecret() KeyNavigation.tab: secretInput } } MicroText { Layout.topMargin: 6 text: root.inputPrompt !== "" ? root.inputPrompt.toUpperCase() : "PASSPHRASE" color: root.inputPrompt !== "" ? Theme.accent : Theme.muted } Field { glyph: ">_" active: secretInput.activeFocus alert: root.failed TextInput { id: secretInput anchors.fill: parent verticalAlignment: TextInput.AlignVCenter enabled: !root.busy color: Theme.text selectionColor: Theme.accent selectedTextColor: Theme.surface font.family: Theme.displayFont font.pixelSize: 14 font.letterSpacing: 1 clip: true echoMode: root.responseVisible ? TextInput.Normal : TextInput.Password passwordCharacter: "▪" passwordMaskDelay: 0 onAccepted: root.submit() KeyNavigation.backtab: userInput } } // Status line; keeps its height so the panel does not jump. MicroText { Layout.fillWidth: true Layout.topMargin: 4 Layout.preferredHeight: 12 text: root.busy && root.message === "" ? "AUTHENTICATING…" : root.message.toUpperCase() color: root.messageIsError ? Theme.hot : root.busy ? Theme.accent : Theme.muted font.pixelSize: 9 } } } // ---- footer ---- Rectangle { Layout.fillWidth: true implicitHeight: 1 color: Theme.textAlpha(0.12) } RowLayout { Layout.fillWidth: true spacing: 14 MicroText { text: "ENTER LOG IN" } MicroText { text: "TAB SWITCH FIELD" } Item { Layout.fillWidth: true } PowerChip { label: "REBOOT"; action: "reboot" } PowerChip { label: "POWER OFF"; action: "poweroff" } } } } }