Spec, basic impl for TextToSuggest (#145)

As discussed in #121. 

Adds a new `IListItem.TextToSuggest` property. If an extension populates that, and the user hits right-arrow when the item is selected, we'll set the `SearchText` to that text. 

I didn't give it a UI treatment, because 
1. I'm not good at XAML
2. As noted in https://github.com/zadjii-msft/PowerToys/issues/121#issuecomment-2464704543, there's complicated edge cases here. You have to track what's actually been typed, and then also have a ghost suggestion for the very first item (before the user types/up/downs at all). 

I didn't think it was valuable to implement that right now before we have A Real App. 

* targets #144 
  * which targets #142 
    * which targets #141 

and those guys are all merged as of #150
This commit is contained in:
Mike Griese
2024-11-12 05:58:43 -08:00
committed by GitHub
parent a41e7e7d00
commit 1e7cc5cbda
6 changed files with 56 additions and 33 deletions

View File

@@ -42,20 +42,20 @@ internal static class ResultHelper
if (entry.Exception is null && !(entry.Key is null))
{
// when key contains keys or fields
// TODO: Investigate query text display (i.e. placholder text?) --> result.QueryTextDisplay = entry.Key.Name;
result.TextToSuggest = entry.Key.Name;
result.Subtitle = RegistryHelper.GetSummary(entry.Key);
result.Title = GetTruncatedText(entry.Key.Name, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
}
else if (entry.Key is null && !(entry.Exception is null))
{
// on error (e.g access denied)
// TODO GH #121 Investigate query text display (i.e placeholder text?) --> result.QueryTextDisplay = entry.KeyPath;
result.TextToSuggest = entry.KeyPath;
result.Subtitle = GetTruncatedText(entry.Exception.Message, MaxTextLength.MaximumSubTitleLengthWithTwoSymbols, TruncateSide.OnlyFromRight);
result.Title = GetTruncatedText(entry.KeyPath, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
}
else
{
// TODO GH #121 Investigate query text display (i.e placeholder text?) --> result.QueryTextDisplay = entry.KeyPath;
result.TextToSuggest = entry.KeyPath;
result.Title = GetTruncatedText(entry.KeyPath, MaxTextLength.MaximumTitleLengthWithTwoSymbols);
}
@@ -76,7 +76,7 @@ internal static class ResultHelper
return new List<ListItem>(0);
}
List<KeyValuePair<string, object>> valueList = new List<KeyValuePair<string, object>>(key.ValueCount);
var valueList = new List<KeyValuePair<string, object>>(key.ValueCount);
var resultList = new List<ListItem>();

View File

@@ -24,6 +24,8 @@ public sealed class ListItemViewModel : INotifyPropertyChanged, IDisposable
internal string Icon { get; private set; }
internal string TextToSuggest { get; private set; }
private readonly Lazy<DetailsViewModel?> _details;
internal DetailsViewModel? Details => _details.Value;
@@ -105,6 +107,7 @@ public sealed class ListItemViewModel : INotifyPropertyChanged, IDisposable
this.Title = model.Title;
this.Subtitle = model.Subtitle;
this.Icon = model.Icon.Icon;
this.TextToSuggest = model.TextToSuggest;
if (model.Tags != null)
{
@@ -150,6 +153,10 @@ public sealed class ListItemViewModel : INotifyPropertyChanged, IDisposable
this.Icon = item.Command.Icon.Icon;
BubbleXamlPropertyChanged(nameof(IcoElement));
break;
case nameof(TextToSuggest):
this.TextToSuggest = item.TextToSuggest;
BubbleXamlPropertyChanged(nameof(TextToSuggest));
break;
}
BubbleXamlPropertyChanged(args.PropertyName);

View File

@@ -13,7 +13,6 @@ using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Navigation;
using WindowsCommandPalette;
namespace WindowsCommandPalette.Views;
@@ -52,18 +51,7 @@ public sealed partial class ListPage : Microsoft.UI.Xaml.Controls.Page, INotifyP
}
}
private bool MoreCommandsAvailable
{
get
{
if (ItemsList.SelectedItem is not ListItemViewModel li)
{
return false;
}
return li.HasMoreCommands;
}
}
private bool MoreCommandsAvailable => ItemsList.SelectedItem is not ListItemViewModel li ? false : li.HasMoreCommands;
public ListPage()
{
@@ -173,7 +161,7 @@ public sealed partial class ListPage : Microsoft.UI.Xaml.Controls.Page, INotifyP
private void MoreCommandsButton_Tapped(object sender, TappedRoutedEventArgs e)
{
FlyoutShowOptions options = new FlyoutShowOptions
var options = new FlyoutShowOptions
{
ShowMode = FlyoutShowMode.Standard,
};
@@ -283,6 +271,15 @@ public sealed partial class ListPage : Microsoft.UI.Xaml.Controls.Page, INotifyP
e.Handled = true;
}
else if (e.Key == Windows.System.VirtualKey.Right)
{
if (!string.IsNullOrEmpty(SelectedItem?.TextToSuggest))
{
FilterBox.Text = SelectedItem.TextToSuggest;
FilterBox.Select(SelectedItem.TextToSuggest.Length, 0);
FilterBox.Focus(FocusState.Keyboard);
}
}
else if (e.Key == Windows.System.VirtualKey.Enter /* && ItemsList.SelectedItem != null */)
{
if (ItemsList.SelectedItem is ListItemViewModel li)
@@ -310,7 +307,7 @@ public sealed partial class ListPage : Microsoft.UI.Xaml.Controls.Page, INotifyP
} // ctrl+k
else if (ctrlPressed && e.Key == Windows.System.VirtualKey.K && ActionsDropdown.Items.Count > 0)
{
FlyoutShowOptions options = new FlyoutShowOptions
var options = new FlyoutShowOptions
{
ShowMode = FlyoutShowMode.Standard,
};

View File

@@ -1,7 +1,7 @@
---
author: Mike Griese
created on: 2024-07-19
last updated: 2024-11-07
last updated: 2024-11-08
issue id: n/a
---
@@ -569,6 +569,7 @@ interface IListItem requires INotifyPropChanged {
IDetails Details{ get; };
IFallbackHandler FallbackHandler{ get; };
String Section { get; };
String TextToSuggest { get; };
}
interface IGridProperties {
@@ -703,6 +704,24 @@ actions (if it sets `Details`).
false`. If the user activates the automatic "Show details" action, then the
github action can then fetch the body and show it.
Each item in the list may also provide `TextToSuggest`. This serves a similar
purpose to `result.QueryTextDisplay` in the original PowerToys Run API. When
provided, DevPal will use that text to provide a suggested search query to the
user. The user can accept that suggestion with a single keypress, and DevPal
will then set the `SearchText` on the page to that `TextToSuggest`. This is
especially useful for dynamic lists, to easily populate the entire `SearchText`
as the user navigates the list.
Consider the Windows Registry command. When the page is initially loaded, it
displays only the top-level registry keys (`HKEY_CURRENT_USER`,
`HKEY_LOCAL_MACHINE`, etc). If the user types `HKC`, the command will filter the
results down to just `HKEY_CURRENT_USER`, `HKEY_CLASSES_ROOT` and
`HKEY_CURRENT_CONFIG`. However, if the user at this point taps the right-arrow
key, DevPall will use the `TextToSuggest` from the `HKEY_CURRENT_USER`
`ListItem` to fill the `SearchText` with `"HKEY_CURRENT_USER\"`. The extension
will then be notified of this change to the search text, and can now return
results nested under `HKEY_CURRENT_USER\`.
An example list page for the Hacker News extension:
```cs

View File

@@ -2,8 +2,6 @@
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System.ComponentModel.DataAnnotations;
namespace Microsoft.CmdPal.Extensions.Helpers;
public class ListItem : BaseObservable, IListItem
@@ -17,6 +15,7 @@ public class ListItem : BaseObservable, IListItem
private IContextItem[] _moreCommands = [];
private IFallbackHandler? _fallbackHandler;
private string _section = string.Empty;
private string _textToSuggest = string.Empty;
public IconDataType? Icon
{
@@ -30,17 +29,7 @@ public class ListItem : BaseObservable, IListItem
public string Title
{
get
{
if (!string.IsNullOrEmpty(this._title))
{
return _title;
}
else
{
return _command?.Name ?? string.Empty;
}
}
get => !string.IsNullOrEmpty(this._title) ? _title : _command?.Name ?? string.Empty;
set
{
@@ -115,6 +104,16 @@ public class ListItem : BaseObservable, IListItem
}
}
public string TextToSuggest
{
get => _textToSuggest;
set
{
_textToSuggest = value;
OnPropertyChanged(nameof(TextToSuggest));
}
}
public ListItem(ICommand command)
{
Command = command;

View File

@@ -214,6 +214,7 @@ namespace Microsoft.CmdPal.Extensions
IDetails Details{ get; };
IFallbackHandler FallbackHandler{ get; };
String Section { get; };
String TextToSuggest { get; };
}
[contract(Microsoft.CmdPal.Extensions.ExtensionsContract, 1)]