#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); // Initialized += Bar_Initialized; MouseEvent += OnMouseEvent; if (shortcuts is { }) { 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) { if (Border is { }) { // 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; } #pragma warning disable CS0067 // The event is never used /// public event EventHandler>? OrientationChanging; /// public event EventHandler>? OrientationChanged; #pragma warning restore CS0067 // The event is never used /// 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.ElementAt (i); } } if (toRemove is { }) { Remove (toRemove); //SetNeedsDraw (); SetNeedsLayout (); } return toRemove as Shortcut; } /// protected override void OnSubViewLayout (LayoutEventArgs args) { LayoutBarItems (args.OldContentSize); } private void LayoutBarItems (Size contentSize) { View? prevBarItem = null; switch (Orientation) { case Orientation.Horizontal: for (var index = 0; index < SubViews.Count; index++) { View barItem = SubViews.ElementAt (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.ElementAt (index); barItem.ColorScheme = ColorScheme; if (!barItem.Visible) { continue; } if (barItem is Shortcut scBarItem) { barItem.X = 0; 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) { if (subView is not Line) { subView.Width = Dim.Auto (DimAutoStyle.Auto, minimumContentDim: maxBarItemWidth); } } } else { foreach (var subView in SubViews) { if (subView is not Line) { 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; } }