diff --git a/Example/Example.cs b/Example/Example.cs index 25fc42ade..08b420e45 100644 --- a/Example/Example.cs +++ b/Example/Example.cs @@ -18,7 +18,7 @@ public class ExampleWindow : Window { public ExampleWindow () { - Title = "Example App (Ctrl+Q to quit)"; + Title = $"Example App ({Application.QuitKey} to quit)"; // Create input components and labels var usernameLabel = new Label () { diff --git a/README.md b/README.md index a344c541a..4b7eb78d8 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ NOTE: This is the WORK IN PROGRESS `v2.x` branch. The `main` branch is the stabl Paste these commands into your favorite terminal on Windows, Mac, or Linux. This will install the [Terminal.Gui.Templates](https://github.com/gui-cs/Terminal.Gui.templates), create a new "Hello World" TUI app, and run it. -(Press `CTRL-Q` to exit the app) +(Press `CTRL-Q` to quit the app) ```powershell dotnet new --install Terminal.Gui.templates @@ -82,7 +82,7 @@ public class ExampleWindow : Window { public ExampleWindow () { - Title = "Example App (Ctrl+Q to quit)"; + Title = $"Example App ({Application.QuitKey} to quit)"; // Create input components and labels var usernameLabel = new Label () { diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs index 017050ef0..75a58f9c3 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeConsole.cs @@ -818,10 +818,24 @@ namespace Terminal.Gui { } /// - /// + /// A stack of keypresses to return when ReadKey is called. /// public static Stack MockKeyPresses = new Stack (); + /// + /// Helper to push a onto . + /// + /// + public static void PushMockKeyPress (Key key) + { + MockKeyPresses.Push (new ConsoleKeyInfo ( + (char)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask), + ConsoleKeyMapping.GetConsoleKeyFromKey (key), + key.HasFlag (Key.ShiftMask), + key.HasFlag (Key.AltMask), + key.HasFlag (Key.CtrlMask))); + } + // // Summary: // Obtains the next character or function key pressed by the user. The pressed key diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 30951dd73..b71bc3f1d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -220,6 +220,8 @@ namespace Terminal.Gui { public override void Init (Action terminalResized) { + FakeConsole.MockKeyPresses.Clear (); + TerminalResized = terminalResized; cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH; diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs index efa1fcec6..592b974fc 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -15,7 +15,7 @@ namespace Terminal.Gui { AutoResetEvent waitForProbe = new AutoResetEvent (false); ConsoleKeyInfo? keyResult = null; MainLoop mainLoop; - Func consoleKeyReaderFn = () => FakeConsole.ReadKey (true); + //Func consoleKeyReaderFn = () => ; /// /// Invoked when a Key is pressed. @@ -31,11 +31,11 @@ namespace Terminal.Gui { // consoleDriver is not needed/used in FakeConsole } - void WindowsKeyReader () + void MockKeyReader () { while (true) { waitForProbe.WaitOne (); - keyResult = consoleKeyReaderFn (); + keyResult = FakeConsole.ReadKey (true); keyReady.Set (); } } @@ -43,7 +43,7 @@ namespace Terminal.Gui { void IMainLoopDriver.Setup (MainLoop mainLoop) { this.mainLoop = mainLoop; - Thread readThread = new Thread (WindowsKeyReader); + Thread readThread = new Thread (MockKeyReader); readThread.Start (); } diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 561d6a8c4..9229934a0 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -34,7 +34,7 @@ namespace Terminal.Gui { /// // A simple Terminal.Gui app that creates a window with a frame and title with /// // 5 rows/columns of padding. /// Application.Init(); - /// var win = new Window ("Hello World - CTRL-Q to quit") { + /// var win = new Window ($"Example App ({Application.QuitKey} to quit)") { /// X = 5, /// Y = 5, /// Width = Dim.Fill (5), diff --git a/Terminal.Gui/Core/ConsoleKeyMapping.cs b/Terminal.Gui/Core/ConsoleKeyMapping.cs index 441adac48..8902c1bf0 100644 --- a/Terminal.Gui/Core/ConsoleKeyMapping.cs +++ b/Terminal.Gui/Core/ConsoleKeyMapping.cs @@ -65,10 +65,30 @@ namespace Terminal.Gui { } return sCode; } - + return null; } + /// + /// Gets the from the provided . + /// + /// + /// + public static ConsoleKey GetConsoleKeyFromKey (Key key) + { + ConsoleModifiers mod = new ConsoleModifiers (); + if (key.HasFlag (Key.ShiftMask)) { + mod |= ConsoleModifiers.Shift; + } + if (key.HasFlag (Key.AltMask)) { + mod |= ConsoleModifiers.Alt; + } + if (key.HasFlag (Key.CtrlMask)) { + mod |= ConsoleModifiers.Control; + } + return (ConsoleKey)ConsoleKeyMapping.GetConsoleKeyFromKey ((uint)(key & ~Key.CtrlMask & ~Key.ShiftMask & ~Key.AltMask), mod, out _, out _); + } + /// /// Get the from a . /// diff --git a/UICatalog/Scenario.cs b/UICatalog/Scenario.cs index 6aca19825..88c58a114 100644 --- a/UICatalog/Scenario.cs +++ b/UICatalog/Scenario.cs @@ -20,7 +20,7 @@ namespace UICatalog { /// /// /// The UI Catalog program uses reflection to find all scenarios and adds them to the - /// ListViews. Press ENTER to run the selected scenario. Press CTRL-Q to exit it. / + /// ListViews. Press ENTER to run the selected scenario. Press the default quit key to quit. / /// /// /// @@ -48,6 +48,9 @@ namespace UICatalog { public class Scenario : IDisposable { private bool _disposedValue; + public string Theme = "Default"; + public string TopLevelColorScheme = "Base"; + /// /// The Window for the . This should be set to in most cases. /// @@ -59,7 +62,6 @@ namespace UICatalog { /// the Scenario picker UI. /// Override to provide any behavior needed. /// - /// The colorscheme to use. /// /// /// The base implementation calls and creates a for @@ -70,34 +72,19 @@ namespace UICatalog { /// before creating any views or calling other Terminal.Gui APIs. /// /// - public virtual void Init (ColorScheme colorScheme) + public virtual void Init () { - //ConfigurationManager.Applied += (a) => { - // if (Application.Top == null) { - // return; - // } - - // //// Apply changes that apply to either UICatalogTopLevel or a Scenario - // //if (Application.Top.MenuBar != null) { - // // Application.Top.MenuBar.ColorScheme = Colors.ColorSchemes ["Menu"]; - // // Application.Top.MenuBar.SetNeedsDisplay (); - // //} - - // //if (Application.Top.StatusBar != null) { - // // Application.Top.StatusBar.ColorScheme = Colors.ColorSchemes ["Menu"]; - // // Application.Top.StatusBar.SetNeedsDisplay (); - // //} - // //Application.Top.SetNeedsDisplay (); - //}; - Application.Init (); + + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); - Win = new Window ($"{Application.QuitKey} to Close - Scenario: {GetName ()}") { + Win = new Window ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), - ColorScheme = colorScheme, + ColorScheme = Colors.ColorSchemes [TopLevelColorScheme], }; Application.Top.Add (Win); } diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs index c731d187c..9d101be42 100644 --- a/UICatalog/Scenarios/ASCIICustomButton.cs +++ b/UICatalog/Scenarios/ASCIICustomButton.cs @@ -11,7 +11,7 @@ namespace UICatalog.Scenarios { private ScrollViewTestWindow scrollViewTestWindow; private MenuItem miSmallerWindow; - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); scrollViewTestWindow = new ScrollViewTestWindow (); @@ -21,7 +21,7 @@ namespace UICatalog.Scenarios { CheckType = MenuItemCheckStyle.Checked }, null, - new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Key.Q | Key.CtrlMask) + new MenuItem("Quit", "",() => Application.RequestStop(),null,null, Application.QuitKey) }) }); Application.Top.Add (menu, scrollViewTestWindow); diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index 45cffdbfd..04a85a898 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Reflection; using System.Text; using Terminal.Gui; +using Terminal.Gui.Configuration; namespace UICatalog.Scenarios { [ScenarioMetadata (Name: "All Views Tester", Description: "Provides a test UI for all classes derived from View.")] @@ -40,16 +41,20 @@ namespace UICatalog.Scenarios { TextField _hText; int _hVal = 0; - public override void Init (ColorScheme colorScheme) + public override void Init () { + // Don't create a sub-win (Scenario.Win); just use Application.Top Application.Init (); - // Don't create a sub-win; just use Applicatiion.Top + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } + public override void Setup () { var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), new StatusItem(Key.F2, "~F2~ Toggle Frame Ruler", () => { ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; Application.Top.SetNeedsDisplay (); diff --git a/UICatalog/Scenarios/BackgroundWorkerCollection.cs b/UICatalog/Scenarios/BackgroundWorkerCollection.cs index 2573b00e5..0c4217063 100644 --- a/UICatalog/Scenarios/BackgroundWorkerCollection.cs +++ b/UICatalog/Scenarios/BackgroundWorkerCollection.cs @@ -36,16 +36,16 @@ namespace UICatalog.Scenarios { new MenuItem ("_Run Worker", "", () => workerApp.RunWorker(), null, null, Key.CtrlMask | Key.R), new MenuItem ("_Cancel Worker", "", () => workerApp.CancelWorker(), null, null, Key.CtrlMask | Key.C), null, - new MenuItem ("_Quit", "", () => Quit(), null, null, Key.CtrlMask | Key.Q) + new MenuItem ("_Quit", "", () => Quit(), null, null, Application.QuitKey) }), new MenuBarItem ("_View", new MenuItem [] { }), new MenuBarItem ("_Window", new MenuItem [] { }) - }); + }); ; menu.MenuOpening += Menu_MenuOpening; Add (menu); var statusBar = new StatusBar (new [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Worker", () => workerApp.RunWorker()), new StatusItem(Key.CtrlMask | Key.C, "~^C~ Cancel Worker", () => workerApp.CancelWorker()) }); @@ -312,7 +312,7 @@ namespace UICatalog.Scenarios { Title = "Run Worker"; - label = new Label ("Press start to do the work or close to exit.") { + label = new Label ("Press start to do the work or close to quit.") { X = Pos.Center (), Y = 1, ColorScheme = Colors.Dialog diff --git a/UICatalog/Scenarios/BordersComparisons.cs b/UICatalog/Scenarios/BordersComparisons.cs index 2ecb4fbcb..3994eef8d 100644 --- a/UICatalog/Scenarios/BordersComparisons.cs +++ b/UICatalog/Scenarios/BordersComparisons.cs @@ -5,7 +5,7 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Layout")] [ScenarioCategory ("Borders")] public class BordersComparisons : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); diff --git a/UICatalog/Scenarios/BordersOnFrameView.cs b/UICatalog/Scenarios/BordersOnFrameView.cs index 9e833f48c..c26099ee0 100644 --- a/UICatalog/Scenarios/BordersOnFrameView.cs +++ b/UICatalog/Scenarios/BordersOnFrameView.cs @@ -5,12 +5,12 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Layout")] [ScenarioCategory ("Borders")] public class BordersOnFrameView : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); var boc = new BordersOnContainers ( - $"CTRL-Q to Close - Scenario: {GetName ()}", + $"{Application.QuitKey} to Quit - Scenario: {GetName ()}", "FrameView", new FrameView ()); diff --git a/UICatalog/Scenarios/BordersOnToplevel.cs b/UICatalog/Scenarios/BordersOnToplevel.cs index ec0d088f0..f738bf630 100644 --- a/UICatalog/Scenarios/BordersOnToplevel.cs +++ b/UICatalog/Scenarios/BordersOnToplevel.cs @@ -5,12 +5,12 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Layout")] [ScenarioCategory ("Borders")] public class BordersOnToplevel : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); var boc = new BordersOnContainers ( - $"CTRL-Q to Close - Scenario: {GetName ()}", + $"{Application.QuitKey} to Quit - Scenario: {GetName ()}", "Toplevel", new Border.ToplevelContainer ()); diff --git a/UICatalog/Scenarios/BordersOnWindow.cs b/UICatalog/Scenarios/BordersOnWindow.cs index e16575809..c2be80ae9 100644 --- a/UICatalog/Scenarios/BordersOnWindow.cs +++ b/UICatalog/Scenarios/BordersOnWindow.cs @@ -5,12 +5,12 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Layout")] [ScenarioCategory ("Borders")] public class BordersOnWindow : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); var boc = new BordersOnContainers ( - $"CTRL-Q to Close - Scenario: {GetName ()}", + $"{Application.QuitKey} to Quit - Scenario: {GetName ()}", "Window", new Window ()); diff --git a/UICatalog/Scenarios/Clipping.cs b/UICatalog/Scenarios/Clipping.cs index 58f89b7a3..6ed96055d 100644 --- a/UICatalog/Scenarios/Clipping.cs +++ b/UICatalog/Scenarios/Clipping.cs @@ -7,7 +7,7 @@ namespace UICatalog.Scenarios { public class Clipping : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); Application.Top.ColorScheme = Colors.Base; diff --git a/UICatalog/Scenarios/CollectionNavigatorTester.cs b/UICatalog/Scenarios/CollectionNavigatorTester.cs index 9846c7fa3..ca09bfed3 100644 --- a/UICatalog/Scenarios/CollectionNavigatorTester.cs +++ b/UICatalog/Scenarios/CollectionNavigatorTester.cs @@ -15,7 +15,7 @@ namespace UICatalog.Scenarios { public class CollectionNavigatorTester : Scenario { // Don't create a Window, just return the top-level view - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); Application.Top.ColorScheme = Colors.Base; @@ -97,9 +97,9 @@ namespace UICatalog.Scenarios { allowMarking, allowMultiSelection, null, - new MenuItem ("_Quit", "", () => Quit(), null, null, Key.Q | Key.CtrlMask), + new MenuItem ("_Quit", $"{Application.QuitKey}", () => Quit(), null, null, Application.QuitKey), }), - new MenuBarItem("_Quit", "CTRL-Q", () => Quit()), + new MenuBarItem("_Quit", $"{Application.QuitKey}", () => Quit()), }); Application.Top.Add (menu); diff --git a/UICatalog/Scenarios/ComputedLayout.cs b/UICatalog/Scenarios/ComputedLayout.cs index 4d3d1dfd4..9e22c3e1f 100644 --- a/UICatalog/Scenarios/ComputedLayout.cs +++ b/UICatalog/Scenarios/ComputedLayout.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Reflection; using System.Text; using Terminal.Gui; +using Terminal.Gui.Configuration; namespace UICatalog.Scenarios { /// @@ -17,10 +18,12 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Layout")] public class ComputedLayout : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); - Application.Top.ColorScheme = colorScheme; + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } public override void Setup () diff --git a/UICatalog/Scenarios/ConfigurationEditor.cs b/UICatalog/Scenarios/ConfigurationEditor.cs index 258cccf13..ebb4bb435 100644 --- a/UICatalog/Scenarios/ConfigurationEditor.cs +++ b/UICatalog/Scenarios/ConfigurationEditor.cs @@ -36,10 +36,12 @@ namespace UICatalog.Scenarios { private static Action _editorColorSchemeChanged; // Don't create a Window, just return the top-level view - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); - Application.Top.ColorScheme = colorScheme; + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } public override void Setup () diff --git a/UICatalog/Scenarios/CsvEditor.cs b/UICatalog/Scenarios/CsvEditor.cs index 7a17beb03..a9e300e6c 100644 --- a/UICatalog/Scenarios/CsvEditor.cs +++ b/UICatalog/Scenarios/CsvEditor.cs @@ -75,7 +75,7 @@ namespace UICatalog.Scenarios { var statusBar = new StatusBar (new StatusItem [] { new StatusItem(Key.CtrlMask | Key.O, "~^O~ Open", () => Open()), new StatusItem(Key.CtrlMask | Key.S, "~^S~ Save", () => Save()), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/DynamicMenuBar.cs b/UICatalog/Scenarios/DynamicMenuBar.cs index ebf710d55..3390d7e69 100644 --- a/UICatalog/Scenarios/DynamicMenuBar.cs +++ b/UICatalog/Scenarios/DynamicMenuBar.cs @@ -13,10 +13,10 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Top Level Windows")] [ScenarioCategory ("Menus")] public class DynamicMenuBar : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); - Application.Top.Add (new DynamicMenuBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}")); + Application.Top.Add (new DynamicMenuBarSample ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}")); } public class DynamicMenuItemList { diff --git a/UICatalog/Scenarios/DynamicStatusBar.cs b/UICatalog/Scenarios/DynamicStatusBar.cs index 6d5d1d73e..2d586da11 100644 --- a/UICatalog/Scenarios/DynamicStatusBar.cs +++ b/UICatalog/Scenarios/DynamicStatusBar.cs @@ -12,10 +12,10 @@ namespace UICatalog.Scenarios { [ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")] [ScenarioCategory ("Top Level Windows")] public class DynamicStatusBar : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); - Application.Top.Add (new DynamicStatusBarSample ($"CTRL-Q to Close - Scenario: {GetName ()}")); + Application.Top.Add (new DynamicStatusBarSample ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}")); } public class DynamicStatusItemList { diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs index 22b5ce678..15a147ff9 100644 --- a/UICatalog/Scenarios/Editor.cs +++ b/UICatalog/Scenarios/Editor.cs @@ -6,6 +6,7 @@ using System.Linq; using System.Text.RegularExpressions; using System.Threading; using System.Globalization; +using Terminal.Gui.Configuration; namespace UICatalog.Scenarios { [ScenarioMetadata (Name: "Editor", Description: "A Text Editor using the TextView control.")] @@ -32,17 +33,19 @@ namespace UICatalog.Scenarios { private bool _forceMinimumPosToZero = true; private List _cultureInfos; - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); _cultureInfos = Application.SupportedCultures; - + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); + Win = new Window (_fileName ?? "Untitled") { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (), - ColorScheme = colorScheme, + ColorScheme = Colors.ColorSchemes [TopLevelColorScheme], }; Application.Top.Add (Win); @@ -121,7 +124,7 @@ namespace UICatalog.Scenarios { new StatusItem(Key.F2, "~F2~ Open", () => Open()), new StatusItem(Key.F3, "~F3~ Save", () => Save()), new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null) }); Application.Top.Add (statusBar); @@ -174,9 +177,9 @@ namespace UICatalog.Scenarios { Win.KeyPress += (s, e) => { var keys = ShortcutHelper.GetModifiersKey (e.KeyEvent); if (_winDialog != null && (e.KeyEvent.Key == Key.Esc - || e.KeyEvent.Key == (Key.Q | Key.CtrlMask))) { + || e.KeyEvent.Key == Application.QuitKey)) { DisposeWinDialog (); - } else if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) { + } else if (e.KeyEvent.Key == Application.QuitKey) { Quit (); e.Handled = true; } else if (_winDialog != null && keys == (Key.Tab | Key.CtrlMask)) { diff --git a/UICatalog/Scenarios/Generic.cs b/UICatalog/Scenarios/Generic.cs index 9b787c468..c0544de04 100644 --- a/UICatalog/Scenarios/Generic.cs +++ b/UICatalog/Scenarios/Generic.cs @@ -5,19 +5,20 @@ namespace UICatalog.Scenarios { [ScenarioMetadata (Name: "Generic", Description: "Generic sample - A template for creating new Scenarios")] [ScenarioCategory ("Controls")] public class MyScenario : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { // The base `Scenario.Init` implementation: // - Calls `Application.Init ()` // - Adds a full-screen Window to Application.Top with a title // that reads "Press to Quit". Access this Window with `this.Win`. - // - Sets the ColorScheme property of `this.Win` to `colorScheme`. - // To overrride this, implement an override of `Init`. - base.Init (colorScheme); - + // - Sets the Theme & the ColorScheme property of `this.Win` to `colorScheme`. + // To override this, implement an override of `Init`. + base.Init (); // A common, alternate, implementation where `this.Win` is not used: - // Application.Init (); - // Application.Top.ColorScheme = colorScheme; + //Application.Init (); + //ConfigurationManager.Themes.Theme = Theme; + //ConfigurationManager.Apply (); + //Application.Top.ColorScheme = Colors.ColorSchemes [TopLevelColorScheme]; } public override void Setup () diff --git a/UICatalog/Scenarios/GraphViewExample.cs b/UICatalog/Scenarios/GraphViewExample.cs index e8f806046..300250545 100644 --- a/UICatalog/Scenarios/GraphViewExample.cs +++ b/UICatalog/Scenarios/GraphViewExample.cs @@ -89,7 +89,7 @@ namespace UICatalog.Scenarios { var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), new StatusItem(Key.CtrlMask | Key.G, "~^G~ Next", ()=>graphs[currentGraph++%graphs.Length]()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/HexEditor.cs b/UICatalog/Scenarios/HexEditor.cs index 62f39cdef..841cf20e5 100644 --- a/UICatalog/Scenarios/HexEditor.cs +++ b/UICatalog/Scenarios/HexEditor.cs @@ -57,7 +57,7 @@ namespace UICatalog.Scenarios { statusBar = new StatusBar (new StatusItem [] { new StatusItem(Key.F2, "~F2~ Open", () => Open()), new StatusItem(Key.F3, "~F3~ Save", () => Save()), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), siPositionChanged = new StatusItem(Key.Null, $"Position: {_hexView.Position} Line: {_hexView.CursorPosition.Y} Col: {_hexView.CursorPosition.X} Line length: {_hexView.BytesPerLine}", () => {}) }); diff --git a/UICatalog/Scenarios/InteractiveTree.cs b/UICatalog/Scenarios/InteractiveTree.cs index e209f9d00..cf581b725 100644 --- a/UICatalog/Scenarios/InteractiveTree.cs +++ b/UICatalog/Scenarios/InteractiveTree.cs @@ -40,7 +40,7 @@ namespace UICatalog.Scenarios { Win.Add (treeView); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), new StatusItem(Key.CtrlMask | Key.C, "~^C~ Add Child", () => AddChildNode()), new StatusItem(Key.CtrlMask | Key.T, "~^T~ Add Root", () => AddRootNode()), new StatusItem(Key.CtrlMask | Key.R, "~^R~ Rename Node", () => RenameNode()), diff --git a/UICatalog/Scenarios/Keys.cs b/UICatalog/Scenarios/Keys.cs index 3278b146d..796103c2c 100644 --- a/UICatalog/Scenarios/Keys.cs +++ b/UICatalog/Scenarios/Keys.cs @@ -1,6 +1,7 @@ using NStack; using System.Collections.Generic; using Terminal.Gui; +using Terminal.Gui.Configuration; namespace UICatalog.Scenarios { [ScenarioMetadata (Name: "Keys", Description: "Shows how to handle keyboard input")] @@ -48,16 +49,18 @@ namespace UICatalog.Scenarios { } } - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); + ConfigurationManager.Themes.Theme = Theme; + ConfigurationManager.Apply (); - Win = new TestWindow ($"CTRL-Q to Close - Scenario: {GetName ()}") { + Win = new TestWindow ($"{Application.QuitKey} to Quit - Scenario: {GetName ()}") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill (), - ColorScheme = colorScheme, + ColorScheme = Colors.ColorSchemes [TopLevelColorScheme], }; Application.Top.Add (Win); } diff --git a/UICatalog/Scenarios/LineViewExample.cs b/UICatalog/Scenarios/LineViewExample.cs index cf537d952..a4e4e7ac4 100644 --- a/UICatalog/Scenarios/LineViewExample.cs +++ b/UICatalog/Scenarios/LineViewExample.cs @@ -92,7 +92,7 @@ namespace UICatalog.Scenarios { Win.Add (verticalArrow); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()) + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/MultiColouredTable.cs b/UICatalog/Scenarios/MultiColouredTable.cs index 88788ae69..86377256c 100644 --- a/UICatalog/Scenarios/MultiColouredTable.cs +++ b/UICatalog/Scenarios/MultiColouredTable.cs @@ -33,7 +33,7 @@ namespace UICatalog.Scenarios { Application.Top.Add (menu); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/Notepad.cs b/UICatalog/Scenarios/Notepad.cs index a55643f45..df73e4448 100644 --- a/UICatalog/Scenarios/Notepad.cs +++ b/UICatalog/Scenarios/Notepad.cs @@ -16,7 +16,7 @@ namespace UICatalog.Scenarios { private StatusItem lenStatusItem; // Don't create a Window, just return the top-level view - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); Application.Top.ColorScheme = Colors.Base; @@ -55,7 +55,7 @@ namespace UICatalog.Scenarios { lenStatusItem = new StatusItem (Key.CharMask, "Len: ", null); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), // These shortcut keys don't seem to work correctly in linux //new StatusItem(Key.CtrlMask | Key.N, "~^O~ Open", () => Open()), diff --git a/UICatalog/Scenarios/RunTExample.cs b/UICatalog/Scenarios/RunTExample.cs index 8a7c9c3ba..7fe96b1e5 100644 --- a/UICatalog/Scenarios/RunTExample.cs +++ b/UICatalog/Scenarios/RunTExample.cs @@ -19,7 +19,7 @@ namespace UICatalog.Scenarios { public ExampleWindow () { - Title = "Example App (Ctrl+Q to quit)"; + Title = $"Example App ({Application.QuitKey} to quit)"; // Create input components and labels var usernameLabel = new Label () { diff --git a/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs b/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs index 5c6ed9859..e57d15f2c 100644 --- a/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs +++ b/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs @@ -16,7 +16,7 @@ namespace UICatalog.Scenarios { private Window _win; private string _lastRunesUsed; - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); diff --git a/UICatalog/Scenarios/SingleBackgroundWorker.cs b/UICatalog/Scenarios/SingleBackgroundWorker.cs index fd27f2684..4a52dbdec 100644 --- a/UICatalog/Scenarios/SingleBackgroundWorker.cs +++ b/UICatalog/Scenarios/SingleBackgroundWorker.cs @@ -36,7 +36,7 @@ namespace UICatalog.Scenarios { Add (menu); var statusBar = new StatusBar (new [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Application.RequestStop()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Application.RequestStop()), new StatusItem(Key.CtrlMask | Key.P, "~^R~ Run Worker", () => RunWorker()) }); Add (statusBar); @@ -136,7 +136,7 @@ namespace UICatalog.Scenarios { top.KeyPress += (s,e) => { // Prevents Ctrl+Q from closing this. // Only Ctrl+C is allowed. - if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) { + if (e.KeyEvent.Key == Application.QuitKey) { e.Handled = true; } }; diff --git a/UICatalog/Scenarios/SyntaxHighlighting.cs b/UICatalog/Scenarios/SyntaxHighlighting.cs index 1885ff6ff..0c61ccee4 100644 --- a/UICatalog/Scenarios/SyntaxHighlighting.cs +++ b/UICatalog/Scenarios/SyntaxHighlighting.cs @@ -45,7 +45,7 @@ namespace UICatalog.Scenarios { Win.Add (textView); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); diff --git a/UICatalog/Scenarios/TabViewExample.cs b/UICatalog/Scenarios/TabViewExample.cs index 0936cdc00..2a321563a 100644 --- a/UICatalog/Scenarios/TabViewExample.cs +++ b/UICatalog/Scenarios/TabViewExample.cs @@ -109,7 +109,7 @@ namespace UICatalog.Scenarios { Win.Add (frameBelow); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); } diff --git a/UICatalog/Scenarios/TableEditor.cs b/UICatalog/Scenarios/TableEditor.cs index d7af084cb..ac78a7d49 100644 --- a/UICatalog/Scenarios/TableEditor.cs +++ b/UICatalog/Scenarios/TableEditor.cs @@ -86,7 +86,7 @@ namespace UICatalog.Scenarios { new StatusItem(Key.F2, "~F2~ OpenExample", () => OpenExample(true)), new StatusItem(Key.F3, "~F3~ CloseExample", () => CloseExample()), new StatusItem(Key.F4, "~F4~ OpenSimple", () => OpenSimple(true)), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/TextViewAutocompletePopup.cs b/UICatalog/Scenarios/TextViewAutocompletePopup.cs index a01ea2c0b..ed1c0725f 100644 --- a/UICatalog/Scenarios/TextViewAutocompletePopup.cs +++ b/UICatalog/Scenarios/TextViewAutocompletePopup.cs @@ -85,7 +85,7 @@ namespace UICatalog.Scenarios { miWrap.Checked = textViewTopLeft.WordWrap; var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), siMultiline = new StatusItem(Key.Null, "", null), siWrap = new StatusItem(Key.Null, "", null) }); diff --git a/UICatalog/Scenarios/TileViewExperiment.cs b/UICatalog/Scenarios/TileViewExperiment.cs index 6430ce9a6..bdae434b4 100644 --- a/UICatalog/Scenarios/TileViewExperiment.cs +++ b/UICatalog/Scenarios/TileViewExperiment.cs @@ -10,7 +10,7 @@ namespace UICatalog.Scenarios { public class TileViewExperiment : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); } diff --git a/UICatalog/Scenarios/TreeUseCases.cs b/UICatalog/Scenarios/TreeUseCases.cs index aa1626c8d..eeba69ee3 100644 --- a/UICatalog/Scenarios/TreeUseCases.cs +++ b/UICatalog/Scenarios/TreeUseCases.cs @@ -34,7 +34,7 @@ namespace UICatalog.Scenarios { Application.Top.Add (menu); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Quit()), }); Application.Top.Add (statusBar); diff --git a/UICatalog/Scenarios/TreeViewFileSystem.cs b/UICatalog/Scenarios/TreeViewFileSystem.cs index d72c7b883..c2c0bc747 100644 --- a/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -41,7 +41,7 @@ namespace UICatalog.Scenarios { var menu = new MenuBar (new MenuBarItem [] { new MenuBarItem ("_File", new MenuItem [] { - new MenuItem ("_Quit", "CTRL-Q", () => Quit()), + new MenuItem ("_Quit", $"{Application.QuitKey}", () => Quit()), }), new MenuBarItem ("_View", new MenuItem [] { miFullPaths = new MenuItem ("_Full Paths", "", () => SetFullName()){Checked = false, CheckType = MenuItemCheckStyle.Checked}, diff --git a/UICatalog/Scenarios/Unicode.cs b/UICatalog/Scenarios/Unicode.cs index 566c0085d..a89d7dc81 100644 --- a/UICatalog/Scenarios/Unicode.cs +++ b/UICatalog/Scenarios/Unicode.cs @@ -37,7 +37,7 @@ namespace UICatalog.Scenarios { Application.Top.Add (menu); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Выход", () => Application.RequestStop()), + new StatusItem(Application.QuitKey, $"{Application.QuitKey} Выход", () => Application.RequestStop()), new StatusItem (Key.Unknown, "~F2~ Создать", null), new StatusItem(Key.Unknown, "~F3~ Со_хранить", null), }); diff --git a/UICatalog/Scenarios/WizardAsView.cs b/UICatalog/Scenarios/WizardAsView.cs index 9727b8d51..bc86ae19d 100644 --- a/UICatalog/Scenarios/WizardAsView.cs +++ b/UICatalog/Scenarios/WizardAsView.cs @@ -10,7 +10,7 @@ namespace UICatalog.Scenarios { [ScenarioCategory ("Wizards")] public class WizardAsView : Scenario { - public override void Init (ColorScheme colorScheme) + public override void Init () { Application.Init (); diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 8c74ca7f9..79dd85b62 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -14,6 +14,8 @@ using Terminal.Gui.Configuration; using static Terminal.Gui.Configuration.ConfigurationManager; using System.Text.Json.Serialization; +#nullable enable + /// /// UI Catalog is a comprehensive sample library for Terminal.Gui. It provides a simple UI for adding to the catalog of scenarios. /// @@ -84,7 +86,9 @@ namespace UICatalog { _selectedScenario = (Scenario)Activator.CreateInstance (_scenarios [item].GetType ()); Application.UseSystemConsole = _useSystemConsole; Application.Init (); - _selectedScenario.Init (Colors.ColorSchemes [_topLevelColorScheme == null ? "Base" : _topLevelColorScheme]); + _selectedScenario.Theme = _cachedTheme; + _selectedScenario.TopLevelColorScheme = _topLevelColorScheme; + _selectedScenario.Init (); _selectedScenario.Setup (); _selectedScenario.Run (); _selectedScenario.Dispose (); @@ -111,7 +115,11 @@ namespace UICatalog { Scenario scenario; while ((scenario = RunUICatalogTopLevel ()) != null) { VerifyObjectsWereDisposed (); - scenario.Init (Colors.ColorSchemes [_topLevelColorScheme]); + ConfigurationManager.Themes.Theme = _cachedTheme; + ConfigurationManager.Apply (); + scenario.Theme = _cachedTheme; + scenario.TopLevelColorScheme = _topLevelColorScheme; + scenario.Init (); scenario.Setup (); scenario.Run (); scenario.Dispose (); @@ -193,10 +201,17 @@ namespace UICatalog { Application.UseSystemConsole = _useSystemConsole; // Run UI Catalog UI. When it exits, if _selectedScenario is != null then - // a Scenario was selected. Otherwise, the user wants to exit UI Catalog. + // a Scenario was selected. Otherwise, the user wants to quit UI Catalog. Application.Init (); - Application.EnableConsoleScrolling = _enableConsoleScrolling; + if (_cachedTheme is null) { + _cachedTheme = ConfigurationManager.Themes.Theme; + } else { + ConfigurationManager.Themes.Theme = _cachedTheme; + ConfigurationManager.Apply (); + } + + //Application.EnableConsoleScrolling = _enableConsoleScrolling; Application.Run (); Application.Shutdown (); @@ -212,6 +227,8 @@ namespace UICatalog { // main app UI can be restored to previous state static int _cachedScenarioIndex = 0; static int _cachedCategoryIndex = 0; + static string? _cachedTheme; + static StringBuilder _aboutMessage; // If set, holds the scenario the user selected @@ -575,9 +592,9 @@ namespace UICatalog { Shortcut = Key.AltMask + theme.Key [0] }; item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = theme.Key == ConfigurationManager.Themes.Theme; + item.Checked = theme.Key == _cachedTheme; // ConfigurationManager.Themes.Theme; item.Action += () => { - ConfigurationManager.Themes.Theme = theme.Key; + ConfigurationManager.Themes.Theme = _cachedTheme = theme.Key; ConfigurationManager.Apply (); }; menuItems.Add (item); diff --git a/UnitTests/Application/ApplicationTests.cs b/UnitTests/Application/ApplicationTests.cs index 7aec50b33..7bc978222 100644 --- a/UnitTests/Application/ApplicationTests.cs +++ b/UnitTests/Application/ApplicationTests.cs @@ -560,16 +560,16 @@ namespace Terminal.Gui.ApplicationTests { var input = "Tests"; // Put a control-q in at the end - Console.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true)); + FakeConsole.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 (c, (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); + FakeConsole.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)); + FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false)); } } - int stackSize = Console.MockKeyPresses.Count; + int stackSize = FakeConsole.MockKeyPresses.Count; int iterations = 0; Application.Iteration = () => { diff --git a/UnitTests/Drivers/ConsoleDriverTests.cs b/UnitTests/Drivers/ConsoleDriverTests.cs index 804ae83d7..fc2ca85bb 100644 --- a/UnitTests/Drivers/ConsoleDriverTests.cs +++ b/UnitTests/Drivers/ConsoleDriverTests.cs @@ -142,6 +142,63 @@ namespace Terminal.Gui.DriverTests { Application.Shutdown (); } + //[Theory] + //[InlineData (typeof (FakeDriver))] + //public void FakeDriver_MockKeyPresses_Press_AfterTimeOut (Type driverType) + //{ + // var driver = (ConsoleDriver)Activator.CreateInstance (driverType); + // Application.Init (driver); + + // // Simulating pressing of QuitKey after a short period of time + // uint quitTime = 100; + // Func closeCallback = (MainLoop loop) => { + // // Prove the scenario is using Application.QuitKey correctly + // output.WriteLine ($" {quitTime}ms elapsed; Simulating keypresses..."); + // FakeConsole.PushMockKeyPress (Key.F); + // FakeConsole.PushMockKeyPress (Key.U); + // FakeConsole.PushMockKeyPress (Key.C); + // FakeConsole.PushMockKeyPress (Key.K); + // return false; + // }; + // output.WriteLine ($"Add timeout to simulate key presses after {quitTime}ms"); + // _ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (quitTime), closeCallback); + + // // If Top doesn't quit within abortTime * 5 (500ms), this will force it + // uint abortTime = quitTime * 5; + // Func forceCloseCallback = (MainLoop loop) => { + // Application.RequestStop (); + // Assert.Fail ($" failed to Quit after {abortTime}ms. Force quit."); + // return false; + // }; + // output.WriteLine ($"Add timeout to force quit after {abortTime}ms"); + // _ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback); + + + // Key key = Key.Unknown; + + // Application.Top.KeyPress += (e) => { + // key = e.KeyEvent.Key; + // output.WriteLine ($" Application.Top.KeyPress: {key}"); + // e.Handled = true; + + // }; + + // int iterations = 0; + // Application.Iteration += () => { + // output.WriteLine ($" iteration {++iterations}"); + + // if (Console.MockKeyPresses.Count == 0) { + // output.WriteLine ($" No more MockKeyPresses; RequestStop"); + // Application.RequestStop (); + // } + // }; + + // Application.Run (); + + // // Shutdown must be called to safely clean up Application if Init has been called + // Application.Shutdown (); + //} + [Theory] [InlineData (typeof (FakeDriver))] public void TerminalResized_Simulation (Type driverType) @@ -319,7 +376,7 @@ namespace Terminal.Gui.DriverTests { Application.Shutdown (); } - + [Fact, AutoInitShutdown] public void AddRune_On_Clip_Left_Or_Right_Replace_Previous_Or_Next_Wide_Rune_With_Space () { @@ -441,7 +498,7 @@ namespace Terminal.Gui.DriverTests { } private static object packetLock = new object (); - + /// /// Sometimes when using remote tools EventKeyRecord sends 'virtual keystrokes'. /// These are indicated with the wVirtualKeyCode of 231. When we see this code diff --git a/UnitTests/Drivers/KeyTests.cs b/UnitTests/Drivers/KeyTests.cs index 3116c8628..ef55d5916 100644 --- a/UnitTests/Drivers/KeyTests.cs +++ b/UnitTests/Drivers/KeyTests.cs @@ -123,7 +123,7 @@ namespace Terminal.Gui.DriverTests { Assert.Equal ("Y, CtrlMask", key.ToString ()); // This will be well compared, because the Key.CtrlMask have a high value. - Assert.False (key == (Key.Q | Key.CtrlMask)); + Assert.False (key == Application.QuitKey); switch (key) { case Key.Q | Key.CtrlMask: // Never goes here. diff --git a/UnitTests/Menus/ContextMenuTests.cs b/UnitTests/Menus/ContextMenuTests.cs index 419aaa9e1..9f1055cf9 100644 --- a/UnitTests/Menus/ContextMenuTests.cs +++ b/UnitTests/Menus/ContextMenuTests.cs @@ -640,8 +640,8 @@ namespace Terminal.Gui.MenuTests { win.Add (label, tf); var statusBar = new StatusBar (new StatusItem [] { - new StatusItem(Key.F1, "~F1~ Help", null), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", null) + new StatusItem (Key.F1, "~F1~ Help", null), + new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null) }); Application.Top.Add (menu, win, statusBar); diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index 12763a163..90812ca09 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -106,7 +106,7 @@ namespace Terminal.Gui.TopLevelTests { var statusBar = new StatusBar (new [] { new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Top2", () => Application.Run (Top2 ())), - new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Application.RequestStop()) + new StatusItem(Application.QuitKey, $"{Application.QuitKey} to Quit", () => Application.RequestStop()) }); top.Add (statusBar); diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index dc484eca6..ae1d196d3 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -1,10 +1,12 @@ using NStack; using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Reflection; using Terminal.Gui; using UICatalog; +using UICatalog.Scenarios; using Xunit; using Xunit.Abstractions; @@ -25,14 +27,17 @@ namespace UICatalog.Tests { int CreateInput (string input) { - // Put a control-q in at the end - FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo ('q', ConsoleKey.Q, shift: false, alt: false, control: true)); + FakeConsole.MockKeyPresses.Clear (); + // Put a QuitKey in at the end + FakeConsole.PushMockKeyPress (Application.QuitKey); foreach (var c in input.Reverse ()) { + Key key = Key.Unknown; if (char.IsLetter (c)) { - FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (char.ToLower (c), (ConsoleKey)char.ToUpper (c), shift: char.IsUpper (c), alt: false, control: false)); + key = (Key)char.ToUpper (c) | (char.IsUpper (c) ? Key.ShiftMask : (Key)0); } else { - FakeConsole.MockKeyPresses.Push (new ConsoleKeyInfo (c, (ConsoleKey)c, shift: false, alt: false, control: false)); + key = (Key)c; } + FakeConsole.PushMockKeyPress (key); } return FakeConsole.MockKeyPresses.Count; } @@ -53,24 +58,47 @@ namespace UICatalog.Tests { Assert.NotEmpty (scenarios); foreach (var scenario in scenarios) { - - output.WriteLine ($"Running Scenario '{scenario}'"); - - Func closeCallback = (MainLoop loop) => { - Application.RequestStop (); - return false; - }; - + output.WriteLine ($"Running Scenario '{scenario.GetName ()}'"); + Application.Init (new FakeDriver ()); - // Close after a short period of time - var token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (100), closeCallback); + // Press QuitKey + Assert.Empty (FakeConsole.MockKeyPresses); + // BUGBUG: For some reason ReadKey is not returning the QuitKey for some Scenarios + // by adding this Space it seems to work. + FakeConsole.PushMockKeyPress (Key.Space); + FakeConsole.PushMockKeyPress (Application.QuitKey); - scenario.Init (Colors.Base); + // The only key we care about is the QuitKey + Application.Top.KeyPress += (View.KeyEventEventArgs args) => { + output.WriteLine ($" Keypress: {args.KeyEvent.Key}"); + Assert.Equal (Application.QuitKey, args.KeyEvent.Key); + args.Handled = false; + }; + + uint abortTime = 500; + // If the scenario doesn't close within 500ms, this will force it to quit + Func forceCloseCallback = (MainLoop loop) => { + Application.RequestStop (); + Assert.Fail ($"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit."); + return false; + }; + //output.WriteLine ($" Add timeout to force quit after {abortTime}ms"); + _ = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (abortTime), forceCloseCallback); + + int iterations = 0; + Application.Iteration += () => { + //output.WriteLine ($" iteration {++iterations}"); + if (FakeConsole.MockKeyPresses.Count == 0) { + Application.RequestStop (); + //Assert.Fail ($"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey}. Force quit."); + } + }; + + scenario.Init (); scenario.Setup (); scenario.Run (); - - scenario.Dispose(); + scenario.Dispose (); Application.Shutdown (); #if DEBUG_IDISPOSABLE @@ -96,26 +124,30 @@ namespace UICatalog.Tests { var item = scenarios.FindIndex (s => s.GetName ().Equals ("Generic", StringComparison.OrdinalIgnoreCase)); var generic = scenarios [item]; - // Setup some fake keypresses - // Passing empty string will cause just a ctrl-q to be fired - int stackSize = CreateInput (""); Application.Init (new FakeDriver ()); - Application.QuitKey = Key.CtrlMask | Key.Q; // Config manager may have set this to a different key + // BUGBUG: For some reason ReadKey is not + // returning the QuitKey for some Scenarios + // by adding this Space it seems to work. + FakeConsole.PushMockKeyPress (Key.Space); + FakeConsole.PushMockKeyPress (Application.QuitKey); int iterations = 0; Application.Iteration = () => { iterations++; + output.WriteLine ($"'Generic' iteration {iterations}"); // Stop if we run out of control... if (iterations == 10) { + output.WriteLine ($"'Generic' had to be force quit!"); Application.RequestStop (); } }; - var ms = 1000; + var ms = 100; var abortCount = 0; Func abortCallback = (MainLoop loop) => { abortCount++; + output.WriteLine ($"'Generic' abortCount {abortCount}"); Application.RequestStop (); return false; }; @@ -125,22 +157,15 @@ namespace UICatalog.Tests { Assert.Equal (Key.CtrlMask | Key.Q, args.KeyEvent.Key); }; - generic.Init (Colors.Base); + generic.Init (); generic.Setup (); - // There is no need to call Application.Begin because Init already creates the Application.Top - // If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run. - //var rs = Application.Begin (Application.Top); generic.Run (); - //Application.End (rs); - Assert.Equal (0, abortCount); // # of key up events should match # of iterations Assert.Equal (1, iterations); - // Using variable in the left side of Assert.Equal/NotEqual give error. Must be used literals values. - //Assert.Equal (stackSize, iterations); - generic.Dispose(); + generic.Dispose (); // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); diff --git a/UnitTests/Views/StatusBarTests.cs b/UnitTests/Views/StatusBarTests.cs index c90f9105f..feb2a0f05 100644 --- a/UnitTests/Views/StatusBarTests.cs +++ b/UnitTests/Views/StatusBarTests.cs @@ -14,11 +14,11 @@ namespace Terminal.Gui.ViewTests { [Fact] public void StatusItem_Constructor () { - var si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null); + var si = new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", null); Assert.Equal (Key.CtrlMask | Key.Q, si.Shortcut); - Assert.Equal ("~^Q~ Quit", si.Title); + Assert.Equal ($"{Application.QuitKey} to Quit", si.Title); Assert.Null (si.Action); - si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { }); + si = new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", () => { }); Assert.NotNull (si.Action); } @@ -72,7 +72,7 @@ namespace Terminal.Gui.ViewTests { public void Run_Action_With_Key_And_Mouse () { var msg = ""; - var sb = new StatusBar (new StatusItem [] { new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => msg = "Quiting...") }); + var sb = new StatusBar (new StatusItem [] { new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit", () => msg = "Quiting...") }); Application.Top.Add (sb); var iteration = 0; @@ -101,26 +101,31 @@ namespace Terminal.Gui.ViewTests { public void Redraw_Output () { var sb = new StatusBar (new StatusItem [] { - new StatusItem (Key.CtrlMask | Key.Q, "~^O~ Open", null), - new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null) + new StatusItem (Key.CtrlMask | Key.O, "~^O~ Open", null), + new StatusItem (Application.QuitKey, $"{Application.QuitKey} to Quit!", null) }); Application.Top.Add (sb); sb.Redraw (sb.Bounds); string expected = @$" -^O Open {Application.Driver.VLine} ^Q Quit +^O Open {Application.Driver.VLine} Q, CtrlMask to Quit! "; - TestHelpers.AssertDriverContentsAre (expected, output); - - sb = new StatusBar (new StatusItem [] { - new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-O~ Open", null), + } + + [Fact] + [AutoInitShutdown] + public void Redraw_Output_CTRLQ () + { + var sb = new StatusBar (new StatusItem [] { + new StatusItem (Key.CtrlMask | Key.O, "~CTRL-O~ Open", null), new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-Q~ Quit", null) }); + Application.Top.Add (sb); sb.Redraw (sb.Bounds); - expected = @$" + string expected = @$" CTRL-O Open {Application.Driver.VLine} CTRL-Q Quit ";