NSettingsIndicator: add default setting indicator (#1080)

N*Widgets: show NSettingsIndicator if settings are not default
This commit is contained in:
Ly-sec
2025-12-20 14:55:09 +01:00
parent c8b76c7b90
commit 9cb6613308
20 changed files with 793 additions and 411 deletions
+4
View File
@@ -882,6 +882,10 @@
"title": "Session Menu"
},
"settings": {
"indicator": {
"default-value": "Default: {value}",
"system-default": "System Default"
},
"about": {
"contributors": {
"section": {
+88
View File
@@ -151,6 +151,30 @@ Singleton {
watchChanges: false
}
// FileView to load default settings for comparison
FileView {
id: defaultSettingsFileView
path: Quickshell.shellDir + "/Assets/settings-default.json"
printErrors: false
watchChanges: false
}
// Cached default settings object
property var _defaultSettings: null
// Load default settings when file is loaded
Connections {
target: defaultSettingsFileView
function onLoaded() {
try {
root._defaultSettings = JSON.parse(defaultSettingsFileView.text());
} catch (e) {
Logger.w("Settings", "Failed to parse default settings file: " + e);
root._defaultSettings = null;
}
}
}
JsonAdapter {
id: adapter
@@ -651,6 +675,70 @@ Singleton {
return path;
}
// -----------------------------------------------------
// Get default value for a setting path (e.g., "general.scaleRatio" or "bar.position")
// Returns undefined if not found
function getDefaultValue(path) {
if (!root._defaultSettings) {
return undefined;
}
var parts = path.split(".");
var current = root._defaultSettings;
for (var i = 0; i < parts.length; i++) {
if (current === undefined || current === null) {
return undefined;
}
current = current[parts[i]];
}
return current;
}
// -----------------------------------------------------
// Compare current value with default value
// Returns true if values differ, false if they match or default is not found
function isValueChanged(path, currentValue) {
var defaultValue = getDefaultValue(path);
if (defaultValue === undefined) {
return false; // Can't compare if default not found
}
// Deep comparison for objects and arrays
if (typeof currentValue === "object" && typeof defaultValue === "object") {
return JSON.stringify(currentValue) !== JSON.stringify(defaultValue);
}
// Simple comparison for primitives
return currentValue !== defaultValue;
}
// -----------------------------------------------------
// Format default value for tooltip display
// Returns a human-readable string representation of the default value
function formatDefaultValueForTooltip(path) {
var defaultValue = getDefaultValue(path);
if (defaultValue === undefined) {
return "";
}
// Format based on type
if (typeof defaultValue === "boolean") {
return defaultValue ? "true" : "false";
} else if (typeof defaultValue === "number") {
return defaultValue.toString();
} else if (typeof defaultValue === "string") {
return defaultValue === "" ? "(empty)" : defaultValue;
} else if (Array.isArray(defaultValue)) {
return defaultValue.length === 0 ? "(empty)" : "[" + defaultValue.length + " items]";
} else if (typeof defaultValue === "object") {
return "(object)";
}
return String(defaultValue);
}
// -----------------------------------------------------
// Public function to trigger immediate settings saving
function saveImmediate() {
+16 -5
View File
@@ -93,13 +93,10 @@ ColumnLayout {
spacing: Style.marginXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.audio.volumes.input-volume.label")
description: I18n.tr("settings.audio.volumes.input-volume.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.audio.volumes.input-volume.label")
description: I18n.tr("settings.audio.volumes.input-volume.description")
from: 0
to: Settings.data.audio.volumeOverdrive ? 1.5 : 1.0
value: AudioService.inputVolume
@@ -136,6 +133,8 @@ ColumnLayout {
value: Settings.data.audio.volumeStep
stepSize: 1
suffix: "%"
isSettings: true
defaultValue: Settings.getDefaultValue("audio.volumeStep")
onValueChanged: Settings.data.audio.volumeStep = value
}
}
@@ -149,6 +148,8 @@ ColumnLayout {
label: I18n.tr("settings.audio.volumes.volume-overdrive.label")
description: I18n.tr("settings.audio.volumes.volume-overdrive.description")
checked: Settings.data.audio.volumeOverdrive
isSettings: true
defaultValue: Settings.getDefaultValue("audio.volumeOverdrive")
onToggled: checked => Settings.data.audio.volumeOverdrive = checked
}
}
@@ -159,6 +160,8 @@ ColumnLayout {
description: I18n.tr("settings.audio.external-mixer.description")
placeholderText: I18n.tr("settings.audio.external-mixer.placeholder")
text: Settings.data.audio.externalMixer
isSettings: true
defaultValue: Settings.getDefaultValue("audio.externalMixer")
onTextChanged: Settings.data.audio.externalMixer = text
}
@@ -262,6 +265,8 @@ ColumnLayout {
description: I18n.tr("settings.audio.media.primary-player.description")
placeholderText: I18n.tr("settings.audio.media.primary-player.placeholder")
text: Settings.data.audio.preferredPlayer
isSettings: true
defaultValue: Settings.getDefaultValue("audio.preferredPlayer")
onTextChanged: {
Settings.data.audio.preferredPlayer = text;
MediaService.updateCurrentPlayer();
@@ -373,6 +378,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.audio.visualizerType
isSettings: true
defaultValue: Settings.getDefaultValue("audio.visualizerType")
onSelected: key => Settings.data.audio.visualizerType = key
}
@@ -390,6 +397,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.audio.visualizerQuality
isSettings: true
defaultValue: Settings.getDefaultValue("audio.visualizerQuality")
onSelected: key => Settings.data.audio.visualizerQuality = key
}
@@ -441,6 +450,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.audio.cavaFrameRate
isSettings: true
defaultValue: Settings.getDefaultValue("audio.cavaFrameRate")
onSelected: key => Settings.data.audio.cavaFrameRate = key
}
}
+24 -17
View File
@@ -53,6 +53,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.bar.position
isSettings: true
defaultValue: Settings.getDefaultValue("bar.position")
onSelected: key => Settings.data.bar.position = key
}
@@ -79,6 +81,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.bar.density
isSettings: true
defaultValue: Settings.getDefaultValue("bar.density")
onSelected: key => Settings.data.bar.density = key
}
@@ -87,6 +91,8 @@ ColumnLayout {
label: I18n.tr("settings.bar.appearance.transparent.label")
description: I18n.tr("settings.bar.appearance.transparent.description")
checked: Settings.data.bar.transparent
isSettings: true
defaultValue: Settings.getDefaultValue("bar.transparent")
onToggled: checked => Settings.data.bar.transparent = checked
}
@@ -95,6 +101,8 @@ ColumnLayout {
label: I18n.tr("settings.bar.appearance.show-outline.label")
description: I18n.tr("settings.bar.appearance.show-outline.description")
checked: Settings.data.bar.showOutline
isSettings: true
defaultValue: Settings.getDefaultValue("bar.showOutline")
onToggled: checked => Settings.data.bar.showOutline = checked
}
@@ -103,6 +111,8 @@ ColumnLayout {
label: I18n.tr("settings.bar.appearance.show-capsule.label")
description: I18n.tr("settings.bar.appearance.show-capsule.description")
checked: Settings.data.bar.showCapsule
isSettings: true
defaultValue: Settings.getDefaultValue("bar.showCapsule")
onToggled: checked => Settings.data.bar.showCapsule = checked
}
@@ -111,17 +121,16 @@ ColumnLayout {
spacing: Style.marginXXS
visible: Settings.data.bar.showCapsule
NLabel {
label: I18n.tr("settings.bar.appearance.capsule-opacity.label")
description: I18n.tr("settings.bar.appearance.capsule-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.bar.appearance.capsule-opacity.label")
description: I18n.tr("settings.bar.appearance.capsule-opacity.description")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.capsuleOpacity
isSettings: true
defaultValue: Settings.getDefaultValue("bar.capsuleOpacity")
onMoved: value => Settings.data.bar.capsuleOpacity = value
text: Math.floor(Settings.data.bar.capsuleOpacity * 100) + "%"
}
@@ -132,6 +141,8 @@ ColumnLayout {
label: I18n.tr("settings.bar.appearance.floating.label")
description: I18n.tr("settings.bar.appearance.floating.description")
checked: Settings.data.bar.floating
isSettings: true
defaultValue: Settings.getDefaultValue("bar.floating")
onToggled: checked => {
Settings.data.bar.floating = checked;
}
@@ -143,6 +154,8 @@ ColumnLayout {
description: I18n.tr("settings.bar.appearance.outer-corners.description")
checked: Settings.data.bar.outerCorners
visible: !Settings.data.bar.floating
isSettings: true
defaultValue: Settings.getDefaultValue("bar.outerCorners")
onToggled: checked => Settings.data.bar.outerCorners = checked
}
@@ -164,18 +177,15 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginXXS
NText {
text: I18n.tr("settings.bar.appearance.margins.vertical")
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.bar.appearance.margins.vertical")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginVertical
isSettings: true
defaultValue: Settings.getDefaultValue("bar.marginVertical")
onMoved: value => Settings.data.bar.marginVertical = value
text: Math.round(Settings.data.bar.marginVertical * 100) + "%"
}
@@ -184,18 +194,15 @@ ColumnLayout {
ColumnLayout {
spacing: Style.marginXXS
NText {
text: I18n.tr("settings.bar.appearance.margins.horizontal")
pointSize: Style.fontSizeXS
color: Color.mOnSurfaceVariant
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.bar.appearance.margins.horizontal")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.bar.marginHorizontal
isSettings: true
defaultValue: Settings.getDefaultValue("bar.marginHorizontal")
onMoved: value => Settings.data.bar.marginHorizontal = value
text: Math.ceil(Settings.data.bar.marginHorizontal * 100) + "%"
}
@@ -37,6 +37,8 @@ ColumnLayout {
label: I18n.tr("settings.desktop-widgets.enabled.label")
description: I18n.tr("settings.desktop-widgets.enabled.description")
checked: Settings.data.desktopWidgets.enabled
isSettings: true
defaultValue: Settings.getDefaultValue("desktopWidgets.enabled")
onToggled: checked => Settings.data.desktopWidgets.enabled = checked
}
+67 -77
View File
@@ -34,6 +34,8 @@ ColumnLayout {
label: I18n.tr("settings.dock.enabled.label")
description: I18n.tr("settings.dock.enabled.description")
checked: Settings.data.dock.enabled
isSettings: true
defaultValue: Settings.getDefaultValue("dock.enabled")
onToggled: checked => Settings.data.dock.enabled = checked
}
@@ -57,106 +59,86 @@ ColumnLayout {
}
]
currentKey: Settings.data.dock.displayMode
isSettings: true
defaultValue: Settings.getDefaultValue("dock.displayMode")
onSelected: key => {
Settings.data.dock.displayMode = key;
}
}
ColumnLayout {
NValueSlider {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.background-opacity.label")
description: I18n.tr("settings.dock.appearance.background-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
onMoved: value => Settings.data.dock.backgroundOpacity = value
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
}
label: I18n.tr("settings.dock.appearance.background-opacity.label")
description: I18n.tr("settings.dock.appearance.background-opacity.description")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.backgroundOpacity
isSettings: true
defaultValue: Settings.getDefaultValue("dock.backgroundOpacity")
onMoved: value => Settings.data.dock.backgroundOpacity = value
text: Math.floor(Settings.data.dock.backgroundOpacity * 100) + "%"
}
ColumnLayout {
NValueSlider {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.dead-opacity.label")
description: I18n.tr("settings.dock.appearance.dead-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.deadOpacity
onMoved: value => Settings.data.dock.deadOpacity = value
text: Math.floor(Settings.data.dock.deadOpacity * 100) + "%"
}
label: I18n.tr("settings.dock.appearance.dead-opacity.label")
description: I18n.tr("settings.dock.appearance.dead-opacity.description")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.dock.deadOpacity
isSettings: true
defaultValue: Settings.getDefaultValue("dock.deadOpacity")
onMoved: value => Settings.data.dock.deadOpacity = value
text: Math.floor(Settings.data.dock.deadOpacity * 100) + "%"
}
ColumnLayout {
NValueSlider {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.floating-distance.label")
description: I18n.tr("settings.dock.appearance.floating-distance.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 4
stepSize: 0.01
value: Settings.data.dock.floatingRatio
onMoved: value => Settings.data.dock.floatingRatio = value
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
}
label: I18n.tr("settings.dock.appearance.floating-distance.label")
description: I18n.tr("settings.dock.appearance.floating-distance.description")
from: 0
to: 4
stepSize: 0.01
value: Settings.data.dock.floatingRatio
isSettings: true
defaultValue: Settings.getDefaultValue("dock.floatingRatio")
onMoved: value => Settings.data.dock.floatingRatio = value
text: Math.floor(Settings.data.dock.floatingRatio * 100) + "%"
}
ColumnLayout {
NValueSlider {
visible: Settings.data.dock.enabled
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.icon-size.label")
description: I18n.tr("settings.dock.appearance.icon-size.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.dock.size
onMoved: value => Settings.data.dock.size = value
text: Math.floor(Settings.data.dock.size * 100) + "%"
}
label: I18n.tr("settings.dock.appearance.icon-size.label")
description: I18n.tr("settings.dock.appearance.icon-size.description")
from: 0
to: 2
stepSize: 0.01
value: Settings.data.dock.size
isSettings: true
defaultValue: Settings.getDefaultValue("dock.size")
onMoved: value => Settings.data.dock.size = value
text: Math.floor(Settings.data.dock.size * 100) + "%"
}
ColumnLayout {
NValueSlider {
visible: Settings.data.dock.enabled && Settings.data.dock.displayMode === "auto_hide"
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.dock.appearance.hide-show-speed.label")
description: I18n.tr("settings.dock.appearance.hide-show-speed.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0.1
to: 2.0
stepSize: 0.01
value: Settings.data.dock.animationSpeed
onMoved: value => Settings.data.dock.animationSpeed = value
text: (Settings.data.dock.animationSpeed * 100).toFixed(0) + "%"
}
label: I18n.tr("settings.dock.appearance.hide-show-speed.label")
description: I18n.tr("settings.dock.appearance.hide-show-speed.description")
from: 0.1
to: 2.0
stepSize: 0.01
value: Settings.data.dock.animationSpeed
isSettings: true
defaultValue: Settings.getDefaultValue("dock.animationSpeed")
onMoved: value => Settings.data.dock.animationSpeed = value
text: (Settings.data.dock.animationSpeed * 100).toFixed(0) + "%"
}
NToggle {
@@ -164,6 +146,8 @@ ColumnLayout {
label: I18n.tr("settings.dock.appearance.inactive-indicators.label")
description: I18n.tr("settings.dock.appearance.inactive-indicators.description")
checked: Settings.data.dock.inactiveIndicators
isSettings: true
defaultValue: Settings.getDefaultValue("dock.inactiveIndicators")
onToggled: checked => Settings.data.dock.inactiveIndicators = checked
}
@@ -172,6 +156,8 @@ ColumnLayout {
label: I18n.tr("settings.dock.appearance.pinned-static.label")
description: I18n.tr("settings.dock.appearance.pinned-static.description")
checked: Settings.data.dock.pinnedStatic
isSettings: true
defaultValue: Settings.getDefaultValue("dock.pinnedStatic")
onToggled: checked => Settings.data.dock.pinnedStatic = checked
}
@@ -180,6 +166,8 @@ ColumnLayout {
label: I18n.tr("settings.dock.monitors.only-same-monitor.label")
description: I18n.tr("settings.dock.monitors.only-same-monitor.description")
checked: Settings.data.dock.onlySameOutput
isSettings: true
defaultValue: Settings.getDefaultValue("dock.onlySameOutput")
onToggled: checked => Settings.data.dock.onlySameOutput = checked
}
@@ -189,6 +177,8 @@ ColumnLayout {
label: I18n.tr("settings.dock.appearance.colorize-icons.label")
description: I18n.tr("settings.dock.appearance.colorize-icons.description")
checked: Settings.data.dock.colorizeIcons
isSettings: true
defaultValue: Settings.getDefaultValue("dock.colorizeIcons")
onToggled: checked => Settings.data.dock.colorizeIcons = checked
}
+59 -56
View File
@@ -91,6 +91,9 @@ ColumnLayout {
searchPlaceholder: I18n.tr("settings.general.fonts.default.search-placeholder")
popupHeight: 420
minimumWidth: 300
isSettings: true
defaultValue: Settings.getDefaultValue("ui.fontDefault")
settingsPath: "ui.fontDefault"
onSelected: key => Settings.data.ui.fontDefault = key
}
@@ -103,79 +106,76 @@ ColumnLayout {
searchPlaceholder: I18n.tr("settings.general.fonts.monospace.search-placeholder")
popupHeight: 320
minimumWidth: 300
isSettings: true
defaultValue: Settings.getDefaultValue("ui.fontFixed")
settingsPath: "ui.fontFixed"
onSelected: key => Settings.data.ui.fontFixed = key
}
ColumnLayout {
NLabel {
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.general.fonts.default.scale.label")
description: I18n.tr("settings.general.fonts.default.scale.description")
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontDefaultScale
isSettings: true
defaultValue: Settings.getDefaultValue("ui.fontDefaultScale")
onMoved: value => Settings.data.ui.fontDefaultScale = value
text: Math.floor(Settings.data.ui.fontDefaultScale * 100) + "%"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NValueSlider {
Layout.fillWidth: true
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontDefaultScale
onMoved: value => Settings.data.ui.fontDefaultScale = value
text: Math.floor(Settings.data.ui.fontDefaultScale * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontDefaultScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontDefaultScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
ColumnLayout {
NLabel {
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.general.fonts.monospace.scale.label")
description: I18n.tr("settings.general.fonts.monospace.scale.description")
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontFixedScale
isSettings: true
defaultValue: Settings.getDefaultValue("ui.fontFixedScale")
onMoved: value => Settings.data.ui.fontFixedScale = value
text: Math.floor(Settings.data.ui.fontFixedScale * 100) + "%"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NValueSlider {
Layout.fillWidth: true
from: 0.75
to: 1.25
stepSize: 0.01
value: Settings.data.ui.fontFixedScale
onMoved: value => Settings.data.ui.fontFixedScale = value
text: Math.floor(Settings.data.ui.fontFixedScale * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontFixedScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.general.fonts.reset-scaling")
onClicked: Settings.data.ui.fontFixedScale = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
@@ -202,6 +202,8 @@ ColumnLayout {
Layout.fillWidth: true
label: I18n.tr("settings.general.language.select.label")
description: I18n.tr("settings.general.language.select.description")
isSettings: true
defaultValue: Settings.getDefaultValue("general.language")
model: [
{
"key": "",
@@ -214,6 +216,7 @@ ColumnLayout {
};
}))
currentKey: Settings.data.general.language
settingsPath: "general.language"
onSelected: key => {
// Need to change language on next frame using "callLater" or it will pull the rug below our feet: the NComboBox would be rebuilt immediately before it can close properly.
Qt.callLater(() => {
@@ -37,6 +37,8 @@ ColumnLayout {
label: I18n.tr("settings.notifications.settings.enabled.label")
description: I18n.tr("settings.notifications.settings.enabled.description")
checked: Settings.data.notifications.enabled !== false
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.enabled")
onToggled: checked => Settings.data.notifications.enabled = checked
}
@@ -77,6 +79,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.notifications.location || "top_right"
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.location") || "top_right"
onSelected: key => Settings.data.notifications.location = key
}
@@ -84,28 +88,24 @@ ColumnLayout {
label: I18n.tr("settings.notifications.settings.always-on-top.label")
description: I18n.tr("settings.notifications.settings.always-on-top.description")
checked: Settings.data.notifications.overlayLayer
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.overlayLayer")
onToggled: checked => Settings.data.notifications.overlayLayer = checked
}
// Background Opacity
ColumnLayout {
spacing: Style.marginXXS
NValueSlider {
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.notifications.settings.background-opacity.label")
description: I18n.tr("settings.notifications.settings.background-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 100
stepSize: 1
value: Settings.data.notifications.backgroundOpacity * 100
onMoved: value => Settings.data.notifications.backgroundOpacity = value / 100
text: Math.round(Settings.data.notifications.backgroundOpacity * 100) + "%"
}
label: I18n.tr("settings.notifications.settings.background-opacity.label")
description: I18n.tr("settings.notifications.settings.background-opacity.description")
from: 0
to: 100
stepSize: 1
value: Settings.data.notifications.backgroundOpacity * 100
isSettings: true
defaultValue: (Settings.getDefaultValue("notifications.backgroundOpacity") || 1) * 100
onMoved: value => Settings.data.notifications.backgroundOpacity = value / 100
text: Math.round(Settings.data.notifications.backgroundOpacity * 100) + "%"
}
// OSD settings moved to the dedicated OSD tab
@@ -159,6 +159,8 @@ ColumnLayout {
description: I18n.tr("settings.notifications.sounds.enabled.description")
checked: Settings.data.notifications?.sounds?.enabled ?? false
visible: SoundService.multimediaAvailable
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.sounds.enabled")
onToggled: checked => Settings.data.notifications.sounds.enabled = checked
}
@@ -168,17 +170,16 @@ ColumnLayout {
Layout.fillWidth: true
visible: SoundService.multimediaAvailable && (Settings.data.notifications?.sounds?.enabled ?? false)
NLabel {
label: I18n.tr("settings.notifications.sounds.volume.label")
description: I18n.tr("settings.notifications.sounds.volume.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.notifications.sounds.volume.label")
description: I18n.tr("settings.notifications.sounds.volume.description")
from: 0
to: 100
stepSize: 1
value: (Settings.data.notifications?.sounds?.volume ?? 0.5) * 100
isSettings: true
defaultValue: (Settings.getDefaultValue("notifications.sounds.volume") || 0.5) * 100
onMoved: value => Settings.data.notifications.sounds.volume = value / 100
text: Math.round((Settings.data.notifications?.sounds?.volume ?? 0.5) * 100) + "%"
}
@@ -191,6 +192,8 @@ ColumnLayout {
label: I18n.tr("settings.notifications.sounds.separate.label")
description: I18n.tr("settings.notifications.sounds.separate.description")
checked: Settings.data.notifications?.sounds?.separateSounds ?? false
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.sounds.separateSounds")
onToggled: checked => Settings.data.notifications.sounds.separateSounds = checked
}
@@ -332,126 +335,110 @@ ColumnLayout {
label: I18n.tr("settings.notifications.duration.respect-expire.label")
description: I18n.tr("settings.notifications.duration.respect-expire.description")
checked: Settings.data.notifications.respectExpireTimeout
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.respectExpireTimeout")
onToggled: checked => Settings.data.notifications.respectExpireTimeout = checked
}
// Low Urgency Duration
ColumnLayout {
spacing: Style.marginXXS
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NLabel {
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.notifications.duration.low-urgency.label")
description: I18n.tr("settings.notifications.duration.low-urgency.description")
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.lowUrgencyDuration
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.lowUrgencyDuration")
onMoved: value => Settings.data.notifications.lowUrgencyDuration = value
text: Settings.data.notifications.lowUrgencyDuration + "s"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.lowUrgencyDuration
onMoved: value => Settings.data.notifications.lowUrgencyDuration = value
text: Settings.data.notifications.lowUrgencyDuration + "s"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.lowUrgencyDuration = 3
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Normal Urgency Duration
ColumnLayout {
spacing: Style.marginXXS
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NLabel {
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.notifications.duration.normal-urgency.label")
description: I18n.tr("settings.notifications.duration.normal-urgency.description")
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.normalUrgencyDuration
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.normalUrgencyDuration")
onMoved: value => Settings.data.notifications.normalUrgencyDuration = value
text: Settings.data.notifications.normalUrgencyDuration + "s"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.normalUrgencyDuration
onMoved: value => Settings.data.notifications.normalUrgencyDuration = value
text: Settings.data.notifications.normalUrgencyDuration + "s"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.normalUrgencyDuration = 8
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Critical Urgency Duration
ColumnLayout {
spacing: Style.marginXXS
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NLabel {
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.notifications.duration.critical-urgency.label")
description: I18n.tr("settings.notifications.duration.critical-urgency.description")
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.criticalUrgencyDuration
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.criticalUrgencyDuration")
onMoved: value => Settings.data.notifications.criticalUrgencyDuration = value
text: Settings.data.notifications.criticalUrgencyDuration + "s"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
from: 1
to: 30
stepSize: 1
value: Settings.data.notifications.criticalUrgencyDuration
onMoved: value => Settings.data.notifications.criticalUrgencyDuration = value
text: Settings.data.notifications.criticalUrgencyDuration + "s"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.notifications.duration.reset")
onClicked: Settings.data.notifications.criticalUrgencyDuration = 15
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
@@ -509,6 +496,8 @@ ColumnLayout {
label: I18n.tr("settings.notifications.toast.keyboard.label")
description: I18n.tr("settings.notifications.toast.keyboard.description")
checked: Settings.data.notifications.enableKeyboardLayoutToast
isSettings: true
defaultValue: Settings.getDefaultValue("notifications.enableKeyboardLayoutToast")
onToggled: checked => Settings.data.notifications.enableKeyboardLayoutToast = checked
}
}
+14 -10
View File
@@ -77,6 +77,8 @@ ColumnLayout {
}
]
currentKey: Settings.data.osd.location || "top_right"
isSettings: true
defaultValue: Settings.getDefaultValue("osd.location") || "top_right"
onSelected: key => Settings.data.osd.location = key
}
}
@@ -101,6 +103,8 @@ ColumnLayout {
label: I18n.tr("settings.osd.enabled.label")
description: I18n.tr("settings.osd.enabled.description")
checked: Settings.data.osd.enabled
isSettings: true
defaultValue: Settings.getDefaultValue("osd.enabled")
onToggled: checked => Settings.data.osd.enabled = checked
}
@@ -108,35 +112,35 @@ ColumnLayout {
label: I18n.tr("settings.osd.always-on-top.label")
description: I18n.tr("settings.osd.always-on-top.description")
checked: Settings.data.osd.overlayLayer
isSettings: true
defaultValue: Settings.getDefaultValue("osd.overlayLayer")
onToggled: checked => Settings.data.osd.overlayLayer = checked
}
NLabel {
label: I18n.tr("settings.osd.background-opacity.label", "Background opacity")
description: I18n.tr("settings.osd.background-opacity.description", "Controls the transparency of the OSD background.")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.osd.background-opacity.label", "Background opacity")
description: I18n.tr("settings.osd.background-opacity.description", "Controls the transparency of the OSD background.")
from: 0
to: 100
stepSize: 1
value: Settings.data.osd.backgroundOpacity * 100
isSettings: true
defaultValue: (Settings.getDefaultValue("osd.backgroundOpacity") || 1) * 100
onMoved: value => Settings.data.osd.backgroundOpacity = value / 100
text: Math.round(Settings.data.osd.backgroundOpacity * 100) + "%"
}
NLabel {
label: I18n.tr("settings.osd.duration.auto-hide.label")
description: I18n.tr("settings.osd.duration.auto-hide.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.osd.duration.auto-hide.label")
description: I18n.tr("settings.osd.duration.auto-hide.description")
from: 500
to: 5000
stepSize: 100
value: Settings.data.osd.autoHideMs
isSettings: true
defaultValue: Settings.getDefaultValue("osd.autoHideMs")
onMoved: value => Settings.data.osd.autoHideMs = value
text: Math.round(Settings.data.osd.autoHideMs / 1000 * 10) / 10 + "s"
}
@@ -24,6 +24,8 @@ ColumnLayout {
label: I18n.tr("settings.system-monitor.enable-nvidia-gpu.label")
description: I18n.tr("settings.system-monitor.enable-nvidia-gpu.description")
checked: Settings.data.systemMonitor.enableNvidiaGpu
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.enableNvidiaGpu")
onToggled: checked => Settings.data.systemMonitor.enableNvidiaGpu = checked
}
@@ -36,6 +38,8 @@ ColumnLayout {
label: I18n.tr("settings.system-monitor.use-custom-highlight-colors.label")
description: I18n.tr("settings.system-monitor.use-custom-highlight-colors.description")
checked: Settings.data.systemMonitor.useCustomColors
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.useCustomColors")
onToggled: {
// If enabling custom colors and no custom color is saved, persist current theme colors
if (checked) {
@@ -136,6 +140,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.cpuWarningThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.cpuWarningThreshold")
onValueChanged: {
Settings.data.systemMonitor.cpuWarningThreshold = value;
// Ensure critical >= warning
@@ -164,6 +170,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.cpuCriticalThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.cpuCriticalThreshold")
onValueChanged: Settings.data.systemMonitor.cpuCriticalThreshold = value
suffix: "%"
}
@@ -186,6 +194,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.cpuPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.cpuPollingInterval")
onValueChanged: Settings.data.systemMonitor.cpuPollingInterval = value
suffix: " ms"
}
@@ -221,6 +231,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.tempWarningThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.tempWarningThreshold")
onValueChanged: {
Settings.data.systemMonitor.tempWarningThreshold = value;
if (Settings.data.systemMonitor.tempCriticalThreshold < value) {
@@ -248,6 +260,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.tempCriticalThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.tempCriticalThreshold")
onValueChanged: Settings.data.systemMonitor.tempCriticalThreshold = value
suffix: "°C"
}
@@ -270,6 +284,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.tempPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.tempPollingInterval")
onValueChanged: Settings.data.systemMonitor.tempPollingInterval = value
suffix: " ms"
}
@@ -307,6 +323,8 @@ ColumnLayout {
to: 120
stepSize: 5
value: Settings.data.systemMonitor.gpuWarningThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.gpuWarningThreshold")
onValueChanged: {
Settings.data.systemMonitor.gpuWarningThreshold = value;
if (Settings.data.systemMonitor.gpuCriticalThreshold < value) {
@@ -334,6 +352,8 @@ ColumnLayout {
to: 120
stepSize: 5
value: Settings.data.systemMonitor.gpuCriticalThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.gpuCriticalThreshold")
onValueChanged: Settings.data.systemMonitor.gpuCriticalThreshold = value
suffix: "°C"
}
@@ -356,6 +376,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.gpuPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.gpuPollingInterval")
onValueChanged: Settings.data.systemMonitor.gpuPollingInterval = value
suffix: " ms"
}
@@ -391,6 +413,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.memWarningThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.memWarningThreshold")
onValueChanged: {
Settings.data.systemMonitor.memWarningThreshold = value;
if (Settings.data.systemMonitor.memCriticalThreshold < value) {
@@ -418,6 +442,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.memCriticalThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.memCriticalThreshold")
onValueChanged: Settings.data.systemMonitor.memCriticalThreshold = value
suffix: "%"
}
@@ -440,6 +466,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.memPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.memPollingInterval")
onValueChanged: Settings.data.systemMonitor.memPollingInterval = value
suffix: " ms"
}
@@ -475,6 +503,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.diskWarningThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.diskWarningThreshold")
onValueChanged: {
Settings.data.systemMonitor.diskWarningThreshold = value;
if (Settings.data.systemMonitor.diskCriticalThreshold < value) {
@@ -502,6 +532,8 @@ ColumnLayout {
to: 100
stepSize: 5
value: Settings.data.systemMonitor.diskCriticalThreshold
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.diskCriticalThreshold")
onValueChanged: Settings.data.systemMonitor.diskCriticalThreshold = value
suffix: "%"
}
@@ -524,6 +556,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.diskPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.diskPollingInterval")
onValueChanged: Settings.data.systemMonitor.diskPollingInterval = value
suffix: " ms"
}
@@ -559,6 +593,8 @@ ColumnLayout {
to: 10000
stepSize: 250
value: Settings.data.systemMonitor.networkPollingInterval
isSettings: true
defaultValue: Settings.getDefaultValue("systemMonitor.networkPollingInterval")
onValueChanged: Settings.data.systemMonitor.networkPollingInterval = value
suffix: " ms"
}
+101 -110
View File
@@ -23,6 +23,8 @@ ColumnLayout {
label: I18n.tr("settings.user-interface.panels-attached-to-bar.label")
description: I18n.tr("settings.user-interface.panels-attached-to-bar.description")
checked: Settings.data.ui.panelsAttachedToBar
isSettings: true
defaultValue: Settings.getDefaultValue("ui.panelsAttachedToBar")
onToggled: checked => Settings.data.ui.panelsAttachedToBar = checked
}
@@ -31,6 +33,8 @@ ColumnLayout {
label: I18n.tr("settings.user-interface.allow-panels-without-bar.label")
description: I18n.tr("settings.user-interface.allow-panels-without-bar.description")
checked: Settings.data.general.allowPanelsOnScreenWithoutBar
isSettings: true
defaultValue: Settings.getDefaultValue("general.allowPanelsOnScreenWithoutBar")
onToggled: checked => Settings.data.general.allowPanelsOnScreenWithoutBar = checked
}
@@ -54,49 +58,39 @@ ColumnLayout {
}
]
currentKey: Settings.data.ui.settingsPanelMode
isSettings: true
defaultValue: Settings.getDefaultValue("ui.settingsPanelMode")
onSelected: key => Settings.data.ui.settingsPanelMode = key
}
// Panel Background Opacity
ColumnLayout {
spacing: Style.marginXXS
NValueSlider {
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.user-interface.panel-background-opacity.label")
description: I18n.tr("settings.user-interface.panel-background-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0.4
to: 1
stepSize: 0.01
value: Settings.data.ui.panelBackgroundOpacity
onMoved: value => Settings.data.ui.panelBackgroundOpacity = value
text: Math.floor(Settings.data.ui.panelBackgroundOpacity * 100) + "%"
}
label: I18n.tr("settings.user-interface.panel-background-opacity.label")
description: I18n.tr("settings.user-interface.panel-background-opacity.description")
from: 0.4
to: 1
stepSize: 0.01
value: Settings.data.ui.panelBackgroundOpacity
isSettings: true
defaultValue: Settings.getDefaultValue("ui.panelBackgroundOpacity")
onMoved: value => Settings.data.ui.panelBackgroundOpacity = value
text: Math.floor(Settings.data.ui.panelBackgroundOpacity * 100) + "%"
}
// Dim desktop opacity
ColumnLayout {
spacing: Style.marginXXS
NValueSlider {
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.user-interface.dimmer-opacity.label")
description: I18n.tr("settings.user-interface.dimmer-opacity.description")
}
NValueSlider {
Layout.fillWidth: true
from: 0
to: 1
stepSize: 0.01
value: Settings.data.general.dimmerOpacity
onMoved: value => Settings.data.general.dimmerOpacity = value
text: Math.floor(Settings.data.general.dimmerOpacity * 100) + "%"
}
label: I18n.tr("settings.user-interface.dimmer-opacity.label")
description: I18n.tr("settings.user-interface.dimmer-opacity.description")
from: 0
to: 1
stepSize: 0.01
value: Settings.data.general.dimmerOpacity
isSettings: true
defaultValue: Settings.getDefaultValue("general.dimmerOpacity")
onMoved: value => Settings.data.general.dimmerOpacity = value
text: Math.floor(Settings.data.general.dimmerOpacity * 100) + "%"
}
NDivider {
@@ -109,6 +103,8 @@ ColumnLayout {
label: I18n.tr("settings.user-interface.tooltips.label")
description: I18n.tr("settings.user-interface.tooltips.description")
checked: Settings.data.ui.tooltipsEnabled
isSettings: true
defaultValue: Settings.getDefaultValue("ui.tooltipsEnabled")
onToggled: checked => Settings.data.ui.tooltipsEnabled = checked
}
@@ -116,6 +112,8 @@ ColumnLayout {
label: I18n.tr("settings.user-interface.shadows.label")
description: I18n.tr("settings.user-interface.shadows.description")
checked: Settings.data.general.enableShadows
isSettings: true
defaultValue: Settings.getDefaultValue("general.enableShadows")
onToggled: checked => Settings.data.general.enableShadows = checked
}
@@ -173,6 +171,8 @@ ColumnLayout {
})
currentKey: Settings.data.general.shadowDirection
isSettings: true
defaultValue: Settings.getDefaultValue("general.shadowDirection")
onSelected: function (key) {
var opt = shadowOptionsMap[key];
@@ -195,21 +195,20 @@ ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.user-interface.scaling.label")
description: I18n.tr("settings.user-interface.scaling.description")
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.user-interface.scaling.label")
description: I18n.tr("settings.user-interface.scaling.description")
from: 0.8
to: 1.2
stepSize: 0.05
value: Settings.data.general.scaleRatio
isSettings: true
defaultValue: Settings.getDefaultValue("general.scaleRatio")
onMoved: value => Settings.data.general.scaleRatio = value
text: Math.floor(Settings.data.general.scaleRatio * 100) + "%"
}
@@ -220,7 +219,7 @@ ColumnLayout {
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.scaling.reset-scaling")
onClicked: Settings.data.general.scaleRatio = 1.0
@@ -232,83 +231,71 @@ ColumnLayout {
}
// Container Border Radius
ColumnLayout {
spacing: Style.marginXXS
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NLabel {
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.user-interface.box-border-radius.label")
description: I18n.tr("settings.user-interface.box-border-radius.description")
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.radiusRatio
isSettings: true
defaultValue: Settings.getDefaultValue("general.radiusRatio")
onMoved: value => Settings.data.general.radiusRatio = value
text: Math.floor(Settings.data.general.radiusRatio * 100) + "%"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NValueSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.radiusRatio
onMoved: value => Settings.data.general.radiusRatio = value
text: Math.floor(Settings.data.general.radiusRatio * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.box-border-radius.reset")
onClicked: Settings.data.general.radiusRatio = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.box-border-radius.reset")
onClicked: Settings.data.general.radiusRatio = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
// Control Border Radius (for UI components)
ColumnLayout {
spacing: Style.marginXXS
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NLabel {
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.user-interface.control-border-radius.label")
description: I18n.tr("settings.user-interface.control-border-radius.description")
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.iRadiusRatio
isSettings: true
defaultValue: Settings.getDefaultValue("general.iRadiusRatio")
onMoved: value => Settings.data.general.iRadiusRatio = value
text: Math.floor(Settings.data.general.iRadiusRatio * 100) + "%"
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NValueSlider {
Layout.fillWidth: true
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.iRadiusRatio
onMoved: value => Settings.data.general.iRadiusRatio = value
text: Math.floor(Settings.data.general.iRadiusRatio * 100) + "%"
}
// Reset button container
Item {
Layout.preferredWidth: 30 * Style.uiScaleRatio
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.control-border-radius.reset")
onClicked: Settings.data.general.iRadiusRatio = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
NIconButton {
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.control-border-radius.reset")
onClicked: Settings.data.general.iRadiusRatio = 1.0
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
}
}
}
@@ -323,21 +310,20 @@ ColumnLayout {
Layout.fillWidth: true
visible: !Settings.data.general.animationDisabled
NLabel {
label: I18n.tr("settings.user-interface.animation-speed.label")
description: I18n.tr("settings.user-interface.animation-speed.description")
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.user-interface.animation-speed.label")
description: I18n.tr("settings.user-interface.animation-speed.description")
from: 0
to: 2.0
stepSize: 0.01
value: Settings.data.general.animationSpeed
isSettings: true
defaultValue: Settings.getDefaultValue("general.animationSpeed")
onMoved: value => Settings.data.general.animationSpeed = Math.max(value, 0.05)
text: Math.round(Settings.data.general.animationSpeed * 100) + "%"
}
@@ -348,7 +334,7 @@ ColumnLayout {
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.user-interface.animation-speed.reset")
onClicked: Settings.data.general.animationSpeed = 1.0
@@ -363,6 +349,8 @@ ColumnLayout {
label: I18n.tr("settings.user-interface.animation-disable.label")
description: I18n.tr("settings.user-interface.animation-disable.description")
checked: Settings.data.general.animationDisabled
isSettings: true
defaultValue: Settings.getDefaultValue("general.animationDisabled")
onToggled: checked => Settings.data.general.animationDisabled = checked
}
}
@@ -388,6 +376,8 @@ ColumnLayout {
label: I18n.tr("settings.general.screen-corners.show-corners.label")
description: I18n.tr("settings.general.screen-corners.show-corners.description")
checked: Settings.data.general.showScreenCorners
isSettings: true
defaultValue: Settings.getDefaultValue("general.showScreenCorners")
onToggled: checked => Settings.data.general.showScreenCorners = checked
}
@@ -395,6 +385,8 @@ ColumnLayout {
label: I18n.tr("settings.general.screen-corners.solid-black.label")
description: I18n.tr("settings.general.screen-corners.solid-black.description")
checked: Settings.data.general.forceBlackScreenCorners
isSettings: true
defaultValue: Settings.getDefaultValue("general.forceBlackScreenCorners")
onToggled: checked => Settings.data.general.forceBlackScreenCorners = checked
}
@@ -402,21 +394,20 @@ ColumnLayout {
spacing: Style.marginXXS
Layout.fillWidth: true
NLabel {
label: I18n.tr("settings.general.screen-corners.radius.label")
description: I18n.tr("settings.general.screen-corners.radius.description")
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.general.screen-corners.radius.label")
description: I18n.tr("settings.general.screen-corners.radius.description")
from: 0
to: 2
stepSize: 0.01
value: Settings.data.general.screenRadiusRatio
isSettings: true
defaultValue: Settings.getDefaultValue("general.screenRadiusRatio")
onMoved: value => Settings.data.general.screenRadiusRatio = value
text: Math.floor(Settings.data.general.screenRadiusRatio * 100) + "%"
}
@@ -427,7 +418,7 @@ ColumnLayout {
Layout.preferredHeight: 30 * Style.uiScaleRatio
NIconButton {
icon: "refresh"
icon: "restore"
baseSize: Style.baseWidgetSize * 0.8
tooltipText: I18n.tr("settings.general.screen-corners.radius.reset")
onClicked: Settings.data.general.screenRadiusRatio = 1.0
+4 -10
View File
@@ -237,13 +237,10 @@ ColumnLayout {
// Transition Duration
ColumnLayout {
NLabel {
label: I18n.tr("settings.wallpaper.look-feel.transition-duration.label")
description: I18n.tr("settings.wallpaper.look-feel.transition-duration.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.wallpaper.look-feel.transition-duration.label")
description: I18n.tr("settings.wallpaper.look-feel.transition-duration.description")
from: 500
to: 10000
stepSize: 100
@@ -255,13 +252,10 @@ ColumnLayout {
// Edge Smoothness
ColumnLayout {
NLabel {
label: I18n.tr("settings.wallpaper.look-feel.edge-smoothness.label")
description: I18n.tr("settings.wallpaper.look-feel.edge-smoothness.description")
}
NValueSlider {
Layout.fillWidth: true
label: I18n.tr("settings.wallpaper.look-feel.edge-smoothness.label")
description: I18n.tr("settings.wallpaper.look-feel.edge-smoothness.description")
from: 0.0
to: 1.0
value: Settings.data.wallpaper.transitionEdgeSmoothness
+71
View File
@@ -15,6 +15,9 @@ RowLayout {
property var model
property string currentKey: ""
property string placeholder: ""
property bool isSettings: false
property var defaultValue: ""
property string settingsPath: ""
readonly property real preferredHeight: Style.baseWidgetSize * 1.1 * Style.uiScaleRatio
readonly property var comboBox: combo
@@ -25,6 +28,71 @@ RowLayout {
Layout.fillWidth: true
opacity: enabled ? 1.0 : 0.6
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
var displayValue = "";
if (defaultValue === "") {
// Try to find the display name for empty key in the model
var found = false;
if (root.model) {
if (Array.isArray(root.model)) {
for (var i = 0; i < root.model.length; i++) {
var item = root.model[i];
if (item && item.key === "") {
displayValue = item.name || I18n.tr("settings.indicator.system-default");
found = true;
break;
}
}
} else if (typeof root.model.get === 'function') {
for (var i = 0; i < root.itemCount(); i++) {
var item = root.getItem(i);
if (item && item.key === "") {
displayValue = item.name || I18n.tr("settings.indicator.system-default");
found = true;
break;
}
}
}
}
// If not found in model, show "System Default" instead of "(empty)"
if (!found) {
displayValue = I18n.tr("settings.indicator.system-default");
}
} else {
// Try to find the display name for the default key in the model
var found = false;
if (root.model) {
if (Array.isArray(root.model)) {
for (var i = 0; i < root.model.length; i++) {
var item = root.model[i];
if (item && item.key === defaultValue) {
displayValue = item.name || String(defaultValue);
found = true;
break;
}
}
} else if (typeof root.model.get === 'function') {
for (var i = 0; i < root.itemCount(); i++) {
var item = root.getItem(i);
if (item && item.key === defaultValue) {
displayValue = item.name || String(defaultValue);
found = true;
break;
}
}
}
}
if (!found) {
displayValue = String(defaultValue);
}
}
return I18n.tr("settings.indicator.default-value", {
"value": displayValue
});
}
function itemCount() {
if (!root.model)
return 0;
@@ -57,6 +125,8 @@ RowLayout {
NLabel {
label: root.label
description: root.description
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
}
ComboBox {
@@ -234,4 +304,5 @@ RowLayout {
}
}
}
}
+28 -6
View File
@@ -1,6 +1,7 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
@@ -9,17 +10,38 @@ ColumnLayout {
property string description: ""
property color labelColor: Color.mOnSurface
property color descriptionColor: Color.mOnSurfaceVariant
property bool showIndicator: false
property string indicatorTooltip: ""
spacing: Style.marginXXS
Layout.fillWidth: true
NText {
text: label
pointSize: Style.fontSizeL
font.weight: Style.fontWeightSemiBold
color: labelColor
visible: label !== ""
RowLayout {
spacing: Style.marginXS
Layout.fillWidth: true
visible: label !== ""
NText {
text: label
pointSize: Style.fontSizeL
font.weight: Style.fontWeightSemiBold
color: labelColor
}
// Settings indicator
Loader {
active: showIndicator
sourceComponent: indicatorComponent
}
}
Component {
id: indicatorComponent
NSettingsIndicator {
show: true
tooltipText: root.indicatorTooltip || ""
Layout.alignment: Qt.AlignVCenter
}
}
NText {
+49
View File
@@ -18,6 +18,9 @@ RowLayout {
property string placeholder: ""
property string searchPlaceholder: I18n.tr("placeholders.search")
property Component delegate: null
property bool isSettings: false
property var defaultValue: ""
property string settingsPath: ""
readonly property real preferredHeight: Style.baseWidgetSize * 1.1
@@ -26,6 +29,49 @@ RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
readonly property bool isValueChanged: isSettings && (currentKey !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
var displayValue = "";
if (defaultValue === "") {
// Try to find the display name for empty key in the model
if (model && model.count > 0) {
for (var i = 0; i < model.count; i++) {
var item = model.get(i);
if (item && item.key === "") {
displayValue = item.name || I18n.tr("settings.indicator.system-default");
break;
}
}
// If not found in model, show "System Default" instead of "(empty)"
if (displayValue === "") {
displayValue = I18n.tr("settings.indicator.system-default");
}
} else {
displayValue = I18n.tr("settings.indicator.system-default");
}
} else {
// Try to find the display name for the default key in the model
if (model && model.count > 0) {
for (var i = 0; i < model.count; i++) {
var item = model.get(i);
if (item && item.key === defaultValue) {
displayValue = item.name || String(defaultValue);
break;
}
}
if (displayValue === "") {
displayValue = String(defaultValue);
}
} else {
displayValue = String(defaultValue);
}
}
return I18n.tr("settings.indicator.default-value", {
"value": displayValue
});
}
// Filtered model for search results
property ListModel filteredModel: ListModel {}
property string searchText: ""
@@ -113,6 +159,8 @@ RowLayout {
NLabel {
label: root.label
description: root.description
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
}
Item {
@@ -339,4 +387,5 @@ RowLayout {
}
}
}
}
+48
View File
@@ -0,0 +1,48 @@
import QtQuick
import QtQuick.Layouts
import qs.Commons
import qs.Services.UI
Rectangle {
id: root
property bool show: false
property string tooltipText: ""
implicitWidth: root.show ? 6 * Style.uiScaleRatio : 0
implicitHeight: root.show ? 6 * Style.uiScaleRatio : 0
width: root.show ? 6 * Style.uiScaleRatio : 0
height: root.show ? 6 * Style.uiScaleRatio : 0
radius: width / 2
color: Color.mOnSurfaceVariant
opacity: 0.6
visible: root.show
Behavior on opacity {
NumberAnimation {
duration: Style.animationFast
}
}
MouseArea {
enabled: root.show && root.tooltipText !== ""
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
cursorShape: Qt.PointingHandCursor
onEntered: {
if (root.tooltipText) {
TooltipService.show(root, root.tooltipText);
}
}
onExited: {
if (root.tooltipText) {
TooltipService.hide();
}
}
}
}
+11
View File
@@ -19,6 +19,9 @@ RowLayout {
property bool enabled: true
property bool hovering: false
property int baseSize: Style.baseWidgetSize
property bool isSettings: false
property var defaultValue: 0
property string settingsPath: ""
// Convenience properties for common naming
property alias minimum: root.from
@@ -37,6 +40,11 @@ RowLayout {
Layout.fillWidth: true
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": String(defaultValue)
}) : ""
Timer {
id: repeatTimer
repeat: true
@@ -78,6 +86,8 @@ RowLayout {
NLabel {
label: root.label
description: root.description
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
}
// Main spinbox container
@@ -405,4 +415,5 @@ RowLayout {
}
}
}
}
+12
View File
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
ColumnLayout {
id: root
@@ -16,6 +17,9 @@ ColumnLayout {
property string fontFamily: Settings.data.ui.fontDefault
property real fontSize: Style.fontSizeS
property int fontWeight: Style.fontWeightRegular
property bool isSettings: false
property var defaultValue: ""
property string settingsPath: ""
property alias text: input.text
property alias placeholderText: input.placeholderText
@@ -26,6 +30,11 @@ ColumnLayout {
spacing: Style.marginS
readonly property bool isValueChanged: isSettings && (text !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": defaultValue === "" ? "(empty)" : String(defaultValue)
}) : ""
NLabel {
label: root.label
description: root.description
@@ -33,6 +42,8 @@ ColumnLayout {
descriptionColor: root.descriptionColor
visible: root.label !== "" || root.description !== ""
Layout.fillWidth: true
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
}
// An active control that blocks input, to avoid events leakage and dragging stuff in the background.
@@ -219,4 +230,5 @@ ColumnLayout {
}
}
}
}
+12
View File
@@ -2,6 +2,7 @@ import QtQuick
import QtQuick.Controls
import QtQuick.Layouts
import qs.Commons
import qs.Widgets
RowLayout {
id: root
@@ -12,6 +13,9 @@ RowLayout {
property bool checked: false
property bool hovering: false
property int baseSize: Math.round(Style.baseWidgetSize * 0.8 * Style.uiScaleRatio)
property bool isSettings: false
property var defaultValue: false
property string settingsPath: ""
signal toggled(bool checked)
signal entered
@@ -22,10 +26,17 @@ RowLayout {
opacity: enabled ? 1.0 : 0.6
spacing: Style.marginM
readonly property bool isValueChanged: isSettings && (checked !== defaultValue)
readonly property string indicatorTooltip: isSettings ? I18n.tr("settings.indicator.default-value", {
"value": typeof defaultValue === "boolean" ? (defaultValue ? "true" : "false") : String(defaultValue)
}) : ""
NLabel {
label: root.label
description: root.description
visible: root.label !== "" || root.description !== ""
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
}
Rectangle {
@@ -96,4 +107,5 @@ RowLayout {
}
}
}
}
+61 -23
View File
@@ -4,7 +4,7 @@ import QtQuick.Layouts
import qs.Commons
import qs.Widgets
RowLayout {
ColumnLayout {
id: root
property real from: 0
@@ -18,35 +18,73 @@ RowLayout {
property real textSize: Style.fontSizeM
property real customHeight: -1
property real customHeightRatio: -1
property string label: ""
property string description: ""
property bool isSettings: false
property var defaultValue: 0
// Signals
signal moved(real value)
signal pressedChanged(bool pressed, real value)
spacing: Style.marginL
implicitHeight: root.customHeight > 0 ? root.customHeight : slider.implicitHeight
spacing: Style.marginS
Layout.fillWidth: true
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.customHeightRatio > 0 ? root.customHeightRatio : root.heightRatio
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed, value)
readonly property bool isValueChanged: isSettings && (value !== defaultValue)
readonly property string indicatorTooltip: {
if (!isSettings) return "";
var defaultVal = defaultValue;
if (typeof defaultVal === "number") {
// If it's a decimal between 0 and 1, format as percentage
if (defaultVal > 0 && defaultVal <= 1 && from >= 0 && from < 1) {
return I18n.tr("settings.indicator.default-value", {
"value": Math.floor(defaultVal * 100) + "%"
});
}
return I18n.tr("settings.indicator.default-value", {
"value": String(defaultVal)
});
}
return I18n.tr("settings.indicator.default-value", {
"value": String(defaultVal)
});
}
NText {
visible: root.text !== ""
text: root.text
pointSize: root.textSize
family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 45 * Style.uiScaleRatio
horizontalAlignment: Text.AlignRight
NLabel {
label: root.label
description: root.description
visible: root.label !== "" || root.description !== ""
showIndicator: root.isSettings && root.isValueChanged
indicatorTooltip: root.indicatorTooltip
Layout.fillWidth: true
}
RowLayout {
spacing: Style.marginL
Layout.fillWidth: true
NSlider {
id: slider
Layout.fillWidth: true
from: root.from
to: root.to
value: root.value
stepSize: root.stepSize
cutoutColor: root.cutoutColor
snapAlways: root.snapAlways
heightRatio: root.customHeightRatio > 0 ? root.customHeightRatio : root.heightRatio
onMoved: root.moved(value)
onPressedChanged: root.pressedChanged(pressed, value)
}
NText {
visible: root.text !== ""
text: root.text
pointSize: root.textSize
family: Settings.data.ui.fontFixed
Layout.alignment: Qt.AlignVCenter
Layout.preferredWidth: 45 * Style.uiScaleRatio
horizontalAlignment: Text.AlignRight
}
}
}