diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs index cc2d2a053..d7572a94d 100644 --- a/Terminal.Gui/Core/Application.cs +++ b/Terminal.Gui/Core/Application.cs @@ -1515,7 +1515,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) { @@ -1527,23 +1526,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) { diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs index de953fd49..beeff72ea 100644 --- a/Terminal.Gui/Core/View.cs +++ b/Terminal.Gui/Core/View.cs @@ -986,7 +986,7 @@ namespace Terminal.Gui { } SetNeedsLayout (); SetNeedsDisplay (); - + OnAdded (new SuperViewChangedEventArgs (this, view)); if (IsInitialized && !view.IsInitialized) { view.BeginInit (); @@ -1040,9 +1040,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 (); @@ -1679,7 +1676,14 @@ namespace Terminal.Gui { return; if (focused?.hasFocus == true && focused == view) return; + if ((focused?.hasFocus == true && focused?.SuperView == view) + || view == this) { + if (!view.hasFocus) { + view.hasFocus = true; + } + return; + } // Make sure that this view is a subview View c; for (c = view.container; c != null; c = c.container) @@ -1697,7 +1701,11 @@ namespace Terminal.Gui { focused.EnsureFocus (); // Send focus upwards - SuperView?.SetFocus (this); + if (SuperView != null) { + SuperView.SetFocus (this); + } else { + SetFocus (this); + } } /// @@ -1712,7 +1720,11 @@ namespace Terminal.Gui { return; } - SuperView?.SetFocus (this); + if (SuperView != null) { + SuperView.SetFocus (this); + } else { + SetFocus (this); + } } /// @@ -2132,9 +2144,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/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..3c5e33edf 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); }; @@ -2957,5 +2957,101 @@ 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); + } + + [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); + } + + [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); + view3.Dispose (); + view3 = null; + }; + top2.Add (view2); + top1.Add (view1, top2); + Application.Begin (top1); + + 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); + } } } diff --git a/UnitTests/TopLevels/ToplevelTests.cs b/UnitTests/TopLevels/ToplevelTests.cs index c159184c6..344a5db0f 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); } } }