Skip to content

Commit df84e67

Browse files
authored
Code Quality: Implemented Command Palette mode in Omnibar (#17104)
1 parent f7ce918 commit df84e67

File tree

4 files changed

+153
-56
lines changed

4 files changed

+153
-56
lines changed

src/Files.App/Data/Items/NavigationBarSuggestionItem.cs

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
// Copyright (c) Files Community
22
// Licensed under the MIT License.
33

4+
using Files.App.Controls;
5+
using Microsoft.UI.Xaml;
6+
47
namespace Files.App.Data.Items
58
{
69
[Obsolete("Remove once Omnibar goes out of experimental.")]
7-
public sealed partial class NavigationBarSuggestionItem : ObservableObject
10+
public sealed partial class NavigationBarSuggestionItem : ObservableObject, IOmnibarTextMemberPathProvider
811
{
12+
private Style? _ThemedIconStyle;
13+
public Style? ThemedIconStyle { get => _ThemedIconStyle; set => SetProperty(ref _ThemedIconStyle, value); }
14+
15+
private string? _Glyph;
16+
public string? Glyph { get => _Glyph; set => SetProperty(ref _Glyph, value); }
17+
918
private string? _Text;
10-
public string? Text
11-
{
12-
get => _Text;
13-
set => SetProperty(ref _Text, value);
14-
}
19+
public string? Text { get => _Text; set => SetProperty(ref _Text, value); }
1520

1621
private string? _PrimaryDisplay;
1722
public string? PrimaryDisplay
@@ -88,5 +93,16 @@ private void UpdatePrimaryDisplay()
8893
}
8994
}
9095
}
96+
97+
public string GetTextMemberPath(string textMemberPath)
98+
{
99+
return textMemberPath switch
100+
{
101+
nameof(Text) => Text,
102+
nameof(PrimaryDisplay) => PrimaryDisplay,
103+
nameof(SearchText) => SearchText,
104+
_ => string.Empty
105+
};
106+
}
91107
}
92108
}

src/Files.App/UserControls/NavigationToolbar.xaml

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<UserControl.Resources>
2828
<ResourceDictionary>
2929
<converters:NullToTrueConverter x:Key="NullToFalseConverter" Inverse="True" />
30+
<converters:NullToVisibilityCollapsedConverter x:Key="NullToVisibilityCollapsedConverter" />
3031
<converters1:BoolNegationConverter x:Key="BoolNegationConverter" />
3132

3233
<ResourceDictionary.MergedDictionaries>
@@ -367,42 +368,50 @@
367368
IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IsFilled=True}"
368369
IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IconType=Outline}"
369370
ModeName="{x:Bind Commands.OpenCommandPalette.LabelWithHotKey, Mode=OneWay}"
370-
PlaceholderText="{helpers:ResourceString Name=OmnibarCommandPaletteModeTextPlaceholder}">
371-
<!--<controls:OmnibarMode.SuggestionItemTemplate>
372-
<DataTemplate x:DataType="data:OmnibarPaletteSuggestionItem">
373-
<Grid Height="48" ColumnSpacing="12">
374-
<Grid.ColumnDefinitions>
375-
<ColumnDefinition Width="Auto" />
376-
<ColumnDefinition Width="*" />
377-
<ColumnDefinition Width="Auto" />
378-
</Grid.ColumnDefinitions>
379-
<controls:ThemedIcon
380-
Width="20"
381-
Height="20"
382-
VerticalAlignment="Center"
383-
Style="{StaticResource App.ThemedIcons.Actions.Copying}" />
384-
<StackPanel Grid.Column="1" VerticalAlignment="Center">
385-
<TextBlock
386-
Style="{StaticResource BodyStrongTextBlockStyle}"
387-
Text="{x:Bind Title}"
388-
TextTrimming="CharacterEllipsis"
389-
TextWrapping="NoWrap" />
390-
<TextBlock
391-
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
392-
Style="{StaticResource CaptionTextBlockStyle}"
393-
Text="{x:Bind Description}"
394-
TextTrimming="CharacterEllipsis"
395-
TextWrapping="NoWrap" />
396-
</StackPanel>
397-
<StackPanel Grid.Column="2" VerticalAlignment="Center">
371+
PlaceholderText="{helpers:ResourceString Name=OmnibarCommandPaletteModeTextPlaceholder}"
372+
SuggestionItemsSource="{x:Bind ViewModel.OmnibarCommandPaletteModeSuggestionItems, Mode=OneWay}"
373+
Text="{x:Bind ViewModel.OmnibarCommandPaletteModeText, Mode=TwoWay}"
374+
TextMemberPath="Text"
375+
UpdateTextOnSelect="False">
376+
<controls:OmnibarMode.SuggestionItemTemplate>
377+
<DataTemplate x:DataType="dataitems:NavigationBarSuggestionItem">
378+
<Grid ColumnSpacing="12">
379+
<Grid.ColumnDefinitions>
380+
<ColumnDefinition Width="16" />
381+
<ColumnDefinition Width="*" />
382+
<ColumnDefinition Width="Auto" />
383+
</Grid.ColumnDefinitions>
384+
385+
<Grid Grid.Column="0">
386+
<Viewbox
387+
Width="16"
388+
Height="16"
389+
Visibility="{x:Bind Glyph, Converter={StaticResource NullToVisibilityCollapsedConverter}}">
390+
<FontIcon Foreground="{ThemeResource App.Theme.IconBaseBrush}" Glyph="{x:Bind Glyph}" />
391+
</Viewbox>
392+
<controls:ThemedIcon Style="{x:Bind ThemedIconStyle}" Visibility="{x:Bind ThemedIconStyle, Converter={StaticResource NullToVisibilityCollapsedConverter}}" />
393+
</Grid>
394+
395+
<!-- Primary Title -->
398396
<TextBlock
399-
Text="{x:Bind HotKeys}"
397+
x:Name="PrimaryDisplayBlock"
398+
Grid.Column="1"
399+
VerticalAlignment="Center"
400+
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
400401
TextTrimming="CharacterEllipsis"
401-
TextWrapping="NoWrap" />
402-
</StackPanel>
403-
</Grid>
404-
</DataTemplate>
405-
</controls:OmnibarMode.SuggestionItemTemplate>-->
402+
TextWrapping="NoWrap">
403+
<Run FontWeight="Normal" Text="{x:Bind PrimaryDisplayPreMatched, Mode=OneWay}" /><Run FontWeight="Bold" Text="{x:Bind PrimaryDisplayMatched, Mode=OneWay}" /><Run FontWeight="Normal" Text="{x:Bind PrimaryDisplayPostMatched, Mode=OneWay}" />
404+
</TextBlock>
405+
406+
<!-- Keyboard Shortcuts -->
407+
<keyboard:KeyboardShortcut
408+
x:Name="RightAlignedKeyboardShortcut"
409+
Grid.Column="2"
410+
VerticalAlignment="Center"
411+
HotKeys="{x:Bind HotKeys}" />
412+
</Grid>
413+
</DataTemplate>
414+
</controls:OmnibarMode.SuggestionItemTemplate>
406415
</controls:OmnibarMode>
407416

