diff --git a/Terminal.Gui/Application/ApplicationNavigation.cs b/Terminal.Gui/Application/ApplicationNavigation.cs index 8794dc2f2..fe9c66b3b 100644 --- a/Terminal.Gui/Application/ApplicationNavigation.cs +++ b/Terminal.Gui/Application/ApplicationNavigation.cs @@ -7,6 +7,7 @@ namespace Terminal.Gui; /// public class ApplicationNavigation { + /// /// Initializes a new instance of the class. /// @@ -15,6 +16,75 @@ public class ApplicationNavigation // TODO: Move navigation key bindings here from AddApplicationKeyBindings } + private View? _focused = null; + + /// + /// Gets the most focused in the application, if there is one. + /// + public View? GetFocused () { return _focused; } + + /// + /// INTERNAL method to record the most focused in the application. + /// + /// + /// Raises . + /// + internal void SetFocused (View? value) + { + if (_focused == value) + { + return; + } + + _focused = value; + + FocusedChanged?.Invoke (null, EventArgs.Empty); + + return; + } + + /// + /// Raised when the most focused in the application has changed. + /// + public event EventHandler? FocusedChanged; + + + /// + /// Gets whether is in the Subview hierarchy of . + /// + /// + /// + /// + public static bool IsInHierarchy (View start, View? view) + { + if (view is null) + { + return false; + } + + if (view == start) + { + return true; + } + + foreach (View subView in start.Subviews) + { + if (view == subView) + { + return true; + } + + var found = IsInHierarchy (subView, view); + if (found) + { + return found; + } + } + + return false; + } + + /// /// Gets the deepest focused subview of the specified . /// diff --git a/Terminal.Gui/View/View.Navigation.cs b/Terminal.Gui/View/View.Navigation.cs index ad33804d2..3874aca49 100644 --- a/Terminal.Gui/View/View.Navigation.cs +++ b/Terminal.Gui/View/View.Navigation.cs @@ -72,6 +72,11 @@ public partial class View // Focus and cross-view navigation management (TabStop { if (Focused.AdvanceFocus (direction, behavior)) { + // TODO: Temporary hack to make Application.Navigation.FocusChanged work + if (Focused.Focused is null) + { + Application.Navigation!.SetFocused (Focused); + } return true; } } @@ -144,6 +149,12 @@ public partial class View // Focus and cross-view navigation management (TabStop SetFocus (view); + // TODO: Temporary hack to make Application.Navigation.FocusChanged work + if (view.Focused is null) + { + Application.Navigation!.SetFocused (view); + } + return true; } @@ -604,6 +615,13 @@ public partial class View // Focus and cross-view navigation management (TabStop { // If there is no SuperView, then this is a top-level view SetFocus (this); + + } + + // TODO: Temporary hack to make Application.Navigation.FocusChanged work + if (HasFocus && Focused.Focused is null) + { + Application.Navigation!.SetFocused (Focused); } // TODO: This is a temporary hack to make overlapped non-Toplevels have a zorder. See also: View.OnDrawContent. diff --git a/UICatalog/Scenarios/AdornmentsEditor.cs b/UICatalog/Scenarios/AdornmentsEditor.cs index a8595d2dd..472ed4897 100644 --- a/UICatalog/Scenarios/AdornmentsEditor.cs +++ b/UICatalog/Scenarios/AdornmentsEditor.cs @@ -34,27 +34,27 @@ public class AdornmentsEditor : View TabStop = TabBehavior.TabGroup; - Application.MouseEvent += Application_MouseEvent; - //ApplicationNavigation.FocusedChanged += ApplicationNavigationOnFocusedChanged; + //Application.MouseEvent += Application_MouseEvent; + Application.Navigation!.FocusedChanged += ApplicationNavigationOnFocusedChanged; Initialized += AdornmentsEditor_Initialized; } - //private void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) - //{ - // if (ApplicationNavigation.IsInHierarchy (this, ApplicationNavigation.Focused)) - // { - // return; - // } + private void ApplicationNavigationOnFocusedChanged (object sender, EventArgs e) + { + if (ApplicationNavigation.IsInHierarchy (this, Application.Navigation!.GetFocused ())) + { + return; + } - // if (ApplicationNavigation.Focused is Adornment adornment) - // { - // ViewToEdit = adornment.Parent; - // } - // else - // { - // ViewToEdit = ApplicationNavigation.Focused; - // } - //} + if (Application.Navigation!.GetFocused () is Adornment adornment) + { + ViewToEdit = adornment.Parent; + } + else + { + ViewToEdit = Application.Navigation.GetFocused (); + } + } /// /// Gets or sets whether the AdornmentsEditor should automatically select the View to edit when the mouse is clicked @@ -128,7 +128,7 @@ public class AdornmentsEditor : View _diagPaddingCheckBox.Y = Pos.Bottom (_paddingEditor); _diagRulerCheckBox = new () { Text = "_Diagnostic Ruler" }; - _diagRulerCheckBox.State = Diagnostics.FastHasFlags(ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked; + _diagRulerCheckBox.State = Diagnostics.FastHasFlags (ViewDiagnosticFlags.Ruler) ? CheckState.Checked : CheckState.UnChecked; _diagRulerCheckBox.Toggle += (s, e) => { @@ -192,7 +192,7 @@ public class AdornmentsEditor : View _borderEditor.AdornmentToEdit = _viewToEdit?.Border ?? null; _paddingEditor.AdornmentToEdit = _viewToEdit?.Padding ?? null; - _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty; + _lblView.Text = $"{_viewToEdit?.GetType ().Name}: {_viewToEdit?.Id}" ?? string.Empty; return; } diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index 5e0dc2a5c..c179ef012 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -40,6 +40,7 @@ public class ScenarioTests : TestsAllViews var initialized = false; var shutdown = false; object timeout = null; + int iterationCount = 0; Application.InitializedChanged += OnApplicationOnInitializedChanged; @@ -106,7 +107,7 @@ public class ScenarioTests : TestsAllViews } Assert.Fail ( - $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms. Force quit."); + $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit."); Application.ResetState (true); @@ -115,6 +116,7 @@ public class ScenarioTests : TestsAllViews void OnApplicationOnIteration (object s, IterationEventArgs a) { + iterationCount++; if (Application.IsInitialized) { // Press QuitKey