diff --git a/Terminal.Gui/Application/Application.Initialization.cs b/Terminal.Gui/Application/Application.Initialization.cs
index 7093b8702..30f2f5821 100644
--- a/Terminal.Gui/Application/Application.Initialization.cs
+++ b/Terminal.Gui/Application/Application.Initialization.cs
@@ -82,7 +82,10 @@ public static partial class Application // Initialization (Init/Shutdown)
ResetState (ignoreDisposed: true);
}
+ Debug.Assert (Navigation is null);
Navigation = new ();
+
+ Debug.Assert(Popover is null);
Popover = new ();
// For UnitTests
diff --git a/Terminal.Gui/Application/Application.Run.cs b/Terminal.Gui/Application/Application.Run.cs
index 4412f5d23..f4a4cf44e 100644
--- a/Terminal.Gui/Application/Application.Run.cs
+++ b/Terminal.Gui/Application/Application.Run.cs
@@ -564,7 +564,7 @@ public static partial class Application // Run (Begin, Run, End, Stop)
{
ArgumentNullException.ThrowIfNull (runState);
- Popover?.HidePopover (Popover?.GetActivePopover ());
+ Popover?.Hide (Popover?.GetActivePopover ());
runState.Toplevel.OnUnloaded ();
diff --git a/Terminal.Gui/Application/Application.cs b/Terminal.Gui/Application/Application.cs
index 5d01106ae..07bec47c7 100644
--- a/Terminal.Gui/Application/Application.cs
+++ b/Terminal.Gui/Application/Application.cs
@@ -153,6 +153,7 @@ public static partial class Application
{
popover.Visible = false;
}
+ Popover?.Dispose ();
Popover = null;
TopLevels.Clear ();
diff --git a/Terminal.Gui/Application/ApplicationPopover.cs b/Terminal.Gui/Application/ApplicationPopover.cs
index 9c0b94462..bc4a0e010 100644
--- a/Terminal.Gui/Application/ApplicationPopover.cs
+++ b/Terminal.Gui/Application/ApplicationPopover.cs
@@ -1,13 +1,12 @@
#nullable enable
-using System.Diagnostics;
-
namespace Terminal.Gui;
///
-/// Helper class for support of views for . Held by
+/// Helper class for support of views for . Held by
+///
///
-public class ApplicationPopover
+public sealed class ApplicationPopover : IDisposable
{
///
/// Initializes a new instance of the class.
@@ -16,27 +15,41 @@ public class ApplicationPopover
private readonly List _popovers = [];
- ///
+ ///
+ /// Gets the list of popovers registered with the application.
+ ///
public IReadOnlyCollection Popovers => _popovers.AsReadOnly ();
///
/// Registers with the application.
- /// This enables the popover to receive keyboard events even when when it is not active.
+ /// This enables the popover to receive keyboard events even when it is not active.
///
+ ///
+ /// When a popover is registered, the View instance lifetime is managed by the application. Call
+ ///
+ /// to manage the lifetime of the popover directly.
+ ///
///
- public void Register (IPopover? popover)
+ /// , after it has been registered.
+ public IPopover? Register (IPopover? popover)
{
if (popover is { } && !_popovers.Contains (popover))
{
_popovers.Add (popover);
-
}
+
+ return popover;
}
///
/// De-registers with the application. Use this to remove the popover and it's
/// keyboard bindings from the application.
///
+ ///
+ /// When a popover is registered, the View instance lifetime is managed by the application. Call
+ ///
+ /// to manage the lifetime of the popover directly.
+ ///
///
///
public bool DeRegister (IPopover? popover)
@@ -61,22 +74,25 @@ public class ApplicationPopover
///
/// Gets the active popover, if any.
///
+ ///
+ /// Note, the active pop over does not necessarily to be registered with the application.
+ ///
///
public IPopover? GetActivePopover () { return _activePopover; }
///
- /// Shows . IPopover implementations should use OnVisibleChnaged/VisibleChanged to be
+ /// Shows . IPopover implementations should use OnVisibleChanaged/VisibleChanged to be
/// notified when the user has done something to cause the popover to be hidden.
///
///
///
- /// Note, this API calls . To disable the popover from processing keyboard events,
+ /// This API calls . To disable the popover from processing keyboard events,
/// either call to
/// remove the popover from the application or set to .
///
///
///
- public void ShowPopover (IPopover? popover)
+ public void Show (IPopover? popover)
{
// If there's an existing popover, hide it.
if (_activePopover is View popoverView)
@@ -87,8 +103,6 @@ public class ApplicationPopover
if (popover is View newPopover)
{
- Register (popover);
-
if (!newPopover.IsInitialized)
{
newPopover.BeginInit ();
@@ -103,10 +117,11 @@ public class ApplicationPopover
///
/// Causes the specified popover to be hidden.
- /// If the popover is dervied from , this is the same as setting to .
+ /// If the popover is dervied from , this is the same as setting
+ /// to .
///
///
- public void HidePopover (IPopover? popover)
+ public void Hide (IPopover? popover)
{
// If there's an existing popover, hide it.
if (_activePopover is View popoverView && popoverView == popover)
@@ -117,7 +132,6 @@ public class ApplicationPopover
}
}
-
///
/// Called when the user presses a key. Dispatches the key to the active popover, if any,
/// otherwise to the popovers in the order they were registered. Inactive popovers only get hotkeys.
@@ -127,9 +141,11 @@ public class ApplicationPopover
internal bool DispatchKeyDown (Key key)
{
// Do active first - Active gets all key down events.
- if (GetActivePopover () as View is { Visible: true } visiblePopover)
+ var activePopover = GetActivePopover () as View;
+
+ if (activePopover is { Visible: true })
{
- if (visiblePopover.NewKeyDownEvent (key))
+ if (activePopover.NewKeyDownEvent (key))
{
return true;
}
@@ -141,7 +157,7 @@ public class ApplicationPopover
foreach (IPopover popover in _popovers)
{
- if (GetActivePopover () == popover || popover is not View popoverView)
+ if (popover == activePopover || popover is not View popoverView)
{
continue;
}
@@ -157,4 +173,18 @@ public class ApplicationPopover
return hotKeyHandled is true;
}
+
+ ///
+ public void Dispose ()
+ {
+ foreach (IPopover popover in _popovers)
+ {
+ if (popover is View view)
+ {
+ view.Dispose ();
+ }
+ }
+
+ _popovers.Clear ();
+ }
}
diff --git a/Terminal.Gui/Application/PopoverBaseImpl.cs b/Terminal.Gui/Application/PopoverBaseImpl.cs
index fce7d7866..64b90532c 100644
--- a/Terminal.Gui/Application/PopoverBaseImpl.cs
+++ b/Terminal.Gui/Application/PopoverBaseImpl.cs
@@ -6,19 +6,18 @@ namespace Terminal.Gui;
///
///
///
-/// To show a Popover, use . To hide a popover,
-/// call with set to .
+/// To show a Popover, use . To hide a popover,
+/// call with set to .
///
///
-/// If the user clicks anywhere not occulded by a SubView of the Popover, presses ,
+/// If the user clicks anywhere not occluded by a SubView of the Popover, presses ,
/// or causes another popover to show, the Popover will be hidden.
///
///
-
public abstract class PopoverBaseImpl : View, IPopover
{
///
- ///
+ /// Creates a new PopoverBaseImpl.
///
protected PopoverBaseImpl ()
{
@@ -28,10 +27,10 @@ public abstract class PopoverBaseImpl : View, IPopover
Height = Dim.Fill ();
ViewportSettings = ViewportSettings.Transparent | ViewportSettings.TransparentMouse;
- //// TODO: Add a diagnostic setting for this?
- TextFormatter.VerticalAlignment = Alignment.End;
- TextFormatter.Alignment = Alignment.End;
- base.Text = "popover";
+ // TODO: Add a diagnostic setting for this?
+ //TextFormatter.VerticalAlignment = Alignment.End;
+ //TextFormatter.Alignment = Alignment.End;
+ //base.Text = "popover";
AddCommand (Command.Quit, Quit);
KeyBindings.Add (Application.QuitKey, Command.Quit);
@@ -55,24 +54,13 @@ public abstract class PopoverBaseImpl : View, IPopover
protected override bool OnVisibleChanging ()
{
bool ret = base.OnVisibleChanging ();
- if (!ret & !Visible)
+ if (!ret && !Visible)
{
- // Whenvver visible is changing to true, we need to resize;
+ // Whenever visible is changing to true, we need to resize;
// it's our only chance because we don't get laid out until we're visible
Layout (Application.Screen.Size);
}
return ret;
}
-
- // TODO: Pretty sure this is not needed. set_Visible SetFocus already
- /////
- //protected override void OnVisibleChanged ()
- //{
- // base.OnVisibleChanged ();
- // if (Visible)
- // {
- // //SetFocus ();
- // }
- //}
}
diff --git a/Terminal.Gui/ConsoleDrivers/V2/ApplicationV2.cs b/Terminal.Gui/ConsoleDrivers/V2/ApplicationV2.cs
index 9baeba301..e6461b144 100644
--- a/Terminal.Gui/ConsoleDrivers/V2/ApplicationV2.cs
+++ b/Terminal.Gui/ConsoleDrivers/V2/ApplicationV2.cs
@@ -1,5 +1,6 @@
#nullable enable
using System.Collections.Concurrent;
+using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
@@ -63,7 +64,10 @@ public class ApplicationV2 : ApplicationImpl
_driverName = driverName;
}
+ Debug.Assert(Application.Navigation is null);
Application.Navigation = new ();
+
+ Debug.Assert (Application.Popover is null);
Application.Popover = new ();
Application.AddKeyBindings ();
diff --git a/Terminal.Gui/Drawing/Color/ColorScheme.Colors.cs b/Terminal.Gui/Drawing/Color/ColorScheme.Colors.cs
index 5b8b130fd..a311032de 100644
--- a/Terminal.Gui/Drawing/Color/ColorScheme.Colors.cs
+++ b/Terminal.Gui/Drawing/Color/ColorScheme.Colors.cs
@@ -44,7 +44,7 @@ public sealed class Colors : INotifyCollectionChanged, IDictionary
/// Menu
///
- /// The menu color scheme; used for , , and
+ /// The menu color scheme; used for , , and
/// .
///
///
diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json
index 31752d43b..230533b29 100644
--- a/Terminal.Gui/Resources/config.json
+++ b/Terminal.Gui/Resources/config.json
@@ -33,7 +33,7 @@
// --------------- View Specific Settings ---------------
- "ContextMenu.DefaultKey": "Shift+F10",
+ "PopoverMenu.DefaultKey": "Shift+F10",
"FileDialog.MaxSearchResults": 10000,
"FileDialogStyle.DefaultUseColors": false,
"FileDialogStyle.DefaultUseUnicodeCharacters": false,
diff --git a/Terminal.Gui/Terminal.Gui.csproj b/Terminal.Gui/Terminal.Gui.csproj
index d8efda92d..67444b57c 100644
--- a/Terminal.Gui/Terminal.Gui.csproj
+++ b/Terminal.Gui/Terminal.Gui.csproj
@@ -81,7 +81,7 @@
-
+
diff --git a/Terminal.Gui/Views/CharMap/CharMap.cs b/Terminal.Gui/Views/CharMap/CharMap.cs
index e15a732dc..a6753dfa7 100644
--- a/Terminal.Gui/Views/CharMap/CharMap.cs
+++ b/Terminal.Gui/Views/CharMap/CharMap.cs
@@ -18,8 +18,6 @@ public class CharMap : View, IDesignable
private const int HEADER_HEIGHT = 1; // Height of the header
private int _rowHeight = 1; // Height of each row of 16 glyphs - changing this is not tested
- private ContextMenu _contextMenu = new ();
-
///
/// Initializes a new instance.
///
@@ -58,7 +56,7 @@ public class CharMap : View, IDesignable
KeyBindings.Add (Key.PageDown, Command.PageDown);
KeyBindings.Add (Key.Home, Command.Start);
KeyBindings.Add (Key.End, Command.End);
- KeyBindings.Add (ContextMenu.DefaultKey, Command.Context);
+ KeyBindings.Add (PopoverMenu.DefaultKey, Command.Context);
MouseBindings.Add (MouseFlags.Button1DoubleClicked, Command.Accept);
MouseBindings.ReplaceCommands (MouseFlags.Button3Clicked, Command.Context);
@@ -505,32 +503,20 @@ public class CharMap : View, IDesignable
SelectedCodePoint = newCodePoint;
- _contextMenu = new ()
- {
- Position = ViewportToScreen (GetCursor (SelectedCodePoint))
- };
+ // This demonstrates how to create an ephemeral Popover; one that exists
+ // ony as long as the popover is visible.
+ // Note, for ephemeral Popovers, hotkeys are not supported.
+ PopoverMenu? contextMenu = new (
+ [
+ new (Strings.charMapCopyGlyph, string.Empty, CopyGlyph),
+ new (Strings.charMapCopyCP, string.Empty, CopyCodePoint)
+ ]);
- MenuBarItem menuItems = new (
- [
- new (
- Strings.charMapCopyGlyph,
- "",
- CopyGlyph,
- null,
- null,
- (KeyCode)Key.G.WithCtrl
- ),
- new (
- Strings.charMapCopyCP,
- "",
- CopyCodePoint,
- null,
- null,
- (KeyCode)Key.P.WithCtrl
- )
- ]
- );
- _contextMenu.Show (menuItems);
+ // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused
+ // and the context menu is disposed when it is closed.
+ Application.Popover?.Register (contextMenu);
+
+ contextMenu?.MakeVisible (ViewportToScreen (GetCursor (SelectedCodePoint)));
return true;
}
diff --git a/Terminal.Gui/Views/FileDialog.cs b/Terminal.Gui/Views/FileDialog.cs
index b21da3d12..cdeb939a8 100644
--- a/Terminal.Gui/Views/FileDialog.cs
+++ b/Terminal.Gui/Views/FileDialog.cs
@@ -70,14 +70,15 @@ public class FileDialog : Dialog, IDesignable
Canceled = true;
_fileSystem = fileSystem;
- Style = new FileDialogStyle (fileSystem);
+ Style = new (fileSystem);
- _btnOk = new Button
+ _btnOk = new()
{
X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
Y = Pos.AnchorEnd (),
IsDefault = true, Text = Style.OkButtonText
};
+
_btnOk.Accepting += (s, e) =>
{
if (e.Cancel)
@@ -88,8 +89,7 @@ public class FileDialog : Dialog, IDesignable
Accept (true);
};
-
- _btnCancel = new Button
+ _btnCancel = new()
{
X = Pos.Align (Alignment.End, AlignmentModes.AddSpaceBetweenItems, alignmentGroupComplete),
Y = Pos.AnchorEnd (),
@@ -97,30 +97,31 @@ public class FileDialog : Dialog, IDesignable
};
_btnCancel.Accepting += (s, e) =>
- {
- if (e.Cancel)
- {
- return;
- }
- if (Modal)
- {
- Application.RequestStop ();
- }
- };
+ {
+ if (e.Cancel)
+ {
+ return;
+ }
- _btnUp = new Button { X = 0, Y = 1, NoPadding = true };
+ if (Modal)
+ {
+ Application.RequestStop ();
+ }
+ };
+
+ _btnUp = new() { X = 0, Y = 1, NoPadding = true };
_btnUp.Text = GetUpButtonText ();
_btnUp.Accepting += (s, e) => _history.Up ();
- _btnBack = new Button { X = Pos.Right (_btnUp) + 1, Y = 1, NoPadding = true };
+ _btnBack = new() { X = Pos.Right (_btnUp) + 1, Y = 1, NoPadding = true };
_btnBack.Text = GetBackButtonText ();
_btnBack.Accepting += (s, e) => _history.Back ();
- _btnForward = new Button { X = Pos.Right (_btnBack) + 1, Y = 1, NoPadding = true };
+ _btnForward = new() { X = Pos.Right (_btnBack) + 1, Y = 1, NoPadding = true };
_btnForward.Text = GetForwardButtonText ();
_btnForward.Accepting += (s, e) => _history.Forward ();
- _tbPath = new TextField { Width = Dim.Fill (), CaptionColor = new Color (Color.Black) };
+ _tbPath = new() { Width = Dim.Fill (), CaptionColor = new (Color.Black) };
_tbPath.KeyDown += (s, k) =>
{
@@ -134,12 +135,12 @@ public class FileDialog : Dialog, IDesignable
_tbPath.Autocomplete = new AppendAutocomplete (_tbPath);
_tbPath.Autocomplete.SuggestionGenerator = new FilepathSuggestionGenerator ();
- _splitContainer = new TileView
+ _splitContainer = new()
{
X = 0,
Y = Pos.Bottom (_btnBack),
Width = Dim.Fill (),
- Height = Dim.Fill (Dim.Func (() => IsInitialized ? _btnOk.Frame.Height : 1)),
+ Height = Dim.Fill (Dim.Func (() => IsInitialized ? _btnOk.Frame.Height : 1))
};
Initialized += (s, e) =>
@@ -150,7 +151,7 @@ public class FileDialog : Dialog, IDesignable
// this.splitContainer.Border.BorderStyle = BorderStyle.None;
- _tableView = new TableView
+ _tableView = new()
{
Width = Dim.Fill (),
Height = Dim.Fill (),
@@ -178,7 +179,7 @@ public class FileDialog : Dialog, IDesignable
typeStyle.MinWidth = 6;
typeStyle.ColorGetter = ColorGetter;
- _treeView = new TreeView { Width = Dim.Fill (), Height = Dim.Fill () };
+ _treeView = new() { Width = Dim.Fill (), Height = Dim.Fill () };
var fileDialogTreeBuilder = new FileSystemTreeBuilder ();
_treeView.TreeBuilder = fileDialogTreeBuilder;
@@ -190,31 +191,33 @@ public class FileDialog : Dialog, IDesignable
_splitContainer.Tiles.ElementAt (0).ContentView.Add (_treeView);
_splitContainer.Tiles.ElementAt (1).ContentView.Add (_tableView);
- _btnToggleSplitterCollapse = new Button
+ _btnToggleSplitterCollapse = new()
{
X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
Y = Pos.AnchorEnd (), Text = GetToggleSplitterText (false)
};
_btnToggleSplitterCollapse.Accepting += (s, e) =>
- {
- Tile tile = _splitContainer.Tiles.ElementAt (0);
+ {
+ Tile tile = _splitContainer.Tiles.ElementAt (0);
- bool newState = !tile.ContentView.Visible;
- tile.ContentView.Visible = newState;
- _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState);
- SetNeedsLayout ();
- };
+ bool newState = !tile.ContentView.Visible;
+ tile.ContentView.Visible = newState;
+ _btnToggleSplitterCollapse.Text = GetToggleSplitterText (newState);
+ SetNeedsLayout ();
+ };
- _tbFind = new TextField
+ _tbFind = new()
{
X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput),
- CaptionColor = new Color (Color.Black),
+ CaptionColor = new (Color.Black),
Width = 30,
Y = Pos.Top (_btnToggleSplitterCollapse),
HotKey = Key.F.WithAlt
};
- _spinnerView = new SpinnerView { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput), Y = Pos.AnchorEnd (1), Visible = false };
+
+ _spinnerView = new()
+ { X = Pos.Align (Alignment.Start, AlignmentModes.AddSpaceBetweenItems, alignmentGroupInput), Y = Pos.AnchorEnd (1), Visible = false };
_tbFind.TextChanged += (s, o) => RestartSearch ();
@@ -242,7 +245,7 @@ public class FileDialog : Dialog, IDesignable
_tableView.Style.ShowHorizontalHeaderUnderline = true;
_tableView.Style.ShowHorizontalScrollIndicators = true;
- _history = new FileDialogHistory (this);
+ _history = new (this);
_tbPath.TextChanged += (s, e) => PathChanged ();
@@ -398,10 +401,10 @@ public class FileDialog : Dialog, IDesignable
Move (0, Viewport.Height / 2);
- SetAttribute (new Attribute (Color.Red, ColorScheme.Normal.Background));
- Driver.AddStr (new string (' ', feedbackPadLeft));
+ SetAttribute (new (Color.Red, ColorScheme.Normal.Background));
+ Driver.AddStr (new (' ', feedbackPadLeft));
Driver.AddStr (_feedback);
- Driver.AddStr (new string (' ', feedbackPadRight));
+ Driver.AddStr (new (' ', feedbackPadRight));
}
return true;
@@ -430,9 +433,9 @@ public class FileDialog : Dialog, IDesignable
_tbPath.Caption = Style.PathCaption;
_tbFind.Caption = Style.SearchCaption;
- _tbPath.Autocomplete.ColorScheme = new ColorScheme (_tbPath.ColorScheme)
+ _tbPath.Autocomplete.ColorScheme = new (_tbPath.ColorScheme)
{
- Normal = new Attribute (Color.Black, _tbPath.ColorScheme.Normal.Background)
+ Normal = new (Color.Black, _tbPath.ColorScheme.Normal.Background)
};
_treeRoots = Style.TreeRootGetter ();
@@ -449,18 +452,18 @@ public class FileDialog : Dialog, IDesignable
// Fiddle factor
int width = AllowedTypes.Max (a => a.ToString ().Length) + 6;
- _allowedTypeMenu = new MenuBarItem (
- "",
- _allowedTypeMenuItems = AllowedTypes.Select (
- (a, i) => new MenuItem (
- a.ToString (),
- null,
- () => { AllowedTypeMenuClicked (i); })
- )
- .ToArray ()
- );
+ _allowedTypeMenu = new (
+ "",
+ _allowedTypeMenuItems = AllowedTypes.Select (
+ (a, i) => new MenuItem (
+ a.ToString (),
+ null,
+ () => { AllowedTypeMenuClicked (i); })
+ )
+ .ToArray ()
+ );
- _allowedTypeMenuBar = new MenuBar
+ _allowedTypeMenuBar = new()
{
Width = width,
Y = 1,
@@ -476,10 +479,10 @@ public class FileDialog : Dialog, IDesignable
// TODO: Using v1's menu bar here is a hack. Need to upgrade this.
_allowedTypeMenuBar.DrawingContent += (s, e) =>
- {
- _allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
- Driver.AddRune (Glyphs.DownArrow);
- };
+ {
+ _allowedTypeMenuBar.Move (e.NewViewport.Width - 1, 0);
+ Driver.AddRune (Glyphs.DownArrow);
+ };
Add (_allowedTypeMenuBar);
}
@@ -803,12 +806,12 @@ public class FileDialog : Dialog, IDesignable
var black = new Color (Color.Black);
// TODO: Add some kind of cache for this
- return new ColorScheme
+ return new()
{
- Normal = new Attribute (color, black),
- HotNormal = new Attribute (color, black),
- Focus = new Attribute (black, color),
- HotFocus = new Attribute (black, color)
+ Normal = new (color, black),
+ HotNormal = new (color, black),
+ Focus = new (black, color),
+ HotFocus = new (black, color)
};
}
@@ -895,7 +898,7 @@ public class FileDialog : Dialog, IDesignable
private string GetToggleSplitterText (bool isExpanded)
{
return isExpanded
- ? new string ((char)Glyphs.LeftArrow.Value, 2)
+ ? new ((char)Glyphs.LeftArrow.Value, 2)
: new string ((char)Glyphs.RightArrow.Value, 2);
}
@@ -1225,49 +1228,48 @@ public class FileDialog : Dialog, IDesignable
return;
}
- var contextMenu = new ContextMenu
- {
- Position = new Point (e.Position.X + 1, e.Position.Y + 1)
- };
+ PopoverMenu? contextMenu = new (
+ [
+ new (Strings.fdCtxNew, string.Empty, New),
+ new (Strings.fdCtxRename, string.Empty, Rename),
+ new (Strings.fdCtxDelete, string.Empty, Delete)
+ ]);
- 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 (menuItems);
+ // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused
+ // and the context menu is disposed when it is closed.
+ Application.Popover?.Register (contextMenu);
+
+ contextMenu?.MakeVisible (e.ScreenPosition);
}
private void ShowHeaderContextMenu (int clickedCol, MouseEventArgs e)
{
string sort = GetProposedNewSortOrder (clickedCol, out bool isAsc);
- var contextMenu = new ContextMenu
- {
- Position = new Point (e.Position.X + 1, e.Position.Y + 1)
- };
+ PopoverMenu? contextMenu = new (
+ [
+ new (
+ string.Format (
+ Strings.fdCtxHide,
+ StripArrows (_tableView.Table.ColumnNames [clickedCol])
+ ),
+ string.Empty,
+ () => HideColumn (clickedCol)
+ ),
+ new (
+ StripArrows (sort),
+ string.Empty,
+ () => SortColumn (clickedCol, isAsc))
+ ]
+ );
- var menuItems = new MenuBarItem (
- [
- new MenuItem (
- string.Format (
- Strings.fdCtxHide,
- StripArrows (_tableView.Table.ColumnNames [clickedCol])
- ),
- string.Empty,
- () => HideColumn (clickedCol)
- ),
- new MenuItem (
- StripArrows (sort),
- string.Empty,
- () => SortColumn (clickedCol, isAsc))
- ]
- );
- contextMenu.Show (menuItems);
+ // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused
+ // and the context menu is disposed when it is closed.
+ Application.Popover?.Register (contextMenu);
+
+ contextMenu?.MakeVisible (e.ScreenPosition);
}
private void SortColumn (int clickedCol)
@@ -1618,6 +1620,7 @@ public class FileDialog : Dialog, IDesignable
{
Modal = false;
OnLoaded ();
+
return true;
}
}
diff --git a/Terminal.Gui/Views/Menu/ContextMenu.cs b/Terminal.Gui/Views/Menu/ContextMenu.cs
deleted file mode 100644
index 90f06c675..000000000
--- a/Terminal.Gui/Views/Menu/ContextMenu.cs
+++ /dev/null
@@ -1,283 +0,0 @@
-#nullable enable
-
-namespace Terminal.Gui;
-
-///
-/// ContextMenu provides a pop-up menu that can be positioned anywhere within a . ContextMenu is
-/// analogous to and, once activated, works like a sub-menu of a (but
-/// can be positioned anywhere).
-///
-/// 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 to , this behavior can be
-/// changed such that all sub-menus are drawn within the ContextMenu frame.
-///
-///
-/// ContextMenus can be activated using the Shift-F10 key (by default; use the to change to
-/// another key).
-///
-///
-/// 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 Toplevel? _container;
- private Key _key = DefaultKey;
- private MouseFlags _mouseFlags = MouseFlags.Button3Clicked;
-
- /// Initializes a context menu with no menu items.
- public ContextMenu ()
- {
- if (IsShow)
- {
- Hide ();
- IsShow = false;
- }
- }
-
- /// The default shortcut key for activating the context menu.
- [SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
- public static Key DefaultKey { get; set; } = Key.F10.WithShift;
-
- ///
- /// 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 which means the context menu will be forced to the right. If
- /// set to , the context menu will be clipped on the left if x is less than zero.
- ///
- 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; }
-
- /// 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
- {
- get => _key;
- set
- {
- Key oldKey = _key;
- _key = value;
- KeyChanged?.Invoke (this, new KeyChangedEventArgs (oldKey, _key));
- }
- }
-
- /// Gets the that is hosting this context menu.
- public MenuBar? MenuBar => _menuBar;
-
- /// Gets or sets the menu items for this context menu.
- public MenuBarItem? MenuItems { get; private set; }
-
- /// specifies the mouse action used to activate the context menu by mouse.
- public MouseFlags MouseFlags
- {
- get => _mouseFlags;
- set
- {
- MouseFlags oldFlags = _mouseFlags;
- _mouseFlags = value;
- MouseFlagsChanged?.Invoke (this, new MouseFlagsChangedEventArgs (oldFlags, value));
- }
- }
-
- /// Gets or sets the menu position.
- public Point Position { get; set; }
-
- ///
- /// Gets or sets if sub-menus will be displayed using a "single frame" menu style. If , the
- /// ContextMenu and any sub-menus that would normally cascade will be displayed within a single frame. If
- /// (the default), sub-menus will cascade using separate frames for each level of the menu
- /// hierarchy.
- ///
- public bool UseSubMenusSingleFrame { get; set; }
-
- /// Disposes the context menu object.
- public void Dispose ()
- {
- if (_menuBar is { })
- {
- _menuBar.MenuAllClosed -= MenuBar_MenuAllClosed;
- _container?.Remove (_menuBar);
- }
- Application.UngrabMouse ();
- _menuBar?.Dispose ();
- _menuBar = null;
- IsShow = false;
-
- if (_container is { })
- {
- _container.Closing -= Container_Closing;
- _container.Deactivate -= Container_Deactivate;
- _container.Disposing -= Container_Disposing;
- }
- }
-
- /// Hides (closes) the ContextMenu.
- public void Hide ()
- {
- RemoveKeyBindings (MenuItems);
- _menuBar?.CleanUp ();
- IsShow = false;
- }
-
- private void RemoveKeyBindings (MenuBarItem? menuBarItem)
- {
- if (menuBarItem is null)
- {
- return;
- }
-
- foreach (MenuItem? 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?.HotKeyBindings.Remove (menuItem.ShortcutKey!);
- }
- }
- }
- }
-
- /// Event invoked when the is changed.
- public event EventHandler? KeyChanged;
-
- /// Event invoked when the is changed.
- public event EventHandler? MouseFlagsChanged;
-
- /// Shows (opens) the ContextMenu, displaying the s it contains.
- public void Show (MenuBarItem? menuItems)
- {
- if (_menuBar is { })
- {
- Hide ();
- Dispose ();
- }
-
- if (menuItems is null || menuItems.Children!.Length == 0)
- {
- return;
- }
-
- MenuItems = menuItems;
- _container = GetTopSuperView (Host);
- _container!.Closing += Container_Closing;
- _container.Deactivate += Container_Deactivate;
- _container.Disposing += Container_Disposing;
- Rectangle viewport = _container.Viewport;
- Point position = Position;
-
- if (Host is { })
- {
- Point pos = Host.Frame.Location;
- pos.Y += Host.Frame.Height > 0 ? Host.Frame.Height - 1 : 0;
-
- if (position != pos)
- {
- Position = position = pos;
- }
- }
-
- Rectangle rect = Menu.MakeFrame (position.X, position.Y, MenuItems.Children);
-
- if (rect.Right >= viewport.Right)
- {
- if (viewport.Right - rect.Width >= 0 || !ForceMinimumPosToZero)
- {
- position.X = viewport.Right - rect.Width;
- }
- else if (ForceMinimumPosToZero)
- {
- position.X = 0;
- }
- }
- else if (ForceMinimumPosToZero && position.X < 0)
- {
- position.X = 0;
- }
-
- if (rect.Bottom >= viewport.Bottom)
- {
- if (viewport.Bottom - rect.Height - 1 >= 0 || !ForceMinimumPosToZero)
- {
- if (Host is null)
- {
- position.Y = viewport.Bottom - rect.Height - 1;
- }
- else
- {
- Point pos = Host.Frame.Location;
- 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
- {
- X = position.X,
- Y = position.Y,
- Width = 0,
- Height = 0,
- UseSubMenusSingleFrame = UseSubMenusSingleFrame,
- Key = Key,
- Menus = [MenuItems]
- };
-
- _menuBar._isContextMenuLoading = true;
- _menuBar.MenuAllClosed += MenuBar_MenuAllClosed;
-
- _container.Add (_menuBar);
- IsShow = true;
- _menuBar.OpenMenu ();
- }
-
- internal static Toplevel? GetTopSuperView (View? view)
- {
- if (view is Toplevel toplevel)
- {
- return toplevel;
- }
-
- for (View? sv = view?.SuperView; sv != null; sv = sv.SuperView)
- {
- if (sv is Toplevel top)
- {
- return top;
- }
- }
-
- return (Toplevel?)view?.SuperView ?? Application.Top;
- }
-
- 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 (); }
-}
diff --git a/Terminal.Gui/Views/Menu/ContextMenuv2.cs b/Terminal.Gui/Views/Menu/ContextMenuv2.cs
deleted file mode 100644
index 994aec4a0..000000000
--- a/Terminal.Gui/Views/Menu/ContextMenuv2.cs
+++ /dev/null
@@ -1,104 +0,0 @@
-#nullable enable
-
-using System.Diagnostics;
-
-namespace Terminal.Gui;
-
-///
-/// ContextMenuv2 provides a Popover menu that can be positioned anywhere within a .
-///
-/// To show the ContextMenu, set to the ContextMenu object and set
-/// property to .
-///
-///
-/// The menu will be hidden when the user clicks outside the menu or when the user presses .
-///
-///
-/// To explicitly hide the menu, set property to .
-///
-///
-/// is the key used to activate the ContextMenus (Shift+F10 by default). Callers can use this in
-/// their keyboard handling code.
-///
-/// The menu will be displayed at the current mouse coordinates.
-///
-public class ContextMenuv2 : PopoverMenu, IDesignable
-{
-
- ///
- /// The mouse flags that will trigger the context menu. The default is which is typically the right mouse button.
- ///
- public MouseFlags MouseFlags { get; set; } = MouseFlags.Button3Clicked;
-
- /// Initializes a context menu with no menu items.
- public ContextMenuv2 () : this ([]) { }
-
- ///
- public ContextMenuv2 (Menuv2? menu) : base (menu)
- {
- Key = DefaultKey;
- }
-
- ///
- public ContextMenuv2 (IEnumerable? menuItems) : this (new Menuv2 (menuItems))
- {
- }
-
- private Key _key = DefaultKey;
-
- /// Specifies the key that will activate the context menu.
- public Key Key
- {
- get => _key;
- set
- {
- Key oldKey = _key;
- _key = value;
- KeyChanged?.Invoke (this, new KeyChangedEventArgs (oldKey, _key));
- }
- }
-
- /// Event raised when the is changed.
- public event EventHandler? KeyChanged;
-
- ///
- public bool EnableForDesign ()
- {
- var shortcut = new Shortcut
- {
- Text = "Quit",
- Title = "Q_uit",
- Key = Key.Z.WithCtrl,
- };
-
- Add (shortcut);
-
- shortcut = new Shortcut
- {
- Text = "Help Text",
- Title = "Help",
- Key = Key.F1,
- };
-
- Add (shortcut);
-
- shortcut = new Shortcut
- {
- Text = "Czech",
- CommandView = new CheckBox ()
- {
- Title = "_Check"
- },
- Key = Key.F9,
- CanFocus = false
- };
-
- Add (shortcut);
-
- // HACK: This enables All Views Tester to show the CM if DefaultKey is pressed
- AddCommand (Command.Context, () => Visible = true);
- HotKeyBindings.Add (DefaultKey, Command.Context);
-
- return true;
- }
-}
diff --git a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs b/Terminal.Gui/Views/Menu/MenuBarItemv2.cs
index 6fe9f121e..0ed8cdfcd 100644
--- a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs
+++ b/Terminal.Gui/Views/Menu/MenuBarItemv2.cs
@@ -74,7 +74,7 @@ public class MenuBarItemv2 : MenuItemv2
null,
Command.NotBound,
commandText,
- new (new (menuItems)))
+ new (menuItems))
{ }
// TODO: Hide base.SubMenu?
diff --git a/Terminal.Gui/Views/Menu/MenuBarv2.cs b/Terminal.Gui/Views/Menu/MenuBarv2.cs
index a129321d5..6ad4560eb 100644
--- a/Terminal.Gui/Views/Menu/MenuBarv2.cs
+++ b/Terminal.Gui/Views/Menu/MenuBarv2.cs
@@ -55,7 +55,7 @@ public class MenuBarv2 : Menuv2, IDesignable
Border.LineStyle = LineStyle.None;
}
- // TODO: This needs to be done whenever a menuitem in any memubaritem changes
+ // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes
foreach (MenuBarItemv2? mbi in SubViews.Select(s => s as MenuBarItemv2))
{
Application.Popover?.Register (mbi?.PopoverMenu);
@@ -86,7 +86,7 @@ public class MenuBarv2 : Menuv2, IDesignable
&& Application.Popover?.GetActivePopover () is PopoverMenu popoverMenu
&& popoverMenu?.Root?.SuperMenuItem?.SuperView == this)
{
- Application.Popover?.HidePopover (popoverMenu);
+ Application.Popover?.Hide (popoverMenu);
}
menuBarItem?.PopoverMenu?.MakeVisible (new Point (menuBarItem.FrameToScreen ().X, menuBarItem.FrameToScreen ().Bottom));
@@ -167,176 +167,6 @@ public class MenuBarv2 : Menuv2, IDesignable
]
)
);
-
- // if (context is not Func actionFn)
- // {
- // actionFn = (_) => true;
- // }
-
- // View? targetView = context as View;
-
- // Add (new MenuItemv2 (targetView,
- // Command.NotBound,
- // "_File",
- // new MenuItem []
- // {
- // new (
- // "_New",
- // "",
- // () => actionFn ("New"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.N
- // ),
- // new (
- // "_Open",
- // "",
- // () => actionFn ("Open"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.O
- // ),
- // new (
- // "_Save",
- // "",
- // () => actionFn ("Save"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.S
- // ),
- //#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type.
- // null,
- //#pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type.
-
- // // Don't use Application.Quit so we can disambiguate between quitting and closing the toplevel
- // new (
- // "_Quit",
- // "",
- // () => actionFn ("Quit"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.Q
- // )
- // }
- // ),
- // new MenuBarItem (
- // "_Edit",
- // new MenuItem []
- // {
- // new (
- // "_Copy",
- // "",
- // () => actionFn ("Copy"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.C
- // ),
- // new (
- // "C_ut",
- // "",
- // () => actionFn ("Cut"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.X
- // ),
- // new (
- // "_Paste",
- // "",
- // () => actionFn ("Paste"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.V
- // ),
- // new MenuBarItem (
- // "_Find and Replace",
- // new MenuItem []
- // {
- // new (
- // "F_ind",
- // "",
- // () => actionFn ("Find"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.F
- // ),
- // new (
- // "_Replace",
- // "",
- // () => actionFn ("Replace"),
- // null,
- // null,
- // KeyCode.CtrlMask | KeyCode.H
- // ),
- // new MenuBarItem (
- // "_3rd Level",
- // new MenuItem []
- // {
- // new (
- // "_1st",
- // "",
- // () => actionFn (
- // "1"
- // ),
- // null,
- // null,
- // KeyCode.F1
- // ),
- // new (
- // "_2nd",
- // "",
- // () => actionFn (
- // "2"
- // ),
- // null,
- // null,
- // KeyCode.F2
- // )
- // }
- // ),
- // new MenuBarItem (
- // "_4th Level",
- // new MenuItem []
- // {
- // new (
- // "_5th",
- // "",
- // () => actionFn (
- // "5"
- // ),
- // null,
- // null,
- // KeyCode.CtrlMask
- // | KeyCode.D5
- // ),
- // new (
- // "_6th",
- // "",
- // () => actionFn (
- // "6"
- // ),
- // null,
- // null,
- // KeyCode.CtrlMask
- // | KeyCode.D6
- // )
- // }
- // )
- // }
- // ),
- // new (
- // "_Select All",
- // "",
- // () => actionFn ("Select All"),
- // null,
- // null,
- // KeyCode.CtrlMask
- // | KeyCode.ShiftMask
- // | KeyCode.S
- // )
- // }
- // ),
- // new MenuBarItem ("_About", "Top-Level", () => actionFn ("About"))
- // ];
return true;
}
}
diff --git a/Terminal.Gui/Views/Menu/MenuItemv2.cs b/Terminal.Gui/Views/Menu/MenuItemv2.cs
index acf031127..f655d0b86 100644
--- a/Terminal.Gui/Views/Menu/MenuItemv2.cs
+++ b/Terminal.Gui/Views/Menu/MenuItemv2.cs
@@ -6,7 +6,7 @@ using Terminal.Gui.Resources;
namespace Terminal.Gui;
///
-/// A -dervied object to be used as a menu item in a . Has title, an
+/// A -derived object to be used as a menu item in a . Has title, an
/// associated help text, and an action to execute on activation.
///
public class MenuItemv2 : Shortcut
@@ -45,7 +45,18 @@ public class MenuItemv2 : Shortcut
{
TargetView = targetView;
Command = command;
+ SubMenu = subMenu;
+ }
+ ///
+ public MenuItemv2 (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null)
+ : base (key ?? Key.Empty, commandText, action, helpText)
+ { }
+
+ ///
+ public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null)
+ : base (Key.Empty, commandText, null, helpText)
+ {
SubMenu = subMenu;
}
@@ -108,7 +119,7 @@ public class MenuItemv2 : Shortcut
ret = base.DispatchCommand (commandContext);
}
- Logging.Trace ($"{commandContext?.Source?.Title}");
+ //Logging.Trace ($"{commandContext?.Source?.Title}");
RaiseAccepted (commandContext);
@@ -150,7 +161,7 @@ public class MenuItemv2 : Shortcut
// TODO: Consider moving Accepted to Shortcut?
///
- /// Riases the / event indicating this item (or submenu)
+ /// Raises the / event indicating this item (or submenu)
/// was accepted. This is used to determine when to hide the menu.
///
///
@@ -167,7 +178,7 @@ public class MenuItemv2 : Shortcut
}
///
- /// Called when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the
+ /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
/// menu.
///
///
@@ -176,7 +187,7 @@ public class MenuItemv2 : Shortcut
protected virtual void OnAccepted (CommandEventArgs args) { }
///
- /// Raised when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the
+ /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the
/// menu.
///
///
diff --git a/Terminal.Gui/Views/Menu/Menuv2.cs b/Terminal.Gui/Views/Menu/Menuv2.cs
index 88df61b67..269acf4e4 100644
--- a/Terminal.Gui/Views/Menu/Menuv2.cs
+++ b/Terminal.Gui/Views/Menu/Menuv2.cs
@@ -2,13 +2,16 @@
namespace Terminal.Gui;
///
-/// A -derived object to be used as a verticaly-oriented menu. Each subview is a .
+/// A -derived object to be used as a vertically-oriented menu. Each subview is a .
///
public class Menuv2 : Bar
{
///
public Menuv2 () : this ([]) { }
+ ///
+ public Menuv2 (IEnumerable? shortcuts) : this (shortcuts?.Cast()) { }
+
///
public Menuv2 (IEnumerable? shortcuts) : base (shortcuts)
{
@@ -18,7 +21,6 @@ public class Menuv2 : Bar
Border!.Thickness = new Thickness (1, 1, 1, 1);
Border.LineStyle = LineStyle.Single;
-
}
///
@@ -50,52 +52,44 @@ public class Menuv2 : Bar
{
base.OnSubViewAdded (view);
- if (view is MenuItemv2 menuItem)
+ switch (view)
{
- menuItem.CanFocus = true;
-
- AddCommand (menuItem.Command, RaiseAccepted);
-
- menuItem.Selecting += MenuItemOnSelecting;
- menuItem.Accepting += MenuItemOnAccepting;
- menuItem.Accepted += MenuItemOnAccepted;
-
- void MenuItemOnSelecting (object? sender, CommandEventArgs e)
+ case MenuItemv2 menuItem:
{
- Logging.Trace ($"Selecting: {e.Context?.Source?.Title}");
- }
+ menuItem.CanFocus = true;
- void MenuItemOnAccepting (object? sender, CommandEventArgs e)
- {
- Logging.Trace ($"Accepting: {e.Context?.Source?.Title}");
- }
+ AddCommand (menuItem.Command, RaiseAccepted);
- void MenuItemOnAccepted (object? sender, CommandEventArgs e)
- {
- Logging.Trace ($"Accepted: {e.Context?.Source?.Title}");
- RaiseAccepted (e.Context);
- }
- }
+ menuItem.Accepted += MenuItemOnAccepted;
- if (view is Line line)
- {
- // Grow line so we get autojoin line
- line.X = Pos.Func (() => -Border!.Thickness.Left);
- line.Width = Dim.Fill ()! + Dim.Func (() => Border!.Thickness.Right);
+ break;
+
+ void MenuItemOnAccepted (object? sender, CommandEventArgs e)
+ {
+ //Logging.Trace ($"Accepted: {e.Context?.Source?.Title}");
+ RaiseAccepted (e.Context);
+ }
+ }
+ case Line line:
+ // Grow line so we get auto-join line
+ line.X = Pos.Func (() => -Border!.Thickness.Left);
+ line.Width = Dim.Fill ()! + Dim.Func (() => Border!.Thickness.Right);
+
+ break;
}
}
// TODO: Consider moving Accepted to Bar?
///
- /// Riases the / event indicating an item in this menu (or submenu)
+ /// Raises the / event indicating an item in this menu (or submenu)
/// was accepted. This is used to determine when to hide the menu.
///
///
///
protected bool? RaiseAccepted (ICommandContext? ctx)
{
- Logging.Trace ($"RaiseAccepted: {ctx}");
+ //Logging.Trace ($"RaiseAccepted: {ctx}");
CommandEventArgs args = new () { Context = ctx };
OnAccepted (args);
@@ -105,7 +99,7 @@ public class Menuv2 : Bar
}
///
- /// Called when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the menu.
+ /// Called when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
///
///
///
@@ -113,7 +107,7 @@ public class Menuv2 : Bar
protected virtual void OnAccepted (CommandEventArgs args) { }
///
- /// Raised when the user has accepted an item in this menu (or submenu. This is used to determine when to hide the menu.
+ /// Raised when the user has accepted an item in this menu (or submenu). This is used to determine when to hide the menu.
///
///
///
@@ -157,7 +151,7 @@ public class Menuv2 : Bar
}
///
- /// Called when the the selected menu item has changed.
+ /// Called when the selected menu item has changed.
///
///
protected virtual void OnSelectedMenuItemChanged (MenuItemv2? selected)
diff --git a/Terminal.Gui/Views/Menu/PopoverMenu.cs b/Terminal.Gui/Views/Menu/PopoverMenu.cs
index 804c1a9a3..555dd9175 100644
--- a/Terminal.Gui/Views/Menu/PopoverMenu.cs
+++ b/Terminal.Gui/Views/Menu/PopoverMenu.cs
@@ -1,24 +1,38 @@
#nullable enable
-using System.Diagnostics;
-
namespace Terminal.Gui;
///
-/// Provides a cascading popover menu.
+/// Provides a cascading menu that pops over all other content. Can be used as a context menu or a drop-down
+/// menu as part of .
///
-public class PopoverMenu : PopoverBaseImpl
+///
+///
+/// To use as a context menu, register the popover menu with and call
+/// .
+///
+///
+public class PopoverMenu : PopoverBaseImpl, IDesignable
{
///
/// Initializes a new instance of the class.
///
- public PopoverMenu () : this (null) { }
+ public PopoverMenu () : this ((Menuv2?)null) { }
+
+ ///
+ public PopoverMenu (IEnumerable? menuItems) : this (new Menuv2 (menuItems)) { }
+
+ ///
+ public PopoverMenu (IEnumerable? menuItems) : this (new Menuv2 (menuItems)) { }
///
/// Initializes a new instance of the class with the specified root .
///
public PopoverMenu (Menuv2? root)
{
+ Key = DefaultKey;
+
base.Visible = false;
+
//base.ColorScheme = Colors.ColorSchemes ["Menu"];
Root = root;
@@ -77,11 +91,6 @@ public class PopoverMenu : PopoverBaseImpl
bool? MoveRight (ICommandContext? ctx)
{
- if (Focused == Root)
- {
- return false;
- }
-
if (MostFocused is MenuItemv2 { SubMenu.Visible: true } focused)
{
focused.SubMenu.SetFocus ();
@@ -93,18 +102,36 @@ public class PopoverMenu : PopoverBaseImpl
}
}
- ///
- /// The mouse flags that will cause the popover menu to be visible. The default is
- /// which is typically the right mouse button.
- ///
- public static MouseFlags MouseFlags { get; set; } = MouseFlags.Button3Clicked;
+ private Key _key = DefaultKey;
+
+ /// Specifies the key that will activate the context menu.
+ public Key Key
+ {
+ get => _key;
+ set
+ {
+ Key oldKey = _key;
+ _key = value;
+ KeyChanged?.Invoke (this, new (oldKey, _key));
+ }
+ }
+
+ /// Raised when is changed.
+ public event EventHandler? KeyChanged;
/// The default key for activating popover menus.
[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
public static Key DefaultKey { get; set; } = Key.F10.WithShift;
///
- /// Makes the popover menu visible and locates it at . The actual position of the menu
+ /// The mouse flags that will cause the popover menu to be visible. The default is
+ /// which is typically the right mouse button.
+ ///
+ public MouseFlags MouseFlags { get; set; } = MouseFlags.Button3Clicked;
+
+ ///
+ /// Makes the popover menu visible and locates it at . The actual position of the
+ /// menu
/// will be adjusted to
/// ensure the menu fully fits on the screen, and the mouse cursor is over the first cell of the
/// first MenuItem.
@@ -114,11 +141,12 @@ public class PopoverMenu : PopoverBaseImpl
{
UpdateKeyBindings ();
SetPosition (idealScreenPosition);
- Application.Popover?.ShowPopover (this);
+ Application.Popover?.Show (this);
}
///
- /// Locates the popover menu at . The actual position of the menu will be adjusted to
+ /// Locates the popover menu at . The actual position of the menu will be
+ /// adjusted to
/// ensure the menu fully fits on the screen, and the mouse cursor is over the first cell of the
/// first MenuItem (if possible).
///
@@ -127,21 +155,24 @@ public class PopoverMenu : PopoverBaseImpl
{
idealScreenPosition ??= Application.GetLastMousePosition ();
- if (idealScreenPosition is { } && Root is { })
+ if (idealScreenPosition is null || Root is null)
{
- Point pos = idealScreenPosition.Value;
-
- if (!Root.IsInitialized)
- {
- Root.BeginInit();
- Root.EndInit ();
- Root.Layout ();
- }
- pos = GetMostVisibleLocationForSubMenu (Root, pos);
-
- Root.X = pos.X;
- Root.Y = pos.Y;
+ return;
}
+
+ Point pos = idealScreenPosition.Value;
+
+ if (!Root.IsInitialized)
+ {
+ Root.BeginInit ();
+ Root.EndInit ();
+ Root.Layout ();
+ }
+
+ pos = GetMostVisibleLocationForSubMenu (Root, pos);
+
+ Root.X = pos.X;
+ Root.Y = pos.Y;
}
///
@@ -156,7 +187,7 @@ public class PopoverMenu : PopoverBaseImpl
else
{
HideAndRemoveSubMenu (_root);
- Application.Popover?.HidePopover (this);
+ Application.Popover?.Hide (this);
}
}
@@ -208,48 +239,34 @@ public class PopoverMenu : PopoverBaseImpl
// TODO: And it needs to clear them first
IEnumerable all = GetMenuItemsOfAllSubMenus ();
- foreach (MenuItemv2 menuItem in all.Where(mi => mi.Command != Command.NotBound))
+ foreach (MenuItemv2 menuItem in all.Where (mi => mi.Command != Command.NotBound))
{
+ Key? key;
if (menuItem.TargetView is { })
{
// A TargetView implies HotKey
- // Automatically set MenuItem.Key
- Key? key = menuItem.TargetView.HotKeyBindings.GetFirstFromCommands (menuItem.Command);
-
- if (key is { IsValid: true })
- {
- if (menuItem.Key.IsValid)
- {
- //Logging.Warning ("Do not specify a Key for MenuItems where a Command is specified. Key will be determined automatically.");
- }
-
- menuItem.Key = key;
- Logging.Trace ($"HotKey: {menuItem.Key}->{menuItem.Command}");
- }
+ key = menuItem.TargetView.HotKeyBindings.GetFirstFromCommands (menuItem.Command);
}
else
{
// No TargetView implies Application HotKey
- Key? key = Application.KeyBindings.GetFirstFromCommands (menuItem.Command);
-
- if (key is { IsValid: true })
- {
- if (menuItem.Key.IsValid)
- {
- // Logging.Warning ("App HotKey: Do not specify a Key for MenuItems where a Command is specified. Key will be determined automatically.");
- }
-
- menuItem.Key = key;
- Logging.Trace ($"App HotKey: {menuItem.Key}->{menuItem.Command}");
- }
+ key = Application.KeyBindings.GetFirstFromCommands (menuItem.Command);
}
+
+ if (key is not { IsValid: true })
+ {
+ continue;
+ }
+
+ if (menuItem.Key.IsValid)
+ {
+ //Logging.Warning ("Do not specify a Key for MenuItems where a Command is specified. Key will be determined automatically.");
+ }
+
+ menuItem.Key = key;
+
+ //Logging.Trace ($"HotKey: {menuItem.Key}->{menuItem.Command}");
}
-
- foreach (MenuItemv2 menuItem in all.Where (mi => mi is { Command: Command.NotBound, Key.IsValid: true }))
- {
-
- }
-
}
///
@@ -332,14 +349,9 @@ public class PopoverMenu : PopoverBaseImpl
{
var menu = menuItem?.SuperView as Menuv2;
- if (menu is { })
- {
- menu.Layout ();
- }
+ menu?.Layout ();
+
// If there's a visible peer, remove / hide it
-
- // Debug.Assert (menu is null || menu?.SubViews.Count (v => v is MenuItemv2 { SubMenu.Visible: true }) < 2);
-
if (menu?.SubViews.FirstOrDefault (v => v is MenuItemv2 { SubMenu.Visible: true }) is MenuItemv2 visiblePeer)
{
HideAndRemoveSubMenu (visiblePeer.SubMenu);
@@ -391,7 +403,7 @@ public class PopoverMenu : PopoverBaseImpl
// TODO: Find the menu item below the mouse, if any, and select it
// TODO: Enable No Border menu style
- menu.Border.LineStyle = LineStyle.Single;
+ menu.Border!.LineStyle = LineStyle.Single;
menu.Border.Thickness = new (1);
if (!menu.IsInitialized)
@@ -403,7 +415,6 @@ public class PopoverMenu : PopoverBaseImpl
menu.ClearFocus ();
base.Add (menu);
-
// IMPORTANT: This must be done after adding the menu to the super view or Add will try
// to set focus to it.
menu.Visible = true;
@@ -417,8 +428,6 @@ public class PopoverMenu : PopoverBaseImpl
if (menu is { Visible: true })
{
// If there's a visible submenu, remove / hide it
- // Debug.Assert (menu.SubViews.Count (v => v is MenuItemv2 { SubMenu.Visible: true }) <= 1);
-
if (menu.SubViews.FirstOrDefault (v => v is MenuItemv2 { SubMenu.Visible: true }) is MenuItemv2 visiblePeer)
{
HideAndRemoveSubMenu (visiblePeer.SubMenu);
@@ -448,12 +457,12 @@ public class PopoverMenu : PopoverBaseImpl
e.Cancel = true;
}
- Logging.Trace ($"{e.Context?.Source?.Title}");
+ //Logging.Trace ($"{e.Context?.Source?.Title}");
}
private void MenuAccepted (object? sender, CommandEventArgs e)
{
- Logging.Trace ($"{e.Context?.Source?.Title}");
+ //Logging.Trace ($"{e.Context?.Source?.Title}");
if (e.Context?.Source is MenuItemv2 { SubMenu: null })
{
@@ -467,14 +476,14 @@ public class PopoverMenu : PopoverBaseImpl
}
///
- /// Riases the / event indicating a menu (or submenu)
+ /// Raises the / event indicating a menu (or submenu)
/// was accepted and the Menus in the PopoverMenu were hidden. Use this to determine when to hide the PopoverMenu.
///
///
///
protected bool? RaiseAccepted (ICommandContext? ctx)
{
- Logging.Trace ($"RaiseAccepted: {ctx}");
+ //Logging.Trace ($"RaiseAccepted: {ctx}");
CommandEventArgs args = new () { Context = ctx };
OnAccepted (args);
@@ -529,4 +538,21 @@ public class PopoverMenu : PopoverBaseImpl
base.Dispose (disposing);
}
+
+
+ ///
+ public bool EnableForDesign (ref readonly TContext context) where TContext : notnull
+ {
+ Root = new Menuv2 (
+ [
+ new MenuItemv2 (this, Command.Cut),
+ new MenuItemv2 (this, Command.Copy),
+ new MenuItemv2 (this, Command.Paste),
+ new Line (),
+ new MenuItemv2 (this, Command.SelectAll)
+ ]);
+
+ Visible = true;
+ return true;
+ }
}
diff --git a/Terminal.Gui/Views/Menu/Menu.cs b/Terminal.Gui/Views/Menuv1/Menu.cs
similarity index 99%
rename from Terminal.Gui/Views/Menu/Menu.cs
rename to Terminal.Gui/Views/Menuv1/Menu.cs
index b7d14d3f0..a4d241140 100644
--- a/Terminal.Gui/Views/Menu/Menu.cs
+++ b/Terminal.Gui/Views/Menuv1/Menu.cs
@@ -3,8 +3,7 @@
namespace Terminal.Gui;
///
-/// An internal class used to represent a menu pop-up menu. Created and managed by and
-/// .
+/// An internal class used to represent a menu pop-up menu. Created and managed by .
///
internal sealed class Menu : View
{
diff --git a/Terminal.Gui/Views/Menu/MenuBar.cs b/Terminal.Gui/Views/Menuv1/MenuBar.cs
similarity index 99%
rename from Terminal.Gui/Views/Menu/MenuBar.cs
rename to Terminal.Gui/Views/Menuv1/MenuBar.cs
index 38d5dc778..bfde40c1b 100644
--- a/Terminal.Gui/Views/Menu/MenuBar.cs
+++ b/Terminal.Gui/Views/Menuv1/MenuBar.cs
@@ -17,7 +17,6 @@ namespace Terminal.Gui;
/// The appears on the first row of the SuperView and uses the full
/// width.
///
-/// See also:
/// The provides global hot keys for the application. See .
///
/// When the menu is created key bindings for each menu item and its sub-menu items are added for each menu
diff --git a/Terminal.Gui/Views/Menu/MenuBarItem.cs b/Terminal.Gui/Views/Menuv1/MenuBarItem.cs
similarity index 100%
rename from Terminal.Gui/Views/Menu/MenuBarItem.cs
rename to Terminal.Gui/Views/Menuv1/MenuBarItem.cs
diff --git a/Terminal.Gui/Views/Menu/MenuClosingEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs
similarity index 100%
rename from Terminal.Gui/Views/Menu/MenuClosingEventArgs.cs
rename to Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs
diff --git a/Terminal.Gui/Views/Menu/MenuItem.cs b/Terminal.Gui/Views/Menuv1/MenuItem.cs
similarity index 99%
rename from Terminal.Gui/Views/Menu/MenuItem.cs
rename to Terminal.Gui/Views/Menuv1/MenuItem.cs
index 7a222ebca..d5dd714bc 100644
--- a/Terminal.Gui/Views/Menu/MenuItem.cs
+++ b/Terminal.Gui/Views/Menuv1/MenuItem.cs
@@ -255,7 +255,7 @@ public class MenuItem
///
/// Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the
- /// that is the parent of the or this
+ /// that is the parent of the this
/// .
///
/// The will be drawn on the MenuItem to the right of the and
diff --git a/Terminal.Gui/Views/Menu/MenuItemCheckStyle.cs b/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs
similarity index 100%
rename from Terminal.Gui/Views/Menu/MenuItemCheckStyle.cs
rename to Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs
diff --git a/Terminal.Gui/Views/Menu/MenuOpenedEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs
similarity index 100%
rename from Terminal.Gui/Views/Menu/MenuOpenedEventArgs.cs
rename to Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs
diff --git a/Terminal.Gui/Views/Menu/MenuOpeningEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs
similarity index 100%
rename from Terminal.Gui/Views/Menu/MenuOpeningEventArgs.cs
rename to Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index 604e34e28..a6e5d58c1 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -418,9 +418,9 @@ public class TextField : View
/// Gets or sets the foreground to use when rendering .
public Color CaptionColor { get; set; }
- /// Get the for this view.
+ /// Get the Context Menu for this view.
[CanBeNull]
- public ContextMenuv2 ContextMenu { get; private set; }
+ public PopoverMenu ContextMenu { get; private set; }
/// Sets or gets the current cursor position.
public virtual int CursorPosition
@@ -800,7 +800,7 @@ public class TextField : View
&& !ev.Flags.HasFlag (MouseFlags.ReportMousePosition)
&& !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
&& !ev.Flags.HasFlag (MouseFlags.Button1TripleClicked)
- && !ev.Flags.HasFlag (PopoverMenu.MouseFlags))
+ && !ev.Flags.HasFlag (ContextMenu!.MouseFlags))
{
return false;
}
@@ -900,7 +900,7 @@ public class TextField : View
ClearAllSelection ();
PrepareSelection (0, _text.Count);
}
- else if (ev.Flags == PopoverMenu.MouseFlags)
+ else if (ev.Flags == ContextMenu!.MouseFlags)
{
PositionCursor (ev);
ShowContextMenu (false);
@@ -1226,7 +1226,7 @@ public class TextField : View
private void CreateContextMenu ()
{
DisposeContextMenu ();
- ContextMenuv2 menu = new (new List ()
+ PopoverMenu menu = new (new List ()
{
new (this, Command.SelectAll, Strings.ctxSelectAll),
new (this, Command.DeleteAll, Strings.ctxDeleteAll),
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index f89c6aeab..c3c768ad6 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -2490,8 +2490,8 @@ public class TextView : View
///
public IAutocomplete Autocomplete { get; protected set; } = new TextViewAutocomplete ();
- /// Get the for this view.
- public ContextMenuv2? ContextMenu { get; private set; }
+ /// Get the Context Menu.
+ public PopoverMenu? ContextMenu { get; private set; }
/// Gets the cursor column.
/// The cursor column.
@@ -4148,17 +4148,17 @@ public class TextView : View
private void AppendClipboard (string text) { Clipboard.Contents += text; }
- private ContextMenuv2 CreateContextMenu ()
+ private PopoverMenu CreateContextMenu ()
{
- ContextMenuv2 menu = new (new List ()
+ PopoverMenu menu = new (new List ()
{
- new (this, Command.SelectAll, Strings.ctxSelectAll),
- new (this, Command.DeleteAll, Strings.ctxDeleteAll),
- new (this, Command.Copy, Strings.ctxCopy),
- new (this, Command.Cut, Strings.ctxCut),
- new (this, Command.Paste, Strings.ctxPaste),
- new (this, Command.Undo, Strings.ctxUndo),
- new (this, Command.Redo, Strings.ctxRedo),
+ new MenuItemv2 (this, Command.SelectAll, Strings.ctxSelectAll),
+ new MenuItemv2 (this, Command.DeleteAll, Strings.ctxDeleteAll),
+ new MenuItemv2 (this, Command.Copy, Strings.ctxCopy),
+ new MenuItemv2 (this, Command.Cut, Strings.ctxCut),
+ new MenuItemv2 (this, Command.Paste, Strings.ctxPaste),
+ new MenuItemv2 (this, Command.Undo, Strings.ctxUndo),
+ new MenuItemv2 (this, Command.Redo, Strings.ctxRedo),
});
menu.KeyChanged += ContextMenu_KeyChanged;
diff --git a/TerminalGuiFluentTesting/GuiTestContext.cs b/TerminalGuiFluentTesting/GuiTestContext.cs
index 9a1195df8..bd3f8d974 100644
--- a/TerminalGuiFluentTesting/GuiTestContext.cs
+++ b/TerminalGuiFluentTesting/GuiTestContext.cs
@@ -57,7 +57,7 @@ public class GuiTestContext : IDisposable
.CreateLogger ("Test Logging");
Logging.Logger = logger;
- v2.Init (null, GetDriverName());
+ v2.Init (null, GetDriverName ());
booting.Release ();
@@ -93,12 +93,12 @@ public class GuiTestContext : IDisposable
private string GetDriverName ()
{
return _driver switch
- {
- V2TestDriver.V2Win => "v2win",
- V2TestDriver.V2Net => "v2net",
- _ =>
- throw new ArgumentOutOfRangeException ()
- };
+ {
+ V2TestDriver.V2Win => "v2win",
+ V2TestDriver.V2Net => "v2net",
+ _ =>
+ throw new ArgumentOutOfRangeException ()
+ };
}
///
@@ -299,14 +299,14 @@ public class GuiTestContext : IDisposable
case V2TestDriver.V2Net:
int netButton = btn switch
- {
- WindowsConsole.ButtonState.Button1Pressed => 0,
- WindowsConsole.ButtonState.Button2Pressed => 1,
- WindowsConsole.ButtonState.Button3Pressed => 2,
- WindowsConsole.ButtonState.RightmostButtonPressed => 2,
- _ => throw new ArgumentOutOfRangeException(nameof(btn))
- };
- foreach (var k in NetSequences.Click(netButton,screenX,screenY))
+ {
+ WindowsConsole.ButtonState.Button1Pressed => 0,
+ WindowsConsole.ButtonState.Button2Pressed => 1,
+ WindowsConsole.ButtonState.Button3Pressed => 2,
+ WindowsConsole.ButtonState.RightmostButtonPressed => 2,
+ _ => throw new ArgumentOutOfRangeException (nameof (btn))
+ };
+ foreach (var k in NetSequences.Click (netButton, screenX, screenY))
{
SendNetKey (k);
}
@@ -452,18 +452,20 @@ public class GuiTestContext : IDisposable
///
/// Registers a right click handler on the added view (or root view) that
- /// will open the supplied .
+ /// will open the supplied .
///
- ///
- ///
+ ///
///
- public GuiTestContext WithContextMenu (ContextMenu ctx, MenuBarItem menuItems)
+ public GuiTestContext WithContextMenu (PopoverMenu? contextMenu)
{
LastView.MouseEvent += (s, e) =>
{
if (e.Flags.HasFlag (MouseFlags.Button3Clicked))
{
- ctx.Show (menuItems);
+ // Registering with the PopoverManager will ensure that the context menu is closed when the view is no longer focused
+ // and the context menu is disposed when it is closed.
+ Application.Popover?.Register (contextMenu);
+ contextMenu?.MakeVisible (e.ScreenPosition);
}
};
diff --git a/Tests/IntegrationTests/FluentTests/BasicFluentAssertionTests.cs b/Tests/IntegrationTests/FluentTests/BasicFluentAssertionTests.cs
index 345d7acc1..93dff6a86 100644
--- a/Tests/IntegrationTests/FluentTests/BasicFluentAssertionTests.cs
+++ b/Tests/IntegrationTests/FluentTests/BasicFluentAssertionTests.cs
@@ -63,22 +63,16 @@ public class BasicFluentAssertionTests
{
var clicked = false;
- var ctx = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("_New File", string.Empty, () => { clicked = true; })
- ]
- );
+ MenuItemv2 [] menuItems = [new ("_New File", string.Empty, () => { clicked = true; })];
using GuiTestContext c = With.A (40, 10, d)
- .WithContextMenu (ctx, menuItems)
+ .WithContextMenu (new PopoverMenu(menuItems))
.ScreenShot ("Before open menu", _out)
// Click in main area inside border
.RightClick (1, 1)
.ScreenShot ("After open menu", _out)
- .LeftClick (3, 3)
+ .LeftClick (2, 2)
.Stop ()
.WriteOutLogs (_out);
Assert.True (clicked);
@@ -90,34 +84,26 @@ public class BasicFluentAssertionTests
{
var clicked = false;
- var ctx = new ContextMenu ();
-
-
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null),
- new MenuItem ("Three", "", null),
- new MenuBarItem (
- "Four",
- [
- new MenuItem ("SubMenu1", "", null),
- new MenuItem ("SubMenu2", "", ()=>clicked=true),
- new MenuItem ("SubMenu3", "", null),
- new MenuItem ("SubMenu4", "", null),
- new MenuItem ("SubMenu5", "", null),
- new MenuItem ("SubMenu6", "", null),
- new MenuItem ("SubMenu7", "", null)
- ]
- ),
- new MenuItem ("Five", "", null),
- new MenuItem ("Six", "", null)
- ]
- );
+ MenuItemv2 [] menuItems = [
+ new ("One", "", null),
+ new ("Two", "", null),
+ new ("Three", "", null),
+ new ("Four", "", new (
+ [
+ new ("SubMenu1", "", null),
+ new ("SubMenu2", "", ()=>clicked=true),
+ new ("SubMenu3", "", null),
+ new ("SubMenu4", "", null),
+ new ("SubMenu5", "", null),
+ new ("SubMenu6", "", null),
+ new ("SubMenu7", "", null)
+ ])),
+ new ("Five", "", null),
+ new ("Six", "", null)
+ ];
using GuiTestContext c = With.A (40, 10,d)
- .WithContextMenu (ctx, menuItems)
+ .WithContextMenu (new PopoverMenu (menuItems))
.ScreenShot ("Before open menu", _out)
// Click in main area inside border
diff --git a/Tests/UnitTests/Application/ApplicationPopoverTests.cs b/Tests/UnitTests/Application/ApplicationPopoverTests.cs
index 20ca40108..b491f1eb9 100644
--- a/Tests/UnitTests/Application/ApplicationPopoverTests.cs
+++ b/Tests/UnitTests/Application/ApplicationPopoverTests.cs
@@ -5,7 +5,7 @@ namespace Terminal.Gui.ApplicationTests;
public class ApplicationPopoverTests
{
[Fact]
- public void Popover_ApplicationInit_Inits ()
+ public void ApplicationInit_Initializes_PopoverManager ()
{
// Arrange
Assert.Null (Application.Popover);
@@ -18,7 +18,7 @@ public class ApplicationPopoverTests
}
[Fact]
- public void Popover_ApplicationShutdown_CleansUp ()
+ public void Application_Shutdown_CleansUp_PopoverManager ()
{
// Arrange
Assert.Null (Application.Popover);
@@ -34,7 +34,7 @@ public class ApplicationPopoverTests
}
[Fact]
- public void Popover_NotCleanedUp_On_End ()
+ public void Application_End_Does_Not_CleanedUp ()
{
// Arrange
Assert.Null (Application.Popover);
@@ -56,7 +56,7 @@ public class ApplicationPopoverTests
}
[Fact]
- public void Popover_Active_Hidden_On_End ()
+ public void Application_End_Hides_Active ()
{
// Arrange
Assert.Null (Application.Popover);
@@ -66,9 +66,9 @@ public class ApplicationPopoverTests
var top = new Toplevel ();
RunState rs = Application.Begin (top);
- IPopoverTestClass popover = new ();
+ PopoverTestClass popover = new ();
- Application.Popover?.ShowPopover (popover);
+ Application.Popover?.Show (popover);
Assert.True (popover.Visible);
// Act
@@ -83,17 +83,80 @@ public class ApplicationPopoverTests
Application.Shutdown ();
}
- public class IPopoverTestClass : View, IPopover
+ [Fact]
+ public void Application_Shutdown_Disposes_Registered_Popovers ()
{
- public List HandledKeys { get; } = new List ();
+ // Arrange
+ Assert.Null (Application.Popover);
+ Application.Init (new FakeDriver ());
+
+ PopoverTestClass popover = new ();
+
+ // Act
+ Application.Popover?.Register (popover);
+ Application.Shutdown ();
+
+ // Test
+ Assert.Equal(1, popover.DisposedCount);
+ }
+
+ [Fact]
+ public void Application_Shutdown_Does_Not_Dispose_DeRegistered_Popovers ()
+ {
+ // Arrange
+ Assert.Null (Application.Popover);
+ Application.Init (new FakeDriver ());
+
+ PopoverTestClass popover = new ();
+
+ Application.Popover?.Register (popover);
+
+ // Act
+ Application.Popover?.DeRegister (popover);
+ Application.Shutdown ();
+
+ // Test
+ Assert.Equal (0, popover.DisposedCount);
+
+ popover.Dispose ();
+ }
+
+ [Fact]
+ public void Application_Shutdown_Does_Not_Dispose_ActiveNotRegistered_Popover ()
+ {
+ // Arrange
+ Assert.Null (Application.Popover);
+ Application.Init (new FakeDriver ());
+
+ PopoverTestClass popover = new ();
+
+ Application.Popover?.Show (popover);
+
+ // Act
+ Application.Shutdown ();
+
+ // Test
+ Assert.Equal (0, popover.DisposedCount);
+
+ popover.Dispose ();
+ }
+
+ public class PopoverTestClass : View, IPopover
+ {
+ public List HandledKeys { get; } = [];
public int NewCommandInvokeCount { get; private set; }
- public IPopoverTestClass ()
+ // NOTE: Hides the base DisposedCount property
+ public new int DisposedCount { get; private set; }
+
+ public PopoverTestClass ()
{
CanFocus = true;
AddCommand (Command.New, NewCommandHandler);
HotKeyBindings.Add (Key.N.WithCtrl, Command.New);
+ return;
+
bool? NewCommandHandler (ICommandContext ctx)
{
NewCommandInvokeCount++;
@@ -107,338 +170,13 @@ public class ApplicationPopoverTests
HandledKeys.Add (key);
return false;
}
+
+ ///
+ protected override void Dispose (bool disposing)
+ {
+ base.Dispose (disposing);
+ DisposedCount++;
+ }
}
- //[Fact]
- //public void Popover_SetToNull ()
- //{
- // // Arrange
- // var popover = new View ();
- // Application.Popover = popover;
- // // Act
- // Application.Popover = null;
-
- // // Assert
- // Assert.Null (Application.Popover);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_VisibleChangedEvent ()
- //{
- // // Arrange
- // var popover = new View ()
- // {
- // Visible = false
- // };
- // Application.Popover = popover;
- // bool eventTriggered = false;
-
- // popover.VisibleChanged += (sender, e) => eventTriggered = true;
-
- // // Act
- // popover.Visible = true;
-
- // // Assert
- // Assert.True (eventTriggered);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_InitializesCorrectly ()
- //{
- // // Arrange
- // var popover = new View ();
-
- // // Act
- // Application.Popover = popover;
-
- // // Assert
- // Assert.True (popover.IsInitialized);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_SetsColorScheme ()
- //{
- // // Arrange
- // var popover = new View ();
- // var topColorScheme = new ColorScheme ();
- // Application.Top = new Toplevel { ColorScheme = topColorScheme };
-
- // // Act
- // Application.Popover = popover;
-
- // // Assert
- // Assert.Equal (topColorScheme, popover.ColorScheme);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_VisibleChangedToTrue_SetsFocus ()
- //{
- // // Arrange
- // var popover = new View ()
- // {
- // Visible = false,
- // CanFocus = true
- // };
- // Application.Popover = popover;
-
- // // Act
- // popover.Visible = true;
-
- // // Assert
- // Assert.True (popover.Visible);
- // Assert.True (popover.HasFocus);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Theory]
- //[InlineData(-1, -1)]
- //[InlineData (0, 0)]
- //[InlineData (2048, 2048)]
- //[InlineData (2049, 2049)]
- //public void Popover_VisibleChangedToTrue_Locates_In_Visible_Position (int x, int y)
- //{
- // // Arrange
- // var popover = new View ()
- // {
- // X = x,
- // Y = y,
- // Visible = false,
- // CanFocus = true,
- // Width = 1,
- // Height = 1
- // };
- // Application.Popover = popover;
-
- // // Act
- // popover.Visible = true;
- // Application.LayoutAndDraw();
-
- // // Assert
- // Assert.True (Application.Screen.Contains (popover.Frame));
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_VisibleChangedToFalse_Hides_And_Removes_Focus ()
- //{
- // // Arrange
- // var popover = new View ()
- // {
- // Visible = false,
- // CanFocus = true
- // };
- // Application.Popover = popover;
- // popover.Visible = true;
-
- // // Act
- // popover.Visible = false;
-
- // // Assert
- // Assert.False (popover.Visible);
- // Assert.False (popover.HasFocus);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_Quit_Command_Hides ()
- //{
- // // Arrange
- // var popover = new View ()
- // {
- // Visible = false,
- // CanFocus = true
- // };
- // Application.Popover = popover;
- // popover.Visible = true;
- // Assert.True (popover.Visible);
- // Assert.True (popover.HasFocus);
-
- // // Act
- // Application.RaiseKeyDownEvent (Application.QuitKey);
-
- // // Assert
- // Assert.False (popover.Visible);
- // Assert.False (popover.HasFocus);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_MouseClick_Outside_Hides_Passes_Event_On ()
- //{
- // // Arrange
- // Application.Top = new Toplevel ()
- // {
- // Id = "top",
- // Height = 10,
- // Width = 10,
- // };
-
- // View otherView = new ()
- // {
- // X = 1,
- // Y = 1,
- // Height = 1,
- // Width = 1,
- // Id = "otherView",
- // };
-
- // bool otherViewPressed = false;
- // otherView.MouseEvent += (sender, e) =>
- // {
- // otherViewPressed = e.Flags.HasFlag(MouseFlags.Button1Pressed);
- // };
-
- // Application.Top.Add (otherView);
-
- // var popover = new View ()
- // {
- // Id = "popover",
- // X = 5,
- // Y = 5,
- // Width = 1,
- // Height = 1,
- // Visible = false,
- // CanFocus = true
- // };
-
- // Application.Popover = popover;
- // popover.Visible = true;
- // Assert.True (popover.Visible);
- // Assert.True (popover.HasFocus);
-
- // // Act
- // // Click on popover
- // Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed, ScreenPosition = new (5, 5) });
- // Assert.True (popover.Visible);
-
- // // Click outside popover (on button)
- // Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed, ScreenPosition = new (1, 1) });
-
- // // Assert
- // Assert.True (otherViewPressed);
- // Assert.False (popover.Visible);
-
- // Application.Top.Dispose ();
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Theory]
- //[InlineData (0, 0, false)]
- //[InlineData (5, 5, true)]
- //[InlineData (10, 10, false)]
- //[InlineData (5, 10, false)]
- //[InlineData (9, 9, false)]
- //public void Popover_MouseClick_Outside_Hides (int mouseX, int mouseY, bool expectedVisible)
- //{
- // // Arrange
- // Application.Top = new Toplevel ()
- // {
- // Id = "top",
- // Height = 10,
- // Width = 10,
- // };
- // var popover = new View ()
- // {
- // Id = "popover",
- // X = 5,
- // Y = 5,
- // Width = 1,
- // Height = 1,
- // Visible = false,
- // CanFocus = true
- // };
-
- // Application.Popover = popover;
- // popover.Visible = true;
- // Assert.True (popover.Visible);
- // Assert.True (popover.HasFocus);
-
- // // Act
- // Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed, ScreenPosition = new (mouseX, mouseY) });
-
- // // Assert
- // Assert.Equal (expectedVisible, popover.Visible);
-
- // Application.Top.Dispose ();
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_SetAndGet_ReturnsCorrectValue ()
- //{
- // // Arrange
- // var view = new View ();
-
- // // Act
- // Application.Popover = view;
-
- // // Assert
- // Assert.Equal (view, Application.Popover);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_SetToNull_HidesPreviousPopover ()
- //{
- // // Arrange
- // var view = new View { Visible = true };
- // Application.Popover = view;
-
- // // Act
- // Application.Popover = null;
-
- // // Assert
- // Assert.False (view.Visible);
- // Assert.Null (Application.Popover);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_SetNewPopover_HidesPreviousPopover ()
- //{
- // // Arrange
- // var oldView = new View { Visible = true };
- // var newView = new View ();
- // Application.Popover = oldView;
-
- // // Act
- // Application.Popover = newView;
-
- // // Assert
- // Assert.False (oldView.Visible);
- // Assert.Equal (newView, Application.Popover);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
-
- //[Fact]
- //public void Popover_SetNewPopover_InitializesAndSetsProperties ()
- //{
- // // Arrange
- // var view = new View ();
-
- // // Act
- // Application.Popover = view;
-
- // // Assert
- // Assert.True (view.IsInitialized);
- // Assert.True (view.Arrangement.HasFlag (ViewArrangement.Overlapped));
- // Assert.Equal (Application.Top?.ColorScheme, view.ColorScheme);
-
- // Application.ResetState (ignoreDisposed: true);
- //}
}
diff --git a/Tests/UnitTests/Views/ContextMenuTests.cs b/Tests/UnitTests/Views/ContextMenuTests.cs
deleted file mode 100644
index b6a69063e..000000000
--- a/Tests/UnitTests/Views/ContextMenuTests.cs
+++ /dev/null
@@ -1,2218 +0,0 @@
-using UnitTests;
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewsTests;
-
-public class ContextMenuTests (ITestOutputHelper output)
-{
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void ContextMenu_Constructors ()
- {
- var cm = new ContextMenu ();
- var top = new Toplevel ();
- Application.Begin (top);
-
- Assert.Equal (Point.Empty, cm.Position);
- Assert.Null (cm.MenuItems);
- Assert.Null (cm.Host);
- cm.Position = new Point (20, 10);
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("First", "", null)
- ]
- );
- cm.Show (menuItems);
- Assert.Equal (new Point (20, 10), cm.Position);
- Assert.Single (cm.MenuItems!.Children);
-
- cm = new ContextMenu
- {
- Position = new Point (5, 10)
- };
-
- menuItems = new MenuBarItem (
- new [] { new MenuItem ("One", "", null), new MenuItem ("Two", "", null) }
- );
- cm.Show (menuItems);
- Assert.Equal (new Point (5, 10), cm.Position);
- Assert.Equal (2, cm.MenuItems!.Children.Length);
- Assert.Null (cm.Host);
-
- var view = new View { X = 5, Y = 10 };
- top.Add (view);
-
- cm = new ContextMenu
- {
- Host = view,
- Position = new Point (5, 10)
- };
-
- menuItems = new MenuBarItem (
- new [] { new MenuItem ("One", "", null), new MenuItem ("Two", "", null) }
- );
- cm.Show (menuItems);
- Assert.Equal (new Point (5, 10), cm.Position);
- Assert.Equal (2, cm.MenuItems.Children.Length);
- Assert.NotNull (cm.Host);
-
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void ContextMenu_Is_Closed_If_Another_MenuBar_Is_Open_Or_Vice_Versa ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
-
- var menuBar = new MenuBar
- {
- Menus =
- [
- new MenuBarItem ("File", "", null),
- new MenuBarItem ("Edit", "", null)
- ]
- };
-
- var top = new Toplevel ();
- top.Add (menuBar);
- Application.Begin (top);
-
- Assert.Null (Application.MouseGrabView);
-
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- Menu menu = (Menu)top.SubViews.First (v => v is Menu);
- Assert.Equal (menu, Application.MouseGrabView);
- Assert.False (menuBar.IsMenuOpen);
- Assert.True (menuBar.NewKeyDownEvent (menuBar.Key));
- Assert.False (ContextMenu.IsShow);
- Assert.Equal (menuBar, Application.MouseGrabView);
- Assert.True (menuBar.IsMenuOpen);
-
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- menu = (Menu)top.SubViews.First (v => v is Menu);
- Assert.Equal (menu, Application.MouseGrabView);
- Assert.False (menuBar.IsMenuOpen);
-#if SUPPORT_ALT_TO_ACTIVATE_MENU
- Assert.True (Application.Top.ProcessKeyUp (new (Key.AltMask)));
- Assert.False (ContextMenu.IsShow);
- Assert.Equal (menu, Application.MouseGrabView);
- Assert.True (menu.IsMenuOpen);
-#endif
-
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- menu = (Menu)top.SubViews.First (v => v is Menu);
- Assert.Equal (menu, Application.MouseGrabView);
- Assert.False (menuBar.IsMenuOpen);
- Assert.False (menuBar.NewMouseEvent (new MouseEventArgs { Position = new (1, 0), Flags = MouseFlags.ReportMousePosition, View = menuBar }));
- Assert.True (ContextMenu.IsShow);
- Assert.Equal (menu, Application.MouseGrabView);
- Assert.False (menuBar.IsMenuOpen);
- Assert.True (menuBar.NewMouseEvent (new MouseEventArgs { Position = new (1, 0), Flags = MouseFlags.Button1Clicked, View = menuBar }));
- Assert.False (ContextMenu.IsShow);
- Assert.Equal (menuBar, Application.MouseGrabView);
- Assert.True (menuBar.IsMenuOpen);
- top.Dispose ();
- }
-
- [Fact (Skip = "#3798 Broke. Will fix in #2975")]
- [AutoInitShutdown]
- public void Draw_A_ContextMenu_Over_A_Borderless_Top ()
- {
- ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-
- Assert.Equal (new Rectangle (0, 0, 20, 15), View.GetClip ()!.GetBounds ());
- DriverAssert.AssertDriverContentsWithFrameAre ("", output);
-
- var top = new Toplevel { X = 2, Y = 2, Width = 15, Height = 4 };
- top.Add (new TextField { X = Pos.Center (), Width = 10, Text = "Test" });
- RunState rs = Application.Begin (top);
- Application.RunIteration (ref rs);
-
- Assert.Equal (new Rectangle (2, 2, 15, 4), top.Frame);
- Assert.Equal (top, Application.Top);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- Test",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (8, 2), Flags = MouseFlags.Button3Clicked });
-
- Application.RunIteration (ref rs);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- Test
-┌───────────────────
-│ Select All Ctrl+
-│ Delete All Ctrl+
-│ Copy Ctrl+
-│ Cut Ctrl+
-│ Paste Ctrl+
-│ Undo Ctrl+
-│ Redo Ctrl+
-└───────────────────",
- output
- );
-
- Application.End (rs);
- top.Dispose ();
- }
-
- [Fact (Skip = "#3798 Broke. Will fix in #2975")]
- [AutoInitShutdown]
- public void Draw_A_ContextMenu_Over_A_Dialog ()
- {
- Toplevel top = new ();
- var win = new Window ();
- top.Add (win);
- RunState rsTop = Application.Begin (top);
- ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-
- Assert.Equal (new Rectangle (0, 0, 20, 15), win.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌──────────────────┐
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-└──────────────────┘",
- output
- );
-
- // Don't use Dialog here as it has more layout logic. Use Window instead.
- var testWindow = new Window { X = 2, Y = 2, Width = 15, Height = 4 };
- testWindow.Add (new TextField { X = Pos.Center (), Width = 10, Text = "Test" });
- RunState rsDialog = Application.Begin (testWindow);
- Application.LayoutAndDraw ();
-
- Assert.Equal (new Rectangle (2, 2, 15, 4), testWindow.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌──────────────────┐
-│ │
-│ ┌─────────────┐ │
-│ │ Test │ │
-│ │ │ │
-│ └─────────────┘ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-│ │
-└──────────────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (9, 3), Flags = MouseFlags.Button3Clicked });
-
- Application.RunIteration (ref rsDialog);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌──────────────────┐
-│ │
-│ ┌─────────────┐ │
-│ │ Test │ │
-┌───────────────────
-│ Select All Ctrl+
-│ Delete All Ctrl+
-│ Copy Ctrl+
-│ Cut Ctrl+
-│ Paste Ctrl+
-│ Undo Ctrl+
-│ Redo Ctrl+
-└───────────────────
-│ │
-└──────────────────┘",
- output
- );
-
- Application.End (rsDialog);
- Application.End (rsTop);
- top.Dispose ();
- }
-
- [Fact (Skip = "#3798 Broke. Will fix in #2975")]
- [AutoInitShutdown]
- public void Draw_A_ContextMenu_Over_A_Top_Dialog ()
- {
- ((FakeDriver)Application.Driver!).SetBufferSize (20, 15);
-
- Assert.Equal (new Rectangle (0, 0, 20, 15), View.GetClip ()!.GetBounds ());
- DriverAssert.AssertDriverContentsWithFrameAre ("", output);
-
- // Don't use Dialog here as it has more layout logic. Use Window instead.
- var dialog = new Window { X = 2, Y = 2, Width = 15, Height = 4 };
- dialog.Add (new TextField { X = Pos.Center (), Width = 10, Text = "Test" });
- RunState rs = Application.Begin (dialog);
- Application.LayoutAndDraw ();
-
- Assert.Equal (new Rectangle (2, 2, 15, 4), dialog.Frame);
- Assert.Equal (dialog, Application.Top);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌─────────────┐
- │ Test │
- │ │
- └─────────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (9, 3), Flags = MouseFlags.Button3Clicked });
-
- var firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌─────────────┐
- │ Test │
-┌───────────────────
-│ Select All Ctrl+
-│ Delete All Ctrl+
-│ Copy Ctrl+
-│ Cut Ctrl+
-│ Paste Ctrl+
-│ Undo Ctrl+
-│ Redo Ctrl+
-└───────────────────",
- output
- );
-
- Application.End (rs);
- dialog.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void ForceMinimumPosToZero_True_False ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (-1, -2)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Assert.Equal (new Point (-1, -2), cm.Position);
-
- Toplevel top = new ();
- Application.Begin (top);
-
- cm.Show (menuItems);
- Assert.Equal (new Point (-1, -2), cm.Position);
- Application.LayoutAndDraw ();
-
- var expected = @"
-┌──────┐
-│ One │
-│ Two │
-└──────┘
-";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (0, 1, 8, 4), pos);
-
- cm.ForceMinimumPosToZero = false;
- cm.Show (menuItems);
- Assert.Equal (new Point (-1, -2), cm.Position);
- Application.LayoutAndDraw ();
-
- expected = @"
- One │
- Two │
-──────┘
-";
-
- pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (1, 0, 7, 3), pos);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Hide_Is_Invoke_At_Container_Closing ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (80, 25)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Toplevel top = new ();
- Application.Begin (top);
- top.Running = true;
-
- Assert.False (ContextMenu.IsShow);
-
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
-
- top.RequestStop ();
- Assert.False (ContextMenu.IsShow);
- top.Dispose ();
- }
-
- //[Fact (Skip = "Redo for CMv2")]
- //[AutoInitShutdown]
- //public void Key_Open_And_Close_The_ContextMenu ()
- //{
- // var tf = new TextField ();
- // var top = new Toplevel ();
- // top.Add (tf);
- // Application.Begin (top);
-
- // Assert.True (Application.RaiseKeyDownEvent (ContextMenu.DefaultKey));
- // Assert.True (tf.ContextMenu.MenuBar!.IsMenuOpen);
- // Assert.True (Application.RaiseKeyDownEvent (ContextMenu.DefaultKey));
-
- // // The last context menu bar opened is always preserved
- // Assert.False (tf.ContextMenu.Visible);
- // top.Dispose ();
- //}
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void KeyChanged_Event ()
- {
- var oldKey = Key.Empty;
- var cm = new ContextMenu ();
-
- cm.KeyChanged += (s, e) => oldKey = e.OldKey;
-
- cm.Key = Key.Space.WithCtrl;
- Assert.Equal (Key.Space.WithCtrl, cm.Key);
- Assert.Equal (ContextMenu.DefaultKey, oldKey);
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void MenuItens_Changing ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Application.LayoutAndDraw ();
-
- var expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
-
- menuItems = new MenuBarItem (
- [
- new MenuItem ("First", "", null),
- new MenuItem ("Second", "", null),
- new MenuItem ("Third", "", null)
- ]
- );
-
- cm.Show (menuItems);
- Application.LayoutAndDraw ();
-
- expected = @"
- ┌─────────┐
- │ First │
- │ Second │
- │ Third │
- └─────────┘
-";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Menus_And_SubMenus_Always_Try_To_Be_On_Screen ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (-1, -2)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null),
- new MenuItem ("Three", "", null),
- new MenuBarItem (
- "Four",
- [
- new MenuItem ("SubMenu1", "", null),
- new MenuItem ("SubMenu2", "", null),
- new MenuItem ("SubMenu3", "", null),
- new MenuItem ("SubMenu4", "", null),
- new MenuItem ("SubMenu5", "", null),
- new MenuItem ("SubMenu6", "", null),
- new MenuItem ("SubMenu7", "", null)
- ]
- ),
- new MenuItem ("Five", "", null),
- new MenuItem ("Six", "", null)
- ]
- );
- Assert.Equal (new Point (-1, -2), cm.Position);
-
- Toplevel top = new ();
- RunState rs = Application.Begin (top);
-
- cm.Show (menuItems);
- Application.RunIteration (ref rs);
-
- Assert.Equal (new Point (-1, -2), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌────────┐
-│ One │
-│ Two │
-│ Three │
-│ Four ►│
-│ Five │
-│ Six │
-└────────┘
-",
- output
- );
-
- View menu = top.SubViews.First (v => v is Menu);
-
- Assert.True (
- menu
- .NewMouseEvent (
- new MouseEventArgs { Position = new (0, 3), Flags = MouseFlags.ReportMousePosition, View = menu }
- )
- );
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (-1, -2), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌────────┐
-│ One │
-│ Two │
-│ Three │
-│ Four ►│┌───────────┐
-│ Five ││ SubMenu1 │
-│ Six ││ SubMenu2 │
-└────────┘│ SubMenu3 │
- │ SubMenu4 │
- │ SubMenu5 │
- │ SubMenu6 │
- │ SubMenu7 │
- └───────────┘
-",
- output
- );
-
- ((FakeDriver)Application.Driver!).SetBufferSize (40, 20);
- cm.Position = new Point (41, -2);
- cm.Show (menuItems);
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, -2), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two │
- │ Three │
- │ Four ►│
- │ Five │
- │ Six │
- └────────┘
-",
- output
- );
-
- menu = top.SubViews.First (v => v is Menu);
- Assert.True (
- menu
- .NewMouseEvent (
- new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = menu }
- )
- );
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, -2), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two │
- │ Three │
- ┌───────────┐│ Four ►│
- │ SubMenu1 ││ Five │
- │ SubMenu2 ││ Six │
- │ SubMenu3 │└────────┘
- │ SubMenu4 │
- │ SubMenu5 │
- │ SubMenu6 │
- │ SubMenu7 │
- └───────────┘
-",
- output
- );
-
- cm.Position = new Point (41, 9);
- cm.Show (menuItems);
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, 9), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two │
- │ Three │
- │ Four ►│
- │ Five │
- │ Six │
- └────────┘
-",
- output
- );
-
- menu = top.SubViews.First (v => v is Menu);
- Assert.True (
- menu
- .NewMouseEvent (
- new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = menu }
- )
- );
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, 9), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- ┌───────────┐│ One │
- │ SubMenu1 ││ Two │
- │ SubMenu2 ││ Three │
- │ SubMenu3 ││ Four ►│
- │ SubMenu4 ││ Five │
- │ SubMenu5 ││ Six │
- │ SubMenu6 │└────────┘
- │ SubMenu7 │
- └───────────┘
-",
- output
- );
-
- cm.Position = new Point (41, 22);
- cm.Show (menuItems);
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, 22), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two │
- │ Three │
- │ Four ►│
- │ Five │
- │ Six │
- └────────┘
-",
- output
- );
-
- menu = top.SubViews.First (v => v is Menu);
- Assert.True (
- menu
- .NewMouseEvent (
- new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = menu }
- )
- );
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (41, 22), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌───────────┐
- │ SubMenu1 │┌────────┐
- │ SubMenu2 ││ One │
- │ SubMenu3 ││ Two │
- │ SubMenu4 ││ Three │
- │ SubMenu5 ││ Four ►│
- │ SubMenu6 ││ Five │
- │ SubMenu7 ││ Six │
- └───────────┘└────────┘
-",
- output
- );
-
- ((FakeDriver)Application.Driver!).SetBufferSize (18, 8);
- cm.Position = new Point (19, 10);
- cm.Show (menuItems);
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (19, 10), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two │
- │ Three │
- │ Four ►│
- │ Five │
- │ Six │
- └────────┘
-",
- output
- );
-
- menu = top.SubViews.First (v => v is Menu);
- Assert.True (
- menu
- .NewMouseEvent (
- new MouseEventArgs { Position = new (30, 3), Flags = MouseFlags.ReportMousePosition, View = menu }
- )
- );
- Application.RunIteration (ref rs);
- Assert.Equal (new Point (19, 10), cm.Position);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
-┌───────────┐────┐
-│ SubMenu1 │ │
-│ SubMenu2 │ │
-│ SubMenu3 │ee │
-│ SubMenu4 │r ►│
-│ SubMenu5 │e │
-│ SubMenu6 │ │
-│ SubMenu7 │────┘
-",
- output
- );
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void MouseFlags_Changing ()
- {
- var lbl = new Label { Text = "Original" };
-
- var cm = new ContextMenu ();
-
- lbl.MouseClick += (s, e) =>
- {
- if (e.Flags == cm.MouseFlags)
- {
- lbl.Text = "Replaced";
- e.Handled = true;
- }
- };
-
- Toplevel top = new ();
- top.Add (lbl);
- Application.Begin (top);
-
- Assert.True (lbl.NewMouseEvent (new MouseEventArgs { Flags = cm.MouseFlags }));
- Assert.Equal ("Replaced", lbl.Text);
-
- lbl.Text = "Original";
- cm.MouseFlags = MouseFlags.Button2Clicked;
- Assert.True (lbl.NewMouseEvent (new MouseEventArgs { Flags = cm.MouseFlags }));
- Assert.Equal ("Replaced", lbl.Text);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- public void MouseFlagsChanged_Event ()
- {
- var oldMouseFlags = new MouseFlags ();
- var cm = new ContextMenu ();
-
- cm.MouseFlagsChanged += (s, e) => oldMouseFlags = e.OldValue;
-
- cm.MouseFlags = MouseFlags.Button2Clicked;
- Assert.Equal (MouseFlags.Button2Clicked, cm.MouseFlags);
- Assert.Equal (MouseFlags.Button3Clicked, oldMouseFlags);
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Position_Changing ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Application.LayoutAndDraw ();
-
- var expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
-
- cm.Position = new Point (5, 10);
-
- cm.Show (menuItems);
- Application.LayoutAndDraw ();
-
- expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void RequestStop_While_ContextMenu_Is_Open_Does_Not_Throws ()
- {
- ContextMenu cm = new ContextMenu
- {
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- new MenuItem [] { new ("One", "", null), new ("Two", "", null) }
- );
- Toplevel top = new ();
- var isMenuAllClosed = false;
- MenuBarItem mi = null;
- int iterations = -1;
-
- Application.Iteration += (s, a) =>
- {
- iterations++;
-
- if (iterations == 0)
- {
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- mi = cm.MenuBar.Menus [0];
-
- mi.Action = () =>
- {
- Assert.True (ContextMenu.IsShow);
-
- var dialog1 = new Dialog () { Id = "dialog1" };
- Application.Run (dialog1);
- dialog1.Dispose ();
- Assert.False (ContextMenu.IsShow);
- Assert.True (isMenuAllClosed);
- };
- cm.MenuBar.MenuAllClosed += (_, _) => isMenuAllClosed = true;
- }
- else if (iterations == 1)
- {
- mi.Action ();
- }
- else if (iterations == 2)
- {
- Application.RequestStop ();
- }
- else if (iterations == 3)
- {
- isMenuAllClosed = false;
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- cm.MenuBar.MenuAllClosed += (_, _) => isMenuAllClosed = true;
- }
- else if (iterations == 4)
- {
- Exception exception = Record.Exception (() => Application.RequestStop ());
- Assert.Null (exception);
- }
- else
- {
- Application.RequestStop ();
- }
- };
-
- var isTopClosed = false;
-
- top.Closing += (_, _) =>
- {
- var dialog2 = new Dialog () { Id = "dialog2" };
- Application.Run (dialog2);
- dialog2.Dispose ();
- Assert.False (ContextMenu.IsShow);
- Assert.True (isMenuAllClosed);
- isTopClosed = true;
- };
-
- Application.Run (top);
-
- Assert.True (isTopClosed);
- Assert.False (ContextMenu.IsShow);
- Assert.True (isMenuAllClosed);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Display_At_Zero_If_The_Toplevel_Height_Is_Less_Than_The_Menu_Height ()
- {
- ((FakeDriver)Application.Driver!).SetBufferSize (80, 3);
-
- var cm = new ContextMenu
- {
- Position = Point.Empty
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Assert.Equal (Point.Empty, cm.Position);
-
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Assert.Equal (Point.Empty, cm.Position);
- Application.LayoutAndDraw ();
-
- var expected = @"
-┌──────┐
-│ One │
-│ Two │";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (0, 0, 8, 3), pos);
-
- cm.Hide ();
- Assert.Equal (Point.Empty, cm.Position);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Display_At_Zero_If_The_Toplevel_Width_Is_Less_Than_The_Menu_Width ()
- {
- ((FakeDriver)Application.Driver!).SetBufferSize (5, 25);
-
- var cm = new ContextMenu
- {
- Position = Point.Empty
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Assert.Equal (Point.Empty, cm.Position);
-
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Assert.Equal (Point.Empty, cm.Position);
- Application.LayoutAndDraw ();
-
- var expected = @"
-┌────
-│ One
-│ Two
-└────";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (0, 1, 5, 4), pos);
-
- cm.Hide ();
- Assert.Equal (Point.Empty, cm.Position);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Display_Below_The_Bottom_Host_If_Has_Enough_Space ()
- {
- var view = new View
- {
- X = 10,
- Y = 5,
- Width = 10,
- Height = 1,
- Text = "View"
- };
-
- var cm = new ContextMenu
- {
- Host = view,
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- var top = new Toplevel ();
- top.Add (view);
- Application.Begin (top);
-
- Assert.Equal (new Point (10, 5), cm.Position);
-
- cm.Show (menuItems);
- top.Draw ();
- Assert.Equal (new Point (10, 5), cm.Position);
-
- var expected = @"
- View
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (10, 5, 18, 5), pos);
-
- cm.Hide ();
- Assert.Equal (new Point (10, 5), cm.Position);
- cm.Host.X = 5;
- cm.Host.Y = 10;
- cm.Host.Height = 3;
-
- cm.Show (menuItems);
- View.SetClipToScreen ();
- Application.Top.Draw ();
- Assert.Equal (new Point (5, 12), cm.Position);
-
- expected = @"
- View
-
-
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (5, 10, 13, 7), pos);
-
- cm.Hide ();
- Assert.Equal (new Point (5, 12), cm.Position);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Ensures_Display_Inside_The_Container_But_Preserves_Position ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (80, 25)
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- Assert.Equal (new Point (80, 25), cm.Position);
-
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Assert.Equal (new Point (80, 25), cm.Position);
- Application.LayoutAndDraw ();
-
- var expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (72, 21, 80, 4), pos);
-
- cm.Hide ();
- Assert.Equal (new Point (80, 25), cm.Position);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Ensures_Display_Inside_The_Container_Without_Overlap_The_Host ()
- {
- var view = new View
- {
- X = Pos.AnchorEnd (10),
- Y = Pos.AnchorEnd (1),
- Width = 10,
- Height = 1,
- Text = "View"
- };
-
- var cm = new ContextMenu
- {
- Host = view
- };
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("One", "", null),
- new MenuItem ("Two", "", null)
- ]
- );
- var top = new Toplevel ();
- top.Add (view);
- Application.Begin (top);
-
- Assert.Equal (new Rectangle (70, 24, 10, 1), view.Frame);
- Assert.Equal (Point.Empty, cm.Position);
-
- cm.Show (menuItems);
- Assert.Equal (new Point (70, 24), cm.Position);
- top.Draw ();
-
- var expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
- View
-";
-
- Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output);
- Assert.Equal (new Rectangle (70, 20, 78, 5), pos);
-
- cm.Hide ();
- Assert.Equal (new Point (70, 24), cm.Position);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Show_Hide_IsShow ()
- {
- ContextMenu cm = new ContextMenu
- {
- Position = new Point (10, 5)
- };
-
- var menuItems = new MenuBarItem (
- new MenuItem [] { new ("One", "", null), new ("Two", "", null) }
- );
-
- Toplevel top = new ();
- Application.Begin (top);
- cm.Show (menuItems);
- Assert.True (ContextMenu.IsShow);
- Application.LayoutAndDraw ();
-
- var expected = @"
- ┌──────┐
- │ One │
- │ Two │
- └──────┘
-";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
-
- cm.Hide ();
- Assert.False (ContextMenu.IsShow);
-
- Application.LayoutAndDraw ();
-
- expected = "";
-
- DriverAssert.AssertDriverContentsAre (expected, output);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void UseSubMenusSingleFrame_True_By_Mouse ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (5, 10),
- UseSubMenusSingleFrame = true
- };
-
- var menuItems = new MenuBarItem (
- "Numbers",
- [
- new MenuItem ("One", "", null),
- new MenuBarItem (
- "Two",
- [
- new MenuItem (
- "Sub-Menu 1",
- "",
- null
- ),
- new MenuItem ("Sub-Menu 2", "", null)
- ]
- ),
- new MenuItem ("Three", "", null)
- ]
- );
- Toplevel top = new ();
- RunState rs = Application.Begin (top);
- cm.Show (menuItems);
- var menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
- Application.LayoutAndDraw ();
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│
- │ Three │
- └────────┘",
- output
- );
-
- // X=5 is the border and so need to use at least one more
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.Button1Clicked });
-
- var firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
- menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
- menu = Application.Top!.SubViews.Last (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 15, 6), menu.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌─────────────┐
- │◄ Two │
- ├─────────────┤
- │ Sub-Menu 1 │
- │ Sub-Menu 2 │
- └─────────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 12), Flags = MouseFlags.Button1Clicked });
-
- firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
- menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│
- │ Three │
- └────────┘",
- output
- );
-
- Application.End (rs);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void UseSubMenusSingleFrame_False_By_Mouse ()
- {
- var cm = new ContextMenu
- {
- Position = new Point (5, 10)
- };
-
- var menuItems = new MenuBarItem (
- "Numbers",
- [
- new MenuItem ("One", "", null),
- new MenuBarItem (
- "Two",
- [
- new MenuItem (
- "Two-Menu 1",
- "",
- null
- ),
- new MenuItem ("Two-Menu 2", "", null)
- ]
- ),
- new MenuBarItem (
- "Three",
- [
- new MenuItem (
- "Three-Menu 1",
- "",
- null
- ),
- new MenuItem ("Three-Menu 2", "", null)
- ]
- )
- ]
- );
- Toplevel top = new ();
- RunState rs = Application.Begin (top);
- cm.Show (menuItems);
-
-
- var menu = Application.Top!.SubViews.First (v => v is Menu);
-
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
- Application.LayoutAndDraw ();
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│
- │ Three ►│
- └────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.ReportMousePosition });
-
- var firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
- menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│┌─────────────┐
- │ Three ►││ Two-Menu 1 │
- └────────┘│ Two-Menu 2 │
- └─────────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 14), Flags = MouseFlags.ReportMousePosition });
-
- firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
- menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│
- │ Three ►│┌───────────────┐
- └────────┘│ Three-Menu 1 │
- │ Three-Menu 2 │
- └───────────────┘",
- output
- );
-
- Application.RaiseMouseEvent (new MouseEventArgs { ScreenPosition = new (6, 13), Flags = MouseFlags.ReportMousePosition });
-
- firstIteration = false;
- Application.RunIteration (ref rs, firstIteration);
- menu = Application.Top!.SubViews.First (v => v is Menu);
- Assert.Equal (new Rectangle (5, 11, 10, 5), menu.Frame);
-
- DriverAssert.AssertDriverContentsWithFrameAre (
- @"
- ┌────────┐
- │ One │
- │ Two ►│┌─────────────┐
- │ Three ►││ Two-Menu 1 │
- └────────┘│ Two-Menu 2 │
- └─────────────┘",
- output
- );
-
- Application.End (rs);
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Handling_TextField_With_Opened_ContextMenu_By_Mouse_HasFocus ()
- {
- var tf1 = new TextField { Width = 10, Text = "TextField 1" };
- var tf2 = new TextField { Y = 2, Width = 10, Text = "TextField 2" };
- var win = new Window ();
- win.Add (tf1, tf2);
- var rs = Application.Begin (win);
-
- Assert.True (tf1.HasFocus);
- Assert.False (tf2.HasFocus);
- Assert.Equal (4, win.SubViews.Count); // TF & TV add autocomplete popup's to their superviews.
- Assert.Empty (Application._cachedViewsUnderMouse);
-
- // Right click on tf2 to open context menu
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 3), Flags = MouseFlags.Button3Clicked });
- Assert.False (tf1.HasFocus);
- Assert.False (tf2.HasFocus);
- Assert.Equal (6, win.SubViews.Count);
- //Assert.True (tf2.ContextMenu.IsMenuOpen);
- Assert.True (win.Focused is Menu);
- Assert.True (Application.MouseGrabView is Menu);
- Assert.Equal (tf2, Application._cachedViewsUnderMouse.LastOrDefault ());
-
- // Click on tf1 to focus it, which cause context menu being closed
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 1), Flags = MouseFlags.Button1Clicked });
- Assert.True (tf1.HasFocus);
- Assert.False (tf2.HasFocus);
- Assert.Equal (5, win.SubViews.Count);
-
- // The last context menu bar opened is always preserved
- Assert.NotNull (tf2.ContextMenu);
- Assert.Equal (win.Focused, tf1);
- Assert.Null (Application.MouseGrabView);
- Assert.Equal (tf1, Application._cachedViewsUnderMouse.LastOrDefault ());
-
- // Click on tf2 to focus it
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 3), Flags = MouseFlags.Button1Clicked });
- Assert.False (tf1.HasFocus);
- Assert.True (tf2.HasFocus);
- Assert.Equal (5, win.SubViews.Count);
-
- // The last context menu bar opened is always preserved
- Assert.NotNull (tf2.ContextMenu);
- Assert.Equal (win.Focused, tf2);
- Assert.Null (Application.MouseGrabView);
- Assert.Equal (tf2, Application._cachedViewsUnderMouse.LastOrDefault ());
-
- Application.End (rs);
- win.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Empty_Menus_Items_Children_Does_Not_Open_The_Menu ()
- {
- var cm = new ContextMenu ();
- Assert.Null (cm.MenuItems);
-
- var top = new Toplevel ();
- Application.Begin (top);
-
- cm.Show (cm.MenuItems);
- Assert.Null (cm.MenuBar);
-
- top.Dispose ();
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void KeyBindings_Removed_On_Close_ContextMenu ()
- {
- var newFile = false;
- var renameFile = false;
- var deleteFile = false;
-
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new MenuItem ("New File", string.Empty, New, null, null, Key.N.WithCtrl),
- new MenuItem ("Rename File", string.Empty, Rename, null, null, Key.R.WithCtrl),
- new MenuItem ("Delete File", string.Empty, Delete, null, null, Key.D.WithCtrl)
- ]
- );
- var top = new Toplevel ();
- Application.Begin (top);
-
- Assert.Null (cm.MenuBar);
- Assert.False (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.D.WithCtrl));
- Assert.False (newFile);
- Assert.False (renameFile);
- Assert.False (deleteFile);
-
- cm.Show (menuItems);
- Assert.True (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.True (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
- Assert.True (cm.MenuBar.HotKeyBindings.TryGet (Key.D.WithCtrl, out _));
-
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (cm.MenuBar!.IsMenuOpen);
- cm.Show (menuItems);
- Assert.True (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (renameFile);
- Assert.False (cm.MenuBar.IsMenuOpen);
- cm.Show (menuItems);
- Assert.True (Application.RaiseKeyDownEvent (Key.D.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (deleteFile);
- Assert.False (cm.MenuBar.IsMenuOpen);
-
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.D.WithCtrl, out _));
-
- newFile = false;
- renameFile = false;
- deleteFile = false;
- Assert.False (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.D.WithCtrl));
- Assert.False (newFile);
- Assert.False (renameFile);
- Assert.False (deleteFile);
-
- top.Dispose ();
-
- void New () { newFile = true; }
-
- void Rename () { renameFile = true; }
-
- void Delete () { deleteFile = true; }
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void KeyBindings_With_ContextMenu_And_MenuBar ()
- {
- var newFile = false;
- var renameFile = false;
-
- var menuBar = new MenuBar
- {
- Menus =
- [
- new (
- "File",
- new MenuItem []
- {
- new ("New", string.Empty, New, null, null, Key.N.WithCtrl)
- })
- ]
- };
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("Rename File", string.Empty, Rename, null, null, Key.R.WithCtrl),
- ]
- );
- var top = new Toplevel ();
- top.Add (menuBar);
- Application.Begin (top);
-
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
- Assert.Null (cm.MenuBar);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (renameFile);
-
- newFile = false;
-
- cm.Show (menuItems);
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.True (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
-
- Assert.True (cm.MenuBar.IsMenuOpen);
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (cm.MenuBar!.IsMenuOpen);
- cm.Show (menuItems);
- Assert.True (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (renameFile);
- Assert.False (cm.MenuBar.IsMenuOpen);
-
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithCtrl, out _));
-
- newFile = false;
- renameFile = false;
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (renameFile);
-
- top.Dispose ();
-
- void New () { newFile = true; }
-
- void Rename () { renameFile = true; }
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void KeyBindings_With_Same_Shortcut_ContextMenu_And_MenuBar ()
- {
- var newMenuBar = false;
- var newContextMenu = false;
-
- var menuBar = new MenuBar
- {
- Menus =
- [
- new (
- "File",
- new MenuItem []
- {
- new ("New", string.Empty, NewMenuBar, null, null, Key.N.WithCtrl)
- })
- ]
- };
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("New File", string.Empty, NewContextMenu, null, null, Key.N.WithCtrl),
- ]
- );
- var top = new Toplevel ();
- top.Add (menuBar);
- Application.Begin (top);
-
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.Null (cm.MenuBar);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newMenuBar);
- Assert.False (newContextMenu);
-
- newMenuBar = false;
-
- cm.Show (menuItems);
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.True (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
-
- Assert.True (cm.MenuBar.IsMenuOpen);
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.False (newMenuBar);
-
- // The most focused shortcut is executed
- Assert.True (newContextMenu);
- Assert.False (cm.MenuBar!.IsMenuOpen);
-
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.N.WithCtrl, out _));
-
- newMenuBar = false;
- newContextMenu = false;
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithCtrl));
- Application.MainLoop!.RunIteration ();
- Assert.True (newMenuBar);
- Assert.False (newContextMenu);
-
- top.Dispose ();
-
- void NewMenuBar () { newMenuBar = true; }
-
- void NewContextMenu () { newContextMenu = true; }
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void HotKeys_Removed_On_Close_ContextMenu ()
- {
- var newFile = false;
- var renameFile = false;
- var deleteFile = false;
-
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("_New File", string.Empty, New, null, null),
- new ("_Rename File", string.Empty, Rename, null, null),
- new ("_Delete File", string.Empty, Delete, null, null)
- ]
- );
- var top = new Toplevel ();
- Application.Begin (top);
-
- Assert.Null (cm.MenuBar);
- Assert.False (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Assert.False (Application.RaiseKeyDownEvent (Key.D.WithAlt));
- Assert.False (newFile);
- Assert.False (renameFile);
- Assert.False (deleteFile);
-
- cm.Show (menuItems);
- Assert.True (cm.MenuBar!.IsMenuOpen);
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.D.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.D.NoShift, out _));
- Assert.Equal (2, Application.Top!.SubViews.Count);
- View [] menus = Application.Top!.SubViews.Where (v => v is Menu m && m.Host == cm.MenuBar).ToArray ();
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.D.WithAlt, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.D.NoShift, out _));
-
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- cm.Show (menuItems);
- Assert.True (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Assert.False (cm.MenuBar.IsMenuOpen);
- Application.MainLoop!.RunIteration ();
- Assert.True (renameFile);
- cm.Show (menuItems);
- Assert.True (Application.RaiseKeyDownEvent (Key.D.WithAlt));
- Assert.False (cm.MenuBar.IsMenuOpen);
- Application.MainLoop!.RunIteration ();
- Assert.True (deleteFile);
-
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.D.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.D.NoShift, out _));
-
- newFile = false;
- renameFile = false;
- deleteFile = false;
- Assert.False (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Assert.False (Application.RaiseKeyDownEvent (Key.D.WithAlt));
- Assert.False (newFile);
- Assert.False (renameFile);
- Assert.False (deleteFile);
-
- top.Dispose ();
-
- void New () { newFile = true; }
-
- void Rename () { renameFile = true; }
-
- void Delete () { deleteFile = true; }
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void HotKeys_With_ContextMenu_And_MenuBar ()
- {
- var newFile = false;
- var renameFile = false;
-
- var menuBar = new MenuBar
- {
- Menus =
- [
- new (
- "_File",
- new MenuItem []
- {
- new ("_New", string.Empty, New)
- })
- ]
- };
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new MenuBarItem (
- "_Edit",
- new MenuItem []
- {
- new ("_Rename File", string.Empty, Rename)
- }
- )
- ]
- );
- var top = new Toplevel ();
- top.Add (menuBar);
- Application.Begin (top);
-
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.F.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- View [] menus = Application.Top!.SubViews.Where (v => v is Menu m && m.Host == menuBar).ToArray ();
- Assert.Empty (menus);
- Assert.Null (cm.MenuBar);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt));
- Assert.True (menuBar.IsMenuOpen);
- Assert.Equal (2, Application.Top!.SubViews.Count);
- menus = Application.Top!.SubViews.Where (v => v is Menu m && m.Host == menuBar).ToArray ();
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Assert.False (menuBar.IsMenuOpen);
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (renameFile);
-
- newFile = false;
-
- cm.Show (menuItems);
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.F.WithAlt, out _));
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.F.NoShift, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.True (cm.MenuBar!.IsMenuOpen);
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.F.WithAlt, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.F.NoShift, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.False (cm.MenuBar!.HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.Equal (4, Application.Top!.SubViews.Count);
- menus = Application.Top!.SubViews.Where (v => v is Menu m && m.Host == cm.MenuBar).ToArray ();
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.True (menus [1].HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.True (menus [1].HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.True (cm.MenuBar.IsMenuOpen);
- Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt));
- Assert.False (cm.MenuBar.IsMenuOpen);
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
-
- cm.Show (menuItems);
- Assert.True (cm.MenuBar.IsMenuOpen);
- Assert.Equal (4, Application.Top!.SubViews.Count);
- menus = Application.Top!.SubViews.Where (v => v is Menu m && m.Host == cm.MenuBar).ToArray ();
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.True (menus [0].HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.False (menus [0].HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (menus [0].HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.False (menus [1].HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.False (menus [1].HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.True (menus [1].HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.True (menus [1].HotKeyBindings.TryGet (Key.R.NoShift, out _));
- Assert.True (Application.RaiseKeyDownEvent (Key.E.NoShift));
- Assert.True (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Assert.False (cm.MenuBar.IsMenuOpen);
- Application.MainLoop!.RunIteration ();
- Assert.True (renameFile);
-
- Assert.Equal (2, Application.Top!.SubViews.Count);
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.F.WithAlt, out _));
- Assert.True (menuBar.HotKeyBindings.TryGet (Key.F.NoShift, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _));
- Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.E.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.E.NoShift, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.WithAlt, out _));
- Assert.False (cm.MenuBar.HotKeyBindings.TryGet (Key.R.NoShift, out _));
-
- newFile = false;
- renameFile = false;
- Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt));
- Assert.True (Application.RaiseKeyDownEvent (Key.N.WithAlt));
- Assert.False (Application.RaiseKeyDownEvent (Key.R.WithAlt));
- Application.MainLoop!.RunIteration ();
- Assert.True (newFile);
- Assert.False (renameFile);
-
- top.Dispose ();
-
- void New () { newFile = true; }
-
- void Rename () { renameFile = true; }
- }
-
- [Fact (Skip = "Redo for CMv2")]
- [AutoInitShutdown]
- public void Opened_MenuBar_Is_Closed_When_Another_MenuBar_Is_Opening_Also_By_HotKey ()
- {
- var menuBar = new MenuBar
- {
- Menus =
- [
- new (
- "_File",
- new MenuItem []
- {
- new ("_New", string.Empty, null)
- })
- ]
- };
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new MenuBarItem (
- "_Edit",
- new MenuItem []
- {
- new ("_Rename File", string.Empty, null)
- }
- )
- ]
- );
- var top = new Toplevel ();
- top.Add (menuBar);
- Application.Begin (top);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt));
- Assert.True (menuBar.IsMenuOpen);
-
- cm.Show (menuItems);
- Assert.False (menuBar.IsMenuOpen);
- Assert.True (cm.MenuBar!.IsMenuOpen);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.F.WithAlt));
- Assert.True (menuBar.IsMenuOpen);
- Assert.False (cm.MenuBar!.IsMenuOpen);
-
- top.Dispose ();
- }
-
- [Theory]
- [InlineData (1)]
- [InlineData (2)]
- [InlineData (3)]
- [AutoInitShutdown]
- public void Mouse_Pressed_Released_Clicked (int button)
- {
- var actionRaised = false;
-
- var menuBar = new MenuBar
- {
- Menus =
- [
- new (
- "_File",
- new MenuItem []
- {
- new ("_New", string.Empty, () => actionRaised = true)
- })
- ]
- };
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("_Rename File", string.Empty, () => actionRaised = true)
- ]
- );
- var top = new Toplevel ();
-
- top.MouseClick += (s, e) =>
- {
- if (e.Flags == cm.MouseFlags)
- {
- cm.Position = new (e.Position.X, e.Position.Y);
- cm.Show (menuItems);
- e.Handled = true;
- }
- };
-
- top.Add (menuBar);
- Application.Begin (top);
-
- // MenuBar
- Application.RaiseMouseEvent (new () { Flags = MouseFlags.Button1Pressed });
- Assert.True (menuBar.IsMenuOpen);
-
- switch (button)
- {
- // Left Button
- case 1:
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Pressed });
- Assert.True (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Released });
- Assert.True (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button1Clicked });
- Assert.False (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.True (actionRaised);
- actionRaised = false;
-
- break;
- // Middle Button
- case 2:
- // Right Button
- case 3:
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button3Pressed });
- Assert.True (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button3Released });
- Assert.True (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 2), Flags = MouseFlags.Button3Clicked });
- Assert.True (menuBar.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
-
- break;
- }
-
- // ContextMenu
- Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 4), Flags = cm.MouseFlags });
- Assert.False (menuBar.IsMenuOpen);
- Assert.True (cm.MenuBar!.IsMenuOpen);
-
- switch (button)
- {
- // Left Button
- case 1:
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 6), Flags = MouseFlags.Button1Pressed });
- Assert.True (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 6), Flags = MouseFlags.Button1Released });
- Assert.True (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 6), Flags = MouseFlags.Button1Clicked });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.True (actionRaised);
- actionRaised = false;
-
- break;
- // Middle Button
- case 2:
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button2Pressed });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button2Released });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button2Clicked });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
-
- break;
- // Right Button
- case 3:
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button3Pressed });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button3Released });
- Assert.False (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
- Application.RaiseMouseEvent (new () { ScreenPosition = new (1, 4), Flags = MouseFlags.Button3Clicked });
- // MouseFlags is the same as cm.MouseFlags. So the context menu is closed and reopened again
- Assert.True (cm.MenuBar!.IsMenuOpen);
- Application.MainLoop.RunIteration ();
- Assert.False (actionRaised);
-
- break;
- }
-
- top.Dispose ();
- }
-
- [Fact]
- [AutoInitShutdown]
- public void Menu_Without_SubMenu_Is_Closed_When_Pressing_Key_Right_Or_Key_Left ()
- {
- var cm = new ContextMenu ();
-
- var menuItems = new MenuBarItem (
- [
- new ("_New", string.Empty, null),
- new ("_Save", string.Empty, null)
- ]
- );
- var top = new Toplevel ();
- Application.Begin (top);
-
- cm.Show (menuItems);
- Assert.True (cm.MenuBar!.IsMenuOpen);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight));
- Assert.False (cm.MenuBar!.IsMenuOpen);
-
- cm.Show (menuItems);
- Assert.True (cm.MenuBar!.IsMenuOpen);
-
- Assert.True (Application.RaiseKeyDownEvent (Key.CursorLeft));
- Assert.False (cm.MenuBar!.IsMenuOpen);
-
- top.Dispose ();
- }
-
- [Fact]
- [AutoInitShutdown]
- public void Menu_Opened_In_SuperView_With_TabView_Has_Precedence_On_Key_Press ()
- {
- var win = new Window
- {
- Title = "My Window",
- X = 0,
- Y = 0,
- Width = Dim.Fill (),
- Height = Dim.Fill ()
- };
-
- // Tab View
- var tabView = new TabView
- {
- X = 1,
- Y = 1,
- Width = Dim.Fill () - 2,
- Height = Dim.Fill () - 2
- };
- tabView.AddTab (new () { DisplayText = "Tab 1" }, true);
- tabView.AddTab (new () { DisplayText = "Tab 2" }, false);
- win.Add (tabView);
-
- // Context Menu
- var menuItems = new MenuBarItem (
- [
- new ("Item 1", "First item", () => MessageBox.Query ("Action", "Item 1 Clicked", "OK")),
- new MenuBarItem (
- "Submenu",
- new List