settings: re-org launcehr tab built-in plugins, make desktop widgets

sub-pages to match the pattern of bar widgets
This commit is contained in:
bbedward
2026-09-22 18:11:12 -04:00
parent 89484b5d85
commit 860542314e
18 changed files with 667 additions and 862 deletions
+13 -1
View File
@@ -200,7 +200,19 @@ Singleton {
"text": I18n.tr("Desktop widgets"),
"icon": "widgets",
"tabIndex": 27,
"hint": I18n.tr("Clocks, system monitors, plugins")
"hubHeader": "DesktopWidgetsTab",
"hint": I18n.tr("Clocks, system monitors, plugins"),
"children": [
{
"id": "desktop_widget",
"hidden": true,
"titleFrom": "selectedWidgetTitle",
"text": I18n.tr("Widget settings"),
"icon": "tune",
"tabIndex": 63,
"hint": I18n.tr("Options of one widget instance")
}
]
},
{
"id": "separator_2",
+1
View File
@@ -14,6 +14,7 @@ Singleton {
// Set only while the widget settings page is showing a dock-hosted widget; empty means bar-hosted.
property string selectedDockId: ""
property string selectedDockWidgetId: ""
property string selectedDesktopWidgetId: ""
property string selectedWidgetSection: ""
property int selectedWidgetIndex: -1
property string selectedWidgetTitle: ""
@@ -218,6 +218,7 @@ FocusScope {
"power_sleep": "PowerSleepTab.qml",
"clipboard": "ClipboardTab.qml",
"desktop_widgets": "DesktopWidgetsTab.qml",
"desktop_widget": "DesktopWidgetTab.qml",
"audio": "AudioTab.qml",
"locale": "LocaleTab.qml",
"multiplexers": "MuxTab.qml",
@@ -16,14 +16,13 @@ Column {
property bool showHeader: true
property bool collapsed: false
property var instances: []
property var expandedStates: ({})
readonly property string sectionKey: groupId ? groupId : ""
readonly property bool dragActive: reorderGroup.active
readonly property bool isDropTarget: dragActive && reorderGroup.target === cardsList
signal collapseToggled(string key)
signal expandedToggled(string instanceId, bool expanded)
signal configureRequested(string instanceId, string title)
signal duplicateRequested(string instanceId)
signal deleteRequested(string instanceId)
@@ -74,10 +73,9 @@ Column {
reorderList: cardsList
instanceData: modelData
isExpanded: section.expandedStates[instanceId] ?? false
opacity: dragging && cardsList.crossSectionActive ? 0 : 1
onExpandedToggled: expanded => section.expandedToggled(instanceId, expanded)
onConfigureRequested: section.configureRequested(instanceId, widgetName)
onDuplicateRequested: section.duplicateRequested(instanceId)
onDeleteRequested: section.deleteRequested(instanceId)
}
@@ -2,18 +2,15 @@ pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Controls
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
import qs.Modules.Settings.DesktopWidgetSettings as DWS
SettingsReorderRow {
id: root
required property var instanceData
property bool isExpanded: false
property bool confirmingDelete: false
readonly property string instanceId: instanceData?.id ?? ""
@@ -21,34 +18,29 @@ SettingsReorderRow {
readonly property var widgetDef: DesktopWidgetRegistry.getWidget(widgetType)
readonly property string widgetName: instanceData?.name ?? widgetDef?.name ?? widgetType
signal expandedToggled(bool expanded)
signal configureRequested
signal deleteRequested
signal duplicateRequested
property Component clockSettingsComponent: Component {
DWS.ClockSettings {}
}
property Component systemMonitorSettingsComponent: Component {
DWS.SystemMonitorSettings {}
}
property Component pluginSettingsComponent: Component {
DWS.PluginDesktopWidgetSettings {
instanceId: root.instanceId
instanceData: root.instanceData
widgetType: root.widgetType
widgetDef: root.widgetDef
}
}
width: parent?.width ?? 400
iconName: widgetDef?.icon ?? "widgets"
title: widgetName
clickable: true
onClicked: expandedToggled(!isExpanded)
onClicked: configureRequested()
trailing: [
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: "chevron_right"
size: Theme.iconSize
color: Theme.onSurfaceVariant
rotation: I18n.isRtl ? 180 : 0
},
Rectangle {
anchors.verticalCenter: parent.verticalCenter
width: Theme.dividerWidth
height: SettingsMetrics.splitDividerHeight
color: Theme.outlineVariant
},
DankToggle {
anchors.verticalCenter: parent.verticalCenter
hideText: true
@@ -173,215 +165,6 @@ SettingsReorderRow {
}
}
}
},
DankIcon {
anchors.verticalCenter: parent.verticalCenter
name: root.isExpanded ? "expand_less" : "expand_more"
size: Theme.iconSize
color: Theme.onSurfaceVariant
}
]
body: Column {
width: parent.width
visible: root.isExpanded
height: root.isExpanded ? implicitHeight : 0
spacing: 0
SettingsTextFieldRow {
leftIconName: "badge"
text: I18n.tr("Name")
value: root.widgetName
onEditingFinished: value => SettingsData.updateDesktopWidgetInstance(root.instanceId, {
name: value
})
}
SettingsDivider {}
SettingsDropdownRow {
readonly property var groupsData: {
const items = [
{
value: "",
label: I18n.tr("None")
}
];
for (const g of SettingsData.desktopWidgetGroups || []) {
items.push({
value: g.id,
label: g.name
});
}
return items;
}
visible: (SettingsData.desktopWidgetGroups || []).length > 0
text: I18n.tr("Group", "noun, dropdown label for a desktop widget group")
options: groupsData.map(g => g.label)
currentValue: groupsData.find(g => g.value === (root.instanceData?.group ?? ""))?.label ?? I18n.tr("None")
onValueChanged: value => SettingsData.updateDesktopWidgetInstance(root.instanceId, {
group: groupsData.find(g => g.label === value)?.value || null
})
}
SettingsDivider {
visible: (SettingsData.desktopWidgetGroups || []).length > 0
}
SettingsToggleRow {
text: I18n.tr("Show on overlay")
checked: instanceData?.config?.showOnOverlay ?? false
onToggled: isChecked => {
SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, {
showOnOverlay: isChecked
});
}
}
SettingsDivider {
visible: CompositorService.isNiri
}
SettingsToggleRow {
visible: CompositorService.isNiri
text: I18n.tr("Show on overview")
checked: instanceData?.config?.showOnOverview ?? false
onToggled: isChecked => {
SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, {
showOnOverview: isChecked
});
}
}
SettingsDivider {
visible: CompositorService.isNiri
}
SettingsToggleRow {
visible: CompositorService.isNiri
text: I18n.tr("Show on overview only")
checked: instanceData?.config?.showOnOverviewOnly ?? false
onToggled: isChecked => {
SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, {
showOnOverviewOnly: isChecked
});
}
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Click through")
checked: instanceData?.config?.clickThrough ?? false
onToggled: isChecked => {
SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, {
clickThrough: isChecked
});
}
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Sync position across displays")
checked: instanceData?.config?.syncPositionAcrossScreens ?? false
onToggled: isChecked => {
if (isChecked)
SessionData.syncDesktopWidgetPositionToAllScreens(root.instanceId);
SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, {
syncPositionAcrossScreens: isChecked
});
}
}
SettingsDivider {}
Item {
width: parent.width
height: ipcColumn.height + Theme.spacingM * 2
Column {
id: ipcColumn
x: Theme.spacingM
width: parent.width - Theme.spacingM * 2
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXS
StyledText {
text: I18n.tr("Command")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
width: parent.width
horizontalAlignment: Text.AlignLeft
}
Rectangle {
width: parent.width
height: ipcText.height + Theme.spacingS * 2
radius: Theme.cornerRadiusS
color: Theme.chipSurface
Row {
x: Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
width: parent.width - Theme.spacingS * 2
StyledText {
id: ipcText
text: "dms ipc call desktopWidget toggleOverlay " + root.instanceId
font.pixelSize: Theme.fontSizeSmall
font.family: Theme.monoFontFamily
color: Theme.surfaceVariantText
width: parent.width - copyBtn.width - Theme.spacingS
elide: Text.ElideMiddle
anchors.verticalCenter: parent.verticalCenter
}
DankButton {
id: copyBtn
iconName: "content_copy"
Accessible.name: I18n.tr("Copy")
backgroundColor: "transparent"
textColor: Theme.surfaceText
buttonHeight: 28
horizontalPadding: 4
anchors.verticalCenter: parent.verticalCenter
onClicked: {
Quickshell.execDetached(["dms", "cl", "copy", "dms ipc call desktopWidget toggleOverlay " + root.instanceId]);
ToastService.showInfo(I18n.tr("Copied to clipboard"));
}
}
}
}
}
}
SettingsDivider {}
Loader {
id: settingsLoader
width: parent.width
active: root.isExpanded && root.widgetType !== ""
sourceComponent: {
switch (root.widgetType) {
case "desktopClock":
return clockSettingsComponent;
case "systemMonitor":
return systemMonitorSettingsComponent;
default:
return pluginSettingsComponent;
}
}
onLoaded: {
if (!item)
return;
item.instanceId = root.instanceId;
item.instanceData = Qt.binding(() => root.instanceData);
}
}
}
}
@@ -34,10 +34,6 @@ DesktopWidgetInstanceSettings {
}
}
SettingsDivider {
visible: root.analog
}
SettingsToggleRow {
visible: root.analog
text: I18n.tr("Show hour numbers")
@@ -45,10 +41,6 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showAnalogNumbers", checked)
}
SettingsDivider {
visible: root.analog
}
SettingsToggleRow {
visible: root.analog
text: I18n.tr("Show seconds")
@@ -56,10 +48,6 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showAnalogSeconds", checked)
}
SettingsDivider {
visible: !root.analog
}
SettingsToggleRow {
visible: !root.analog
text: I18n.tr("Show seconds")
@@ -67,8 +55,6 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showDigitalSeconds", checked)
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Show date")
checked: root.cfg.showDate ?? true
@@ -1,6 +1,5 @@
import QtQuick
import qs.Common
import qs.Widgets
import qs.Modules.Settings.Widgets
Column {
@@ -11,7 +10,7 @@ Column {
property bool showAppearance: true
property bool showPlacement: true
readonly property var cfg: instanceData?.config ?? {}
default property alias rows: rowsColumn.data
default property alias rows: optionsCard.content
function updateConfig(key, value) {
const updates = {};
@@ -19,78 +18,54 @@ Column {
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, updates);
}
width: parent?.width ?? 400
spacing: 0
width: parent?.width ?? 0
spacing: Theme.spacingL
Column {
id: rowsColumn
width: parent.width
spacing: 0
SettingsCard {
id: optionsCard
}
SettingsDivider {
SettingsCard {
title: I18n.tr("Appearance")
visible: root.showAppearance
SettingsSliderRow {
text: I18n.tr("Opacity")
minimum: 0
maximum: 100
value: Math.round((root.cfg.transparency ?? 0.8) * 100)
onSliderValueChanged: newValue => root.updateConfig("transparency", newValue / 100)
}
SettingsColorPicker {
colorMode: root.cfg.colorMode ?? "primary"
customColor: root.cfg.customColor ?? "#ffffff"
onColorModeSelected: mode => root.updateConfig("colorMode", mode)
onCustomColorSelected: selectedColor => root.updateConfig("customColor", selectedColor.toString())
}
}
SettingsSliderRow {
visible: root.showAppearance
text: I18n.tr("Opacity")
minimum: 0
maximum: 100
value: Math.round((root.cfg.transparency ?? 0.8) * 100)
onSliderValueChanged: newValue => root.updateConfig("transparency", newValue / 100)
}
SettingsDivider {
visible: root.showAppearance
}
SettingsColorPicker {
visible: root.showAppearance
colorMode: root.cfg.colorMode ?? "primary"
customColor: root.cfg.customColor ?? "#ffffff"
onColorModeSelected: mode => root.updateConfig("colorMode", mode)
onCustomColorSelected: selectedColor => root.updateConfig("customColor", selectedColor.toString())
}
SettingsDivider {
visible: root.showAppearance && root.showPlacement
}
SettingsDisplayPicker {
SettingsCard {
title: I18n.tr("Displays")
visible: root.showPlacement
displayPreferences: root.cfg.displayPreferences ?? ["all"]
onPreferencesChanged: prefs => root.updateConfig("displayPreferences", prefs)
}
SettingsDivider {
visible: root.showPlacement
}
SettingsDisplayPicker {
displayPreferences: root.cfg.displayPreferences ?? ["all"]
onPreferencesChanged: prefs => root.updateConfig("displayPreferences", prefs)
}
Item {
visible: root.showPlacement
width: parent.width
height: resetRow.height + Theme.spacingM * 2
SettingsRow {
iconName: "drag_pan"
title: I18n.tr("Reset Position")
clickable: true
onClicked: SessionData.resetDesktopWidgetInstanceGeometry(root.instanceId, ["x", "y"])
}
Row {
id: resetRow
x: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
DankButton {
text: I18n.tr("Reset Position")
backgroundColor: Theme.chipSurface
textColor: Theme.surfaceText
onClicked: SessionData.resetDesktopWidgetInstanceGeometry(root.instanceId, ["x", "y"])
}
DankButton {
text: I18n.tr("Reset Size")
backgroundColor: Theme.chipSurface
textColor: Theme.surfaceText
onClicked: SessionData.resetDesktopWidgetInstanceGeometry(root.instanceId, ["width", "height"])
}
SettingsRow {
iconName: "open_in_full"
title: I18n.tr("Reset Size")
clickable: true
onClicked: SessionData.resetDesktopWidgetInstanceGeometry(root.instanceId, ["width", "height"])
}
}
}
@@ -3,20 +3,20 @@ pragma ComponentBehavior: Bound
import QtQuick
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
DesktopWidgetInstanceSettings {
id: root
readonly property var gpus: DgopService.availableGpus ?? []
readonly property var gpuLabels: gpus.map(gpu => (gpu.driver ? gpu.driver.toUpperCase() + " " : "") + (gpu.displayName ?? ""))
SettingsToggleRow {
text: I18n.tr("Show header")
checked: root.cfg.showHeader ?? true
onToggled: checked => root.updateConfig("showHeader", checked)
}
SettingsDivider {}
SettingsButtonGroupRow {
readonly property var intervals: [60, 300, 600, 1800]
text: I18n.tr("Graph time range")
@@ -30,18 +30,12 @@ DesktopWidgetInstanceSettings {
}
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("CPU")
checked: root.cfg.showCpu ?? true
onToggled: checked => root.updateConfig("showCpu", checked)
}
SettingsDivider {
visible: root.cfg.showCpu ?? true
}
SettingsToggleRow {
enabled: root.cfg.showCpu ?? true
text: I18n.tr("CPU graph")
@@ -49,10 +43,6 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showCpuGraph", checked)
}
SettingsDivider {
visible: root.cfg.showCpu ?? true
}
SettingsToggleRow {
enabled: root.cfg.showCpu ?? true
text: I18n.tr("CPU temperature")
@@ -60,112 +50,32 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showCpuTemp", checked)
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("GPU temperature")
checked: root.cfg.showGpuTemp ?? false
onToggled: checked => root.updateConfig("showGpuTemp", checked)
}
SettingsDivider {
visible: (root.cfg.showGpuTemp ?? false) && DgopService.availableGpus.length > 0
}
Item {
width: parent.width
height: gpuSelectColumn.height + Theme.spacingM * 2
visible: (root.cfg.showGpuTemp ?? false) && DgopService.availableGpus.length > 0
Column {
id: gpuSelectColumn
width: parent.width - Theme.spacingM * 2
x: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingS
StyledText {
text: I18n.tr("GPU", "graphics processor label in system monitor")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
Column {
width: parent.width
spacing: Theme.spacingXS
Repeater {
model: DgopService.availableGpus
Rectangle {
required property var modelData
readonly property bool isSelected: (root.cfg.gpuPciId ?? "") === modelData.pciId
width: parent.width
height: 44
radius: Theme.cornerRadius
color: isSelected ? Theme.primarySelected : Theme.chipSurface
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.width: Theme.outlineWidthFocused
Row {
anchors.fill: parent
anchors.margins: Theme.spacingS
spacing: Theme.spacingS
DankIcon {
name: "videocam"
size: Theme.iconSizeSmall
color: isSelected ? Theme.primary : Theme.surfaceVariantText
anchors.verticalCenter: parent.verticalCenter
}
Column {
width: parent.width - Theme.iconSizeSmall - Theme.spacingS
anchors.verticalCenter: parent.verticalCenter
spacing: 0
StyledText {
text: modelData.displayName || "Unknown GPU"
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
width: parent.width
elide: Text.ElideRight
}
StyledText {
text: modelData.driver || ""
font.pixelSize: Theme.fontSizeSmall - 2
color: Theme.surfaceVariantText
visible: text !== ""
}
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: root.updateConfig("gpuPciId", modelData.pciId)
}
}
}
}
SettingsDropdownRow {
visible: root.cfg.showGpuTemp ?? false
text: I18n.tr("GPU")
options: root.gpuLabels
emptyText: I18n.tr("No GPU detected", "empty state when no graphics card is found")
currentValue: root.gpuLabels[root.gpus.findIndex(gpu => gpu.pciId === root.cfg.gpuPciId)] ?? ""
onValueChanged: value => {
const index = root.gpuLabels.indexOf(value);
if (index < 0)
return;
root.updateConfig("gpuPciId", root.gpus[index].pciId);
}
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Memory")
checked: root.cfg.showMemory ?? true
onToggled: checked => root.updateConfig("showMemory", checked)
}
SettingsDivider {
visible: root.cfg.showMemory ?? true
}
SettingsToggleRow {
enabled: root.cfg.showMemory ?? true
text: I18n.tr("Memory graph")
@@ -173,18 +83,12 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showMemoryGraph", checked)
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Network")
checked: root.cfg.showNetwork ?? true
onToggled: checked => root.updateConfig("showNetwork", checked)
}
SettingsDivider {
visible: root.cfg.showNetwork ?? true
}
SettingsToggleRow {
enabled: root.cfg.showNetwork ?? true
text: I18n.tr("Network graph")
@@ -192,26 +96,18 @@ DesktopWidgetInstanceSettings {
onToggled: checked => root.updateConfig("showNetworkGraph", checked)
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Disk")
checked: root.cfg.showDisk ?? true
onToggled: checked => root.updateConfig("showDisk", checked)
}
SettingsDivider {}
SettingsToggleRow {
text: I18n.tr("Top processes")
checked: root.cfg.showTopProcesses ?? false
onToggled: checked => root.updateConfig("showTopProcesses", checked)
}
SettingsDivider {
visible: root.cfg.showTopProcesses ?? false
}
SettingsButtonGroupRow {
readonly property var counts: [3, 5, 10]
visible: root.cfg.showTopProcesses ?? false
@@ -226,10 +122,6 @@ DesktopWidgetInstanceSettings {
}
}
SettingsDivider {
visible: root.cfg.showTopProcesses ?? false
}
SettingsButtonGroupRow {
visible: root.cfg.showTopProcesses ?? false
text: I18n.tr("Sort by")
@@ -243,8 +135,6 @@ DesktopWidgetInstanceSettings {
}
}
SettingsDivider {}
SettingsDropdownRow {
text: I18n.tr("Layout")
options: [I18n.tr("Auto"), I18n.tr("Grid"), I18n.tr("List")]
@@ -0,0 +1,189 @@
pragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import qs.Common
import qs.Services
import qs.Widgets
import qs.Modules.Settings.Widgets
import qs.Modules.Settings.DesktopWidgetSettings as DWS
Item {
id: root
readonly property string instanceId: SettingsUiState.selectedDesktopWidgetId
readonly property var instanceData: (SettingsData.desktopWidgetInstances || []).find(instance => instance.id === instanceId) ?? null
readonly property string widgetType: instanceData?.widgetType ?? ""
readonly property var widgetDef: DesktopWidgetRegistry.getWidget(widgetType)
readonly property string widgetName: instanceData?.name ?? widgetDef?.name ?? widgetType
readonly property var cfg: instanceData?.config ?? {}
readonly property string overlayCommand: "dms ipc call desktopWidget toggleOverlay " + instanceId
readonly property var groupOptions: [
{
"value": "",
"label": I18n.tr("None")
}
].concat((SettingsData.desktopWidgetGroups || []).map(group => ({
"value": group.id,
"label": group.name
})))
function updateConfig(key, value) {
const updates = {};
updates[key] = value;
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, updates);
}
onWidgetNameChanged: SettingsUiState.selectedWidgetTitle = widgetName
Component {
id: clockSettings
DWS.ClockSettings {
instanceId: root.instanceId
instanceData: root.instanceData
}
}
Component {
id: systemMonitorSettings
DWS.SystemMonitorSettings {
instanceId: root.instanceId
instanceData: root.instanceData
}
}
Component {
id: pluginSettings
DWS.PluginDesktopWidgetSettings {
instanceId: root.instanceId
instanceData: root.instanceData
widgetType: root.widgetType
widgetDef: root.widgetDef
}
}
SettingsPage {
visible: root.instanceData !== null
SettingsCard {
SettingsToggleRow {
iconName: root.widgetDef?.icon ?? "widgets"
text: root.widgetDef?.name ?? root.widgetType
description: root.widgetDef?.description ?? ""
checked: root.instanceData?.enabled ?? true
onToggled: checked => SettingsData.updateDesktopWidgetInstance(root.instanceId, {
"enabled": checked
})
}
SettingsTextFieldRow {
leftIconName: "badge"
text: I18n.tr("Name")
value: root.widgetName
onEditingFinished: value => SettingsData.updateDesktopWidgetInstance(root.instanceId, {
"name": value
})
}
SettingsDropdownRow {
visible: root.groupOptions.length > 1
text: I18n.tr("Group", "noun, dropdown label for a desktop widget group")
options: root.groupOptions.map(group => group.label)
currentValue: root.groupOptions.find(group => group.value === (root.instanceData?.group ?? ""))?.label ?? I18n.tr("None")
onValueChanged: value => SettingsData.updateDesktopWidgetInstance(root.instanceId, {
"group": root.groupOptions.find(group => group.label === value)?.value || null
})
}
}
Loader {
width: parent.width
active: root.instanceData !== null
sourceComponent: {
switch (root.widgetType) {
case "desktopClock":
return clockSettings;
case "systemMonitor":
return systemMonitorSettings;
default:
return pluginSettings;
}
}
}
SettingsCard {
title: I18n.tr("Behavior")
SettingsToggleRow {
text: I18n.tr("Show on overlay")
checked: root.cfg.showOnOverlay ?? false
onToggled: checked => root.updateConfig("showOnOverlay", checked)
}
SettingsToggleRow {
visible: CompositorService.isNiri
text: I18n.tr("Show on overview")
checked: root.cfg.showOnOverview ?? false
onToggled: checked => root.updateConfig("showOnOverview", checked)
}
SettingsToggleRow {
visible: CompositorService.isNiri
text: I18n.tr("Show on overview only")
checked: root.cfg.showOnOverviewOnly ?? false
onToggled: checked => root.updateConfig("showOnOverviewOnly", checked)
}
SettingsToggleRow {
text: I18n.tr("Click through")
checked: root.cfg.clickThrough ?? false
onToggled: checked => root.updateConfig("clickThrough", checked)
}
SettingsToggleRow {
text: I18n.tr("Sync position across displays")
checked: root.cfg.syncPositionAcrossScreens ?? false
onToggled: checked => {
if (checked)
SessionData.syncDesktopWidgetPositionToAllScreens(root.instanceId);
root.updateConfig("syncPositionAcrossScreens", checked);
}
}
}
SettingsCard {
title: I18n.tr("Command")
SettingsRow {
body: Row {
width: parent.width
spacing: Theme.spacingS
StyledText {
anchors.verticalCenter: parent.verticalCenter
width: parent.width - copyButton.width - parent.spacing
text: root.overlayCommand
font.pixelSize: Theme.fontSizeSmall
font.family: Theme.monoFontFamily
color: Theme.surfaceVariantText
elide: Text.ElideMiddle
}
DankActionButton {
id: copyButton
anchors.verticalCenter: parent.verticalCenter
iconName: "content_copy"
Accessible.name: I18n.tr("Copy")
onClicked: {
Quickshell.execDetached(["dms", "cl", "copy", root.overlayCommand]);
ToastService.showInfo(I18n.tr("Copied to clipboard"));
}
}
}
}
}
}
}
@@ -13,7 +13,6 @@ Item {
LayoutMirroring.enabled: I18n.isRtl
LayoutMirroring.childrenInherit: true
property var expandedStates: ({})
property var groupCollapsedStates: ({})
property var parentModal: null
property string editingGroupId: ""
@@ -43,12 +42,10 @@ Item {
groupCollapsedStates = states;
}
function setExpanded(instanceId, expanded) {
if (expanded === (expandedStates[instanceId] ?? false))
return;
var states = Object.assign({}, expandedStates);
states[instanceId] = expanded;
expandedStates = states;
function configureWidget(instanceId, title) {
SettingsUiState.selectedDesktopWidgetId = instanceId;
SettingsUiState.selectedWidgetTitle = title;
parentModal?.navigateTo("desktop_widget");
}
function showWidgetBrowser() {
@@ -283,11 +280,10 @@ Item {
showHeader: true
collapsed: root.groupCollapsedStates[modelData.id] ?? false
instances: root.allInstances.filter(inst => inst.group === modelData.id)
expandedStates: root.expandedStates
visible: instances.length > 0 || root.dragActive
onCollapseToggled: key => root.toggleCollapsed(key)
onExpandedToggled: (instanceId, expanded) => root.setExpanded(instanceId, expanded)
onConfigureRequested: (instanceId, title) => root.configureWidget(instanceId, title)
onDuplicateRequested: instanceId => SettingsData.duplicateDesktopWidgetInstance(instanceId)
onDeleteRequested: instanceId => {
SettingsData.removeDesktopWidgetInstance(instanceId);
@@ -313,11 +309,10 @@ Item {
showHeader: root.allGroups.length > 0
collapsed: root.groupCollapsedStates["_ungrouped"] ?? false
instances: ungroupedInstances
expandedStates: root.expandedStates
visible: ungroupedInstances.length > 0 || root.dragActive
onCollapseToggled: key => root.toggleCollapsed(key)
onExpandedToggled: (instanceId, expanded) => root.setExpanded(instanceId, expanded)
onConfigureRequested: (instanceId, title) => root.configureWidget(instanceId, title)
onDuplicateRequested: instanceId => SettingsData.duplicateDesktopWidgetInstance(instanceId)
onDeleteRequested: instanceId => {
SettingsData.removeDesktopWidgetInstance(instanceId);
+160 -237
View File
@@ -14,6 +14,7 @@ Item {
readonly property bool keybindsAvailable: KeybindsService.available
readonly property string defaultLauncherKeybindSearch: "spotlight toggle"
readonly property string spotlightBarKeybindSearch: "spotlight-bar"
readonly property var builtInPluginIds: ["dms_settings", "dms_notepad", "dms_sysmon", "dms_colorpicker", "dms_settings_search", "dms_clipboard_search", "dms_power", "dms_vpn", "dms_qr_generator"]
function openKeybindsSearch(query) {
if (!root.parentModal)
@@ -35,6 +36,25 @@ Item {
return keys.join(", ");
}
function lastLaunchedText(lastUsed) {
if (!lastUsed)
return I18n.tr("Never used");
const date = new Date(lastUsed);
const diffMs = Date.now() - date.getTime();
const diffMins = Math.floor(diffMs / (1000 * 60));
const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
const diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (diffMins < 1)
return I18n.tr("Last launched just now");
if (diffMins < 60)
return diffMins === 1 ? I18n.tr("Last launched %1 minute ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffMins) : I18n.tr("Last launched %1 minutes ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffMins);
if (diffHours < 24)
return diffHours === 1 ? I18n.tr("Last launched %1 hour ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffHours) : I18n.tr("Last launched %1 hours ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffHours);
if (diffDays < 7)
return diffDays === 1 ? I18n.tr("Last launched %1 day ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffDays) : I18n.tr("Last launched %1 days ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffDays);
return I18n.tr("Last launched %1", "app usage list in launcher settings, %1 is a localized date").arg(date.toLocaleDateString());
}
Component.onCompleted: {
if (KeybindsService.available)
KeybindsService.loadBinds(false);
@@ -320,78 +340,41 @@ Item {
}
}
Repeater {
model: hiddenAppsCard.hiddenAppsModel
SettingsRow {
id: hiddenAppRow
required property var modelData
title: modelData.name
subtitle: modelData.comment || modelData.id
leading: AppIconRenderer {
width: Theme.iconSize
height: Theme.iconSize
iconValue: hiddenAppRow.modelData.icon || "application-x-executable"
iconSize: Theme.iconSize
fallbackText: (hiddenAppRow.modelData.name || "?").charAt(0).toUpperCase()
}
DankActionButton {
iconName: "visibility"
Accessible.name: I18n.tr("Show")
iconColor: Theme.primary
onClicked: SessionData.showApp(hiddenAppRow.modelData.id)
}
}
}
SettingsRow {
body: Column {
id: hiddenAppsList
visible: hiddenAppsCard.hiddenAppsModel.length === 0
body: StyledText {
width: parent.width
spacing: Theme.spacingS
Repeater {
model: hiddenAppsCard.hiddenAppsModel
delegate: Rectangle {
width: hiddenAppsList.width
height: 48
radius: Theme.cornerRadius
color: Theme.floatingWindowFieldColor
border.width: 0
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
AppIconRenderer {
width: 24
height: 24
iconValue: modelData.icon || "application-x-executable"
iconSize: 24
fallbackText: (modelData.name || "?").charAt(0).toUpperCase()
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
StyledText {
text: modelData.name
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
StyledText {
text: modelData.comment || modelData.id
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
visible: text.length > 0
}
}
}
DankActionButton {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
iconName: "visibility"
Accessible.name: I18n.tr("Show")
iconSize: 18
iconColor: Theme.primary
onClicked: SessionData.showApp(modelData.id)
}
}
}
StyledText {
width: parent.width
text: I18n.tr("No hidden apps.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
visible: hiddenAppsCard.hiddenAppsModel.length === 0
}
text: I18n.tr("No hidden apps.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
}
}
}
@@ -431,77 +414,63 @@ Item {
}
}
Repeater {
model: appOverridesCard.overridesModel
SettingsRow {
id: overrideRow
required property var modelData
title: modelData.name
subtitle: modelData.originalName !== modelData.name ? modelData.originalName : modelData.id
leading: AppIconRenderer {
width: Theme.iconSize
height: Theme.iconSize
iconValue: overrideRow.modelData.icon || "application-x-executable"
iconSize: Theme.iconSize
fallbackText: (overrideRow.modelData.name || "?").charAt(0).toUpperCase()
}
DankActionButton {
iconName: "delete"
tooltipText: I18n.tr("Reset to default")
iconColor: Theme.error
onClicked: SessionData.clearAppOverride(overrideRow.modelData.id)
}
}
}
SettingsRow {
body: Column {
id: overridesList
visible: appOverridesCard.overridesModel.length === 0
body: StyledText {
width: parent.width
spacing: Theme.spacingS
text: I18n.tr("No app customizations.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
}
}
}
Repeater {
model: appOverridesCard.overridesModel
SettingsCard {
width: parent.width
iconName: "apps"
title: I18n.tr("Built-in apps", "launcher settings card, DMS tools listed with the installed apps")
settingKey: "launcherBuiltInApps"
tags: ["launcher", "apps", "builtin", "settings", "notepad", "system monitor", "color picker"]
delegate: Rectangle {
width: overridesList.width
height: 48
radius: Theme.cornerRadius
color: Theme.floatingWindowFieldColor
border.width: 0
Repeater {
model: root.builtInPluginIds.filter(id => AppSearchService.builtInPlugins[id]?.isLauncher !== true)
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
SettingsToggleRow {
required property string modelData
readonly property var plugin: AppSearchService.builtInPlugins[modelData]
AppIconRenderer {
width: 24
height: 24
iconValue: modelData.icon || "application-x-executable"
iconSize: 24
fallbackText: (modelData.name || "?").charAt(0).toUpperCase()
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
StyledText {
text: modelData.name
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
StyledText {
text: modelData.originalName !== modelData.name ? modelData.originalName : modelData.id
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
DankActionButton {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
iconName: "delete"
tooltipText: I18n.tr("Reset to default")
iconSize: 18
iconColor: Theme.error
onClicked: SessionData.clearAppOverride(modelData.id)
}
}
}
StyledText {
width: parent.width
text: I18n.tr("No app customizations.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
visible: appOverridesCard.overridesModel.length === 0
}
iconName: plugin?.cornerIcon ?? "extension"
text: plugin?.name ?? modelData
checked: SettingsData.getBuiltInPluginSetting(modelData, "enabled", true)
onToggled: checked => SettingsData.setBuiltInPluginSetting(modelData, "enabled", checked)
}
}
}
@@ -596,10 +565,18 @@ Item {
title: modelData.name
subtitle: modelData.trigger ? I18n.tr("Trigger: %1", "launcher plugin subtitle, %1 is the trigger prefix text").arg(modelData.trigger) : I18n.tr("No trigger")
iconName: modelData.iconType !== "unicode" ? modelData.icon : ""
trailingBadge: modelData.isBuiltIn ? "DMS" : ""
textIcon: modelData.iconType === "unicode" ? modelData.icon : ""
DankBadge {
anchors.verticalCenter: parent.verticalCenter
visible: pluginRow.modelData.isBuiltIn
text: I18n.tr("Built-in", "badge on launcher plugins that ship with DMS")
color: Theme.primaryHover
textColor: Theme.primary
}
DankToggle {
anchors.verticalCenter: parent.verticalCenter
hideText: true
checked: {
switch (pluginRow.modelData.id) {
@@ -644,10 +621,10 @@ Item {
iconName: "extension"
title: I18n.tr("Built-in plugins")
settingKey: "launcherBuiltInPlugins"
tags: ["launcher", "plugins", "trigger", "builtin", "settings", "notepad", "calculator", "clipboard", "power"]
tags: ["launcher", "plugins", "trigger", "builtin", "power", "vpn", "qr", "clipboard", "search"]
Repeater {
model: ["dms_settings", "dms_notepad", "dms_sysmon", "dms_settings_search", "dms_clipboard_search", "dms_power", "dms_vpn", "dms_colorpicker", "dms_qr_generator"]
model: root.builtInPluginIds.filter(id => AppSearchService.builtInPlugins[id]?.isLauncher === true)
SettingsRow {
id: builtInRow
@@ -657,14 +634,12 @@ Item {
iconName: plugin?.cornerIcon ?? "extension"
title: plugin?.name ?? modelData
subtitle: plugin?.comment ?? ""
DankTextField {
outlined: true
leftIconName: "keyboard"
labelText: I18n.tr("Trigger", "noun, launcher plugin trigger prefix text field label")
width: Theme.fontSizeMedium * 6 + Theme.iconButtonSize
visible: builtInRow.plugin?.isLauncher === true
anchors.verticalCenter: parent.verticalCenter
onTextEdited: SettingsData.setBuiltInPluginSetting(builtInRow.modelData, "trigger", text)
Component.onCompleted: text = SettingsData.getBuiltInPluginSetting(builtInRow.modelData, "trigger", builtInRow.plugin?.defaultTrigger ?? "")
@@ -790,110 +765,58 @@ Item {
}
}
SettingsRow {
body: Column {
id: rankedAppsList
width: parent.width
spacing: Theme.spacingS
Repeater {
model: recentAppsCard.rankedAppsModel
Repeater {
model: recentAppsCard.rankedAppsModel
SettingsRow {
id: rankedAppRow
delegate: Rectangle {
width: rankedAppsList.width
height: 48
radius: Theme.cornerRadius
color: Theme.floatingWindowFieldColor
border.width: 0
required property var modelData
required property int index
Row {
anchors.left: parent.left
anchors.leftMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
title: modelData.name || I18n.tr("Unknown App")
subtitle: root.lastLaunchedText(modelData.lastUsed)
leading: [
StyledText {
anchors.verticalCenter: parent.verticalCenter
width: Theme.iconSize
text: (rankedAppRow.index + 1).toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Theme.fontWeightMedium
color: Theme.primary
},
AppIconRenderer {
anchors.verticalCenter: parent.verticalCenter
width: Theme.iconSize
height: Theme.iconSize
iconValue: rankedAppRow.modelData.icon || "application-x-executable"
iconSize: Theme.iconSize
fallbackText: (rankedAppRow.modelData.name || "?").charAt(0).toUpperCase()
}
]
StyledText {
text: (index + 1).toString()
font.pixelSize: Theme.fontSizeSmall
font.weight: Theme.fontWeightMedium
color: Theme.primary
width: 20
anchors.verticalCenter: parent.verticalCenter
}
AppIconRenderer {
width: 24
height: 24
iconValue: modelData.icon || "application-x-executable"
iconSize: 24
fallbackText: (modelData.name || "?").charAt(0).toUpperCase()
anchors.verticalCenter: parent.verticalCenter
}
Column {
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingXXS
StyledText {
text: modelData.name || I18n.tr("Unknown App")
font.pixelSize: Theme.fontSizeMedium
font.weight: Theme.fontWeightMedium
color: Theme.surfaceText
}
StyledText {
text: {
if (!modelData.lastUsed)
return I18n.tr("Never used");
var date = new Date(modelData.lastUsed);
var now = new Date();
var diffMs = now - date;
var diffMins = Math.floor(diffMs / (1000 * 60));
var diffHours = Math.floor(diffMs / (1000 * 60 * 60));
var diffDays = Math.floor(diffMs / (1000 * 60 * 60 * 24));
if (diffMins < 1)
return I18n.tr("Last launched just now");
if (diffMins < 60)
return diffMins === 1 ? I18n.tr("Last launched %1 minute ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffMins) : I18n.tr("Last launched %1 minutes ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffMins);
if (diffHours < 24)
return diffHours === 1 ? I18n.tr("Last launched %1 hour ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffHours) : I18n.tr("Last launched %1 hours ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffHours);
if (diffDays < 7)
return diffDays === 1 ? I18n.tr("Last launched %1 day ago", "singular, app usage list in launcher settings, %1 is 1").arg(diffDays) : I18n.tr("Last launched %1 days ago", "plural, app usage list in launcher settings, %1 is a count").arg(diffDays);
return I18n.tr("Last launched %1", "app usage list in launcher settings, %1 is a localized date").arg(date.toLocaleDateString());
}
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceVariantText
}
}
}
DankActionButton {
anchors.right: parent.right
anchors.rightMargin: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
circular: true
iconName: "close"
Accessible.name: I18n.tr("Remove")
iconSize: 16
iconColor: Theme.error
onClicked: {
var currentRanking = Object.assign({}, AppUsageHistoryData.appUsageRanking || {});
delete currentRanking[modelData.id];
AppUsageHistoryData.appUsageRanking = currentRanking;
AppUsageHistoryData.saveSettings();
}
}
DankActionButton {
iconName: "close"
Accessible.name: I18n.tr("Remove")
iconColor: Theme.error
onClicked: {
const currentRanking = Object.assign({}, AppUsageHistoryData.appUsageRanking || {});
delete currentRanking[rankedAppRow.modelData.id];
AppUsageHistoryData.appUsageRanking = currentRanking;
AppUsageHistoryData.saveSettings();
}
}
}
}
StyledText {
width: parent.width
text: I18n.tr("No apps have been launched yet.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
visible: recentAppsCard.rankedAppsModel.length === 0
}
SettingsRow {
visible: recentAppsCard.rankedAppsModel.length === 0
body: StyledText {
width: parent.width
text: I18n.tr("No apps have been launched yet.")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceVariantText
horizontalAlignment: Text.AlignHCenter
}
}
}
@@ -1,7 +1,6 @@
pragma ComponentBehavior: Bound
import QtQuick
import QtQuick.Layouts
import qs.Common
import qs.Services
import qs.Widgets
@@ -225,80 +224,66 @@ FocusScope {
}
}
DankCollapsibleSection {
id: pluginDetails
width: parent.width
SettingsCard {
title: I18n.tr("Plugin details", "plugin metadata and maintenance")
settingKey: "pluginDetails"
visible: root.pluginData !== null
collapsible: true
expanded: false
SettingsCard {
Layout.fillWidth: true
visible: pluginDetails.expanded
settingKey: "pluginDetails"
SettingsRow {
title: [root.pluginData?.version, root.pluginData?.author].filter(Boolean).join(" · ")
}
SettingsRow {
subtitle: root.pluginData?.description || ""
visible: subtitle !== ""
}
SettingsRow {
title: [root.pluginData?.version, root.pluginData?.author].filter(Boolean).join(" · ")
}
SettingsRow {
subtitle: root.pluginData?.description || ""
visible: subtitle !== ""
}
SettingsRow {
visible: root.permissions.length > 0
body: Flow {
width: parent.width
spacing: Theme.spacingXS
SettingsRow {
visible: root.permissions.length > 0
body: Flow {
width: parent.width
spacing: Theme.spacingXS
Repeater {
model: root.permissions
Repeater {
model: root.permissions
DankBadge {
required property string modelData
Rectangle {
required property string modelData
height: Theme.iconSizeMedium
width: permissionText.implicitWidth + Theme.spacingS * 2
radius: Theme.fullRadius(width, height)
color: Theme.withAlpha(Theme.primary, Theme.tonalTintAlpha)
StyledText {
id: permissionText
anchors.centerIn: parent
text: parent.modelData
font.pixelSize: Theme.fontSizeSmall
color: Theme.primary
}
}
text: modelData
color: Theme.primaryHover
textColor: Theme.primary
}
}
}
}
SettingsRow {
iconName: "download"
title: I18n.tr("Update plugin")
clickable: true
visible: DMSService.dmsAvailable && root.isLoaded && root.hasUpdate && !root.isSystemPlugin
enabled: !root.operationPending
onClicked: root.requestUpdate()
}
SettingsRow {
iconName: "download"
title: I18n.tr("Update plugin")
clickable: true
visible: DMSService.dmsAvailable && root.isLoaded && root.hasUpdate && !root.isSystemPlugin
enabled: !root.operationPending
onClicked: root.requestUpdate()
}
SettingsRow {
iconName: "refresh"
title: I18n.tr("Reload plugin")
clickable: true
visible: root.isLoaded
enabled: !root.isReloading && !root.operationPending
onClicked: root.reload()
}
SettingsRow {
iconName: "refresh"
title: I18n.tr("Reload plugin")
clickable: true
visible: root.isLoaded
enabled: !root.isReloading && !root.operationPending
onClicked: root.reload()
}
SettingsRow {
iconName: "delete"
title: I18n.tr("Uninstall plugin")
clickable: true
visible: DMSService.dmsAvailable && !root.isSystemPlugin
enabled: !root.operationPending
onClicked: root.requestUninstall()
}
SettingsRow {
iconName: "delete"
title: I18n.tr("Uninstall plugin")
clickable: true
visible: DMSService.dmsAvailable && !root.isSystemPlugin
enabled: !root.operationPending
onClicked: root.requestUninstall()
}
}
}
@@ -5,7 +5,7 @@ import qs.Common
import qs.Services
import qs.Widgets
Item {
SettingsRow {
id: root
property string colorMode: "primary"
@@ -15,99 +15,84 @@ Item {
signal colorModeSelected(string mode)
signal customColorSelected(color selectedColor)
width: parent?.width ?? 0
height: colorColumn.height + Theme.spacingM * 2
title: I18n.tr("Color")
Column {
id: colorColumn
width: parent.width - Theme.spacingM * 2
x: Theme.spacingM
anchors.verticalCenter: parent.verticalCenter
spacing: Theme.spacingM
body: Row {
width: parent.width
spacing: Theme.spacingS
StyledText {
text: I18n.tr("Color")
font.pixelSize: Theme.fontSizeMedium
color: Theme.surfaceText
}
Repeater {
model: [
{
id: "primary",
label: I18n.tr("Primary"),
color: Theme.primary
},
{
id: "secondary",
label: I18n.tr("Secondary"),
color: Theme.secondary
},
{
id: "custom",
label: I18n.tr("Custom"),
color: root.customColor
}
]
Row {
width: parent.width
spacing: Theme.spacingS
Rectangle {
required property var modelData
required property int index
Repeater {
model: [
{
id: "primary",
label: I18n.tr("Primary"),
color: Theme.primary
},
{
id: "secondary",
label: I18n.tr("Secondary"),
color: Theme.secondary
},
{
id: "custom",
label: I18n.tr("Custom"),
color: root.customColor
}
]
width: (parent.width - Theme.spacingS * 2) / 3
height: Theme.listItemHeight + Theme.spacingXS
radius: Theme.cornerRadius
color: root.colorMode === modelData.id ? Theme.primarySelected : Theme.chipSurface
border.color: root.colorMode === modelData.id ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.width: Theme.outlineWidthFocused
Rectangle {
required property var modelData
required property int index
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
width: (parent.width - Theme.spacingS * 2) / 3
height: Theme.listItemHeight + Theme.spacingXS
radius: Theme.cornerRadius
color: root.colorMode === modelData.id ? Theme.primarySelected : Theme.chipSurface
border.color: root.colorMode === modelData.id ? Theme.primary : Theme.withAlpha(Theme.primary, 0)
border.width: Theme.outlineWidthFocused
DankColorSwatch {
width: Theme.iconSize
height: Theme.iconSize
swatchColor: modelData.color
anchors.horizontalCenter: parent.horizontalCenter
Column {
anchors.centerIn: parent
spacing: Theme.spacingXS
DankColorSwatch {
width: Theme.iconSize
height: Theme.iconSize
swatchColor: modelData.color
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
visible: modelData.id === "custom"
anchors.centerIn: parent
name: "colorize"
size: Theme.iconSizeSmall
color: Theme.background
}
}
StyledText {
text: modelData.label
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
DankIcon {
visible: modelData.id === "custom"
anchors.centerIn: parent
name: "colorize"
size: Theme.iconSizeSmall
color: Theme.background
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.id !== "custom") {
root.colorModeSelected(modelData.id);
return;
}
PopoutService.colorPickerModal.selectedColor = root.customColor;
PopoutService.colorPickerModal.pickerTitle = root.pickerTitle;
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
root.customColorSelected(selectedColor);
root.colorModeSelected("custom");
};
PopoutService.colorPickerModal.show();
StyledText {
text: modelData.label
font.pixelSize: Theme.fontSizeSmall
color: Theme.surfaceText
anchors.horizontalCenter: parent.horizontalCenter
}
}
MouseArea {
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
onClicked: {
if (modelData.id !== "custom") {
root.colorModeSelected(modelData.id);
return;
}
PopoutService.colorPickerModal.selectedColor = root.customColor;
PopoutService.colorPickerModal.pickerTitle = root.pickerTitle;
PopoutService.colorPickerModal.onColorSelectedCallback = function (selectedColor) {
root.customColorSelected(selectedColor);
root.colorModeSelected("custom");
};
PopoutService.colorPickerModal.show();
}
}
}
@@ -7,5 +7,7 @@ DankReorderList {
readonly property bool isSettingsRow: true
readonly property bool transparentSlot: true
readonly property bool firstInGroup: parent?.isSettingsGroupHost ? parent.isEdge(root, true) : true
readonly property bool lastInGroup: parent?.isSettingsGroupHost ? parent.isEdge(root, false) : true
Component.onCompleted: flickable = QmlUtils.findParentFlickable(root.parent)
}
@@ -14,8 +14,8 @@ SettingsRow {
paintBackground: true
rowColor: dragging ? Theme.blend(SettingsMetrics.rowColor, Theme.onSurface, Theme.stateLayerDrag) : SettingsMetrics.rowColor
topRadius: dragging || position === 0 ? Theme.groupedListOuterRadius : Theme.groupedListInnerRadius
bottomRadius: dragging || position === reorderList.count - 1 ? Theme.groupedListOuterRadius : Theme.groupedListInnerRadius
topRadius: dragging || (position === 0 && reorderList.firstInGroup) ? Theme.groupedListOuterRadius : Theme.groupedListInnerRadius
bottomRadius: dragging || (position === reorderList.count - 1 && reorderList.lastInGroup) ? Theme.groupedListOuterRadius : Theme.groupedListInnerRadius
z: dragging ? 100 : 0
function focusHandle(reason) {
@@ -86,8 +86,16 @@ T.Control {
}
function _edge(first) {
if (groupItem.parent?.isSettingsGroupHost)
return groupItem.parent.isEdge(groupItem, first);
const container = groupItem.parent;
if (container?.isSettingsGroupHost)
return container.isEdge(groupItem, first);
if (!_edgeInContainer(first))
return false;
const host = container?.parent;
return host?.isSettingsGroupHost ? host.isEdge(container, first) : true;
}
function _edgeInContainer(first) {
const siblings = groupItem.parent?.visibleChildren;
if (!siblings)
return true;
@@ -136,6 +136,7 @@ TAB_INDEX_MAP = {
"GammaControlTab.qml": 25,
"DisplayWidgetsTab.qml": 26,
"DesktopWidgetsTab.qml": 27,
"DesktopWidgetTab.qml": 63,
"AudioTab.qml": 29,
"LocaleTab.qml": 30,
"GreeterTab.qml": 31,
@@ -4443,6 +4443,30 @@
],
"icon": "border_style"
},
{
"section": "launcherBuiltInApps",
"label": "Built-in apps",
"tabIndex": 9,
"category": "Launcher",
"keywords": [
"app drawer",
"app menu",
"applications",
"apps",
"built",
"builtin",
"color picker",
"drawer",
"launcher",
"menu",
"notepad",
"settings",
"start",
"start menu",
"system monitor"
],
"icon": "apps"
},
{
"section": "launcherBuiltInPlugins",
"label": "Built-in plugins",
@@ -4455,19 +4479,19 @@
"applications",
"built",
"builtin",
"calculator",
"clipboard",
"drawer",
"extensions",
"launcher",
"menu",
"notepad",
"plugins",
"power",
"settings",
"qr",
"search",
"start",
"start menu",
"trigger",
"vpn",
"widgets"
],
"icon": "extension"
@@ -9490,6 +9514,32 @@
],
"icon": "add"
},
{
"section": "_hub_desktop_widgets",
"label": "Desktop widgets",
"tabIndex": 27,
"page": "desktop_widgets",
"category": "Desktop widgets",
"keywords": [
"addons",
"clocks",
"components",
"conky",
"desktop",
"desktop clock",
"extensions",
"modules",
"monitors",
"plugins",
"settings",
"system",
"time",
"watch",
"widget",
"widgets"
],
"icon": "widgets"
},
{
"section": "_tab_27",
"label": "Desktop widgets",
@@ -14858,6 +14908,29 @@
],
"parentLabel": "Interface style"
},
{
"section": "_tab_54",
"label": "Bar widgets: Widget settings",
"tabIndex": 54,
"category": "Widget settings",
"keywords": [
"bar",
"components",
"instance",
"modules",
"options",
"panel",
"settings",
"statusbar",
"taskbar",
"topbar",
"widget",
"widgets"
],
"icon": "tune",
"parentLabel": "Bar widgets",
"description": "Options of one widget instance"
},
{
"section": "barWidgetGeneral",
"label": "Show in dock",
@@ -14880,29 +14953,6 @@
"parentLabel": "Bar widgets",
"icon": "SettingsUiState.selectedWidgetIcon"
},
{
"section": "_tab_54",
"label": "Widget settings",
"tabIndex": 54,
"category": "Widget settings",
"keywords": [
"bar",
"components",
"instance",
"modules",
"options",
"panel",
"settings",
"statusbar",
"taskbar",
"topbar",
"widget",
"widgets"
],
"icon": "tune",
"parentLabel": "Bar widgets",
"description": "Options of one widget instance"
},
{
"section": "mutedApps",
"label": "Muted apps",
@@ -15593,5 +15643,26 @@
"parentLabel": "Security",
"description": "DMS removes its managed block from /etc/pam.d/greetd and stops write services",
"conditionKey": "greeterAvailable"
},
{
"section": "_tab_63",
"label": "Desktop widgets: Widget settings",
"tabIndex": 63,
"category": "Widget settings",
"keywords": [
"components",
"conky",
"desktop",
"desktop clock",
"instance",
"modules",
"options",
"settings",
"widget",
"widgets"
],
"icon": "tune",
"parentLabel": "Desktop widgets",
"description": "Options of one widget instance"
}
]