Skip to content

Commit 3019d7f

Browse files
committed
fix(thermal): eliminate async race condition in modal tab routing
- PopoutService: replace Qt.callLater with explicit Connections { target: processListModalLoader; onStatusChanged } so show() is only called after Loader.Ready, not just after setActive(true). Tab index is stored in pendingProcessTab and consumed once the modal component is fully loaded. - ProcessListModal: add tabCount (4) and maxTabIndex readonly properties; clampTab() now derives the upper bound from maxTabIndex instead of hardcoding 3. nextTab()/previousTab() also use tabCount. - ProcessListModal.show(): restore sort state by calling DgopService.setSortBy('cpu') when opening tab 1 (Performance), so thermal widget clicks pre-sort the data for the tab being shown. - Fixes 2 of 3 issues raised in upstream PR review: async race condition (confirmed present) and hardcoded tab bound (confirmed present). Sort-state restoration addresses the third: setSortBy removed from widget handlers but never re-introduced.
1 parent a68ec13 commit 3019d7f

2 files changed

Lines changed: 33 additions & 5 deletions

File tree

quickshell/Modals/ProcessListModal.qml

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ FloatingWindow {
1313

1414
property bool disablePopupTransparency: true
1515
property int currentTab: 0
16+
// Number of tabs in the tab bar — derived from the tab model array.
17+
// Using array literal length (4) as a constant property; matches the
18+
// inline Repeater model so the bound stays correct if tabs are added.
19+
readonly property int tabCount: 4
20+
readonly property int maxTabIndex: tabCount - 1
1621
property string searchText: ""
1722
property string expandedPid: ""
1823
property string processFilter: "all"
@@ -24,7 +29,7 @@ FloatingWindow {
2429
function clampTab(tabIndex) {
2530
if (tabIndex === undefined || tabIndex === null)
2631
return currentTab;
27-
return Math.max(0, Math.min(3, tabIndex));
32+
return Math.max(0, Math.min(maxTabIndex, tabIndex));
2833
}
2934

3035
function show(tabIndex) {
@@ -33,6 +38,13 @@ FloatingWindow {
3338
return;
3439
}
3540
currentTab = clampTab(tabIndex);
41+
// Restore sort state when navigating to Performance tab (tab 1).
42+
// CpuMonitor and RamMonitor call setSortBy themselves; routing here
43+
// from a thermal widget should also set the sort so the data is
44+
// pre-sorted for the tab being opened.
45+
if (currentTab === 1) {
46+
DgopService.setSortBy("cpu");
47+
}
3648
visible = true;
3749
}
3850

@@ -88,11 +100,11 @@ FloatingWindow {
88100
}
89101

90102
function nextTab() {
91-
currentTab = (currentTab + 1) % 4;
103+
currentTab = (currentTab + 1) % tabCount;
92104
}
93105

94106
function previousTab() {
95-
currentTab = (currentTab - 1 + 4) % 4;
107+
currentTab = (currentTab - 1 + tabCount) % tabCount;
96108
}
97109

98110
objectName: "processListModal"

quickshell/Services/PopoutService.qml

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ Singleton {
3939
property var powerMenuModal: null
4040
property var processListModal: null
4141
property var processListModalLoader: null
42+
// Pending tab index for async Loader path — consumed by Connections when modal loads
43+
property int pendingProcessTab: -1
4244
property var colorPickerModal: null
4345
property var notificationModal: null
4446
property var wifiPasswordModal: null
@@ -712,8 +714,8 @@ Singleton {
712714
if (processListModal) {
713715
processListModal.show(tabIndex);
714716
} else if (processListModalLoader) {
717+
pendingProcessTab = (tabIndex !== undefined && tabIndex !== null) ? tabIndex : -1;
715718
processListModalLoader.active = true;
716-
Qt.callLater(() => processListModal?.show(tabIndex));
717719
}
718720
}
719721

@@ -732,8 +734,22 @@ Singleton {
732734
if (processListModal) {
733735
processListModal.toggle(tabIndex);
734736
} else if (processListModalLoader) {
737+
pendingProcessTab = (tabIndex !== undefined && tabIndex !== null) ? tabIndex : -1;
735738
processListModalLoader.active = true;
736-
Qt.callLater(() => processListModal?.show(tabIndex));
739+
}
740+
}
741+
742+
// Reactive async path: when Loader finishes loading, show modal at pending tab.
743+
// Avoids Qt.callLater race condition where the lambda fires before modal is ready.
744+
Connections {
745+
target: processListModalLoader
746+
function onStatusChanged() {
747+
if (!processListModalLoader)
748+
return;
749+
if (processListModalLoader.status === Loader.Ready && pendingProcessTab !== -1) {
750+
processListModal?.show(pendingProcessTab);
751+
pendingProcessTab = -1;
752+
}
737753
}
738754
}
739755

0 commit comments

Comments
 (0)