mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-02-24 04:00:02 +01:00
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:
@@ -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>();
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)]
|
||||
|
||||
Reference in New Issue
Block a user