import QtQuick import Quickshell import qs.Common import qs.Widgets Item { id: root required property string pluginId property var pluginService: null default property list content signal settingChanged property var variants: [] property alias variantsModel: variantsListModel implicitHeight: hasPermission ? settingsColumn.implicitHeight : errorText.implicitHeight height: implicitHeight readonly property bool isDesktopPlugin: { if (!pluginService || !pluginId) return false; const plugin = pluginService.availablePlugins[pluginId]; return plugin?.type === "desktop"; } readonly property bool hasPermission: { if (!pluginService || !pluginId) return true; const allPlugins = pluginService.availablePlugins; const plugin = allPlugins[pluginId]; if (!plugin) return true; const permissions = Array.isArray(plugin.permissions) ? plugin.permissions : []; return permissions.indexOf("settings_write") !== -1; } Component.onCompleted: { loadVariants(); } onPluginServiceChanged: { if (pluginService) { loadVariants(); for (let i = 0; i < content.length; i++) { const child = content[i]; if (child.loadValue) { child.loadValue(); } } } } onContentChanged: { for (let i = 0; i < content.length; i++) { const item = content[i]; if (item instanceof Item) { item.parent = settingsColumn; } } } Connections { target: pluginService function onPluginDataChanged(changedPluginId) { if (changedPluginId === pluginId) { loadVariants(); } } } function loadVariants() { if (!pluginService || !pluginId) { variants = []; return; } variants = pluginService.getPluginVariants(pluginId); syncVariantsToModel(); } function syncVariantsToModel() { variantsListModel.clear(); for (let i = 0; i < variants.length; i++) { variantsListModel.append(variants[i]); } } onVariantsChanged: { syncVariantsToModel(); } ListModel { id: variantsListModel } function createVariant(variantName, variantConfig) { if (!pluginService || !pluginId) { return null; } return pluginService.createPluginVariant(pluginId, variantName, variantConfig); } function removeVariant(variantId) { if (!pluginService || !pluginId) { return; } pluginService.removePluginVariant(pluginId, variantId); } function updateVariant(variantId, variantConfig) { if (!pluginService || !pluginId) { return; } pluginService.updatePluginVariant(pluginId, variantId, variantConfig); } function saveValue(key, value) { if (!pluginService) { return; } if (!hasPermission) { console.warn("PluginSettings: Plugin", pluginId, "does not have settings_write permission"); return; } if (pluginService.savePluginData) { pluginService.savePluginData(pluginId, key, value); settingChanged(); } } function loadValue(key, defaultValue) { if (pluginService && pluginService.loadPluginData) { return pluginService.loadPluginData(pluginId, key, defaultValue); } return defaultValue; } function findFlickable(item) { var current = item?.parent; while (current) { if (current.contentY !== undefined && current.contentHeight !== undefined) { return current; } current = current.parent; } return null; } function ensureItemVisible(item) { if (!item) return; var flickable = findFlickable(root); if (!flickable) return; var itemGlobalY = item.mapToItem(null, 0, 0).y; var itemHeight = item.height; var flickableGlobalY = flickable.mapToItem(null, 0, 0).y; var viewportHeight = flickable.height; var itemRelativeY = itemGlobalY - flickableGlobalY; var viewportTop = 0; var viewportBottom = viewportHeight; if (itemRelativeY < viewportTop) { flickable.contentY = Math.max(0, flickable.contentY - (viewportTop - itemRelativeY) - Theme.spacingL); } else if (itemRelativeY + itemHeight > viewportBottom) { flickable.contentY = Math.min(flickable.contentHeight - viewportHeight, flickable.contentY + (itemRelativeY + itemHeight - viewportBottom) + Theme.spacingL); } } StyledText { id: errorText visible: pluginService && !root.hasPermission anchors.fill: parent text: I18n.tr("This plugin does not have 'settings_write' permission.\n\nAdd \"permissions\": [\"settings_read\", \"settings_write\"] to plugin.json") color: Theme.error font.pixelSize: Theme.fontSizeMedium wrapMode: Text.WordWrap horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter } Column { id: settingsColumn visible: root.hasPermission width: parent.width spacing: Theme.spacingM Item { id: desktopDisplaySettings visible: root.isDesktopPlugin width: parent.width height: visible ? displaySettingsColumn.implicitHeight : 0 Column { id: displaySettingsColumn width: parent.width spacing: Theme.spacingS Rectangle { width: parent.width height: 1 color: Theme.outline opacity: 0.3 visible: root.content.length > 0 } StyledText { text: I18n.tr("Display Settings") font.pixelSize: Theme.fontSizeMedium font.weight: Font.Medium color: Theme.surfaceText } StyledText { text: I18n.tr("Choose which displays show this widget") font.pixelSize: Theme.fontSizeSmall color: Theme.surfaceVariantText width: parent.width wrapMode: Text.WordWrap } DankToggle { width: parent.width text: I18n.tr("All displays") checked: { const prefs = root.loadValue("displayPreferences", ["all"]); return Array.isArray(prefs) && (prefs.includes("all") || prefs.length === 0); } onToggled: isChecked => { if (isChecked) { root.saveValue("displayPreferences", ["all"]); } else { root.saveValue("displayPreferences", []); } } } Column { width: parent.width spacing: Theme.spacingXS visible: { const prefs = root.loadValue("displayPreferences", ["all"]); return !Array.isArray(prefs) || (!prefs.includes("all") && prefs.length >= 0); } Repeater { model: Quickshell.screens DankToggle { required property var modelData width: parent.width text: SettingsData.getScreenDisplayName(modelData) description: modelData.width + "×" + modelData.height checked: { const prefs = root.loadValue("displayPreferences", ["all"]); if (!Array.isArray(prefs) || prefs.includes("all")) return false; return prefs.some(p => p.name === modelData.name); } onToggled: isChecked => { var prefs = root.loadValue("displayPreferences", ["all"]); if (!Array.isArray(prefs) || prefs.includes("all")) { prefs = []; } prefs = prefs.filter(p => p.name !== modelData.name); if (isChecked) { prefs.push({ name: modelData.name, model: modelData.model || "" }); } root.saveValue("displayPreferences", prefs); } } } } } } } }