From 61630e447bec3ff6ae02f6ea27b12699d0e55ed7 Mon Sep 17 00:00:00 2001 From: bbedward Date: Mon, 22 Dec 2025 20:29:01 -0500 Subject: [PATCH] desktop-widgets: add overlay IPC and overview option --- CHANGELOG.MD | 2 + quickshell/DMSShellIPC.qml | 54 +++++++++ .../Modules/Plugins/DesktopPluginWrapper.qml | 14 ++- .../Settings/DesktopWidgetInstanceCard.qml | 109 +++++++++++++++++- .../WorkspaceOverlays/NiriOverviewOverlay.qml | 6 - 5 files changed, 173 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.MD b/CHANGELOG.MD index 5b64d90c..60b06106 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -12,4 +12,6 @@ This file is more of a quick reference so I know what to account for before next - Added desktop widget plugins - dev guidance available - builtin clock & dgop widgets + - new IPC targets - Initial RTL support/i18n +- Theme registry diff --git a/quickshell/DMSShellIPC.qml b/quickshell/DMSShellIPC.qml index f91b5e4c..c8297b36 100644 --- a/quickshell/DMSShellIPC.qml +++ b/quickshell/DMSShellIPC.qml @@ -893,4 +893,58 @@ Item { target: "clipboard" } + + IpcHandler { + function toggleOverlay(instanceId: string): string { + if (!instanceId) + return "ERROR: No instance ID specified"; + + const instance = SettingsData.getDesktopWidgetInstance(instanceId); + if (!instance) + return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`; + + const currentValue = instance.config?.showOnOverlay ?? false; + SettingsData.updateDesktopWidgetInstanceConfig(instanceId, { + showOnOverlay: !currentValue + }); + return !currentValue ? `DESKTOP_WIDGET_OVERLAY_ENABLED: ${instanceId}` : `DESKTOP_WIDGET_OVERLAY_DISABLED: ${instanceId}`; + } + + function setOverlay(instanceId: string, enabled: string): string { + if (!instanceId) + return "ERROR: No instance ID specified"; + + const instance = SettingsData.getDesktopWidgetInstance(instanceId); + if (!instance) + return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`; + + const enabledBool = enabled === "true" || enabled === "1"; + SettingsData.updateDesktopWidgetInstanceConfig(instanceId, { + showOnOverlay: enabledBool + }); + return enabledBool ? `DESKTOP_WIDGET_OVERLAY_ENABLED: ${instanceId}` : `DESKTOP_WIDGET_OVERLAY_DISABLED: ${instanceId}`; + } + + function list(): string { + const instances = SettingsData.desktopWidgetInstances || []; + if (instances.length === 0) + return "No desktop widgets configured"; + return instances.map(i => `${i.id} [${i.widgetType}] ${i.name || i.widgetType}`).join("\n"); + } + + function status(instanceId: string): string { + if (!instanceId) + return "ERROR: No instance ID specified"; + + const instance = SettingsData.getDesktopWidgetInstance(instanceId); + if (!instance) + return `DESKTOP_WIDGET_NOT_FOUND: ${instanceId}`; + + const overlay = instance.config?.showOnOverlay ?? false; + const overview = instance.config?.showOnOverview ?? false; + return `overlay: ${overlay}, overview: ${overview}`; + } + + target: "desktopWidget" + } } diff --git a/quickshell/Modules/Plugins/DesktopPluginWrapper.qml b/quickshell/Modules/Plugins/DesktopPluginWrapper.qml index fa9b2373..4854530d 100644 --- a/quickshell/Modules/Plugins/DesktopPluginWrapper.qml +++ b/quickshell/Modules/Plugins/DesktopPluginWrapper.qml @@ -21,6 +21,10 @@ Item { readonly property bool isBuiltin: pluginId === "desktopClock" || pluginId === "systemMonitor" readonly property var activeComponent: isBuiltin ? builtinComponent : PluginService.pluginDesktopComponents[pluginId] ?? null + readonly property bool showOnOverlay: instanceData?.config?.showOnOverlay ?? false + readonly property bool showOnOverview: instanceData?.config?.showOnOverview ?? false + readonly property bool overviewActive: CompositorService.isNiri && NiriService.inOverview + Connections { target: PluginService enabled: !root.isBuiltin @@ -202,7 +206,15 @@ Item { color: "transparent" WlrLayershell.namespace: "quickshell:desktop-widget:" + root.pluginId + (root.instanceId ? ":" + root.instanceId : "") - WlrLayershell.layer: root.isInteracting && !CompositorService.useHyprlandFocusGrab ? WlrLayer.Overlay : WlrLayer.Bottom + WlrLayershell.layer: { + if (root.isInteracting && !CompositorService.useHyprlandFocusGrab) + return WlrLayer.Overlay; + if (root.showOnOverlay) + return WlrLayer.Overlay; + if (root.showOnOverview && root.overviewActive) + return WlrLayer.Overlay; + return WlrLayer.Bottom; + } WlrLayershell.exclusionMode: ExclusionMode.Ignore WlrLayershell.keyboardFocus: { if (!root.isInteracting) diff --git a/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml b/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml index a8ef16e1..7e7e950b 100644 --- a/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml +++ b/quickshell/Modules/Settings/DesktopWidgetInstanceCard.qml @@ -1,6 +1,7 @@ pragma ComponentBehavior: Bound import QtQuick +import Quickshell import qs.Common import qs.Services import qs.Widgets @@ -68,8 +69,11 @@ SettingsCard { DankToggle { checked: instanceData?.enabled ?? true onToggled: isChecked => { - if (!root.instanceId) return; - SettingsData.updateDesktopWidgetInstance(root.instanceId, { enabled: isChecked }); + if (!root.instanceId) + return; + SettingsData.updateDesktopWidgetInstance(root.instanceId, { + enabled: isChecked + }); } } } @@ -123,8 +127,102 @@ SettingsCard { width: parent.width - 80 - Theme.spacingM text: root.widgetName onEditingFinished: { - if (!root.instanceId) return; - SettingsData.updateDesktopWidgetInstance(root.instanceId, { name: text }); + if (!root.instanceId) + return; + SettingsData.updateDesktopWidgetInstance(root.instanceId, { + name: text + }); + } + } + } + } + + SettingsDivider {} + + SettingsToggleRow { + text: I18n.tr("Show on Overlay") + checked: instanceData?.config?.showOnOverlay ?? false + onToggled: isChecked => { + if (!root.instanceId) + return; + 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 => { + if (!root.instanceId) + return; + SettingsData.updateDesktopWidgetInstanceConfig(root.instanceId, { + showOnOverview: 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 + } + + Rectangle { + width: parent.width + height: ipcText.height + Theme.spacingS * 2 + radius: Theme.cornerRadius / 2 + color: Theme.surfaceHover + + 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" + 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")); + } + } } } } @@ -149,7 +247,8 @@ SettingsCard { } onLoaded: { - if (!item) return; + if (!item) + return; item.instanceId = root.instanceId; item.instanceData = Qt.binding(() => root.instanceData); } diff --git a/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml b/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml index 155f915a..0d0b558e 100644 --- a/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml +++ b/quickshell/Modules/WorkspaceOverlays/NiriOverviewOverlay.qml @@ -227,12 +227,6 @@ Scope { } } - onAnimatingOutChanged: { - if (!animatingOut || scaleAnimation.animation.running) - return; - Qt.callLater(niriOverviewScope.resetState); - } - Behavior on opacity { NumberAnimation { duration: Theme.expressiveDurations.expressiveDefaultSpatial