From 5c61115ed99a77cb64fd2f589c966fdc3045c24b Mon Sep 17 00:00:00 2001 From: Adrian Alonso Date: Sun, 3 Nov 2019 11:39:55 -0300 Subject: [PATCH 01/18] Fixed key events traversal for modal dialogs The key must be propagated initially to the first chain element before evaluating if we should stop because of the handled or the Modal condition. Otherwise the modal dialog won't get any process key notification. This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804 --- Terminal.Gui/Core.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 3dad2f01f..42bb91289 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -1812,25 +1812,25 @@ namespace Terminal.Gui { { var chain = toplevels.ToList(); foreach (var topLevel in chain) { - if (topLevel.Modal) - break; if (topLevel.ProcessHotKey (ke)) return; + if (topLevel.Modal) + break; } foreach (var topLevel in chain) { - if (topLevel.Modal) - break; if (topLevel.ProcessKey (ke)) return; + if (topLevel.Modal) + break; } foreach (var topLevel in chain) { - if (topLevel.Modal) - break; // Process the key normally if (topLevel.ProcessColdKey (ke)) return; + if (topLevel.Modal) + break; } } From 153824ae3bcd0ed4d57f901b7bb0717ce7a1e2ab Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 19 Nov 2019 17:51:20 +0000 Subject: [PATCH 02/18] Fixes Button repainting issue when changing the text length to one smaller --- Designer/Program.cs | 201 ++++++++++++++++++++++++++++++++++- Terminal.Gui/Views/Button.cs | 12 ++- 2 files changed, 208 insertions(+), 5 deletions(-) diff --git a/Designer/Program.cs b/Designer/Program.cs index d3dac91a4..e6a2bb67e 100644 --- a/Designer/Program.cs +++ b/Designer/Program.cs @@ -1,7 +1,12 @@ using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; using Terminal.Gui; namespace Designer { +#if false class Surface : Window { public Surface () : base ("Designer") { @@ -37,9 +42,201 @@ namespace Designer { Height = Dim.Fill () }; - //Application.Top.Add (menu); - Application.Top.Add (login, password); + surface.Add (login, password); + Application.Top.Add (menu, surface); Application.Run (); } } +#elif false + class MainClass { + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("Repaint Issue") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + RadioGroup radioGroup = new RadioGroup (1, 1, new [] { "Short", "Longer Text --> Will not be repainted <--", "Short" }); + + Button replaceButtonLonger = new Button (1, 10, "Replace Texts above Longer") { + Clicked = () => { radioGroup.RadioLabels = new string [] { "Longer than before", "Shorter Text", "Longer than before" }; } + }; + + Button replaceButtonSmaller = new Button (35, 10, "Replace Texts above Smaller") { + Clicked = () => { radioGroup.RadioLabels = new string [] { "Short", "Longer Text --> Will not be repainted <--", "Short" }; } + }; + + window.Add (radioGroup, replaceButtonLonger, replaceButtonSmaller); + Application.Top.Add (window); + Application.Run (); + } + } +#elif false + class MainClass { + public static void Main (string [] args) + { + + string[] radioLabels = { "First", "Second" }; + Application.Init(); + + Window window = new Window("Redraw issue when setting coordinates of label") { X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill() }; + + Label radioLabel = new Label("Radio selection: ") { X = 1, Y = 1 }; + Label otherLabel = new Label("Other label: ") { X = Pos.Left(radioLabel), Y = Pos.Top(radioLabel) + radioLabels.Length }; + + RadioGroup radioGroup = new RadioGroup(radioLabels) { X = Pos.Right(radioLabel), Y = Pos.Top(radioLabel) }; + RadioGroup radioGroup2 = new RadioGroup(new[] { "Option 1 of the second radio group", "Option 2 of the second radio group" }) { X = Pos.Right(radioLabel), Y = Pos.Top(otherLabel) }; + + Button replaceButton = new Button(1, 10, "Add radio labels") { + Clicked = () => + { + radioGroup.RadioLabels = new[] { "First", "Second", "Third <- Third ->", "Fourth <- Fourth ->" }; + otherLabel.Y = Pos.Top(radioLabel) + radioGroup.RadioLabels.Length; + //Application.Refresh(); // Even this won't redraw the app correctly, only a terminal resize will re-render the view. + //typeof(Application).GetMethod("TerminalResized", System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.NonPublic).Invoke(null, null); + } + }; + + window.Add(radioLabel, otherLabel, radioGroup, radioGroup2, replaceButton); + Application.Top.Add(window); + Application.Run(); + } + } +#elif false + class MainClass { + static TaskScheduler syncContextTaskScheduler; + + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("When awaiting a method it is awaited until the cursor moves") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + + Button button = new Button (1, 1, "Load Items"); + ListView itemsList = new ListView (); + itemsList.X = Pos.X (button); + itemsList.Y = Pos.Y (button) + 4; + button.Clicked += async () => { + Application.MainLoop.Invoke (async () => { + itemsList.Clear (); + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button"); + var items = await LoadItemsAsync (); + + //However the following line is not executed + //until the button is clicked again or + //until the cursor is moved to the next view/control + Debug.WriteLine ($"Got {items.Count} items)"); + itemsList.SetSource (items); + + //Without calling this the UI is not updated + //this.LayoutSubviews (); + }); + }; + + window.Add (itemsList, button); + Application.Top.Add (window); + Application.Run (); + } + + private static Task> LoadItemsAsync () + { + try { + // Do something that takes lot of times. + List items = new List () { "One", "Two", "Three" }; + return Task.FromResult (items); + } catch (TaskCanceledException ex) { + Debug.WriteLine (ex.Message); + return Task.FromResult (new List ()); + } + } + } +#elif false + class MainClass { + static TaskScheduler syncContextTaskScheduler; + + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("When awaiting a method it is awaited until the cursor moves") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + + Button button = new Button (1, 1, "Load Items"); + ListView itemsList = new ListView (); + itemsList.X = Pos.X (button); + itemsList.Y = Pos.Y (button) + 4; + button.Clicked += async () => { + itemsList.Clear (); + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button"); + using (CancellationTokenSource cancellationTokenSource = new CancellationTokenSource ()) { + if (button.Text == "Cancel") { + button.Text = "Load Items"; + cancellationTokenSource.Cancel (); + } else + button.Text = "Cancel"; + syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext (); + await Task>.Run (() => LoadItemsAsync (cancellationTokenSource.Token).Result).ContinueWith (task => { + //However the following line is not executed + //until the button is clicked again or + //until the cursor is moved to the next view/control + var items = task.Result; + Debug.WriteLine ($"Got {items.Count} items)"); + itemsList.SetSource (items); + + //Without calling this the UI is not updated + //this.LayoutSubviews (); + button.Text = "Load Items"; + }, CancellationToken.None, + TaskContinuationOptions.OnlyOnRanToCompletion, + syncContextTaskScheduler); + } + }; + + window.Add (itemsList, button); + Application.Top.Add (window); + Application.Run (); + } + + private static Task> LoadItemsAsync (CancellationToken cancellationToken) + { + try { + // Do something that takes lot of times. + //Task.Delay (3000); + Thread.Sleep (800); + if (cancellationToken.IsCancellationRequested) + throw new TaskCanceledException ("Task was cancelled"); + + List items = new List () { "One", "Two", "Three" }; + cancellationToken.ThrowIfCancellationRequested (); + return Task.FromResult (items); + } catch (TaskCanceledException ex) { + Debug.WriteLine (ex.Message); + return Task.FromResult (new List ()); + } + } + } +#elif true + class MainClass { + public static void Main (string [] args) + { + Application.Init (); + + Window window = new Window ("Button repainting issue when changing the text length to one smaller than before.") { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + string strLonger = "Button with a long text"; + Button button = new Button (1, 5, strLonger); + button.Clicked += () => { + //When the button is 'clicked' the following is executed + Debug.WriteLine ($"Clicked the button with text '{button.Text}'"); + if (button.Text != strLonger) { + button.Text = strLonger; + } else + button.Text = "Small text"; + }; + + window.Add (button); + Application.Top.Add (window); + Application.Run (); + } + } + +#endif } diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index 7304000e6..d6496f53f 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -68,10 +68,16 @@ namespace Terminal.Gui { CanFocus = true; this.IsDefault = is_default; Text = text; + int w = SetWidthHeight (text, is_default); + Frame = new Rect (0, 0, w, 1); + } + + private int SetWidthHeight (ustring text, bool is_default) + { int w = text.Length + 4 + (is_default ? 2 : 0); Width = w; Height = 1; - Frame = new Rect (0, 0, w, 1); + return w; } /// @@ -146,6 +152,7 @@ namespace Terminal.Gui { public override void Redraw (Rect region) { + SetWidthHeight (text, is_default); Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); Move (0, 0); Driver.AddStr (shown_text); @@ -166,8 +173,7 @@ namespace Terminal.Gui { { if (Char.ToUpper ((char)key.KeyValue) == hot_key) { this.SuperView.SetFocus (this); - if (Clicked != null) - Clicked (); + Clicked?.Invoke (); return true; } return false; From bc5609012ce293991c007245f021cb333ce8f455 Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 13 Nov 2019 22:27:54 +0000 Subject: [PATCH 03/18] Fixes #290 issue "Redraw issue when setting coordinates of label" --- Terminal.Gui/Core.cs | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 42bb91289..2cbdd80a5 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -430,7 +430,7 @@ namespace Terminal.Gui { SetNeedsDisplay (Bounds); } - bool layoutNeeded = true; + internal bool layoutNeeded = true; internal void SetNeedsLayout () { @@ -2054,11 +2054,28 @@ namespace Terminal.Gui { DrawBounds (state.Toplevel); state.Toplevel.PositionCursor (); Driver.Refresh (); + } else if (CheckLayoutNeeded (state.Toplevel)) { + TerminalResized (); + layoutNeeded = false; } else Driver.UpdateCursor (); } } + static bool layoutNeeded; + static bool CheckLayoutNeeded (View view) + { + if (view.layoutNeeded) + return layoutNeeded = view.layoutNeeded; + + for (int i = 0; view.Subviews.Count > i; i++) { + CheckLayoutNeeded (view.Subviews [i]); + if (layoutNeeded) + return layoutNeeded; + } + return layoutNeeded; + } + internal static bool DebugDrawBounds; // Need to look into why this does not work properly. From 4fb58b04cd558523daacacf5ccecdebcb48ced4f Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 30 Nov 2019 19:24:59 +0000 Subject: [PATCH 04/18] Fixed bug in Button that caused a loop redraw calling TerminalResized --- Terminal.Gui/Views/Button.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs index d6496f53f..c11bdbe40 100644 --- a/Terminal.Gui/Views/Button.cs +++ b/Terminal.Gui/Views/Button.cs @@ -102,6 +102,10 @@ namespace Terminal.Gui { } set { + if (text?.Length != value?.Length) { + SetWidthHeight (value, is_default); + SetNeedsLayout (); + } text = value; Update (); } @@ -152,7 +156,6 @@ namespace Terminal.Gui { public override void Redraw (Rect region) { - SetWidthHeight (text, is_default); Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal); Move (0, 0); Driver.AddStr (shown_text); From 9b1f28af6d5f189d532c3c59c9ff3ed90868c61a Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Sat, 29 Feb 2020 10:51:36 -0500 Subject: [PATCH 05/18] Prepare for 0.26 --- Terminal.Gui/Terminal.Gui.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 677657304..8dee18aa1 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -9,7 +9,7 @@ true Terminal.Gui - 0.24 + 0.65 Miguel de Icaza MIT https://github.com/migueldeicaza/gui.cs/ From b2c4a03913673a25109cddae2dc2fa5323b01c76 Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Sat, 29 Feb 2020 11:11:48 -0500 Subject: [PATCH 06/18] Restore some files that were deleted by Daniel's commit that I had not restored --- Designer/Designer.csproj | 3 +-- Designer/packages.config | 4 ++++ Example/packages.config | 4 ++++ Terminal.Gui/packages.config | 5 +++++ packages.config | 4 ++++ 5 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 Designer/packages.config create mode 100644 Example/packages.config create mode 100644 Terminal.Gui/packages.config create mode 100644 packages.config diff --git a/Designer/Designer.csproj b/Designer/Designer.csproj index a81a74d95..e87bd0a6b 100644 --- a/Designer/Designer.csproj +++ b/Designer/Designer.csproj @@ -40,7 +40,6 @@ - @@ -53,4 +52,4 @@ - \ No newline at end of file + diff --git a/Designer/packages.config b/Designer/packages.config new file mode 100644 index 000000000..e18eee05a --- /dev/null +++ b/Designer/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/Example/packages.config b/Example/packages.config new file mode 100644 index 000000000..e18eee05a --- /dev/null +++ b/Example/packages.config @@ -0,0 +1,4 @@ + + + + diff --git a/Terminal.Gui/packages.config b/Terminal.Gui/packages.config new file mode 100644 index 000000000..7c57b8167 --- /dev/null +++ b/Terminal.Gui/packages.config @@ -0,0 +1,5 @@ + + + + + diff --git a/packages.config b/packages.config new file mode 100644 index 000000000..e18eee05a --- /dev/null +++ b/packages.config @@ -0,0 +1,4 @@ + + + + From bafbc93748b106d9c12a8704c5f1b5079d781d0b Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 29 Feb 2020 16:44:16 +0000 Subject: [PATCH 07/18] Fixed out of range exception and text redraw when navigate backward (#320) --- Terminal.Gui/Views/TextView.cs | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index f0f42b5cc..8d239e86c 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -807,7 +807,7 @@ namespace Terminal.Gui { currentRow--; if (currentRow < topRow) { topRow--; - + SetNeedsDisplay (); } currentLine = GetCurrentLine (); currentColumn = currentLine.Count; @@ -1149,18 +1149,19 @@ namespace Terminal.Gui { SuperView.SetFocus (this); - var maxCursorPositionableLine = (model.Count - 1) - topRow; - if (ev.Y > maxCursorPositionableLine) { - currentRow = maxCursorPositionableLine; - } else { - currentRow = ev.Y + topRow; + if (model.Count > 0) { + var maxCursorPositionableLine = (model.Count - 1) - topRow; + if (ev.Y > maxCursorPositionableLine) { + currentRow = maxCursorPositionableLine; + } else { + currentRow = ev.Y + topRow; + } + var r = GetCurrentLine (); + if (ev.X - leftColumn >= r.Count) + currentColumn = r.Count - leftColumn; + else + currentColumn = ev.X - leftColumn; } - var r = GetCurrentLine (); - if (ev.X - leftColumn >= r.Count) - currentColumn = r.Count - leftColumn; - else - currentColumn = ev.X - leftColumn; - PositionCursor (); return true; } From 638ccfd36479499c4cc3925ee6b24742aaa5360f Mon Sep 17 00:00:00 2001 From: imaras Date: Sat, 29 Feb 2020 17:44:46 +0100 Subject: [PATCH 08/18] Typo fix (#321) --- docfx/articles/overview.md | 2 +- docs/articles/overview.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docfx/articles/overview.md b/docfx/articles/overview.md index aee9be278..bbabe2efa 100644 --- a/docfx/articles/overview.md +++ b/docfx/articles/overview.md @@ -128,7 +128,7 @@ void SetupMyView (View myView) Y = 2, Width = 30, Height = 1 - } + }; myView.Add (username); } ``` diff --git a/docs/articles/overview.html b/docs/articles/overview.html index df75bf79f..af659aba6 100644 --- a/docs/articles/overview.html +++ b/docs/articles/overview.html @@ -165,7 +165,7 @@ the Subviews. You can add a view to an existing view, by calling the Y = 2, Width = 30, Height = 1 - } + }; myView.Add (username); }

