mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-30 01:38:01 +01:00
Merge pull request #2117 from tig/fix_2109_menubar_spacing
Fixes #2109. MenuBar has extra space on left. Refactors MenuTests. Better Menu API docs.
This commit is contained in:
@@ -2,8 +2,24 @@
|
||||
|
||||
namespace Terminal.Gui {
|
||||
/// <summary>
|
||||
/// A context menu window derived from <see cref="MenuBar"/> containing menu items
|
||||
/// which can be opened in any position.
|
||||
/// ContextMenu provides a pop-up menu that can be positioned anywhere within a <see cref="View"/>.
|
||||
/// ContextMenu is analogous to <see cref="MenuBar"/> and, once activated, works like a sub-menu
|
||||
/// of a <see cref="MenuBarItem"/> (but can be positioned anywhere).
|
||||
/// <para>
|
||||
/// By default, a ContextMenu with sub-menus is displayed in a cascading manner, where each sub-menu pops out of the ContextMenu frame
|
||||
/// (either to the right or left, depending on where the ContextMenu is relative to the edge of the screen). By setting
|
||||
/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-menus are
|
||||
/// drawn within the ContextMenu frame.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// ContextMenus can be activated using the Shift-F10 key (by default; use the <see cref="Key"/> to change to another key).
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Callers can cause the ContextMenu to be activated on a right-mouse click (or other interaction) by calling <see cref="Show()"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// ContextMenus are located using screen using screen coordinates and appear above all other Views.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public sealed class ContextMenu : IDisposable {
|
||||
private static MenuBar menuBar;
|
||||
@@ -12,15 +28,15 @@ namespace Terminal.Gui {
|
||||
private Toplevel container;
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a context menu with empty menu items.
|
||||
/// Initializes a context menu with no menu items.
|
||||
/// </summary>
|
||||
public ContextMenu () : this (0, 0, new MenuBarItem ()) { }
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a context menu with menu items from a host <see cref="View"/>.
|
||||
/// Initializes a context menu, with a <see cref="View"/> specifiying the parent/hose of the menu.
|
||||
/// </summary>
|
||||
/// <param name="host">The host view.</param>
|
||||
/// <param name="menuItems">The menu items.</param>
|
||||
/// <param name="menuItems">The menu items for the context menu.</param>
|
||||
public ContextMenu (View host, MenuBarItem menuItems) :
|
||||
this (host.Frame.X, host.Frame.Y, menuItems)
|
||||
{
|
||||
@@ -28,10 +44,10 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initialize a context menu with menu items.
|
||||
/// Initializes a context menu with menu items at a specific screen location.
|
||||
/// </summary>
|
||||
/// <param name="x">The left position.</param>
|
||||
/// <param name="y">The top position.</param>
|
||||
/// <param name="x">The left position (screen relative).</param>
|
||||
/// <param name="y">The top position (screen relative).</param>
|
||||
/// <param name="menuItems">The menu items.</param>
|
||||
public ContextMenu (int x, int y, MenuBarItem menuItems)
|
||||
{
|
||||
@@ -48,7 +64,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disposes the all the context menu objects instances.
|
||||
/// Disposes the context menu object.
|
||||
/// </summary>
|
||||
public void Dispose ()
|
||||
{
|
||||
@@ -65,7 +81,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Open the <see cref="MenuItems"/> menu items.
|
||||
/// Shows (opens) the ContextMenu, displaying the <see cref="MenuItem"/>s it contains.
|
||||
/// </summary>
|
||||
public void Show ()
|
||||
{
|
||||
@@ -110,7 +126,7 @@ namespace Terminal.Gui {
|
||||
} else if (ForceMinimumPosToZero && position.Y < 0) {
|
||||
position.Y = 0;
|
||||
}
|
||||
|
||||
|
||||
menuBar = new MenuBar (new [] { MenuItems }) {
|
||||
X = position.X,
|
||||
Y = position.Y,
|
||||
@@ -139,7 +155,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Close the <see cref="MenuItems"/> menu items.
|
||||
/// Hides (closes) the ContextMenu.
|
||||
/// </summary>
|
||||
public void Hide ()
|
||||
{
|
||||
@@ -158,7 +174,7 @@ namespace Terminal.Gui {
|
||||
public event Action<MouseFlags> MouseFlagsChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or set the menu position.
|
||||
/// Gets or sets the menu position.
|
||||
/// </summary>
|
||||
public Point Position { get; set; }
|
||||
|
||||
@@ -168,7 +184,7 @@ namespace Terminal.Gui {
|
||||
public MenuBarItem MenuItems { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Gui.Key"/> used to activate the context menu by keyboard.
|
||||
/// <see cref="Gui.Key"/> specifies they keyboard key that will activate the context menu with the keyboard.
|
||||
/// </summary>
|
||||
public Key Key {
|
||||
get => key;
|
||||
@@ -180,7 +196,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Gui.MouseFlags"/> used to activate the context menu by mouse.
|
||||
/// <see cref="Gui.MouseFlags"/> specifies the mouse action used to activate the context menu by mouse.
|
||||
/// </summary>
|
||||
public MouseFlags MouseFlags {
|
||||
get => mouseFlags;
|
||||
@@ -192,7 +208,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets information whether menu is showing or not.
|
||||
/// Gets whether the ContextMenu is showing or not.
|
||||
/// </summary>
|
||||
public static bool IsShow { get; private set; }
|
||||
|
||||
@@ -203,8 +219,9 @@ namespace Terminal.Gui {
|
||||
public View Host { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether forces the minimum position to zero
|
||||
/// if the left or right position are negative.
|
||||
/// Sets or gets whether the context menu be forced to the right, ensuring it is not clipped, if the x position
|
||||
/// is less than zero. The default is <see langword="true"/> which means the context menu will be forced to the right.
|
||||
/// If set to <see langword="false"/>, the context menu will be clipped on the left if x is less than zero.
|
||||
/// </summary>
|
||||
public bool ForceMinimumPosToZero { get; set; } = true;
|
||||
|
||||
@@ -214,7 +231,9 @@ namespace Terminal.Gui {
|
||||
public MenuBar MenuBar { get => menuBar; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
|
||||
/// Gets or sets if sub-menus will be displayed using a "single frame" menu style. If <see langword="true"/>, the ContextMenu
|
||||
/// and any sub-menus that would normally cascade will be displayed within a single frame. If <see langword="false"/> (the default),
|
||||
/// sub-menus will cascade using separate frames for each level of the menu hierarchy.
|
||||
/// </summary>
|
||||
public bool UseSubMenusSingleFrame { get; set; }
|
||||
}
|
||||
@@ -1,13 +1,3 @@
|
||||
//
|
||||
// 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;
|
||||
@@ -21,23 +11,24 @@ namespace Terminal.Gui {
|
||||
[Flags]
|
||||
public enum MenuItemCheckStyle {
|
||||
/// <summary>
|
||||
/// The menu item will be shown normally, with no check indicator.
|
||||
/// The menu item will be shown normally, with no check indicator. The default.
|
||||
/// </summary>
|
||||
NoCheck = 0b_0000_0000,
|
||||
|
||||
/// <summary>
|
||||
/// The menu item will indicate checked/un-checked state (see <see cref="Checked"/>.
|
||||
/// The menu item will indicate checked/un-checked state (see <see cref="Checked"/>).
|
||||
/// </summary>
|
||||
Checked = 0b_0000_0001,
|
||||
|
||||
/// <summary>
|
||||
/// The menu item is part of a menu radio group (see <see cref="Checked"/> and will indicate selected state.
|
||||
/// The menu item is part of a menu radio group (see <see cref="Checked"/>) and will indicate selected state.
|
||||
/// </summary>
|
||||
Radio = 0b_0000_0010,
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="MenuItem"/> has a title, an associated help text, and an action to execute on activation.
|
||||
/// A <see cref="MenuItem"/> has title, an associated help text, and an action to execute on activation.
|
||||
/// MenuItems can also have a checked indicator (see <see cref="Checked"/>).
|
||||
/// </summary>
|
||||
public class MenuItem {
|
||||
ustring title;
|
||||
@@ -78,14 +69,28 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// The HotKey is used to activate a <see cref="MenuItem"/> with the keyboard. HotKeys are defined by prefixing the <see cref="Title"/>
|
||||
/// of a MenuItem with an underscore ('_').
|
||||
/// <para>
|
||||
/// Pressing Alt-Hotkey for a <see cref="MenuBarItem"/> (menu items on the menu bar) works even if the menu is not active).
|
||||
/// Once a menu has focus and is active, pressing just the HotKey will activate the MenuItem.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// For example for a MenuBar with a "_File" MenuBarItem that contains a "_New" MenuItem, Alt-F will open the File menu.
|
||||
/// Pressing the N key will then activate the New MenuItem.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See also <see cref="Shortcut"/> which enable global key-bindings to menu items.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public Rune HotKey;
|
||||
|
||||
/// <summary>
|
||||
/// This is the global setting that can be used as a global <see cref="ShortcutHelper.Shortcut"/> to invoke the action on the menu.
|
||||
/// Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the <see cref="View"/> that is
|
||||
/// the parent of the <see cref="MenuBar"/> or <see cref="ContextMenu"/> this <see cref="MenuItem"/>.
|
||||
/// <para>
|
||||
/// The <see cref="Key"/> will be drawn on the MenuItem to the right of the <see cref="Title"/> and <see cref="Help"/> text. See <see cref="ShortcutTag"/>.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public Key Shortcut {
|
||||
get => shortcutHelper.Shortcut;
|
||||
@@ -97,12 +102,12 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The keystroke combination used in the <see cref="ShortcutHelper.ShortcutTag"/> as string.
|
||||
/// Gets the text describing the keystroke combination defined by <see cref="Shortcut"/>.
|
||||
/// </summary>
|
||||
public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the title.
|
||||
/// Gets or sets the title of the menu item .
|
||||
/// </summary>
|
||||
/// <value>The title.</value>
|
||||
public ustring Title {
|
||||
@@ -116,34 +121,46 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the help text for the menu item.
|
||||
/// Gets or sets the help text for the menu item. The help text is drawn to the right of the <see cref="Title"/>.
|
||||
/// </summary>
|
||||
/// <value>The help text.</value>
|
||||
public ustring Help { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action to be invoked when the menu is triggered
|
||||
/// Gets or sets the action to be invoked when the menu item is triggered.
|
||||
/// </summary>
|
||||
/// <value>Method to invoke.</value>
|
||||
public Action Action { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the action to be invoked if the menu can be triggered
|
||||
/// Gets or sets the action to be invoked to determine if the menu can be triggered. If <see cref="CanExecute"/> returns <see langword="true"/>
|
||||
/// the menu item will be enabled. Otherwise, it will be disabled.
|
||||
/// </summary>
|
||||
/// <value>Function to determine if action is ready to be executed.</value>
|
||||
/// <value>Function to determine if the action is can be executed or not.</value>
|
||||
public Func<bool> CanExecute { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Shortcut to check if the menu item is enabled
|
||||
/// Returns <see langword="true"/> if the menu item is enabled. This method is a wrapper around <see cref="CanExecute"/>.
|
||||
/// </summary>
|
||||
public bool IsEnabled ()
|
||||
{
|
||||
return CanExecute == null ? true : CanExecute ();
|
||||
}
|
||||
|
||||
internal int Width => 1 + TitleLength + (Help.ConsoleWidth > 0 ? Help.ConsoleWidth + 2 : 0) +
|
||||
(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) +
|
||||
(ShortcutTag.ConsoleWidth > 0 ? ShortcutTag.ConsoleWidth + 2 : 0) + 2;
|
||||
//
|
||||
// ┌─────────────────────────────┐
|
||||
// │ Quit Quit UI Catalog Ctrl+Q │
|
||||
// └─────────────────────────────┘
|
||||
// ┌─────────────────┐
|
||||
// │ ◌ TopLevel Alt+T │
|
||||
// └─────────────────┘
|
||||
// TODO: Replace the `2` literals with named constants
|
||||
internal int Width => 1 + // space before Title
|
||||
TitleLength +
|
||||
2 + // space after Title - BUGBUG: This should be 1
|
||||
(Checked || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) + // check glyph + space
|
||||
(Help.ConsoleWidth > 0 ? 2 + Help.ConsoleWidth : 0) + // Two spaces before Help
|
||||
(ShortcutTag.ConsoleWidth > 0 ? 2 + ShortcutTag.ConsoleWidth : 0); // Pad two spaces before shortcut tag (which are also aligned right)
|
||||
|
||||
/// <summary>
|
||||
/// Sets or gets whether the <see cref="MenuItem"/> shows a check indicator or not. See <see cref="MenuItemCheckStyle"/>.
|
||||
@@ -151,12 +168,12 @@ namespace Terminal.Gui {
|
||||
public bool Checked { set; get; }
|
||||
|
||||
/// <summary>
|
||||
/// Sets or gets the type selection indicator the menu item will be displayed with.
|
||||
/// Sets or gets the <see cref="MenuItemCheckStyle"/> of a menu item where <see cref="Checked"/> is set to <see langword="true"/>.
|
||||
/// </summary>
|
||||
public MenuItemCheckStyle CheckType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the parent for this <see cref="MenuItem"/>.
|
||||
/// Gets the parent for this <see cref="MenuItem"/>.
|
||||
/// </summary>
|
||||
/// <value>The parent.</value>
|
||||
public MenuItem Parent { get; internal set; }
|
||||
@@ -167,7 +184,7 @@ namespace Terminal.Gui {
|
||||
internal bool IsFromSubMenu { get { return Parent != null; } }
|
||||
|
||||
/// <summary>
|
||||
/// Merely a debugging aid to see the interaction with main
|
||||
/// Merely a debugging aid to see the interaction with main.
|
||||
/// </summary>
|
||||
public MenuItem GetMenuItem ()
|
||||
{
|
||||
@@ -175,7 +192,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Merely a debugging aid to see the interaction with main
|
||||
/// Merely a debugging aid to see the interaction with main.
|
||||
/// </summary>
|
||||
public bool GetMenuBarItem ()
|
||||
{
|
||||
@@ -213,14 +230,15 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="MenuBarItem"/> contains <see cref="MenuBarItem"/>s or <see cref="MenuItem"/>s.
|
||||
/// <see cref="MenuBarItem"/> is a menu item on an app's <see cref="MenuBar"/>.
|
||||
/// MenuBarItems do not support <see cref="MenuItem.Shortcut"/>.
|
||||
/// </summary>
|
||||
public class MenuBarItem : MenuItem {
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="MenuBarItem"/> as a <see cref="MenuItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="title">Title for the menu item.</param>
|
||||
/// <param name="help">Help text to display.</param>
|
||||
/// <param name="help">Help text to display. Will be displayed next to the Title surrounded by parentheses.</param>
|
||||
/// <param name="action">Action to invoke when the menu item is activated.</param>
|
||||
/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
|
||||
/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
|
||||
@@ -289,19 +307,6 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
//static int GetMaxTitleLength (MenuItem [] children)
|
||||
//{
|
||||
// int maxLength = 0;
|
||||
// foreach (var item in children) {
|
||||
// int len = GetMenuBarItemLength (item.Title);
|
||||
// if (len > maxLength)
|
||||
// maxLength = len;
|
||||
// item.IsFromSubMenu = true;
|
||||
// }
|
||||
|
||||
// return maxLength;
|
||||
//}
|
||||
|
||||
void SetChildrensParent (MenuItem [] childrens)
|
||||
{
|
||||
foreach (var child in childrens) {
|
||||
@@ -363,12 +368,6 @@ namespace Terminal.Gui {
|
||||
Title = title;
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// Gets or sets the title to display.
|
||||
///// </summary>
|
||||
///// <value>The title.</value>
|
||||
//public ustring Title { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets an array of <see cref="MenuItem"/> objects that are the children of this <see cref="MenuBarItem"/>
|
||||
/// </summary>
|
||||
@@ -391,8 +390,8 @@ namespace Terminal.Gui {
|
||||
}
|
||||
int minX = x;
|
||||
int minY = y;
|
||||
int maxW = (items.Max (z => z?.Width) ?? 0) + 2;
|
||||
int maxH = items.Length + 2;
|
||||
int maxW = (items.Max (z => z?.Width) ?? 0) + 2; // This 2 is frame border?
|
||||
int maxH = items.Length + 2; // This 2 is frame border?
|
||||
if (parent != null && x + maxW > Driver.Cols) {
|
||||
minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0);
|
||||
}
|
||||
@@ -459,6 +458,7 @@ namespace Terminal.Gui {
|
||||
return GetNormalColor ();
|
||||
}
|
||||
|
||||
// Draws the Menu, within the Frame
|
||||
public override void Redraw (Rect bounds)
|
||||
{
|
||||
Driver.SetAttribute (GetNormalColor ());
|
||||
@@ -477,13 +477,14 @@ namespace Terminal.Gui {
|
||||
Move (1, i + 1);
|
||||
|
||||
Driver.SetAttribute (DetermineColorSchemeFor (item, i));
|
||||
for (int p = Bounds.X; p < Frame.Width - 2; p++) {
|
||||
for (int p = Bounds.X; p < Frame.Width - 2; p++) { // This - 2 is for the border
|
||||
if (p < 0)
|
||||
continue;
|
||||
if (item == null)
|
||||
Driver.AddRune (Driver.HLine);
|
||||
else if (i == 0 && p == 0 && host.UseSubMenusSingleFrame && item.Parent.Parent != null)
|
||||
Driver.AddRune (Driver.LeftArrow);
|
||||
// This `- 3` is left border + right border + one row in from right
|
||||
else if (p == Frame.Width - 3 && barItems.SubMenu (barItems.Children [i]) != null)
|
||||
Driver.AddRune (Driver.RightArrow);
|
||||
else
|
||||
@@ -527,6 +528,7 @@ namespace Terminal.Gui {
|
||||
HotKeySpecifier = MenuBar.HotKeySpecifier,
|
||||
Text = textToDraw
|
||||
};
|
||||
// The -3 is left/right border + one space (not sure what for)
|
||||
tf.Draw (ViewToScreen (new Rect (2, i + 1, Frame.Width - 3, 1)),
|
||||
i == current ? ColorScheme.Focus : GetNormalColor (),
|
||||
i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
|
||||
@@ -832,17 +834,27 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Provides a menu bar with drop-down and cascading menus.
|
||||
/// <para>
|
||||
/// Provides a menu bar that spans the top of a <see cref="Toplevel"/> View with drop-down and cascading menus.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// By default, any sub-sub-menus (sub-menus of the <see cref="MenuItem"/>s added to <see cref="MenuBarItem"/>s)
|
||||
/// are displayed in a cascading manner, where each sub-sub-menu pops out of the sub-menu frame
|
||||
/// (either to the right or left, depending on where the sub-menu is relative to the edge of the screen). By setting
|
||||
/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-sub-menus are
|
||||
/// drawn within a single frame below the MenuBar.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The <see cref="MenuBar"/> appears on the first row of the terminal.
|
||||
/// The <see cref="MenuBar"/> appears on the first row of the parent <see cref="Toplevel"/> View and uses the full width.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The <see cref="MenuBar"/> provides global hotkeys for the application.
|
||||
/// The <see cref="MenuBar"/> provides global hotkeys for the application. See <see cref="MenuItem.HotKey"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// See also: <see cref="ContextMenu"/>
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class MenuBar : View {
|
||||
@@ -850,7 +862,7 @@ namespace Terminal.Gui {
|
||||
internal int selectedSub;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the array of <see cref="MenuBarItem"/>s for the menu. Only set this when the <see cref="MenuBar"/> is visible.
|
||||
/// Gets or sets the array of <see cref="MenuBarItem"/>s for the menu. Only set this after the <see cref="MenuBar"/> is visible.
|
||||
/// </summary>
|
||||
/// <value>The menu array.</value>
|
||||
public MenuBarItem [] Menus { get; set; }
|
||||
@@ -873,7 +885,7 @@ namespace Terminal.Gui {
|
||||
|
||||
static ustring shortcutDelimiter = "+";
|
||||
/// <summary>
|
||||
/// Used for change the shortcut delimiter separator.
|
||||
/// Sets or gets the shortcut delimiter separator. The default is "+".
|
||||
/// </summary>
|
||||
public static ustring ShortcutDelimiter {
|
||||
get => shortcutDelimiter;
|
||||
@@ -893,6 +905,13 @@ namespace Terminal.Gui {
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets if the sub-menus must be displayed in a single or multiple frames.
|
||||
/// <para>
|
||||
/// By default any sub-sub-menus (sub-menus of the main <see cref="MenuItem"/>s) are displayed in a cascading manner,
|
||||
/// where each sub-sub-menu pops out of the sub-menu frame
|
||||
/// (either to the right or left, depending on where the sub-menu is relative to the edge of the screen). By setting
|
||||
/// <see cref="UseSubMenusSingleFrame"/> to <see langword="true"/>, this behavior can be changed such that all sub-sub-menus are
|
||||
/// drawn within a single frame below the MenuBar.
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public bool UseSubMenusSingleFrame {
|
||||
get => useSubMenusSingleFrame;
|
||||
@@ -1029,6 +1048,14 @@ namespace Terminal.Gui {
|
||||
isCleaning = false;
|
||||
}
|
||||
|
||||
// The column where the MenuBar starts
|
||||
static int xOrigin = 0;
|
||||
// Spaces before the Title
|
||||
static int leftPadding = 1;
|
||||
// Spaces after the Title
|
||||
static int rightPadding = 1;
|
||||
// Spaces after the submenu Title, before Help
|
||||
static int parensAroundHelp = 3;
|
||||
///<inheritdoc/>
|
||||
public override void Redraw (Rect bounds)
|
||||
{
|
||||
@@ -1038,7 +1065,7 @@ namespace Terminal.Gui {
|
||||
Driver.AddRune (' ');
|
||||
|
||||
Move (1, 0);
|
||||
int pos = 1;
|
||||
int pos = 0;
|
||||
|
||||
for (int i = 0; i < Menus.Length; i++) {
|
||||
var menu = Menus [i];
|
||||
@@ -1051,8 +1078,9 @@ namespace Terminal.Gui {
|
||||
hotColor = ColorScheme.HotNormal;
|
||||
normalColor = GetNormalColor ();
|
||||
}
|
||||
DrawHotString (menu.Help.IsEmpty ? $" {menu.Title} " : $" {menu.Title} {menu.Help} ", hotColor, normalColor);
|
||||
pos += 1 + menu.TitleLength + (menu.Help.ConsoleWidth > 0 ? menu.Help.ConsoleWidth + 2 : 0) + 2;
|
||||
// Note Help on MenuBar is drawn with parens around it
|
||||
DrawHotString (menu.Help.IsEmpty ? $" {menu.Title} " : $" {menu.Title} ({menu.Help}) ", hotColor, normalColor);
|
||||
pos += leftPadding + menu.TitleLength + (menu.Help.ConsoleWidth > 0 ? leftPadding + menu.Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
|
||||
}
|
||||
PositionCursor ();
|
||||
}
|
||||
@@ -1067,14 +1095,10 @@ namespace Terminal.Gui {
|
||||
for (int i = 0; i < Menus.Length; i++) {
|
||||
if (i == selected) {
|
||||
pos++;
|
||||
if (IsMenuOpen)
|
||||
Move (pos + 1, 0);
|
||||
else {
|
||||
Move (pos + 1, 0);
|
||||
}
|
||||
Move (pos + 1, 0);
|
||||
return;
|
||||
} else {
|
||||
pos += 1 + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + 2;
|
||||
pos += leftPadding + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1112,7 +1136,7 @@ namespace Terminal.Gui {
|
||||
public event Action<MenuClosingEventArgs> MenuClosing;
|
||||
|
||||
/// <summary>
|
||||
/// Raised when all the menu are closed.
|
||||
/// Raised when all the menu is closed.
|
||||
/// </summary>
|
||||
public event Action MenuAllClosed;
|
||||
|
||||
@@ -1135,7 +1159,7 @@ namespace Terminal.Gui {
|
||||
internal bool isMenuClosing;
|
||||
|
||||
/// <summary>
|
||||
/// True if the menu is open; otherwise false.
|
||||
/// <see langword="true"/> if the menu is open; otherwise <see langword="true"/>.
|
||||
/// </summary>
|
||||
public bool IsMenuOpen { get; protected set; }
|
||||
|
||||
@@ -1168,7 +1192,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Virtual method that will invoke the <see cref="MenuClosing"/>
|
||||
/// Virtual method that will invoke the <see cref="MenuClosing"/>.
|
||||
/// </summary>
|
||||
/// <param name="currentMenu">The current menu to be closed.</param>
|
||||
/// <param name="reopen">Whether the current menu will be reopen.</param>
|
||||
@@ -1181,7 +1205,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Virtual method that will invoke the <see cref="MenuAllClosed"/>
|
||||
/// Virtual method that will invoke the <see cref="MenuAllClosed"/>.
|
||||
/// </summary>
|
||||
public virtual void OnMenuAllClosed ()
|
||||
{
|
||||
@@ -1191,7 +1215,7 @@ namespace Terminal.Gui {
|
||||
View lastFocused;
|
||||
|
||||
/// <summary>
|
||||
/// Get the lasted focused view before open the menu.
|
||||
/// Gets the view that was last focused before opening the menu.
|
||||
/// </summary>
|
||||
public View LastFocused { get; private set; }
|
||||
|
||||
@@ -1209,6 +1233,7 @@ namespace Terminal.Gui {
|
||||
int pos = 0;
|
||||
switch (subMenu) {
|
||||
case null:
|
||||
// Open a submenu below a MenuBar
|
||||
lastFocused = lastFocused ?? (SuperView == null ? Application.Current.MostFocused : SuperView.MostFocused);
|
||||
if (openSubMenu != null && !CloseMenu (false, true))
|
||||
return;
|
||||
@@ -1221,8 +1246,10 @@ namespace Terminal.Gui {
|
||||
openMenu.Dispose ();
|
||||
}
|
||||
|
||||
// This positions the submenu horizontally aligned with the first character of the
|
||||
// menu it belongs to's text
|
||||
for (int i = 0; i < index; i++)
|
||||
pos += 1 + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + 2;
|
||||
pos += Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + leftPadding + rightPadding;
|
||||
openMenu = new Menu (this, Frame.X + pos, Frame.Y + 1, Menus [index]);
|
||||
openCurrentMenu = openMenu;
|
||||
openCurrentMenu.previousSubFocused = openMenu;
|
||||
@@ -1235,6 +1262,7 @@ namespace Terminal.Gui {
|
||||
openMenu.SetFocus ();
|
||||
break;
|
||||
default:
|
||||
// Opens a submenu next to another submenu (openSubMenu)
|
||||
if (openSubMenu == null)
|
||||
openSubMenu = new List<Menu> ();
|
||||
if (sIndex > -1) {
|
||||
@@ -1275,7 +1303,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the current Menu programatically.
|
||||
/// Opens the Menu programatically, as though the F9 key were pressed.
|
||||
/// </summary>
|
||||
public void OpenMenu ()
|
||||
{
|
||||
@@ -1357,7 +1385,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Closes the current Menu programatically, if open and not canceled.
|
||||
/// Closes the Menu programmatically if open and not canceled (as though F9 were pressed).
|
||||
/// </summary>
|
||||
public bool CloseMenu (bool ignoreUseSubMenusSingleFrame = false)
|
||||
{
|
||||
@@ -1459,26 +1487,6 @@ namespace Terminal.Gui {
|
||||
if (openSubMenu.Count > 0)
|
||||
openCurrentMenu = openSubMenu.Last ();
|
||||
|
||||
//if (openMenu.Subviews.Count == 0)
|
||||
// return;
|
||||
//if (index == 0) {
|
||||
// //SuperView.SetFocus (previousSubFocused);
|
||||
// FocusPrev ();
|
||||
// return;
|
||||
//}
|
||||
|
||||
//for (int i = openMenu.Subviews.Count - 1; i > index; i--) {
|
||||
// isMenuClosing = true;
|
||||
// if (openMenu.Subviews.Count - 1 > 0)
|
||||
// SuperView.SetFocus (openMenu.Subviews [i - 1]);
|
||||
// else
|
||||
// SuperView.SetFocus (openMenu);
|
||||
// if (openMenu != null) {
|
||||
// Remove (openMenu.Subviews [i]);
|
||||
// openMenu.Remove (openMenu.Subviews [i]);
|
||||
// }
|
||||
// RemoveSubMenu (i);
|
||||
//}
|
||||
isMenuClosing = false;
|
||||
}
|
||||
|
||||
@@ -1774,10 +1782,10 @@ namespace Terminal.Gui {
|
||||
if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1TripleClicked || me.Flags == MouseFlags.Button1Clicked ||
|
||||
(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
|
||||
(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
|
||||
int pos = 1;
|
||||
int pos = xOrigin;
|
||||
int cx = me.X;
|
||||
for (int i = 0; i < Menus.Length; i++) {
|
||||
if (cx >= pos && cx < pos + 1 + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + 2) {
|
||||
if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + rightPadding) {
|
||||
if (me.Flags == MouseFlags.Button1Clicked) {
|
||||
if (Menus [i].IsTopLevel) {
|
||||
var menu = new Menu (this, i, 0, Menus [i]);
|
||||
@@ -1806,7 +1814,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
pos += 1 + Menus [i].TitleLength + 2;
|
||||
pos += leftPadding + Menus [i].TitleLength + rightPadding;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
@@ -1879,47 +1887,6 @@ namespace Terminal.Gui {
|
||||
handled = false;
|
||||
return false;
|
||||
}
|
||||
//if (me.View != this && me.Flags != MouseFlags.Button1Pressed)
|
||||
// return true;
|
||||
//else if (me.View != this && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
|
||||
// Application.UngrabMouse ();
|
||||
// host.CloseAllMenus ();
|
||||
// return true;
|
||||
//}
|
||||
|
||||
|
||||
//if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Pressed))
|
||||
// return false;
|
||||
|
||||
//if (Application.MouseGrabView != null) {
|
||||
// if (me.View is MenuBar || me.View is Menu) {
|
||||
// me.X -= me.OfX;
|
||||
// me.Y -= me.OfY;
|
||||
// me.View.MouseEvent (me);
|
||||
// return true;
|
||||
// } else if (!(me.View is MenuBar || me.View is Menu) && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
|
||||
// Application.UngrabMouse ();
|
||||
// CloseAllMenus ();
|
||||
// }
|
||||
//} else if (!isMenuClosed && selected == -1 && me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
|
||||
// Application.GrabMouse (this);
|
||||
// return true;
|
||||
//}
|
||||
|
||||
//if (Application.MouseGrabView != null) {
|
||||
// if (Application.MouseGrabView == me.View && me.View == current) {
|
||||
// me.X -= me.OfX;
|
||||
// me.Y -= me.OfY;
|
||||
// } else if (me.View != current && me.View is MenuBar && me.View is Menu) {
|
||||
// Application.UngrabMouse ();
|
||||
// Application.GrabMouse (me.View);
|
||||
// } else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
|
||||
// Application.UngrabMouse ();
|
||||
// CloseMenu ();
|
||||
// }
|
||||
//} else if ((!isMenuClosed && selected > -1)) {
|
||||
// Application.GrabMouse (current);
|
||||
//}
|
||||
|
||||
handled = true;
|
||||
|
||||
@@ -1973,12 +1940,13 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
public MenuBarItem NewMenuBarItem { get; set; }
|
||||
/// <summary>
|
||||
/// Flag that allows you to cancel the opening of the menu.
|
||||
/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
|
||||
/// event handler, the event will be canceled.
|
||||
/// </summary>
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>
|
||||
/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
|
||||
public MenuOpeningEventArgs (MenuBarItem currentMenu)
|
||||
@@ -1997,7 +1965,7 @@ namespace Terminal.Gui {
|
||||
public MenuBarItem CurrentMenu { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the current menu will be reopen.
|
||||
/// Indicates whether the current menu will reopen.
|
||||
/// </summary>
|
||||
public bool Reopen { get; }
|
||||
|
||||
@@ -2007,15 +1975,16 @@ namespace Terminal.Gui {
|
||||
public bool IsSubMenu { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Flag that allows you to cancel the opening of the menu.
|
||||
/// Flag that allows the cancellation of the event. If set to <see langword="true"/> in the
|
||||
/// event handler, the event will be canceled.
|
||||
/// </summary>
|
||||
public bool Cancel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>
|
||||
/// Initializes a new instance of <see cref="MenuClosingEventArgs"/>.
|
||||
/// </summary>
|
||||
/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
|
||||
/// <param name="reopen">Whether the current menu will be reopen.</param>
|
||||
/// <param name="reopen">Whether the current menu will reopen.</param>
|
||||
/// <param name="isSubMenu">Indicates whether it is a sub-menu.</param>
|
||||
public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu)
|
||||
{
|
||||
|
||||
@@ -43,12 +43,14 @@ namespace UICatalog.Scenarios {
|
||||
Height = Dim.Fill (1),
|
||||
};
|
||||
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
var fileMenu = new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_Open CSV", "", () => Open()),
|
||||
new MenuItem ("_Save", "", () => Save()),
|
||||
new MenuItem ("_Quit", "", () => Quit()),
|
||||
}),
|
||||
new MenuItem ("_Quit", "Quits The App", () => Quit()),
|
||||
});
|
||||
//fileMenu.Help = "Help";
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
fileMenu,
|
||||
new MenuBarItem ("_Edit", new MenuItem [] {
|
||||
new MenuItem ("_New Column", "", () => AddColumn()),
|
||||
new MenuItem ("_New Row", "", () => AddRow()),
|
||||
|
||||
@@ -116,6 +116,7 @@ namespace UICatalog.Scenarios {
|
||||
new MenuBarItem ("_Languages", GetSupportedCultures ())
|
||||
})
|
||||
});
|
||||
|
||||
Top.Add (menu);
|
||||
|
||||
var statusBar = new StatusBar (new StatusItem [] {
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace UICatalog {
|
||||
|
||||
_menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
|
||||
new MenuItem ("_Quit", "Quit UI Catalog", () => Application.RequestStop(), null, null, Key.Q | Key.CtrlMask)
|
||||
}),
|
||||
new MenuBarItem ("_Color Scheme", CreateColorSchemeMenuItems()),
|
||||
new MenuBarItem ("Diag_nostics", CreateDiagnosticMenuItems()),
|
||||
@@ -178,7 +178,7 @@ namespace UICatalog {
|
||||
new MenuItem ("gui.cs _README", "", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), null, null, Key.F2),
|
||||
new MenuItem ("_About...",
|
||||
"About UI Catalog", () => MessageBox.Query ("About UI Catalog", aboutMessage.ToString(), "_Ok"), null, null, Key.CtrlMask | Key.A),
|
||||
})
|
||||
}),
|
||||
});
|
||||
|
||||
_leftPane = new FrameView ("Categories") {
|
||||
@@ -318,7 +318,7 @@ namespace UICatalog {
|
||||
{
|
||||
List<MenuItem> menuItems = new List<MenuItem> ();
|
||||
var item = new MenuItem ();
|
||||
item.Title = "_Disable/Enable Mouse";
|
||||
item.Title = "_Disable Mouse";
|
||||
item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
|
||||
item.CheckType |= MenuItemCheckStyle.Checked;
|
||||
item.Checked = Application.IsMouseDisabled;
|
||||
@@ -334,7 +334,8 @@ namespace UICatalog {
|
||||
|
||||
List<MenuItem> menuItems = new List<MenuItem> ();
|
||||
var item = new MenuItem ();
|
||||
item.Title = "Keybindings";
|
||||
item.Title = "_Key Bindings";
|
||||
item.Help = "Change which keys do what";
|
||||
item.Action += () => {
|
||||
var dlg = new KeyBindingsDialog ();
|
||||
Application.Run (dlg);
|
||||
|
||||
@@ -592,7 +592,7 @@ namespace Terminal.Gui.Core {
|
||||
Assert.Equal (new Point (9, 3), tf.ContextMenu.Position);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
File Edit
|
||||
|
||||
|
||||
Label: TextField
|
||||
@@ -612,7 +612,7 @@ namespace Terminal.Gui.Core {
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 32, 17), pos);
|
||||
Assert.Equal (new Rect (1, 0, 32, 17), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
@@ -656,7 +656,7 @@ namespace Terminal.Gui.Core {
|
||||
Assert.Equal (new Point (10, 5), tf.ContextMenu.Position);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
File Edit
|
||||
┌ Window ──────────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
@@ -676,7 +676,7 @@ namespace Terminal.Gui.Core {
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 44, 17), pos);
|
||||
Assert.Equal (new Rect (1, 0, 44, 17), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
using static Terminal.Gui.Views.MenuTests;
|
||||
|
||||
namespace Terminal.Gui.Views {
|
||||
public class MenuTests {
|
||||
@@ -705,16 +707,15 @@ Edit
|
||||
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
|
||||
Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -723,12 +724,11 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│┌─────────────┐
|
||||
@@ -738,12 +738,11 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 25, 7), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.CursorLeft, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -752,16 +751,14 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
@@ -785,11 +782,11 @@ Edit
|
||||
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -799,7 +796,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -808,7 +805,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -818,7 +815,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│┌─────────────┐
|
||||
@@ -828,7 +825,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 25, 7), pos);
|
||||
Assert.Equal (new Rect (1, 0, 25, 7), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -838,7 +835,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -847,7 +844,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 70,
|
||||
@@ -857,11 +854,11 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
@@ -887,16 +884,16 @@ Edit
|
||||
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
|
||||
Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -905,13 +902,13 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.CursorDown, null)));
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Enter, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌─────────────┐
|
||||
│◄ Two │
|
||||
├─────────────┤
|
||||
@@ -921,12 +918,12 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 15, 7), pos);
|
||||
Assert.Equal (new Rect (1, 0, 15, 7), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [2].ProcessKey (new KeyEvent (Key.Enter, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -935,16 +932,16 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new KeyEvent (Key.Esc, null)));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
@@ -970,11 +967,11 @@ Edit
|
||||
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -984,7 +981,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -993,7 +990,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -1003,7 +1000,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌─────────────┐
|
||||
│◄ Two │
|
||||
├─────────────┤
|
||||
@@ -1013,7 +1010,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 15, 7), pos);
|
||||
Assert.Equal (new Rect (1, 0, 15, 7), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 1,
|
||||
@@ -1023,7 +1020,7 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
┌────────┐
|
||||
│ One │
|
||||
│ Two ►│
|
||||
@@ -1032,7 +1029,7 @@ Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 10, 6), pos);
|
||||
Assert.Equal (new Rect (1, 0, 10, 6), pos);
|
||||
|
||||
Assert.False (menu.MouseEvent (new MouseEvent () {
|
||||
X = 70,
|
||||
@@ -1042,11 +1039,11 @@ Edit
|
||||
}));
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
Numbers
|
||||
Numbers
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 9, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 8, 1), pos);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
@@ -1074,11 +1071,11 @@ Edit
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
File Edit
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 11, 1), pos);
|
||||
|
||||
Assert.True (menu.ProcessKey (new (Key.N, null)));
|
||||
Application.MainLoop.MainIteration ();
|
||||
@@ -1088,11 +1085,11 @@ Edit
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
File Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 1), pos);
|
||||
Assert.Equal (new Rect (1, 0, 11, 1), pos);
|
||||
|
||||
Assert.True (menu.ProcessKey (new (Key.CursorRight, null)));
|
||||
Assert.True (menu.ProcessKey (new (Key.C, null)));
|
||||
@@ -1100,21 +1097,150 @@ Edit
|
||||
Assert.True (copyAction);
|
||||
}
|
||||
|
||||
// Defines the expected strings for a Menu. Currently supports
|
||||
// - MenuBar with any number of MenuItems
|
||||
// - Each top-level MenuItem can have a SINGLE sub-menu
|
||||
//
|
||||
// TODO: Enable multiple sub-menus
|
||||
// TODO: Enable checked sub-menus
|
||||
// TODO: Enable sub-menus with sub-menus (perhaps better to put this in a separate class with focused unit tests?)
|
||||
//
|
||||
// E.g:
|
||||
//
|
||||
// File Edit
|
||||
// New Copy
|
||||
public class ExpectedMenuBar : MenuBar {
|
||||
FakeDriver d = ((FakeDriver)Application.Driver);
|
||||
|
||||
// Each MenuBar title has a 1 space pad on each side
|
||||
// See `static int leftPadding` and `static int rightPadding` on line 1037 of Menu.cs
|
||||
public string MenuBarText {
|
||||
get {
|
||||
string txt = string.Empty;
|
||||
foreach (var m in Menus) {
|
||||
|
||||
txt += " " + m.Title.ToString () + " ";
|
||||
}
|
||||
return txt;
|
||||
}
|
||||
}
|
||||
|
||||
// The expected strings when the menu is closed
|
||||
public string ClosedMenuText => MenuBarText + "\n";
|
||||
|
||||
// Padding for the X of the sub menu Frane
|
||||
// Menu.cs - Line 1239 in `internal void OpenMenu` is where the Menu is created
|
||||
string padding (int i)
|
||||
{
|
||||
int n = 0;
|
||||
while (i > 0){
|
||||
n += Menus [i-1].TitleLength + 2;
|
||||
i--;
|
||||
}
|
||||
return new string (' ', n);
|
||||
}
|
||||
|
||||
// Define expected menu frame
|
||||
// "┌──────┐"
|
||||
// "│ New │"
|
||||
// "└──────┘"
|
||||
//
|
||||
// The width of the Frame is determined in Menu.cs line 144, where `Width` is calculated
|
||||
// 1 space before the Title and 2 spaces after the Title/Check/Help
|
||||
public string expectedTopRow (int i) => $"{d.ULCorner}{new String (d.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{d.URCorner} \n";
|
||||
// The 3 spaces at end are a result of Menu.cs line 1062 where `pos` is calculated (` + spacesAfterTitle`)
|
||||
public string expectedMenuItemRow (int i) => $"{d.VLine} {Menus [i].Children [0].Title} {d.VLine} \n";
|
||||
public string expectedBottomRow (int i) => $"{d.LLCorner}{new String (d.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{d.LRCorner} \n";
|
||||
|
||||
// The fulll expected string for an open sub menu
|
||||
public string expectedSubMenuOpen (int i) => ClosedMenuText +
|
||||
(Menus [i].Children.Length > 0 ?
|
||||
padding (i) + expectedTopRow (i) +
|
||||
padding (i) + expectedMenuItemRow (i) +
|
||||
padding (i) + expectedBottomRow (i)
|
||||
:
|
||||
"");
|
||||
|
||||
public ExpectedMenuBar (MenuBarItem [] menus) : base (menus)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void MenuBar_Submenus_Alignment_Correct ()
|
||||
{
|
||||
// Define the expected menu
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("Really Long Sub Menu", "", null)
|
||||
}),
|
||||
new MenuBarItem ("123", new MenuItem [] {
|
||||
new MenuItem ("Copy", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Format", new MenuItem [] {
|
||||
new MenuItem ("Word Wrap", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Help", new MenuItem [] {
|
||||
new MenuItem ("About", "", null)
|
||||
}),
|
||||
new MenuBarItem ("1", new MenuItem [] {
|
||||
new MenuItem ("2", "", null)
|
||||
}),
|
||||
new MenuBarItem ("3", new MenuItem [] {
|
||||
new MenuItem ("2", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Last one", new MenuItem [] {
|
||||
new MenuItem ("Test", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
|
||||
for (var i = 0; i < expectedMenu.Menus.Length; i++) {
|
||||
items [i] = new MenuBarItem (expectedMenu.Menus [i].Title, new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus [i].Children [0].Title, "", null)
|
||||
});
|
||||
}
|
||||
var menu = new MenuBar (items);
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
|
||||
for (var i = 0; i < expectedMenu.Menus.Length; i++) {
|
||||
menu.OpenMenu (i);
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (i), output);
|
||||
}
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void HotKey_MenuBar_ProcessHotKey_Menu_ProcessKey ()
|
||||
{
|
||||
var newAction = false;
|
||||
var copyAction = false;
|
||||
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_New", "", () => newAction = true)
|
||||
// Define the expected menu
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
}),
|
||||
new MenuBarItem ("_Edit", new MenuItem [] {
|
||||
new MenuItem ("_Copy", "", () => copyAction = true)
|
||||
new MenuBarItem ("Edit", new MenuItem [] {
|
||||
new MenuItem ("Copy", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
// The real menu
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "", () => newAction = true)
|
||||
}),
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "", () => copyAction = true)
|
||||
}),
|
||||
});
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
Assert.False (newAction);
|
||||
@@ -1123,15 +1249,7 @@ Edit
|
||||
Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.F, new KeyModifiers () { Alt = true })));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.N, null)));
|
||||
Application.MainLoop.MainIteration ();
|
||||
@@ -1140,15 +1258,7 @@ Edit
|
||||
Assert.True (menu.ProcessHotKey (new (Key.AltMask | Key.E, new KeyModifiers () { Alt = true })));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
┌───────┐
|
||||
│ Copy │
|
||||
└───────┘
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 16, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.C, null)));
|
||||
Application.MainLoop.MainIteration ();
|
||||
@@ -1158,127 +1268,114 @@ Edit
|
||||
[Fact, AutoInitShutdown]
|
||||
public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys ()
|
||||
{
|
||||
// With HotKeys
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("_New", "", null)
|
||||
}),
|
||||
new MenuBarItem ("_Edit", new MenuItem [] {
|
||||
new MenuItem ("_Copy", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 4), pos);
|
||||
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
┌───────┐
|
||||
│ Copy │
|
||||
└───────┘
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 16, 4), pos);
|
||||
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 1), pos);
|
||||
|
||||
// Without HotKeys
|
||||
menu = new MenuBar (new MenuBarItem [] {
|
||||
// Define the expected menu
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
new MenuItem ("12", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Edit", new MenuItem [] {
|
||||
new MenuItem ("Copy", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
// Test without HotKeys first
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem (expectedMenu.Menus[0].Title, new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus[0].Children[0].Title, "", null)
|
||||
}),
|
||||
new MenuBarItem (expectedMenu.Menus[1].Title, new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus[1].Children[0].Title, "", null)
|
||||
})
|
||||
});
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
// Open first
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
// Open second
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
┌───────┐
|
||||
│ Copy │
|
||||
└───────┘
|
||||
";
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 16, 4), pos);
|
||||
// Close menu
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
|
||||
Application.Top.Remove (menu);
|
||||
|
||||
// Now test WITH HotKeys
|
||||
menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "", null)
|
||||
}),
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "", null)
|
||||
}),
|
||||
});
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
// Open first
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
// Open second
|
||||
Assert.True (Application.Top.Subviews [1].ProcessKey (new (Key.CursorRight, null)));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
// Close menu
|
||||
Assert.True (menu.ProcessHotKey (new (Key.F9, new KeyModifiers ())));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu ()
|
||||
{
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
// Define the expected menu
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
new MenuItem ("Open", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Edit", new MenuItem [] {
|
||||
new MenuItem ("Copy", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
// Test without HotKeys first
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[0].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[0].Children[0].Title, "", null)
|
||||
}),
|
||||
new MenuBarItem ("_" + expectedMenu.Menus[1].Title, new MenuItem [] {
|
||||
new MenuItem ("_" + expectedMenu.Menus[1].Children[0].Title, "", null)
|
||||
}),
|
||||
});
|
||||
|
||||
Application.Top.Add (menu);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 13, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
@@ -1300,110 +1397,98 @@ Edit
|
||||
[Fact, AutoInitShutdown]
|
||||
public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse ()
|
||||
{
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
// File Edit Format
|
||||
//┌──────┐ ┌───────┐
|
||||
//│ New │ │ Wrap │
|
||||
//└──────┘ └───────┘
|
||||
|
||||
// Define the expected menu
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Edit", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Edit", new MenuItem [] {}),
|
||||
new MenuBarItem ("Format", new MenuItem [] {
|
||||
new MenuItem ("Wrap", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem (expectedMenu.Menus[0].Title, new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus[0].Children[0].Title, "", null)
|
||||
}),
|
||||
new MenuBarItem (expectedMenu.Menus[1].Title, new MenuItem [] {}),
|
||||
new MenuBarItem (expectedMenu.Menus[2].Title, new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus[2].Children[0].Title, "", null)
|
||||
})
|
||||
});
|
||||
|
||||
var tf = new TextField () { Y = 2, Width = 10 };
|
||||
Application.Top.Add (menu, tf);
|
||||
|
||||
Application.Begin (Application.Top);
|
||||
|
||||
Assert.True (tf.HasFocus);
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit Format
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 15, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
┌───────┐
|
||||
│ Wrap │
|
||||
└───────┘
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 23, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (2), output);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 1, Y = 0, Flags = MouseFlags.ReportMousePosition, View = menu }));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 4), pos);
|
||||
|
||||
Assert.True (menu.MouseEvent (new MouseEvent () { X = 8, Y = 0, Flags = MouseFlags.Button1Pressed, View = menu }));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Assert.True (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard ()
|
||||
{
|
||||
var menu = new MenuBar (new MenuBarItem [] {
|
||||
var expectedMenu = new ExpectedMenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("File", new MenuItem [] {
|
||||
new MenuItem ("New", "", null)
|
||||
}),
|
||||
new MenuBarItem ("Edit", new MenuItem [] {
|
||||
}),
|
||||
new MenuBarItem ("Edit", Array.Empty<MenuItem> ()),
|
||||
new MenuBarItem ("Format", new MenuItem [] {
|
||||
new MenuItem ("Wrap", "", null)
|
||||
})
|
||||
});
|
||||
|
||||
MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length];
|
||||
for (var i = 0; i < expectedMenu.Menus.Length; i++) {
|
||||
items [i] = new MenuBarItem (expectedMenu.Menus [i].Title, expectedMenu.Menus [i].Children.Length > 0
|
||||
? new MenuItem [] {
|
||||
new MenuItem (expectedMenu.Menus [i].Children [0].Title, "", null),
|
||||
}
|
||||
: Array.Empty<MenuItem> ());
|
||||
}
|
||||
var menu = new MenuBar (items);
|
||||
|
||||
var tf = new TextField () { Y = 2, Width = 10 };
|
||||
Application.Top.Add (menu, tf);
|
||||
|
||||
@@ -1413,76 +1498,40 @@ Edit
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
var expected = @"
|
||||
File Edit Format
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen(0), output);
|
||||
|
||||
// Right - Edit has no sub menu; this tests that no sub menu shows
|
||||
Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
// Right - Format
|
||||
Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorRight, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
┌───────┐
|
||||
│ Wrap │
|
||||
└───────┘
|
||||
";
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (2), output);
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 23, 4), pos);
|
||||
// Left - Edit
|
||||
Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (1), output);
|
||||
|
||||
Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
|
||||
Assert.True (menu.openMenu.ProcessKey (new KeyEvent (Key.CursorLeft, new KeyModifiers ())));
|
||||
Assert.True (menu.IsMenuOpen);
|
||||
Assert.False (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
┌──────┐
|
||||
│ New │
|
||||
└──────┘
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 4), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.expectedSubMenuOpen (0), output);
|
||||
|
||||
Assert.True (menu.ProcessHotKey (new KeyEvent (Key.F9, new KeyModifiers ())));
|
||||
Assert.False (menu.IsMenuOpen);
|
||||
Assert.True (tf.HasFocus);
|
||||
Application.Top.Redraw (Application.Top.Bounds);
|
||||
expected = @"
|
||||
File Edit Format
|
||||
";
|
||||
|
||||
pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
|
||||
Assert.Equal (new Rect (2, 0, 22, 1), pos);
|
||||
GraphViewTests.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output);
|
||||
}
|
||||
|
||||
[Fact, AutoInitShutdown]
|
||||
|
||||
@@ -247,7 +247,7 @@ namespace Terminal.Gui.Core {
|
||||
win.Frame.Right, win.Frame.Bottom));
|
||||
Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
|
||||
var expected = @"
|
||||
Menu
|
||||
Menu
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
@@ -310,7 +310,7 @@ namespace Terminal.Gui.Core {
|
||||
win.Frame.Right, win.Frame.Bottom));
|
||||
Assert.Equal (new Rect (0, 20, 78, 1), label.Frame);
|
||||
var expected = @"
|
||||
Menu
|
||||
Menu
|
||||
┌──────────────────────────────────────────────────────────────────────────────┐
|
||||
│ │
|
||||
│ │
|
||||
|
||||
Reference in New Issue
Block a user