Skip to content

Code Quality: Implemented Command Palette mode in Omnibar #17104

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
May 20, 2025
28 changes: 22 additions & 6 deletions src/Files.App/Data/Items/NavigationBarSuggestionItem.cs
Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
// Copyright (c) Files Community
// Licensed under the MIT License.

using Files.App.Controls;
using Microsoft.UI.Xaml;

namespace Files.App.Data.Items
{
[Obsolete("Remove once Omnibar goes out of experimental.")]
public sealed partial class NavigationBarSuggestionItem : ObservableObject
public sealed partial class NavigationBarSuggestionItem : ObservableObject, IOmnibarTextMemberPathProvider
{
private Style? _ThemedIconStyle;
public Style? ThemedIconStyle { get => _ThemedIconStyle; set => SetProperty(ref _ThemedIconStyle, value); }

private string? _Glyph;
public string? Glyph { get => _Glyph; set => SetProperty(ref _Glyph, value); }

private string? _Text;
public string? Text
{
get => _Text;
set => SetProperty(ref _Text, value);
}
public string? Text { get => _Text; set => SetProperty(ref _Text, value); }

private string? _PrimaryDisplay;
public string? PrimaryDisplay
Expand Down Expand Up @@ -88,5 +93,16 @@ private void UpdatePrimaryDisplay()
}
}
}

public string GetTextMemberPath(string textMemberPath)
{
return textMemberPath switch
{
nameof(Text) => Text,
nameof(PrimaryDisplay) => PrimaryDisplay,
nameof(SearchText) => SearchText,
_ => string.Empty
};
}
}
}
99 changes: 54 additions & 45 deletions src/Files.App/UserControls/NavigationToolbar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<UserControl.Resources>
<ResourceDictionary>
<converters:NullToTrueConverter x:Key="NullToFalseConverter" Inverse="True" />
<converters:NullToVisibilityCollapsedConverter x:Key="NullToVisibilityCollapsedConverter" />
<converters1:BoolNegationConverter x:Key="BoolNegationConverter" />

<ResourceDictionary.MergedDictionaries>
Expand Down Expand Up @@ -367,42 +368,50 @@
IconOnActive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IsFilled=True}"
IconOnInactive="{controls:ThemedIconMarkup Style={StaticResource App.ThemedIcons.Omnibar.Commands}, IconType=Outline}"
ModeName="{x:Bind Commands.OpenCommandPalette.LabelWithHotKey, Mode=OneWay}"
PlaceholderText="{helpers:ResourceString Name=OmnibarCommandPaletteModeTextPlaceholder}">
<!--<controls:OmnibarMode.SuggestionItemTemplate>
<DataTemplate x:DataType="data:OmnibarPaletteSuggestionItem">
<Grid Height="48" ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<controls:ThemedIcon
Width="20"
Height="20"
VerticalAlignment="Center"
Style="{StaticResource App.ThemedIcons.Actions.Copying}" />
<StackPanel Grid.Column="1" VerticalAlignment="Center">
<TextBlock
Style="{StaticResource BodyStrongTextBlockStyle}"
Text="{x:Bind Title}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
<TextBlock
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Style="{StaticResource CaptionTextBlockStyle}"
Text="{x:Bind Description}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
</StackPanel>
<StackPanel Grid.Column="2" VerticalAlignment="Center">
PlaceholderText="{helpers:ResourceString Name=OmnibarCommandPaletteModeTextPlaceholder}"
SuggestionItemsSource="{x:Bind ViewModel.OmnibarCommandPaletteModeSuggestionItems, Mode=OneWay}"
Text="{x:Bind ViewModel.OmnibarCommandPaletteModeText, Mode=TwoWay}"
TextMemberPath="Text"
UpdateTextOnSelect="False">
<controls:OmnibarMode.SuggestionItemTemplate>
<DataTemplate x:DataType="dataitems:NavigationBarSuggestionItem">
<Grid ColumnSpacing="12">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="16" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<Grid Grid.Column="0">
<Viewbox
Width="16"
Height="16"
Visibility="{x:Bind Glyph, Converter={StaticResource NullToVisibilityCollapsedConverter}}">
<FontIcon Foreground="{ThemeResource App.Theme.IconBaseBrush}" Glyph="{x:Bind Glyph}" />
</Viewbox>
<controls:ThemedIcon Style="{x:Bind ThemedIconStyle}" Visibility="{x:Bind ThemedIconStyle, Converter={StaticResource NullToVisibilityCollapsedConverter}}" />
</Grid>

