From bcc31e0da03addfbdb382c7364808cfcb59b0af8 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 17 Nov 2020 21:57:45 +0000 Subject: [PATCH 1/3] Trying fixing #518. Almost functions work on both Windows and Unix with the NetDriver. --- Example/Example.csproj | 2 +- .../CursesDriver/CursesDriver.cs | 4 +- .../ConsoleDrivers/FakeDriver/FakeDriver.cs | 86 +---- .../ConsoleDrivers/FakeDriver/FakeMainLoop.cs | 89 ++++++ Terminal.Gui/ConsoleDrivers/NetDriver.cs | 299 ++++++++++++------ Terminal.Gui/ConsoleDrivers/WindowsDriver.cs | 1 + Terminal.Gui/Core/Application.cs | 2 +- Terminal.Gui/Core/ConsoleDriver.cs | 5 + Terminal.Gui/Core/View.cs | 4 + UICatalog/Scenarios/Threading.cs | 1 + UnitTests/ApplicationTests.cs | 4 +- UnitTests/ConsoleDriverTests.cs | 6 +- UnitTests/DimTests.cs | 8 +- UnitTests/MainLoopTests.cs | 32 +- UnitTests/PosTests.cs | 8 +- UnitTests/ScenarioTests.cs | 4 +- UnitTests/ViewTests.cs | 16 +- 17 files changed, 354 insertions(+), 217 deletions(-) create mode 100644 Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs diff --git a/Example/Example.csproj b/Example/Example.csproj index 7ef1a34737..a3d79ade27 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.1 + net5.0 diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs index ee06284e9b..3551b06996 100644 --- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs @@ -17,9 +17,9 @@ namespace Terminal.Gui { /// This is the Curses driver for the gui.cs/Terminal framework. /// internal class CursesDriver : ConsoleDriver { -#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member public override int Cols => Curses.Cols; public override int Rows => Curses.Lines; + public override int Top => 0; // Current row, and current col, tracked by Move/AddRune only int ccol, crow; @@ -907,7 +907,5 @@ static public bool Suspend () killpg (0, signal); return true; } -#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } - } diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs index 7fc3e76a38..7566d6928d 100644 --- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs @@ -15,15 +15,11 @@ namespace Terminal.Gui { /// Implements a mock ConsoleDriver for unit testing /// public class FakeDriver : ConsoleDriver { +#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member int cols, rows; - /// - /// - /// public override int Cols => cols; - /// - /// - /// public override int Rows => rows; + public override int Top => 0; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] contents; @@ -49,9 +45,6 @@ void UpdateOffscreen () static bool sync = false; - /// - /// - /// public FakeDriver () { cols = FakeConsole.WindowWidth; @@ -62,11 +55,6 @@ public FakeDriver () bool needMove; // Current row, and current col, tracked by Move/AddCh only int ccol, crow; - /// - /// - /// - /// - /// public override void Move (int col, int row) { ccol = col; @@ -84,10 +72,6 @@ public override void Move (int col, int row) } - /// - /// - /// - /// public override void AddRune (Rune rune) { rune = MakePrintable (rune); @@ -113,19 +97,12 @@ public override void AddRune (Rune rune) UpdateScreen (); } - /// - /// - /// - /// public override void AddStr (ustring str) { foreach (var rune in str) AddRune (rune); } - /// - /// - /// public override void End () { FakeConsole.ResetColor (); @@ -138,10 +115,6 @@ static Attribute MakeColor (ConsoleColor f, ConsoleColor b) return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) }; } - /// - /// - /// - /// public override void Init (Action terminalResized) { Colors.TopLevel = new ColorScheme (); @@ -185,12 +158,6 @@ public override void Init (Action terminalResized) //MockConsole.Clear (); } - /// - /// - /// - /// - /// - /// public override Attribute MakeAttribute (Color fore, Color back) { return MakeColor ((ConsoleColor)fore, (ConsoleColor)back); @@ -211,9 +178,6 @@ void SetColor (int color) } } - /// - /// - /// public override void UpdateScreen () { int rows = Rows; @@ -233,9 +197,6 @@ public override void UpdateScreen () } } - /// - /// - /// public override void Refresh () { int rows = Rows; @@ -267,40 +228,24 @@ public override void Refresh () FakeConsole.CursorLeft = savedCol; } - /// - /// - /// public override void UpdateCursor () { // } - /// - /// - /// public override void StartReportingMouseMoves () { } - /// - /// - /// public override void StopReportingMouseMoves () { } - /// - /// - /// public override void Suspend () { } int currentAttribute; - /// - /// - /// - /// public override void SetAttribute (Attribute c) { currentAttribute = c.value; @@ -417,18 +362,10 @@ private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key) return keyMod != Key.Null ? keyMod | key : key; } - /// - /// - /// - /// - /// - /// - /// - /// public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called - (mainLoop.Driver as NetMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) { + (mainLoop.Driver as FakeMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) { var map = MapKey (consoleKey); if (map == (Key)0xffffffff) return; @@ -452,38 +389,23 @@ public override void PrepareToRun (MainLoop mainLoop, Action keyHandle }; } - /// - /// - /// - /// - /// public override void SetColors (ConsoleColor foreground, ConsoleColor background) { throw new NotImplementedException (); } - /// - /// - /// - /// - /// public override void SetColors (short foregroundColorId, short backgroundColorId) { throw new NotImplementedException (); } - /// - /// - /// public override void CookMouse () { } - /// - /// - /// public override void UncookMouse () { } +#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member } } \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs new file mode 100644 index 0000000000..42c9ea1e0f --- /dev/null +++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeMainLoop.cs @@ -0,0 +1,89 @@ +using System; +using System.Threading; + +namespace Terminal.Gui { + /// + /// Mainloop intended to be used with the .NET System.Console API, and can + /// be used on Windows and Unix, it is cross platform but lacks things like + /// file descriptor monitoring. + /// + /// + /// This implementation is used for FakeDriver. + /// + public class FakeMainLoop : IMainLoopDriver { + AutoResetEvent keyReady = new AutoResetEvent (false); + AutoResetEvent waitForProbe = new AutoResetEvent (false); + ConsoleKeyInfo? keyResult = null; + MainLoop mainLoop; + Func consoleKeyReaderFn = null; + + /// + /// Invoked when a Key is pressed. + /// + public Action KeyPressed; + + /// + /// Initializes the class. + /// + /// + /// Passing a consoleKeyReaderfn is provided to support unit test scenarios. + /// + /// The method to be called to get a key from the console. + public FakeMainLoop (Func consoleKeyReaderFn = null) + { + if (consoleKeyReaderFn == null) { + throw new ArgumentNullException ("key reader function must be provided."); + } + this.consoleKeyReaderFn = consoleKeyReaderFn; + } + + void WindowsKeyReader () + { + while (true) { + waitForProbe.WaitOne (); + keyResult = consoleKeyReaderFn (); + keyReady.Set (); + } + } + + void IMainLoopDriver.Setup (MainLoop mainLoop) + { + this.mainLoop = mainLoop; + Thread readThread = new Thread (WindowsKeyReader); + readThread.Start (); + } + + void IMainLoopDriver.Wakeup () + { + } + + bool IMainLoopDriver.EventsPending (bool wait) + { + long now = DateTime.UtcNow.Ticks; + + int waitTimeout; + if (mainLoop.timeouts.Count > 0) { + waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); + if (waitTimeout < 0) + return true; + } else + waitTimeout = -1; + + if (!wait) + waitTimeout = 0; + + keyResult = null; + waitForProbe.Set (); + keyReady.WaitOne (waitTimeout); + return keyResult.HasValue; + } + + void IMainLoopDriver.MainIteration () + { + if (keyResult.HasValue) { + KeyPressed?.Invoke (keyResult.Value); + keyResult = null; + } + } + } +} \ No newline at end of file diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs index 418385edbb..ab36cf957e 100644 --- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs @@ -8,46 +8,46 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using System.Threading.Tasks; using NStack; namespace Terminal.Gui { internal class NetDriver : ConsoleDriver { - int cols, rows; + int cols, rows, top; public override int Cols => cols; public override int Rows => rows; + public override int Top => top; // The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag int [,,] contents; bool [] dirtyLine; + public NetDriver () + { + ResizeScreen (); + UpdateOffscreen (); + } + void UpdateOffscreen () { int cols = Cols; int rows = Rows; contents = new int [rows, cols, 3]; - for (int r = 0; r < rows; r++) { + dirtyLine = new bool [rows]; + for (int row = 0; row < rows; row++) { for (int c = 0; c < cols; c++) { - contents [r, c, 0] = ' '; - contents [r, c, 1] = MakeColor (ConsoleColor.Gray, ConsoleColor.Black); - contents [r, c, 2] = 0; + contents [row, c, 0] = ' '; + contents [row, c, 1] = (ushort)Colors.TopLevel.Normal; + contents [row, c, 2] = 0; + dirtyLine [row] = true; } } - dirtyLine = new bool [rows]; - for (int row = 0; row < rows; row++) - dirtyLine [row] = true; } static bool sync = false; - public NetDriver () - { - cols = Console.WindowWidth; - rows = Console.WindowHeight - 1; - UpdateOffscreen (); - } - bool needMove; // Current row, and current col, tracked by Move/AddCh only int ccol, crow; @@ -57,15 +57,18 @@ public override void Move (int col, int row) crow = row; if (Clip.Contains (col, row)) { - Console.CursorTop = row; - Console.CursorLeft = col; - needMove = false; + if (cols == Console.WindowWidth && rows == Console.WindowHeight) { + Console.SetCursorPosition (col, row); + needMove = false; + } } else { - Console.CursorTop = Clip.Y; - Console.CursorLeft = Clip.X; - needMove = true; + if (cols == Console.WindowWidth && rows == Console.WindowHeight) { + if (Console.WindowHeight > 0) { + Console.SetCursorPosition (Clip.X, Clip.Y); + } + needMove = true; + } } - } public override void AddRune (Rune rune) @@ -73,8 +76,9 @@ public override void AddRune (Rune rune) rune = MakePrintable (rune); if (Clip.Contains (ccol, crow)) { if (needMove) { - //Console.CursorLeft = ccol; - //Console.CursorTop = crow; + if (cols == Console.WindowWidth && rows == Console.WindowHeight) { + Console.SetCursorPosition (ccol, crow); + } needMove = false; } contents [crow, ccol, 0] = (int)(uint)rune; @@ -102,7 +106,14 @@ public override void AddStr (ustring str) public override void End () { Console.ResetColor (); - Console.Clear (); + Clear (); + } + + void Clear () + { + if (Rows > 0) { + Console.Clear (); + } } static Attribute MakeColor (ConsoleColor f, ConsoleColor b) @@ -111,15 +122,16 @@ static Attribute MakeColor (ConsoleColor f, ConsoleColor b) return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) }; } - public override void Init (Action terminalResized) { + TerminalResized = terminalResized; + Console.TreatControlCAsInput = true; + Colors.TopLevel = new ColorScheme (); Colors.Base = new ColorScheme (); Colors.Dialog = new ColorScheme (); Colors.Menu = new ColorScheme (); Colors.Error = new ColorScheme (); - Clip = new Rect (0, 0, Cols, Rows); Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black); Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan); @@ -151,7 +163,15 @@ public override void Init (Action terminalResized) Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray); Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red); Colors.Error.HotFocus = Colors.Error.HotNormal; - Console.Clear (); + Clear (); + } + + void ResizeScreen () + { + cols = Console.WindowWidth; + rows = Console.WindowHeight; + Clip = new Rect (0, 0, Cols, Rows); + top = Console.WindowTop; } public override Attribute MakeAttribute (Color fore, Color back) @@ -176,31 +196,14 @@ void SetColor (int color) public override void UpdateScreen () { - int rows = Rows; - int cols = Cols; - - Console.CursorTop = 0; - Console.CursorLeft = 0; - for (int row = 0; row < rows; row++) { - dirtyLine [row] = false; - for (int col = 0; col < cols; col++) { - contents [row, col, 2] = 0; - var color = contents [row, col, 1]; - if (color != redrawColor) - SetColor (color); - Console.Write ((char)contents [row, col, 0]); - } + if (Rows == 0) { + return; } - } - public override void Refresh () - { int rows = Rows; int cols = Cols; - var savedRow = Console.CursorTop; - var savedCol = Console.CursorLeft; - for (int row = 0; row < rows; row++) { + for (int row = top; row < rows; row++) { if (!dirtyLine [row]) continue; dirtyLine [row] = false; @@ -208,8 +211,9 @@ public override void Refresh () if (contents [row, col, 2] != 1) continue; - Console.CursorTop = row; - Console.CursorLeft = col; + if (Console.WindowHeight > 0) { + Console.SetCursorPosition (col, row); + } for (; col < cols && contents [row, col, 2] == 1; col++) { var color = contents [row, col, 1]; if (color != redrawColor) @@ -220,13 +224,29 @@ public override void Refresh () } } } - Console.CursorTop = savedRow; - Console.CursorLeft = savedCol; + + UpdateCursor (); + } + + public override void Refresh () + { + if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) { + ResizeScreen (); + UpdateOffscreen (); + TerminalResized.Invoke (); + } + + UpdateScreen (); } public override void UpdateCursor () { - // + // Prevents the exception of size changing during resizing. + try { + if (ccol > 0 && ccol < Console.WindowWidth && crow > 0 && crow < Console.WindowHeight) { + Console.SetCursorPosition (ccol, crow); + } + } catch (ArgumentOutOfRangeException) { } } public override void StartReportingMouseMoves () @@ -249,6 +269,7 @@ public override void SetAttribute (Attribute c) Key MapKey (ConsoleKeyInfo keyInfo) { + MapKeyModifiers (keyInfo); switch (keyInfo.Key) { case ConsoleKey.Escape: return Key.Esc; @@ -298,42 +319,75 @@ Key MapKey (ConsoleKeyInfo keyInfo) 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.A + delta); - if (keyInfo.Modifiers == ConsoleModifiers.Alt) + 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.Shift) - return (Key)((uint)Key.A + delta); - else - return (Key)((uint)'a' + delta); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) { + return (Key)((uint)Key.A + delta); + } + } + return (Key)((uint)keyInfo.KeyChar); } if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) { var delta = key - ConsoleKey.D0; - if (keyInfo.Modifiers == ConsoleModifiers.Alt) + if (keyInfo.Modifiers == ConsoleModifiers.Alt) { return (Key)(((uint)Key.AltMask) | ((uint)Key.D0 + delta)); - if (keyInfo.Modifiers == ConsoleModifiers.Shift) - return (Key)((uint)keyInfo.KeyChar); - return (Key)((uint)Key.D0 + delta); + } + if (keyInfo.Modifiers == ConsoleModifiers.Control) { + return (Key)(((uint)Key.CtrlMask) | ((uint)Key.D0 + delta)); + } + if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) { + if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) { + return (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 (Key)((uint)Key.F1 + delta); + } - return (Key)((int)Key.F1 + delta); + return (Key)((uint)Key.F1 + delta); + } + if (keyInfo.KeyChar != 0) { + return (Key)((uint)keyInfo.KeyChar); } + return (Key)(0xffffffff); } - KeyModifiers keyModifiers = new KeyModifiers (); + KeyModifiers keyModifiers; + + void MapKeyModifiers (ConsoleKeyInfo keyInfo) + { + if (keyModifiers == null) + keyModifiers = new KeyModifiers (); + + if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) + keyModifiers.Shift = true; + if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) + keyModifiers.Ctrl = true; + if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) + keyModifiers.Alt = true; + } public override void PrepareToRun (MainLoop mainLoop, Action keyHandler, Action keyDownHandler, Action keyUpHandler, Action mouseHandler) { // Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called (mainLoop.Driver as NetMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) { var map = MapKey (consoleKey); - if (map == (Key)0xffffffff) + if (map == (Key)0xffffffff) { return; + } keyHandler (new KeyEvent (map, keyModifiers)); keyUpHandler (new KeyEvent (map, keyModifiers)); + keyModifiers = null; }; } @@ -368,14 +422,17 @@ public override void UncookMouse () /// file descriptor monitoring. /// /// - /// This implementation is used for both NetDriver and FakeDriver. + /// This implementation is used for NetDriver. /// public class NetMainLoop : IMainLoopDriver { - AutoResetEvent keyReady = new AutoResetEvent (false); - AutoResetEvent waitForProbe = new AutoResetEvent (false); + ManualResetEventSlim keyReady = new ManualResetEventSlim (false); + ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false); + ManualResetEventSlim winChange = new ManualResetEventSlim (false); ConsoleKeyInfo? keyResult = null; MainLoop mainLoop; - Func consoleKeyReaderFn = null; + ConsoleDriver consoleDriver; + bool winChanged; + CancellationTokenSource tokenSource = new CancellationTokenSource (); /// /// Invoked when a Key is pressed. @@ -383,66 +440,126 @@ public class NetMainLoop : IMainLoopDriver { public Action KeyPressed; /// - /// Initializes the class. + /// Initializes the class with the console driver. /// /// - /// Passing a consoleKeyReaderfn is provided to support unit test sceanrios. + /// Passing a consoleDriver is provided to capture windows resizing. /// - /// The method to be called to get a key from the console. - public NetMainLoop (Func consoleKeyReaderFn = null) + /// The console driver used by this Net main loop. + public NetMainLoop (ConsoleDriver consoleDriver = null) { - if (consoleKeyReaderFn == null) { - throw new ArgumentNullException ("key reader function must be provided."); + if (consoleDriver == null) { + throw new ArgumentNullException ("console driver instance must be provided."); } - this.consoleKeyReaderFn = consoleKeyReaderFn; + this.consoleDriver = consoleDriver; } - void WindowsKeyReader () + void KeyReader () { while (true) { - waitForProbe.WaitOne (); - keyResult = consoleKeyReaderFn(); + waitForProbe.Wait (); + waitForProbe.Reset (); + keyResult = Console.ReadKey (true); keyReady.Set (); } } + void CheckWinChange () + { + while (true) { + winChange.Wait (); + winChange.Reset (); + WaitWinChange (); + winChanged = true; + keyReady.Set (); + } + } + + void WaitWinChange () + { + while (true) { + if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows + || Console.WindowTop != consoleDriver.Top) { // Top only working on Windows. + return; + } + } + } + void IMainLoopDriver.Setup (MainLoop mainLoop) { this.mainLoop = mainLoop; - Thread readThread = new Thread (WindowsKeyReader); - readThread.Start (); + Task.Run (KeyReader); + Task.Run (CheckWinChange); } void IMainLoopDriver.Wakeup () { + keyReady.Set (); } bool IMainLoopDriver.EventsPending (bool wait) { long now = DateTime.UtcNow.Ticks; - int waitTimeout; + waitForProbe.Set (); + winChange.Set (); + + if (CheckTimers (wait, out var waitTimeout)) { + return true; + } + + try { + if (!tokenSource.IsCancellationRequested) { + keyReady.Wait (waitTimeout, tokenSource.Token); + } + } catch (OperationCanceledException) { + return true; + } finally { + keyReady.Reset (); + } + + if (!tokenSource.IsCancellationRequested) { + return keyResult.HasValue || CheckTimers (wait, out _) || winChanged; + } + + tokenSource.Dispose (); + tokenSource = new CancellationTokenSource (); + return true; + } + + bool CheckTimers (bool wait, out int waitTimeout) + { + long now = DateTime.UtcNow.Ticks; + if (mainLoop.timeouts.Count > 0) { waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond); if (waitTimeout < 0) return true; - } else + } else { waitTimeout = -1; + } if (!wait) waitTimeout = 0; - keyResult = null; - waitForProbe.Set (); - keyReady.WaitOne (waitTimeout); - return keyResult.HasValue; + int ic; + lock (mainLoop.idleHandlers) { + ic = mainLoop.idleHandlers.Count; + } + + return ic > 0; } void IMainLoopDriver.MainIteration () { if (keyResult.HasValue) { - KeyPressed?.Invoke (keyResult.Value); + var kr = keyResult; keyResult = null; + KeyPressed?.Invoke (kr.Value); + } + if (winChanged) { + winChanged = false; + consoleDriver.Refresh (); } } } diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs index 48a1321486..714a6a76fe 100644 --- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs +++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs @@ -510,6 +510,7 @@ internal class WindowsDriver : ConsoleDriver, IMainLoopDriver { public override int Cols => cols; public override int Rows => rows; + public override int Top => 0; public WindowsDriver () { diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 35f6fc195a..a3b2ce667a 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -190,8 +190,8 @@ static void Init (Func topLevelFactory, ConsoleDriver driver = null, I if (Driver == null) { var p = Environment.OSVersion.Platform; if (UseSystemConsole) { - mainLoopDriver = new NetMainLoop (() => Console.ReadKey (true)); Driver = new NetDriver (); + mainLoopDriver = new NetMainLoop (Driver); } else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) { var windowsDriver = new WindowsDriver (); mainLoopDriver = windowsDriver; diff --git a/Terminal.Gui/Core/ConsoleDriver.cs b/Terminal.Gui/Core/ConsoleDriver.cs index 54511d6421..c0084e9ce0 100644 --- a/Terminal.Gui/Core/ConsoleDriver.cs +++ b/Terminal.Gui/Core/ConsoleDriver.cs @@ -541,6 +541,11 @@ public abstract class ConsoleDriver { /// The current number of rows in the terminal. /// public abstract int Rows { get; } + /// + /// The current top in the terminal. + /// + public abstract int Top { get; } + /// /// Initializes the driver /// diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index add9947049..3eb7ed4a4a 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1095,6 +1095,10 @@ public void DrawHotString (ustring text, bool focused, ColorScheme scheme) /// Row. public void Move (int col, int row) { + if (Driver.Rows == 0) { + return; + } + ViewToScreen (col, row, out var rcol, out var rrow); Driver.Move (rcol, rrow); } diff --git a/UICatalog/Scenarios/Threading.cs b/UICatalog/Scenarios/Threading.cs index 479c49e0a7..68ebf92f9b 100644 --- a/UICatalog/Scenarios/Threading.cs +++ b/UICatalog/Scenarios/Threading.cs @@ -127,6 +127,7 @@ private async Task MethodAsync () await Task.Delay (3000); LogJob ("Returning from task method"); await _itemsList.SetSourceAsync (items); + _itemsList.SetNeedsDisplay (); } private CancellationTokenSource cancellationTokenSource; diff --git a/UnitTests/ApplicationTests.cs b/UnitTests/ApplicationTests.cs index 7a8234825e..110ebcdac5 100644 --- a/UnitTests/ApplicationTests.cs +++ b/UnitTests/ApplicationTests.cs @@ -28,7 +28,7 @@ public void Init_Shutdown_Cleans_Up () Assert.Null (Application.MainLoop); Assert.Null (Application.Driver); - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); Assert.NotNull (Application.Current); Assert.NotNull (Application.CurrentView); Assert.NotNull (Application.Top); @@ -66,7 +66,7 @@ public void RunState_Dispose_Cleans_Up () void Init () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey(true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); Assert.NotNull (Application.Driver); Assert.NotNull (Application.MainLoop); } diff --git a/UnitTests/ConsoleDriverTests.cs b/UnitTests/ConsoleDriverTests.cs index 05d94bab52..c241f9682e 100644 --- a/UnitTests/ConsoleDriverTests.cs +++ b/UnitTests/ConsoleDriverTests.cs @@ -11,7 +11,7 @@ public class ConsoleDriverTests { public void Init_Inits () { var driver = new FakeDriver (); - Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); driver.Init (() => { }); Assert.Equal (80, Console.BufferWidth); @@ -27,7 +27,7 @@ public void Init_Inits () public void End_Cleans_Up () { var driver = new FakeDriver (); - Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); driver.Init (() => { }); FakeConsole.ForegroundColor = ConsoleColor.Red; @@ -50,7 +50,7 @@ public void End_Cleans_Up () public void SetColors_Changes_Colors () { var driver = new FakeDriver (); - Application.Init (driver, new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true))); driver.Init (() => { }); Assert.Equal (ConsoleColor.Gray, Console.ForegroundColor); Assert.Equal (ConsoleColor.Black, Console.BackgroundColor); diff --git a/UnitTests/DimTests.cs b/UnitTests/DimTests.cs index a414e94d18..1b8f9b7fa5 100644 --- a/UnitTests/DimTests.cs +++ b/UnitTests/DimTests.cs @@ -247,7 +247,7 @@ public void Percent_ThrowsOnIvalid () [Fact] public void Dim_Validation_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -279,7 +279,7 @@ public void Dim_Validation_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Ano [Fact] public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Null () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -300,7 +300,7 @@ public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue [Fact] public void Dim_Validation_Do_Not_Throws_If_NewValue_Is_DimAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -333,7 +333,7 @@ public void Only_DimAbsolute_And_DimFactor_As_A_Different_Procedure_For_Assignin { // Testing with the Button because it properly handles the Dim class. - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; diff --git a/UnitTests/MainLoopTests.cs b/UnitTests/MainLoopTests.cs index 78a8686006..2e9b85d409 100644 --- a/UnitTests/MainLoopTests.cs +++ b/UnitTests/MainLoopTests.cs @@ -18,7 +18,7 @@ public class MainLoopTests { [Fact] public void Constructor_Setups_Driver () { - var ml = new MainLoop (new NetMainLoop(() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); Assert.NotNull (ml.Driver); } @@ -26,7 +26,7 @@ public void Constructor_Setups_Driver () [Fact] public void AddIdle_Adds_And_Removes () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); Func fnTrue = () => { return true; }; Func fnFalse = () => { return false; }; @@ -60,7 +60,7 @@ public void AddIdle_Adds_And_Removes () [Fact] public void AddIdle_Function_GetsCalled_OnIteration () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn = () => { @@ -76,7 +76,7 @@ public void AddIdle_Function_GetsCalled_OnIteration () [Fact] public void RemoveIdle_Function_NotCalled () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn = () => { @@ -93,7 +93,7 @@ public void RemoveIdle_Function_NotCalled () [Fact] public void AddThenRemoveIdle_Function_NotCalled () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn = () => { @@ -111,7 +111,7 @@ public void AddThenRemoveIdle_Function_NotCalled () [Fact] public void AddTwice_Function_CalledTwice () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn = () => { @@ -139,7 +139,7 @@ public void AddTwice_Function_CalledTwice () [Fact] public void False_Idle_Stops_It_Being_Called_Again () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn1 = () => { @@ -172,7 +172,7 @@ public void False_Idle_Stops_It_Being_Called_Again () [Fact] public void AddIdle_Twice_Returns_False_Called_Twice () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn1 = () => { @@ -204,7 +204,7 @@ public void AddIdle_Twice_Returns_False_Called_Twice () [Fact] public void Run_Runs_Idle_Stop_Stops_Idle () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var functionCalled = 0; Func fn = () => { @@ -226,7 +226,7 @@ public void Run_Runs_Idle_Stop_Stops_Idle () [Fact] public void AddTimer_Adds_Removes_NoFaults () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = 100; var callbackCount = 0; @@ -246,7 +246,7 @@ public void AddTimer_Adds_Removes_NoFaults () [Fact] public void AddTimer_Run_Called () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = 100; var callbackCount = 0; @@ -274,7 +274,7 @@ class MillisecondTolerance : IEqualityComparer { [Fact] public void AddTimer_Run_CalledAtApproximatelyRightTime () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = TimeSpan.FromMilliseconds (50); var watch = new System.Diagnostics.Stopwatch (); @@ -300,7 +300,7 @@ public void AddTimer_Run_CalledAtApproximatelyRightTime () [Fact] public void AddTimer_Run_CalledTwiceApproximatelyRightTime () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = TimeSpan.FromMilliseconds (50); var watch = new System.Diagnostics.Stopwatch (); @@ -328,7 +328,7 @@ public void AddTimer_Run_CalledTwiceApproximatelyRightTime () [Fact] public void AddTimer_Remove_NotCalled () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = TimeSpan.FromMilliseconds (50); // Force stop if 10 iterations @@ -357,7 +357,7 @@ public void AddTimer_Remove_NotCalled () [Fact] public void AddTimer_ReturnFalse_StopsBeingCalled () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = TimeSpan.FromMilliseconds (50); // Force stop if 10 iterations @@ -390,7 +390,7 @@ public void AddTimer_ReturnFalse_StopsBeingCalled () [Fact] public void Invoke_Adds_Idle () { - var ml = new MainLoop (new NetMainLoop (() => FakeConsole.ReadKey (true))); + var ml = new MainLoop (new FakeMainLoop (() => FakeConsole.ReadKey (true))); var actionCalled = 0; ml.Invoke (() => { actionCalled++; }); diff --git a/UnitTests/PosTests.cs b/UnitTests/PosTests.cs index 5f51c998c4..3c7a2793b0 100644 --- a/UnitTests/PosTests.cs +++ b/UnitTests/PosTests.cs @@ -262,7 +262,7 @@ public void LeftTopBottomRight_Win_ShouldNotThrow () // Setup Fake driver (Window win, Button button) setup () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); Application.Iteration = () => { Application.RequestStop (); }; @@ -374,7 +374,7 @@ public void Percent_ThrowsOnIvalid () [Fact] public void Pos_Validation_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -406,7 +406,7 @@ public void Pos_Validation_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Ano [Fact] public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Null () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -428,7 +428,7 @@ public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue [Fact] public void Pos_Validation_Do_Not_Throws_If_NewValue_Is_PosAbsolute_And_OldValue_Is_Another_Type_After_Sets_To_LayoutStyle_Absolute () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; diff --git a/UnitTests/ScenarioTests.cs b/UnitTests/ScenarioTests.cs index e60a8448d6..007a18143e 100644 --- a/UnitTests/ScenarioTests.cs +++ b/UnitTests/ScenarioTests.cs @@ -55,7 +55,7 @@ public void Run_All_Scenarios () Application.RequestStop (); } }; - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = 1000; var abortCount = 0; @@ -107,7 +107,7 @@ public void Run_Generic () Application.RequestStop (); } }; - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var ms = 1000; var abortCount = 0; diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 56435e8062..dfb45df8bb 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -544,7 +544,7 @@ public void CanFocus_Set_Changes_TabIndex_And_TabStop () [Fact] public void Initialized_Event_Comparing_With_Added_Event () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = new Toplevel () { Id = "0", }; @@ -643,7 +643,7 @@ public void Initialized_Event_Comparing_With_Added_Event () [Fact] public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = new Toplevel () { Id = "0", }; @@ -751,7 +751,7 @@ public void CanFocus_Faced_With_Container () [Fact] public void CanFocus_Faced_With_Container_Before_Run () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -788,7 +788,7 @@ public void CanFocus_Faced_With_Container_Before_Run () [Fact] public void CanFocus_Faced_With_Container_After_Run () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -831,7 +831,7 @@ public void CanFocus_Faced_With_Container_After_Run () [Fact] public void CanFocus_Container_ToFalse_Turns_All_Subviews_ToFalse_Too () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -866,7 +866,7 @@ public void CanFocus_Container_ToFalse_Turns_All_Subviews_ToFalse_Too () [Fact] public void CanFocus_Container_Toggling_All_Subviews_To_Old_Value_When_Is_True () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; @@ -910,7 +910,7 @@ public void Navigation_With_Null_Focused_View () { // Non-regression test for #882 (NullReferenceException during keyboard navigation when Focused is null) - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); Application.Top.Ready += () => { Assert.Null (Application.Top.Focused); @@ -928,7 +928,7 @@ public void Navigation_With_Null_Focused_View () [Fact] public void Multi_Thread_Toplevels () { - Application.Init (new FakeDriver (), new NetMainLoop (() => FakeConsole.ReadKey (true))); + Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true))); var t = Application.Top; var w = new Window (); From 854bca5846beb3fc9fe47cdcb82b5e5ebbfef395 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 17 Nov 2020 22:03:32 +0000 Subject: [PATCH 2/3] Ammend AutoSize. --- Terminal.Gui/Core/View.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 3eb7ed4a4a..729bd1a94a 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1912,7 +1912,7 @@ public virtual ustring Text { /// /// Used by to resize the view's with the . - /// Setting to true only work if the and are null or + /// Setting to true only work if the and are null or /// values and doesn't work with layout, /// to avoid breaking the and settings. /// From 991f479cb847297aadee2bc304b6f06dce005409 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 18 Nov 2020 19:52:02 +0000 Subject: [PATCH 3/3] Changed the menu Diagnostics and added code for passing an argument -usc for UseSystemConsole. --- UICatalog/UICatalog.cs | 165 ++++++++++++++++++++++++++++++++--------- 1 file changed, 130 insertions(+), 35 deletions(-) diff --git a/UICatalog/UICatalog.cs b/UICatalog/UICatalog.cs index 8c68e99b39..1df8d2773c 100644 --- a/UICatalog/UICatalog.cs +++ b/UICatalog/UICatalog.cs @@ -62,6 +62,7 @@ public class UICatalogApp { private static Scenario _runningScenario = null; private static bool _useSystemConsole = false; + private static ConsoleDriver.DiagnosticFlags _diagnosticFlags; static void Main (string [] args) { @@ -72,14 +73,20 @@ static void Main (string [] args) _scenarios = Scenario.GetDerivedClasses ().OrderBy (t => Scenario.ScenarioMetadata.GetName (t)).ToList (); + if (args.Length > 0 && args.Contains("-usc")) { + _useSystemConsole = true; + args = args.Where (val => val != "-usc").ToArray (); + } if (args.Length > 0) { var item = _scenarios.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals (args [0], StringComparison.OrdinalIgnoreCase)); _runningScenario = (Scenario)Activator.CreateInstance (_scenarios [item]); + Application.UseSystemConsole = _useSystemConsole; Application.Init (); _runningScenario.Init (Application.Top, _baseColorScheme); _runningScenario.Setup (); _runningScenario.Run (); _runningScenario = null; + Application.Shutdown (); return; } @@ -87,7 +94,7 @@ static void Main (string [] args) while ((scenario = GetScenarioToRun ()) != null) { #if DEBUG_IDISPOSABLE // Validate there are no outstanding Responder-based instances - // after a sceanario was selected to run. This proves the main UI Catalog + // after a scenario was selected to run. This proves the main UI Catalog // 'app' closed cleanly. foreach (var inst in Responder.Instances) { Debug.Assert (inst.WasDisposed); @@ -95,7 +102,6 @@ static void Main (string [] args) Responder.Instances.Clear (); #endif - Application.UseSystemConsole = _useSystemConsole; scenario.Init (Application.Top, _baseColorScheme); scenario.Setup (); scenario.Run (); @@ -136,7 +142,7 @@ static void LoadedHandler () /// private static Scenario GetScenarioToRun () { - Application.UseSystemConsole = false; + Application.UseSystemConsole = _useSystemConsole; Application.Init (); // Set this here because not initialized until driver is loaded @@ -160,7 +166,7 @@ private static Scenario GetScenarioToRun () new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask) }), new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()), - new MenuBarItem ("Diag_ostics", CreateDiagnosticMenuItems()), + new MenuBarItem ("Diag_nostics", 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), @@ -270,43 +276,132 @@ private static Scenario GetScenarioToRun () static MenuItem [] CreateDiagnosticMenuItems () { + const string OFF = "Diagnostics: _Off"; + const string FRAME_RULER = "Diagnostics: Frame _Ruler"; + const string FRAME_PADDING = "Diagnostics: _Frame Padding"; var index = 0; - MenuItem CheckedMenuMenuItem (ustring menuItem, Action action, Func checkFunction) - { - var mi = new MenuItem (); - mi.Title = menuItem; - mi.Shortcut = Key.AltMask + index.ToString () [0]; + List menuItems = new List (); + foreach (Enum diag in Enum.GetValues(_diagnosticFlags.GetType())) { + var item = new MenuItem (); + item.Title = GetDiagnosticsTitle (diag); + item.Shortcut = Key.AltMask + index.ToString () [0]; index++; - mi.CheckType |= MenuItemCheckStyle.Checked; - mi.Checked = checkFunction (); - mi.Action = () => { - action?.Invoke (); - mi.Title = menuItem; - mi.Checked = checkFunction (); + item.CheckType |= MenuItemCheckStyle.Checked; + item.Checked = _diagnosticFlags.HasFlag (diag); + item.Action += () => { + var t = GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off); + if (item.Title == t && !item.Checked) { + _diagnosticFlags &= ~(ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler); + item.Checked = true; + } else if (item.Title == t && item.Checked) { + _diagnosticFlags |= (ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler); + item.Checked = false; + } else { + var f = GetDiagnosticsEnumValue (item.Title); + if (_diagnosticFlags.HasFlag (f)) { + SetDiagnosticsFlag (f, false); + } else { + SetDiagnosticsFlag (f, true); + } + } + foreach (var menuItem in menuItems) { + if (menuItem.Title == t) { + menuItem.Checked = !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FrameRuler) + && !_diagnosticFlags.HasFlag (ConsoleDriver.DiagnosticFlags.FramePadding); + } else if (menuItem.Title != t) { + menuItem.Checked = _diagnosticFlags.HasFlag (GetDiagnosticsEnumValue (menuItem.Title)); + } + } + ConsoleDriver.Diagnostics = _diagnosticFlags; + _top.SetNeedsDisplay (); }; - return mi; + menuItems.Add (item); } + return menuItems.ToArray (); - return new MenuItem [] { - CheckedMenuMenuItem ("Use _System Console", - () => { - _useSystemConsole = !_useSystemConsole; - }, - () => _useSystemConsole), - CheckedMenuMenuItem ("Diagnostics: _Frame Padding", - () => { - ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding; - _top.SetNeedsDisplay (); - }, - () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding), - CheckedMenuMenuItem ("Diagnostics: Frame _Ruler", - () => { - ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; - _top.SetNeedsDisplay (); - }, - () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler), - }; + string GetDiagnosticsTitle (Enum diag) + { + switch (Enum.GetName (_diagnosticFlags.GetType (), diag)) { + case "Off": + return OFF; + case "FrameRuler": + return FRAME_RULER; + case "FramePadding": + return FRAME_PADDING; + } + return ""; + } + + Enum GetDiagnosticsEnumValue (ustring title) + { + switch (title.ToString ()) { + case FRAME_RULER: + return ConsoleDriver.DiagnosticFlags.FrameRuler; + case FRAME_PADDING: + return ConsoleDriver.DiagnosticFlags.FramePadding; + } + return null; + } + + void SetDiagnosticsFlag (Enum diag, bool add) + { + switch (diag) { + case ConsoleDriver.DiagnosticFlags.FrameRuler: + if (add) { + _diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FrameRuler; + } else { + _diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FrameRuler; + } + break; + case ConsoleDriver.DiagnosticFlags.FramePadding: + if (add) { + _diagnosticFlags |= ConsoleDriver.DiagnosticFlags.FramePadding; + } else { + _diagnosticFlags &= ~ConsoleDriver.DiagnosticFlags.FramePadding; + } + break; + default: + _diagnosticFlags = default; + break; + } + } + + //MenuItem CheckedMenuMenuItem (ustring menuItem, Action action, Func checkFunction) + //{ + // var mi = new MenuItem (); + // mi.Title = menuItem; + // mi.Shortcut = Key.AltMask + index.ToString () [0]; + // index++; + // mi.CheckType |= MenuItemCheckStyle.Checked; + // mi.Checked = checkFunction (); + // mi.Action = () => { + // action?.Invoke (); + // mi.Title = menuItem; + // mi.Checked = checkFunction (); + // }; + // return mi; + //} + + //return new MenuItem [] { + // CheckedMenuMenuItem ("Use _System Console", + // () => { + // _useSystemConsole = !_useSystemConsole; + // }, + // () => _useSystemConsole), + // CheckedMenuMenuItem ("Diagnostics: _Frame Padding", + // () => { + // ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FramePadding; + // _top.SetNeedsDisplay (); + // }, + // () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding), + // CheckedMenuMenuItem ("Diagnostics: Frame _Ruler", + // () => { + // ConsoleDriver.Diagnostics ^= ConsoleDriver.DiagnosticFlags.FrameRuler; + // _top.SetNeedsDisplay (); + // }, + // () => (ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FrameRuler) == ConsoleDriver.DiagnosticFlags.FrameRuler), + //}; } static void SetColorScheme ()