[PTRun]Fix cutting off and using previous input (#19215)

* Deselect previous selected item because it might not be valid.

* fast execute non-delayed queries

* Speling fixxes.

* Fixed _isTextSetProgrammatically check for when PTRSearchQueryFastResultsWithDelay = false

* Resoved some comments.

* Added partial delay for fast plugins

* Updates settings UI for second throttle value. Changed text.

* 'Verbiage' update
This commit is contained in:
Jeff Lord 2022-08-10 06:07:53 -04:00 committed by GitHub
parent 5d6160cf7a
commit 7f4a2ca6db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 285 additions and 61 deletions

View File

@ -159,15 +159,16 @@ namespace PowerLauncher
SearchBox.QueryTextBox.DataContext = _viewModel;
SearchBox.QueryTextBox.PreviewKeyDown += Launcher_KeyDown;
SetupSearchTextBoxReactiveness(_viewModel.GetSearchQueryResultsWithDelaySetting(), _viewModel.GetSearchInputDelaySetting());
SetupSearchTextBoxReactiveness(_viewModel.GetSearchQueryResultsWithDelaySetting());
_viewModel.RegisterSettingsChangeListener(
(s, prop_e) =>
{
if (prop_e.PropertyName == nameof(PowerToysRunSettings.SearchQueryResultsWithDelay) || prop_e.PropertyName == nameof(PowerToysRunSettings.SearchInputDelay))
if (prop_e.PropertyName == nameof(PowerToysRunSettings.SearchQueryResultsWithDelay) || prop_e.PropertyName == nameof(PowerToysRunSettings.SearchInputDelay) || prop_e.PropertyName == nameof(PowerToysRunSettings.SearchInputDelayFast))
{
Application.Current.Dispatcher.Invoke(() =>
{
SetupSearchTextBoxReactiveness(_viewModel.GetSearchQueryResultsWithDelaySetting(), _viewModel.GetSearchInputDelaySetting());
SetupSearchTextBoxReactiveness(_viewModel.GetSearchQueryResultsWithDelaySetting());
});
}
});
@ -191,7 +192,7 @@ namespace PowerLauncher
BringProcessToForeground();
}
private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay, int searchInputDelayMs)
private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay)
{
if (_reactiveSubscription != null)
{
@ -206,10 +207,54 @@ namespace PowerLauncher
_reactiveSubscription = Observable.FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
add => SearchBox.QueryTextBox.TextChanged += add,
remove => SearchBox.QueryTextBox.TextChanged -= remove)
.Do(@event => ClearAutoCompleteText((TextBox)@event.Sender))
.Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelayFast))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, false)))
.Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, true)))
.Subscribe();
/*
if (_settings.PTRSearchQueryFastResultsWithDelay)
{
// old mode, delay fast and delayed execution
_reactiveSubscription = Observable.FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
add => SearchBox.QueryTextBox.TextChanged += add,
remove => SearchBox.QueryTextBox.TextChanged -= remove)
.Do(@event => ClearAutoCompleteText((TextBox)@event.Sender))
.Throttle(TimeSpan.FromMilliseconds(searchInputDelayMs))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender)))
.Subscribe();
}
else
{
if (_settings.PTRSearchQueryFastResultsWithPartialDelay)
{
// new mode, fire non-delayed right away, and then later the delayed execution
_reactiveSubscription = Observable.FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
add => SearchBox.QueryTextBox.TextChanged += add,
remove => SearchBox.QueryTextBox.TextChanged -= remove)
.Do(@event => ClearAutoCompleteText((TextBox)@event.Sender))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, false)))
.Throttle(TimeSpan.FromMilliseconds(searchInputDelayMs))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, true)))
.Subscribe();
}
else
{
// new mode, fire non-delayed after short delay, and then later the delayed execution
_reactiveSubscription = Observable.FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
add => SearchBox.QueryTextBox.TextChanged += add,
remove => SearchBox.QueryTextBox.TextChanged -= remove)
.Do(@event => ClearAutoCompleteText((TextBox)@event.Sender))
.Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelayFast))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, false)))
.Throttle(TimeSpan.FromMilliseconds(searchInputDelayMs))
.Do(@event => Dispatcher.InvokeAsync(() => PerformSearchQuery((TextBox)@event.Sender, true)))
.Subscribe();
}
}
*/
}
else
{
@ -475,21 +520,79 @@ namespace PowerLauncher
{
SearchBox.AutoCompleteTextBlock.Text = string.Empty;
}
var showResultsWithDelay = _viewModel.GetSearchQueryResultsWithDelaySetting();
// only if we are using throttled search and throttled 'fast' search, do we need to do anything different with the current results.
if (showResultsWithDelay && _settings.PTRSearchQueryFastResultsWithDelay)
{
// Default means we don't do anything we did not do before... leave the results as is, they will be changed as needed when results are returned
var pTRunStartNewSearchAction = _settings.PTRunStartNewSearchAction ?? "Default";
if (pTRunStartNewSearchAction == "DeSelect")
{
// leave the results, be deselect anything to it will not be activated by <enter> key, can still be arrow-key or clicked though
if (!_isTextSetProgrammatically)
{
DeselectAllResults();
}
}
else if (pTRunStartNewSearchAction == "Clear")
{
// remove all results to prepare for new results, this causes flashing usually and is not cool
if (!_isTextSetProgrammatically)
{
ClearResults();
}
}
}
}
private void ClearResults()
{
_viewModel.Results.SelectedItem = null;
System.Threading.Tasks.Task.Run(() =>
{
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
{
_viewModel.Results.Clear();
_viewModel.Results.Results.NotifyChanges();
}));
});
}
private void DeselectAllResults()
{
Application.Current.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal, new Action(() =>
{
_viewModel.Results.SelectedIndex = -1;
}));
}
private void PerformSearchQuery(TextBox textBox)
{
PerformSearchQuery(textBox, null);
}
private void PerformSearchQuery(TextBox textBox, bool? delayedExecution)
{
var text = textBox.Text;
if (_isTextSetProgrammatically)
{
textBox.SelectionStart = textBox.Text.Length;
_isTextSetProgrammatically = false;
// because IF this is delayedExecution = false (run fast queries) we know this will be called again with delayedExecution = true
// if we don't do this, the second (partner) call will not be called _isTextSetProgrammatically = true also, and we need it to.
if (delayedExecution.HasValue && delayedExecution.Value)
{
_isTextSetProgrammatically = false;
}
}
else
{
_viewModel.QueryText = text;
_viewModel.Query();
_viewModel.Query(delayedExecution);
}
}

