mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-10-21 18:54:01 -04:00
* fix(settings): show what frame mode controls With frame mode on, many settings did nothing or were hidden while still applying, and nothing said so. The frame takes its color and opacity from the first non-island bar, but that bar's Surface card is hidden while the frame styles it, so neither had a visible control. Add Background and Override/Opacity rows to the Appearance Frame card, bound to the same frame bar the renderer reads. Settings the frame provides in connected mode get a "Managed by Frame in Connected Mode" note (Interface style border, corner style, background blur and dock opacity, shadow targets, launcher border); settings with no effect get "Disabled by Frame Mode" (maximize detection, bar shadow, the bar's overlay layer in separate mode). Bar size, the bar's overlay layer in connected mode, dock Over fullscreen and the controls of overlay-layer docks and launchers are shown again, since they still apply. Notification Over fullscreen shows as forced on when the frame covers every display, and launcher rows the Spotlight style ignores are disabled. Also relabel the frame Width row as Thickness, raise the inset padding maximum to its automatic range, and drop a FrameWindow handler for the removed frameOpacity signal. No new strings. * settings: show what frame mode controls, fix review findings --------- Co-authored-by: purian23 <[email protected]>
467 lines
19 KiB
QML
467 lines
19 KiB
QML
import QtQuick
|
|
import Quickshell
|
|
import qs.Common
|
|
import qs.Services
|
|
import qs.DCommon.Widgets
|
|
import qs.Modules.Settings.Widgets
|
|
|
|
Column {
|
|
id: root
|
|
|
|
property var parentModal: null
|
|
|
|
readonly property bool frameForcesOverlay: Quickshell.screens.length > 0 && Quickshell.screens.every(screen => CompositorService.frameWindowVisibleForScreen(screen))
|
|
|
|
width: parent?.width ?? 0
|
|
spacing: Theme.spacingL
|
|
|
|
function indexedRules(predicate) {
|
|
return (SettingsData.notificationRules || []).map((rule, index) => ({
|
|
rule: rule,
|
|
index: index
|
|
})).filter(entry => predicate(entry.rule));
|
|
}
|
|
|
|
readonly property var dndBypassRules: indexedRules(rule => rule.bypassDnd === true)
|
|
|
|
readonly property var timeoutOptions: [
|
|
{
|
|
text: I18n.tr("Never"),
|
|
value: 0
|
|
},
|
|
{
|
|
text: I18n.duration(1),
|
|
value: 1000
|
|
},
|
|
{
|
|
text: I18n.duration(3),
|
|
value: 3000
|
|
},
|
|
{
|
|
text: I18n.duration(5),
|
|
value: 5000
|
|
},
|
|
{
|
|
text: I18n.duration(8),
|
|
value: 8000
|
|
},
|
|
{
|
|
text: I18n.duration(10),
|
|
value: 10000
|
|
},
|
|
{
|
|
text: I18n.duration(15),
|
|
value: 15000
|
|
},
|
|
{
|
|
text: I18n.duration(30),
|
|
value: 30000
|
|
},
|
|
{
|
|
text: I18n.duration(60),
|
|
value: 60000
|
|
},
|
|
{
|
|
text: I18n.duration(120),
|
|
value: 120000
|
|
},
|
|
{
|
|
text: I18n.duration(300),
|
|
value: 300000
|
|
},
|
|
{
|
|
text: I18n.duration(600),
|
|
value: 600000
|
|
}
|
|
]
|
|
|
|
function getTimeoutText(value) {
|
|
if (value === undefined || value === null || isNaN(value))
|
|
return I18n.duration(5);
|
|
for (let i = 0; i < timeoutOptions.length; i++) {
|
|
if (timeoutOptions[i].value === value)
|
|
return timeoutOptions[i].text;
|
|
}
|
|
if (value === 0)
|
|
return I18n.tr("Never");
|
|
return I18n.duration(value / 1000);
|
|
}
|
|
|
|
function applyTimeout(key, text) {
|
|
const option = timeoutOptions.find(opt => opt.text === text);
|
|
if (option)
|
|
SettingsData.set(key, option.value);
|
|
}
|
|
|
|
SettingsCard {
|
|
iconName: "notifications"
|
|
title: I18n.tr("Popups", "notification settings card title, popup notifications")
|
|
settingKey: "notificationPopups"
|
|
|
|
headerActions: DButton {
|
|
text: I18n.tr("Preview")
|
|
buttonHeight: Theme.buttonHeightXS
|
|
onClicked: NotificationService.sendTestNotifications()
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationPopupPosition"
|
|
tags: ["notification", "popup", "position", "screen", "location"]
|
|
text: I18n.tr("Position")
|
|
currentValue: {
|
|
if (SettingsData.notificationPopupPosition === -1)
|
|
return I18n.tr("Top Center", "screen position option");
|
|
switch (SettingsData.notificationPopupPosition) {
|
|
case SettingsData.Position.Top:
|
|
return I18n.tr("Top Right", "screen position option");
|
|
case SettingsData.Position.Bottom:
|
|
return I18n.tr("Bottom Left", "screen position option");
|
|
case SettingsData.Position.Left:
|
|
return I18n.tr("Top Left", "screen position option");
|
|
case SettingsData.Position.Right:
|
|
return I18n.tr("Bottom Right", "screen position option");
|
|
case SettingsData.Position.BottomCenter:
|
|
return I18n.tr("Bottom Center", "screen position option");
|
|
default:
|
|
return I18n.tr("Top Right", "screen position option");
|
|
}
|
|
}
|
|
options: [I18n.tr("Top Right", "screen position option"), I18n.tr("Top Left", "screen position option"), I18n.tr("Top Center", "screen position option"), I18n.tr("Bottom Center", "screen position option"), I18n.tr("Bottom Right", "screen position option"), I18n.tr("Bottom Left", "screen position option")]
|
|
onValueChanged: value => {
|
|
switch (value) {
|
|
case I18n.tr("Top Right", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", SettingsData.Position.Top);
|
|
break;
|
|
case I18n.tr("Top Left", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", SettingsData.Position.Left);
|
|
break;
|
|
case I18n.tr("Top Center", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", -1);
|
|
break;
|
|
case I18n.tr("Bottom Center", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", SettingsData.Position.BottomCenter);
|
|
break;
|
|
case I18n.tr("Bottom Right", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", SettingsData.Position.Right);
|
|
break;
|
|
case I18n.tr("Bottom Left", "screen position option"):
|
|
SettingsData.set("notificationPopupPosition", SettingsData.Position.Bottom);
|
|
break;
|
|
}
|
|
NotificationService.sendTestNotifications();
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationFocusedMonitor"
|
|
tags: ["notification", "popup", "focused", "monitor", "display", "screen", "active"]
|
|
text: I18n.tr("Focused display only")
|
|
checked: SettingsData.notificationFocusedMonitor
|
|
onToggled: checked => SettingsData.set("notificationFocusedMonitor", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationOverlayEnabled"
|
|
tags: ["notification", "overlay", "fullscreen", "priority"]
|
|
text: I18n.tr("Over fullscreen")
|
|
enabled: !root.frameForcesOverlay
|
|
checked: root.frameForcesOverlay || SettingsData.notificationOverlayEnabled
|
|
onToggled: checked => SettingsData.set("notificationOverlayEnabled", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationCompactMode"
|
|
tags: ["notification", "compact", "size", "display", "mode"]
|
|
text: I18n.tr("Compact")
|
|
checked: SettingsData.notificationCompactMode
|
|
onToggled: checked => SettingsData.set("notificationCompactMode", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationDedupeEnabled"
|
|
tags: ["notification", "duplicate", "dedupe", "stack", "coalesce", "repeat"]
|
|
text: I18n.tr("Suppress duplicates")
|
|
checked: SettingsData.notificationDedupeEnabled
|
|
onToggled: checked => SettingsData.set("notificationDedupeEnabled", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationPopupBodyInvokesAction"
|
|
tags: ["notification", "popup", "click", "body", "action", "invoke", "expand", "open"]
|
|
text: I18n.tr("Body click runs default action")
|
|
checked: SettingsData.notificationPopupBodyInvokesAction
|
|
onToggled: checked => SettingsData.set("notificationPopupBodyInvokesAction", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
title: I18n.tr("Timeouts", "notification settings card title, popup dismiss times per urgency")
|
|
settingKey: "notificationTimeouts"
|
|
tags: ["notification", "timeout", "duration", "expire", "priority"]
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationTimeoutLow"
|
|
tags: ["notification", "timeout", "low", "priority", "duration"]
|
|
text: I18n.tr("Low")
|
|
currentValue: root.getTimeoutText(SettingsData.notificationTimeoutLow)
|
|
options: root.timeoutOptions.map(opt => opt.text)
|
|
onValueChanged: value => root.applyTimeout("notificationTimeoutLow", value)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationTimeoutNormal"
|
|
tags: ["notification", "timeout", "normal", "priority", "duration"]
|
|
text: I18n.tr("Normal")
|
|
currentValue: root.getTimeoutText(SettingsData.notificationTimeoutNormal)
|
|
options: root.timeoutOptions.map(opt => opt.text)
|
|
onValueChanged: value => root.applyTimeout("notificationTimeoutNormal", value)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationTimeoutCritical"
|
|
tags: ["notification", "timeout", "critical", "priority", "duration"]
|
|
text: I18n.tr("Critical", "adjective, critical notification urgency row label")
|
|
currentValue: root.getTimeoutText(SettingsData.notificationTimeoutCritical)
|
|
options: root.timeoutOptions.map(opt => opt.text)
|
|
onValueChanged: value => root.applyTimeout("notificationTimeoutCritical", value)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationIgnoreAppTimeout"
|
|
tags: ["notification", "timeout", "expire", "app", "override", "duration"]
|
|
text: I18n.tr("Ignore app-requested timeout")
|
|
checked: SettingsData.notificationIgnoreAppTimeout
|
|
onToggled: checked => SettingsData.set("notificationIgnoreAppTimeout", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationShowTimeoutBar"
|
|
tags: ["notification", "timeout", "progress", "bar", "timer", "countdown"]
|
|
text: I18n.tr("Timeout progress bar")
|
|
checked: SettingsData.notificationShowTimeoutBar
|
|
onToggled: checked => SettingsData.set("notificationShowTimeoutBar", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
iconName: "notifications_off"
|
|
title: I18n.tr("Do not disturb")
|
|
settingKey: "doNotDisturb"
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "doNotDisturb"
|
|
tags: ["notification", "dnd", "mute", "silent", "suppress"]
|
|
text: I18n.tr("Do not disturb")
|
|
checked: SessionData.doNotDisturb
|
|
onToggled: checked => SessionData.setDoNotDisturb(checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationDndWhileScreenSharing"
|
|
tags: ["notification", "dnd", "screenshare", "sharing", "privacy"]
|
|
text: I18n.tr("Automatically enable while screen sharing", "do not disturb auto enable while screen sharing toggle")
|
|
checked: SettingsData.notificationDndWhileScreenSharing
|
|
onToggled: checked => SettingsData.set("notificationDndWhileScreenSharing", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationDndAllowCritical"
|
|
tags: ["notification", "dnd", "critical", "priority", "urgent", "bypass"]
|
|
text: I18n.tr("Allow critical")
|
|
description: I18n.tr("Critical notifications still pop up", "do not disturb allow critical toggle description")
|
|
checked: SettingsData.notificationDndAllowCritical
|
|
onToggled: checked => SettingsData.set("notificationDndAllowCritical", checked)
|
|
}
|
|
|
|
SettingsRow {
|
|
subtitle: I18n.tr("Right-click a notification and choose \"Allow in Do Not Disturb\" to add an app")
|
|
}
|
|
|
|
Repeater {
|
|
model: root.dndBypassRules
|
|
|
|
delegate: SettingsRow {
|
|
required property var modelData
|
|
|
|
title: modelData.rule?.pattern || I18n.tr("Unknown")
|
|
singleLineTitle: true
|
|
|
|
DActionButton {
|
|
iconName: "delete"
|
|
iconColor: Theme.error
|
|
tooltipText: I18n.tr("Remove")
|
|
onClicked: SettingsData.removeNotificationRule(modelData.index)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
settingKey: "notificationLinks"
|
|
|
|
SettingsNavRow {
|
|
iconName: "rule_settings"
|
|
title: I18n.tr("Rules")
|
|
hint: SettingsTabs.page("notification_rules")?.hint ?? ""
|
|
onClicked: keyboard => root.parentModal?.navigateTo("notification_rules", keyboard)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
iconName: "visibility_off"
|
|
title: I18n.tr("Privacy", "notification settings card title")
|
|
settingKey: "notificationPrivacy"
|
|
tags: ["notification", "privacy", "hide", "content", "lock"]
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationPopupPrivacyMode"
|
|
tags: ["notification", "popup", "privacy", "body", "content", "hide"]
|
|
text: I18n.tr("Privacy mode")
|
|
description: I18n.tr("Popups hide the message until expanded", "notification privacy mode toggle description")
|
|
checked: SettingsData.notificationPopupPrivacyMode
|
|
onToggled: checked => SettingsData.set("notificationPopupPrivacyMode", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
readonly property var lockNotifications: SettingsData.lockWidgetInstance("lockNotifications")
|
|
readonly property int currentMode: !lockNotifications || lockNotifications.enabled === false ? 0 : (lockNotifications.config?.mode ?? 1)
|
|
|
|
settingKey: "lockNotificationsMode"
|
|
tags: ["lock", "screen", "notification", "notifications", "privacy"]
|
|
visible: lockNotifications !== null
|
|
text: I18n.tr("Lock screen content")
|
|
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[currentMode] || options[0]
|
|
onValueChanged: value => {
|
|
const idx = options.indexOf(value);
|
|
if (idx < 0)
|
|
return;
|
|
SettingsData.updateDesktopWidgetInstance(lockNotifications.id, {
|
|
enabled: idx > 0
|
|
});
|
|
if (idx > 0)
|
|
SettingsData.updateDesktopWidgetInstanceConfig(lockNotifications.id, {
|
|
mode: idx
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
iconName: "history"
|
|
title: I18n.tr("History")
|
|
settingKey: "notificationHistory"
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationHistoryEnabled"
|
|
tags: ["notification", "history", "enable", "disable", "save"]
|
|
text: I18n.tr("History")
|
|
checked: SettingsData.notificationHistoryEnabled
|
|
onToggled: checked => SettingsData.set("notificationHistoryEnabled", checked)
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationHistoryMaxAgeDays"
|
|
tags: ["notification", "history", "max", "age", "days", "retention"]
|
|
text: I18n.tr("Retention", "notification history setting label, how long entries are kept")
|
|
readonly property var retentionDays: [0, 1, 3, 7, 14, 30]
|
|
options: retentionDays.map(days => days === 0 ? I18n.tr("Forever", "notification history retention option") : I18n.duration(days * 86400))
|
|
currentValue: {
|
|
const index = retentionDays.indexOf(SettingsData.notificationHistoryMaxAgeDays);
|
|
return index >= 0 ? options[index] : I18n.duration(SettingsData.notificationHistoryMaxAgeDays * 86400);
|
|
}
|
|
onValueChanged: value => {
|
|
const index = options.indexOf(value);
|
|
SettingsData.set("notificationHistoryMaxAgeDays", index >= 0 ? retentionDays[index] : 7);
|
|
}
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
settingKey: "notificationHistoryMaxCount"
|
|
tags: ["notification", "history", "max", "count", "limit"]
|
|
text: I18n.tr("Maximum entries")
|
|
value: SettingsData.notificationHistoryMaxCount
|
|
minimum: 10
|
|
maximum: 200
|
|
step: 10
|
|
unit: ""
|
|
onSliderValueChanged: newValue => SettingsData.set("notificationHistoryMaxCount", newValue)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationHistorySaveLow"
|
|
tags: ["notification", "history", "save", "low", "priority"]
|
|
text: I18n.tr("Low")
|
|
checked: SettingsData.notificationHistorySaveLow
|
|
onToggled: checked => SettingsData.set("notificationHistorySaveLow", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationHistorySaveNormal"
|
|
tags: ["notification", "history", "save", "normal", "priority"]
|
|
text: I18n.tr("Normal")
|
|
checked: SettingsData.notificationHistorySaveNormal
|
|
onToggled: checked => SettingsData.set("notificationHistorySaveNormal", checked)
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationHistorySaveCritical"
|
|
tags: ["notification", "history", "save", "critical", "priority"]
|
|
text: I18n.tr("Critical")
|
|
checked: SettingsData.notificationHistorySaveCritical
|
|
onToggled: checked => SettingsData.set("notificationHistorySaveCritical", checked)
|
|
}
|
|
}
|
|
|
|
SettingsCard {
|
|
iconName: "palette"
|
|
title: I18n.tr("Appearance")
|
|
settingKey: "notificationAppearance"
|
|
tags: ["notification", "font", "size", "layers", "animation", "speed"]
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationSummaryFontSize"
|
|
tags: ["notification", "font", "summary", "size"]
|
|
text: I18n.tr("Summary font size")
|
|
options: [I18n.tr("Unset"), "10", "12", "14", "16", "18"]
|
|
currentValue: (SettingsData.notificationSummaryFontSize || I18n.tr("Unset")).toString()
|
|
onValueChanged: value => {
|
|
SettingsData.set("notificationSummaryFontSize", Number(value === I18n.tr("Unset") ? 0 : value));
|
|
NotificationService.sendTestNotifications();
|
|
}
|
|
}
|
|
|
|
SettingsDropdownRow {
|
|
settingKey: "notificationBodyFontSize"
|
|
tags: ["notification", "font", "body", "size"]
|
|
text: I18n.tr("Body font size")
|
|
options: [I18n.tr("Unset"), "10", "12", "14", "16", "18"]
|
|
currentValue: (SettingsData.notificationBodyFontSize || I18n.tr("Unset")).toString()
|
|
onValueChanged: value => {
|
|
SettingsData.set("notificationBodyFontSize", Number(value === I18n.tr("Unset") ? 0 : value));
|
|
NotificationService.sendTestNotifications();
|
|
}
|
|
}
|
|
|
|
SettingsToggleRow {
|
|
settingKey: "notificationForegroundLayers"
|
|
tags: ["notification", "foreground", "layers", "surface", "blur", "glass", "contrast", "cards"]
|
|
text: I18n.tr("Foreground layers")
|
|
checked: SettingsData.notificationForegroundLayers ?? true
|
|
onToggled: checked => SettingsData.set("notificationForegroundLayers", checked)
|
|
}
|
|
|
|
SettingsSliderRow {
|
|
settingKey: "notificationAnimationDuration"
|
|
tags: ["notification", "animation", "duration", "speed"]
|
|
text: I18n.tr("Animation duration")
|
|
minimumLabel: I18n.tr("Off")
|
|
minimum: 0
|
|
maximum: 800
|
|
value: SettingsData.notificationAnimationDuration
|
|
unit: "ms"
|
|
onSliderValueChanged: newValue => SettingsData.set("notificationAnimationDuration", newValue)
|
|
}
|
|
}
|
|
}
|