summaryrefslogtreecommitdiff
path: root/raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml
diff options
context:
space:
mode:
authorNippy <nippy@rp1.hu>2026-05-08 19:50:49 +0200
committerNippy <nippy@rp1.hu>2026-05-08 19:50:49 +0200
commit3462bcc46ecf74f576ea4397f16492c16bd75b10 (patch)
treec5ab7aef4498647e057bda8d49c11e5f9dda74c9 /raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml
parent56616f693b4f263cbf0d47da43b67424efa6022d (diff)
downloadRaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.tar.gz
RaveOS-PKGBUILD-3462bcc46ecf74f576ea4397f16492c16bd75b10.zip
raveos update
Diffstat (limited to 'raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml')
-rw-r--r--raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml784
1 files changed, 784 insertions, 0 deletions
diff --git a/raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml b/raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml
new file mode 100644
index 0000000..b3d3a41
--- /dev/null
+++ b/raveos-hyprland-theme/theme-data/dms/Modules/Settings/GreeterTab.qml
@@ -0,0 +1,784 @@
+pragma ComponentBehavior: Bound
+
+import QtQuick
+import QtQuick.Layouts
+import Quickshell
+import Quickshell.Io
+import qs.Common
+import qs.Modals.Common
+import qs.Modals.FileBrowser
+import qs.Services
+import qs.Widgets
+import qs.Modules.Settings.Widgets
+
+Item {
+ id: root
+
+ readonly property bool greeterFprintToggleAvailable: SettingsData.greeterFingerprintCanEnable || SettingsData.greeterEnableFprint
+ readonly property bool greeterU2fToggleAvailable: SettingsData.greeterU2fCanEnable || SettingsData.greeterEnableU2f
+
+ function greeterFingerprintDescription() {
+ const source = SettingsData.greeterFingerprintSource;
+ const reason = SettingsData.greeterFingerprintReason;
+
+ if (source === "pam") {
+ switch (reason) {
+ case "configured_externally":
+ return SettingsData.greeterEnableFprint ? I18n.tr("Enabled. PAM already provides fingerprint auth.") : I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.");
+ case "missing_enrollment":
+ return SettingsData.greeterEnableFprint ? I18n.tr("Enabled. PAM provides fingerprint auth, but no prints are enrolled yet.") : I18n.tr("PAM provides fingerprint auth, but no prints are enrolled yet.");
+ case "missing_reader":
+ return I18n.tr("PAM provides fingerprint auth, but no reader was detected.");
+ default:
+ return I18n.tr("PAM provides fingerprint auth, but availability could not be confirmed.");
+ }
+ }
+
+ switch (reason) {
+ case "ready":
+ return SettingsData.greeterEnableFprint ? I18n.tr("Authentication changes apply automatically. Fingerprint-only login may not unlock Keyring.") : I18n.tr("Only affects DMS-managed PAM. If greetd already includes pam_fprintd, fingerprint stays enabled.");
+ case "missing_enrollment":
+ if (SettingsData.greeterEnableFprint)
+ return I18n.tr("Enabled, but no prints are enrolled yet. Enroll fingerprints and run Sync.");
+ return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and run Sync later.");
+ case "missing_reader":
+ return SettingsData.greeterEnableFprint ? I18n.tr("Enabled, but no fingerprint reader was detected.") : I18n.tr("No fingerprint reader detected.");
+ case "missing_pam_support":
+ return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.");
+ default:
+ return SettingsData.greeterEnableFprint ? I18n.tr("Enabled, but fingerprint availability could not be confirmed.") : I18n.tr("Fingerprint availability could not be confirmed.");
+ }
+ }
+
+ function greeterU2fDescription() {
+ const source = SettingsData.greeterU2fSource;
+ const reason = SettingsData.greeterU2fReason;
+
+ if (source === "pam") {
+ return SettingsData.greeterEnableU2f ? I18n.tr("Enabled. PAM already provides security-key auth.") : I18n.tr("PAM already provides security-key auth. Enable this to show it at login.");
+ }
+
+ switch (reason) {
+ case "ready":
+ return SettingsData.greeterEnableU2f ? I18n.tr("Authentication changes apply automatically.") : I18n.tr("Available.");
+ case "missing_key_registration":
+ if (SettingsData.greeterEnableU2f)
+ return I18n.tr("Enabled, but no registered security key was found yet. Register a key and run Sync.");
+ return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.");
+ case "missing_pam_support":
+ return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.");
+ default:
+ return SettingsData.greeterEnableU2f ? I18n.tr("Enabled, but security-key availability could not be confirmed.") : I18n.tr("Security-key availability could not be confirmed.");
+ }
+ }
+
+ function refreshAuthDetection() {
+ SettingsData.refreshAuthAvailability();
+ }
+
+ onVisibleChanged: {
+ if (visible)
+ refreshAuthDetection();
+ }
+
+ ConfirmModal {
+ id: greeterActionConfirm
+ }
+
+ FileBrowserModal {
+ id: greeterWallpaperBrowserModal
+ browserTitle: I18n.tr("Select greeter background image")
+ browserIcon: "wallpaper"
+ browserType: "wallpaper"
+ showHiddenFiles: true
+ fileExtensions: ["*.jpg", "*.jpeg", "*.png", "*.bmp", "*.gif", "*.webp", "*.jxl", "*.avif", "*.heif"]
+ onFileSelected: path => {
+ SettingsData.set("greeterWallpaperPath", path);
+ close();
+ }
+ }
+
+ property string greeterStatusText: ""
+ property bool greeterStatusRunning: false
+ property bool greeterSyncRunning: false
+ property bool greeterInstallActionRunning: false
+ property string greeterStatusStdout: ""
+ property string greeterStatusStderr: ""
+ property string greeterSyncStdout: ""
+ property string greeterSyncStderr: ""
+ property string greeterSudoProbeStderr: ""
+ property string greeterTerminalFallbackStderr: ""
+ property bool greeterTerminalFallbackFromPrecheck: false
+ property var cachedFontFamilies: []
+ property bool fontsEnumerated: false
+ property bool greeterBinaryExists: false
+ property bool greeterEnabled: false
+ readonly property bool greeterInstalled: greeterBinaryExists || greeterEnabled
+
+ readonly property string greeterActionLabel: {
+ if (!root.greeterInstalled)
+ return I18n.tr("Install");
+ if (!root.greeterEnabled)
+ return I18n.tr("Activate");
+ return I18n.tr("Uninstall");
+ }
+ readonly property string greeterActionIcon: {
+ if (!root.greeterInstalled)
+ return "download";
+ if (!root.greeterEnabled)
+ return "login";
+ return "delete";
+ }
+ readonly property var greeterActionCommand: {
+ if (!root.greeterInstalled)
+ return ["dms", "greeter", "install", "--terminal"];
+ if (!root.greeterEnabled)
+ return ["dms", "greeter", "enable", "--terminal"];
+ return ["dms", "greeter", "uninstall", "--terminal", "--yes"];
+ }
+ property string greeterPendingAction: ""
+
+ function checkGreeterInstallState() {
+ greetdEnabledCheckProcess.running = true;
+ greeterBinaryCheckProcess.running = true;
+ }
+
+ function runGreeterStatus() {
+ greeterStatusText = "";
+ greeterStatusStdout = "";
+ greeterStatusStderr = "";
+ greeterStatusRunning = true;
+ greeterStatusProcess.running = true;
+ }
+
+ function runGreeterInstallAction() {
+ root.greeterPendingAction = !root.greeterInstalled ? "install" : !root.greeterEnabled ? "activate" : "uninstall";
+ greeterStatusText = I18n.tr("Opening terminal: ") + root.greeterActionLabel + "…";
+ greeterInstallActionRunning = true;
+ greeterInstallActionProcess.running = true;
+ }
+
+ function promptGreeterActionConfirm() {
+ var title, message, confirmText;
+ if (!root.greeterInstalled) {
+ title = I18n.tr("Install Greeter", "greeter action confirmation");
+ message = I18n.tr("Install the DMS greeter? A terminal will open for sudo authentication.");
+ confirmText = I18n.tr("Install");
+ } else if (!root.greeterEnabled) {
+ title = I18n.tr("Activate Greeter", "greeter action confirmation");
+ message = I18n.tr("Activate the DMS greeter? A terminal will open for sudo authentication. Run Sync after activation to apply your settings.");
+ confirmText = I18n.tr("Activate");
+ } else {
+ title = I18n.tr("Uninstall Greeter", "greeter action confirmation");
+ message = I18n.tr("Uninstall the DMS greeter? This will remove configuration and restore your previous display manager. A terminal will open for sudo authentication.");
+ confirmText = I18n.tr("Uninstall");
+ }
+ greeterActionConfirm.showWithOptions({
+ "title": title,
+ "message": message,
+ "confirmText": confirmText,
+ "cancelText": I18n.tr("Cancel"),
+ "confirmColor": Theme.primary,
+ "onConfirm": () => root.runGreeterInstallAction(),
+ "onCancel": () => {}
+ });
+ }
+
+ function runGreeterSync() {
+ greeterSyncStdout = "";
+ greeterSyncStderr = "";
+ greeterSudoProbeStderr = "";
+ greeterTerminalFallbackStderr = "";
+ greeterTerminalFallbackFromPrecheck = false;
+ greeterStatusText = I18n.tr("Checking whether sudo authentication is needed…");
+ greeterSyncRunning = true;
+ greeterSudoProbeProcess.running = true;
+ }
+
+ function launchGreeterSyncTerminalFallback(fromPrecheck, statusText) {
+ greeterTerminalFallbackFromPrecheck = fromPrecheck;
+ if (statusText && statusText !== "")
+ greeterStatusText = statusText;
+ greeterTerminalFallbackStderr = "";
+ greeterTerminalFallbackProcess.running = true;
+ }
+
+ function enumerateFonts() {
+ if (fontsEnumerated)
+ return;
+ var fonts = [];
+ var availableFonts = Qt.fontFamilies();
+ for (var i = 0; i < availableFonts.length; i++) {
+ var fontName = availableFonts[i];
+ if (fontName.startsWith("."))
+ continue;
+ fonts.push(fontName);
+ }
+ fonts.sort();
+ fonts.unshift("Default");
+ cachedFontFamilies = fonts;
+ fontsEnumerated = true;
+ }
+
+ Component.onCompleted: {
+ refreshAuthDetection();
+ Qt.callLater(enumerateFonts);
+ Qt.callLater(checkGreeterInstallState);
+ }
+
+ Process {
+ id: greetdEnabledCheckProcess
+ command: ["systemctl", "is-enabled", "greetd"]
+ running: false
+
+ stdout: StdioCollector {
+ onStreamFinished: root.greeterEnabled = text.trim() === "enabled"
+ }
+ }
+
+ Process {
+ id: greeterBinaryCheckProcess
+ command: ["sh", "-c", "test -f /usr/bin/dms-greeter || test -f /usr/local/bin/dms-greeter"]
+ running: false
+
+ onExited: exitCode => {
+ root.greeterBinaryExists = (exitCode === 0);
+ }
+ }
+
+ Process {
+ id: greeterStatusProcess
+ command: ["dms", "greeter", "status"]
+ running: false
+
+ stdout: StdioCollector {
+ onStreamFinished: {
+ root.greeterStatusStdout = text || "";
+ }
+ }
+
+ stderr: StdioCollector {
+ onStreamFinished: root.greeterStatusStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ root.greeterStatusRunning = false;
+ const out = (root.greeterStatusStdout || "").trim();
+ const err = (root.greeterStatusStderr || "").trim();
+ if (exitCode === 0) {
+ root.greeterStatusText = out !== "" ? out : I18n.tr("No status output.");
+ if (err !== "")
+ root.greeterStatusText = root.greeterStatusText + "\n\nstderr:\n" + err;
+ return;
+ }
+ var failure = I18n.tr("Failed to run 'dms greeter status'. Ensure DMS is installed and dms is in PATH.", "greeter status error") + " (exit " + exitCode + ")";
+ if (out !== "")
+ failure = failure + "\n\n" + out;
+ if (err !== "")
+ failure = failure + "\n\nstderr:\n" + err;
+ root.greeterStatusText = failure;
+ }
+ }
+
+ Process {
+ id: greeterSyncProcess
+ command: ["dms", "greeter", "sync", "--yes"]
+ running: false
+
+ stdout: StdioCollector {
+ onStreamFinished: root.greeterSyncStdout = text || ""
+ }
+
+ stderr: StdioCollector {
+ onStreamFinished: root.greeterSyncStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ root.greeterSyncRunning = false;
+ const out = (root.greeterSyncStdout || "").trim();
+ const err = (root.greeterSyncStderr || "").trim();
+ if (exitCode === 0) {
+ var success = I18n.tr("Sync completed successfully.");
+ if (out !== "")
+ success = success + "\n\n" + out;
+ if (err !== "")
+ success = success + "\n\nstderr:\n" + err;
+ root.greeterStatusText = success;
+ } else {
+ var failure = I18n.tr("Sync failed in background mode. Trying terminal mode so you can authenticate interactively.") + " (exit " + exitCode + ")";
+ if (out !== "")
+ failure = failure + "\n\n" + out;
+ if (err !== "")
+ failure = failure + "\n\nstderr:\n" + err;
+ root.greeterStatusText = failure;
+ root.launchGreeterSyncTerminalFallback(false, "");
+ }
+ root.checkGreeterInstallState();
+ }
+ }
+
+ Process {
+ id: greeterSudoProbeProcess
+ command: ["sudo", "-n", "true"]
+ running: false
+
+ stderr: StdioCollector {
+ onStreamFinished: root.greeterSudoProbeStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ const err = (root.greeterSudoProbeStderr || "").trim();
+ if (exitCode === 0) {
+ root.greeterStatusText = I18n.tr("Running greeter sync…");
+ greeterSyncProcess.running = true;
+ return;
+ }
+
+ var authNeeded = I18n.tr("Sync needs sudo authentication. Opening terminal so you can use password or fingerprint.");
+ if (err !== "")
+ authNeeded = authNeeded + "\n\n" + err;
+ root.launchGreeterSyncTerminalFallback(true, authNeeded);
+ }
+ }
+
+ Process {
+ id: greeterTerminalFallbackProcess
+ command: ["dms", "greeter", "sync", "--terminal", "--yes"]
+ running: false
+
+ stderr: StdioCollector {
+ onStreamFinished: root.greeterTerminalFallbackStderr = text || ""
+ }
+
+ onExited: exitCode => {
+ root.greeterSyncRunning = false;
+ if (exitCode === 0) {
+ var launched = root.greeterTerminalFallbackFromPrecheck ? I18n.tr("Terminal opened. Complete sync authentication there; it will close automatically when done.") : I18n.tr("Terminal fallback opened. Complete sync there; it will close automatically when done.");
+ root.greeterStatusText = root.greeterStatusText ? root.greeterStatusText + "\n\n" + launched : launched;
+ return;
+ }
+ var fallback = I18n.tr("Terminal fallback failed. Install one of the supported terminal emulators or run 'dms greeter sync' manually.") + " (exit " + exitCode + ")";
+ const err = (root.greeterTerminalFallbackStderr || "").trim();
+ if (err !== "")
+ fallback = fallback + "\n\nstderr:\n" + err;
+ root.greeterStatusText = root.greeterStatusText ? root.greeterStatusText + "\n\n" + fallback : fallback;
+ }
+ }
+
+ Process {
+ id: greeterInstallActionProcess
+ command: root.greeterActionCommand
+ running: false
+
+ onExited: exitCode => {
+ root.greeterInstallActionRunning = false;
+ const pending = root.greeterPendingAction;
+ root.greeterPendingAction = "";
+ if (exitCode === 0) {
+ if (pending === "install")
+ root.greeterStatusText = I18n.tr("Install complete. Greeter has been installed.");
+ else if (pending === "activate")
+ root.greeterStatusText = I18n.tr("Greeter activated. greetd is now enabled.");
+ else
+ root.greeterStatusText = I18n.tr("Uninstall complete. Greeter has been removed.");
+ } else {
+ root.greeterStatusText = I18n.tr("Action failed or terminal was closed.") + " (exit " + exitCode + ")";
+ }
+ root.checkGreeterInstallState();
+ }
+ }
+
+ readonly property var _lockDateFormatPresets: [
+ {
+ format: "",
+ label: I18n.tr("System Default", "date format option")
+ },
+ {
+ format: "ddd d",
+ label: I18n.tr("Day Date", "date format option")
+ },
+ {
+ format: "ddd MMM d",
+ label: I18n.tr("Day Month Date", "date format option")
+ },
+ {
+ format: "MMM d",
+ label: I18n.tr("Month Date", "date format option")
+ },
+ {
+ format: "M/d",
+ label: I18n.tr("Numeric (M/D)", "date format option")
+ },
+ {
+ format: "d/M",
+ label: I18n.tr("Numeric (D/M)", "date format option")
+ },
+ {
+ format: "ddd d MMM yyyy",
+ label: I18n.tr("Full with Year", "date format option")
+ },
+ {
+ format: "yyyy-MM-dd",
+ label: I18n.tr("ISO Date", "date format option")
+ },
+ {
+ format: "dddd, MMMM d",
+ label: I18n.tr("Full Day & Month", "date format option")
+ }
+ ]
+ readonly property var _wallpaperFillModes: ["Stretch", "Fit", "Fill", "Tile", "TileVertically", "TileHorizontally", "Pad"]
+
+ DankFlickable {
+ anchors.fill: parent
+ clip: true
+ contentHeight: mainColumn.height + Theme.spacingXL
+ contentWidth: width
+
+ Column {
+ id: mainColumn
+ topPadding: 4
+ width: Math.min(550, parent.width - Theme.spacingL * 2)
+ anchors.horizontalCenter: parent.horizontalCenter
+ spacing: Theme.spacingXL
+
+ SettingsCard {
+ width: parent.width
+ iconName: "info"
+ title: I18n.tr("Greeter Status")
+ settingKey: "greeterStatus"
+
+ StyledText {
+ text: I18n.tr("Check sync status on demand. Sync copies your theme, settings, and wallpaper configuration to the login screen. Authentication changes apply automatically.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ }
+
+ Item {
+ width: 1
+ height: Theme.spacingS
+ }
+
+ Rectangle {
+ width: parent.width
+ height: Math.min(180, statusTextArea.implicitHeight + Theme.spacingM * 2)
+ radius: Theme.cornerRadius
+ color: Theme.surfaceContainerHighest
+
+ StyledText {
+ id: statusTextArea
+ anchors.fill: parent
+ anchors.margins: Theme.spacingM
+ text: root.greeterStatusRunning ? I18n.tr("Checking…", "greeter status loading") : (root.greeterStatusText || I18n.tr("Click Refresh to check status.", "greeter status placeholder"))
+ font.pixelSize: Theme.fontSizeSmall
+ font.family: "monospace"
+ color: root.greeterStatusRunning ? Theme.surfaceVariantText : Theme.surfaceText
+ wrapMode: Text.Wrap
+ verticalAlignment: Text.AlignTop
+ }
+ }
+
+ Item {
+ width: 1
+ height: Theme.spacingM
+ }
+
+ RowLayout {
+ width: parent.width
+ spacing: Theme.spacingS
+
+ DankButton {
+ text: root.greeterActionLabel
+ iconName: root.greeterActionIcon
+ horizontalPadding: Theme.spacingL
+ onClicked: root.promptGreeterActionConfirm()
+ enabled: !root.greeterInstallActionRunning && !root.greeterSyncRunning
+ }
+
+ Item {
+ Layout.fillWidth: true
+ }
+
+ DankButton {
+ text: I18n.tr("Refresh")
+ iconName: "refresh"
+ horizontalPadding: Theme.spacingL
+ onClicked: root.runGreeterStatus()
+ enabled: !root.greeterStatusRunning
+ }
+
+ DankButton {
+ text: I18n.tr("Sync")
+ iconName: "sync"
+ horizontalPadding: Theme.spacingL
+ onClicked: root.runGreeterSync()
+ enabled: root.greeterInstalled && !root.greeterSyncRunning && !root.greeterInstallActionRunning
+ }
+ }
+ }
+
+ SettingsCard {
+ width: parent.width
+ iconName: "fingerprint"
+ title: I18n.tr("Login Authentication")
+ settingKey: "greeterAuth"
+
+ StyledText {
+ text: I18n.tr("Enable fingerprint or security key for DMS Greeter. Authentication changes apply automatically.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterEnableFprint"
+ tags: ["greeter", "fingerprint", "fprintd", "login", "auth"]
+ text: I18n.tr("Enable fingerprint at login")
+ description: root.greeterFingerprintDescription()
+ descriptionColor: (SettingsData.greeterFingerprintReason === "ready" || SettingsData.greeterFingerprintReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
+ checked: SettingsData.greeterEnableFprint
+ enabled: root.greeterFprintToggleAvailable
+ onToggled: checked => SettingsData.set("greeterEnableFprint", checked)
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterEnableU2f"
+ tags: ["greeter", "u2f", "security", "key", "login", "auth"]
+ text: I18n.tr("Enable security key at login")
+ description: root.greeterU2fDescription()
+ descriptionColor: (SettingsData.greeterU2fReason === "ready" || SettingsData.greeterU2fReason === "configured_externally") ? Theme.surfaceVariantText : Theme.warning
+ checked: SettingsData.greeterEnableU2f
+ enabled: root.greeterU2fToggleAvailable
+ onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
+ }
+ }
+
+ SettingsCard {
+ width: parent.width
+ iconName: "palette"
+ title: I18n.tr("Greeter Appearance")
+ settingKey: "greeterAppearance"
+
+ StyledText {
+ text: I18n.tr("Font")
+ font.pixelSize: Theme.fontSizeMedium
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ topPadding: Theme.spacingM
+ }
+
+ SettingsDropdownRow {
+ settingKey: "greeterFontFamily"
+ tags: ["greeter", "font", "typography"]
+ text: I18n.tr("Greeter font")
+ description: I18n.tr("Font used on the login screen")
+ options: root.fontsEnumerated ? root.cachedFontFamilies : ["Default"]
+ currentValue: (!SettingsData.greeterFontFamily || SettingsData.greeterFontFamily === "" || SettingsData.greeterFontFamily === Theme.defaultFontFamily) ? "Default" : (SettingsData.greeterFontFamily || "Default")
+ enableFuzzySearch: true
+ popupWidthOffset: 100
+ maxPopupHeight: 400
+ onValueChanged: value => {
+ if (value === "Default")
+ SettingsData.set("greeterFontFamily", "");
+ else
+ SettingsData.set("greeterFontFamily", value);
+ }
+ }
+
+ StyledText {
+ text: I18n.tr("Time format")
+ font.pixelSize: Theme.fontSizeMedium
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ topPadding: Theme.spacingM
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterUse24Hour"
+ tags: ["greeter", "time", "24hour"]
+ text: I18n.tr("24-hour clock")
+ description: I18n.tr("Greeter only — does not affect main clock")
+ checked: SettingsData.greeterUse24HourClock
+ onToggled: checked => SettingsData.set("greeterUse24HourClock", checked)
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterShowSeconds"
+ tags: ["greeter", "time", "seconds"]
+ text: I18n.tr("Show seconds")
+ checked: SettingsData.greeterShowSeconds
+ onToggled: checked => SettingsData.set("greeterShowSeconds", checked)
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterPadHours"
+ tags: ["greeter", "time", "12hour"]
+ text: I18n.tr("Pad hours (02:00 vs 2:00)")
+ visible: !SettingsData.greeterUse24HourClock
+ checked: SettingsData.greeterPadHours12Hour
+ onToggled: checked => SettingsData.set("greeterPadHours12Hour", checked)
+ }
+
+ StyledText {
+ text: I18n.tr("Date format on greeter")
+ font.pixelSize: Theme.fontSizeMedium
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ topPadding: Theme.spacingM
+ }
+
+ SettingsDropdownRow {
+ settingKey: "greeterLockDateFormat"
+ tags: ["greeter", "date", "format"]
+ text: I18n.tr("Date format")
+ description: I18n.tr("Greeter only — format for the date on the login screen")
+ options: root._lockDateFormatPresets.map(p => p.label)
+ currentValue: {
+ var current = (SettingsData.greeterLockDateFormat !== undefined && SettingsData.greeterLockDateFormat !== "") ? SettingsData.greeterLockDateFormat : SettingsData.lockDateFormat || "";
+ var match = root._lockDateFormatPresets.find(p => p.format === current);
+ return match ? match.label : (current ? I18n.tr("Custom: ") + current : root._lockDateFormatPresets[0].label);
+ }
+ onValueChanged: value => {
+ var preset = root._lockDateFormatPresets.find(p => p.label === value);
+ SettingsData.set("greeterLockDateFormat", preset ? preset.format : "");
+ }
+ }
+
+ StyledText {
+ text: I18n.tr("Background")
+ font.pixelSize: Theme.fontSizeMedium
+ font.weight: Font.Medium
+ color: Theme.surfaceText
+ topPadding: Theme.spacingM
+ }
+
+ StyledText {
+ text: I18n.tr("Use a custom image for the login screen, or leave empty to use your desktop wallpaper.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ }
+
+ Row {
+ width: parent.width
+ spacing: Theme.spacingS
+
+ DankTextField {
+ id: greeterWallpaperPathField
+ width: parent.width - browseGreeterWallpaperButton.width - Theme.spacingS
+ placeholderText: I18n.tr("Use desktop wallpaper")
+ text: SettingsData.greeterWallpaperPath
+ backgroundColor: Theme.surfaceContainerHighest
+ onTextChanged: {
+ if (text !== SettingsData.greeterWallpaperPath)
+ SettingsData.set("greeterWallpaperPath", text);
+ }
+ }
+
+ DankButton {
+ id: browseGreeterWallpaperButton
+ text: I18n.tr("Browse")
+ horizontalPadding: Theme.spacingL
+ onClicked: greeterWallpaperBrowserModal.open()
+ }
+ }
+
+ SettingsDropdownRow {
+ settingKey: "greeterWallpaperFillMode"
+ tags: ["greeter", "wallpaper", "background", "fill"]
+ text: I18n.tr("Wallpaper fill mode")
+ description: I18n.tr("How the background image is scaled")
+ options: root._wallpaperFillModes.map(m => I18n.tr(m, "wallpaper fill mode"))
+ currentValue: {
+ var mode = (SettingsData.greeterWallpaperFillMode && SettingsData.greeterWallpaperFillMode !== "") ? SettingsData.greeterWallpaperFillMode : (SettingsData.wallpaperFillMode || "Fill");
+ var idx = root._wallpaperFillModes.indexOf(mode);
+ return idx >= 0 ? I18n.tr(root._wallpaperFillModes[idx], "wallpaper fill mode") : I18n.tr("Fill", "wallpaper fill mode");
+ }
+ onValueChanged: value => {
+ var idx = root._wallpaperFillModes.map(m => I18n.tr(m, "wallpaper fill mode")).indexOf(value);
+ if (idx >= 0)
+ SettingsData.set("greeterWallpaperFillMode", root._wallpaperFillModes[idx]);
+ }
+ }
+
+ StyledText {
+ text: I18n.tr("Layout and module positions on the greeter are synced from your shell (e.g. bar config). Run Sync to apply.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ topPadding: Theme.spacingS
+ }
+ }
+
+ SettingsCard {
+ width: parent.width
+ iconName: "history"
+ title: I18n.tr("Greeter Behavior")
+ settingKey: "greeterBehavior"
+
+ StyledText {
+ text: I18n.tr("Convenience options for the login screen. Sync to apply.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterRememberLastSession"
+ tags: ["greeter", "session", "remember", "login"]
+ text: I18n.tr("Remember last session")
+ description: I18n.tr("Pre-select the last used session on the greeter")
+ checked: SettingsData.greeterRememberLastSession
+ onToggled: checked => SettingsData.set("greeterRememberLastSession", checked)
+ }
+
+ SettingsToggleRow {
+ settingKey: "greeterRememberLastUser"
+ tags: ["greeter", "user", "remember", "login", "username"]
+ text: I18n.tr("Remember last user")
+ description: I18n.tr("Pre-fill the last successful username on the greeter")
+ checked: SettingsData.greeterRememberLastUser
+ onToggled: checked => SettingsData.set("greeterRememberLastUser", checked)
+ }
+ }
+
+ SettingsCard {
+ width: parent.width
+ iconName: "extension"
+ title: I18n.tr("Dependencies & documentation")
+ settingKey: "greeterDeps"
+
+ StyledText {
+ text: I18n.tr("DMS greeter needs: greetd, dms-greeter. Fingerprint: fprintd, pam_fprintd. Security keys: pam_u2f. Add your user to the greeter group. Authentication changes apply automatically and may open a terminal when sudo authentication is required.")
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ width: parent.width
+ wrapMode: Text.Wrap
+ }
+
+ StyledText {
+ text: I18n.tr("Installation and PAM setup: see the ") + "<a href=\"https://danklinux.com/docs/dankgreeter/installation\" style=\"text-decoration:none; color:" + Theme.primary + ";\">DankGreeter docs</a> " + I18n.tr("or run ") + "'dms greeter install'."
+ textFormat: Text.RichText
+ font.pixelSize: Theme.fontSizeSmall
+ color: Theme.surfaceVariantText
+ linkColor: Theme.primary
+ width: parent.width
+ wrapMode: Text.Wrap
+ onLinkActivated: url => Qt.openUrlExternally(url)
+
+ MouseArea {
+ anchors.fill: parent
+ cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
+ acceptedButtons: Qt.NoButton
+ propagateComposedEvents: true
+ }
+ }
+ }
+ }
+ }
+}