2025-08-22 18:02:10 +02:00
// Copyright (c) Microsoft Corporation
// The Microsoft Corporation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using Microsoft.UI.Xaml ;
using Microsoft.UI.Xaml.Controls ;
using Windows.System ;
namespace KeyboardManagerEditorUI.Controls
{
[TemplatePart(Name = KeyPresenter, Type = typeof(KeyCharPresenter))]
[TemplateVisualState(Name = NormalState, GroupName = "CommonStates")]
[TemplateVisualState(Name = DisabledState, GroupName = "CommonStates")]
[TemplateVisualState(Name = InvalidState, GroupName = "CommonStates")]
2026-02-03 16:24:55 +01:00
[TemplateVisualState(Name = WarningState, GroupName = "CommonStates")]
2025-08-22 18:02:10 +02:00
public sealed partial class KeyVisual : Control
{
private const string KeyPresenter = "KeyPresenter" ;
private const string NormalState = "Normal" ;
private const string DisabledState = "Disabled" ;
private const string InvalidState = "Invalid" ;
2026-02-03 16:24:55 +01:00
private const string WarningState = "Warning" ;
2025-08-22 18:02:10 +02:00
private KeyCharPresenter _keyPresenter ;
public object Content
{
get = > ( object ) GetValue ( ContentProperty ) ;
set = > SetValue ( ContentProperty , value ) ;
}
public static readonly DependencyProperty ContentProperty = DependencyProperty . Register ( nameof ( Content ) , typeof ( object ) , typeof ( KeyVisual ) , new PropertyMetadata ( default ( string ) , OnContentChanged ) ) ;
2026-02-03 16:24:55 +01:00
public State State
2025-08-22 18:02:10 +02:00
{
2026-02-03 16:24:55 +01:00
get = > ( State ) GetValue ( StateProperty ) ;
set = > SetValue ( StateProperty , value ) ;
2025-08-22 18:02:10 +02:00
}
2026-02-03 16:24:55 +01:00
public static readonly DependencyProperty StateProperty = DependencyProperty . Register ( nameof ( State ) , typeof ( State ) , typeof ( KeyVisual ) , new PropertyMetadata ( State . Normal , OnStateChanged ) ) ;
2025-08-22 18:02:10 +02:00
public bool RenderKeyAsGlyph
{
get = > ( bool ) GetValue ( RenderKeyAsGlyphProperty ) ;
set = > SetValue ( RenderKeyAsGlyphProperty , value ) ;
}
public static readonly DependencyProperty RenderKeyAsGlyphProperty = DependencyProperty . Register ( nameof ( RenderKeyAsGlyph ) , typeof ( bool ) , typeof ( KeyVisual ) , new PropertyMetadata ( false , OnContentChanged ) ) ;
#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
public KeyVisual ( )
#pragma warning restore CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable.
{
this . DefaultStyleKey = typeof ( KeyVisual ) ;
}
protected override void OnApplyTemplate ( )
{
IsEnabledChanged - = KeyVisual_IsEnabledChanged ;
_keyPresenter = ( KeyCharPresenter ) this . GetTemplateChild ( KeyPresenter ) ;
Update ( ) ;
SetVisualStates ( ) ;
IsEnabledChanged + = KeyVisual_IsEnabledChanged ;
base . OnApplyTemplate ( ) ;
}
private static void OnContentChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
{
( ( KeyVisual ) d ) . SetVisualStates ( ) ;
}
2026-02-03 16:24:55 +01:00
private static void OnStateChanged ( DependencyObject d , DependencyPropertyChangedEventArgs e )
2025-08-22 18:02:10 +02:00
{
( ( KeyVisual ) d ) . SetVisualStates ( ) ;
}
private void SetVisualStates ( )
{
if ( this ! = null )
{
2026-02-03 16:24:55 +01:00
if ( State = = State . Error )
2025-08-22 18:02:10 +02:00
{
VisualStateManager . GoToState ( this , InvalidState , true ) ;
}
2026-02-03 16:24:55 +01:00
else if ( State = = State . Warning )
{
VisualStateManager . GoToState ( this , WarningState , true ) ;
}
2025-08-22 18:02:10 +02:00
else if ( ! IsEnabled )
{
VisualStateManager . GoToState ( this , DisabledState , true ) ;
}
else
{
VisualStateManager . GoToState ( this , NormalState , true ) ;
}
}
}
private void Update ( )
{
if ( Content = = null )
{
return ;
}
2026-02-03 16:24:55 +01:00
if ( Content is string key )
2025-08-22 18:02:10 +02:00
{
2026-02-03 16:24:55 +01:00
switch ( key )
{
case "Copilot" :
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "CopilotKeyCharPresenterStyle" ] ;
break ;
case "Office" :
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "OfficeKeyCharPresenterStyle" ] ;
break ;
default :
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "DefaultKeyCharPresenterStyle" ] ;
break ;
}
2025-08-22 18:02:10 +02:00
return ;
}
if ( Content is int keyCode )
{
VirtualKey virtualKey = ( VirtualKey ) keyCode ;
switch ( virtualKey )
{
case VirtualKey . Enter :
SetGlyphOrText ( "\uE751" , virtualKey ) ;
break ;
case VirtualKey . Back :
SetGlyphOrText ( "\uE750" , virtualKey ) ;
break ;
case VirtualKey . Shift :
case ( VirtualKey ) 160 : // Left Shift
case ( VirtualKey ) 161 : // Right Shift
SetGlyphOrText ( "\uE752" , virtualKey ) ;
break ;
case VirtualKey . Up :
_keyPresenter . Content = "\uE0E4" ;
break ;
case VirtualKey . Down :
_keyPresenter . Content = "\uE0E5" ;
break ;
case VirtualKey . Left :
_keyPresenter . Content = "\uE0E2" ;
break ;
case VirtualKey . Right :
_keyPresenter . Content = "\uE0E3" ;
break ;
case VirtualKey . LeftWindows :
case VirtualKey . RightWindows :
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "WindowsKeyCharPresenterStyle" ] ;
break ;
}
}
}
private void SetGlyphOrText ( string glyph , VirtualKey key )
{
if ( RenderKeyAsGlyph )
{
_keyPresenter . Content = glyph ;
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "GlyphKeyCharPresenterStyle" ] ;
}
else
{
_keyPresenter . Content = key . ToString ( ) ;
_keyPresenter . Style = ( Style ) Application . Current . Resources [ "DefaultKeyCharPresenterStyle" ] ;
}
}
private void KeyVisual_IsEnabledChanged ( object sender , DependencyPropertyChangedEventArgs e )
{
SetVisualStates ( ) ;
}
}
2026-02-03 16:24:55 +01:00
public enum State
{
Normal ,
Error ,
Warning ,
}
2025-08-22 18:02:10 +02:00
}