From 661dc27d265e6f2826838d9693eea04c3621ff45 Mon Sep 17 00:00:00 2001 From: feng-yifan Date: Mon, 27 Jul 2026 02:24:21 +0800 Subject: [PATCH] fix(settings): reset AppBrowserPopup model on close to avoid crash (#2925) Reopening the autostart "Browse" picker crashed in QQmlIncubatorPrivate::incubate. Drop the ListView model on hide() and rebind it on show() so each open starts with a fresh QQmlDelegateModel instead of reusing a stale incubation queue. (cherry picked from commit 3182c708573223a44362af3c16418d6be31abbf0) --- .../Settings/Widgets/AppBrowserPopup.qml | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml b/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml index bd4a73b52..ebc5d95b9 100644 --- a/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml +++ b/quickshell/Modules/Settings/Widgets/AppBrowserPopup.qml @@ -167,7 +167,13 @@ FloatingWindow { width: parent.width height: parent.height - searchField.height - Theme.spacingM spacing: Theme.spacingS - model: root.filteredApps + // Start with no model. It is (re)bound in show() so that each open + // gets a fresh QQmlDelegateModel, avoiding a crash where a stale + // incubation queue from a previous open races with a background + // refreshApplications() replacing the underlying DesktopEntries + // QObjectModel (SIGSEGV in QQmlIncubatorPrivate::incubate via + // libQt6QmlModels). See hide()/onVisibleChanged() for the teardown. + model: null clip: true delegate: Rectangle { @@ -316,11 +322,21 @@ FloatingWindow { function show() { updateFilteredApps(); + // Rebind the model reactively (search relies on filteredApps changes flowing + // through), and do it after populating so the ListView starts incubating from + // a fully-formed array rather than []. + appList.model = Qt.binding(() => root.filteredApps); visible = true; Qt.callLater(() => searchField.forceActiveFocus()); } function hide() { + // Drop the model before clearing visible, so the ListView releases its + // QQmlDelegateModel (and any in-flight incubators) synchronously on this + // frame. This is the crux of the fix: a later show() will allocate a fresh + // DelegateModel instead of reusing one whose incubation queue may hold + // references invalidated by a concurrent refreshApplications(). + appList.model = null; visible = false; searchQuery = ""; filteredApps = []; @@ -330,6 +346,9 @@ FloatingWindow { onVisibleChanged: { if (!visible) { + // Guard against visibility being cleared without going through hide() + // (e.g. window manager close, FloatingWindow.onClosed -> hide()). + appList.model = null; searchQuery = ""; filteredApps = []; selectedIndex = -1;