408417
<controls:OmnibarMode
@@ -467,12 +476,12 @@
467476
<Grid Margin="-16">
468477

469478
<!-- Enable icon again if we add option to always display on the toolbar
470-
<ThemedIcon
471-
x:Name="StatusCenterIcon"
472-
Width="16"
473-
Height="16"
474-
x:Load="{x:Bind OngoingTasksViewModel.HasAnyItemInProgress, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
475-
Style="{StaticResource App.ThemedIcons.StatusCenter}" />-->
479+
<ThemedIcon
480+
x:Name="StatusCenterIcon"
481+
Width="16"
482+
Height="16"
483+
x:Load="{x:Bind OngoingTasksViewModel.HasAnyItemInProgress, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
484+
Style="{StaticResource App.ThemedIcons.StatusCenter}" />-->
476485

477486
<ProgressRing
478487
x:Name="MedianOperationProgressRing"
@@ -576,29 +585,29 @@
576585
</VisualState.Setters>
577586
</VisualState>
578587
</VisualStateGroup>
579-
588+
580589
<VisualStateGroup x:Name="RightActionsGroup">
581590
<VisualState>
582591
<VisualState.Setters>
583592
<Setter Target="RightSideActionsStackPanel.Margin" Value="0" />
584593
</VisualState.Setters>
585-
</VisualState>
594+
</VisualState>
586595
<VisualState x:Name="StatusButtonVisible">
587596
<VisualState.StateTriggers>
588597
<triggers:IsEqualStateTrigger Value="{x:Bind OngoingTasksViewModel.HasAnyItem, Mode=OneWay}" To="True" />
589598
</VisualState.StateTriggers>
590599
<VisualState.Setters>
591600
<Setter Target="RightSideActionsStackPanel.Margin" Value="0,0,4,0" />
592601
</VisualState.Setters>
593-
</VisualState>
602+
</VisualState>
594603
<VisualState x:Name="ShelfButtonVisible">
595604
<VisualState.StateTriggers>
596605
<triggers:IsEqualStateTrigger Value="{x:Bind ViewModel.ShowShelfPaneToggleButton, Mode=OneWay}" To="True" />
597606
</VisualState.StateTriggers>
598607
<VisualState.Setters>
599608
<Setter Target="RightSideActionsStackPanel.Margin" Value="0,0,4,0" />
600609
</VisualState.Setters>
601-
</VisualState>
610+
</VisualState>
602611
<VisualState x:Name="UpdateButtonVisible">
603612
<VisualState.StateTriggers>
604613
<triggers:IsEqualStateTrigger Value="{x:Bind ViewModel.IsUpdateAvailable, Mode=OneWay}" To="True" />
@@ -608,7 +617,7 @@
608617
</VisualState.Setters>
609618
</VisualState>
610619
</VisualStateGroup>
611-
620+
612621
<VisualStateGroup x:Name="StatusIconStates">
613622
<VisualState x:Name="TasksSuccess">
614623
<VisualState.StateTriggers>

src/Files.App/UserControls/NavigationToolbar.xaml.cs

