Skip to content
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

Fixes key handling for Windows. #172

Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
250 changes: 158 additions & 92 deletions Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

Large diffs are not rendered by default.

2,295 changes: 1,548 additions & 747 deletions Terminal.Gui/ConsoleDrivers/ConsoleKeyMapping.cs

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -368,8 +368,8 @@ static KeyCode MapCursesKey (int cursesKey)
case Curses.KeyEnd: return KeyCode.End;
case Curses.KeyNPage: return KeyCode.PageDown;
case Curses.KeyPPage: return KeyCode.PageUp;
case Curses.KeyDeleteChar: return KeyCode.DeleteChar;
case Curses.KeyInsertChar: return KeyCode.InsertChar;
case Curses.KeyDeleteChar: return KeyCode.Delete;
case Curses.KeyInsertChar: return KeyCode.Insert;
case Curses.KeyTab: return KeyCode.Tab;
case Curses.KeyBackTab: return KeyCode.Tab | KeyCode.ShiftMask;
case Curses.KeyBackspace: return KeyCode.Backspace;
Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,9 +233,9 @@ KeyCode MapKey (ConsoleKeyInfo keyInfo)
case ConsoleKey.Backspace:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Backspace);
case ConsoleKey.Delete:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.DeleteChar);
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Delete);
case ConsoleKey.Insert:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.InsertChar);
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Insert);
case ConsoleKey.PrintScreen:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.PrintScreen);

Expand Down
4 changes: 2 additions & 2 deletions Terminal.Gui/ConsoleDrivers/NetDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1033,9 +1033,9 @@ KeyCode MapKey (ConsoleKeyInfo keyInfo)
case ConsoleKey.Backspace:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Backspace);
case ConsoleKey.Delete:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.DeleteChar);
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Delete);
case ConsoleKey.Insert:
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.InsertChar);
return ConsoleKeyMapping.MapToKeyCodeModifiers (keyInfo.Modifiers, KeyCode.Insert);

case ConsoleKey.Oem1:
case ConsoleKey.Oem2:
Expand Down
310 changes: 179 additions & 131 deletions Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

Large diffs are not rendered by default.

129 changes: 49 additions & 80 deletions Terminal.Gui/Input/Key.cs
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,8 @@ public Key (string str)
/// The encoded key value.
/// </summary>
/// <para>
/// IMPORTANT: Lowercase alpha keys are encoded (in <see cref="Gui.KeyCode"/>) as values between 65 and 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
/// IMPORTANT: Lowercase alpha keys are encoded (in <see cref="Gui.KeyCode"/>) as values between 65 and
/// 90 corresponding to the un-shifted A to Z keys on a keyboard. Enum values
/// are provided for these (e.g. <see cref="KeyCode.A"/>, <see cref="KeyCode.B"/>, etc.). Even though the values are the same as the ASCII
/// values for uppercase characters, these enum values represent *lowercase*, un-shifted characters.
/// </para>
Expand All @@ -147,24 +148,36 @@ public Key (string str)
public KeyBindingScope Scope { get; set; } = KeyBindingScope.Focused;

/// <summary>
/// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers.
/// The key value as a Rune. This is the actual value of the key pressed, and is independent of the modifiers. Useful
/// for determining if a key represents is a printable character.
/// </summary>
/// <remarks>
/// <para>
/// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether
/// the shift key was pressed.
/// Keys with Ctrl or Alt modifiers will return <see langword="default"/>.
/// </para>
/// <para>
/// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether
/// <see cref="KeyCode.ShiftMask"/> is set.
/// </para>
/// <para>
/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
/// </para>
/// </remarks>
public Rune AsRune => ToRune (KeyCode);

/// <summary>
/// Converts a <see cref="KeyCode"/> to a <see cref="Rune"/>.
/// Converts a <see cref="KeyCode"/> to a <see cref="Rune"/>. Useful
/// for determining if a key represents is a printable character.
/// </summary>
/// <remarks>
/// <para>
/// If the key is a letter (a-z or A-Z), the Rune be the upper or lower case letter depending on whether
/// the shift key was pressed.
/// Keys with Ctrl or Alt modifiers will return <see langword="default"/>.
/// </para>
/// <para>
/// If the key is a letter key (A-Z), the Rune will be the upper or lower case letter depending on whether
/// <see cref="KeyCode.ShiftMask"/> is set.
/// </para>
/// <para>
/// If the key is outside of the <see cref="KeyCode.CharMask"/> range, the returned Rune will be <see langword="default"/>.
/// </para>
/// </remarks>
Expand All @@ -176,13 +189,16 @@ public static Rune ToRune (KeyCode key)
return default;
}

// Extract the base key (removing modifier flags)
var baseKey = key & ~KeyCode.CtrlMask & ~KeyCode.AltMask & ~KeyCode.ShiftMask;
// Extract the base key code
var baseKey = key;
if (baseKey.HasFlag(KeyCode.ShiftMask)) {
baseKey &= ~KeyCode.ShiftMask;
}