<!-- Primary Title -->
<TextBlock
Text="{x:Bind HotKeys}"
x:Name="PrimaryDisplayBlock"
Grid.Column="1"
VerticalAlignment="Center"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
TextTrimming="CharacterEllipsis"
TextWrapping="NoWrap" />
</StackPanel>
</Grid>
</DataTemplate>
</controls:OmnibarMode.SuggestionItemTemplate>-->
TextWrapping="NoWrap">
<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}" />
</TextBlock>

<!-- Keyboard Shortcuts -->
<keyboard:KeyboardShortcut
x:Name="RightAlignedKeyboardShortcut"
Grid.Column="2"
VerticalAlignment="Center"
HotKeys="{x:Bind HotKeys}" />
</Grid>
</DataTemplate>
</controls:OmnibarMode.SuggestionItemTemplate>
</controls:OmnibarMode>

<controls:OmnibarMode
Expand Down Expand Up @@ -467,12 +476,12 @@
<Grid Margin="-16">

<!-- Enable icon again if we add option to always display on the toolbar
<ThemedIcon
x:Name="StatusCenterIcon"
Width="16"
Height="16"
x:Load="{x:Bind OngoingTasksViewModel.HasAnyItemInProgress, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
Style="{StaticResource App.ThemedIcons.StatusCenter}" />-->
<ThemedIcon
x:Name="StatusCenterIcon"
Width="16"
Height="16"
x:Load="{x:Bind OngoingTasksViewModel.HasAnyItemInProgress, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
Style="{StaticResource App.ThemedIcons.StatusCenter}" />-->

<ProgressRing
x:Name="MedianOperationProgressRing"
Expand Down Expand Up @@ -576,29 +585,29 @@
</VisualState.Setters>
</VisualState>
</VisualStateGroup>

<VisualStateGroup x:Name="RightActionsGroup">
<VisualState>
<VisualState.Setters>
<Setter Target="RightSideActionsStackPanel.Margin" Value="0" />
</VisualState.Setters>
</VisualState>
</VisualState>
<VisualState x:Name="StatusButtonVisible">
<VisualState.StateTriggers>
<triggers:IsEqualStateTrigger Value="{x:Bind OngoingTasksViewModel.HasAnyItem, Mode=OneWay}" To="True" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RightSideActionsStackPanel.Margin" Value="0,0,4,0" />
</VisualState.Setters>
</VisualState>
</VisualState>
<VisualState x:Name="ShelfButtonVisible">
<VisualState.StateTriggers>
<triggers:IsEqualStateTrigger Value="{x:Bind ViewModel.ShowShelfPaneToggleButton, Mode=OneWay}" To="True" />
</VisualState.StateTriggers>
<VisualState.Setters>
<Setter Target="RightSideActionsStackPanel.Margin" Value="0,0,4,0" />
</VisualState.Setters>
</VisualState>
</VisualState>
<VisualState x:Name="UpdateButtonVisible">
<VisualState.StateTriggers>
<triggers:IsEqualStateTrigger Value="{x:Bind ViewModel.IsUpdateAvailable, Mode=OneWay}" To="True" />
Expand All @@ -608,7 +617,7 @@
</VisualState.Setters>
</VisualState>
</VisualStateGroup>

