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  }