mirror of
https://github.com/AvengeMedia/DankMaterialShell.git
synced 2026-01-27 15:02:50 -05:00
desktop widgets: centralize config in desktop widgets tab, variants
always available
This commit is contained in:
417
quickshell/Modules/Settings/DesktopWidgetBrowser.qml
Normal file
417
quickshell/Modules/Settings/DesktopWidgetBrowser.qml
Normal file
@@ -0,0 +1,417 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
|
||||
FloatingWindow {
|
||||
id: root
|
||||
|
||||
property string searchQuery: ""
|
||||
property var filteredWidgets: []
|
||||
property int selectedIndex: -1
|
||||
property bool keyboardNavigationActive: false
|
||||
property var parentModal: null
|
||||
|
||||
signal closed
|
||||
signal widgetAdded(string widgetType)
|
||||
|
||||
function updateFilteredWidgets() {
|
||||
const allWidgets = DesktopWidgetRegistry.registeredWidgetsList || [];
|
||||
if (!searchQuery || searchQuery.length === 0) {
|
||||
filteredWidgets = allWidgets.slice();
|
||||
return;
|
||||
}
|
||||
|
||||
var filtered = [];
|
||||
var query = searchQuery.toLowerCase();
|
||||
|
||||
for (var i = 0; i < allWidgets.length; i++) {
|
||||
var widget = allWidgets[i];
|
||||
var name = widget.name ? widget.name.toLowerCase() : "";
|
||||
var description = widget.description ? widget.description.toLowerCase() : "";
|
||||
var id = widget.id ? widget.id.toLowerCase() : "";
|
||||
|
||||
if (name.indexOf(query) !== -1 || description.indexOf(query) !== -1 || id.indexOf(query) !== -1)
|
||||
filtered.push(widget);
|
||||
}
|
||||
|
||||
filteredWidgets = filtered;
|
||||
selectedIndex = -1;
|
||||
keyboardNavigationActive = false;
|
||||
}
|
||||
|
||||
function selectNext() {
|
||||
if (filteredWidgets.length === 0)
|
||||
return;
|
||||
keyboardNavigationActive = true;
|
||||
selectedIndex = Math.min(selectedIndex + 1, filteredWidgets.length - 1);
|
||||
}
|
||||
|
||||
function selectPrevious() {
|
||||
if (filteredWidgets.length === 0)
|
||||
return;
|
||||
keyboardNavigationActive = true;
|
||||
selectedIndex = Math.max(selectedIndex - 1, -1);
|
||||
if (selectedIndex === -1)
|
||||
keyboardNavigationActive = false;
|
||||
}
|
||||
|
||||
function selectWidget() {
|
||||
if (selectedIndex < 0 || selectedIndex >= filteredWidgets.length)
|
||||
return;
|
||||
var widget = filteredWidgets[selectedIndex];
|
||||
addWidget(widget);
|
||||
}
|
||||
|
||||
function addWidget(widget) {
|
||||
const widgetType = widget.id;
|
||||
const defaultConfig = DesktopWidgetRegistry.getDefaultConfig(widgetType);
|
||||
const name = widget.name || widgetType;
|
||||
SettingsData.createDesktopWidgetInstance(widgetType, name, defaultConfig);
|
||||
root.widgetAdded(widgetType);
|
||||
root.hide();
|
||||
}
|
||||
|
||||
function show() {
|
||||
updateFilteredWidgets();
|
||||
if (parentModal)
|
||||
parentModal.shouldHaveFocus = false;
|
||||
visible = true;
|
||||
Qt.callLater(() => {
|
||||
searchField.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
function hide() {
|
||||
visible = false;
|
||||
root.closed();
|
||||
if (!parentModal)
|
||||
return;
|
||||
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
||||
Qt.callLater(() => {
|
||||
if (parentModal && parentModal.modalFocusScope)
|
||||
parentModal.modalFocusScope.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
objectName: "desktopWidgetBrowser"
|
||||
title: I18n.tr("Add Desktop Widget")
|
||||
minimumSize: Qt.size(400, 350)
|
||||
implicitWidth: 500
|
||||
implicitHeight: 550
|
||||
color: Theme.surfaceContainer
|
||||
visible: false
|
||||
|
||||
onVisibleChanged: {
|
||||
if (visible) {
|
||||
updateFilteredWidgets();
|
||||
Qt.callLater(() => {
|
||||
searchField.forceActiveFocus();
|
||||
});
|
||||
return;
|
||||
}
|
||||
searchQuery = "";
|
||||
filteredWidgets = [];
|
||||
selectedIndex = -1;
|
||||
keyboardNavigationActive = false;
|
||||
if (!parentModal)
|
||||
return;
|
||||
parentModal.shouldHaveFocus = Qt.binding(() => parentModal.shouldBeVisible);
|
||||
Qt.callLater(() => {
|
||||
if (parentModal && parentModal.modalFocusScope)
|
||||
parentModal.modalFocusScope.forceActiveFocus();
|
||||
});
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DesktopWidgetRegistry
|
||||
function onRegistryChanged() {
|
||||
if (root.visible)
|
||||
root.updateFilteredWidgets();
|
||||
}
|
||||
}
|
||||
|
||||
FocusScope {
|
||||
id: widgetKeyHandler
|
||||
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
|
||||
Keys.onPressed: event => {
|
||||
switch (event.key) {
|
||||
case Qt.Key_Escape:
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Down:
|
||||
root.selectNext();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Up:
|
||||
root.selectPrevious();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_Return:
|
||||
case Qt.Key_Enter:
|
||||
if (root.keyboardNavigationActive) {
|
||||
root.selectWidget();
|
||||
} else if (root.filteredWidgets.length > 0) {
|
||||
root.addWidget(root.filteredWidgets[0]);
|
||||
}
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (event.modifiers & Qt.ControlModifier) {
|
||||
switch (event.key) {
|
||||
case Qt.Key_N:
|
||||
case Qt.Key_J:
|
||||
root.selectNext();
|
||||
event.accepted = true;
|
||||
return;
|
||||
case Qt.Key_P:
|
||||
case Qt.Key_K:
|
||||
root.selectPrevious();
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: 0
|
||||
|
||||
Item {
|
||||
id: titleBar
|
||||
width: parent.width
|
||||
height: 48
|
||||
|
||||
Rectangle {
|
||||
anchors.fill: parent
|
||||
color: Theme.withAlpha(Theme.surfaceContainer, 0.5)
|
||||
}
|
||||
|
||||
Row {
|
||||
anchors.left: parent.left
|
||||
anchors.leftMargin: Theme.spacingL
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankIcon {
|
||||
name: "add_circle"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Add Desktop Widget")
|
||||
font.pixelSize: Theme.fontSizeXLarge
|
||||
color: Theme.surfaceText
|
||||
font.weight: Font.Medium
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
DankActionButton {
|
||||
circular: false
|
||||
iconName: "close"
|
||||
iconSize: Theme.iconSize - 4
|
||||
iconColor: Theme.surfaceText
|
||||
anchors.right: parent.right
|
||||
anchors.rightMargin: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
onClicked: root.hide()
|
||||
}
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: parent.height - titleBar.height
|
||||
|
||||
Column {
|
||||
id: contentColumn
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Select a widget to add to your desktop. Each widget is a separate instance with its own settings.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outline
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
id: searchField
|
||||
width: parent.width
|
||||
height: 48
|
||||
cornerRadius: Theme.cornerRadius
|
||||
backgroundColor: Theme.withAlpha(Theme.surfaceContainerHigh, Theme.popupTransparency)
|
||||
normalBorderColor: Theme.outlineMedium
|
||||
focusedBorderColor: Theme.primary
|
||||
leftIconName: "search"
|
||||
leftIconSize: Theme.iconSize
|
||||
leftIconColor: Theme.surfaceVariantText
|
||||
leftIconFocusedColor: Theme.primary
|
||||
showClearButton: true
|
||||
textColor: Theme.surfaceText
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
placeholderText: I18n.tr("Search widgets...")
|
||||
text: root.searchQuery
|
||||
focus: true
|
||||
ignoreLeftRightKeys: true
|
||||
keyForwardTargets: [widgetKeyHandler]
|
||||
onTextEdited: {
|
||||
root.searchQuery = text;
|
||||
root.updateFilteredWidgets();
|
||||
}
|
||||
Keys.onPressed: event => {
|
||||
if (event.key === Qt.Key_Escape) {
|
||||
root.hide();
|
||||
event.accepted = true;
|
||||
return;
|
||||
}
|
||||
if (event.key === Qt.Key_Down || event.key === Qt.Key_Up || ((event.key === Qt.Key_Return || event.key === Qt.Key_Enter) && text.length === 0))
|
||||
event.accepted = false;
|
||||
}
|
||||
}
|
||||
|
||||
DankListView {
|
||||
id: widgetList
|
||||
|
||||
width: parent.width
|
||||
height: parent.height - y
|
||||
spacing: Theme.spacingS
|
||||
model: root.filteredWidgets
|
||||
clip: true
|
||||
|
||||
delegate: Rectangle {
|
||||
id: delegateRoot
|
||||
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: widgetList.width
|
||||
height: 72
|
||||
radius: Theme.cornerRadius
|
||||
property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex
|
||||
color: isSelected ? Theme.primarySelected : widgetArea.containsMouse ? Theme.primaryHover : Theme.withAlpha(Theme.surfaceVariant, 0.3)
|
||||
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.outline, 0.2)
|
||||
border.width: isSelected ? 2 : 1
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Rectangle {
|
||||
width: 44
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: Theme.primarySelected
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
DankIcon {
|
||||
anchors.centerIn: parent
|
||||
name: delegateRoot.modelData.icon || "widgets"
|
||||
size: Theme.iconSize
|
||||
color: Theme.primary
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: 2
|
||||
width: parent.width - 44 - Theme.iconSize - Theme.spacingM * 3
|
||||
|
||||
Row {
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: delegateRoot.modelData.name || delegateRoot.modelData.id
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.weight: Font.Medium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
visible: delegateRoot.modelData.type === "plugin"
|
||||
width: pluginLabel.implicitWidth + Theme.spacingXS * 2
|
||||
height: 18
|
||||
radius: 9
|
||||
color: Theme.withAlpha(Theme.secondary, 0.15)
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
id: pluginLabel
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Plugin")
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
color: Theme.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
text: delegateRoot.modelData.description || ""
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.outline
|
||||
elide: Text.ElideRight
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 2
|
||||
visible: text !== ""
|
||||
}
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: "add"
|
||||
size: Theme.iconSize - 4
|
||||
color: Theme.primary
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: widgetArea
|
||||
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: Qt.PointingHandCursor
|
||||
onClicked: root.addWidget(delegateRoot.modelData)
|
||||
}
|
||||
|
||||
Behavior on color {
|
||||
ColorAnimation {
|
||||
duration: Theme.shortDuration
|
||||
easing.type: Theme.standardEasing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
width: widgetList.width
|
||||
height: emptyText.visible ? 60 : 0
|
||||
|
||||
StyledText {
|
||||
id: emptyText
|
||||
visible: root.filteredWidgets.length === 0
|
||||
text: root.searchQuery.length > 0 ? I18n.tr("No widgets match your search") : I18n.tr("No widgets available")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
anchors.centerIn: parent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
158
quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml
Normal file
158
quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml
Normal file
@@ -0,0 +1,158 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
import qs.Modules.Settings.DesktopWidgetSettings as DWS
|
||||
|
||||
SettingsCard {
|
||||
id: root
|
||||
|
||||
required property var instanceData
|
||||
property bool isExpanded: false
|
||||
|
||||
readonly property string instanceId: instanceData?.id ?? ""
|
||||
readonly property string widgetType: instanceData?.widgetType ?? ""
|
||||
readonly property var widgetDef: DesktopWidgetRegistry.getWidget(widgetType)
|
||||
readonly property string widgetName: instanceData?.name ?? widgetDef?.name ?? widgetType
|
||||
|
||||
signal deleteRequested
|
||||
|
||||
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
|
||||
collapsible: true
|
||||
expanded: isExpanded
|
||||
|
||||
onExpandedChanged: isExpanded = expanded
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
|
||||
Item {
|
||||
width: parent.width - toggleRow.width - deleteBtn.width - Theme.spacingS * 2
|
||||
height: 1
|
||||
}
|
||||
|
||||
Row {
|
||||
id: toggleRow
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: (instanceData?.enabled ?? true) ? I18n.tr("Enabled") : I18n.tr("Disabled")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankToggle {
|
||||
checked: instanceData?.enabled ?? true
|
||||
onToggled: isChecked => {
|
||||
if (!root.instanceId) return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, { enabled: isChecked });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
id: deleteBtn
|
||||
iconName: "delete"
|
||||
backgroundColor: "transparent"
|
||||
textColor: Theme.error
|
||||
buttonHeight: 32
|
||||
horizontalPadding: 4
|
||||
onClicked: root.deleteRequested()
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
visible: root.isExpanded
|
||||
opacity: visible ? 1 : 0
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: nameRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: nameRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Name")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
width: 80
|
||||
}
|
||||
|
||||
DankTextField {
|
||||
width: parent.width - 80 - Theme.spacingM
|
||||
text: root.widgetName
|
||||
onEditingFinished: {
|
||||
if (!root.instanceId) return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, { name: text });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property string instanceId: ""
|
||||
property var instanceData: null
|
||||
|
||||
readonly property var cfg: instanceData?.config ?? {}
|
||||
|
||||
function updateConfig(key, value) {
|
||||
if (!instanceId)
|
||||
return;
|
||||
var updates = {};
|
||||
updates[key] = value;
|
||||
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, updates);
|
||||
}
|
||||
|
||||
width: parent?.width ?? 400
|
||||
spacing: 0
|
||||
|
||||
SettingsDropdownRow {
|
||||
text: I18n.tr("Clock Style")
|
||||
options: [I18n.tr("Digital"), I18n.tr("Analog"), I18n.tr("Stacked")]
|
||||
currentValue: {
|
||||
switch (cfg.style) {
|
||||
case "analog":
|
||||
return I18n.tr("Analog");
|
||||
case "stacked":
|
||||
return I18n.tr("Stacked");
|
||||
default:
|
||||
return I18n.tr("Digital");
|
||||
}
|
||||
}
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case I18n.tr("Analog"):
|
||||
root.updateConfig("style", "analog");
|
||||
return;
|
||||
case I18n.tr("Stacked"):
|
||||
root.updateConfig("style", "stacked");
|
||||
return;
|
||||
default:
|
||||
root.updateConfig("style", "digital");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.style === "analog"
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.style === "analog"
|
||||
text: I18n.tr("Show Hour Numbers")
|
||||
checked: cfg.showAnalogNumbers ?? false
|
||||
onToggled: checked => root.updateConfig("showAnalogNumbers", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.style === "analog"
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.style === "analog"
|
||||
text: I18n.tr("Show Seconds")
|
||||
checked: cfg.showAnalogSeconds ?? true
|
||||
onToggled: checked => root.updateConfig("showAnalogSeconds", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Show Date")
|
||||
checked: cfg.showDate ?? true
|
||||
onToggled: checked => root.updateConfig("showDate", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Transparency")
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: Math.round((cfg.transparency ?? 0.8) * 100)
|
||||
unit: "%"
|
||||
onSliderValueChanged: newValue => root.updateConfig("transparency", newValue / 100)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsColorPicker {
|
||||
colorMode: cfg.colorMode ?? "primary"
|
||||
customColor: cfg.customColor ?? "#ffffff"
|
||||
onColorModeSelected: mode => root.updateConfig("colorMode", mode)
|
||||
onCustomColorSelected: selectedColor => root.updateConfig("customColor", selectedColor.toString())
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: cfg.displayPreferences ?? ["all"]
|
||||
onPreferencesChanged: prefs => root.updateConfig("displayPreferences", prefs)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: resetRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: resetRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Position")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Size")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property string instanceId: ""
|
||||
property var instanceData: null
|
||||
property string widgetType: ""
|
||||
property var widgetDef: null
|
||||
|
||||
readonly property var cfg: instanceData?.config ?? {}
|
||||
readonly property string settingsPath: widgetDef?.settingsComponent ?? ""
|
||||
|
||||
function updateConfig(key, value) {
|
||||
if (!instanceId)
|
||||
return;
|
||||
var updates = {};
|
||||
updates[key] = value;
|
||||
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, updates);
|
||||
}
|
||||
|
||||
QtObject {
|
||||
id: instanceScopedPluginService
|
||||
|
||||
readonly property var availablePlugins: PluginService.availablePlugins
|
||||
readonly property var loadedPlugins: PluginService.loadedPlugins
|
||||
readonly property var pluginDesktopComponents: PluginService.pluginDesktopComponents
|
||||
|
||||
signal pluginDataChanged(string pluginId)
|
||||
signal pluginLoaded(string pluginId)
|
||||
signal pluginUnloaded(string pluginId)
|
||||
|
||||
function loadPluginData(pluginId, key, defaultValue) {
|
||||
const cfg = root.instanceData?.config;
|
||||
if (cfg && key in cfg)
|
||||
return cfg[key];
|
||||
return SettingsData.getPluginSetting(root.widgetType, key, defaultValue);
|
||||
}
|
||||
|
||||
function savePluginData(pluginId, key, value) {
|
||||
root.updateConfig(key, value);
|
||||
Qt.callLater(() => pluginDataChanged(root.widgetType));
|
||||
return true;
|
||||
}
|
||||
|
||||
function getPluginVariants(pluginId) {
|
||||
return PluginService.getPluginVariants(pluginId);
|
||||
}
|
||||
|
||||
function isPluginLoaded(pluginId) {
|
||||
return PluginService.isPluginLoaded(pluginId);
|
||||
}
|
||||
}
|
||||
|
||||
width: parent?.width ?? 400
|
||||
spacing: 0
|
||||
|
||||
Loader {
|
||||
id: pluginSettingsLoader
|
||||
width: parent.width
|
||||
active: root.settingsPath !== ""
|
||||
|
||||
source: root.settingsPath
|
||||
|
||||
onLoaded: {
|
||||
if (!item)
|
||||
return;
|
||||
if (item.instanceId !== undefined)
|
||||
item.instanceId = root.instanceId;
|
||||
if (item.instanceData !== undefined)
|
||||
item.instanceData = Qt.binding(() => root.instanceData);
|
||||
if (item.pluginService !== undefined)
|
||||
item.pluginService = instanceScopedPluginService;
|
||||
if (item.reloadChildValues)
|
||||
Qt.callLater(item.reloadChildValues);
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
visible: root.settingsPath === ""
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: cfg.displayPreferences ?? ["all"]
|
||||
onPreferencesChanged: prefs => root.updateConfig("displayPreferences", prefs)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: resetRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: resetRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Position")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Size")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
import qs.Modules.Settings.Widgets
|
||||
|
||||
Column {
|
||||
id: root
|
||||
|
||||
property string instanceId: ""
|
||||
property var instanceData: null
|
||||
|
||||
readonly property var cfg: instanceData?.config ?? {}
|
||||
|
||||
function updateConfig(key, value) {
|
||||
if (!instanceId)
|
||||
return;
|
||||
var updates = {};
|
||||
updates[key] = value;
|
||||
SettingsData.updateDesktopWidgetInstanceConfig(instanceId, updates);
|
||||
}
|
||||
|
||||
width: parent?.width ?? 400
|
||||
spacing: 0
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Show Header")
|
||||
checked: cfg.showHeader ?? true
|
||||
onToggled: checked => root.updateConfig("showHeader", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: graphIntervalColumn.height + Theme.spacingM * 2
|
||||
|
||||
Column {
|
||||
id: graphIntervalColumn
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Graph Time Range")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
model: ["1m", "5m", "10m", "30m"]
|
||||
currentIndex: {
|
||||
switch (cfg.graphInterval ?? 60) {
|
||||
case 60:
|
||||
return 0;
|
||||
case 300:
|
||||
return 1;
|
||||
case 600:
|
||||
return 2;
|
||||
case 1800:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 48
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
const values = [60, 300, 600, 1800];
|
||||
root.updateConfig("graphInterval", values[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("CPU")
|
||||
checked: cfg.showCpu ?? true
|
||||
onToggled: checked => root.updateConfig("showCpu", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.showCpu ?? true
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.showCpu ?? true
|
||||
text: I18n.tr("CPU Graph")
|
||||
checked: cfg.showCpuGraph ?? true
|
||||
onToggled: checked => root.updateConfig("showCpuGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.showCpu ?? true
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.showCpu ?? true
|
||||
text: I18n.tr("CPU Temperature")
|
||||
checked: cfg.showCpuTemp ?? true
|
||||
onToggled: checked => root.updateConfig("showCpuTemp", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("GPU Temperature")
|
||||
checked: cfg.showGpuTemp ?? false
|
||||
onToggled: checked => root.updateConfig("showGpuTemp", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: (cfg.showGpuTemp ?? false) && DgopService.availableGpus.length > 0
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: gpuSelectColumn.height + Theme.spacingM * 2
|
||||
visible: (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")
|
||||
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: (cfg.gpuPciId ?? "") === modelData.pciId
|
||||
|
||||
width: parent.width
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: isSelected ? Theme.primarySelected : Theme.surfaceHover
|
||||
border.color: isSelected ? Theme.primary : "transparent"
|
||||
border.width: 2
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Memory")
|
||||
checked: cfg.showMemory ?? true
|
||||
onToggled: checked => root.updateConfig("showMemory", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.showMemory ?? true
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.showMemory ?? true
|
||||
text: I18n.tr("Memory Graph")
|
||||
checked: cfg.showMemoryGraph ?? true
|
||||
onToggled: checked => root.updateConfig("showMemoryGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Network")
|
||||
checked: cfg.showNetwork ?? true
|
||||
onToggled: checked => root.updateConfig("showNetwork", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.showNetwork ?? true
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: cfg.showNetwork ?? true
|
||||
text: I18n.tr("Network Graph")
|
||||
checked: cfg.showNetworkGraph ?? true
|
||||
onToggled: checked => root.updateConfig("showNetworkGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Disk")
|
||||
checked: cfg.showDisk ?? true
|
||||
onToggled: checked => root.updateConfig("showDisk", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Top Processes")
|
||||
checked: cfg.showTopProcesses ?? false
|
||||
onToggled: checked => root.updateConfig("showTopProcesses", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: cfg.showTopProcesses ?? false
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: topProcessesColumn.height + Theme.spacingM * 2
|
||||
visible: cfg.showTopProcesses ?? false
|
||||
|
||||
Column {
|
||||
id: topProcessesColumn
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width - processCountButtons.width - Theme.spacingM
|
||||
text: I18n.tr("Process Count")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: processCountButtons
|
||||
model: ["3", "5", "10"]
|
||||
currentIndex: {
|
||||
switch (cfg.topProcessCount ?? 3) {
|
||||
case 3:
|
||||
return 0;
|
||||
case 5:
|
||||
return 1;
|
||||
case 10:
|
||||
return 2;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 36
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
const values = [3, 5, 10];
|
||||
root.updateConfig("topProcessCount", values[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width - sortByButtons.width - Theme.spacingM
|
||||
text: I18n.tr("Sort By")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: sortByButtons
|
||||
model: ["CPU", "MEM"]
|
||||
currentIndex: (cfg.topProcessSortBy ?? "cpu") === "cpu" ? 0 : 1
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 48
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
root.updateConfig("topProcessSortBy", index === 0 ? "cpu" : "memory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDropdownRow {
|
||||
text: I18n.tr("Layout")
|
||||
options: [I18n.tr("Auto"), I18n.tr("Grid"), I18n.tr("List")]
|
||||
currentValue: {
|
||||
switch (cfg.layoutMode ?? "auto") {
|
||||
case "grid":
|
||||
return I18n.tr("Grid");
|
||||
case "list":
|
||||
return I18n.tr("List");
|
||||
default:
|
||||
return I18n.tr("Auto");
|
||||
}
|
||||
}
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case I18n.tr("Grid"):
|
||||
root.updateConfig("layoutMode", "grid");
|
||||
return;
|
||||
case I18n.tr("List"):
|
||||
root.updateConfig("layoutMode", "list");
|
||||
return;
|
||||
default:
|
||||
root.updateConfig("layoutMode", "auto");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Transparency")
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: Math.round((cfg.transparency ?? 0.8) * 100)
|
||||
unit: "%"
|
||||
onSliderValueChanged: newValue => root.updateConfig("transparency", newValue / 100)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsColorPicker {
|
||||
colorMode: cfg.colorMode ?? "primary"
|
||||
customColor: cfg.customColor ?? "#ffffff"
|
||||
onColorModeSelected: mode => root.updateConfig("colorMode", mode)
|
||||
onCustomColorSelected: selectedColor => root.updateConfig("customColor", selectedColor.toString())
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: cfg.displayPreferences ?? ["all"]
|
||||
onPreferencesChanged: prefs => root.updateConfig("displayPreferences", prefs)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: resetRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: resetRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Position")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Size")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
if (!root.instanceId)
|
||||
return;
|
||||
SettingsData.updateDesktopWidgetInstance(root.instanceId, {
|
||||
positions: {}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
pragma ComponentBehavior: Bound
|
||||
|
||||
import QtQuick
|
||||
import Quickshell
|
||||
import qs.Common
|
||||
import qs.Services
|
||||
import qs.Widgets
|
||||
@@ -9,6 +10,23 @@ import qs.Modules.Settings.Widgets
|
||||
Item {
|
||||
id: root
|
||||
|
||||
property var expandedStates: ({})
|
||||
property var parentModal: null
|
||||
|
||||
DesktopWidgetBrowser {
|
||||
id: widgetBrowser
|
||||
parentModal: root.parentModal
|
||||
onWidgetAdded: widgetType => {
|
||||
ToastService.showInfo(I18n.tr("Widget added"));
|
||||
}
|
||||
}
|
||||
|
||||
PluginBrowser {
|
||||
id: desktopPluginBrowser
|
||||
parentModal: root.parentModal
|
||||
typeFilter: "desktop-widget"
|
||||
}
|
||||
|
||||
DankFlickable {
|
||||
anchors.fill: parent
|
||||
clip: true
|
||||
@@ -23,686 +41,94 @@ Item {
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "schedule"
|
||||
title: I18n.tr("Desktop Clock")
|
||||
collapsible: true
|
||||
expanded: false
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Enable Desktop Clock")
|
||||
checked: SettingsData.desktopClockEnabled
|
||||
onToggled: checked => SettingsData.set("desktopClockEnabled", checked)
|
||||
}
|
||||
iconName: "widgets"
|
||||
title: I18n.tr("Desktop Widgets")
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
visible: SettingsData.desktopClockEnabled
|
||||
opacity: visible ? 1 : 0
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDropdownRow {
|
||||
text: I18n.tr("Clock Style")
|
||||
options: [I18n.tr("Digital"), I18n.tr("Analog"), I18n.tr("Stacked")]
|
||||
currentValue: {
|
||||
switch (SettingsData.desktopClockStyle) {
|
||||
case "analog":
|
||||
return I18n.tr("Analog");
|
||||
case "stacked":
|
||||
return I18n.tr("Stacked");
|
||||
default:
|
||||
return I18n.tr("Digital");
|
||||
}
|
||||
}
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case I18n.tr("Analog"):
|
||||
SettingsData.set("desktopClockStyle", "analog");
|
||||
return;
|
||||
case I18n.tr("Stacked"):
|
||||
SettingsData.set("desktopClockStyle", "stacked");
|
||||
return;
|
||||
default:
|
||||
SettingsData.set("desktopClockStyle", "digital");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.desktopClockStyle === "analog"
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.desktopClockStyle === "analog"
|
||||
text: I18n.tr("Show Hour Numbers")
|
||||
checked: SettingsData.desktopClockShowAnalogNumbers
|
||||
onToggled: checked => SettingsData.set("desktopClockShowAnalogNumbers", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.desktopClockStyle === "analog"
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.desktopClockStyle === "analog"
|
||||
text: I18n.tr("Show Seconds")
|
||||
checked: SettingsData.desktopClockShowAnalogSeconds
|
||||
onToggled: checked => SettingsData.set("desktopClockShowAnalogSeconds", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Show Date")
|
||||
checked: SettingsData.desktopClockShowDate
|
||||
onToggled: checked => SettingsData.set("desktopClockShowDate", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Transparency")
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: Math.round(SettingsData.desktopClockTransparency * 100)
|
||||
unit: "%"
|
||||
onSliderValueChanged: newValue => SettingsData.set("desktopClockTransparency", newValue / 100)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsColorPicker {
|
||||
colorMode: SettingsData.desktopClockColorMode
|
||||
customColor: SettingsData.desktopClockCustomColor
|
||||
onColorModeSelected: mode => SettingsData.set("desktopClockColorMode", mode)
|
||||
onCustomColorSelected: selectedColor => SettingsData.set("desktopClockCustomColor", selectedColor.toString())
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: SettingsData.desktopClockDisplayPreferences
|
||||
onPreferencesChanged: prefs => SettingsData.set("desktopClockDisplayPreferences", prefs)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
StyledText {
|
||||
width: parent.width
|
||||
height: clockResetRow.height + Theme.spacingM * 2
|
||||
text: I18n.tr("Add and configure widgets that appear on your desktop")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
|
||||
Row {
|
||||
id: clockResetRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
Row {
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Position")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
SettingsData.set("desktopClockX", -1);
|
||||
SettingsData.set("desktopClockY", -1);
|
||||
}
|
||||
}
|
||||
DankButton {
|
||||
text: I18n.tr("Add Widget")
|
||||
iconName: "add"
|
||||
onClicked: widgetBrowser.show()
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Size")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
SettingsData.set("desktopClockWidth", 280);
|
||||
SettingsData.set("desktopClockHeight", 180);
|
||||
}
|
||||
}
|
||||
DankButton {
|
||||
text: I18n.tr("Browse Plugins")
|
||||
iconName: "store"
|
||||
onClicked: desktopPluginBrowser.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
Column {
|
||||
id: instancesColumn
|
||||
width: parent.width
|
||||
iconName: "monitoring"
|
||||
title: I18n.tr("System Monitor")
|
||||
collapsible: true
|
||||
expanded: false
|
||||
spacing: Theme.spacingM
|
||||
visible: SettingsData.desktopWidgetInstances.length > 0
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Enable System Monitor")
|
||||
checked: SettingsData.systemMonitorEnabled
|
||||
onToggled: checked => SettingsData.set("systemMonitorEnabled", checked)
|
||||
}
|
||||
Repeater {
|
||||
id: instanceRepeater
|
||||
model: ScriptModel {
|
||||
id: instancesModel
|
||||
objectProp: "id"
|
||||
values: SettingsData.desktopWidgetInstances
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: 0
|
||||
visible: SettingsData.systemMonitorEnabled
|
||||
opacity: visible ? 1 : 0
|
||||
DesktopWidgetInstanceCard {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
Behavior on opacity {
|
||||
NumberAnimation {
|
||||
duration: Theme.mediumDuration
|
||||
easing.type: Theme.emphasizedEasing
|
||||
readonly property string instanceIdRef: modelData.id
|
||||
readonly property var liveInstanceData: {
|
||||
const instances = SettingsData.desktopWidgetInstances || [];
|
||||
return instances.find(inst => inst.id === instanceIdRef) ?? modelData;
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
width: instancesColumn.width
|
||||
instanceData: liveInstanceData
|
||||
isExpanded: root.expandedStates[instanceIdRef] ?? false
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Show Header")
|
||||
checked: SettingsData.systemMonitorShowHeader
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowHeader", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: graphIntervalColumn.height + Theme.spacingM * 2
|
||||
|
||||
Column {
|
||||
id: graphIntervalColumn
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingS
|
||||
|
||||
StyledText {
|
||||
text: I18n.tr("Graph Time Range")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
model: ["1m", "5m", "10m", "30m"]
|
||||
currentIndex: {
|
||||
switch (SettingsData.systemMonitorGraphInterval) {
|
||||
case 60:
|
||||
return 0;
|
||||
case 300:
|
||||
return 1;
|
||||
case 600:
|
||||
return 2;
|
||||
case 1800:
|
||||
return 3;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 48
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
const values = [60, 300, 600, 1800];
|
||||
SettingsData.set("systemMonitorGraphInterval", values[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("CPU")
|
||||
checked: SettingsData.systemMonitorShowCpu
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowCpu", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowCpu
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.systemMonitorShowCpu
|
||||
text: I18n.tr("CPU Graph")
|
||||
checked: SettingsData.systemMonitorShowCpuGraph
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowCpuGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowCpu
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.systemMonitorShowCpu
|
||||
text: I18n.tr("CPU Temperature")
|
||||
checked: SettingsData.systemMonitorShowCpuTemp
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowCpuTemp", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("GPU Temperature")
|
||||
checked: SettingsData.systemMonitorShowGpuTemp
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowGpuTemp", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowGpuTemp && DgopService.availableGpus.length > 0
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: gpuSelectColumn.height + Theme.spacingM * 2
|
||||
visible: SettingsData.systemMonitorShowGpuTemp && 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")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
|
||||
Repeater {
|
||||
model: DgopService.availableGpus
|
||||
|
||||
Rectangle {
|
||||
required property var modelData
|
||||
|
||||
width: parent.width
|
||||
height: 44
|
||||
radius: Theme.cornerRadius
|
||||
color: SettingsData.systemMonitorGpuPciId === modelData.pciId ? Theme.primarySelected : Theme.surfaceHover
|
||||
border.color: SettingsData.systemMonitorGpuPciId === modelData.pciId ? Theme.primary : "transparent"
|
||||
border.width: 2
|
||||
|
||||
Row {
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingS
|
||||
spacing: Theme.spacingS
|
||||
|
||||
DankIcon {
|
||||
name: "videocam"
|
||||
size: Theme.iconSizeSmall
|
||||
color: SettingsData.systemMonitorGpuPciId === modelData.pciId ? 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: SettingsData.set("systemMonitorGpuPciId", modelData.pciId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Memory")
|
||||
checked: SettingsData.systemMonitorShowMemory
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowMemory", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowMemory
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.systemMonitorShowMemory
|
||||
text: I18n.tr("Memory Graph")
|
||||
checked: SettingsData.systemMonitorShowMemoryGraph
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowMemoryGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Network")
|
||||
checked: SettingsData.systemMonitorShowNetwork
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowNetwork", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowNetwork
|
||||
}
|
||||
|
||||
SettingsToggleRow {
|
||||
visible: SettingsData.systemMonitorShowNetwork
|
||||
text: I18n.tr("Network Graph")
|
||||
checked: SettingsData.systemMonitorShowNetworkGraph
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowNetworkGraph", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Disk")
|
||||
checked: SettingsData.systemMonitorShowDisk
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowDisk", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsToggleRow {
|
||||
text: I18n.tr("Top Processes")
|
||||
checked: SettingsData.systemMonitorShowTopProcesses
|
||||
onToggled: checked => SettingsData.set("systemMonitorShowTopProcesses", checked)
|
||||
}
|
||||
|
||||
SettingsDivider {
|
||||
visible: SettingsData.systemMonitorShowTopProcesses
|
||||
}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: topProcessesColumn.height + Theme.spacingM * 2
|
||||
visible: SettingsData.systemMonitorShowTopProcesses
|
||||
|
||||
Column {
|
||||
id: topProcessesColumn
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width - processCountButtons.width - Theme.spacingM
|
||||
text: I18n.tr("Process Count")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: processCountButtons
|
||||
model: ["3", "5", "10"]
|
||||
currentIndex: {
|
||||
switch (SettingsData.systemMonitorTopProcessCount) {
|
||||
case 3:
|
||||
return 0;
|
||||
case 5:
|
||||
return 1;
|
||||
case 10:
|
||||
return 2;
|
||||
default:
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 36
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
const values = [3, 5, 10];
|
||||
SettingsData.set("systemMonitorTopProcessCount", values[index]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width - sortByButtons.width - Theme.spacingM
|
||||
text: I18n.tr("Sort By")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButtonGroup {
|
||||
id: sortByButtons
|
||||
model: ["CPU", "MEM"]
|
||||
currentIndex: SettingsData.systemMonitorTopProcessSortBy === "cpu" ? 0 : 1
|
||||
buttonHeight: 32
|
||||
minButtonWidth: 48
|
||||
textSize: Theme.fontSizeSmall
|
||||
checkEnabled: false
|
||||
onSelectionChanged: (index, selected) => {
|
||||
if (!selected)
|
||||
return;
|
||||
SettingsData.set("systemMonitorTopProcessSortBy", index === 0 ? "cpu" : "memory");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDropdownRow {
|
||||
text: I18n.tr("Layout")
|
||||
options: [I18n.tr("Auto"), I18n.tr("Grid"), I18n.tr("List")]
|
||||
currentValue: {
|
||||
switch (SettingsData.systemMonitorLayoutMode) {
|
||||
case "grid":
|
||||
return I18n.tr("Grid");
|
||||
case "list":
|
||||
return I18n.tr("List");
|
||||
default:
|
||||
return I18n.tr("Auto");
|
||||
}
|
||||
}
|
||||
onValueChanged: value => {
|
||||
switch (value) {
|
||||
case I18n.tr("Grid"):
|
||||
SettingsData.set("systemMonitorLayoutMode", "grid");
|
||||
onExpandedChanged: {
|
||||
if (expanded === (root.expandedStates[instanceIdRef] ?? false))
|
||||
return;
|
||||
case I18n.tr("List"):
|
||||
SettingsData.set("systemMonitorLayoutMode", "list");
|
||||
return;
|
||||
default:
|
||||
SettingsData.set("systemMonitorLayoutMode", "auto");
|
||||
}
|
||||
var states = Object.assign({}, root.expandedStates);
|
||||
states[instanceIdRef] = expanded;
|
||||
root.expandedStates = states;
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsSliderRow {
|
||||
text: I18n.tr("Transparency")
|
||||
minimum: 0
|
||||
maximum: 100
|
||||
value: Math.round(SettingsData.systemMonitorTransparency * 100)
|
||||
unit: "%"
|
||||
onSliderValueChanged: newValue => SettingsData.set("systemMonitorTransparency", newValue / 100)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsColorPicker {
|
||||
colorMode: SettingsData.systemMonitorColorMode
|
||||
customColor: SettingsData.systemMonitorCustomColor
|
||||
onColorModeSelected: mode => SettingsData.set("systemMonitorColorMode", mode)
|
||||
onCustomColorSelected: selectedColor => SettingsData.set("systemMonitorCustomColor", selectedColor.toString())
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
SettingsDisplayPicker {
|
||||
displayPreferences: SettingsData.systemMonitorDisplayPreferences
|
||||
onPreferencesChanged: prefs => SettingsData.set("systemMonitorDisplayPreferences", prefs)
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: sysMonResetRow.height + Theme.spacingM * 2
|
||||
|
||||
Row {
|
||||
id: sysMonResetRow
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Position")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
SettingsData.set("systemMonitorX", -1);
|
||||
SettingsData.set("systemMonitorY", -1);
|
||||
}
|
||||
}
|
||||
|
||||
DankButton {
|
||||
text: I18n.tr("Reset Size")
|
||||
backgroundColor: Theme.surfaceHover
|
||||
textColor: Theme.surfaceText
|
||||
buttonHeight: 36
|
||||
onClicked: {
|
||||
SettingsData.set("systemMonitorWidth", 320);
|
||||
SettingsData.set("systemMonitorHeight", 480);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SettingsDivider {}
|
||||
|
||||
Item {
|
||||
width: parent.width
|
||||
height: variantsColumn.height + Theme.spacingM * 2
|
||||
|
||||
Column {
|
||||
id: variantsColumn
|
||||
width: parent.width - Theme.spacingM * 2
|
||||
x: Theme.spacingM
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
spacing: Theme.spacingM
|
||||
|
||||
Row {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingM
|
||||
|
||||
StyledText {
|
||||
width: parent.width - addVariantBtn.width - Theme.spacingM
|
||||
text: I18n.tr("Widget Variants")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceText
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
}
|
||||
|
||||
DankButton {
|
||||
id: addVariantBtn
|
||||
text: I18n.tr("Add")
|
||||
iconName: "add"
|
||||
onClicked: {
|
||||
const variant = SettingsData.createSystemMonitorVariant("Monitor " + (SettingsData.systemMonitorVariants.length + 1), SettingsData.getDefaultSystemMonitorConfig());
|
||||
if (variant)
|
||||
ToastService.showInfo(I18n.tr("Variant created - expand to configure"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: variantsListColumn
|
||||
width: parent.width
|
||||
spacing: Theme.spacingS
|
||||
visible: SettingsData.systemMonitorVariants.length > 0
|
||||
|
||||
property var expandedStates: ({})
|
||||
|
||||
Repeater {
|
||||
model: SettingsData.systemMonitorVariants
|
||||
|
||||
SystemMonitorVariantCard {
|
||||
required property var modelData
|
||||
required property int index
|
||||
|
||||
width: parent.width
|
||||
variant: modelData
|
||||
expanded: variantsListColumn.expandedStates[modelData.id] || false
|
||||
|
||||
onExpandToggled: isExpanded => {
|
||||
var states = JSON.parse(JSON.stringify(variantsListColumn.expandedStates));
|
||||
states[modelData.id] = isExpanded;
|
||||
variantsListColumn.expandedStates = states;
|
||||
}
|
||||
|
||||
onDeleteRequested: {
|
||||
SettingsData.removeSystemMonitorVariant(modelData.id);
|
||||
ToastService.showInfo(I18n.tr("Variant removed"));
|
||||
}
|
||||
|
||||
onNameChanged: newName => {
|
||||
SettingsData.updateSystemMonitorVariant(modelData.id, {
|
||||
name: newName
|
||||
});
|
||||
}
|
||||
|
||||
onConfigChanged: (key, value) => {
|
||||
var update = {};
|
||||
update[key] = value;
|
||||
SettingsData.updateSystemMonitorVariant(modelData.id, update);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.systemMonitorVariants.length === 0
|
||||
text: I18n.tr("No variants created. Click Add to create a new monitor widget.")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
}
|
||||
onDeleteRequested: {
|
||||
SettingsData.removeDesktopWidgetInstance(instanceIdRef);
|
||||
ToastService.showInfo(I18n.tr("Widget removed"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StyledText {
|
||||
visible: SettingsData.desktopWidgetInstances.length === 0
|
||||
text: I18n.tr("No widgets added. Click \"Add Widget\" to get started.")
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
color: Theme.surfaceVariantText
|
||||
width: parent.width
|
||||
wrapMode: Text.WordWrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
SettingsCard {
|
||||
width: parent.width
|
||||
iconName: "info"
|
||||
|
||||
@@ -17,6 +17,7 @@ FloatingWindow {
|
||||
property bool isLoading: false
|
||||
property var parentModal: null
|
||||
property bool pendingInstallHandled: false
|
||||
property string typeFilter: ""
|
||||
|
||||
function updateFilteredPlugins() {
|
||||
var filtered = [];
|
||||
@@ -29,6 +30,12 @@ FloatingWindow {
|
||||
if (!SessionData.showThirdPartyPlugins && !isFirstParty)
|
||||
continue;
|
||||
|
||||
if (typeFilter !== "") {
|
||||
var hasCapability = plugin.capabilities && plugin.capabilities.includes(typeFilter);
|
||||
if (!hasCapability)
|
||||
continue;
|
||||
}
|
||||
|
||||
if (query.length === 0) {
|
||||
filtered.push(plugin);
|
||||
continue;
|
||||
@@ -76,6 +83,11 @@ FloatingWindow {
|
||||
if (enableAfterInstall) {
|
||||
Qt.callLater(() => {
|
||||
PluginService.enablePlugin(pluginName);
|
||||
const plugin = PluginService.availablePlugins[pluginName];
|
||||
if (plugin?.type === "desktop") {
|
||||
const defaultConfig = DesktopWidgetRegistry.getDefaultConfig(pluginName);
|
||||
SettingsData.createDesktopWidgetInstance(pluginName, plugin.name || pluginName, defaultConfig);
|
||||
}
|
||||
hide();
|
||||
});
|
||||
}
|
||||
@@ -149,6 +161,33 @@ FloatingWindow {
|
||||
isLoading = false;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: DMSService
|
||||
|
||||
function onPluginsListReceived(plugins) {
|
||||
root.isLoading = false;
|
||||
root.allPlugins = plugins;
|
||||
root.updateFilteredPlugins();
|
||||
}
|
||||
|
||||
function onInstalledPluginsReceived(plugins) {
|
||||
var pluginMap = {};
|
||||
for (var i = 0; i < plugins.length; i++) {
|
||||
var plugin = plugins[i];
|
||||
if (plugin.id)
|
||||
pluginMap[plugin.id] = true;
|
||||
if (plugin.name)
|
||||
pluginMap[plugin.name] = true;
|
||||
}
|
||||
var updated = root.allPlugins.map(p => {
|
||||
var isInstalled = pluginMap[p.name] || pluginMap[p.id] || false;
|
||||
return Object.assign({}, p, { installed: isInstalled });
|
||||
});
|
||||
root.allPlugins = updated;
|
||||
root.updateFilteredPlugins();
|
||||
}
|
||||
}
|
||||
|
||||
ConfirmModal {
|
||||
id: urlInstallConfirm
|
||||
}
|
||||
@@ -355,8 +394,8 @@ FloatingWindow {
|
||||
property bool isSelected: root.keyboardNavigationActive && index === root.selectedIndex
|
||||
property bool isInstalled: modelData.installed || false
|
||||
property bool isFirstParty: modelData.firstParty || false
|
||||
color: isSelected ? Theme.primarySelected : Qt.rgba(Theme.surfaceVariant.r, Theme.surfaceVariant.g, Theme.surfaceVariant.b, 0.3)
|
||||
border.color: isSelected ? Theme.primary : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.2)
|
||||
color: isSelected ? Theme.primarySelected : Theme.withAlpha(Theme.surfaceVariant, 0.3)
|
||||
border.color: isSelected ? Theme.primary : Theme.withAlpha(Theme.outline, 0.2)
|
||||
border.width: isSelected ? 2 : 1
|
||||
|
||||
Column {
|
||||
@@ -396,8 +435,8 @@ FloatingWindow {
|
||||
height: 16
|
||||
width: firstPartyText.implicitWidth + Theme.spacingXS * 2
|
||||
radius: 8
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.15)
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.4)
|
||||
color: Theme.withAlpha(Theme.primary, 0.15)
|
||||
border.color: Theme.withAlpha(Theme.primary, 0.4)
|
||||
border.width: 1
|
||||
visible: isFirstParty
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -416,8 +455,8 @@ FloatingWindow {
|
||||
height: 16
|
||||
width: thirdPartyText.implicitWidth + Theme.spacingXS * 2
|
||||
radius: 8
|
||||
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.15)
|
||||
border.color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.4)
|
||||
color: Theme.withAlpha(Theme.warning, 0.15)
|
||||
border.color: Theme.withAlpha(Theme.warning, 0.4)
|
||||
border.width: 1
|
||||
visible: !isFirstParty
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
@@ -501,8 +540,10 @@ FloatingWindow {
|
||||
cursorShape: isInstalled ? Qt.ArrowCursor : Qt.PointingHandCursor
|
||||
enabled: !isInstalled
|
||||
onClicked: {
|
||||
if (!isInstalled)
|
||||
root.installPlugin(modelData.name, false);
|
||||
if (isInstalled)
|
||||
return;
|
||||
const isDesktop = modelData.type === "desktop";
|
||||
root.installPlugin(modelData.name, isDesktop);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -529,8 +570,8 @@ FloatingWindow {
|
||||
height: 18
|
||||
width: capabilityText.implicitWidth + Theme.spacingXS * 2
|
||||
radius: 9
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
|
||||
color: Theme.withAlpha(Theme.primary, 0.1)
|
||||
border.color: Theme.withAlpha(Theme.primary, 0.3)
|
||||
border.width: 1
|
||||
|
||||
StyledText {
|
||||
|
||||
@@ -28,6 +28,8 @@ StyledRect {
|
||||
property string pluginSettingsPath: pluginData ? (pluginData.settingsPath || "") : ""
|
||||
property var pluginPermissions: pluginData ? (pluginData.permissions || []) : []
|
||||
property bool hasSettings: pluginData && pluginData.settings !== undefined && pluginData.settings !== ""
|
||||
property bool isDesktopPlugin: pluginData ? (pluginData.type === "desktop") : false
|
||||
property bool showSettings: hasSettings && !isDesktopPlugin
|
||||
property bool isSystemPlugin: pluginData ? (pluginData.source === "system") : false
|
||||
property string requiresDms: pluginData ? (pluginData.requires_dms || "") : ""
|
||||
property bool meetsRequirements: requiresDms ? PluginService.checkPluginCompatibility(requiresDms) : true
|
||||
@@ -55,8 +57,8 @@ StyledRect {
|
||||
anchors.fill: parent
|
||||
anchors.bottomMargin: root.isExpanded ? settingsContainer.height : 0
|
||||
hoverEnabled: true
|
||||
cursorShape: root.hasSettings ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: root.hasSettings
|
||||
cursorShape: root.showSettings ? Qt.PointingHandCursor : Qt.ArrowCursor
|
||||
enabled: root.showSettings
|
||||
onClicked: {
|
||||
root.expandedPluginId = root.expandedPluginId === root.pluginId ? "" : root.pluginId;
|
||||
}
|
||||
@@ -103,7 +105,7 @@ StyledRect {
|
||||
width: incompatIcon.width + Theme.spacingXS * 2
|
||||
height: 18
|
||||
radius: 9
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.15)
|
||||
color: Theme.withAlpha(Theme.error, 0.15)
|
||||
visible: !root.meetsRequirements
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
@@ -135,11 +137,28 @@ StyledRect {
|
||||
}
|
||||
|
||||
DankIcon {
|
||||
name: root.hasSettings ? (root.isExpanded ? "expand_less" : "expand_more") : ""
|
||||
name: root.showSettings ? (root.isExpanded ? "expand_less" : "expand_more") : ""
|
||||
size: 16
|
||||
color: root.hasSettings ? Theme.primary : "transparent"
|
||||
color: root.showSettings ? Theme.primary : "transparent"
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
visible: root.hasSettings
|
||||
visible: root.showSettings
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
width: desktopLabel.implicitWidth + Theme.spacingXS * 2
|
||||
height: 18
|
||||
radius: 9
|
||||
color: Theme.withAlpha(Theme.secondary, 0.15)
|
||||
visible: root.isDesktopPlugin
|
||||
anchors.verticalCenter: parent.verticalCenter
|
||||
|
||||
StyledText {
|
||||
id: desktopLabel
|
||||
anchors.centerIn: parent
|
||||
text: I18n.tr("Desktop Widget")
|
||||
font.pixelSize: Theme.fontSizeSmall - 2
|
||||
color: Theme.secondary
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -333,8 +352,8 @@ StyledRect {
|
||||
height: 20
|
||||
width: permissionText.implicitWidth + Theme.spacingXS * 2
|
||||
radius: 10
|
||||
color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.1)
|
||||
border.color: Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.3)
|
||||
color: Theme.withAlpha(Theme.primary, 0.1)
|
||||
border.color: Theme.withAlpha(Theme.primary, 0.3)
|
||||
border.width: 1
|
||||
|
||||
StyledText {
|
||||
@@ -354,9 +373,9 @@ StyledRect {
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
height: root.isExpanded && root.hasSettings ? (settingsLoader.item ? settingsLoader.item.implicitHeight + Theme.spacingL * 2 : 0) : 0
|
||||
height: root.isExpanded && root.showSettings ? (settingsLoader.item ? settingsLoader.item.implicitHeight + Theme.spacingL * 2 : 0) : 0
|
||||
clip: true
|
||||
focus: root.isExpanded && root.hasSettings
|
||||
focus: root.isExpanded && root.showSettings
|
||||
|
||||
Keys.onPressed: event => {
|
||||
event.accepted = true;
|
||||
@@ -374,7 +393,7 @@ StyledRect {
|
||||
id: settingsLoader
|
||||
anchors.fill: parent
|
||||
anchors.margins: Theme.spacingL
|
||||
active: root.isExpanded && root.hasSettings && root.isLoaded
|
||||
active: root.isExpanded && root.showSettings && root.isLoaded
|
||||
asynchronous: false
|
||||
|
||||
source: {
|
||||
|
||||
@@ -80,7 +80,7 @@ FocusScope {
|
||||
width: parent.width
|
||||
height: dmsWarningColumn.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.warning.r, Theme.warning.g, Theme.warning.b, 0.1)
|
||||
color: Theme.withAlpha(Theme.warning, 0.1)
|
||||
border.color: Theme.warning
|
||||
border.width: 1
|
||||
visible: !DMSService.dmsAvailable
|
||||
@@ -126,7 +126,7 @@ FocusScope {
|
||||
width: parent.width
|
||||
height: incompatWarningColumn.implicitHeight + Theme.spacingM * 2
|
||||
radius: Theme.cornerRadius
|
||||
color: Qt.rgba(Theme.error.r, Theme.error.g, Theme.error.b, 0.1)
|
||||
color: Theme.withAlpha(Theme.error, 0.1)
|
||||
border.color: Theme.error
|
||||
border.width: 1
|
||||
visible: incompatPlugins.length > 0
|
||||
|
||||
@@ -12,10 +12,14 @@ Item {
|
||||
|
||||
signal preferencesChanged(var preferences)
|
||||
|
||||
readonly property bool allDisplaysEnabled: {
|
||||
if (!Array.isArray(displayPreferences) || displayPreferences.length === 0)
|
||||
return true;
|
||||
return displayPreferences.includes("all");
|
||||
property bool localAllDisplays: true
|
||||
|
||||
onDisplayPreferencesChanged: {
|
||||
if (!Array.isArray(displayPreferences) || displayPreferences.length === 0) {
|
||||
localAllDisplays = true;
|
||||
return;
|
||||
}
|
||||
localAllDisplays = displayPreferences.includes("all");
|
||||
}
|
||||
|
||||
width: parent?.width ?? 0
|
||||
@@ -37,8 +41,9 @@ Item {
|
||||
DankToggle {
|
||||
width: parent.width
|
||||
text: I18n.tr("All displays")
|
||||
checked: root.allDisplaysEnabled
|
||||
checked: root.localAllDisplays
|
||||
onToggled: isChecked => {
|
||||
root.localAllDisplays = isChecked;
|
||||
if (isChecked) {
|
||||
root.preferencesChanged(["all"]);
|
||||
return;
|
||||
@@ -58,7 +63,7 @@ Item {
|
||||
Column {
|
||||
width: parent.width
|
||||
spacing: Theme.spacingXS
|
||||
visible: !root.allDisplaysEnabled
|
||||
visible: !root.localAllDisplays
|
||||
|
||||
Repeater {
|
||||
model: Quickshell.screens
|
||||
@@ -66,17 +71,20 @@ Item {
|
||||
DankToggle {
|
||||
required property var modelData
|
||||
|
||||
property bool localChecked: {
|
||||
const prefs = root.displayPreferences;
|
||||
if (!Array.isArray(prefs) || prefs.includes("all"))
|
||||
return true;
|
||||
return prefs.some(p => p.name === modelData.name);
|
||||
}
|
||||
|
||||
width: parent.width
|
||||
text: SettingsData.getScreenDisplayName(modelData)
|
||||
description: modelData.width + "×" + modelData.height
|
||||
checked: {
|
||||
const prefs = root.displayPreferences;
|
||||
if (!Array.isArray(prefs) || prefs.includes("all"))
|
||||
return false;
|
||||
return prefs.some(p => p.name === modelData.name);
|
||||
}
|
||||
checked: localChecked
|
||||
onToggled: isChecked => {
|
||||
var prefs = root.displayPreferences;
|
||||
localChecked = isChecked;
|
||||
var prefs = JSON.parse(JSON.stringify(root.displayPreferences));
|
||||
if (!Array.isArray(prefs) || prefs.includes("all"))
|
||||
prefs = [];
|
||||
prefs = prefs.filter(p => p.name !== modelData.name);
|
||||
|
||||
Reference in New Issue
Block a user