#nullable enable
using static System.Formats.Asn1.AsnWriter;
namespace Terminal.Gui;
///
/// An internal class used to represent a menu pop-up menu. Created and managed by and
/// .
///
internal sealed class Menu : View
{
private readonly MenuBarItem? _barItems;
private readonly MenuBar _host;
internal int _currentChild;
internal View? _previousSubFocused;
internal static Rectangle MakeFrame (int x, int y, MenuItem? []? items, Menu? parent = null)
{
if (items is null || items.Length == 0)
{
return Rectangle.Empty;
}
int minX = x;
int minY = y;
const int borderOffset = 2; // This 2 is for the space around
int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset;
int maxH = items.Length + borderOffset;
if (parent is { } && x + maxW > Driver.Cols)
{
minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
}
if (y + maxH > Driver.Rows)
{
minY = Math.Max (Driver.Rows - maxH, 0);
}
return new (minX, minY, maxW, maxH);
}
internal required MenuBar Host
{
get => _host;
init
{
ArgumentNullException.ThrowIfNull (value);
_host = value;
}
}
internal required MenuBarItem? BarItems
{
get => _barItems!;
init
{
ArgumentNullException.ThrowIfNull (value);
_barItems = value;
// Debugging aid so ToString() is helpful
Text = _barItems.Title;
}
}
internal Menu? Parent { get; init; }
public override void BeginInit ()
{
base.BeginInit ();
var frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent);
if (Frame.X != frame.X)
{
X = frame.X;
}
if (Frame.Y != frame.Y)
{
Y = frame.Y;
}
Width = frame.Width;
Height = frame.Height;
if (_barItems.Children is { })
{
foreach (MenuItem? menuItem in _barItems.Children)
{
if (menuItem is { })
{
menuItem._menuBar = Host;
if (menuItem.ShortcutKey != Key.Empty)
{
KeyBinding keyBinding = new ([Command.Select], KeyBindingScope.HotKey, menuItem);
// Remove an existent ShortcutKey
menuItem._menuBar.KeyBindings.Remove (menuItem.ShortcutKey!);
menuItem._menuBar.KeyBindings.Add (menuItem.ShortcutKey!, keyBinding);
}
}
}
}
if (_barItems is { IsTopLevel: true })
{
// This is a standalone MenuItem on a MenuBar
ColorScheme = _host.ColorScheme;
CanFocus = true;
}
else
{
_currentChild = -1;
for (var i = 0; i < _barItems.Children?.Length; i++)
{
if (_barItems.Children [i]?.IsEnabled () == true)
{
_currentChild = i;
break;
}
}
ColorScheme = _host.ColorScheme;
CanFocus = true;
WantMousePositionReports = _host.WantMousePositionReports;
}
BorderStyle = _host.MenusBorderStyle;
AddCommand (
Command.Right,
() =>
{
_host.NextMenu (
!_barItems.IsTopLevel
|| (_barItems.Children is { Length: > 0 }
&& _currentChild > -1
&& _currentChild < _barItems.Children.Length
&& _barItems.Children [_currentChild]!.IsFromSubMenu),
_barItems.Children is { Length: > 0 }
&& _currentChild > -1
&& _host.UseSubMenusSingleFrame
&& _barItems.SubMenu (
_barItems.Children [_currentChild]!
)
!= null!
);
return true;
}
);
AddKeyBindingsHotKey (_barItems);
}
public Menu ()
{
if (Application.Top is { })
{
Application.Top.DrawContentComplete += Current_DrawContentComplete;
Application.Top.SizeChanging += Current_TerminalResized;
}
Application.MouseEvent += Application_RootMouseEvent;
// Things this view knows how to do
AddCommand (Command.Up, () => MoveUp ());
AddCommand (Command.Down, () => MoveDown ());
AddCommand (
Command.Left,
() =>
{
_host!.PreviousMenu (true);
return true;
}
);
AddCommand (
Command.Cancel,
() =>
{
CloseAllMenus ();
return true;
}
);
AddCommand (
Command.Accept,
() =>
{
RunSelected ();
return true;
}
);
AddCommand (Command.Select, ctx => _host?.SelectItem ((ctx.KeyBinding?.Context as MenuItem)!));
AddCommand (Command.Toggle, ctx => ExpandCollapse ((ctx.KeyBinding?.Context as MenuItem)!));
AddCommand (Command.HotKey, ctx => _host?.SelectItem ((ctx.KeyBinding?.Context as MenuItem)!));
// Default key bindings for this view
KeyBindings.Add (Key.CursorUp, Command.Up);
KeyBindings.Add (Key.CursorDown, Command.Down);
KeyBindings.Add (Key.CursorLeft, Command.Left);
KeyBindings.Add (Key.CursorRight, Command.Right);
KeyBindings.Add (Key.Esc, Command.Cancel);
}
private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem)
{
if (menuBarItem is null || menuBarItem.Children is null)
{
return;
}
IEnumerable