diff --git a/Flow.Launcher.Core/Plugin/QueryBuilder.cs b/Flow.Launcher.Core/Plugin/QueryBuilder.cs index df336e14b..ef387b693 100644 --- a/Flow.Launcher.Core/Plugin/QueryBuilder.cs +++ b/Flow.Launcher.Core/Plugin/QueryBuilder.cs @@ -10,35 +10,31 @@ namespace Flow.Launcher.Core.Plugin public static Query Build(string text, Dictionary nonGlobalPlugins) { // replace multiple white spaces with one white space - var terms = text.Split(new[] { Query.TermSeperater }, StringSplitOptions.RemoveEmptyEntries); + var terms = text.Split(Query.TermSeparator, StringSplitOptions.RemoveEmptyEntries); if (terms.Length == 0) { // nothing was typed return null; } - var rawQuery = string.Join(Query.TermSeperater, terms); + var rawQuery = string.Join(Query.TermSeparator, terms); string actionKeyword, search; string possibleActionKeyword = terms[0]; - List actionParameters; + string[] searchTerms; + if (nonGlobalPlugins.TryGetValue(possibleActionKeyword, out var pluginPair) && !pluginPair.Metadata.Disabled) { // use non global plugin for query actionKeyword = possibleActionKeyword; - actionParameters = terms.Skip(1).ToList(); - search = actionParameters.Count > 0 ? rawQuery.Substring(actionKeyword.Length + 1) : string.Empty; + search = terms.Length > 1 ? rawQuery[(actionKeyword.Length + 1)..] : string.Empty; + searchTerms = terms[1..]; } else { // non action keyword actionKeyword = string.Empty; search = rawQuery; + searchTerms = terms; } - var query = new Query - { - Terms = terms, - RawQuery = rawQuery, - ActionKeyword = actionKeyword, - Search = search - }; + var query = new Query(rawQuery, search,terms, searchTerms, actionKeyword); return query; } diff --git a/Flow.Launcher.Plugin/Query.cs b/Flow.Launcher.Plugin/Query.cs index 1eb5c9c14..3fcf3c1d7 100644 --- a/Flow.Launcher.Plugin/Query.cs +++ b/Flow.Launcher.Plugin/Query.cs @@ -1,4 +1,5 @@ -using System; +using JetBrains.Annotations; +using System; using System.Collections.Generic; using System.Linq; @@ -11,11 +12,12 @@ namespace Flow.Launcher.Plugin /// /// to allow unit tests for plug ins /// - public Query(string rawQuery, string search, string[] terms, string actionKeyword = "") + public Query(string rawQuery, string search, string[] terms, string[] searchTerms, string actionKeyword = "") { Search = search; RawQuery = rawQuery; Terms = terms; + SearchTerms = searchTerms; ActionKeyword = actionKeyword; } @@ -23,7 +25,7 @@ namespace Flow.Launcher.Plugin /// Raw query, this includes action keyword if it has /// We didn't recommend use this property directly. You should always use Search property. /// - public string RawQuery { get; internal set; } + public string RawQuery { get; internal init; } /// /// Search part of a query. @@ -31,45 +33,53 @@ namespace Flow.Launcher.Plugin /// Since we allow user to switch a exclusive plugin to generic plugin, /// so this property will always give you the "real" query part of the query /// - public string Search { get; internal set; } + public string Search { get; internal init; } /// - /// The raw query splited into a string array. + /// The search string split into a string array. /// - public string[] Terms { get; set; } + public string[] SearchTerms { get; init; } + + /// + /// The raw query split into a string array + /// + [Obsolete("It may or may not include action keyword, which can be confusing. Use SearchTerms instead")] + public string[] Terms { get; init; } /// /// Query can be splited into multiple terms by whitespace /// - public const string TermSeperater = " "; + public const string TermSeparator = " "; + + [Obsolete("Typo")] + public const string TermSeperater = TermSeparator; /// /// User can set multiple action keywords seperated by ';' /// - public const string ActionKeywordSeperater = ";"; + public const string ActionKeywordSeparator = ";"; + + [Obsolete("Typo")] + public const string ActionKeywordSeperater = ActionKeywordSeparator; + /// /// '*' is used for System Plugin /// public const string GlobalPluginWildcardSign = "*"; - public string ActionKeyword { get; set; } + public string ActionKeyword { get; init; } /// /// Return first search split by space if it has /// public string FirstSearch => SplitSearch(0); + private string _secondToEndSearch; + /// /// strings from second search (including) to last search /// - public string SecondToEndSearch - { - get - { - var index = string.IsNullOrEmpty(ActionKeyword) ? 1 : 2; - return string.Join(TermSeperater, Terms.Skip(index).ToArray()); - } - } + public string SecondToEndSearch => SearchTerms.Length > 1 ? (_secondToEndSearch ??= string.Join(' ', SearchTerms[1..])) : ""; /// /// Return second search split by space if it has @@ -83,16 +93,9 @@ namespace Flow.Launcher.Plugin private string SplitSearch(int index) { - try - { - return string.IsNullOrEmpty(ActionKeyword) ? Terms[index] : Terms[index + 1]; - } - catch (IndexOutOfRangeException) - { - return string.Empty; - } + return index < SearchTerms.Length ? SearchTerms[index] : string.Empty; } public override string ToString() => RawQuery; } -} +} \ No newline at end of file diff --git a/Flow.Launcher/ActionKeywords.xaml.cs b/Flow.Launcher/ActionKeywords.xaml.cs index fbb451781..e116e6f4d 100644 --- a/Flow.Launcher/ActionKeywords.xaml.cs +++ b/Flow.Launcher/ActionKeywords.xaml.cs @@ -30,7 +30,7 @@ namespace Flow.Launcher private void ActionKeyword_OnLoaded(object sender, RoutedEventArgs e) { - tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeperater, plugin.Metadata.ActionKeywords.ToArray()); + tbOldActionKeyword.Text = string.Join(Query.ActionKeywordSeparator, plugin.Metadata.ActionKeywords.ToArray()); tbAction.Focus(); } diff --git a/Flow.Launcher/Helper/HotKeyMapper.cs b/Flow.Launcher/Helper/HotKeyMapper.cs index 4d871b345..fe25ecf18 100644 --- a/Flow.Launcher/Helper/HotKeyMapper.cs +++ b/Flow.Launcher/Helper/HotKeyMapper.cs @@ -6,6 +6,8 @@ using NHotkey.Wpf; using Flow.Launcher.Core.Resource; using System.Windows; using Flow.Launcher.ViewModel; +using System.Threading.Tasks; +using System.Threading; namespace Flow.Launcher.Helper { @@ -19,10 +21,15 @@ namespace Flow.Launcher.Helper mainViewModel = mainVM; settings = mainViewModel._settings; - SetHotkey(settings.Hotkey, OnHotkey); + SetHotkey(settings.Hotkey, OnToggleHotkey); LoadCustomPluginHotkey(); } + internal static void OnToggleHotkey(object sender, HotkeyEventArgs args) + { + mainViewModel.ToggleFlowLauncher(); + } + private static void SetHotkey(string hotkeyStr, EventHandler action) { var hotkey = new HotkeyModel(hotkeyStr); @@ -53,44 +60,6 @@ namespace Flow.Launcher.Helper } } - internal static void OnHotkey(object sender, HotkeyEventArgs e) - { - if (!ShouldIgnoreHotkeys()) - { - UpdateLastQUeryMode(); - - mainViewModel.ToggleFlowLauncher(); - e.Handled = true; - } - } - - /// - /// Checks if Flow Launcher should ignore any hotkeys - /// - private static bool ShouldIgnoreHotkeys() - { - return settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen(); - } - - private static void UpdateLastQUeryMode() - { - switch(settings.LastQueryMode) - { - case LastQueryMode.Empty: - mainViewModel.ChangeQueryText(string.Empty); - break; - case LastQueryMode.Preserved: - mainViewModel.LastQuerySelected = true; - break; - case LastQueryMode.Selected: - mainViewModel.LastQuerySelected = false; - break; - default: - throw new ArgumentException($"wrong LastQueryMode: <{settings.LastQueryMode}>"); - - } - } - internal static void LoadCustomPluginHotkey() { if (settings.CustomPluginHotkeys == null) @@ -106,7 +75,7 @@ namespace Flow.Launcher.Helper { SetHotkey(hotkey.Hotkey, (s, e) => { - if (ShouldIgnoreHotkeys()) + if (mainViewModel.ShouldIgnoreHotkeys()) return; mainViewModel.MainWindowVisibility = Visibility.Visible; diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index e84a3148e..057c3f07d 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -262,7 +262,7 @@ namespace Flow.Launcher { if (_settings.HideWhenDeactive) { - Hide(); + _viewModel.Hide(); } } diff --git a/Flow.Launcher/SettingWindow.xaml.cs b/Flow.Launcher/SettingWindow.xaml.cs index 4b58b9c6a..f10b7ab44 100644 --- a/Flow.Launcher/SettingWindow.xaml.cs +++ b/Flow.Launcher/SettingWindow.xaml.cs @@ -25,6 +25,7 @@ namespace Flow.Launcher public readonly IPublicAPI API; private Settings settings; private SettingWindowViewModel viewModel; + private static MainViewModel mainViewModel; public SettingWindow(IPublicAPI api, SettingWindowViewModel viewModel) { @@ -128,7 +129,7 @@ namespace Flow.Launcher if (HotkeyControl.CurrentHotkeyAvailable) { - HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnHotkey); + HotKeyMapper.SetHotkey(HotkeyControl.CurrentHotkey, HotKeyMapper.OnToggleHotkey); HotKeyMapper.RemoveHotkey(settings.Hotkey); settings.Hotkey = HotkeyControl.CurrentHotkey.ToString(); } diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 911f118bd..e54014110 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -21,6 +21,8 @@ using Microsoft.VisualStudio.Threading; using System.Threading.Channels; using ISavable = Flow.Launcher.Plugin.ISavable; using System.Windows.Threading; +using NHotkey; + namespace Flow.Launcher.ViewModel { @@ -108,7 +110,9 @@ namespace Flow.Launcher.ViewModel } Log.Error("MainViewModel", "Unexpected ResultViewUpdate ends"); - }; + } + + ; void continueAction(Task t) { @@ -145,6 +149,24 @@ namespace Flow.Launcher.ViewModel } } + private void UpdateLastQUeryMode() + { + switch (_settings.LastQueryMode) + { + case LastQueryMode.Empty: + ChangeQueryText(string.Empty); + break; + case LastQueryMode.Preserved: + LastQuerySelected = true; + break; + case LastQueryMode.Selected: + LastQuerySelected = false; + break; + default: + throw new ArgumentException($"wrong LastQueryMode: <{_settings.LastQueryMode}>"); + + } + } private void InitializeKeyCommands() { @@ -156,7 +178,18 @@ namespace Flow.Launcher.ViewModel } else { - MainWindowVisibility = Visibility.Collapsed; + Hide(); + } + }); + + ClearQueryCommand = new RelayCommand(_ => + { + if (!string.IsNullOrEmpty(QueryText)) + { + ChangeQueryText(string.Empty); + + // Push Event to UI SystemQuery has changed + //OnPropertyChanged(nameof(SystemQueryText)); } }); @@ -194,7 +227,7 @@ namespace Flow.Launcher.ViewModel if (hideWindow) { - MainWindowVisibility = Visibility.Collapsed; + Hide(); } if (SelectedIsFromQueryResults()) @@ -244,7 +277,7 @@ namespace Flow.Launcher.ViewModel Owner = Application.Current.MainWindow }; - MainWindowVisibility = Visibility.Collapsed; + Hide(); PluginManager .ReloadData() @@ -343,7 +376,6 @@ namespace Flow.Launcher.ViewModel } public Visibility ProgressBarVisibility { get; set; } - public Visibility MainWindowVisibility { get; set; } public ICommand EscCommand { get; set; } @@ -357,6 +389,7 @@ namespace Flow.Launcher.ViewModel public ICommand LoadHistoryCommand { get; set; } public ICommand OpenResultCommand { get; set; } public ICommand ReloadPluginDataCommand { get; set; } + public ICommand ClearQueryCommand { get; private set; } public string OpenResultCommandModifiers { get; private set; } @@ -668,7 +701,7 @@ namespace Flow.Launcher.ViewModel OpenResultCommandModifiers = _settings.OpenResultModifiers ?? DefaultOpenResultModifiers; } - internal void ToggleFlowLauncher() + public async void ToggleFlowLauncher() { if (MainWindowVisibility != Visibility.Visible) { @@ -676,12 +709,48 @@ namespace Flow.Launcher.ViewModel } else { + switch (_settings.LastQueryMode) + { + case LastQueryMode.Empty: + ChangeQueryText(string.Empty); + Application.Current.MainWindow.Opacity = 0; // Trick for no delay + await Task.Delay(100); + Application.Current.MainWindow.Opacity = 1; + break; + case LastQueryMode.Preserved: + LastQuerySelected = true; + break; + case LastQueryMode.Selected: + LastQuerySelected = false; + break; + default: + throw new ArgumentException($"wrong LastQueryMode: <{_settings.LastQueryMode}>"); + } MainWindowVisibility = Visibility.Collapsed; } } + public void Hide() + { + if (MainWindowVisibility != Visibility.Collapsed) + { + ToggleFlowLauncher(); + } + } + #endregion + + /// + /// Checks if Flow Launcher should ignore any hotkeys + /// + public bool ShouldIgnoreHotkeys() + { + return _settings.IgnoreHotkeysOnFullscreen && WindowsInteropHelper.IsWindowFullscreen(); + } + + + #region Public Methods public void Save() diff --git a/Flow.Launcher/ViewModel/PluginViewModel.cs b/Flow.Launcher/ViewModel/PluginViewModel.cs index 6a03663f2..22dc34cfd 100644 --- a/Flow.Launcher/ViewModel/PluginViewModel.cs +++ b/Flow.Launcher/ViewModel/PluginViewModel.cs @@ -36,7 +36,7 @@ namespace Flow.Launcher.ViewModel public Visibility ActionKeywordsVisibility => PluginPair.Metadata.ActionKeywords.Count == 1 ? Visibility.Visible : Visibility.Collapsed; public string InitilizaTime => PluginPair.Metadata.InitTime + "ms"; public string QueryTime => PluginPair.Metadata.AvgQueryTime + "ms"; - public string ActionKeywordsText => string.Join(Query.ActionKeywordSeperater, PluginPair.Metadata.ActionKeywords); + public string ActionKeywordsText => string.Join(Query.ActionKeywordSeparator, PluginPair.Metadata.ActionKeywords); public int Priority => PluginPair.Metadata.Priority; public void ChangeActionKeyword(string newActionKeyword, string oldActionKeyword) diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs index d7cfcaf6a..ec6f8423d 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/ChromeBookmarkLoader.cs @@ -17,7 +17,7 @@ namespace Flow.Launcher.Plugin.BrowserBookmark private List LoadChromeBookmarks() { var bookmarks = new List(); - String platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome\User Data"), "Google Chrome")); bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Google\Chrome SxS\User Data"), "Google Chrome Canary")); bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Chromium\User Data"), "Chromium")); diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs index d982ee99e..276f6368e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/EdgeBookmarkLoader.cs @@ -10,22 +10,17 @@ namespace Flow.Launcher.Plugin.BrowserBookmark { public class EdgeBookmarkLoader : ChromiumBookmarkLoader { - - private readonly List _bookmarks = new(); - - private void LoadEdgeBookmarks() + private List LoadEdgeBookmarks() { + var bookmarks = new List(); var platformPath = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); - LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge"); - LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev"); - LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary"); + bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge\User Data"), "Microsoft Edge")); + bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge Dev\User Data"), "Microsoft Edge Dev")); + bookmarks.AddRange(LoadBookmarks(Path.Combine(platformPath, @"Microsoft\Edge SxS\User Data"), "Microsoft Edge Canary")); + + return bookmarks; } - public override List GetBookmarks() - { - _bookmarks.Clear(); - LoadEdgeBookmarks(); - return _bookmarks; - } + public override List GetBookmarks() => LoadEdgeBookmarks(); } } \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml index 10abea6da..ab854b8ed 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Languages/en.xaml @@ -17,6 +17,6 @@ Load Browser From: Browser Name DataDirectoryPath - Add Custom Browser Bookmark - Delete Custom Browser Bookmark + Add + Delete \ No newline at end of file diff --git a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml index e13663a8d..ff3fbb38e 100644 --- a/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml +++ b/Plugins/Flow.Launcher.Plugin.BrowserBookmark/Views/CustomBrowserSetting.xaml @@ -5,7 +5,8 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:Flow.Launcher.Plugin.BrowserBookmark.Models" mc:Ignorable="d" - Title="CustomBrowserSetting" Height="450" Width="600" + WindowStartupLocation="CenterScreen" + Title="CustomBrowserSetting" Height="350" Width="600" KeyDown="WindowKeyDown" > @@ -13,25 +14,25 @@ - - - + + + + HorizontalAlignment="Left" VerticalAlignment="Center" Margin="80 0 0 0"/> + HorizontalAlignment="Left" VerticalAlignment="Center" Margin="80 0 0 0"/> + HorizontalAlignment="Left" VerticalAlignment="Center" Width="100" Height="30" Margin="50 0 0 0"/> - -