123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258 |
- #nullable enable
- using System;
- using System.Text;
- using Terminal.Gui;
- namespace UICatalog.Scenarios;
- /// <summary>
- /// A Button that can expand or collapse a view.
- /// </summary>
- /// <remarks>
- /// <para>
- /// Add this button to a view's Border to allow the user to expand or collapse the view via either the keyboard
- /// (F4) or mouse.
- /// </para>
- /// <para>
- /// If <see cref="Orientation"/> is set to <see cref="Terminal.Gui.Orientation.Vertical"/>, the button will appear
- /// at the top/right.
- /// If <see cref="Orientation"/> is set to <see cref="Terminal.Gui.Orientation.Horizontal"/>, the button will
- /// appear at the
- /// bottom/left.
- /// </para>
- /// </remarks>
- /// <example>
- /// private void MyView_Initialized (object sender, EventArgs e)
- /// {
- /// Border.Add(new ExpanderButton ());
- /// ...
- /// </example>
- public class ExpanderButton : Button
- {
- public ExpanderButton ()
- {
- CanFocus = false;
- Width = 1;
- Height = 1;
- NoDecorations = true;
- NoPadding = true;
- AddCommand (Command.HotKey, Toggle);
- AddCommand (Command.Toggle, Toggle);
- KeyBindings.Add (Key.F4, Command.Toggle);
- Orientation = Orientation.Vertical;
- HighlightStyle = HighlightStyle.None;
- Initialized += ExpanderButton_Initialized;
- EnabledChanged += (sender, args) =>
- {
- ShowHide ();
- };
- }
- private void ShowHide ()
- {
- if (!Enabled)
- {
- Visible = false;
- }
- if (SuperView is Border { } border)
- {
- switch (Orientation)
- {
- case Orientation.Vertical:
- Visible = border.Thickness.Top > 0;
- break;
- case Orientation.Horizontal:
- Visible = border.Border?.Thickness.Left > 0;
- break;
- }
- }
- }
- private void ExpanderButton_Initialized (object? sender, EventArgs e)
- {
- ShadowStyle = ShadowStyle.None;
- ExpandOrCollapse (Collapsed);
- if (SuperView is Border { } border)
- {
- border.ThicknessChanged += (o, args) => ShowHide ();
- }
- }
- private Orientation _orientation = Orientation.Horizontal;
- /// <summary>Orientation.</summary>
- /// <remarks>
- /// <para>
- /// If <see cref="Orientation"/> is set to <see cref="Orientation.Vertical"/>, the button will appear at the
- /// top/right.
- /// If <see cref="Orientation"/> is set to <see cref="Orientation.Horizontal"/>, the button will appear at the
- /// bottom/left.
- /// </para>
- /// </remarks>
- public Orientation Orientation
- {
- get => _orientation;
- set => OnOrientationChanging (value);
- }
- /// <summary>Called when the orientation is changing. Invokes the <see cref="OrientationChanging"/> event.</summary>
- /// <param name="newOrientation"></param>
- /// <returns>True of the event was cancelled.</returns>
- protected virtual bool OnOrientationChanging (Orientation newOrientation)
- {
- CancelEventArgs<Orientation> args = new CancelEventArgs<Orientation> (in _orientation, ref newOrientation);
- OrientationChanging?.Invoke (this, args);
- if (!args.Cancel)
- {
- _orientation = newOrientation;
- if (Orientation == Orientation.Vertical)
- {
- X = Pos.AnchorEnd ();
- Y = 0;
- CollapseGlyph = new ('\u21d1'); // ⇑
- ExpandGlyph = new ('\u21d3'); // ⇓
- }
- else
- {
- X = 0;
- Y = Pos.AnchorEnd ();
- CollapseGlyph = new ('\u21d0'); // ⇐
- ExpandGlyph = new ('\u21d2'); // ⇒
- }
- ExpandOrCollapse (Collapsed);
- }
- return args.Cancel;
- }
- /// <summary>
- /// Fired when the orientation has changed. Can be cancelled.
- /// </summary>
- public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
- /// <summary>
- /// The glyph that indicates the button will collapse the view.
- /// </summary>
- public Rune CollapseGlyph { get; set; }
- /// <summary>
- /// The glyph that indicates the button will expand the view.
- /// </summary>
- public Rune ExpandGlyph { get; set; }
- private bool _collapsed;
- /// <summary>
- /// Gets or sets a value indicating whether the view is collapsed.
- /// </summary>
- public bool Collapsed
- {
- get => _collapsed;
- set => OnCollapsedChanging (value);
- }
- /// <summary>Called when the orientation is changing. Invokes the <see cref="OrientationChanging"/> event.</summary>
- /// <param name="newValue"></param>
- /// <returns>True of the event was cancelled.</returns>
- protected virtual bool OnCollapsedChanging (bool newValue)
- {
- CancelEventArgs<bool> args = new (ref _collapsed, ref newValue);
- CollapsedChanging?.Invoke (this, args);
- if (!args.Cancel)
- {
- _collapsed = args.NewValue;
- ExpandOrCollapse (_collapsed);
- }
- return args.Cancel;
- }
- /// <summary>
- /// Fired when the orientation has changed. Can be cancelled.
- /// </summary>
- public event EventHandler<CancelEventArgs<bool>>? CollapsedChanging;
- /// <summary>
- /// Collapses or Expands the view.
- /// </summary>
- /// <returns></returns>
- public bool? Toggle ()
- {
- Collapsed = !Collapsed;
- return true;
- }
- private Dim? _previousDim;
- private void ExpandOrCollapse (bool collapse)
- {
- Text = $"{(Collapsed ? ExpandGlyph : CollapseGlyph)}";
- View? superView = SuperView;
- if (superView is Adornment adornment)
- {
- superView = adornment.Parent;
- }
- if (superView is null)
- {
- return;
- }
- if (collapse)
- {
- // Collapse
- if (Orientation == Orientation.Vertical)
- {
- _previousDim = superView!.Height!;
- superView.Height = 1;
- }
- else
- {
- _previousDim = superView!.Width!;
- superView.Width = 1;
- }
- }
- else
- {
- if (_previousDim is null)
- {
- return;
- }
- // Expand
- if (Orientation == Orientation.Vertical)
- {
- superView.Height = _previousDim;
- }
- else
- {
- superView.Width = _previousDim;
- }
- }
- foreach (View subview in superView.Subviews)
- {
- subview.Visible = !Collapsed;
- }
- }
- }
|