using System; namespace Terminal.Gui { /// /// A context menu window derived from containing menu items /// which can be opened in any position. /// public sealed class ContextMenu : IDisposable { private static MenuBar menuBar; private Key key = Key.F10 | Key.ShiftMask; private MouseFlags mouseFlags = MouseFlags.Button3Clicked; private Toplevel container; /// /// Initialize a context menu with empty menu items. /// public ContextMenu () : this (0, 0, new MenuBarItem ()) { } /// /// Initialize a context menu with menu items from a host . /// /// The host view. /// The menu items. public ContextMenu (View host, MenuBarItem menuItems) : this (host.Frame.X, host.Frame.Y, menuItems) { Host = host; } /// /// Initialize a context menu with menu items. /// /// The left position. /// The top position. /// The menu items. public ContextMenu (int x, int y, MenuBarItem menuItems) { if (IsShow) { Hide (); } MenuItens = menuItems; Position = new Point (x, y); } private void MenuBar_MenuAllClosed () { Dispose (); } /// public void Dispose () { if (IsShow) { menuBar.MenuAllClosed -= MenuBar_MenuAllClosed; menuBar.Dispose (); menuBar = null; IsShow = false; } if (container != null) { container.Closing -= Container_Closing; container.Resized -= Container_Resized; } } /// /// Open the menu items. /// public void Show () { if (menuBar != null) { Hide (); } container = Application.Current; container.Closing += Container_Closing; container.Resized += Container_Resized; var frame = container.Frame; var position = Position; if (Host != null) { Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y); var pos = new Point (x, y); pos.Y += Host.Frame.Height - 1; if (position != pos) { Position = position = pos; } } var rect = Menu.MakeFrame (position.X, position.Y, MenuItens.Children); if (rect.Right >= frame.Right) { if (frame.Right - rect.Width >= 0 || !ForceMinimumPosToZero) { position.X = frame.Right - rect.Width; } else if (ForceMinimumPosToZero) { position.X = 0; } } else if (ForceMinimumPosToZero && position.X < 0) { position.X = 0; } if (rect.Bottom >= frame.Bottom) { if (frame.Bottom - rect.Height - 1 >= 0 || !ForceMinimumPosToZero) { if (Host == null) { position.Y = frame.Bottom - rect.Height - 1; } else { Host.ViewToScreen (container.Frame.X, container.Frame.Y, out int x, out int y); var pos = new Point (x, y); position.Y = pos.Y - rect.Height - 1; } } else if (ForceMinimumPosToZero) { position.Y = 0; } } else if (ForceMinimumPosToZero && position.Y < 0) { position.Y = 0; } menuBar = new MenuBar (new [] { MenuItens }) { X = position.X, Y = position.Y, Width = 0, Height = 0, UseSubMenusSingleFrame = UseSubMenusSingleFrame }; menuBar.isContextMenuLoading = true; menuBar.MenuAllClosed += MenuBar_MenuAllClosed; IsShow = true; menuBar.OpenMenu (); } private void Container_Resized (Size obj) { if (IsShow) { Show (); } } private void Container_Closing (ToplevelClosingEventArgs obj) { Hide (); } /// /// Close the menu items. /// public void Hide () { menuBar.CleanUp (); Dispose (); } /// /// Event invoked when the is changed. /// public event Action KeyChanged; /// /// Event invoked when the is changed. /// public event Action MouseFlagsChanged; /// /// Gets or set the menu position. /// public Point Position { get; set; } /// /// Gets or sets the menu items for this context menu. /// public MenuBarItem MenuItens { get; set; } /// /// The used to activate the context menu by keyboard. /// public Key Key { get => key; set { var oldKey = key; key = value; KeyChanged?.Invoke (oldKey); } } /// /// The used to activate the context menu by mouse. /// public MouseFlags MouseFlags { get => mouseFlags; set { var oldFlags = mouseFlags; mouseFlags = value; MouseFlagsChanged?.Invoke (oldFlags); } } /// /// Gets information whether menu is showing or not. /// public static bool IsShow { get; private set; } /// /// The host which position will be used, /// otherwise if it's null the container will be used. /// public View Host { get; set; } /// /// Gets or sets whether forces the minimum position to zero /// if the left or right position are negative. /// public bool ForceMinimumPosToZero { get; set; } = true; /// /// Gets the that is hosting this context menu. /// public MenuBar MenuBar { get => menuBar; } /// /// Gets or sets if the sub-menus must be displayed in a single or multiple frames. /// public bool UseSubMenusSingleFrame { get; set; } } }