using System; using System.Text; using Terminal.Gui; namespace UICatalog.Scenarios; /// /// A Button that can expand or collapse a view. /// /// /// /// 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. /// /// /// If is set to , the button will appear /// at the top/right. /// If is set to , the button will /// appear at the /// bottom/left. /// /// /// /// private void MyView_Initialized (object sender, EventArgs e) /// { /// Border.Add(new ExpanderButton ()); /// ... /// public class ExpanderButton : Button { public ExpanderButton () { CanFocus = false; Width = 1; Height = 1; NoDecorations = true; NoPadding = true; ShadowStyle = ShadowStyle.None; AddCommand (Command.HotKey, Toggle); AddCommand (Command.ToggleExpandCollapse, Toggle); KeyBindings.Add (Key.F4, Command.ToggleExpandCollapse); Orientation = Orientation.Vertical; Initialized += ExpanderButton_Initialized; } private void ExpanderButton_Initialized (object sender, EventArgs e) { ExpandOrCollapse (Collapsed); } private Orientation _orientation = Orientation.Horizontal; /// Orientation. /// /// /// If is set to , the button will appear at the /// top/right. /// If is set to , the button will appear at the /// bottom/left. /// /// public Orientation Orientation { get => _orientation; set => OnOrientationChanging (value); } /// Called when the orientation is changing. Invokes the event. /// /// True of the event was cancelled. protected virtual bool OnOrientationChanging (Orientation newOrientation) { var args = new OrientationEventArgs (newOrientation); OrientationChanging?.Invoke (this, args); if (!args.Cancel) { _orientation = newOrientation; if (Orientation == Orientation.Vertical) { X = Pos.AnchorEnd (); Y = 0; CollapsedGlyph = new ('\u21d1'); // ⇑ ExpandedGlyph = new ('\u21d3'); // ⇓ } else { X = 0; Y = Pos.AnchorEnd (); CollapsedGlyph = new ('\u21d0'); // ⇐ ExpandedGlyph = new ('\u21d2'); // ⇒ } ExpandOrCollapse (Collapsed); } return args.Cancel; } /// /// Fired when the orientation has changed. Can be cancelled by setting /// to true. /// public event EventHandler OrientationChanging; /// /// The glyph to display when the view is collapsed. /// public Rune CollapsedGlyph { get; set; } /// /// The glyph to display when the view is expanded. /// public Rune ExpandedGlyph { get; set; } private bool _collapsed; /// /// Gets or sets a value indicating whether the view is collapsed. /// public bool Collapsed { get => _collapsed; set => OnCollapsedChanging (value); } /// Called when the orientation is changing. Invokes the event. /// /// /// True of the event was cancelled. protected virtual bool OnCollapsedChanging (bool newValue) { CancelEventArgs args = new (ref _collapsed, ref newValue); CollapsedChanging?.Invoke (this, args); if (!args.Cancel) { _collapsed = args.NewValue; ExpandOrCollapse (_collapsed); View superView = SuperView; if (superView is Adornment adornment) { superView = adornment.Parent; } foreach (View subview in superView.Subviews) { subview.Visible = !Collapsed; subview.Enabled = !Collapsed; } } return args.Cancel; } /// /// Fired when the orientation has changed. Can be cancelled by setting /// to true. /// public event EventHandler> CollapsedChanging; /// /// Collapses or Expands the view. /// /// public bool? Toggle () { Collapsed = !Collapsed; return true; } private Dim _previousDim; private void ExpandOrCollapse (bool collapse) { Text = $"{(Collapsed ? CollapsedGlyph : ExpandedGlyph)}"; 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; } } } }