chore(merge): bar autohide + hyprland fixes

This commit is contained in:
Lemmy
2026-03-18 09:00:47 -04:00
12 changed files with 97 additions and 117 deletions
+10
View File
@@ -85,6 +85,12 @@ Item {
property ListModel centerWidgetsModel: ListModel {}
property ListModel rightWidgetsModel: ListModel {}
// Guard: set when Bar is destroyed; prevents Qt.callLater callbacks from running
// during/after teardown (avoids SIGSEGV in QV4::Object::insertMember when rapid
// workspace switch causes load/unload overlap with async widget incubation)
property bool _destroyed: false
Component.onDestruction: root._destroyed = true
// Sync a ListModel with widget data, preserving delegates when only settings change
function syncWidgetModel(model, newWidgets) {
var validWidgets = filterValidWidgets(newWidgets);
@@ -134,6 +140,8 @@ Item {
}
function _syncFromRevision() {
if (root._destroyed)
return;
var widgets = Settings.getBarWidgetsForScreen(screen?.name);
if (widgets) {
syncWidgetModel(leftWidgetsModel, widgets.left);
@@ -152,6 +160,8 @@ Item {
}
function _initModels() {
if (root._destroyed)
return;
var widgets = Settings.getBarWidgetsForScreen(screen?.name);
if (widgets) {
syncWidgetModel(leftWidgetsModel, widgets.left);
+2 -27
View File
@@ -72,33 +72,8 @@ Item {
Loader {
id: loader
anchors.fill: parent
asynchronous: false
// Deferred activation to prevent re-entrant incubation crash:
// When ListModel.append() creates this delegate, the Repeater is mid-incubation.
// If this Loader activates synchronously (asynchronous: false) during delegate
// finalization, it triggers nested QQmlIncubatorPrivate::incubate which corrupts
// the V4 heap (SIGSEGV in QV4::Object::insertMember).
// Deferring to the next event loop iteration breaks the nesting.
property bool _ready: false
active: _ready && root.checkWidgetExists() && (root.reloadCounter >= 0)
Timer {
id: activateTimer
interval: 0
onTriggered: loader._ready = true
}
Component.onCompleted: activateTimer.start()
// Reset _ready when reloadCounter changes to force a deferred re-activation
Connections {
target: root
function onReloadCounterChanged() {
loader._ready = false;
activateTimer.restart();
}
}
asynchronous: true
active: root.checkWidgetExists() && (root.reloadCounter >= 0)
sourceComponent: {
// Depend on reloadCounter to force re-fetch of component
+1 -1
View File
@@ -50,7 +50,7 @@ NIconButton {
// If using distro logo, don't use theme icon.
icon: (customIconPath === "" && !useDistroLogo) ? customIcon : ""
tooltipText: {
if (PanelService.getPanel("controlCenterPanel", screen)?.isPanelOpen) {
if (!screen || PanelService.getPanel("controlCenterPanel", screen)?.isPanelOpen) {
return "";
} else {
return I18n.tr("tooltips.open-control-center");
+1 -1
View File
@@ -408,7 +408,7 @@ Item {
}
onEntered: {
if ((isVertical || scrollingMode === "never") && !PanelService.getPanel("mediaPlayerPanel", screen)?.isPanelOpen) {
if (screen && (isVertical || scrollingMode === "never") && !PanelService.getPanel("mediaPlayerPanel", screen)?.isPanelOpen) {
TooltipService.show(root, title, BarService.getTooltipDirection(root.screen?.name));
}
}
+2 -2
View File
@@ -76,7 +76,7 @@ Item {
function onVolumeAtMaximum() {
if (!firstVolumeReceived) {
firstVolumeReceived = true;
firstVolumeReceived = true;
} else {
// Hide any tooltip while the pill is visible / being updated
TooltipService.hide();
@@ -87,7 +87,7 @@ Item {
function onVolumeAtMinimum() {
if (!firstVolumeReceived) {
firstVolumeReceived = true;
firstVolumeReceived = true;
} else {
// Hide any tooltip while the pill is visible / being updated
TooltipService.hide();
+4 -12
View File
@@ -271,20 +271,12 @@ Item {
Settings.data.dock.pinnedApps = pinnedApps;
}
// Deferred to next event-loop iteration via Timer { interval: 0 } to avoid
// re-entrant incubation: Qt.callLater() can still fire within the same event
// processing cycle, so it is not sufficient. localWorkspaces.append() inside
// refreshWorkspaces() causes the Repeater to create WorkspacePill delegates
// mid-incubation, corrupting the V4 heap (SIGSEGV in QV4::Object::insertMember).
Timer {
id: refreshTimer
interval: 0
onTriggered: root.refreshWorkspaces()
}
// Deferred via Qt.callLater to avoid synchronous ListModel mutations during
// signal cascades. Qt.callLater deduplicates by function identity, so rapid
// calls from multiple signal handlers coalesce into a single refresh.
function scheduleRefresh() {
if (!root.isDestroying)
refreshTimer.restart();
Qt.callLater(root.refreshWorkspaces);
}
Component.onCompleted: scheduleRefresh()