From 43ed4b59ebe050df756770ed85edd3f7837c9178 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 10:58:25 -0600 Subject: [PATCH 01/13] Initial commit. Added interface. Testing with RadioGroup --- Terminal.Gui/View/Orientation/IOrientation.cs | 127 ++++++++++++++++++ .../Orientation}/Orientation.cs | 0 Terminal.Gui/Views/OrientationEventArgs.cs | 19 --- Terminal.Gui/Views/RadioGroup.cs | 104 +++++++++----- Terminal.Gui/Views/Slider.cs | 7 +- UICatalog/Scenarios/ExpanderButton.cs | 7 +- 6 files changed, 200 insertions(+), 64 deletions(-) create mode 100644 Terminal.Gui/View/Orientation/IOrientation.cs rename Terminal.Gui/{Views/GraphView => View/Orientation}/Orientation.cs (100%) delete mode 100644 Terminal.Gui/Views/OrientationEventArgs.cs diff --git a/Terminal.Gui/View/Orientation/IOrientation.cs b/Terminal.Gui/View/Orientation/IOrientation.cs new file mode 100644 index 0000000000..908ce32bf6 --- /dev/null +++ b/Terminal.Gui/View/Orientation/IOrientation.cs @@ -0,0 +1,127 @@ + +namespace Terminal.Gui; +using System; + +/// +/// Implement this interface to provide orientation support. +/// +public interface IOrientation +{ + /// + /// Gets or sets the orientation of the View. + /// + Orientation Orientation { get; set; } + + /// + /// Raised when is changing. Can be cancelled. + /// + public event EventHandler> OrientationChanging; + + /// + /// Called when is changing. + /// + /// The current orienation. + /// The new orienation. + /// to cancel the change. + public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; } + + /// + /// + /// + public event EventHandler> OrientationChanged; + + /// + /// Called when has been changed. + /// + /// + /// + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) { return; } +} + + +/// +/// Helper class for implementing . +/// +public class OrientationHelper +{ + private Orientation _orientation = Orientation.Vertical; + private readonly IOrientation _owner; + + /// + /// Initializes a new instance of the class. + /// + /// + public OrientationHelper (IOrientation owner) + { + _owner = owner; + } + + /// + /// Gets or sets the orientation of the View. + /// + public Orientation Orientation + { + get => _orientation; + set + { + var args = new CancelEventArgs (in _orientation, ref value); + OrientationChanging?.Invoke (_owner, args); + if (args.Cancel) + { + return; + } + + if (_owner?.OnOrientationChanging (value, _orientation) ?? false) + { + return; + } + + Orientation old = _orientation; + if (_orientation != value) + { + _orientation = value; + _owner.Orientation = value; + } + + args = new CancelEventArgs (in old, ref _orientation); + OrientationChanged?.Invoke (_owner, args); + + _owner?.OnOrientationChanged (old, _orientation); + } + } + + /// + /// + /// + public event EventHandler> OrientationChanging; + + /// + /// + /// + /// + /// + /// + protected bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) + { + return _owner?.OnOrientationChanging (currentOrientation, newOrientation) ?? false; + } + + /// + /// + /// + public event EventHandler> OrientationChanged; + + /// + /// + /// + /// + /// + /// + protected void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + _owner?.OnOrientationChanged (oldOrientation, newOrientation); + } +} + + diff --git a/Terminal.Gui/Views/GraphView/Orientation.cs b/Terminal.Gui/View/Orientation/Orientation.cs similarity index 100% rename from Terminal.Gui/Views/GraphView/Orientation.cs rename to Terminal.Gui/View/Orientation/Orientation.cs diff --git a/Terminal.Gui/Views/OrientationEventArgs.cs b/Terminal.Gui/Views/OrientationEventArgs.cs deleted file mode 100644 index 8a633ca833..0000000000 --- a/Terminal.Gui/Views/OrientationEventArgs.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace Terminal.Gui; - -/// for events. -public class OrientationEventArgs : EventArgs -{ - /// Constructs a new instance. - /// the new orientation - public OrientationEventArgs (Orientation orientation) - { - Orientation = orientation; - Cancel = false; - } - - /// If set to true, the orientation change operation will be canceled, if applicable. - public bool Cancel { get; set; } - - /// The new orientation. - public Orientation Orientation { get; set; } -} \ No newline at end of file diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index f0dc5174be..1565193b3d 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -1,14 +1,14 @@ namespace Terminal.Gui; /// Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time. -public class RadioGroup : View, IDesignable +public class RadioGroup : View, IDesignable, IOrientation { private int _cursor; private List<(int pos, int length)> _horizontal; private int _horizontalSpace = 2; - private Orientation _orientation = Orientation.Vertical; private List _radioLabels = []; private int _selected; + private readonly OrientationHelper _orientationHelper; /// /// Initializes a new instance of the class. @@ -103,6 +103,13 @@ public RadioGroup () return true; }); + _orientationHelper = new OrientationHelper (this); + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); + + //OrientationChanging += (sender, e) => OnOrientationChanging (e.CurrentValue, e.NewValue); + //OrientationChanged += (sender, e) => OnOrientationChanged (e.CurrentValue, e.NewValue); + SetupKeyBindings (); LayoutStarted += RadioGroup_LayoutStarted; @@ -142,15 +149,15 @@ private void RadioGroup_MouseClick (object sender, MouseEventEventArgs e) int viewportX = e.MouseEvent.Position.X; int viewportY = e.MouseEvent.Position.Y; - int pos = _orientation == Orientation.Horizontal ? viewportX : viewportY; + int pos = Orientation == Orientation.Horizontal ? viewportX : viewportY; - int rCount = _orientation == Orientation.Horizontal + int rCount = Orientation == Orientation.Horizontal ? _horizontal.Last ().pos + _horizontal.Last ().length : _radioLabels.Count; if (pos < rCount) { - int c = _orientation == Orientation.Horizontal + int c = Orientation == Orientation.Horizontal ? _horizontal.FindIndex (x => x.pos <= viewportX && x.pos + x.length - 2 >= viewportX) : viewportY; @@ -173,7 +180,7 @@ public int HorizontalSpace get => _horizontalSpace; set { - if (_horizontalSpace != value && _orientation == Orientation.Horizontal) + if (_horizontalSpace != value && Orientation == Orientation.Horizontal) { _horizontalSpace = value; UpdateTextFormatterText (); @@ -182,16 +189,6 @@ public int HorizontalSpace } } - /// - /// Gets or sets the for this . The default is - /// . - /// - public Orientation Orientation - { - get => _orientation; - set => OnOrientationChanged (value); - } - /// /// The radio labels to display. A key binding will be added for each radio enabling the user to select /// and/or focus the radio label using the keyboard. See for details on how HotKeys work. @@ -323,30 +320,45 @@ public override void OnDrawContent (Rectangle viewport) } } - /// Called when the view orientation has changed. Invokes the event. - /// - /// True of the event was cancelled. - public virtual bool OnOrientationChanged (Orientation newOrientation) + /// + /// Gets or sets the for this . The default is + /// . + /// + public Orientation Orientation { - var args = new OrientationEventArgs (newOrientation); - OrientationChanged?.Invoke (this, args); + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } - if (!args.Cancel) - { - _orientation = newOrientation; - SetupKeyBindings (); - SetContentSize (); - } + #region IOrientation + /// + public event EventHandler> OrientationChanging; + + /// + public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) + { + return false; + } + + /// + public event EventHandler> OrientationChanged; - return args.Cancel; + /// Called when has changed. + /// + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + SetupKeyBindings (); + SetContentSize (); } + #endregion IOrientation // TODO: This should be cancelable /// Called whenever the current selected item changes. Invokes the event. /// /// public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem) - { + { if (_selected == selectedItem) { return; @@ -355,12 +367,6 @@ public virtual void OnSelectedItemChanged (int selectedItem, int previousSelecte SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem)); } - /// - /// Fired when the view orientation has changed. Can be cancelled by setting - /// to true. - /// - public event EventHandler OrientationChanged; - /// public override Point? PositionCursor () { @@ -429,7 +435,7 @@ private void MoveUpLeft () private void SetContentSize () { - switch (_orientation) + switch (Orientation) { case Orientation.Vertical: var width = 0; @@ -469,3 +475,27 @@ public bool EnableForDesign () return true; } } + +public class RadioGroupHorizontal : RadioGroup, IOrientation +{ + private bool _preventOrientationChange = false; + public RadioGroupHorizontal () : base () + { + Orientation = Orientation.Horizontal; + _preventOrientationChange = true; + + OrientationChanging += RadioGroupHorizontal_OrientationChanging; + } + + private void RadioGroupHorizontal_OrientationChanging (object sender, CancelEventArgs e) + { + //e.Cancel = _preventOrientationChange; + } + + /// + bool IOrientation.OnOrientationChanging (Orientation currrentOrientation, Orientation newOrientation) + { + return _preventOrientationChange; + } + +} \ No newline at end of file diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index 61ac401d6e..a613ce7d5b 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -310,17 +310,16 @@ public bool UseMinimumSize #region Events /// - /// Fired when the slider orientation has changed. Can be cancelled by setting - /// to true. + /// Fired when the slider orientation has changed. Can be cancelled. /// - public event EventHandler OrientationChanged; + public event EventHandler> OrientationChanged; /// Called when the slider orientation has changed. Invokes the event. /// /// True of the event was cancelled. public virtual bool OnOrientationChanged (Orientation newOrientation) { - var args = new OrientationEventArgs (newOrientation); + var args = new CancelEventArgs (in _config._sliderOrientation, ref newOrientation); OrientationChanged?.Invoke (this, args); if (!args.Cancel) diff --git a/UICatalog/Scenarios/ExpanderButton.cs b/UICatalog/Scenarios/ExpanderButton.cs index 2959d338ab..f0d5c80732 100644 --- a/UICatalog/Scenarios/ExpanderButton.cs +++ b/UICatalog/Scenarios/ExpanderButton.cs @@ -74,7 +74,7 @@ public Orientation Orientation /// True of the event was cancelled. protected virtual bool OnOrientationChanging (Orientation newOrientation) { - var args = new OrientationEventArgs (newOrientation); + var args = new CancelEventArgs (in _orientation, ref newOrientation); OrientationChanging?.Invoke (this, args); if (!args.Cancel) @@ -105,10 +105,9 @@ protected virtual bool OnOrientationChanging (Orientation newOrientation) } /// - /// Fired when the orientation has changed. Can be cancelled by setting - /// to true. + /// Fired when the orientation has changed. Can be cancelled. /// - public event EventHandler OrientationChanging; + public event EventHandler> OrientationChanging; /// /// The glyph to display when the view is collapsed. From 74521d831d8edbd6ed8fde5594898276925b443b Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:06:51 -0600 Subject: [PATCH 02/13] Added Orientation unit tests. Code cleanup --- Terminal.Gui/View/Orientation/IOrientation.cs | 15 ++- Terminal.Gui/Views/RadioGroup.cs | 6 +- UnitTests/UnitTests.csproj | 10 +- .../Orientation/OrientationHelperTests.cs | 107 ++++++++++++++++++ .../View/Orientation/OrientationTests.cs | 90 +++++++++++++++ 5 files changed, 220 insertions(+), 8 deletions(-) create mode 100644 UnitTests/View/Orientation/OrientationHelperTests.cs create mode 100644 UnitTests/View/Orientation/OrientationTests.cs diff --git a/Terminal.Gui/View/Orientation/IOrientation.cs b/Terminal.Gui/View/Orientation/IOrientation.cs index 908ce32bf6..25bdf7fccd 100644 --- a/Terminal.Gui/View/Orientation/IOrientation.cs +++ b/Terminal.Gui/View/Orientation/IOrientation.cs @@ -20,8 +20,8 @@ public interface IOrientation /// /// Called when is changing. /// - /// The current orienation. - /// The new orienation. + /// The current orientation. + /// The new orientation. /// to cancel the change. public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; } @@ -65,6 +65,11 @@ public Orientation Orientation get => _orientation; set { + if (_orientation == value) + { + return; + } + var args = new CancelEventArgs (in _orientation, ref value); OrientationChanging?.Invoke (_owner, args); if (args.Cancel) @@ -81,7 +86,11 @@ public Orientation Orientation if (_orientation != value) { _orientation = value; - _owner.Orientation = value; + + if (_owner is { }) + { + _owner.Orientation = value; + } } args = new CancelEventArgs (in old, ref _orientation); diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 1565193b3d..49e821772b 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -380,8 +380,10 @@ public virtual void OnSelectedItemChanged (int selectedItem, int previousSelecte break; case Orientation.Horizontal: - x = _horizontal [_cursor].pos; - + if (_horizontal.Count > 0) + { + x = _horizontal [_cursor].pos; + } break; default: diff --git a/UnitTests/UnitTests.csproj b/UnitTests/UnitTests.csproj index 2a9fbc394a..941e25c9b6 100644 --- a/UnitTests/UnitTests.csproj +++ b/UnitTests/UnitTests.csproj @@ -30,9 +30,10 @@ - - - + + + + @@ -58,6 +59,9 @@ + + + False diff --git a/UnitTests/View/Orientation/OrientationHelperTests.cs b/UnitTests/View/Orientation/OrientationHelperTests.cs new file mode 100644 index 0000000000..5294c17264 --- /dev/null +++ b/UnitTests/View/Orientation/OrientationHelperTests.cs @@ -0,0 +1,107 @@ +using Moq; + +namespace Terminal.Gui.ViewTests.OrientationTests; + +public class OrientationHelperTests +{ + [Fact] + public void Orientation_Set_NewValue_InvokesChangingAndChangedEvents () + { + // Arrange + Mock mockIOrientation = new Mock (); + var orientationHelper = new OrientationHelper (mockIOrientation.Object); + var changingEventInvoked = false; + var changedEventInvoked = false; + + orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked = true; }; + orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; }; + + // Act + orientationHelper.Orientation = Orientation.Horizontal; + + // Assert + Assert.True (changingEventInvoked, "OrientationChanging event was not invoked."); + Assert.True (changedEventInvoked, "OrientationChanged event was not invoked."); + } + + [Fact] + public void Orientation_Set_NewValue_InvokesOnChangingAndOnChangedOverrides () + { + // Arrange + Mock mockIOrientation = new Mock (); + var onChangingOverrideCalled = false; + var onChangedOverrideCalled = false; + + mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny (), It.IsAny ())) + .Callback (() => onChangingOverrideCalled = true) + .Returns (false); // Ensure it doesn't cancel the change + + mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny (), It.IsAny ())) + .Callback (() => onChangedOverrideCalled = true); + + var orientationHelper = new OrientationHelper (mockIOrientation.Object); + + // Act + orientationHelper.Orientation = Orientation.Horizontal; + + // Assert + Assert.True (onChangingOverrideCalled, "OnOrientationChanging override was not called."); + Assert.True (onChangedOverrideCalled, "OnOrientationChanged override was not called."); + } + + [Fact] + public void Orientation_Set_SameValue_DoesNotInvokeChangingOrChangedEvents () + { + // Arrange + Mock mockIOrientation = new Mock (); + var orientationHelper = new OrientationHelper (mockIOrientation.Object); + orientationHelper.Orientation = Orientation.Vertical; // Set initial orientation + var changingEventInvoked = false; + var changedEventInvoked = false; + + orientationHelper.OrientationChanging += (sender, e) => { changingEventInvoked = true; }; + orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; }; + + // Act + orientationHelper.Orientation = Orientation.Vertical; // Set to the same value + + // Assert + Assert.False (changingEventInvoked, "OrientationChanging event was invoked."); + Assert.False (changedEventInvoked, "OrientationChanged event was invoked."); + } + + [Fact] + public void Orientation_Set_NewValue_OrientationChanging_CancellationPreventsChange () + { + // Arrange + Mock mockIOrientation = new Mock (); + var orientationHelper = new OrientationHelper (mockIOrientation.Object); + orientationHelper.OrientationChanging += (sender, e) => { e.Cancel = true; }; // Cancel the change + + // Act + orientationHelper.Orientation = Orientation.Horizontal; + + // Assert + Assert.Equal (Orientation.Vertical, orientationHelper.Orientation); // Initial orientation is Vertical + } + + [Fact] + public void Orientation_Set_NewValue_OnOrientationChanging_CancelsChange () + { + // Arrange + Mock mockIOrientation = new Mock (); + + mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny (), It.IsAny ())) + .Returns (true); // Override to return true, cancelling the change + + var orientationHelper = new OrientationHelper (mockIOrientation.Object); + + // Act + orientationHelper.Orientation = Orientation.Horizontal; + + // Assert + Assert.Equal ( + Orientation.Vertical, + orientationHelper.Orientation); // Initial orientation is Vertical, and it should remain unchanged due to cancellation + } +} diff --git a/UnitTests/View/Orientation/OrientationTests.cs b/UnitTests/View/Orientation/OrientationTests.cs new file mode 100644 index 0000000000..3e5734be0f --- /dev/null +++ b/UnitTests/View/Orientation/OrientationTests.cs @@ -0,0 +1,90 @@ +namespace Terminal.Gui.ViewTests.OrientationTests; + +public class OrientationTests +{ + private class CustomView : View, IOrientation + { + private readonly OrientationHelper _orientationHelper; + + public CustomView () + { + _orientationHelper = new (this); + Orientation = Orientation.Vertical; + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); + } + + public Orientation Orientation + { + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } + + public event EventHandler> OrientationChanging; + public event EventHandler> OrientationChanged; + + public bool CancelOnOrientationChanging { get; set; } + + public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) + { + // Custom logic before orientation changes + return CancelOnOrientationChanging; // Return true to cancel the change + } + + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + // Custom logic after orientation has changed + } + } + + [Fact] + public void Orientation_Change_IsSuccessful () + { + // Arrange + var customView = new CustomView (); + var orientationChanged = false; + customView.OrientationChanged += (sender, e) => orientationChanged = true; + + // Act + customView.Orientation = Orientation.Horizontal; + + // Assert + Assert.True (orientationChanged, "OrientationChanged event was not invoked."); + Assert.Equal (Orientation.Horizontal, customView.Orientation); + } + + [Fact] + public void Orientation_Change_OrientationChanging_Set_Cancel_IsCancelled () + { + // Arrange + var customView = new CustomView (); + customView.OrientationChanging += (sender, e) => e.Cancel = true; // Cancel the orientation change + var orientationChanged = false; + customView.OrientationChanged += (sender, e) => orientationChanged = true; + + // Act + customView.Orientation = Orientation.Horizontal; + + // Assert + Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation."); + Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation + } + + [Fact] + public void Orientation_Change_OnOrientationChanging_Return_True_IsCancelled () + { + // Arrange + var customView = new CustomView (); + customView.CancelOnOrientationChanging = true; // Cancel the orientation change + + var orientationChanged = false; + customView.OrientationChanged += (sender, e) => orientationChanged = true; + + // Act + customView.Orientation = Orientation.Horizontal; + + // Assert + Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation."); + Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation + } +} From 329cd2f06c89097f5c1c4356c3df69b1139943ad Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:10:43 -0600 Subject: [PATCH 03/13] One class per file --- .../View/Orientation/OrientationHelper.cs | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 Terminal.Gui/View/Orientation/OrientationHelper.cs diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs new file mode 100644 index 0000000000..f2278ed987 --- /dev/null +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -0,0 +1,94 @@ +namespace Terminal.Gui; + +/// +/// Helper class for implementing . +/// +public class OrientationHelper +{ + private Orientation _orientation = Orientation.Vertical; + private readonly IOrientation _owner; + + /// + /// Initializes a new instance of the class. + /// + /// + public OrientationHelper (IOrientation owner) + { + _owner = owner; + } + + /// + /// Gets or sets the orientation of the View. + /// + public Orientation Orientation + { + get => _orientation; + set + { + if (_orientation == value) + { + return; + } + + var args = new CancelEventArgs (in _orientation, ref value); + OrientationChanging?.Invoke (_owner, args); + if (args.Cancel) + { + return; + } + + if (_owner?.OnOrientationChanging (value, _orientation) ?? false) + { + return; + } + + Orientation old = _orientation; + if (_orientation != value) + { + _orientation = value; + + if (_owner is { }) + { + _owner.Orientation = value; + } + } + + args = new CancelEventArgs (in old, ref _orientation); + OrientationChanged?.Invoke (_owner, args); + + _owner?.OnOrientationChanged (old, _orientation); + } + } + + /// + /// + /// + public event EventHandler> OrientationChanging; + + /// + /// + /// + /// + /// + /// + protected bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) + { + return _owner?.OnOrientationChanging (currentOrientation, newOrientation) ?? false; + } + + /// + /// + /// + public event EventHandler> OrientationChanged; + + /// + /// + /// + /// + /// + /// + protected void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + _owner?.OnOrientationChanged (oldOrientation, newOrientation); + } +} From 840e198e856a97d3558ec1bc80fff4444bd7f010 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:25:49 -0600 Subject: [PATCH 04/13] Beefed up unit tests --- Terminal.Gui/View/Orientation/IOrientation.cs | 98 +------------------ .../View/Orientation/OrientationHelper.cs | 81 ++++++++------- Terminal.Gui/Views/RadioGroup.cs | 51 +++------- .../View/Orientation/OrientationTests.cs | 46 +++++++++ 4 files changed, 107 insertions(+), 169 deletions(-) diff --git a/Terminal.Gui/View/Orientation/IOrientation.cs b/Terminal.Gui/View/Orientation/IOrientation.cs index 25bdf7fccd..52bf2f49f3 100644 --- a/Terminal.Gui/View/Orientation/IOrientation.cs +++ b/Terminal.Gui/View/Orientation/IOrientation.cs @@ -37,100 +37,4 @@ public interface IOrientation /// /// public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) { return; } -} - - -/// -/// Helper class for implementing . -/// -public class OrientationHelper -{ - private Orientation _orientation = Orientation.Vertical; - private readonly IOrientation _owner; - - /// - /// Initializes a new instance of the class. - /// - /// - public OrientationHelper (IOrientation owner) - { - _owner = owner; - } - - /// - /// Gets or sets the orientation of the View. - /// - public Orientation Orientation - { - get => _orientation; - set - { - if (_orientation == value) - { - return; - } - - var args = new CancelEventArgs (in _orientation, ref value); - OrientationChanging?.Invoke (_owner, args); - if (args.Cancel) - { - return; - } - - if (_owner?.OnOrientationChanging (value, _orientation) ?? false) - { - return; - } - - Orientation old = _orientation; - if (_orientation != value) - { - _orientation = value; - - if (_owner is { }) - { - _owner.Orientation = value; - } - } - - args = new CancelEventArgs (in old, ref _orientation); - OrientationChanged?.Invoke (_owner, args); - - _owner?.OnOrientationChanged (old, _orientation); - } - } - - /// - /// - /// - public event EventHandler> OrientationChanging; - - /// - /// - /// - /// - /// - /// - protected bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) - { - return _owner?.OnOrientationChanging (currentOrientation, newOrientation) ?? false; - } - - /// - /// - /// - public event EventHandler> OrientationChanged; - - /// - /// - /// - /// - /// - /// - protected void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) - { - _owner?.OnOrientationChanged (oldOrientation, newOrientation); - } -} - - +} \ No newline at end of file diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index f2278ed987..613557e100 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -3,6 +3,14 @@ /// /// Helper class for implementing . /// +/// +/// +/// Implements the standard pattern for changing/changed events. +/// +/// +/// Views that implement should add a OrientationHelper property. See as an example. +/// +/// public class OrientationHelper { private Orientation _orientation = Orientation.Vertical; @@ -11,11 +19,8 @@ public class OrientationHelper /// /// Initializes a new instance of the class. /// - /// - public OrientationHelper (IOrientation owner) - { - _owner = owner; - } + /// Specifies the object that owns this helper instance. + public OrientationHelper (IOrientation owner) { _owner = owner; } /// /// Gets or sets the orientation of the View. @@ -30,19 +35,25 @@ public Orientation Orientation return; } - var args = new CancelEventArgs (in _orientation, ref value); - OrientationChanging?.Invoke (_owner, args); - if (args.Cancel) + // Best practice is to invoke the virtual method first. + // This allows derived classes to handle the event and potentially cancel it. + if (_owner?.OnOrientationChanging (value, _orientation) ?? false) { return; } - if (_owner?.OnOrientationChanging (value, _orientation) ?? false) + // If the event is not canceled by the virtual method, raise the event to notify any external subscribers. + CancelEventArgs args = new (in _orientation, ref value); + OrientationChanging?.Invoke (_owner, args); + + if (args.Cancel) { return; } + // If the event is not canceled, update the value. Orientation old = _orientation; + if (_orientation != value) { _orientation = value; @@ -53,42 +64,40 @@ public Orientation Orientation } } - args = new CancelEventArgs (in old, ref _orientation); - OrientationChanged?.Invoke (_owner, args); - + // Best practice is to invoke the virtual method first. _owner?.OnOrientationChanged (old, _orientation); + + // Even though Changed is not cancelable, it is still a good practice to raise the event after. + args = new (in old, ref _orientation); + OrientationChanged?.Invoke (_owner, args); } } /// - /// + /// Raised when the orientation is changing. This is cancelable. /// + /// + /// + /// Views that implement should raise after the orientation has changed + /// (_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);). + /// + /// + /// This event will be raised after the method is called (assuming it was not canceled). + /// + /// public event EventHandler> OrientationChanging; /// - /// - /// - /// - /// - /// - protected bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) - { - return _owner?.OnOrientationChanging (currentOrientation, newOrientation) ?? false; - } - - /// - /// + /// Raised when the orientation has changed. /// + /// + /// + /// Views that implement should raise after the orientation has changed + /// (_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);). + /// + /// + /// This event will be raised after the method is called. + /// + /// public event EventHandler> OrientationChanged; - - /// - /// - /// - /// - /// - /// - protected void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) - { - _owner?.OnOrientationChanged (oldOrientation, newOrientation); - } } diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 49e821772b..1113cf9de4 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -44,6 +44,7 @@ public RadioGroup () { return false; } + MoveDownRight (); return true; @@ -58,6 +59,7 @@ public RadioGroup () { return false; } + MoveHome (); return true; @@ -72,6 +74,7 @@ public RadioGroup () { return false; } + MoveEnd (); return true; @@ -93,6 +96,7 @@ public RadioGroup () ctx => { SetFocus (); + if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count) { SelectedItem = (int)ctx.KeyBinding?.Context!; @@ -103,13 +107,10 @@ public RadioGroup () return true; }); - _orientationHelper = new OrientationHelper (this); + _orientationHelper = new (this); _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); - //OrientationChanging += (sender, e) => OnOrientationChanging (e.CurrentValue, e.NewValue); - //OrientationChanged += (sender, e) => OnOrientationChanged (e.CurrentValue, e.NewValue); - SetupKeyBindings (); LayoutStarted += RadioGroup_LayoutStarted; @@ -331,16 +332,14 @@ public Orientation Orientation } #region IOrientation - /// + + /// public event EventHandler> OrientationChanging; - /// - public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) - { - return false; - } + /// + public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; } - /// + /// public event EventHandler> OrientationChanged; /// Called when has changed. @@ -351,6 +350,7 @@ public void OnOrientationChanged (Orientation oldOrientation, Orientation newOri SetupKeyBindings (); SetContentSize (); } + #endregion IOrientation // TODO: This should be cancelable @@ -363,6 +363,7 @@ public virtual void OnSelectedItemChanged (int selectedItem, int previousSelecte { return; } + _selected = selectedItem; SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem)); } @@ -384,6 +385,7 @@ public virtual void OnSelectedItemChanged (int selectedItem, int previousSelecte { x = _horizontal [_cursor].pos; } + break; default: @@ -470,34 +472,11 @@ private void SetContentSize () } } - /// + /// public bool EnableForDesign () { RadioLabels = new [] { "Option _1", "Option _2", "Option _3" }; + return true; } } - -public class RadioGroupHorizontal : RadioGroup, IOrientation -{ - private bool _preventOrientationChange = false; - public RadioGroupHorizontal () : base () - { - Orientation = Orientation.Horizontal; - _preventOrientationChange = true; - - OrientationChanging += RadioGroupHorizontal_OrientationChanging; - } - - private void RadioGroupHorizontal_OrientationChanging (object sender, CancelEventArgs e) - { - //e.Cancel = _preventOrientationChange; - } - - /// - bool IOrientation.OnOrientationChanging (Orientation currrentOrientation, Orientation newOrientation) - { - return _preventOrientationChange; - } - -} \ No newline at end of file diff --git a/UnitTests/View/Orientation/OrientationTests.cs b/UnitTests/View/Orientation/OrientationTests.cs index 3e5734be0f..01eeda71f0 100644 --- a/UnitTests/View/Orientation/OrientationTests.cs +++ b/UnitTests/View/Orientation/OrientationTests.cs @@ -25,14 +25,19 @@ public Orientation Orientation public bool CancelOnOrientationChanging { get; set; } + public bool OnOrientationChangingCalled { get; private set; } + public bool OnOrientationChangedCalled { get; private set; } + public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { + OnOrientationChangingCalled = true; // Custom logic before orientation changes return CancelOnOrientationChanging; // Return true to cancel the change } public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) { + OnOrientationChangedCalled = true; // Custom logic after orientation has changed } } @@ -87,4 +92,45 @@ public void Orientation_Change_OnOrientationChanging_Return_True_IsCancelled () Assert.False (orientationChanged, "OrientationChanged event was invoked despite cancellation."); Assert.Equal (Orientation.Vertical, customView.Orientation); // Assuming Vertical is the default orientation } + + + [Fact] + public void OrientationChanging_VirtualMethodCalledBeforeEvent () + { + // Arrange + var radioGroup = new CustomView (); + bool eventCalled = false; + + radioGroup.OrientationChanging += (sender, e) => + { + eventCalled = true; + Assert.True (radioGroup.OnOrientationChangingCalled, "OnOrientationChanging was not called before the event."); + }; + + // Act + radioGroup.Orientation = Orientation.Horizontal; + + // Assert + Assert.True (eventCalled, "OrientationChanging event was not called."); + } + + [Fact] + public void OrientationChanged_VirtualMethodCalledBeforeEvent () + { + // Arrange + var radioGroup = new CustomView (); + bool eventCalled = false; + + radioGroup.OrientationChanged += (sender, e) => + { + eventCalled = true; + Assert.True (radioGroup.OnOrientationChangedCalled, "OnOrientationChanged was not called before the event."); + }; + + // Act + radioGroup.Orientation = Orientation.Horizontal; + + // Assert + Assert.True (eventCalled, "OrientationChanged event was not called."); + } } From b04846b7eccbda530b8636e18127b215067655a3 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:33:02 -0600 Subject: [PATCH 05/13] Adde example --- .../View/Orientation/OrientationHelper.cs | 39 +++++++++++++++++-- 1 file changed, 36 insertions(+), 3 deletions(-) diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index 613557e100..0a513487ad 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -7,10 +7,43 @@ /// /// Implements the standard pattern for changing/changed events. /// -/// -/// Views that implement should add a OrientationHelper property. See as an example. -/// /// +/// +/// +/// private class OrientedView : View, IOrientation +/// { +/// private readonly OrientationHelper _orientationHelper; +/// +/// public OrientedView () +/// { +/// _orientationHelper = new (this); +/// Orientation = Orientation.Vertical; +/// _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); +/// _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); +/// } +/// +/// public Orientation Orientation +/// { +/// get => _orientationHelper.Orientation; +/// set => _orientationHelper.Orientation = value; +/// } +/// +/// public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging; +/// public event EventHandler<CancelEventArgs<Orientation>> OrientationChanged; +/// +/// public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) +/// { +/// // Custom logic before orientation changes +/// return false; // Return true to cancel the change +/// } +/// +/// public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) +/// { +/// // Custom logic after orientation has changed +/// } +/// } +/// +/// public class OrientationHelper { private Orientation _orientation = Orientation.Vertical; From 6d464a0b045acba9c794d74f9e35561c4c6e8ba9 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:33:32 -0600 Subject: [PATCH 06/13] API docs --- Terminal.Gui/View/Orientation/IOrientation.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Terminal.Gui/View/Orientation/IOrientation.cs b/Terminal.Gui/View/Orientation/IOrientation.cs index 52bf2f49f3..80551f1196 100644 --- a/Terminal.Gui/View/Orientation/IOrientation.cs +++ b/Terminal.Gui/View/Orientation/IOrientation.cs @@ -5,6 +5,9 @@ namespace Terminal.Gui; /// /// Implement this interface to provide orientation support. /// +/// +/// See for a helper class that implements this interface. +/// public interface IOrientation { /// From 3b89159dfec306970c87c9c682a7c7beeefe8e9a Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:34:38 -0600 Subject: [PATCH 07/13] API docs --- .../View/Orientation/OrientationHelper.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index 0a513487ad..3ff93d53e5 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -5,11 +5,11 @@ /// /// /// -/// Implements the standard pattern for changing/changed events. +/// Implements the standard pattern for changing/changed events. /// /// /// -/// +/// /// private class OrientedView : View, IOrientation /// { /// private readonly OrientationHelper _orientationHelper; @@ -52,7 +52,7 @@ public class OrientationHelper /// /// Initializes a new instance of the class. /// - /// Specifies the object that owns this helper instance. + /// Specifies the object that owns this helper instance and implements . public OrientationHelper (IOrientation owner) { _owner = owner; } /// @@ -111,11 +111,13 @@ public Orientation Orientation /// /// /// - /// Views that implement should raise after the orientation has changed + /// Views that implement should raise + /// after the orientation has changed /// (_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);). /// /// - /// This event will be raised after the method is called (assuming it was not canceled). + /// This event will be raised after the method is called (assuming + /// it was not canceled). /// /// public event EventHandler> OrientationChanging; @@ -125,7 +127,8 @@ public Orientation Orientation /// /// /// - /// Views that implement should raise after the orientation has changed + /// Views that implement should raise + /// after the orientation has changed /// (_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);). /// /// From 112d04390978d4bc94ef99cd58b9cd1fccf72b16 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 12:48:55 -0600 Subject: [PATCH 08/13] Upgraded Slider --- Terminal.Gui/Views/Slider.cs | 87 ++++++++++++++------------- UICatalog/Scenarios/AllViewsTester.cs | 10 ++- 2 files changed, 48 insertions(+), 49 deletions(-) diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index a613ce7d5b..ee0f2f4d0d 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -21,7 +21,7 @@ public Slider (List options, Orientation orientation = Orientation.Horiz /// keyboard or mouse. /// /// -public class Slider : View +public class Slider : View, IOrientation { private readonly SliderConfiguration _config = new (); @@ -31,6 +31,8 @@ public class Slider : View // Options private List> _options; + private OrientationHelper _orientationHelper; + #region Initialize private void SetInitialProperties ( @@ -45,11 +47,13 @@ private void SetInitialProperties ( _options = options ?? new List> (); - _config._sliderOrientation = orientation; + _orientationHelper = new (this); + _orientationHelper.Orientation = _config._sliderOrientation = orientation; + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); SetDefaultStyle (); SetCommands (); - SetContentSize (); // BUGBUG: This should not be needed - Need to ensure SetRelativeLayout gets called during EndInit @@ -222,12 +226,45 @@ public SliderType Type } } - /// Slider Orientation. + + /// + /// Gets or sets the . The default is . + /// public Orientation Orientation { - get => _config._sliderOrientation; - set => OnOrientationChanged (value); + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } + + #region IOrientation members + + /// + public event EventHandler> OrientationChanging; + + /// + public event EventHandler> OrientationChanged; + + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + _config._sliderOrientation = newOrientation; + + switch (_config._sliderOrientation) + { + case Orientation.Horizontal: + Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─' + + break; + case Orientation.Vertical: + Style.SpaceChar = new () { Rune = Glyphs.VLine }; + + break; + } + + SetKeyBindings (); + SetContentSize (); } + #endregion /// Legends Orientation. public Orientation LegendsOrientation @@ -309,42 +346,6 @@ public bool UseMinimumSize #region Events - /// - /// Fired when the slider orientation has changed. Can be cancelled. - /// - public event EventHandler> OrientationChanged; - - /// Called when the slider orientation has changed. Invokes the event. - /// - /// True of the event was cancelled. - public virtual bool OnOrientationChanged (Orientation newOrientation) - { - var args = new CancelEventArgs (in _config._sliderOrientation, ref newOrientation); - OrientationChanged?.Invoke (this, args); - - if (!args.Cancel) - { - _config._sliderOrientation = newOrientation; - - switch (_config._sliderOrientation) - { - case Orientation.Horizontal: - Style.SpaceChar = new () { Rune = Glyphs.HLine }; // '─' - - break; - case Orientation.Vertical: - Style.SpaceChar = new () { Rune = Glyphs.VLine }; - - break; - } - - SetKeyBindings (); - SetContentSize (); - } - - return args.Cancel; - } - /// Event raised when the slider option/s changed. The dictionary contains: key = option index, value = T public event EventHandler> OptionsChanged; @@ -1737,7 +1738,7 @@ internal bool ExtendMinus () internal bool Select () { - SetFocusedOption(); + SetFocusedOption (); return true; } diff --git a/UICatalog/Scenarios/AllViewsTester.cs b/UICatalog/Scenarios/AllViewsTester.cs index cc440b8204..ed5d74bf60 100644 --- a/UICatalog/Scenarios/AllViewsTester.cs +++ b/UICatalog/Scenarios/AllViewsTester.cs @@ -272,9 +272,9 @@ public override void Main () _orientation.SelectedItemChanged += (s, selected) => { - if (_curView?.GetType ().GetProperty ("Orientation") is { } prop) + if (_curView is IOrientation orientatedView) { - prop.GetSetMethod ()?.Invoke (_curView, new object [] { _orientation.SelectedItem }); + orientatedView.Orientation = (Orientation)_orientation.SelectedItem; } }; _settingsPane.Add (label, _orientation); @@ -358,11 +358,9 @@ private View CreateClass (Type type) view.Title = "_Test Title"; } - // TODO: Add IOrientation so this doesn't require reflection - // If the view supports a Title property, set it so we have something to look at - if (view?.GetType ().GetProperty ("Orientation") is { } prop) + if (view is IOrientation orientatedView) { - _orientation.SelectedItem = (int)prop.GetGetMethod ()!.Invoke (view, null)!; + _orientation.SelectedItem = (int)orientatedView.Orientation; _orientation.Enabled = true; } else From e8b32b050a98245e90d761f9aaf9db407e836f6a Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 13:10:45 -0600 Subject: [PATCH 09/13] Upgraed Bar --- Terminal.Gui/Views/Bar.cs | 58 +++++++++++++++++++++---- Terminal.Gui/Views/RadioGroup.cs | 3 -- Terminal.Gui/Views/StatusBar.cs | 72 +++++++++++++++++++++++++++++++- UICatalog/Scenarios/Bars.cs | 2 +- 4 files changed, 122 insertions(+), 13 deletions(-) diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index 07447fba81..16d533cd9a 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -11,8 +11,10 @@ namespace Terminal.Gui; /// align them in a specific order. /// /// -public class Bar : View +public class Bar : View, IOrientation, IDesignable { + private readonly OrientationHelper _orientationHelper; + /// public Bar () : this ([]) { } @@ -24,6 +26,10 @@ public Bar (IEnumerable shortcuts) Width = Dim.Auto (); Height = Dim.Auto (); + _orientationHelper = new (this); + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); + Initialized += Bar_Initialized; if (shortcuts is null) @@ -46,7 +52,7 @@ public override void SetBorderStyle (LineStyle value) Border.LineStyle = value; } - private Orientation _orientation = Orientation.Horizontal; + #region IOrientation members /// /// Gets or sets the for this . The default is @@ -58,15 +64,27 @@ public override void SetBorderStyle (LineStyle value) /// Vertical orientation arranges the command, help, and key parts of each s from left to right. /// /// + public Orientation Orientation { - get => _orientation; - set - { - _orientation = value; - SetNeedsLayout (); - } + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } + + /// + public event EventHandler> OrientationChanging; + + /// + public event EventHandler> OrientationChanged; + + /// Called when has changed. + /// + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + SetNeedsLayout (); } + #endregion private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd; @@ -226,4 +244,28 @@ internal override void OnLayoutStarted (LayoutEventArgs args) break; } } + + /// + public bool EnableForDesign () + { + var shortcut = new Shortcut + { + Text = "Quit", + Title = "Q_uit", + Key = Key.Z.WithCtrl, + }; + + Add (shortcut); + + shortcut = new Shortcut + { + Text = "Help Text", + Title = "Help", + Key = Key.F1, + }; + + Add (shortcut); + + return true; + } } diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 1113cf9de4..783aa81bc7 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -336,9 +336,6 @@ public Orientation Orientation /// public event EventHandler> OrientationChanging; - /// - public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; } - /// public event EventHandler> OrientationChanged; diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs index b4df14e6b0..4136335cf9 100644 --- a/Terminal.Gui/Views/StatusBar.cs +++ b/Terminal.Gui/Views/StatusBar.cs @@ -10,7 +10,7 @@ namespace Terminal.Gui; /// to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. So for each context must be a /// new instance of a status bar. /// -public class StatusBar : Bar +public class StatusBar : Bar, IDesignable { /// public StatusBar () : this ([]) { } @@ -74,4 +74,74 @@ public override View Add (View view) return view; } + + /// + bool IDesignable.EnableForDesign () + { + var shortcut = new Shortcut + { + Text = "Quit", + Title = "Q_uit", + Key = Key.Z.WithCtrl, + }; + + Add (shortcut); + + shortcut = new Shortcut + { + Text = "Help Text", + Title = "Help", + Key = Key.F1, + }; + + Add (shortcut); + + shortcut = new Shortcut + { + Title = "_Show/Hide", + Key = Key.F10, + CommandView = new CheckBox + { + CanFocus = false, + Text = "_Show/Hide" + }, + }; + + Add (shortcut); + + var button1 = new Button + { + Text = "I'll Hide", + // Visible = false + }; + button1.Accept += Button_Clicked; + Add (button1); + + shortcut.Accept += (s, e) => + { + button1.Visible = !button1.Visible; + button1.Enabled = button1.Visible; + e.Handled = false; + }; + + Add (new Label + { + HotKeySpecifier = new Rune ('_'), + Text = "Fo_cusLabel", + CanFocus = true + }); + + var button2 = new Button + { + Text = "Or me!", + }; + button2.Accept += (s, e) => Application.RequestStop (); + + Add (button2); + + return true; + + void Button_Clicked (object sender, EventArgs e) { MessageBox.Query ("Hi", $"You clicked {sender}"); } + } + } diff --git a/UICatalog/Scenarios/Bars.cs b/UICatalog/Scenarios/Bars.cs index c2027d8eca..a9ac5fe6e7 100644 --- a/UICatalog/Scenarios/Bars.cs +++ b/UICatalog/Scenarios/Bars.cs @@ -408,7 +408,7 @@ private void ConfigureMenu (Bar bar) bar.Add (shortcut1, shortcut2, line, shortcut3); } - private void ConfigStatusBar (Bar bar) + public void ConfigStatusBar (Bar bar) { var shortcut = new Shortcut { From aec901f52843342ac8187725ca855af9d9c693bb Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 13:14:06 -0600 Subject: [PATCH 10/13] Upgraded Line --- Terminal.Gui/Views/Line.cs | 48 ++++++++++++++++++++++++++------------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs index 6a25fa55b7..a42f58ebb9 100644 --- a/Terminal.Gui/Views/Line.cs +++ b/Terminal.Gui/Views/Line.cs @@ -1,43 +1,61 @@ namespace Terminal.Gui; /// Draws a single line using the specified by . -public class Line : View +public class Line : View, IOrientation { + private readonly OrientationHelper _orientationHelper; + /// Constructs a Line object. public Line () { BorderStyle = LineStyle.Single; Border.Thickness = new Thickness (0); SuperViewRendersLineCanvas = true; + + _orientationHelper = new (this); + _orientationHelper.Orientation = Orientation.Horizontal; + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); } - private Orientation _orientation; + #region IOrientation members /// /// The direction of the line. If you change this you will need to manually update the Width/Height of the /// control to cover a relevant area based on the new direction. /// public Orientation Orientation { - get => _orientation; - set - { - _orientation = value; + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } + + /// + public event EventHandler> OrientationChanging; - switch (Orientation) - { - case Orientation.Horizontal: - Height = 1; + /// + public event EventHandler> OrientationChanged; + + /// Called when has changed. + /// + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + + switch (Orientation) + { + case Orientation.Horizontal: + Height = 1; - break; - case Orientation.Vertical: - Width = 1; + break; + case Orientation.Vertical: + Width = 1; - break; + break; - } } } + #endregion /// public override void SetBorderStyle (LineStyle value) From 16b374bae470e82a07f95cdd6e71da12b7c3a8f5 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 13:19:45 -0600 Subject: [PATCH 11/13] Upgraded Shortcut --- Terminal.Gui/Views/Shortcut.cs | 603 +++++++++++++++++---------------- 1 file changed, 317 insertions(+), 286 deletions(-) diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 7ddbe7c5a0..245308564b 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -1,10 +1,8 @@ -using System.ComponentModel; -using System.Threading.Channels; - -namespace Terminal.Gui; +namespace Terminal.Gui; /// -/// Displays a command, help text, and a key binding. When the key specified by is pressed, the command will be invoked. Useful for +/// Displays a command, help text, and a key binding. When the key specified by is pressed, the +/// command will be invoked. Useful for /// displaying a command in such as a /// menu, toolbar, or status bar. /// @@ -12,12 +10,13 @@ namespace Terminal.Gui; /// /// The following user actions will invoke the , causing the /// event to be fired: -/// - Clicking on the . -/// - Pressing the key specified by . -/// - Pressing the HotKey specified by . +/// - Clicking on the . +/// - Pressing the key specified by . +/// - Pressing the HotKey specified by . /// /// -/// If is , will invoked +/// If is , will invoked +/// /// command regardless of what View has focus, enabling an application-wide keyboard shortcut. /// /// @@ -37,8 +36,10 @@ namespace Terminal.Gui; /// If the is , the text is not displayed. /// /// -public class Shortcut : View +public class Shortcut : View, IOrientation, IDesignable { + private readonly OrientationHelper _orientationHelper; + /// /// Creates a new instance of . /// @@ -60,6 +61,10 @@ public Shortcut (Key key, string commandText, Action action, string helpText = n Width = GetWidthDimAuto (); Height = Dim.Auto (DimAutoStyle.Content, 1); + _orientationHelper = new (this); + _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); + _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); + AddCommand (Command.HotKey, ctx => OnAccept (ctx)); AddCommand (Command.Accept, ctx => OnAccept (ctx)); AddCommand (Command.Select, ctx => OnSelect (ctx)); @@ -132,31 +137,49 @@ Dim GetWidthDimAuto () } } - /// /// Creates a new instance of . /// public Shortcut () : this (Key.Empty, string.Empty, null) { } - private Orientation _orientation = Orientation.Horizontal; + #region IOrientation members /// - /// Gets or sets the for this . The default is - /// , which is ideal for status bar, menu bar, and tool bar items If set to - /// , - /// the Shortcut will be configured for vertical layout, which is ideal for menu items. + /// Gets or sets the for this . The default is + /// . /// + /// + /// + /// Horizontal orientation arranges the command, help, and key parts of each s from right to + /// left + /// Vertical orientation arranges the command, help, and key parts of each s from left to + /// right. + /// + /// + public Orientation Orientation { - get => _orientation; - set - { - _orientation = value; + get => _orientationHelper.Orientation; + set => _orientationHelper.Orientation = value; + } - // TODO: Determine what, if anything, is opinionated about the orientation. - } + /// + public event EventHandler> OrientationChanging; + + /// + public event EventHandler> OrientationChanged; + + /// Called when has changed. + /// + /// + public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + { + // TODO: Determine what, if anything, is opinionated about the orientation. + SetNeedsLayout (); } + #endregion + private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast; /// @@ -344,7 +367,6 @@ private void Shortcut_MouseClick (object sender, MouseEventEventArgs e) private void Subview_MouseClick (object sender, MouseEventEventArgs e) { // TODO: Remove. This does nothing. - return; } #region Command @@ -434,359 +456,368 @@ public View CommandView SetKeyViewDefaultLayout (); ShowHide (); UpdateKeyBinding (); - - return; } } private void SetCommandViewDefaultLayout () -{ - CommandView.Margin.Thickness = GetMarginThickness (); - CommandView.X = Pos.Align (Alignment.End, AlignmentModes); - CommandView.Y = 0; //Pos.Center (); -} + { + CommandView.Margin.Thickness = GetMarginThickness (); + CommandView.X = Pos.Align (Alignment.End, AlignmentModes); + CommandView.Y = 0; //Pos.Center (); + } -private void Shortcut_TitleChanged (object sender, EventArgs e) -{ - // If the Title changes, update the CommandView text. - // This is a helper to make it easier to set the CommandView text. - // CommandView is public and replaceable, but this is a convenience. - _commandView.Text = Title; -} + private void Shortcut_TitleChanged (object sender, EventArgs e) + { + // If the Title changes, update the CommandView text. + // This is a helper to make it easier to set the CommandView text. + // CommandView is public and replaceable, but this is a convenience. + _commandView.Text = Title; + } -#endregion Command + #endregion Command -#region Help + #region Help -/// -/// The subview that displays the help text for the command. Internal for unit testing. -/// -internal View HelpView { get; } = new (); + /// + /// The subview that displays the help text for the command. Internal for unit testing. + /// + internal View HelpView { get; } = new (); -private void SetHelpViewDefaultLayout () -{ - HelpView.Margin.Thickness = GetMarginThickness (); - HelpView.X = Pos.Align (Alignment.End, AlignmentModes); - HelpView.Y = 0; //Pos.Center (); - HelpView.Width = Dim.Auto (DimAutoStyle.Text); - HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1; - - HelpView.Visible = true; - HelpView.VerticalTextAlignment = Alignment.Center; -} + private void SetHelpViewDefaultLayout () + { + HelpView.Margin.Thickness = GetMarginThickness (); + HelpView.X = Pos.Align (Alignment.End, AlignmentModes); + HelpView.Y = 0; //Pos.Center (); + HelpView.Width = Dim.Auto (DimAutoStyle.Text); + HelpView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1; + + HelpView.Visible = true; + HelpView.VerticalTextAlignment = Alignment.Center; + } -/// -/// Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to -/// . -/// -public override string Text -{ - get => HelpView?.Text; - set + /// + /// Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to + /// . + /// + public override string Text { - if (HelpView != null) + get => HelpView?.Text; + set { - HelpView.Text = value; - ShowHide (); + if (HelpView != null) + { + HelpView.Text = value; + ShowHide (); + } } } -} -/// -/// Gets or sets the help text displayed in the middle of the Shortcut. -/// -public string HelpText -{ - get => HelpView?.Text; - set + /// + /// Gets or sets the help text displayed in the middle of the Shortcut. + /// + public string HelpText { - if (HelpView != null) + get => HelpView?.Text; + set { - HelpView.Text = value; - ShowHide (); + if (HelpView != null) + { + HelpView.Text = value; + ShowHide (); + } } } -} -#endregion Help + #endregion Help -#region Key + #region Key -private Key _key = Key.Empty; + private Key _key = Key.Empty; -/// -/// Gets or sets the that will be bound to the command. -/// -public Key Key -{ - get => _key; - set + /// + /// Gets or sets the that will be bound to the command. + /// + public Key Key { - if (value == null) + get => _key; + set { - throw new ArgumentNullException (); - } + if (value == null) + { + throw new ArgumentNullException (); + } - _key = value; + _key = value; - UpdateKeyBinding (); + UpdateKeyBinding (); - KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}"; - ShowHide (); + KeyView.Text = Key == Key.Empty ? string.Empty : $"{Key}"; + ShowHide (); + } } -} -private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey; + private KeyBindingScope _keyBindingScope = KeyBindingScope.HotKey; -/// -/// Gets or sets the scope for the key binding for how is bound to . -/// -public KeyBindingScope KeyBindingScope -{ - get => _keyBindingScope; - set + /// + /// Gets or sets the scope for the key binding for how is bound to . + /// + public KeyBindingScope KeyBindingScope { - _keyBindingScope = value; + get => _keyBindingScope; + set + { + _keyBindingScope = value; - UpdateKeyBinding (); + UpdateKeyBinding (); + } } -} -/// -/// Gets the subview that displays the key. Internal for unit testing. -/// + /// + /// Gets the subview that displays the key. Internal for unit testing. + /// -internal View KeyView { get; } = new (); + internal View KeyView { get; } = new (); -private int _minimumKeyTextSize; + private int _minimumKeyTextSize; -/// -/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other s. -/// -public int MinimumKeyTextSize -{ - get => _minimumKeyTextSize; - set + /// + /// Gets or sets the minimum size of the key text. Useful for aligning the key text with other s. + /// + public int MinimumKeyTextSize { - if (value == _minimumKeyTextSize) + get => _minimumKeyTextSize; + set { - //return; - } + if (value == _minimumKeyTextSize) + { + //return; + } - _minimumKeyTextSize = value; - SetKeyViewDefaultLayout (); - CommandView.SetNeedsLayout (); - HelpView.SetNeedsLayout (); - KeyView.SetNeedsLayout (); - SetSubViewNeedsDisplay (); + _minimumKeyTextSize = value; + SetKeyViewDefaultLayout (); + CommandView.SetNeedsLayout (); + HelpView.SetNeedsLayout (); + KeyView.SetNeedsLayout (); + SetSubViewNeedsDisplay (); + } } -} - -private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; } -private void SetKeyViewDefaultLayout () -{ - KeyView.Margin.Thickness = GetMarginThickness (); - KeyView.X = Pos.Align (Alignment.End, AlignmentModes); - KeyView.Y = 0; //Pos.Center (); - KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize)); - KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1; - - KeyView.Visible = true; - - // Right align the text in the keyview - KeyView.TextAlignment = Alignment.End; - KeyView.VerticalTextAlignment = Alignment.Center; - KeyView.KeyBindings.Clear (); -} + private int GetMinimumKeyViewSize () { return MinimumKeyTextSize; } -private void UpdateKeyBinding () -{ - if (Key != null) + private void SetKeyViewDefaultLayout () { - // Disable the command view key bindings - CommandView.KeyBindings.Remove (Key); - CommandView.KeyBindings.Remove (CommandView.HotKey); - KeyBindings.Remove (Key); - KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept); - //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept); + KeyView.Margin.Thickness = GetMarginThickness (); + KeyView.X = Pos.Align (Alignment.End, AlignmentModes); + KeyView.Y = 0; //Pos.Center (); + KeyView.Width = Dim.Auto (DimAutoStyle.Text, Dim.Func (GetMinimumKeyViewSize)); + KeyView.Height = CommandView?.Visible == true ? Dim.Height (CommandView) : 1; + + KeyView.Visible = true; + + // Right align the text in the keyview + KeyView.TextAlignment = Alignment.End; + KeyView.VerticalTextAlignment = Alignment.Center; + KeyView.KeyBindings.Clear (); } -} - -#endregion Key -#region Accept Handling + private void UpdateKeyBinding () + { + if (Key != null) + { + // Disable the command view key bindings + CommandView.KeyBindings.Remove (Key); + CommandView.KeyBindings.Remove (CommandView.HotKey); + KeyBindings.Remove (Key); + KeyBindings.Add (Key, KeyBindingScope | KeyBindingScope.HotKey, Command.Accept); -/// -/// Called when the command is received. This -/// occurs -/// - if the user clicks anywhere on the shortcut with the mouse -/// - if the user presses Key -/// - if the user presses the HotKey specified by CommandView -/// - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept). -/// -protected bool? OnAccept (CommandContext ctx) -{ - var cancel = false; + //KeyBindings.Add (Key, KeyBindingScope.HotKey, Command.Accept); + } + } - switch (ctx.KeyBinding?.Scope) - { - case KeyBindingScope.Application: - cancel = base.OnAccept () == true; + #endregion Key - break; + #region Accept Handling - case KeyBindingScope.Focused: - base.OnAccept (); + /// + /// Called when the command is received. This + /// occurs + /// - if the user clicks anywhere on the shortcut with the mouse + /// - if the user presses Key + /// - if the user presses the HotKey specified by CommandView + /// - if HasFocus and the user presses Space or Enter (or any other key bound to Command.Accept). + /// + protected bool? OnAccept (CommandContext ctx) + { + var cancel = false; - // cancel if we're focused - cancel = true; + switch (ctx.KeyBinding?.Scope) + { + case KeyBindingScope.Application: + cancel = base.OnAccept () == true; - break; + break; - case KeyBindingScope.HotKey: - cancel = base.OnAccept () == true; + case KeyBindingScope.Focused: + base.OnAccept (); - if (CanFocus) - { - SetFocus (); + // cancel if we're focused cancel = true; - } - break; + break; - default: - // Mouse - cancel = base.OnAccept () == true; + case KeyBindingScope.HotKey: + cancel = base.OnAccept () == true; - break; - } + if (CanFocus) + { + SetFocus (); + cancel = true; + } - CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding); + break; - if (Action is { }) - { - Action.Invoke (); - // Assume if there's a subscriber to Action, it's handled. - cancel = true; - } + default: + // Mouse + cancel = base.OnAccept () == true; - return cancel; -} + break; + } -/// -/// Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the -/// mouse. -/// -/// -/// Note, the event is fired first, and if cancelled, the event will not be invoked. -/// -[CanBeNull] -public Action Action { get; set; } + CommandView.InvokeCommand (Command.Accept, ctx.Key, ctx.KeyBinding); -#endregion Accept Handling + if (Action is { }) + { + Action.Invoke (); -private bool? OnSelect (CommandContext ctx) -{ - if (CommandView.GetSupportedCommands ().Contains (Command.Select)) - { - return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding); - } - return false; + // Assume if there's a subscriber to Action, it's handled. + cancel = true; + } -} + return cancel; + } + /// + /// Gets or sets the action to be invoked when the shortcut key is pressed or the shortcut is clicked on with the + /// mouse. + /// + /// + /// Note, the event is fired first, and if cancelled, the event will not be invoked. + /// + [CanBeNull] + public Action Action { get; set; } -#region Focus + #endregion Accept Handling -/// -public override ColorScheme ColorScheme -{ - get => base.ColorScheme; - set + private bool? OnSelect (CommandContext ctx) { - base.ColorScheme = value; - SetColors (); + if (CommandView.GetSupportedCommands ().Contains (Command.Select)) + { + return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding); + } + + return false; } -} -/// -/// -internal void SetColors () -{ - // Border should match superview. - Border.ColorScheme = SuperView?.ColorScheme; + #region Focus - if (HasFocus) + /// + public override ColorScheme ColorScheme { - // When we have focus, we invert the colors - base.ColorScheme = new (base.ColorScheme) + get => base.ColorScheme; + set { - Normal = base.ColorScheme.Focus, - HotNormal = base.ColorScheme.HotFocus, - HotFocus = base.ColorScheme.HotNormal, - Focus = base.ColorScheme.Normal - }; - } - else - { - base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme; + base.ColorScheme = value; + SetColors (); + } } - // Set KeyView's colors to show "hot" - if (IsInitialized && base.ColorScheme is { }) + /// + /// + internal void SetColors () { - var cs = new ColorScheme (base.ColorScheme) + // Border should match superview. + Border.ColorScheme = SuperView?.ColorScheme; + + if (HasFocus) { - Normal = base.ColorScheme.HotNormal, - HotNormal = base.ColorScheme.Normal - }; - KeyView.ColorScheme = cs; + // When we have focus, we invert the colors + base.ColorScheme = new (base.ColorScheme) + { + Normal = base.ColorScheme.Focus, + HotNormal = base.ColorScheme.HotFocus, + HotFocus = base.ColorScheme.HotNormal, + Focus = base.ColorScheme.Normal + }; + } + else + { + base.ColorScheme = SuperView?.ColorScheme ?? base.ColorScheme; + } + + // Set KeyView's colors to show "hot" + if (IsInitialized && base.ColorScheme is { }) + { + var cs = new ColorScheme (base.ColorScheme) + { + Normal = base.ColorScheme.HotNormal, + HotNormal = base.ColorScheme.Normal + }; + KeyView.ColorScheme = cs; + } } -} -View _lastFocusedView; -/// -public override bool OnEnter (View view) -{ - SetColors (); - _lastFocusedView = view; + private View _lastFocusedView; - return base.OnEnter (view); -} + /// + public override bool OnEnter (View view) + { + SetColors (); + _lastFocusedView = view; -/// -public override bool OnLeave (View view) -{ - SetColors (); - _lastFocusedView = this; + return base.OnEnter (view); + } - return base.OnLeave (view); -} + /// + public override bool OnLeave (View view) + { + SetColors (); + _lastFocusedView = this; -#endregion Focus + return base.OnLeave (view); + } -/// -protected override void Dispose (bool disposing) -{ - if (disposing) + #endregion Focus + + /// + public bool EnableForDesign () { - if (CommandView?.IsAdded == false) - { - CommandView.Dispose (); - } + Title = "_Shortcut"; + HelpText = "Shortcut help"; + Key = Key.F1; + return true; + } - if (HelpView?.IsAdded == false) + /// + protected override void Dispose (bool disposing) + { + if (disposing) { - HelpView.Dispose (); - } + if (CommandView?.IsAdded == false) + { + CommandView.Dispose (); + } - if (KeyView?.IsAdded == false) - { - KeyView.Dispose (); + if (HelpView?.IsAdded == false) + { + HelpView.Dispose (); + } + + if (KeyView?.IsAdded == false) + { + KeyView.Dispose (); + } } - } - base.Dispose (disposing); -} + base.Dispose (disposing); + } } From 2097a172134787d6a690735a35962fd37277a539 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 13:24:24 -0600 Subject: [PATCH 12/13] Default orientation is Horiz --- .../View/Orientation/OrientationHelper.cs | 2 +- Terminal.Gui/Views/RadioGroup.cs | 1 + .../View/Orientation/OrientationHelperTests.cs | 18 +++++++++--------- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index 3ff93d53e5..72ccef0fa5 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -46,7 +46,7 @@ /// public class OrientationHelper { - private Orientation _orientation = Orientation.Vertical; + private Orientation _orientation; private readonly IOrientation _owner; /// diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index 783aa81bc7..e8d8451613 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -108,6 +108,7 @@ public RadioGroup () }); _orientationHelper = new (this); + _orientationHelper.Orientation = Orientation.Vertical; _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e); _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e); diff --git a/UnitTests/View/Orientation/OrientationHelperTests.cs b/UnitTests/View/Orientation/OrientationHelperTests.cs index 5294c17264..bd7a884aef 100644 --- a/UnitTests/View/Orientation/OrientationHelperTests.cs +++ b/UnitTests/View/Orientation/OrientationHelperTests.cs @@ -17,7 +17,7 @@ public void Orientation_Set_NewValue_InvokesChangingAndChangedEvents () orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; }; // Act - orientationHelper.Orientation = Orientation.Horizontal; + orientationHelper.Orientation = Orientation.Vertical; // Assert Assert.True (changingEventInvoked, "OrientationChanging event was not invoked."); @@ -42,7 +42,7 @@ public void Orientation_Set_NewValue_InvokesOnChangingAndOnChangedOverrides () var orientationHelper = new OrientationHelper (mockIOrientation.Object); // Act - orientationHelper.Orientation = Orientation.Horizontal; + orientationHelper.Orientation = Orientation.Vertical; // Assert Assert.True (onChangingOverrideCalled, "OnOrientationChanging override was not called."); @@ -55,7 +55,7 @@ public void Orientation_Set_SameValue_DoesNotInvokeChangingOrChangedEvents () // Arrange Mock mockIOrientation = new Mock (); var orientationHelper = new OrientationHelper (mockIOrientation.Object); - orientationHelper.Orientation = Orientation.Vertical; // Set initial orientation + orientationHelper.Orientation = Orientation.Horizontal; // Set initial orientation var changingEventInvoked = false; var changedEventInvoked = false; @@ -63,7 +63,7 @@ public void Orientation_Set_SameValue_DoesNotInvokeChangingOrChangedEvents () orientationHelper.OrientationChanged += (sender, e) => { changedEventInvoked = true; }; // Act - orientationHelper.Orientation = Orientation.Vertical; // Set to the same value + orientationHelper.Orientation = Orientation.Horizontal; // Set to the same value // Assert Assert.False (changingEventInvoked, "OrientationChanging event was invoked."); @@ -79,10 +79,10 @@ public void Orientation_Set_NewValue_OrientationChanging_CancellationPreventsCha orientationHelper.OrientationChanging += (sender, e) => { e.Cancel = true; }; // Cancel the change // Act - orientationHelper.Orientation = Orientation.Horizontal; + orientationHelper.Orientation = Orientation.Vertical; // Assert - Assert.Equal (Orientation.Vertical, orientationHelper.Orientation); // Initial orientation is Vertical + Assert.Equal (Orientation.Horizontal, orientationHelper.Orientation); // Initial orientation is Horizontal } [Fact] @@ -97,11 +97,11 @@ public void Orientation_Set_NewValue_OnOrientationChanging_CancelsChange () var orientationHelper = new OrientationHelper (mockIOrientation.Object); // Act - orientationHelper.Orientation = Orientation.Horizontal; + orientationHelper.Orientation = Orientation.Vertical; // Assert Assert.Equal ( - Orientation.Vertical, - orientationHelper.Orientation); // Initial orientation is Vertical, and it should remain unchanged due to cancellation + Orientation.Horizontal, + orientationHelper.Orientation); // Initial orientation is Horizontal, and it should remain unchanged due to cancellation } } From 6220863b9c4540d895659e68b12e919c1751f9a5 Mon Sep 17 00:00:00 2001 From: Tig Date: Mon, 22 Jul 2024 14:26:59 -0600 Subject: [PATCH 13/13] Fixed issues @bdisp noted: --- Terminal.Gui/View/Orientation/IOrientation.cs | 7 +++---- Terminal.Gui/View/Orientation/OrientationHelper.cs | 11 +++++------ Terminal.Gui/Views/Bar.cs | 5 ++--- Terminal.Gui/Views/Line.cs | 7 +++---- Terminal.Gui/Views/RadioGroup.cs | 5 ++--- Terminal.Gui/Views/Shortcut.cs | 5 ++--- Terminal.Gui/Views/Slider.cs | 4 ++-- UnitTests/View/Orientation/OrientationHelperTests.cs | 2 +- UnitTests/View/Orientation/OrientationTests.cs | 4 ++-- 9 files changed, 22 insertions(+), 28 deletions(-) diff --git a/Terminal.Gui/View/Orientation/IOrientation.cs b/Terminal.Gui/View/Orientation/IOrientation.cs index 80551f1196..34470878e9 100644 --- a/Terminal.Gui/View/Orientation/IOrientation.cs +++ b/Terminal.Gui/View/Orientation/IOrientation.cs @@ -29,15 +29,14 @@ public interface IOrientation public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; } /// - /// + /// Raised when has changed. /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// /// Called when has been changed. /// - /// /// /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) { return; } + public void OnOrientationChanged (Orientation newOrientation) { return; } } \ No newline at end of file diff --git a/Terminal.Gui/View/Orientation/OrientationHelper.cs b/Terminal.Gui/View/Orientation/OrientationHelper.cs index 72ccef0fa5..2227494dc0 100644 --- a/Terminal.Gui/View/Orientation/OrientationHelper.cs +++ b/Terminal.Gui/View/Orientation/OrientationHelper.cs @@ -29,7 +29,7 @@ /// } /// /// public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging; -/// public event EventHandler<CancelEventArgs<Orientation>> OrientationChanged; +/// public event EventHandler<EventArgs<Orientation>> OrientationChanged; /// /// public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) /// { @@ -37,7 +37,7 @@ /// return false; // Return true to cancel the change /// } /// -/// public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) +/// public void OnOrientationChanged (Orientation newOrientation) /// { /// // Custom logic after orientation has changed /// } @@ -98,11 +98,10 @@ public Orientation Orientation } // Best practice is to invoke the virtual method first. - _owner?.OnOrientationChanged (old, _orientation); + _owner?.OnOrientationChanged (_orientation); // Even though Changed is not cancelable, it is still a good practice to raise the event after. - args = new (in old, ref _orientation); - OrientationChanged?.Invoke (_owner, args); + OrientationChanged?.Invoke (_owner, new (in _orientation)); } } @@ -135,5 +134,5 @@ public Orientation Orientation /// This event will be raised after the method is called. /// /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; } diff --git a/Terminal.Gui/Views/Bar.cs b/Terminal.Gui/Views/Bar.cs index 16d533cd9a..1141b5de11 100644 --- a/Terminal.Gui/Views/Bar.cs +++ b/Terminal.Gui/Views/Bar.cs @@ -75,12 +75,11 @@ public Orientation Orientation public event EventHandler> OrientationChanging; /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// Called when has changed. - /// /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { SetNeedsLayout (); } diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs index a42f58ebb9..730d5a1aab 100644 --- a/Terminal.Gui/Views/Line.cs +++ b/Terminal.Gui/Views/Line.cs @@ -34,15 +34,14 @@ public Orientation Orientation public event EventHandler> OrientationChanging; /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// Called when has changed. - /// /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { - switch (Orientation) + switch (newOrientation) { case Orientation.Horizontal: Height = 1; diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs index e8d8451613..5ce71c7a4c 100644 --- a/Terminal.Gui/Views/RadioGroup.cs +++ b/Terminal.Gui/Views/RadioGroup.cs @@ -338,12 +338,11 @@ public Orientation Orientation public event EventHandler> OrientationChanging; /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// Called when has changed. - /// /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { SetupKeyBindings (); SetContentSize (); diff --git a/Terminal.Gui/Views/Shortcut.cs b/Terminal.Gui/Views/Shortcut.cs index 245308564b..9ffb5e72a8 100644 --- a/Terminal.Gui/Views/Shortcut.cs +++ b/Terminal.Gui/Views/Shortcut.cs @@ -167,12 +167,11 @@ public Orientation Orientation public event EventHandler> OrientationChanging; /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// Called when has changed. - /// /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { // TODO: Determine what, if anything, is opinionated about the orientation. SetNeedsLayout (); diff --git a/Terminal.Gui/Views/Slider.cs b/Terminal.Gui/Views/Slider.cs index ee0f2f4d0d..69a9add713 100644 --- a/Terminal.Gui/Views/Slider.cs +++ b/Terminal.Gui/Views/Slider.cs @@ -242,10 +242,10 @@ public Orientation Orientation public event EventHandler> OrientationChanging; /// - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; /// - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { _config._sliderOrientation = newOrientation; diff --git a/UnitTests/View/Orientation/OrientationHelperTests.cs b/UnitTests/View/Orientation/OrientationHelperTests.cs index bd7a884aef..cc6a1f240f 100644 --- a/UnitTests/View/Orientation/OrientationHelperTests.cs +++ b/UnitTests/View/Orientation/OrientationHelperTests.cs @@ -36,7 +36,7 @@ public void Orientation_Set_NewValue_InvokesOnChangingAndOnChangedOverrides () .Callback (() => onChangingOverrideCalled = true) .Returns (false); // Ensure it doesn't cancel the change - mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny (), It.IsAny ())) + mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny ())) .Callback (() => onChangedOverrideCalled = true); var orientationHelper = new OrientationHelper (mockIOrientation.Object); diff --git a/UnitTests/View/Orientation/OrientationTests.cs b/UnitTests/View/Orientation/OrientationTests.cs index 01eeda71f0..d000ca9dbf 100644 --- a/UnitTests/View/Orientation/OrientationTests.cs +++ b/UnitTests/View/Orientation/OrientationTests.cs @@ -21,7 +21,7 @@ public Orientation Orientation } public event EventHandler> OrientationChanging; - public event EventHandler> OrientationChanged; + public event EventHandler> OrientationChanged; public bool CancelOnOrientationChanging { get; set; } @@ -35,7 +35,7 @@ public bool OnOrientationChanging (Orientation currentOrientation, Orientation n return CancelOnOrientationChanging; // Return true to cancel the change } - public void OnOrientationChanged (Orientation oldOrientation, Orientation newOrientation) + public void OnOrientationChanged (Orientation newOrientation) { OnOrientationChangedCalled = true; // Custom logic after orientation has changed