<VisualStateGroup x:Name="StatusIconStates">
<VisualState x:Name="TasksSuccess">
<VisualState.StateTriggers>
Expand Down
52 changes: 47 additions & 5 deletions src/Files.App/UserControls/NavigationToolbar.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -256,19 +256,61 @@ private void ClickablePath_GettingFocus(UIElement sender, GettingFocusEventArgs

private async void Omnibar_QuerySubmitted(Omnibar sender, OmnibarQuerySubmittedEventArgs args)
{
await ViewModel.HandleItemNavigationAsync(args.Text);
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
{
await ViewModel.HandleItemNavigationAsync(args.Text);
}
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
{
}
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
{
}
}

private async void Omnibar_SuggestionChosen(Omnibar sender, OmnibarSuggestionChosenEventArgs args)
{
if (args.SelectedItem is OmnibarPathModeSuggestionModel item &&
!string.IsNullOrEmpty(item.Path))
await ViewModel.HandleItemNavigationAsync(item.Path);
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
{
if (args.SelectedItem is OmnibarPathModeSuggestionModel item &&
!string.IsNullOrEmpty(item.Path))
await ViewModel.HandleItemNavigationAsync(item.Path);
}
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
{
if (args.SelectedItem is not NavigationBarSuggestionItem item || item.Text is not { } commandText)
return;

var command = Commands[commandText];
if (command == Commands.None)
await DialogDisplayHelper.ShowDialogAsync(Strings.InvalidCommand.GetLocalizedResource(),
string.Format(Strings.InvalidCommandContent.GetLocalizedResource(), commandText));
else if (!command.IsExecutable)
await DialogDisplayHelper.ShowDialogAsync(Strings.CommandNotExecutable.GetLocalizedResource(),
string.Format(Strings.CommandNotExecutableContent.GetLocalizedResource(), command.Code));
else
await command.ExecuteAsync();

Omnibar.ChangeMode(OmnibarPathMode);
}
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
{
}
}

private async void Omnibar_TextChanged(Omnibar sender, OmnibarTextChangedEventArgs args)
{
await ViewModel.PopulateOmnibarSuggestionsForPathMode();
if (Omnibar.CurrentSelectedMode == OmnibarPathMode)
{
await ViewModel.PopulateOmnibarSuggestionsForPathMode();
}
else if (Omnibar.CurrentSelectedMode == OmnibarCommandPaletteMode)
{
ViewModel.PopulateOmnibarSuggestionsForCommandPaletteMode();
}
else if (Omnibar.CurrentSelectedMode == OmnibarSearchMode)
{
}
}

private async void BreadcrumbBar_ItemClicked(Controls.BreadcrumbBar sender, Controls.BreadcrumbBarItemClickedEventArgs args)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ public sealed partial class NavigationToolbarViewModel : ObservableObject, IAddr

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

internal ObservableCollection<NavigationBarSuggestionItem> OmnibarCommandPaletteModeSuggestionItems { get; } = [];

public bool IsSingleItemOverride { get; set; }

public bool SearchHasFocus { get; private set; }
Expand Down Expand Up @@ -218,6 +220,8 @@ public string? PathText
}
}

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

private bool _IsOmnibarFocused;
public bool IsOmnibarFocused
Expand All @@ -239,6 +243,8 @@ public bool IsOmnibarFocused
_ = PopulateOmnibarSuggestionsForPathMode();
break;
case OmnibarPaletteModeName:
if (OmnibarCommandPaletteModeSuggestionItems.Count is 0)
PopulateOmnibarSuggestionsForCommandPaletteMode();
break;
case OmnibarSearchModeName:
break;
Expand Down Expand Up @@ -1126,6 +1132,30 @@ void AddNoResultsItem()
}
}

public void PopulateOmnibarSuggestionsForCommandPaletteMode()
{
OmnibarCommandPaletteModeText ??= string.Empty;
OmnibarCommandPaletteModeSuggestionItems.Clear();

var suggestionItems = Commands.Where(command =>
command.IsExecutable &&
command.IsAccessibleGlobally &&
(command.Description.Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase) ||
command.Code.ToString().Contains(OmnibarCommandPaletteModeText, StringComparison.OrdinalIgnoreCase)))
.Select(command => new NavigationBarSuggestionItem()
{
ThemedIconStyle = command.Glyph.ToThemedIconStyle(),
Glyph = command.Glyph.BaseGlyph,
Text = command.Code.ToString(),
PrimaryDisplay = command.Description,
HotKeys = command.HotKeys,
SearchText = OmnibarCommandPaletteModeText,
});

foreach (var item in suggestionItems)
OmnibarCommandPaletteModeSuggestionItems.Add(item);
}

[Obsolete("Remove once Omnibar goes out of experimental.")]
public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPage shellpage)
{
Expand Down