Lines changed: 47 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -256,19 +256,61 @@ private void ClickablePath_GettingFocus(UIElement sender, GettingFocusEventArgs
256256

257257
private async void Omnibar_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedEventArgs args)
258258
{
259-
await ViewModel.HandleItemNavigationAsync(args.Text);
259+
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
260+
{
261+
await ViewModel.HandleItemNavigationAsync(args.Text);
262+
}
263+
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
264+
{
265+
}
266+
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
267+
{
268+
}
260269
}
261270

262271
private async void Omnibar_SuggestionChosen(Omnibar sender, OmnibarSuggestionChosenEventArgs args)
263272
{
264-
if (args.SelectedItem is OmnibarPathModeSuggestionModel item &&
265-
!string.IsNullOrEmpty(item.Path))
266-
await ViewModel.HandleItemNavigationAsync(item.Path);
273+
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
274+
{
275+
if (args.SelectedItem is OmnibarPathModeSuggestionModel item &&
276+
!string.IsNullOrEmpty(item.Path))
277+
await ViewModel.HandleItemNavigationAsync(item.Path);
278+
}
279+
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
280+
{
281+
if (args.SelectedItem is not NavigationBarSuggestionItem item || item.Text is not { } commandText)
282+
return;
283+
284+
var command = Commands[commandText];
285+
if (command == Commands.None)
286+
await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(),
287+
string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), commandText));
288+
else if (!command.IsExecutable)
289+
await DialogDisplayHelper.ShowDialogAsync(Strings.CommandNotExecutable.GetLocalizedResource(),
290+
string.Format(Strings.CommandNotExecutableContent.GetLocalizedResource(), command.Code));
291+
else
292+
await command.ExecuteAsync();
293+
294+
Omnibar.ChangeMode(OmnibarPathMode);
295+
}
296+
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
297+
{
298+
}
267299
}
268300

269301
private async void Omnibar_TextChanged(Omnibar sender, OmnibarTextChangedEventArgs args)
270302
{
271-
await ViewModel.PopulateOmnibarSuggestionsForPathMode();
303+
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
304+
{
305+
await ViewModel.PopulateOmnibarSuggestionsForPathMode();
306+
}
307+
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
308+
{
309+
ViewModel.PopulateOmnibarSuggestionsForCommandPaletteMode();
310+
}
311+
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
312+
{
313+
}
272314
}
273315

274316
private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Controls.BreadcrumbBarItemClickedEventArgs args)

src/Files.App/ViewModels/UserControls/NavigationToolbarViewModel.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr
7171

7272
internal ObservableCollection<OmnibarPathModeSuggestionModel> PathModeSuggestionItems { get; } = [];
7373

74+
internal ObservableCollection<NavigationBarSuggestionItem> OmnibarCommandPaletteModeSuggestionItems { get; } = [];
75+
7476
public bool IsSingleItemOverride { get; set; }
7577

7678
public bool SearchHasFocus { get; private set; }
@@ -218,6 +220,8 @@ public string? PathText
218220
}
219221
}
220222

223+
private string? _OmnibarCommandPaletteModeText;
224+
public string? OmnibarCommandPaletteModeText { get => _OmnibarCommandPaletteModeText; set => SetProperty(ref _OmnibarCommandPaletteModeText, value); }
221225

222226
private bool _IsOmnibarFocused;
223227
public bool IsOmnibarFocused
@@ -239,6 +243,8 @@ public bool IsOmnibarFocused
239243
_ = PopulateOmnibarSuggestionsForPathMode();
240244
break;
241245
case OmnibarPaletteModeName:
246+
if (OmnibarCommandPaletteModeSuggestionItems.Count is 0)
247+
PopulateOmnibarSuggestionsForCommandPaletteMode();
242248
break;
243249
case OmnibarSearchModeName:
244250
break;
@@ -1126,6 +1132,30 @@ void AddNoResultsItem()
11261132
}
11271133
}
11281134

1135+
public void PopulateOmnibarSuggestionsForCommandPaletteMode()
1136+
{
1137+
OmnibarCommandPaletteModeText ??= string.Empty;
1138+
OmnibarCommandPaletteModeSuggestionItems.Clear();
1139+
1140+
var suggestionItems = Commands.Where(command =>
1141+
command.IsExecutable &&
1142+
command.IsAccessibleGlobally &&
1143+
(command.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase) ||
1144+
command.Code.ToString().Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase)))
1145+
.Select(command => new NavigationBarSuggestionItem()
1146+
{
1147+
ThemedIconStyle = command.Glyph.ToThemedIconStyle(),
1148+
Glyph = command.Glyph.BaseGlyph,
1149+
Text = command.Code.ToString(),
1150+
PrimaryDisplay = command.Description,
1151+
HotKeys = command.HotKeys,
1152+
SearchText = OmnibarCommandPaletteModeText,
1153+
});
1154+
1155+
foreach (var item in suggestionItems)
1156+
OmnibarCommandPaletteModeSuggestionItems.Add(item);
1157+
}
1158+
11291159
[Obsolete("Remove once Omnibar goes out of experimental.")]
11301160
public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPage shellpage)
11311161
{

0 commit comments

Comments
 (0)