From c0d17672af1a9ec06222d5d99a1f716075677a10 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Mon, 26 Jan 2026 15:30:33 -0800 Subject: [PATCH] feat(avalonia): add Avalonia settings views for 8 plugins - Add i18n injection to Application.Resources for DynamicResource bindings - Create Avalonia settings views for plugins: - Explorer (ExplorerSettings + QuickAccessLinkSettings dialog) - BrowserBookmark (SettingsControl + CustomBrowserSettingWindow) - Calculator (CalculatorSettings) - ProcessKiller (SettingsControl) - PluginsManager (PluginsManagerSettings) - WebSearch (SettingsControl) - Shell (ShellSetting + converter) - Program (ProgramSetting) - Add IsAvalonia detection pattern for dual-framework dialog support - Update 11 plugin .csproj files with CopyToAvaloniaOutput targets - Add System.Threading.Tasks using for async RelayCommand support --- Flow.Launcher.Avalonia/App.axaml.cs | 4 + .../Resource/Internationalization.cs | 37 +- ...low.Launcher.Plugin.BrowserBookmark.csproj | 15 +- .../Main.cs | 8 +- .../Avalonia/CustomBrowserSettingWindow.axaml | 46 +++ .../CustomBrowserSettingWindow.axaml.cs | 58 ++++ .../Views/Avalonia/SettingsControl.axaml | 81 +++++ .../Views/Avalonia/SettingsControl.axaml.cs | 130 +++++++ .../Flow.Launcher.Plugin.Calculator.csproj | 15 +- .../Flow.Launcher.Plugin.Calculator/Main.cs | 8 +- .../Views/Avalonia/CalculatorSettings.axaml | 43 +++ .../Avalonia/CalculatorSettings.axaml.cs | 24 ++ .../Flow.Launcher.Plugin.Explorer.csproj | 15 +- Plugins/Flow.Launcher.Plugin.Explorer/Main.cs | 8 +- .../ViewModels/SettingsViewModel.cs | 56 ++- .../Views/Avalonia/ExplorerSettings.axaml | 320 ++++++++++++++++++ .../Views/Avalonia/ExplorerSettings.axaml.cs | 85 +++++ .../Avalonia/QuickAccessLinkSettings.axaml | 86 +++++ .../Avalonia/QuickAccessLinkSettings.axaml.cs | 240 +++++++++++++ ...low.Launcher.Plugin.PluginIndicator.csproj | 13 +- ...Flow.Launcher.Plugin.PluginsManager.csproj | 15 +- .../Main.cs | 11 +- .../Avalonia/PluginsManagerSettings.axaml | 29 ++ .../Avalonia/PluginsManagerSettings.axaml.cs | 24 ++ .../Flow.Launcher.Plugin.ProcessKiller.csproj | 18 +- .../Main.cs | 9 +- .../Views/Avalonia/SettingsControl.axaml | 29 ++ .../Views/Avalonia/SettingsControl.axaml.cs | 24 ++ .../Flow.Launcher.Plugin.Program.csproj | 11 + ...nOrCloseShellAfterPressEnabledConverter.cs | 22 ++ .../Flow.Launcher.Plugin.Shell.csproj | 15 +- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 8 +- .../Views/Avalonia/ShellSetting.axaml | 91 +++++ .../Views/Avalonia/ShellSetting.axaml.cs | 25 ++ .../Flow.Launcher.Plugin.Sys.csproj | 14 +- .../Flow.Launcher.Plugin.Url.csproj | 13 +- .../Flow.Launcher.Plugin.WebSearch.csproj | 18 +- .../Flow.Launcher.Plugin.WebSearch/Main.cs | 6 + .../Views/Avalonia/SettingsControl.axaml | 65 ++++ .../Views/Avalonia/SettingsControl.axaml.cs | 87 +++++ ...low.Launcher.Plugin.WindowsSettings.csproj | 13 +- 41 files changed, 1810 insertions(+), 29 deletions(-) create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/CustomBrowserSettingWindow.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/CustomBrowserSettingWindow.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/SettingsControl.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/SettingsControl.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Calculator/Views/Avalonia/CalculatorSettings.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.Calculator/Views/Avalonia/CalculatorSettings.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/Avalonia/ExplorerSettings.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/Avalonia/ExplorerSettings.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/Avalonia/QuickAccessLinkSettings.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.Explorer/Views/Avalonia/QuickAccessLinkSettings.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Views/Avalonia/PluginsManagerSettings.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.PluginsManager/Views/Avalonia/PluginsManagerSettings.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/Avalonia/SettingsControl.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.ProcessKiller/Views/Avalonia/SettingsControl.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Shell/Converters/Avalonia/LeaveShellOpenOrCloseShellAfterPressEnabledConverter.cs create mode 100644 Plugins/Flow.Launcher.Plugin.Shell/Views/Avalonia/ShellSetting.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.Shell/Views/Avalonia/ShellSetting.axaml.cs create mode 100644 Plugins/Flow.Launcher.Plugin.WebSearch/Views/Avalonia/SettingsControl.axaml create mode 100644 Plugins/Flow.Launcher.Plugin.WebSearch/Views/Avalonia/SettingsControl.axaml.cs diff --git a/Flow.Launcher.Avalonia/App.axaml.cs b/Flow.Launcher.Avalonia/App.axaml.cs index 14919a92c..4afb52982 100644 --- a/Flow.Launcher.Avalonia/App.axaml.cs +++ b/Flow.Launcher.Avalonia/App.axaml.cs @@ -36,6 +36,10 @@ public partial class App : Application ConfigureDI(); AvaloniaXamlLoader.Load(this); + + // Inject translations into Application.Resources for DynamicResource bindings in plugins + var i18n = Ioc.Default.GetRequiredService(); + i18n.InjectIntoApplicationResources(); } public override void OnFrameworkInitializationCompleted() diff --git a/Flow.Launcher.Avalonia/Resource/Internationalization.cs b/Flow.Launcher.Avalonia/Resource/Internationalization.cs index 0705eca14..c783714f7 100644 --- a/Flow.Launcher.Avalonia/Resource/Internationalization.cs +++ b/Flow.Launcher.Avalonia/Resource/Internationalization.cs @@ -201,6 +201,37 @@ public class Internationalization Log.Debug(ClassName, $"Parsed {count} strings from {filePath}"); } + /// + /// Inject all translations into Avalonia's Application.Resources so {DynamicResource} works. + /// This enables plugin settings to use {DynamicResource key} for localization. + /// Must be called after Application is initialized. + /// + public void InjectIntoApplicationResources() + { + try + { + var app = Application.Current; + if (app == null) + { + Log.Warn(ClassName, "Cannot inject resources: Application.Current is null"); + return; + } + + var count = 0; + foreach (var kvp in _translations) + { + app.Resources[kvp.Key] = kvp.Value; + count++; + } + + Log.Info(ClassName, $"Injected {count} translations into Application.Resources"); + } + catch (Exception e) + { + Log.Exception(ClassName, "Failed to inject translations into Application.Resources", e); + } + } + /// /// Get a translated string by key. /// @@ -208,12 +239,10 @@ public class Internationalization { if (_translations.TryGetValue(key, out var translation)) { - Log.Debug(ClassName, $"Translation found for '{key}': '{translation}'"); return translation; } Log.Warn(ClassName, $"Translation not found for key: {key}"); - Log.Debug(ClassName, $"Available keys (first 20): {string.Join(", ", _translations.Keys.Take(20))}"); return $"[{key}]"; } @@ -260,6 +289,10 @@ public class Internationalization } ChangeCultureInfo(languageCode); + + // Re-inject into Application.Resources for DynamicResource bindings + InjectIntoApplicationResources(); + Log.Info(ClassName, $"Language changed to: {languageCode}"); } } diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj index 8249b354a..03ff64eb6 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -1,4 +1,4 @@ - + Library @@ -108,6 +108,19 @@ + + + + + + ..\..\Output\$(Configuration)\Avalonia\Plugins\$(AssemblyName)\ + + + + + + + diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs index 07ce510fb..2545dabbc 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Main.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Linq; @@ -10,6 +10,7 @@ using Flow.Launcher.Plugin.BrowserBookmark.Commands; using Flow.Launcher.Plugin.BrowserBookmark.Models; using Flow.Launcher.Plugin.BrowserBookmark.Views; using Flow.Launcher.Plugin.SharedCommands; +using AvaloniaControl = Avalonia.Controls.Control; namespace Flow.Launcher.Plugin.BrowserBookmark; @@ -225,6 +226,11 @@ public class Main : ISettingProvider, IPlugin, IReloadable, IPluginI18n, IContex return new SettingsControl(_settings); } + public AvaloniaControl CreateSettingPanelAvalonia() + { + return new Views.Avalonia.SettingsControl(_settings); + } + public List LoadContextMenus(Result selectedResult) { return new List() diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/CustomBrowserSettingWindow.axaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/CustomBrowserSettingWindow.axaml new file mode 100644 index 000000000..8015eb927 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/Avalonia/CustomBrowserSettingWindow.axaml @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + +