mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 09:47:58 +01:00
merged with v2_develop
This commit is contained in:
@@ -16,14 +16,14 @@ public static class Program
|
||||
|
||||
#region The code in this region is not intended for use in a self-contained single-file. It's just here to make sure there is no functionality break with localization in Terminal.Gui using single-file
|
||||
|
||||
if (Equals (Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures.Count == 0)
|
||||
if (Equals (Thread.CurrentThread.CurrentUICulture, CultureInfo.InvariantCulture) && Application.SupportedCultures?.Count == 0)
|
||||
{
|
||||
// Only happens if the project has <InvariantGlobalization>true</InvariantGlobalization>
|
||||
Debug.Assert (Application.SupportedCultures.Count == 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.Assert (Application.SupportedCultures.Count > 0);
|
||||
Debug.Assert (Application.SupportedCultures?.Count > 0);
|
||||
Debug.Assert (Equals (CultureInfo.CurrentCulture, Thread.CurrentThread.CurrentUICulture));
|
||||
}
|
||||
|
||||
|
||||
42
Terminal.Gui/View/Orientation/IOrientation.cs
Normal file
42
Terminal.Gui/View/Orientation/IOrientation.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
|
||||
namespace Terminal.Gui;
|
||||
using System;
|
||||
|
||||
/// <summary>
|
||||
/// Implement this interface to provide orientation support.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// See <see cref="OrientationHelper"/> for a helper class that implements this interface.
|
||||
/// </remarks>
|
||||
public interface IOrientation
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the orientation of the View.
|
||||
/// </summary>
|
||||
Orientation Orientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when <see cref="Orientation"/> is changing. Can be cancelled.
|
||||
/// </summary>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <summary>
|
||||
/// Called when <see cref="Orientation"/> is changing.
|
||||
/// </summary>
|
||||
/// <param name="currentOrientation">The current orientation.</param>
|
||||
/// <param name="newOrientation">The new orientation.</param>
|
||||
/// <returns><see langword="true"/> to cancel the change.</returns>
|
||||
public bool OnOrientationChanging (Orientation currentOrientation, Orientation newOrientation) { return false; }
|
||||
|
||||
/// <summary>
|
||||
/// Raised when <see cref="Orientation"/> has changed.
|
||||
/// </summary>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Called when <see cref="Orientation"/> has been changed.
|
||||
/// </summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
/// <returns></returns>
|
||||
public void OnOrientationChanged (Orientation newOrientation) { return; }
|
||||
}
|
||||
138
Terminal.Gui/View/Orientation/OrientationHelper.cs
Normal file
138
Terminal.Gui/View/Orientation/OrientationHelper.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for implementing <see cref="IOrientation"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Implements the standard pattern for changing/changed events.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <example>
|
||||
/// <code>
|
||||
/// 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<EventArgs<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 newOrientation)
|
||||
/// {
|
||||
/// // Custom logic after orientation has changed
|
||||
/// }
|
||||
/// }
|
||||
/// </code>
|
||||
/// </example>
|
||||
public class OrientationHelper
|
||||
{
|
||||
private Orientation _orientation;
|
||||
private readonly IOrientation _owner;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="OrientationHelper"/> class.
|
||||
/// </summary>
|
||||
/// <param name="owner">Specifies the object that owns this helper instance and implements <see cref="IOrientation"/>.</param>
|
||||
public OrientationHelper (IOrientation owner) { _owner = owner; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the orientation of the View.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
if (_orientation == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 the event is not canceled by the virtual method, raise the event to notify any external subscribers.
|
||||
CancelEventArgs<Orientation> 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;
|
||||
|
||||
if (_owner is { })
|
||||
{
|
||||
_owner.Orientation = value;
|
||||
}
|
||||
}
|
||||
|
||||
// Best practice is to invoke the virtual method first.
|
||||
_owner?.OnOrientationChanged (_orientation);
|
||||
|
||||
// Even though Changed is not cancelable, it is still a good practice to raise the event after.
|
||||
OrientationChanged?.Invoke (_owner, new (in _orientation));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the orientation is changing. This is cancelable.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Views that implement <see cref="IOrientation"/> should raise <see cref="IOrientation.OrientationChanging"/>
|
||||
/// after the orientation has changed
|
||||
/// (<code>_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);</code>).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This event will be raised after the <see cref="IOrientation.OnOrientationChanging"/> method is called (assuming
|
||||
/// it was not canceled).
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when the orientation has changed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Views that implement <see cref="IOrientation"/> should raise <see cref="IOrientation.OrientationChanged"/>
|
||||
/// after the orientation has changed
|
||||
/// (<code>_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);</code>).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This event will be raised after the <see cref="IOrientation.OnOrientationChanged"/> method is called.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
}
|
||||
@@ -11,8 +11,10 @@ namespace Terminal.Gui;
|
||||
/// align them in a specific order.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class Bar : View
|
||||
public class Bar : View, IOrientation, IDesignable
|
||||
{
|
||||
private readonly OrientationHelper _orientationHelper;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Bar () : this ([]) { }
|
||||
|
||||
@@ -24,6 +26,10 @@ public class Bar : View
|
||||
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 class Bar : View
|
||||
Border.LineStyle = value;
|
||||
}
|
||||
|
||||
private Orientation _orientation = Orientation.Horizontal;
|
||||
#region IOrientation members
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
|
||||
@@ -58,16 +64,27 @@ public class Bar : View
|
||||
/// Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to right.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
SetNeedsLayout ();
|
||||
}
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <summary>Called when <see cref="Orientation"/> has changed.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
public void OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
SetNeedsLayout ();
|
||||
}
|
||||
#endregion
|
||||
|
||||
private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd;
|
||||
|
||||
/// <summary>
|
||||
@@ -224,4 +241,28 @@ public class Bar : View
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,43 +1,60 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Draws a single line using the <see cref="LineStyle"/> specified by <see cref="View.BorderStyle"/>.</summary>
|
||||
public class Line : View
|
||||
public class Line : View, IOrientation
|
||||
{
|
||||
private readonly OrientationHelper _orientationHelper;
|
||||
|
||||
/// <summary>Constructs a Line object.</summary>
|
||||
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
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <summary>Called when <see cref="Orientation"/> has changed.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
public void OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
|
||||
switch (newOrientation)
|
||||
{
|
||||
_orientation = value;
|
||||
case Orientation.Horizontal:
|
||||
Height = 1;
|
||||
|
||||
switch (Orientation)
|
||||
{
|
||||
case Orientation.Horizontal:
|
||||
Height = 1;
|
||||
break;
|
||||
case Orientation.Vertical:
|
||||
Width = 1;
|
||||
|
||||
break;
|
||||
case Orientation.Vertical:
|
||||
Width = 1;
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void SetBorderStyle (LineStyle value)
|
||||
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary><see cref="EventArgs"/> for <see cref="Orientation"/> events.</summary>
|
||||
public class OrientationEventArgs : EventArgs
|
||||
{
|
||||
/// <summary>Constructs a new instance.</summary>
|
||||
/// <param name="orientation">the new orientation</param>
|
||||
public OrientationEventArgs (Orientation orientation)
|
||||
{
|
||||
Orientation = orientation;
|
||||
Cancel = false;
|
||||
}
|
||||
|
||||
/// <summary>If set to true, the orientation change operation will be canceled, if applicable.</summary>
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
/// <summary>The new orientation.</summary>
|
||||
public Orientation Orientation { get; set; }
|
||||
}
|
||||
@@ -1,14 +1,14 @@
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>Displays a group of labels each with a selected indicator. Only one of those can be selected at a given time.</summary>
|
||||
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<string> _radioLabels = [];
|
||||
private int _selected;
|
||||
private readonly OrientationHelper _orientationHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="RadioGroup"/> class.
|
||||
@@ -54,6 +54,7 @@ public class RadioGroup : View, IDesignable
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MoveHome ();
|
||||
|
||||
return true;
|
||||
@@ -68,6 +69,7 @@ public class RadioGroup : View, IDesignable
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
MoveEnd ();
|
||||
|
||||
return true;
|
||||
@@ -89,6 +91,7 @@ public class RadioGroup : View, IDesignable
|
||||
ctx =>
|
||||
{
|
||||
SetFocus ();
|
||||
|
||||
if (ctx.KeyBinding?.Context is { } && (int)ctx.KeyBinding?.Context! < _radioLabels.Count)
|
||||
{
|
||||
SelectedItem = (int)ctx.KeyBinding?.Context!;
|
||||
@@ -99,6 +102,11 @@ public class RadioGroup : View, IDesignable
|
||||
return true;
|
||||
});
|
||||
|
||||
_orientationHelper = new (this);
|
||||
_orientationHelper.Orientation = Orientation.Vertical;
|
||||
_orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
|
||||
_orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
|
||||
|
||||
SetupKeyBindings ();
|
||||
|
||||
LayoutStarted += RadioGroup_LayoutStarted;
|
||||
@@ -138,15 +146,15 @@ public class RadioGroup : View, IDesignable
|
||||
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;
|
||||
|
||||
@@ -169,7 +177,7 @@ public class RadioGroup : View, IDesignable
|
||||
get => _horizontalSpace;
|
||||
set
|
||||
{
|
||||
if (_horizontalSpace != value && _orientation == Orientation.Horizontal)
|
||||
if (_horizontalSpace != value && Orientation == Orientation.Horizontal)
|
||||
{
|
||||
_horizontalSpace = value;
|
||||
UpdateTextFormatterText ();
|
||||
@@ -178,16 +186,6 @@ public class RadioGroup : View, IDesignable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
|
||||
/// <see cref="Orientation.Vertical"/>.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set => OnOrientationChanged (value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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 <see cref="View.HotKey"/> for details on how HotKeys work.
|
||||
@@ -319,44 +317,49 @@ public class RadioGroup : View, IDesignable
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Called when the view orientation has changed. Invokes the <see cref="OrientationChanged"/> event.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
/// <returns>True of the event was cancelled.</returns>
|
||||
public virtual bool OnOrientationChanged (Orientation newOrientation)
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Orientation"/> for this <see cref="RadioGroup"/>. The default is
|
||||
/// <see cref="Orientation.Vertical"/>.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
var args = new OrientationEventArgs (newOrientation);
|
||||
OrientationChanged?.Invoke (this, args);
|
||||
|
||||
if (!args.Cancel)
|
||||
{
|
||||
_orientation = newOrientation;
|
||||
SetupKeyBindings ();
|
||||
SetContentSize ();
|
||||
}
|
||||
|
||||
return args.Cancel;
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
#region IOrientation
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <summary>Called when <see cref="Orientation"/> has changed.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
public void OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
SetupKeyBindings ();
|
||||
SetContentSize ();
|
||||
}
|
||||
|
||||
#endregion IOrientation
|
||||
|
||||
// TODO: This should be cancelable
|
||||
/// <summary>Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.</summary>
|
||||
/// <param name="selectedItem"></param>
|
||||
/// <param name="previousSelectedItem"></param>
|
||||
public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
|
||||
{
|
||||
{
|
||||
if (_selected == selectedItem)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_selected = selectedItem;
|
||||
SelectedItemChanged?.Invoke (this, new (selectedItem, previousSelectedItem));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the view orientation has changed. Can be cancelled by setting
|
||||
/// <see cref="OrientationEventArgs.Cancel"/> to true.
|
||||
/// </summary>
|
||||
public event EventHandler<OrientationEventArgs> OrientationChanged;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override Point? PositionCursor ()
|
||||
{
|
||||
@@ -370,7 +373,10 @@ public class RadioGroup : View, IDesignable
|
||||
|
||||
break;
|
||||
case Orientation.Horizontal:
|
||||
x = _horizontal [_cursor].pos;
|
||||
if (_horizontal.Count > 0)
|
||||
{
|
||||
x = _horizontal [_cursor].pos;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -424,7 +430,7 @@ public class RadioGroup : View, IDesignable
|
||||
|
||||
private void SetContentSize ()
|
||||
{
|
||||
switch (_orientation)
|
||||
switch (Orientation)
|
||||
{
|
||||
case Orientation.Vertical:
|
||||
var width = 0;
|
||||
@@ -457,10 +463,11 @@ public class RadioGroup : View, IDesignable
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
/// <inheritdoc/>
|
||||
public bool EnableForDesign ()
|
||||
{
|
||||
RadioLabels = new [] { "Option _1", "Option _2", "Option _3" };
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.ComponentModel;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Terminal.Gui;
|
||||
namespace Terminal.Gui;
|
||||
|
||||
/// <summary>
|
||||
/// Displays a command, help text, and a key binding. When the key specified by <see cref="Key"/> is pressed, the command will be invoked. Useful for
|
||||
/// Displays a command, help text, and a key binding. When the key specified by <see cref="Key"/> is pressed, the
|
||||
/// command will be invoked. Useful for
|
||||
/// displaying a command in <see cref="Bar"/> such as a
|
||||
/// menu, toolbar, or status bar.
|
||||
/// </summary>
|
||||
@@ -12,12 +10,13 @@ namespace Terminal.Gui;
|
||||
/// <para>
|
||||
/// The following user actions will invoke the <see cref="Command.Accept"/>, causing the
|
||||
/// <see cref="View.Accept"/> event to be fired:
|
||||
/// - Clicking on the <see cref="Shortcut"/>.
|
||||
/// - Pressing the key specified by <see cref="Key"/>.
|
||||
/// - Pressing the HotKey specified by <see cref="CommandView"/>.
|
||||
/// - Clicking on the <see cref="Shortcut"/>.
|
||||
/// - Pressing the key specified by <see cref="Key"/>.
|
||||
/// - Pressing the HotKey specified by <see cref="CommandView"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked <see cref="Command.Accept"/>
|
||||
/// If <see cref="KeyBindingScope"/> is <see cref="KeyBindingScope.Application"/>, <see cref="Key"/> will invoked
|
||||
/// <see cref="Command.Accept"/>
|
||||
/// command regardless of what View has focus, enabling an application-wide keyboard shortcut.
|
||||
/// </para>
|
||||
/// <para>
|
||||
@@ -37,8 +36,10 @@ namespace Terminal.Gui;
|
||||
/// If the <see cref="Key"/> is <see cref="Key.Empty"/>, the <see cref="Key"/> text is not displayed.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class Shortcut : View
|
||||
public class Shortcut : View, IOrientation, IDesignable
|
||||
{
|
||||
private readonly OrientationHelper _orientationHelper;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Shortcut"/>.
|
||||
/// </summary>
|
||||
@@ -60,6 +61,10 @@ public class Shortcut : View
|
||||
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,48 @@ public class Shortcut : View
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of <see cref="Shortcut"/>.
|
||||
/// </summary>
|
||||
public Shortcut () : this (Key.Empty, string.Empty, null) { }
|
||||
|
||||
private Orientation _orientation = Orientation.Horizontal;
|
||||
#region IOrientation members
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Orientation"/> for this <see cref="Shortcut"/>. The default is
|
||||
/// <see cref="Orientation.Horizontal"/>, which is ideal for status bar, menu bar, and tool bar items If set to
|
||||
/// <see cref="Orientation.Vertical"/>,
|
||||
/// the Shortcut will be configured for vertical layout, which is ideal for menu items.
|
||||
/// Gets or sets the <see cref="Orientation"/> for this <see cref="Bar"/>. The default is
|
||||
/// <see cref="Orientation.Horizontal"/>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Horizontal orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from right to
|
||||
/// left
|
||||
/// Vertical orientation arranges the command, help, and key parts of each <see cref="Shortcut"/>s from left to
|
||||
/// right.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _orientation;
|
||||
set
|
||||
{
|
||||
_orientation = value;
|
||||
|
||||
// TODO: Determine what, if anything, is opinionated about the orientation.
|
||||
}
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <summary>Called when <see cref="Orientation"/> has changed.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
public void OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
// TODO: Determine what, if anything, is opinionated about the orientation.
|
||||
SetNeedsLayout ();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast;
|
||||
|
||||
/// <summary>
|
||||
@@ -344,7 +366,6 @@ public class Shortcut : View
|
||||
private void Subview_MouseClick (object sender, MouseEventEventArgs e)
|
||||
{
|
||||
// TODO: Remove. This does nothing.
|
||||
return;
|
||||
}
|
||||
|
||||
#region Command
|
||||
@@ -434,8 +455,6 @@ public class Shortcut : View
|
||||
SetKeyViewDefaultLayout ();
|
||||
ShowHide ();
|
||||
UpdateKeyBinding ();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -475,38 +494,38 @@ public class Shortcut : View
|
||||
HelpView.VerticalTextAlignment = Alignment.Center;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
|
||||
/// .
|
||||
/// </summary>
|
||||
public override string Text
|
||||
{
|
||||
get => HelpView?.Text;
|
||||
set
|
||||
/// <summary>
|
||||
/// Gets or sets the help text displayed in the middle of the Shortcut. Identical in function to <see cref="HelpText"/>
|
||||
/// .
|
||||
/// </summary>
|
||||
public override string Text
|
||||
{
|
||||
if (HelpView is {})
|
||||
get => HelpView?.Text;
|
||||
set
|
||||
{
|
||||
HelpView.Text = value;
|
||||
ShowHide ();
|
||||
if (HelpView is { })
|
||||
{
|
||||
HelpView.Text = value;
|
||||
ShowHide ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the help text displayed in the middle of the Shortcut.
|
||||
/// </summary>
|
||||
public string HelpText
|
||||
{
|
||||
get => HelpView?.Text;
|
||||
set
|
||||
/// <summary>
|
||||
/// Gets or sets the help text displayed in the middle of the Shortcut.
|
||||
/// </summary>
|
||||
public string HelpText
|
||||
{
|
||||
if (HelpView is {})
|
||||
get => HelpView?.Text;
|
||||
set
|
||||
{
|
||||
HelpView.Text = value;
|
||||
ShowHide ();
|
||||
if (HelpView is { })
|
||||
{
|
||||
HelpView.Text = value;
|
||||
ShowHide ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Help
|
||||
|
||||
@@ -561,7 +580,7 @@ public string HelpText
|
||||
private int _minimumKeyTextSize;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
|
||||
/// Gets or sets the minimum size of the key text. Useful for aligning the key text with other <see cref="Shortcut"/>s.
|
||||
/// </summary>
|
||||
public int MinimumKeyTextSize
|
||||
{
|
||||
@@ -675,6 +694,7 @@ public string HelpText
|
||||
if (Action is { })
|
||||
{
|
||||
Action.Invoke ();
|
||||
|
||||
// Assume if there's a subscriber to Action, it's handled.
|
||||
cancel = true;
|
||||
}
|
||||
@@ -700,11 +720,10 @@ public string HelpText
|
||||
{
|
||||
return CommandView.InvokeCommand (Command.Select, ctx.Key, ctx.KeyBinding);
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
#region Focus
|
||||
|
||||
/// <inheritdoc/>
|
||||
@@ -753,7 +772,8 @@ public string HelpText
|
||||
}
|
||||
}
|
||||
|
||||
View _lastFocusedView;
|
||||
private View _lastFocusedView;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool OnEnter (View view)
|
||||
{
|
||||
@@ -774,6 +794,16 @@ public string HelpText
|
||||
|
||||
#endregion Focus
|
||||
|
||||
/// <inheritdoc/>
|
||||
public bool EnableForDesign ()
|
||||
{
|
||||
Title = "_Shortcut";
|
||||
HelpText = "Shortcut help";
|
||||
Key = Key.F1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
protected override void Dispose (bool disposing)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ public class Slider : Slider<object>
|
||||
/// keyboard or mouse.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class Slider<T> : View
|
||||
public class Slider<T> : View, IOrientation
|
||||
{
|
||||
private readonly SliderConfiguration _config = new ();
|
||||
|
||||
@@ -31,6 +31,8 @@ public class Slider<T> : View
|
||||
// Options
|
||||
private List<SliderOption<T>> _options;
|
||||
|
||||
private OrientationHelper _orientationHelper;
|
||||
|
||||
#region Initialize
|
||||
|
||||
private void SetInitialProperties (
|
||||
@@ -45,11 +47,13 @@ public class Slider<T> : View
|
||||
|
||||
_options = options ?? new List<SliderOption<T>> ();
|
||||
|
||||
_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,13 +226,46 @@ public class Slider<T> : View
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>Slider Orientation. <see cref="Gui.Orientation"></see></summary>
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="Orientation"/>. The default is <see cref="Orientation.Horizontal"/>.
|
||||
/// </summary>
|
||||
public Orientation Orientation
|
||||
{
|
||||
get => _config._sliderOrientation;
|
||||
set => OnOrientationChanged (value);
|
||||
get => _orientationHelper.Orientation;
|
||||
set => _orientationHelper.Orientation = value;
|
||||
}
|
||||
|
||||
#region IOrientation members
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <inheritdoc />
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnOrientationChanged (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
|
||||
|
||||
/// <summary>Legends Orientation. <see cref="Gui.Orientation"></see></summary>
|
||||
public Orientation LegendsOrientation
|
||||
{
|
||||
@@ -309,43 +346,6 @@ public class Slider<T> : View
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the slider orientation has changed. Can be cancelled by setting
|
||||
/// <see cref="OrientationEventArgs.Cancel"/> to true.
|
||||
/// </summary>
|
||||
public event EventHandler<OrientationEventArgs> OrientationChanged;
|
||||
|
||||
/// <summary>Called when the slider orientation has changed. Invokes the <see cref="OrientationChanged"/> event.</summary>
|
||||
/// <param name="newOrientation"></param>
|
||||
/// <returns>True of the event was cancelled.</returns>
|
||||
public virtual bool OnOrientationChanged (Orientation newOrientation)
|
||||
{
|
||||
var args = new OrientationEventArgs (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;
|
||||
}
|
||||
|
||||
/// <summary>Event raised when the slider option/s changed. The dictionary contains: key = option index, value = T</summary>
|
||||
public event EventHandler<SliderEventArgs<T>> OptionsChanged;
|
||||
|
||||
@@ -1742,7 +1742,7 @@ public class Slider<T> : View
|
||||
|
||||
internal bool Select ()
|
||||
{
|
||||
SetFocusedOption();
|
||||
SetFocusedOption ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
/// </summary>
|
||||
public class StatusBar : Bar
|
||||
public class StatusBar : Bar, IDesignable
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public StatusBar () : this ([]) { }
|
||||
@@ -73,4 +73,74 @@ public class StatusBar : Bar
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
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}"); }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -272,9 +272,9 @@ public class AllViewsTester : Scenario
|
||||
|
||||
_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 @@ public class AllViewsTester : Scenario
|
||||
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
|
||||
|
||||
@@ -402,7 +402,7 @@ public class Bars : Scenario
|
||||
bar.Add (shortcut1, shortcut2, line, shortcut3);
|
||||
}
|
||||
|
||||
private void ConfigStatusBar (Bar bar)
|
||||
public void ConfigStatusBar (Bar bar)
|
||||
{
|
||||
var shortcut = new Shortcut
|
||||
{
|
||||
|
||||
@@ -74,7 +74,7 @@ public class ExpanderButton : Button
|
||||
/// <returns>True of the event was cancelled.</returns>
|
||||
protected virtual bool OnOrientationChanging (Orientation newOrientation)
|
||||
{
|
||||
var args = new OrientationEventArgs (newOrientation);
|
||||
var args = new CancelEventArgs<Orientation> (in _orientation, ref newOrientation);
|
||||
OrientationChanging?.Invoke (this, args);
|
||||
|
||||
if (!args.Cancel)
|
||||
@@ -103,10 +103,9 @@ public class ExpanderButton : Button
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fired when the orientation has changed. Can be cancelled by setting
|
||||
/// <see cref="OrientationEventArgs.Cancel"/> to true.
|
||||
/// Fired when the orientation has changed. Can be cancelled.
|
||||
/// </summary>
|
||||
public event EventHandler<OrientationEventArgs> OrientationChanging;
|
||||
public event EventHandler<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
|
||||
/// <summary>
|
||||
/// The glyph to display when the view is collapsed.
|
||||
|
||||
@@ -31,9 +31,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="[2024.2.0,)" PrivateAssets="all" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="[17.10,18)" />
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="ReportGenerator" Version="5.3.8" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="21.0.29" />
|
||||
<PackageReference Include="Moq" Version="[4.20.70,5)" />
|
||||
<PackageReference Include="ReportGenerator" Version="[5.3.8,6)" />
|
||||
<PackageReference Include="TestableIO.System.IO.Abstractions.TestingHelpers" Version="[21.0.29,22)" />
|
||||
<PackageReference Include="xunit" Version="[2.9.0,3)" />
|
||||
<PackageReference Include="Xunit.Combinatorial" Version="[1.6.24,2)" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="[2.8.2,3)">
|
||||
@@ -59,6 +59,9 @@
|
||||
<Using Include="Terminal.Gui" />
|
||||
<Using Include="Xunit" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="View\Orientation\" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="FineCodeCoverage">
|
||||
<Enabled>
|
||||
False
|
||||
|
||||
107
UnitTests/View/Orientation/OrientationHelperTests.cs
Normal file
107
UnitTests/View/Orientation/OrientationHelperTests.cs
Normal file
@@ -0,0 +1,107 @@
|
||||
using Moq;
|
||||
|
||||
namespace Terminal.Gui.ViewTests.OrientationTests;
|
||||
|
||||
public class OrientationHelperTests
|
||||
{
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_InvokesChangingAndChangedEvents ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
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.Vertical;
|
||||
|
||||
// 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<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var onChangingOverrideCalled = false;
|
||||
var onChangedOverrideCalled = false;
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
|
||||
.Callback (() => onChangingOverrideCalled = true)
|
||||
.Returns (false); // Ensure it doesn't cancel the change
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanged (It.IsAny<Orientation> ()))
|
||||
.Callback (() => onChangedOverrideCalled = true);
|
||||
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// 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<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
orientationHelper.Orientation = Orientation.Horizontal; // 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.Horizontal; // 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<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
orientationHelper.OrientationChanging += (sender, e) => { e.Cancel = true; }; // Cancel the change
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (Orientation.Horizontal, orientationHelper.Orientation); // Initial orientation is Horizontal
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Orientation_Set_NewValue_OnOrientationChanging_CancelsChange ()
|
||||
{
|
||||
// Arrange
|
||||
Mock<IOrientation> mockIOrientation = new Mock<IOrientation> ();
|
||||
|
||||
mockIOrientation.Setup (x => x.OnOrientationChanging (It.IsAny<Orientation> (), It.IsAny<Orientation> ()))
|
||||
.Returns (true); // Override to return true, cancelling the change
|
||||
|
||||
var orientationHelper = new OrientationHelper (mockIOrientation.Object);
|
||||
|
||||
// Act
|
||||
orientationHelper.Orientation = Orientation.Vertical;
|
||||
|
||||
// Assert
|
||||
Assert.Equal (
|
||||
Orientation.Horizontal,
|
||||
orientationHelper.Orientation); // Initial orientation is Horizontal, and it should remain unchanged due to cancellation
|
||||
}
|
||||
}
|
||||
136
UnitTests/View/Orientation/OrientationTests.cs
Normal file
136
UnitTests/View/Orientation/OrientationTests.cs
Normal file
@@ -0,0 +1,136 @@
|
||||
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<CancelEventArgs<Orientation>> OrientationChanging;
|
||||
public event EventHandler<EventArgs<Orientation>> OrientationChanged;
|
||||
|
||||
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 newOrientation)
|
||||
{
|
||||
OnOrientationChangedCalled = true;
|
||||
// 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
|
||||
}
|
||||
|
||||
|
||||
[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.");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user