diff --git a/Flow.Launcher/MainWindow.xaml.cs b/Flow.Launcher/MainWindow.xaml.cs index 06b2dda9eed..b207cddc8c9 100644 --- a/Flow.Launcher/MainWindow.xaml.cs +++ b/Flow.Launcher/MainWindow.xaml.cs @@ -76,6 +76,9 @@ public partial class MainWindow : IDisposable private const double DefaultRightMargin = 66; //* this value from base.xaml private bool _isClockPanelAnimating = false; + // Search Delay + private bool _ignoreTextChange = false; + // IDisposable private bool _disposed = false; @@ -1356,7 +1359,7 @@ private void QueryTextBox_KeyUp(object sender, KeyEventArgs e) if (_viewModel.QueryText != QueryTextBox.Text) { BindingExpression be = QueryTextBox.GetBindingExpression(TextBox.TextProperty); - be.UpdateSource(); + be?.UpdateSource(); } } @@ -1421,11 +1424,19 @@ private void SetupResizeMode() private void QueryTextBox_TextChanged1(object sender, TextChangedEventArgs e) { + if (_ignoreTextChange) return; var textBox = (TextBox)sender; _viewModel.QueryText = textBox.Text; _viewModel.Query(_settings.SearchQueryResultsWithDelay); } + public void SetQueryTextBoxText(string text) + { + _ignoreTextChange = true; + QueryTextBox.Text = text; + _ignoreTextChange = false; + } + #endregion #region Dialog Jump diff --git a/Flow.Launcher/ViewModel/MainViewModel.cs b/Flow.Launcher/ViewModel/MainViewModel.cs index c35f96e6200..868c5555a2b 100644 --- a/Flow.Launcher/ViewModel/MainViewModel.cs +++ b/Flow.Launcher/ViewModel/MainViewModel.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -37,7 +38,7 @@ public partial class MainViewModel : BaseModel, ISavable, IDisposable, IResultUp private Query _lastQuery; private bool _previousIsHomeQuery; - private Query _progressQuery; // Used for QueryResultAsync + private readonly ConcurrentDictionary _progressQueryDict = new(); // Used for QueryResultAsync private Query _updateQuery; // Used for ResultsUpdated private string _queryTextBeforeLeaveResults; private string _ignoredQueryText; // Used to ignore query text change when switching between context menu and query results @@ -759,7 +760,14 @@ public void ChangeQueryText(string queryText, bool isReQuery = false) if (QueryText != queryText) { // Change query text first - QueryText = queryText; + // We use private field and manually set QueryTextBox instead of QueryText setter + // so that QueryTextBox_TextChanged1 will not be invoked to avoid duplicated Query calls + _queryText = queryText; + if (Application.Current?.MainWindow is MainWindow mainWindow) + { + mainWindow.SetQueryTextBoxText(queryText); + } + // When we are changing query from codes, we should not delay the query Query(false, isReQuery: false); @@ -791,7 +799,14 @@ private async Task ChangeQueryTextAsync(string queryText, bool isReQuery = false if (QueryText != queryText) { // Change query text first - QueryText = queryText; + // We use private field and manually set QueryTextBox instead of QueryText setter + // so that QueryTextBox_TextChanged1 will not be invoked to avoid duplicated Query calls + _queryText = queryText; + if (Application.Current?.MainWindow is MainWindow mainWindow) + { + mainWindow.SetQueryTextBoxText(queryText); + } + // When we are changing query from codes, we should not delay the query await QueryAsync(false, isReQuery: false); @@ -870,11 +885,13 @@ private ResultsViewModel SelectedResults } _queryTextBeforeLeaveResults = QueryText; + // We can use QueryText setter because the Query as follows does not requery + QueryText = string.Empty; + // Because of Fody's optimization // setter won't be called when property value is not changed. // so we need manually call Query() // http://stackoverflow.com/posts/25895769/revisions - QueryText = string.Empty; // When we are changing query because selected results are changed to history or context menu, // we should not delay the query Query(false); @@ -1404,6 +1421,9 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b return; } + // Create a Guid for this update session so that we can filter out in progress checking + var updateGuid = Guid.NewGuid(); + try { _updateSource?.Dispose(); @@ -1415,7 +1435,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b ProgressBarVisibility = Visibility.Hidden; - _progressQuery = query; + _progressQueryDict.TryAdd(updateGuid, query); _updateQuery = query; // Switch to ThreadPool thread @@ -1470,7 +1490,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b _ = Task.Delay(200, currentCancellationToken).ContinueWith(_ => { // start the progress bar if query takes more than 200 ms and this is the current running query and it didn't finish yet - if (_progressQuery != null && _progressQuery.OriginalQuery == query.OriginalQuery) + if (_progressQueryDict.TryGetValue(updateGuid, out var progressQuery) && + progressQuery.OriginalQuery == query.OriginalQuery) { ProgressBarVisibility = Visibility.Visible; } @@ -1526,7 +1547,7 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b // this should happen once after all queries are done so progress bar should continue // until the end of all querying - _progressQuery = null; + _progressQueryDict.Remove(updateGuid, out _); if (!currentCancellationToken.IsCancellationRequested) { @@ -1536,8 +1557,8 @@ private async Task QueryResultsAsync(bool searchDelay, bool isReQuery = false, b } finally { - // this make sures progress query is null when this query is canceled - _progressQuery = null; + // this ensures the query is removed from the progress tracking dictionary when this query is canceled or completes + _progressQueryDict.Remove(updateGuid, out _); } // Local function