Immediately select search text on opening page (#38842)

closes #38712
closes #38315
related to #38379 (and might sufficiently close that too)

This immediately selects the search text when a page is loaded.
![38712-select-search-text-000](https://github.com/user-attachments/assets/9db8b455-9afb-4b11-9a30-8ddaa23cdb64)
This commit is contained in:
Mike Griese
2025-04-16 14:47:55 -05:00
committed by GitHub
parent e95a570d48
commit 67463abf98
3 changed files with 44 additions and 28 deletions

View File

@@ -52,6 +52,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
public string SearchText { get; private set; } = string.Empty; public string SearchText { get; private set; } = string.Empty;
public string InitialSearchText { get; private set; } = string.Empty;
public CommandItemViewModel EmptyContent { get; private set; } public CommandItemViewModel EmptyContent { get; private set; }
private bool _isDynamic; private bool _isDynamic;
@@ -128,7 +130,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
try try
{ {
IListItem[] newItems = _model.Unsafe!.GetItems(); var newItems = _model.Unsafe!.GetItems();
// Collect all the items into new viewmodels // Collect all the items into new viewmodels
Collection<ListItemViewModel> newViewModels = []; Collection<ListItemViewModel> newViewModels = [];
@@ -136,7 +138,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
// TODO we can probably further optimize this by also keeping a // TODO we can probably further optimize this by also keeping a
// HashSet of every ExtensionObject we currently have, and only // HashSet of every ExtensionObject we currently have, and only
// building new viewmodels for the ones we haven't already built. // building new viewmodels for the ones we haven't already built.
foreach (IListItem? item in newItems) foreach (var item in newItems)
{ {
ListItemViewModel viewModel = new(item, new(this)); ListItemViewModel viewModel = new(item, new(this));
@@ -147,8 +149,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
} }
} }
IEnumerable<ListItemViewModel> firstTwenty = newViewModels.Take(20); var firstTwenty = newViewModels.Take(20);
foreach (ListItemViewModel? item in firstTwenty) foreach (var item in firstTwenty)
{ {
item?.SafeInitializeProperties(); item?.SafeInitializeProperties();
} }
@@ -233,7 +235,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
iterable = Items.ToArray(); iterable = Items.ToArray();
} }
foreach (ListItemViewModel item in iterable) foreach (var item in iterable)
{ {
ct.ThrowIfCancellationRequested(); ct.ThrowIfCancellationRequested();
@@ -266,8 +268,8 @@ public partial class ListViewModel : PageViewModel, IDisposable
return 1; return 1;
} }
MatchResult nameMatch = StringMatcher.FuzzySearch(query, listItem.Title); var nameMatch = StringMatcher.FuzzySearch(query, listItem.Title);
MatchResult descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle); var descriptionMatch = StringMatcher.FuzzySearch(query, listItem.Subtitle);
return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max(); return new[] { nameMatch.Score, (descriptionMatch.Score - 4) / 2, 0 }.Max();
} }
@@ -280,7 +282,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
// Similarly stolen from ListHelpers.FilterList // Similarly stolen from ListHelpers.FilterList
public static IEnumerable<ListItemViewModel> FilterList(IEnumerable<ListItemViewModel> items, string query) public static IEnumerable<ListItemViewModel> FilterList(IEnumerable<ListItemViewModel> items, string query)
{ {
IOrderedEnumerable<ScoredListItemViewModel> scores = items var scores = items
.Where(i => !i.IsInErrorState) .Where(i => !i.IsInErrorState)
.Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) }) .Select(li => new ScoredListItemViewModel() { ViewModel = li, Score = ScoreListItem(query, li) })
.Where(score => score.Score > 0) .Where(score => score.Score > 0)
@@ -359,7 +361,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
{ {
base.InitializeProperties(); base.InitializeProperties();
IListPage? model = _model.Unsafe; var model = _model.Unsafe;
if (model == null) if (model == null)
{ {
return; // throw? return; // throw?
@@ -373,8 +375,9 @@ public partial class ListViewModel : PageViewModel, IDisposable
_modelPlaceholderText = model.PlaceholderText; _modelPlaceholderText = model.PlaceholderText;
UpdateProperty(nameof(PlaceholderText)); UpdateProperty(nameof(PlaceholderText));
SearchText = model.SearchText; InitialSearchText = SearchText = model.SearchText;
UpdateProperty(nameof(SearchText)); UpdateProperty(nameof(SearchText));
UpdateProperty(nameof(InitialSearchText));
EmptyContent = new(new(model.EmptyContent), PageContext); EmptyContent = new(new(model.EmptyContent), PageContext);
EmptyContent.SlowInitializeProperties(); EmptyContent.SlowInitializeProperties();
@@ -385,7 +388,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
public void LoadMoreIfNeeded() public void LoadMoreIfNeeded()
{ {
IListPage? model = this._model.Unsafe; var model = this._model.Unsafe;
if (model == null) if (model == null)
{ {
return; return;
@@ -412,7 +415,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
{ {
base.FetchProperty(propertyName); base.FetchProperty(propertyName);
IListPage? model = this._model.Unsafe; var model = this._model.Unsafe;
if (model == null) if (model == null)
{ {
return; // throw? return; // throw?
@@ -475,13 +478,13 @@ public partial class ListViewModel : PageViewModel, IDisposable
lock (_listLock) lock (_listLock)
{ {
foreach (ListItemViewModel item in Items) foreach (var item in Items)
{ {
item.SafeCleanup(); item.SafeCleanup();
} }
Items.Clear(); Items.Clear();
foreach (ListItemViewModel item in FilteredItems) foreach (var item in FilteredItems)
{ {
item.SafeCleanup(); item.SafeCleanup();
} }
@@ -489,7 +492,7 @@ public partial class ListViewModel : PageViewModel, IDisposable
FilteredItems.Clear(); FilteredItems.Clear();
} }
IListPage? model = _model.Unsafe; var model = _model.Unsafe;
if (model != null) if (model != null)
{ {
model.ItemsChanged -= Model_ItemsChanged; model.ItemsChanged -= Model_ItemsChanged;

View File

@@ -250,18 +250,31 @@ public sealed partial class SearchBar : UserControl,
private void Page_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e) private void Page_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{ {
var property = e.PropertyName; var property = e.PropertyName;
if (CurrentPageViewModel is ListViewModel list &&
property == nameof(ListViewModel.SearchText))
{
// Only if the text actually changed...
// (sometimes this triggers on a round-trip of the SearchText)
if (FilterBox.Text != list.SearchText)
{
// ... Update our displayed text, and...
FilterBox.Text = list.SearchText;
// ... Move the cursor to the end of the input if (CurrentPageViewModel is ListViewModel list)
FilterBox.Select(FilterBox.Text.Length, 0); {
if (property == nameof(ListViewModel.SearchText))
{
// Only if the text actually changed...
// (sometimes this triggers on a round-trip of the SearchText)
if (FilterBox.Text != list.SearchText)
{
// ... Update our displayed text, and...
FilterBox.Text = list.SearchText;
// ... Move the cursor to the end of the input
FilterBox.Select(FilterBox.Text.Length, 0);
}
}
else if (property == nameof(ListViewModel.InitialSearchText))
{
// GH #38712:
// The ListPage will notify us of the `InitialSearchText` when
// we first load the viewmodel. We can use that as an
// opportunity to immediately select the search text. That lets
// the user start typing a new search without manually
// selecting the old one.
SelectSearch();
} }
} }
} }

View File

@@ -88,7 +88,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Open services (Ctrl+O). /// Looks up a localized string similar to Open services.
/// </summary> /// </summary>
internal static string wox_plugin_service_open_services { internal static string wox_plugin_service_open_services {
get { get {
@@ -133,7 +133,7 @@ namespace Microsoft.CmdPal.Ext.WindowsServices.Properties {
} }
/// <summary> /// <summary>
/// Looks up a localized string similar to Restart (Ctrl+R). /// Looks up a localized string similar to Restart.
/// </summary> /// </summary>
internal static string wox_plugin_service_restart { internal static string wox_plugin_service_restart {
get { get {