-
-
Notifications
You must be signed in to change notification settings - Fork 2.3k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implementing IFormattable on KeyGesture #15828
Changes from 14 commits
2fbc2f4
590d270
3f7b0dd
6480830
c57137a
448f947
8f1ddda
7550c24
7a7685b
b51db46
2a31f07
0ad8ef6
46e926a
bffa604
5611602
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
|
||
namespace Avalonia.Input.Platform | ||
{ | ||
|
||
/// <summary> | ||
/// Provides platform specific formatting information for the KeyGesture class | ||
/// </summary> | ||
/// <param name="platformKeyOverrides">A dictionary of Key to String overrides for specific characters, for example Key.Left to "Left Arrow" or "←" on Mac. | ||
/// A null value is assumed to be the Invariant, so the included set of common overrides will be skipped if this is null. If only the common overrides are | ||
/// desired, pass an empty Dictionary instead.</param> | ||
/// <param name="meta">The string to use for the Meta modifier, defaults to "Cmd"</param> | ||
/// <param name="ctrl">The string to use for the Ctrl modifier, defaults to "Ctrl"</param> | ||
/// <param name="alt">The string to use for the Alt modifier, defaults to "Alt"</param> | ||
/// <param name="shift">The string to use for the Shift modifier, defaults to "Shift"</param> | ||
public sealed class KeyGestureFormatInfo(IReadOnlyDictionary<Key, string>? platformKeyOverrides = null, | ||
string meta = "Cmd", | ||
string ctrl = "Ctrl", | ||
string alt = "Alt", | ||
string shift = "Shift") : IFormatProvider | ||
{ | ||
/// <summary> | ||
/// The Invariant format. Only uses strings straight from the appropriate Enums. | ||
/// </summary> | ||
public static KeyGestureFormatInfo Invariant { get; } = new(); | ||
|
||
/// <summary> | ||
/// The string used to represent Meta on the appropriate platform. Defaults to "Cmd". | ||
/// </summary> | ||
public string Meta { get; } = meta; | ||
|
||
/// <summary> | ||
/// The string used to represent Ctrl on the appropriate platform. Defaults to "Ctrl". | ||
/// </summary> | ||
public string Ctrl { get; } = ctrl; | ||
|
||
/// <summary> | ||
/// The string used to represent Alt on the appropriate platform. Defaults to "Alt". | ||
/// </summary> | ||
public string Alt { get; } = alt; | ||
|
||
/// <summary> | ||
/// The string used to represent Shift on the appropriate platform. Defaults to "Shift". | ||
/// </summary> | ||
public string Shift { get; } = shift; | ||
|
||
public object? GetFormat(Type? formatType) => formatType == typeof(KeyGestureFormatInfo) ? this : null; | ||
|
||
/// <summary> | ||
/// Gets the most appropriate KeyGestureFormatInfo for the IFormatProvider requested. This will be, in order: | ||
/// 1. The provided IFormatProvider as a KeyGestureFormatInfo | ||
/// 2. The currently registered platform specific KeyGestureFormatInfo, if present. | ||
/// 3. The Invariant otherwise. | ||
/// </summary> | ||
/// <param name="formatProvider">The IFormatProvider to get a KeyGestureFormatInfo for.</param> | ||
/// <returns></returns> | ||
public static KeyGestureFormatInfo GetInstance(IFormatProvider? formatProvider) | ||
=> formatProvider?.GetFormat(typeof(KeyGestureFormatInfo)) as KeyGestureFormatInfo | ||
?? AvaloniaLocator.Current.GetService<KeyGestureFormatInfo>() | ||
?? Invariant; | ||
|
||
/// <summary> | ||
/// A dictionary of the common platform Key overrides. These are used as a fallback | ||
/// if platformKeyOverrides doesn't contain the Key in question. | ||
/// </summary> | ||
|
||
private static readonly Dictionary<Key, string> s_commonKeyOverrides = new() | ||
{ | ||
{ Key.Add , "+" }, | ||
{ Key.D0 , "0" }, | ||
{ Key.D1 , "1" }, | ||
{ Key.D2 , "2" }, | ||
{ Key.D3 , "3" }, | ||
{ Key.D4 , "4" }, | ||
{ Key.D5 , "5" }, | ||
{ Key.D6 , "6" }, | ||
{ Key.D7 , "7" }, | ||
{ Key.D8 , "8" }, | ||
{ Key.D9 , "9" }, | ||
{ Key.Decimal , "." }, | ||
{ Key.Divide , "/" }, | ||
{ Key.Multiply , "*" }, | ||
{ Key.OemBackslash , "\\" }, | ||
{ Key.OemCloseBrackets , "]" }, | ||
{ Key.OemComma , "," }, | ||
{ Key.OemMinus , "-" }, | ||
{ Key.OemOpenBrackets , "[" }, | ||
{ Key.OemPeriod , "." }, | ||
{ Key.OemPipe , "|" }, | ||
{ Key.OemPlus , "+" }, | ||
{ Key.OemQuestion , "/" }, | ||
{ Key.OemQuotes , "\"" }, | ||
{ Key.OemSemicolon , ";" }, | ||
{ Key.OemTilde , "`" }, | ||
{ Key.Separator , "/" }, | ||
{ Key.Subtract , "-" }, | ||
{ Key.Back , "Backspace" }, | ||
{ Key.Down , "Down Arrow" }, | ||
{ Key.Left , "Left Arrow" }, | ||
{ Key.Right , "Right Arrow" }, | ||
{ Key.Up , "Up Arrow" } | ||
}; | ||
|
||
/// <summary> | ||
/// Checks the platformKeyOverrides and s_commonKeyOverrides Dictionaries, in order, for the appropriate | ||
/// string to represent the given Key on this platform. | ||
/// NOTE: If platformKeyOverrides is null, this is assumed to be the Invariant and the Dictionaries are not checked. | ||
/// The plain Enum string is returned instead. | ||
/// </summary> | ||
/// <param name="key">The Key to format.</param> | ||
/// <returns>The appropriate platform specific or common override if present, key.ToString() if not or this is the Invariant.</returns> | ||
public string FormatKey(Key key) | ||
{ | ||
/* | ||
* The absence of an Overrides dictionary indicates this is the invariant, and | ||
MrJul marked this conversation as resolved.
Show resolved
Hide resolved
|
||
* so should just return the default ToString() value. | ||
*/ | ||
if (platformKeyOverrides == null) | ||
return key.ToString(); | ||
|
||
return platformKeyOverrides.TryGetValue(key, out string? result) ? result : | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit: can be simplified to |
||
s_commonKeyOverrides.TryGetValue(key, out string? cresult) ? cresult : | ||
key.ToString() ; | ||
|
||
} | ||
|
||
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,7 +22,7 @@ public class PlatformKeyGestureConverter : IValueConverter | |
} | ||
else if (value is KeyGesture gesture && targetType == typeof(string)) | ||
{ | ||
return ToPlatformString(gesture); | ||
return gesture.ToString("p", null); | ||
} | ||
else | ||
{ | ||
|
@@ -41,156 +41,7 @@ public class PlatformKeyGestureConverter : IValueConverter | |
/// </summary> | ||
/// <param name="gesture">The gesture.</param> | ||
/// <returns>The gesture formatted according to the current platform.</returns> | ||
public static string ToPlatformString(KeyGesture gesture) | ||
{ | ||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) | ||
{ | ||
return ToString(gesture, "Win"); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) | ||
{ | ||
return ToString(gesture, "Super"); | ||
} | ||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) | ||
{ | ||
return ToOSXString(gesture); | ||
} | ||
else | ||
{ | ||
return gesture.ToString(); | ||
} | ||
} | ||
|
||
private static string ToString(KeyGesture gesture, string meta) | ||
{ | ||
var s = StringBuilderCache.Acquire(); | ||
|
||
static void Plus(StringBuilder s) | ||
{ | ||
if (s.Length > 0) | ||
{ | ||
s.Append("+"); | ||
} | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Control)) | ||
{ | ||
s.Append("Ctrl"); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Shift)) | ||
{ | ||
Plus(s); | ||
s.Append("Shift"); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Alt)) | ||
{ | ||
Plus(s); | ||
s.Append("Alt"); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Meta)) | ||
{ | ||
Plus(s); | ||
s.Append(meta); | ||
} | ||
|
||
Plus(s); | ||
s.Append(ToString(gesture.Key)); | ||
|
||
return StringBuilderCache.GetStringAndRelease(s); | ||
} | ||
|
||
private static string ToOSXString(KeyGesture gesture) | ||
{ | ||
var s = StringBuilderCache.Acquire(); | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Control)) | ||
{ | ||
s.Append('⌃'); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Alt)) | ||
{ | ||
s.Append('⌥'); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Shift)) | ||
{ | ||
s.Append('⇧'); | ||
} | ||
|
||
if (gesture.KeyModifiers.HasAllFlags(KeyModifiers.Meta)) | ||
{ | ||
s.Append('⌘'); | ||
} | ||
|
||
s.Append(ToOSXString(gesture.Key)); | ||
|
||
return StringBuilderCache.GetStringAndRelease(s); | ||
} | ||
|
||
private static string ToString(Key key) | ||
{ | ||
return key switch | ||
{ | ||
Key.Add => "+", | ||
Key.Back => "Backspace", | ||
Key.D0 => "0", | ||
Key.D1 => "1", | ||
Key.D2 => "2", | ||
Key.D3 => "3", | ||
Key.D4 => "4", | ||
Key.D5 => "5", | ||
Key.D6 => "6", | ||
Key.D7 => "7", | ||
Key.D8 => "8", | ||
Key.D9 => "9", | ||
Key.Decimal => ".", | ||
Key.Divide => "/", | ||
Key.Down => "Down Arrow", | ||
Key.Left => "Left Arrow", | ||
Key.Multiply => "*", | ||
Key.OemBackslash => "\\", | ||
Key.OemCloseBrackets => "]", | ||
Key.OemComma => ",", | ||
Key.OemMinus => "-", | ||
Key.OemOpenBrackets => "[", | ||
Key.OemPeriod=> ".", | ||
Key.OemPipe => "|", | ||
Key.OemPlus => "+", | ||
Key.OemQuestion => "/", | ||
Key.OemQuotes => "\"", | ||
Key.OemSemicolon => ";", | ||
Key.OemTilde => "`", | ||
Key.Right => "Right Arrow", | ||
Key.Separator => "/", | ||
Key.Subtract => "-", | ||
Key.Up => "Up Arrow", | ||
_ => key.ToString(), | ||
}; | ||
} | ||
|
||
private static string ToOSXString(Key key) | ||
{ | ||
return key switch | ||
{ | ||
Key.Back => "⌫", | ||
Key.Down => "↓", | ||
Key.End => "↘", | ||
Key.Escape => "⎋", | ||
Key.Home => "↖", | ||
Key.Left => "←", | ||
Key.Return => "↩", | ||
Key.PageDown => "⇞", | ||
Key.PageUp => "⇟", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't use macOS/iOS, but I think There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did think that looked weird, and doing a bit of searching have found other references to the same. I'll fix those up. |
||
Key.Right => "→", | ||
Key.Space => "␣", | ||
Key.Tab => "⇥", | ||
Key.Up => "↑", | ||
_ => ToString(key), | ||
}; | ||
} | ||
public static string ToPlatformString(KeyGesture gesture) => gesture.ToString("p", null); | ||
|
||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We probably should consider using CultureInfo passed as IFormatProvider to be used for localization here. And pass it down from IValueConverter. But that's a bigger issue for another day.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
True, I imagine "Up Arrow" is not going to be universally understood by every language on Earth. I'll take a look into how that sort of thing is done, but as you say it's a bigger problem for another day. I'll just take steps to try and make sure I'm not making that other day any harder than it needs to be.