bar: avoid recreating model on every settings without breaking bar widgets reordering/deletion.

This commit is contained in:
Lemmy
2026-01-29 17:01:50 -05:00
parent 8125236d4e
commit 010d1ad854
4 changed files with 118 additions and 31 deletions
+101 -31
View File
@@ -74,9 +74,79 @@ Item {
readonly property real capsuleHeight: Style.getCapsuleHeightForDensity(barDensity, barHeight)
readonly property real barFontSize: Style.getBarFontSizeForDensity(barHeight, capsuleHeight, barIsVertical)
// Bar widgets (per-screen)
// Bar widgets (per-screen) - initial configuration
// Note: Updates are handled via Connections to BarService.widgetsRevisionChanged
readonly property var barWidgets: Settings.getBarWidgetsForScreen(screen?.name)
// Stable ListModels for each section - prevents Repeater recreation on settings changes
property ListModel leftWidgetsModel: ListModel {}
property ListModel centerWidgetsModel: ListModel {}
property ListModel rightWidgetsModel: ListModel {}
// Sync a ListModel with widget data, preserving delegates when only settings change
function syncWidgetModel(model, newWidgets) {
var validWidgets = filterValidWidgets(newWidgets);
// Build list of current IDs in model
var currentIds = [];
for (var i = 0; i < model.count; i++) {
currentIds.push(model.get(i).id);
}
// Build list of new IDs
var newIds = validWidgets.map(w => w.id);
// Check if structure changed (different IDs or order)
var structureChanged = currentIds.length !== newIds.length;
if (!structureChanged) {
for (var i = 0; i < currentIds.length; i++) {
if (currentIds[i] !== newIds[i]) {
structureChanged = true;
break;
}
}
}
Logger.d("Bar", "syncWidgetModel:", currentIds.join("|"), "→", newIds.join("|"), "changed:", structureChanged);
if (structureChanged) {
// Rebuild model - IDs changed
model.clear();
for (var i = 0; i < validWidgets.length; i++) {
model.append(validWidgets[i]);
}
}
// If structure didn't change, delegates are preserved and will read fresh settings
}
// Sync models when widget revision changes
// Note: We use Connections instead of onBarWidgetsChanged because getBarWidgetsForScreen
// returns the same object reference (Settings.data.bar.widgets) even when content changes,
// so QML won't detect the change via property binding.
Connections {
target: BarService
function onWidgetsRevisionChanged() {
Logger.d("Bar", "onWidgetsRevisionChanged, revision:", BarService.widgetsRevision, "screen:", root.screen?.name);
var widgets = Settings.getBarWidgetsForScreen(root.screen?.name);
if (widgets) {
root.syncWidgetModel(root.leftWidgetsModel, widgets.left);
root.syncWidgetModel(root.centerWidgetsModel, widgets.center);
root.syncWidgetModel(root.rightWidgetsModel, widgets.right);
}
}
}
// Initialize models
Component.onCompleted: {
Logger.d("Bar", "Bar Component.onCompleted for screen:", screen?.name);
var widgets = Settings.getBarWidgetsForScreen(screen?.name);
if (widgets) {
syncWidgetModel(leftWidgetsModel, widgets.left);
syncWidgetModel(centerWidgetsModel, widgets.center);
syncWidgetModel(rightWidgetsModel, widgets.right);
}
}
// Fill the parent (the Loader)
anchors.fill: parent
@@ -274,18 +344,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.left)
model: root.leftWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "left",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.left.length
"sectionWidgetsCount": root.leftWidgetsModel.count
})
Layout.alignment: Qt.AlignHCenter
}
@@ -299,18 +369,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.center)
model: root.centerWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "center",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.center.length
"sectionWidgetsCount": root.centerWidgetsModel.count
})
Layout.alignment: Qt.AlignHCenter
}
@@ -325,18 +395,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.right)
model: root.rightWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "right",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.right.length
"sectionWidgetsCount": root.rightWidgetsModel.count
})
Layout.alignment: Qt.AlignHCenter
}
@@ -380,18 +450,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.left)
model: root.leftWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "left",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.left.length
"sectionWidgetsCount": root.leftWidgetsModel.count
})
Layout.alignment: Qt.AlignVCenter
}
@@ -407,18 +477,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.center)
model: root.centerWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "center",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.center.length
"sectionWidgetsCount": root.centerWidgetsModel.count
})
Layout.alignment: Qt.AlignVCenter
}
@@ -435,18 +505,18 @@ Item {
spacing: Style.marginS
Repeater {
model: root.filterValidWidgets(root.barWidgets.right)
model: root.rightWidgetsModel
delegate: BarWidgetLoader {
required property var modelData
required property var model
required property int index
widgetId: modelData.id || ""
widgetId: model.id || ""
widgetScreen: root.screen
widgetProps: ({
"widgetId": modelData.id,
"widgetId": model.id,
"section": "right",
"sectionWidgetIndex": index,
"sectionWidgetsCount": root.barWidgets.right.length
"sectionWidgetsCount": root.rightWidgetsModel.count
})
Layout.alignment: Qt.AlignVCenter
}
@@ -53,6 +53,7 @@ NBox {
var widgets = _getWidgetsContainer();
widgets[section].push(newWidget);
_saveWidgets(widgets);
BarService.widgetsRevision++;
}
function _removeWidgetFromSection(section, index) {
@@ -62,6 +63,7 @@ NBox {
var removedWidgets = newArray.splice(index, 1);
widgets[section] = newArray;
_saveWidgets(widgets);
BarService.widgetsRevision++;
if (removedWidgets[0].id === "ControlCenter" && BarService.lookupWidget("ControlCenter") === undefined) {
ToastService.showWarning(I18n.tr("toast.missing-control-center.label"), I18n.tr("toast.missing-control-center.description"), 12000);
@@ -78,9 +80,12 @@ NBox {
newArray.splice(toIndex, 0, item);
widgets[section] = newArray;
_saveWidgets(widgets);
BarService.widgetsRevision++;
}
}
// Note: _updateWidgetSettingsInSection does NOT increment revision
// because it only changes settings, not widget structure
function _updateWidgetSettingsInSection(section, index, settings) {
var widgets = _getWidgetsContainer();
widgets[section][index] = settings;
@@ -98,6 +103,7 @@ NBox {
targetArray.push(widget);
widgets[toSection] = targetArray;
_saveWidgets(widgets);
BarService.widgetsRevision++;
}
}
@@ -40,6 +40,7 @@ ColumnLayout {
}
}
Settings.data.bar.widgets[section].push(newWidget);
BarService.widgetsRevision++;
}
function _removeWidgetFromSection(section, index) {
@@ -48,6 +49,7 @@ ColumnLayout {
var newArray = widgets[section].slice();
var removedWidgets = newArray.splice(index, 1);
widgets[section] = newArray;
BarService.widgetsRevision++;
if (removedWidgets[0].id === "ControlCenter" && BarService.lookupWidget("ControlCenter") === undefined) {
ToastService.showWarning(I18n.tr("toast.missing-control-center.label"), I18n.tr("toast.missing-control-center.description"), 12000);
@@ -63,9 +65,12 @@ ColumnLayout {
newArray.splice(fromIndex, 1);
newArray.splice(toIndex, 0, item);
widgets[section] = newArray;
BarService.widgetsRevision++;
}
}
// Note: _updateWidgetSettingsInSection does NOT increment revision
// because it only changes settings, not widget structure
function _updateWidgetSettingsInSection(section, index, settings) {
Settings.data.bar.widgets[section][index] = settings;
}
@@ -80,6 +85,8 @@ ColumnLayout {
var targetArray = widgets[toSection].slice();
targetArray.push(widget);
widgets[toSection] = targetArray;
BarService.widgetsRevision++;
Logger.d("BarTab", "_moveWidgetBetweenSections: revision now", BarService.widgetsRevision);
}
}
+4
View File
@@ -24,6 +24,10 @@ Singleton {
property var readyBars: ({})
// Revision counter - increment when widget list structure changes (add/remove/reorder)
// This triggers Bar.qml to re-sync its ListModels
property int widgetsRevision: 0
// Registry to store actual widget instances
// Key format: "screenName|section|widgetId|index"
property var widgetInstances: ({})