#nullable enable namespace Terminal.Gui; /// /// A -derived object to be used as a vertically-oriented menu. Each subview is a . /// public class Menuv2 : Bar { /// public Menuv2 () : this ([]) { } /// public Menuv2 (IEnumerable? menuItems) : this (menuItems?.Cast ()) { } /// public Menuv2 (IEnumerable? shortcuts) : base (shortcuts) { Orientation = Orientation.Vertical; Width = Dim.Auto (); Height = Dim.Auto (DimAutoStyle.Content, 1); Border!.Thickness = new Thickness (1, 1, 1, 1); Border.LineStyle = LineStyle.Single; } /// /// Gets or sets the menu item that opened this menu as a sub-menu. /// public MenuItemv2? SuperMenuItem { get; set; } /// protected override void OnVisibleChanged () { if (Visible) { SelectedMenuItem = SubViews.Where (mi => mi is MenuItemv2).ElementAtOrDefault (0) as MenuItemv2; } } /// public override void EndInit () { base.EndInit (); if (Border is { }) { } } /// protected override void OnSubViewAdded (View view) { base.OnSubViewAdded (view); switch (view) { case MenuItemv2 menuItem: { menuItem.CanFocus = true; AddCommand (menuItem.Command, RaiseAccepted); menuItem.Accepted += MenuItemOnAccepted; break; void MenuItemOnAccepted (object? sender, CommandEventArgs e) { Logging.Trace ($"MenuItemOnAccepted: {e.Context?.Source?.Title}"); RaiseAccepted (e.Context); } } case Line line: // Grow line so we get auto-join line line.X = Pos.Func (() => -Border!.Thickness.Left); line.Width = Dim.Fill ()! + Dim.Func (() => Border!.Thickness.Right); break; } } /// protected override bool OnAccepting (CommandEventArgs args) { Logging.Trace ($"{args.Context}"); if (SuperMenuItem is { }) { Logging.Trace ($"Invoking Accept on SuperMenuItem: {SuperMenuItem.Title}..."); return SuperMenuItem?.SuperView?.InvokeCommand (Command.Accept, args.Context) is true; } return false; } // TODO: Consider moving Accepted to Bar? /// /// Raises the / event indicating an item in this menu (or submenu) /// was accepted. This is used to determine when to hide the menu. /// /// /// protected bool? RaiseAccepted (ICommandContext? ctx) { //Logging.Trace ($"RaiseAccepted: {ctx}"); CommandEventArgs args = new () { Context = ctx }; OnAccepted (args); Accepted?.Invoke (this, args); return true; } /// /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu. /// /// /// /// protected virtual void OnAccepted (CommandEventArgs args) { } /// /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu. /// /// /// /// See for more information. /// /// public event EventHandler? Accepted; /// protected override void OnFocusedChanged (View? previousFocused, View? focused) { base.OnFocusedChanged (previousFocused, focused); SelectedMenuItem = focused as MenuItemv2; RaiseSelectedMenuItemChanged (SelectedMenuItem); } /// /// Gets or set the currently selected menu item. This is a helper that /// tracks . /// public MenuItemv2? SelectedMenuItem { get => Focused as MenuItemv2; set { if (value == Focused) { return; } // Note we DO NOT set focus here; This property tracks Focused } } internal void RaiseSelectedMenuItemChanged (MenuItemv2? selected) { //Logging.Trace ($"RaiseSelectedMenuItemChanged: {selected?.Title}"); OnSelectedMenuItemChanged (selected); SelectedMenuItemChanged?.Invoke (this, selected); } /// /// Called when the selected menu item has changed. /// /// protected virtual void OnSelectedMenuItemChanged (MenuItemv2? selected) { } /// /// Raised when the selected menu item has changed. /// public event EventHandler? SelectedMenuItemChanged; }