From 01d4b4f55f68b4a7e17aebf05a2e2633f33422a0 Mon Sep 17 00:00:00 2001 From: BDisp Date: Mon, 26 Oct 2020 00:46:51 +0000 Subject: [PATCH] Added wide improvements on keys managements. --- Example/demo.cs | 30 +- .../CursesDriver/CursesDriver.cs | 96 ++++-- .../ConsoleDrivers/CursesDriver/constants.cs | 14 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 106 ++++-- Terminal.Gui/ConsoleDrivers/NetDriver.cs | 10 +- Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 42 +-- Terminal.Gui/Core/Application.cs | 8 + Terminal.Gui/Core/Event.cs | 313 +++++++++--------- Terminal.Gui/Core/ShortCutHelper.cs | 206 ++++++++++++ Terminal.Gui/Core/Toplevel.cs | 8 +- Terminal.Gui/Views/ComboBox.cs | 2 +- Terminal.Gui/Views/DateField.cs | 10 +- Terminal.Gui/Views/HexView.cs | 2 +- Terminal.Gui/Views/ListView.cs | 10 +- Terminal.Gui/Views/Menu.cs | 170 ++-------- Terminal.Gui/Views/ScrollView.cs | 2 +- Terminal.Gui/Views/TextField.cs | 31 +- Terminal.Gui/Views/TextView.cs | 30 +- Terminal.Gui/Views/TimeField.cs | 12 +- Terminal.Gui/Windows/FileDialog.cs | 8 +- UICatalog/Scenarios/AllViewsTester.cs | 2 +- UICatalog/Scenarios/ComputedLayout.cs | 2 +- UICatalog/Scenarios/DynamicMenuBar.cs | 228 +++++++------ UICatalog/Scenarios/Editor.cs | 2 +- UICatalog/Scenarios/HexEditor.cs | 2 +- UICatalog/Scenarios/Keys.cs | 2 +- UICatalog/Scenarios/Unicode.cs | 2 +- UICatalog/UICatalog.cs | 6 +- UnitTests/ApplicationTests.cs | 4 +- UnitTests/ScenarioTests.cs | 2 +- 30 files changed, 783 insertions(+), 579 deletions(-) create mode 100644 Terminal.Gui/Core/ShortCutHelper.cs diff --git a/Example/demo.cs b/Example/demo.cs index 9ab69011c..a763a4440 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -247,9 +247,9 @@ static class Demo { new MenuItem ("_Close", "", () => { if (Quit ()) { running = MainApp; Application.RequestStop (); } }, null, null, Key.AltMask | Key.F4), }), new MenuBarItem ("_Edit", new MenuItem [] { - new MenuItem ("_Copy", "", null, null, null, Key.ControlC), - new MenuItem ("C_ut", "", null, null, null, Key.ControlX), - new MenuItem ("_Paste", "", null, null, null, Key.ControlV) + new MenuItem ("_Copy", "", null, null, null, Key.C | Key.CtrlMask), + new MenuItem ("C_ut", "", null, null, null, Key.X | Key.CtrlMask), + new MenuItem ("_Paste", "", null, null, null, Key.V | Key.CtrlMask) }), }); ntop.Add (menu); @@ -597,19 +597,19 @@ static class Demo { menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }, null, null, Key.AltMask | Key.ControlD), - new MenuItem ("_New", "Creates new file", NewFile, null, null, Key.AltMask | Key.ControlN), - new MenuItem ("_Open", "", Open, null, null, Key.AltMask | Key.ControlO), - new MenuItem ("_Hex", "", () => { running = ShowHex; Application.RequestStop (); }, null, null, Key.AltMask | Key.ControlH), + new MenuItem ("Text _Editor Demo", "", () => { running = Editor; Application.RequestStop (); }, null, null, Key.AltMask | Key.CtrlMask | Key.D), + new MenuItem ("_New", "Creates new file", NewFile, null, null, Key.AltMask | Key.CtrlMask| Key.N), + new MenuItem ("_Open", "", Open, null, null, Key.AltMask | Key.CtrlMask| Key.O), + new MenuItem ("_Hex", "", () => { running = ShowHex; Application.RequestStop (); }, null, null, Key.AltMask | Key.CtrlMask | Key.H), new MenuItem ("_Close", "", Close, null, null, Key.AltMask | Key.F4), new MenuItem ("_Disabled", "", () => { }, () => false), null, - new MenuItem ("_Quit", "", () => { if (Quit ()) { running = null; top.Running = false; } }, null, null, Key.ControlQ) + new MenuItem ("_Quit", "", () => { if (Quit ()) { running = null; top.Running = false; } }, null, null, Key.CtrlMask | Key.Q) }), new MenuBarItem ("_Edit", new MenuItem [] { - new MenuItem ("_Copy", "", Copy, null, null, Key.AltMask | Key.ControlC), - new MenuItem ("C_ut", "", Cut, null, null, Key.AltMask | Key.ControlX), - new MenuItem ("_Paste", "", Paste, null, null, Key.AltMask | Key.ControlV), + new MenuItem ("_Copy", "", Copy, null, null, Key.AltMask | Key.CtrlMask | Key.C), + new MenuItem ("C_ut", "", Cut, null, null, Key.AltMask | Key.CtrlMask| Key.X), + new MenuItem ("_Paste", "", Paste, null, null, Key.AltMask | Key.CtrlMask| Key.V), new MenuBarItem ("_Find and Replace", new MenuItem [] { menuItems [0], menuItems [1] }), menuItems[3] @@ -620,8 +620,8 @@ static class Demo { new MenuItem ("Search Single Item", "", ComboBoxDemo, null, null, Key.AltMask + 2.ToString () [0]) }), new MenuBarItem ("A_ssorted", new MenuItem [] { - new MenuItem ("_Show text alignments", "", () => ShowTextAlignments (), null, null, Key.AltMask | Key.ControlG), - new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo (), null, null, Key.AltMask | Key.ControlK) + new MenuItem ("_Show text alignments", "", () => ShowTextAlignments (), null, null, Key.AltMask | Key.CtrlMask | Key.G), + new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo (), null, null, Key.AltMask | Key.CtrlMask | Key.K) }), new MenuBarItem ("_Test Menu and SubMenus", new MenuBarItem [] { new MenuBarItem ("SubMenu1Item_1", new MenuBarItem [] { @@ -662,7 +662,7 @@ static class Demo { new StatusItem(Key.F1, "~F1~ Help", () => Help()), new StatusItem(Key.F2, "~F2~ Load", Load), new StatusItem(Key.F3, "~F3~ Save", Save), - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) { running = null; top.Running = false; } }) + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { if (Quit ()) { running = null; top.Running = false; } }) }); win.Add (drag, dragText); @@ -689,7 +689,7 @@ static class Demo { private static void Win_KeyPress (View.KeyEventEventArgs e) { - if (e.KeyEvent.Key == Key.ControlT) { + if ((e.KeyEvent.Key & (Key.CtrlMask | Key.T)) != 0) { if (menu.IsMenuOpen) menu.CloseMenu (); else diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index 20dedb5c6..52a8cabe7 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -182,6 +182,18 @@ namespace Terminal.Gui { case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask; case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask; case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask; + case Curses.ShiftAltKeyUp: return Key.CursorUp | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyDown: return Key.CursorDown | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyRight: return Key.CursorRight | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyNPage: return Key.PageDown | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyPPage: return Key.PageUp | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyHome: return Key.Home | Key.ShiftMask | Key.AltMask; + case Curses.ShiftAltKeyEnd: return Key.End | Key.ShiftMask | Key.AltMask; + case Curses.AltCtrlKeyNPage: return Key.PageDown | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyPPage: return Key.PageUp | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyHome: return Key.Home | Key.AltMask | Key.CtrlMask; + case Curses.AltCtrlKeyEnd: return Key.End | Key.AltMask | Key.CtrlMask; default: return Key.Unknown; } } @@ -457,7 +469,7 @@ namespace Terminal.Gui { return; keyModifiers = new KeyModifiers (); - Key k; + Key k = Key.Null; if (code == Curses.KEY_CODE_YES) { if (wch == Curses.KeyResize) { @@ -485,6 +497,9 @@ namespace Terminal.Gui { } else if (wch >= 313 && wch <= 324) { // Alt+(F1 - F12) wch -= 48; k = Key.AltMask | MapCursesKey (wch); + } else if (wch >= 325 && wch <= 327) { // Shift+Alt+(F1 - F3) + wch -= 60; + k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch); } keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); keyHandler (new KeyEvent (k, MapKeyModifiers (k))); @@ -500,7 +515,6 @@ namespace Terminal.Gui { if (code == Curses.KEY_CODE_YES) { k = Key.AltMask | MapCursesKey (wch); - keyHandler (new KeyEvent (k, MapKeyModifiers (k))); } if (code == 0) { KeyEvent key; @@ -509,20 +523,15 @@ namespace Terminal.Gui { // Simulates the AltMask itself by pressing Alt + Space. if (wch2 == (int)Key.Space) { k = Key.AltMask; - key = new KeyEvent (k, MapKeyModifiers (k)); - } else if (wch2 - (int)Key.Space >= 'A' && wch2 - (int)Key.Space <= 'Z') { + } else if (wch2 - (int)Key.Space >= (uint)Key.A && wch2 - (int)Key.Space <= (uint)Key.Z) { k = (Key)((uint)Key.AltMask + (wch2 - (int)Key.Space)); - key = new KeyEvent (k, MapKeyModifiers (k)); - } else if (wch2 >= (uint)Key.ControlA && wch2 <= (uint)Key.ControlZ) { - k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); - key = new KeyEvent (k, MapKeyModifiers (k)); - } else if (wch2 >= '0' && wch2 <= '9') { - k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - '0')); - key = new KeyEvent (k, MapKeyModifiers (k)); + } else if (wch2 >= (uint)Key.A - 64 && wch2 <= (uint)Key.Z - 64) { + k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + (wch2 + 64)); + } else if (wch2 >= (uint)Key.D0 && wch2 <= (uint)Key.D9) { + k = (Key)((uint)Key.AltMask + (uint)Key.D0 + (wch2 - (uint)Key.D0)); } else if (wch2 == 27) { k = (Key)wch2; - key = new KeyEvent (k, MapKeyModifiers (k)); - } else if (wch2 == 91) { + } else if (wch2 == Curses.KEY_CODE_SEQ) { int [] c = null; while (code == 0) { code = Curses.get_wch (out wch2); @@ -531,46 +540,62 @@ namespace Terminal.Gui { c [c.Length - 1] = wch2; } } - if (c [0] == 49 && c [1] == 59 && c [2] == 55) { // Ctrl+Alt+(F1 - F4) + if (c [0] == 49 && c [1] == 59 && c [2] == 55 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Alt+(F1 - F4) wch2 = c [3] + 185; k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F5 - F8) + } else if (c [0] == 49 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Alt+(F5 - F8) wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126) { // Ctrl+Alt+(F9 - F12) + } else if (c [0] == 50 && c [2] == 59 && c [3] == 55 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Alt+(F9 - F12) wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; k = Key.CtrlMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [1] == 59 && c [2] == 56) { // Ctrl+Shift+Alt+(F1 - F4) + } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] >= 80 && c [3] <= 83) { // Ctrl+Shift+Alt+(F1 - F4) wch2 = c [3] + 185; k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126) { // Ctrl+Shift+Alt+(F5 - F8) + } else if (c [0] == 49 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Ctrl+Shift+Alt+(F5 - F8) wch2 = c [1] == 53 ? c [1] + 216 : c [1] + 215; k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); - } else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126) { // Ctrl+Shift+Alt+(F9 - F12) + } else if (c [0] == 50 && c [2] == 59 && c [3] == 56 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Ctrl+Shift+Alt+(F9 - F12) wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; k = Key.CtrlMask | Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); + } else if (c [0] == 49 && c [1] == 59 && c [2] == 52 && c [3] == 83) { // Shift+Alt+(F4) + wch2 = 268; + k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); + } else if (c [0] == 49 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 53 && c [1] <= 57) { // Shift+Alt+(F5 - F8) + wch2 = c [1] < 55 ? c [1] + 216 : c [1] + 215; + k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); + } else if (c [0] == 50 && c [2] == 59 && c [3] == 52 && c [4] == 126 && c [1] >= 48 && c [1] <= 52) { // Shift+Alt+(F9 - F12) + wch2 = c [1] < 51 ? c [1] + 225 : c [1] + 224; + k = Key.ShiftMask | Key.AltMask | MapCursesKey (wch2); + } else if (c [0] == 54 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyNPage + k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageDown; + } else if (c [0] == 53 && c [1] == 59 && c [2] == 56 && c [3] == 126) { // Shift+Ctrl+Alt+KeyPPage + k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.PageUp; + } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 72) { // Shift+Ctrl+Alt+KeyHome + k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.Home; + } else if (c [0] == 49 && c [1] == 59 && c [2] == 56 && c [3] == 70) { // Shift+Ctrl+Alt+KeyEnd + k = Key.ShiftMask | Key.CtrlMask | Key.AltMask | Key.End; } else { k = MapCursesKey (wch2); } - key = new KeyEvent (k, MapKeyModifiers (k)); } else { // Unfortunately there are no way to differentiate Ctrl+Alt+alfa and Ctrl+Shift+Alt+alfa. + if (((Key)wch2 & Key.CtrlMask) != 0) { + keyModifiers.Ctrl = true; + } if (wch2 == 0) { k = Key.CtrlMask | Key.AltMask | Key.Space; + } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { + keyModifiers.Shift = true; + keyModifiers.Alt = true; + } else if (wch2 < 256) { + k = (Key)wch2; + keyModifiers.Alt = true; } else { - if (((Key)wch2).ToString ().Contains ("Control")) { - keyModifiers.Ctrl = true; - } - if (wch2 < 256) { - k = Key.AltMask | (Key)wch2; - } else { - //k = (Key)wch2; - //keyModifiers.Alt = true; - k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); - } + k = (Key)((uint)(Key.AltMask | Key.CtrlMask) + wch2); } - key = new KeyEvent (k, MapKeyModifiers (k)); } + key = new KeyEvent (k, MapKeyModifiers (k)); keyDownHandler (key); keyHandler (key); } else { @@ -586,10 +611,11 @@ namespace Terminal.Gui { k = (Key)wch; if (wch == 0) { k = Key.CtrlMask | Key.Space; - } else if (wch >= (uint)Key.ControlA && wch <= (uint)Key.ControlZ) { - k = (Key)wch; - keyModifiers.Ctrl = true; - } else if (wch >= 'A' && wch <= 'Z') { + } else if (wch >= (uint)Key.A - 64 && wch <= (uint)Key.Z - 64) { + if ((Key)(wch + 64) != Key.J) { + k = Key.CtrlMask | (Key)(wch + 64); + } + } else if (wch >= (uint)Key.A && wch <= (uint)Key.Z) { keyModifiers.Shift = true; } keyDownHandler (new KeyEvent (k, MapKeyModifiers (k))); diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs index fb40fad8c..e32c54775 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/constants.cs @@ -52,6 +52,7 @@ namespace Unix.Terminal { public const int COLOR_CYAN = unchecked((int)0x6); public const int COLOR_WHITE = unchecked((int)0x7); public const int KEY_CODE_YES = unchecked((int)0x100); + public const int KEY_CODE_SEQ = unchecked((int)0x5b); [Flags] public enum Event : long { @@ -152,7 +153,18 @@ namespace Unix.Terminal { public const int ShiftCtrlKeyPPage = unchecked((int)0x22c + LeftRightUpNPagePPage); public const int ShiftCtrlKeyHome = unchecked((int)0x218 + Home); public const int ShiftCtrlKeyEnd = unchecked((int)0x213 + DownEnd); - + public const int ShiftAltKeyUp = unchecked((int)0x235 + LeftRightUpNPagePPage); + public const int ShiftAltKeyDown = unchecked((int)0x20c + DownEnd); + public const int ShiftAltKeyLeft = unchecked((int)0x220 + LeftRightUpNPagePPage); + public const int ShiftAltKeyRight = unchecked((int)0x22f + LeftRightUpNPagePPage); + public const int ShiftAltKeyNPage = unchecked((int)0x225 + LeftRightUpNPagePPage); + public const int ShiftAltKeyPPage = unchecked((int)0x22a + LeftRightUpNPagePPage); + public const int ShiftAltKeyHome = unchecked((int)0x216 + Home); + public const int ShiftAltKeyEnd = unchecked((int)0x211 + DownEnd); + public const int AltCtrlKeyNPage = unchecked((int)0x228 + LeftRightUpNPagePPage); + public const int AltCtrlKeyPPage = unchecked((int)0x22d + LeftRightUpNPagePPage); + public const int AltCtrlKeyHome = unchecked((int)0x219 + Home); + public const int AltCtrlKeyEnd = unchecked((int)0x214 + DownEnd); public const int LC_ALL = 6; static public int ColorPair(int n){ return 0 + n * 256; diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 13e670c4f..7fc3e76a3 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -310,33 +310,35 @@ namespace Terminal.Gui { { switch (keyInfo.Key) { case ConsoleKey.Escape: - return Key.Esc; + return MapKeyModifiers (keyInfo, Key.Esc); case ConsoleKey.Tab: return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab; case ConsoleKey.Home: - return Key.Home; + return MapKeyModifiers (keyInfo, Key.Home); case ConsoleKey.End: - return Key.End; + return MapKeyModifiers (keyInfo, Key.End); case ConsoleKey.LeftArrow: - return Key.CursorLeft; + return MapKeyModifiers (keyInfo, Key.CursorLeft); case ConsoleKey.RightArrow: - return Key.CursorRight; + return MapKeyModifiers (keyInfo, Key.CursorRight); case ConsoleKey.UpArrow: - return Key.CursorUp; + return MapKeyModifiers (keyInfo, Key.CursorUp); case ConsoleKey.DownArrow: - return Key.CursorDown; + return MapKeyModifiers (keyInfo, Key.CursorDown); case ConsoleKey.PageUp: - return Key.PageUp; + return MapKeyModifiers (keyInfo, Key.PageUp); case ConsoleKey.PageDown: - return Key.PageDown; + return MapKeyModifiers (keyInfo, Key.PageDown); case ConsoleKey.Enter: - return Key.Enter; + return MapKeyModifiers (keyInfo, Key.Enter); case ConsoleKey.Spacebar: - return Key.Space; + return MapKeyModifiers (keyInfo, Key.Space); case ConsoleKey.Backspace: - return Key.Backspace; + return MapKeyModifiers (keyInfo, Key.Backspace); case ConsoleKey.Delete: - return Key.Delete; + return MapKeyModifiers (keyInfo, Key.DeleteChar); + case ConsoleKey.Insert: + return MapKeyModifiers (keyInfo, Key.InsertChar); case ConsoleKey.Oem1: case ConsoleKey.Oem2: @@ -351,38 +353,69 @@ namespace Terminal.Gui { case ConsoleKey.OemComma: case ConsoleKey.OemPlus: case ConsoleKey.OemMinus: + if (keyInfo.KeyChar == 0) + return Key.Unknown; + return (Key)((uint)keyInfo.KeyChar); } var key = keyInfo.Key; if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { var delta = key - ConsoleKey.A; - if (keyInfo.Modifiers == ConsoleModifiers.Control) - return (Key)((uint)Key.ControlA + delta); - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta)); - if (keyInfo.Modifiers == ConsoleModifiers.Shift) - return (Key)((uint)'A' + delta); - else - return (Key)((uint)'a' + delta); + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0) { + return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.A + delta)); + } else { + return (Key)((uint)keyInfo.KeyChar); + } + } + return (Key)((uint)keyInfo.KeyChar); } if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) - return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta)); - if (keyInfo.Modifiers == ConsoleModifiers.Shift) - return (Key)((uint)keyInfo.KeyChar); - return (Key)((uint)'0' + delta); + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } + return (Key)((uint)keyInfo.KeyChar); } - if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) { + if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) { var delta = key - ConsoleKey.F1; + if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta)); + } - return (Key)((int)Key.F1 + delta); + return (Key)((uint)Key.F1 + delta); } + return (Key)(0xffffffff); } - KeyModifiers keyModifiers = new KeyModifiers (); + KeyModifiers keyModifiers; + + private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) + { + Key keyMod = new Key (); + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + keyMod = Key.ShiftMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + keyMod |= Key.CtrlMask; + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + keyMod |= Key.AltMask; + + return keyMod != Key.Null ? keyMod | key : key; + } /// /// @@ -399,6 +432,21 @@ namespace Terminal.Gui { var map = MapKey (consoleKey); if (map == (Key)0xffffffff) return; + + if (keyModifiers == null) + keyModifiers = new KeyModifiers (); + switch (consoleKey.Modifiers) { + case ConsoleModifiers.Alt: + keyModifiers.Alt = true; + break; + case ConsoleModifiers.Shift: + keyModifiers.Shift = true; + break; + case ConsoleModifiers.Control: + keyModifiers.Ctrl = true; + break; + } + keyHandler (new KeyEvent (map, keyModifiers)); keyUpHandler (new KeyEvent (map, keyModifiers)); }; diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index cf2f9e31d..418385edb 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -299,21 +299,21 @@ namespace Terminal.Gui { if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { var delta = key - ConsoleKey.A; if (keyInfo.Modifiers == ConsoleModifiers.Control) - return (Key)((uint)Key.ControlA + delta); + return (Key)((uint)Key.A + delta); if (keyInfo.Modifiers == ConsoleModifiers.Alt) - return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta)); + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); if (keyInfo.Modifiers == ConsoleModifiers.Shift) - return (Key)((uint)'A' + delta); + return (Key)((uint)Key.A + delta); else return (Key)((uint)'a' + delta); } if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { var delta = key - ConsoleKey.D0; if (keyInfo.Modifiers == ConsoleModifiers.Alt) - return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta)); + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); if (keyInfo.Modifiers == ConsoleModifiers.Shift) return (Key)((uint)keyInfo.KeyChar); - return (Key)((uint)'0' + delta); + return (Key)((uint)Key.D0 + delta); } if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) { var delta = key - ConsoleKey.F1; diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 9812983cc..48a132148 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -1074,25 +1074,25 @@ namespace Terminal.Gui { return MapKeyModifiers (keyInfo, Key.InsertChar); case ConsoleKey.NumPad0: - return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar; + return keyInfoEx.NumLock ? Key.D0 : Key.InsertChar; case ConsoleKey.NumPad1: - return keyInfoEx.NumLock ? (Key)(uint)'1' : Key.End; + return keyInfoEx.NumLock ? Key.D1 : Key.End; case ConsoleKey.NumPad2: - return keyInfoEx.NumLock ? (Key)(uint)'2' : Key.CursorDown; + return keyInfoEx.NumLock ? Key.D2 : Key.CursorDown; case ConsoleKey.NumPad3: - return keyInfoEx.NumLock ? (Key)(uint)'3' : Key.PageDown; + return keyInfoEx.NumLock ? Key.D3 : Key.PageDown; case ConsoleKey.NumPad4: - return keyInfoEx.NumLock ? (Key)(uint)'4' : Key.CursorLeft; + return keyInfoEx.NumLock ? Key.D4 : Key.CursorLeft; case ConsoleKey.NumPad5: - return keyInfoEx.NumLock ? (Key)(uint)'5' : (Key)((uint)keyInfo.KeyChar); + return keyInfoEx.NumLock ? Key.D5 : (Key)((uint)keyInfo.KeyChar); case ConsoleKey.NumPad6: - return keyInfoEx.NumLock ? (Key)(uint)'6' : Key.CursorRight; + return keyInfoEx.NumLock ? Key.D6 : Key.CursorRight; case ConsoleKey.NumPad7: - return keyInfoEx.NumLock ? (Key)(uint)'7' : Key.Home; + return keyInfoEx.NumLock ? Key.D7 : Key.Home; case ConsoleKey.NumPad8: - return keyInfoEx.NumLock ? (Key)(uint)'8' : Key.CursorUp; + return keyInfoEx.NumLock ? Key.D8 : Key.CursorUp; case ConsoleKey.NumPad9: - return keyInfoEx.NumLock ? (Key)(uint)'9' : Key.PageUp; + return keyInfoEx.NumLock ? Key.D9 : Key.PageUp; case ConsoleKey.Oem1: case ConsoleKey.Oem2: @@ -1119,16 +1119,14 @@ namespace Terminal.Gui { if (key >= ConsoleKey.A && key <= ConsoleKey.Z) { var delta = key - ConsoleKey.A; if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)((uint)Key.ControlA + delta); + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.A + delta)); } if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta)); + return (Key)(((uint)Key.AltMask) | ((uint)Key.A + delta)); } if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { - if (keyInfo.KeyChar == 0) { - return (Key)(((uint)Key.AltMask | (uint)Key.CtrlMask) | ((uint)Key.ControlA + delta)); - } else { - return (Key)((uint)keyInfo.KeyChar); + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta)); } } //return (Key)((uint)alphaBase + delta); @@ -1137,13 +1135,15 @@ namespace Terminal.Gui { if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { var delta = key - ConsoleKey.D0; if (keyInfo.Modifiers == ConsoleModifiers.Alt) { - return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta)); + return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); } if (keyInfo.Modifiers == ConsoleModifiers.Control) { - return (Key)(((uint)Key.CtrlMask) | ((uint)'0' + delta)); + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); } - if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { - return MapKeyModifiers (keyInfo, (Key)((uint)'0' + delta)); + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { + return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta)); + } } return (Key)((uint)keyInfo.KeyChar); } @@ -1172,7 +1172,7 @@ namespace Terminal.Gui { if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) keyMod |= Key.AltMask; - return keyMod != Key.ControlSpace ? keyMod | key : key; + return keyMod != Key.Null ? keyMod | key : key; } public override void Init (Action terminalResized) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index f21ba903f..47868627d 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -168,6 +168,14 @@ namespace Terminal.Gui { { if (_initialized && driver == null) return; + // Used only for start debugging on Unix. +//#if DEBUG +// while (!System.Diagnostics.Debugger.IsAttached) { +// System.Threading.Thread.Sleep (100); +// } +// System.Diagnostics.Debugger.Break (); +//#endif + // This supports Unit Tests and the passing of a mock driver/loopdriver if (driver != null) { if (mainLoopDriver == null) { diff --git a/Terminal.Gui/Core/Event.cs b/Terminal.Gui/Core/Event.cs index 0939fb435..04ae3b961 100644 --- a/Terminal.Gui/Core/Event.cs +++ b/Terminal.Gui/Core/Event.cs @@ -46,9 +46,12 @@ namespace Terminal.Gui { /// /// If the is set, then the value is that of the special mask, /// otherwise, the value is the one of the lower bits (as extracted by ) + /// + /// Numerics keys are the values between 48 and 57 corresponding to 0 to 9 + /// /// /// - /// Control keys are the values between 1 and 26 corresponding to Control-A to Control-Z + /// Upper alpha keys are the values between 65 and 90 corresponding to A to Z /// /// /// Unicode runes are also stored here, the letter 'A" for example is encoded as a value 65 (not surfaced in the enum). @@ -70,130 +73,171 @@ namespace Terminal.Gui { SpecialMask = 0xfff00000, /// - /// The key code for the user pressing Control-spacebar + /// The key code representing null or empty /// - ControlSpace = 0, - - /// - /// The key code for the user pressing Control-A - /// - ControlA = 1, - /// - /// The key code for the user pressing Control-B - /// - ControlB, - /// - /// The key code for the user pressing Control-C - /// - ControlC, - /// - /// The key code for the user pressing Control-D - /// - ControlD, - /// - /// The key code for the user pressing Control-E - /// - ControlE, - /// - /// The key code for the user pressing Control-F - /// - ControlF, - /// - /// The key code for the user pressing Control-G - /// - ControlG, - /// - /// The key code for the user pressing Control-H - /// - ControlH, - /// - /// The key code for the user pressing Control-I (same as the tab key). - /// - ControlI, - /// - /// The key code for the user pressing Control-J - /// - ControlJ, - /// - /// The key code for the user pressing Control-K - /// - ControlK, - /// - /// The key code for the user pressing Control-L - /// - ControlL, - /// - /// The key code for the user pressing Control-M - /// - ControlM, - /// - /// The key code for the user pressing Control-N (same as the return key). - /// - ControlN, - /// - /// The key code for the user pressing Control-O - /// - ControlO, - /// - /// The key code for the user pressing Control-P - /// - ControlP, - /// - /// The key code for the user pressing Control-Q - /// - ControlQ, - /// - /// The key code for the user pressing Control-R - /// - ControlR, - /// - /// The key code for the user pressing Control-S - /// - ControlS, - /// - /// The key code for the user pressing Control-T - /// - ControlT, - /// - /// The key code for the user pressing Control-U - /// - ControlU, - /// - /// The key code for the user pressing Control-V - /// - ControlV, - /// - /// The key code for the user pressing Control-W - /// - ControlW, - /// - /// The key code for the user pressing Control-X - /// - ControlX, - /// - /// The key code for the user pressing Control-Y - /// - ControlY, - /// - /// The key code for the user pressing Control-Z - /// - ControlZ, - - /// - /// The key code for the user pressing the escape key - /// - Esc = 27, + Null = 0, /// /// The key code for the user pressing the return key. /// Enter = '\n', + /// + /// The key code for the user pressing the escape key + /// + Esc = 27, + /// /// The key code for the user pressing the space bar /// Space = 32, + /// + /// Digit 0. + /// + D0 = 48, + /// + /// Digit 1. + /// + D1, + /// + /// Digit 2. + /// + D2, + /// + /// Digit 3. + /// + D3, + /// + /// Digit 4. + /// + D4, + /// + /// Digit 5. + /// + D5, + /// + /// Digit 6. + /// + D6, + /// + /// Digit 7. + /// + D7, + /// + /// Digit 8. + /// + D8, + /// + /// Digit 9. + /// + D9, + + /// + /// The key code for the user pressing Shift-A + /// + A = 65, + /// + /// The key code for the user pressing Shift-B + /// + B, + /// + /// The key code for the user pressing Shift-C + /// + C, + /// + /// The key code for the user pressing Shift-D + /// + D, + /// + /// The key code for the user pressing Shift-E + /// + E, + /// + /// The key code for the user pressing Shift-F + /// + F, + /// + /// The key code for the user pressing Shift-G + /// + G, + /// + /// The key code for the user pressing Shift-H + /// + H, + /// + /// The key code for the user pressing Shift-I + /// + I, + /// + /// The key code for the user pressing Shift-J + /// + J, + /// + /// The key code for the user pressing Shift-K + /// + K, + /// + /// The key code for the user pressing Shift-L + /// + L, + /// + /// The key code for the user pressing Shift-M + /// + M, + /// + /// The key code for the user pressing Shift-N + /// + N, + /// + /// The key code for the user pressing Shift-O + /// + O, + /// + /// The key code for the user pressing Shift-P + /// + P, + /// + /// The key code for the user pressing Shift-Q + /// + Q, + /// + /// The key code for the user pressing Shift-R + /// + R, + /// + /// The key code for the user pressing Shift-S + /// + S, + /// + /// The key code for the user pressing Shift-T + /// + T, + /// + /// The key code for the user pressing Shift-U + /// + U, + /// + /// The key code for the user pressing Shift-V + /// + V, + /// + /// The key code for the user pressing Shift-W + /// + W, + /// + /// The key code for the user pressing Shift-X + /// + X, + /// + /// The key code for the user pressing Shift-Y + /// + Y, + /// + /// The key code for the user pressing Shift-Z + /// + Z, + /// /// The key code for the user pressing the delete key. /// @@ -261,6 +305,7 @@ namespace Terminal.Gui { /// Insert character key /// InsertChar, + /// /// F1 key. /// @@ -309,6 +354,7 @@ namespace Terminal.Gui { /// F12 key. /// F12, + /// /// The key code for the user pressing the tab key (forwards tab key). /// @@ -317,46 +363,7 @@ namespace Terminal.Gui { /// Shift-tab key (backwards tab key). /// BackTab, - /// - /// Digit 0. - /// - D0 = 48, - /// - /// Digit 1. - /// - D1, - /// - /// Digit 2. - /// - D2, - /// - /// Digit 3. - /// - D3, - /// - /// Digit 4. - /// - D4, - /// - /// Digit 5. - /// - D5, - /// - /// Digit 6. - /// - D6, - /// - /// Digit 7. - /// - D7, - /// - /// Digit 8. - /// - D8, - /// - /// Digit 9. - /// - D9, + /// /// A key with an unknown mapping was raised. /// diff --git a/Terminal.Gui/Core/ShortCutHelper.cs b/Terminal.Gui/Core/ShortCutHelper.cs new file mode 100644 index 000000000..3c3abd8eb --- /dev/null +++ b/Terminal.Gui/Core/ShortCutHelper.cs @@ -0,0 +1,206 @@ +using NStack; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Terminal.Gui { + /// + /// Represents a helper to manipulate shortcut keys used on views. + /// + public class ShortCutHelper { + private Key shortCut; + + /// + /// This is the global setting that can be used as a global shortcut to invoke the action on the view. + /// + public virtual Key ShortCut { + get => shortCut; + set { + if (shortCut != value && (PostShortCutValidation (value) || value == Key.Null)) { + shortCut = value; + } + } + } + + /// + /// The keystroke combination used in the as string. + /// + public virtual ustring ShortCutTag => GetShortCutTag (shortCut); + + /// + /// Get the key as string. + /// + /// The shortcut key. + /// + public static ustring GetShortCutTag (Key shortCut) + { + if (shortCut == Key.Null) { + return ""; + } + + var k = shortCut; + var delimiter = MenuBar.ShortCutDelimiter; + ustring tag = ustring.Empty; + var sCut = GetKeyToString (k, out Key knm).ToString (); + if (knm == Key.Unknown) { + k &= ~Key.Unknown; + sCut = GetKeyToString (k, out _).ToString (); + } + if ((k & Key.CtrlMask) != 0) { + tag = "Ctrl"; + } + if ((k & Key.ShiftMask) != 0) { + if (!tag.IsEmpty) { + tag += delimiter; + } + tag += "Shift"; + } + if ((k & Key.AltMask) != 0) { + if (!tag.IsEmpty) { + tag += delimiter; + } + tag += "Alt"; + } + + ustring [] keys = ustring.Make (sCut).Split (","); + for (int i = 0; i < keys.Length; i++) { + var key = keys [i].TrimSpace (); + if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) { + continue; + } + if (!tag.IsEmpty) { + tag += delimiter; + } + if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) { + k = (uint)Key.AltMask + k; + tag += ((char)k).ToString (); + } else if (key.Length == 2 && key.StartsWith ("D")) { + tag += ((char)key.ElementAt (1)).ToString (); + } else { + tag += key; + } + } + + return tag; + } + + /// + /// Return key as string. + /// + /// The key to extract. + /// Correspond to the non modifier key. + public static ustring GetKeyToString (Key key, out Key knm) + { + if (key == Key.Null) { + knm = Key.Null; + return ""; + } + + knm = key; + var mK = key & (Key.AltMask | Key.CtrlMask | Key.ShiftMask); + knm &= ~mK; + for (uint i = (uint)Key.F1; i < (uint)Key.F12; i++) { + if (knm == (Key)i) { + mK |= (Key)i; + } + } + knm &= ~mK; + uint.TryParse (knm.ToString (), out uint c); + var s = mK == Key.Null ? "" : mK.ToString (); + if (s != "" && (knm != Key.Null || c > 0)) { + s += ","; + } + s += c == 0 ? knm == Key.Null ? "" : knm.ToString () : ((char)c).ToString (); + return s; + } + + /// + /// Allows to retrieve a from a + /// + /// The key as string. + public static Key GetShortCutFromTag (ustring tag) + { + var sCut = tag; + if (sCut.IsEmpty) { + return default; + } + + Key key = Key.Null; + //var hasCtrl = false; + var delimiter = MenuBar.ShortCutDelimiter; + + ustring [] keys = sCut.Split (delimiter); + for (int i = 0; i < keys.Length; i++) { + var k = keys [i]; + if (k == "Ctrl") { + //hasCtrl = true; + key |= Key.CtrlMask; + } else if (k == "Shift") { + key |= Key.ShiftMask; + } else if (k == "Alt") { + key |= Key.AltMask; + } else if (k.StartsWith ("F") && k.Length > 1) { + int.TryParse (k.Substring (1).ToString (), out int n); + for (uint j = (uint)Key.F1; j <= (uint)Key.F12; j++) { + int.TryParse (((Key)j).ToString ().Substring (1), out int f); + if (f == n) { + key |= (Key)j; + } + } + } else { + key |= (Key)Enum.Parse (typeof (Key), k.ToString ()); + } + } + + return key; + } + + /// + /// Lookup for a on range of keys. + /// + /// The source key. + /// First key in range. + /// Last key in range. + public static bool CheckKeysFlagRange (Key key, Key first, Key last) + { + for (uint i = (uint)first; i < (uint)last; i++) { + if ((key | (Key)i) == key) { + return true; + } + } + return false; + } + + /// + /// Used at key down or key press validation. + /// + /// The key to validate. + /// true if is valid.falseotherwise. + public static bool PreShortCutValidation (Key key) + { + if ((key & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 && !CheckKeysFlagRange (key, Key.F1, Key.F12)) { + return false; + } + + return true; + } + + /// + /// Used at key up validation. + /// + /// The key to validate. + /// true if is valid.falseotherwise. + public static bool PostShortCutValidation (Key key) + { + GetKeyToString (key, out Key knm); + + if (CheckKeysFlagRange (key, Key.F1, Key.F12) || + ((key & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) != 0 && knm != Key.Null && knm != Key.Unknown)) { + return true; + } + return false; + } + } +} diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index 9f0f135e6..1d5dc207f 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -164,11 +164,11 @@ namespace Terminal.Gui { return true; switch (keyEvent.Key) { - case Key.ControlQ: + case Key.Q | Key.CtrlMask: // FIXED: stop current execution of this container Application.RequestStop (); break; - case Key.ControlZ: + case Key.Z | Key.CtrlMask: Driver.Suspend (); return true; @@ -181,7 +181,7 @@ namespace Terminal.Gui { case Key.Tab: case Key.CursorRight: case Key.CursorDown: - case Key.ControlI: // Unix + case Key.I | Key.CtrlMask: // Unix var old = GetDeepestFocusedSubview (Focused); if (!FocusNext ()) FocusNext (); @@ -206,7 +206,7 @@ namespace Terminal.Gui { } return true; - case Key.ControlL: + case Key.L | Key.CtrlMask: Application.Refresh (); return true; } diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs index 37bc36e5e..b5da7cf96 100644 --- a/Terminal.Gui/Views/ComboBox.cs +++ b/Terminal.Gui/Views/ComboBox.cs @@ -357,7 +357,7 @@ namespace Terminal.Gui { } // Unix emulation - if (e.Key == Key.ControlU) { + if (e.Key == (Key.U | Key.CtrlMask)) { Reset (); return true; } diff --git a/Terminal.Gui/Views/DateField.cs b/Terminal.Gui/Views/DateField.cs index 943f5973a..14178e572 100644 --- a/Terminal.Gui/Views/DateField.cs +++ b/Terminal.Gui/Views/DateField.cs @@ -299,7 +299,7 @@ namespace Terminal.Gui { { switch (kb.Key) { case Key.DeleteChar: - case Key.ControlD: + case Key.D | Key.CtrlMask: if (ReadOnly) return true; @@ -317,22 +317,22 @@ namespace Terminal.Gui { // Home, C-A case Key.Home: - case Key.ControlA: + case Key.A | Key.CtrlMask: CursorPosition = 1; break; case Key.CursorLeft: - case Key.ControlB: + case Key.B | Key.CtrlMask: DecCursorPosition (); break; case Key.End: - case Key.ControlE: // End + case Key.E | Key.CtrlMask: // End CursorPosition = FieldLen; break; case Key.CursorRight: - case Key.ControlF: + case Key.F | Key.CtrlMask: IncCursorPosition (); break; diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs index 435ec2cb6..d499afd5f 100644 --- a/Terminal.Gui/Views/HexView.cs +++ b/Terminal.Gui/Views/HexView.cs @@ -325,7 +325,7 @@ namespace Terminal.Gui { case Key.PageUp: MoveUp (bytesPerLine * Frame.Height); break; - case Key.ControlV: + case Key.V | Key.CtrlMask: case Key.PageDown: MoveDown (bytesPerLine * Frame.Height); break; diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index f625b5ebe..84404d71e 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -348,14 +348,14 @@ namespace Terminal.Gui { switch (kb.Key) { case Key.CursorUp: - case Key.ControlP: + case Key.P | Key.CtrlMask: return MoveUp (); case Key.CursorDown: - case Key.ControlN: + case Key.N | Key.CtrlMask: return MoveDown (); - case Key.ControlV: + case Key.V | Key.CtrlMask: case Key.PageDown: return MovePageDown (); @@ -464,7 +464,7 @@ namespace Terminal.Gui { public virtual bool MoveDown () { if (source.Count == 0) { - // Do we set lastSelectedItem to zero here? + // Do we set lastSelectedItem to -1 here? return false; //Nothing for us to move to } if (selected >= source.Count) { @@ -496,7 +496,7 @@ namespace Terminal.Gui { public virtual bool MoveUp () { if (source.Count == 0) { - // Do we set lastSelectedItem to zero here? + // Do we set lastSelectedItem to -1 here? return false; //Nothing for us to move to } if (selected >= source.Count) { diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 90e5ebecb..b6bfcb353 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -41,17 +41,19 @@ namespace Terminal.Gui { /// public class MenuItem { ustring title; - Key shortCut; + + ShortCutHelper shortCutHelper; /// /// Initializes a new instance of /// - public MenuItem (Key shortCut = Key.ControlSpace) + public MenuItem (Key shortCut = Key.Null) { Title = ""; Help = ""; - if (shortCut != Key.ControlSpace) { - ShortCut = shortCut; + shortCutHelper = new ShortCutHelper (); + if (shortCut != Key.Null) { + shortCutHelper.ShortCut = shortCut; } } @@ -64,15 +66,16 @@ namespace Terminal.Gui { /// Function to determine if the action can currently be executed. /// The of this menu item. /// The keystroke combination. - public MenuItem (ustring title, ustring help, Action action, Func canExecute = null, MenuItem parent = null, Key shortCut = Key.ControlSpace) + public MenuItem (ustring title, ustring help, Action action, Func canExecute = null, MenuItem parent = null, Key shortCut = Key.Null) { Title = title ?? ""; Help = help ?? ""; Action = action; CanExecute = canExecute; Parent = parent; - if (shortCut != Key.ControlSpace) { - ShortCut = shortCut; + shortCutHelper = new ShortCutHelper (); + if (shortCut != Key.Null) { + shortCutHelper.ShortCut = shortCut; } } @@ -84,25 +87,21 @@ namespace Terminal.Gui { public Rune HotKey; /// - /// This is the global setting that can be used as a global shortcut to invoke the action on the menu. + /// This is the global setting that can be used as a global to invoke the action on the menu. /// public Key ShortCut { - get => shortCut; + get => shortCutHelper.ShortCut; set { - if (shortCut != value) { - if (GetKeyToString (value).Contains ("Control")) { - shortCut = Key.CtrlMask | value; - } else { - shortCut = value; - } + if (shortCutHelper.ShortCut != value && (ShortCutHelper.PostShortCutValidation (value) || value == Key.Null)) { + shortCutHelper.ShortCut = value; } } } /// - /// The keystroke combination used in the as string. + /// The keystroke combination used in the as string. /// - public ustring ShortCutTag => GetShortCutTag (shortCut); + public ustring ShortCutTag => ShortCutHelper.GetShortCutTag (shortCutHelper.ShortCut); /// /// Gets or sets the title. @@ -201,137 +200,6 @@ namespace Terminal.Gui { } } } - - /// - /// Get the key as string. - /// - /// The shortcut key. - /// - public ustring GetShortCutTag (Key shortCut) - { - if (shortCut == Key.ControlSpace) { - return ""; - } - - var k = (uint)shortCut; - var delimiter = MenuBar.ShortCutDelimiter; - ustring tag = ustring.Empty; - var sCut = GetKeyToString (shortCut).ToString (); - if (sCut.Contains ("Control") || (shortCut & Key.CtrlMask) != 0) { - tag = "Ctrl"; - } - if ((shortCut & Key.ShiftMask) != 0) { - if (!tag.IsEmpty) { - tag += delimiter; - } - tag += "Shift"; - } - if ((shortCut & Key.AltMask) != 0) { - if (!tag.IsEmpty) { - tag += delimiter; - } - tag += "Alt"; - } - - ustring [] keys = ustring.Make (sCut).Split (","); - for (int i = 0; i < keys.Length; i++) { - var key = keys [i].TrimSpace (); - if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) { - continue; - } - if (!tag.IsEmpty) { - tag += delimiter; - } - if (key.Contains ("Control")) { - tag += ((char)key.ElementAt (7)).ToString (); - } else if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) { - k = (uint)Key.AltMask + k; - tag += ((char)k).ToString (); - } else if (key.Length == 2 && key.StartsWith ("D")) { - tag += ((char)key.ElementAt (1)).ToString (); - } else { - tag += key; - } - } - - return tag; - } - - ustring GetKeyToString (Key key) - { - if (key == Key.ControlSpace) { - return ""; - } - - var mK = key & (Key.AltMask | Key.CtrlMask | Key.ShiftMask); - for (uint i = (uint)Key.F1; i < (uint)Key.F12; i++) { - if ((key | (Key)i) == key) { - mK |= (Key)i; - } - } - var k = key; - k &= ~mK; - int.TryParse (k.ToString (), out int c); - var s = mK == Key.ControlSpace ? "" : mK.ToString (); - if (s != "" && (k != Key.ControlSpace || c > 0)) { - s += ","; - } - s += c == 0 ? k == Key.ControlSpace ? "" : k.ToString () : ((char)c).ToString (); - return s; - } - - /// - /// Allows to generate a from a - /// - /// The key as string. - /// - public Key CreateShortCutFromTag (ustring tag) - { - var sCut = tag; - if (sCut.IsEmpty) { - return default; - } - - Key key = Key.ControlSpace; - var hasCtrl = false; - var delimiter = MenuBar.ShortCutDelimiter; - - ustring [] keys = sCut.Split (delimiter); - for (int i = 0; i < keys.Length; i++) { - var k = keys [i]; - if (k == "Ctrl") { - hasCtrl = true; - key |= Key.CtrlMask; - } else if (k == "Shift") { - key |= Key.ShiftMask; - } else if (k == "Alt") { - key |= Key.AltMask; - } else if (k.StartsWith ("F") && k.Length > 1) { - int.TryParse (k.Substring (1).ToString (), out int n); - for (uint j = (uint)Key.F1; j < (uint)Key.F12; j++) { - int.TryParse (((Key)j).ToString ().Substring (1), out int f); - if (f == n) { - key |= (Key)j; - } - } - } else if (k [0] >= 'A' && k [0] <= 'Z') { - if (hasCtrl) { - var n = k [0] - 'A' + 1; - var d = n - (uint)Key.ControlA; - key |= (Key)((uint)Key.ControlA + d); - } else { - key |= (Key)k [0]; - } - } else if (k [0] >= '0' && k [0] <= '9') { - //var n = k [0] - (uint)Key.D0 + 1; - //var d = n - (uint)Key.D0; - //key |= (Key)((uint)Key.D0 + d); - key |= (Key)k [0]; - } - } - - return key; - } } /// @@ -513,7 +381,7 @@ namespace Terminal.Gui { } else { current = -1; - for (int i = 0; i < barItems.Children.Length; i++) { + for (int i = 0; i < barItems.Children?.Length; i++) { if (barItems.Children [i] != null) { current = i; break; @@ -1473,7 +1341,7 @@ namespace Terminal.Gui { if (mi == null) { continue; } - if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.ShortCut != Key.ControlSpace && mi.ShortCut == (Key)key) { + if ((!(mi is MenuBarItem mbiTopLevel) || mbiTopLevel.IsTopLevel) && mi.ShortCut != Key.Null && mi.ShortCut == (Key)key) { var action = mi.Action; if (action != null) { Application.MainLoop.AddIdle (() => { @@ -1548,7 +1416,7 @@ namespace Terminal.Gui { break; case Key.Esc: - case Key.ControlC: + case Key.C | Key.CtrlMask: //TODO: Running = false; CloseMenu (); if (openedByAltKey) { diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs index b5e74fd3e..1245b35c2 100644 --- a/Terminal.Gui/Views/ScrollView.cs +++ b/Terminal.Gui/Views/ScrollView.cs @@ -741,7 +741,7 @@ namespace Terminal.Gui { case Key.PageUp: return ScrollUp (Bounds.Height); - case Key.ControlV: + case Key.V | Key.CtrlMask: case Key.PageDown: return ScrollDown (Bounds.Height); diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 6dd9e3ec4..9f084a919 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -347,7 +347,7 @@ namespace Terminal.Gui { switch (GetModifiersKey (kb)) { case Key.DeleteChar: - case Key.ControlD | Key.CtrlMask: + case Key.D | Key.CtrlMask: if (ReadOnly) return true; @@ -381,6 +381,8 @@ namespace Terminal.Gui { break; case Key.Home | Key.ShiftMask: + case Key.Home | Key.ShiftMask | Key.CtrlMask: + case Key.A | Key.ShiftMask | Key.CtrlMask: if (point > 0) { int x = point; point = 0; @@ -389,6 +391,8 @@ namespace Terminal.Gui { break; case Key.End | Key.ShiftMask: + case Key.End | Key.ShiftMask | Key.CtrlMask: + case Key.E | Key.ShiftMask | Key.CtrlMask: if (point < text.Count) { int x = point; point = text.Count; @@ -398,7 +402,8 @@ namespace Terminal.Gui { // Home, C-A case Key.Home: - case Key.ControlA | Key.CtrlMask: + case Key.Home | Key.CtrlMask: + case Key.A | Key.CtrlMask: ClearAllSelection (); point = 0; Adjust (); @@ -441,7 +446,7 @@ namespace Terminal.Gui { break; case Key.CursorLeft: - case Key.ControlB | Key.CtrlMask: + case Key.B | Key.CtrlMask: ClearAllSelection (); if (point > 0) { point--; @@ -450,14 +455,15 @@ namespace Terminal.Gui { break; case Key.End: - case Key.ControlE | Key.CtrlMask: // End + case Key.End | Key.CtrlMask: + case Key.E | Key.CtrlMask: // End ClearAllSelection (); point = text.Count; Adjust (); break; case Key.CursorRight: - case Key.ControlF | Key.CtrlMask: + case Key.F | Key.CtrlMask: ClearAllSelection (); if (point == text.Count) break; @@ -465,7 +471,7 @@ namespace Terminal.Gui { Adjust (); break; - case Key.ControlK | Key.CtrlMask: // kill-to-end + case Key.K | Key.CtrlMask: // kill-to-end if (ReadOnly) return true; @@ -478,7 +484,7 @@ namespace Terminal.Gui { break; // Undo - case Key.ControlZ | Key.CtrlMask: + case Key.Z | Key.CtrlMask: if (ReadOnly) return true; @@ -494,7 +500,7 @@ namespace Terminal.Gui { break; //Redo - case Key.ControlY | Key.CtrlMask: // Control-y, yank + case Key.Y | Key.CtrlMask: // Control-y, yank if (ReadOnly) return true; @@ -530,6 +536,7 @@ namespace Terminal.Gui { break; case Key.CursorLeft | Key.CtrlMask: + case Key.CursorUp | Key.CtrlMask: case (Key)((int)'b' + Key.AltMask): ClearAllSelection (); int bw = WordBackward (point); @@ -539,6 +546,7 @@ namespace Terminal.Gui { break; case Key.CursorRight | Key.CtrlMask: + case Key.CursorDown | Key.CtrlMask: case (Key)((int)'f' + Key.AltMask): ClearAllSelection (); int fw = WordForward (point); @@ -552,18 +560,18 @@ namespace Terminal.Gui { SetNeedsDisplay (); break; - case Key.ControlC | Key.CtrlMask: + case Key.C | Key.CtrlMask: Copy (); break; - case Key.ControlX | Key.CtrlMask: + case Key.X | Key.CtrlMask: if (ReadOnly) return true; Cut (); break; - case Key.ControlV | Key.CtrlMask: + case Key.V | Key.CtrlMask: Paste (); break; @@ -817,6 +825,7 @@ namespace Terminal.Gui { SelectedLength = 0; SelectedText = ""; start = 0; + SetNeedsDisplay (); } void SetSelectedStartSelectedLength () diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index 704f65a2a..a37619c96 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -734,13 +734,13 @@ namespace Terminal.Gui { // Handle some state here - whether the last command was a kill // operation and the column tracking (up/down) switch (kb.Key) { - case Key.ControlN: + case Key.N | Key.CtrlMask: case Key.CursorDown: - case Key.ControlP: + case Key.P | Key.CtrlMask: case Key.CursorUp: lastWasKill = false; break; - case Key.ControlK: + case Key.K | Key.CtrlMask: break; default: lastWasKill = false; @@ -751,7 +751,7 @@ namespace Terminal.Gui { // Dispatch the command. switch (kb.Key) { case Key.PageDown: - case Key.ControlV: + case Key.V | Key.CtrlMask: int nPageDnShift = Frame.Height - 1; if (currentRow < model.Count) { if (columnTrack == -1) @@ -782,17 +782,17 @@ namespace Terminal.Gui { } break; - case Key.ControlN: + case Key.N | Key.CtrlMask: case Key.CursorDown: MoveDown (); break; - case Key.ControlP: + case Key.P | Key.CtrlMask: case Key.CursorUp: MoveUp (); break; - case Key.ControlF: + case Key.F | Key.CtrlMask: case Key.CursorRight: var currentLine = GetCurrentLine (); if (currentColumn < currentLine.Count) { @@ -817,7 +817,7 @@ namespace Terminal.Gui { } break; - case Key.ControlB: + case Key.B | Key.CtrlMask: case Key.CursorLeft: if (currentColumn > 0) { currentColumn--; @@ -880,7 +880,7 @@ namespace Terminal.Gui { // Home, C-A case Key.Home: - case Key.ControlA: + case Key.A | Key.CtrlMask: currentColumn = 0; if (currentColumn < leftColumn) { leftColumn = 0; @@ -889,7 +889,7 @@ namespace Terminal.Gui { PositionCursor (); break; case Key.DeleteChar: - case Key.ControlD: // Delete + case Key.D | Key.CtrlMask: // Delete if (isReadOnly) break; currentLine = GetCurrentLine (); @@ -909,7 +909,7 @@ namespace Terminal.Gui { break; case Key.End: - case Key.ControlE: // End + case Key.E | Key.CtrlMask: // End currentLine = GetCurrentLine (); currentColumn = currentLine.Count; int pcol = leftColumn; @@ -921,7 +921,7 @@ namespace Terminal.Gui { PositionCursor (); break; - case Key.ControlK: // kill-to-end + case Key.K | Key.CtrlMask: // kill-to-end if (isReadOnly) break; currentLine = GetCurrentLine (); @@ -946,14 +946,14 @@ namespace Terminal.Gui { lastWasKill = true; break; - case Key.ControlY: // Control-y, yank + case Key.Y | Key.CtrlMask: // Control-y, yank if (isReadOnly) break; InsertText (Clipboard.Contents); selecting = false; break; - case Key.ControlSpace: + case Key.Space | Key.CtrlMask: selecting = true; selectionStartColumn = currentColumn; selectionStartRow = currentRow; @@ -964,7 +964,7 @@ namespace Terminal.Gui { selecting = false; break; - case Key.ControlW: + case Key.W | Key.CtrlMask: SetClipboard (GetRegion ()); if (!isReadOnly) ClearRegion (); diff --git a/Terminal.Gui/Views/TimeField.cs b/Terminal.Gui/Views/TimeField.cs index e133f685f..bf22d9fec 100644 --- a/Terminal.Gui/Views/TimeField.cs +++ b/Terminal.Gui/Views/TimeField.cs @@ -217,7 +217,7 @@ namespace Terminal.Gui { { switch (kb.Key) { case Key.DeleteChar: - case Key.ControlD: + case Key.D | Key.CtrlMask: if (ReadOnly) return true; @@ -235,28 +235,28 @@ namespace Terminal.Gui { // Home, C-A case Key.Home: - case Key.ControlA: + case Key.A | Key.CtrlMask: CursorPosition = 1; break; case Key.CursorLeft: - case Key.ControlB: + case Key.B | Key.CtrlMask: DecCursorPosition (); break; case Key.End: - case Key.ControlE: // End + case Key.E | Key.CtrlMask: // End CursorPosition = FieldLen; break; case Key.CursorRight: - case Key.ControlF: + case Key.F | Key.CtrlMask: IncCursorPosition (); break; default: // Ignore non-numeric characters. - if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9')) + if (kb.Key < (Key)((int)Key.D0) || kb.Key > (Key)((int)Key.D9)) return false; if (ReadOnly) diff --git a/Terminal.Gui/Windows/FileDialog.cs b/Terminal.Gui/Windows/FileDialog.cs index f3a50dc46..a3e6c89df 100644 --- a/Terminal.Gui/Windows/FileDialog.cs +++ b/Terminal.Gui/Windows/FileDialog.cs @@ -279,16 +279,16 @@ namespace Terminal.Gui { { switch (keyEvent.Key) { case Key.CursorUp: - case Key.ControlP: + case Key.P | Key.CtrlMask: MoveUp (); return true; case Key.CursorDown: - case Key.ControlN: + case Key.N | Key.CtrlMask: MoveDown (); return true; - case Key.ControlV: + case Key.V | Key.CtrlMask: case Key.PageDown: var n = (selected + Frame.Height); if (n > infos.Count) @@ -325,7 +325,7 @@ namespace Terminal.Gui { return true; case Key.Space: - case Key.ControlT: + case Key.T | Key.CtrlMask: PerformMultipleSelection (); return true; diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index 57bfcf8aa..635eae25f 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -59,7 +59,7 @@ namespace UICatalog { public override void Setup () { var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()), + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), new StatusItem(Key.F2, "~F2~ Toggle Frame Ruler", () => { ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; Top.SetNeedsDisplay (); diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index aef345b0b..2789a8a92 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -28,7 +28,7 @@ namespace UICatalog { Top.Add (menu); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()), + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), }); Top.Add (statusBar); diff --git a/UICatalog/Scenarios/DynamicMenuBar.cs b/UICatalog/Scenarios/DynamicMenuBar.cs index b6472c467..3c8d433da 100644 --- a/UICatalog/Scenarios/DynamicMenuBar.cs +++ b/UICatalog/Scenarios/DynamicMenuBar.cs @@ -207,74 +207,6 @@ namespace UICatalog { }; Add (_frmMenuDetails); - _btnAdd.Clicked += () => { - if (MenuBar == null) { - MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok"); - _btnAddMenuBar.SetFocus (); - return; - } - - var frameDetails = new DynamicMenuBarDetails (null, _currentMenuBarItem != null); - var item = frameDetails.EnterMenuItem (); - if (item == null) { - return; - } - - if (!(_currentMenuBarItem is MenuBarItem)) { - var parent = _currentMenuBarItem.Parent as MenuBarItem; - var idx = parent.GetChildrenIndex (_currentMenuBarItem); - _currentMenuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { }, _currentMenuBarItem.Parent); - _currentMenuBarItem.CheckType = item.checkStyle; - parent.Children [idx] = _currentMenuBarItem; - } else { - MenuItem newMenu = CreateNewMenu (item, _currentMenuBarItem); - var menuBarItem = _currentMenuBarItem as MenuBarItem; - if (menuBarItem == null) { - menuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { newMenu }, _currentMenuBarItem.Parent); - } else if (menuBarItem.Children == null) { - menuBarItem.Children = new MenuItem [] { newMenu }; - } else { - var childrens = menuBarItem.Children; - Array.Resize (ref childrens, childrens.Length + 1); - childrens [childrens.Length - 1] = newMenu; - menuBarItem.Children = childrens; - } - DataContext.Menus.Add (new DynamicMenuItemList (newMenu.Title, newMenu)); - _lstMenus.MoveDown (); - } - }; - - _btnRemove.Clicked += () => { - var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null; - if (menuItem != null) { - var childrens = ((MenuBarItem)_currentMenuBarItem).Children; - childrens [_lstMenus.SelectedItem] = null; - int i = 0; - foreach (var c in childrens) { - if (c != null) { - childrens [i] = c; - i++; - } - } - Array.Resize (ref childrens, childrens.Length - 1); - if (childrens.Length == 0) { - if (_currentMenuBarItem.Parent == null) { - ((MenuBarItem)_currentMenuBarItem).Children = null; - _currentMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title)); - } else { - _currentMenuBarItem = new MenuItem (_currentMenuBarItem.Title, _currentMenuBarItem.Help, _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentEditMenuBarItem.Title)), null, _currentMenuBarItem.Parent); - } - } else { - ((MenuBarItem)_currentMenuBarItem).Children = childrens; - } - DataContext.Menus.RemoveAt (_lstMenus.SelectedItem); - if (_lstMenus.Source.Count > 0 && _lstMenus.SelectedItem > _lstMenus.Source.Count - 1) { - _lstMenus.SelectedItem = _lstMenus.Source.Count - 1; - } - _lstMenus.SetNeedsDisplay (); - } - }; - _btnMenuBarUp.Clicked += () => { var i = _currentSelectedMenuBar; var menuItem = _menuBar != null && _menuBar.Menus.Length > 0 ? _menuBar.Menus [i] : null; @@ -369,8 +301,7 @@ namespace UICatalog { Add (_btnCancel); _lstMenus.SelectedItemChanged += (e) => { - var menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [e.Item].MenuItem : null; - SetFrameDetails (menuBarItem); + SetFrameDetails (); }; _btnOk.Clicked += () => { @@ -389,6 +320,75 @@ namespace UICatalog { } }; + _btnAdd.Clicked += () => { + if (MenuBar == null) { + MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok"); + _btnAddMenuBar.SetFocus (); + return; + } + + var frameDetails = new DynamicMenuBarDetails (null, _currentMenuBarItem != null); + var item = frameDetails.EnterMenuItem (); + if (item == null) { + return; + } + + if (!(_currentMenuBarItem is MenuBarItem)) { + var parent = _currentMenuBarItem.Parent as MenuBarItem; + var idx = parent.GetChildrenIndex (_currentMenuBarItem); + _currentMenuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { }, _currentMenuBarItem.Parent); + _currentMenuBarItem.CheckType = item.checkStyle; + parent.Children [idx] = _currentMenuBarItem; + } else { + MenuItem newMenu = CreateNewMenu (item, _currentMenuBarItem); + var menuBarItem = _currentMenuBarItem as MenuBarItem; + if (menuBarItem == null) { + menuBarItem = new MenuBarItem (_currentMenuBarItem.Title, new MenuItem [] { newMenu }, _currentMenuBarItem.Parent); + } else if (menuBarItem.Children == null) { + menuBarItem.Children = new MenuItem [] { newMenu }; + } else { + var childrens = menuBarItem.Children; + Array.Resize (ref childrens, childrens.Length + 1); + childrens [childrens.Length - 1] = newMenu; + menuBarItem.Children = childrens; + } + DataContext.Menus.Add (new DynamicMenuItemList (newMenu.Title, newMenu)); + _lstMenus.MoveDown (); + } + }; + + _btnRemove.Clicked += () => { + var menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null; + if (menuItem != null) { + var childrens = ((MenuBarItem)_currentMenuBarItem).Children; + childrens [_lstMenus.SelectedItem] = null; + int i = 0; + foreach (var c in childrens) { + if (c != null) { + childrens [i] = c; + i++; + } + } + Array.Resize (ref childrens, childrens.Length - 1); + if (childrens.Length == 0) { + if (_currentMenuBarItem.Parent == null) { + ((MenuBarItem)_currentMenuBarItem).Children = null; + //_currentMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentMenuBarItem.Title)); + } else { + _currentMenuBarItem = new MenuItem (_currentMenuBarItem.Title, _currentMenuBarItem.Help, _frmMenuDetails.CreateAction (_currentEditMenuBarItem, new DynamicMenuItem (_currentEditMenuBarItem.Title)), null, _currentMenuBarItem.Parent); + } + } else { + ((MenuBarItem)_currentMenuBarItem).Children = childrens; + } + DataContext.Menus.RemoveAt (_lstMenus.SelectedItem); + if (_lstMenus.Source.Count > 0 && _lstMenus.SelectedItem > _lstMenus.Source.Count - 1) { + _lstMenus.SelectedItem = _lstMenus.Source.Count - 1; + } + _lstMenus.SetNeedsDisplay (); + SetFrameDetails (); + } + }; + _lstMenus.OpenSelectedItem += (e) => { _currentMenuBarItem = DataContext.Menus [e.Item].MenuItem; if (!(_currentMenuBarItem is MenuBarItem)) { @@ -503,8 +503,16 @@ namespace UICatalog { var lstMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter); - void SetFrameDetails (MenuItem menuItem = null) + void SetFrameDetails (MenuItem menuBarItem = null) { + MenuItem menuItem; + + if (menuBarItem == null) { + menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [_lstMenus.SelectedItem].MenuItem : null; + } else { + menuItem = menuBarItem; + } + _currentEditMenuBarItem = menuItem; _frmMenuDetails.EditMenuBarItem (menuItem); var f = _btnOk.CanFocus == _frmMenuDetails.CanFocus; @@ -555,14 +563,14 @@ namespace UICatalog { newMenu = new MenuItem (item.title, item.help, null, null, parent); newMenu.CheckType = item.checkStyle; newMenu.Action = _frmMenuDetails.CreateAction (newMenu, item); - newMenu.ShortCut = newMenu.CreateShortCutFromTag (item.shortCut); + newMenu.ShortCut = ShortCutHelper.GetShortCutFromTag (item.shortCut); } else if (item.isTopLevel) { newMenu = new MenuBarItem (item.title, item.help, null); newMenu.Action = _frmMenuDetails.CreateAction (newMenu, item); } else { newMenu = new MenuBarItem (item.title, item.help, null); ((MenuBarItem)newMenu).Children [0].Action = _frmMenuDetails.CreateAction (newMenu, item); - ((MenuBarItem)newMenu).Children [0].ShortCut = newMenu.CreateShortCutFromTag (item.shortCut); + ((MenuBarItem)newMenu).Children [0].ShortCut = ShortCutHelper.GetShortCutFromTag (item.shortCut); } return newMenu; @@ -600,7 +608,7 @@ namespace UICatalog { DataContext.Menus = new List (); } _currentEditMenuBarItem.Action = _frmMenuDetails.CreateAction (_currentEditMenuBarItem, menuItem); - _currentEditMenuBarItem.ShortCut = _currentEditMenuBarItem.CreateShortCutFromTag (menuItem.shortCut); + _currentEditMenuBarItem.ShortCut = ShortCutHelper.GetShortCutFromTag (menuItem.shortCut); } if (_currentEditMenuBarItem.Parent == null) { @@ -713,54 +721,53 @@ namespace UICatalog { ReadOnly = true }; _txtShortCut.KeyDown += (e) => { - var k = GetModifiersKey (e.KeyEvent); - if (((k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) { - _txtShortCut.Text = ""; + if (!ProcessKey (e.KeyEvent)) { return; } - GetShortCut (k); - e.Handled = true; + var k = GetModifiersKey (e.KeyEvent); + if (CheckShortCut (k, true)) { + e.Handled = true; + } }; - bool CheckFlagRange (Key key, Key first, Key last) + bool ProcessKey (KeyEvent ev) { - for (uint i = (uint)first; i < (uint)last; i++) { - if ((key | (Key)i) == key) { - return true; - } + switch (ev.Key) { + case Key.CursorUp: + case Key.CursorDown: + case Key.Tab: + case Key.BackTab: + return false; } - return false; + + return true; } - void GetShortCut (Key k) + bool CheckShortCut (Key k, bool pre) { var m = _menuItem != null ? _menuItem : new MenuItem (); - var s = m.GetShortCutTag (k); - if (s.Contains ("Unknow")) { + if (pre && !ShortCutHelper.PreShortCutValidation (k)) { _txtShortCut.Text = ""; - } else { - _txtShortCut.Text = s; + return false; } + if (!pre) { + if (!ShortCutHelper.PostShortCutValidation (ShortCutHelper.GetShortCutFromTag (_txtShortCut.Text))) { + _txtShortCut.Text = ""; + return false; + } + return true; + } + _txtShortCut.Text = ShortCutHelper.GetShortCutTag (k); + + return true; } _txtShortCut.KeyUp += (e) => { var k = GetModifiersKey (e.KeyEvent); - if ((k & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) == 0 || ((k | Key.D0) == 0 && !CheckFlagRange (k, Key.F1, Key.F12))) { - return; + if (CheckShortCut (k, false)) { + e.Handled = true; } - - e.Handled = true; - var kVal = (Key)e.KeyEvent.KeyValue; - if ((kVal & Key.D0) != 0) { - - } else if ((kVal == Key.CtrlMask || kVal == Key.ShiftMask || kVal == Key.AltMask || - (kVal & (Key.CtrlMask | Key.ShiftMask)) != 0 || (kVal & (Key.CtrlMask | Key.AltMask)) != 0 || (kVal & (Key.ShiftMask | Key.AltMask)) != 0 || - (kVal & (Key.CtrlMask | Key.ShiftMask | Key.AltMask)) != 0) && !kVal.ToString ().Contains ("Control")) { - return; - } - GetShortCut (k); - e.Handled = true; }; Add (_txtShortCut); @@ -865,6 +872,7 @@ namespace UICatalog { Height = Dim.Fill () - 1; _dialog.Add (this); _txtTitle.SetFocus (); + _txtTitle.CursorPosition = _txtTitle.Text.Length; Application.Run (_dialog); if (valid) { @@ -884,6 +892,7 @@ namespace UICatalog { if (menuItem == null) { hasParent = false; CanFocus = false; + CleanEditMenuBarItem (); return; } else { hasParent = menuItem.Parent != null; @@ -902,6 +911,17 @@ namespace UICatalog { _txtShortCut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked; } + void CleanEditMenuBarItem () + { + _txtTitle.Text = ""; + _txtHelp.Text = ""; + _txtAction.Text = ""; + _ckbIsTopLevel.Checked = false; + _ckbSubMenu.Checked = false; + _rbChkStyle.SelectedItem = (int)MenuItemCheckStyle.NoCheck; + _txtShortCut.Text = ""; + } + ustring GetTargetAction (Action action) { var me = action.Target; diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 932a5beea..8c273f1b0 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -41,7 +41,7 @@ namespace UICatalog { var statusBar = new StatusBar (new StatusItem [] { new StatusItem(Key.F2, "~F2~ Open", () => Open()), new StatusItem(Key.F3, "~F3~ Save", () => Save()), - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()), + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), }); Top.Add (statusBar); diff --git a/UICatalog/Scenarios/HexEditor.cs b/UICatalog/Scenarios/HexEditor.cs index c9015f72a..40bd7d724 100644 --- a/UICatalog/Scenarios/HexEditor.cs +++ b/UICatalog/Scenarios/HexEditor.cs @@ -42,7 +42,7 @@ namespace UICatalog { //new StatusItem(Key.Enter, "~ENTER~ ApplyEdits", () => { _hexView.ApplyEdits(); }), new StatusItem(Key.F2, "~F2~ Open", () => Open()), new StatusItem(Key.F3, "~F3~ Save", () => Save()), - new StatusItem(Key.ControlQ, "~^Q~ Quit", () => Quit()), + new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), }); Top.Add (statusBar); diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index 7fc06a9af..e6ec5a98b 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -101,7 +101,7 @@ namespace UICatalog { Y = Pos.Top (editLabel) + 4, }; Win.Add (keyLogLabel); - var fakeKeyPress = new KeyEvent (Key.ControlA, new KeyModifiers () { + var fakeKeyPress = new KeyEvent (Key.CtrlMask | Key.A, new KeyModifiers () { Alt = true, Ctrl = true, Shift = true diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs index 9ac674d42..8da3cb3d2 100644 --- a/UICatalog/Scenarios/Unicode.cs +++ b/UICatalog/Scenarios/Unicode.cs @@ -37,7 +37,7 @@ namespace UICatalog { Top.Add (menu); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem (Key.ControlQ, "~^Q~ Выход", () => Application.RequestStop()), + new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Выход", () => Application.RequestStop()), new StatusItem (Key.Unknown, "~F2~ Создать", null), new StatusItem(Key.Unknown, "~F3~ Со_хранить", null), }); diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 2fcac5be2..288d1331c 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -157,14 +157,14 @@ namespace UICatalog { _menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.ControlQ) + new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask) }), new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()), new MenuBarItem ("Diag_ostics", CreateDiagnosticMenuItems()), new MenuBarItem ("_Help", new MenuItem [] { new MenuItem ("_gui.cs API Overview", "", () => OpenUrl ("https://migueldeicaza.github.io/gui.cs/articles/overview.html"), null, null, Key.F1), new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/migueldeicaza/gui.cs"), null, null, Key.F2), - new MenuItem ("_About...", "About this app", () => MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.ControlA), + new MenuItem ("_About...", "About this app", () => MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A), }) }); @@ -229,7 +229,7 @@ namespace UICatalog { _capslock, _numlock, _scrolllock, - new StatusItem(Key.ControlQ, "~CTRL-Q~ Quit", () => { + new StatusItem(Key.Q | Key.CtrlMask, "~CTRL-Q~ Quit", () => { if (_runningScenario is null){ // This causes GetScenarioToRun to return null _runningScenario = null; diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index 2c73ed370..9d53254fe 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -168,7 +168,7 @@ namespace Terminal.Gui { Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true)); foreach (var c in input.Reverse ()) { if (char.IsLetter (c)) { - Console.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); + Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); } else { Console.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false)); } @@ -188,7 +188,7 @@ namespace Terminal.Gui { int keyUps = 0; var output = string.Empty; Application.Top.KeyUp += (View.KeyEventEventArgs args) => { - if (args.KeyEvent.Key != Key.ControlQ) { + if (args.KeyEvent.Key != (Key.CtrlMask | Key.Q)) { output += (char)args.KeyEvent.KeyValue; } keyUps++; diff --git a/UnitTests/ScenarioTests.cs b/UnitTests/ScenarioTests.cs index c9914b8a6..e60a8448d 100644 --- a/UnitTests/ScenarioTests.cs +++ b/UnitTests/ScenarioTests.cs @@ -119,7 +119,7 @@ namespace Terminal.Gui { var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback); Application.Top.KeyPress += (View.KeyEventEventArgs args) => { - Assert.Equal (Key.ControlQ, args.KeyEvent.Key); + Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key); }; var scenario = (Scenario)Activator.CreateInstance (scenarioClass);