diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs
index e738d6d1d..00f07a16c 100644
--- a/Terminal.Gui/Views/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialog.cs
@@ -1207,19 +1207,19 @@ public class FileDialog : Dialog
var contextMenu = new ContextMenu
{
- Position = new Point (e.MouseEvent.Position.X + 1, e.MouseEvent.Position.Y + 1),
- MenuItems = new MenuBarItem (
+ Position = new Point (e.MouseEvent.Position.X + 1, e.MouseEvent.Position.Y + 1)
+ };
+
+ var menuItems = new MenuBarItem (
[
new MenuItem (Strings.fdCtxNew, string.Empty, New),
new MenuItem (Strings.fdCtxRename, string.Empty, Rename),
new MenuItem (Strings.fdCtxDelete, string.Empty, Delete)
]
- )
- };
-
+ );
_tableView.SetSelection (clickedCell.Value.X, clickedCell.Value.Y, false);
- contextMenu.Show ();
+ contextMenu.Show (menuItems);
}
private void ShowHeaderContextMenu (int clickedCol, MouseEventEventArgs e)
@@ -1228,8 +1228,10 @@ public class FileDialog : Dialog
var contextMenu = new ContextMenu
{
- Position = new Point (e.MouseEvent.Position.X + 1, e.MouseEvent.Position.Y + 1),
- MenuItems = new MenuBarItem (
+ Position = new Point (e.MouseEvent.Position.X + 1, e.MouseEvent.Position.Y + 1)
+ };
+
+ var menuItems = new MenuBarItem (
[
new MenuItem (
string.Format (
@@ -1244,10 +1246,8 @@ public class FileDialog : Dialog
string.Empty,
() => SortColumn (clickedCol, isAsc))
]
- )
- };
-
- contextMenu.Show ();
+ );
+ contextMenu.Show (menuItems);
}
private void SortColumn (int clickedCol)
diff --git a/Terminal.Gui/Views/Menu/ContextMenu.cs b/Terminal.Gui/Views/Menu/ContextMenu.cs
index 6a6131195..0ceec6d74 100644
--- a/Terminal.Gui/Views/Menu/ContextMenu.cs
+++ b/Terminal.Gui/Views/Menu/ContextMenu.cs
@@ -1,4 +1,6 @@
-namespace Terminal.Gui;
+#nullable enable
+
+namespace Terminal.Gui;
///
/// ContextMenu provides a pop-up menu that can be positioned anywhere within a . ContextMenu is
@@ -16,15 +18,15 @@
///
///
/// Callers can cause the ContextMenu to be activated on a right-mouse click (or other interaction) by calling
-/// .
+/// .
///
/// ContextMenus are located using screen coordinates and appear above all other Views.
///
public sealed class ContextMenu : IDisposable
{
- private static MenuBar _menuBar;
+ private static MenuBar? _menuBar;
- private Toplevel _container;
+ private Toplevel? _container;
private Key _key = DefaultKey;
private MouseFlags _mouseFlags = MouseFlags.Button3Clicked;
@@ -33,15 +35,9 @@ public sealed class ContextMenu : IDisposable
{
if (IsShow)
{
- if (_menuBar.SuperView is { })
- {
- Hide ();
- }
-
+ Hide ();
IsShow = false;
}
-
- MenuItems = new MenuBarItem ();
}
/// The default shortcut key for activating the context menu.
@@ -56,13 +52,13 @@ public sealed class ContextMenu : IDisposable
public bool ForceMinimumPosToZero { get; set; } = true;
/// The host which position will be used, otherwise if it's null the container will be used.
- public View Host { get; set; }
+ public View? Host { get; set; }
/// Gets whether the ContextMenu is showing or not.
public static bool IsShow { get; private set; }
/// Specifies the key that will activate the context menu.
- public Key Key
+ public new Key Key
{
get => _key;
set
@@ -74,10 +70,10 @@ public sealed class ContextMenu : IDisposable
}
/// Gets the that is hosting this context menu.
- public MenuBar MenuBar => _menuBar;
+ public MenuBar? MenuBar => _menuBar;
/// Gets or sets the menu items for this context menu.
- public MenuBarItem MenuItems { get; set; }
+ public MenuBarItem? MenuItems { get; private set; }
/// specifies the mouse action used to activate the context menu by mouse.
public MouseFlags MouseFlags
@@ -105,11 +101,10 @@ public sealed class ContextMenu : IDisposable
/// Disposes the context menu object.
public void Dispose ()
{
- if (_menuBar is null)
+ if (_menuBar is { })
{
- return;
+ _menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
}
- _menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
Application.UngrabMouse ();
_menuBar?.Dispose ();
_menuBar = null;
@@ -126,18 +121,49 @@ public sealed class ContextMenu : IDisposable
/// Hides (closes) the ContextMenu.
public void Hide ()
{
+ RemoveKeyBindings (MenuItems);
_menuBar?.CleanUp ();
IsShow = false;
}
+ private void RemoveKeyBindings (MenuBarItem? menuBarItem)
+ {
+ if (menuBarItem is null)
+ {
+ return;
+ }
+
+ foreach (var menuItem in menuBarItem.Children!)
+ {
+ // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
+ if (menuItem is null)
+ {
+ continue;
+ }
+
+ if (menuItem is MenuBarItem barItem)
+ {
+ RemoveKeyBindings (barItem);
+ }
+ else
+ {
+ if (menuItem.ShortcutKey != Key.Empty)
+ {
+ // Remove an existent ShortcutKey
+ _menuBar?.KeyBindings.Remove (menuItem.ShortcutKey);
+ }
+ }
+ }
+ }
+
/// Event invoked when the is changed.
- public event EventHandler KeyChanged;
+ public event EventHandler? KeyChanged;
/// Event invoked when the is changed.
- public event EventHandler MouseFlagsChanged;
+ public event EventHandler? MouseFlagsChanged;
/// Shows (opens) the ContextMenu, displaying the s it contains.
- public void Show ()
+ public void Show (MenuBarItem? menuItems)
{
if (_menuBar is { })
{
@@ -145,6 +171,12 @@ public sealed class ContextMenu : IDisposable
Dispose ();
}
+ if (menuItems is null || menuItems.Children.Length == 0)
+ {
+ return;
+ }
+
+ MenuItems = menuItems;
_container = Application.Current;
_container!.Closing += Container_Closing;
_container.Deactivate += Container_Deactivate;
@@ -155,7 +187,7 @@ public sealed class ContextMenu : IDisposable
if (Host is { })
{
Point pos = Host.ViewportToScreen (frame).Location;
- pos.Y += Host.Frame.Height - 1;
+ pos.Y += Host.Frame.Height > 0 ? Host.Frame.Height - 1 : 0;
if (position != pos)
{
@@ -224,9 +256,9 @@ public sealed class ContextMenu : IDisposable
_menuBar.OpenMenu ();
}
- private void Container_Closing (object sender, ToplevelClosingEventArgs obj) { Hide (); }
- private void Container_Deactivate (object sender, ToplevelEventArgs e) { Hide (); }
- private void Container_Disposing (object sender, EventArgs e) { Dispose (); }
+ private void Container_Closing (object? sender, ToplevelClosingEventArgs obj) { Hide (); }
+ private void Container_Deactivate (object? sender, ToplevelEventArgs e) { Hide (); }
+ private void Container_Disposing (object? sender, EventArgs e) { Dispose (); }
- private void MenuBar_MenuAllClosed (object sender, EventArgs e) { Hide (); }
+ private void MenuBar_MenuAllClosed (object? sender, EventArgs e) { Hide (); }
}
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menu/Menu.cs
index 7119c28aa..091e87aa7 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menu/Menu.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
namespace Terminal.Gui;
///
@@ -11,7 +13,7 @@ internal sealed class Menu : View
internal int _currentChild;
internal View _previousSubFocused;
- internal static Rectangle MakeFrame (int x, int y, MenuItem [] items, Menu parent = null)
+ internal static Rectangle MakeFrame (int x, int y, MenuItem [] items, Menu? parent = null)
{
if (items is null || items.Length == 0)
{
@@ -68,6 +70,25 @@ internal sealed class Menu : View
Frame = MakeFrame (Frame.X, Frame.Y, _barItems?.Children, Parent);
+ 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
@@ -166,9 +187,9 @@ internal sealed class Menu : View
return true;
}
);
- AddCommand (Command.Select, ctx => _host?.SelectItem (ctx.KeyBinding?.Context as MenuItem));
- AddCommand (Command.ToggleExpandCollapse, ctx => ExpandCollapse (ctx.KeyBinding?.Context as MenuItem));
- AddCommand (Command.HotKey, ctx => _host?.SelectItem (ctx.KeyBinding?.Context as MenuItem));
+ AddCommand (Command.Select, ctx => _host?.SelectItem ((ctx.KeyBinding?.Context as MenuItem)!));
+ AddCommand (Command.ToggleExpandCollapse, 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.LineUp);
@@ -179,7 +200,7 @@ internal sealed class Menu : View
KeyBindings.Add (Key.Enter, Command.Accept);
}
- private void AddKeyBindingsHotKey (MenuBarItem menuBarItem)
+ private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem)
{
if (menuBarItem is null || menuBarItem.Children is null)
{
@@ -200,7 +221,7 @@ internal sealed class Menu : View
}
}
- private void RemoveKeyBindingsHotKey (MenuBarItem menuBarItem)
+ private void RemoveKeyBindingsHotKey (MenuBarItem? menuBarItem)
{
if (menuBarItem is null || menuBarItem.Children is null)
{
@@ -219,7 +240,7 @@ internal sealed class Menu : View
/// Called when a key bound to Command.ToggleExpandCollapse is pressed. This means a hot key was pressed.
///
- private bool ExpandCollapse (MenuItem menuItem)
+ private bool ExpandCollapse (MenuItem? menuItem)
{
if (!IsInitialized || !Visible)
{
@@ -243,7 +264,7 @@ internal sealed class Menu : View
if (m?.Children?.Length > 0)
{
- MenuItem item = _barItems.Children [_currentChild];
+ MenuItem? item = _barItems.Children [_currentChild];
if (item is null)
{
@@ -297,7 +318,7 @@ internal sealed class Menu : View
return _host.OnInvokingKeyBindings (keyEvent, scope);
}
- private void Current_TerminalResized (object sender, SizeChangedEventArgs e)
+ private void Current_TerminalResized (object? sender, SizeChangedEventArgs e)
{
if (_host.IsMenuOpen)
{
@@ -320,7 +341,7 @@ internal sealed class Menu : View
}
}
- private void Application_RootMouseEvent (object sender, MouseEvent a)
+ private void Application_RootMouseEvent (object? sender, MouseEvent a)
{
if (a.View is { } and (MenuBar or not Menu))
{
@@ -350,7 +371,7 @@ internal sealed class Menu : View
}
}
- internal Attribute DetermineColorSchemeFor (MenuItem item, int index)
+ internal Attribute DetermineColorSchemeFor (MenuItem? item, int index)
{
if (item is null)
{
@@ -456,7 +477,7 @@ internal sealed class Menu : View
continue;
}
- string textToDraw = null;
+ string? textToDraw = null;
Rune nullCheckedChar = Glyphs.CheckStateNone;
Rune checkChar = Glyphs.Selected;
Rune uncheckedChar = Glyphs.UnSelected;
@@ -468,7 +489,7 @@ internal sealed class Menu : View
}
// Support Checked even though CheckType wasn't set
- if (item.CheckType == MenuItemCheckStyle.Checked && item.Checked is null)
+ if (item is { CheckType: MenuItemCheckStyle.Checked, Checked: null })
{
textToDraw = $"{nullCheckedChar} {item.Title}";
}
@@ -548,7 +569,7 @@ internal sealed class Menu : View
// PositionCursor ();
}
- private void Current_DrawContentComplete (object sender, DrawEventArgs e)
+ private void Current_DrawContentComplete (object? sender, DrawEventArgs e)
{
if (Visible)
{
@@ -573,9 +594,9 @@ internal sealed class Menu : View
return _host?.PositionCursor ();
}
- public void Run (Action action)
+ public void Run (Action? action)
{
- if (action is null || _host is null)
+ if (action is null)
{
return;
}
@@ -900,7 +921,7 @@ internal sealed class Menu : View
if (pos == -1
&& this != _host.OpenCurrentMenu
- && subMenu.Children != _host.OpenCurrentMenu._barItems.Children
+ && subMenu.Children != _host.OpenCurrentMenu!._barItems.Children
&& !_host.CloseMenu (false, true))
{
return false;
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menu/MenuBar.cs
index 766e89cdf..35cdfa326 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menu/MenuBar.cs
@@ -1,3 +1,5 @@
+#nullable enable
+
namespace Terminal.Gui;
///
@@ -50,14 +52,14 @@ public class MenuBar : View, IDesignable
internal bool _isMenuClosing;
internal bool _isMenuOpening;
- internal Menu _openMenu;
- internal List