Support search delay

This commit is contained in:
Jack251970 2025-03-15 21:58:55 +08:00
parent 4bb8bffb78
commit b1a46817f0
4 changed files with 130 additions and 51 deletions

View file

@ -275,6 +275,9 @@ namespace Flow.Launcher.Infrastructure.UserSettings
public bool LeaveCmdOpen { get; set; }
public bool HideWhenDeactivated { get; set; } = true;
public bool SearchQueryResultsWithDelay { get; set; } = false;
public int SearchInputDelay { get; set; } = 150;
[JsonConverter(typeof(JsonStringEnumConverter))]
public SearchWindowScreens SearchWindowScreen { get; set; } = SearchWindowScreens.Cursor;

View file

@ -104,6 +104,7 @@
<PackageReference Include="NHotkey.Wpf" Version="3.0.0" />
<PackageReference Include="PropertyChanged.Fody" Version="3.4.0" />
<PackageReference Include="SemanticVersioning" Version="3.0.0" />
<PackageReference Include="System.Reactive.Linq" Version="6.0.1" />
<PackageReference Include="TaskScheduler" Version="2.12.1" />
<PackageReference Include="VirtualizingWrapPanel" Version="2.1.1" />
</ItemGroup>

View file

@ -5,7 +5,6 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Animation;
using System.Windows.Controls;
using System.Windows.Forms;
using Flow.Launcher.Core.Plugin;
using Flow.Launcher.Core.Resource;
using Flow.Launcher.Helper;
@ -27,6 +26,7 @@ using DataObject = System.Windows.DataObject;
using System.Windows.Media;
using System.Windows.Interop;
using Windows.Win32;
using System.Reactive.Linq;
namespace Flow.Launcher
{
@ -68,6 +68,7 @@ namespace Flow.Launcher
var handle = new WindowInteropHelper(this).Handle;
var win = HwndSource.FromHwnd(handle);
win.AddHook(WndProc);
SetupSearchTextBoxReactiveness(_settings.SearchQueryResultsWithDelay);
};
}
@ -204,63 +205,63 @@ namespace Flow.Launcher
switch (e.PropertyName)
{
case nameof(MainViewModel.MainWindowVisibilityStatus):
{
Dispatcher.Invoke(() =>
{
if (_viewModel.MainWindowVisibilityStatus)
Dispatcher.Invoke(() =>
{
if (_settings.UseSound)
if (_viewModel.MainWindowVisibilityStatus)
{
SoundPlay();
}
if (_settings.UseSound)
{
SoundPlay();
}
UpdatePosition();
PreviewReset();
Activate();
QueryTextBox.Focus();
_settings.ActivateTimes++;
if (!_viewModel.LastQuerySelected)
UpdatePosition();
PreviewReset();
Activate();
QueryTextBox.Focus();
_settings.ActivateTimes++;
if (!_viewModel.LastQuerySelected)
{
QueryTextBox.SelectAll();
_viewModel.LastQuerySelected = true;
}
if (_viewModel.ProgressBarVisibility == Visibility.Visible &&
isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Begin(ProgressBar, true);
isProgressBarStoryboardPaused = false;
}
if (_settings.UseAnimation)
WindowAnimator();
}
else if (!isProgressBarStoryboardPaused)
{
QueryTextBox.SelectAll();
_viewModel.LastQuerySelected = true;
_progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
if (_viewModel.ProgressBarVisibility == Visibility.Visible &&
isProgressBarStoryboardPaused)
});
break;
}
case nameof(MainViewModel.ProgressBarVisibility):
{
Dispatcher.Invoke(() =>
{
if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
else if (_viewModel.MainWindowVisibilityStatus &&
isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Begin(ProgressBar, true);
isProgressBarStoryboardPaused = false;
}
if (_settings.UseAnimation)
WindowAnimator();
}
else if (!isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
});
break;
}
case nameof(MainViewModel.ProgressBarVisibility):
{
Dispatcher.Invoke(() =>
{
if (_viewModel.ProgressBarVisibility == Visibility.Hidden && !isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Stop(ProgressBar);
isProgressBarStoryboardPaused = true;
}
else if (_viewModel.MainWindowVisibilityStatus &&
isProgressBarStoryboardPaused)
{
_progressBarStoryboard.Begin(ProgressBar, true);
isProgressBarStoryboardPaused = false;
}
});
break;
}
});
break;
}
case nameof(MainViewModel.QueryTextCursorMovedToEnd):
if (_viewModel.QueryTextCursorMovedToEnd)
{
@ -415,10 +416,10 @@ namespace Flow.Launcher
{
switch (e.Button)
{
case MouseButtons.Left:
case System.Windows.Forms.MouseButtons.Left:
_viewModel.ToggleFlowLauncher();
break;
case MouseButtons.Right:
case System.Windows.Forms.MouseButtons.Right:
contextMenu.IsOpen = true;
// Get context menu handle and bring it to the foreground
@ -857,5 +858,78 @@ namespace Flow.Launcher
be.UpdateSource();
}
}
#region Search Delay
// Edited from: https://github.com/microsoft/PowerToys
private IDisposable _reactiveSubscription;
private void SetupSearchTextBoxReactiveness(bool showResultsWithDelay)
{
if (_reactiveSubscription != null)
{
_reactiveSubscription.Dispose();
_reactiveSubscription = null;
}
QueryTextBox.TextChanged -= QueryTextBox_TextChanged;
if (showResultsWithDelay)
{
_reactiveSubscription = Observable.FromEventPattern<TextChangedEventHandler, TextChangedEventArgs>(
conversion => (sender, eventArg) => conversion(sender, eventArg),
add => QueryTextBox.TextChanged += add,
remove => QueryTextBox.TextChanged -= remove)
.Do(@event => ClearAutoCompleteText((TextBox)@event.Sender))
.Throttle(TimeSpan.FromMilliseconds(_settings.SearchInputDelay))
.Do(@event => Dispatcher.Invoke(() => PerformSearchQuery((TextBox)@event.Sender)))
.Subscribe();
}
else
{
QueryTextBox.TextChanged += QueryTextBox_TextChanged;
}
}
private void QueryTextBox_TextChanged(object sender, TextChangedEventArgs e)
{
var textBox = (TextBox)sender;
ClearAutoCompleteText(textBox);
PerformSearchQuery(textBox);
}
private void ClearAutoCompleteText(TextBox textBox)
{
var text = textBox.Text;
var autoCompleteText = QueryTextSuggestionBox.Text;
if (ShouldAutoCompleteTextBeEmpty(text, autoCompleteText))
{
QueryTextSuggestionBox.Text = string.Empty;
}
}
private static bool ShouldAutoCompleteTextBeEmpty(string queryText, string autoCompleteText)
{
if (string.IsNullOrEmpty(autoCompleteText))
{
return false;
}
else
{
// Using Ordinal this is internal
return string.IsNullOrEmpty(queryText) || !autoCompleteText.StartsWith(queryText, StringComparison.Ordinal);
}
}
private void PerformSearchQuery(TextBox textBox)
{
var text = textBox.Text;
_viewModel.QueryText = text;
_viewModel.Query();
}
#endregion
}
}

View file

@ -569,7 +569,6 @@ namespace Flow.Launcher.ViewModel
{
_queryText = value;
OnPropertyChanged();
Query();
}
}
@ -631,6 +630,7 @@ namespace Flow.Launcher.ViewModel
{
// re-query is done in QueryText's setter method
QueryText = queryText;
Query();
// set to false so the subsequent set true triggers
// PropertyChanged and MoveQueryTextToEnd is called
QueryTextCursorMovedToEnd = false;
@ -695,6 +695,7 @@ namespace Flow.Launcher.ViewModel
else
{
QueryText = string.Empty;
Query();
}
}