View File

@ -115,6 +115,11 @@ namespace PowerLauncher
_settings.SearchInputDelay = overloadSettings.Properties.SearchInputDelay;
}
if (_settings.SearchInputDelayFast != overloadSettings.Properties.SearchInputDelayFast)
{
_settings.SearchInputDelayFast = overloadSettings.Properties.SearchInputDelayFast;
}
if (_settings.SearchClickedItemWeight != overloadSettings.Properties.SearchClickedItemWeight)
{
_settings.SearchClickedItemWeight = overloadSettings.Properties.SearchClickedItemWeight;

View File

@ -455,10 +455,15 @@ namespace PowerLauncher.ViewModel
}
public void Query()
{
Query(null);
}
public void Query(bool? delayedExecution)
{
if (SelectedIsFromQueryResults())
{
QueryResults();
QueryResults(delayedExecution);
}
else if (HistorySelected())
{
@ -510,6 +515,11 @@ namespace PowerLauncher.ViewModel
}
private void QueryResults()
{
QueryResults(null);
}
private void QueryResults(bool? delayedExecution)
{
var queryTuning = GetQueryTuningOptions();
var doFinalSort = queryTuning.SearchQueryTuningEnabled && queryTuning.SearchWaitForSlowResults;
@ -542,20 +552,44 @@ namespace PowerLauncher.ViewModel
// Contains all the plugins for which this raw query is valid
var plugins = pluginQueryPairs.Keys.ToList();
var sw = System.Diagnostics.Stopwatch.StartNew();
try
{
currentCancellationToken.ThrowIfCancellationRequested();
var resultPluginPair = new System.Collections.Concurrent.ConcurrentDictionary<PluginMetadata, List<Result>>();
var resultPluginPair = new List<(List<Result>, PluginMetadata)>();
// To execute a query corresponding to each plugin
foreach (KeyValuePair<PluginPair, Query> pluginQueryItem in pluginQueryPairs)
if (_settings.PTRunNonDelayedSearchInParallel)
{
Parallel.ForEach(pluginQueryPairs, (pluginQueryItem) =>
{
try
{
var plugin = pluginQueryItem.Key;
var query = pluginQueryItem.Value;
var results = PluginManager.QueryForPlugin(plugin, query);
resultPluginPair[plugin.Metadata] = results;
currentCancellationToken.ThrowIfCancellationRequested();
}
catch (OperationCanceledException)
{
// nothing to do here
}
});
sw.Stop();
}
else
{
var plugin = pluginQueryItem.Key;
var query = pluginQueryItem.Value;
var results = PluginManager.QueryForPlugin(plugin, query);
resultPluginPair.Add((results, plugin.Metadata));
currentCancellationToken.ThrowIfCancellationRequested();
// To execute a query corresponding to each plugin
foreach (KeyValuePair<PluginPair, Query> pluginQueryItem in pluginQueryPairs)
{
var plugin = pluginQueryItem.Key;
var query = pluginQueryItem.Value;
var results = PluginManager.QueryForPlugin(plugin, query);
resultPluginPair[plugin.Metadata] = results;
currentCancellationToken.ThrowIfCancellationRequested();
}
}
lock (_addResultsLock)
@ -566,7 +600,7 @@ namespace PowerLauncher.ViewModel
Results.Clear();
foreach (var p in resultPluginPair)
{
UpdateResultView(p.Item1, queryText, currentCancellationToken);
UpdateResultView(p.Value, queryText, currentCancellationToken);
currentCancellationToken.ThrowIfCancellationRequested();
}
@ -588,54 +622,56 @@ namespace PowerLauncher.ViewModel
bool noInitialResults = numResults == 0;
// Run the slower query of the DelayedExecution plugins
currentCancellationToken.ThrowIfCancellationRequested();
Parallel.ForEach(plugins, (plugin) =>
{
try
if (!delayedExecution.HasValue || delayedExecution.Value)
{
// Run the slower query of the DelayedExecution plugins
currentCancellationToken.ThrowIfCancellationRequested();
Parallel.ForEach(plugins, (plugin) =>
{
Query query;
pluginQueryPairs.TryGetValue(plugin, out query);
var results = PluginManager.QueryForPlugin(plugin, query, true);
currentCancellationToken.ThrowIfCancellationRequested();
if ((results?.Count ?? 0) != 0)
try
{
lock (_addResultsLock)
Query query;
pluginQueryPairs.TryGetValue(plugin, out query);
var results = PluginManager.QueryForPlugin(plugin, query, true);
currentCancellationToken.ThrowIfCancellationRequested();
if ((results?.Count ?? 0) != 0)
{
// Using CurrentCultureIgnoreCase since this is user facing
if (queryText.Equals(_currentQuery, StringComparison.CurrentCultureIgnoreCase))
lock (_addResultsLock)
{
currentCancellationToken.ThrowIfCancellationRequested();
// Using CurrentCultureIgnoreCase since this is user facing
if (queryText.Equals(_currentQuery, StringComparison.CurrentCultureIgnoreCase))
{
currentCancellationToken.ThrowIfCancellationRequested();
// Remove the original results from the plugin
Results.Results.RemoveAll(r => r.Result.PluginID == plugin.Metadata.ID);
currentCancellationToken.ThrowIfCancellationRequested();
// Remove the original results from the plugin
Results.Results.RemoveAll(r => r.Result.PluginID == plugin.Metadata.ID);
currentCancellationToken.ThrowIfCancellationRequested();
// Add the new results from the plugin
UpdateResultView(results, queryText, currentCancellationToken);
// Add the new results from the plugin
UpdateResultView(results, queryText, currentCancellationToken);
currentCancellationToken.ThrowIfCancellationRequested();
numResults = Results.Results.Count;
if (!doFinalSort)
{
Results.Sort(queryTuning);
}
}
currentCancellationToken.ThrowIfCancellationRequested();
numResults = Results.Results.Count;
if (!doFinalSort)
{
Results.Sort(queryTuning);
UpdateResultsListViewAfterQuery(queryText, noInitialResults, true);
}
}
currentCancellationToken.ThrowIfCancellationRequested();
if (!doFinalSort)
{
UpdateResultsListViewAfterQuery(queryText, noInitialResults, true);
}
}
}
}
catch (OperationCanceledException)
{
// nothing to do here
}
});
catch (OperationCanceledException)
{
// nothing to do here
}
});
}
}
catch (OperationCanceledException)
{
@ -1135,6 +1171,11 @@ namespace PowerLauncher.ViewModel
return _settings.SearchQueryResultsWithDelay;
}
public int GetSearchInputDelayFastSetting()
{
return _settings.SearchInputDelayFast;
}
public int GetSearchInputDelaySetting()
{
return _settings.SearchInputDelay;

View File

@ -82,12 +82,31 @@ namespace Wox.Infrastructure.UserSettings
private int _searchInputDelay = 150;
private int _searchInputDelayFast = 30;
private int _searchClickedItemWeight = 5;
private bool _searchQueryTuningEnabled;
private bool _searchWaitForSlowResults;
public int SearchInputDelayFast
{
get
{
return _searchInputDelayFast;
}
set
{
if (_searchInputDelayFast != value)
{
_searchInputDelayFast = value;
OnPropertyChanged(nameof(SearchInputDelayFast));
}
}
}
public int SearchInputDelay
{
get
@ -168,6 +187,13 @@ namespace Wox.Infrastructure.UserSettings
public string QueryBoxFontWeight { get; set; }
public bool PTRunNonDelayedSearchInParallel { get; set; } = true;
public string PTRunStartNewSearchAction { get; set; }
public bool PTRSearchQueryFastResultsWithDelay { get; set; }
// public bool PTRSearchQueryFastResultsWithPartialDelay { get; set; }
public string QueryBoxFontStretch { get; set; }
public string ResultFont { get; set; } = FontFamily.GenericSansSerif.Name;

View File

@ -57,6 +57,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
[JsonPropertyName("search_input_delay")]
public int SearchInputDelay { get; set; }
[JsonPropertyName("search_input_delay_fast")]
public int SearchInputDelayFast { get; set; }
[JsonPropertyName("search_clicked_item_weight")]
public int SearchClickedItemWeight { get; set; }

View File

@ -313,6 +313,23 @@ namespace Microsoft.PowerToys.Settings.UI.Library.ViewModels
}
}
public int SearchInputDelayFast
{
get
{
return settings.Properties.SearchInputDelayFast;
}
set
{
if (settings.Properties.SearchInputDelayFast != value)
{
settings.Properties.SearchInputDelayFast = value;
UpdateSettings();
}
}
}
public int SearchInputDelay
{
get

View File

@ -415,16 +415,28 @@
<value>Clear the previous query on launch</value>
</data>
<data name="PowerLauncher_SearchQueryResultsWithDelay.Header" xml:space="preserve">
<value>Delay search</value>
<value>Input Smoothing</value>
<comment>This is about adding a delay to wait for more input before executing a search</comment>
</data>
<data name="PowerLauncher_SearchQueryResultsWithDelay.Description" xml:space="preserve">
<value>Add a delay to wait for more input before executing a search</value>
<value>Wait for more input before searching. This reduces interface jumpiness and system load.</value>
</data>
<data name="PowerLauncher_SearchInputDelayMs.Header" xml:space="preserve">
<value>Search delay (ms)</value>
<data name="PowerLauncher_FastSearchInputDelayMs.Header" xml:space="preserve">
<value>Immediate plugins</value>
</data>
<data name="PowerLauncher_FastSearchInputDelayMs.Description" xml:space="preserve">
<value>Affects the plugins that make the UI wait for their results by this amount. Recommended: 30-50 ms.</value>
</data>
<data name="PowerLauncher_SlowSearchInputDelayMs.Header" xml:space="preserve">
<value>Background execution plugins</value>
</data>
<data name="PowerLauncher_SlowSearchInputDelayMs.Description" xml:space="preserve">
<value>Affects the plugins that execute in the background by this amount. Recommended: 100-150 ms.</value>
</data>
<data name="PowerLauncher_SearchInputDelayMs.Header" xml:space="preserve">
<value>Fast plugin throttle (ms)</value>
<comment>ms = milliseconds</comment>
</data>
</data>
<data name="KeyboardManager_KeysMappingLayoutRightHeader.Text" xml:space="preserve">
<value>To:</value>
<comment>Keyboard Manager mapping keys view right header</comment>

View File

@ -103,9 +103,23 @@
</controls:Setting>
</controls:SettingExpander.Header>
<controls:SettingExpander.Content>
<controls:Setting x:Uid="PowerLauncher_SearchInputDelayMs" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.SearchQueryResultsWithDelay}" Style="{StaticResource ExpanderContentSettingStyle}">
<controls:Setting.ActionContent>
<muxc:NumberBox Minimum="0"
<StackPanel>
<controls:Setting x:Uid="PowerLauncher_FastSearchInputDelayMs" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.SearchQueryResultsWithDelay}" Style="{StaticResource ExpanderContentSettingStyle}">
<controls:Setting.ActionContent>
<muxc:NumberBox Minimum="0"
Maximum="500"
Value="{x:Bind Mode=TwoWay, Path=ViewModel.SearchInputDelayFast}"
MinWidth="{StaticResource SettingActionControlMinWidth}"
SpinButtonPlacementMode="Compact"
HorizontalAlignment="Left"
SmallChange="10"
LargeChange="50"/>
</controls:Setting.ActionContent>
</controls:Setting>
<controls:Setting x:Uid="PowerLauncher_SlowSearchInputDelayMs" IsEnabled="{x:Bind Mode=OneWay, Path=ViewModel.SearchQueryResultsWithDelay}" Style="{StaticResource ExpanderContentSettingStyle}">
<controls:Setting.ActionContent>
<muxc:NumberBox Minimum="0"
Maximum="1000"
Value="{x:Bind Mode=TwoWay, Path=ViewModel.SearchInputDelay}"
MinWidth="{StaticResource SettingActionControlMinWidth}"
@ -113,8 +127,11 @@
HorizontalAlignment="Left"
SmallChange="10"
LargeChange="50"/>
</controls:Setting.ActionContent>
</controls:Setting>
</controls:Setting.ActionContent>
</controls:Setting>
</StackPanel>
</controls:SettingExpander.Content>
</controls:SettingExpander>
<controls:SettingExpander IsExpanded="True">