Skip to content

Commit

Permalink
Feature: Added a Command Palette (#12977)
Browse files Browse the repository at this point in the history
  • Loading branch information
hishitetsu authored Jul 28, 2023
1 parent 7de0fa4 commit 2e36593
Show file tree
Hide file tree
Showing 10 changed files with 248 additions and 50 deletions.
7 changes: 6 additions & 1 deletion specs/RichCommand/CommandList.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ This is the list of all commands defined in `CommandCodes` enum except `None`.
| | RestoreRecycleBin | Restore | Restore selected item(s) from recycle bin | |
| | RestoreAllRecycleBin | Restore All Items | Restore all items from recycle bin | |
| | OpenItem | Open | Open item(s) | Enter |
| | OpenItemWithApplicationPicker | Open With | Open item(s) with selected application | |
| | OpenItemWithApplicationPicker | Open with | Open item(s) with selected application | |
| | OpenParentFolder | Open parent folder | Open parent folder of searched item | |
| | OpenFileLocation | Open file location | Open the item's location | |
| | RefreshItems | Refresh | Refresh page contents | Ctrl+R, F5 |
Expand Down Expand Up @@ -73,6 +73,7 @@ This is the list of all commands defined in `CommandCodes` enum except `None`.
| | OpenSettings | Settings | Open settings page | Ctrl+, |
| | OpenTerminal | Open in terminal | Open folder in terminal | Ctrl+\` |
| | OpenTerminalAsAdmin | Open in terminal as administrator | Open folder in terminal as administrator | Ctrl+Shift+\` |
| | OpenCommandPalette | Command palette | Open command palette | Ctrl+Shift+P |
| Layout | LayoutDecreaseSize | Decrease size | Decrease icon size in grid view | Ctrl+- |
| | LayoutIncreaseSize | Increase size | Increase icon size in grid view | Ctrl++ |
| | LayoutDetails | Details | Switch to details view | Ctrl+Shift+1 |
Expand Down Expand Up @@ -131,6 +132,9 @@ This is the list of all commands defined in `CommandCodes` enum except `None`.
| | CloseTabsToTheRightSelected | Close tabs to the right | Close tabs to the right of selected tab | |
| | CloseOtherTabsCurrent | Close other tabs | Close tabs other than current tab | |
| | CloseOtherTabsSelected | Close other tabs | Close tabs other than selected tab | |
| | OpenDirectoryInNewPane | Open in new pane | Open directory in new pane | |
| | OpenDirectoryInNewTab | Open in new tab | Open directory in new tab | |
| | OpenInNewWindowItem | Open in new window | Open directory in new window | |
| | ReopenClosedTab | Reopen closed tab | Reopen last closed tab | Ctrl+Shift+T |
| | PreviousTab | Moves to the previous tab | Move to the previous tab | Ctrl+Shift+Tab |
| | NextTab | Moves to the next tab | Move to the next tab | Ctrl+Tab |
Expand All @@ -143,3 +147,4 @@ This is the list of all commands defined in `CommandCodes` enum except `None`.
| | GitPull | Pull | Run git pull | |
| | GitPush | Push | Run git push | |
| | GitSync | Sync | Run git pull and then git push | |
| Tags | OpenAllTaggedItems | Open all | Open all tagged items | |
31 changes: 31 additions & 0 deletions src/Files.App/Actions/Open/OpenCommandPaletteAction.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

namespace Files.App.Actions
{
internal class OpenCommandPaletteAction : IAction
{
private readonly IContentPageContext _context;

public string Label
=> "CommandPalette".GetLocalizedResource();

public string Description
=> "OpenCommandPaletteDescription".GetLocalizedResource();

public HotKey HotKey
=> new(Keys.P, KeyModifiers.CtrlShift);

public OpenCommandPaletteAction()
{
_context = Ioc.Default.GetRequiredService<IContentPageContext>();
}

public Task ExecuteAsync()
{
_context.ShellPage?.ToolbarViewModel.OpenCommandPalette();

return Task.CompletedTask;
}
}
}
1 change: 1 addition & 0 deletions src/Files.App/Data/Commands/CommandCodes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public enum CommandCodes
OpenSettings,
OpenTerminal,
OpenTerminalAsAdmin,
OpenCommandPalette,

// Layout
LayoutDecreaseSize,
Expand Down
16 changes: 16 additions & 0 deletions src/Files.App/Data/Commands/Manager/CommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,20 @@ internal class CommandManager : ICommandManager
private IImmutableDictionary<HotKey, IRichCommand> hotKeys = new Dictionary<HotKey, IRichCommand>().ToImmutableDictionary();

public IRichCommand this[CommandCodes code] => commands.TryGetValue(code, out var command) ? command : None;
public IRichCommand this[string code]
{
get
{
try
{
return commands[Enum.Parse<CommandCodes>(code, true)];
}
catch
{
return None;
}
}
}
public IRichCommand this[HotKey hotKey]
=> hotKeys.TryGetValue(hotKey with { IsVisible = true }, out var command) ? command
: hotKeys.TryGetValue(hotKey with { IsVisible = false }, out command) ? command
Expand Down Expand Up @@ -91,6 +105,7 @@ public IRichCommand this[HotKey hotKey]
public IRichCommand OpenSettings => commands[CommandCodes.OpenSettings];
public IRichCommand OpenTerminal => commands[CommandCodes.OpenTerminal];
public IRichCommand OpenTerminalAsAdmin => commands[CommandCodes.OpenTerminalAsAdmin];
public IRichCommand OpenCommandPalette => commands[CommandCodes.OpenCommandPalette];
public IRichCommand LayoutDecreaseSize => commands[CommandCodes.LayoutDecreaseSize];
public IRichCommand LayoutIncreaseSize => commands[CommandCodes.LayoutIncreaseSize];
public IRichCommand LayoutDetails => commands[CommandCodes.LayoutDetails];
Expand Down Expand Up @@ -252,6 +267,7 @@ public CommandManager()
[CommandCodes.OpenSettings] = new OpenSettingsAction(),
[CommandCodes.OpenTerminal] = new OpenTerminalAction(),
[CommandCodes.OpenTerminalAsAdmin] = new OpenTerminalAsAdminAction(),
[CommandCodes.OpenCommandPalette] = new OpenCommandPaletteAction(),
[CommandCodes.LayoutDecreaseSize] = new LayoutDecreaseSizeAction(),
[CommandCodes.LayoutIncreaseSize] = new LayoutIncreaseSizeAction(),
[CommandCodes.LayoutDetails] = new LayoutDetailsAction(),
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Data/Commands/Manager/ICommandManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ namespace Files.App.Data.Commands
public interface ICommandManager : IEnumerable<IRichCommand>
{
IRichCommand this[CommandCodes code] { get; }
IRichCommand this[string code] { get; }
IRichCommand this[HotKey customHotKey] { get; }

IRichCommand None { get; }
Expand Down Expand Up @@ -89,6 +90,7 @@ public interface ICommandManager : IEnumerable<IRichCommand>
IRichCommand OpenSettings { get; }
IRichCommand OpenTerminal { get; }
IRichCommand OpenTerminalAsAdmin { get; }
IRichCommand OpenCommandPalette { get; }

IRichCommand LayoutDecreaseSize { get; }
IRichCommand LayoutIncreaseSize { get; }
Expand Down
38 changes: 38 additions & 0 deletions src/Files.App/Data/Items/NavigationBarSuggestionItem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using Microsoft.UI.Xaml.Media;

namespace Files.App.Data.Items
{
public class NavigationBarSuggestionItem : ObservableObject
{
private string? _Text;
public string? Text
{
get => _Text;
set => SetProperty(ref _Text, value);
}

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

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

private string? _SupplementaryDisplay;
public string? SupplementaryDisplay
{
get => _SupplementaryDisplay;
set => SetProperty(ref _SupplementaryDisplay, value);
}
}
}
20 changes: 19 additions & 1 deletion src/Files.App/Strings/en-US/Resources.resw
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@
<value>Clear all items</value>
</data>
<data name="NavigationToolbarVisiblePath.PlaceholderText" xml:space="preserve">
<value>Enter a path</value>
<value>Enter a path to navigate to or type ">" to open the command palette</value>
</data>
<data name="Search" xml:space="preserve">
<value>Search</value>
Expand Down Expand Up @@ -3404,4 +3404,22 @@
<data name="OpenAllTaggedItemsDescription" xml:space="preserve">
<value>Open all tagged items</value>
</data>
<data name="InvalidCommand" xml:space="preserve">
<value>Invalid command</value>
</data>
<data name="InvalidCommandContent" xml:space="preserve">
<value>'{0}' is not recognized as a command.</value>
</data>
<data name="CommandNotExecutable" xml:space="preserve">
<value>Command not executable</value>
</data>
<data name="CommandNotExecutableContent" xml:space="preserve">
<value>The '{0}' command is not ready to be executed.</value>
</data>
<data name="CommandPalette" xml:space="preserve">
<value>Command palette</value>
</data>
<data name="OpenCommandPaletteDescription" xml:space="preserve">
<value>Open command palette</value>
</data>
</root>
43 changes: 40 additions & 3 deletions src/Files.App/UserControls/AddressToolbar.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:extensions="using:CommunityToolkit.WinUI.UI"
xmlns:helpers="using:Files.App.Helpers"
xmlns:items="using:Files.App.Data.Items"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers"
xmlns:uc="using:Files.App.UserControls"
Expand All @@ -23,6 +24,7 @@

<UserControl.Resources>
<ResourceDictionary>
<converters:NullToTrueConverter x:Key="NullToFalseConverter" Inverse="True" />
<converters1:BoolNegationConverter x:Key="BoolNegationConverter" />

<ResourceDictionary.MergedDictionaries>
Expand Down Expand Up @@ -124,7 +126,6 @@
BorderBrush="{ThemeResource SystemBaseMediumLowColor}"
BorderThickness="{ThemeResource TextControlBorderThemeThickness}"
CornerRadius="{StaticResource ControlCornerRadius}"
DisplayMemberPath="Name"
FocusDisengaged="VisiblePath_LostFocus"
FontWeight="SemiBold"
ItemsSource="{x:Bind ViewModel.NavigationBarSuggestions, Mode=OneWay}"
Expand All @@ -137,8 +138,44 @@
ScrollViewer.VerticalScrollBarVisibility="Hidden"
Text="{x:Bind ViewModel.PathText, Mode=OneWay}"
TextChanged="{x:Bind ViewModel.VisiblePath_TextChanged, Mode=OneWay}"
TextMemberPath="ItemPath"
Visibility="{x:Bind converters:MultiBooleanConverter.OrNotConvertToVisibility(ShowSearchBox, ViewModel.IsSearchBoxVisible), Mode=OneWay}" />
TextMemberPath="Text"
Visibility="{x:Bind converters:MultiBooleanConverter.OrNotConvertToVisibility(ShowSearchBox, ViewModel.IsSearchBoxVisible), Mode=OneWay}">
<AutoSuggestBox.ItemTemplate>
<DataTemplate x:DataType="items:NavigationBarSuggestionItem">
<StackPanel Margin="0,4">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>

<!-- Primary Display -->
<!-- This is used to display paths or command descriptions. -->
<TextBlock
Grid.Column="0"
Foreground="{ThemeResource TextFillColorPrimaryBrush}"
Text="{x:Bind PrimaryDisplay, Mode=OneWay}" />

<!-- Supplementary Display -->
<!-- This is used to display command hotkeys. -->
<TextBlock
Grid.Column="1"
FontWeight="Normal"
Foreground="{ThemeResource TextFillColorTertiaryBrush}"
Text="{x:Bind SupplementaryDisplay, Mode=OneWay}" />
</Grid>

<!-- Secondary Display -->
<TextBlock
x:Name="SecondaryDisplayBlock"
x:Load="{x:Bind SecondaryDisplay, Mode=OneWay, Converter={StaticResource NullToFalseConverter}}"
FontWeight="Normal"
Foreground="{ThemeResource TextFillColorSecondaryBrush}"
Text="{x:Bind SecondaryDisplay, Mode=OneWay}" />
</StackPanel>
</DataTemplate>
</AutoSuggestBox.ItemTemplate>
</AutoSuggestBox>

<Grid
x:Name="ClickablePath"
Expand Down
14 changes: 8 additions & 6 deletions src/Files.App/UserControls/AddressToolbar.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,16 +1,11 @@
// Copyright (c) 2023 Files Community
// Licensed under the MIT License. See the LICENSE.

using CommunityToolkit.Mvvm.DependencyInjection;
using Files.App.Data.Commands;
using Files.App.ViewModels;
using Files.Core.Services.Settings;
using Microsoft.UI.Input;
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Controls;
using Microsoft.UI.Xaml.Controls.Primitives;
using Microsoft.UI.Xaml.Input;
using System.Windows.Input;
using Windows.System;
using FocusManager = Microsoft.UI.Xaml.Input.FocusManager;

Expand Down Expand Up @@ -72,7 +67,14 @@ private void VisiblePath_Loaded(object _, RoutedEventArgs e)
{
// AutoSuggestBox won't receive focus unless it's fully loaded
VisiblePath.Focus(FocusState.Programmatic);
DependencyObjectHelpers.FindChild<TextBox>(VisiblePath)?.SelectAll();

if (DependencyObjectHelpers.FindChild<TextBox>(VisiblePath) is TextBox textBox)
{
if (textBox.Text.StartsWith(">"))
textBox.Select(1, textBox.Text.Length - 1);
else
textBox.SelectAll();
}
}

private void ManualPathEntryItem_Click(object _, PointerRoutedEventArgs e)
Expand Down
Loading

0 comments on commit 2e36593

Please sign in to comment.