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; }
}
}