From 679224e1d95ce1532fc0f5c64149e906d56dcd8f Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:40:51 +0800 Subject: [PATCH 1/7] feat(settings): Add usage badges to widget selector Adds a visual indicator to the "Add Widget" dropdown in the Bar settings panel to show which widgets are already in use and where. - A small text badge ("L", "C", "R") now appears next to any widget that is already on a panel. - The badges are reactive and update automatically when widgets are added or removed. - This helps prevent accidental duplicate additions and makes widget management easier. --- Modules/Settings/Extras/SectionEditor.qml | 1 + Modules/Settings/Tabs/BarTab.qml | 50 +++++++++--- Services/BarService.qml | 4 + Widgets/NSearchableComboBox.qml | 97 ++++++++++++++++------- 4 files changed, 112 insertions(+), 40 deletions(-) diff --git a/Modules/Settings/Extras/SectionEditor.qml b/Modules/Settings/Extras/SectionEditor.qml index 58989aa4..99c2a6b1 100644 --- a/Modules/Settings/Extras/SectionEditor.qml +++ b/Modules/Settings/Extras/SectionEditor.qml @@ -83,6 +83,7 @@ NBox { Item { Layout.fillWidth: true } + NSearchableComboBox { id: comboBox model: availableWidgets diff --git a/Modules/Settings/Tabs/BarTab.qml b/Modules/Settings/Tabs/BarTab.qml index 47207f0d..5fc32878 100644 --- a/Modules/Settings/Tabs/BarTab.qml +++ b/Modules/Settings/Tabs/BarTab.qml @@ -300,9 +300,7 @@ ColumnLayout { Layout.bottomMargin: Style.marginXL } - // --------------------------------- // Signal functions - // --------------------------------- function _addWidgetToSection(widgetId, section) { var newWidget = { "id": widgetId @@ -372,19 +370,51 @@ ColumnLayout { } } + // Data model functions + function getWidgetLocations(widgetId) { + if (!BarService) + return [] + const instances = BarService.getAllRegisteredWidgets() + const locations = {} + for (var i = 0; i < instances.length; i++) { + if (instances[i].widgetId === widgetId) { + const section = instances[i].section + if (section === "left") + locations["L"] = true + else if (section === "center") + locations["C"] = true + else if (section === "right") + locations["R"] = true + } + } + return Object.keys(locations).join('') + } + + function updateAvailableWidgetsModel() { + availableWidgets.clear() + const widgets = BarWidgetRegistry.getAvailableWidgets() + widgets.forEach(entry => { + availableWidgets.append({ + "key": entry, + "name": entry, + "badgeLocations": getWidgetLocations(entry) + }) + }) + } + // Base list model for all combo boxes ListModel { id: availableWidgets } Component.onCompleted: { - // Fill out availableWidgets ListModel - availableWidgets.clear() - BarWidgetRegistry.getAvailableWidgets().forEach(entry => { - availableWidgets.append({ - "key": entry, - "name": entry - }) - }) + updateAvailableWidgetsModel() + } + + Connections { + target: BarService + function onActiveWidgetsChanged() { + updateAvailableWidgetsModel() + } } } diff --git a/Services/BarService.qml b/Services/BarService.qml index 9a28b550..05c66c1e 100644 --- a/Services/BarService.qml +++ b/Services/BarService.qml @@ -15,6 +15,8 @@ Singleton { // Key format: "screenName|section|widgetId|index" property var widgetInstances: ({}) + signal activeWidgetsChanged() + signal barReadyChanged(string screenName) // Simple timer that run once when the widget structure has changed @@ -68,6 +70,7 @@ Singleton { timerCheckVisualizer.restart() Logger.log("BarService", "Registered widget:", key) + root.activeWidgetsChanged() } // Unregister a widget instance @@ -75,6 +78,7 @@ Singleton { const key = [screenName, section, widgetId, index].join("|") delete widgetInstances[key] Logger.log("BarService", "Unregistered widget:", key) + root.activeWidgetsChanged() } // Lookup a specific widget instance (returns the actual QML instance) diff --git a/Widgets/NSearchableComboBox.qml b/Widgets/NSearchableComboBox.qml index 462d5a6b..4e7baf5e 100644 --- a/Widgets/NSearchableComboBox.qml +++ b/Widgets/NSearchableComboBox.qml @@ -20,6 +20,7 @@ RowLayout { property string currentKey: "" property string placeholder: "" property string searchPlaceholder: I18n.tr("placeholders.search") + property Component delegate: null readonly property real preferredHeight: Style.baseWidgetSize * 1.1 @@ -187,43 +188,79 @@ RowLayout { horizontalPolicy: ScrollBar.AlwaysOff verticalPolicy: ScrollBar.AsNeeded - delegate: ItemDelegate { - width: listView.width - hoverEnabled: true - highlighted: ListView.view.currentIndex === index + delegate: root.delegate ? root.delegate : defaultDelegate - onHoveredChanged: { - if (hovered) { - ListView.view.currentIndex = index + Component { + id: defaultDelegate + ItemDelegate { + id: delegateRoot + width: listView.width + hoverEnabled: true + highlighted: ListView.view.currentIndex === index + + onHoveredChanged: { + if (hovered) { + ListView.view.currentIndex = index + } } - } - onClicked: { - root.selected(filteredModel.get(index).key) - combo.currentIndex = root.findIndexByKeyInFiltered(filteredModel.get(index).key) - combo.popup.close() - } + onClicked: { + root.selected(filteredModel.get(index).key) + combo.currentIndex = root.findIndexByKeyInFiltered(filteredModel.get(index).key) + combo.popup.close() + } - contentItem: NText { - text: name - pointSize: Style.fontSizeM - color: highlighted ? Color.mOnTertiary : Color.mOnSurface - verticalAlignment: Text.AlignVCenter - elide: Text.ElideRight - Behavior on color { - ColorAnimation { - duration: Style.animationFast + contentItem: RowLayout { + width: parent.width + spacing: Style.marginM + + NText { + text: name + pointSize: Style.fontSizeM + color: highlighted ? Color.mOnTertiary : Color.mOnSurface + verticalAlignment: Text.AlignVCenter + elide: Text.ElideRight + Layout.fillWidth: true + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } + } + } + + RowLayout { + spacing: Style.marginS + Layout.alignment: Qt.AlignRight + + Repeater { + model: badgeLocations + + delegate: NBox { + width: Style.baseWidgetSize * 0.7 + height: Style.baseWidgetSize * 0.7 + color: "transparent" + radius: Style.radiusS + border.width: 0 + + NText { + anchors.centerIn: parent + text: modelData + pointSize: Style.fontSizeXXS + font.weight: Style.fontWeightBold + color: highlighted ? Color.mOnTertiary : Color.mOnSurface + } + } } } } - - background: Rectangle { - width: listView.width - color: highlighted ? Color.mTertiary : Color.transparent - radius: Style.radiusS - Behavior on color { - ColorAnimation { - duration: Style.animationFast + background: Rectangle { + width: listView.width + color: highlighted ? Color.mTertiary : Color.transparent + radius: Style.radiusS + Behavior on color { + ColorAnimation { + duration: Style.animationFast + } } } } From 524e2127650e2535917f2177a87e19b6ba0cdc10 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:48:26 +0800 Subject: [PATCH 2/7] docs(i18n): Explain widget usage badges in settings --- Assets/Translations/en.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index 7c1cdaa5..b25a1943 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -223,7 +223,7 @@ "widgets": { "section": { "label": "Widgets positioning", - "description": "Drag and drop widgets to reorder them within each section, or use the add/remove buttons to manage widgets." + "description": "Drag and drop widgets to reorder them. Badges indicate usage: [L]eft, [C]enter, [R]ight." } }, "monitors": { From d4c16de8185f90dfe28a129dd480db2331ba2ee6 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:55:22 +0800 Subject: [PATCH 3/7] docs(i18n): Translate widget badge explanation to Chinese (Simplified) --- Assets/Translations/zh-CN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 000b0b7f..d6560b1b 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -222,7 +222,7 @@ "widgets": { "section": { "label": "小部件定位", - "description": "拖放小部件以在每个部分内重新排序,或使用添加/删除按钮管理小部件。" + "description": "拖放小部件以重新排序。徽章表示使用情况:[L]左、[C]中、[R]右。" } }, "monitors": { From 20d7dfc003736f5f1388d22c7bd89a16453153ea Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:57:00 +0800 Subject: [PATCH 4/7] docs(i18n): Translate widget badge explanation to German --- Assets/Translations/de.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 1d6e4b48..e607f72c 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -222,7 +222,7 @@ "widgets": { "section": { "label": "Widget-Positionierung", - "description": "Widgets per Drag & Drop innerhalb jeder Sektion neu anordnen oder Add/Remove-Buttons zum Verwalten verwenden." + "description": "Widgets per Drag & Drop neu anordnen. Abzeichen zeigen die Verwendung an: [L]inks, [M]itte, [R]echts." } }, "monitors": { From e447a7d11a61fdfb02ca474dc4598b1522b7ba16 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:57:27 +0800 Subject: [PATCH 5/7] docs(i18n): Translate widget badge explanation to Spanish --- Assets/Translations/es.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index 3fa757bf..a259d0d2 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -222,7 +222,7 @@ "widgets": { "section": { "label": "Posicionamiento de widgets", - "description": "Arrastra y suelta widgets para reordenarlos dentro de cada sección, o usa los botones de añadir/eliminar para gestionar los widgets." + "description": "Arrastra y suelta widgets para reordenarlos. Las insignias indican el uso: [I]zquierda, [C]entro, [D]erecha." } }, "monitors": { From 5bc7ca4adf3f0ac701175bb60ecfefcf7e9857ca Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:58:25 +0800 Subject: [PATCH 6/7] docs(i18n): Translate widget badge explanation to French --- Assets/Translations/fr.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index 5c105b26..b51b4e6d 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -222,7 +222,7 @@ "widgets": { "section": { "label": "Positionnement des widgets", - "description": "Glissez-déposez les widgets pour les réorganiser dans chaque section, ou utilisez les boutons ajouter/supprimer pour gérer les widgets." + "description": "Faites glisser et déposez les widgets pour les réorganiser. Les badges indiquent l'utilisation : [G]auche, [C]entre, [D]roite." } }, "monitors": { From cf44574ac4c81956a6690309ab916ca71e65405f Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Tue, 14 Oct 2025 03:59:04 +0800 Subject: [PATCH 7/7] docs(i18n): Translate widget badge explanation to Portuguese --- Assets/Translations/pt.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index 6b591e0c..572e4d4d 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -222,7 +222,7 @@ "widgets": { "section": { "label": "Posicionamento dos widgets", - "description": "Arraste e solte os widgets para reordená-los em cada seção, ou use os botões de adicionar/remover para gerenciar os widgets." + "description": "Arraste e solte widgets para reordená-los. Os emblemas indicam o uso: [E]squerda, [C]entro, [D]ireita." } }, "monitors": {