mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-08-02 03:28:28 -04:00
9967db0c5a
- Introduce external management of greetd PAM
- New functionality to validate and apply custom PAM service paths in lockscreen
Port 1.5
(cherry picked from commit b169fe0d77)
725 lines
34 KiB
QML
725 lines
34 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import Quickshell.Io
|
|
import qs.Common
|
|
import qs.Modals.FileBrowser
|
|
import qs.Services
|
|
import qs.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
|
|
Item {
|
|
id: root
|
|
|
|
readonly property bool lockFprintToggleAvailable: SettingsData.lockFingerprintCanEnable || SettingsData.enableFprint
|
|
readonly property bool lockU2fToggleAvailable: SettingsData.lockU2fCanEnable || SettingsData.enableU2f
|
|
|
|
property var authServices: []
|
|
property bool authValidateRunning: false
|
|
property bool authValidateOk: false
|
|
property bool authValidateWarn: false
|
|
property string authValidateMessage: ""
|
|
property string authPendingApplyPath: ""
|
|
property bool authShowCustom: false
|
|
|
|
readonly property string authAutoLabel: I18n.tr("Auto (recommended)")
|
|
readonly property string authCustomLabel: I18n.tr("Custom…")
|
|
|
|
function authServiceLabel(service) {
|
|
const label = service.name.split("-").map(w => w.charAt(0).toUpperCase() + w.slice(1)).join("-");
|
|
return service.dir === "/etc/pam.d" ? label : label + " (" + service.dir + ")";
|
|
}
|
|
|
|
readonly property var authOptions: {
|
|
var opts = [authAutoLabel];
|
|
for (var i = 0; i < authServices.length; i++)
|
|
opts.push(authServiceLabel(authServices[i]));
|
|
opts.push(authCustomLabel);
|
|
return opts;
|
|
}
|
|
|
|
readonly property string authCurrentValue: {
|
|
if (SettingsData.lockPamPath === "")
|
|
return authAutoLabel;
|
|
for (var i = 0; i < authServices.length; i++) {
|
|
if (authServices[i].path === SettingsData.lockPamPath)
|
|
return authServiceLabel(authServices[i]);
|
|
}
|
|
return authCustomLabel;
|
|
}
|
|
|
|
function refreshAuthServices() {
|
|
authListServicesProcess.running = true;
|
|
}
|
|
|
|
function applyAutoAuthSource() {
|
|
SettingsData.set("lockPamPath", "");
|
|
SettingsData.set("lockPamInlineFprint", false);
|
|
SettingsData.set("lockPamInlineU2f", false);
|
|
root.authValidateOk = false;
|
|
root.authValidateWarn = false;
|
|
root.authValidateMessage = "";
|
|
}
|
|
|
|
function validateAndApplyAuthSource(path) {
|
|
if (!path || path === "")
|
|
return;
|
|
if (!path.startsWith("/")) {
|
|
root.authValidateOk = false;
|
|
root.authValidateWarn = false;
|
|
root.authValidateMessage = I18n.tr("Not a valid path");
|
|
return;
|
|
}
|
|
root.authPendingApplyPath = path;
|
|
root.authValidateMessage = "";
|
|
root.authValidateOk = false;
|
|
root.authValidateWarn = false;
|
|
root.authValidateRunning = true;
|
|
authValidateProcess.command = ["dms", "auth", "validate", "--path", path, "--json"];
|
|
authValidateProcess.running = true;
|
|
}
|
|
|
|
function lockFingerprintDescription() {
|
|
switch (SettingsData.lockFingerprintReason) {
|
|
case "ready":
|
|
return SettingsData.enableFprint ? I18n.tr("Authentication changes apply automatically.") : I18n.tr("Use fingerprint authentication for the lock screen.");
|
|
case "missing_enrollment":
|
|
if (SettingsData.enableFprint)
|
|
return I18n.tr("Enabled, but no prints are enrolled yet. Authentication changes apply automatically once you enroll fingerprints.");
|
|
return I18n.tr("Fingerprint reader detected, but no prints are enrolled yet. You can enable this now and enroll later.");
|
|
case "missing_reader":
|
|
return SettingsData.enableFprint ? 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.");
|
|
default:
|
|
return SettingsData.enableFprint ? I18n.tr("Enabled, but fingerprint availability could not be confirmed.") : I18n.tr("Fingerprint availability could not be confirmed.");
|
|
}
|
|
}
|
|
|
|
function lockU2fDescription() {
|
|
switch (SettingsData.lockU2fReason) {
|
|
case "ready":
|
|
return SettingsData.enableU2f ? I18n.tr("Authentication changes apply automatically.") : I18n.tr("Use a security key for lock screen authentication.", "lock screen U2F security key setting");
|
|
case "missing_key_registration":
|
|
if (SettingsData.enableU2f)
|
|
return I18n.tr("Enabled, but no registered security key was found yet. Authentication changes apply automatically once your key is registered or your U2F config is updated.");
|
|
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.");
|
|
default:
|
|
return SettingsData.enableU2f ? I18n.tr("Enabled, but security-key availability could not be confirmed.") : I18n.tr("Security-key availability could not be confirmed.");
|
|
}
|
|
}
|
|
|
|
function refreshAuthDetection() {
|
|
SettingsData.refreshAuthAvailability();
|
|
}
|
|
|
|
Component.onCompleted: {
|
|
refreshAuthDetection();
|
|
refreshAuthServices();
|
|
}
|
|
onVisibleChanged: {
|
|
if (visible) {
|
|
refreshAuthDetection();
|
|
refreshAuthServices();
|
|
}
|
|
}
|
|
|
|
FileBrowserModal {
|
|
id: videoBrowserModal
|
|
browserTitle: I18n.tr("Select Video or Folder")
|
|
browserIcon: "movie"
|
|
browserType: "video"
|
|
showHiddenFiles: false
|
|
fileExtensions: ["*.mp4", "*.mkv", "*.webm", "*.mov", "*.avi", "*.m4v"]
|
|
onFileSelected: path => SettingsData.set("lockScreenVideoPath", path)
|
|
}
|
|
|
|
Process {
|
|
id: authListServicesProcess
|
|
command: ["dms", "auth", "list-services", "--json"]
|
|
running: false
|
|
|
|
property string collected: ""
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: authListServicesProcess.collected = text || ""
|
|
}
|
|
|
|
onExited: exitCode => {
|
|
if (exitCode !== 0) {
|
|
root.authServices = [];
|
|
return;
|
|
}
|
|
try {
|
|
const data = JSON.parse(authListServicesProcess.collected);
|
|
root.authServices = (data && Array.isArray(data.services)) ? data.services.filter(s => s.hasAuth) : [];
|
|
} catch (e) {
|
|
root.authServices = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
Process {
|
|
id: authValidateProcess
|
|
running: false
|
|
|
|
property string collected: ""
|
|
|
|
stdout: StdioCollector {
|
|
onStreamFinished: authValidateProcess.collected = text || ""
|
|
}
|
|
|
|
onExited: exitCode => {
|
|
root.authValidateRunning = false;
|
|
var data = null;
|
|
try {
|
|
data = JSON.parse(authValidateProcess.collected);
|
|
} catch (e) {
|
|
data = null;
|
|
}
|
|
if (!data) {
|
|
root.authValidateOk = false;
|
|
root.authValidateWarn = false;
|
|
root.authValidateMessage = I18n.tr("Could not run validation. Ensure DMS is installed and dms is in PATH.");
|
|
return;
|
|
}
|
|
if (data.valid) {
|
|
SettingsData.set("lockPamPath", root.authPendingApplyPath);
|
|
SettingsData.set("lockPamInlineFprint", data.inlineFingerprint === true);
|
|
SettingsData.set("lockPamInlineU2f", data.inlineU2f === true);
|
|
const warns = Array.isArray(data.warnings) ? data.warnings : [];
|
|
root.authValidateOk = true;
|
|
root.authValidateWarn = warns.length > 0;
|
|
root.authValidateMessage = warns.length > 0 ? (I18n.tr("Applied with warnings:") + "\n" + warns.join("\n")) : I18n.tr("Applied successfully.");
|
|
} else {
|
|
root.authValidateOk = false;
|
|
root.authValidateWarn = false;
|
|
const errs = Array.isArray(data.errors) ? data.errors : [];
|
|
const missing = Array.isArray(data.missingModules) ? data.missingModules : [];
|
|
var msg = I18n.tr("Invalid — not applied.");
|
|
if (errs.length > 0)
|
|
msg = msg + "\n" + errs.join("\n");
|
|
if (missing.length > 0)
|
|
msg = msg + "\n" + I18n.tr("Missing modules: ") + missing.join(", ");
|
|
root.authValidateMessage = msg;
|
|
}
|
|
}
|
|
}
|
|
|
|
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: "lock"
|
|
title: I18n.tr("Lock Screen layout")
|
|
settingKey: "lockLayout"
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowPowerActions"
|
|
tags: ["lock", "screen", "power", "actions", "shutdown", "reboot"]
|
|
text: I18n.tr("Show Power Actions", "Enable power action icon on the lock screen window")
|
|
checked: SettingsData.lockScreenShowPowerActions
|
|
onToggled: checked => SettingsData.set("lockScreenShowPowerActions", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowSystemIcons"
|
|
tags: ["lock", "screen", "system", "icons", "status"]
|
|
text: I18n.tr("Show System Icons", "Enable system status icons on the lock screen window")
|
|
checked: SettingsData.lockScreenShowSystemIcons
|
|
onToggled: checked => SettingsData.set("lockScreenShowSystemIcons", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowTime"
|
|
tags: ["lock", "screen", "time", "clock", "display"]
|
|
text: I18n.tr("Show System Time", "Enable system time display on the lock screen window")
|
|
checked: SettingsData.lockScreenShowTime
|
|
onToggled: checked => SettingsData.set("lockScreenShowTime", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowDate"
|
|
tags: ["lock", "screen", "date", "calendar", "display"]
|
|
text: I18n.tr("Show System Date", "Enable system date display on the lock screen window")
|
|
checked: SettingsData.lockScreenShowDate
|
|
onToggled: checked => SettingsData.set("lockScreenShowDate", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowProfileImage"
|
|
tags: ["lock", "screen", "profile", "image", "avatar", "picture"]
|
|
text: I18n.tr("Show Profile Image", "Enable profile image display on the lock screen window")
|
|
checked: SettingsData.lockScreenShowProfileImage
|
|
onToggled: checked => SettingsData.set("lockScreenShowProfileImage", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowPasswordField"
|
|
tags: ["lock", "screen", "password", "field", "input", "visible"]
|
|
text: I18n.tr("Show Password Field", "Enable password field display on the lock screen window")
|
|
description: I18n.tr("If the field is hidden, it will appear as soon as a key is pressed.")
|
|
checked: SettingsData.lockScreenShowPasswordField
|
|
onToggled: checked => SettingsData.set("lockScreenShowPasswordField", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenShowMediaPlayer"
|
|
tags: ["lock", "screen", "media", "player", "music", "mpris"]
|
|
text: I18n.tr("Show Media Player", "Enable media player controls on the lock screen window")
|
|
checked: SettingsData.lockScreenShowMediaPlayer
|
|
onToggled: checked => SettingsData.set("lockScreenShowMediaPlayer", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "lockScreenNotificationMode"
|
|
tags: ["lock", "screen", "notification", "notifications", "privacy"]
|
|
text: I18n.tr("Notification Display", "lock screen notification privacy setting")
|
|
description: I18n.tr("Control what notification information is shown on the lock screen", "lock screen notification privacy setting")
|
|
options: [I18n.tr("Disabled", "lock screen notification mode option"), I18n.tr("Count Only", "lock screen notification mode option"), I18n.tr("App Names", "lock screen notification mode option"), I18n.tr("Full Content", "lock screen notification mode option")]
|
|
currentValue: options[SettingsData.lockScreenNotificationMode] || options[0]
|
|
onValueChanged: value => {
|
|
const idx = options.indexOf(value);
|
|
if (idx >= 0) {
|
|
SettingsData.set("lockScreenNotificationMode", idx);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "palette"
|
|
title: I18n.tr("Lock Screen Appearance")
|
|
settingKey: "lockAppearance"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Customize the font and background of the lock screen, or leave empty to use your theme font and desktop wallpaper. Changes apply instantly.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
SettingsFontDropdownRow {
|
|
settingKey: "lockScreenFontFamily"
|
|
tags: ["lock", "screen", "font", "typography"]
|
|
text: I18n.tr("Lock screen font")
|
|
description: I18n.tr("Font used for the clock and date on the lock screen")
|
|
currentFont: SettingsData.lockScreenFontFamily || ""
|
|
onFontSelected: family => SettingsData.set("lockScreenFontFamily", family)
|
|
}
|
|
|
|
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 lock screen, or leave empty to use your desktop wallpaper.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
SettingsWallpaperPicker {
|
|
width: parent.width
|
|
path: SettingsData.lockScreenWallpaperPath
|
|
fillMode: SettingsData.lockScreenWallpaperFillMode
|
|
browserTitle: I18n.tr("Select lock screen background image")
|
|
fillModeSettingKey: "lockScreenWallpaperFillMode"
|
|
fillModeTags: ["lock", "screen", "wallpaper", "background", "fill"]
|
|
onPathSelected: path => SettingsData.set("lockScreenWallpaperPath", path)
|
|
onFillModeSelected: mode => SettingsData.set("lockScreenWallpaperFillMode", mode)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "key"
|
|
title: I18n.tr("Authentication Source")
|
|
settingKey: "lockAuthSource"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Auto resolves the system auth stack automatically. Picking a source pins the lock screen to that PAM file.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "lockPamPath"
|
|
tags: ["lock", "screen", "pam", "authentication", "source", "service"]
|
|
text: I18n.tr("Authentication Source")
|
|
description: SettingsData.lockPamPath !== "" ? I18n.tr("Pinned to ") + SettingsData.lockPamPath : I18n.tr("Which PAM service the lock screen uses to authenticate")
|
|
options: root.authOptions
|
|
currentValue: root.authCurrentValue
|
|
onValueChanged: value => {
|
|
if (value === root.authAutoLabel) {
|
|
root.authShowCustom = false;
|
|
root.applyAutoAuthSource();
|
|
return;
|
|
}
|
|
if (value === root.authCustomLabel) {
|
|
root.authShowCustom = true;
|
|
return;
|
|
}
|
|
root.authShowCustom = false;
|
|
var svc = root.authServices.find(s => root.authServiceLabel(s) === value);
|
|
if (svc)
|
|
root.validateAndApplyAuthSource(svc.path);
|
|
}
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
visible: root.authShowCustom || root.authCurrentValue === root.authCustomLabel
|
|
|
|
StyledText {
|
|
text: I18n.tr("Enter the absolute path to a PAM service file, then validate and apply.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DankTextField {
|
|
id: customPamField
|
|
width: parent.width - validatePamButton.width - Theme.spacingS
|
|
placeholderText: "/etc/pam.d/my-service"
|
|
text: SettingsData.lockPamPath
|
|
backgroundColor: Theme.surfaceContainerHighest
|
|
}
|
|
|
|
DankButton {
|
|
id: validatePamButton
|
|
text: I18n.tr("Validate & Apply")
|
|
enabled: !root.authValidateRunning && customPamField.text.trim() !== ""
|
|
onClicked: root.validateAndApplyAuthSource(customPamField.text.trim())
|
|
}
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
width: parent.width
|
|
height: Math.min(160, authStatusText.implicitHeight + Theme.spacingM * 2)
|
|
radius: Theme.cornerRadius
|
|
color: Theme.surfaceContainerHighest
|
|
visible: root.authValidateMessage !== ""
|
|
|
|
StyledText {
|
|
id: authStatusText
|
|
anchors.fill: parent
|
|
anchors.margins: Theme.spacingM
|
|
text: root.authValidateMessage
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
font.family: "monospace"
|
|
color: !root.authValidateOk ? Theme.error : (root.authValidateWarn ? Theme.warning : Theme.surfaceVariantText)
|
|
wrapMode: Text.Wrap
|
|
verticalAlignment: Text.AlignTop
|
|
}
|
|
}
|
|
|
|
StyledText {
|
|
visible: (SettingsData.lockPamInlineFprint && SettingsData.enableFprint) || (SettingsData.lockPamInlineU2f && SettingsData.enableU2f)
|
|
text: I18n.tr("The chosen PAM stack already prompts for fingerprint or security key, so DMS's separate prompts are suppressed to avoid a double prompt.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.warning
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
topPadding: Theme.spacingS
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "lock"
|
|
title: I18n.tr("Lock Screen behaviour")
|
|
settingKey: "lockBehavior"
|
|
|
|
StyledText {
|
|
text: I18n.tr("loginctl not available - lock integration requires DMS socket connection")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.warning
|
|
visible: !SessionService.loginctlAvailable
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "loginctlLockIntegration"
|
|
tags: ["lock", "screen", "loginctl", "dbus", "integration", "external"]
|
|
text: I18n.tr("Enable loginctl lock integration")
|
|
description: I18n.tr("Bind lock screen to dbus signals from loginctl. Disable if using an external lock screen")
|
|
checked: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
|
enabled: SessionService.loginctlAvailable
|
|
onToggled: checked => {
|
|
if (!SessionService.loginctlAvailable)
|
|
return;
|
|
SettingsData.set("loginctlLockIntegration", checked);
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockBeforeSuspend"
|
|
tags: ["lock", "screen", "suspend", "sleep", "automatic"]
|
|
text: I18n.tr("Lock before suspend")
|
|
description: I18n.tr("Automatically lock the screen when the system prepares to suspend")
|
|
checked: SettingsData.lockBeforeSuspend
|
|
visible: SessionService.loginctlAvailable && SettingsData.loginctlLockIntegration
|
|
onToggled: checked => SettingsData.set("lockBeforeSuspend", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenPowerOffMonitorsOnLock"
|
|
tags: ["lock", "screen", "monitor", "display", "dpms", "power"]
|
|
text: I18n.tr("Power off monitors on lock")
|
|
description: I18n.tr("Turn off all displays immediately when the lock screen activates")
|
|
checked: SettingsData.lockScreenPowerOffMonitorsOnLock
|
|
onToggled: checked => SettingsData.set("lockScreenPowerOffMonitorsOnLock", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockAtStartup"
|
|
tags: ["lock", "screen", "startup", "start", "boot", "login", "automatic"]
|
|
text: I18n.tr("Lock at startup")
|
|
description: I18n.tr("Automatically lock the screen when DMS starts")
|
|
checked: SettingsData.lockAtStartup
|
|
onToggled: checked => SettingsData.set("lockAtStartup", checked)
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Lock screen 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
|
|
topPadding: Theme.spacingS
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "enableFprint"
|
|
tags: ["lock", "screen", "fingerprint", "authentication", "biometric", "fprint"]
|
|
text: I18n.tr("Enable fingerprint authentication")
|
|
description: root.lockFingerprintDescription()
|
|
descriptionColor: SettingsData.lockFingerprintReason === "ready" ? Theme.surfaceVariantText : Theme.warning
|
|
checked: SettingsData.enableFprint
|
|
enabled: root.lockFprintToggleAvailable
|
|
onToggled: checked => SettingsData.set("enableFprint", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "enableU2f"
|
|
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "fido", "authentication", "hardware"]
|
|
text: I18n.tr("Enable security key authentication", "Enable FIDO2/U2F hardware security key for lock screen")
|
|
description: root.lockU2fDescription()
|
|
descriptionColor: SettingsData.lockU2fReason === "ready" ? Theme.surfaceVariantText : Theme.warning
|
|
checked: SettingsData.enableU2f
|
|
enabled: root.lockU2fToggleAvailable
|
|
onToggled: checked => SettingsData.set("enableU2f", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "u2fMode"
|
|
tags: ["lock", "screen", "u2f", "yubikey", "security", "key", "mode", "factor", "second"]
|
|
text: I18n.tr("Security key mode", "lock screen U2F security key mode setting")
|
|
description: I18n.tr("'Alternative' lets the key unlock on its own. 'Second factor' requires password or fingerprint first, then the key.", "lock screen U2F security key mode setting")
|
|
visible: SettingsData.enableU2f
|
|
options: [I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method"), I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint")]
|
|
currentValue: SettingsData.u2fMode === "and" ? I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint") : I18n.tr("Alternative (OR)", "U2F mode option: key works as standalone unlock method")
|
|
onValueChanged: value => {
|
|
if (value === I18n.tr("Second Factor (AND)", "U2F mode option: key required after password or fingerprint"))
|
|
SettingsData.set("u2fMode", "and");
|
|
else
|
|
SettingsData.set("u2fMode", "or");
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "movie"
|
|
title: I18n.tr("Video Screensaver")
|
|
settingKey: "videoScreensaver"
|
|
|
|
StyledText {
|
|
visible: !MultimediaService.available
|
|
text: I18n.tr("QtMultimedia is not available - video screensaver requires qt multimedia services")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.warning
|
|
width: parent.width
|
|
wrapMode: Text.WordWrap
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenVideoEnabled"
|
|
tags: ["lock", "screen", "video", "screensaver", "animation", "movie"]
|
|
text: I18n.tr("Enable Video Screensaver")
|
|
description: I18n.tr("Play a video when the screen locks.")
|
|
enabled: MultimediaService.available
|
|
checked: SettingsData.lockScreenVideoEnabled
|
|
onToggled: checked => SettingsData.set("lockScreenVideoEnabled", checked)
|
|
}
|
|
|
|
Column {
|
|
width: parent.width
|
|
spacing: Theme.spacingXS
|
|
visible: SettingsData.lockScreenVideoEnabled && MultimediaService.available
|
|
|
|
StyledText {
|
|
text: I18n.tr("Video Path")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Path to a video file or folder containing videos")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.outlineVariant
|
|
wrapMode: Text.WordWrap
|
|
width: parent.width
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingS
|
|
|
|
DankTextField {
|
|
id: videoPathField
|
|
width: parent.width - browseVideoButton.width - Theme.spacingS
|
|
placeholderText: I18n.tr("/path/to/videos")
|
|
text: SettingsData.lockScreenVideoPath
|
|
backgroundColor: Theme.surfaceContainerHighest
|
|
onTextChanged: {
|
|
if (text !== SettingsData.lockScreenVideoPath) {
|
|
SettingsData.set("lockScreenVideoPath", text);
|
|
}
|
|
}
|
|
}
|
|
|
|
DankButton {
|
|
id: browseVideoButton
|
|
text: I18n.tr("Browse")
|
|
onClicked: videoBrowserModal.open()
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "lockScreenVideoCycling"
|
|
tags: ["lock", "screen", "video", "screensaver", "cycling", "random", "shuffle"]
|
|
text: I18n.tr("Automatic Cycling")
|
|
description: I18n.tr("Pick a different random video each time from the same folder")
|
|
visible: SettingsData.lockScreenVideoEnabled && MultimediaService.available
|
|
enabled: MultimediaService.available
|
|
checked: SettingsData.lockScreenVideoCycling
|
|
onToggled: checked => SettingsData.set("lockScreenVideoCycling", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
width: parent.width
|
|
iconName: "monitor"
|
|
title: I18n.tr("Lock Screen Display")
|
|
settingKey: "lockDisplay"
|
|
|
|
StyledText {
|
|
text: I18n.tr("Choose which monitors show the lock screen interface. Other monitors will display a solid color for OLED burn-in protection.")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
|
|
SettingsDisplayPicker {
|
|
width: parent.width
|
|
displayPreferences: SettingsData.screenPreferences?.lockScreen || ["all"]
|
|
onPreferencesChanged: prefs => {
|
|
var p = SettingsData.screenPreferences || {};
|
|
var updated = Object.assign({}, p);
|
|
updated["lockScreen"] = prefs;
|
|
SettingsData.set("screenPreferences", updated);
|
|
}
|
|
}
|
|
|
|
Row {
|
|
width: parent.width
|
|
spacing: Theme.spacingM
|
|
visible: {
|
|
var prefs = SettingsData.screenPreferences?.lockScreen;
|
|
return Array.isArray(prefs) && !prefs.includes("all") && prefs.length > 0;
|
|
}
|
|
|
|
Column {
|
|
width: parent.width - inactiveColorPreview.width - Theme.spacingM
|
|
spacing: Theme.spacingXS
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
StyledText {
|
|
text: I18n.tr("Inactive Monitor Color")
|
|
font.pixelSize: Theme.fontSizeMedium
|
|
color: Theme.surfaceText
|
|
}
|
|
|
|
StyledText {
|
|
text: I18n.tr("Color displayed on monitors without the lock screen")
|
|
font.pixelSize: Theme.fontSizeSmall
|
|
color: Theme.surfaceVariantText
|
|
width: parent.width
|
|
wrapMode: Text.Wrap
|
|
}
|
|
}
|
|
|
|
Rectangle {
|
|
id: inactiveColorPreview
|
|
width: 48
|
|
height: 48
|
|
radius: Theme.cornerRadius
|
|
color: SettingsData.lockScreenInactiveColor
|
|
border.color: Theme.outline
|
|
border.width: 1
|
|
anchors.verticalCenter: parent.verticalCenter
|
|
|
|
MouseArea {
|
|
anchors.fill: parent
|
|
cursorShape: Qt.PointingHandCursor
|
|
onClicked: {
|
|
if (!PopoutService.colorPickerModal)
|
|
return;
|
|
PopoutService.colorPickerModal.selectedColor = SettingsData.lockScreenInactiveColor;
|
|
PopoutService.colorPickerModal.pickerTitle = I18n.tr("Inactive Monitor Color");
|
|
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
|
|
SettingsData.set("lockScreenInactiveColor", selectedColor);
|
|
};
|
|
PopoutService.colorPickerModal.show();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|