//
// Menu.cs: application menus and submenus
//
// Authors:
// Miguel de Icaza (miguel@gnome.org)
//
// TODO:
// Add accelerator support, but should also support chords (Shortcut in MenuItem)
// Allow menus inside menus
using System;
using NStack;
using System.Linq;
using System.Collections.Generic;
namespace Terminal.Gui {
///
/// Specifies how a shows selection state.
///
[Flags]
public enum MenuItemCheckStyle {
///
/// The menu item will be shown normally, with no check indicator.
///
NoCheck = 0b_0000_0000,
///
/// The menu item will indicate checked/un-checked state (see .
///
Checked = 0b_0000_0001,
///
/// The menu item is part of a menu radio group (see and will indicate selected state.
///
Radio = 0b_0000_0010,
};
///
/// A has a title, an associated help text, and an action to execute on activation.
///
public class MenuItem {
ustring title;
ShortcutHelper shortcutHelper;
///
/// Initializes a new instance of
///
public MenuItem (Key shortcut = Key.Null)
{
Title = "";
Help = "";
shortcutHelper = new ShortcutHelper ();
if (shortcut != Key.Null) {
shortcutHelper.Shortcut = shortcut;
}
}
///
/// Initializes a new instance of .
///
/// Title for the menu item.
/// Help text to display.
/// Action to invoke when the menu item is activated.
/// Function to determine if the action can currently be executed.
/// The of this menu item.
/// The keystroke combination.
public MenuItem (ustring title, ustring help, Action action, Func canExecute = null, MenuItem parent = null, Key shortcut = Key.Null)
{
Title = title ?? "";
Help = help ?? "";
Action = action;
CanExecute = canExecute;
Parent = parent;
shortcutHelper = new ShortcutHelper ();
if (shortcut != Key.Null) {
shortcutHelper.Shortcut = shortcut;
}
}
///
/// The HotKey is used when the menu is active, the shortcut can be triggered when the menu is not active.
/// For example HotKey would be "N" when the File Menu is open (assuming there is a "_New" entry
/// if the Shortcut is set to "Control-N", this would be a global hotkey that would trigger as well
///
public Rune HotKey;
///
/// This is the global setting that can be used as a global to invoke the action on the menu.
///
public Key Shortcut {
get => shortcutHelper.Shortcut;
set {
if (shortcutHelper.Shortcut != value && (ShortcutHelper.PostShortcutValidation (value) || value == Key.Null)) {
shortcutHelper.Shortcut = value;
}
}
}
///
/// The keystroke combination used in the as string.
///
public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
///
/// Gets or sets the title.
///
/// The title.
public ustring Title {
get { return title; }
set {
if (title != value) {
title = value;
GetHotKey ();
}
}
}
///
/// Gets or sets the help text for the menu item.
///
/// The help text.
public ustring Help { get; set; }
///
/// Gets or sets the action to be invoked when the menu is triggered
///
/// Method to invoke.
public Action Action { get; set; }
///
/// Gets or sets the action to be invoked if the menu can be triggered
///
/// Function to determine if action is ready to be executed.
public Func CanExecute { get; set; }
///
/// Shortcut to check if the menu item is enabled
///
public bool IsEnabled ()
{
return CanExecute == null ? true : CanExecute ();
}
internal int Width => Title.RuneCount + Help.RuneCount + 1 + 2 +
(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) +
(ShortcutTag.RuneCount > 0 ? ShortcutTag.RuneCount + 2 : 0);
///
/// Sets or gets whether the shows a check indicator or not. See .
///
public bool Checked { set; get; }
///
/// Sets or gets the type selection indicator the menu item will be displayed with.
///
public MenuItemCheckStyle CheckType { get; set; }
///
/// Gets or sets the parent for this .
///
/// The parent.
public MenuItem Parent { get; internal set; }
///
/// Gets if this is from a sub-menu.
///
internal bool IsFromSubMenu { get {return Parent != null; } }
///
/// Merely a debugging aid to see the interaction with main
///
public MenuItem GetMenuItem ()
{
return this;
}
///
/// Merely a debugging aid to see the interaction with main
///
public bool GetMenuBarItem ()
{
return IsFromSubMenu;
}
void GetHotKey ()
{
bool nextIsHot = false;
foreach (var x in title) {
if (x == '_') {
nextIsHot = true;
} else {
if (nextIsHot) {
HotKey = Char.ToUpper ((char)x);
break;
}
nextIsHot = false;
HotKey = default;
}
}
}
}
///
/// A contains s or s.
///
public class MenuBarItem : MenuItem {
///
/// Initializes a new as a .
///
/// Title for the menu item.
/// Help text to display.
/// Action to invoke when the menu item is activated.
/// Function to determine if the action can currently be executed.
/// The parent of this if exist, otherwise is null.
public MenuBarItem (ustring title, ustring help, Action action, Func canExecute = null, MenuItem parent = null) : base (title, help, action, canExecute, parent)
{
SetTitle (title ?? "");
Children = null;
}
///
/// Initializes a new .
///
/// Title for the menu item.
/// The items in the current menu.
/// The parent of this if exist, otherwise is null.
public MenuBarItem (ustring title, MenuItem [] children, MenuItem parent = null)
{
if (children == null) {
throw new ArgumentNullException (nameof (children), "The parameter cannot be null. Use an empty array instead.");
}
SetTitle (title ?? "");
if (parent != null) {
Parent = parent;
}
SetChildrensParent (children);
Children = children;
}
///
/// Initializes a new with separate list of items.
///
/// Title for the menu item.
/// The list of items in the current menu.
/// The parent of this if exist, otherwise is null.
public MenuBarItem (ustring title, List