diff --git a/Terminal.Gui/View/Navigation/FocusEventArgs.cs b/Terminal.Gui/View/Navigation/FocusEventArgs.cs
index 2af837d14..bbe7b2264 100644
--- a/Terminal.Gui/View/Navigation/FocusEventArgs.cs
+++ b/Terminal.Gui/View/Navigation/FocusEventArgs.cs
@@ -1,25 +1,23 @@
namespace Terminal.Gui;
/// Defines the event arguments for
-public class FocusEventArgs : EventArgs
+public class HasFocusEventArgs : CancelEventArgs
{
/// Initializes a new instance.
- /// The view that is losing focus.
- /// The view that is gaining focus.
- public FocusEventArgs (View leaving, View entering) {
- Leaving = leaving;
- Entering = entering;
+ /// The current value of .
+ /// The value will have if the event is not cancelled.
+ /// The view that is losing focus.
+ /// The view that is gaining focus.
+ public HasFocusEventArgs (bool currentHasFocus, bool newHasFocus, View currentFocused, View newFocused) : base (ref currentHasFocus, ref newHasFocus)
+ {
+ CurrentFocused = currentFocused;
+ NewFocused = newFocused;
}
- ///
- /// Gets or sets whether the event should be canceled. Set to to prevent the focus change.
- ///
- public bool Cancel { get; set; }
-
/// Gets or sets the view that is losing focus.
- public View Leaving { get; set; }
+ public View CurrentFocused { get; set; }
/// Gets or sets the view that is gaining focus.
- public View Entering { get; set; }
+ public View NewFocused { get; set; }
}
diff --git a/Terminal.Gui/View/View.Hierarchy.cs b/Terminal.Gui/View/View.Hierarchy.cs
index 8c7eddd22..baa64006d 100644
--- a/Terminal.Gui/View/View.Hierarchy.cs
+++ b/Terminal.Gui/View/View.Hierarchy.cs
@@ -193,17 +193,18 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
return view;
}
- // If a view being removed is focused, it should lose focus.
- if (view.HasFocus)
- {
- view.FocusChanged(this, true);
- }
-
Rectangle touched = view.Frame;
_subviews.Remove (view);
_tabIndexes!.Remove (view);
view._superView = null;
//view._tabIndex = -1;
+
+ // If a view being removed is focused, it should lose focus.
+ if (view.HasFocus)
+ {
+ view.HasFocus = false;
+ }
+
SetNeedsLayout ();
SetNeedsDisplay ();
diff --git a/Terminal.Gui/View/View.Navigation.cs b/Terminal.Gui/View/View.Navigation.cs
index b861fa855..df8577396 100644
--- a/Terminal.Gui/View/View.Navigation.cs
+++ b/Terminal.Gui/View/View.Navigation.cs
@@ -46,7 +46,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
if (value)
{
// NOTE: If Application.Navigation is null, we pass null to FocusChanging. For unit tests.
- if (FocusChanging (Application.Navigation?.GetFocused ()))
+ if (SetHasFocusTrue (Application.Navigation?.GetFocused ()))
{
// The change happened
// HasFocus is now true
@@ -54,7 +54,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
}
else
{
- FocusChanged (null);
+ SetHasFocusFalse (null);
}
}
}
@@ -67,7 +67,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
///
public bool SetFocus ()
{
- return FocusChanging (Application.Navigation?.GetFocused ());
+ return SetHasFocusTrue (Application.Navigation?.GetFocused ());
}
///
@@ -78,7 +78,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
///
/// if was changed to .
///
- private bool FocusChanging ([CanBeNull] View previousFocusedView, bool traversingUp = false)
+ private bool SetHasFocusTrue ([CanBeNull] View previousFocusedView, bool traversingUp = false)
{
Debug.Assert (ApplicationNavigation.IsInHierarchy (SuperView, this));
@@ -90,7 +90,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
if (CanFocus && SuperView is { CanFocus: false })
{
- Debug.WriteLine($@"WARNING: Attempt to FocusChanging where SuperView.CanFocus == false. {this}");
+ Debug.WriteLine ($@"WARNING: Attempt to FocusChanging where SuperView.CanFocus == false. {this}");
return false;
}
@@ -108,10 +108,6 @@ public partial class View // Focus and cross-view navigation management (TabStop
if (!traversingUp)
{
- if (NotifyFocusChanging (previousFocusedView))
- {
- return false;
- }
// If we're here, we can be focused. But we may have subviews.
@@ -123,31 +119,35 @@ public partial class View // Focus and cross-view navigation management (TabStop
}
// Couldn't restore focus, so use Advance to navigate to the next focusable subview
- if (AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop))
+ if (AdvanceFocus (NavigationDirection.Forward, null))
{
// A subview was focused. We're done because the subview has focus and it recursed up the superview hierarchy.
return true;
}
}
+ if (NotifyFocusChanging (false, true, previousFocusedView, this))
+ {
+ return false;
+ }
+
// If we're here, we're the most-focusable view in the application OR we're traversing up the superview hierarchy.
// If we previously had a subview with focus (`Focused = subview`), we need to make sure that all subviews down the `subview`-hierarchy LeaveFocus.
// LeaveFocus will recurse down the subview hierarchy and will also set PreviouslyMostFocused
View focused = Focused;
- focused?.FocusChanged (this, true);
+ focused?.SetHasFocusFalse (this, true);
// We need to ensure all superviews up the superview hierarchy have focus.
// Any of them may cancel gaining focus. In which case we need to back out.
if (SuperView is { HasFocus: false } sv)
{
- // Tell EnterFocus that we're traversing up the superview hierarchy
- if (!sv.FocusChanging (previousFocusedView, true))
+ // Tell SetHasFocusTrue that we're traversing up the superview hierarchy
+ if (!sv.SetHasFocusTrue (previousFocusedView, true))
{
- // The change was cancelled
+ // The change didn't happen.
return false;
}
-
}
// If we're here:
@@ -161,33 +161,35 @@ public partial class View // Focus and cross-view navigation management (TabStop
_hasFocus = true;
// Ensure that the peer loses focus
- focusedPeer?.FocusChanged (this, true);
+ focusedPeer?.SetHasFocusFalse (this, true);
// We're the most focused view in the application, we need to set the focused view to this view.
Application.Navigation?.SetFocused (this);
+ NotifyFocusChanged (HasFocus, previousFocusedView, this);
+
SetNeedsDisplay ();
// Post-conditions - prove correctness
if (HasFocus == previousValue)
{
- throw new InvalidOperationException ($"FocusChanging was not cancelled and the HasFocus value did not change.");
+ throw new InvalidOperationException ($"NotifyFocusChanging was not cancelled and the HasFocus value did not change.");
}
return true;
}
- private bool NotifyFocusChanging (View leavingView)
+ private bool NotifyFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
{
// Call the virtual method
- if (OnHasFocusChanging (leavingView))
+ if (OnHasFocusChanging (currentHasFocus, newHasFocus, currentFocused, newFocused))
{
// The event was cancelled
return true;
}
- var args = new FocusEventArgs (leavingView, this);
+ var args = new HasFocusEventArgs (currentHasFocus, newHasFocus, currentFocused, newFocused);
HasFocusChanging?.Invoke (this, args);
if (args.Cancel)
@@ -199,23 +201,28 @@ public partial class View // Focus and cross-view navigation management (TabStop
return false;
}
- /// Virtual method invoked when the focus is changing to this View.
- /// The view that is currently Focused. May be .
- /// , if the event is to be cancelled, otherwise.
- protected virtual bool OnHasFocusChanging ([CanBeNull] View currentlyFocusedView)
+ ///
+ /// Invoked when is about to change. This method is called before the event is raised.
+ ///
+ /// The current value of .
+ /// The value will have if the event is not cancelled.
+ /// The view that is currently Focused. May be .
+ /// The view that will be focused. May be .
+ /// , if the change to is to be cancelled, otherwise.
+ protected virtual bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
{
return false;
}
/// Raised when the view is gaining (entering) focus. Can be cancelled.
- public event EventHandler HasFocusChanging;
+ public event EventHandler HasFocusChanging;
///
/// Called when focus has changed to another view.
///
/// The view that now has focus. If there is no view that has focus.
///
- private void FocusChanged ([CanBeNull] View focusedVew, bool traversingDown = false)
+ private void SetHasFocusFalse ([CanBeNull] View focusedVew, bool traversingDown = false)
{
// Pre-conditions
if (!_hasFocus)
@@ -234,7 +241,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
return;
}
- if (SuperView is {} && SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
+ if (SuperView is { } && SuperView.AdvanceFocus (NavigationDirection.Forward, TabStop))
{
// The above will cause FocusChanged, so we can return
return;
@@ -245,7 +252,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
// // Temporarily ensure this view can't get focus
// bool prevCanFocus = _canFocus;
// _canFocus = false;
- // ApplicationNavigation.MoveNextView ();
+ // Application.Navigation.;
// _canFocus = prevCanFocus;
// // The above will cause LeaveFocus, so we can return
@@ -266,7 +273,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
{
if (bottom.HasFocus)
{
- bottom.FocusChanged (focusedVew, true);
+ bottom.SetHasFocusFalse (focusedVew, true);
}
bottom = bottom.SuperView;
}
@@ -275,25 +282,29 @@ public partial class View // Focus and cross-view navigation management (TabStop
bool previousValue = HasFocus;
- // Call the virtual method - NOTE: Leave cannot be cancelled
- OnHasFocusChanged (focusedVew);
-
- var args = new FocusEventArgs (focusedVew, this);
- HasFocusChanged?.Invoke (this, args);
+ // Note, can't be cancelled.
+ NotifyFocusChanging (HasFocus, !HasFocus, focusedVew, this);
// Get whatever peer has focus, if any
View focusedPeer = SuperView?.Focused;
_hasFocus = false;
+ NotifyFocusChanged (HasFocus, this, focusedVew);
+
if (!traversingDown && CanFocus && Visible && Enabled)
{
// Now ensure all views up the superview-hierarchy are unfocused
if (SuperView is { HasFocus: true } && focusedPeer == this)
{
- SuperView.FocusChanged (focusedVew);
+ SuperView.SetHasFocusFalse (focusedVew);
}
}
+ if (SuperView is { })
+ {
+ SuperView._previouslyMostFocused = this;
+ }
+
// Post-conditions - prove correctness
if (HasFocus == previousValue)
{
@@ -309,15 +320,39 @@ public partial class View // Focus and cross-view navigation management (TabStop
[CanBeNull]
private View _previouslyMostFocused;
- /// Virtual method invoked after another view gets focus. May be .
- /// The view is now focused.
- protected virtual void OnHasFocusChanged ([CanBeNull] View focusedVew)
+ private void NotifyFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
+ {
+ // Call the virtual method
+ OnHasFocusChanged (newHasFocus, previousFocusedView, focusedVew);
+
+ // Raise the event
+ var args = new HasFocusEventArgs (newHasFocus, newHasFocus, previousFocusedView, focusedVew);
+ HasFocusChanged?.Invoke (this, args);
+ }
+
+ ///
+ /// Invoked after has changed. This method is called before the event is raised.
+ ///
+ ///
+ ///
+ /// This event cannot be cancelled.
+ ///
+ ///
+ /// The new value of .
+ ///
+ /// The view that is now focused. May be
+ protected virtual void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
{
return;
}
- /// Raised when the view is gaining (entering) focus. Can not be cancelled.
- public event EventHandler HasFocusChanged;
+ /// Raised after has changed.
+ ///
+ ///
+ /// This event cannot be cancelled.
+ ///
+ ///
+ public event EventHandler HasFocusChanged;
#endregion HasFocus
@@ -351,12 +386,12 @@ public partial class View // Focus and cross-view navigation management (TabStop
View focused = Focused;
- if (focused is {} && focused.AdvanceFocus (direction, behavior))
+ if (focused is { } && focused.AdvanceFocus (direction, behavior))
{
return true;
}
- View[] index = GetScopedTabIndexes (direction, behavior);
+ View [] index = GetScopedTabIndexes (direction, behavior);
if (index.Length == 0)
{
@@ -389,11 +424,11 @@ public partial class View // Focus and cross-view navigation management (TabStop
if (view.HasFocus)
{
// We could not advance
- return true;
+ return view == this;
}
// The subview does not have focus, but at least one other that can. Can this one be focused?
- return view.FocusChanging (Focused);
+ return view.SetHasFocusTrue (Focused);
}
@@ -593,7 +628,7 @@ public partial class View // Focus and cross-view navigation management (TabStop
HasFocus = false;
}
- if (_canFocus && !HasFocus && Visible && SuperView is { } && SuperView.Focused is null )
+ if (_canFocus && !HasFocus && Visible && SuperView is { } && SuperView.Focused is null)
{
// If CanFocus is set to true and this view does not have focus, make it enter focus
SetFocus ();
diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs
index d307e1349..67648948a 100644
--- a/Terminal.Gui/Views/ComboBox.cs
+++ b/Terminal.Gui/Views/ComboBox.cs
@@ -300,7 +300,7 @@ public class ComboBox : View, IDesignable
}
///
- protected override bool OnHasFocusChanging (View view)
+ protected override bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
{
bool cancel = false;
if (!_search.HasFocus && !_listview.HasFocus)
@@ -310,14 +310,14 @@ public class ComboBox : View, IDesignable
_search.CursorPosition = _search.Text.GetRuneCount ();
- return cancel;
+ return cancel;
}
/// Virtual method which invokes the event.
public virtual void OnExpanded () { Expanded?.Invoke (this, EventArgs.Empty); }
///
- protected override void OnHasFocusChanged (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
if (_source?.Count > 0
&& _selectedItem > -1
@@ -943,25 +943,25 @@ public class ComboBox : View, IDesignable
}
}
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
{
- if (_hideDropdownListOnClick)
+ if (newHasFocus)
{
- _isFocusing = true;
- _highlighted = _container.SelectedItem;
- Application.GrabMouse (this);
+ if (_hideDropdownListOnClick)
+ {
+ _isFocusing = true;
+ _highlighted = _container.SelectedItem;
+ Application.GrabMouse (this);
+ }
}
-
- return false; // Don't cancel the focus switch
- }
-
- protected override void OnHasFocusChanged (View view)
- {
- if (_hideDropdownListOnClick)
+ else
{
- _isFocusing = false;
- _highlighted = _container.SelectedItem;
- Application.UngrabMouse ();
+ if (_hideDropdownListOnClick)
+ {
+ _isFocusing = false;
+ _highlighted = _container.SelectedItem;
+ Application.UngrabMouse ();
+ }
}
}
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index daff465b7..955a21499 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -739,7 +739,7 @@ public class ListView : View, IDesignable
}
///
- protected override bool OnHasFocusChanging (View view)
+ protected override bool OnHasFocusChanging (bool currentHasFocus, bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
{
if (_lastSelectedItem != _selected)
{
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs
index 1de4f77b9..308a876d4 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -587,11 +587,9 @@ internal sealed class Menu : View
_host.Run (action);
}
- protected override void OnHasFocusChanged (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
_host.LostFocus (view);
-
- return;
}
private void RunSelected ()
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index e1a5f2d18..611da8112 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -392,7 +392,7 @@ public class MenuBar : View, IDesignable
_selected = 0;
SetNeedsDisplay ();
- _previousFocused = SuperView is null ? Application.Current?.Focused : SuperView.Focused;
+ _previousFocused = null;//SuperView is null ? Application.Current?.Focused : SuperView.Focused;
OpenMenu (_selected);
if (!SelectEnabledItem (
@@ -453,7 +453,7 @@ public class MenuBar : View, IDesignable
if (_openMenu is null)
{
- _previousFocused = SuperView is null ? Application.Current?.Focused ?? null : SuperView.Focused;
+ _previousFocused = null;//SuperView is null ? Application.Current?.Focused ?? null : SuperView.Focused;
}
OpenMenu (idx, sIdx, subMenu);
@@ -599,14 +599,13 @@ public class MenuBar : View, IDesignable
}
IsMenuOpen = false;
-
break;
case true:
_selectedSub = -1;
SetNeedsDisplay ();
RemoveAllOpensSubMenus ();
- OpenCurrentMenu._previousSubFocused.SetFocus ();
+ OpenCurrentMenu?._previousSubFocused.SetFocus ();
_openSubMenu = null;
IsMenuOpen = true;
@@ -756,7 +755,7 @@ public class MenuBar : View, IDesignable
{
case null:
// Open a submenu below a MenuBar
- _lastFocused ??= SuperView is null ? Application.Current?.MostFocused : SuperView.MostFocused;
+ _lastFocused = null;//Application.Navigation.GetFocused();// ??= SuperView is null ? Application.Current?.MostFocused : SuperView.MostFocused;
if (_openSubMenu is { } && !CloseMenu (false, true))
{
diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs
index 754b89061..7ae3bf485 100644
--- a/Terminal.Gui/Views/Shortcut.cs
+++ b/Terminal.Gui/Views/Shortcut.cs
@@ -834,22 +834,10 @@ public class Shortcut : View, IOrientation, IDesignable
}
}
- private View _lastFocusedView;
-
///
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
SetColors ();
- _lastFocusedView = view;
-
- return false; // Don't cancel the focus switch
- }
-
- ///
- protected override void OnHasFocusChanged (View view)
- {
- SetColors ();
- _lastFocusedView = this;
}
#endregion Focus
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index f52b02972..14b62fc63 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -1033,7 +1033,7 @@ public class TextField : View
}
///
- protected override void OnHasFocusChanged (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
{
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index 58b2c75b5..2e9db834e 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -3650,7 +3650,7 @@ public class TextView : View
}
///
- protected override void OnHasFocusChanged (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View previousFocusedView, View view)
{
if (Application.MouseGrabView is { } && Application.MouseGrabView == this)
{
diff --git a/Terminal.Gui/Views/TreeView/TreeView.cs b/Terminal.Gui/Views/TreeView/TreeView.cs
index 9b8da2bae..0468dd873 100644
--- a/Terminal.Gui/Views/TreeView/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView/TreeView.cs
@@ -1158,14 +1158,16 @@ public class TreeView : View, ITreeView where T : class
}
///
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View currentFocused, [CanBeNull] View newFocused)
{
- if (SelectedObject is null && Objects.Any ())
+ if (newHasFocus)
{
- SelectedObject = Objects.First ();
+ // If there is no selected object and there are objects in the tree, select the first one
+ if (SelectedObject is null && Objects.Any ())
+ {
+ SelectedObject = Objects.First ();
+ }
}
-
- return false; // Don't cancel the focus switch
}
///
diff --git a/UICatalog/Scenarios/ASCIICustomButton.cs b/UICatalog/Scenarios/ASCIICustomButton.cs
index 90f18eb87..e82d66398 100644
--- a/UICatalog/Scenarios/ASCIICustomButton.cs
+++ b/UICatalog/Scenarios/ASCIICustomButton.cs
@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
+using JetBrains.Annotations;
using Terminal.Gui;
namespace UICatalog.Scenarios;
@@ -110,23 +111,23 @@ public class ASCIICustomButtonTest : Scenario
Add (_border, _fill, title);
}
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
{
- _border.Visible = false;
- _fill.Visible = true;
- PointerEnter?.Invoke (this);
-
- return false; // don't cancel
- }
-
- protected override void OnHasFocusChanged (View view)
- {
- _border.Visible = true;
- _fill.Visible = false;
-
- if (view == null)
+ if (newHasFocus)
{
- view = this;
+ _border.Visible = false;
+ _fill.Visible = true;
+ PointerEnter?.Invoke (this);
+ }
+ else
+ {
+ _border.Visible = true;
+ _fill.Visible = false;
+
+ //if (view == null)
+ //{
+ // view = this;
+ //}
}
}
diff --git a/UICatalog/Scenarios/KeyBindings.cs b/UICatalog/Scenarios/KeyBindings.cs
index 2da18601c..ad38f8a95 100644
--- a/UICatalog/Scenarios/KeyBindings.cs
+++ b/UICatalog/Scenarios/KeyBindings.cs
@@ -82,8 +82,8 @@ public sealed class KeyBindings : Scenario
foreach (var appBinding in Application.KeyBindings.Bindings)
{
- var commands = Application.KeyBindings.GetCommands (appBinding.Key);
- appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
+ var commands = Application.KeyBindings.GetCommands (appBinding.Key);
+ appBindings.Add ($"{appBinding.Key} -> {appBinding.Value.BoundView?.GetType ().Name} - {commands [0]}");
}
ObservableCollection hotkeyBindings = new ();
@@ -125,8 +125,7 @@ public sealed class KeyBindings : Scenario
};
appWindow.Add (_focusedBindingsListView);
- appWindow.HasFocusChanged += AppWindow_Leave;
- appWindow.HasFocusChanging += AppWindow_Leave;
+ appWindow.HasFocusChanged += AppWindow_HasFocusChanged;
appWindow.DrawContent += AppWindow_DrawContent;
// Run - Start the application.
@@ -148,11 +147,14 @@ public sealed class KeyBindings : Scenario
}
}
- private void AppWindow_Leave (object sender, FocusEventArgs e)
+ private void AppWindow_HasFocusChanged (object sender, HasFocusEventArgs e)
{
- foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
+ if (e.NewValue)
{
- _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
+ foreach (var binding in Application.Top.MostFocused.KeyBindings.Bindings.Where (b => b.Value.Scope == KeyBindingScope.Focused))
+ {
+ _focusedBindings.Add ($"{binding.Key} -> {binding.Value.Commands [0]}");
+ }
}
}
}
diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs
index 784b21da5..70e977a29 100644
--- a/UnitTests/TestHelpers.cs
+++ b/UnitTests/TestHelpers.cs
@@ -240,6 +240,7 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute
public override void Before (MethodInfo methodUnderTest)
{
Debug.WriteLine ($"Before: {methodUnderTest.Name}");
+ Application.ResetState ();
Assert.Null (Application.Driver);
Application.Driver = new FakeDriver { Rows = 25, Cols = 25 };
base.Before (methodUnderTest);
diff --git a/UnitTests/View/Navigation/HasFocusChangeEventTests.cs b/UnitTests/View/Navigation/HasFocusChangeEventTests.cs
new file mode 100644
index 000000000..4a6167182
--- /dev/null
+++ b/UnitTests/View/Navigation/HasFocusChangeEventTests.cs
@@ -0,0 +1,740 @@
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.ViewTests;
+
+public class HasFocusChangeEventTests (ITestOutputHelper _output) : TestsAllViews
+{
+ [Fact]
+ public void HasFocusChanging_SetFocus_Raises ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ Assert.True (view.CanFocus);
+ Assert.False (view.HasFocus);
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+ }
+
+
+ [Fact]
+ public void HasFocusChanging_SetFocus_SubView_SetFocus_Raises ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ view.SetFocus ();
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+
+ [Fact]
+ public void HasFocusChanging_SetFocus_On_SubView_SubView_SetFocus_Raises ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ subview.SetFocus ();
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+ [Fact]
+ public void HasFocusChanging_SetFocus_CompoundSubView_Raises ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subViewEnterCount = 0;
+ var subViewLeaveCount = 0;
+
+ var subView = new View
+ {
+ Id = "subView",
+ CanFocus = true
+ };
+ subView.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subViewEnterCount++;
+ }
+ else
+ {
+ subViewLeaveCount++;
+ }
+ };
+
+ var subviewSubView1EnterCount = 0;
+ var subviewSubView1LeaveCount = 0;
+
+ var subViewSubView1 = new View
+ {
+ Id = "subViewSubView1",
+ CanFocus = false
+ };
+ subViewSubView1.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewSubView1EnterCount++;
+ }
+ else
+ {
+ subviewSubView1LeaveCount++;
+ }
+ };
+
+ var subviewSubView2EnterCount = 0;
+ var subviewSubView2LeaveCount = 0;
+
+ var subViewSubView2 = new View
+ {
+ Id = "subViewSubView2",
+ CanFocus = true
+ };
+ subViewSubView2.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewSubView2EnterCount++;
+ }
+ else
+ {
+ subviewSubView2EnterCount++;
+ }
+ };
+
+ var subviewSubView3EnterCount = 0;
+ var subviewSubView3LeaveCount = 0;
+
+ var subViewSubView3 = new View
+ {
+ Id = "subViewSubView3",
+ CanFocus = false
+ };
+ subViewSubView3.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewSubView3EnterCount++;
+ }
+ else
+ {
+ subviewSubView3LeaveCount++;
+ }
+ };
+
+ subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+ view.Add (subView);
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.True (subView.HasFocus);
+ Assert.False (subViewSubView1.HasFocus);
+ Assert.True (subViewSubView2.HasFocus);
+ Assert.False (subViewSubView3.HasFocus);
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subViewEnterCount);
+ Assert.Equal (0, subViewLeaveCount);
+
+ Assert.Equal (0, subviewSubView1EnterCount);
+ Assert.Equal (0, subviewSubView1LeaveCount);
+
+ Assert.Equal (1, subviewSubView2EnterCount);
+ Assert.Equal (0, subviewSubView2LeaveCount);
+
+ Assert.Equal (0, subviewSubView3EnterCount);
+ Assert.Equal (0, subviewSubView3LeaveCount);
+ }
+
+ [Fact]
+ public void HasFocusChanging_Can_Cancel ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ e.Cancel = true;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ view.SetFocus ();
+
+ Assert.False (view.HasFocus);
+ Assert.False (subview.HasFocus);
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (0, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+ [Fact]
+ public void HasFocusChanging_SetFocus_On_SubView_Can_Cancel ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ e.Cancel = true;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ subview.SetFocus ();
+
+ Assert.False (view.HasFocus);
+ Assert.False (subview.HasFocus);
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+ [Fact]
+ public void HasFocusChanging_SubView_Can_Cancel ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ e.Cancel = true;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ view.SetFocus ();
+
+ Assert.True (view.HasFocus);
+ Assert.False (subview.HasFocus);
+
+ Assert.Equal (1, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+
+ [Fact]
+ public void HasFocusChanging_SetFocus_On_Subview_SubView_Can_Cancel ()
+ {
+ var hasFocusTrueCount = 0;
+ var hasFocusFalseCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrueCount++;
+ }
+ else
+ {
+ hasFocusFalseCount++;
+ }
+ };
+
+ var subviewHasFocusTrueCount = 0;
+ var subviewHasFocusFalseCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ subviewHasFocusTrueCount++;
+ e.Cancel = true;
+ }
+ else
+ {
+ subviewHasFocusFalseCount++;
+ }
+ };
+
+ view.Add (subview);
+
+ subview.SetFocus ();
+
+ Assert.False (view.HasFocus);
+ Assert.False (subview.HasFocus);
+
+ Assert.Equal (0, hasFocusTrueCount);
+ Assert.Equal (0, hasFocusFalseCount);
+
+ Assert.Equal (1, subviewHasFocusTrueCount);
+ Assert.Equal (0, subviewHasFocusFalseCount);
+ }
+
+ [Fact]
+ public void RemoveFocus_Raises_HasFocusChanged ()
+ {
+ var nEnter = 0;
+ var nLeave = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) => nEnter++;
+ view.HasFocusChanged += (s, e) => nLeave++;
+
+ Assert.True (view.CanFocus);
+ Assert.False (view.HasFocus);
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.Equal (1, nEnter);
+ Assert.Equal (0, nLeave);
+
+ view.HasFocus = false;
+ Assert.Equal (1, nEnter);
+ Assert.Equal (1, nLeave);
+ }
+
+
+ [Fact]
+ public void RemoveFocus_SubView_Raises_HasFocusChanged ()
+ {
+ var viewEnterCount = 0;
+ var viewLeaveCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) => viewEnterCount++;
+ view.HasFocusChanged += (s, e) => viewLeaveCount++;
+
+ var subviewEnterCount = 0;
+ var subviewLeaveCount = 0;
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ subview.HasFocusChanging += (s, e) => subviewEnterCount++;
+ subview.HasFocusChanged += (s, e) => subviewLeaveCount++;
+
+ view.Add (subview);
+
+ view.SetFocus ();
+
+ view.HasFocus = false;
+
+ Assert.Equal (1, viewEnterCount);
+ Assert.Equal (1, viewLeaveCount);
+
+ Assert.Equal (1, subviewEnterCount);
+ Assert.Equal (1, subviewLeaveCount);
+
+ view.SetFocus ();
+
+ Assert.Equal (2, viewEnterCount);
+ Assert.Equal (1, viewLeaveCount);
+
+ Assert.Equal (2, subviewEnterCount);
+ Assert.Equal (1, subviewLeaveCount);
+
+ subview.HasFocus = false;
+
+ Assert.Equal (2, viewEnterCount);
+ Assert.Equal (2, viewLeaveCount);
+
+ Assert.Equal (2, subviewEnterCount);
+ Assert.Equal (2, subviewLeaveCount);
+ }
+
+ [Fact]
+ public void RemoveFocus_CompoundSubView_Raises_HasFocusChanged ()
+ {
+ var viewEnterCount = 0;
+ var viewLeaveCount = 0;
+
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ view.HasFocusChanging += (s, e) => viewEnterCount++;
+ view.HasFocusChanged += (s, e) => viewLeaveCount++;
+
+ var subViewEnterCount = 0;
+ var subViewLeaveCount = 0;
+
+ var subView = new View
+ {
+ Id = "subView",
+ CanFocus = true
+ };
+ subView.HasFocusChanging += (s, e) => subViewEnterCount++;
+ subView.HasFocusChanged += (s, e) => subViewLeaveCount++;
+
+ var subviewSubView1EnterCount = 0;
+ var subviewSubView1LeaveCount = 0;
+
+ var subViewSubView1 = new View
+ {
+ Id = "subViewSubView1",
+ CanFocus = false
+ };
+ subViewSubView1.HasFocusChanging += (s, e) => subviewSubView1EnterCount++;
+ subViewSubView1.HasFocusChanged += (s, e) => subviewSubView1LeaveCount++;
+
+ var subviewSubView2EnterCount = 0;
+ var subviewSubView2LeaveCount = 0;
+
+ var subViewSubView2 = new View
+ {
+ Id = "subViewSubView2",
+ CanFocus = true
+ };
+ subViewSubView2.HasFocusChanging += (s, e) => subviewSubView2EnterCount++;
+ subViewSubView2.HasFocusChanged += (s, e) => subviewSubView2LeaveCount++;
+
+ var subviewSubView3EnterCount = 0;
+ var subviewSubView3LeaveCount = 0;
+
+ var subViewSubView3 = new View
+ {
+ Id = "subViewSubView3",
+ CanFocus = false
+ };
+ subViewSubView3.HasFocusChanging += (s, e) => subviewSubView3EnterCount++;
+ subViewSubView3.HasFocusChanged += (s, e) => subviewSubView3LeaveCount++;
+
+ subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
+
+ view.Add (subView);
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.True (subView.HasFocus);
+ Assert.False (subViewSubView1.HasFocus);
+ Assert.True (subViewSubView2.HasFocus);
+ Assert.False (subViewSubView3.HasFocus);
+
+ view.HasFocus = false;
+ Assert.False (view.HasFocus);
+ Assert.False (subView.HasFocus);
+ Assert.False (subViewSubView1.HasFocus);
+ Assert.False (subViewSubView2.HasFocus);
+ Assert.False (subViewSubView3.HasFocus);
+
+ Assert.Equal (1, viewEnterCount);
+ Assert.Equal (1, viewLeaveCount);
+
+ Assert.Equal (1, subViewEnterCount);
+ Assert.Equal (1, subViewLeaveCount);
+
+ Assert.Equal (0, subviewSubView1EnterCount);
+ Assert.Equal (0, subviewSubView1LeaveCount);
+
+ Assert.Equal (1, subviewSubView2EnterCount);
+ Assert.Equal (1, subviewSubView2LeaveCount);
+
+ Assert.Equal (0, subviewSubView3EnterCount);
+ Assert.Equal (0, subviewSubView3LeaveCount);
+ }
+
+ [Fact]
+ public void HasFocus_False_Leave_Raised ()
+ {
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+ Assert.True (view.CanFocus);
+ Assert.False (view.HasFocus);
+
+ var leaveInvoked = 0;
+
+ view.HasFocusChanged += (s, e) => leaveInvoked++;
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.Equal (0, leaveInvoked);
+
+ view.HasFocus = false;
+ Assert.False (view.HasFocus);
+ Assert.Equal (1, leaveInvoked);
+ }
+
+ [Fact]
+ public void HasFocus_False_Leave_Raised_ForAllSubViews ()
+ {
+ var view = new View
+ {
+ Id = "view",
+ CanFocus = true
+ };
+
+ var subview = new View
+ {
+ Id = "subview",
+ CanFocus = true
+ };
+ view.Add (subview);
+
+ var leaveInvoked = 0;
+
+ view.HasFocusChanged += (s, e) => leaveInvoked++;
+ subview.HasFocusChanged += (s, e) => leaveInvoked++;
+
+ view.SetFocus ();
+ Assert.True (view.HasFocus);
+ Assert.Equal (0, leaveInvoked);
+
+ view.HasFocus = false;
+ Assert.False (view.HasFocus);
+ Assert.False (subview.HasFocus);
+ Assert.Equal (2, leaveInvoked);
+ }
+}
diff --git a/UnitTests/View/Navigation/HasFocusEventTests.cs b/UnitTests/View/Navigation/HasFocusEventTests.cs
deleted file mode 100644
index 35a1e23f0..000000000
--- a/UnitTests/View/Navigation/HasFocusEventTests.cs
+++ /dev/null
@@ -1,378 +0,0 @@
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewTests;
-
-public class HasFocusEventTests (ITestOutputHelper _output) : TestsAllViews
-{
- [Fact]
- public void SetFocus_Raises_HasFocusChanging ()
- {
- var nEnter = 0;
- var nLeave = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => nEnter++;
- view.HasFocusChanged += (s, e) => nLeave++;
-
- Assert.True (view.CanFocus);
- Assert.False (view.HasFocus);
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.Equal (1, nEnter);
- Assert.Equal (0, nLeave);
- }
-
- [Fact]
- public void RemoveFocus_Raises_HasFocusChanged ()
- {
- var nEnter = 0;
- var nLeave = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => nEnter++;
- view.HasFocusChanged += (s, e) => nLeave++;
-
- Assert.True (view.CanFocus);
- Assert.False (view.HasFocus);
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.Equal (1, nEnter);
- Assert.Equal (0, nLeave);
-
- view.HasFocus = false;
- Assert.Equal (1, nEnter);
- Assert.Equal (1, nLeave);
- }
-
- [Fact]
- public void SetFocus_SubView_SetFocus_Raises_HasFocusChanging ()
- {
- var viewEnterCount = 0;
- var viewLeaveCount = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => viewEnterCount++;
- view.HasFocusChanged += (s, e) => viewLeaveCount++;
-
- var subviewEnterCount = 0;
- var subviewLeaveCount = 0;
-
- var subview = new View
- {
- Id = "subview",
- CanFocus = true
- };
- subview.HasFocusChanging += (s, e) => subviewEnterCount++;
- subview.HasFocusChanged += (s, e) => subviewLeaveCount++;
-
- view.Add (subview);
-
- view.SetFocus ();
-
- Assert.Equal (1, viewEnterCount);
- Assert.Equal (0, viewLeaveCount);
-
- Assert.Equal (1, subviewEnterCount);
- Assert.Equal (0, subviewLeaveCount);
- }
-
- [Fact]
- public void RemoveFocus_SubView_Raises_HasFocusChanged ()
- {
- var viewEnterCount = 0;
- var viewLeaveCount = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => viewEnterCount++;
- view.HasFocusChanged += (s, e) => viewLeaveCount++;
-
- var subviewEnterCount = 0;
- var subviewLeaveCount = 0;
-
- var subview = new View
- {
- Id = "subview",
- CanFocus = true
- };
- subview.HasFocusChanging += (s, e) => subviewEnterCount++;
- subview.HasFocusChanged += (s, e) => subviewLeaveCount++;
-
- view.Add (subview);
-
- view.SetFocus ();
-
- view.HasFocus = false;
-
- Assert.Equal (1, viewEnterCount);
- Assert.Equal (1, viewLeaveCount);
-
- Assert.Equal (1, subviewEnterCount);
- Assert.Equal (1, subviewLeaveCount);
-
- view.SetFocus ();
-
- Assert.Equal (2, viewEnterCount);
- Assert.Equal (1, viewLeaveCount);
-
- Assert.Equal (2, subviewEnterCount);
- Assert.Equal (1, subviewLeaveCount);
-
- subview.HasFocus = false;
-
- Assert.Equal (2, viewEnterCount);
- Assert.Equal (2, viewLeaveCount);
-
- Assert.Equal (2, subviewEnterCount);
- Assert.Equal (2, subviewLeaveCount);
- }
-
- [Fact]
- public void SetFocus_CompoundSubView_Raises_HasFocusChanging ()
- {
- var viewEnterCount = 0;
- var viewLeaveCount = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => viewEnterCount++;
- view.HasFocusChanged += (s, e) => viewLeaveCount++;
-
- var subViewEnterCount = 0;
- var subViewLeaveCount = 0;
-
- var subView = new View
- {
- Id = "subView",
- CanFocus = true
- };
- subView.HasFocusChanging += (s, e) => subViewEnterCount++;
- subView.HasFocusChanged += (s, e) => subViewLeaveCount++;
-
- var subviewSubView1EnterCount = 0;
- var subviewSubView1LeaveCount = 0;
-
- var subViewSubView1 = new View
- {
- Id = "subViewSubView1",
- CanFocus = false
- };
- subViewSubView1.HasFocusChanging += (s, e) => subviewSubView1EnterCount++;
- subViewSubView1.HasFocusChanged += (s, e) => subviewSubView1LeaveCount++;
-
- var subviewSubView2EnterCount = 0;
- var subviewSubView2LeaveCount = 0;
-
- var subViewSubView2 = new View
- {
- Id = "subViewSubView2",
- CanFocus = true
- };
- subViewSubView2.HasFocusChanging += (s, e) => subviewSubView2EnterCount++;
- subViewSubView2.HasFocusChanged += (s, e) => subviewSubView2LeaveCount++;
-
- var subviewSubView3EnterCount = 0;
- var subviewSubView3LeaveCount = 0;
-
- var subViewSubView3 = new View
- {
- Id = "subViewSubView3",
- CanFocus = false
- };
- subViewSubView3.HasFocusChanging += (s, e) => subviewSubView3EnterCount++;
- subViewSubView3.HasFocusChanged += (s, e) => subviewSubView3LeaveCount++;
-
- subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
-
- view.Add (subView);
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.True (subView.HasFocus);
- Assert.False (subViewSubView1.HasFocus);
- Assert.True (subViewSubView2.HasFocus);
- Assert.False (subViewSubView3.HasFocus);
-
- Assert.Equal (1, viewEnterCount);
- Assert.Equal (0, viewLeaveCount);
-
- Assert.Equal (1, subViewEnterCount);
- Assert.Equal (0, subViewLeaveCount);
-
- Assert.Equal (0, subviewSubView1EnterCount);
- Assert.Equal (0, subviewSubView1LeaveCount);
-
- Assert.Equal (1, subviewSubView2EnterCount);
- Assert.Equal (0, subviewSubView2LeaveCount);
-
- Assert.Equal (0, subviewSubView3EnterCount);
- Assert.Equal (0, subviewSubView3LeaveCount);
- }
-
- [Fact]
- public void RemoveFocus_CompoundSubView_Raises_HasFocusChanged ()
- {
- var viewEnterCount = 0;
- var viewLeaveCount = 0;
-
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- view.HasFocusChanging += (s, e) => viewEnterCount++;
- view.HasFocusChanged += (s, e) => viewLeaveCount++;
-
- var subViewEnterCount = 0;
- var subViewLeaveCount = 0;
-
- var subView = new View
- {
- Id = "subView",
- CanFocus = true
- };
- subView.HasFocusChanging += (s, e) => subViewEnterCount++;
- subView.HasFocusChanged += (s, e) => subViewLeaveCount++;
-
- var subviewSubView1EnterCount = 0;
- var subviewSubView1LeaveCount = 0;
-
- var subViewSubView1 = new View
- {
- Id = "subViewSubView1",
- CanFocus = false
- };
- subViewSubView1.HasFocusChanging += (s, e) => subviewSubView1EnterCount++;
- subViewSubView1.HasFocusChanged += (s, e) => subviewSubView1LeaveCount++;
-
- var subviewSubView2EnterCount = 0;
- var subviewSubView2LeaveCount = 0;
-
- var subViewSubView2 = new View
- {
- Id = "subViewSubView2",
- CanFocus = true
- };
- subViewSubView2.HasFocusChanging += (s, e) => subviewSubView2EnterCount++;
- subViewSubView2.HasFocusChanged += (s, e) => subviewSubView2LeaveCount++;
-
- var subviewSubView3EnterCount = 0;
- var subviewSubView3LeaveCount = 0;
-
- var subViewSubView3 = new View
- {
- Id = "subViewSubView3",
- CanFocus = false
- };
- subViewSubView3.HasFocusChanging += (s, e) => subviewSubView3EnterCount++;
- subViewSubView3.HasFocusChanged += (s, e) => subviewSubView3LeaveCount++;
-
- subView.Add (subViewSubView1, subViewSubView2, subViewSubView3);
-
- view.Add (subView);
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.True (subView.HasFocus);
- Assert.False (subViewSubView1.HasFocus);
- Assert.True (subViewSubView2.HasFocus);
- Assert.False (subViewSubView3.HasFocus);
-
- view.HasFocus = false;
- Assert.False (view.HasFocus);
- Assert.False (subView.HasFocus);
- Assert.False (subViewSubView1.HasFocus);
- Assert.False (subViewSubView2.HasFocus);
- Assert.False (subViewSubView3.HasFocus);
-
- Assert.Equal (1, viewEnterCount);
- Assert.Equal (1, viewLeaveCount);
-
- Assert.Equal (1, subViewEnterCount);
- Assert.Equal (1, subViewLeaveCount);
-
- Assert.Equal (0, subviewSubView1EnterCount);
- Assert.Equal (0, subviewSubView1LeaveCount);
-
- Assert.Equal (1, subviewSubView2EnterCount);
- Assert.Equal (1, subviewSubView2LeaveCount);
-
- Assert.Equal (0, subviewSubView3EnterCount);
- Assert.Equal (0, subviewSubView3LeaveCount);
- }
-
- [Fact]
- public void HasFocus_False_Leave_Raised ()
- {
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
- Assert.True (view.CanFocus);
- Assert.False (view.HasFocus);
-
- var leaveInvoked = 0;
-
- view.HasFocusChanged += (s, e) => leaveInvoked++;
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.Equal (0, leaveInvoked);
-
- view.HasFocus = false;
- Assert.False (view.HasFocus);
- Assert.Equal (1, leaveInvoked);
- }
-
- [Fact]
- public void HasFocus_False_Leave_Raised_ForAllSubViews ()
- {
- var view = new View
- {
- Id = "view",
- CanFocus = true
- };
-
- var subview = new View
- {
- Id = "subview",
- CanFocus = true
- };
- view.Add (subview);
-
- var leaveInvoked = 0;
-
- view.HasFocusChanged += (s, e) => leaveInvoked++;
- subview.HasFocusChanged += (s, e) => leaveInvoked++;
-
- view.SetFocus ();
- Assert.True (view.HasFocus);
- Assert.Equal (0, leaveInvoked);
-
- view.HasFocus = false;
- Assert.False (view.HasFocus);
- Assert.False (subview.HasFocus);
- Assert.Equal (2, leaveInvoked);
- }
-}
diff --git a/UnitTests/View/Navigation/NavigationTests.cs b/UnitTests/View/Navigation/NavigationTests.cs
index 297de677c..d89b4e0f8 100644
--- a/UnitTests/View/Navigation/NavigationTests.cs
+++ b/UnitTests/View/Navigation/NavigationTests.cs
@@ -1,4 +1,5 @@
-using Xunit.Abstractions;
+using JetBrains.Annotations;
+using Xunit.Abstractions;
using static System.Net.Mime.MediaTypeNames;
namespace Terminal.Gui.ViewTests;
@@ -87,7 +88,8 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
[Theory]
[MemberData (nameof (AllViewTypes))]
- public void AllViews_Enter_Leave_Events (Type viewType)
+ [SetupFakeDriver]
+ public void AllViews_HasFocus_Changed_Event (Type viewType)
{
View view = CreateInstanceIfNotGeneric (viewType);
@@ -112,13 +114,13 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
return;
}
- Application.Init (new FakeDriver ());
-
Toplevel top = new ()
{
Height = 10,
Width = 10
};
+ Application.Current = top;
+ Application.Navigation = new ApplicationNavigation();
View otherView = new ()
{
@@ -135,25 +137,34 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
view.Width = 10;
view.Height = 1;
- var nEnter = 0;
- var nLeave = 0;
+ var hasFocusTrue = 0;
+ var hasFocusFalse = 0;
- view.HasFocusChanging += (s, e) => nEnter++;
- view.HasFocusChanged += (s, e) => nLeave++;
+ view.HasFocusChanged += (s, e) =>
+ {
+ if (e.NewValue)
+ {
+ hasFocusTrue++;
+ }
+ else
+ {
+ hasFocusFalse++;
+ }
+ };
top.Add (view, otherView);
Assert.False (view.HasFocus);
Assert.False (otherView.HasFocus);
- Application.Begin (top);
+ Application.Current.SetFocus ();
Assert.True (Application.Current!.HasFocus);
Assert.True (top.HasFocus);
// Start with the focus on our test view
Assert.True (view.HasFocus);
- Assert.Equal (1, nEnter);
- Assert.Equal (0, nLeave);
+ Assert.Equal (1, hasFocusTrue);
+ Assert.Equal (0, hasFocusFalse);
// Use keyboard to navigate to next view (otherView).
var tries = 0;
@@ -189,8 +200,8 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
}
}
- Assert.Equal (1, nEnter);
- Assert.Equal (1, nLeave);
+ Assert.Equal (1, hasFocusTrue);
+ Assert.Equal (1, hasFocusFalse);
Assert.False (view.HasFocus);
Assert.True (otherView.HasFocus);
@@ -218,8 +229,8 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
throw new ArgumentOutOfRangeException ();
}
- Assert.Equal (2, nEnter);
- Assert.Equal (1, nLeave);
+ Assert.Equal (2, hasFocusTrue);
+ Assert.Equal (1, hasFocusFalse);
Assert.True (view.HasFocus);
Assert.False (otherView.HasFocus);
@@ -229,17 +240,18 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews
bool otherViewHasFocus = otherView.HasFocus;
bool viewHasFocus = view.HasFocus;
- int enterCount = nEnter;
- int leaveCount = nLeave;
+ int enterCount = hasFocusTrue;
+ int leaveCount = hasFocusFalse;
top.Dispose ();
- Application.Shutdown ();
Assert.False (otherViewHasFocus);
Assert.True (viewHasFocus);
Assert.Equal (2, enterCount);
Assert.Equal (1, leaveCount);
+
+ Application.ResetState ();
}
[Theory]
diff --git a/UnitTests/View/Navigation/RestoreFocusTests.cs b/UnitTests/View/Navigation/RestoreFocusTests.cs
index 6e7a1fdd7..4fe26722e 100644
--- a/UnitTests/View/Navigation/RestoreFocusTests.cs
+++ b/UnitTests/View/Navigation/RestoreFocusTests.cs
@@ -106,13 +106,15 @@ public class RestoreFocusTests (ITestOutputHelper _output) : TestsAllViews
var tabGroup1SubView1 = new View
{
Id = "tabGroup1SubView1",
- CanFocus = true
+ CanFocus = true,
+ TabStop = TabBehavior.TabStop
};
var tabGroup1SubView2 = new View
{
Id = "tabGroup1SubView2",
- CanFocus = true
+ CanFocus = true,
+ TabStop = TabBehavior.TabStop
};
tabGroup1.Add (tabGroup1SubView1, tabGroup1SubView2);
@@ -126,13 +128,15 @@ public class RestoreFocusTests (ITestOutputHelper _output) : TestsAllViews
var tabGroup2SubView1 = new View
{
Id = "tabGroup2SubView1",
- CanFocus = true
+ CanFocus = true,
+ TabStop = TabBehavior.TabStop
};
var tabGroup2SubView2 = new View
{
Id = "tabGroup2SubView2",
- CanFocus = true
+ CanFocus = true,
+ TabStop = TabBehavior.TabStop
};
tabGroup2.Add (tabGroup2SubView1, tabGroup2SubView2);
diff --git a/UnitTests/Views/ContextMenuTests.cs b/UnitTests/Views/ContextMenuTests.cs
index 99f9e3dfe..f26fff6bd 100644
--- a/UnitTests/Views/ContextMenuTests.cs
+++ b/UnitTests/Views/ContextMenuTests.cs
@@ -1188,7 +1188,13 @@ public class ContextMenuTests (ITestOutputHelper output)
};
Toplevel top = new ();
RunState rs = Application.Begin (top);
+ top.SetFocus ();
+ Assert.NotNull (Application.Current);
+
cm.Show ();
+ Assert.True(ContextMenu.IsShow);
+ Assert.True (Application.Top.Subviews [0].HasFocus);
+ Assert.Equal(Application.Top.Subviews [0], Application.Navigation.GetFocused());
Assert.Equal (new Rectangle (5, 11, 10, 5), Application.Top.Subviews [0].Frame);
Application.Refresh ();
diff --git a/UnitTests/Views/OverlappedTests.cs b/UnitTests/Views/OverlappedTests.cs
index c0f118fa1..0ce8c16b2 100644
--- a/UnitTests/Views/OverlappedTests.cs
+++ b/UnitTests/Views/OverlappedTests.cs
@@ -1,5 +1,6 @@
#nullable enable
using System.Threading;
+using JetBrains.Annotations;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
@@ -1277,14 +1278,10 @@ public class OverlappedTests
{
public bool IsFocused { get; private set; }
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedVew)
{
- IsFocused = true;
-
- return false;
+ IsFocused = newHasFocus;
}
-
- protected override void OnHasFocusChanged (View view) { IsFocused = false; }
}
private class TestView : View
@@ -1295,16 +1292,9 @@ public class OverlappedTests
}
public bool IsFocused { get; private set; }
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? focusedVew)
{
- IsFocused = true;
-
- return false; // don't cancel
- }
-
- protected override void OnHasFocusChanged (View view)
- {
- IsFocused = false;
+ IsFocused = newHasFocus;
}
}
}
diff --git a/UnitTests/Views/ScrollViewTests.cs b/UnitTests/Views/ScrollViewTests.cs
index 41e9def18..5a1337b0e 100644
--- a/UnitTests/Views/ScrollViewTests.cs
+++ b/UnitTests/Views/ScrollViewTests.cs
@@ -1,4 +1,5 @@
using System.Text;
+using JetBrains.Annotations;
using Xunit.Abstractions;
namespace Terminal.Gui.ViewsTests;
@@ -1119,25 +1120,19 @@ public class ScrollViewTests (ITestOutputHelper output)
CanFocus = true;
}
- protected override bool OnHasFocusChanging (View view)
+ protected override void OnHasFocusChanged (bool newHasFocus, [CanBeNull] View previousFocusedView, [CanBeNull] View focusedVew)
{
- Border.LineStyle = LineStyle.None;
- Border.Thickness = new (0);
- labelFill.Visible = true;
- view = this;
-
- return false; // don't cancel
- }
-
- protected override void OnHasFocusChanged (View view)
- {
- Border.LineStyle = LineStyle.Single;
- Border.Thickness = new (1);
- labelFill.Visible = false;
-
- if (view == null)
+ if (newHasFocus)
{
- view = this;
+ Border.LineStyle = LineStyle.None;
+ Border.Thickness = new (0);
+ labelFill.Visible = true;
+ }
+ else
+ {
+ Border.LineStyle = LineStyle.Single;
+ Border.Thickness = new (1);
+ labelFill.Visible = false;
}
}
}