diff --git a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs index ce1269a2995..027eb3f926d 100644 --- a/Flow.Launcher.Infrastructure/UserSettings/Settings.cs +++ b/Flow.Launcher.Infrastructure/UserSettings/Settings.cs @@ -173,7 +173,20 @@ public bool ShowHomePage } } - public bool ShowHistoryResultsForHomePage { get; set; } = false; + private bool _showHistoryResultsForHomePage = false; + public bool ShowHistoryResultsForHomePage + { + get => _showHistoryResultsForHomePage; + set + { + if (_showHistoryResultsForHomePage != value) + { + _showHistoryResultsForHomePage = value; + OnPropertyChanged(); + } + } + } + public int MaxHistoryResultsToShowForHomePage { get; set; } = 5; public int CustomExplorerIndex { get; set; } = 0; @@ -395,29 +408,29 @@ public List RegisteredHotkeys var list = FixedHotkeys(); // Customizeable hotkeys - if(!string.IsNullOrEmpty(Hotkey)) + if (!string.IsNullOrEmpty(Hotkey)) list.Add(new(Hotkey, "flowlauncherHotkey", () => Hotkey = "")); - if(!string.IsNullOrEmpty(PreviewHotkey)) + if (!string.IsNullOrEmpty(PreviewHotkey)) list.Add(new(PreviewHotkey, "previewHotkey", () => PreviewHotkey = "")); - if(!string.IsNullOrEmpty(AutoCompleteHotkey)) + if (!string.IsNullOrEmpty(AutoCompleteHotkey)) list.Add(new(AutoCompleteHotkey, "autoCompleteHotkey", () => AutoCompleteHotkey = "")); - if(!string.IsNullOrEmpty(AutoCompleteHotkey2)) + if (!string.IsNullOrEmpty(AutoCompleteHotkey2)) list.Add(new(AutoCompleteHotkey2, "autoCompleteHotkey", () => AutoCompleteHotkey2 = "")); - if(!string.IsNullOrEmpty(SelectNextItemHotkey)) + if (!string.IsNullOrEmpty(SelectNextItemHotkey)) list.Add(new(SelectNextItemHotkey, "SelectNextItemHotkey", () => SelectNextItemHotkey = "")); - if(!string.IsNullOrEmpty(SelectNextItemHotkey2)) + if (!string.IsNullOrEmpty(SelectNextItemHotkey2)) list.Add(new(SelectNextItemHotkey2, "SelectNextItemHotkey", () => SelectNextItemHotkey2 = "")); - if(!string.IsNullOrEmpty(SelectPrevItemHotkey)) + if (!string.IsNullOrEmpty(SelectPrevItemHotkey)) list.Add(new(SelectPrevItemHotkey, "SelectPrevItemHotkey", () => SelectPrevItemHotkey = "")); - if(!string.IsNullOrEmpty(SelectPrevItemHotkey2)) + if (!string.IsNullOrEmpty(SelectPrevItemHotkey2)) list.Add(new(SelectPrevItemHotkey2, "SelectPrevItemHotkey", () => SelectPrevItemHotkey2 = "")); - if(!string.IsNullOrEmpty(SettingWindowHotkey)) + if (!string.IsNullOrEmpty(SettingWindowHotkey)) list.Add(new(SettingWindowHotkey, "SettingWindowHotkey", () => SettingWindowHotkey = "")); - if(!string.IsNullOrEmpty(OpenContextMenuHotkey)) + if (!string.IsNullOrEmpty(OpenContextMenuHotkey)) list.Add(new(OpenContextMenuHotkey, "OpenContextMenuHotkey", () => OpenContextMenuHotkey = "")); - if(!string.IsNullOrEmpty(SelectNextPageHotkey)) + if (!string.IsNullOrEmpty(SelectNextPageHotkey)) list.Add(new(SelectNextPageHotkey, "SelectNextPageHotkey", () => SelectNextPageHotkey = "")); - if(!string.IsNullOrEmpty(SelectPrevPageHotkey)) + if (!string.IsNullOrEmpty(SelectPrevPageHotkey)) list.Add(new(SelectPrevPageHotkey, "SelectPrevPageHotkey", () => SelectPrevPageHotkey = "")); if (!string.IsNullOrEmpty(CycleHistoryUpHotkey)) list.Add(new(CycleHistoryUpHotkey, "CycleHistoryUpHotkey", () => CycleHistoryUpHotkey = "")); diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 7c324e7cc0a..bb29d78e5e8 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -113,7 +113,7 @@ private void OnSourceInitialized(object sender, EventArgs e) Win32Helper.DisableControlBox(this); } - private async void OnLoaded(object sender, RoutedEventArgs _) + private void OnLoaded(object sender, RoutedEventArgs _) { // Check first launch if (_settings.FirstLaunch) @@ -283,6 +283,7 @@ private async void OnLoaded(object sender, RoutedEventArgs _) InitializeContextMenu(); break; case nameof(Settings.ShowHomePage): + case nameof(Settings.ShowHistoryResultsForHomePage): if (_viewModel.QueryResultsSelected() && string.IsNullOrEmpty(_viewModel.QueryText)) { _viewModel.QueryResults(); diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index 72a93574beb..807275fcbd5 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -265,7 +265,7 @@ public void RegisterResultsUpdatedEvent() if (token.IsCancellationRequested) return; - if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(resultsCopy, pair.Metadata, e.Query, token))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); @@ -791,7 +791,7 @@ private ResultsViewModel SelectedResults public Visibility ProgressBarVisibility { get; set; } public Visibility MainWindowVisibility { get; set; } - + // This is to be used for determining the visibility status of the main window instead of MainWindowVisibility // because it is more accurate and reliable representation than using Visibility as a condition check public bool MainWindowVisibilityStatus { get; set; } = true; @@ -1068,7 +1068,7 @@ private bool CanExternalPreviewSelectedResult(out string path) path = QueryResultsPreviewed() ? Results.SelectedItem?.Result?.Preview.FilePath : string.Empty; return !string.IsNullOrEmpty(path); } - + private bool QueryResultsPreviewed() { var previewed = PreviewSelectedItem == Results.SelectedItem; @@ -1278,8 +1278,6 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // Update the query's IsReQuery property to true if this is a re-query query.IsReQuery = isReQuery; - - ICollection plugins = Array.Empty(); if (currentIsHomeQuery) { @@ -1310,8 +1308,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } } - var validPluginNames = plugins.Select(x => $"<{x.Metadata.Name}>"); - App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: {string.Join(" ", validPluginNames)}"); + App.API.LogDebug(ClassName, $"Valid <{plugins.Count}> plugins: {string.Join(" ", plugins.Select(x => $"<{x.Metadata.Name}>"))}"); // Do not wait for performance improvement /*if (string.IsNullOrEmpty(query.ActionKeyword)) @@ -1339,6 +1336,12 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b Task[] tasks; if (currentIsHomeQuery) { + if (ShouldClearExistingResultsForNonQuery(plugins)) + { + Results.Clear(); + App.API.LogDebug(ClassName, $"Existing results are cleared for non-query"); + } + tasks = plugins.Select(plugin => plugin.Metadata.HomeDisabled switch { false => QueryTaskAsync(plugin, currentCancellationToken), @@ -1430,7 +1433,7 @@ await PluginManager.QueryHomeForPluginAsync(plugin, query, token) : App.API.LogDebug(ClassName, $"Update results for plugin <{plugin.Metadata.Name}>"); // Indicate if to clear existing results so to show only ones from plugins with action keywords - var shouldClearExistingResults = ShouldClearExistingResults(query, currentIsHomeQuery); + var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); _lastQuery = query; _previousIsHomeQuery = currentIsHomeQuery; @@ -1452,8 +1455,13 @@ void QueryHistoryTask(CancellationToken token) App.API.LogDebug(ClassName, $"Update results for history"); + // Indicate if to clear existing results so to show only ones from plugins with action keywords + var shouldClearExistingResults = ShouldClearExistingResultsForQuery(query, currentIsHomeQuery); + _lastQuery = query; + _previousIsHomeQuery = currentIsHomeQuery; + if (!_resultsUpdateChannelWriter.TryWrite(new ResultsForUpdate(results, _historyMetadata, query, - token))) + token, reSelect, shouldClearExistingResults))) { App.API.LogError(ClassName, "Unable to add item to Result Update Queue"); } @@ -1539,7 +1547,9 @@ private async Task BuildQueryAsync(IEnumerable builtIn /// /// Determines whether the existing search results should be cleared based on the current query and the previous query type. - /// This is needed because of the design that treats plugins with action keywords and global action keywords separately. Results are gathered + /// This is used to indicate to QueryTaskAsync or QueryHistoryTask whether to clear results. If both QueryTaskAsync and QueryHistoryTask + /// are not called then use ShouldClearExistingResultsForNonQuery instead. + /// This method needed because of the design that treats plugins with action keywords and global action keywords separately. Results are gathered /// either from plugins with matching action keywords or global action keyword, but not both. So when the current results are from plugins /// with a matching action keyword and a new result set comes from a new query with the global action keyword, the existing results need to be cleared, /// and vice versa. The same applies to home page query results. @@ -1550,19 +1560,39 @@ private async Task BuildQueryAsync(IEnumerable builtIn /// The current query. /// A flag indicating if the current query is a home query. /// True if the existing results should be cleared, false otherwise. - private bool ShouldClearExistingResults(Query query, bool currentIsHomeQuery) + private bool ShouldClearExistingResultsForQuery(Query query, bool currentIsHomeQuery) { // If previous or current results are from home query, we need to clear them if (_previousIsHomeQuery || currentIsHomeQuery) { - App.API.LogDebug(ClassName, $"Cleared old results"); + App.API.LogDebug(ClassName, $"Existing results should be cleared for query"); return true; } // If the last and current query are not home query type, we need to check the action keyword if (_lastQuery?.ActionKeyword != query?.ActionKeyword) { - App.API.LogDebug(ClassName, $"Cleared old results"); + App.API.LogDebug(ClassName, $"Existing results should be cleared for query"); + return true; + } + + return false; + } + + /// + /// Determines whether existing results should be cleared for non-query calls. + /// A non-query call is where QueryTaskAsync and QueryHistoryTask methods are both not called. + /// QueryTaskAsync and QueryHistoryTask both handle result updating (clearing if required) so directly calling + /// Results.Clear() is not required. However when both are not called, we need to directly clear results and this + /// method determines on the condition when clear results should happen. + /// + /// The collection of plugins to check. + /// True if existing results should be cleared, false otherwise. + private bool ShouldClearExistingResultsForNonQuery(ICollection plugins) + { + if (!Settings.ShowHistoryResultsForHomePage && (plugins.Count == 0 || plugins.All(x => x.Metadata.HomeDisabled == true))) + { + App.API.LogDebug(ClassName, $"Existing results should be cleared for non-query"); return true; } diff --git a/Flow.Launcher/ViewModel/ResultsViewModel.cs b/Flow.Launcher/ViewModel/ResultsViewModel.cs index b100bba2518..9cd36e5c480 100644 --- a/Flow.Launcher/ViewModel/ResultsViewModel.cs +++ b/Flow.Launcher/ViewModel/ResultsViewModel.cs @@ -245,7 +245,10 @@ private List NewResults(ICollection resultsFo var newResults = resultsForUpdates.SelectMany(u => u.Results, (u, r) => new ResultViewModel(r, _settings)); if (resultsForUpdates.Any(x => x.shouldClearExistingResults)) + { + App.API.LogDebug("NewResults", $"Existing results are cleared for query"); return newResults.OrderByDescending(rv => rv.Result.Score).ToList(); + } return Results.Where(r => r?.Result != null && resultsForUpdates.All(u => u.ID != r.Result.PluginID)) .Concat(newResults)