From ad0f3fa33b09bb773dc01a9001871376ae3f6735 Mon Sep 17 00:00:00 2001 From: bbedward Date: Thu, 4 Dec 2025 19:37:21 -0500 Subject: [PATCH] dankbar: convert center section to use WidgetHost --- quickshell/Modules/DankBar/CenterSection.qml | 587 ++++++------------ quickshell/Modules/Settings/WidgetsTab.qml | 2 +- .../Modules/Settings/WidgetsTabSection.qml | 176 +++++- 3 files changed, 343 insertions(+), 422 deletions(-) diff --git a/quickshell/Modules/DankBar/CenterSection.qml b/quickshell/Modules/DankBar/CenterSection.qml index c3882535..281d594f 100644 --- a/quickshell/Modules/DankBar/CenterSection.qml +++ b/quickshell/Modules/DankBar/CenterSection.qml @@ -1,6 +1,5 @@ import QtQuick import qs.Common -import qs.Services Item { id: root @@ -19,7 +18,7 @@ Item { property bool forceVerticalLayout: false readonly property bool isVertical: overrideAxisLayout ? forceVerticalLayout : (axis?.isVertical ?? false) - readonly property real spacing: { + readonly property real widgetSpacing: { const baseSpacing = noBackground ? 2 : Theme.spacingXS; const outlineThickness = (barConfig?.widgetOutlineEnabled ?? false) ? (barConfig?.widgetOutlineThickness ?? 1) : 0; return baseSpacing + (outlineThickness * 2); @@ -33,36 +32,32 @@ Item { if (SettingsData.centeringMode === "geometric") { applyGeometricLayout(); } else { - // Default to index layout or if value is not 'geometric' applyIndexLayout(); } } function applyGeometricLayout() { - if ((isVertical ? height : width) <= 0 || !visible) { + if ((isVertical ? height : width) <= 0 || !visible) return; - } centerWidgets = []; totalWidgets = 0; totalSize = 0; for (var i = 0; i < centerRepeater.count; i++) { - const item = centerRepeater.itemAt(i); - if (item && item.active && item.item && getWidgetVisible(item.widgetId)) { - centerWidgets.push(item.item); + const loader = centerRepeater.itemAt(i); + if (loader && loader.active && loader.item) { + centerWidgets.push(loader.item); totalWidgets++; - totalSize += isVertical ? item.item.height : item.item.width; + totalSize += isVertical ? loader.item.height : loader.item.width; } } - if (totalWidgets === 0) { + if (totalWidgets === 0) return; - } - if (totalWidgets > 1) { - totalSize += spacing * (totalWidgets - 1); - } + if (totalWidgets > 1) + totalSize += widgetSpacing * (totalWidgets - 1); positionWidgetsGeometric(); } @@ -70,7 +65,6 @@ Item { function positionWidgetsGeometric() { const parentLength = isVertical ? height : width; const parentCenter = parentLength / 2; - let currentPos = parentCenter - (totalSize / 2); centerWidgets.forEach(widget => { @@ -81,67 +75,53 @@ Item { widget.anchors.horizontalCenter = undefined; widget.x = currentPos; } - const widgetSize = isVertical ? widget.height : widget.width; - currentPos += widgetSize + spacing; + currentPos += widgetSize + widgetSpacing; }); } function applyIndexLayout() { - if ((isVertical ? height : width) <= 0 || !visible) { + if ((isVertical ? height : width) <= 0 || !visible) return; - } centerWidgets = []; totalWidgets = 0; totalSize = 0; - let configuredWidgets = 0; let configuredMiddleWidget = null; let configuredLeftWidget = null; let configuredRightWidget = null; - for (var i = 0; i < centerRepeater.count; i++) { - const item = centerRepeater.itemAt(i); - if (item && getWidgetVisible(item.widgetId)) { - configuredWidgets++; - } - } - + const configuredWidgets = centerRepeater.count; const isOddConfigured = configuredWidgets % 2 === 1; const configuredMiddlePos = Math.floor(configuredWidgets / 2); const configuredLeftPos = isOddConfigured ? -1 : ((configuredWidgets / 2) - 1); const configuredRightPos = isOddConfigured ? -1 : (configuredWidgets / 2); - let currentConfigIndex = 0; for (var i = 0; i < centerRepeater.count; i++) { - const item = centerRepeater.itemAt(i); - if (item && getWidgetVisible(item.widgetId)) { - if (isOddConfigured && currentConfigIndex === configuredMiddlePos && item.active && item.item) { - configuredMiddleWidget = item.item; - } - if (!isOddConfigured && currentConfigIndex === configuredLeftPos && item.active && item.item) { - configuredLeftWidget = item.item; - } - if (!isOddConfigured && currentConfigIndex === configuredRightPos && item.active && item.item) { - configuredRightWidget = item.item; - } - if (item.active && item.item) { - centerWidgets.push(item.item); - totalWidgets++; - totalSize += isVertical ? item.item.height : item.item.width; - } - currentConfigIndex++; + const wrapper = centerRepeater.itemAt(i); + if (!wrapper) + continue; + + if (isOddConfigured && i === configuredMiddlePos && wrapper.active && wrapper.item) + configuredMiddleWidget = wrapper.item; + if (!isOddConfigured && i === configuredLeftPos && wrapper.active && wrapper.item) + configuredLeftWidget = wrapper.item; + if (!isOddConfigured && i === configuredRightPos && wrapper.active && wrapper.item) + configuredRightWidget = wrapper.item; + + if (wrapper.active && wrapper.item) { + centerWidgets.push(wrapper.item); + totalWidgets++; + totalSize += isVertical ? wrapper.item.height : wrapper.item.width; } } - if (totalWidgets === 0) { + if (totalWidgets === 0) return; - } - if (totalWidgets > 1) { - totalSize += spacing * (totalWidgets - 1); - } + if (totalWidgets > 1) + totalSize += widgetSpacing * (totalWidgets - 1); positionWidgetsByIndex(configuredWidgets, configuredMiddleWidget, configuredLeftWidget, configuredRightWidget); } @@ -151,11 +131,10 @@ Item { const isOddConfigured = configuredWidgets % 2 === 1; centerWidgets.forEach(widget => { - if (isVertical) { + if (isVertical) widget.anchors.verticalCenter = undefined; - } else { + else widget.anchors.horizontalCenter = undefined; - } }); if (isOddConfigured && configuredMiddleWidget) { @@ -163,222 +142,154 @@ Item { const middleIndex = centerWidgets.indexOf(middleWidget); const middleSize = isVertical ? middleWidget.height : middleWidget.width; - if (isVertical) { + if (isVertical) middleWidget.y = parentCenter - (middleSize / 2); - } else { + else middleWidget.x = parentCenter - (middleSize / 2); - } let currentPos = isVertical ? middleWidget.y : middleWidget.x; for (var i = middleIndex - 1; i >= 0; i--) { const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; - currentPos -= (spacing + size); - if (isVertical) { + currentPos -= (widgetSpacing + size); + if (isVertical) centerWidgets[i].y = currentPos; - } else { + else centerWidgets[i].x = currentPos; - } } currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize; for (var i = middleIndex + 1; i < totalWidgets; i++) { - currentPos += spacing; - if (isVertical) { + currentPos += widgetSpacing; + if (isVertical) centerWidgets[i].y = currentPos; - } else { + else centerWidgets[i].x = currentPos; - } currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; } - } else { - if (totalWidgets === 1) { - const widget = centerWidgets[0]; - const size = isVertical ? widget.height : widget.width; - if (isVertical) { - widget.y = parentCenter - (size / 2); - } else { - widget.x = parentCenter - (size / 2); + return; + } + + if (totalWidgets === 1) { + const widget = centerWidgets[0]; + const size = isVertical ? widget.height : widget.width; + if (isVertical) + widget.y = parentCenter - (size / 2); + else + widget.x = parentCenter - (size / 2); + return; + } + + if (!configuredLeftWidget || !configuredRightWidget) { + if (totalWidgets % 2 === 1) { + const middleIndex = Math.floor(totalWidgets / 2); + const middleWidget = centerWidgets[middleIndex]; + + if (!middleWidget) + return; + + const middleSize = isVertical ? middleWidget.height : middleWidget.width; + + if (isVertical) + middleWidget.y = parentCenter - (middleSize / 2); + else + middleWidget.x = parentCenter - (middleSize / 2); + + let currentPos = isVertical ? middleWidget.y : middleWidget.x; + for (var i = middleIndex - 1; i >= 0; i--) { + const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; + currentPos -= (widgetSpacing + size); + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; } - return; - } - if (!configuredLeftWidget || !configuredRightWidget) { - if (totalWidgets % 2 === 1) { - const middleIndex = Math.floor(totalWidgets / 2); - const middleWidget = centerWidgets[middleIndex]; - - if (!middleWidget) { - return; - } - - const middleSize = isVertical ? middleWidget.height : middleWidget.width; - - if (isVertical) { - middleWidget.y = parentCenter - (middleSize / 2); - } else { - middleWidget.x = parentCenter - (middleSize / 2); - } - - let currentPos = isVertical ? middleWidget.y : middleWidget.x; - for (var i = middleIndex - 1; i >= 0; i--) { - const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; - currentPos -= (spacing + size); - if (isVertical) { - centerWidgets[i].y = currentPos; - } else { - centerWidgets[i].x = currentPos; - } - } - - currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize; - for (var i = middleIndex + 1; i < totalWidgets; i++) { - currentPos += spacing; - if (isVertical) { - centerWidgets[i].y = currentPos; - } else { - centerWidgets[i].x = currentPos; - } - currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; - } - } else { - const leftIndex = (totalWidgets / 2) - 1; - const rightIndex = totalWidgets / 2; - const fallbackLeft = centerWidgets[leftIndex]; - const fallbackRight = centerWidgets[rightIndex]; - - if (!fallbackLeft || !fallbackRight) { - return; - } - - const halfSpacing = spacing / 2; - const leftSize = isVertical ? fallbackLeft.height : fallbackLeft.width; - - if (isVertical) { - fallbackLeft.y = parentCenter - halfSpacing - leftSize; - fallbackRight.y = parentCenter + halfSpacing; - } else { - fallbackLeft.x = parentCenter - halfSpacing - leftSize; - fallbackRight.x = parentCenter + halfSpacing; - } - - let currentPos = isVertical ? fallbackLeft.y : fallbackLeft.x; - for (var i = leftIndex - 1; i >= 0; i--) { - const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; - currentPos -= (spacing + size); - if (isVertical) { - centerWidgets[i].y = currentPos; - } else { - centerWidgets[i].x = currentPos; - } - } - - currentPos = (isVertical ? fallbackRight.y + fallbackRight.height : fallbackRight.x + fallbackRight.width); - for (var i = rightIndex + 1; i < totalWidgets; i++) { - currentPos += spacing; - if (isVertical) { - centerWidgets[i].y = currentPos; - } else { - centerWidgets[i].x = currentPos; - } - currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; - } + currentPos = (isVertical ? middleWidget.y : middleWidget.x) + middleSize; + for (var i = middleIndex + 1; i < totalWidgets; i++) { + currentPos += widgetSpacing; + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; + currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; } - return; - } - - const leftWidget = configuredLeftWidget; - const rightWidget = configuredRightWidget; - const leftIndex = centerWidgets.indexOf(leftWidget); - const rightIndex = centerWidgets.indexOf(rightWidget); - const halfSpacing = spacing / 2; - const leftSize = isVertical ? leftWidget.height : leftWidget.width; - - if (isVertical) { - leftWidget.y = parentCenter - halfSpacing - leftSize; - rightWidget.y = parentCenter + halfSpacing; } else { - leftWidget.x = parentCenter - halfSpacing - leftSize; - rightWidget.x = parentCenter + halfSpacing; - } + const leftIndex = (totalWidgets / 2) - 1; + const rightIndex = totalWidgets / 2; + const fallbackLeft = centerWidgets[leftIndex]; + const fallbackRight = centerWidgets[rightIndex]; + + if (!fallbackLeft || !fallbackRight) + return; + + const halfSpacing = widgetSpacing / 2; + const leftSize = isVertical ? fallbackLeft.height : fallbackLeft.width; - let currentPos = isVertical ? leftWidget.y : leftWidget.x; - for (var i = leftIndex - 1; i >= 0; i--) { - const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; - currentPos -= (spacing + size); if (isVertical) { - centerWidgets[i].y = currentPos; + fallbackLeft.y = parentCenter - halfSpacing - leftSize; + fallbackRight.y = parentCenter + halfSpacing; } else { - centerWidgets[i].x = currentPos; + fallbackLeft.x = parentCenter - halfSpacing - leftSize; + fallbackRight.x = parentCenter + halfSpacing; + } + + let currentPos = isVertical ? fallbackLeft.y : fallbackLeft.x; + for (var i = leftIndex - 1; i >= 0; i--) { + const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; + currentPos -= (widgetSpacing + size); + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; + } + + currentPos = (isVertical ? fallbackRight.y + fallbackRight.height : fallbackRight.x + fallbackRight.width); + for (var i = rightIndex + 1; i < totalWidgets; i++) { + currentPos += widgetSpacing; + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; + currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; } } - - currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width); - for (var i = rightIndex + 1; i < totalWidgets; i++) { - currentPos += spacing; - if (isVertical) { - centerWidgets[i].y = currentPos; - } else { - centerWidgets[i].x = currentPos; - } - currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; - } - } - } - - function getWidgetVisible(widgetId) { - const widgetVisibility = { - "cpuUsage": DgopService.dgopAvailable, - "memUsage": DgopService.dgopAvailable, - "cpuTemp": DgopService.dgopAvailable, - "gpuTemp": DgopService.dgopAvailable, - "network_speed_monitor": DgopService.dgopAvailable - }; - return widgetVisibility[widgetId] ?? true; - } - - function getWidgetComponent(widgetId) { - // Build dynamic component map including plugins - let baseMap = { - "launcherButton": "launcherButtonComponent", - "workspaceSwitcher": "workspaceSwitcherComponent", - "focusedWindow": "focusedWindowComponent", - "runningApps": "runningAppsComponent", - "clock": "clockComponent", - "music": "mediaComponent", - "weather": "weatherComponent", - "systemTray": "systemTrayComponent", - "privacyIndicator": "privacyIndicatorComponent", - "clipboard": "clipboardComponent", - "cpuUsage": "cpuUsageComponent", - "memUsage": "memUsageComponent", - "diskUsage": "diskUsageComponent", - "cpuTemp": "cpuTempComponent", - "gpuTemp": "gpuTempComponent", - "notificationButton": "notificationButtonComponent", - "battery": "batteryComponent", - "controlCenterButton": "controlCenterButtonComponent", - "idleInhibitor": "idleInhibitorComponent", - "spacer": "spacerComponent", - "separator": "separatorComponent", - "network_speed_monitor": "networkComponent", - "keyboard_layout_name": "keyboardLayoutNameComponent", - "vpn": "vpnComponent", - "notepadButton": "notepadButtonComponent", - "colorPicker": "colorPickerComponent", - "systemUpdate": "systemUpdateComponent" - }; - - // For built-in components, get from components property - const componentKey = baseMap[widgetId]; - if (componentKey && root.components[componentKey]) { - return root.components[componentKey]; + return; } - // For plugin components, get from PluginService - var parts = widgetId.split(":"); - var pluginId = parts[0]; - let pluginComponents = PluginService.getWidgetComponents(); - return pluginComponents[pluginId] || null; + const leftWidget = configuredLeftWidget; + const rightWidget = configuredRightWidget; + const leftIndex = centerWidgets.indexOf(leftWidget); + const rightIndex = centerWidgets.indexOf(rightWidget); + const halfSpacing = widgetSpacing / 2; + const leftSize = isVertical ? leftWidget.height : leftWidget.width; + + if (isVertical) { + leftWidget.y = parentCenter - halfSpacing - leftSize; + rightWidget.y = parentCenter + halfSpacing; + } else { + leftWidget.x = parentCenter - halfSpacing - leftSize; + rightWidget.x = parentCenter + halfSpacing; + } + + let currentPos = isVertical ? leftWidget.y : leftWidget.x; + for (var i = leftIndex - 1; i >= 0; i--) { + const size = isVertical ? centerWidgets[i].height : centerWidgets[i].width; + currentPos -= (widgetSpacing + size); + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; + } + + currentPos = (isVertical ? rightWidget.y + rightWidget.height : rightWidget.x + rightWidget.width); + for (var i = rightIndex + 1; i < totalWidgets; i++) { + currentPos += widgetSpacing; + if (isVertical) + centerWidgets[i].y = currentPos; + else + centerWidgets[i].x = currentPos; + currentPos += isVertical ? centerWidgets[i].height : centerWidgets[i].width; + } } height: parent.height @@ -392,177 +303,71 @@ Item { onTriggered: root.updateLayout() } - Component.onCompleted: { - layoutTimer.restart(); - } + Component.onCompleted: layoutTimer.restart() onWidthChanged: { - if (width > 0) { + if (width > 0) layoutTimer.restart(); - } } onHeightChanged: { - if (height > 0) { + if (height > 0) layoutTimer.restart(); - } } onVisibleChanged: { - if (visible && (isVertical ? height : width) > 0) { + if (visible && (isVertical ? height : width) > 0) layoutTimer.restart(); - } } Repeater { id: centerRepeater model: root.widgetsModel - Loader { + onCountChanged: layoutTimer.restart() + + Item { property var itemData: modelData - property string widgetId: itemData.widgetId - property var widgetData: itemData - property int spacerSize: itemData.size || 20 + readonly property real itemSpacing: root.widgetSpacing - anchors.verticalCenter: !root.isVertical ? parent.verticalCenter : undefined - anchors.horizontalCenter: root.isVertical ? parent.horizontalCenter : undefined - active: root.getWidgetVisible(itemData.widgetId) && (itemData.widgetId !== "music" || MprisController.activePlayer !== null) - sourceComponent: root.getWidgetComponent(itemData.widgetId) - opacity: (itemData.enabled !== false) ? 1 : 0 - asynchronous: false + width: widgetLoader.item ? widgetLoader.item.width : 0 + height: widgetLoader.item ? widgetLoader.item.height : 0 - onLoaded: { - if (!item) { - return; - } - item.widthChanged.connect(() => { - if (layoutTimer) - layoutTimer.restart(); - }); - item.heightChanged.connect(() => { - if (layoutTimer) - layoutTimer.restart(); - }); - if (root.axis && "axis" in item) { - item.axis = Qt.binding(() => root.axis); - } - if (root.axis && "isVertical" in item) { - try { - item.isVertical = Qt.binding(() => root.axis.isVertical); - } catch (e) {} + readonly property bool active: widgetLoader.active + readonly property var item: widgetLoader.item + + WidgetHost { + id: widgetLoader + + anchors.verticalCenter: !root.isVertical ? parent.verticalCenter : undefined + anchors.horizontalCenter: root.isVertical ? parent.horizontalCenter : undefined + + widgetId: itemData.widgetId + widgetData: itemData + spacerSize: itemData.size || 20 + components: root.components + isInColumn: root.isVertical + axis: root.axis + section: "center" + parentScreen: root.parentScreen + widgetThickness: root.widgetThickness + barThickness: root.barThickness + barSpacing: root.barSpacing + barConfig: root.barConfig + isFirst: index === 0 + isLast: index === centerRepeater.count - 1 + sectionSpacing: parent.itemSpacing + isLeftBarEdge: false + isRightBarEdge: false + isTopBarEdge: false + isBottomBarEdge: false + + onContentItemReady: contentItem => { + contentItem.widthChanged.connect(() => layoutTimer.restart()); + contentItem.heightChanged.connect(() => layoutTimer.restart()); } - // Inject properties for plugin widgets - if ("section" in item) { - item.section = root.section; - } - if ("parentScreen" in item) { - item.parentScreen = Qt.binding(() => root.parentScreen); - } - if ("widgetThickness" in item) { - item.widgetThickness = Qt.binding(() => root.widgetThickness); - } - if ("barThickness" in item) { - item.barThickness = Qt.binding(() => root.barThickness); - } - if ("barSpacing" in item) { - item.barSpacing = Qt.binding(() => root.barSpacing); - } - if ("barConfig" in item) { - item.barConfig = Qt.binding(() => root.barConfig); - } - if ("sectionSpacing" in item) { - item.sectionSpacing = Qt.binding(() => root.spacing); - } - if ("widgetData" in item) { - item.widgetData = Qt.binding(() => widgetData); - } - - if ("isFirst" in item) { - item.isFirst = Qt.binding(() => { - for (var i = 0; i < centerRepeater.count; i++) { - const checkItem = centerRepeater.itemAt(i); - if (checkItem && checkItem.active && checkItem.item) { - return checkItem.item === item; - } - } - return false; - }); - } - - if ("isLast" in item) { - item.isLast = Qt.binding(() => { - for (var i = centerRepeater.count - 1; i >= 0; i--) { - const checkItem = centerRepeater.itemAt(i); - if (checkItem && checkItem.active && checkItem.item) { - return checkItem.item === item; - } - } - return false; - }); - } - - if ("isLeftBarEdge" in item) { - item.isLeftBarEdge = false; - } - if ("isRightBarEdge" in item) { - item.isRightBarEdge = false; - } - if ("isTopBarEdge" in item) { - item.isTopBarEdge = false; - } - if ("isBottomBarEdge" in item) { - item.isBottomBarEdge = false; - } - - if (item.pluginService !== undefined) { - var parts = model.widgetId.split(":"); - var pluginId = parts[0]; - var variantId = parts.length > 1 ? parts[1] : null; - - if (item.pluginId !== undefined) { - item.pluginId = pluginId; - } - if (item.variantId !== undefined) { - item.variantId = variantId; - } - if (item.variantData !== undefined && variantId) { - item.variantData = PluginService.getPluginVariantData(pluginId, variantId); - } - item.pluginService = PluginService; - } - - if (item.popoutService !== undefined) { - item.popoutService = PopoutService; - } - - layoutTimer.restart(); - } - - onActiveChanged: { - layoutTimer.restart(); - } - } - } - - Connections { - target: PluginService - function onPluginLoaded(pluginId) { - // Force refresh of component lookups - for (var i = 0; i < centerRepeater.count; i++) { - var item = centerRepeater.itemAt(i); - if (item && item.widgetId.startsWith(pluginId)) { - item.sourceComponent = root.getWidgetComponent(item.widgetId); - } - } - } - function onPluginUnloaded(pluginId) { - // Force refresh of component lookups - for (var i = 0; i < centerRepeater.count; i++) { - var item = centerRepeater.itemAt(i); - if (item && item.widgetId.startsWith(pluginId)) { - item.sourceComponent = root.getWidgetComponent(item.widgetId); - } + onActiveChanged: layoutTimer.restart() } } } diff --git a/quickshell/Modules/Settings/WidgetsTab.qml b/quickshell/Modules/Settings/WidgetsTab.qml index 09c12efc..b279c036 100644 --- a/quickshell/Modules/Settings/WidgetsTab.qml +++ b/quickshell/Modules/Settings/WidgetsTab.qml @@ -130,7 +130,7 @@ Item { { "id": "gpuTemp", "text": I18n.tr("GPU Temperature"), - "description": I18n.tr("GPU temperature display"), + "description": "", "icon": "auto_awesome_mosaic", "warning": !DgopService.dgopAvailable ? I18n.tr("Requires 'dgop' tool") : I18n.tr("This widget prevents GPU power off states, which can significantly impact battery life on laptops. It is not recommended to use this on laptops with hybrid graphics."), "enabled": DgopService.dgopAvailable diff --git a/quickshell/Modules/Settings/WidgetsTabSection.qml b/quickshell/Modules/Settings/WidgetsTabSection.qml index a6b7184d..fb64ee1c 100644 --- a/quickshell/Modules/Settings/WidgetsTabSection.qml +++ b/quickshell/Modules/Settings/WidgetsTabSection.qml @@ -168,7 +168,17 @@ Column { } StyledText { - text: modelData.description + text: { + if (modelData.id === "gpuTemp") { + var selectedIdx = modelData.selectedGpuIndex !== undefined ? modelData.selectedGpuIndex : 0; + if (DgopService.availableGpus && DgopService.availableGpus.length > selectedIdx) { + var gpu = DgopService.availableGpus[selectedIdx]; + return gpu.driver ? gpu.driver.toUpperCase() : ""; + } + return I18n.tr("No GPU detected"); + } + return modelData.description; + } font.pixelSize: Theme.fontSizeSmall color: modelData.enabled ? Theme.outline : Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.6) elide: Text.ElideRight @@ -185,39 +195,37 @@ Column { anchors.verticalCenter: parent.verticalCenter spacing: Theme.spacingXS - Item { - width: 60 - height: 32 + DankActionButton { + id: gpuMenuButton visible: modelData.id === "gpuTemp" + buttonSize: 32 + iconName: "more_vert" + iconSize: 18 + iconColor: Theme.outline + onClicked: { + gpuContextMenu.widgetData = modelData; + gpuContextMenu.sectionId = root.sectionId; + gpuContextMenu.widgetIndex = index; - DankDropdown { - id: gpuDropdown - anchors.fill: parent - popupWidth: -1 - currentValue: { - var selectedIndex = modelData.selectedGpuIndex !== undefined ? modelData.selectedGpuIndex : 0; - if (DgopService.availableGpus && DgopService.availableGpus.length > selectedIndex && selectedIndex >= 0) { - var gpu = DgopService.availableGpus[selectedIndex]; - return gpu.driver.toUpperCase(); - } - return DgopService.availableGpus && DgopService.availableGpus.length > 0 ? DgopService.availableGpus[0].driver.toUpperCase() : ""; + var buttonPos = gpuMenuButton.mapToItem(root, 0, 0); + var popupWidth = gpuContextMenu.width; + var popupHeight = gpuContextMenu.height; + + var xPos = buttonPos.x - popupWidth - Theme.spacingS; + if (xPos < 0) { + xPos = buttonPos.x + gpuMenuButton.width + Theme.spacingS; } - options: { - var gpuOptions = []; - if (DgopService.availableGpus && DgopService.availableGpus.length > 0) { - for (var i = 0; i < DgopService.availableGpus.length; i++) { - var gpu = DgopService.availableGpus[i]; - gpuOptions.push(gpu.driver.toUpperCase()); - } - } - return gpuOptions; - } - onValueChanged: value => { - var gpuIndex = options.indexOf(value); - if (gpuIndex >= 0) { - root.gpuSelectionChanged(root.sectionId, index, gpuIndex); - } + + var yPos = buttonPos.y - popupHeight / 2 + gpuMenuButton.height / 2; + if (yPos < 0) { + yPos = Theme.spacingS; + } else if (yPos + popupHeight > root.height) { + yPos = root.height - popupHeight - Theme.spacingS; } + + gpuContextMenu.x = xPos; + gpuContextMenu.y = yPos; + gpuContextMenu.open(); } } @@ -1123,4 +1131,112 @@ Column { } } } + + Popup { + id: gpuContextMenu + + property var widgetData: null + property string sectionId: "" + property int widgetIndex: -1 + + width: 250 + height: gpuMenuColumn.implicitHeight + Theme.spacingS * 2 + padding: 0 + modal: true + focus: true + closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside + + background: Rectangle { + color: Theme.withAlpha(Theme.surfaceContainer, Theme.popupTransparency) + radius: Theme.cornerRadius + border.color: Qt.rgba(Theme.outline.r, Theme.outline.g, Theme.outline.b, 0.08) + border.width: 0 + } + + contentItem: Item { + Column { + id: gpuMenuColumn + anchors.fill: parent + anchors.margins: Theme.spacingS + spacing: 2 + + Repeater { + model: DgopService.availableGpus || [] + + delegate: Rectangle { + required property var modelData + required property int index + + width: gpuMenuColumn.width + height: 40 + radius: Theme.cornerRadius + color: gpuOptionArea.containsMouse ? Qt.rgba(Theme.primary.r, Theme.primary.g, Theme.primary.b, 0.12) : "transparent" + + property bool isSelected: { + var selectedIdx = gpuContextMenu.widgetData ? (gpuContextMenu.widgetData.selectedGpuIndex !== undefined ? gpuContextMenu.widgetData.selectedGpuIndex : 0) : 0; + return index === selectedIdx; + } + + Row { + anchors.left: parent.left + anchors.leftMargin: Theme.spacingS + anchors.right: checkIcon.left + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + spacing: Theme.spacingS + + DankIcon { + name: "memory" + size: 18 + color: isSelected ? Theme.primary : Theme.surfaceText + anchors.verticalCenter: parent.verticalCenter + } + + Column { + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + + StyledText { + text: modelData.driver ? modelData.driver.toUpperCase() : "" + font.pixelSize: Theme.fontSizeSmall + font.weight: Font.Medium + color: isSelected ? Theme.primary : Theme.surfaceText + } + + StyledText { + text: modelData.displayName || "" + font.pixelSize: Theme.fontSizeSmall + color: Theme.surfaceVariantText + elide: Text.ElideRight + width: 180 + } + } + } + + DankIcon { + id: checkIcon + anchors.right: parent.right + anchors.rightMargin: Theme.spacingS + anchors.verticalCenter: parent.verticalCenter + name: "check" + size: 18 + color: Theme.primary + visible: isSelected + } + + MouseArea { + id: gpuOptionArea + anchors.fill: parent + hoverEnabled: true + cursorShape: Qt.PointingHandCursor + onClicked: { + root.gpuSelectionChanged(gpuContextMenu.sectionId, gpuContextMenu.widgetIndex, index); + gpuContextMenu.close(); + } + } + } + } + } + } + } }