diff --git a/quickshell/Modals/ProcessListModal.qml b/quickshell/Modals/ProcessListModal.qml index a313fbd69..730de86a9 100644 --- a/quickshell/Modals/ProcessListModal.qml +++ b/quickshell/Modals/ProcessListModal.qml @@ -13,6 +13,11 @@ FloatingWindow { property bool disablePopupTransparency: true property int currentTab: 0 + // Number of tabs in the tab bar — derived from the tab model array. + // Using array literal length (4) as a constant property; matches the + // inline Repeater model so the bound stays correct if tabs are added. + readonly property int tabCount: 4 + readonly property int maxTabIndex: tabCount - 1 property string searchText: "" property string expandedPid: "" property string processFilter: "all" @@ -21,11 +26,25 @@ FloatingWindow { signal closingModal - function show() { + function clampTab(tabIndex) { + if (tabIndex === undefined || tabIndex === null) + return currentTab; + return Math.max(0, Math.min(maxTabIndex, tabIndex)); + } + + function show(tabIndex) { if (!DgopService.dgopAvailable) { log.warn("dgop is not available"); return; } + currentTab = clampTab(tabIndex); + // Restore sort state when navigating to Performance tab (tab 1). + // CpuMonitor and RamMonitor call setSortBy themselves; routing here + // from a thermal widget should also set the sort so the data is + // pre-sorted for the tab being opened. + if (currentTab === 1) { + DgopService.setSortBy("cpu"); + } visible = true; } @@ -35,12 +54,18 @@ FloatingWindow { processContextMenu.close(); } - function toggle() { + function toggle(tabIndex) { if (!DgopService.dgopAvailable) { log.warn("dgop is not available"); return; } - visible = !visible; + // If already visible on the target tab, just hide. + // Otherwise delegate to show() which handles clampTab, sort state, and visibility. + if (visible && currentTab === clampTab(tabIndex)) { + hide(); + return; + } + show(tabIndex); } function focusOrToggle() { @@ -75,11 +100,11 @@ FloatingWindow { } function nextTab() { - currentTab = (currentTab + 1) % 4; + currentTab = (currentTab + 1) % tabCount; } function previousTab() { - currentTab = (currentTab - 1 + 4) % 4; + currentTab = (currentTab - 1 + tabCount) % tabCount; } objectName: "processListModal" diff --git a/quickshell/Modules/DankBar/DankBarContent.qml b/quickshell/Modules/DankBar/DankBarContent.qml index c06b9e88b..7adfbb71f 100644 --- a/quickshell/Modules/DankBar/DankBarContent.qml +++ b/quickshell/Modules/DankBar/DankBarContent.qml @@ -1180,22 +1180,7 @@ Item { parentScreen: barWindow.screen widgetData: parent.widgetData onCpuTempClicked: { - processListPopoutLoader.active = true; - if (!processListPopoutLoader.item) { - return; - } - const effectiveBarConfig = topBarContent.barConfig; - const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1)); - if (processListPopoutLoader.item.setBarContext) { - processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0); - } - if (processListPopoutLoader.item.setTriggerPosition) { - const globalPos = cpuTempWidget.mapToItem(null, 0, 0); - const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, cpuTempWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig); - const widgetSection = topBarContent.getWidgetSection(parent) || "right"; - processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig); - } - PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "cpu_temp"); + PopoutService.toggleProcessListModal(1); } } } @@ -1213,22 +1198,7 @@ Item { parentScreen: barWindow.screen widgetData: parent.widgetData onGpuTempClicked: { - processListPopoutLoader.active = true; - if (!processListPopoutLoader.item) { - return; - } - const effectiveBarConfig = topBarContent.barConfig; - const barPosition = barWindow.axis?.edge === "left" ? 2 : (barWindow.axis?.edge === "right" ? 3 : (barWindow.axis?.edge === "top" ? 0 : 1)); - if (processListPopoutLoader.item.setBarContext) { - processListPopoutLoader.item.setBarContext(barPosition, effectiveBarConfig?.bottomGap ?? 0); - } - if (processListPopoutLoader.item.setTriggerPosition) { - const globalPos = gpuTempWidget.mapToItem(null, 0, 0); - const pos = SettingsData.getPopupTriggerPosition(globalPos, barWindow.screen, barWindow.effectiveBarThickness, gpuTempWidget.width, effectiveBarConfig?.spacing ?? 4, barPosition, effectiveBarConfig); - const widgetSection = topBarContent.getWidgetSection(parent) || "right"; - processListPopoutLoader.item.setTriggerPosition(pos.x, pos.y, pos.width, widgetSection, barWindow.screen, barPosition, barWindow.effectiveBarThickness, effectiveBarConfig?.spacing ?? 4, effectiveBarConfig); - } - PopoutManager.requestPopout(processListPopoutLoader.item, undefined, "gpu_temp"); + PopoutService.toggleProcessListModal(3); } } } diff --git a/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml b/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml index 40663871d..648ed30f0 100644 --- a/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml +++ b/quickshell/Modules/DankBar/Widgets/CpuTemperature.qml @@ -133,7 +133,6 @@ BasePill { acceptedButtons: Qt.LeftButton onPressed: mouse => { root.triggerRipple(this, mouse.x, mouse.y); - DgopService.setSortBy("cpu"); cpuTempClicked(); } } diff --git a/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml b/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml index cf07f3eb2..97bf7b9c5 100644 --- a/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml +++ b/quickshell/Modules/DankBar/Widgets/GpuTemperature.qml @@ -201,7 +201,6 @@ BasePill { acceptedButtons: Qt.LeftButton onPressed: mouse => { root.triggerRipple(this, mouse.x, mouse.y); - DgopService.setSortBy("cpu"); gpuTempClicked(); } } diff --git a/quickshell/Services/PopoutService.qml b/quickshell/Services/PopoutService.qml index b6c1cb163..fca37ba26 100644 --- a/quickshell/Services/PopoutService.qml +++ b/quickshell/Services/PopoutService.qml @@ -39,6 +39,8 @@ Singleton { property var powerMenuModal: null property var processListModal: null property var processListModalLoader: null + // Pending tab index for async Loader path — consumed by Connections when modal loads + property int pendingProcessTab: -1 property var colorPickerModal: null property var notificationModal: null property var wifiPasswordModal: null @@ -708,12 +710,12 @@ Singleton { } } - function showProcessListModal() { + function showProcessListModal(tabIndex) { if (processListModal) { - processListModal.show(); + processListModal.show(tabIndex); } else if (processListModalLoader) { + pendingProcessTab = (tabIndex !== undefined && tabIndex !== null) ? tabIndex : -1; processListModalLoader.active = true; - Qt.callLater(() => processListModal?.show()); } } @@ -728,12 +730,26 @@ Singleton { } } - function toggleProcessListModal() { + function toggleProcessListModal(tabIndex) { if (processListModal) { - processListModal.toggle(); + processListModal.toggle(tabIndex); } else if (processListModalLoader) { + pendingProcessTab = (tabIndex !== undefined && tabIndex !== null) ? tabIndex : -1; processListModalLoader.active = true; - Qt.callLater(() => processListModal?.show()); + } + } + + // Reactive async path: when Loader finishes loading, show modal at pending tab. + // Avoids Qt.callLater race condition where the lambda fires before modal is ready. + Connections { + target: processListModalLoader + function onStatusChanged() { + if (!processListModalLoader) + return; + if (processListModalLoader.status === Loader.Ready && pendingProcessTab !== -1) { + processListModal?.show(pendingProcessTab); + pendingProcessTab = -1; + } } }