1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2026-08-01 19:18:28 -04:00
Files
DankMaterialShell/quickshell/Modules/Settings/LockScreenTab.qml
T
purian23 b169fe0d77 feat: new PAM auth management & settings in greeter/lockscreen
- Introduce external management of greetd PAM
- New functionality to validate and apply custom PAM service paths in lockscreen

Port 1.5
2026-07-15 10:38:06 -04:00

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();
}
}
}
}
}
}
}
}