diff --git a/src/Files.App/Data/Commands/HotKey/HotKey.cs b/src/Files.App/Data/Commands/HotKey/HotKey.cs index 424e9bbe73e4..8b90f42fc04e 100644 --- a/src/Files.App/Data/Commands/HotKey/HotKey.cs +++ b/src/Files.App/Data/Commands/HotKey/HotKey.cs @@ -11,7 +11,7 @@ namespace Files.App.Data.Commands [DebuggerDisplay("{Code}")] public readonly struct HotKey : IEquatable { - private static readonly FrozenDictionary modifiers = new Dictionary() + public static readonly FrozenDictionary modifiers = new Dictionary() { [KeyModifiers.Menu] = GetKeyString("Menu"), [KeyModifiers.Ctrl] = GetKeyString("Control"), @@ -19,7 +19,7 @@ namespace Files.App.Data.Commands [KeyModifiers.Win] = GetKeyString("Windows"), }.ToFrozenDictionary(); - private static readonly FrozenDictionary keys = new Dictionary() + public static readonly FrozenDictionary keys = new Dictionary() { [Keys.Enter] = GetKeyString("Enter"), [Keys.Space] = GetKeyString("Space"), diff --git a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs index f78dd6673e94..90ff2a4a4df5 100644 --- a/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs +++ b/src/Files.App/Data/Items/NavigationBarSuggestionItem.cs @@ -69,6 +69,13 @@ public string? SupplementaryDisplay set => SetProperty(ref _SupplementaryDisplay, value); } + private HotKeyCollection _HotKeys = new(); + public HotKeyCollection HotKeys + { + get => _HotKeys; + set => SetProperty(ref _HotKeys, value); + } + private void UpdatePrimaryDisplay() { if (SearchText is null || PrimaryDisplay is null) diff --git a/src/Files.App/UserControls/AddressToolbar.xaml b/src/Files.App/UserControls/AddressToolbar.xaml index f60185b763b4..89f8b2a05a19 100644 --- a/src/Files.App/UserControls/AddressToolbar.xaml +++ b/src/Files.App/UserControls/AddressToolbar.xaml @@ -12,6 +12,7 @@ xmlns:extensions="using:CommunityToolkit.WinUI.UI" xmlns:helpers="using:Files.App.Helpers" xmlns:items="using:Files.App.Data.Items" + xmlns:keyboard="using:Files.App.UserControls.KeyboardShortcut" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:triggers="using:CommunityToolkit.WinUI.UI.Triggers" xmlns:uc="using:Files.App.UserControls" @@ -28,6 +29,8 @@ + + @@ -313,34 +316,39 @@ - + - - - + + - - + - + Foreground="{ThemeResource TextFillColorSecondaryBrush}" + Text="{x:Bind SecondaryDisplay, Mode=OneWay}" /> - - + + + + diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs new file mode 100644 index 000000000000..3194cdf177ba --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.Properties.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcut + { + public static readonly DependencyProperty ItemTypeProperty = + DependencyProperty.Register( + nameof(ItemType), + typeof(KeyboardShortcutItemKind), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemKind.Outlined, (d, e) => ((KeyboardShortcutItem)d).OnItemTypePropertyChanged())); + + public KeyboardShortcutItemKind ItemType + { + get => (KeyboardShortcutItemKind)GetValue(ItemTypeProperty); + set => SetValue(ItemTypeProperty, value); + } + + public static readonly DependencyProperty SizeProperty = + DependencyProperty.Register( + nameof(Size), + typeof(KeyboardShortcutItemSize), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemSize.Small, (d, e) => ((KeyboardShortcutItem)d).OnSizePropertyChanged())); + + public KeyboardShortcutItemSize Size + { + get => (KeyboardShortcutItemSize)GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public static readonly DependencyProperty HotKeysProperty = + DependencyProperty.Register( + nameof(HotKeys), + typeof(HotKeyCollection), + typeof(KeyboardShortcut), + new PropertyMetadata(defaultValue: new(), (d, e) => ((KeyboardShortcut)d).OnHotKeysPropertyChanged())); + + public HotKeyCollection HotKeys + { + get => (HotKeyCollection)GetValue(HotKeysProperty); + set => SetValue(HotKeysProperty, value); + } + + public void OnItemTypePropertyChanged() + { + OnItemTypeChanged(); + } + + public void OnSizePropertyChanged() + { + OnSizeChanged(); + } + + private void OnHotKeysPropertyChanged() + { + OnHotKeysChanged(); + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs new file mode 100644 index 000000000000..a1398e9b8cbd --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.cs @@ -0,0 +1,103 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using CommunityToolkit.WinUI.UI; +using Microsoft.UI.Input; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Automation; +using Microsoft.UI.Xaml.Automation.Peers; +using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Controls.Primitives; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcut : Control + { + internal const string KeyboardShortcutItemsControl = "PART_KeyboardShortcutItemsControl"; + + public KeyboardShortcut() + { + DefaultStyleKey = typeof(KeyboardShortcut); + } + + private void OnItemTypeChanged() + { + } + + private void OnSizeChanged() + { + } + + private void OnHotKeysChanged() + { + if (HotKeys.IsEmpty) + return; + + List items = new(); + + foreach (var item in HotKeys) + { + if (items.Any()) + { + items.Add(new() { Text = ",", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + + switch(item.Key, item.Modifier) + { + // No keys or modifiers specified + case (Keys.None, KeyModifiers.None): + break; + + // Key modifiers only + case (Keys.None, _): + GetModifierCode(item.Modifier); + items.RemoveAt(items.Count - 1); + break; + + // Keys only + case (_, KeyModifiers.None): + var key = HotKey.keys[item.Key]; + items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); + break; + + // Others + default: + GetModifierCode(item.Modifier); + key = HotKey.keys[item.Key]; + items.Add(new() { Text = key, ItemType = ItemType, Size = Size }); + break; + } + + void GetModifierCode(KeyModifiers modifier) + { + if (modifier.HasFlag(KeyModifiers.Menu)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Menu], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Ctrl)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Ctrl], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Shift)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Shift], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + if (modifier.HasFlag(KeyModifiers.Win)) + { + items.Add(new() { Text = HotKey.modifiers[KeyModifiers.Win], ItemType = ItemType, Size = Size }); + items.Add(new() { Text = "+", ItemType = KeyboardShortcutItemKind.TextOnly, Size = Size }); + } + } + } + + // Set value + if (GetTemplateChild(KeyboardShortcutItemsControl) is ItemsControl keyboardShortcutItemsControl) + { + keyboardShortcutItemsControl.ItemsSource = items; + } + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml new file mode 100644 index 000000000000..1cc08525a54e --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcut.xaml @@ -0,0 +1,171 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs new file mode 100644 index 000000000000..7b694933119d --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.Properties.cs @@ -0,0 +1,65 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcutItem + { + public static readonly DependencyProperty ItemTypeProperty = + DependencyProperty.Register( + nameof(ItemType), + typeof(KeyboardShortcutItemKind), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemKind.Outlined, (d, e) => ((KeyboardShortcutItem)d).OnItemTypePropertyChanged())); + + public KeyboardShortcutItemKind ItemType + { + get => (KeyboardShortcutItemKind)GetValue(ItemTypeProperty); + set => SetValue(ItemTypeProperty, value); + } + + public static readonly DependencyProperty SizeProperty = + DependencyProperty.Register( + nameof(Size), + typeof(KeyboardShortcutItemSize), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: KeyboardShortcutItemSize.Small, (d, e) => ((KeyboardShortcutItem)d).OnSizePropertyChanged())); + + public KeyboardShortcutItemSize Size + { + get => (KeyboardShortcutItemSize)GetValue(SizeProperty); + set => SetValue(SizeProperty, value); + } + + public static readonly DependencyProperty TextProperty = + DependencyProperty.Register( + nameof(Text), + typeof(string), + typeof(KeyboardShortcutItem), + new PropertyMetadata(defaultValue: string.Empty, (d, e) => ((KeyboardShortcutItem)d).OnTextPropertyChanged())); + + public string Text + { + get => (string)GetValue(TextProperty); + set => SetValue(TextProperty, value); + } + + public void OnItemTypePropertyChanged() + { + OnItemTypeChanged(); + } + + public void OnSizePropertyChanged() + { + OnSizeChanged(); + } + + public void OnTextPropertyChanged() + { + OnTextChanged(); + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs new file mode 100644 index 000000000000..fb174fceda9a --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItem.cs @@ -0,0 +1,46 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public sealed partial class KeyboardShortcutItem : Control + { + internal const string SmallState = "Small"; + internal const string MediumState = "Medium"; + internal const string LargeState = "Large"; + + internal const string MainTextTextBlock = "PART_MainTextTextBlock"; + + public KeyboardShortcutItem() + { + DefaultStyleKey = typeof(KeyboardShortcutItem); + } + + private void OnItemTypeChanged() + { + } + + private void OnSizeChanged() + { + switch (Size) + { + case KeyboardShortcutItemSize.Small: + VisualStateManager.GoToState(this, SmallState, true); + break; + case KeyboardShortcutItemSize.Medium: + VisualStateManager.GoToState(this, MediumState, true); + break; + case KeyboardShortcutItemSize.Large: + VisualStateManager.GoToState(this, LargeState, true); + break; + } + } + + private void OnTextChanged() + { + } + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs new file mode 100644 index 000000000000..2233a13cccb9 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemKind.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.UserControls.KeyboardShortcut +{ + public enum KeyboardShortcutItemKind + { + Outlined, + + Filled, + + TextOnly, + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs new file mode 100644 index 000000000000..80a629d00421 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemSize.cs @@ -0,0 +1,14 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +namespace Files.App.UserControls.KeyboardShortcut +{ + public enum KeyboardShortcutItemSize + { + Small, + + Medium, + + Large, + } +} diff --git a/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs new file mode 100644 index 000000000000..132e916c11c0 --- /dev/null +++ b/src/Files.App/UserControls/KeyboardShortcut/KeyboardShortcutItemStyleSelector.cs @@ -0,0 +1,33 @@ +// Copyright (c) 2023 Files Community +// Licensed under the MIT License. See the LICENSE. + +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls; + +namespace Files.App.UserControls.KeyboardShortcut +{ + public class KeyboardShortcutItemStyleSelector : StyleSelector + { + public Style OutlinedItemStyle { get; set; } = null!; + + public Style FilledItemStyle { get; set; } = null!; + + public Style TextOnlyItemStyle { get; set; } = null!; + + protected override Style SelectStyleCore(object item, DependencyObject container) + { + if (container is KeyboardShortcutItem keyboardShortcutItem) + { + return keyboardShortcutItem.ItemType switch + { + KeyboardShortcutItemKind.Outlined => OutlinedItemStyle, + KeyboardShortcutItemKind.Filled => FilledItemStyle, + KeyboardShortcutItemKind.TextOnly => TextOnlyItemStyle, + _ => OutlinedItemStyle, + }; + } + + return OutlinedItemStyle; + } + } +} diff --git a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs index 01603ecaa120..ff16fb2ec9fa 100644 --- a/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs +++ b/src/Files.App/ViewModels/UserControls/AddressToolbarViewModel.cs @@ -839,14 +839,15 @@ public async Task SetAddressBarSuggestionsAsync(AutoSuggestBox sender, IShellPag { IsCommandPaletteOpen = true; var searchText = sender.Text.Substring(1).Trim(); - suggestions = Commands.Where(command => command.IsExecutable && - (command.Description.Contains(searchText, StringComparison.OrdinalIgnoreCase) - || command.Code.ToString().Contains(searchText, StringComparison.OrdinalIgnoreCase))) + suggestions = Commands.Where(command => + command.IsExecutable && + (command.Description.Contains(searchText, StringComparison.OrdinalIgnoreCase) || + command.Code.ToString().Contains(searchText, StringComparison.OrdinalIgnoreCase))) .Select(command => new NavigationBarSuggestionItem() { Text = ">" + command.Code, PrimaryDisplay = command.Description, - SupplementaryDisplay = command.HotKeyText, + HotKeys = command.HotKeys, SearchText = searchText, }).ToList(); }