From d5c584425c42e4d1e24cda11c297f24d0de43d08 Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 26 Mar 2023 00:18:47 +0000 Subject: [PATCH 1/7] Fixes #2446. Currently is allowed two views on different toplevels having HasFocus as true. --- Terminal.Gui/Core/View.cs | 13 ++++-- Terminal.Gui/Core/Window.cs | 2 +- UnitTests/Core/ViewTests.cs | 62 ++++++++++++++-------------- UnitTests/TopLevels/ToplevelTests.cs | 28 ++++++------- UnitTests/TopLevels/WindowTests.cs | 19 ++++++++- 5 files changed, 74 insertions(+), 50 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 8ea963ab1..31fb088b7 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -983,7 +983,7 @@ namespace Terminal.Gui { } SetNeedsLayout (); SetNeedsDisplay (); - + OnAdded (new SuperViewChangedEventArgs (this, view)); if (IsInitialized && !view.IsInitialized) { view.BeginInit (); @@ -1676,7 +1676,10 @@ namespace Terminal.Gui { return; if (focused?.hasFocus == true && focused == view) return; - + if (focused?.hasFocus == true && focused?.SuperView == view) { + view.hasFocus = true; + return; + } // Make sure that this view is a subview View c; for (c = view.container; c != null; c = c.container) @@ -1694,7 +1697,11 @@ namespace Terminal.Gui { focused.EnsureFocus (); // Send focus upwards - SuperView?.SetFocus (this); + if (SuperView != null) { + SuperView.SetFocus (this); + } else { + SetFocus (this); + } } /// diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs index 04b56b701..5ceacc719 100644 --- a/Terminal.Gui/Core/Window.cs +++ b/Terminal.Gui/Core/Window.cs @@ -100,7 +100,7 @@ namespace Terminal.Gui { public override void OnCanFocusChanged () { - if (MostFocused == null && CanFocus && Visible) { + if (IsInitialized && MostFocused == null && CanFocus && Visible) { EnsureFocus (); } diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index f6994e148..68db325f7 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -238,7 +238,7 @@ namespace Terminal.Gui.CoreTests { var v = new View (new Rect (0, 0, 10, 24)); var t = new View (); - v.Added += (s,e) => { + v.Added += (s, e) => { Assert.Same (v.SuperView, e.Parent); Assert.Same (t, e.Parent); Assert.Same (v, e.Child); @@ -658,7 +658,7 @@ namespace Terminal.Gui.CoreTests { int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0; - w.Added += (s,e) => { + w.Added += (s, e) => { Assert.Equal (e.Parent.Frame.Width, w.Frame.Width); Assert.Equal (e.Parent.Frame.Height, w.Frame.Height); }; @@ -1232,10 +1232,10 @@ namespace Terminal.Gui.CoreTests { Assert.Equal (80, view.Bounds.Width); Assert.Equal (25, view.Bounds.Height); bool layoutStarted = false; - view.LayoutStarted += (s,e) => layoutStarted = true; + view.LayoutStarted += (s, e) => layoutStarted = true; view.OnLayoutStarted (null); Assert.True (layoutStarted); - view.LayoutComplete += (s,e) => layoutStarted = false; + view.LayoutComplete += (s, e) => layoutStarted = false; view.OnLayoutComplete (null); Assert.False (layoutStarted); view.X = Pos.Center () - 41; @@ -1252,7 +1252,7 @@ namespace Terminal.Gui.CoreTests { { var wasClicked = false; var view = new Button ("Click Me"); - view.Clicked += (s,e) => wasClicked = !wasClicked; + view.Clicked += (s, e) => wasClicked = !wasClicked; Application.Top.Add (view); view.ProcessKey (new KeyEvent (Key.Enter, null)); @@ -1281,7 +1281,7 @@ namespace Terminal.Gui.CoreTests { { var wasClicked = false; var button = new Button ("Click Me"); - button.Clicked += (s,e) => wasClicked = !wasClicked; + button.Clicked += (s, e) => wasClicked = !wasClicked; var win = new Window () { Width = Dim.Fill (), Height = Dim.Fill () }; win.Add (button); Application.Top.Add (win); @@ -1452,11 +1452,11 @@ namespace Terminal.Gui.CoreTests { [AutoInitShutdown] public void CanFocus_Sets_To_False_On_Single_View_Focus_View_On_Another_Toplevel () { - var view1 = new View () { Width = 10, Height = 1, CanFocus = true }; - var win1 = new Window () { Width = Dim.Percent (50), Height = Dim.Fill () }; + var view1 = new View () { Id = "view1", Width = 10, Height = 1, CanFocus = true }; + var win1 = new Window () { Id = "win1", Width = Dim.Percent (50), Height = Dim.Fill () }; win1.Add (view1); - var view2 = new View () { Width = 20, Height = 2, CanFocus = true }; - var win2 = new Window () { X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; + var view2 = new View () { Id = "view2", Width = 20, Height = 2, CanFocus = true }; + var win2 = new Window () { Id = "win2", X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; win2.Add (view2); Application.Top.Add (win1, win2); Application.Begin (Application.Top); @@ -1464,7 +1464,7 @@ namespace Terminal.Gui.CoreTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.True (view2.HasFocus); + Assert.False (view2.HasFocus); view1.CanFocus = false; Assert.False (view1.CanFocus); @@ -1477,12 +1477,12 @@ namespace Terminal.Gui.CoreTests { [AutoInitShutdown] public void CanFocus_Sets_To_False_With_Two_Views_Focus_Another_View_On_The_Same_Toplevel () { - var view1 = new View () { Width = 10, Height = 1, CanFocus = true }; - var view12 = new View () { Y = 5, Width = 10, Height = 1, CanFocus = true }; - var win1 = new Window () { Width = Dim.Percent (50), Height = Dim.Fill () }; + var view1 = new View () { Id = "view1", Width = 10, Height = 1, CanFocus = true }; + var view12 = new View () { Id = "view12", Y = 5, Width = 10, Height = 1, CanFocus = true }; + var win1 = new Window () { Id = "win1", Width = Dim.Percent (50), Height = Dim.Fill () }; win1.Add (view1, view12); - var view2 = new View () { Width = 20, Height = 2, CanFocus = true }; - var win2 = new Window () { X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; + var view2 = new View () { Id = "view2", Width = 20, Height = 2, CanFocus = true }; + var win2 = new Window () { Id = "win2", X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; win2.Add (view2); Application.Top.Add (win1, win2); Application.Begin (Application.Top); @@ -1490,7 +1490,7 @@ namespace Terminal.Gui.CoreTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.True (view2.HasFocus); + Assert.False (view2.HasFocus); view1.CanFocus = false; Assert.False (view1.CanFocus); @@ -1503,11 +1503,11 @@ namespace Terminal.Gui.CoreTests { [AutoInitShutdown] public void CanFocus_Sets_To_False_On_Toplevel_Focus_View_On_Another_Toplevel () { - var view1 = new View () { Width = 10, Height = 1, CanFocus = true }; - var win1 = new Window () { Width = Dim.Percent (50), Height = Dim.Fill () }; + var view1 = new View () { Id = "view1", Width = 10, Height = 1, CanFocus = true }; + var win1 = new Window () { Id = "win1", Width = Dim.Percent (50), Height = Dim.Fill () }; win1.Add (view1); - var view2 = new View () { Width = 20, Height = 2, CanFocus = true }; - var win2 = new Window () { X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; + var view2 = new View () { Id = "view2", Width = 20, Height = 2, CanFocus = true }; + var win2 = new Window () { Id = "win2", X = Pos.Right (win1), Width = Dim.Fill (), Height = Dim.Fill () }; win2.Add (view2); Application.Top.Add (win1, win2); Application.Begin (Application.Top); @@ -1515,7 +1515,7 @@ namespace Terminal.Gui.CoreTests { Assert.True (view1.CanFocus); Assert.True (view1.HasFocus); Assert.True (view2.CanFocus); - Assert.True (view2.HasFocus); + Assert.False (view2.HasFocus); win1.CanFocus = false; Assert.False (view1.CanFocus); @@ -2025,7 +2025,7 @@ namespace Terminal.Gui.CoreTests { Width = Dim.Fill (), Height = Dim.Fill () }; - view.DrawContent += (s,e) => { + view.DrawContent += (s, e) => { view.DrawFrame (view.Bounds); var savedClip = Application.Driver.Clip; Application.Driver.Clip = new Rect (1, 1, view.Bounds.Width - 2, view.Bounds.Height - 2); @@ -2073,7 +2073,7 @@ namespace Terminal.Gui.CoreTests { Width = Dim.Fill (), Height = Dim.Fill () }; - view.DrawContent += (s,e) => { + view.DrawContent += (s, e) => { view.DrawFrame (view.Bounds); var savedClip = Application.Driver.Clip; Application.Driver.Clip = new Rect (1, 1, view.Bounds.Width - 2, view.Bounds.Height - 2); @@ -2268,7 +2268,7 @@ This is a tes var tvCalled = false; var view = new View ("View") { Width = 10, Height = 10 }; - view.DrawContentComplete += (s,e) => viewCalled = true; + view.DrawContentComplete += (s, e) => viewCalled = true; var tv = new TextView () { Y = 11, Width = 10, Height = 10 }; tv.DrawContentComplete += (s, e) => tvCalled = true; @@ -2294,7 +2294,7 @@ This is a tes e.Handled = true; keyDown = true; }; - view.KeyPress += (s,e) => { + view.KeyPress += (s, e) => { Assert.Equal (Key.a, e.KeyEvent.Key); Assert.False (keyPress); Assert.False (view.IsKeyPress); @@ -2390,7 +2390,7 @@ This is a tes var keyUp = false; var view = new DerivedView (); - view.KeyDown += (s,e) => { + view.KeyDown += (s, e) => { Assert.Equal (-1, e.KeyEvent.KeyValue); Assert.Equal (shift, e.KeyEvent.IsShift); Assert.Equal (alt, e.KeyEvent.IsAlt); @@ -2441,15 +2441,15 @@ This is a tes var view1 = new View { CanFocus = true }; var subView1 = new View { CanFocus = true }; var subView1subView1 = new View { CanFocus = true }; - view1.Leave += (s,e) => { + view1.Leave += (s, e) => { view1Leave = true; }; - subView1.Leave += (s,e) => { + subView1.Leave += (s, e) => { subView1.Remove (subView1subView1); subView1Leave = true; }; view1.Add (subView1); - subView1subView1.Leave += (s,e) => { + subView1subView1.Leave += (s, e) => { // This is never invoked subView1subView1Leave = true; }; @@ -2908,7 +2908,7 @@ At 0,0 Assert.Equal (new Rect (0, 0, 30, 1), label.NeedDisplay); Assert.Equal (new Rect (0, 0, 13, 1), button.NeedDisplay); - top.LayoutComplete += (s,e) => { + top.LayoutComplete += (s, e) => { Assert.Equal (new Rect (0, 0, 80, 25), top.NeedDisplay); }; diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index a742a88cb..3d544379d 100644 --- a/UnitTests/TopLevels/ToplevelTests.cs +++ b/UnitTests/TopLevels/ToplevelTests.cs @@ -340,22 +340,22 @@ namespace Terminal.Gui.TopLevelTests { { var isRunning = false; - var win1 = new Window ("Win1") { Width = Dim.Percent (50f), Height = Dim.Fill () }; - var lblTf1W1 = new Label ("Enter text in TextField on Win1:"); - var tf1W1 = new TextField ("Text1 on Win1") { X = Pos.Right (lblTf1W1) + 1, Width = Dim.Fill () }; - var lblTvW1 = new Label ("Enter text in TextView on Win1:") { Y = Pos.Bottom (lblTf1W1) + 1 }; - var tvW1 = new TextView () { X = Pos.Left (tf1W1), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win1" }; - var lblTf2W1 = new Label ("Enter text in TextField on Win1:") { Y = Pos.Bottom (lblTvW1) + 1 }; - var tf2W1 = new TextField ("Text2 on Win1") { X = Pos.Left (tf1W1), Width = Dim.Fill () }; + var win1 = new Window ("Win1") { Id = "win1", Width = Dim.Percent (50f), Height = Dim.Fill () }; + var lblTf1W1 = new Label ("Enter text in TextField on Win1:") { Id = "lblTf1W1" }; + var tf1W1 = new TextField ("Text1 on Win1") { Id = "tf1W1", X = Pos.Right (lblTf1W1) + 1, Width = Dim.Fill () }; + var lblTvW1 = new Label ("Enter text in TextView on Win1:") { Id = "lblTvW1", Y = Pos.Bottom (lblTf1W1) + 1 }; + var tvW1 = new TextView () { Id = "tvW1", X = Pos.Left (tf1W1), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win1" }; + var lblTf2W1 = new Label ("Enter text in TextField on Win1:") { Id = "lblTf2W1", Y = Pos.Bottom (lblTvW1) + 1 }; + var tf2W1 = new TextField ("Text2 on Win1") { Id = "tf2W1", X = Pos.Left (tf1W1), Width = Dim.Fill () }; win1.Add (lblTf1W1, tf1W1, lblTvW1, tvW1, lblTf2W1, tf2W1); - var win2 = new Window ("Win2") { X = Pos.Right (win1) + 1, Width = Dim.Percent (50f), Height = Dim.Fill () }; - var lblTf1W2 = new Label ("Enter text in TextField on Win2:"); - var tf1W2 = new TextField ("Text1 on Win2") { X = Pos.Right (lblTf1W2) + 1, Width = Dim.Fill () }; - var lblTvW2 = new Label ("Enter text in TextView on Win2:") { Y = Pos.Bottom (lblTf1W2) + 1 }; - var tvW2 = new TextView () { X = Pos.Left (tf1W2), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win2" }; - var lblTf2W2 = new Label ("Enter text in TextField on Win2:") { Y = Pos.Bottom (lblTvW2) + 1 }; - var tf2W2 = new TextField ("Text2 on Win2") { X = Pos.Left (tf1W2), Width = Dim.Fill () }; + var win2 = new Window ("Win2") { Id = "win2", X = Pos.Right (win1) + 1, Width = Dim.Percent (50f), Height = Dim.Fill () }; + var lblTf1W2 = new Label ("Enter text in TextField on Win2:") { Id = "lblTf1W2" }; + var tf1W2 = new TextField ("Text1 on Win2") { Id = "tf1W2", X = Pos.Right (lblTf1W2) + 1, Width = Dim.Fill () }; + var lblTvW2 = new Label ("Enter text in TextView on Win2:") { Id = "lblTvW2", Y = Pos.Bottom (lblTf1W2) + 1 }; + var tvW2 = new TextView () { Id = "tvW2", X = Pos.Left (tf1W2), Width = Dim.Fill (), Height = 2, Text = "First line Win1\nSecond line Win2" }; + var lblTf2W2 = new Label ("Enter text in TextField on Win2:") { Id = "lblTf2W2", Y = Pos.Bottom (lblTvW2) + 1 }; + var tf2W2 = new TextField ("Text2 on Win2") { Id = "tf2W2", X = Pos.Left (tf1W2), Width = Dim.Fill () }; win2.Add (lblTf1W2, tf1W2, lblTvW2, tvW2, lblTf2W2, tf2W2); var top = Application.Top; diff --git a/UnitTests/TopLevels/WindowTests.cs b/UnitTests/TopLevels/WindowTests.cs index 21efaca61..dac961faa 100644 --- a/UnitTests/TopLevels/WindowTests.cs +++ b/UnitTests/TopLevels/WindowTests.cs @@ -138,7 +138,7 @@ namespace Terminal.Gui.TopLevelTests { string expectedOld = null; string expected = null; - r.TitleChanged += (s,args) => { + r.TitleChanged += (s, args) => { Assert.Equal (expectedOld, args.OldTitle); Assert.Equal (r.Title, args.NewTitle); }; @@ -235,7 +235,24 @@ namespace Terminal.Gui.TopLevelTests { │└────────────────┘│ │ ^Q Quit │ ^O Open│ └──────────────────┘", output); + } + [Fact, AutoInitShutdown] + public void OnCanFocusChanged_Only_Must_ContentView_Forces_SetFocus_After_IsInitialized_Is_True () + { + var win1 = new Window () { Id = "win1", Width = 10, Height = 1 }; + var view1 = new View () { Id = "view1", Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true }; + var win2 = new Window () { Id = "win2", Y = 6, Width = 10, Height = 1 }; + var view2 = new View () { Id = "view2", Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true }; + win2.Add (view2); + win1.Add (view1, win2); + + Application.Begin (win1); + + Assert.True (win1.HasFocus); + Assert.True (view1.HasFocus); + Assert.False (win2.HasFocus); + Assert.False (view2.HasFocus); } } } From 9ca23e5c4adc28976244e2d318f5c5e5494bacaa Mon Sep 17 00:00:00 2001 From: BDisp Date: Sun, 26 Mar 2023 12:38:58 +0100 Subject: [PATCH 2/7] Only set hasFocus to true if it's false. --- Terminal.Gui/Core/View.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 31fb088b7..381c0d2ac 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1677,7 +1677,9 @@ namespace Terminal.Gui { if (focused?.hasFocus == true && focused == view) return; if (focused?.hasFocus == true && focused?.SuperView == view) { - view.hasFocus = true; + if (!view.hasFocus) { + view.hasFocus = true; + } return; } // Make sure that this view is a subview From 1eaa0a9925f8bc7b6b985caa0bbada36ca55ec12 Mon Sep 17 00:00:00 2001 From: BDisp Date: Tue, 28 Mar 2023 15:53:03 +0100 Subject: [PATCH 3/7] Fixes #2457. View without subviews mustn't set CanFocus to false on Remove them. --- Terminal.Gui/Core/View.cs | 3 --- UnitTests/Core/ViewTests.cs | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index 381c0d2ac..da23b63d5 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1037,9 +1037,6 @@ namespace Terminal.Gui { view.tabIndex = -1; SetNeedsLayout (); SetNeedsDisplay (); - if (subviews.Count < 1) { - CanFocus = false; - } foreach (var v in subviews) { if (v.Frame.IntersectsWith (touched)) view.SetNeedsDisplay (); diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index 68db325f7..1784e565e 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -2957,5 +2957,43 @@ At 0,0 TestHelpers.AssertDriverContentsWithFrameAre (expected, output); } + + [Fact, AutoInitShutdown] + public void Remove_Does_Not_Change_Focus () + { + Assert.True (Application.Top.CanFocus); + Assert.False (Application.Top.HasFocus); + + var container = new View () { Width = 10, Height = 10 }; + var leave = false; + container.Leave += (s, e) => leave = true; + Assert.False (container.CanFocus); + var child = new View () { Width = Dim.Fill (), Height = Dim.Fill (), CanFocus = true }; + container.Add (child); + + Assert.True (container.CanFocus); + Assert.False (container.HasFocus); + Assert.True (child.CanFocus); + Assert.False (child.HasFocus); + + Application.Top.Add (container); + Application.Begin (Application.Top); + + Assert.True (Application.Top.CanFocus); + Assert.True (Application.Top.HasFocus); + Assert.True (container.CanFocus); + Assert.True (container.HasFocus); + Assert.True (child.CanFocus); + Assert.True (child.HasFocus); + + container.Remove (child); + child.Dispose (); + child = null; + Assert.True (Application.Top.HasFocus); + Assert.True (container.CanFocus); + Assert.True (container.HasFocus); + Assert.Null (child); + Assert.False (leave); + } } } From df95acc77d636be3958312315eb79ac00d5c231c Mon Sep 17 00:00:00 2001 From: BDisp Date: Thu, 30 Mar 2023 14:31:59 +0100 Subject: [PATCH 4/7] View with a null SuperView should be allowed to set focus. --- Terminal.Gui/Core/View.cs | 10 ++++++++-- UnitTests/Core/ViewTests.cs | 12 ++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index da23b63d5..ed4fd1510 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -1673,7 +1673,9 @@ namespace Terminal.Gui { return; if (focused?.hasFocus == true && focused == view) return; - if (focused?.hasFocus == true && focused?.SuperView == view) { + if ((focused?.hasFocus == true && focused?.SuperView == view) + || view == this) { + if (!view.hasFocus) { view.hasFocus = true; } @@ -1715,7 +1717,11 @@ namespace Terminal.Gui { return; } - SuperView?.SetFocus (this); + if (SuperView != null) { + SuperView.SetFocus (this); + } else { + SetFocus (this); + } } /// diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index 1784e565e..00d742ccb 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -2995,5 +2995,17 @@ At 0,0 Assert.Null (child); Assert.False (leave); } + + [Fact, AutoInitShutdown] + public void SetFocus_View_With_Null_Superview_Does_Not_Throw_Exception () + { + Assert.True (Application.Top.CanFocus); + Assert.False (Application.Top.HasFocus); + + var exception = Record.Exception (Application.Top.SetFocus); + Assert.Null (exception); + Assert.True (Application.Top.CanFocus); + Assert.True (Application.Top.HasFocus); + } } } From f0a4212e6d9d859699504ebfcc15bf863c417036 Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 31 Mar 2023 15:24:24 +0100 Subject: [PATCH 5/7] Prevents FocusNext throw exception if tabIndexes collection was modified. --- Terminal.Gui/Core/View.cs | 3 +-- UnitTests/Core/ViewTests.cs | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index ed4fd1510..032b1c550 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -2141,9 +2141,8 @@ namespace Terminal.Gui { FocusFirst (); return focused != null; } - var n = tabIndexes.Count; var focusedIdx = -1; - for (var i = 0; i < n; i++) { + for (var i = 0; i < tabIndexes.Count; i++) { var w = tabIndexes [i]; if (w.HasFocus) { diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index 00d742ccb..e9e0f8b2e 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -3007,5 +3007,42 @@ At 0,0 Assert.True (Application.Top.CanFocus); Assert.True (Application.Top.HasFocus); } + + [Fact, AutoInitShutdown] + public void FocusNext_Does_Not_Throws_If_A_View_Was_Removed_From_The_Collection () + { + var top1 = Application.Top; + var view1 = new View () { Id = "view1", Width = 10, Height = 5, CanFocus = true }; + var top2 = new Toplevel () { Id = "top2", Y = 1, Width = 10, Height = 5 }; + var view2 = new View () { Id = "view2", Y = 1, Width = 10, Height = 5, CanFocus = true }; + View view3 = null; + var removed = false; + view2.Enter += (s, e) => { + if (!removed) { + removed = true; + view3 = new View () { Id = "view3", Y = 1, Width = 10, Height = 5 }; + Application.Current.Add (view3); + Application.Current.BringSubviewToFront (view3); + Assert.False (view3.HasFocus); + } + }; + view2.Leave += (s, e) => Application.Current.Remove (view3); + top2.Add (view2); + top1.Add (view1, top2); + Application.Begin (top1); + + Assert.True (top1.HasFocus); + Assert.True (view1.HasFocus); + Assert.False (view2.HasFocus); + + Assert.True (top1.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers { Ctrl = true }))); + Assert.True (top1.HasFocus); + Assert.False (view1.HasFocus); + Assert.True (view2.HasFocus); + + var exception = Record.Exception (() => top1.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers { Ctrl = true }))); + Assert.Null (exception); + Assert.True (removed); + } } } From ecba20457675699d8fdd49aa1f228a56a4cbddad Mon Sep 17 00:00:00 2001 From: BDisp Date: Fri, 31 Mar 2023 15:32:02 +0100 Subject: [PATCH 6/7] Improving unit test. --- UnitTests/Core/ViewTests.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/UnitTests/Core/ViewTests.cs b/UnitTests/Core/ViewTests.cs index e9e0f8b2e..3c5e33edf 100644 --- a/UnitTests/Core/ViewTests.cs +++ b/UnitTests/Core/ViewTests.cs @@ -3026,7 +3026,11 @@ At 0,0 Assert.False (view3.HasFocus); } }; - view2.Leave += (s, e) => Application.Current.Remove (view3); + view2.Leave += (s, e) => { + Application.Current.Remove (view3); + view3.Dispose (); + view3 = null; + }; top2.Add (view2); top1.Add (view1, top2); Application.Begin (top1); @@ -3034,15 +3038,20 @@ At 0,0 Assert.True (top1.HasFocus); Assert.True (view1.HasFocus); Assert.False (view2.HasFocus); + Assert.False (removed); + Assert.Null (view3); Assert.True (top1.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers { Ctrl = true }))); Assert.True (top1.HasFocus); Assert.False (view1.HasFocus); Assert.True (view2.HasFocus); + Assert.True (removed); + Assert.NotNull (view3); var exception = Record.Exception (() => top1.ProcessKey (new KeyEvent (Key.Tab | Key.CtrlMask, new KeyModifiers { Ctrl = true }))); Assert.Null (exception); Assert.True (removed); + Assert.Null (view3); } } } From ec30be606f510e7ec7ddbd794a94d7697ff4895e Mon Sep 17 00:00:00 2001 From: BDisp Date: Wed, 15 Mar 2023 21:48:38 +0000 Subject: [PATCH 7/7] Fixes #2418. SetToplevelsSize is wrongly forcing toplevels resizing to console size. --- Terminal.Gui/Core/Application.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index 1785d41c6..181fd2756 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -1547,7 +1547,6 @@ namespace Terminal.Gui { static void TerminalResized () { var full = new Rect (0, 0, Driver.Cols, Driver.Rows); - SetToplevelsSize (full); Resized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height }); Driver.Clip = full; foreach (var t in toplevels) { @@ -1559,23 +1558,6 @@ namespace Terminal.Gui { Refresh (); } - static void SetToplevelsSize (Rect full) - { - if (MdiTop == null) { - foreach (var t in toplevels) { - if (t?.SuperView == null && !t.Modal) { - t.Frame = full; - t.Width = full.Width; - t.Height = full.Height; - } - } - } else { - Top.Frame = full; - Top.Width = full.Width; - Top.Height = full.Height; - } - } - static bool SetCurrentAsTop () { if (MdiTop == null && Current != Top && Current?.SuperView == null && Current?.Modal == false) {