KeyVisual.cs
1 // Copyright (c) Microsoft Corporation 2 // The Microsoft Corporation licenses this file to you under the MIT license. 3 // See the LICENSE file in the project root for more information. 4 5 using Microsoft.UI.Xaml; 6 using Microsoft.UI.Xaml.Controls; 7 using Microsoft.UI.Xaml.Markup; 8 using Windows.System; 9 10 namespace Microsoft.CmdPal.UI.Controls; 11 12 [TemplatePart(Name = KeyPresenter, Type = typeof(ContentPresenter))] 13 [TemplateVisualState(Name = "Normal", GroupName = "CommonStates")] 14 [TemplateVisualState(Name = "Disabled", GroupName = "CommonStates")] 15 [TemplateVisualState(Name = "Default", GroupName = "StateStates")] 16 [TemplateVisualState(Name = "Error", GroupName = "StateStates")] 17 public sealed partial class KeyVisual : Control 18 { 19 private const string KeyPresenter = "KeyPresenter"; 20 private KeyVisual? _keyVisual; 21 private ContentPresenter _keyPresenter = new(); 22 23 public object Content 24 { 25 get => GetValue(ContentProperty); 26 set => SetValue(ContentProperty, value); 27 } 28 29 public static readonly DependencyProperty ContentProperty = DependencyProperty.Register("Content", typeof(object), typeof(KeyVisual), new PropertyMetadata(default(string), OnContentChanged)); 30 31 public VisualType VisualType 32 { 33 get => (VisualType)GetValue(VisualTypeProperty); 34 set => SetValue(VisualTypeProperty, value); 35 } 36 37 public static readonly DependencyProperty VisualTypeProperty = DependencyProperty.Register("VisualType", typeof(VisualType), typeof(KeyVisual), new PropertyMetadata(default(VisualType), OnSizeChanged)); 38 39 public bool IsError 40 { 41 get => (bool)GetValue(IsErrorProperty); 42 set => SetValue(IsErrorProperty, value); 43 } 44 45 public static readonly DependencyProperty IsErrorProperty = DependencyProperty.Register("IsError", typeof(bool), typeof(KeyVisual), new PropertyMetadata(false, OnIsErrorChanged)); 46 47 public KeyVisual() 48 { 49 this.DefaultStyleKey = typeof(KeyVisual); 50 this.Style = GetStyleSize("TextKeyVisualStyle"); 51 } 52 53 protected override void OnApplyTemplate() 54 { 55 IsEnabledChanged -= KeyVisual_IsEnabledChanged; 56 _keyVisual = this; 57 _keyPresenter = (ContentPresenter)_keyVisual.GetTemplateChild(KeyPresenter); 58 Update(); 59 SetEnabledState(); 60 SetErrorState(); 61 IsEnabledChanged += KeyVisual_IsEnabledChanged; 62 base.OnApplyTemplate(); 63 } 64 65 private static void OnContentChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 66 { 67 ((KeyVisual)d).Update(); 68 } 69 70 private static void OnSizeChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 71 { 72 ((KeyVisual)d).Update(); 73 } 74 75 private static void OnIsErrorChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) 76 { 77 ((KeyVisual)d).SetErrorState(); 78 } 79 80 private void Update() 81 { 82 if (_keyVisual is null) 83 { 84 return; 85 } 86 87 if (_keyVisual.Content is not null) 88 { 89 if (_keyVisual.Content.GetType() == typeof(string)) 90 { 91 _keyVisual.Style = GetStyleSize("TextKeyVisualStyle"); 92 _keyVisual._keyPresenter.Content = _keyVisual.Content; 93 } 94 else 95 { 96 _keyVisual.Style = GetStyleSize("IconKeyVisualStyle"); 97 98 switch ((int)_keyVisual.Content) 99 { 100 /* We can enable other glyphs in the future 101 case 13: // The Enter key or button. 102 _keyVisual._keyPresenter.Content = "\uE751"; break; 103 104 case 8: // The Back key or button. 105 _keyVisual._keyPresenter.Content = "\uE750"; break; 106 107 case 16: // The right Shift key or button. 108 case 160: // The left Shift key or button. 109 case 161: // The Shift key or button. 110 _keyVisual._keyPresenter.Content = "\uE752"; break; */ 111 112 case 38: _keyVisual._keyPresenter.Content = "\uE0E4"; break; // The Up Arrow key or button. 113 case 40: _keyVisual._keyPresenter.Content = "\uE0E5"; break; // The Down Arrow key or button. 114 case 37: _keyVisual._keyPresenter.Content = "\uE0E2"; break; // The Left Arrow key or button. 115 case 39: _keyVisual._keyPresenter.Content = "\uE0E3"; break; // The Right Arrow key or button. 116 117 case 91: // The left Windows key 118 case 92: // The right Windows key 119 var winIcon = XamlReader.Load(@"<PathIcon xmlns=""http://schemas.microsoft.com/winfx/2006/xaml/presentation"" Data=""M683 1229H0V546h683v683zm819 0H819V546h683v683zm-819 819H0v-683h683v683zm819 0H819v-683h683v683z"" />") as PathIcon; 120 var winIconContainer = new Viewbox 121 { 122 Child = winIcon, 123 HorizontalAlignment = HorizontalAlignment.Center, 124 VerticalAlignment = VerticalAlignment.Center, 125 }; 126 127 var iconDimensions = GetIconSize(); 128 winIconContainer.Height = iconDimensions; 129 winIconContainer.Width = iconDimensions; 130 _keyVisual._keyPresenter.Content = winIconContainer; 131 break; 132 default: _keyVisual._keyPresenter.Content = ((VirtualKey)_keyVisual.Content).ToString(); break; 133 } 134 } 135 } 136 } 137 138 public Style GetStyleSize(string styleName) 139 { 140 return VisualType == VisualType.Small 141 ? (Style)App.Current.Resources["Small" + styleName] 142 : VisualType == VisualType.SmallOutline 143 ? (Style)App.Current.Resources["SmallOutline" + styleName] 144 : VisualType == VisualType.TextOnly 145 ? (Style)App.Current.Resources["Only" + styleName] 146 : (Style)App.Current.Resources["Default" + styleName]; 147 } 148 149 public double GetIconSize() 150 { 151 return VisualType == VisualType.Small || VisualType == VisualType.SmallOutline 152 ? (double)App.Current.Resources["SmallIconSize"] 153 : (double)App.Current.Resources["DefaultIconSize"]; 154 } 155 156 private void KeyVisual_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e) 157 { 158 SetEnabledState(); 159 } 160 161 private void SetErrorState() 162 { 163 VisualStateManager.GoToState(this, IsError ? "Error" : "Default", true); 164 } 165 166 private void SetEnabledState() 167 { 168 VisualStateManager.GoToState(this, IsEnabled ? "Normal" : "Disabled", true); 169 } 170 } 171 172 public enum VisualType 173 { 174 Small, 175 SmallOutline, 176 TextOnly, 177 Large, 178 }