From 49ae8f1b210c43bfda00008d59c60557011d5eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Wed, 26 Aug 2020 10:29:31 +0200 Subject: [PATCH 1/3] Fix #882 Fix NullReferenceException during keyboard navigation when `Focused` is null. --- Terminal.Gui/Core/Toplevel.cs | 4 ++++ UnitTests/ViewTests.cs | 29 +++++++++++++++++++++++++---- 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs index ab5334a05..5d8914374 100644 --- a/Terminal.Gui/Core/Toplevel.cs +++ b/Terminal.Gui/Core/Toplevel.cs @@ -215,6 +215,10 @@ namespace Terminal.Gui { View GetDeepestFocusedSubview (View view) { + if (view == null) { + return null; + } + foreach (var v in view.Subviews) { if (v.HasFocus) { return GetDeepestFocusedSubview (v); diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs index 9dd570375..66a20fb01 100644 --- a/UnitTests/ViewTests.cs +++ b/UnitTests/ViewTests.cs @@ -35,7 +35,7 @@ namespace Terminal.Gui { Assert.Empty (r.Subviews); Assert.False (r.WantContinuousButtonPressed); Assert.False (r.WantMousePositionReports); - Assert.Null (r.GetEnumerator().Current); + Assert.Null (r.GetEnumerator ().Current); Assert.Null (r.SuperView); Assert.Null (r.MostFocused); @@ -64,7 +64,7 @@ namespace Terminal.Gui { Assert.Null (r.MostFocused); // Rect with values - r = new View (new Rect(1, 2, 3, 4)); + r = new View (new Rect (1, 2, 3, 4)); Assert.NotNull (r); Assert.Equal (LayoutStyle.Absolute, r.LayoutStyle); Assert.Equal ("View()({X=1,Y=2,Width=3,Height=4})", r.ToString ()); @@ -115,7 +115,7 @@ namespace Terminal.Gui { var sub1 = new View (); root.Add (sub1); var sub2 = new View (); - sub1.Width = Dim.Width(sub2); + sub1.Width = Dim.Width (sub2); Assert.Throws (() => root.LayoutSubviews ()); @@ -551,7 +551,7 @@ namespace Terminal.Gui { var t = new Toplevel () { Id = "0", }; - var w = new Window () {Id = "t", Width = Dim.Fill (), Height = Dim.Fill () }; + var w = new Window () { Id = "t", Width = Dim.Fill (), Height = Dim.Fill () }; var v1 = new View () { Id = "v1", Width = Dim.Fill (), Height = Dim.Fill () }; var v2 = new View () { Id = "v2", Width = Dim.Fill (), Height = Dim.Fill () }; var sv1 = new View () { Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill () }; @@ -900,6 +900,27 @@ namespace Terminal.Gui { Application.Shutdown (); } + + [Fact] + 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.Top.Ready += () => { + Assert.Null (Application.Top.Focused); + }; + + // Keyboard navigation with tab + Console.MockKeyPresses.Push (new ConsoleKeyInfo ('\t', ConsoleKey.Tab, false, false, false)); + + Application.Iteration += () => Application.RequestStop (); + + Application.Run (); + Application.Shutdown (); + } + [Fact] public void Multi_Thread_Toplevels () { From f4cd2ca1c3556a987fe231f2a58059b1eadfe00d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Melvyn=20La=C3=AFly?= Date: Wed, 26 Aug 2020 10:29:57 +0200 Subject: [PATCH 2/3] Fix typos --- UnitTests/ScenarioTests.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/UnitTests/ScenarioTests.cs b/UnitTests/ScenarioTests.cs index ad80c68e5..c9914b8a6 100644 --- a/UnitTests/ScenarioTests.cs +++ b/UnitTests/ScenarioTests.cs @@ -5,7 +5,7 @@ using Terminal.Gui; using UICatalog; using Xunit; -// Alais Console to MockConsole so we don't accidentally use Console +// Alias Console to MockConsole so we don't accidentally use Console using Console = Terminal.Gui.FakeConsole; namespace Terminal.Gui { @@ -32,18 +32,18 @@ namespace Terminal.Gui { } /// - /// This runs through all Sceanrios defined in UI Catalog, calling Init, Setup, and Run. + /// This runs through all Scenarios defined in UI Catalog, calling Init, Setup, and Run. /// It puts a Ctrl-Q in the input queue so the Scenario immediately exits. /// Should find any egregious regressions. /// [Fact] - public void Run_All_Sceanrios () + public void Run_All_Scenarios () { List scenarioClasses = Scenario.GetDerivedClasses (); Assert.NotEmpty (scenarioClasses); foreach (var scenarioClass in scenarioClasses) { - // Setup some fake kepresses + // Setup some fake keypresses // Passing empty string will cause just a ctrl-q to be fired Console.MockKeyPresses.Clear (); int stackSize = CreateInput (""); @@ -95,7 +95,7 @@ namespace Terminal.Gui { var item = scenarioClasses.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals ("Generic", StringComparison.OrdinalIgnoreCase)); var scenarioClass = scenarioClasses[item]; - // Setup some fake kepresses + // Setup some fake keypresses // Passing empty string will cause just a ctrl-q to be fired int stackSize = CreateInput (""); From 6119547df3720e04e473456c6bd4c16ece5377b3 Mon Sep 17 00:00:00 2001 From: blakepell Date: Thu, 3 Sep 2020 10:10:10 -0400 Subject: [PATCH 3/3] TextView MoveHome and MoveEnd methods. --- Terminal.Gui/Views/TextView.cs | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index c484cf802..398bd926b 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -234,6 +234,18 @@ namespace Terminal.Gui { /// /// /// + /// Control-Home + /// + /// Scrolls to the first line and moves the cursor there. + /// + /// + /// + /// Control-End + /// + /// Scrolls to the last line and moves the cursor there. + /// + /// + /// /// Delete, Control-d /// /// Deletes the character in front of the cursor. @@ -1004,15 +1016,11 @@ namespace Terminal.Gui { break; case Key.CtrlMask | Key.End: - currentRow = model.Count; - TrackColumn (); - PositionCursor (); + MoveEnd (); break; case Key.CtrlMask | Key.Home: - currentRow = 0; - TrackColumn (); - PositionCursor (); + MoveHome (); break; default: @@ -1086,6 +1094,26 @@ namespace Terminal.Gui { Rune RuneAt (int col, int row) => model.GetLine (row) [col]; + /// + /// Will scroll the to the last line and position the cursor there. + /// + public void MoveEnd () + { + currentRow = model.Count - 1; + TrackColumn (); + PositionCursor (); + } + + /// + /// Will scroll the to the first line and position the cursor there. + /// + public void MoveHome () + { + currentRow = 0; + TrackColumn (); + PositionCursor (); + } + bool MoveNext (ref int col, ref int row, out Rune rune) { var line = model.GetLine (row);