diff --git a/Terminal.Gui/View/View.Mouse.cs b/Terminal.Gui/View/View.Mouse.cs index 22b637794..67b3a9c8a 100644 --- a/Terminal.Gui/View/View.Mouse.cs +++ b/Terminal.Gui/View/View.Mouse.cs @@ -351,7 +351,7 @@ public partial class View // Mouse APIs // BUGBUG: This should be named NewMouseClickEvent. Fix this in https://github.com/gui-cs/Terminal.Gui/issues/3029 // Pre-conditions - if (!Enabled) + if (!Enabled || !CanFocus) { // QUESTION: Is this right? Should a disabled view eat mouse clicks? return args.Handled = false; diff --git a/Terminal.Gui/View/View.Navigation.cs b/Terminal.Gui/View/View.Navigation.cs index 2ac8d540d..a8a2022aa 100644 --- a/Terminal.Gui/View/View.Navigation.cs +++ b/Terminal.Gui/View/View.Navigation.cs @@ -421,7 +421,7 @@ public partial class View // Focus and cross-view navigation management (TabStop /// private (bool focusSet, bool cancelled) SetHasFocusTrue (View? previousFocusedView, bool traversingUp = false) { - Debug.Assert (ApplicationNavigation.IsInHierarchy (SuperView, this)); + Debug.Assert (SuperView is null || ApplicationNavigation.IsInHierarchy (SuperView, this)); // Pre-conditions if (_hasFocus) diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index d30ced79e..e7f9e5eb9 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -32,6 +32,7 @@ public class Bar : View, IOrientation, IDesignable _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); Initialized += Bar_Initialized; + MouseEvent += OnMouseEvent; if (shortcuts is null) { @@ -44,6 +45,38 @@ public class Bar : View, IOrientation, IDesignable } } + private void OnMouseEvent (object? sender, MouseEventEventArgs e) + { + NavigationDirection direction = NavigationDirection.Backward; + + if (e.MouseEvent.Flags == MouseFlags.WheeledDown) + { + e.Handled = true; + } + + if (e.MouseEvent.Flags == MouseFlags.WheeledUp) + { + direction = NavigationDirection.Forward; + e.Handled = true; + } + + if (e.MouseEvent.Flags == MouseFlags.WheeledRight) + { + e.Handled = true; + } + + if (e.MouseEvent.Flags == MouseFlags.WheeledLeft) + { + direction = NavigationDirection.Forward; + e.Handled = true; + } + + if (e.Handled) + { + e.Handled = AdvanceFocus (direction, TabBehavior.TabStop); + } + } + private void Bar_Initialized (object? sender, EventArgs e) { ColorScheme = Colors.ColorSchemes ["Menu"]; } /// @@ -243,6 +276,7 @@ public class Bar : View, IOrientation, IDesignable } } + /// public bool EnableForDesign () { diff --git a/Terminal.Gui/Views/CheckBox.cs b/Terminal.Gui/Views/CheckBox.cs index 8b7154460..763a0d992 100644 --- a/Terminal.Gui/Views/CheckBox.cs +++ b/Terminal.Gui/Views/CheckBox.cs @@ -26,7 +26,7 @@ public class CheckBox : View AddCommand (Command.Select, AdvanceCheckState); // Accept (Enter key and double-click) - Raise Accept event - DO NOT advance state - AddCommand (Command.Accept, RaiseAcceptEvent); + AddCommand (Command.Accept, () => RaiseAcceptEvent ()); // Hotkey - Advance state and raise Select event - DO NOT raise Accept AddCommand (Command.HotKey, AdvanceCheckState); @@ -45,7 +45,9 @@ public class CheckBox : View if (e.MouseEvent.Flags.HasFlag (MouseFlags.Button1Clicked)) { - e.Handled = AdvanceCheckState () == true; + AdvanceCheckState (); + ; + // e.Handled = AdvanceCheckState () == true; } #if CHECKBOX_SUPPORTS_DOUBLE_CLICK_ACCEPT @@ -147,11 +149,6 @@ public class CheckBox : View return null; } - if (RaiseSelectEvent () == true) - { - return true; - } - CancelEventArgs e = new (in _checkedState, ref value); if (OnCheckedStateChanging (e)) @@ -174,6 +171,11 @@ public class CheckBox : View CheckedStateChanged?.Invoke (this, args); + if (RaiseSelectEvent () == true) + { + return true; + } + return false; } @@ -183,7 +185,7 @@ public class CheckBox : View /// The state cahnge can be cancelled by setting the args.Cancel to . /// /// - protected virtual bool OnCheckedStateChanging (CancelEventArgs args) { return false;} + protected virtual bool OnCheckedStateChanging (CancelEventArgs args) { return false; } /// Raised when the state is changing. /// @@ -241,7 +243,7 @@ public class CheckBox : View bool? cancelled = ChangeCheckedState (e.NewValue); - return !cancelled; + return cancelled; } /// diff --git a/Terminal.Gui/Views/Menuv2.cs b/Terminal.Gui/Views/Menuv2.cs index 59debbf0b..a07be7073 100644 --- a/Terminal.Gui/Views/Menuv2.cs +++ b/Terminal.Gui/Views/Menuv2.cs @@ -17,13 +17,30 @@ public class Menuv2 : Bar Orientation = Orientation.Vertical; Width = Dim.Auto (); Height = Dim.Auto (DimAutoStyle.Content, 1); - ColorScheme = Colors.ColorSchemes ["Menu"]; Initialized += Menuv2_Initialized; + VisibleChanged += OnVisibleChanged; + } + + private void OnVisibleChanged (object sender, EventArgs e) + { + if (Visible) + { + //Application.GrabMouse(this); + } + else + { + if (Application.MouseGrabView == this) + { + //Application.UngrabMouse (); + } + } } private void Menuv2_Initialized (object sender, EventArgs e) { Border.Thickness = new Thickness (1, 1, 1, 1); + Border.LineStyle = LineStyle.Single; + ColorScheme = Colors.ColorSchemes ["Menu"]; } // Menuv2 arranges the items horizontally. @@ -52,7 +69,6 @@ public class Menuv2 : Bar if (view is Shortcut shortcut) { shortcut.CanFocus = true; - shortcut.KeyBindingScope = KeyBindingScope.Application; shortcut.Orientation = Orientation.Vertical; shortcut.HighlightStyle |= HighlightStyle.Hover; @@ -64,20 +80,18 @@ public class Menuv2 : Bar void ShortcutOnAccept (object sender, HandledEventArgs e) { - if (Arrangement.HasFlag(ViewArrangement.Overlapped) && Visible) + if (Arrangement.HasFlag (ViewArrangement.Overlapped) && Visible) { Visible = false; e.Handled = true; return; - - //Enabled = Visible; } - if (!e.Handled) - { - RaiseAcceptEvent (); - } + //if (!e.Handled) + //{ + // RaiseAcceptEvent (); + //} } } diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 5967b6253..d79c09cb3 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -70,7 +70,22 @@ public class RadioGroup : View, IDesignable, IOrientation return true; } ); + AddCommand ( + Command.Select, + () => + { + if (SelectedItem == Cursor) + { + if (!MoveDownRight ()) + { + MoveHome (); + } + } + SelectedItem = Cursor; + + return true; + }); AddCommand ( Command.Select, () => diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 2a56de929..83e92b81b 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -102,7 +102,7 @@ public class Shortcut : View, IOrientation, IDesignable AddCommand (Command.HotKey, ctx => DispatchAcceptCommand (ctx)); AddCommand (Command.Accept, ctx => DispatchAcceptCommand (ctx)); - AddCommand (Command.Select, ctx => OnSelect (ctx)); + AddCommand (Command.Select, ctx => RaiseSelectEvent ()); TitleChanged += Shortcut_TitleChanged; // This needs to be set before CommandView is set @@ -123,8 +123,8 @@ public class Shortcut : View, IOrientation, IDesignable // If the user clicks anywhere on the Shortcut, other than the CommandView, invoke the Command MouseClick += Shortcut_MouseClick; - HelpView.MouseClick += Subview_MouseClick; - KeyView.MouseClick += Subview_MouseClick; + //HelpView.MouseClick += Subview_MouseClick; + //KeyView.MouseClick += Subview_MouseClick; LayoutStarted += OnLayoutStarted; Initialized += OnInitialized; @@ -375,8 +375,8 @@ public class Shortcut : View, IOrientation, IDesignable if (!e.Handled) { - // If the subview (likely CommandView) didn't handle the mouse click, invoke the command. - e.Handled = InvokeCommand (Command.Accept) == true; + // If the subview (likely CommandView) didn't handle the mouse click, invoke the Select command. + e.Handled = CommandView.InvokeCommand (Command.Accept) == true; } if (CanFocus) @@ -387,7 +387,11 @@ public class Shortcut : View, IOrientation, IDesignable private void Subview_MouseClick (object sender, MouseEventEventArgs e) { - // TODO: Remove. This does nothing. + if (!e.Handled) + { + // If the subview (likely CommandView) didn't handle the mouse click, invoke the command. + e.Handled = InvokeCommand (Command.Accept) == true; + } } #region IOrientation members @@ -520,10 +524,23 @@ public class Shortcut : View, IOrientation, IDesignable _commandView.Select += CommandViewOnSelect; + _commandView.MouseClick += CommandViewOnMouseClick; + + void CommandViewOnMouseClick (object sender, MouseEventEventArgs e) + { + //if (!e.Handled) + { + // If the subview (likely CommandView) didn't handle the mouse click, invoke the command. + InvokeCommand (Command.HotKey); + } + } + void CommandViewOnSelect (object sender, HandledEventArgs e) { - SetFocus (); - //OnAccept (); + //e.Handled = true; + RaiseAcceptEvent (); + + // SetFocus (); } SetCommandViewDefaultLayout (); @@ -769,10 +786,10 @@ public class Shortcut : View, IOrientation, IDesignable cancel = RaiseAcceptEvent () == true; - if (!cancel && ctx.KeyBinding?.BoundView != CommandView) - { - // CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding); - } + //if (!cancel && ctx.KeyBinding?.BoundView != CommandView) + //{ + // // CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding); + //} if (Action is { }) { diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs index dea851446..dd97f6571 100644 --- a/Terminal.Gui/Views/TextView.cs +++ b/Terminal.Gui/Views/TextView.cs @@ -2460,8 +2460,8 @@ public class TextView : View KeyBindings.Add (Key.C.WithCtrl, Command.Copy); - KeyBindings.Add (Key.W.WithCtrl, Command.Cut); - KeyBindings.Add (Key.X.WithCtrl, Command.Cut); + KeyBindings.Add (Key.W.WithCtrl, Command.Cut); // Move to Unix? + KeyBindings.Add (Key.X.WithCtrl, Command.Cut); KeyBindings.Add (Key.CursorLeft.WithCtrl, Command.WordLeft); @@ -2495,7 +2495,6 @@ public class TextView : View KeyBindings.Add (Key.V.WithAlt, Command.PageUp); KeyBindings.Add (Key.F.WithAlt, Command.WordRight); KeyBindings.Add (Key.K.WithAlt, Command.CutToStartLine); // kill-to-start - #endif _currentCulture = Thread.CurrentThread.CurrentUICulture; diff --git a/UICatalog/Scenarios/Shortcuts.cs b/UICatalog/Scenarios/Shortcuts.cs index 1116551d2..e843bdc62 100644 --- a/UICatalog/Scenarios/Shortcuts.cs +++ b/UICatalog/Scenarios/Shortcuts.cs @@ -38,13 +38,13 @@ public class Shortcuts : Scenario var eventLog = new ListView { X = Pos.AnchorEnd (), - Width = 40, Height = Dim.Fill (4), ColorScheme = Colors.ColorSchemes ["Toplevel"], Source = new ListWrapper (eventSource), BorderStyle = LineStyle.Double, Title = "E_vents" }; + eventLog.Width = Dim.Func (() => Math.Min (Application.Top.Viewport.Width / 2, eventLog?.MaxLength + eventLog.GetAdornmentsThickness ().Horizontal ?? 0)); Application.Top.Add (eventLog); var vShortcut1 = new Shortcut @@ -72,15 +72,16 @@ public class Shortcuts : Scenario CommandView = new RadioGroup { Orientation = Orientation.Vertical, - RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"] + RadioLabels = ["O_ne", "T_wo", "Th_ree", "Fo_ur"], + CanFocus = false }, }; ((RadioGroup)vShortcut2.CommandView).SelectedItemChanged += (o, args) => - { - eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}"); - eventLog.MoveDown (); - }; + { + eventSource.Add ($"SelectedItemChanged: {o.GetType ().Name} - {args.SelectedItem}"); + eventLog.MoveDown (); + }; Application.Top.Add (vShortcut2); @@ -89,7 +90,12 @@ public class Shortcuts : Scenario Orientation = Orientation.Vertical, X = 0, Y = Pos.Bottom (vShortcut2), - CommandView = new CheckBox { Text = "_Align" }, + CommandView = new CheckBox + { + Text = "_Align", + CanFocus = false, + HighlightStyle = HighlightStyle.None, + }, Key = Key.F5.WithCtrl.WithAlt.WithShift, HelpText = "Width is Fill", Width = Dim.Fill () - Dim.Width (eventLog), @@ -97,30 +103,30 @@ public class Shortcuts : Scenario }; ((CheckBox)vShortcut3.CommandView).CheckedStateChanging += (s, e) => - { - if (vShortcut3.CommandView is CheckBox cb) - { - eventSource.Add ($"Toggle: {cb.Text}"); - eventLog.MoveDown (); + { + if (vShortcut3.CommandView is CheckBox cb) + { + eventSource.Add ($"{vShortcut3.Id}.CommandView.CheckedStateChanging: {cb.Text}"); + eventLog.MoveDown (); - var max = 0; - var toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Orientation: Orientation.Vertical, Width: not DimAbsolute }); + var max = 0; + var toAlign = Application.Top.Subviews.Where (v => v is Shortcut { Orientation: Orientation.Vertical, Width: not DimAbsolute }); - if (e.NewValue == CheckState.Checked) - { - foreach (Shortcut peer in toAlign) - { - // DANGER: KeyView is internal so we can't access it. So we assume this is how it works. - max = Math.Max (max, peer.Key.ToString ().GetColumns ()); - } - } + if (e.NewValue == CheckState.Checked) + { + foreach (Shortcut peer in toAlign) + { + // DANGER: KeyView is internal so we can't access it. So we assume this is how it works. + max = Math.Max (max, peer.Key.ToString ().GetColumns ()); + } + } - foreach (Shortcut peer in toAlign) - { - peer.MinimumKeyTextSize = max; - } - } - }; + foreach (Shortcut peer in toAlign) + { + peer.MinimumKeyTextSize = max; + } + } + }; Application.Top.Add (vShortcut3); var vShortcut4 = new Shortcut @@ -132,6 +138,8 @@ public class Shortcuts : Scenario CommandView = new Button { Title = "_Button", + ShadowStyle = ShadowStyle.None, + HighlightStyle = HighlightStyle.None }, HelpText = "Width is Fill", Key = Key.K, @@ -156,21 +164,21 @@ public class Shortcuts : Scenario }; ((CheckBox)vShortcut5.CommandView).CheckedStateChanging += (s, e) => - { - if (vShortcut5.CommandView is CheckBox cb) - { - eventSource.Add ($"Toggle: {cb.Text}"); - eventLog.MoveDown (); + { + if (vShortcut5.CommandView is CheckBox cb) + { + eventSource.Add ($"Toggle: {cb.Text}"); + eventLog.MoveDown (); - foreach (Shortcut peer in Application.Top.Subviews.Where (v => v is Shortcut)!) - { - if (peer.CanFocus) - { - peer.CommandView.CanFocus = e.NewValue == CheckState.Checked; - } - } - } - }; + //foreach (Shortcut peer in Application.Top.Subviews.Where (v => v is Shortcut)!) + //{ + // if (peer.CanFocus) + // { + // peer.CommandView.CanFocus = e.NewValue == CheckState.Checked; + // } + //} + } + }; Application.Top.Add (vShortcut5); var vShortcutSlider = new Shortcut @@ -194,10 +202,10 @@ public class Shortcuts : Scenario ((Slider)vShortcutSlider.CommandView).SetOption (0); ((Slider)vShortcutSlider.CommandView).OptionsChanged += (o, args) => - { - eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider)o).GetSetOptions ())}"); - eventLog.MoveDown (); - }; + { + eventSource.Add ($"OptionsChanged: {o.GetType ().Name} - {string.Join (",", ((Slider)o).GetSetOptions ())}"); + eventLog.MoveDown (); + }; Application.Top.Add (vShortcutSlider); @@ -258,20 +266,20 @@ public class Shortcuts : Scenario AutoReset = true, }; timer.Elapsed += (o, args) => - { - if (hShortcut1.CommandView is ProgressBar pb) - { - if (pb.Fraction == 1.0) - { - pb.Fraction = 0; - } - pb.Fraction += 0.01f; + { + if (hShortcut1.CommandView is ProgressBar pb) + { + if (pb.Fraction == 1.0) + { + pb.Fraction = 0; + } + pb.Fraction += 0.01f; - Application.Wakeup (); + Application.Wakeup (); - pb.SetNeedsDisplay (); - } - }; + pb.SetNeedsDisplay (); + } + }; timer.Start (); Application.Top.Add (hShortcut1); @@ -314,12 +322,12 @@ public class Shortcuts : Scenario CanFocus = false }; bgColor.ColorChanged += (o, args) => - { - Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme) - { - Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.CurrentValue), - }; - }; + { + Application.Top.ColorScheme = new ColorScheme (Application.Top.ColorScheme) + { + Normal = new Attribute (Application.Top.ColorScheme.Normal.Foreground, args.CurrentValue), + }; + }; hShortcutBG.CommandView = bgColor; Application.Top.Add (hShortcutBG); @@ -336,9 +344,9 @@ public class Shortcuts : Scenario CanFocus = false }; hShortcut3.Accept += (o, args) => - { - Application.RequestStop (); - }; + { + Application.RequestStop (); + }; Application.Top.Add (hShortcut3); @@ -347,30 +355,30 @@ public class Shortcuts : Scenario if (sh is Shortcut shortcut) { shortcut.Select += (o, args) => - { - eventSource.Add ($"Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); - eventLog.MoveDown (); - args.Handled = true; - }; + { + eventSource.Add ($"{shortcut!.Id}.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); + eventLog.MoveDown (); + args.Handled = true; + }; shortcut.CommandView.Select += (o, args) => - { - eventSource.Add ($"CommandView.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); - eventLog.MoveDown (); - }; + { + eventSource.Add ($"{shortcut!.Id}.CommandView.Select: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); + eventLog.MoveDown (); + }; shortcut.Accept += (o, args) => - { - eventSource.Add ($"Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); - eventLog.MoveDown (); - args.Handled = true; - }; + { + eventSource.Add ($"{shortcut!.Id}.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); + eventLog.MoveDown (); + args.Handled = true; + }; shortcut.CommandView.Accept += (o, args) => - { - eventSource.Add ($"CommandView.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); - eventLog.MoveDown (); - }; + { + eventSource.Add ($"{shortcut!.Id}.CommandView.Accept: {shortcut!.CommandView.Text} {shortcut!.CommandView.GetType ().Name}"); + eventLog.MoveDown (); + }; } } @@ -380,6 +388,7 @@ public class Shortcuts : Scenario private void Button_Clicked (object sender, HandledEventArgs e) { e.Handled = true; - MessageBox.Query ("Hi", $"You clicked {sender}"); + View view = sender as View; + MessageBox.Query ("Hi", $"You clicked {view!.Text}", "_Ok"); } } diff --git a/UnitTests/Input/KeyBindingTests.cs b/UnitTests/Input/KeyBindingTests.cs index fe3d137e7..e5628da5a 100644 --- a/UnitTests/Input/KeyBindingTests.cs +++ b/UnitTests/Input/KeyBindingTests.cs @@ -103,7 +103,9 @@ public class KeyBindingTests public void Defaults () { var keyBindings = new KeyBindings (); - Assert.Throws (() => keyBindings.GetKeyFromCommands (Command.Accept)); + Assert.Empty (keyBindings.Bindings); + Assert.Null (keyBindings.GetKeyFromCommands (Command.Accept)); + Assert.Null (keyBindings.BoundView); } [Fact] @@ -173,9 +175,6 @@ public class KeyBindingTests key = keyBindings.GetKeyFromCommands (commands2); Assert.Equal (Key.B, key); - - // Negative case - Assert.Throws (() => key = keyBindings.GetKeyFromCommands (Command.RightEnd)); } [Fact] @@ -186,17 +185,14 @@ public class KeyBindingTests Key key = keyBindings.GetKeyFromCommands (Command.Right); Assert.Equal (Key.A, key); - - // Negative case - Assert.Throws (() => key = keyBindings.GetKeyFromCommands (Command.Left)); } // GetKeyFromCommands [Fact] - public void GetKeyFromCommands_Unknown_Throws_InvalidOperationException () + public void GetKeyFromCommands_Unknown_Returns_Key_Empty () { var keyBindings = new KeyBindings (); - Assert.Throws (() => keyBindings.GetKeyFromCommands (Command.Accept)); + Assert.Null (keyBindings.GetKeyFromCommands (Command.Accept)); } [Fact] diff --git a/UnitTests/TestHelpers.cs b/UnitTests/TestHelpers.cs index 265037cb5..88224d8e3 100644 --- a/UnitTests/TestHelpers.cs +++ b/UnitTests/TestHelpers.cs @@ -48,7 +48,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute bool useFakeClipboard = true, bool fakeClipboardAlwaysThrowsNotSupportedException = false, bool fakeClipboardIsSupportedAlwaysTrue = false, - ConfigurationManager.ConfigLocations configLocation = ConfigurationManager.ConfigLocations.None + ConfigLocations configLocation = ConfigLocations.None ) { AutoInit = autoInit; @@ -59,7 +59,7 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute FakeDriver.FakeBehaviors.FakeClipboardAlwaysThrowsNotSupportedException = fakeClipboardAlwaysThrowsNotSupportedException; FakeDriver.FakeBehaviors.FakeClipboardIsSupportedAlwaysFalse = fakeClipboardIsSupportedAlwaysTrue; - ConfigurationManager.Locations = configLocation; + Locations = configLocation; } private readonly Type _driverType; @@ -73,10 +73,8 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute if (AutoInit) { - try + // try { - // TODO: This Dispose call is here until all unit tests that don't correctly dispose Toplevel's they create are fixed. - //Application.Top?.Dispose (); Application.Shutdown (); #if DEBUG_IDISPOSABLE if (Responder.Instances.Count == 0) @@ -89,22 +87,21 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute } #endif } - catch (Exception e) - { - Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}"); - } - finally + //catch (Exception e) + //{ + // Assert.Fail ($"Application.Shutdown threw an exception after the test exited: {e}"); + //} + //finally { #if DEBUG_IDISPOSABLE Responder.Instances.Clear (); - Application.ResetState (ignoreDisposed: true); + Application.ResetState (true); #endif - ConfigurationManager.Reset (); - ConfigurationManager.Locations = CM.ConfigLocations.None; } - - } + + // Enable subsequent tests that call Init to get all config files (the default). + Locations = ConfigLocations.All; } public override void Before (MethodInfo methodUnderTest) @@ -113,8 +110,6 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute if (AutoInit) { - ConfigurationManager.Reset (); - #if DEBUG_IDISPOSABLE // Clear out any lingering Responder instances from previous tests @@ -134,10 +129,15 @@ public class AutoInitShutdownAttribute : BeforeAfterTestAttribute private bool AutoInit { get; } private List _savedValues; - - } +/// +/// Enables test functions annotated with the [TestRespondersDisposed] attribute to ensure all Views are disposed. +/// +/// +/// On Before, sets Configuration.Locations to ConfigLocations.DefaultOnly. +/// On After, sets Configuration.Locations to ConfigLocations.All. +/// [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)] public class TestRespondersDisposed : BeforeAfterTestAttribute { @@ -148,6 +148,9 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute Debug.WriteLine ($"After: {methodUnderTest.Name}"); base.After (methodUnderTest); + // Reset the to default All + Locations = ConfigLocations.All; + #if DEBUG_IDISPOSABLE Assert.Empty (Responder.Instances); #endif @@ -156,6 +159,11 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute public override void Before (MethodInfo methodUnderTest) { Debug.WriteLine ($"Before: {methodUnderTest.Name}"); + + // Force the use of the default config file + Locations = ConfigLocations.DefaultOnly; + Reset (); + base.Before (methodUnderTest); #if DEBUG_IDISPOSABLE @@ -167,15 +175,17 @@ public class TestRespondersDisposed : BeforeAfterTestAttribute } // TODO: Make this inherit from TestRespondersDisposed so that all tests that don't dispose Views correctly can be identified and fixed +/// +/// Enables test functions annotated with the [SetupFakeDriver] attribute to set Application.Driver to new +/// FakeDriver(). The driver is set up with 25 rows and columns. +/// +/// +/// On Before, sets Configuration.Locations to ConfigLocations.DefaultOnly. +/// On After, sets Configuration.Locations to ConfigLocations.All. +/// [AttributeUsage (AttributeTargets.Class | AttributeTargets.Method)] public class SetupFakeDriverAttribute : BeforeAfterTestAttribute { - /// - /// Enables test functions annotated with the [SetupFakeDriver] attribute to set Application.Driver to new - /// FakeDriver(). The driver is setup with 25 rows and columns. - /// - public SetupFakeDriverAttribute () { } - public override void After (MethodInfo methodUnderTest) { Debug.WriteLine ($"After: {methodUnderTest.Name}"); @@ -185,16 +195,25 @@ public class SetupFakeDriverAttribute : BeforeAfterTestAttribute Application.Driver = null; base.After (methodUnderTest); + + // Reset the to default All + Locations = ConfigLocations.All; } public override void Before (MethodInfo methodUnderTest) { Debug.WriteLine ($"Before: {methodUnderTest.Name}"); - Application.ResetState (ignoreDisposed: true); + + // Force the use of the default config file + Locations = ConfigLocations.DefaultOnly; + Reset (); + + Application.ResetState (true); Assert.Null (Application.Driver); Application.Driver = new FakeDriver { Rows = 25, Cols = 25 }; base.Before (methodUnderTest); + } } diff --git a/UnitTests/UICatalog/ScenarioTests.cs b/UnitTests/UICatalog/ScenarioTests.cs index c179ef012..d7549b594 100644 --- a/UnitTests/UICatalog/ScenarioTests.cs +++ b/UnitTests/UICatalog/ScenarioTests.cs @@ -30,6 +30,10 @@ public class ScenarioTests : TestsAllViews Assert.Null (_timeoutLock); _timeoutLock = new (); + // Disable any UIConfig settings + ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations; + ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly; + // If a previous test failed, this will ensure that the Application is in a clean state Application.ResetState (true); @@ -72,6 +76,9 @@ public class ScenarioTests : TestsAllViews _timeoutLock = null; } + // Restore the configuration locations + ConfigurationManager.Locations = savedConfigLocations; + ConfigurationManager.Reset (); return; void OnApplicationOnInitializedChanged (object s, EventArgs a) @@ -109,6 +116,10 @@ public class ScenarioTests : TestsAllViews Assert.Fail ( $"'{scenario.GetName ()}' failed to Quit with {Application.QuitKey} after {abortTime}ms and {iterationCount} iterations. Force quit."); + // Restore the configuration locations + ConfigurationManager.Locations = savedConfigLocations; + ConfigurationManager.Reset (); + Application.ResetState (true); return false; @@ -135,6 +146,10 @@ public class ScenarioTests : TestsAllViews [Fact] public void Run_All_Views_Tester_Scenario () { + // Disable any UIConfig settings + ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations; + ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly; + Window _leftPane; ListView _classListView; FrameView _hostPane; @@ -270,69 +285,69 @@ public class ScenarioTests : TestsAllViews _classListView.OpenSelectedItem += (s, a) => { _settingsPane.SetFocus (); }; _classListView.SelectedItemChanged += (s, args) => - { - // Remove existing class, if any - if (_curView != null) - { - _curView.LayoutComplete -= LayoutCompleteHandler; - _hostPane.Remove (_curView); - _curView.Dispose (); - _curView = null; - _hostPane.FillRect (_hostPane.Viewport); - } + { + // Remove existing class, if any + if (_curView != null) + { + _curView.LayoutComplete -= LayoutCompleteHandler; + _hostPane.Remove (_curView); + _curView.Dispose (); + _curView = null; + _hostPane.FillRect (_hostPane.Viewport); + } - _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]); - }; + _curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]); + }; _xRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView); _xText.TextChanged += (s, args) => - { - try - { - _xVal = int.Parse (_xText.Text); - DimPosChanged (_curView); - } - catch - { } - }; + { + try + { + _xVal = int.Parse (_xText.Text); + DimPosChanged (_curView); + } + catch + { } + }; _yText.TextChanged += (s, e) => - { - try - { - _yVal = int.Parse (_yText.Text); - DimPosChanged (_curView); - } - catch - { } - }; + { + try + { + _yVal = int.Parse (_yText.Text); + DimPosChanged (_curView); + } + catch + { } + }; _yRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView); _wRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView); _wText.TextChanged += (s, args) => - { - try - { - _wVal = int.Parse (_wText.Text); - DimPosChanged (_curView); - } - catch - { } - }; + { + try + { + _wVal = int.Parse (_wText.Text); + DimPosChanged (_curView); + } + catch + { } + }; _hText.TextChanged += (s, args) => - { - try - { - _hVal = int.Parse (_hText.Text); - DimPosChanged (_curView); - } - catch - { } - }; + { + try + { + _hVal = int.Parse (_hText.Text); + DimPosChanged (_curView); + } + catch + { } + }; _hRadioGroup.SelectedItemChanged += (s, selected) => DimPosChanged (_curView); @@ -345,23 +360,23 @@ public class ScenarioTests : TestsAllViews var iterations = 0; Application.Iteration += (s, a) => - { - iterations++; + { + iterations++; - if (iterations < _viewClasses.Count) - { - _classListView.MoveDown (); + if (iterations < _viewClasses.Count) + { + _classListView.MoveDown (); - Assert.Equal ( - _curView.GetType ().Name, - _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name - ); - } - else - { - Application.RequestStop (); - } - }; + Assert.Equal ( + _curView.GetType ().Name, + _viewClasses.Values.ToArray () [_classListView.SelectedItem].Name + ); + } + else + { + Application.RequestStop (); + } + }; Application.Run (top); @@ -370,6 +385,10 @@ public class ScenarioTests : TestsAllViews top.Dispose (); Application.Shutdown (); + // Restore the configuration locations + ConfigurationManager.Locations = savedConfigLocations; + ConfigurationManager.Reset (); + void DimPosChanged (View view) { if (view == null) @@ -586,6 +605,10 @@ public class ScenarioTests : TestsAllViews [Fact] public void Run_Generic () { + // Disable any UIConfig settings + ConfigurationManager.ConfigLocations savedConfigLocations = ConfigurationManager.Locations; + ConfigurationManager.Locations = ConfigurationManager.ConfigLocations.DefaultOnly; + ObservableCollection scenarios = Scenario.GetScenarios (); Assert.NotEmpty (scenarios); @@ -603,40 +626,40 @@ public class ScenarioTests : TestsAllViews var abortCount = 0; Func abortCallback = () => - { - abortCount++; - _output.WriteLine ($"'Generic' abortCount {abortCount}"); - Application.RequestStop (); + { + abortCount++; + _output.WriteLine ($"'Generic' abortCount {abortCount}"); + Application.RequestStop (); - return false; - }; + return false; + }; var iterations = 0; object token = null; Application.Iteration += (s, a) => - { - if (token == null) - { - // Timeout only must start at first iteration - token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback); - } + { + if (token == null) + { + // Timeout only must start at first iteration + token = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (ms), abortCallback); + } - iterations++; - _output.WriteLine ($"'Generic' iteration {iterations}"); + iterations++; + _output.WriteLine ($"'Generic' iteration {iterations}"); - // Stop if we run out of control... - if (iterations == 10) - { - _output.WriteLine ("'Generic' had to be force quit!"); - Application.RequestStop (); - } - }; + // Stop if we run out of control... + if (iterations == 10) + { + _output.WriteLine ("'Generic' had to be force quit!"); + Application.RequestStop (); + } + }; Application.KeyDown += (sender, args) => - { - Assert.Equal (Application.QuitKey, args.KeyCode); - }; + { + Assert.Equal (Application.QuitKey, args.KeyCode); + }; generic.Main (); @@ -650,6 +673,10 @@ public class ScenarioTests : TestsAllViews // Shutdown must be called to safely clean up Application if Init has been called Application.Shutdown (); + // Restore the configuration locations + ConfigurationManager.Locations = savedConfigLocations; + ConfigurationManager.Reset (); + #if DEBUG_IDISPOSABLE Assert.Empty (Responder.Instances); #endif diff --git a/UnitTests/View/Navigation/NavigationTests.cs b/UnitTests/View/Navigation/NavigationTests.cs index 763219d76..aa5b34060 100644 --- a/UnitTests/View/Navigation/NavigationTests.cs +++ b/UnitTests/View/Navigation/NavigationTests.cs @@ -129,21 +129,24 @@ public class NavigationTests (ITestOutputHelper _output) : TestsAllViews var hasFocusFalse = 0; view.HasFocusChanged += (s, e) => - { - if (e.NewValue) - { - hasFocusTrue++; - } - else - { - hasFocusFalse++; - } - }; + { + if (e.NewValue) + { + hasFocusTrue++; + } + else + { + hasFocusFalse++; + } + }; top.Add (view, otherView); Assert.False (view.HasFocus); Assert.False (otherView.HasFocus); + // Ensure the view is Visible + view.Visible = true; + Application.Top.SetFocus (); Assert.True (Application.Top!.HasFocus); Assert.True (top.HasFocus);