From 9d5f74ca8f3126d277580905a90aad4605c8e069 Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Mon, 15 Jan 2024 20:33:53 +0100 Subject: [PATCH 001/151] Add sound effect volume Signed-off-by: Florian Grabmeier --- .../UserSettings/Settings.cs | 2 + Flow.Launcher/Languages/en.xaml | 2 + Flow.Launcher/MainWindow.xaml.cs | 7 +- Flow.Launcher/Resources/open.wav | Bin 80116 -> 105884 bytes Flow.Launcher/SettingWindow.xaml | 78 +++++++++++++++--- .../ViewModel/SettingWindowViewModel.cs | 9 ++ 6 files changed, 83 insertions(+), 15 deletions(-) diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index ca1674315..274f88dc6 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -54,6 +54,8 @@ namespace Flow.Launcher.Infrastructure.UserSettings public bool UseGlyphIcons { get; set; } = true; public bool UseAnimation { get; set; } = true; public bool UseSound { get; set; } = true; + public double SoundVolume { get; set; } = 50; + public bool UseClock { get; set; } = true; public bool UseDate { get; set; } = false; public string TimeFormat { get; set; } = "hh:mm tt"; diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index d36a49538..4bc79ccb3 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -156,6 +156,8 @@ Dark Sound Effect Play a small sound when the search window opens + Sound Effect Volume + Adjust the volume of the sound effect Animation Use Animation in UI Animation Speed diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 3a914d488..70765c1dc 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -23,8 +23,7 @@ using System.Windows.Threading; using System.Windows.Data; using ModernWpf.Controls; using Key = System.Windows.Input.Key; -using System.Media; -using static Flow.Launcher.ViewModel.SettingWindowViewModel; +using System.Windows.Media; namespace Flow.Launcher { @@ -39,7 +38,7 @@ namespace Flow.Launcher private ContextMenu contextMenu; private MainViewModel _viewModel; private bool _animating; - SoundPlayer animationSound = new SoundPlayer(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav"); + MediaPlayer animationSound = new MediaPlayer(); #endregion @@ -113,6 +112,8 @@ namespace Flow.Launcher { if (_settings.UseSound) { + animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); + animationSound.Volume = _settings.SoundVolume / 100.0; animationSound.Play(); } UpdatePosition(); diff --git a/Flow.Launcher/Resources/open.wav b/Flow.Launcher/Resources/open.wav index 4f13724f7093ad2d66c595cf8a672b6dabf008ba..0f692c75b377aec4a8682fc978d5d821ee39247b 100644 GIT binary patch literal 105884 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 z zD4b{jQZYLhfi%gnvHGlqUNBt(P=bAR1OoZ5oC02Me_gXOlY)0`VgPfkqORn+WR&1oa); zU?S@Kn&Wm@5IBj0=k&|Zhm^~9?K;5Q(|X7kZT{ZdC5-^zz)DXVY@y(>z5_*{B&jMAan#(tzqg$7(d#mRgXWNC(|>IV);=F(4=Uom_^sBX!olhb($+bRHqd0K7u-ymxAFSPR_Va zd z6li|4@;n;}m>Xud?vKad{~JKQH6tSz@GP{V-^Ud?eOBQT!$l{HqEFjl&Od{0ArDRa ze7sbWjJpv?r-$vJTvTQ|?En=gIyDf5=u79gI*|IEhXXMo`)i`0)EYTB)~9($@xyc3 zHLr&-QsbpD?`!sO2TC*ARoLCK>TsBzss>gA!LWI1rc6xh<*{<^4&0CU#JOgnrftP% zHYogTj(plbJ@t@7+lV4TwV(=pH_t@i0Eo#}vzZ}Kv>l^AmAu຾ST~1}iZ@E(N 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 diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 1e886c022..57b4e0684 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2474,27 +2474,81 @@ - - - - - - - + + + + + + + - +  - - + + + + + + + + + + + + + + + + + + + +  + + + + diff --git a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs index 231e65539..86a25ffb1 100644 --- a/Flow.Launcher/ViewModel/SettingWindowViewModel.cs +++ b/Flow.Launcher/ViewModel/SettingWindowViewModel.cs @@ -63,6 +63,9 @@ namespace Flow.Launcher.ViewModel case nameof(Settings.PreviewHotkey): OnPropertyChanged(nameof(AlwaysPreviewToolTip)); break; + case nameof(Settings.SoundVolume): + OnPropertyChanged(nameof(SoundEffectVolume)); + break; } }; @@ -631,6 +634,12 @@ namespace Flow.Launcher.ViewModel set => Settings.UseSound = value; } + public double SoundEffectVolume + { + get => Settings.SoundVolume; + set => Settings.SoundVolume = value; + } + public bool UseClock { get => Settings.UseClock; From b52b2a06228b353d3b2bbad652ee858793f3dac3 Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 17 Jan 2024 10:21:14 +0100 Subject: [PATCH 002/151] Fix open audio file only once Signed-off-by: Florian Grabmeier --- Flow.Launcher/MainWindow.xaml.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 70765c1dc..b745eaee7 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -49,7 +49,9 @@ namespace Flow.Launcher _settings = settings; InitializeComponent(); - InitializePosition(); + InitializePosition(); + + animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); } public MainWindow() @@ -112,7 +114,7 @@ namespace Flow.Launcher { if (_settings.UseSound) { - animationSound.Open(new Uri(AppDomain.CurrentDomain.BaseDirectory + "Resources\\open.wav")); + animationSound.Position = TimeSpan.Zero; animationSound.Volume = _settings.SoundVolume / 100.0; animationSound.Play(); } From 04291fd0bd7cc62520a451e207c3c52a88a7266d Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 17 Jan 2024 10:45:16 +0100 Subject: [PATCH 003/151] Update icons Signed-off-by: Florian Grabmeier --- Flow.Launcher/SettingWindow.xaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 57b4e0684..96f80f0c5 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2498,7 +2498,7 @@ OnContent="{DynamicResource enable}" Style="{DynamicResource SideToggleSwitch}" /> -  +  From 83d59b1103dc4439f77da8384cf24a81e814eacb Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Thu, 25 Jan 2024 22:48:44 +0100 Subject: [PATCH 004/151] Add open target for program shortcuts Signed-off-by: Florian Grabmeier --- .../Flow.Launcher.Plugin.Program/Languages/en.xaml | 1 + .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 14 +++++++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml index 39807db2c..d7e87b50b 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Program/Languages/en.xaml @@ -73,6 +73,7 @@ Run As Administrator Open containing folder Disable this program from displaying + Open target folder Program Search programs in Flow Launcher diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 7d08d3670..788aa8724 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -261,7 +261,19 @@ namespace Flow.Launcher.Plugin.Program.Programs }, IcoPath = "Images/folder.png", Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe838"), - } + }, + new Result + { + Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), + Action = _ => + { + Main.Context.API.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); + + return true; + }, + IcoPath = "Images/folder.png", + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), + }, }; return contextMenus; } From 0a9eb09d7e869ded6ea7530425fcf006f3fa1c98 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:40:54 -0500 Subject: [PATCH 005/151] Add DeepCopy method --- Flow.Launcher.Plugin/Result.cs | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/Flow.Launcher.Plugin/Result.cs b/Flow.Launcher.Plugin/Result.cs index bfb0b0411..fc6c5d185 100644 --- a/Flow.Launcher.Plugin/Result.cs +++ b/Flow.Launcher.Plugin/Result.cs @@ -183,6 +183,31 @@ namespace Flow.Launcher.Plugin return Title + SubTitle + Score; } + /// + /// Clones the current result + /// + public Result Clone() + { + return new Result + { + Title = Title, + SubTitle = SubTitle, + ActionKeywordAssigned = ActionKeywordAssigned, + CopyText = CopyText, + AutoCompleteText = AutoCompleteText, + IcoPath = IcoPath, + RoundedIcon = RoundedIcon, + Icon = Icon, + Glyph = Glyph, + Action = Action, + AsyncAction = AsyncAction, + Score = Score, + TitleHighlightData = TitleHighlightData, + OriginQuery = OriginQuery, + PluginDirectory = PluginDirectory + }; + } + /// /// Additional data associated with this result /// From 62a26ff0ca8e6ef62c47ff6d26e1a4f8fc5302f3 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:41:42 -0500 Subject: [PATCH 006/151] Move copying to appropiate sections --- Flow.Launcher/ViewModel/MainViewModel.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..ef533ed31 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -662,19 +662,7 @@ namespace Flow.Launcher.ViewModel if (selected == lastContextMenuResult) { // Use copy to keep the original results unchanged - results = lastContextMenuResults.ConvertAll(result => new Result - { - Title = result.Title, - SubTitle = result.SubTitle, - IcoPath = result.IcoPath, - PluginDirectory = result.PluginDirectory, - Action = result.Action, - ContextData = result.ContextData, - Glyph = result.Glyph, - OriginQuery = result.OriginQuery, - Score = result.Score, - AsyncAction = result.AsyncAction, - }); + results = lastContextMenuResults.ToList(); } else { @@ -689,7 +677,7 @@ namespace Flow.Launcher.ViewModel if (!string.IsNullOrEmpty(query)) { - var filtered = results.Where + var filtered = results.Select(x => x.Clone()).Where ( r => { From 3b8fc29d6f913e0762ba819779190e3c72db6432 Mon Sep 17 00:00:00 2001 From: Garulf <535299+Garulf@users.noreply.github.com> Date: Sun, 4 Feb 2024 11:46:57 -0500 Subject: [PATCH 007/151] Remove unused code --- Flow.Launcher/ViewModel/MainViewModel.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ef533ed31..9717f40e1 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -661,8 +661,7 @@ namespace Flow.Launcher.ViewModel List results; if (selected == lastContextMenuResult) { - // Use copy to keep the original results unchanged - results = lastContextMenuResults.ToList(); + results = lastContextMenuResults; } else { From 8656eddf41432dde7f6c591f65d282e03d1d4cd6 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:25:29 +0200 Subject: [PATCH 008/151] Change protection level of ReQuery instance --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index db481d410..ab93e08e2 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -208,7 +208,7 @@ namespace Flow.Launcher.ViewModel } [RelayCommand] - private void ReQuery() + public void ReQuery() { if (SelectedIsFromQueryResults()) { From 0522fc79f44f1a3c485aca252a095c30948f213f Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:27:34 +0200 Subject: [PATCH 009/151] Add ReQuery Instance --- Flow.Launcher/PublicAPIInstance.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 36309a22a..5efc2324e 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -315,6 +315,8 @@ namespace Flow.Launcher public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback); public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback); + public void ReQuery() => _mainVM.ReQuery(); + #endregion #region Private Methods From 50f71c07313bb872a20a5a82ee679616fc4e4e56 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Fri, 23 Feb 2024 23:28:16 +0200 Subject: [PATCH 010/151] Add ReQuery to API --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 49fe680f1..78f1e0afa 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -291,5 +291,11 @@ namespace Flow.Launcher.Plugin /// /// public bool IsGameModeOn(); + + /// + /// Reload Query + /// + /// + public void ReQuery(); } } From e722927d9e0e1e2dd72359cc11cc80b7a54dc24b Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:05:54 +0200 Subject: [PATCH 011/151] ReQuery method overloading, reselect flag. --- Flow.Launcher/ViewModel/MainViewModel.cs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index ab93e08e2..6e53879f8 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,6 +33,7 @@ namespace Flow.Launcher.ViewModel #region Private Fields private bool _isQueryRunning; + private bool _reselect = true; private Query _lastQuery; private Result lastContextMenuResult = new Result(); private List lastContextMenuResults = new List(); @@ -216,6 +217,15 @@ namespace Flow.Launcher.ViewModel } } + public void ReQuery(bool reselect) + { + if (SelectedIsFromQueryResults()) + { + _reselect = reselect; + QueryResults(isReQuery: true); + } + } + [RelayCommand] private void LoadContextMenu() { @@ -1151,7 +1161,8 @@ namespace Flow.Launcher.ViewModel } } - Results.AddResults(resultsForUpdates, token); + Results.AddResults(resultsForUpdates, token, _reselect); + _reselect = true; } #endregion From 1d6c4cee2e703510ad60e0d011d55d4d0170dc4a Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:11:30 +0200 Subject: [PATCH 012/151] AddResults & UpdateResults new param "reselect" --- Flow.Launcher/ViewModel/ResultsViewModel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index bb07ce085..d02dc9bd5 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -147,23 +147,23 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void AddResults(IEnumerable resultsForUpdates, CancellationToken token) + public void AddResults(IEnumerable resultsForUpdates, CancellationToken token, bool reselect = true) { var newResults = NewResults(resultsForUpdates); if (token.IsCancellationRequested) return; - UpdateResults(newResults, token); + UpdateResults(newResults, token, reselect); } - private void UpdateResults(List newResults, CancellationToken token = default) + private void UpdateResults(List newResults, CancellationToken token = default, bool reselect = true) { lock (_collectionLock) { // update UI in one run, so it can avoid UI flickering Results.Update(newResults, token); - if (Results.Any()) + if (reselect && Results.Any()) SelectedItem = Results[0]; } From ae84dae66dd98a7c894684d51b0fb98304242715 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:13:09 +0200 Subject: [PATCH 013/151] ReQuery new param "reselect" --- Flow.Launcher/PublicAPIInstance.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Flow.Launcher/PublicAPIInstance.cs b/Flow.Launcher/PublicAPIInstance.cs index 5efc2324e..b49bf39d3 100644 --- a/Flow.Launcher/PublicAPIInstance.cs +++ b/Flow.Launcher/PublicAPIInstance.cs @@ -315,7 +315,7 @@ namespace Flow.Launcher public void RegisterGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Add(callback); public void RemoveGlobalKeyboardCallback(Func callback) => _globalKeyboardHandlers.Remove(callback); - public void ReQuery() => _mainVM.ReQuery(); + public void ReQuery(bool reselect = true) => _mainVM.ReQuery(reselect); #endregion From c8a2f10620e46265b879077244ccc452a7dfcff4 Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Sun, 25 Feb 2024 04:29:14 +0200 Subject: [PATCH 014/151] Introduce param "reselect" to ReQuery instance --- Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs index 78f1e0afa..c95a8ce7b 100644 --- a/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs +++ b/Flow.Launcher.Plugin/Interfaces/IPublicAPI.cs @@ -293,9 +293,10 @@ namespace Flow.Launcher.Plugin public bool IsGameModeOn(); /// - /// Reload Query + /// Reloads the query. + /// This method should run /// - /// - public void ReQuery(); + /// Choose the first result after reload if true; keep the last selected result if false. Default is true. + public void ReQuery(bool reselect = true); } } From 6057eae05bcacf7f55de601e23b52d066bcba63d Mon Sep 17 00:00:00 2001 From: Florian Grabmeier Date: Wed, 13 Mar 2024 12:59:59 +0100 Subject: [PATCH 015/151] Fix missing using directive Signed-off-by: Florian Grabmeier --- Flow.Launcher/MainWindow.xaml.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 2a82dcdeb..503704046 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -26,6 +26,7 @@ using Key = System.Windows.Input.Key; using System.Media; using static Flow.Launcher.ViewModel.SettingWindowViewModel; using DataObject = System.Windows.DataObject; +using System.Windows.Media; namespace Flow.Launcher { From 2d805e53bc3adedf17fe4eb21631c039f21f3a7f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Wed, 20 Mar 2024 00:47:14 -0500 Subject: [PATCH 016/151] revise structure --- Flow.Launcher/ViewModel/MainViewModel.cs | 53 +++++++++++++-------- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 24 ++++------ 2 files changed, 40 insertions(+), 37 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 6e53879f8..23e746bd7 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -177,7 +177,8 @@ namespace Flow.Launcher.ViewModel var token = e.Token == default ? _updateToken : e.Token; PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query); - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, token))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(e.Results, pair.Metadata, e.Query, + token))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } @@ -191,7 +192,8 @@ namespace Flow.Launcher.ViewModel Hide(); await PluginManager.ReloadDataAsync().ConfigureAwait(false); - Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); + Notification.Show(InternationalizationManager.Instance.GetTranslation("success"), + InternationalizationManager.Instance.GetTranslation("completedSuccessfully")); } [RelayCommand] @@ -221,8 +223,7 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - _reselect = reselect; - QueryResults(isReQuery: true); + QueryResults(isReQuery: true, reselect: reselect); } } @@ -275,6 +276,7 @@ namespace Flow.Launcher.ViewModel { autoCompleteText = $"{result.ActionKeywordAssigned} {defaultSuggestion}"; } + autoCompleteText = SelectedResults.SelectedItem.QuerySuggestionText; } @@ -296,11 +298,13 @@ namespace Flow.Launcher.ViewModel { results.SelectedIndex = int.Parse(index); } + var result = results.SelectedItem?.Result; if (result == null) { return; } + var hideWindow = await result.ExecuteAsync(new ActionContext { // not null means pressing modifier key + number, should ignore the modifier key @@ -413,6 +417,7 @@ namespace Flow.Launcher.ViewModel public bool GameModeStatus { get; set; } = false; private string _queryText; + public string QueryText { get => _queryText; @@ -436,6 +441,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowSize += 100; Settings.WindowLeft -= 50; } + OnPropertyChanged(); } @@ -451,6 +457,7 @@ namespace Flow.Launcher.ViewModel Settings.WindowLeft += 50; Settings.WindowSize -= 100; } + OnPropertyChanged(); } @@ -530,18 +537,17 @@ namespace Flow.Launcher.ViewModel { if (QueryText != queryText) { - // re-query is done in QueryText's setter method QueryText = queryText; // set to false so the subsequent set true triggers // PropertyChanged and MoveQueryTextToEnd is called QueryTextCursorMovedToEnd = false; - } else if (isReQuery) { Query(isReQuery: true); } + QueryTextCursorMovedToEnd = true; }); } @@ -611,8 +617,8 @@ namespace Flow.Launcher.ViewModel public string OpenResultCommandModifiers => Settings.OpenResultModifiers; - public string PreviewHotkey - { + public string PreviewHotkey + { get { // TODO try to patch issue #1755 @@ -626,6 +632,7 @@ namespace Flow.Launcher.ViewModel { Settings.PreviewHotkey = "F1"; } + return Settings.PreviewHotkey; } } @@ -694,7 +701,6 @@ namespace Flow.Launcher.ViewModel results.Add(ContextMenuTopMost(selected)); results.Add(ContextMenuPluginInfo(selected.PluginID)); } - if (!string.IsNullOrEmpty(query)) @@ -713,7 +719,6 @@ namespace Flow.Launcher.ViewModel r.Score = match.Score; return true; - }).ToList(); ContextMenu.AddResults(filtered, id); } @@ -740,10 +745,7 @@ namespace Flow.Launcher.ViewModel Title = string.Format(title, h.Query), SubTitle = string.Format(time, h.ExecutedDateTime), IcoPath = "Images\\history.png", - OriginQuery = new Query - { - RawQuery = h.Query - }, + OriginQuery = new Query { RawQuery = h.Query }, Action = _ => { SelectedResults = Results; @@ -771,7 +773,7 @@ namespace Flow.Launcher.ViewModel private readonly IReadOnlyList _emptyResult = new List(); - private async void QueryResults(bool isReQuery = false) + private async void QueryResults(bool isReQuery = false, bool reselect = true) { _updateSource?.Cancel(); @@ -880,20 +882,23 @@ namespace Flow.Launcher.ViewModel // Task.Yield will force it to run in ThreadPool await Task.Yield(); - IReadOnlyList results = await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); + IReadOnlyList results = + await PluginManager.QueryForPluginAsync(plugin, query, currentCancellationToken); currentCancellationToken.ThrowIfCancellationRequested(); results ??= _emptyResult; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, currentCancellationToken))) + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, + currentCancellationToken, reselect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } } } - private Query ConstructQuery(string queryText, IEnumerable customShortcuts, IEnumerable builtInShortcuts) + private Query ConstructQuery(string queryText, IEnumerable customShortcuts, + IEnumerable builtInShortcuts) { if (string.IsNullOrWhiteSpace(queryText)) { @@ -930,7 +935,9 @@ namespace Flow.Launcher.ViewModel } catch (Exception e) { - Log.Exception($"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", e); + Log.Exception( + $"{nameof(MainViewModel)}.{nameof(ConstructQuery)}|Error when expanding shortcut {shortcut.Key}", + e); } } }); @@ -1075,6 +1082,7 @@ namespace Flow.Launcher.ViewModel { SelectedResults = Results; } + switch (Settings.LastQueryMode) { case LastQueryMode.Empty: @@ -1122,7 +1130,7 @@ namespace Flow.Launcher.ViewModel /// /// To avoid deadlock, this method should not called from main thread /// - public void UpdateResultView(IEnumerable resultsForUpdates) + public void UpdateResultView(ICollection resultsForUpdates) { if (!resultsForUpdates.Any()) return; @@ -1161,7 +1169,10 @@ namespace Flow.Launcher.ViewModel } } - Results.AddResults(resultsForUpdates, token, _reselect); + // it should be the same for all results + bool reSelect = resultsForUpdates.First().ReSelectFirstResult; + + Results.AddResults(resultsForUpdates, token, reSelect); _reselect = true; } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index 4cb5b1a95..c1daace56 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -4,23 +4,15 @@ using System.Threading; namespace Flow.Launcher.ViewModel { - public struct ResultsForUpdate + public record struct ResultsForUpdate( + IReadOnlyList Results, + PluginMetadata Metadata, + Query Query, + CancellationToken Token, + bool ReSelectFirstResult = true) { - public IReadOnlyList Results { get; } + public string ID { get; } = Metadata.ID; - public PluginMetadata Metadata { get; } - public string ID { get; } - - public Query Query { get; } - public CancellationToken Token { get; } - - public ResultsForUpdate(IReadOnlyList results, PluginMetadata metadata, Query query, CancellationToken token) - { - Results = results; - Metadata = metadata; - Query = query; - Token = token; - ID = metadata.ID; - } + public bool ReSelectFirstResult { get; set; } } } From b2f7d34a4d8259576e339a0f81293dbe7eeed6f6 Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Wed, 20 Mar 2024 12:26:39 -0500 Subject: [PATCH 017/151] fix weird issue --- Flow.Launcher/ViewModel/MainViewModel.cs | 10 +++++----- Flow.Launcher/ViewModel/ResultsForUpdate.cs | 2 -- 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 23e746bd7..46102ca92 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -223,7 +223,7 @@ namespace Flow.Launcher.ViewModel { if (SelectedIsFromQueryResults()) { - QueryResults(isReQuery: true, reselect: reselect); + QueryResults(isReQuery: true, reSelect: reselect); } } @@ -773,7 +773,7 @@ namespace Flow.Launcher.ViewModel private readonly IReadOnlyList _emptyResult = new List(); - private async void QueryResults(bool isReQuery = false, bool reselect = true) + private async void QueryResults(bool isReQuery = false, bool reSelect = true) { _updateSource?.Cancel(); @@ -848,7 +848,7 @@ namespace Flow.Launcher.ViewModel var tasks = plugins.Select(plugin => plugin.Metadata.Disabled switch { - false => QueryTask(plugin), + false => QueryTask(plugin, reSelect), true => Task.CompletedTask }).ToArray(); @@ -876,7 +876,7 @@ namespace Flow.Launcher.ViewModel } // Local function - async Task QueryTask(PluginPair plugin) + async Task QueryTask(PluginPair plugin, bool reSelect = true) { // Since it is wrapped within a ThreadPool Thread, the synchronous context is null // Task.Yield will force it to run in ThreadPool @@ -890,7 +890,7 @@ namespace Flow.Launcher.ViewModel results ??= _emptyResult; if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, plugin.Metadata, query, - currentCancellationToken, reselect))) + currentCancellationToken, reSelect))) { Log.Error("MainViewModel", "Unable to add item to Result Update Queue"); } diff --git a/Flow.Launcher/ViewModel/ResultsForUpdate.cs b/Flow.Launcher/ViewModel/ResultsForUpdate.cs index c1daace56..bc0be0de8 100644 --- a/Flow.Launcher/ViewModel/ResultsForUpdate.cs +++ b/Flow.Launcher/ViewModel/ResultsForUpdate.cs @@ -12,7 +12,5 @@ namespace Flow.Launcher.ViewModel bool ReSelectFirstResult = true) { public string ID { get; } = Metadata.ID; - - public bool ReSelectFirstResult { get; set; } } } From c0034d8e07d1d86c96d950ad3dae8a96dba05f1a Mon Sep 17 00:00:00 2001 From: AminSallah <124622454+AminSallah@users.noreply.github.com> Date: Wed, 20 Mar 2024 21:17:24 +0200 Subject: [PATCH 018/151] Remove unnecessary top class _reselect parameter --- Flow.Launcher/ViewModel/MainViewModel.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 46102ca92..bb0505973 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -33,7 +33,6 @@ namespace Flow.Launcher.ViewModel #region Private Fields private bool _isQueryRunning; - private bool _reselect = true; private Query _lastQuery; private Result lastContextMenuResult = new Result(); private List lastContextMenuResults = new List(); @@ -1173,7 +1172,6 @@ namespace Flow.Launcher.ViewModel bool reSelect = resultsForUpdates.First().ReSelectFirstResult; Results.AddResults(resultsForUpdates, token, reSelect); - _reselect = true; } #endregion From ec7ecfacd053a102374f15c2ed7fa618ef944215 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:17:05 +0800 Subject: [PATCH 019/151] Add Args property --- .../Flow.Launcher.Plugin.Program/Programs/Win32.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 788aa8724..61d3511b7 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -39,15 +39,20 @@ namespace Flow.Launcher.Plugin.Program.Programs public string FullPath { get; set; } /// - /// Path of the executable for .lnk, or the URL for .url. Arguments are included if any. + /// Path of the executable for .lnk, or the URL for .url /// public string LnkResolvedPath { get; set; } /// - /// Path of the actual executable file. Args are included. + /// Path of the actual executable file /// public string ExecutablePath => LnkResolvedPath ?? FullPath; + /// + /// Arguments for the executable. + /// + public string Args { get; set; } + public string ParentDirectory { get; set; } /// @@ -339,7 +344,7 @@ namespace Flow.Launcher.Plugin.Program.Programs var args = _helper.arguments; if (!string.IsNullOrEmpty(args)) { - program.LnkResolvedPath += " " + args; + program.Args = args; } var description = _helper.description; @@ -636,7 +641,7 @@ namespace Flow.Launcher.Plugin.Program.Programs private static IEnumerable ProgramsHasher(IEnumerable programs) { var startMenuPaths = GetStartMenuPaths(); - return programs.GroupBy(p => p.ExecutablePath.ToLowerInvariant()) + return programs.GroupBy(p => (p.ExecutablePath + p.Args).ToLowerInvariant()) .AsParallel() .SelectMany(g => { From f73e3d57d61fbb2067bb1fc2bf852f9c7bf806ef Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 01:17:59 +0800 Subject: [PATCH 020/151] Only show target folder context menu for .lnk files --- .../Programs/Win32.cs | 30 +++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index 61d3511b7..8a3e5ada0 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -267,22 +267,28 @@ namespace Flow.Launcher.Plugin.Program.Programs IcoPath = "Images/folder.png", Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe838"), }, - new Result - { - Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), - Action = _ => - { - Main.Context.API.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); - - return true; - }, - IcoPath = "Images/folder.png", - Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), - }, }; + if (Extension(FullPath) == ShortcutExtension) + { + contextMenus.Add(OpenTargetFolderContextMenuResult(api)); + } return contextMenus; } + private Result OpenTargetFolderContextMenuResult(IPublicAPI api) + { + return new Result + { + Title = api.GetTranslation("flowlauncher_plugin_program_open_target_folder"), + Action = _ => + { + api.OpenDirectory(Path.GetDirectoryName(ExecutablePath), ExecutablePath); + return true; + }, + IcoPath = "Images/folder.png", + Glyph = new GlyphInfo(FontFamily: "/Resources/#Segoe Fluent Icons", Glyph: "\xe8de"), + }; + } public override string ToString() { From df589cea497667f2d0c8ecd2bdedd0f96a7e3874 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sun, 31 Mar 2024 20:07:18 +0800 Subject: [PATCH 021/151] Fix program indexing logic when cache is empty --- Plugins/Flow.Launcher.Plugin.Program/Main.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Main.cs b/Plugins/Flow.Launcher.Plugin.Program/Main.cs index c02573ed8..e0a7f23de 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Main.cs @@ -84,7 +84,7 @@ namespace Flow.Launcher.Plugin.Program Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload win32 programs <{_win32s.Length}>"); Log.Info($"|Flow.Launcher.Plugin.Program.Main|Number of preload uwps <{_uwps.Length}>"); - bool cacheEmpty = !_win32s.Any() && !_uwps.Any(); + bool cacheEmpty = !_win32s.Any() || !_uwps.Any(); if (cacheEmpty || _settings.LastIndexTime.AddHours(30) < DateTime.Now) { From 2a00c48dea76529b3b9d43d83713434c1164089d Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Mon, 1 Apr 2024 20:38:25 +0800 Subject: [PATCH 022/151] Fix typo --- Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml index 282a0e809..91f32a844 100644 --- a/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.Sys/Languages/en.xaml @@ -19,7 +19,7 @@ Open Recycle Bin Exit Save Settings - Restart Flow Launcher" + Restart Flow Launcher Settings Reload Plugin Data Check For Update @@ -62,4 +62,4 @@ System Commands Provides System related commands. e.g. shutdown, lock, settings etc. - \ No newline at end of file + From 578ad99cec599b3783af7e51d14ebe007bbb34d1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 16:05:54 +0000 Subject: [PATCH 023/151] Bump softprops/action-gh-release from 1 to 2 (#2606) --- .github/workflows/default_plugins.yml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/default_plugins.yml b/.github/workflows/default_plugins.yml index 2cb04fe0d..a0a816877 100644 --- a/.github/workflows/default_plugins.yml +++ b/.github/workflows/default_plugins.yml @@ -65,7 +65,7 @@ jobs: - name: Publish BrowserBookmark if: steps.changes.outputs.browserbookmark == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.BrowserBookmark" files: "Flow.Launcher.Plugin.BrowserBookmark.zip" @@ -92,7 +92,7 @@ jobs: - name: Publish Calculator if: steps.changes.outputs.calculator == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Calculator" files: "Flow.Launcher.Plugin.Calculator.zip" @@ -119,7 +119,7 @@ jobs: - name: Publish Explorer if: steps.changes.outputs.explorer == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Explorer" files: "Flow.Launcher.Plugin.Explorer.zip" @@ -146,7 +146,7 @@ jobs: - name: Publish PluginIndicator if: steps.changes.outputs.pluginindicator == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginIndicator" files: "Flow.Launcher.Plugin.PluginIndicator.zip" @@ -173,7 +173,7 @@ jobs: - name: Publish PluginsManager if: steps.changes.outputs.pluginsmanager == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.PluginsManager" files: "Flow.Launcher.Plugin.PluginsManager.zip" @@ -200,7 +200,7 @@ jobs: - name: Publish ProcessKiller if: steps.changes.outputs.processkiller == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.ProcessKiller" files: "Flow.Launcher.Plugin.ProcessKiller.zip" @@ -227,7 +227,7 @@ jobs: - name: Publish Program if: steps.changes.outputs.program == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Program" files: "Flow.Launcher.Plugin.Program.zip" @@ -254,7 +254,7 @@ jobs: - name: Publish Shell if: steps.changes.outputs.shell == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Shell" files: "Flow.Launcher.Plugin.Shell.zip" @@ -281,7 +281,7 @@ jobs: - name: Publish Sys if: steps.changes.outputs.sys == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Sys" files: "Flow.Launcher.Plugin.Sys.zip" @@ -308,7 +308,7 @@ jobs: - name: Publish Url if: steps.changes.outputs.url == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.Url" files: "Flow.Launcher.Plugin.Url.zip" @@ -335,7 +335,7 @@ jobs: - name: Publish WebSearch if: steps.changes.outputs.websearch == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.WebSearch" files: "Flow.Launcher.Plugin.WebSearch.zip" @@ -362,7 +362,7 @@ jobs: - name: Publish WindowsSettings if: steps.changes.outputs.windowssettings == 'true' - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 with: repository: "Flow-Launcher/Flow.Launcher.Plugin.WindowsSettings" files: "Flow.Launcher.Plugin.WindowsSettings.zip" From bd3608f020ae774135402f9e121dd521065f664c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 1 Apr 2024 22:22:15 +0000 Subject: [PATCH 024/151] Bump Microsoft.Data.Sqlite from 8.0.2 to 8.0.3 Bumps [Microsoft.Data.Sqlite](https://github.com/dotnet/efcore) from 8.0.2 to 8.0.3. - [Release notes](https://github.com/dotnet/efcore/releases) - [Commits](https://github.com/dotnet/efcore/compare/v8.0.2...v8.0.3) --- updated-dependencies: - dependency-name: Microsoft.Data.Sqlite dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .../Flow.Launcher.Plugin.BrowserBookmark.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 81374abe8..fe118c2c3 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Flow.Launcher.Plugin.BrowserBookmark.csproj @@ -56,7 +56,7 @@ - + \ No newline at end of file From cd7673418acecf1a56e02c3da3b9125d63f25ad1 Mon Sep 17 00:00:00 2001 From: VictoriousRaptor <10308169+VictoriousRaptor@users.noreply.github.com> Date: Sat, 6 Apr 2024 11:54:52 +0800 Subject: [PATCH 025/151] Show target path in hover tooltip --- Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs index f3d971991..a64a708ef 100644 --- a/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs +++ b/Plugins/Flow.Launcher.Plugin.Program/Programs/Win32.cs @@ -183,6 +183,7 @@ namespace Flow.Launcher.Plugin.Program.Programs Score = matchResult.Score, TitleHighlightData = matchResult.MatchData, ContextData = this, + TitleToolTip = $"{title}\n{ExecutablePath}", Action = c => { // Ctrl + Enter to open containing folder From 950b4c91d3243f3e4ed61e3c9f81d6073ed7ba4a Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 11 Apr 2024 13:20:38 +0600 Subject: [PATCH 026/151] Code cleanup: Flow.Launcher/{Converters,Helper} --- .editorconfig | 4 +- .../BoolToIMEConversionModeConverter.cs | 57 +- .../Converters/BoolToVisibilityConverter.cs | 59 +- .../Converters/BorderClipConverter.cs | 58 +- .../DateTimeFormatToNowConverter.cs | 23 +- .../DiameterToCenterPointConverter.cs | 25 +- .../Converters/HighlightTextConverter.cs | 65 +- .../Converters/IconRadiusConverter.cs | 27 +- .../OpenResultHotkeyVisibilityConverter.cs | 29 +- Flow.Launcher/Converters/OrdinalConverter.cs | 25 +- .../Converters/QuerySuggestionBoxConverter.cs | 89 ++- .../Converters/StringToKeyBindingConverter.cs | 41 +- Flow.Launcher/Converters/TextConverter.cs | 43 +- Flow.Launcher/Helper/AutoStartup.cs | 77 +- Flow.Launcher/Helper/DWMDropShadow.cs | 105 ++- Flow.Launcher/Helper/DataWebRequestFactory.cs | 111 ++- Flow.Launcher/Helper/ErrorReporting.cs | 77 +- Flow.Launcher/Helper/HotKeyMapper.cs | 169 +++-- Flow.Launcher/Helper/SingleInstance.cs | 697 +++++++++--------- Flow.Launcher/Helper/SingletonWindowOpener.cs | 43 +- Flow.Launcher/Helper/SyntaxSugars.cs | 33 +- .../Helper/WallpaperPathRetrieval.cs | 63 +- Flow.Launcher/Helper/WindowsInteropHelper.cs | 236 +++--- 23 files changed, 1069 insertions(+), 1087 deletions(-) diff --git a/.editorconfig b/.editorconfig index 11a0bcdf6..e1b8ed79e 100644 --- a/.editorconfig +++ b/.editorconfig @@ -58,7 +58,7 @@ dotnet_style_prefer_conditional_expression_over_return = true:silent ############################### # Style Definitions dotnet_naming_style.pascal_case_style.capitalization = pascal_case -# Use PascalCase for constant fields +# Use PascalCase for constant fields dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style @@ -134,7 +134,7 @@ csharp_preserve_single_line_statements = true csharp_preserve_single_line_blocks = true csharp_using_directive_placement = outside_namespace:silent csharp_prefer_simple_using_statement = true:suggestion -csharp_style_namespace_declarations = block_scoped:silent +csharp_style_namespace_declarations = file_scoped:silent csharp_style_prefer_method_group_conversion = true:silent csharp_style_expression_bodied_lambdas = true:silent csharp_style_expression_bodied_local_functions = false:silent diff --git a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs index ffb7d65b8..41e879913 100644 --- a/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs +++ b/Flow.Launcher/Converters/BoolToIMEConversionModeConverter.cs @@ -3,39 +3,38 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Input; -namespace Flow.Launcher.Converters -{ - internal class BoolToIMEConversionModeConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => ImeConversionModeValues.Alphanumeric, - _ => ImeConversionModeValues.DoNotCare - }; - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) +internal class BoolToIMEConversionModeConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value switch { - throw new NotImplementedException(); - } + true => ImeConversionModeValues.Alphanumeric, + _ => ImeConversionModeValues.DoNotCare + }; } - internal class BoolToIMEStateConverter : IValueConverter + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value switch - { - true => InputMethodState.Off, - _ => InputMethodState.DoNotCare - }; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } + throw new NotImplementedException(); + } +} + +internal class BoolToIMEStateConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value switch + { + true => InputMethodState.Off, + _ => InputMethodState.DoNotCare + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs index ac3b2cfd0..c0fe6ab55 100644 --- a/Flow.Launcher/Converters/BoolToVisibilityConverter.cs +++ b/Flow.Launcher/Converters/BoolToVisibilityConverter.cs @@ -2,40 +2,39 @@ using System.Windows; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class BoolToVisibilityConverter : IValueConverter { - public class BoolToVisibilityConverter : IValueConverter + public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + return (value, parameter) switch { - return (value, parameter) switch - { - (true, not null) => Visibility.Collapsed, - (_, not null) => Visibility.Visible, + (true, not null) => Visibility.Collapsed, + (_, not null) => Visibility.Visible, - (true, null) => Visibility.Visible, - (_, null) => Visibility.Collapsed - }; - } - - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); + (true, null) => Visibility.Visible, + (_, null) => Visibility.Collapsed + }; } - public class SplitterConverter : IValueConverter - /* Prevents the dragging part of the preview area from working when preview is turned off. */ - { - public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) - { - return (value, parameter) switch - { - (true, not null) => 0, - (_, not null) => 5, - - (true, null) => 5, - (_, null) => 0 - }; - } - - public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); - } + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); +} + +public class SplitterConverter : IValueConverter +/* Prevents the dragging part of the preview area from working when preview is turned off. */ +{ + public object Convert(object value, System.Type targetType, object parameter, CultureInfo culture) + { + return (value, parameter) switch + { + (true, not null) => 0, + (_, not null) => 5, + + (true, null) => 5, + (_, null) => 0 + }; + } + + public object ConvertBack(object value, System.Type targetType, object parameter, CultureInfo culture) => throw new System.InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/BorderClipConverter.cs b/Flow.Launcher/Converters/BorderClipConverter.cs index a8d9b34c6..9b0579c33 100644 --- a/Flow.Launcher/Converters/BorderClipConverter.cs +++ b/Flow.Launcher/Converters/BorderClipConverter.cs @@ -7,43 +7,41 @@ using System.Windows.Shapes; // For Clipping inside listbox item -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class BorderClipConverter : IMultiValueConverter { - public class BorderClipConverter : IMultiValueConverter + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + if (values is not [double width, double height, CornerRadius radius]) { - if (values is not [double width, double height, CornerRadius radius]) - { - return DependencyProperty.UnsetValue; - } - - Path myPath = new Path(); - if (width < Double.Epsilon || height < Double.Epsilon) - { - return Geometry.Empty; - } - var radiusHeight = radius.TopLeft; - - // Drawing Round box for bottom round, and rect for top area of listbox. - var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); - var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); - - GeometryGroup myGeometryGroup = new GeometryGroup(); - myGeometryGroup.Children.Add(corner); - myGeometryGroup.Children.Add(box); - - CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); - myPath.Data = c1; - - myPath.Data.Freeze(); - return myPath.Data; + return DependencyProperty.UnsetValue; } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + Path myPath = new Path(); + if (width < Double.Epsilon || height < Double.Epsilon) { - throw new NotSupportedException(); + return Geometry.Empty; } + var radiusHeight = radius.TopLeft; + + // Drawing Round box for bottom round, and rect for top area of listbox. + var corner = new RectangleGeometry(new Rect(0, 0, width, height), radius.TopLeft, radius.TopLeft); + var box = new RectangleGeometry(new Rect(0, 0, width, radiusHeight), 0, 0); + + GeometryGroup myGeometryGroup = new GeometryGroup(); + myGeometryGroup.Children.Add(corner); + myGeometryGroup.Children.Add(box); + + CombinedGeometry c1 = new CombinedGeometry(GeometryCombineMode.Union, corner, box); + myPath.Data = c1; + + myPath.Data.Freeze(); + return myPath.Data; } + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); + } } diff --git a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs index 3c46fd01a..7d75ffd5c 100644 --- a/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs +++ b/Flow.Launcher/Converters/DateTimeFormatToNowConverter.cs @@ -2,18 +2,17 @@ using System.Globalization; using System.Windows.Data; -namespace Flow.Launcher.Converters -{ - public class DateTimeFormatToNowConverter : IValueConverter - { +namespace Flow.Launcher.Converters; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - return value is not string format ? null : DateTime.Now.ToString(format); - } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotImplementedException(); - } +public class DateTimeFormatToNowConverter : IValueConverter +{ + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is not string format ? null : DateTime.Now.ToString(format); + } + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs index e81bb2507..24a316603 100644 --- a/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs +++ b/Flow.Launcher/Converters/DiameterToCenterPointConverter.cs @@ -3,23 +3,22 @@ using System.Globalization; using System.Windows; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class DiameterToCenterPointConverter : IValueConverter { - public class DiameterToCenterPointConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + if (value is double d) { - if (value is double d) - { - return new Point(d / 2, d / 2); - } - - return new Point(0, 0); + return new Point(d / 2, d / 2); } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } + return new Point(0, 0); + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); } } diff --git a/Flow.Launcher/Converters/HighlightTextConverter.cs b/Flow.Launcher/Converters/HighlightTextConverter.cs index 3f02443e2..436f5fed5 100644 --- a/Flow.Launcher/Converters/HighlightTextConverter.cs +++ b/Flow.Launcher/Converters/HighlightTextConverter.cs @@ -5,45 +5,44 @@ using System.Windows; using System.Windows.Data; using System.Windows.Documents; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class HighlightTextConverter : IMultiValueConverter { - public class HighlightTextConverter : IMultiValueConverter + public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo) { - public object Convert(object[] value, Type targetType, object parameter, CultureInfo cultureInfo) - { - if (value.Length < 2) - return new Run(string.Empty); - - if (value[0] is not string text) - return new Run(string.Empty); - - if (value[1] is not List { Count: > 0 } highlightData) - // No highlight data, just return the text - return new Run(text); - - var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle"); - var textBlock = new Span(); + if (value.Length < 2) + return new Run(string.Empty); - for (var i = 0; i < text.Length; i++) + if (value[0] is not string text) + return new Run(string.Empty); + + if (value[1] is not List { Count: > 0 } highlightData) + // No highlight data, just return the text + return new Run(text); + + var highlightStyle = (Style)Application.Current.FindResource("HighlightStyle"); + var textBlock = new Span(); + + for (var i = 0; i < text.Length; i++) + { + var currentCharacter = text.Substring(i, 1); + var run = new Run(currentCharacter) { - var currentCharacter = text.Substring(i, 1); - var run = new Run(currentCharacter) - { - Style = ShouldHighlight(highlightData, i) ? highlightStyle : null - }; - textBlock.Inlines.Add(run); - } - return textBlock; + Style = ShouldHighlight(highlightData, i) ? highlightStyle : null + }; + textBlock.Inlines.Add(run); } + return textBlock; + } - public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) - { - return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; - } + public object[] ConvertBack(object value, Type[] targetType, object parameter, CultureInfo culture) + { + return new[] { DependencyProperty.UnsetValue, DependencyProperty.UnsetValue }; + } - private bool ShouldHighlight(List highlightData, int index) - { - return highlightData.Contains(index); - } + private bool ShouldHighlight(List highlightData, int index) + { + return highlightData.Contains(index); } } diff --git a/Flow.Launcher/Converters/IconRadiusConverter.cs b/Flow.Launcher/Converters/IconRadiusConverter.cs index d0bdc9d1c..d26b39d6b 100644 --- a/Flow.Launcher/Converters/IconRadiusConverter.cs +++ b/Flow.Launcher/Converters/IconRadiusConverter.cs @@ -2,20 +2,19 @@ using System.Globalization; using System.Windows.Data; -namespace Flow.Launcher.Converters -{ - public class IconRadiusConverter : IMultiValueConverter - { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) - { - if (values is not [double size, bool isIconCircular]) - throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); +namespace Flow.Launcher.Converters; - return isIconCircular ? size / 2 : size; - } - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) - { - throw new NotSupportedException(); - } +public class IconRadiusConverter : IMultiValueConverter +{ + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + { + if (values is not [double size, bool isIconCircular]) + throw new ArgumentException("IconRadiusConverter must have 2 parameters: [double, bool]"); + + return isIconCircular ? size / 2 : size; + } + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotSupportedException(); } } diff --git a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs index 7b7bd0906..7ab13190a 100644 --- a/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs +++ b/Flow.Launcher/Converters/OpenResultHotkeyVisibilityConverter.cs @@ -4,24 +4,23 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +[ValueConversion(typeof(bool), typeof(Visibility))] +public class OpenResultHotkeyVisibilityConverter : IValueConverter { - [ValueConversion(typeof(bool), typeof(Visibility))] - public class OpenResultHotkeyVisibilityConverter : IValueConverter + private const int MaxVisibleHotkeys = 10; + + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - private const int MaxVisibleHotkeys = 10; + var number = int.MaxValue; - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var number = int.MaxValue; + if (value is ListBoxItem listBoxItem + && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox) + number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - if (value is ListBoxItem listBoxItem - && ItemsControl.ItemsControlFromItemContainer(listBoxItem) is ListBox listBox) - number = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - - return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed; - } - - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); + return number <= MaxVisibleHotkeys ? Visibility.Visible : Visibility.Collapsed; } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/OrdinalConverter.cs b/Flow.Launcher/Converters/OrdinalConverter.cs index 438d4effa..9aed2e07b 100644 --- a/Flow.Launcher/Converters/OrdinalConverter.cs +++ b/Flow.Launcher/Converters/OrdinalConverter.cs @@ -3,23 +3,22 @@ using System.Globalization; using System.Windows.Controls; using System.Windows.Data; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class OrdinalConverter : IValueConverter { - public class OrdinalConverter : IValueConverter + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + if (value is not ListBoxItem listBoxItem + || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox) { - if (value is not ListBoxItem listBoxItem - || ItemsControl.ItemsControlFromItemContainer(listBoxItem) is not ListBox listBox) - { - return 0; - } - - var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; - return res == 10 ? 0 : res; // 10th item => HOTKEY+0 - + return 0; } - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); + var res = listBox.ItemContainerGenerator.IndexFromContainer(listBoxItem) + 1; + return res == 10 ? 0 : res; // 10th item => HOTKEY+0 + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs index 4a8c9e92b..fabd01a24 100644 --- a/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs +++ b/Flow.Launcher/Converters/QuerySuggestionBoxConverter.cs @@ -6,63 +6,62 @@ using System.Windows.Media; using Flow.Launcher.Infrastructure.Logger; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Converters +namespace Flow.Launcher.Converters; + +public class QuerySuggestionBoxConverter : IMultiValueConverter { - public class QuerySuggestionBoxConverter : IMultiValueConverter + public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { - public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) + // values[0] is TextBox: The textbox displaying the autocomplete suggestion + // values[1] is ResultViewModel: Currently selected item in the list + // values[2] is string: Query text + if ( + values.Length != 3 || + values[0] is not TextBox queryTextBox || + values[1] is null || + values[2] is not string queryText || + string.IsNullOrEmpty(queryText) + ) + return string.Empty; + + if (values[1] is not ResultViewModel selectedItem) + return Binding.DoNothing; + + try { - // values[0] is TextBox: The textbox displaying the autocomplete suggestion - // values[1] is ResultViewModel: Currently selected item in the list - // values[2] is string: Query text - if ( - values.Length != 3 || - values[0] is not TextBox queryTextBox || - values[1] is null || - values[2] is not string queryText || - string.IsNullOrEmpty(queryText) - ) + var selectedResult = selectedItem.Result; + var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; + var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; + + if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) return string.Empty; - - if (values[1] is not ResultViewModel selectedItem) - return Binding.DoNothing; - - try - { - var selectedResult = selectedItem.Result; - var selectedResultActionKeyword = string.IsNullOrEmpty(selectedResult.ActionKeywordAssigned) ? "" : selectedResult.ActionKeywordAssigned + " "; - var selectedResultPossibleSuggestion = selectedResultActionKeyword + selectedResult.Title; - - if (!selectedResultPossibleSuggestion.StartsWith(queryText, StringComparison.CurrentCultureIgnoreCase)) - return string.Empty; - // For AutocompleteQueryCommand. - // When user typed lower case and result title is uppercase, we still want to display suggestion - selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); + // For AutocompleteQueryCommand. + // When user typed lower case and result title is uppercase, we still want to display suggestion + selectedItem.QuerySuggestionText = queryText + selectedResultPossibleSuggestion.Substring(queryText.Length); - // Check if Text will be larger than our QueryTextBox - Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); - // TODO: Obsolete warning? - var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black); + // Check if Text will be larger than our QueryTextBox + Typeface typeface = new Typeface(queryTextBox.FontFamily, queryTextBox.FontStyle, queryTextBox.FontWeight, queryTextBox.FontStretch); + // TODO: Obsolete warning? + var ft = new FormattedText(queryTextBox.Text, CultureInfo.DefaultThreadCurrentCulture, System.Windows.FlowDirection.LeftToRight, typeface, queryTextBox.FontSize, Brushes.Black); - var offset = queryTextBox.Padding.Right; + var offset = queryTextBox.Padding.Right; - if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0) - return string.Empty; - - return selectedItem.QuerySuggestionText; - } - catch (Exception e) - { - Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e); + if (ft.Width + offset > queryTextBox.ActualWidth || queryTextBox.HorizontalOffset != 0) return string.Empty; - } + + return selectedItem.QuerySuggestionText; } - - public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + catch (Exception e) { - throw new NotImplementedException(); + Log.Exception(nameof(QuerySuggestionBoxConverter), "fail to convert text for suggestion box", e); + return string.Empty; } } + + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); + } } diff --git a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs index 22526bd82..21bf584e7 100644 --- a/Flow.Launcher/Converters/StringToKeyBindingConverter.cs +++ b/Flow.Launcher/Converters/StringToKeyBindingConverter.cs @@ -3,28 +3,27 @@ using System.Globalization; using System.Windows.Data; using System.Windows.Input; -namespace Flow.Launcher.Converters -{ - class StringToKeyBindingConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - if (parameter is not string mode || value is not string hotkeyStr) - return null; - - var converter = new KeyGestureConverter(); - var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); - return mode switch - { - "key" => key?.Key, - "modifiers" => key?.Modifiers, - _ => null - }; - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) +class StringToKeyBindingConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (parameter is not string mode || value is not string hotkeyStr) + return null; + + var converter = new KeyGestureConverter(); + var key = (KeyGesture)converter.ConvertFromString(hotkeyStr); + return mode switch { - throw new NotImplementedException(); - } + "key" => key?.Key, + "modifiers" => key?.Modifiers, + _ => null + }; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + throw new NotImplementedException(); } } diff --git a/Flow.Launcher/Converters/TextConverter.cs b/Flow.Launcher/Converters/TextConverter.cs index c0b34d053..5f0e1ea82 100644 --- a/Flow.Launcher/Converters/TextConverter.cs +++ b/Flow.Launcher/Converters/TextConverter.cs @@ -4,28 +4,27 @@ using System.Windows.Data; using Flow.Launcher.Core.Resource; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Converters -{ - public class TextConverter : IValueConverter - { - public object Convert(object value, Type targetType, object parameter, CultureInfo culture) - { - var id = value?.ToString(); - var translationKey = id switch - { - PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease", - PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated", - PluginStoreItemViewModel.None => "pluginStore_None", - PluginStoreItemViewModel.Installed => "pluginStore_Installed", - _ => null - }; - - if (translationKey is null) - return id; - - return InternationalizationManager.Instance.GetTranslation(translationKey); - } +namespace Flow.Launcher.Converters; - public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); +public class TextConverter : IValueConverter +{ + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + var id = value?.ToString(); + var translationKey = id switch + { + PluginStoreItemViewModel.NewRelease => "pluginStore_NewRelease", + PluginStoreItemViewModel.RecentlyUpdated => "pluginStore_RecentlyUpdated", + PluginStoreItemViewModel.None => "pluginStore_None", + PluginStoreItemViewModel.Installed => "pluginStore_Installed", + _ => null + }; + + if (translationKey is null) + return id; + + return InternationalizationManager.Instance.GetTranslation(translationKey); } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new InvalidOperationException(); } diff --git a/Flow.Launcher/Helper/AutoStartup.cs b/Flow.Launcher/Helper/AutoStartup.cs index bf36b7f6f..4bff30caf 100644 --- a/Flow.Launcher/Helper/AutoStartup.cs +++ b/Flow.Launcher/Helper/AutoStartup.cs @@ -3,57 +3,56 @@ using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Logger; using Microsoft.Win32; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class AutoStartup { - public class AutoStartup + private const string StartupPath = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"; + + public static bool IsEnabled { - private const string StartupPath = "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"; - - public static bool IsEnabled - { - get - { - try - { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - var path = key?.GetValue(Constant.FlowLauncher) as string; - return path == Constant.ExecutablePath; - } - catch (Exception e) - { - Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}"); - } - - return false; - } - } - - public static void Disable() + get { try { using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.DeleteValue(Constant.FlowLauncher, false); + var path = key?.GetValue(Constant.FlowLauncher) as string; + return path == Constant.ExecutablePath; } catch (Exception e) { - Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}"); - throw; + Log.Error("AutoStartup", $"Ignoring non-critical registry error (querying if enabled): {e}"); } - } - internal static void Enable() + return false; + } + } + + public static void Disable() + { + try { - try - { - using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); - key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); - } - catch (Exception e) - { - Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}"); - throw; - } + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.DeleteValue(Constant.FlowLauncher, false); + } + catch (Exception e) + { + Log.Error("AutoStartup", $"Failed to disable auto-startup: {e}"); + throw; + } + } + + internal static void Enable() + { + try + { + using var key = Registry.CurrentUser.OpenSubKey(StartupPath, true); + key?.SetValue(Constant.FlowLauncher, $"\"{Constant.ExecutablePath}\""); + } + catch (Exception e) + { + Log.Error("AutoStartup", $"Failed to enable auto-startup: {e}"); + throw; } } } diff --git a/Flow.Launcher/Helper/DWMDropShadow.cs b/Flow.Launcher/Helper/DWMDropShadow.cs index 3a1f82d6f..e448acd4c 100644 --- a/Flow.Launcher/Helper/DWMDropShadow.cs +++ b/Flow.Launcher/Helper/DWMDropShadow.cs @@ -4,70 +4,69 @@ using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class DwmDropShadow { - public class DwmDropShadow + + [DllImport("dwmapi.dll", PreserveSig = true)] + private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); + + [DllImport("dwmapi.dll")] + private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); + + /// + /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven). + /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect, + /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window). + /// + /// Window to which the shadow will be applied + public static void DropShadowToWindow(Window window) { - - [DllImport("dwmapi.dll", PreserveSig = true)] - private static extern int DwmSetWindowAttribute(IntPtr hwnd, int attr, ref int attrValue, int attrSize); - - [DllImport("dwmapi.dll")] - private static extern int DwmExtendFrameIntoClientArea(IntPtr hWnd, ref Margins pMarInset); - - /// - /// Drops a standard shadow to a WPF Window, even if the window isborderless. Only works with DWM (Vista and Seven). - /// This method is much more efficient than setting AllowsTransparency to true and using the DropShadow effect, - /// as AllowsTransparency involves a huge permormance issue (hardware acceleration is turned off for all the window). - /// - /// Window to which the shadow will be applied - public static void DropShadowToWindow(Window window) + if (!DropShadow(window)) { - if (!DropShadow(window)) - { - window.SourceInitialized += window_SourceInitialized; - } + window.SourceInitialized += window_SourceInitialized; } + } - private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo + private static void window_SourceInitialized(object sender, EventArgs e) //fixed typo + { + Window window = (Window)sender; + + DropShadow(window); + + window.SourceInitialized -= window_SourceInitialized; + } + + /// + /// The actual method that makes API calls to drop the shadow to the window + /// + /// Window to which the shadow will be applied + /// True if the method succeeded, false if not + private static bool DropShadow(Window window) + { + try { - Window window = (Window)sender; + WindowInteropHelper helper = new WindowInteropHelper(window); + int val = 2; + int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4); - DropShadow(window); - - window.SourceInitialized -= window_SourceInitialized; - } - - /// - /// The actual method that makes API calls to drop the shadow to the window - /// - /// Window to which the shadow will be applied - /// True if the method succeeded, false if not - private static bool DropShadow(Window window) - { - try + if (ret1 == 0) { - WindowInteropHelper helper = new WindowInteropHelper(window); - int val = 2; - int ret1 = DwmSetWindowAttribute(helper.Handle, 2, ref val, 4); - - if (ret1 == 0) - { - Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 }; - int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m); - return ret2 == 0; - } - else - { - return false; - } + Margins m = new Margins { Bottom = 0, Left = 0, Right = 0, Top = 0 }; + int ret2 = DwmExtendFrameIntoClientArea(helper.Handle, ref m); + return ret2 == 0; } - catch (Exception) + else { - // Probably dwmapi.dll not found (incompatible OS) return false; } } - + catch (Exception) + { + // Probably dwmapi.dll not found (incompatible OS) + return false; + } } -} \ No newline at end of file + +} diff --git a/Flow.Launcher/Helper/DataWebRequestFactory.cs b/Flow.Launcher/Helper/DataWebRequestFactory.cs index b3b7e9c63..2e72ee240 100644 --- a/Flow.Launcher/Helper/DataWebRequestFactory.cs +++ b/Flow.Launcher/Helper/DataWebRequestFactory.cs @@ -2,68 +2,67 @@ using System.IO; using System.Net; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class DataWebRequestFactory : IWebRequestCreate { - public class DataWebRequestFactory : IWebRequestCreate + class DataWebRequest : WebRequest { - class DataWebRequest : WebRequest + private readonly Uri _uri; + + public DataWebRequest(Uri uri) { - private readonly Uri m_uri; - - public DataWebRequest(Uri uri) - { - m_uri = uri; - } - - public override WebResponse GetResponse() - { - return new DataWebResponse(m_uri); - } + _uri = uri; } - class DataWebResponse : WebResponse + public override WebResponse GetResponse() { - private readonly string m_contentType; - private readonly byte[] m_data; - - public DataWebResponse(Uri uri) - { - string uriString = uri.AbsoluteUri; - - int commaIndex = uriString.IndexOf(','); - var headers = uriString.Substring(0, commaIndex).Split(';'); - m_contentType = headers[0]; - string dataString = uriString.Substring(commaIndex + 1); - m_data = Convert.FromBase64String(dataString); - } - - public override string ContentType - { - get { return m_contentType; } - set - { - throw new NotSupportedException(); - } - } - - public override long ContentLength - { - get { return m_data.Length; } - set - { - throw new NotSupportedException(); - } - } - - public override Stream GetResponseStream() - { - return new MemoryStream(m_data); - } - } - - public WebRequest Create(Uri uri) - { - return new DataWebRequest(uri); + return new DataWebResponse(_uri); } } + + class DataWebResponse : WebResponse + { + private readonly string _contentType; + private readonly byte[] _data; + + public DataWebResponse(Uri uri) + { + string uriString = uri.AbsoluteUri; + + int commaIndex = uriString.IndexOf(','); + var headers = uriString.Substring(0, commaIndex).Split(';'); + _contentType = headers[0]; + string dataString = uriString.Substring(commaIndex + 1); + _data = Convert.FromBase64String(dataString); + } + + public override string ContentType + { + get { return _contentType; } + set + { + throw new NotSupportedException(); + } + } + + public override long ContentLength + { + get { return _data.Length; } + set + { + throw new NotSupportedException(); + } + } + + public override Stream GetResponseStream() + { + return new MemoryStream(_data); + } + } + + public WebRequest Create(Uri uri) + { + return new DataWebRequest(uri); + } } diff --git a/Flow.Launcher/Helper/ErrorReporting.cs b/Flow.Launcher/Helper/ErrorReporting.cs index b5da2efad..5b79c520d 100644 --- a/Flow.Launcher/Helper/ErrorReporting.cs +++ b/Flow.Launcher/Helper/ErrorReporting.cs @@ -4,45 +4,52 @@ using NLog; using Flow.Launcher.Infrastructure; using Flow.Launcher.Infrastructure.Exception; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public static class ErrorReporting { - public static class ErrorReporting + private static void Report(Exception e) { - private static void Report(Exception e) - { - var logger = LogManager.GetLogger("UnHandledException"); - logger.Fatal(ExceptionFormatter.FormatExcpetion(e)); - var reportWindow = new ReportWindow(e); - reportWindow.Show(); - } + var logger = LogManager.GetLogger("UnHandledException"); + logger.Fatal(ExceptionFormatter.FormatExcpetion(e)); + var reportWindow = new ReportWindow(e); + reportWindow.Show(); + } - public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e) - { - //handle non-ui thread exceptions - Report((Exception)e.ExceptionObject); - } + public static void UnhandledExceptionHandle(object sender, UnhandledExceptionEventArgs e) + { + //handle non-ui thread exceptions + Report((Exception)e.ExceptionObject); + } - public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) - { - //handle ui thread exceptions - Report(e.Exception); - //prevent application exist, so the user can copy prompted error info - e.Handled = true; - } - - public static string RuntimeInfo() - { - var info = $"\nFlow Launcher version: {Constant.Version}" + - $"\nOS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()}" + - $"\nIntPtr Length: {IntPtr.Size}" + - $"\nx64: {Environment.Is64BitOperatingSystem}"; - return info; - } + public static void DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) + { + //handle ui thread exceptions + Report(e.Exception); + //prevent application exist, so the user can copy prompted error info + e.Handled = true; + } - public static string DependenciesInfo() - { - var info = $"\nPython Path: {Constant.PythonPath}\nNode Path: {Constant.NodePath}"; - return info; - } + public static string RuntimeInfo() + { + var info = + $""" + + Flow Launcher version: {Constant.Version} + OS Version: {ExceptionFormatter.GetWindowsFullVersionFromRegistry()} + IntPtr Length: {IntPtr.Size} + x64: {Environment.Is64BitOperatingSystem} + """; + return info; + } + + public static string DependenciesInfo() + { + var info = $""" + + Python Path: {Constant.PythonPath} + Node Path: {Constant.NodePath} + """; + return info; } } diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 3949d2828..21ddf276a 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -7,97 +7,96 @@ using Flow.Launcher.Core.Resource; using System.Windows; using Flow.Launcher.ViewModel; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +internal static class HotKeyMapper { - internal static class HotKeyMapper + private static Settings _settings; + private static MainViewModel _mainViewModel; + + internal static void Initialize(MainViewModel mainVM) { - private static Settings settings; - private static MainViewModel mainViewModel; + _mainViewModel = mainVM; + _settings = _mainViewModel.Settings; - internal static void Initialize(MainViewModel mainVM) + SetHotkey(_settings.Hotkey, OnToggleHotkey); + LoadCustomPluginHotkey(); + } + + internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + { + if (!_mainViewModel.ShouldIgnoreHotkeys()) + _mainViewModel.ToggleFlowLauncher(); + } + + private static void SetHotkey(string hotkeyStr, EventHandler action) + { + var hotkey = new HotkeyModel(hotkeyStr); + SetHotkey(hotkey, action); + } + + internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) + { + string hotkeyStr = hotkey.ToString(); + try { - mainViewModel = mainVM; - settings = mainViewModel.Settings; - - SetHotkey(settings.Hotkey, OnToggleHotkey); - LoadCustomPluginHotkey(); + HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); } - - internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + catch (Exception) { - if (!mainViewModel.ShouldIgnoreHotkeys()) - mainViewModel.ToggleFlowLauncher(); - } - - private static void SetHotkey(string hotkeyStr, EventHandler action) - { - var hotkey = new HotkeyModel(hotkeyStr); - SetHotkey(hotkey, action); - } - - internal static void SetHotkey(HotkeyModel hotkey, EventHandler action) - { - string hotkeyStr = hotkey.ToString(); - try - { - HotkeyManager.Current.AddOrReplace(hotkeyStr, hotkey.CharKey, hotkey.ModifierKeys, action); - } - catch (Exception) - { - string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); - string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); - MessageBox.Show(errorMsg,errorMsgTitle); - } - } - - internal static void RemoveHotkey(string hotkeyStr) - { - if (!string.IsNullOrEmpty(hotkeyStr)) - { - HotkeyManager.Current.Remove(hotkeyStr); - } - } - - internal static void LoadCustomPluginHotkey() - { - if (settings.CustomPluginHotkeys == null) - return; - - foreach (CustomPluginHotkey hotkey in settings.CustomPluginHotkeys) - { - SetCustomQueryHotkey(hotkey); - } - } - - internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) - { - SetHotkey(hotkey.Hotkey, (s, e) => - { - if (mainViewModel.ShouldIgnoreHotkeys()) - return; - - mainViewModel.Show(); - mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true); - }); - } - - internal static bool CheckAvailability(HotkeyModel currentHotkey) - { - try - { - HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); - - return true; - } - catch - { - } - finally - { - HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); - } - - return false; + string errorMsg = string.Format(InternationalizationManager.Instance.GetTranslation("registerHotkeyFailed"), hotkeyStr); + string errorMsgTitle = InternationalizationManager.Instance.GetTranslation("MessageBoxTitle"); + MessageBox.Show(errorMsg,errorMsgTitle); } } + + internal static void RemoveHotkey(string hotkeyStr) + { + if (!string.IsNullOrEmpty(hotkeyStr)) + { + HotkeyManager.Current.Remove(hotkeyStr); + } + } + + internal static void LoadCustomPluginHotkey() + { + if (_settings.CustomPluginHotkeys == null) + return; + + foreach (CustomPluginHotkey hotkey in _settings.CustomPluginHotkeys) + { + SetCustomQueryHotkey(hotkey); + } + } + + internal static void SetCustomQueryHotkey(CustomPluginHotkey hotkey) + { + SetHotkey(hotkey.Hotkey, (s, e) => + { + if (_mainViewModel.ShouldIgnoreHotkeys()) + return; + + _mainViewModel.Show(); + _mainViewModel.ChangeQueryText(hotkey.ActionKeyword, true); + }); + } + + internal static bool CheckAvailability(HotkeyModel currentHotkey) + { + try + { + HotkeyManager.Current.AddOrReplace("HotkeyAvailabilityTest", currentHotkey.CharKey, currentHotkey.ModifierKeys, (sender, e) => { }); + + return true; + } + catch + { + } + finally + { + HotkeyManager.Current.Remove("HotkeyAvailabilityTest"); + } + + return false; + } } diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index 739fed378..380da1647 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -12,375 +12,374 @@ using System.Windows; // http://blogs.microsoft.co.il/arik/2010/05/28/wpf-single-instance-application/ // modified to allow single instace restart -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +internal enum WM { - internal enum WM + NULL = 0x0000, + CREATE = 0x0001, + DESTROY = 0x0002, + MOVE = 0x0003, + SIZE = 0x0005, + ACTIVATE = 0x0006, + SETFOCUS = 0x0007, + KILLFOCUS = 0x0008, + ENABLE = 0x000A, + SETREDRAW = 0x000B, + SETTEXT = 0x000C, + GETTEXT = 0x000D, + GETTEXTLENGTH = 0x000E, + PAINT = 0x000F, + CLOSE = 0x0010, + QUERYENDSESSION = 0x0011, + QUIT = 0x0012, + QUERYOPEN = 0x0013, + ERASEBKGND = 0x0014, + SYSCOLORCHANGE = 0x0015, + SHOWWINDOW = 0x0018, + ACTIVATEAPP = 0x001C, + SETCURSOR = 0x0020, + MOUSEACTIVATE = 0x0021, + CHILDACTIVATE = 0x0022, + QUEUESYNC = 0x0023, + GETMINMAXINFO = 0x0024, + + WINDOWPOSCHANGING = 0x0046, + WINDOWPOSCHANGED = 0x0047, + + CONTEXTMENU = 0x007B, + STYLECHANGING = 0x007C, + STYLECHANGED = 0x007D, + DISPLAYCHANGE = 0x007E, + GETICON = 0x007F, + SETICON = 0x0080, + NCCREATE = 0x0081, + NCDESTROY = 0x0082, + NCCALCSIZE = 0x0083, + NCHITTEST = 0x0084, + NCPAINT = 0x0085, + NCACTIVATE = 0x0086, + GETDLGCODE = 0x0087, + SYNCPAINT = 0x0088, + NCMOUSEMOVE = 0x00A0, + NCLBUTTONDOWN = 0x00A1, + NCLBUTTONUP = 0x00A2, + NCLBUTTONDBLCLK = 0x00A3, + NCRBUTTONDOWN = 0x00A4, + NCRBUTTONUP = 0x00A5, + NCRBUTTONDBLCLK = 0x00A6, + NCMBUTTONDOWN = 0x00A7, + NCMBUTTONUP = 0x00A8, + NCMBUTTONDBLCLK = 0x00A9, + + SYSKEYDOWN = 0x0104, + SYSKEYUP = 0x0105, + SYSCHAR = 0x0106, + SYSDEADCHAR = 0x0107, + COMMAND = 0x0111, + SYSCOMMAND = 0x0112, + + MOUSEMOVE = 0x0200, + LBUTTONDOWN = 0x0201, + LBUTTONUP = 0x0202, + LBUTTONDBLCLK = 0x0203, + RBUTTONDOWN = 0x0204, + RBUTTONUP = 0x0205, + RBUTTONDBLCLK = 0x0206, + MBUTTONDOWN = 0x0207, + MBUTTONUP = 0x0208, + MBUTTONDBLCLK = 0x0209, + MOUSEWHEEL = 0x020A, + XBUTTONDOWN = 0x020B, + XBUTTONUP = 0x020C, + XBUTTONDBLCLK = 0x020D, + MOUSEHWHEEL = 0x020E, + + + CAPTURECHANGED = 0x0215, + + ENTERSIZEMOVE = 0x0231, + EXITSIZEMOVE = 0x0232, + + IME_SETCONTEXT = 0x0281, + IME_NOTIFY = 0x0282, + IME_CONTROL = 0x0283, + IME_COMPOSITIONFULL = 0x0284, + IME_SELECT = 0x0285, + IME_CHAR = 0x0286, + IME_REQUEST = 0x0288, + IME_KEYDOWN = 0x0290, + IME_KEYUP = 0x0291, + + NCMOUSELEAVE = 0x02A2, + + DWMCOMPOSITIONCHANGED = 0x031E, + DWMNCRENDERINGCHANGED = 0x031F, + DWMCOLORIZATIONCOLORCHANGED = 0x0320, + DWMWINDOWMAXIMIZEDCHANGE = 0x0321, + + #region Windows 7 + DWMSENDICONICTHUMBNAIL = 0x0323, + DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, + #endregion + + USER = 0x0400, + + // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. + // It's relatively safe to reuse. + TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 + APP = 0x8000 +} + +[SuppressUnmanagedCodeSecurity] +internal static class NativeMethods +{ + /// + /// Delegate declaration that matches WndProc signatures. + /// + public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); + + [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] + private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); + + + [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] + private static extern IntPtr _LocalFree(IntPtr hMem); + + + public static string[] CommandLineToArgvW(string cmdLine) { - NULL = 0x0000, - CREATE = 0x0001, - DESTROY = 0x0002, - MOVE = 0x0003, - SIZE = 0x0005, - ACTIVATE = 0x0006, - SETFOCUS = 0x0007, - KILLFOCUS = 0x0008, - ENABLE = 0x000A, - SETREDRAW = 0x000B, - SETTEXT = 0x000C, - GETTEXT = 0x000D, - GETTEXTLENGTH = 0x000E, - PAINT = 0x000F, - CLOSE = 0x0010, - QUERYENDSESSION = 0x0011, - QUIT = 0x0012, - QUERYOPEN = 0x0013, - ERASEBKGND = 0x0014, - SYSCOLORCHANGE = 0x0015, - SHOWWINDOW = 0x0018, - ACTIVATEAPP = 0x001C, - SETCURSOR = 0x0020, - MOUSEACTIVATE = 0x0021, - CHILDACTIVATE = 0x0022, - QUEUESYNC = 0x0023, - GETMINMAXINFO = 0x0024, + IntPtr argv = IntPtr.Zero; + try + { + int numArgs = 0; - WINDOWPOSCHANGING = 0x0046, - WINDOWPOSCHANGED = 0x0047, + argv = _CommandLineToArgvW(cmdLine, out numArgs); + if (argv == IntPtr.Zero) + { + throw new Win32Exception(); + } + var result = new string[numArgs]; - CONTEXTMENU = 0x007B, - STYLECHANGING = 0x007C, - STYLECHANGED = 0x007D, - DISPLAYCHANGE = 0x007E, - GETICON = 0x007F, - SETICON = 0x0080, - NCCREATE = 0x0081, - NCDESTROY = 0x0082, - NCCALCSIZE = 0x0083, - NCHITTEST = 0x0084, - NCPAINT = 0x0085, - NCACTIVATE = 0x0086, - GETDLGCODE = 0x0087, - SYNCPAINT = 0x0088, - NCMOUSEMOVE = 0x00A0, - NCLBUTTONDOWN = 0x00A1, - NCLBUTTONUP = 0x00A2, - NCLBUTTONDBLCLK = 0x00A3, - NCRBUTTONDOWN = 0x00A4, - NCRBUTTONUP = 0x00A5, - NCRBUTTONDBLCLK = 0x00A6, - NCMBUTTONDOWN = 0x00A7, - NCMBUTTONUP = 0x00A8, - NCMBUTTONDBLCLK = 0x00A9, + for (int i = 0; i < numArgs; i++) + { + IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); + result[i] = Marshal.PtrToStringUni(currArg); + } - SYSKEYDOWN = 0x0104, - SYSKEYUP = 0x0105, - SYSCHAR = 0x0106, - SYSDEADCHAR = 0x0107, - COMMAND = 0x0111, - SYSCOMMAND = 0x0112, + return result; + } + finally + { - MOUSEMOVE = 0x0200, - LBUTTONDOWN = 0x0201, - LBUTTONUP = 0x0202, - LBUTTONDBLCLK = 0x0203, - RBUTTONDOWN = 0x0204, - RBUTTONUP = 0x0205, - RBUTTONDBLCLK = 0x0206, - MBUTTONDOWN = 0x0207, - MBUTTONUP = 0x0208, - MBUTTONDBLCLK = 0x0209, - MOUSEWHEEL = 0x020A, - XBUTTONDOWN = 0x020B, - XBUTTONUP = 0x020C, - XBUTTONDBLCLK = 0x020D, - MOUSEHWHEEL = 0x020E, - - - CAPTURECHANGED = 0x0215, - - ENTERSIZEMOVE = 0x0231, - EXITSIZEMOVE = 0x0232, - - IME_SETCONTEXT = 0x0281, - IME_NOTIFY = 0x0282, - IME_CONTROL = 0x0283, - IME_COMPOSITIONFULL = 0x0284, - IME_SELECT = 0x0285, - IME_CHAR = 0x0286, - IME_REQUEST = 0x0288, - IME_KEYDOWN = 0x0290, - IME_KEYUP = 0x0291, - - NCMOUSELEAVE = 0x02A2, - - DWMCOMPOSITIONCHANGED = 0x031E, - DWMNCRENDERINGCHANGED = 0x031F, - DWMCOLORIZATIONCOLORCHANGED = 0x0320, - DWMWINDOWMAXIMIZEDCHANGE = 0x0321, - - #region Windows 7 - DWMSENDICONICTHUMBNAIL = 0x0323, - DWMSENDICONICLIVEPREVIEWBITMAP = 0x0326, - #endregion - - USER = 0x0400, - - // This is the hard-coded message value used by WinForms for Shell_NotifyIcon. - // It's relatively safe to reuse. - TRAYMOUSEMESSAGE = 0x800, //WM_USER + 1024 - APP = 0x8000 + IntPtr p = _LocalFree(argv); + // Otherwise LocalFree failed. + // Assert.AreEqual(IntPtr.Zero, p); + } } - [SuppressUnmanagedCodeSecurity] - internal static class NativeMethods - { - /// - /// Delegate declaration that matches WndProc signatures. - /// - public delegate IntPtr MessageHandler(WM uMsg, IntPtr wParam, IntPtr lParam, out bool handled); +} - [DllImport("shell32.dll", EntryPoint = "CommandLineToArgvW", CharSet = CharSet.Unicode)] - private static extern IntPtr _CommandLineToArgvW([MarshalAs(UnmanagedType.LPWStr)] string cmdLine, out int numArgs); +public interface ISingleInstanceApp +{ + void OnSecondAppStarted(); +} +/// +/// This class checks to make sure that only one instance of +/// this application is running at a time. +/// +/// +/// Note: this class should be used with some caution, because it does no +/// security checking. For example, if one instance of an app that uses this class +/// is running as Administrator, any other instance, even if it is not +/// running as Administrator, can activate it with command line arguments. +/// For most apps, this will not be much of an issue. +/// +public static class SingleInstance + where TApplication: Application , ISingleInstanceApp - [DllImport("kernel32.dll", EntryPoint = "LocalFree", SetLastError = true)] - private static extern IntPtr _LocalFree(IntPtr hMem); - - - public static string[] CommandLineToArgvW(string cmdLine) - { - IntPtr argv = IntPtr.Zero; - try - { - int numArgs = 0; - - argv = _CommandLineToArgvW(cmdLine, out numArgs); - if (argv == IntPtr.Zero) - { - throw new Win32Exception(); - } - var result = new string[numArgs]; - - for (int i = 0; i < numArgs; i++) - { - IntPtr currArg = Marshal.ReadIntPtr(argv, i * Marshal.SizeOf(typeof(IntPtr))); - result[i] = Marshal.PtrToStringUni(currArg); - } - - return result; - } - finally - { - - IntPtr p = _LocalFree(argv); - // Otherwise LocalFree failed. - // Assert.AreEqual(IntPtr.Zero, p); - } - } - - } - - public interface ISingleInstanceApp - { - void OnSecondAppStarted(); - } +{ + #region Private Fields /// - /// This class checks to make sure that only one instance of - /// this application is running at a time. + /// String delimiter used in channel names. /// - /// - /// Note: this class should be used with some caution, because it does no - /// security checking. For example, if one instance of an app that uses this class - /// is running as Administrator, any other instance, even if it is not - /// running as Administrator, can activate it with command line arguments. - /// For most apps, this will not be much of an issue. - /// - public static class SingleInstance - where TApplication: Application , ISingleInstanceApp - + private const string Delimiter = ":"; + + /// + /// Suffix to the channel name. + /// + private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; + + /// + /// Application mutex. + /// + internal static Mutex singleInstanceMutex; + + #endregion + + #region Public Properties + + #endregion + + #region Public Methods + + /// + /// Checks if the instance of the application attempting to start is the first instance. + /// If not, activates the first instance. + /// + /// True if this is the first instance of the application. + public static bool InitializeAsFirstInstance( string uniqueName ) { - #region Private Fields + // Build unique application Id and the IPC channel name. + string applicationIdentifier = uniqueName + Environment.UserName; - /// - /// String delimiter used in channel names. - /// - private const string Delimiter = ":"; + string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); - /// - /// Suffix to the channel name. - /// - private const string ChannelNameSuffix = "SingeInstanceIPCChannel"; - - /// - /// Application mutex. - /// - internal static Mutex singleInstanceMutex; - - #endregion - - #region Public Properties - - #endregion - - #region Public Methods - - /// - /// Checks if the instance of the application attempting to start is the first instance. - /// If not, activates the first instance. - /// - /// True if this is the first instance of the application. - public static bool InitializeAsFirstInstance( string uniqueName ) + // Create mutex based on unique application Id to check if this is the first instance of the application. + bool firstInstance; + singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); + if (firstInstance) { - // Build unique application Id and the IPC channel name. - string applicationIdentifier = uniqueName + Environment.UserName; - - string channelName = String.Concat(applicationIdentifier, Delimiter, ChannelNameSuffix); - - // Create mutex based on unique application Id to check if this is the first instance of the application. - bool firstInstance; - singleInstanceMutex = new Mutex(true, applicationIdentifier, out firstInstance); - if (firstInstance) - { - _ = CreateRemoteService(channelName); - return true; - } - else - { - _ = SignalFirstInstance(channelName); - return false; - } + _ = CreateRemoteService(channelName); + return true; } - - /// - /// Cleans up single-instance code, clearing shared resources, mutexes, etc. - /// - public static void Cleanup() + else { - singleInstanceMutex?.ReleaseMutex(); + _ = SignalFirstInstance(channelName); + return false; } - - #endregion - - #region Private Methods - - /// - /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. - /// - /// List of command line arg strings. - private static IList GetCommandLineArgs( string uniqueApplicationName ) - { - string[] args = null; - - try - { - // The application was not clickonce deployed, get args from standard API's - args = Environment.GetCommandLineArgs(); - } - catch (NotSupportedException) - { - - // The application was clickonce deployed - // Clickonce deployed apps cannot recieve traditional commandline arguments - // As a workaround commandline arguments can be written to a shared location before - // the app is launched and the app can obtain its commandline arguments from the - // shared location - string appFolderPath = Path.Combine( - Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); - - string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); - if (File.Exists(cmdLinePath)) - { - try - { - using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) - { - args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); - } - - File.Delete(cmdLinePath); - } - catch (IOException) - { - } - } - } - - if (args == null) - { - args = new string[] { }; - } - - return new List(args); - } - - /// - /// Creates a remote server pipe for communication. - /// Once receives signal from client, will activate first instance. - /// - /// Application's IPC channel name. - private static async Task CreateRemoteService(string channelName) - { - using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) - { - while(true) - { - // Wait for connection to the pipe - await pipeServer.WaitForConnectionAsync(); - if (Application.Current != null) - { - // Do an asynchronous call to ActivateFirstInstance function - Application.Current.Dispatcher.Invoke(ActivateFirstInstance); - } - // Disconect client - pipeServer.Disconnect(); - } - } - } - - /// - /// Creates a client pipe and sends a signal to server to launch first instance - /// - /// Application's IPC channel name. - /// - /// Command line arguments for the second instance, passed to the first instance to take appropriate action. - /// - private static async Task SignalFirstInstance(string channelName) - { - // Create a client pipe connected to server - using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) - { - // Connect to the available pipe - await pipeClient.ConnectAsync(0); - } - } - - /// - /// Callback for activating first instance of the application. - /// - /// Callback argument. - /// Always null. - private static object ActivateFirstInstanceCallback(object o) - { - ActivateFirstInstance(); - return null; - } - - /// - /// Activates the first instance of the application with arguments from a second instance. - /// - /// List of arguments to supply the first instance of the application. - private static void ActivateFirstInstance() - { - // Set main window state and process command line args - if (Application.Current == null) - { - return; - } - - ((TApplication)Application.Current).OnSecondAppStarted(); - } - - #endregion } + + /// + /// Cleans up single-instance code, clearing shared resources, mutexes, etc. + /// + public static void Cleanup() + { + singleInstanceMutex?.ReleaseMutex(); + } + + #endregion + + #region Private Methods + + /// + /// Gets command line args - for ClickOnce deployed applications, command line args may not be passed directly, they have to be retrieved. + /// + /// List of command line arg strings. + private static IList GetCommandLineArgs( string uniqueApplicationName ) + { + string[] args = null; + + try + { + // The application was not clickonce deployed, get args from standard API's + args = Environment.GetCommandLineArgs(); + } + catch (NotSupportedException) + { + + // The application was clickonce deployed + // Clickonce deployed apps cannot recieve traditional commandline arguments + // As a workaround commandline arguments can be written to a shared location before + // the app is launched and the app can obtain its commandline arguments from the + // shared location + string appFolderPath = Path.Combine( + Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), uniqueApplicationName); + + string cmdLinePath = Path.Combine(appFolderPath, "cmdline.txt"); + if (File.Exists(cmdLinePath)) + { + try + { + using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) + { + args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); + } + + File.Delete(cmdLinePath); + } + catch (IOException) + { + } + } + } + + if (args == null) + { + args = new string[] { }; + } + + return new List(args); + } + + /// + /// Creates a remote server pipe for communication. + /// Once receives signal from client, will activate first instance. + /// + /// Application's IPC channel name. + private static async Task CreateRemoteService(string channelName) + { + using (NamedPipeServerStream pipeServer = new NamedPipeServerStream(channelName, PipeDirection.In)) + { + while(true) + { + // Wait for connection to the pipe + await pipeServer.WaitForConnectionAsync(); + if (Application.Current != null) + { + // Do an asynchronous call to ActivateFirstInstance function + Application.Current.Dispatcher.Invoke(ActivateFirstInstance); + } + // Disconect client + pipeServer.Disconnect(); + } + } + } + + /// + /// Creates a client pipe and sends a signal to server to launch first instance + /// + /// Application's IPC channel name. + /// + /// Command line arguments for the second instance, passed to the first instance to take appropriate action. + /// + private static async Task SignalFirstInstance(string channelName) + { + // Create a client pipe connected to server + using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", channelName, PipeDirection.Out)) + { + // Connect to the available pipe + await pipeClient.ConnectAsync(0); + } + } + + /// + /// Callback for activating first instance of the application. + /// + /// Callback argument. + /// Always null. + private static object ActivateFirstInstanceCallback(object o) + { + ActivateFirstInstance(); + return null; + } + + /// + /// Activates the first instance of the application with arguments from a second instance. + /// + /// List of arguments to supply the first instance of the application. + private static void ActivateFirstInstance() + { + // Set main window state and process command line args + if (Application.Current == null) + { + return; + } + + ((TApplication)Application.Current).OnSecondAppStarted(); + } + + #endregion } diff --git a/Flow.Launcher/Helper/SingletonWindowOpener.cs b/Flow.Launcher/Helper/SingletonWindowOpener.cs index 8efc9be99..b5c2d8b55 100644 --- a/Flow.Launcher/Helper/SingletonWindowOpener.cs +++ b/Flow.Launcher/Helper/SingletonWindowOpener.cs @@ -2,27 +2,26 @@ using System.Linq; using System.Windows; -namespace Flow.Launcher.Helper -{ - public static class SingletonWindowOpener - { - public static T Open(params object[] args) where T : Window - { - var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) - ?? (T)Activator.CreateInstance(typeof(T), args); - - // Fix UI bug - // Add `window.WindowState = WindowState.Normal` - // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar - // Not sure why this works tho - // Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`) - // https://stackoverflow.com/a/59719760/4230390 - window.WindowState = WindowState.Normal; - window.Show(); - - window.Focus(); +namespace Flow.Launcher.Helper; - return (T)window; - } +public static class SingletonWindowOpener +{ + public static T Open(params object[] args) where T : Window + { + var window = Application.Current.Windows.OfType().FirstOrDefault(x => x.GetType() == typeof(T)) + ?? (T)Activator.CreateInstance(typeof(T), args); + + // Fix UI bug + // Add `window.WindowState = WindowState.Normal` + // If only use `window.Show()`, Settings-window doesn't show when minimized in taskbar + // Not sure why this works tho + // Probably because, when `.Show()` fails, `window.WindowState == Minimized` (not `Normal`) + // https://stackoverflow.com/a/59719760/4230390 + window.WindowState = WindowState.Normal; + window.Show(); + + window.Focus(); + + return (T)window; } -} \ No newline at end of file +} diff --git a/Flow.Launcher/Helper/SyntaxSugars.cs b/Flow.Launcher/Helper/SyntaxSugars.cs index 7e0349422..41250dadf 100644 --- a/Flow.Launcher/Helper/SyntaxSugars.cs +++ b/Flow.Launcher/Helper/SyntaxSugars.cs @@ -1,24 +1,23 @@ using System; -namespace Flow.Launcher.Helper -{ - public static class SyntaxSugars - { - public static TResult CallOrRescueDefault(Func callback) - { - return CallOrRescueDefault(callback, default(TResult)); - } +namespace Flow.Launcher.Helper; - public static TResult CallOrRescueDefault(Func callback, TResult def) +public static class SyntaxSugars +{ + public static TResult CallOrRescueDefault(Func callback) + { + return CallOrRescueDefault(callback, default(TResult)); + } + + public static TResult CallOrRescueDefault(Func callback, TResult def) + { + try { - try - { - return callback(); - } - catch - { - return def; - } + return callback(); + } + catch + { + return def; } } } diff --git a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs index 9e5d77283..e08e227cc 100644 --- a/Flow.Launcher/Helper/WallpaperPathRetrieval.cs +++ b/Flow.Launcher/Helper/WallpaperPathRetrieval.cs @@ -5,44 +5,43 @@ using System.Text; using System.Windows.Media; using Microsoft.Win32; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public static class WallpaperPathRetrieval { - public static class WallpaperPathRetrieval + [DllImport("user32.dll", CharSet = CharSet.Unicode)] + private static extern Int32 SystemParametersInfo(UInt32 action, + Int32 uParam, StringBuilder vParam, UInt32 winIni); + private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73; + private static int MAX_PATH = 260; + + public static string GetWallpaperPath() { - [DllImport("user32.dll", CharSet = CharSet.Unicode)] - private static extern Int32 SystemParametersInfo(UInt32 action, - Int32 uParam, StringBuilder vParam, UInt32 winIni); - private static readonly UInt32 SPI_GETDESKWALLPAPER = 0x73; - private static int MAX_PATH = 260; + var wallpaper = new StringBuilder(MAX_PATH); + SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0); - public static string GetWallpaperPath() + var str = wallpaper.ToString(); + if (string.IsNullOrEmpty(str)) + return null; + + return str; + } + + public static Color GetWallpaperColor() + { + RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Control Panel\Colors", true); + var result = key?.GetValue("Background", null); + if (result is string strResult) { - var wallpaper = new StringBuilder(MAX_PATH); - SystemParametersInfo(SPI_GETDESKWALLPAPER, MAX_PATH, wallpaper, 0); - - var str = wallpaper.ToString(); - if (string.IsNullOrEmpty(str)) - return null; - - return str; - } - - public static Color GetWallpaperColor() - { - RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Colors", true); - var result = key.GetValue(@"Background", null); - if (result != null && result is string) + try + { + var parts = strResult.Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList(); + return Color.FromRgb(parts[0], parts[1], parts[2]); + } + catch { - try - { - var parts = result.ToString().Trim().Split(new[] {' '}, 3).Select(byte.Parse).ToList(); - return Color.FromRgb(parts[0], parts[1], parts[2]); - } - catch - { - } } - return Colors.Transparent; } + return Colors.Transparent; } } diff --git a/Flow.Launcher/Helper/WindowsInteropHelper.cs b/Flow.Launcher/Helper/WindowsInteropHelper.cs index 16a96cd64..89fbec967 100644 --- a/Flow.Launcher/Helper/WindowsInteropHelper.cs +++ b/Flow.Launcher/Helper/WindowsInteropHelper.cs @@ -8,156 +8,152 @@ using System.Windows.Interop; using System.Windows.Media; using Point = System.Windows.Point; -namespace Flow.Launcher.Helper +namespace Flow.Launcher.Helper; + +public class WindowsInteropHelper { - public class WindowsInteropHelper + private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style + private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu + private static IntPtr _hwnd_shell; + private static IntPtr _hwnd_desktop; + + //Accessors for shell and desktop handlers + //Will set the variables once and then will return them + private static IntPtr HWND_SHELL { - private const int GWL_STYLE = -16; //WPF's Message code for Title Bar's Style - private const int WS_SYSMENU = 0x80000; //WPF's Message code for System Menu - private static IntPtr _hwnd_shell; - private static IntPtr _hwnd_desktop; - - //Accessors for shell and desktop handlers - //Will set the variables once and then will return them - private static IntPtr HWND_SHELL + get { - get - { - return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow(); - } + return _hwnd_shell != IntPtr.Zero ? _hwnd_shell : _hwnd_shell = GetShellWindow(); } - private static IntPtr HWND_DESKTOP + } + private static IntPtr HWND_DESKTOP + { + get { - get - { - return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow(); - } + return _hwnd_desktop != IntPtr.Zero ? _hwnd_desktop : _hwnd_desktop = GetDesktopWindow(); } + } - [DllImport("user32.dll", SetLastError = true)] - internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - - [DllImport("user32.dll")] - internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowLong(IntPtr hWnd, int nIndex); - [DllImport("user32.dll")] - internal static extern IntPtr GetForegroundWindow(); + [DllImport("user32.dll")] + internal static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); - [DllImport("user32.dll")] - internal static extern IntPtr GetDesktopWindow(); + [DllImport("user32.dll")] + internal static extern IntPtr GetForegroundWindow(); - [DllImport("user32.dll")] - internal static extern IntPtr GetShellWindow(); + [DllImport("user32.dll")] + internal static extern IntPtr GetDesktopWindow(); - [DllImport("user32.dll", SetLastError = true)] - internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc); + [DllImport("user32.dll")] + internal static extern IntPtr GetShellWindow(); - [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] - internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + [DllImport("user32.dll", SetLastError = true)] + internal static extern int GetWindowRect(IntPtr hwnd, out RECT rc); - [DllImport("user32.DLL")] - public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] + internal static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.DLL")] + public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow); - const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass"; - const string WINDOW_CLASS_WINTAB = "Flip3D"; - const string WINDOW_CLASS_PROGMAN = "Progman"; - const string WINDOW_CLASS_WORKERW = "WorkerW"; + const string WINDOW_CLASS_CONSOLE = "ConsoleWindowClass"; + const string WINDOW_CLASS_WINTAB = "Flip3D"; + const string WINDOW_CLASS_PROGMAN = "Progman"; + const string WINDOW_CLASS_WORKERW = "WorkerW"; - public static bool IsWindowFullscreen() + public static bool IsWindowFullscreen() + { + //get current active window + IntPtr hWnd = GetForegroundWindow(); + + if (hWnd.Equals(IntPtr.Zero)) { - //get current active window - IntPtr hWnd = GetForegroundWindow(); - - if (!hWnd.Equals(IntPtr.Zero)) - { - //if current active window is NOT desktop or shell - if (!(hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL))) - { - StringBuilder sb = new StringBuilder(256); - GetClassName(hWnd, sb, sb.Capacity); - string windowClass = sb.ToString(); - - //for Win+Tab (Flip3D) - if (windowClass == WINDOW_CLASS_WINTAB) - { - return false; - } - - RECT appBounds; - GetWindowRect(hWnd, out appBounds); - - //for console (ConsoleWindowClass), we have to check for negative dimensions - if (windowClass == WINDOW_CLASS_CONSOLE) - { - return appBounds.Top < 0 && appBounds.Bottom < 0; - } - - //for desktop (Progman or WorkerW, depends on the system), we have to check - if (windowClass == WINDOW_CLASS_PROGMAN || windowClass == WINDOW_CLASS_WORKERW) - { - IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null); - hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView"); - if (!hWndDesktop.Equals(IntPtr.Zero)) - { - return false; - } - } - - Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; - if ((appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width) - { - return true; - } - } - } - return false; } - /// - /// disable windows toolbar's control box - /// this will also disable system menu with Alt+Space hotkey - /// - public static void DisableControlBox(Window win) + //if current active window is desktop or shell, exit early + if (hWnd.Equals(HWND_DESKTOP) || hWnd.Equals(HWND_SHELL)) { - var hwnd = new WindowInteropHelper(win).Handle; - SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); + return false; } - /// - /// Transforms pixels to Device Independent Pixels used by WPF - /// - /// current window, required to get presentation source - /// horizontal position in pixels - /// vertical position in pixels - /// point containing device independent pixels - public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY) + StringBuilder sb = new StringBuilder(256); + GetClassName(hWnd, sb, sb.Capacity); + string windowClass = sb.ToString(); + + //for Win+Tab (Flip3D) + if (windowClass == WINDOW_CLASS_WINTAB) { - Matrix matrix; - var source = PresentationSource.FromVisual(visual); - if (source != null) + return false; + } + + RECT appBounds; + GetWindowRect(hWnd, out appBounds); + + //for console (ConsoleWindowClass), we have to check for negative dimensions + if (windowClass == WINDOW_CLASS_CONSOLE) + { + return appBounds.Top < 0 && appBounds.Bottom < 0; + } + + //for desktop (Progman or WorkerW, depends on the system), we have to check + if (windowClass is WINDOW_CLASS_PROGMAN or WINDOW_CLASS_WORKERW) + { + IntPtr hWndDesktop = FindWindowEx(hWnd, IntPtr.Zero, "SHELLDLL_DefView", null); + hWndDesktop = FindWindowEx(hWndDesktop, IntPtr.Zero, "SysListView32", "FolderView"); + if (!hWndDesktop.Equals(IntPtr.Zero)) { - matrix = source.CompositionTarget.TransformFromDevice; + return false; } - else - { - using (var src = new HwndSource(new HwndSourceParameters())) - { - matrix = src.CompositionTarget.TransformFromDevice; - } - } - return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); } + Rectangle screenBounds = Screen.FromHandle(hWnd).Bounds; + return (appBounds.Bottom - appBounds.Top) == screenBounds.Height && (appBounds.Right - appBounds.Left) == screenBounds.Width; + } - [StructLayout(LayoutKind.Sequential)] - public struct RECT + /// + /// disable windows toolbar's control box + /// this will also disable system menu with Alt+Space hotkey + /// + public static void DisableControlBox(Window win) + { + var hwnd = new WindowInteropHelper(win).Handle; + SetWindowLong(hwnd, GWL_STYLE, GetWindowLong(hwnd, GWL_STYLE) & ~WS_SYSMENU); + } + + /// + /// Transforms pixels to Device Independent Pixels used by WPF + /// + /// current window, required to get presentation source + /// horizontal position in pixels + /// vertical position in pixels + /// point containing device independent pixels + public static Point TransformPixelsToDIP(Visual visual, double unitX, double unitY) + { + Matrix matrix; + var source = PresentationSource.FromVisual(visual); + if (source is not null) { - public int Left; - public int Top; - public int Right; - public int Bottom; + matrix = source.CompositionTarget.TransformFromDevice; } + else + { + using var src = new HwndSource(new HwndSourceParameters()); + matrix = src.CompositionTarget.TransformFromDevice; + } + return new Point((int)(matrix.M11 * unitX), (int)(matrix.M22 * unitY)); + } + + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; } } From f7bf681ee2146c19ef25ec319c455dc44462dec0 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Thu, 11 Apr 2024 13:54:50 +0600 Subject: [PATCH 027/151] Replace `using` block with a `using` declaration in `SingleInstance.cs` --- Flow.Launcher/Helper/SingleInstance.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Flow.Launcher/Helper/SingleInstance.cs b/Flow.Launcher/Helper/SingleInstance.cs index 380da1647..8e72e4266 100644 --- a/Flow.Launcher/Helper/SingleInstance.cs +++ b/Flow.Launcher/Helper/SingleInstance.cs @@ -293,10 +293,8 @@ public static class SingleInstance { try { - using (TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode)) - { - args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); - } + using TextReader reader = new StreamReader(cmdLinePath, Encoding.Unicode); + args = NativeMethods.CommandLineToArgvW(reader.ReadToEnd()); File.Delete(cmdLinePath); } From 914f4009566be72ee7d376be91ddabe48335afc7 Mon Sep 17 00:00:00 2001 From: Yusyuriv Date: Fri, 12 Apr 2024 17:13:45 +0600 Subject: [PATCH 028/151] WIP: new HotkeyControl --- Flow.Launcher/HotkeyControl.xaml | 62 +++++++++++++++------ Flow.Launcher/HotkeyControl.xaml.cs | 83 ++++++++++++++++++++++++++--- Flow.Launcher/SettingWindow.xaml | 14 +++-- Flow.Launcher/SettingWindow.xaml.cs | 37 ++----------- 4 files changed, 132 insertions(+), 64 deletions(-) diff --git a/Flow.Launcher/HotkeyControl.xaml b/Flow.Launcher/HotkeyControl.xaml index acf4a21ec..bea652d56 100644 --- a/Flow.Launcher/HotkeyControl.xaml +++ b/Flow.Launcher/HotkeyControl.xaml @@ -5,14 +5,10 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:input="clr-namespace:System.Windows.Input;assembly=PresentationCore" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - Height="24" - d:DesignHeight="300" + d:DesignHeight="45" d:DesignWidth="300" mc:Ignorable="d"> - - - - + - \ No newline at end of file + diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 3da66caeb..f08a80147 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,4 +1,6 @@ using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; @@ -9,14 +11,37 @@ using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure.Hotkey; using Flow.Launcher.Plugin; using System.Threading; +using NHotkey; namespace Flow.Launcher { - public partial class HotkeyControl : UserControl + public partial class HotkeyControl : UserControl, IDisposable, INotifyPropertyChanged { + public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register( + nameof(Hotkey), + typeof(string), + typeof(HotkeyControl), + new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault) + ); public HotkeyModel CurrentHotkey { get; private set; } public bool CurrentHotkeyAvailable { get; private set; } + public string Hotkey + { + get => (string)GetValue(HotkeyProperty); + set + { + SetValue(HotkeyProperty, value); + OnPropertyChanged(nameof(KeysToDisplay)); + } + } + + public string[] KeysToDisplay => Hotkey.Split(" + "); + + #nullable enable + public EventHandler? Action { get; set; } + #nullable restore + public event EventHandler HotkeyChanged; /// @@ -29,6 +54,28 @@ namespace Flow.Launcher public HotkeyControl() { InitializeComponent(); + Loaded += HotkeyControl_Loaded; + } + + private void HotkeyControl_LostFocus(object o, RoutedEventArgs routedEventArgs) + { + HotKeyMapper.SetHotkey(CurrentHotkey, Action); + } + + private void HotkeyControl_GotFocus(object o, RoutedEventArgs routedEventArgs) + { + HotKeyMapper.RemoveHotkey(Hotkey); + } + + private void HotkeyControl_Loaded(object sender, RoutedEventArgs e) + { + _ = SetHotkeyAsync(Hotkey, false); + + if (Action is not null) + { + GotFocus += HotkeyControl_GotFocus; + LostFocus += HotkeyControl_LostFocus; + } } private CancellationTokenSource hotkeyUpdateSource; @@ -68,8 +115,8 @@ namespace Flow.Launcher public async Task SetHotkeyAsync(HotkeyModel keyModel, bool triggerValidate = true) { - tbHotkey.Text = keyModel.ToString(); - tbHotkey.Select(tbHotkey.Text.Length, 0); + // tbHotkey.Text = keyModel.ToString(); + // tbHotkey.Select(tbHotkey.Text.Length, 0); if (triggerValidate) { @@ -86,6 +133,7 @@ namespace Flow.Launcher if (CurrentHotkeyAvailable) { CurrentHotkey = keyModel; + Hotkey = keyModel.ToString(); // To trigger LostFocus FocusManager.SetFocusedElement(FocusManager.GetFocusScope(this), null); Keyboard.ClearFocus(); @@ -94,9 +142,10 @@ namespace Flow.Launcher else { CurrentHotkey = keyModel; + Hotkey = keyModel.ToString(); } } - + public Task SetHotkeyAsync(string keyStr, bool triggerValidate = true) { return SetHotkeyAsync(new HotkeyModel(keyStr), triggerValidate); @@ -104,12 +153,12 @@ namespace Flow.Launcher private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); - public new bool IsFocused => tbHotkey.IsFocused; + // public new bool IsFocused => tbHotkey.IsFocused; private void tbHotkey_LostFocus(object sender, RoutedEventArgs e) { - tbHotkey.Text = CurrentHotkey?.ToString() ?? ""; - tbHotkey.Select(tbHotkey.Text.Length, 0); + // tbHotkey.Text = CurrentHotkey?.ToString() ?? ""; + // tbHotkey.Select(tbHotkey.Text.Length, 0); } private void tbHotkey_GotFocus(object sender, RoutedEventArgs e) @@ -137,5 +186,25 @@ namespace Flow.Launcher } tbMsg.Visibility = Visibility.Visible; } + + public void Dispose() + { + hotkeyUpdateSource?.Dispose(); + Loaded -= HotkeyControl_Loaded; + GotFocus -= HotkeyControl_GotFocus; + LostFocus -= HotkeyControl_LostFocus; + } + + public event PropertyChangedEventHandler PropertyChanged; + + protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + private void OnButtonClicked(object sender, RoutedEventArgs e) + { + // TODO + } } } diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 96f80f0c5..e1b42e3d3 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -693,7 +693,7 @@ - @@ -720,7 +720,7 @@ - + @@ -2657,9 +2657,8 @@ Margin="0,0,0,0" HorizontalAlignment="Right" HorizontalContentAlignment="Right" - GotFocus="OnHotkeyControlFocused" - Loaded="OnHotkeyControlLoaded" - LostFocus="OnHotkeyControlFocusLost" /> + Hotkey="{Binding Settings.Hotkey}" + Action="OnToggleHotkey" />  @@ -2684,8 +2683,7 @@ Margin="0,0,0,0" HorizontalAlignment="Right" HorizontalContentAlignment="Right" - Loaded="OnPreviewHotkeyControlLoaded" - LostFocus="OnPreviewHotkeyControlFocusLost" + Hotkey="{Binding Settings.PreviewHotkey}" ValidateKeyGesture="True" />  diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 7301be130..e7e9273bb 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -17,6 +17,7 @@ using System.Windows.Forms; using System.Windows.Input; using System.Windows.Interop; using System.Windows.Navigation; +using NHotkey; using Button = System.Windows.Controls.Button; using Control = System.Windows.Controls.Control; using KeyEventArgs = System.Windows.Input.KeyEventArgs; @@ -110,41 +111,9 @@ namespace Flow.Launcher #region Hotkey - private void OnHotkeyControlLoaded(object sender, RoutedEventArgs e) + private void OnToggleHotkey(object sender, HotkeyEventArgs e) { - _ = HotkeyControl.SetHotkeyAsync(viewModel.Settings.Hotkey, false); - } - - private void OnHotkeyControlFocused(object sender, RoutedEventArgs e) - { - HotKeyMapper.RemoveHotkey(settings.Hotkey); - } - - private void OnHotkeyControlFocusLost(object sender, RoutedEventArgs e) - { - if (HotkeyControl.CurrentHotkeyAvailable) - { - HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey); - HotKeyMapper.RemoveHotkey(settings.Hotkey); - settings.Hotkey = HotkeyControl.CurrentHotkey.ToString(); - } - else - { - HotKeyMapper.SetHotkey(new HotkeyModel(settings.Hotkey), HotKeyMapper.OnToggleHotkey); - } - } - - private void OnPreviewHotkeyControlLoaded(object sender, RoutedEventArgs e) - { - _ = PreviewHotkeyControl.SetHotkeyAsync(settings.PreviewHotkey, false); - } - - private void OnPreviewHotkeyControlFocusLost(object sender, RoutedEventArgs e) - { - if (PreviewHotkeyControl.CurrentHotkeyAvailable) - { - settings.PreviewHotkey = PreviewHotkeyControl.CurrentHotkey.ToString(); - } + HotKeyMapper.OnToggleHotkey(sender, e); } private void OnDeleteCustomHotkeyClick(object sender, RoutedEventArgs e) From a0b9d7155844aaf3c5c15a4d8cc95d55bb7af919 Mon Sep 17 00:00:00 2001 From: DB p Date: Fri, 12 Apr 2024 20:56:27 +0900 Subject: [PATCH 029/151] Sizes and Margins Adjust --- Flow.Launcher/HotkeyControl.xaml | 13 ++++++++----- Flow.Launcher/SettingWindow.xaml | 30 ++++++++++-------------------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/Flow.Launcher/HotkeyControl.xaml b/Flow.Launcher/HotkeyControl.xaml index bea652d56..2dc051205 100644 --- a/Flow.Launcher/HotkeyControl.xaml +++ b/Flow.Launcher/HotkeyControl.xaml @@ -5,7 +5,6 @@ xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:input="clr-namespace:System.Windows.Input;assembly=PresentationCore" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" - d:DesignHeight="45" d:DesignWidth="300" mc:Ignorable="d"> @@ -39,11 +38,16 @@ - + + + + - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 2339941008f40344479c0ac318c223575c633a0f Mon Sep 17 00:00:00 2001 From: DB p Date: Sat, 13 Apr 2024 18:13:25 +0900 Subject: [PATCH 043/151] Fix Rec Button --- Flow.Launcher/HotkeyControl.xaml | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/Flow.Launcher/HotkeyControl.xaml b/Flow.Launcher/HotkeyControl.xaml index cf0fff2ee..1a347ab13 100644 --- a/Flow.Launcher/HotkeyControl.xaml +++ b/Flow.Launcher/HotkeyControl.xaml @@ -79,7 +79,7 @@ + + diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs new file mode 100644 index 000000000..d83b44fac --- /dev/null +++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Flow.Launcher.Resources.Controls +{ + public partial class HotkeyDisplay : UserControl + { + public HotkeyDisplay() + { + InitializeComponent(); + } + + public string Keys + { + get { return (string)GetValue(KeysValueProperty); } + set { SetValue(KeysValueProperty, value); } + } + public static readonly DependencyProperty KeysValueProperty = + DependencyProperty.Register("Keys", typeof(string), typeof(HotkeyDisplay), new PropertyMetadata(string.Empty)); + } +} diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 3e4f827a6..064e86382 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2722,7 +2722,9 @@ Title="{DynamicResource flowlauncherHotkey}" Icon="" Sub="{DynamicResource flowlauncherHotkeyToolTip}" - Type="Inside" /> + Type="Inside"> + + Date: Thu, 18 Apr 2024 00:32:10 +0900 Subject: [PATCH 069/151] Added Hotkey Display Control --- .../Resources/Controls/HotkeyDisplay.xaml | 61 ++++++++++++++++++- .../Resources/Controls/HotkeyDisplay.xaml.cs | 25 +++++++- Flow.Launcher/SettingWindow.xaml | 4 +- 3 files changed, 85 insertions(+), 5 deletions(-) diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml index 269f095f3..d4483393d 100644 --- a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml +++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml @@ -9,7 +9,62 @@ d:DesignWidth="800" mc:Ignorable="d"> - + + + + + + + + diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs index d83b44fac..3549647aa 100644 --- a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs +++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs @@ -1,5 +1,8 @@ using System; +using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; using System.Globalization; using System.Linq; using System.Text; @@ -13,6 +16,9 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using Windows.Media.Capture.Frames; +using static System.Windows.Forms.LinkLabel; +using static ABI.System.Collections.Generic.IReadOnlyDictionary_Delegates; namespace Flow.Launcher.Resources.Controls { @@ -21,6 +27,9 @@ namespace Flow.Launcher.Resources.Controls public HotkeyDisplay() { InitializeComponent(); + //List stringList =e.NewValue.Split('+').ToList(); + KeysControl.ItemsSource = Values; + } public string Keys @@ -29,6 +38,20 @@ namespace Flow.Launcher.Resources.Controls set { SetValue(KeysValueProperty, value); } } public static readonly DependencyProperty KeysValueProperty = - DependencyProperty.Register("Keys", typeof(string), typeof(HotkeyDisplay), new PropertyMetadata(string.Empty)); + DependencyProperty.Register("Keys", typeof(string), typeof(HotkeyDisplay), new PropertyMetadata(string.Empty, valueChanged)); + + private static void valueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + var control = d as UserControl; + if (null == control) return; // This should not be possible + + var newValue = e.NewValue as string; + if (null == newValue) return; + + //String[] Values = newValue.Split('+'); + //Debug.WriteLine(Values[0]); + } + + public ObservableCollection Values { get; set; } } } diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 064e86382..1beafd26b 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2729,7 +2729,9 @@ Title="{DynamicResource flowlauncherHotkey}" Icon="" Sub="{DynamicResource flowlauncherHotkeyToolTip}" - Type="Inside" /> + Type="Inside"> + + From 66125fce2c041472c667f9b2a400dd670aa4231f Mon Sep 17 00:00:00 2001 From: Hongtao Zhang Date: Wed, 17 Apr 2024 10:45:09 -0500 Subject: [PATCH 070/151] Split Key to display --- .../Resources/Controls/HotkeyDisplay.xaml.cs | 21 +++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs index 3549647aa..7ffd90d5f 100644 --- a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs +++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml.cs @@ -28,8 +28,8 @@ namespace Flow.Launcher.Resources.Controls { InitializeComponent(); //List stringList =e.NewValue.Split('+').ToList(); + Values = new ObservableCollection(); KeysControl.ItemsSource = Values; - } public string Keys @@ -37,10 +37,13 @@ namespace Flow.Launcher.Resources.Controls get { return (string)GetValue(KeysValueProperty); } set { SetValue(KeysValueProperty, value); } } - public static readonly DependencyProperty KeysValueProperty = - DependencyProperty.Register("Keys", typeof(string), typeof(HotkeyDisplay), new PropertyMetadata(string.Empty, valueChanged)); - private static void valueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + public static readonly DependencyProperty KeysValueProperty = + DependencyProperty.Register("Keys", typeof(string), typeof(HotkeyDisplay), + new PropertyMetadata(string.Empty, keyChanged)); + + + private static void keyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var control = d as UserControl; if (null == control) return; // This should not be possible @@ -48,8 +51,14 @@ namespace Flow.Launcher.Resources.Controls var newValue = e.NewValue as string; if (null == newValue) return; - //String[] Values = newValue.Split('+'); - //Debug.WriteLine(Values[0]); + if (d is not HotkeyDisplay hotkeyDisplay) + return; + + hotkeyDisplay.Values.Clear(); + foreach (var key in newValue.Split('+')) + { + hotkeyDisplay.Values.Add(key); + } } public ObservableCollection Values { get; set; } From c6b62f9917ddaf1fa3adba0978372b8e62b4eb62 Mon Sep 17 00:00:00 2001 From: DB p Date: Thu, 18 Apr 2024 00:48:29 +0900 Subject: [PATCH 071/151] Fix HotkeyDisplay xaml --- .../Resources/Controls/HotkeyDisplay.xaml | 59 +------------------ 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml index d4483393d..778c77fe5 100644 --- a/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml +++ b/Flow.Launcher/Resources/Controls/HotkeyDisplay.xaml @@ -9,62 +9,7 @@ d:DesignWidth="800" mc:Ignorable="d"> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + From 5c3d3c7fbfb2fa5f34fcd711b247e21705a8c240 Mon Sep 17 00:00:00 2001 From: DB p Date: Thu, 18 Apr 2024 01:00:31 +0900 Subject: [PATCH 072/151] Custom Hotkeys to Expander --- Flow.Launcher/SettingWindow.xaml | 241 ++++++++++++++++--------------- 1 file changed, 122 insertions(+), 119 deletions(-) diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 1beafd26b..1f60e326d 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2751,128 +2751,131 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + + diff --git a/Flow.Launcher/HotkeyControl.xaml.cs b/Flow.Launcher/HotkeyControl.xaml.cs index 01584ee27..546b252df 100644 --- a/Flow.Launcher/HotkeyControl.xaml.cs +++ b/Flow.Launcher/HotkeyControl.xaml.cs @@ -1,30 +1,38 @@ -using System; +#nullable enable + using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Linq; -using System.Runtime.CompilerServices; +using System.Threading.Tasks; using System.Windows; -using System.Windows.Controls; using System.Windows.Input; -using System.Windows.Media; using Flow.Launcher.Core.Resource; using Flow.Launcher.Helper; using Flow.Launcher.Infrastructure.Hotkey; -using Flow.Launcher.Plugin; -using JetBrains.Annotations; - -#nullable enable namespace Flow.Launcher { - public partial class HotkeyControl : INotifyPropertyChanged + public partial class HotkeyControl { + public string WindowTitle { + get { return (string)GetValue(WindowTitleProperty); } + set { SetValue(WindowTitleProperty, value); } + } + + public static readonly DependencyProperty WindowTitleProperty = DependencyProperty.Register( + nameof(WindowTitle), + typeof(string), + typeof(HotkeyControl), + new PropertyMetadata(string.Empty) + ); + /// /// Designed for Preview Hotkey and KeyGesture. /// public static readonly DependencyProperty ValidateKeyGestureProperty = DependencyProperty.Register( - nameof(ValidateKeyGesture), typeof(bool), typeof(HotkeyControl), - new PropertyMetadata(default(bool))); + nameof(ValidateKeyGesture), + typeof(bool), + typeof(HotkeyControl), + new PropertyMetadata(default(bool)) + ); public bool ValidateKeyGesture { @@ -33,7 +41,11 @@ namespace Flow.Launcher } public static readonly DependencyProperty DefaultHotkeyProperty = DependencyProperty.Register( - nameof(DefaultHotkey), typeof(string), typeof(HotkeyControl), new PropertyMetadata(default(string))); + nameof(DefaultHotkey), + typeof(string), + typeof(HotkeyControl), + new PropertyMetadata(default(string)) + ); public string DefaultHotkey { @@ -54,7 +66,11 @@ namespace Flow.Launcher public static readonly DependencyProperty ChangeHotkeyProperty = DependencyProperty.Register( - nameof(ChangeHotkey), typeof(ICommand), typeof(HotkeyControl), new PropertyMetadata(default(ICommand))); + nameof(ChangeHotkey), + typeof(ICommand), + typeof(HotkeyControl), + new PropertyMetadata(default(ICommand)) + ); public ICommand? ChangeHotkey { @@ -64,8 +80,11 @@ namespace Flow.Launcher public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register( - nameof(Hotkey), typeof(string), typeof(HotkeyControl), - new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHotkeyChanged)); + nameof(Hotkey), + typeof(string), + typeof(HotkeyControl), + new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHotkeyChanged) + ); public string Hotkey { @@ -81,148 +100,50 @@ namespace Flow.Launcher SetKeysToDisplay(CurrentHotkey); } - /*------------------ New Logic Structure Part------------------------*/ - - private void OnPreviewKeyDown(object sender, KeyEventArgs e) - { - if (HotkeyBtn.IsChecked != true) - return; - e.Handled = true; - - //when alt is pressed, the real key should be e.SystemKey - Key key = e.Key == Key.System ? e.SystemKey : e.Key; - - SpecialKeyState specialKeyState = GlobalHotkey.CheckModifiers(); - - var hotkeyModel = new HotkeyModel( - specialKeyState.AltPressed, - specialKeyState.ShiftPressed, - specialKeyState.WinPressed, - specialKeyState.CtrlPressed, - key); - - CurrentHotkey = hotkeyModel; - SetKeysToDisplay(CurrentHotkey); - } - - - // public new bool IsFocused => tbHotkey.IsFocused; - - private void tbHotkey_LostFocus(object sender, RoutedEventArgs e) - { - // tbHotkey.Text = CurrentHotkey?.ToString() ?? ""; - // tbHotkey.Select(tbHotkey.Text.Length, 0); - } - - private void tbHotkey_GotFocus(object sender, RoutedEventArgs e) - { - ResetMessage(); - } - - private void ResetMessage() - { - tbMsg.Text = InternationalizationManager.Instance.GetTranslation("flowlauncherPressHotkey"); - tbMsg.SetResourceReference(TextBox.ForegroundProperty, "Color05B"); - } - - public event PropertyChangedEventHandler? PropertyChanged; - - protected virtual void OnPropertyChanged([CallerMemberName] string? propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); - public string? Message { get; set; } - public bool MessageVisibility { get; set; } - public SolidColorBrush? MessageColor { get; set; } + public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none"); - - public bool CurrentHotkeyAvailable { get; private set; } - - - private void SetMessage(string messageKey, bool error) - { - Message = InternationalizationManager.Instance.GetTranslation(messageKey); - MessageColor = error ? new SolidColorBrush(Colors.Green) : new SolidColorBrush(Colors.Red); - MessageVisibility = true; - } - - - private string EmptyHotkeyKey = "none"; - public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation(EmptyHotkeyKey); - private const string KeySeparator = " + "; - - public ObservableCollection KeysToDisplay { get; set; } = new ObservableCollection(); - - public bool IsEmpty => KeysToDisplay.Count == 0 || (KeysToDisplay.Count == 1 && KeysToDisplay[0] == EmptyHotkey); + public ObservableCollection KeysToDisplay { get; set; } = new(); public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None); - public void StartRecording() + public void GetNewHotkey(object sender, RoutedEventArgs e) { - if (!HotkeyBtn.IsChecked ?? false) - { - return; - } + OpenHotkeyDialog(); + } + private async Task OpenHotkeyDialog() + { if (!string.IsNullOrEmpty(Hotkey)) { HotKeyMapper.RemoveHotkey(Hotkey); } - /* 1. Key Recording Start */ - /* 2. Key Display area clear - * 3. Key Display when typing*/ - } - private void StopRecording() - { - try + var dialog = new HotkeyControlDialog(Hotkey, DefaultHotkey, WindowTitle); + await dialog.ShowAsync(); + switch (dialog.ResultType) { - var converter = new KeyGestureConverter(); - _ = (KeyGesture)converter.ConvertFromString(CurrentHotkey.ToString())!; + case HotkeyControlDialog.EResultType.Cancel: + SetHotkey(Hotkey); + return; + case HotkeyControlDialog.EResultType.Save: + SetHotkey(dialog.ResultValue); + break; + case HotkeyControlDialog.EResultType.Delete: + Delete(); + break; } - catch (Exception e) when (e is NotSupportedException or InvalidEnumArgumentException) - { - SetMessage("Hotkey Invalid", true); - CurrentHotkey = new HotkeyModel(Hotkey); - SetKeysToDisplay(CurrentHotkey); - return; - } - - HotkeyBtn.IsChecked = false; - - SetHotkey(CurrentHotkey, true); - } - - private void ResetToDefault() - { - if (!string.IsNullOrEmpty(Hotkey)) - HotKeyMapper.RemoveHotkey(Hotkey); - Hotkey = DefaultHotkey; - CurrentHotkey = new HotkeyModel(Hotkey); - - SetKeysToDisplay(CurrentHotkey); - - SetHotkey(CurrentHotkey); - - HotkeyBtn.IsChecked = false; } private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) { - // tbHotkey.Text = keyModel.ToString(); - // tbHotkey.Select(tbHotkey.Text.Length, 0); - if (triggerValidate) { bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture); - SetMessage(hotkeyAvailable ? "success" : "hotkeyUnavailable", !hotkeyAvailable); if (!hotkeyAvailable) { @@ -246,7 +167,6 @@ namespace Flow.Launcher HotKeyMapper.RemoveHotkey(Hotkey); Hotkey = ""; SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None)); - HotkeyBtn.IsChecked = false; } private void SetKeysToDisplay(HotkeyModel? hotkey) @@ -269,14 +189,5 @@ namespace Flow.Launcher { SetHotkey(new HotkeyModel(keyStr), triggerValidate); } - - - private void HotkeyBtn_OnChecked(object sender, RoutedEventArgs e) => StartRecording(); - - - private void ResetButton_OnClick(object sender, RoutedEventArgs e) => ResetToDefault(); - private void DeleteBtn_OnClick(object sender, RoutedEventArgs e) => Delete(); - - private void StopRecordingBtn_Click(object sender, RoutedEventArgs e) => StopRecording(); } } diff --git a/Flow.Launcher/HotkeyControl2.xaml b/Flow.Launcher/HotkeyControl2.xaml deleted file mode 100644 index 730c9604b..000000000 --- a/Flow.Launcher/HotkeyControl2.xaml +++ /dev/null @@ -1,77 +0,0 @@ - - - diff --git a/Flow.Launcher/HotkeyControl2.xaml.cs b/Flow.Launcher/HotkeyControl2.xaml.cs deleted file mode 100644 index 86973a754..000000000 --- a/Flow.Launcher/HotkeyControl2.xaml.cs +++ /dev/null @@ -1,193 +0,0 @@ -#nullable enable - -using System.Collections.ObjectModel; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Input; -using Flow.Launcher.Core.Resource; -using Flow.Launcher.Helper; -using Flow.Launcher.Infrastructure.Hotkey; - -namespace Flow.Launcher -{ - public partial class HotkeyControl2 - { - public string WindowTitle { - get { return (string)GetValue(WindowTitleProperty); } - set { SetValue(WindowTitleProperty, value); } - } - - public static readonly DependencyProperty WindowTitleProperty = DependencyProperty.Register( - nameof(WindowTitle), - typeof(string), - typeof(HotkeyControl2), - new PropertyMetadata(string.Empty) - ); - - /// - /// Designed for Preview Hotkey and KeyGesture. - /// - public static readonly DependencyProperty ValidateKeyGestureProperty = DependencyProperty.Register( - nameof(ValidateKeyGesture), - typeof(bool), - typeof(HotkeyControl2), - new PropertyMetadata(default(bool)) - ); - - public bool ValidateKeyGesture - { - get { return (bool)GetValue(ValidateKeyGestureProperty); } - set { SetValue(ValidateKeyGestureProperty, value); } - } - - public static readonly DependencyProperty DefaultHotkeyProperty = DependencyProperty.Register( - nameof(DefaultHotkey), - typeof(string), - typeof(HotkeyControl2), - new PropertyMetadata(default(string)) - ); - - public string DefaultHotkey - { - get { return (string)GetValue(DefaultHotkeyProperty); } - set { SetValue(DefaultHotkeyProperty, value); } - } - - private static void OnHotkeyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) - { - if (d is not HotkeyControl2 hotkeyControl) - { - return; - } - - hotkeyControl.SetKeysToDisplay(new HotkeyModel(hotkeyControl.Hotkey)); - hotkeyControl.CurrentHotkey = new HotkeyModel(hotkeyControl.Hotkey); - } - - - public static readonly DependencyProperty ChangeHotkeyProperty = DependencyProperty.Register( - nameof(ChangeHotkey), - typeof(ICommand), - typeof(HotkeyControl2), - new PropertyMetadata(default(ICommand)) - ); - - public ICommand? ChangeHotkey - { - get { return (ICommand)GetValue(ChangeHotkeyProperty); } - set { SetValue(ChangeHotkeyProperty, value); } - } - - - public static readonly DependencyProperty HotkeyProperty = DependencyProperty.Register( - nameof(Hotkey), - typeof(string), - typeof(HotkeyControl2), - new FrameworkPropertyMetadata("", FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnHotkeyChanged) - ); - - public string Hotkey - { - get { return (string)GetValue(HotkeyProperty); } - set { SetValue(HotkeyProperty, value); } - } - - public HotkeyControl2() - { - InitializeComponent(); - - HotkeyList.ItemsSource = KeysToDisplay; - SetKeysToDisplay(CurrentHotkey); - } - - private static bool CheckHotkeyAvailability(HotkeyModel hotkey, bool validateKeyGesture) => - hotkey.Validate(validateKeyGesture) && HotKeyMapper.CheckAvailability(hotkey); - - public string EmptyHotkey => InternationalizationManager.Instance.GetTranslation("none"); - - public ObservableCollection KeysToDisplay { get; set; } = new(); - - public HotkeyModel CurrentHotkey { get; private set; } = new(false, false, false, false, Key.None); - - - public void GetNewHotkey(object sender, RoutedEventArgs e) - { - OpenHotkeyDialog(); - } - - private async Task OpenHotkeyDialog() - { - if (!string.IsNullOrEmpty(Hotkey)) - { - HotKeyMapper.RemoveHotkey(Hotkey); - } - - var dialog = new HotkeyControl2Dialog(Hotkey, DefaultHotkey, WindowTitle); - await dialog.ShowAsync(); - switch (dialog.ResultType) - { - case HotkeyControl2Dialog.EResultType.Cancel: - SetHotkey(Hotkey); - return; - case HotkeyControl2Dialog.EResultType.Save: - SetHotkey(dialog.ResultValue); - break; - case HotkeyControl2Dialog.EResultType.Delete: - Delete(); - break; - } - } - - - private void SetHotkey(HotkeyModel keyModel, bool triggerValidate = true) - { - if (triggerValidate) - { - bool hotkeyAvailable = CheckHotkeyAvailability(keyModel, ValidateKeyGesture); - - if (!hotkeyAvailable) - { - return; - } - - Hotkey = keyModel.ToString(); - SetKeysToDisplay(CurrentHotkey); - ChangeHotkey?.Execute(keyModel); - } - else - { - Hotkey = keyModel.ToString(); - ChangeHotkey?.Execute(keyModel); - } - } - - public void Delete() - { - if (!string.IsNullOrEmpty(Hotkey)) - HotKeyMapper.RemoveHotkey(Hotkey); - Hotkey = ""; - SetKeysToDisplay(new HotkeyModel(false, false, false, false, Key.None)); - } - - private void SetKeysToDisplay(HotkeyModel? hotkey) - { - KeysToDisplay.Clear(); - - if (hotkey == null || hotkey == default(HotkeyModel)) - { - KeysToDisplay.Add(EmptyHotkey); - return; - } - - foreach (var key in hotkey.Value.EnumerateDisplayKeys()!) - { - KeysToDisplay.Add(key); - } - } - - public void SetHotkey(string? keyStr, bool triggerValidate = true) - { - SetHotkey(new HotkeyModel(keyStr), triggerValidate); - } - } -} diff --git a/Flow.Launcher/HotkeyControl2Dialog.xaml b/Flow.Launcher/HotkeyControlDialog.xaml similarity index 99% rename from Flow.Launcher/HotkeyControl2Dialog.xaml rename to Flow.Launcher/HotkeyControlDialog.xaml index 2eb62c419..6912a1999 100644 --- a/Flow.Launcher/HotkeyControl2Dialog.xaml +++ b/Flow.Launcher/HotkeyControlDialog.xaml @@ -1,5 +1,5 @@  InternationalizationManager.Instance.GetTranslation("none"); - public HotkeyControl2Dialog(string hotkey, string defaultHotkey, string windowTitle = "") + public HotkeyControlDialog(string hotkey, string defaultHotkey, string windowTitle = "") { WindowTitle = windowTitle switch { diff --git a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml index 84615c155..b60b8e47e 100644 --- a/Flow.Launcher/Resources/Pages/WelcomePage2.xaml +++ b/Flow.Launcher/Resources/Pages/WelcomePage2.xaml @@ -110,7 +110,7 @@ FontSize="14" FontWeight="SemiBold" Text="{DynamicResource flowlauncherHotkey}" /> - - - - @@ -2771,7 +2771,7 @@ Title="{DynamicResource SettingWindowHotkey}" Icon="" Type="Inside"> - @@ -2793,7 +2793,7 @@ Title="{DynamicResource SelectNextPageHotkey}" Icon="" Type="Inside"> - @@ -2802,7 +2802,7 @@ Title="{DynamicResource SelectPrevPageHotkey}" Icon="" Type="Inside"> - @@ -2836,7 +2836,7 @@ Icon="" Sub="{DynamicResource autoCompleteHotkeyToolTip}"> - @@ -2845,7 +2845,7 @@ Title="{DynamicResource autoCompleteHotkey}" Sub="{DynamicResource AdditionalHotkeyToolTip}" Type="InsideFit"> - @@ -2858,7 +2858,7 @@ Margin="0,4,0,0" Icon=""> - @@ -2867,7 +2867,7 @@ Title="{DynamicResource SelectNextItemHotkey}" Sub="{DynamicResource AdditionalHotkeyToolTip}" Type="InsideFit"> - @@ -2879,7 +2879,7 @@ Margin="0,4,0,0" Icon=""> - @@ -2888,7 +2888,7 @@ Title="{DynamicResource SelectPrevItemHotkey}" Sub="{DynamicResource AdditionalHotkeyToolTip}" Type="InsideFit"> - From 806e1246126a242908f43e406290f60a88775ddb Mon Sep 17 00:00:00 2001 From: DB p Date: Tue, 23 Apr 2024 12:40:23 +0900 Subject: [PATCH 113/151] - Fix Static Hotkey's Uppercases - Adjust Warning Style in dialog - Adjust Warning Text --- Flow.Launcher/HotkeyControlDialog.xaml | 42 +++++++++++--------------- Flow.Launcher/Languages/en.xaml | 2 +- Flow.Launcher/SettingWindow.xaml | 18 +++++------ 3 files changed, 28 insertions(+), 34 deletions(-) diff --git a/Flow.Launcher/HotkeyControlDialog.xaml b/Flow.Launcher/HotkeyControlDialog.xaml index 6912a1999..322f82366 100644 --- a/Flow.Launcher/HotkeyControlDialog.xaml +++ b/Flow.Launcher/HotkeyControlDialog.xaml @@ -24,9 +24,7 @@ - + @@ -38,9 +36,7 @@ FontWeight="SemiBold" Text="{Binding WindowTitle}" TextAlignment="Left" /> - + - + - - - - - - + + + + diff --git a/Flow.Launcher/Languages/en.xaml b/Flow.Launcher/Languages/en.xaml index c92f227b3..6205c06bc 100644 --- a/Flow.Launcher/Languages/en.xaml +++ b/Flow.Launcher/Languages/en.xaml @@ -310,7 +310,7 @@ Invalid plugin hotkey Update Binding Hotkey - It's unavailable hotkey. + This hotkey is unavailable. Press the keys you want to use for this function. diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 15db60573..89a454fc0 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -2731,13 +2731,13 @@ Title="{DynamicResource OpenContainFolderHotkey}" Icon="" Type="Inside"> - + - + - + - + - + - - + + - - + + From 5a45c5a012d1c0f6832337cf307167dd9c6a93f0 Mon Sep 17 00:00:00 2001 From: Kevin Zhang <45326534+taooceros@users.noreply.github.com> Date: Tue, 23 Apr 2024 11:39:25 -0500 Subject: [PATCH 114/151] Use Shell Execute for Shell Plugin RunCommand it would not behavior like win+r without this. try `shell:startup` --- Plugins/Flow.Launcher.Plugin.Shell/Main.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs index f3c34d41d..b3b02cb6d 100644 --- a/Plugins/Flow.Launcher.Plugin.Shell/Main.cs +++ b/Plugins/Flow.Launcher.Plugin.Shell/Main.cs @@ -264,6 +264,7 @@ namespace Flow.Launcher.Plugin.Shell var arguments = parts[1]; info.FileName = filename; info.ArgumentList.Add(arguments); + info.UseShellExecute = true; } else { From 7fbea552c8dfd749a19eb8428f685700f68d4f64 Mon Sep 17 00:00:00 2001 From: DB p Date: Wed, 24 Apr 2024 04:10:52 +0900 Subject: [PATCH 115/151] Add Vietnamese Support --- .github/actions/spelling/expect.txt | 1 + .../Resource/AvailableLanguages.cs | 7 +- Flow.Launcher/Languages/vi-vn.xaml | 383 ++++++++++++++++++ 3 files changed, 389 insertions(+), 2 deletions(-) create mode 100644 Flow.Launcher/Languages/vi-vn.xaml diff --git a/.github/actions/spelling/expect.txt b/.github/actions/spelling/expect.txt index c4b1ee849..59566af64 100644 --- a/.github/actions/spelling/expect.txt +++ b/.github/actions/spelling/expect.txt @@ -97,6 +97,7 @@ Português Português (Brasil) Italiano Slovenský +Tiếng Việt Droplex Preinstalled errormetadatafile diff --git a/Flow.Launcher.Core/Resource/AvailableLanguages.cs b/Flow.Launcher.Core/Resource/AvailableLanguages.cs index b6d394d11..46c1ecb54 100644 --- a/Flow.Launcher.Core/Resource/AvailableLanguages.cs +++ b/Flow.Launcher.Core/Resource/AvailableLanguages.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; namespace Flow.Launcher.Core.Resource { @@ -27,6 +27,8 @@ namespace Flow.Launcher.Core.Resource public static Language Turkish = new Language("tr", "Türkçe"); public static Language Czech = new Language("cs", "čeština"); public static Language Arabic = new Language("ar", "اللغة العربية"); + public static Language Vietnamese = new Language("vi-vn", "Tiếng Việt"); + public static List GetAvailableLanguages() { @@ -54,7 +56,8 @@ namespace Flow.Launcher.Core.Resource Slovak, Turkish, Czech, - Arabic + Arabic, + Vietnamese }; return languages; } diff --git a/Flow.Launcher/Languages/vi-vn.xaml b/Flow.Launcher/Languages/vi-vn.xaml new file mode 100644 index 000000000..a54c17df4 --- /dev/null +++ b/Flow.Launcher/Languages/vi-vn.xaml @@ -0,0 +1,383 @@ + + + + Không thể đăng ký phím nóng "{0}". Phím nóng có thể được sử dụng bởi một chương trình khác. Chuyển sang phím nóng khác hoặc thoát khỏi chương trình khác. + Trình khởi chạy luồng + Không thể khởi động {0} + Định dạng tệp plugin Flow Launcher không chính xác + Đặt ở vị trí hàng đầu trong truy vấn này + Hủy trên cùng trong truy vấn này + Thực thi truy vấn:{0} + Thời gian thực hiện lần cuối:{0} + Mở + Cài đặt + Über + Thoát + Đóng + Sao chép + Di chuyển + Dán + Hoàn tác + Chọn tất cả + Ngày tháng + Thư Mục + Văn bản + Chế độ trò chơi + Tạm dừng sử dụng phím nóng. + Đặt lại vị trí + Đặt lại vị trí của cửa sổ tìm kiếm + + + Cài đặt + Tổng quan + Chế độ Portabler + Lưu trữ tất cả cài đặt và dữ liệu người dùng trong một thư mục (hữu ích khi sử dụng với thiết bị lưu trữ di động hoặc dịch vụ đám mây). + Khởi động Flow Launcher khi khởi động hệ thống + Không lưu được tính năng tự khởi động khi khởi động hệ thống + Ẩn Flow Launcher khi mất tiêu điểm + Không hiển thị thông báo khi có phiên bản mới + Vị trí Suchfenster + Ghi nhớ vị trí cuối cùng + Màn hình bằng con trỏ chuột + Màn hình có cửa sổ được tập trung + Màn hình chính + Màn hình tùy chỉnh + Tìm kiếm vị trí cửa sổ trên màn hình + Trung tâm + Trung tâm trên cùng + Liên kết oben + Trên cùng bên phải + Vị trí tùy chỉnh + Sprache + Chọn kiểu truy vấn + Hiển thị/ẩn các kết quả trước đó khi Flow Launcher được kích hoạt lại. + Giữ lại truy vấn cuối cùng + Chọn truy vấn cuối cùng + Trống truy vấn cuối cùng + Số kết quả tối đa + Có thể thiết lập nhanh chóng bằng CTRL+Plus và CTRL+Minus. + Bỏ qua phím nóng khi cửa sổ ở chế độ toàn màn hình + Tắt Flow Launcher khi ứng dụng toàn màn hình đang hoạt động (Được khuyến nghị để chơi trò chơi). + Trình quản lý ngày tháng tiêu chuẩn + Chọn trình quản lý tệp để sử dụng khi mở thư mục. + Trình duyệt chuẩn + Cài đặt cho tab mới, cửa sổ mới và chế độ riêng tư. + Python-Pfad + Node.js-Pfad + Vui lòng chọn chương trình Node.js + Vui lòng chọn pythonw.exe + Luôn bắt đầu nhập ở chế độ tiếng Anh + Tạm thời chuyển phương thức nhập sang tiếng Anh khi kích hoạt Flow. + Cập nhật tự động + Chọn + Ẩn Trình khởi chạy luồng khi khởi động + Ẩn biểu tượng thanh trạng thái + Khi biểu tượng bị ẩn khỏi khay, bạn có thể mở menu Cài đặt bằng cách nhấp chuột phải vào cửa sổ tìm kiếm. + Độ chính xác của tìm kiếm truy vấn + Kết quả tìm kiếm bắt buộc. + Hoạt động bính âm + Cho phép bạn sử dụng Bính âm để tìm kiếm. Bính âm là hệ thống ký hiệu La Mã tiêu chuẩn để dịch văn bản tiếng Trung. + Luôn xem trước + Luôn mở bảng xem trước khi Flow kích hoạt. Nhấn {0} để chuyển đổi chế độ xem trước. + Hiệu ứng đổ bóng không được phép nếu chủ đề hiện tại bật hiệu ứng làm mờ + + + Plugin tìm kiếm + Ctrl+F để tìm kiếm plugin + Không tìm thấy kết quả nào + Vui lòng thử tìm kiếm khác. + Tiện ích mở rộng + Tiện ích mở rộng + Tìm kiếm thêm plugin + Bật + Tắt + Cài đặt từ hành động + Từ khóa hành động + Từ hành động hiện tại + Từ hành động mới + Thay đổi từ hành động + Ưu tiên hiện tại + Ưu tiên mới + Ưu tiên + Thay đổi mức độ ưu tiên của kết quả plugin + Trình cắm + tác giả + Khởi tạo ban đầu: + Abfragezeit: + Phiên bản + Trang web + Gỡ cài đặt + + + + Tải tiện ích mở rộng + Bản phát hành mới + Cập nhật gần đây + Tiện ích mở rộng + Đã cài đặt + Làm mới + Cài đặt + Gỡ cài đặt + Cập nhật + Plugin đã được cài đặt + Phiên bản mới + Plugin này đã được cập nhật trong vòng 7 ngày qua + Đã có bản cập nhật mới + + + + + Giao Diện + Giao diện + Tìm kiếm thêm chủ đề + Cách tạo chủ đề + Xin chào! + Explorer + Tìm kiếm tệp, thư mục và nội dung tệp + Tìm kiếm trên web + Tìm kiếm trên web với sự hỗ trợ của công cụ tìm kiếm khác + Chương trình + Khởi chạy chương trình với tư cách quản trị viên hoặc người dùng khác + ProcessKiller + Chấm dứt các tiến trình không mong muốn + Phông chữ hộp truy vấn + Phông chữ kết quả + chế độ cửa sổ + độ mờ + Chủ đề {0} không tồn tại nên mẫu mặc định được kích hoạt + Tải chủ đề {0} không thành công, mẫu mặc định được kích hoạt + Mở thư mục chủ đề + Mở thư mục chủ đề + Bảng màu + Mặc định hệ thống + Sáng + Tối + Hiệu ứng âm thanh + Phát âm thanh khi cửa sổ tìm kiếm mở + Âm lượng hiệu ứng âm thanh + Điều chỉnh âm lượng của hiệu ứng âm thanh + Hoạt hình + Sử dụng hình động trong giao diện + Tốc độ hoạt ảnh + Tốc độ của hoạt ảnh giao diện người dùng + Chậm + Trung bình + Nhanh + Tùy chỉnh + Giờ + Ngày + + + Phím tắt + Phím tắt + Phím nóng của Flow Launcher + Nhập phím tắt để hiển thị/ẩn Flow Launcher. + Xem trước phím nóng + Nhập phím tắt để hiển thị/ẩn bản xem trước trong cửa sổ tìm kiếm. + Mở công cụ sửa đổi kết quả + Chọn phím bổ trợ để mở kết quả đã chọn từ bàn phím. + Giải thích phím nóng + Hiển thị phím tắt chọn kết quả cùng với kết quả. + Phím tắt truy vấn tùy chỉnh + Lối tắt truy vấn tùy chỉnh + Phím tắt tích hợp + Truy vấn + Phím tắt + Mở rộng + Mô tả + Xóa + Chỉnh sửa + Thêm + Vui lòng chọn một mục + Bạn có chắc chắn muốn xóa tổ hợp phím plugin {0} không? + Bạn có chắc chắn muốn xóa lối tắt: {0} với phần mở rộng {1} không? + Nhận văn bản từ bảng nhớ tạm. + Nhận đường dẫn từ trình thám hiểm đang hoạt động. + Hiệu ứng đổ bóng trong cửa sổ truy vấn + Hiệu ứng đổ bóng gây rất nhiều áp lực lên GPU. Không nên dùng nếu hiệu suất máy tính của bạn bị hạn chế. + Chiều rộng kích thước cửa sổ + Bạn cũng có thể nhanh chóng điều chỉnh điều này bằng cách sử dụng Ctrl+[ và Ctrl+]. + Sử dụng biểu tượng Segoe + Sử dụng Biểu tượng Segoe Fluent cho kết quả truy vấn nếu được hỗ trợ + Nhấn phím + + + Proxy HTTP + Proxy HTTP hoạt động + Máy chủ HTTP + Cổng + Tài Khoản + Mật khẩu + Proxy thử nghiệm + Lưu + Máy chủ không được để trống + Cổng máy chủ không được để trống + Định dạng cổng Falsches + Proxy đã được lưu thành công + Proxy này được chấp nhận + Kết nối với proxy không thành công + + + Giới thiệu + Trang web + GitHub + Tài liệu + Phiên bản + Biểu tượng + Bạn đã kích hoạt Flow Launcher {0} lần + Kiểm tra các bản cập nhật + Trở thành nhà tài trợ + Đã tồn tại một phiên bản mới ({0}). Vui lòng khởi động lại Flow Launcher. + Kiểm tra cập nhật không thành công. Vui lòng kiểm tra kết nối và cài đặt proxy của bạn tới api.github.com. + + Tải xuống bản cập nhật không thành công. Vui lòng kiểm tra cài đặt kết nối và proxy của bạn tới github-cloud.s3.amazonaws.com, + hoặc truy cập https://github.com/Flow-Launcher/Flow.Launcher/releases để tải xuống các bản cập nhật theo cách thủ công. + + Ghi chú phát hành + Mẹo sử dụng + Công cụ dành cho nhà phát triển + cài đặt thư mục + Thư mục nhật ký + Xóa tệp nhật ký + Bạn có chắc chắn muốn xóa tất cả nhật ký không? + Wizard + + + Chọn trình quản lý tệp + Vui lòng chỉ định vị trí tệp của trình quản lý tệp mà bạn đang sử dụng và thêm đối số nếu cần. Đối số mặc định là "%d" và một đường dẫn được nhập tại vị trí đó. Ví dụ: Nếu một lệnh được yêu cầu như "totalcmd.exe /A c:\windows", đối số là /A "%d". + "%f" là một đối số đại diện cho đường dẫn tệp. Nó được sử dụng để nhấn mạnh tên tệp/thư mục khi mở một vị trí tệp cụ thể trong trình quản lý tệp của bên thứ 3. Đối số này chỉ có sẵn trong phần "Arg for File" mục. Nếu trình quản lý tệp không có chức năng đó, bạn có thể sử dụng "%d". + Trình quản lý ngày tháng + Tên hồ sơ + Đường dẫn quản lý tệp + Đối số cho thư mục + Đối số cho tệp + + + Trình duyệt web tiêu chuẩn + Mặc định sử dụng mặc định trình duyệt của hệ điều hành. Nếu được chỉ định, Flow sẽ sử dụng trình duyệt này. + Trình duyệt + Tên trình duyệt + Đường dẫn trình duyệt + Cửa sổ mới + Thẻ Mới + Chế độ riêng tư + + + Thay đổi mức độ ưu tiên + Số càng cao thì kết quả được xếp hạng càng cao. Hãy thử số 5. ​​Nếu bạn muốn kết quả sâu hơn so với các plugin khác, hãy sử dụng số âm + Vui lòng chỉ định số nguyên hợp lệ cho mức độ ưu tiên! + + + Từ khóa hành động cũ + Từ khóa hành động mới + Viết tắt + Fertig + Không thể tìm thấy plugin được chỉ định + Từ khóa hành động mới không được để trống + Từ khóa hành động đã được một plugin khác sử dụng. Vui lòng đặt từ khóa hành động khác. + Thành công + Đã hoàn tất thành công + Sử dụng * nếu bạn muốn xác định từ khóa hành động. + + + Phím nóng truy vấn tùy chỉnh + Nhấn phím nóng tùy chỉnh để mở Flow Launcher và tự động nhập truy vấn được chỉ định. + Xem trước + Tổ hợp phím không khả dụng, vui lòng chọn tổ hợp phím khác + Tổ hợp phím plugin không hợp lệ + Cập nhật + + + Phím tắt truy vấn tùy chỉnh + Nhập một phím tắt tự động mở rộng theo truy vấn đã chỉ định. + Một phím tắt được mở rộng khi nó khớp chính xác với truy vấn. + +Nếu bạn thêm tiền tố '@' trong khi nhập phím tắt, phím tắt đó sẽ khớp với bất kỳ vị trí nào trong truy vấn. Các phím tắt dựng sẵn khớp với bất kỳ vị trí nào trong truy vấn. + + Phím tắt đã tồn tại, vui lòng nhập Phím tắt mới hoặc chỉnh sửa phím tắt hiện có. + Phím tắt và/hoặc phần mở rộng của nó trống. + + + Tổ hợp phím không khả dụng + + + Phiên bản + Thời gian + Vui lòng cho chúng tôi biết ứng dụng bị lỗi như thế nào để chúng tôi có thể khắc phục lỗi. + Gửi báo cáo + Huỷ bỏ + Chung + Ngoại lệ + Loại ngoại lệ + Nguồn + Ngăn xếp dấu vết + Gửi + Đã gửi báo cáo thành công + Báo cáo lỗi + Trình khởi chạy luồng có lỗi + + + Cảnh báo nhỏ... + + + Kiểm tra cập nhật mới + Bạn đã có phiên bản mới nhất + Tìm thấy cập nhật + Đang cập nhật... + + Flow Launcher không thể di chuyển dữ liệu hồ sơ của bạn sang phiên bản cập nhật mới. + Vui lòng di chuyển thủ công thư mục dữ liệu hồ sơ của bạn từ {0} sang {1} + + Cập nhật mới + Đã có sẵn V{0} của Flow Launcher + Đã xảy ra lỗi khi cài đặt bản cập nhật. + Cập nhật + Viết tắt + Cập nhật không thành công + Kiểm tra kết nối Internet của bạn và cập nhật cài đặt proxy để truy cập github-cloud.s3.amazonaws.com. + Bản cập nhật này sẽ khởi động lại Flow Launcher + Các tệp sau sẽ được cập nhật + Cập nhật tệp + mô tả Cập nhật + + + Bỏ qua + Chào mừng bạn đến với Trình khởi chạy luồng + Xin chào, đây là lần đầu tiên bạn sử dụng Flow Launcher! + Trước khi bạn bắt đầu, trình hướng dẫn này sẽ giúp thiết lập Flow Launcher. Bạn có thể bỏ qua điều này nếu bạn muốn. Vui lòng chọn ngôn ngữ + Tìm kiếm và khởi chạy tất cả các tệp và ứng dụng trên PC của bạn + Tìm kiếm mọi thứ từ ứng dụng, tệp, dấu trang, YouTube, Twitter và hơn thế nữa. Tất cả đều từ bàn phím thoải mái mà không cần chạm vào chuột. + Trình khởi chạy Flow bắt đầu bằng phím nóng bên dưới, hãy tiếp tục và dùng thử ngay bây giờ. Để thay đổi nó, hãy nhấp vào đầu vào và nhấn phím nóng mong muốn trên bàn phím. + phím tắt + Từ khóa và lệnh hành động + Tìm kiếm trên web, khởi chạy ứng dụng hoặc chạy các chức năng khác nhau thông qua plugin Flow Launcher. Một số chức năng nhất định bắt đầu bằng từ khóa hành động và nếu cần, chúng có thể được sử dụng mà không cần từ khóa hành động. Hãy thử các truy vấn bên dưới trong Flow Launcher. + Bắt đầu Flow-Launcher + Xong. Thưởng thức Flow Launcher. Đừng quên phím tắt để khởi động Flow Launcher :) + + + + Menu Quay lại / Ngữ cảnh + Mục Điều hướng + Mở menu ngữ cảnh + Mở thư mục chứa + Chạy với tư cách quản trị viên / mở thư mục trong trình quản lý tệp tiêu chuẩn + Lịch sử tìm kiếm + Quay lại kết quả trong menu ngữ cảnh + Tự động hoàn thành + Mở/chạy mục đã chọn + Mở cửa sổ cài đặt + Dữ liệu plugin không tải + + Thời tiết + Thời tiết theo kết quả trên Google + > ping 8.8.8.8 + Lệnh Shell + Bluetooth + Bluetooth trong Cài đặt Windows + sn + Ghi Chú + + + + + From ee17888de6338efa4819291d559e5465e50be25e Mon Sep 17 00:00:00 2001 From: DB p Date: Wed, 24 Apr 2024 04:18:53 +0900 Subject: [PATCH 116/151] Delete vi-vn.xaml --- Flow.Launcher/Languages/vi-vn.xaml | 383 ----------------------------- 1 file changed, 383 deletions(-) delete mode 100644 Flow.Launcher/Languages/vi-vn.xaml diff --git a/Flow.Launcher/Languages/vi-vn.xaml b/Flow.Launcher/Languages/vi-vn.xaml deleted file mode 100644 index a54c17df4..000000000 --- a/Flow.Launcher/Languages/vi-vn.xaml +++ /dev/null @@ -1,383 +0,0 @@ - - - - Không thể đăng ký phím nóng "{0}". Phím nóng có thể được sử dụng bởi một chương trình khác. Chuyển sang phím nóng khác hoặc thoát khỏi chương trình khác. - Trình khởi chạy luồng - Không thể khởi động {0} - Định dạng tệp plugin Flow Launcher không chính xác - Đặt ở vị trí hàng đầu trong truy vấn này - Hủy trên cùng trong truy vấn này - Thực thi truy vấn:{0} - Thời gian thực hiện lần cuối:{0} - Mở - Cài đặt - Über - Thoát - Đóng - Sao chép - Di chuyển - Dán - Hoàn tác - Chọn tất cả - Ngày tháng - Thư Mục - Văn bản - Chế độ trò chơi - Tạm dừng sử dụng phím nóng. - Đặt lại vị trí - Đặt lại vị trí của cửa sổ tìm kiếm - - - Cài đặt - Tổng quan - Chế độ Portabler - Lưu trữ tất cả cài đặt và dữ liệu người dùng trong một thư mục (hữu ích khi sử dụng với thiết bị lưu trữ di động hoặc dịch vụ đám mây). - Khởi động Flow Launcher khi khởi động hệ thống - Không lưu được tính năng tự khởi động khi khởi động hệ thống - Ẩn Flow Launcher khi mất tiêu điểm - Không hiển thị thông báo khi có phiên bản mới - Vị trí Suchfenster - Ghi nhớ vị trí cuối cùng - Màn hình bằng con trỏ chuột - Màn hình có cửa sổ được tập trung - Màn hình chính - Màn hình tùy chỉnh - Tìm kiếm vị trí cửa sổ trên màn hình - Trung tâm - Trung tâm trên cùng - Liên kết oben - Trên cùng bên phải - Vị trí tùy chỉnh - Sprache - Chọn kiểu truy vấn - Hiển thị/ẩn các kết quả trước đó khi Flow Launcher được kích hoạt lại. - Giữ lại truy vấn cuối cùng - Chọn truy vấn cuối cùng - Trống truy vấn cuối cùng - Số kết quả tối đa - Có thể thiết lập nhanh chóng bằng CTRL+Plus và CTRL+Minus. - Bỏ qua phím nóng khi cửa sổ ở chế độ toàn màn hình - Tắt Flow Launcher khi ứng dụng toàn màn hình đang hoạt động (Được khuyến nghị để chơi trò chơi). - Trình quản lý ngày tháng tiêu chuẩn - Chọn trình quản lý tệp để sử dụng khi mở thư mục. - Trình duyệt chuẩn - Cài đặt cho tab mới, cửa sổ mới và chế độ riêng tư. - Python-Pfad - Node.js-Pfad - Vui lòng chọn chương trình Node.js - Vui lòng chọn pythonw.exe - Luôn bắt đầu nhập ở chế độ tiếng Anh - Tạm thời chuyển phương thức nhập sang tiếng Anh khi kích hoạt Flow. - Cập nhật tự động - Chọn - Ẩn Trình khởi chạy luồng khi khởi động - Ẩn biểu tượng thanh trạng thái - Khi biểu tượng bị ẩn khỏi khay, bạn có thể mở menu Cài đặt bằng cách nhấp chuột phải vào cửa sổ tìm kiếm. - Độ chính xác của tìm kiếm truy vấn - Kết quả tìm kiếm bắt buộc. - Hoạt động bính âm - Cho phép bạn sử dụng Bính âm để tìm kiếm. Bính âm là hệ thống ký hiệu La Mã tiêu chuẩn để dịch văn bản tiếng Trung. - Luôn xem trước - Luôn mở bảng xem trước khi Flow kích hoạt. Nhấn {0} để chuyển đổi chế độ xem trước. - Hiệu ứng đổ bóng không được phép nếu chủ đề hiện tại bật hiệu ứng làm mờ - - - Plugin tìm kiếm - Ctrl+F để tìm kiếm plugin - Không tìm thấy kết quả nào - Vui lòng thử tìm kiếm khác. - Tiện ích mở rộng - Tiện ích mở rộng - Tìm kiếm thêm plugin - Bật - Tắt - Cài đặt từ hành động - Từ khóa hành động - Từ hành động hiện tại - Từ hành động mới - Thay đổi từ hành động - Ưu tiên hiện tại - Ưu tiên mới - Ưu tiên - Thay đổi mức độ ưu tiên của kết quả plugin - Trình cắm - tác giả - Khởi tạo ban đầu: - Abfragezeit: - Phiên bản - Trang web - Gỡ cài đặt - - - - Tải tiện ích mở rộng - Bản phát hành mới - Cập nhật gần đây - Tiện ích mở rộng - Đã cài đặt - Làm mới - Cài đặt - Gỡ cài đặt - Cập nhật - Plugin đã được cài đặt - Phiên bản mới - Plugin này đã được cập nhật trong vòng 7 ngày qua - Đã có bản cập nhật mới - - - - - Giao Diện - Giao diện - Tìm kiếm thêm chủ đề - Cách tạo chủ đề - Xin chào! - Explorer - Tìm kiếm tệp, thư mục và nội dung tệp - Tìm kiếm trên web - Tìm kiếm trên web với sự hỗ trợ của công cụ tìm kiếm khác - Chương trình - Khởi chạy chương trình với tư cách quản trị viên hoặc người dùng khác - ProcessKiller - Chấm dứt các tiến trình không mong muốn - Phông chữ hộp truy vấn - Phông chữ kết quả - chế độ cửa sổ - độ mờ - Chủ đề {0} không tồn tại nên mẫu mặc định được kích hoạt - Tải chủ đề {0} không thành công, mẫu mặc định được kích hoạt - Mở thư mục chủ đề - Mở thư mục chủ đề - Bảng màu - Mặc định hệ thống - Sáng - Tối - Hiệu ứng âm thanh - Phát âm thanh khi cửa sổ tìm kiếm mở - Âm lượng hiệu ứng âm thanh - Điều chỉnh âm lượng của hiệu ứng âm thanh - Hoạt hình - Sử dụng hình động trong giao diện - Tốc độ hoạt ảnh - Tốc độ của hoạt ảnh giao diện người dùng - Chậm - Trung bình - Nhanh - Tùy chỉnh - Giờ - Ngày - - - Phím tắt - Phím tắt - Phím nóng của Flow Launcher - Nhập phím tắt để hiển thị/ẩn Flow Launcher. - Xem trước phím nóng - Nhập phím tắt để hiển thị/ẩn bản xem trước trong cửa sổ tìm kiếm. - Mở công cụ sửa đổi kết quả - Chọn phím bổ trợ để mở kết quả đã chọn từ bàn phím. - Giải thích phím nóng - Hiển thị phím tắt chọn kết quả cùng với kết quả. - Phím tắt truy vấn tùy chỉnh - Lối tắt truy vấn tùy chỉnh - Phím tắt tích hợp - Truy vấn - Phím tắt - Mở rộng - Mô tả - Xóa - Chỉnh sửa - Thêm - Vui lòng chọn một mục - Bạn có chắc chắn muốn xóa tổ hợp phím plugin {0} không? - Bạn có chắc chắn muốn xóa lối tắt: {0} với phần mở rộng {1} không? - Nhận văn bản từ bảng nhớ tạm. - Nhận đường dẫn từ trình thám hiểm đang hoạt động. - Hiệu ứng đổ bóng trong cửa sổ truy vấn - Hiệu ứng đổ bóng gây rất nhiều áp lực lên GPU. Không nên dùng nếu hiệu suất máy tính của bạn bị hạn chế. - Chiều rộng kích thước cửa sổ - Bạn cũng có thể nhanh chóng điều chỉnh điều này bằng cách sử dụng Ctrl+[ và Ctrl+]. - Sử dụng biểu tượng Segoe - Sử dụng Biểu tượng Segoe Fluent cho kết quả truy vấn nếu được hỗ trợ - Nhấn phím - - - Proxy HTTP - Proxy HTTP hoạt động - Máy chủ HTTP - Cổng - Tài Khoản - Mật khẩu - Proxy thử nghiệm - Lưu - Máy chủ không được để trống - Cổng máy chủ không được để trống - Định dạng cổng Falsches - Proxy đã được lưu thành công - Proxy này được chấp nhận - Kết nối với proxy không thành công - - - Giới thiệu - Trang web - GitHub - Tài liệu - Phiên bản - Biểu tượng - Bạn đã kích hoạt Flow Launcher {0} lần - Kiểm tra các bản cập nhật - Trở thành nhà tài trợ - Đã tồn tại một phiên bản mới ({0}). Vui lòng khởi động lại Flow Launcher. - Kiểm tra cập nhật không thành công. Vui lòng kiểm tra kết nối và cài đặt proxy của bạn tới api.github.com. - - Tải xuống bản cập nhật không thành công. Vui lòng kiểm tra cài đặt kết nối và proxy của bạn tới github-cloud.s3.amazonaws.com, - hoặc truy cập https://github.com/Flow-Launcher/Flow.Launcher/releases để tải xuống các bản cập nhật theo cách thủ công. - - Ghi chú phát hành - Mẹo sử dụng - Công cụ dành cho nhà phát triển - cài đặt thư mục - Thư mục nhật ký - Xóa tệp nhật ký - Bạn có chắc chắn muốn xóa tất cả nhật ký không? - Wizard - - - Chọn trình quản lý tệp - Vui lòng chỉ định vị trí tệp của trình quản lý tệp mà bạn đang sử dụng và thêm đối số nếu cần. Đối số mặc định là "%d" và một đường dẫn được nhập tại vị trí đó. Ví dụ: Nếu một lệnh được yêu cầu như "totalcmd.exe /A c:\windows", đối số là /A "%d". - "%f" là một đối số đại diện cho đường dẫn tệp. Nó được sử dụng để nhấn mạnh tên tệp/thư mục khi mở một vị trí tệp cụ thể trong trình quản lý tệp của bên thứ 3. Đối số này chỉ có sẵn trong phần "Arg for File" mục. Nếu trình quản lý tệp không có chức năng đó, bạn có thể sử dụng "%d". - Trình quản lý ngày tháng - Tên hồ sơ - Đường dẫn quản lý tệp - Đối số cho thư mục - Đối số cho tệp - - - Trình duyệt web tiêu chuẩn - Mặc định sử dụng mặc định trình duyệt của hệ điều hành. Nếu được chỉ định, Flow sẽ sử dụng trình duyệt này. - Trình duyệt - Tên trình duyệt - Đường dẫn trình duyệt - Cửa sổ mới - Thẻ Mới - Chế độ riêng tư - - - Thay đổi mức độ ưu tiên - Số càng cao thì kết quả được xếp hạng càng cao. Hãy thử số 5. ​​Nếu bạn muốn kết quả sâu hơn so với các plugin khác, hãy sử dụng số âm - Vui lòng chỉ định số nguyên hợp lệ cho mức độ ưu tiên! - - - Từ khóa hành động cũ - Từ khóa hành động mới - Viết tắt - Fertig - Không thể tìm thấy plugin được chỉ định - Từ khóa hành động mới không được để trống - Từ khóa hành động đã được một plugin khác sử dụng. Vui lòng đặt từ khóa hành động khác. - Thành công - Đã hoàn tất thành công - Sử dụng * nếu bạn muốn xác định từ khóa hành động. - - - Phím nóng truy vấn tùy chỉnh - Nhấn phím nóng tùy chỉnh để mở Flow Launcher và tự động nhập truy vấn được chỉ định. - Xem trước - Tổ hợp phím không khả dụng, vui lòng chọn tổ hợp phím khác - Tổ hợp phím plugin không hợp lệ - Cập nhật - - - Phím tắt truy vấn tùy chỉnh - Nhập một phím tắt tự động mở rộng theo truy vấn đã chỉ định. - Một phím tắt được mở rộng khi nó khớp chính xác với truy vấn. - -Nếu bạn thêm tiền tố '@' trong khi nhập phím tắt, phím tắt đó sẽ khớp với bất kỳ vị trí nào trong truy vấn. Các phím tắt dựng sẵn khớp với bất kỳ vị trí nào trong truy vấn. - - Phím tắt đã tồn tại, vui lòng nhập Phím tắt mới hoặc chỉnh sửa phím tắt hiện có. - Phím tắt và/hoặc phần mở rộng của nó trống. - - - Tổ hợp phím không khả dụng - - - Phiên bản - Thời gian - Vui lòng cho chúng tôi biết ứng dụng bị lỗi như thế nào để chúng tôi có thể khắc phục lỗi. - Gửi báo cáo - Huỷ bỏ - Chung - Ngoại lệ - Loại ngoại lệ - Nguồn - Ngăn xếp dấu vết - Gửi - Đã gửi báo cáo thành công - Báo cáo lỗi - Trình khởi chạy luồng có lỗi - - - Cảnh báo nhỏ... - - - Kiểm tra cập nhật mới - Bạn đã có phiên bản mới nhất - Tìm thấy cập nhật - Đang cập nhật... - - Flow Launcher không thể di chuyển dữ liệu hồ sơ của bạn sang phiên bản cập nhật mới. - Vui lòng di chuyển thủ công thư mục dữ liệu hồ sơ của bạn từ {0} sang {1} - - Cập nhật mới - Đã có sẵn V{0} của Flow Launcher - Đã xảy ra lỗi khi cài đặt bản cập nhật. - Cập nhật - Viết tắt - Cập nhật không thành công - Kiểm tra kết nối Internet của bạn và cập nhật cài đặt proxy để truy cập github-cloud.s3.amazonaws.com. - Bản cập nhật này sẽ khởi động lại Flow Launcher - Các tệp sau sẽ được cập nhật - Cập nhật tệp - mô tả Cập nhật - - - Bỏ qua - Chào mừng bạn đến với Trình khởi chạy luồng - Xin chào, đây là lần đầu tiên bạn sử dụng Flow Launcher! - Trước khi bạn bắt đầu, trình hướng dẫn này sẽ giúp thiết lập Flow Launcher. Bạn có thể bỏ qua điều này nếu bạn muốn. Vui lòng chọn ngôn ngữ - Tìm kiếm và khởi chạy tất cả các tệp và ứng dụng trên PC của bạn - Tìm kiếm mọi thứ từ ứng dụng, tệp, dấu trang, YouTube, Twitter và hơn thế nữa. Tất cả đều từ bàn phím thoải mái mà không cần chạm vào chuột. - Trình khởi chạy Flow bắt đầu bằng phím nóng bên dưới, hãy tiếp tục và dùng thử ngay bây giờ. Để thay đổi nó, hãy nhấp vào đầu vào và nhấn phím nóng mong muốn trên bàn phím. - phím tắt - Từ khóa và lệnh hành động - Tìm kiếm trên web, khởi chạy ứng dụng hoặc chạy các chức năng khác nhau thông qua plugin Flow Launcher. Một số chức năng nhất định bắt đầu bằng từ khóa hành động và nếu cần, chúng có thể được sử dụng mà không cần từ khóa hành động. Hãy thử các truy vấn bên dưới trong Flow Launcher. - Bắt đầu Flow-Launcher - Xong. Thưởng thức Flow Launcher. Đừng quên phím tắt để khởi động Flow Launcher :) - - - - Menu Quay lại / Ngữ cảnh - Mục Điều hướng - Mở menu ngữ cảnh - Mở thư mục chứa - Chạy với tư cách quản trị viên / mở thư mục trong trình quản lý tệp tiêu chuẩn - Lịch sử tìm kiếm - Quay lại kết quả trong menu ngữ cảnh - Tự động hoàn thành - Mở/chạy mục đã chọn - Mở cửa sổ cài đặt - Dữ liệu plugin không tải - - Thời tiết - Thời tiết theo kết quả trên Google - > ping 8.8.8.8 - Lệnh Shell - Bluetooth - Bluetooth trong Cài đặt Windows - sn - Ghi Chú - - - - - From d2818db6bf5ec36c2a4c5ecc824d83b82e53d71a Mon Sep 17 00:00:00 2001 From: DB p Date: Wed, 24 Apr 2024 04:48:31 +0900 Subject: [PATCH 117/151] - Fix Keyboard Navigation Control for Tray Menu - Add Focus Style for toggle --- Flow.Launcher.Infrastructure/Constant.cs | 1 + Flow.Launcher/MainWindow.xaml.cs | 45 ++++++++++++++++-------- Flow.Launcher/SettingWindow.xaml | 44 ++++++++++++++--------- 3 files changed, 59 insertions(+), 31 deletions(-) diff --git a/Flow.Launcher.Infrastructure/Constant.cs b/Flow.Launcher.Infrastructure/Constant.cs index 7a95b52d5..8a95ee79f 100644 --- a/Flow.Launcher.Infrastructure/Constant.cs +++ b/Flow.Launcher.Infrastructure/Constant.cs @@ -7,6 +7,7 @@ namespace Flow.Launcher.Infrastructure public static class Constant { public const string FlowLauncher = "Flow.Launcher"; + public const string FlowLauncherFullName = "Flow Launcher"; public const string Plugins = "Plugins"; public const string PluginMetadataFileName = "plugin.json"; diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 503704046..d7df5a23b 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -27,6 +27,8 @@ using System.Media; using static Flow.Launcher.ViewModel.SettingWindowViewModel; using DataObject = System.Windows.DataObject; using System.Windows.Media; +using System.Windows.Interop; +using System.Runtime.InteropServices; namespace Flow.Launcher { @@ -34,11 +36,14 @@ namespace Flow.Launcher { #region Private Fields + [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern IntPtr SetForegroundWindow(IntPtr hwnd); + private readonly Storyboard _progressBarStoryboard = new Storyboard(); private bool isProgressBarStoryboardPaused; private Settings _settings; private NotifyIcon _notifyIcon; - private ContextMenu contextMenu; + private ContextMenuStrip contextMenu; private MainViewModel _viewModel; private bool _animating; MediaPlayer animationSound = new MediaPlayer(); @@ -258,11 +263,11 @@ namespace Flow.Launcher private void UpdateNotifyIconText() { var menu = contextMenu; - ((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")"; - ((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("GameMode"); - ((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset"); - ((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); - ((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); + //((MenuItem)menu.Items[0]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")"; + //((MenuItem)menu.Items[1]).Header = InternationalizationManager.Instance.GetTranslation("GameMode"); + //((MenuItem)menu.Items[2]).Header = InternationalizationManager.Instance.GetTranslation("PositionReset"); + //((MenuItem)menu.Items[3]).Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"); + //((MenuItem)menu.Items[4]).Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"); } @@ -270,12 +275,12 @@ namespace Flow.Launcher { _notifyIcon = new NotifyIcon { - Text = Infrastructure.Constant.FlowLauncher, + Text = Infrastructure.Constant.FlowLauncherFullName, Icon = Properties.Resources.app, Visible = !_settings.HideNotifyIcon }; - contextMenu = new ContextMenu(); + var contextMenu = new ContextMenu(); var openIcon = new FontIcon { @@ -283,7 +288,8 @@ namespace Flow.Launcher }; var open = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", Icon = openIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayOpen") + " (" + _settings.Hotkey + ")", + Icon = openIcon }; var gamemodeIcon = new FontIcon { @@ -291,7 +297,8 @@ namespace Flow.Launcher }; var gamemode = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("GameMode"), Icon = gamemodeIcon + Header = InternationalizationManager.Instance.GetTranslation("GameMode"), + Icon = gamemodeIcon }; var positionresetIcon = new FontIcon { @@ -299,7 +306,8 @@ namespace Flow.Launcher }; var positionreset = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), Icon = positionresetIcon + Header = InternationalizationManager.Instance.GetTranslation("PositionReset"), + Icon = positionresetIcon }; var settingsIcon = new FontIcon { @@ -307,7 +315,8 @@ namespace Flow.Launcher }; var settings = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), Icon = settingsIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTraySettings"), + Icon = settingsIcon }; var exitIcon = new FontIcon { @@ -315,7 +324,8 @@ namespace Flow.Launcher }; var exit = new MenuItem { - Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), Icon = exitIcon + Header = InternationalizationManager.Instance.GetTranslation("iconTrayExit"), + Icon = exitIcon }; open.Click += (o, e) => _viewModel.ToggleFlowLauncher(); @@ -333,7 +343,6 @@ namespace Flow.Launcher contextMenu.Items.Add(settings); contextMenu.Items.Add(exit); - _notifyIcon.ContextMenuStrip = new ContextMenuStrip(); // it need for close the context menu. if not, context menu can't close. _notifyIcon.MouseClick += (o, e) => { switch (e.Button) @@ -341,9 +350,15 @@ namespace Flow.Launcher case MouseButtons.Left: _viewModel.ToggleFlowLauncher(); break; - case MouseButtons.Right: + contextMenu.IsOpen = true; + // Get context menu handle and bring it to the foreground + if (PresentationSource.FromVisual(contextMenu) is HwndSource hwndSource) + { + _ = SetForegroundWindow(hwndSource.Handle); + } + contextMenu.Focus(); break; } }; diff --git a/Flow.Launcher/SettingWindow.xaml b/Flow.Launcher/SettingWindow.xaml index 96f80f0c5..00de0512c 100644 --- a/Flow.Launcher/SettingWindow.xaml +++ b/Flow.Launcher/SettingWindow.xaml @@ -67,6 +67,20 @@ +