diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj index c7a72218907..bba46638487 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj +++ b/Plugins/Flow.Launcher.Plugin.Sys/Flow.Launcher.Plugin.Sys.csproj @@ -38,6 +38,7 @@ + diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png b/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png new file mode 100644 index 00000000000..704e9474eb2 Binary files /dev/null and b/Plugins/Flow.Launcher.Plugin.Sys/Images/theme_selector.png differ diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml index 91f32a844f8..2a266f8f651 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml @@ -27,6 +27,7 @@ Flow Launcher Tips Flow Launcher UserData Folder Toggle Game Mode + Set the Flow Launcher Theme Shutdown Computer @@ -49,8 +50,9 @@ Visit Flow Launcher's documentation for more help and how to use tips Open the location where Flow Launcher's settings are stored Toggle Game Mode + Quickly change the Flow Launcher theme - + Success All Flow Launcher settings saved Reloaded all applicable plugin data diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs index 1ec07915d2b..0dbb46be9f2 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Sys/Main.cs @@ -18,9 +18,10 @@ namespace Flow.Launcher.Plugin.Sys { - public class Main : IPlugin, ISettingProvider, IPluginI18n + public class Main : IPlugin, ISettingProvider, IPluginI18n, IDisposable { private PluginInitContext context; + private ThemeSelector themeSelector; private Dictionary KeywordTitleMappings = new Dictionary(); #region DllImport @@ -58,6 +59,11 @@ public Control CreateSettingPanel() public List Query(Query query) { + if(query.Search.StartsWith(ThemeSelector.Keyword)) + { + return themeSelector.Query(query); + } + var commands = Commands(); var results = new List(); foreach (var c in commands) @@ -106,6 +112,7 @@ private string GetDynamicTitle(Query query, Result result) public void Init(PluginInitContext context) { this.context = context; + themeSelector = new ThemeSelector(context); KeywordTitleMappings = new Dictionary{ {"Shutdown", "flowlauncher_plugin_sys_shutdown_computer_cmd"}, {"Restart", "flowlauncher_plugin_sys_restart_computer_cmd"}, @@ -126,7 +133,8 @@ public void Init(PluginInitContext context) {"Open Log Location", "flowlauncher_plugin_sys_open_log_location_cmd"}, {"Flow Launcher Tips", "flowlauncher_plugin_sys_open_docs_tips_cmd"}, {"Flow Launcher UserData Folder", "flowlauncher_plugin_sys_open_userdata_location_cmd"}, - {"Toggle Game Mode", "flowlauncher_plugin_sys_toggle_game_mode_cmd"} + {"Toggle Game Mode", "flowlauncher_plugin_sys_toggle_game_mode_cmd"}, + {"Set Flow Launcher Theme", "flowlauncher_plugin_sys_theme_selector_cmd"} }; } @@ -426,6 +434,18 @@ private List Commands() context.API.ToggleGameMode(); return true; } + }, + new Result + { + Title = "Set Flow Launcher Theme", + SubTitle = context.API.GetTranslation("flowlauncher_plugin_sys_theme_selector"), + IcoPath = "Images\\theme_selector.png", + Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"), + Action = c => + { + context.API.ChangeQuery($"{ThemeSelector.Keyword} "); + return false; + } } }); @@ -441,5 +461,10 @@ public string GetTranslatedPluginDescription() { return context.API.GetTranslation("flowlauncher_plugin_sys_plugin_description"); } + + public void Dispose() + { + themeSelector.Dispose(); + } } } diff --git a/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs new file mode 100644 index 00000000000..75825042153 --- /dev/null +++ b/Plugins/Flow.Launcher.Plugin.Sys/ThemeSelector.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Flow.Launcher.Core.Resource; + +namespace Flow.Launcher.Plugin.Sys +{ + public class ThemeSelector : IDisposable + { + public const string Keyword = "fltheme"; + + private readonly PluginInitContext context; + private IEnumerable themes; + + public ThemeSelector(PluginInitContext context) + { + this.context = context; + context.API.VisibilityChanged += OnVisibilityChanged; + } + + public List Query(Query query) + { + if (query.IsReQuery) + { + LoadThemes(); + } + + string search = query.Search[(query.Search.IndexOf(Keyword, StringComparison.Ordinal) + Keyword.Length + 1)..]; + + if (string.IsNullOrWhiteSpace(search)) + { + return themes.Select(CreateThemeResult) + .OrderBy(x => x.Title) + .ToList(); + } + + return themes.Select(theme => (theme, matchResult: context.API.FuzzySearch(search, theme))) + .Where(x => x.matchResult.IsSearchPrecisionScoreMet()) + .Select(x => CreateThemeResult(x.theme, x.matchResult.Score, x.matchResult.MatchData)) + .OrderBy(x => x.Title) + .ToList(); + } + + private void OnVisibilityChanged(object sender, VisibilityChangedEventArgs args) + { + if (args.IsVisible && !context.CurrentPluginMetadata.Disabled) + { + LoadThemes(); + } + } + + private void LoadThemes() + => themes = ThemeManager.Instance.LoadAvailableThemes().Select(Path.GetFileNameWithoutExtension); + + private static Result CreateThemeResult(string theme) => CreateThemeResult(theme, 0, null); + + private static Result CreateThemeResult(string theme, int score, IList highlightData) + { + string title; + if (theme == ThemeManager.Instance.Settings.Theme) + { + title = $"{theme} ★"; + score = 2000; + } + else + { + title = theme; + } + + return new Result + { + Title = title, + TitleHighlightData = highlightData, + Glyph = new GlyphInfo("/Resources/#Segoe Fluent Icons", "\ue790"), + Score = score, + Action = c => + { + ThemeManager.Instance.ChangeTheme(theme); + return true; + } + }; + } + + public void Dispose() + { + if (context != null && context.API != null) + { + context.API.VisibilityChanged -= OnVisibilityChanged; + } + } + } +}