mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
4200270b55
port 1.5
765 lines
32 KiB
QML
765 lines
32 KiB
QML
pragma ComponentBehavior: Bound
|
|
|
|
import QtQuick
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Modals.Common
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
LayoutMirroring.enabled: I18n.isRtl
|
|
LayoutMirroring.childrenInherit: true
|
|
|
|
readonly property bool greeterFprintToggleAvailable: SettingsData.greeterFingerprintCanEnable || SettingsData.greeterEnableFprint
|
|
readonly property bool greeterU2fToggleAvailable: SettingsData.greeterU2fCanEnable || SettingsData.greeterEnableU2f
|
|
|
|
function greeterFingerprintDescription() {
|
|
if (SettingsData.greeterPamExternallyManaged)
|
|
return I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status");
|
|
if (SettingsData.greeterFingerprintSource === "pam")
|
|
return I18n.tr("PAM already provides fingerprint auth. Enable this to show it at login.", "greeter fingerprint login setting");
|
|
|
|
switch (SettingsData.greeterFingerprintReason) {
|
|
case "ready":
|
|
return I18n.tr("Authentication changes apply automatically.", "greeter auth setting description");
|
|
case "missing_enrollment":
|
|
return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and run Sync later.", "greeter fingerprint login setting");
|
|
case "missing_reader":
|
|
return I18n.tr("No fingerprint reader detected.", "fingerprint setting status");
|
|
case "missing_pam_support":
|
|
return I18n.tr("Not available — install fprintd and pam_fprintd, or configure greetd PAM.", "greeter fingerprint login setting");
|
|
default:
|
|
return I18n.tr("Fingerprint availability could not be confirmed.", "fingerprint setting status");
|
|
}
|
|
}
|
|
|
|
function greeterU2fDescription() {
|
|
if (SettingsData.greeterPamExternallyManaged)
|
|
return I18n.tr("Managed by the primary PAM source.", "factor managed by PAM source status");
|
|
if (SettingsData.greeterU2fSource === "pam")
|
|
return I18n.tr("PAM already provides security-key auth. Enable this to show it at login.", "greeter security key login setting");
|
|
|
|
switch (SettingsData.greeterU2fReason) {
|
|
case "ready":
|
|
return I18n.tr("Authentication changes apply automatically.", "greeter auth setting description");
|
|
case "missing_key_registration":
|
|
return I18n.tr("Security-key support was detected, but no registered key was found yet. You can enable this now and register one later.", "security key setting status");
|
|
case "missing_pam_support":
|
|
return I18n.tr("Not available — install or configure pam_u2f, or configure greetd PAM.", "greeter security key login setting");
|
|
default:
|
|
return I18n.tr("Security-key availability could not be confirmed.", "security key setting status");
|
|
}
|
|
}
|
|
|
|
function refreshAuthDetection() {
|
|
SettingsData.refreshAuthAvailability();
|
|
}
|
|
|
|
onVisibleChanged: {
|
|
if (visible)
|
|
refreshAuthDetection();
|
|
}
|
|
|
|
ConfirmModal {
|
|
id: greeterActionConfirm
|
|
}
|
|
|
|
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 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;
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
refreshAuthDetection();
|
|
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;
|
|
SettingsData.clearGreeterSyncPending();
|
|
ToastService.showInfo(I18n.tr("Greeter sync complete"));
|
|
} 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 authentication there; it will close automatically when done.") : I18n.tr("Terminal fallback opened. Complete authentication there; it will close automatically when done.");
|
|
root.greeterStatusText = root.greeterStatusText ? root.greeterStatusText + "\n\n" + launched : launched;
|
|
SettingsData.clearGreeterSyncPending();
|
|
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")
|
|
}
|
|
]
|
|
|
|
DankFlickable {
|
|
anchors.fill: parent
|
|
clip: true
|
|
contentHeight: mainColumn.height + Theme.spacingXL + (syncPendingPill.shown ? syncPendingPill.height + Theme.spacingL : 0)
|
|
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("Status")
|
|
settingKey: "greeterStatus"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Sync applies your theme and settings to the login screen. Other users should run dms greeter sync --profile instead of a full sync. Authentication changes apply automatically.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
Flow {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DankButton {
|
|
text: root.greeterActionLabel
|
|
iconName: root.greeterActionIcon
|
|
horizontalPadding: Theme.spacingL
|
|
onClicked: root.promptGreeterActionConfirm()
|
|
enabled: !root.greeterInstallActionRunning && !root.greeterSyncRunning
|
|
}
|
|
|
|
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("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
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "greeterPamExternallyManaged"
|
|
tags: ["greeter", "pam", "managed", "external", "greetd", "auth"]
|
|
text: I18n.tr("Use system PAM authentication", "system PAM policy toggle")
|
|
description: I18n.tr("DMS removes its managed block from /etc/pam.d/greetd and stops writing to it", "greeter system PAM toggle description")
|
|
checked: SettingsData.greeterPamExternallyManaged
|
|
onToggled: checked => SettingsData.set("greeterPamExternallyManaged", checked)
|
|
}
|
|
|
|
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 && !SettingsData.greeterPamExternallyManaged
|
|
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 && !SettingsData.greeterPamExternallyManaged
|
|
onToggled: checked => SettingsData.set("greeterEnableU2f", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "palette"
|
|
title: I18n.tr("Appearance")
|
|
settingKey: "greeterAppearance"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Font")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
topPadding: Theme.spacingM
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
SettingsFontDropdownRow {
|
|
settingKey: "greeterFontFamily"
|
|
tags: ["greeter", "font", "typography"]
|
|
text: I18n.tr("Greeter font")
|
|
description: I18n.tr("Font used on the login screen")
|
|
currentFont: SettingsData.greeterFontFamily || ""
|
|
onFontSelected: family => SettingsData.set("greeterFontFamily", family)
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Date format on greeter")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
font.weight: Font.Medium
|
|
color: Theme.surfaceText
|
|
topPadding: Theme.spacingM
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
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
|
|
width: parent.width
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
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
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
SettingsWallpaperPicker {
|
|
width: parent.width
|
|
path: SettingsData.greeterWallpaperPath
|
|
fillMode: SettingsData.greeterWallpaperFillMode
|
|
fallbackFillMode: SettingsData.wallpaperFillMode || "Fill"
|
|
browserTitle: I18n.tr("Select greeter background image")
|
|
fillModeSettingKey: "greeterWallpaperFillMode"
|
|
fillModeTags: ["greeter", "wallpaper", "background", "fill"]
|
|
onPathSelected: path => SettingsData.set("greeterWallpaperPath", path)
|
|
onFillModeSelected: mode => SettingsData.set("greeterWallpaperFillMode", mode)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "history"
|
|
title: I18n.tr("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
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
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)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "greeterAutoLogin"
|
|
tags: ["greeter", "autologin", "login", "startup", "password"]
|
|
text: I18n.tr("Auto-login on startup")
|
|
description: SettingsData.greeterRememberLastUser && SettingsData.greeterRememberLastSession ? I18n.tr("Skip the greeter password after boot until you sign out. Lock screen unlock is unchanged. Takes effect on the next reboot after sync.") : I18n.tr("Requires remembering the last user and session. Enable those options first.")
|
|
checked: SettingsData.greeterAutoLogin
|
|
enabled: SettingsData.greeterRememberLastUser && SettingsData.greeterRememberLastSession
|
|
onToggled: checked => SettingsData.set("greeterAutoLogin", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "extension"
|
|
title: I18n.tr("Dependencies & documentation")
|
|
settingKey: "greeterDeps"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Requires greetd, dms-greeter, and your user in the greeter group (plus fprintd/pam_fprintd for fingerprint, pam_u2f for security keys). Auth changes apply automatically and may open a terminal for sudo.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
horizontalAlignment: Text.AlignLeft
|
|
}
|
|
|
|
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
|
|
horizontalAlignment: Text.AlignLeft
|
|
onLinkActivated: url => Qt.openUrlExternally(url)
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: parent.hoveredLink ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
acceptedButtons: Qt.NoButton
|
|
propagateComposedEvents: true
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: syncPendingPill
|
|
|
|
readonly property bool shown: SettingsData.greeterSyncPending && root.greeterInstalled
|
|
|
|
anchors.horizontalCenter: parent.horizontalCenter
|
|
anchors.bottom: parent.bottom
|
|
anchors.bottomMargin: shown ? Theme.spacingL : Theme.spacingXS
|
|
width: pillRow.implicitWidth + Theme.spacingL * 2
|
|
height: 44
|
|
radius: height / 2
|
|
color: Theme.primary
|
|
opacity: shown ? 1 : 0
|
|
visible: opacity > 0
|
|
|
|
Behavior on opacity {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
easing.type: Theme.standardEasing
|
|
}
|
|
}
|
|
|
|
Behavior on anchors.bottomMargin {
|
|
NumberAnimation {
|
|
duration: Theme.shortDuration
|
|
easing.type: Theme.standardEasing
|
|
}
|
|
}
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
hoverEnabled: true
|
|
acceptedButtons: Qt.AllButtons
|
|
cursorShape: !root.greeterSyncRunning && !root.greeterInstallActionRunning ? Qt.PointingHandCursor : Qt.ArrowCursor
|
|
onClicked: mouse => {
|
|
if (mouse.button === Qt.LeftButton && !root.greeterSyncRunning && !root.greeterInstallActionRunning)
|
|
root.runGreeterSync();
|
|
}
|
|
}
|
|
|
|
Row {
|
|
id: pillRow
|
|
anchors.centerIn: parent
|
|
spacing: Theme.spacingS
|
|
|
|
DankIcon {
|
|
id: syncPillIcon
|
|
name: "sync"
|
|
size: Theme.iconSize - 4
|
|
color: Theme.primaryText
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
RotationAnimation on rotation {
|
|
running: root.greeterSyncRunning && syncPendingPill.shown
|
|
from: 0
|
|
to: 360
|
|
duration: 1000
|
|
loops: Animation.Infinite
|
|
onRunningChanged: {
|
|
if (!running)
|
|
syncPillIcon.rotation = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
text: root.greeterSyncRunning ? I18n.tr("Syncing...") : I18n.tr("Sync to apply")
|
|
color: Theme.primaryText
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
}
|
|
|
|
DankActionButton {
|
|
iconName: "close"
|
|
iconSize: Theme.iconSize - 6
|
|
iconColor: Theme.primaryText
|
|
buttonSize: 28
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
onClicked: SettingsData.revertGreeterSyncPending()
|
|
}
|
|
}
|
|
}
|
|
}
|