#nullable enable namespace Terminal.Gui; /// /// Provides a horizontally or vertically oriented container for s to be used as a menu, toolbar, or status /// bar. /// /// /// /// Any can be added to a . However, the is designed to work with /// objects. The class provides a way to display a command, help, and key and /// align them in a specific order. /// /// public class Bar : View, IOrientation, IDesignable { private readonly OrientationHelper _orientationHelper; /// public Bar () : this ([]) { } /// public Bar (IEnumerable shortcuts) { CanFocus = true; 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; MouseEvent += OnMouseEvent; if (shortcuts is null) { return; } foreach (Shortcut shortcut in shortcuts) { Add (shortcut); } } private void OnMouseEvent (object? sender, MouseEventArgs e) { NavigationDirection direction = NavigationDirection.Backward; if (e.Flags == MouseFlags.WheeledDown) { e.Handled = true; } if (e.Flags == MouseFlags.WheeledUp) { direction = NavigationDirection.Forward; e.Handled = true; } if (e.Flags == MouseFlags.WheeledRight) { e.Handled = true; } if (e.Flags == MouseFlags.WheeledLeft) { direction = NavigationDirection.Forward; e.Handled = true; } if (e.Handled) { e.Handled = AdvanceFocus (direction, TabBehavior.TabStop); } } /// public override void EndInit () { base.EndInit (); ColorScheme = Colors.ColorSchemes ["Menu"]; } /// public override void SetBorderStyle (LineStyle value) { // The default changes the thickness. We don't want that. We just set the style. Border.LineStyle = value; } #region IOrientation members /// /// 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 => _orientationHelper.Orientation; set => _orientationHelper.Orientation = value; } /// public event EventHandler>? OrientationChanging; /// public event EventHandler>? OrientationChanged; /// Called when has changed. /// public void OnOrientationChanged (Orientation newOrientation) { // BUGBUG: this should not be SuperView.GetContentSize LayoutBarItems (SuperView?.GetContentSize () ?? Application.Screen.Size); } #endregion private AlignmentModes _alignmentModes = AlignmentModes.StartToEnd; /// /// Gets or sets the for this . The default is /// . /// public AlignmentModes AlignmentModes { get => _alignmentModes; set { _alignmentModes = value; SetNeedsDraw (); SetNeedsLayout (); } } // TODO: Move this to View /// Inserts a in the specified index of . /// The zero-based index at which item should be inserted. /// The item to insert. public void AddShortcutAt (int index, Shortcut item) { List savedSubViewList = Subviews.ToList (); int count = savedSubViewList.Count; RemoveAll (); for (var i = 0; i <= count; i++) { if (i == index) { Add (item); } if (i < count) { Add (savedSubViewList [i]); } } SetNeedsDraw (); SetNeedsLayout (); } // TODO: Move this to View /// Removes a at specified index of . /// The zero-based index of the item to remove. /// The removed. public Shortcut? RemoveShortcut (int index) { View? toRemove = null; for (var i = 0; i < Subviews.Count; i++) { if (i == index) { toRemove = Subviews [i]; } } if (toRemove is { }) { Remove (toRemove); SetNeedsDraw (); SetNeedsLayout (); } return toRemove as Shortcut; } /// protected override void OnSubviewLayout (LayoutEventArgs args) { LayoutBarItems (args.OldContentSize); } // This is used to calculate the minimum width of the Bar when the width is NOT Dim.Auto private int? _minimumDimAutoWidth; private void LayoutBarItems (Size contentSize) { View? prevBarItem = null; switch (Orientation) { case Orientation.Horizontal: for (var index = 0; index < Subviews.Count; index++) { View barItem = Subviews [index]; barItem.ColorScheme = ColorScheme; barItem.X = Pos.Align (Alignment.Start, AlignmentModes); barItem.Y = 0; //Pos.Center (); } break; case Orientation.Vertical: if (Width!.Has (out _)) { // Set the overall size of the Bar and arrange the views vertically var minKeyWidth = 0; List shortcuts = Subviews.Where (s => s is Shortcut && s.Visible).Cast ().ToList (); foreach (Shortcut shortcut in shortcuts) { // Get the largest width of all KeyView's minKeyWidth = int.Max (minKeyWidth, shortcut.KeyView.Text.GetColumns ()); } var _maxBarItemWidth = 0; for (var index = 0; index < Subviews.Count; index++) { View barItem = Subviews [index]; barItem.X = 0; barItem.ColorScheme = ColorScheme; if (!barItem.Visible) { continue; } if (barItem is Shortcut scBarItem) { scBarItem.MinimumKeyTextSize = minKeyWidth; scBarItem.Width = scBarItem.GetWidthDimAuto (); barItem.Layout (Application.Screen.Size); _maxBarItemWidth = Math.Max (_maxBarItemWidth, barItem.Frame.Width); } if (prevBarItem == null) { // TODO: Just use Pos.Align! barItem.Y = 0; } else { // TODO: Just use Pos.Align! // Align the view to the bottom of the previous view barItem.Y = Pos.Bottom (prevBarItem); } prevBarItem = barItem; } foreach (var subView in Subviews) { subView.Width = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: _maxBarItemWidth); } } else { foreach (var subView in Subviews) { subView.Width = Dim.Fill(); } } 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); shortcut = new Shortcut { Text = "Czech", CommandView = new CheckBox () { Title = "_Check" }, Key = Key.F9, CanFocus = false }; Add (shortcut); return true; } }