switch (baseKey) {
case >= KeyCode.A and <= KeyCode.Z when !key.HasFlag (KeyCode.ShiftMask):
return new Rune ((uint)(baseKey + 32));
case >= KeyCode.A and <= KeyCode.Z:
case >= KeyCode.A and <= KeyCode.Z when key.HasFlag (KeyCode.ShiftMask):
return new Rune ((uint)baseKey);
case > KeyCode.Null and < KeyCode.A:
return new Rune ((uint)baseKey);
Expand Down Expand Up @@ -214,7 +230,9 @@ public static Rune ToRune (KeyCode key)
public bool IsCtrl => (KeyCode & KeyCode.CtrlMask) != 0;

/// <summary>
/// Gets a value indicating whether the KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
/// Gets a value indicating whether the key represents a key in the range of <see cref="KeyCode.A"/> to <see cref="KeyCode.Z"/>,
/// regardless of the <see cref="KeyCode.ShiftMask"/>. This is useful for testing if a key is based on these keys which are
/// special cased.
/// </summary>
/// <remarks>
/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
Expand All @@ -224,7 +242,9 @@ public static Rune ToRune (KeyCode key)
public bool IsKeyCodeAtoZ => GetIsKeyCodeAtoZ (KeyCode);

/// <summary>
/// Tests if a KeyCode is composed of a lower case letter from 'a' to 'z', independent of the shift key.
/// Tests if a KeyCode represents a key in the range of <see cref="KeyCode.A"/> to <see cref="KeyCode.Z"/>,
/// regardless of the <see cref="KeyCode.ShiftMask"/>. This is useful for testing if a key is based on these keys which are
/// special cased.
/// </summary>
/// <remarks>
/// IMPORTANT: Lowercase alpha keys are encoded in <see cref="Key.KeyCode"/> as values between 65 and 90 corresponding to
Expand All @@ -241,66 +261,6 @@ public static bool GetIsKeyCodeAtoZ (KeyCode keyCode)
return true;
}

if ((keyCode & ~KeyCode.Space & ~KeyCode.ShiftMask) is >= (KeyCode)'�' and <= (KeyCode)'�') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 13 is KeyCode.D0) {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 16 is >= KeyCode.D1 and <= KeyCode.D9 and not KeyCode.D7) {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 8 is KeyCode.D7) {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 24 is (KeyCode)'\'') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 16 is (KeyCode)'�') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 32 is (KeyCode)'\\') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 1 is (KeyCode)'+') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 84 is (KeyCode)'�') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 16 is (KeyCode)'�') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) + 32 is (KeyCode)'~') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 2 is (KeyCode)'<') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 15 is (KeyCode)',') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 12 is (KeyCode)'.') {
return true;
}

if ((keyCode & ~KeyCode.ShiftMask) - 50 is (KeyCode)'-') {
return true;
}

return (keyCode & KeyCode.CharMask) is >= KeyCode.A and <= KeyCode.Z;
}

Expand Down Expand Up @@ -363,22 +323,26 @@ public static bool GetIsKeyCodeAtoZ (KeyCode keyCode)

#region Operators
/// <summary>
/// Explicitly cast a <see cref="Key"/> to a <see cref="Rune"/>. The conversion is lossy.
/// Explicitly cast a <see cref="Key"/> to a <see cref="Rune"/>. The conversion is lossy because properties
/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
/// </summary>
/// <remarks>
/// Uses <see cref="AsRune"/>.
/// </remarks>
/// <param name="kea"></param>
public static explicit operator Rune (Key kea) => kea.AsRune;

// BUGBUG: (Tig) I do not think this cast operator is really needed.
/// <summary>
/// Explicitly cast <see cref="Key"/> to a <see langword="uint"/>. The conversion is never lossy.
/// Explicitly cast <see cref="Key"/> to a <see langword="uint"/>. The conversion is lossy because properties
/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
/// </summary>
/// <param name="kea"></param>
public static explicit operator uint (Key kea) => (uint)kea.KeyCode;

/// <summary>
/// Explicitly cast <see cref="Key"/> to a <see cref="KeyCode"/>. The conversion is never lossy.
/// Explicitly cast <see cref="Key"/> to a <see cref="KeyCode"/>. The conversion is lossy because properties
/// such as <see cref="Handled"/> are not encoded in <see cref="KeyCode"/>.
/// </summary>
/// <param name="key"></param>
public static explicit operator KeyCode (Key key) => key.KeyCode;
Expand Down Expand Up @@ -492,7 +456,7 @@ static string GetKeyString (KeyCode key)
return ((Rune)(uint)(key + 32)).ToString ();
}

if (key is >= KeyCode.Space and < KeyCode.A) {
if (key is > KeyCode.Space and < KeyCode.A) {
return ((Rune)(uint)key).ToString ();
}

Expand Down Expand Up @@ -552,7 +516,12 @@ public static string ToString (KeyCode key, Rune separator)
}

string result = sb.ToString ();
result = TrimEndRune (result, separator);
if (result.Length > 1) {
// Only trim the end if there was actually a modifier
// This deals with e.g. the OemPlus (+) key where the separator
// is the same as the key char.
result = TrimEndRune (result, separator);
}
return result;
}

Expand All @@ -563,7 +532,7 @@ static string TrimEndRune (string input, Rune runeToTrim)

if (input.EndsWith (runeString)) {
// Remove the rune from the end of the string
return input.Substring (0, input.Length - runeString.Length);
return input [..^runeString.Length];
}

return input;
Expand Down Expand Up @@ -975,12 +944,12 @@ public static bool TryParse (string text, [NotNullWhen (true)] out Key key)
/// <summary>
/// The <see cref="Key"/> object for Insert Character key.
/// </summary>
public static Key InsertChar => new (KeyCode.InsertChar);
public static Key InsertChar => new (KeyCode.Insert);

/// <summary>
/// The <see cref="Key"/> object for Delete Character key.
/// </summary>
public static Key DeleteChar => new (KeyCode.DeleteChar);
public static Key DeleteChar => new (KeyCode.Delete);

/// <summary>
/// The <see cref="Key"/> object for Print Screen key.
Expand Down
1 change: 1 addition & 0 deletions Terminal.Gui/Terminal.Gui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -113,5 +113,6 @@
<EnableSourceLink>true</EnableSourceLink>
<!--<DebugType>Embedded</DebugType>-->
<Authors>Miguel de Icaza, Tig Kindel (@tig), @BDisp</Authors>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
</Project>
Loading