The container of a given view is called the SuperView and it is a property of every From 92ffa10a31a822ab96cc56e8e2ca266e9e7e2e8a Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 29 Feb 2020 16:45:29 +0000 Subject: [PATCH 09/18] Fixes issue #306 async/await hang (#312) * Fixed async/await hang * Fixed async/await hang with calling Wakeup * Moved Wake Up into lock statement --- Terminal.Gui/Core.cs | 2 +- Terminal.Gui/MonoCurses/mainloop.cs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 2cbdd80a5..9f0f397c4 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -1717,7 +1717,7 @@ namespace Terminal.Gui { public override void Post (SendOrPostCallback d, object state) { - mainLoop.AddIdle (() => { + mainLoop.AddIdle (() => { d (state); return false; }); diff --git a/Terminal.Gui/MonoCurses/mainloop.cs b/Terminal.Gui/MonoCurses/mainloop.cs index 98ba8aca1..280c4dfee 100644 --- a/Terminal.Gui/MonoCurses/mainloop.cs +++ b/Terminal.Gui/MonoCurses/mainloop.cs @@ -354,8 +354,10 @@ namespace Mono.Terminal { ///

public Func AddIdle (Func idleHandler) { - lock (idleHandlers) + lock (idleHandlers) { idleHandlers.Add (idleHandler); + driver.Wakeup (); + } return idleHandler; } From 4600d19c7863edc7554ca8d53dccf52bf0d53c56 Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Sat, 29 Feb 2020 17:46:18 +0100 Subject: [PATCH 10/18] Support menu items that are null so they can be drawn as a menu separator (#304) --- Terminal.Gui/Views/Menu.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs index 31e28b088..ee120eb5f 100644 --- a/Terminal.Gui/Views/Menu.cs +++ b/Terminal.Gui/Views/Menu.cs @@ -124,12 +124,7 @@ namespace Terminal.Gui { static Rect MakeFrame (int x, int y, MenuItem [] items) { - int maxW = 0; - - foreach (var item in items) { - var l = item.Width; - maxW = Math.Max (l, maxW); - } + int maxW = items.Max(z=>z?.Width) ?? 0; return new Rect (x, y, maxW + 2, items.Length + 2); } From 10ea062caf16bd9ed226cf97fd949eb53de13b37 Mon Sep 17 00:00:00 2001 From: Fabian R Date: Sat, 29 Feb 2020 10:47:29 -0600 Subject: [PATCH 11/18] Fixed and Enabled Library reinitialization (#291) - Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time. - Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it. - Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle. - Minor change to Core.cs::Application.Init(Func) for better initialization status tracking, via backend property instead of relying on the Top field. --- Terminal.Gui/Core.cs | 11 +++++++---- Terminal.Gui/Drivers/WindowsDriver.cs | 8 ++++++++ 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core.cs b/Terminal.Gui/Core.cs index 9f0f397c4..e2cb24d79 100644 --- a/Terminal.Gui/Core.cs +++ b/Terminal.Gui/Core.cs @@ -243,7 +243,7 @@ namespace Terminal.Gui { /// Points to the current driver in use by the view, it is a convenience property /// for simplifying the development of new views. /// - public static ConsoleDriver Driver = Application.Driver; + public static ConsoleDriver Driver { get { return Application.Driver; } } static IList empty = new List (0).AsReadOnly (); @@ -1741,13 +1741,15 @@ namespace Terminal.Gui { /// public static void Init () => Init (() => Toplevel.Create ()); + static bool _initialized = false; + /// /// Initializes the Application /// static void Init (Func topLevelFactory) { - if (Top != null) - return; + if (_initialized) return; + _initialized = true; var p = Environment.OSVersion.Platform; Mono.Terminal.IMainLoopDriver mainLoopDriver; @@ -1978,9 +1980,10 @@ namespace Terminal.Gui { runState.Dispose (); } - static void Shutdown () + public static void Shutdown () { Driver.End (); + _initialized = false; } static void Redraw (View view) diff --git a/Terminal.Gui/Drivers/WindowsDriver.cs b/Terminal.Gui/Drivers/WindowsDriver.cs index 6ad870c92..0074181c7 100644 --- a/Terminal.Gui/Drivers/WindowsDriver.cs +++ b/Terminal.Gui/Drivers/WindowsDriver.cs @@ -100,6 +100,11 @@ namespace Terminal.Gui { var err = Marshal.GetLastWin32Error (); Console.WriteLine ("Error: {0}", err); } + + if (ScreenBuffer != IntPtr.Zero) + CloseHandle(ScreenBuffer); + + ScreenBuffer = IntPtr.Zero; } private bool ContinueListeningForConsoleEvents = true; @@ -352,6 +357,9 @@ namespace Terminal.Gui { [DllImport ("kernel32.dll", SetLastError = true)] static extern IntPtr GetStdHandle (int nStdHandle); + [DllImport("kernel32.dll", SetLastError = true)] + static extern bool CloseHandle(IntPtr handle); + [DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)] public static extern bool ReadConsoleInput ( IntPtr hConsoleInput, From 6730e549ad915f17967c14782d046503bfb9f7fb Mon Sep 17 00:00:00 2001 From: Timothy Date: Sun, 1 Mar 2020 00:48:46 +0800 Subject: [PATCH 12/18] Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315) --- Terminal.Gui/Views/ListView.cs | 124 ++++++++++++++++----------------- 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index 729a898c5..ec3450ff0 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -95,68 +95,6 @@ namespace Terminal.Gui { int top; int selected; - // - // This class is the built-in IListDataSource that renders arbitrary - // IList instances - // - class ListWrapper : IListDataSource { - IList src; - BitArray marks; - int count; - - public ListWrapper (IList source) - { - count = source.Count; - marks = new BitArray (count); - this.src = source; - } - - public int Count => src.Count; - - void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width) - { - int byteLen = ustr.Length; - int used = 0; - for (int i = 0; i < byteLen;) { - (var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen); - var count = Rune.ColumnWidth (rune); - if (used+count >= width) - break; - driver.AddRune (rune); - used += count; - i += size; - } - for (; used < width; used++) { - driver.AddRune (' '); - } - } - - public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width) - { - container.Move (col, line); - var t = src [item]; - if (t is ustring) { - RenderUstr (driver, (ustring)t, col, line, width); - } else if (t is string) { - RenderUstr (driver, (string)t, col, line, width); - } else - RenderUstr (driver, t.ToString (), col, line, width); - } - - public bool IsMarked (int item) - { - if (item >= 0 && item < count) - return marks [item]; - return false; - } - - public void SetMark (int item, bool value) - { - if (item >= 0 && item < count) - marks [item] = value; - } - } - IListDataSource source; /// /// Gets or sets the IListDataSource backing this view, use SetSource() if you want to set a new IList source. @@ -437,4 +375,66 @@ namespace Terminal.Gui { return true; } } + + /// + /// This class is the built-in IListDataSource that renders arbitrary + /// IList instances + /// + public class ListWrapper : IListDataSource { + IList src; + BitArray marks; + int count; + + public ListWrapper (IList source) + { + count = source.Count; + marks = new BitArray (count); + this.src = source; + } + + public int Count => src.Count; + + void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width) + { + int byteLen = ustr.Length; + int used = 0; + for (int i = 0; i < byteLen;) { + (var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen); + var count = Rune.ColumnWidth (rune); + if (used+count >= width) + break; + driver.AddRune (rune); + used += count; + i += size; + } + for (; used < width; used++) { + driver.AddRune (' '); + } + } + + public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width) + { + container.Move (col, line); + var t = src [item]; + if (t is ustring) { + RenderUstr (driver, (ustring)t, col, line, width); + } else if (t is string) { + RenderUstr (driver, (string)t, col, line, width); + } else + RenderUstr (driver, t.ToString (), col, line, width); + } + + public bool IsMarked (int item) + { + if (item >= 0 && item < count) + return marks [item]; + return false; + } + + public void SetMark (int item, bool value) + { + if (item >= 0 && item < count) + marks [item] = value; + } + } } From 93ccb6368daa9d189943e9b1d075d6a00a230514 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 29 Feb 2020 16:50:37 +0000 Subject: [PATCH 13/18] Resizing the MessageBox width to accommodate all message text (#299) * Fixed key events traversal for modal dialogs The key must be propagated initially to the first chain element before evaluating if we should stop because of the handled or the Modal condition. Otherwise the modal dialog won't get any process key notification. This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804 * Resizing the MessageBox width to accommodate all message text Co-authored-by: Adrian Alonso --- Example/demo.cs | 2 +- Terminal.Gui/Dialogs/MessageBox.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Example/demo.cs b/Example/demo.cs index 34a78ec2e..3cdf6a6f4 100644 --- a/Example/demo.cs +++ b/Example/demo.cs @@ -223,7 +223,7 @@ static class Demo { var d = new OpenDialog ("Open", "Open a file"); Application.Run (d); - MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "ok"); + MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok"); } public static void ShowHex (Toplevel top) diff --git a/Terminal.Gui/Dialogs/MessageBox.cs b/Terminal.Gui/Dialogs/MessageBox.cs index e038bf757..bbb53acf9 100644 --- a/Terminal.Gui/Dialogs/MessageBox.cs +++ b/Terminal.Gui/Dialogs/MessageBox.cs @@ -43,7 +43,7 @@ namespace Terminal.Gui { /// Width for the window. /// Height for the window. /// Title for the query. - /// Message to display, might contain multiple lines.. + /// Message to display, might contain multiple lines. /// Array of buttons to add. public static int ErrorQuery (int width, int height, string title, string message, params string [] buttons) { @@ -55,10 +55,10 @@ namespace Terminal.Gui { int textWidth = Label.MaxWidth (message, width); int clicked = -1, count = 0; - var d = new Dialog (title, width, height); + var d = new Dialog (title, Math.Max(width, textWidth) + 4, height); if (useErrorColors) d.ColorScheme = Colors.Error; - + foreach (var s in buttons) { int n = count++; var b = new Button (s); @@ -69,7 +69,7 @@ namespace Terminal.Gui { d.AddButton (b); } if (message != null) { - var l = new Label ((width - 4 - textWidth) / 2, 0, message); + var l = new Label (textWidth > width ? 0 : (width - 4 - textWidth) / 2, 0, message); d.Add (l); } From 329ddf58c52036250577c84aed6ba39105c929c3 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 29 Feb 2020 16:51:15 +0000 Subject: [PATCH 14/18] Timefield format with bounds values (#303) * Implemented lower and upper bounds to TimeField * Passing old text to the Changed event handler * Change sepChar from char to string in TimeField * Changing comparison from ':' to sepChar.ToCharArray () [0] --- Terminal.Gui/Views/TextField.cs | 7 ++-- Terminal.Gui/Views/TimeField.cs | 68 +++++++++++++++++++++++++++------ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs index 215c66064..16187a339 100644 --- a/Terminal.Gui/Views/TextField.cs +++ b/Terminal.Gui/Views/TextField.cs @@ -35,7 +35,7 @@ namespace Terminal.Gui { /// Client code can hook up to this event, it is /// raised when the text in the entry changes. /// - public event EventHandler Changed; + public event EventHandler Changed; /// /// Public constructor that creates a text field, with layout controlled with X, Y, Width and Height. @@ -98,7 +98,10 @@ namespace Terminal.Gui { } set { + ustring oldText = ustring.Make(text); text = TextModel.ToRunes (value); + Changed?.Invoke(this, oldText); + if (point > text.Count) point = Math.Max (text.Count-1, 0); @@ -193,8 +196,6 @@ namespace Terminal.Gui { void SetText (List newText) { text = newText; - if (Changed != null) - Changed (this, EventArgs.Empty); } void SetText (IEnumerable newText) diff --git a/Terminal.Gui/Views/TimeField.cs b/Terminal.Gui/Views/TimeField.cs index fc9f860d7..cacfa944c 100644 --- a/Terminal.Gui/Views/TimeField.cs +++ b/Terminal.Gui/Views/TimeField.cs @@ -1,4 +1,4 @@ -// + // // TimeField.cs: text entry for time // // Author: Jörg Preiß @@ -25,9 +25,9 @@ namespace Terminal.Gui { int longFieldLen = 8; int shortFieldLen = 5; int FieldLen { get { return isShort ? shortFieldLen : longFieldLen; } } - - string longFormat = " hh:mm:ss"; - string shortFormat = " hh:mm"; + string sepChar; + string longFormat; + string shortFormat; string Format { get { return isShort ? shortFormat : longFormat; } } @@ -40,9 +40,20 @@ namespace Terminal.Gui { /// If true, the seconds are hidden. public TimeField (int x, int y, DateTime time, bool isShort = false) : base (x, y, isShort ? 7 : 10, "") { + CultureInfo cultureInfo = CultureInfo.CurrentCulture; + sepChar = cultureInfo.DateTimeFormat.TimeSeparator; + longFormat = $" HH{sepChar}mm{sepChar}ss"; + shortFormat = $" HH{sepChar}mm"; this.isShort = isShort; CursorPosition = 1; Time = time; + Changed += TimeField_Changed; + } + + private void TimeField_Changed (object sender, ustring e) + { + if (!DateTime.TryParseExact (Text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) + Text = e; } /// @@ -72,31 +83,66 @@ namespace Terminal.Gui { bool SetText (ustring text) { - if (!DateTime.TryParseExact (text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) + ustring [] vals = text.Split (ustring.Make (sepChar)); + bool isValidTime = true; + int hour = Int32.Parse (vals [0].ToString ()); + int minute = Int32.Parse (vals [1].ToString ()); + int second = isShort ? 0 : Int32.Parse (vals [2].ToString ()); + if (hour < 0) { + isValidTime = false; + hour = 0; + vals [0] = "0"; + } else if (hour > 23) { + isValidTime = false; + hour = 23; + vals [0] = "23"; + } + if (minute < 0) { + isValidTime = false; + minute = 0; + vals [1] = "0"; + } else if (minute > 59) { + isValidTime = false; + minute = 59; + vals [1] = "59"; + } + if (second < 0) { + isValidTime = false; + second = 0; + vals [2] = "0"; + } else if (second > 59) { + isValidTime = false; + second = 59; + vals [2] = "59"; + } + string time = isShort ? $" {hour,2:00}{sepChar}{minute,2:00}" : $" {hour,2:00}{sepChar}{minute,2:00}{sepChar}{second,2:00}"; + Text = time; + + if (!DateTime.TryParseExact (text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result) || + !isValidTime) return false; - Text = text; return true; } void IncCursorPosition () { - if (CursorPosition == FieldLen) + if (CursorPosition == FieldLen) return; - if (Text [++CursorPosition] == ':') + if (Text [++CursorPosition] == sepChar.ToCharArray () [0]) CursorPosition++; } void DecCursorPosition () { - if (CursorPosition == 1) + if (CursorPosition == 1) return; - if (Text [--CursorPosition] == ':') + if (Text [--CursorPosition] == sepChar.ToCharArray () [0]) CursorPosition--; } void AdjCursorPosition () { - if (Text [CursorPosition] == ':') + if (Text [CursorPosition] == sepChar.ToCharArray () [0]) CursorPosition++; } From cb6bb3f88a4485c152fba61c86ffac7efd406209 Mon Sep 17 00:00:00 2001 From: "Kasper B. Graversen" Date: Sat, 29 Feb 2020 17:51:49 +0100 Subject: [PATCH 15/18] extract methods on ListView to make it controlable from other controls (#310) * moveup * MoveDown * MovePageDown * MovePageUp * MarkUnmarkRow --- Terminal.Gui/Views/ListView.cs | 124 ++++++++++++++++++++------------- 1 file changed, 76 insertions(+), 48 deletions(-) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index ec3450ff0..bdc7f75c1 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -283,69 +283,97 @@ namespace Terminal.Gui { switch (kb.Key) { case Key.CursorUp: case Key.ControlP: - if (selected > 0) { - selected--; - if (selected < top) - top = selected; - if (SelectedChanged != null) - SelectedChanged (); - SetNeedsDisplay (); - } - return true; + return MoveUp(); case Key.CursorDown: case Key.ControlN: - if (selected + 1 < source.Count) { - selected++; - if (selected >= top + Frame.Height) - top++; - if (SelectedChanged != null) - SelectedChanged (); - SetNeedsDisplay (); - } - return true; + return MoveDown(); case Key.ControlV: case Key.PageDown: - var n = (selected + Frame.Height); - if (n > source.Count) - n = source.Count - 1; - if (n != selected) { - selected = n; - if (source.Count >= Frame.Height) - top = selected; - else - top = 0; - if (SelectedChanged != null) - SelectedChanged (); - SetNeedsDisplay (); - } - return true; + return MovePageDown(); case Key.PageUp: - n = (selected - Frame.Height); - if (n < 0) - n = 0; - if (n != selected) { - selected = n; - top = selected; - if (SelectedChanged != null) - SelectedChanged (); - SetNeedsDisplay (); - } - return true; + return MovePageUp(); case Key.Space: - if (allowsMarking) { - Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); - SetNeedsDisplay (); + if (MarkUnmarkRow()) return true; - } - break; + else + break; } return base.ProcessKey (kb); } + public virtual bool MarkUnmarkRow(){ + if (allowsMarking){ + Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem)); + SetNeedsDisplay(); + return true; + } + + return false; + } + + public virtual bool MovePageUp(){ + int n = (selected - Frame.Height); + if (n < 0) + n = 0; + if (n != selected){ + selected = n; + top = selected; + if (SelectedChanged != null) + SelectedChanged(); + SetNeedsDisplay(); + } + + return true; + } + + public virtual bool MovePageDown(){ + var n = (selected + Frame.Height); + if (n > source.Count) + n = source.Count - 1; + if (n != selected){ + selected = n; + if (source.Count >= Frame.Height) + top = selected; + else + top = 0; + if (SelectedChanged != null) + SelectedChanged(); + SetNeedsDisplay(); + } + + return true; + } + + public virtual bool MoveDown(){ + if (selected + 1 < source.Count){ + selected++; + if (selected >= top + Frame.Height) + top++; + if (SelectedChanged != null) + SelectedChanged(); + SetNeedsDisplay(); + } + + return true; + } + + public virtual bool MoveUp(){ + if (selected > 0){ + selected--; + if (selected < top) + top = selected; + if (SelectedChanged != null) + SelectedChanged(); + SetNeedsDisplay(); + } + + return true; + } + /// /// Positions the cursor in this view /// From bad518c1e6b8fdb2f84c1df8d4585759db582460 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sat, 29 Feb 2020 16:54:27 +0000 Subject: [PATCH 16/18] Allowing list items selection (#302) --- Terminal.Gui/Views/ListView.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs index bdc7f75c1..71ea3a709 100644 --- a/Terminal.Gui/Views/ListView.cs +++ b/Terminal.Gui/Views/ListView.cs @@ -151,7 +151,7 @@ namespace Terminal.Gui { set { if (source == null) return; - + if (top < 0 || top >= source.Count) throw new ArgumentException ("value"); top = value; @@ -279,7 +279,7 @@ namespace Terminal.Gui { { if (source == null) return base.ProcessKey (kb); - + switch (kb.Key) { case Key.CursorUp: case Key.ControlP: @@ -379,7 +379,10 @@ namespace Terminal.Gui { /// public override void PositionCursor() { - Move (0, selected-top); + if (allowsMarking) + Move (1, selected - top); + else + Move (0, selected - top); } public override bool MouseEvent(MouseEvent me) @@ -387,16 +390,21 @@ namespace Terminal.Gui { if (!me.Flags.HasFlag (MouseFlags.Button1Clicked)) return false; - if (!HasFocus) + if (!HasFocus) SuperView.SetFocus (this); if (source == null) return false; - + if (me.Y + top >= source.Count) return true; selected = top + me.Y; + if (allowsMarking) { + Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem)); + SetNeedsDisplay (); + return true; + } if (SelectedChanged != null) SelectedChanged(); SetNeedsDisplay (); From e45c965d973415ae937ec2cd0cd45f6ef28f038f Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 8 Mar 2020 10:22:41 +0000 Subject: [PATCH 17/18] Fetch from upstream/master --- Designer/Designer.csproj | 2 +- Example/Example.csproj | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Designer/Designer.csproj b/Designer/Designer.csproj index e87bd0a6b..4374b4802 100644 --- a/Designer/Designer.csproj +++ b/Designer/Designer.csproj @@ -52,4 +52,4 @@ - + \ No newline at end of file diff --git a/Example/Example.csproj b/Example/Example.csproj index 3f6523667..95808f859 100644 --- a/Example/Example.csproj +++ b/Example/Example.csproj @@ -1,4 +1,4 @@ - + Debug @@ -52,4 +52,4 @@ - + \ No newline at end of file From e03a4d1a86692784dea428048ebd5284dbc56899 Mon Sep 17 00:00:00 2001 From: Miguel de Icaza Date: Mon, 9 Mar 2020 14:30:15 -0400 Subject: [PATCH 18/18] Prepare for 0.70 --- Terminal.Gui/Terminal.Gui.csproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj index 8dee18aa1..2f42588eb 100644 --- a/Terminal.Gui/Terminal.Gui.csproj +++ b/Terminal.Gui/Terminal.Gui.csproj @@ -9,7 +9,7 @@ true Terminal.Gui - 0.65 + 0.70 Miguel de Icaza MIT https://github.com/migueldeicaza/gui.cs/ @@ -18,7 +18,7 @@ Miguel de Icaza Application framework for creating modern console applications using .NET Gui.cs is a framework for creating console user interfaces - 0.25: Added new TimeField from Jörg Preiß; Fixes for Backtab by Martin Björkström; ListView now supports simple selection; Bug fixes by giladlevi, Daniel Cazzulino and Marius Ungureanu; New Application.Run of T entry point by Daniel Cazzulino; Added various View methods to bring forward, backwards and move views in the hierarchy; Switch to Portable PDBs by Daniel Cazzulino; Dims can now be compared by Daniel Cazzulino; OnMenuOpen handler by giladlevi; Various memory usage optimizations by giladlevi; FileDialog.FilePath is now a full path by Yanwei Wang; ISupportInitialize/ISupportInitializeNotification is now supported thanks to the work from Daniel Cazzulino; Support for non-modal TopLevels by Daniel Cazzulino and Adrian Alonso; 0.24: the Windows driver implements WakeUp, allowing some scenarios like bug #207 to be fixed; + 0.70: Bug fixes (320, 321, 306, 304, 291, 299, 303); Surface ListView.ListWrapper, surface various internal methods for use in ListView; Allow list item selection; ; 0.65: Added new TimeField from Jörg Preiß; Fixes for Backtab by Martin Björkström; ListView now supports simple selection; Bug fixes by giladlevi, Daniel Cazzulino and Marius Ungureanu; New Application.Run of T entry point by Daniel Cazzulino; Added various View methods to bring forward, backwards and move views in the hierarchy; Switch to Portable PDBs by Daniel Cazzulino; Dims can now be compared by Daniel Cazzulino; OnMenuOpen handler by giladlevi; Various memory usage optimizations by giladlevi; FileDialog.FilePath is now a full path by Yanwei Wang; ISupportInitialize/ISupportInitializeNotification is now supported thanks to the work from Daniel Cazzulino; Support for non-modal TopLevels by Daniel Cazzulino and Adrian Alonso; 0.24: the Windows driver implements WakeUp, allowing some scenarios like bug #207 to be fixed; 0.23: Better support for disabled menu items; Raises text changed event after the internals have been updated; Fix Caps-NumLock; Alt-HotKey now work on menus 0.22: Correct vertical scrollview behavior, Small curses driver fix for terminals without mouse support, TextView support for scrolling, Surface Used property on TextField, Surface Cursor on RadioGroup.