mirror of
https://github.com/noctalia-dev/noctalia-shell.git
synced 2026-05-11 17:08:27 +08:00
bar: avoid recreating model on every settings without breaking bar widgets reordering/deletion.
This commit is contained in:
+101
-31
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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: ({})
|
||||
|
||||
Reference in New Issue
Block a user