From b34f97130621961e7bce4f1923a853ece64f2302 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:00:46 +0800 Subject: [PATCH 01/17] feat(tray): Implement core blacklist filtering logic --- Modules/Bar/Widgets/Tray.qml | 68 ++++++++++++++++++++++++++++++++++-- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 30a3f7b9..85e42afc 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -19,7 +19,51 @@ Rectangle { readonly property string barPosition: Settings.data.bar.position readonly property bool isVertical: barPosition === "left" || barPosition === "right" readonly property bool compact: (Settings.data.bar.density === "compact") - readonly property real itemSize: isVertical ? Math.round(width * 0.7) : Math.round(height * 0.7) + property real itemSize: isVertical ? Math.round(width * 0.7) : Math.round(height * 0.7) + property list blacklist: Settings.data.bar.trayBlacklist || [] // Read from settings + property var filteredItems: [] + + function wildCardMatch(str, rule) { + return str.toLowerCase().includes(rule.toLowerCase()); // Simple substring match + } + + function updateFilteredItems() { + if (!root.blacklist || root.blacklist.length === 0) { + if (SystemTray.items && SystemTray.items.values) { + filteredItems = SystemTray.items.values + } else { + filteredItems = [] + } + return + } + + let newItems = [] + if (SystemTray.items && SystemTray.items.values) { + const trayItems = SystemTray.items.values + for (var i = 0; i < trayItems.length; i++) { + const item = trayItems[i] + if (!item) { + continue + } + + const title = item.tooltipTitle || item.name || item.id || "" + + let isBlacklisted = false + for (var j = 0; j < root.blacklist.length; j++) { + const rule = root.blacklist[j] + if (wildCardMatch(title, rule)) { + isBlacklisted = true + break + } + } + + if (!isBlacklisted) { + newItems.push(item) + } + } + } + filteredItems = newItems + } function onLoaded() { // When the widget is fully initialized with its props set the screen for the trayMenu @@ -28,7 +72,25 @@ Rectangle { } } - visible: SystemTray.items.values.length > 0 + Connections { + target: SystemTray.items + function onValuesChanged() { + root.updateFilteredItems() + } + } + + Connections { + target: Settings + function onSettingsSaved() { + root.updateFilteredItems() + } + } + + Component.onCompleted: { + root.updateFilteredItems() // Initial update + } + + visible: filteredItems.length > 0 implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : (trayFlow.implicitWidth + Style.marginS * scaling * 2) implicitHeight: isVertical ? (trayFlow.implicitHeight + Style.marginS * scaling * 2) : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) @@ -44,7 +106,7 @@ Rectangle { Repeater { id: repeater - model: SystemTray.items + model: filteredItems delegate: Item { width: itemSize From 8172b901cd3c7fe7c686bd8f0f314ebf8a065c15 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:01:27 +0800 Subject: [PATCH 02/17] feat(settings): Integrate tray blacklist with global settings --- Assets/settings-default.json | 1 + Commons/Settings.qml | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/Assets/settings-default.json b/Assets/settings-default.json index a80e2bb4..d7791e57 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -9,6 +9,7 @@ "floating": false, "marginVertical": 0.25, "marginHorizontal": 0.25, + "trayBlacklist": [], "widgets": { "left": [ { diff --git a/Commons/Settings.qml b/Commons/Settings.qml index c0939caf..04e9f507 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -33,6 +33,7 @@ Singleton { // Signal emitted when settings are loaded after startupcale changes signal settingsLoaded + signal settingsSaved // ----------------------------------------------------- // ----------------------------------------------------- @@ -76,6 +77,7 @@ Singleton { if (Quickshell.env("NOCTALIA_SETTINGS_FALLBACK")) { settingsFallbackFileView.writeAdapter() } + root.settingsSaved() // Emit signal after saving } } @@ -142,6 +144,7 @@ Singleton { property bool floating: false property real marginVertical: 0.25 property real marginHorizontal: 0.25 + property list trayBlacklist: [] // Widget configuration for modular bar system property JsonObject widgets @@ -351,6 +354,17 @@ Singleton { } } + // ----------------------------------------------------- + // Public function to trigger immediate settings saving + function saveImmediate() { + settingsFileView.writeAdapter() + // Write to fallback location if set + if (Quickshell.env("NOCTALIA_SETTINGS_FALLBACK")) { + settingsFallbackFileView.writeAdapter() + } + root.settingsSaved() // Emit signal after saving + } + // ----------------------------------------------------- // Generate default settings at the root of the repo function generateDefaultSettings() { From 85043d537047b5fc01feccf20071dc8c034004db Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:01:44 +0800 Subject: [PATCH 03/17] feat(ui): Implement Tray widget settings UI --- .../Settings/Bar/BarWidgetSettingsDialog.qml | 10 +- .../Bar/WidgetSettings/TraySettings.qml | 111 ++++++++++++++++++ Services/BarWidgetRegistry.qml | 3 + 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 Modules/Settings/Bar/WidgetSettings/TraySettings.qml diff --git a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml index 1c2a1aaf..06729c66 100644 --- a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml +++ b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml @@ -110,7 +110,12 @@ Popup { onClicked: { if (settingsLoader.item && settingsLoader.item.saveSettings) { var newSettings = settingsLoader.item.saveSettings() - root.updateWidgetSettings(sectionId, widgetSettings.widgetIndex, newSettings) + if (widgetSettings.widgetId === "Tray") { + Settings.data.bar.trayBlacklist = newSettings.blacklist || [] + Settings.saveImmediate() + } else { + root.updateWidgetSettings(sectionId, widgetSettings.widgetIndex, newSettings) + } widgetSettings.close() } } @@ -134,7 +139,8 @@ Popup { "SystemMonitor": "WidgetSettings/SystemMonitorSettings.qml", "Volume": "WidgetSettings/VolumeSettings.qml", "Workspace": "WidgetSettings/WorkspaceSettings.qml", - "Taskbar": "WidgetSettings/TaskbarSettings.qml" + "Taskbar": "WidgetSettings/TaskbarSettings.qml", + "Tray": "WidgetSettings/TraySettings.qml" } const source = widgetSettingsMap[widgetId] diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml new file mode 100644 index 00000000..f0efd28b --- /dev/null +++ b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml @@ -0,0 +1,111 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import qs.Commons +import qs.Widgets + +ColumnLayout { + // Properties to receive data from parent + property var widgetData: ({}) // Expected by BarWidgetSettingsDialog + property var widgetMetadata: ({}) // Expected by BarWidgetSettingsDialog + + // Local state for the blacklist + property var localBlacklist: widgetData.blacklist || Settings.data.bar.trayBlacklist || [] + + ListModel { + id: blacklistModel + } + + Component.onCompleted: { + // Populate the ListModel from localBlacklist + for (var i = 0; i < localBlacklist.length; i++) { + blacklistModel.append({"rule": localBlacklist[i]}) + } + } + + spacing: Style.marginM * scaling + + // Input for new blacklist items + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS * scaling + + NTextInput { + id: newRuleInput + Layout.fillWidth: true + placeholderText: I18n.tr("settings.bar.widget-settings.tray.blacklist.placeholder") + } + + NIconButton { + icon: "add" + enabled: newRuleInput.text.length > 0 + onClicked: { + if (newRuleInput.text.length > 0) { + var newRule = newRuleInput.text.trim() + var exists = false + for (var i = 0; i < blacklistModel.count; i++) { + if (blacklistModel.get(i).rule === newRule) { + exists = true + break + } + } + if (!exists) { + blacklistModel.append({"rule": newRule}) + newRuleInput.text = "" + } + } + } + } + } + + // List of current blacklist items + ListView { + Layout.fillWidth: true + Layout.preferredHeight: 150 * scaling + clip: true + model: blacklistModel + delegate: Rectangle { + width: ListView.width + height: 40 * scaling + color: Color.transparent // Make background transparent + visible: model.rule !== undefined && model.rule !== "" // Only visible if rule exists + + RowLayout { + anchors.fill: parent + anchors.leftMargin: Style.marginM * scaling + anchors.rightMargin: Style.marginS * scaling + spacing: Style.marginS * scaling + + NText { + Layout.fillWidth: true + text: model.rule + elide: Text.ElideRight + } + + NIconButton { + Layout.alignment: Qt.AlignRight + icon: "close" + baseSize: 24 * scaling + colorBg: Color.transparent + colorFg: Color.mError + onClicked: { + blacklistModel.remove(index) + } + } + } + } + } + + // This function will be called by the dialog to get the new settings + function saveSettings() { + var newBlacklist = [] + for (var i = 0; i < blacklistModel.count; i++) { + newBlacklist.push(blacklistModel.get(i).rule) + } + + // Return the updated settings for this widget instance + var settings = Object.assign({}, widgetData || {}) + settings.blacklist = newBlacklist + return settings + } +} \ No newline at end of file diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index cddb544a..799a5ce0 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -116,6 +116,9 @@ Singleton { "onlySameOutput": true, "onlyActiveWorkspaces": true }, + "Tray": { + "allowUserSettings": true + }, "Workspace": { "allowUserSettings": true, "labelMode": "index", From c986b3426864e45a29ea87be95548442f272cd0d Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:26:17 +0800 Subject: [PATCH 04/17] feat(i18n): Add English translations for tray blacklist --- Assets/Translations/en.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/Translations/en.json b/Assets/Translations/en.json index ff91166a..7cc99f0b 100644 --- a/Assets/Translations/en.json +++ b/Assets/Translations/en.json @@ -269,6 +269,13 @@ "label": "Monitors display", "description": "Show bar on specific monitors. Defaults to all if none are chosen." } + }, + "tray": { + "blacklist": { + "label": "Blacklist", + "description": "Add tray exclusion rules, supports wildcards (*).", + "placeholder": "e.g., nm-applet, Fcitx*" + } } }, "dock": { From 27cacdff17e99b19b3f98a1c38edc37d7f4b182d Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:41:24 +0800 Subject: [PATCH 05/17] feat(i18n): update tray blacklist translation in German --- Assets/Translations/de.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/Translations/de.json b/Assets/Translations/de.json index 021a823f..49612882 100644 --- a/Assets/Translations/de.json +++ b/Assets/Translations/de.json @@ -271,6 +271,13 @@ "label": "Nur Apps vom gleichen Bildschirm", "description": "Zeige nur Apps vom dem Bildschirm an, wo sich das Dock befindet." } + }, + "tray": { + "blacklist": { + "label": "Ausschlussliste", + "description": "Füge Ausschlussregeln für die Tray-Symbolleiste hinzu, unterstützt Platzhalter (*).", + "placeholder": "z.B., nm-applet, Fcitx*" + } } }, "dock": { From b406f1ecf218f5fa8b8904ad8bd6f5fb72f04fc8 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:42:27 +0800 Subject: [PATCH 06/17] feat(i18n): update tray blacklist translation in Spanish --- Assets/Translations/es.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/Translations/es.json b/Assets/Translations/es.json index f33d2e0c..cfd6174c 100644 --- a/Assets/Translations/es.json +++ b/Assets/Translations/es.json @@ -267,6 +267,13 @@ "label": "Visualización en monitores", "description": "Muestra la barra en monitores específicos. Por defecto, se muestra en todos si no se elige ninguno." } + }, + "tray": { + "blacklist": { + "label": "Lista negra", + "description": "Agregar reglas de exclusión de la bandeja, admite comodines (*).", + "placeholder": "ej., nm-applet, Fcitx*" + } } }, "dock": { From cc20a7f7337e582243c2e5caf1964304b4ee41a1 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:43:05 +0800 Subject: [PATCH 07/17] feat(i18n): update tray blacklist translation in French --- Assets/Translations/fr.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Assets/Translations/fr.json b/Assets/Translations/fr.json index a087896a..83689471 100644 --- a/Assets/Translations/fr.json +++ b/Assets/Translations/fr.json @@ -264,9 +264,16 @@ }, "monitors": { "section": { - "label": "Affichage sur les moniteur", + "label": "Affichage sur les moniteurs", "description": "Afficher la barre sur des moniteurs spécifiques. Par défaut, sur tous si aucun n'est choisi." } + }, + "tray": { + "blacklist": { + "label": "Liste noire", + "description": "Ajouter des règles d'exclusion pour la boîte à miniatures, prend en charge les caractères génériques (*).", + "placeholder": "ex: nm-applet, Fcitx*" + } } }, "dock": { From d1d70ca428333525d6a4187dade8967a9f48841e Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:43:45 +0800 Subject: [PATCH 08/17] feat(i18n): update tray blacklist translation in Portuguese --- Assets/Translations/pt.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/Translations/pt.json b/Assets/Translations/pt.json index d9b1d538..c27bb52e 100644 --- a/Assets/Translations/pt.json +++ b/Assets/Translations/pt.json @@ -267,6 +267,13 @@ "label": "Exibição nos monitores", "description": "Mostra a barra em monitores específicos. O padrão é todos, se nenhum for escolhido." } + }, + "tray": { + "blacklist": { + "label": "Lista Negra", + "description": "Adicione regras de exclusão para a bandeja do sistema, suporta curingas (*).", + "placeholder": "ex: nm-applet, Fcitx*" + } } }, "dock": { From 8cb9a5082e7b04af5c4cf223fec07bbb76de7e9b Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:44:31 +0800 Subject: [PATCH 09/17] feat(i18n): update tray blacklist translation in Chinese Simplified --- Assets/Translations/zh-CN.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Assets/Translations/zh-CN.json b/Assets/Translations/zh-CN.json index 8bdfbce4..e35bb440 100644 --- a/Assets/Translations/zh-CN.json +++ b/Assets/Translations/zh-CN.json @@ -267,6 +267,13 @@ "label": "显示器显示", "description": "在特定显示器上显示状态栏。如果未选择,则默认为全部。" } + }, + "tray": { + "blacklist": { + "label": "黑名单", + "description": "添加托盘排除规则,支持通配符 (*)。", + "placeholder": "例如:nm-applet, Fcitx*" + } } }, "dock": { From 5de6560d421d02a05b6f688f825cd37c2ebdc9d9 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 01:57:17 +0800 Subject: [PATCH 10/17] fix(ui): Correct TraySettings label and description --- .../Settings/Bar/WidgetSettings/TraySettings.qml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml index f0efd28b..40019e80 100644 --- a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml @@ -30,10 +30,12 @@ ColumnLayout { Layout.fillWidth: true spacing: Style.marginS * scaling - NTextInput { - id: newRuleInput - Layout.fillWidth: true - placeholderText: I18n.tr("settings.bar.widget-settings.tray.blacklist.placeholder") + NTextInput { + id: newRuleInput + Layout.fillWidth: true + label: I18n.tr("settings.bar.tray.blacklist.label") + description: I18n.tr("settings.bar.tray.blacklist.description") + placeholderText: I18n.tr("settings.bar.tray.blacklist.placeholder") } NIconButton { @@ -69,7 +71,7 @@ ColumnLayout { height: 40 * scaling color: Color.transparent // Make background transparent visible: model.rule !== undefined && model.rule !== "" // Only visible if rule exists - + RowLayout { anchors.fill: parent anchors.leftMargin: Style.marginM * scaling @@ -108,4 +110,4 @@ ColumnLayout { settings.blacklist = newBlacklist return settings } -} \ No newline at end of file +} From b30879b38d28978ee042a317ff4d2c1319255842 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 03:22:06 +0800 Subject: [PATCH 11/17] Fix: Adjust tray module and icon size --- Modules/Bar/Widgets/Tray.qml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 85e42afc..acc11dc7 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -19,7 +19,7 @@ Rectangle { readonly property string barPosition: Settings.data.bar.position readonly property bool isVertical: barPosition === "left" || barPosition === "right" readonly property bool compact: (Settings.data.bar.density === "compact") - property real itemSize: isVertical ? Math.round(width * 0.7) : Math.round(height * 0.7) + property real itemSize: Math.round(Style.capsuleHeight * 0.65 * scaling) property list blacklist: Settings.data.bar.trayBlacklist || [] // Read from settings property var filteredItems: [] @@ -91,8 +91,8 @@ Rectangle { } visible: filteredItems.length > 0 - implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : (trayFlow.implicitWidth + Style.marginS * scaling * 2) - implicitHeight: isVertical ? (trayFlow.implicitHeight + Style.marginS * scaling * 2) : Math.round(Style.capsuleHeight * scaling) + implicitWidth: isVertical ? Math.round(Style.capsuleHeight * scaling) : (trayFlow.implicitWidth + Style.marginM * 2 * scaling) + implicitHeight: isVertical ? (trayFlow.implicitHeight + Style.marginM * 2 * scaling) : Math.round(Style.capsuleHeight * scaling) radius: Math.round(Style.radiusM * scaling) color: Settings.data.bar.showCapsule ? Color.mSurfaceVariant : Color.transparent From 2f2bcdebc88add2d1db61d332b7ae3b51204be73 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 03:34:09 +0800 Subject: [PATCH 12/17] feat: Add custom settings and blacklist for Tray module --- Modules/Bar/Widgets/Tray.qml | 19 ++++++++++++++++++- .../Settings/Bar/BarWidgetSettingsDialog.qml | 7 +------ Services/BarWidgetRegistry.qml | 3 ++- 3 files changed, 21 insertions(+), 8 deletions(-) diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index acc11dc7..03750fe1 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -16,11 +16,28 @@ Rectangle { property ShellScreen screen property real scaling: 1.0 + // Widget properties passed from Bar.qml for per-instance settings + property string widgetId: "" + property string section: "" + property int sectionWidgetIndex: -1 + property int sectionWidgetsCount: 0 + + property var widgetMetadata: BarWidgetRegistry.widgetMetadata[widgetId] + property var widgetSettings: { + if (section && sectionWidgetIndex >= 0) { + var widgets = Settings.data.bar.widgets[section] + if (widgets && sectionWidgetIndex < widgets.length) { + return widgets[sectionWidgetIndex] + } + } + return {} + } + readonly property string barPosition: Settings.data.bar.position readonly property bool isVertical: barPosition === "left" || barPosition === "right" readonly property bool compact: (Settings.data.bar.density === "compact") property real itemSize: Math.round(Style.capsuleHeight * 0.65 * scaling) - property list blacklist: Settings.data.bar.trayBlacklist || [] // Read from settings + property list blacklist: widgetSettings.blacklist || Settings.data.bar.trayBlacklist || [] // Read from settings property var filteredItems: [] function wildCardMatch(str, rule) { diff --git a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml index 06729c66..4d31f7d0 100644 --- a/Modules/Settings/Bar/BarWidgetSettingsDialog.qml +++ b/Modules/Settings/Bar/BarWidgetSettingsDialog.qml @@ -110,12 +110,7 @@ Popup { onClicked: { if (settingsLoader.item && settingsLoader.item.saveSettings) { var newSettings = settingsLoader.item.saveSettings() - if (widgetSettings.widgetId === "Tray") { - Settings.data.bar.trayBlacklist = newSettings.blacklist || [] - Settings.saveImmediate() - } else { - root.updateWidgetSettings(sectionId, widgetSettings.widgetIndex, newSettings) - } + root.updateWidgetSettings(sectionId, widgetSettings.widgetIndex, newSettings) widgetSettings.close() } } diff --git a/Services/BarWidgetRegistry.qml b/Services/BarWidgetRegistry.qml index 799a5ce0..efb85537 100644 --- a/Services/BarWidgetRegistry.qml +++ b/Services/BarWidgetRegistry.qml @@ -117,7 +117,8 @@ Singleton { "onlyActiveWorkspaces": true }, "Tray": { - "allowUserSettings": true + "allowUserSettings": true, + "blacklist": [] }, "Workspace": { "allowUserSettings": true, From 8915de4673970dd179d4c1495aa724b2bbe7cb5b Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 03:36:28 +0800 Subject: [PATCH 13/17] refactor: Use saveImmediate() in Settings.qml saveTimer --- Commons/Settings.qml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 04e9f507..4bd97d90 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -72,12 +72,7 @@ Singleton { running: false interval: 1000 onTriggered: { - settingsFileView.writeAdapter() - // Write to fallback location if set - if (Quickshell.env("NOCTALIA_SETTINGS_FALLBACK")) { - settingsFallbackFileView.writeAdapter() - } - root.settingsSaved() // Emit signal after saving + root.saveImmediate() } } From f47216033eae1296455063e1380eb77ce9bc6dae Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 05:00:13 +0800 Subject: [PATCH 14/17] feat(tray): Remove global trayBlacklist --- Assets/settings-default.json | 2 +- Commons/Settings.qml | 2 +- Modules/Bar/Widgets/Tray.qml | 2 +- Modules/Settings/Bar/WidgetSettings/TraySettings.qml | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Assets/settings-default.json b/Assets/settings-default.json index d7791e57..7d607878 100644 --- a/Assets/settings-default.json +++ b/Assets/settings-default.json @@ -9,7 +9,7 @@ "floating": false, "marginVertical": 0.25, "marginHorizontal": 0.25, - "trayBlacklist": [], + "widgets": { "left": [ { diff --git a/Commons/Settings.qml b/Commons/Settings.qml index 4bd97d90..6867a2c0 100644 --- a/Commons/Settings.qml +++ b/Commons/Settings.qml @@ -139,7 +139,7 @@ Singleton { property bool floating: false property real marginVertical: 0.25 property real marginHorizontal: 0.25 - property list trayBlacklist: [] + // Widget configuration for modular bar system property JsonObject widgets diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 03750fe1..8361a1d6 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -37,7 +37,7 @@ Rectangle { readonly property bool isVertical: barPosition === "left" || barPosition === "right" readonly property bool compact: (Settings.data.bar.density === "compact") property real itemSize: Math.round(Style.capsuleHeight * 0.65 * scaling) - property list blacklist: widgetSettings.blacklist || Settings.data.bar.trayBlacklist || [] // Read from settings + property list blacklist: widgetSettings.blacklist || widgetMetadata.blacklist || [] // Read from settings property var filteredItems: [] function wildCardMatch(str, rule) { diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml index 40019e80..dca3ea10 100644 --- a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml @@ -10,7 +10,7 @@ ColumnLayout { property var widgetMetadata: ({}) // Expected by BarWidgetSettingsDialog // Local state for the blacklist - property var localBlacklist: widgetData.blacklist || Settings.data.bar.trayBlacklist || [] + property var localBlacklist: widgetData.blacklist || [] ListModel { id: blacklistModel From 1455c84b0ccf8ea874280576821cb721260f89b2 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 05:16:43 +0800 Subject: [PATCH 15/17] fix(tray): Improving regex escaping logic. --- Modules/Bar/Widgets/Tray.qml | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 8361a1d6..88bd9f6b 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -41,7 +41,28 @@ Rectangle { property var filteredItems: [] function wildCardMatch(str, rule) { - return str.toLowerCase().includes(rule.toLowerCase()); // Simple substring match + if (!str || !rule) { + return false; + } + Logger.log("Tray", "wildCardMatch - Input str:", str, "rule:", rule); + + // Escape all special regex characters in the rule + let escapedRule = rule.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + // Convert '*' to '.*' for wildcard matching + let pattern = escapedRule.replace(/\\\*/g, '.*'); + // Add ^ and $ to match the entire string + pattern = '^' + pattern + '$'; + + Logger.log("Tray", "wildCardMatch - Generated pattern:", pattern); + + try { + const regex = new RegExp(pattern, 'i'); // 'i' for case-insensitive + Logger.log("Tray", "wildCardMatch - Regex test result:", regex.test(str)); + return regex.test(str); + } catch (e) { + Logger.warn("Tray", "Invalid regex pattern for wildcard match:", rule, e.message); + return false; // If regex is invalid, it won't match + } } function updateFilteredItems() { From 20c54e292ffbd531e7547559fefab57c6e1d861b Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 07:17:11 +0800 Subject: [PATCH 16/17] feat(tray): Implement debouncing for tray item updates --- Modules/Bar/Widgets/Tray.qml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Modules/Bar/Widgets/Tray.qml b/Modules/Bar/Widgets/Tray.qml index 88bd9f6b..b940d267 100644 --- a/Modules/Bar/Widgets/Tray.qml +++ b/Modules/Bar/Widgets/Tray.qml @@ -65,7 +65,18 @@ Rectangle { } } - function updateFilteredItems() { + // Debounce timer for updateFilteredItems to prevent excessive calls + // when multiple events (e.g., SystemTray changes, settings saves) + // trigger it in rapid succession, reducing redundant processing. + Timer { + id: updateDebounceTimer + interval: 100 // milliseconds + running: false + repeat: false + onTriggered: _performFilteredItemsUpdate() + } + + function _performFilteredItemsUpdate() { if (!root.blacklist || root.blacklist.length === 0) { if (SystemTray.items && SystemTray.items.values) { filteredItems = SystemTray.items.values @@ -103,6 +114,10 @@ Rectangle { filteredItems = newItems } + function updateFilteredItems() { + updateDebounceTimer.restart() + } + function onLoaded() { // When the widget is fully initialized with its props set the screen for the trayMenu if (trayMenu.item) { From 2bcdcb1e9e5a3451619a73c4fd6f3d6f1f0ee910 Mon Sep 17 00:00:00 2001 From: loner <2788892716@qq.com> Date: Fri, 10 Oct 2025 09:25:15 +0800 Subject: [PATCH 17/17] refactor(tray): refactor blacklist UI layout and fix display issues --- .../Bar/WidgetSettings/TraySettings.qml | 89 ++++++++++++------- 1 file changed, 56 insertions(+), 33 deletions(-) diff --git a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml index dca3ea10..77f00d5d 100644 --- a/Modules/Settings/Bar/WidgetSettings/TraySettings.qml +++ b/Modules/Settings/Bar/WidgetSettings/TraySettings.qml @@ -25,37 +25,46 @@ ColumnLayout { spacing: Style.marginM * scaling - // Input for new blacklist items - RowLayout { + ColumnLayout { Layout.fillWidth: true spacing: Style.marginS * scaling - NTextInput { - id: newRuleInput - Layout.fillWidth: true - label: I18n.tr("settings.bar.tray.blacklist.label") - description: I18n.tr("settings.bar.tray.blacklist.description") - placeholderText: I18n.tr("settings.bar.tray.blacklist.placeholder") + NLabel { + label: I18n.tr("settings.bar.tray.blacklist.label") + description: I18n.tr("settings.bar.tray.blacklist.description") } - NIconButton { - icon: "add" - enabled: newRuleInput.text.length > 0 - onClicked: { - if (newRuleInput.text.length > 0) { - var newRule = newRuleInput.text.trim() - var exists = false - for (var i = 0; i < blacklistModel.count; i++) { - if (blacklistModel.get(i).rule === newRule) { - exists = true - break + RowLayout { + Layout.fillWidth: true + spacing: Style.marginS * scaling + + NTextInput { + id: newRuleInput + Layout.fillWidth: true + placeholderText: I18n.tr("settings.bar.tray.blacklist.placeholder") + } + + NIconButton { + Layout.alignment: Qt.AlignVCenter + icon: "add" + baseSize: Style.baseWidgetSize * 0.8 * scaling + onClicked: { + if (newRuleInput.text.length > 0) { + var newRule = newRuleInput.text.trim() + var exists = false + for (var i = 0; i < blacklistModel.count; i++) { + if (blacklistModel.get(i).rule === newRule) { + exists = true + break + } + } + if (!exists) { + blacklistModel.append({"rule": newRule}) + newRuleInput.text = "" } } - if (!exists) { - blacklistModel.append({"rule": newRule}) - newRuleInput.text = "" - } } + enabled: newRuleInput.text.length > 0 } } } @@ -64,32 +73,46 @@ ColumnLayout { ListView { Layout.fillWidth: true Layout.preferredHeight: 150 * scaling + Layout.topMargin: Style.marginL * scaling // Increased top margin clip: true model: blacklistModel - delegate: Rectangle { + delegate: Item { width: ListView.width height: 40 * scaling - color: Color.transparent // Make background transparent - visible: model.rule !== undefined && model.rule !== "" // Only visible if rule exists - RowLayout { + Rectangle { + id: itemBackground anchors.fill: parent - anchors.leftMargin: Style.marginM * scaling + anchors.margins: Style.marginXS * scaling + color: Color.transparent // Make background transparent + border.color: Color.mOutline + border.width: Math.max(1, Style.borderS * scaling) + radius: Style.radiusS * scaling + visible: model.rule !== undefined && model.rule !== "" // Only visible if rule exists + } + + Row { + anchors.fill: parent + anchors.leftMargin: Style.marginS * scaling anchors.rightMargin: Style.marginS * scaling spacing: Style.marginS * scaling NText { - Layout.fillWidth: true text: model.rule elide: Text.ElideRight + verticalAlignment: Text.AlignVCenter + Layout.fillWidth: true } NIconButton { - Layout.alignment: Qt.AlignRight + width: 16 * scaling + height: 16 * scaling icon: "close" - baseSize: 24 * scaling - colorBg: Color.transparent - colorFg: Color.mError + baseSize: 8 * scaling + colorBg: Color.mSurfaceVariant + colorFg: Color.mOnSurface + colorBgHover: Color.mError + colorFgHover: Color.mOnError onClicked: { blacklistModel.remove(index) }