diff --git a/Examples/UICatalog/Resources/config.json b/Examples/UICatalog/Resources/config.json index e47ea567c..17d6edcf5 100644 --- a/Examples/UICatalog/Resources/config.json +++ b/Examples/UICatalog/Resources/config.json @@ -149,8 +149,8 @@ "FrameView.DefaultBorderStyle": "Double", "MessageBox.DefaultMinimumHeight": 0, "Button.DefaultHighlightStates": "In, Pressed", - "Menuv2.DefaultBorderStyle": "Heavy", - "MenuBarv2.DefaultBorderStyle": "Heavy", + "Menu.DefaultBorderStyle": "Heavy", + "MenuBar.DefaultBorderStyle": "Heavy", "Schemes": [ { "UI Catalog Scheme": { diff --git a/Examples/UICatalog/Scenario.cs b/Examples/UICatalog/Scenario.cs index 0fa13e6db..4d9f0c759 100644 --- a/Examples/UICatalog/Scenario.cs +++ b/Examples/UICatalog/Scenario.cs @@ -219,6 +219,8 @@ public class Scenario : IDisposable } } + // BUGBUG: This is incompatible with modals. This should be using the new equivalent of Toplevel.Ready + // BUGBUG: which will be IsRunningChanged with newIsRunning == true private void OnApplicationSessionBegun (object? sender, SessionTokenEventArgs e) { SubscribeAllSubViews (Application.TopRunnable!); diff --git a/Examples/UICatalog/Scenarios/Bars.cs b/Examples/UICatalog/Scenarios/Bars.cs index 730ddf969..669f4e9c0 100644 --- a/Examples/UICatalog/Scenarios/Bars.cs +++ b/Examples/UICatalog/Scenarios/Bars.cs @@ -80,7 +80,7 @@ public class Bars : Scenario }; menuBarLikeExamples.Add (label); - //bar = new MenuBarv2 + //bar = new MenuBar //{ // Id = "menuBar", // X = Pos.Right (label), @@ -128,7 +128,7 @@ public class Bars : Scenario }; menuLikeExamples.Add (label); - bar = new Menuv2 + bar = new Menu { Id = "menu", X = Pos.Left (label), @@ -147,7 +147,7 @@ public class Bars : Scenario }; menuLikeExamples.Add (label); - Menuv2 popOverMenu = new Menuv2 + Menu popOverMenu = new Menu { Id = "popupMenu", X = Pos.Left (label), diff --git a/Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs b/Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs index 3028eb4ee..fba9130e3 100644 --- a/Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs +++ b/Examples/UICatalog/Scenarios/CharacterMap/CharacterMap.cs @@ -176,13 +176,13 @@ public class CharacterMap : Scenario top.Add (_categoryList); - var menu = new MenuBarv2 + var menu = new MenuBar { Menus = [ new ( "_File", - new MenuItemv2 [] + new MenuItem [] { new ( "_Quit", @@ -337,14 +337,14 @@ public class CharacterMap : Scenario ); } - private MenuItemv2 CreateMenuShowWidth () + private MenuItem CreateMenuShowWidth () { CheckBox cb = new () { Title = "_Show Glyph Width", CheckedState = _charMap!.ShowGlyphWidths ? CheckState.Checked : CheckState.None }; - var item = new MenuItemv2 { CommandView = cb }; + var item = new MenuItem { CommandView = cb }; item.Action += () => { @@ -357,7 +357,7 @@ public class CharacterMap : Scenario return item; } - private MenuItemv2 CreateMenuUnicodeCategorySelector () + private MenuItem CreateMenuUnicodeCategorySelector () { // First option is "All" (no filter), followed by all UnicodeCategory names string [] allCategoryNames = Enum.GetNames (); diff --git a/Examples/UICatalog/Scenarios/ClassExplorer.cs b/Examples/UICatalog/Scenarios/ClassExplorer.cs index efcb0ceeb..14c87e387 100644 --- a/Examples/UICatalog/Scenarios/ClassExplorer.cs +++ b/Examples/UICatalog/Scenarios/ClassExplorer.cs @@ -1,6 +1,5 @@ -using System; -using System.Collections.Generic; -using System.Linq; +#nullable enable + using System.Reflection; using System.Text; @@ -11,63 +10,45 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TreeView")] public class ClassExplorer : Scenario { - private MenuItem _highlightModelTextOnly; - private MenuItem _miShowPrivate; - private TextView _textView; - private TreeView _treeView; + private CheckBox? _highlightModelTextOnlyCheckBox; + private CheckBox? _showPrivateCheckBox; + private TextView? _textView; + private TreeView? _treeView; public override void Main () { Application.Init (); - var top = new Toplevel (); - var menu = new MenuBar - { - Menus = - [ - new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) }), - new MenuBarItem ( - "_View", - new [] - { - _miShowPrivate = - new MenuItem ( - "_Include Private", - "", - () => ShowPrivate () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked }, - new ("_Expand All", "", () => _treeView.ExpandAll ()), - new ("_Collapse All", "", () => _treeView.CollapseAll ()) - } - ), - new MenuBarItem ( - "_Style", - new [] - { - _highlightModelTextOnly = new MenuItem ( - "_Highlight Model Text Only", - "", - () => OnCheckHighlightModelTextOnly () - ) { CheckType = MenuItemCheckStyle.Checked } - } - ) - ] - }; - top.Add (menu); - - var win = new Window + Window win = new () { Title = GetName (), - Y = Pos.Bottom (menu) + BorderStyle = LineStyle.None }; - _treeView = new TreeView { X = 0, Y = 1, Width = Dim.Percent (50), Height = Dim.Fill () }; + // MenuBar + MenuBar menuBar = new (); - var lblSearch = new Label { Text = "Search" }; - var tfSearch = new TextField { Width = 20, X = Pos.Right (lblSearch) }; + // Search controls + Label lblSearch = new () + { + Y = Pos.Bottom (menuBar), + Title = "Search:" + }; - win.Add (lblSearch); - win.Add (tfSearch); + TextField tfSearch = new () + { + Y = Pos.Top (lblSearch), + X = Pos.Right (lblSearch) + 1, + Width = 20 + }; + + // TreeView + _treeView = new () + { + Y = Pos.Bottom (lblSearch), + Width = Dim.Percent (50), + Height = Dim.Fill () + }; TreeViewTextFilter filter = new (_treeView); _treeView.Filter = filter; @@ -76,7 +57,7 @@ public class ClassExplorer : Scenario { filter.Text = tfSearch.Text; - if (_treeView.SelectedObject != null) + if (_treeView.SelectedObject is { }) { _treeView.EnsureVisible (_treeView.SelectedObject); } @@ -87,111 +68,146 @@ public class ClassExplorer : Scenario _treeView.TreeBuilder = new DelegateTreeBuilder (ChildGetter, CanExpand); _treeView.SelectionChanged += TreeView_SelectionChanged; - win.Add (_treeView); + // TextView for details + _textView = new () + { + X = Pos.Right (_treeView), + Y = Pos.Top (_treeView), + Width = Dim.Fill (), + Height = Dim.Fill (), + ReadOnly = true, + }; - _textView = new TextView { X = Pos.Right (_treeView), Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + // Menu setup + _showPrivateCheckBox = new () + { + Title = "_Include Private" + }; + _showPrivateCheckBox.CheckedStateChanged += (s, e) => ShowPrivate (); - win.Add (_textView); + _highlightModelTextOnlyCheckBox = new () + { + Title = "_Highlight Model Text Only" + }; + _highlightModelTextOnlyCheckBox.CheckedStateChanged += (s, e) => OnCheckHighlightModelTextOnly (); - top.Add (win); + menuBar.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); - Application.Run (top); - top.Dispose (); + menuBar.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + CommandView = _showPrivateCheckBox + }, + new MenuItem + { + Title = "_Expand All", + Action = () => _treeView?.ExpandAll () + }, + new MenuItem + { + Title = "_Collapse All", + Action = () => _treeView?.CollapseAll () + } + ] + ) + ); + + menuBar.Add ( + new MenuBarItem ( + "_Style", + [ + new MenuItem + { + CommandView = _highlightModelTextOnlyCheckBox + } + ] + ) + ); + + // Add views in order of visual appearance + win.Add (menuBar, lblSearch, tfSearch, _treeView, _textView); + + Application.Run (win); + win.Dispose (); Application.Shutdown (); } - private bool CanExpand (object arg) { return arg is Assembly || arg is Type || arg is ShowForType; } + private bool CanExpand (object arg) => arg is Assembly or Type or ShowForType; private IEnumerable ChildGetter (object arg) { try { - if (arg is Assembly a) - { - return a.GetTypes (); - } - - if (arg is Type t) - { - // Note that here we cannot simply return the enum values as the same object cannot appear under multiple branches - return Enum.GetValues (typeof (Showable)) - .Cast () - - // Although we new the Type every time the delegate is called state is preserved because the class has appropriate equality members - .Select (v => new ShowForType (v, t)); - } - - if (arg is ShowForType show) - { - switch (show.ToShow) - { - case Showable.Properties: - return show.Type.GetProperties (GetFlags ()); - case Showable.Constructors: - return show.Type.GetConstructors (GetFlags ()); - case Showable.Events: - return show.Type.GetEvents (GetFlags ()); - case Showable.Fields: - return show.Type.GetFields (GetFlags ()); - case Showable.Methods: - return show.Type.GetMethods (GetFlags ()); - } - } + return arg switch + { + Assembly assembly => assembly.GetTypes (), + Type type => Enum.GetValues (typeof (Showable)) + .Cast () + .Select (v => new ShowForType (v, type)), + ShowForType show => show.ToShow switch + { + Showable.Properties => show.Type.GetProperties (GetFlags ()), + Showable.Constructors => show.Type.GetConstructors (GetFlags ()), + Showable.Events => show.Type.GetEvents (GetFlags ()), + Showable.Fields => show.Type.GetFields (GetFlags ()), + Showable.Methods => show.Type.GetMethods (GetFlags ()), + _ => Enumerable.Empty () + }, + _ => Enumerable.Empty () + }; } catch (Exception) { return Enumerable.Empty (); } - - return Enumerable.Empty (); } - private BindingFlags GetFlags () - { - if (_miShowPrivate.Checked == true) - { - return BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic; - } - - return BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; - } + private BindingFlags GetFlags () => + _showPrivateCheckBox?.CheckedState == CheckState.Checked + ? BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic + : BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public; private string GetRepresentation (object model) { try { - if (model is Assembly ass) - { - return ass.GetName ().Name; - } - - if (model is PropertyInfo p) - { - return p.Name; - } - - if (model is FieldInfo f) - { - return f.Name; - } - - if (model is EventInfo ei) - { - return ei.Name; - } + return model switch + { + Assembly assembly => assembly.GetName ().Name ?? string.Empty, + PropertyInfo propertyInfo => propertyInfo.Name, + FieldInfo fieldInfo => fieldInfo.Name, + EventInfo eventInfo => eventInfo.Name, + _ => model.ToString () ?? string.Empty + }; } catch (Exception ex) { return ex.Message; } - - return model.ToString (); } private void OnCheckHighlightModelTextOnly () { - _treeView.Style.HighlightModelTextOnly = !_treeView.Style.HighlightModelTextOnly; - _highlightModelTextOnly.Checked = _treeView.Style.HighlightModelTextOnly; + if (_treeView is null) + { + return; + } + + _treeView.Style.HighlightModelTextOnly = _highlightModelTextOnlyCheckBox?.CheckedState == CheckState.Checked; _treeView.SetNeedsDraw (); } @@ -199,17 +215,21 @@ public class ClassExplorer : Scenario private void ShowPrivate () { - _miShowPrivate.Checked = !_miShowPrivate.Checked; - _treeView.RebuildTree (); - _treeView.SetFocus (); + _treeView?.RebuildTree (); + _treeView?.SetFocus (); } - private void TreeView_SelectionChanged (object sender, SelectionChangedEventArgs e) + private void TreeView_SelectionChanged (object? sender, SelectionChangedEventArgs e) { - object val = e.NewValue; + if (_treeView is null || _textView is null) + { + return; + } + + object? val = e.NewValue; object [] all = _treeView.GetAllSelectedObjects ().ToArray (); - if (val == null || val is ShowForType) + if (val is null or ShowForType) { return; } @@ -218,69 +238,73 @@ public class ClassExplorer : Scenario { if (all.Length > 1) { - _textView.Text = all.Length + " Objects"; + _textView.Text = $"{all.Length} Objects"; } else { - var sb = new StringBuilder (); + StringBuilder sb = new (); - // tell the user about the currently selected tree node - sb.AppendLine (e.NewValue.GetType ().Name); + sb.AppendLine (e.NewValue?.GetType ().Name ?? string.Empty); - if (val is Assembly ass) + switch (val) { - sb.AppendLine ($"Location:{ass.Location}"); - sb.AppendLine ($"FullName:{ass.FullName}"); - } + case Assembly assembly: + sb.AppendLine ($"Location:{assembly.Location}"); + sb.AppendLine ($"FullName:{assembly.FullName}"); - if (val is PropertyInfo p) - { - sb.AppendLine ($"Name:{p.Name}"); - sb.AppendLine ($"Type:{p.PropertyType}"); - sb.AppendLine ($"CanWrite:{p.CanWrite}"); - sb.AppendLine ($"CanRead:{p.CanRead}"); - } + break; - if (val is FieldInfo f) - { - sb.AppendLine ($"Name:{f.Name}"); - sb.AppendLine ($"Type:{f.FieldType}"); - } + case PropertyInfo propertyInfo: + sb.AppendLine ($"Name:{propertyInfo.Name}"); + sb.AppendLine ($"Type:{propertyInfo.PropertyType}"); + sb.AppendLine ($"CanWrite:{propertyInfo.CanWrite}"); + sb.AppendLine ($"CanRead:{propertyInfo.CanRead}"); - if (val is EventInfo ev) - { - sb.AppendLine ($"Name:{ev.Name}"); - sb.AppendLine ("Parameters:"); + break; - foreach (ParameterInfo parameter in ev.EventHandlerType.GetMethod ("Invoke") - .GetParameters ()) - { - sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); - } - } + case FieldInfo fieldInfo: + sb.AppendLine ($"Name:{fieldInfo.Name}"); + sb.AppendLine ($"Type:{fieldInfo.FieldType}"); - if (val is MethodInfo method) - { - sb.AppendLine ($"Name:{method.Name}"); - sb.AppendLine ($"IsPublic:{method.IsPublic}"); - sb.AppendLine ($"IsStatic:{method.IsStatic}"); - sb.AppendLine ($"Parameters:{(method.GetParameters ().Any () ? "" : "None")}"); + break; - foreach (ParameterInfo parameter in method.GetParameters ()) - { - sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); - } - } + case EventInfo eventInfo: + sb.AppendLine ($"Name:{eventInfo.Name}"); + sb.AppendLine ("Parameters:"); - if (val is ConstructorInfo ctor) - { - sb.AppendLine ($"Name:{ctor.Name}"); - sb.AppendLine ($"Parameters:{(ctor.GetParameters ().Any () ? "" : "None")}"); + if (eventInfo.EventHandlerType?.GetMethod ("Invoke") is { } invokeMethod) + { + foreach (ParameterInfo parameter in invokeMethod.GetParameters ()) + { + sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); + } + } - foreach (ParameterInfo parameter in ctor.GetParameters ()) - { - sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); - } + break; + + case MethodInfo methodInfo: + sb.AppendLine ($"Name:{methodInfo.Name}"); + sb.AppendLine ($"IsPublic:{methodInfo.IsPublic}"); + sb.AppendLine ($"IsStatic:{methodInfo.IsStatic}"); + sb.AppendLine ($"Parameters:{(methodInfo.GetParameters ().Length > 0 ? string.Empty : "None")}"); + + foreach (ParameterInfo parameter in methodInfo.GetParameters ()) + { + sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); + } + + break; + + case ConstructorInfo constructorInfo: + sb.AppendLine ($"Name:{constructorInfo.Name}"); + sb.AppendLine ($"Parameters:{(constructorInfo.GetParameters ().Length > 0 ? string.Empty : "None")}"); + + foreach (ParameterInfo parameter in constructorInfo.GetParameters ()) + { + sb.AppendLine ($" {parameter.ParameterType} {parameter.Name}"); + } + + break; } _textView.Text = sb.ToString ().Replace ("\r\n", "\n"); @@ -303,7 +327,7 @@ public class ClassExplorer : Scenario Methods } - private class ShowForType + private sealed class ShowForType { public ShowForType (Showable toShow, Type type) { @@ -314,13 +338,11 @@ public class ClassExplorer : Scenario public Showable ToShow { get; } public Type Type { get; } - // Make sure to implement Equals methods on your objects if you intend to return new instances every time in ChildGetter - public override bool Equals (object obj) - { - return obj is ShowForType type && EqualityComparer.Default.Equals (Type, type.Type) && ToShow == type.ToShow; - } + public override bool Equals (object? obj) => + obj is ShowForType type && EqualityComparer.Default.Equals (Type, type.Type) && ToShow == type.ToShow; - public override int GetHashCode () { return HashCode.Combine (Type, ToShow); } - public override string ToString () { return ToShow.ToString (); } + public override int GetHashCode () => HashCode.Combine (Type, ToShow); + + public override string ToString () => ToShow.ToString (); } } diff --git a/Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs b/Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs index 2c2dc89ed..1516647d9 100644 --- a/Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs +++ b/Examples/UICatalog/Scenarios/CollectionNavigatorTester.cs @@ -1,14 +1,13 @@ -using System; -using System.Collections.Generic; +#nullable enable + using System.Collections.ObjectModel; -using System.Linq; namespace UICatalog.Scenarios; [ScenarioMetadata ( - "Collection Navigator", - "Demonstrates keyboard navigation in ListView & TreeView (CollectionNavigator)." - )] + "Collection Navigator", + "Demonstrates keyboard navigation in ListView & TreeView (CollectionNavigator)." + )] [ScenarioCategory ("Controls")] [ScenarioCategory ("ListView")] [ScenarioCategory ("TreeView")] @@ -16,120 +15,165 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Mouse and Keyboard")] public class CollectionNavigatorTester : Scenario { - private ObservableCollection _items = new ObservableCollection (new ObservableCollection () - { - "a", - "b", - "bb", - "c", - "ccc", - "ccc", - "cccc", - "ddd", - "dddd", - "dddd", - "ddddd", - "dddddd", - "ddddddd", - "this", - "this is a test", - "this was a test", - "this and", - "that and that", - "the", - "think", - "thunk", - "thunks", - "zip", - "zap", - "zoo", - "@jack", - "@sign", - "@at", - "@ateme", - "n@", - "n@brown", - ".net", - "$100.00", - "$101.00", - "$101.10", - "$101.11", - "$200.00", - "$210.99", - "$$", - "apricot", - "arm", - "丗丙业丞", - "丗丙丛", - "text", - "egg", - "candle", - " <- space", - "\t<- tab", - "\n<- newline", - "\r<- formfeed", - "q", - "quit", - "quitter" - }.ToList ()); + private ObservableCollection _items = new ( + [ + "a", + "b", + "bb", + "c", + "ccc", + "ccc", + "cccc", + "ddd", + "dddd", + "dddd", + "ddddd", + "dddddd", + "ddddddd", + "this", + "this is a test", + "this was a test", + "this and", + "that and that", + "the", + "think", + "thunk", + "thunks", + "zip", + "zap", + "zoo", + "@jack", + "@sign", + "@at", + "@ateme", + "n@", + "n@brown", + ".net", + "$100.00", + "$101.00", + "$101.10", + "$101.11", + "$200.00", + "$210.99", + "$$", + "apricot", + "arm", + "丗丙业丞", + "丗丙丛", + "text", + "egg", + "candle", + " <- space", + "\t<- tab", + "\n<- newline", + "\r<- formfeed", + "q", + "quit", + "quitter" + ] + ); - private Toplevel top; - private ListView _listView; - private TreeView _treeView; + private Window? _top; + private ListView? _listView; + private TreeView? _treeView; + private CheckBox? _allowMarkingCheckBox; + private CheckBox? _allowMultiSelectionCheckBox; - // Don't create a Window, just return the top-level view public override void Main () { Application.Init (); - top = new Toplevel { SchemeName = "Base" }; - var allowMarking = new MenuItem ("Allow _Marking", "", null) + Window top = new () { - CheckType = MenuItemCheckStyle.Checked, Checked = false + SchemeName = "Base" }; - allowMarking.Action = () => allowMarking.Checked = _listView.AllowsMarking = !_listView.AllowsMarking; + _top = top; - var allowMultiSelection = new MenuItem ("Allow Multi _Selection", "", null) + // MenuBar + MenuBar menu = new (); + + _allowMarkingCheckBox = new () { - CheckType = MenuItemCheckStyle.Checked, Checked = false + Title = "Allow _Marking" }; - allowMultiSelection.Action = () => - allowMultiSelection.Checked = - _listView.AllowsMultipleSelection = !_listView.AllowsMultipleSelection; - allowMultiSelection.CanExecute = () => (bool)allowMarking.Checked; + _allowMarkingCheckBox.CheckedStateChanged += (s, e) => + { + if (_listView is { }) + { + _listView.AllowsMarking = _allowMarkingCheckBox.CheckedState == CheckState.Checked; + } - var menu = new MenuBar + if (_allowMultiSelectionCheckBox is { }) + { + _allowMultiSelectionCheckBox.Enabled = _allowMarkingCheckBox.CheckedState == CheckState.Checked; + } + }; + + _allowMultiSelectionCheckBox = new () { - Menus = - [ - new MenuBarItem ( - "_Configure", - new [] - { - allowMarking, - allowMultiSelection, - null, - new ( - "_Quit", - $"{Application.QuitKey}", - () => Quit (), - null, - null, - (KeyCode)Application.QuitKey - ) - } - ), - new MenuBarItem ("_Quit", $"{Application.QuitKey}", () => Quit ()) - ] + Title = "Allow Multi _Selection", + Enabled = false }; + _allowMultiSelectionCheckBox.CheckedStateChanged += (s, e) => + { + if (_listView is { }) + { + _listView.AllowsMultipleSelection = + _allowMultiSelectionCheckBox.CheckedState == CheckState.Checked; + } + }; + + menu.Add ( + new MenuBarItem ( + "_Configure", + [ + new MenuItem + { + CommandView = _allowMarkingCheckBox + }, + new MenuItem + { + CommandView = _allowMultiSelectionCheckBox + }, + new MenuItem + { + Title = "_Quit", + Key = Application.QuitKey, + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_Quit", + [ + new MenuItem + { + Title = "_Quit", + Key = Application.QuitKey, + Action = Quit + } + ] + ) + ); + top.Add (menu); _items = new (_items.OrderBy (i => i, StringComparer.OrdinalIgnoreCase)); CreateListView (); - var vsep = new Line { Orientation = Orientation.Vertical, X = Pos.Right (_listView), Y = 1, Height = Dim.Fill () }; + + Line vsep = new () + { + Orientation = Orientation.Vertical, + X = Pos.Right (_listView!), + Y = 1, + Height = Dim.Fill () + }; top.Add (vsep); CreateTreeView (); @@ -140,7 +184,12 @@ public class CollectionNavigatorTester : Scenario private void CreateListView () { - var label = new Label + if (_top is null) + { + return; + } + + Label label = new () { Text = "ListView", TextAlignment = Alignment.Center, @@ -149,9 +198,9 @@ public class CollectionNavigatorTester : Scenario Width = Dim.Percent (50), Height = 1 }; - top.Add (label); + _top.Add (label); - _listView = new ListView + _listView = new () { X = 0, Y = Pos.Bottom (label), @@ -160,7 +209,7 @@ public class CollectionNavigatorTester : Scenario AllowsMarking = false, AllowsMultipleSelection = false }; - top.Add (_listView); + _top.Add (_listView); _listView.SetSource (_items); @@ -169,7 +218,12 @@ public class CollectionNavigatorTester : Scenario private void CreateTreeView () { - var label = new Label + if (_top is null || _listView is null) + { + return; + } + + Label label = new () { Text = "TreeView", TextAlignment = Alignment.Center, @@ -178,23 +232,26 @@ public class CollectionNavigatorTester : Scenario Width = Dim.Percent (50), Height = 1 }; - top.Add (label); + _top.Add (label); - _treeView = new TreeView + _treeView = new () { - X = Pos.Right (_listView) + 1, Y = Pos.Bottom (label), Width = Dim.Fill (), Height = Dim.Fill () + X = Pos.Right (_listView) + 1, + Y = Pos.Bottom (label), + Width = Dim.Fill (), + Height = Dim.Fill () }; _treeView.Style.HighlightModelTextOnly = true; - top.Add (_treeView); + _top.Add (_treeView); - var root = new TreeNode ("IsLetterOrDigit examples"); + TreeNode root = new ("IsLetterOrDigit examples"); root.Children = _items.Where (i => char.IsLetterOrDigit (i [0])) .Select (i => new TreeNode (i)) .Cast () .ToList (); _treeView.AddObject (root); - root = new TreeNode ("Non-IsLetterOrDigit examples"); + root = new ("Non-IsLetterOrDigit examples"); root.Children = _items.Where (i => !char.IsLetterOrDigit (i [0])) .Select (i => new TreeNode (i)) diff --git a/Examples/UICatalog/Scenarios/ContextMenus.cs b/Examples/UICatalog/Scenarios/ContextMenus.cs index bb3a7b19e..5baaabded 100644 --- a/Examples/UICatalog/Scenarios/ContextMenus.cs +++ b/Examples/UICatalog/Scenarios/ContextMenus.cs @@ -112,13 +112,13 @@ public class ContextMenus : Scenario { _winContextMenu = new ( [ - new MenuItemv2 + new MenuItem { Title = "C_ultures", SubMenu = GetSupportedCultureMenu (), }, new Line (), - new MenuItemv2 + new MenuItem { Title = "_Configuration...", HelpText = "Show configuration", @@ -130,12 +130,12 @@ public class ContextMenus : Scenario "Ok" ) }, - new MenuItemv2 + new MenuItem { Title = "M_ore options", SubMenu = new ( [ - new MenuItemv2 + new MenuItem { Title = "_Setup...", HelpText = "Perform setup", @@ -149,7 +149,7 @@ public class ContextMenus : Scenario ), Key = Key.T.WithCtrl }, - new MenuItemv2 + new MenuItem { Title = "_Maintenance...", HelpText = "Maintenance mode", @@ -165,7 +165,7 @@ public class ContextMenus : Scenario ]) }, new Line (), - new MenuItemv2 + new MenuItem { Title = "_Quit", Action = () => Application.RequestStop () @@ -177,14 +177,14 @@ public class ContextMenus : Scenario Application.Popover?.Register (_winContextMenu); } - private Menuv2 GetSupportedCultureMenu () + private Menu GetSupportedCultureMenu () { - List supportedCultures = []; + List supportedCultures = []; int index = -1; foreach (CultureInfo c in _cultureInfos!) { - MenuItemv2 culture = new (); + MenuItem culture = new (); culture.CommandView = new CheckBox { CanFocus = false }; @@ -215,17 +215,17 @@ public class ContextMenus : Scenario supportedCultures.Add (culture); } - Menuv2 menu = new (supportedCultures.ToArray ()); + Menu menu = new (supportedCultures.ToArray ()); return menu; - void CreateAction (List cultures, MenuItemv2 culture) + void CreateAction (List cultures, MenuItem culture) { culture.Action += () => { Thread.CurrentThread.CurrentUICulture = new (culture.HelpText); - foreach (MenuItemv2 item in cultures) + foreach (MenuItem item in cultures) { ((CheckBox)item.CommandView).CheckedState = Thread.CurrentThread.CurrentUICulture.Name == item.HelpText ? CheckState.Checked : CheckState.UnChecked; diff --git a/Examples/UICatalog/Scenarios/CsvEditor.cs b/Examples/UICatalog/Scenarios/CsvEditor.cs index f61d03b14..5690a7509 100644 --- a/Examples/UICatalog/Scenarios/CsvEditor.cs +++ b/Examples/UICatalog/Scenarios/CsvEditor.cs @@ -1,8 +1,7 @@ -using System; +#nullable enable + using System.Data; using System.Globalization; -using System.IO; -using System.Linq; using System.Text.RegularExpressions; using CsvHelper; @@ -14,99 +13,37 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Controls")] [ScenarioCategory ("Dialogs")] [ScenarioCategory ("Text and Formatting")] -[ScenarioCategory ("Dialogs")] [ScenarioCategory ("Arrangement")] [ScenarioCategory ("Files and IO")] public class CsvEditor : Scenario { - private string _currentFile; - private DataTable _currentTable; - private MenuItem _miCentered; - private MenuItem _miLeft; - private MenuItem _miRight; - private TextField _selectedCellTextField; - private TableView _tableView; + private string? _currentFile; + private DataTable? _currentTable; + private CheckBox? _miCenteredCheckBox; + private CheckBox? _miLeftCheckBox; + private CheckBox? _miRightCheckBox; + private TextField? _selectedCellTextField; + private TableView? _tableView; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel appWindow = new () + Window appWindow = new () { - Title = $"{GetName ()}" + Title = GetName () }; - //appWindow.Height = Dim.Fill (1); // status bar + // MenuBar + MenuBar menu = new (); - _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (2) }; - - var fileMenu = new MenuBarItem ( - "_File", - new MenuItem [] - { - new ("_Open CSV", "", () => Open ()), - new ("_Save", "", () => Save ()), - new ("_Quit", "Quits The App", () => Quit ()) - } - ); - - //fileMenu.Help = "Help"; - var menu = new MenuBar + _tableView = new () { - Menus = - [ - fileMenu, - new ( - "_Edit", - new MenuItem [] - { - new ("_New Column", "", () => AddColumn ()), - new ("_New Row", "", () => AddRow ()), - new ( - "_Rename Column", - "", - () => RenameColumn () - ), - new ("_Delete Column", "", () => DeleteColum ()), - new ("_Move Column", "", () => MoveColumn ()), - new ("_Move Row", "", () => MoveRow ()), - new ("_Sort Asc", "", () => Sort (true)), - new ("_Sort Desc", "", () => Sort (false)) - } - ), - new ( - "_View", - new [] - { - _miLeft = new ( - "_Align Left", - "", - () => Align (Alignment.Start) - ), - _miRight = new ( - "_Align Right", - "", - () => Align (Alignment.End) - ), - _miCentered = new ( - "_Align Centered", - "", - () => Align (Alignment.Center) - ), - - // Format requires hard typed data table, when we read a CSV everything is untyped (string) so this only works for new columns in this demo - _miCentered = new ( - "_Set Format Pattern", - "", - () => SetFormat () - ) - } - ) - ] + X = 0, + Y = Pos.Bottom (menu), + Width = Dim.Fill (), + Height = Dim.Fill (1) }; - appWindow.Add (menu); _selectedCellTextField = new () { @@ -116,50 +53,162 @@ public class CsvEditor : Scenario }; _selectedCellTextField.TextChanged += SelectedCellLabel_TextChanged; - var statusBar = new StatusBar ( - [ - new (Application.QuitKey, "Quit", Quit, "Quit!"), - new (Key.O.WithCtrl, "Open", Open, "Open a file."), - new (Key.S.WithCtrl, "Save", Save, "Save current."), - new () - { - HelpText = "Cell:", - CommandView = _selectedCellTextField, - AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast, - Enabled = false - } - ]) + // StatusBar + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", Quit, "Quit!"), + new (Key.O.WithCtrl, "Open", Open, "Open a file."), + new (Key.S.WithCtrl, "Save", Save, "Save current."), + new () + { + HelpText = "Cell:", + CommandView = _selectedCellTextField, + AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast, + Enabled = false + } + ] + ) { AlignmentModes = AlignmentModes.IgnoreFirstOrLast }; - appWindow.Add (statusBar); - appWindow.Add (_tableView); + // Setup menu checkboxes for alignment + _miLeftCheckBox = new () + { + Title = "_Align Left" + }; + _miLeftCheckBox.CheckedStateChanged += (s, e) => Align (Alignment.Start); + + _miRightCheckBox = new () + { + Title = "_Align Right" + }; + _miRightCheckBox.CheckedStateChanged += (s, e) => Align (Alignment.End); + + _miCenteredCheckBox = new () + { + Title = "_Align Centered" + }; + _miCenteredCheckBox.CheckedStateChanged += (s, e) => Align (Alignment.Center); + + MenuBarItem fileMenu = new ( + "_File", + [ + new MenuItem + { + Title = "_Open CSV", + Action = Open + }, + new MenuItem + { + Title = "_Save", + Action = Save + }, + new MenuItem + { + Title = "_Quit", + HelpText = "Quits The App", + Action = Quit + } + ] + ); + + menu.Add (fileMenu); + + menu.Add ( + new MenuBarItem ( + "_Edit", + [ + new MenuItem + { + Title = "_New Column", + Action = AddColumn + }, + new MenuItem + { + Title = "_New Row", + Action = AddRow + }, + new MenuItem + { + Title = "_Rename Column", + Action = RenameColumn + }, + new MenuItem + { + Title = "_Delete Column", + Action = DeleteColum + }, + new MenuItem + { + Title = "_Move Column", + Action = MoveColumn + }, + new MenuItem + { + Title = "_Move Row", + Action = MoveRow + }, + new MenuItem + { + Title = "_Sort Asc", + Action = () => Sort (true) + }, + new MenuItem + { + Title = "_Sort Desc", + Action = () => Sort (false) + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + CommandView = _miLeftCheckBox + }, + new MenuItem + { + CommandView = _miRightCheckBox + }, + new MenuItem + { + CommandView = _miCenteredCheckBox + }, + new MenuItem + { + Title = "_Set Format Pattern", + Action = SetFormat + } + ] + ) + ); + + appWindow.Add (menu, _tableView, statusBar); _tableView.SelectedCellChanged += OnSelectedCellChanged; _tableView.CellActivated += EditCurrentCell; _tableView.KeyDown += TableViewKeyPress; - //SetupScrollBar (); - - // Run - Start the application. Application.Run (appWindow); appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } private void AddColumn () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _tableView is null || _currentTable is null) { return; } if (GetText ("Enter column name", "Name:", "", out string colName)) { - var col = new DataColumn (colName); + DataColumn col = new (colName); int newColIdx = Math.Min ( Math.Max (0, _tableView.SelectedColumn + 1), @@ -209,7 +258,7 @@ public class CsvEditor : Scenario private void AddRow () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -224,7 +273,7 @@ public class CsvEditor : Scenario private void Align (Alignment newAlignment) { - if (NoTableLoaded ()) + if (NoTableLoaded () || _tableView is null) { return; } @@ -232,24 +281,27 @@ public class CsvEditor : Scenario ColumnStyle style = _tableView.Style.GetOrCreateColumnStyle (_tableView.SelectedColumn); style.Alignment = newAlignment; - _miLeft.Checked = style.Alignment == Alignment.Start; - _miRight.Checked = style.Alignment == Alignment.End; - _miCentered.Checked = style.Alignment == Alignment.Center; + if (_miLeftCheckBox is { }) + { + _miLeftCheckBox.CheckedState = style.Alignment == Alignment.Start ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miRightCheckBox is { }) + { + _miRightCheckBox.CheckedState = style.Alignment == Alignment.End ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miCenteredCheckBox is { }) + { + _miCenteredCheckBox.CheckedState = style.Alignment == Alignment.Center ? CheckState.Checked : CheckState.UnChecked; + } _tableView.Update (); } - private void ClearColumnStyles () - { - _tableView.Style.ColumnStyles.Clear (); - _tableView.Update (); - } - - private void CloseExample () { _tableView.Table = null; } - private void DeleteColum () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _tableView is null || _currentTable is null) { return; } @@ -272,16 +324,16 @@ public class CsvEditor : Scenario } } - private void EditCurrentCell (object sender, CellActivatedEventArgs e) + private void EditCurrentCell (object? sender, CellActivatedEventArgs e) { - if (e.Table == null) + if (e.Table is null || _currentTable is null || _tableView is null) { return; } var oldValue = _currentTable.Rows [e.Row] [e.Col].ToString (); - if (GetText ("Enter new value", _currentTable.Columns [e.Col].ColumnName, oldValue, out string newText)) + if (GetText ("Enter new value", _currentTable.Columns [e.Col].ColumnName, oldValue ?? "", out string newText)) { try { @@ -301,20 +353,20 @@ public class CsvEditor : Scenario { var okPressed = false; - var ok = new Button { Text = "Ok", IsDefault = true }; + Button ok = new () { Text = "Ok", IsDefault = true }; ok.Accepting += (s, e) => - { - okPressed = true; - Application.RequestStop (); - }; - var cancel = new Button { Text = "Cancel" }; + { + okPressed = true; + Application.RequestStop (); + }; + Button cancel = new () { Text = "Cancel" }; cancel.Accepting += (s, e) => { Application.RequestStop (); }; - var d = new Dialog { Title = title, Buttons = [ok, cancel] }; + Dialog d = new () { Title = title, Buttons = [ok, cancel] }; - var lbl = new Label { X = 0, Y = 1, Text = label }; + Label lbl = new () { X = 0, Y = 1, Text = label }; - var tf = new TextField { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; + TextField tf = new () { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; d.Add (lbl, tf); tf.SetFocus (); @@ -322,14 +374,14 @@ public class CsvEditor : Scenario Application.Run (d); d.Dispose (); - enteredText = okPressed ? tf.Text : null; + enteredText = okPressed ? tf.Text : string.Empty; return okPressed; } private void MoveColumn () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -367,7 +419,7 @@ public class CsvEditor : Scenario private void MoveRow () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -394,7 +446,7 @@ public class CsvEditor : Scenario return; } - object [] arrayItems = currentRow.ItemArray; + object?[] arrayItems = currentRow.ItemArray; _currentTable.Rows.Remove (currentRow); // Removing and Inserting the same DataRow seems to result in it loosing its values so we have to create a new instance @@ -416,7 +468,7 @@ public class CsvEditor : Scenario private bool NoTableLoaded () { - if (_tableView.Table == null) + if (_tableView?.Table is null) { MessageBox.ErrorQuery ("No Table Loaded", "No table has currently be opened", "Ok"); @@ -426,31 +478,47 @@ public class CsvEditor : Scenario return false; } - private void OnSelectedCellChanged (object sender, SelectedCellChangedEventArgs e) + private void OnSelectedCellChanged (object? sender, SelectedCellChangedEventArgs e) { + if (_selectedCellTextField is null || _tableView is null) + { + return; + } + // only update the text box if the user is not manually editing it if (!_selectedCellTextField.HasFocus) { _selectedCellTextField.Text = $"{_tableView.SelectedRow},{_tableView.SelectedColumn}"; } - if (_tableView.Table == null || _tableView.SelectedColumn == -1) + if (_tableView.Table is null || _tableView.SelectedColumn == -1) { return; } - ColumnStyle style = _tableView.Style.GetColumnStyleIfAny (_tableView.SelectedColumn); + ColumnStyle? style = _tableView.Style.GetColumnStyleIfAny (_tableView.SelectedColumn); - _miLeft.Checked = style?.Alignment == Alignment.Start; - _miRight.Checked = style?.Alignment == Alignment.End; - _miCentered.Checked = style?.Alignment == Alignment.Center; + if (_miLeftCheckBox is { }) + { + _miLeftCheckBox.CheckedState = style?.Alignment == Alignment.Start ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miRightCheckBox is { }) + { + _miRightCheckBox.CheckedState = style?.Alignment == Alignment.End ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miCenteredCheckBox is { }) + { + _miCenteredCheckBox.CheckedState = style?.Alignment == Alignment.Center ? CheckState.Checked : CheckState.UnChecked; + } } private void Open () { - var ofd = new FileDialog + FileDialog ofd = new () { - AllowedTypes = new () { new AllowedType ("Comma Separated Values", ".csv") } + AllowedTypes = [new AllowedType ("Comma Separated Values", ".csv")] }; ofd.Style.OkButtonText = "Open"; @@ -471,13 +539,13 @@ public class CsvEditor : Scenario try { - using var reader = new CsvReader (File.OpenText (filename), CultureInfo.InvariantCulture); + using CsvReader reader = new (File.OpenText (filename), CultureInfo.InvariantCulture); - var dt = new DataTable (); + DataTable dt = new (); reader.Read (); - if (reader.ReadHeader ()) + if (reader.ReadHeader () && reader.HeaderRecord is { }) { foreach (string h in reader.HeaderRecord) { @@ -501,8 +569,16 @@ public class CsvEditor : Scenario // Only set the current filename if we successfully loaded the entire file _currentFile = filename; - _selectedCellTextField.SuperView.Enabled = true; - Application.TopRunnable.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}"; + + if (_selectedCellTextField?.SuperView is { }) + { + _selectedCellTextField.SuperView.Enabled = true; + } + + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Title = $"{GetName ()} - {Path.GetFileName (_currentFile)}"; + } } catch (Exception ex) { @@ -518,7 +594,7 @@ public class CsvEditor : Scenario private void RenameColumn () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -534,17 +610,17 @@ public class CsvEditor : Scenario private void Save () { - if (_tableView.Table == null || string.IsNullOrWhiteSpace (_currentFile)) + if (_tableView?.Table is null || string.IsNullOrWhiteSpace (_currentFile) || _currentTable is null) { MessageBox.ErrorQuery ("No file loaded", "No file is currently loaded", "Ok"); return; } - using var writer = new CsvWriter ( - new StreamWriter (File.OpenWrite (_currentFile)), - CultureInfo.InvariantCulture - ); + using CsvWriter writer = new ( + new StreamWriter (File.OpenWrite (_currentFile)), + CultureInfo.InvariantCulture + ); foreach (string col in _currentTable.Columns.Cast ().Select (c => c.ColumnName)) { @@ -555,7 +631,7 @@ public class CsvEditor : Scenario foreach (DataRow row in _currentTable.Rows) { - foreach (object item in row.ItemArray) + foreach (object? item in row.ItemArray) { writer.WriteField (item); } @@ -564,8 +640,13 @@ public class CsvEditor : Scenario } } - private void SelectedCellLabel_TextChanged (object sender, EventArgs e) + private void SelectedCellLabel_TextChanged (object? sender, EventArgs e) { + if (_selectedCellTextField is null || _tableView is null) + { + return; + } + // if user is in the text control and editing the selected cell if (!_selectedCellTextField.HasFocus) { @@ -577,14 +658,14 @@ public class CsvEditor : Scenario if (match.Success) { - _tableView.SelectedColumn = int.Parse (match.Groups [1].Value); - _tableView.SelectedRow = int.Parse (match.Groups [2].Value); + _tableView.SelectedColumn = int.Parse (match.Groups [2].Value); + _tableView.SelectedRow = int.Parse (match.Groups [1].Value); } } private void SetFormat () { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -611,46 +692,19 @@ public class CsvEditor : Scenario } } - private void SetTable (DataTable dataTable) { _tableView.Table = new DataTableSource (_currentTable = dataTable); } + private void SetTable (DataTable dataTable) + { + if (_tableView is null) + { + return; + } - //private void SetupScrollBar () - //{ - // var scrollBar = new ScrollBarView (_tableView, true); - - // scrollBar.ChangedPosition += (s, e) => - // { - // _tableView.RowOffset = scrollBar.Position; - - // if (_tableView.RowOffset != scrollBar.Position) - // { - // scrollBar.Position = _tableView.RowOffset; - // } - - // _tableView.SetNeedsDraw (); - // }; - // /* - // scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => { - // tableView.LeftItem = scrollBar.OtherScrollBarView.Position; - // if (tableView.LeftItem != scrollBar.OtherScrollBarView.Position) { - // scrollBar.OtherScrollBarView.Position = tableView.LeftItem; - // } - // tableView.SetNeedsDraw (); - // };*/ - - // _tableView.DrawingContent += (s, e) => - // { - // scrollBar.Size = _tableView.Table?.Rows ?? 0; - // scrollBar.Position = _tableView.RowOffset; - - // //scrollBar.OtherScrollBarView.Size = tableView.Maxlength - 1; - // //scrollBar.OtherScrollBarView.Position = tableView.LeftItem; - // scrollBar.Refresh (); - // }; - //} + _tableView.Table = new DataTableSource (_currentTable = dataTable); + } private void Sort (bool asc) { - if (NoTableLoaded ()) + if (NoTableLoaded () || _currentTable is null || _tableView is null) { return; } @@ -668,9 +722,14 @@ public class CsvEditor : Scenario SetTable (_currentTable.DefaultView.ToTable ()); } - private void TableViewKeyPress (object sender, Key e) + private void TableViewKeyPress (object? sender, Key e) { - if (e.KeyCode == KeyCode.Delete) + if (_currentTable is null || _tableView is null) + { + return; + } + + if (e.KeyCode == Key.Delete) { if (_tableView.FullRowSelect) { diff --git a/Examples/UICatalog/Scenarios/DynamicMenuBar.cs b/Examples/UICatalog/Scenarios/DynamicMenuBar.cs deleted file mode 100644 index 28a13a916..000000000 --- a/Examples/UICatalog/Scenarios/DynamicMenuBar.cs +++ /dev/null @@ -1,1413 +0,0 @@ -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; - -#pragma warning disable CS0618 // Type or member is obsolete - -namespace UICatalog.Scenarios; - -[ScenarioMetadata ("Dynamic MenuBar", "Demonstrates how to change a MenuBar dynamically.")] -[ScenarioCategory ("Arrangement")] -[ScenarioCategory ("Menus")] -public class DynamicMenuBar : Scenario -{ - public override void Main () - { - // Init - Application.Init (); - - // Setup - Create a top-level application window and configure it. - DynamicMenuBarSample appWindow = new () - { - Title = GetQuitKeyAndName () - }; - - // Run - Start the application. - Application.Run (appWindow); - appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. - Application.Shutdown (); - } - - public class Binding - { - private readonly PropertyInfo _sourceBindingProperty; - private readonly object _sourceDataContext; - private readonly IValueConverter _valueConverter; - - public Binding ( - View source, - string sourcePropertyName, - View target, - string targetPropertyName, - IValueConverter valueConverter = null - ) - { - Target = target; - Source = source; - SourcePropertyName = sourcePropertyName; - TargetPropertyName = targetPropertyName; - _sourceDataContext = Source.GetType ().GetProperty ("DataContext").GetValue (Source); - _sourceBindingProperty = _sourceDataContext.GetType ().GetProperty (SourcePropertyName); - _valueConverter = valueConverter; - UpdateTarget (); - - var notifier = (INotifyPropertyChanged)_sourceDataContext; - - if (notifier != null) - { - notifier.PropertyChanged += (s, e) => - { - if (e.PropertyName == SourcePropertyName) - { - UpdateTarget (); - } - }; - } - } - - public View Source { get; } - public string SourcePropertyName { get; } - public View Target { get; } - public string TargetPropertyName { get; } - - private void UpdateTarget () - { - try - { - object sourceValue = _sourceBindingProperty.GetValue (_sourceDataContext); - - if (sourceValue == null) - { - return; - } - - object finalValue = _valueConverter?.Convert (sourceValue) ?? sourceValue; - - PropertyInfo targetProperty = Target.GetType ().GetProperty (TargetPropertyName); - targetProperty.SetValue (Target, finalValue); - } - catch (Exception ex) - { - MessageBox.ErrorQuery ("Binding Error", $"Binding failed: {ex}.", "Ok"); - } - } - } - - public class DynamicMenuBarDetails : FrameView - { - private bool _hasParent; - private MenuItem _menuItem; - - public DynamicMenuBarDetails (MenuItem menuItem = null, bool hasParent = false) : this () - { - _menuItem = menuItem; - _hasParent = hasParent; - Title = menuItem == null ? "Adding New Menu." : "Editing Menu."; - } - - public DynamicMenuBarDetails () - { - var lblTitle = new Label { Y = 1, Text = "Title:" }; - Add (lblTitle); - - TextTitle = new () { X = Pos.Right (lblTitle) + 2, Y = Pos.Top (lblTitle), Width = Dim.Fill () }; - Add (TextTitle); - - var lblHelp = new Label { X = Pos.Left (lblTitle), Y = Pos.Bottom (lblTitle) + 1, Text = "Help:" }; - Add (lblHelp); - - TextHelp = new () { X = Pos.Left (TextTitle), Y = Pos.Top (lblHelp), Width = Dim.Fill () }; - Add (TextHelp); - - var lblAction = new Label { X = Pos.Left (lblTitle), Y = Pos.Bottom (lblHelp) + 1, Text = "Action:" }; - Add (lblAction); - - TextAction = new () - { - X = Pos.Left (TextTitle), Y = Pos.Top (lblAction), Width = Dim.Fill (), Height = 5 - }; - Add (TextAction); - - var lblHotKey = new Label { X = Pos.Left (lblTitle), Y = Pos.Bottom (lblAction) + 5, Text = "HotKey:" }; - Add (lblHotKey); - - TextHotKey = new () - { - X = Pos.Left (TextTitle), Y = Pos.Bottom (lblAction) + 5, Width = 2, ReadOnly = true - }; - - TextHotKey.TextChanging += (s, e) => - { - if (!string.IsNullOrEmpty (e.Result) && char.IsLower (e.Result [0])) - { - e.Result = e.Result.ToUpper (); - } - }; - TextHotKey.TextChanged += (s, _) => TextHotKey.SelectAll (); - TextHotKey.SelectAll (); - Add (TextHotKey); - - CkbIsTopLevel = new () - { - X = Pos.Left (lblTitle), Y = Pos.Bottom (lblHotKey) + 1, Text = "IsTopLevel" - }; - Add (CkbIsTopLevel); - - CkbSubMenu = new () - { - X = Pos.Left (lblTitle), - Y = Pos.Bottom (CkbIsTopLevel), - CheckedState = (_menuItem == null ? !_hasParent : HasSubMenus (_menuItem)) ? CheckState.Checked : CheckState.UnChecked, - Text = "Has sub-menus" - }; - Add (CkbSubMenu); - - CkbNullCheck = new () - { - X = Pos.Left (lblTitle), Y = Pos.Bottom (CkbSubMenu), Text = "Allow null checked" - }; - Add (CkbNullCheck); - - var rChkLabels = new [] { "NoCheck", "Checked", "Radio" }; - - OsChkStyle = new () - { - X = Pos.Left (lblTitle), Y = Pos.Bottom (CkbSubMenu) + 1, Labels = rChkLabels - }; - Add (OsChkStyle); - - var lblShortcut = new Label - { - X = Pos.Right (CkbSubMenu) + 10, Y = Pos.Top (CkbSubMenu), Text = "Shortcut:" - }; - Add (lblShortcut); - - TextShortcutKey = new () - { - X = Pos.X (lblShortcut), Y = Pos.Bottom (lblShortcut), Width = Dim.Fill (), ReadOnly = true - }; - - TextShortcutKey.KeyDown += (s, e) => - { - TextShortcutKey.Text = e.ToString (); - - }; - - Add (TextShortcutKey); - - var btnShortcut = new Button - { - X = Pos.X (lblShortcut), Y = Pos.Bottom (TextShortcutKey) + 1, Text = "Clear Shortcut" - }; - btnShortcut.Accepting += (s, e) => { TextShortcutKey.Text = ""; }; - Add (btnShortcut); - - CkbIsTopLevel.CheckedStateChanging += (s, e) => - { - if ((_menuItem != null && _menuItem.Parent != null && e.Result == CheckState.Checked) - || (_menuItem == null && _hasParent && e.Result == CheckState.Checked)) - { - MessageBox.ErrorQuery ( - "Invalid IsTopLevel", - "Only menu bar can have top level menu item!", - "Ok" - ); - e.Handled = true; - - return; - } - - if (e.Result == CheckState.Checked) - { - CkbSubMenu.CheckedState = CheckState.UnChecked; - CkbSubMenu.SetNeedsDraw (); - TextHelp.Enabled = true; - TextAction.Enabled = true; - TextShortcutKey.Enabled = true; - } - else - { - if ((_menuItem == null && !_hasParent) || _menuItem.Parent == null) - { - CkbSubMenu.CheckedState = CheckState.Checked; - CkbSubMenu.SetNeedsDraw (); - TextShortcutKey.Enabled = false; - } - - TextHelp.Text = ""; - TextHelp.Enabled = false; - TextAction.Text = ""; - - TextShortcutKey.Enabled = - e.Result == CheckState.Checked && CkbSubMenu.CheckedState == CheckState.UnChecked; - } - }; - - CkbSubMenu.CheckedStateChanged += (s, e) => - { - if (e.Value == CheckState.Checked) - { - CkbIsTopLevel.CheckedState = CheckState.UnChecked; - CkbIsTopLevel.SetNeedsDraw (); - TextHelp.Text = ""; - TextHelp.Enabled = false; - TextAction.Text = ""; - TextAction.Enabled = false; - TextShortcutKey.Text = ""; - TextShortcutKey.Enabled = false; - } - else - { - if (!_hasParent) - { - CkbIsTopLevel.CheckedState = CheckState.Checked; - CkbIsTopLevel.SetNeedsDraw (); - TextShortcutKey.Enabled = true; - } - - TextHelp.Enabled = true; - TextAction.Enabled = true; - - if (_hasParent) - { - TextShortcutKey.Enabled = CkbIsTopLevel.CheckedState == CheckState.UnChecked - && e.Value == CheckState.UnChecked; - } - } - }; - - CkbNullCheck.CheckedStateChanged += (s, e) => - { - if (_menuItem != null) - { - _menuItem.AllowNullChecked = e.Value == CheckState.Checked; - } - }; - - //Add (_frmMenuDetails); - } - - public CheckBox CkbIsTopLevel { get; } - public CheckBox CkbNullCheck { get; } - public CheckBox CkbSubMenu { get; } - public OptionSelector OsChkStyle { get; } - public TextView TextAction { get; } - public TextField TextHelp { get; } - public TextField TextHotKey { get; } - public TextField TextShortcutKey { get; } - public TextField TextTitle { get; } - - public Action CreateAction (MenuItem menuItem, DynamicMenuItem item) - { - switch (menuItem.CheckType) - { - case MenuItemCheckStyle.NoCheck: - return () => MessageBox.ErrorQuery (item.Title, item.Action, "Ok"); - case MenuItemCheckStyle.Checked: - return menuItem.ToggleChecked; - case MenuItemCheckStyle.Radio: - break; - } - - return () => - { - menuItem.Checked = true; - var parent = menuItem?.Parent as MenuBarItem; - - if (parent != null) - { - MenuItem [] childrens = parent.Children; - - for (var i = 0; i < childrens.Length; i++) - { - MenuItem child = childrens [i]; - - if (child != menuItem) - { - child.Checked = false; - } - } - } - }; - } - - public void EditMenuBarItem (MenuItem menuItem) - { - if (menuItem == null) - { - _hasParent = false; - Enabled = false; - CleanEditMenuBarItem (); - - return; - } - - _hasParent = menuItem.Parent != null; - Enabled = true; - _menuItem = menuItem; - TextTitle.Text = menuItem?.Title ?? ""; - TextHelp.Text = menuItem?.Help ?? ""; - - TextAction.Text = menuItem.Action != null - ? GetTargetAction (menuItem.Action) - : string.Empty; - TextHotKey.Text = menuItem?.HotKey != Key.Empty ? menuItem.HotKey.ToString () : ""; - CkbIsTopLevel.CheckedState = IsTopLevel (menuItem) ? CheckState.Checked : CheckState.UnChecked; - CkbSubMenu.CheckedState = HasSubMenus (menuItem) ? CheckState.Checked : CheckState.UnChecked; - CkbNullCheck.CheckedState = menuItem.AllowNullChecked ? CheckState.Checked : CheckState.UnChecked; - TextHelp.Enabled = CkbSubMenu.CheckedState == CheckState.UnChecked; - TextAction.Enabled = CkbSubMenu.CheckedState == CheckState.UnChecked; - OsChkStyle.Value = (int)(menuItem?.CheckType ?? MenuItemCheckStyle.NoCheck); - TextShortcutKey.Text = menuItem?.ShortcutTag ?? ""; - - TextShortcutKey.Enabled = CkbIsTopLevel.CheckedState == CheckState.Checked && CkbSubMenu.CheckedState == CheckState.UnChecked - || CkbIsTopLevel.CheckedState == CheckState.UnChecked && CkbSubMenu.CheckedState == CheckState.UnChecked; - } - - public DynamicMenuItem EnterMenuItem () - { - var valid = false; - - if (_menuItem == null) - { - var m = new DynamicMenuItem (); - TextTitle.Text = m.Title; - TextHelp.Text = m.Help; - TextAction.Text = m.Action; - TextHotKey.Text = m.HotKey ?? string.Empty; - CkbIsTopLevel.CheckedState = CheckState.UnChecked; - CkbSubMenu.CheckedState = !_hasParent ? CheckState.Checked : CheckState.UnChecked; - CkbNullCheck.CheckedState = CheckState.UnChecked; - TextHelp.Enabled = _hasParent; - TextAction.Enabled = _hasParent; - TextShortcutKey.Enabled = _hasParent; - } - else - { - EditMenuBarItem (_menuItem); - } - - var btnOk = new Button { IsDefault = true, Text = "Ok" }; - - btnOk.Accepting += (s, e) => - { - if (string.IsNullOrEmpty (TextTitle.Text)) - { - MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok"); - } - else - { - valid = true; - Application.RequestStop (); - } - }; - var btnCancel = new Button { Text = "Cancel" }; - - btnCancel.Accepting += (s, e) => - { - TextTitle.Text = string.Empty; - Application.RequestStop (); - }; - - var dialog = new Dialog - { Title = "Enter the menu details.", Buttons = [btnOk, btnCancel], Height = Dim.Auto (DimAutoStyle.Content, 23, Application.Screen.Height) }; - - Width = Dim.Fill (); - Height = Dim.Fill () - 2; - dialog.Add (this); - TextTitle.SetFocus (); - TextTitle.CursorPosition = TextTitle.Text.Length; - Application.Run (dialog); - dialog.Dispose (); - - if (valid) - { - return new () - { - Title = TextTitle.Text, - Help = TextHelp.Text, - Action = TextAction.Text, - HotKey = TextHotKey.Text, - IsTopLevel = CkbIsTopLevel?.CheckedState == CheckState.Checked, - HasSubMenu = CkbSubMenu?.CheckedState == CheckState.Checked, - CheckStyle = OsChkStyle.Value == 0 ? MenuItemCheckStyle.NoCheck : - OsChkStyle.Value == 1 ? MenuItemCheckStyle.Checked : - MenuItemCheckStyle.Radio, - ShortcutKey = TextShortcutKey.Text, - AllowNullChecked = CkbNullCheck?.CheckedState == CheckState.Checked, - }; - } - - return null; - } - - public void UpdateParent (ref MenuItem menuItem) - { - var parent = menuItem.Parent as MenuBarItem; - int idx = parent.GetChildrenIndex (menuItem); - - if (!(menuItem is MenuBarItem)) - { - menuItem = new MenuBarItem (menuItem.Title, new MenuItem [] { }, menuItem.Parent); - - if (idx > -1) - { - parent.Children [idx] = menuItem; - } - } - else - { - menuItem = new ( - menuItem.Title, - menuItem.Help, - CreateAction (menuItem, new ()), - null, - menuItem.Parent - ); - - if (idx > -1) - { - parent.Children [idx] = menuItem; - } - } - } - - private void CleanEditMenuBarItem () - { - TextTitle.Text = ""; - TextHelp.Text = ""; - TextAction.Text = ""; - TextHotKey.Text = ""; - CkbIsTopLevel.CheckedState = CheckState.UnChecked; - CkbSubMenu.CheckedState = CheckState.UnChecked; - OsChkStyle.Value = (int)MenuItemCheckStyle.NoCheck; - TextShortcutKey.Text = ""; - } - - private string GetTargetAction (Action action) - { - object me = action.Target; - - if (me == null) - { - throw new ArgumentException (); - } - - var v = new object (); - - foreach (FieldInfo field in me.GetType ().GetFields ()) - { - if (field.Name == "item") - { - v = field.GetValue (me); - } - } - - return v == null || !(v is DynamicMenuItem item) ? string.Empty : item.Action; - } - - private bool HasSubMenus (MenuItem menuItem) - { - var menuBarItem = menuItem as MenuBarItem; - - if (menuBarItem != null && menuBarItem.Children != null && (menuBarItem.Children.Length > 0 || menuBarItem.Action == null)) - { - return true; - } - - return false; - } - - private bool IsTopLevel (MenuItem menuItem) - { - var topLevel = menuItem as MenuBarItem; - - if (topLevel != null && topLevel.Parent == null && (topLevel.Children == null || topLevel.Children.Length == 0) && topLevel.Action != null) - { - return true; - } - - return false; - } - } - - public class DynamicMenuBarSample : Window - { - private readonly ListView _lstMenus; - private MenuItem _currentEditMenuBarItem; - private MenuItem _currentMenuBarItem; - private int _currentSelectedMenuBar; - private MenuBar _menuBar; - - public DynamicMenuBarSample () - { - DataContext = new (); - - var frmDelimiter = new FrameView - { - X = Pos.Center (), - Y = 3, - Width = 25, - Height = 4, - Title = "Shortcut Delimiter:" - }; - - var txtDelimiter = new TextField - { - X = Pos.Center (), Width = 2, Text = Key.Separator.ToString () - }; - - - var frmMenu = new FrameView { Y = 7, Width = Dim.Percent (50), Height = Dim.Fill (), Title = "Menus:" }; - - var btnAddMenuBar = new Button { Y = 1, Text = "Add a MenuBar" }; - frmMenu.Add (btnAddMenuBar); - - var btnMenuBarUp = new Button { X = Pos.Center (), Text = Glyphs.UpArrow.ToString () }; - frmMenu.Add (btnMenuBarUp); - - var btnMenuBarDown = new Button { X = Pos.Center (), Y = Pos.Bottom (btnMenuBarUp), Text = Glyphs.DownArrow.ToString () }; - frmMenu.Add (btnMenuBarDown); - - var btnRemoveMenuBar = new Button { Y = 1, Text = "Remove a MenuBar" }; - - btnRemoveMenuBar.X = Pos.AnchorEnd (0) - (Pos.Right (btnRemoveMenuBar) - Pos.Left (btnRemoveMenuBar)); - frmMenu.Add (btnRemoveMenuBar); - - var btnPrevious = new Button - { - X = Pos.Left (btnAddMenuBar), Y = Pos.Top (btnAddMenuBar) + 2, Text = Glyphs.LeftArrow.ToString () - }; - frmMenu.Add (btnPrevious); - - var btnAdd = new Button { Y = Pos.Top (btnPrevious) + 2, Text = " Add " }; - btnAdd.X = Pos.AnchorEnd (); - frmMenu.Add (btnAdd); - - var btnNext = new Button { X = Pos.X (btnAdd), Y = Pos.Top (btnPrevious), Text = Glyphs.RightArrow.ToString () }; - frmMenu.Add (btnNext); - - var lblMenuBar = new Label - { - SchemeName = "Dialog", - TextAlignment = Alignment.Center, - X = Pos.Right (btnPrevious) + 1, - Y = Pos.Top (btnPrevious), - - Width = Dim.Fill () - Dim.Func (_ => btnAdd.Frame.Width + 1), - Height = 1 - }; - - lblMenuBar.TextChanged += (s, e) => - { - if (lblMenuBar.Text.Contains ("_")) - { - lblMenuBar.Text = lblMenuBar.Text.Replace ("_", ""); - } - }; - frmMenu.Add (lblMenuBar); - lblMenuBar.WantMousePositionReports = true; - lblMenuBar.CanFocus = true; - - var lblParent = new Label - { - TextAlignment = Alignment.Center, - X = Pos.Right (btnPrevious) + 1, - Y = Pos.Top (btnPrevious) + 1, - - Width = Dim.Fill () - Dim.Width (btnAdd) - 1 - }; - frmMenu.Add (lblParent); - - var btnPreviowsParent = new Button - { - X = Pos.Left (btnAddMenuBar), Y = Pos.Top (btnPrevious) + 1, Text = ".." - }; - frmMenu.Add (btnPreviowsParent); - - _lstMenus = new () - { - SchemeName = "Dialog", - X = Pos.Right (btnPrevious) + 1, - Y = Pos.Top (btnPrevious) + 2, - Width = lblMenuBar.Width, - Height = Dim.Fill (), - Source = new ListWrapper ([]) - }; - frmMenu.Add (_lstMenus); - - //lblMenuBar.TabIndex = btnPrevious.TabIndex + 1; - //_lstMenus.TabIndex = lblMenuBar.TabIndex + 1; - //btnNext.TabIndex = _lstMenus.TabIndex + 1; - //btnAdd.TabIndex = btnNext.TabIndex + 1; - - var btnRemove = new Button { X = Pos.Left (btnAdd), Y = Pos.Top (btnAdd) + 1, Text = "Remove" }; - frmMenu.Add (btnRemove); - - var btnUp = new Button { X = Pos.Right (_lstMenus) + 2, Y = Pos.Top (btnRemove) + 2, Text = Glyphs.UpArrow.ToString () }; - frmMenu.Add (btnUp); - - var btnDown = new Button { X = Pos.Right (_lstMenus) + 2, Y = Pos.Top (btnUp) + 1, Text = Glyphs.DownArrow.ToString () }; - frmMenu.Add (btnDown); - - Add (frmMenu); - - var frmMenuDetails = new DynamicMenuBarDetails - { - X = Pos.Right (frmMenu), - Y = Pos.Top (frmMenu), - Width = Dim.Fill (), - Height = Dim.Fill (2), - Title = "Menu Details:" - }; - Add (frmMenuDetails); - - btnMenuBarUp.Accepting += (s, e) => - { - int i = _currentSelectedMenuBar; - - MenuBarItem menuItem = _menuBar != null && _menuBar.Menus.Length > 0 - ? _menuBar.Menus [i] - : null; - - if (menuItem != null) - { - MenuBarItem [] menus = _menuBar.Menus; - - if (i > 0) - { - menus [i] = menus [i - 1]; - menus [i - 1] = menuItem; - _currentSelectedMenuBar = i - 1; - _menuBar.SetNeedsDraw (); - } - } - }; - - btnMenuBarDown.Accepting += (s, e) => - { - int i = _currentSelectedMenuBar; - - MenuBarItem menuItem = _menuBar != null && _menuBar.Menus.Length > 0 - ? _menuBar.Menus [i] - : null; - - if (menuItem != null) - { - MenuBarItem [] menus = _menuBar.Menus; - - if (i < menus.Length - 1) - { - menus [i] = menus [i + 1]; - menus [i + 1] = menuItem; - _currentSelectedMenuBar = i + 1; - _menuBar.SetNeedsDraw (); - } - } - }; - - btnUp.Accepting += (s, e) => - { - int i = _lstMenus.SelectedItem.Value; - MenuItem menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null; - - if (menuItem != null) - { - MenuItem [] childrens = ((MenuBarItem)_currentMenuBarItem).Children; - - if (i > 0) - { - childrens [i] = childrens [i - 1]; - childrens [i - 1] = menuItem; - DataContext.Menus [i] = DataContext.Menus [i - 1]; - - DataContext.Menus [i - 1] = - new () { Title = menuItem.Title, MenuItem = menuItem }; - _lstMenus.SelectedItem = i - 1; - } - } - }; - - btnDown.Accepting += (s, e) => - { - int i = _lstMenus.SelectedItem.Value; - MenuItem menuItem = DataContext.Menus.Count > 0 ? DataContext.Menus [i].MenuItem : null; - - if (menuItem != null) - { - MenuItem [] childrens = ((MenuBarItem)_currentMenuBarItem).Children; - - if (i < childrens.Length - 1) - { - childrens [i] = childrens [i + 1]; - childrens [i + 1] = menuItem; - DataContext.Menus [i] = DataContext.Menus [i + 1]; - - DataContext.Menus [i + 1] = - new () { Title = menuItem.Title, MenuItem = menuItem }; - _lstMenus.SelectedItem = i + 1; - } - } - }; - - btnPreviowsParent.Accepting += (s, e) => - { - if (_currentMenuBarItem != null && _currentMenuBarItem.Parent != null) - { - MenuItem mi = _currentMenuBarItem; - _currentMenuBarItem = _currentMenuBarItem.Parent as MenuBarItem; - SetListViewSource (_currentMenuBarItem, true); - int i = ((MenuBarItem)_currentMenuBarItem).GetChildrenIndex (mi); - - if (i > -1) - { - _lstMenus.SelectedItem = i; - } - - if (_currentMenuBarItem.Parent != null) - { - DataContext.Parent = _currentMenuBarItem.Title; - } - else - { - DataContext.Parent = string.Empty; - } - } - else - { - DataContext.Parent = string.Empty; - } - }; - - var btnOk = new Button { X = Pos.Right (frmMenu) + 20, Y = Pos.Bottom (frmMenuDetails), Text = "Ok" }; - Add (btnOk); - - var btnCancel = new Button { X = Pos.Right (btnOk) + 3, Y = Pos.Top (btnOk), Text = "Cancel" }; - btnCancel.Accepting += (s, e) => { SetFrameDetails (_currentEditMenuBarItem); }; - Add (btnCancel); - - txtDelimiter.TextChanging += (s, e) => - { - if (!string.IsNullOrEmpty (e.Result)) - { - Key.Separator = e.Result.ToRunes () [0]; - } - else - { - e.Handled = true; - txtDelimiter.SelectAll (); - } - }; - txtDelimiter.TextChanged += (s, _) => - { - txtDelimiter.SelectAll (); - SetFrameDetails (); - }; - frmDelimiter.Add (txtDelimiter); - txtDelimiter.SelectAll (); - Add (frmDelimiter); - - _lstMenus.SelectedItemChanged += (s, e) => { SetFrameDetails (); }; - - btnOk.Accepting += (s, e) => - { - if (string.IsNullOrEmpty (frmMenuDetails.TextTitle.Text) && _currentEditMenuBarItem != null) - { - MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok"); - } - else if (_currentEditMenuBarItem != null) - { - var menuItem = new DynamicMenuItem - { - Title = frmMenuDetails.TextTitle.Text, - Help = frmMenuDetails.TextHelp.Text, - Action = frmMenuDetails.TextAction.Text, - HotKey = frmMenuDetails.TextHotKey.Text, - IsTopLevel = frmMenuDetails.CkbIsTopLevel?.CheckedState == CheckState.Checked, - HasSubMenu = frmMenuDetails.CkbSubMenu?.CheckedState == CheckState.Checked, - CheckStyle = frmMenuDetails.OsChkStyle.Value == 0 - ? MenuItemCheckStyle.NoCheck - : frmMenuDetails.OsChkStyle.Value == 1 - ? MenuItemCheckStyle.Checked - : MenuItemCheckStyle.Radio, - ShortcutKey = frmMenuDetails.TextShortcutKey.Text - }; - UpdateMenuItem (_currentEditMenuBarItem, menuItem, _lstMenus.SelectedItem.Value); - } - }; - - btnAdd.Accepting += (s, e) => - { - if (MenuBar == null) - { - MessageBox.ErrorQuery ("Menu Bar Error", "Must add a MenuBar first!", "Ok"); - btnAddMenuBar.SetFocus (); - - return; - } - - var frameDetails = new DynamicMenuBarDetails (null, _currentMenuBarItem != null); - DynamicMenuItem item = frameDetails.EnterMenuItem (); - - if (item == null) - { - return; - } - - if (_currentMenuBarItem is not MenuBarItem) - { - var parent = _currentMenuBarItem.Parent as MenuBarItem; - int idx = parent.GetChildrenIndex (_currentMenuBarItem); - - _currentMenuBarItem = new MenuBarItem ( - _currentMenuBarItem.Title, - new MenuItem [] { }, - _currentMenuBarItem.Parent - ); - _currentMenuBarItem.CheckType = item.CheckStyle; - parent.Children [idx] = _currentMenuBarItem; - } - else - { - MenuItem newMenu = CreateNewMenu (item, _currentMenuBarItem); - var menuBarItem = _currentMenuBarItem as MenuBarItem; - menuBarItem.AddMenuBarItem (MenuBar, newMenu); - - - DataContext.Menus.Add (new () { Title = newMenu.Title, MenuItem = newMenu }); - _lstMenus.MoveDown (); - } - }; - - btnRemove.Accepting += (s, e) => - { - MenuItem menuItem = (DataContext.Menus.Count > 0 && _lstMenus.SelectedItem is {} selectedItem - ? DataContext.Menus [selectedItem].MenuItem - : _currentEditMenuBarItem); - - if (menuItem != null) - { - menuItem.RemoveMenuItem (); - - if (_currentEditMenuBarItem == menuItem) - { - _currentEditMenuBarItem = null; - - if (menuItem.Parent is null) - { - _currentSelectedMenuBar = Math.Max (Math.Min (_currentSelectedMenuBar, _menuBar.Menus.Length - 1), 0); - } - - SelectCurrentMenuBarItem (); - } - - if (_lstMenus.SelectedItem is {} selected) - { - DataContext.Menus?.RemoveAt (selected); - } - - if (_lstMenus.Source.Count > 0 && _lstMenus.SelectedItem > _lstMenus.Source.Count - 1) - { - _lstMenus.SelectedItem = _lstMenus.Source.Count - 1; - } - - if (_menuBar.Menus.Length == 0) - { - RemoveMenuBar (); - } - - _lstMenus.SetNeedsDraw (); - SetFrameDetails (); - } - }; - - _lstMenus.OpenSelectedItem += (s, e) => - { - _currentMenuBarItem = DataContext.Menus [e.Item.Value].MenuItem; - - if (!(_currentMenuBarItem is MenuBarItem)) - { - MessageBox.ErrorQuery ("Menu Open Error", "Must allows sub menus first!", "Ok"); - - return; - } - - DataContext.Parent = _currentMenuBarItem.Title; - DataContext.Menus = new (); - SetListViewSource (_currentMenuBarItem, true); - MenuItem menuBarItem = DataContext.Menus.Count > 0 ? DataContext.Menus [0].MenuItem : null; - SetFrameDetails (menuBarItem); - }; - - _lstMenus.HasFocusChanging += (s, e) => - { - MenuItem menuBarItem = _lstMenus.SelectedItem is {} selectedItem && DataContext.Menus.Count > 0 - ? DataContext.Menus [selectedItem].MenuItem - : null; - SetFrameDetails (menuBarItem); - }; - - btnNext.Accepting += (s, e) => - { - if (_menuBar != null && _currentSelectedMenuBar + 1 < _menuBar.Menus.Length) - { - _currentSelectedMenuBar++; - } - - SelectCurrentMenuBarItem (); - }; - - btnPrevious.Accepting += (s, e) => - { - if (_currentSelectedMenuBar - 1 > -1) - { - _currentSelectedMenuBar--; - } - - SelectCurrentMenuBarItem (); - }; - - lblMenuBar.HasFocusChanging += (s, e) => - { - if (_menuBar?.Menus != null) - { - _currentMenuBarItem = _menuBar.Menus [_currentSelectedMenuBar]; - SetFrameDetails (_menuBar.Menus [_currentSelectedMenuBar]); - } - }; - - btnAddMenuBar.Accepting += (s, e) => - { - var frameDetails = new DynamicMenuBarDetails (null); - DynamicMenuItem item = frameDetails.EnterMenuItem (); - - if (item == null) - { - return; - } - - if (MenuBar == null) - { - _menuBar = new (); - Add (_menuBar); - } - - var newMenu = CreateNewMenu (item) as MenuBarItem; - newMenu.AddMenuBarItem (MenuBar); - - _currentMenuBarItem = newMenu; - _currentMenuBarItem.CheckType = item.CheckStyle; - - if (Key.TryParse (item.ShortcutKey, out Key key)) - { - _currentMenuBarItem.ShortcutKey = key; - } - - _currentSelectedMenuBar = _menuBar.Menus.Length - 1; - _menuBar.Menus [_currentSelectedMenuBar] = newMenu; - lblMenuBar.Text = newMenu.Title; - SetListViewSource (_currentMenuBarItem, true); - SetFrameDetails (_menuBar.Menus [_currentSelectedMenuBar]); - _menuBar.SetNeedsDraw (); - }; - - btnRemoveMenuBar.Accepting += (s, e) => - { - if (_menuBar == null) - { - return; - } - - if (_menuBar != null && _menuBar.Menus.Length > 0) - { - _currentMenuBarItem.RemoveMenuItem (); - - - - if (_currentSelectedMenuBar - 1 >= 0 && _menuBar.Menus.Length > 0) - { - _currentSelectedMenuBar--; - } - - _currentMenuBarItem = _menuBar.Menus?.Length > 0 - ? _menuBar.Menus [_currentSelectedMenuBar] - : null; - } - - RemoveMenuBar (); - - SetListViewSource (_currentMenuBarItem, true); - SetFrameDetails (); - }; - - void RemoveMenuBar () - { - if (MenuBar != null && _currentMenuBarItem == null && _menuBar.Menus.Length == 0) - { - Remove (_menuBar); - _menuBar.Dispose (); - _menuBar = null; - DataContext.Menus = new (); - _currentMenuBarItem = null; - _currentSelectedMenuBar = -1; - lblMenuBar.Text = string.Empty; - } - else - { - lblMenuBar.Text = _menuBar.Menus [_currentSelectedMenuBar].Title; - } - } - - SetFrameDetails (); - - var ustringConverter = new UStringValueConverter (); - ListWrapperConverter listWrapperConverter = new ListWrapperConverter (); - - var bdgMenuBar = new Binding (this, "MenuBar", lblMenuBar, "Text", ustringConverter); - var bdgParent = new Binding (this, "Parent", lblParent, "Text", ustringConverter); - var bdgMenus = new Binding (this, "Menus", _lstMenus, "Source", listWrapperConverter); - - void SetFrameDetails (MenuItem menuBarItem = null) - { - MenuItem menuItem; - - if (menuBarItem == null) - { - menuItem = _lstMenus.SelectedItem is {} selectedItem && DataContext.Menus.Count > 0 - ? DataContext.Menus [selectedItem].MenuItem - : _currentEditMenuBarItem; - } - else - { - menuItem = menuBarItem; - } - - _currentEditMenuBarItem = menuItem; - frmMenuDetails.EditMenuBarItem (menuItem); - bool f = btnOk.Enabled == frmMenuDetails.Enabled; - - if (!f) - { - btnOk.Enabled = frmMenuDetails.Enabled; - btnCancel.Enabled = frmMenuDetails.Enabled; - } - } - - void SelectCurrentMenuBarItem () - { - MenuBarItem menuBarItem = null; - - if (_menuBar?.Menus is { Length: > 0 }) - { - menuBarItem = _menuBar.Menus [_currentSelectedMenuBar]; - lblMenuBar.Text = menuBarItem.Title; - } - - SetFrameDetails (menuBarItem); - _currentMenuBarItem = menuBarItem; - DataContext.Menus = new (); - SetListViewSource (_currentMenuBarItem, true); - lblParent.Text = string.Empty; - - if (_currentMenuBarItem is null) - { - lblMenuBar.Text = string.Empty; - } - } - - void SetListViewSource (MenuItem currentMenuBarItem, bool fill = false) - { - DataContext.Menus = []; - var menuBarItem = currentMenuBarItem as MenuBarItem; - - if (menuBarItem != null && menuBarItem?.Children == null) - { - return; - } - - if (!fill) - { - return; - } - - if (menuBarItem != null) - { - foreach (MenuItem child in menuBarItem?.Children) - { - var m = new DynamicMenuItemList { Title = child.Title, MenuItem = child }; - DataContext.Menus.Add (m); - } - } - } - - MenuItem CreateNewMenu (DynamicMenuItem item, MenuItem parent = null) - { - MenuItem newMenu; - - if (item.HasSubMenu) - { - newMenu = new MenuBarItem (item.Title, new MenuItem [] { }, parent); - } - else if (parent != null) - { - newMenu = new (item.Title, item.Help, null, null, parent); - newMenu.CheckType = item.CheckStyle; - newMenu.Action = frmMenuDetails.CreateAction (newMenu, item); - - if (Key.TryParse (item.ShortcutKey, out Key key)) - { - newMenu.ShortcutKey = key; - } - newMenu.AllowNullChecked = item.AllowNullChecked; - } - else if (item.IsTopLevel) - { - newMenu = new MenuBarItem (item.Title, item.Help, null); - newMenu.Action = frmMenuDetails.CreateAction (newMenu, item); - - if (Key.TryParse (item.ShortcutKey, out Key key)) - { - newMenu.ShortcutKey = key; - } - } - else - { - newMenu = new MenuBarItem (item.Title, item.Help, null); - - ((MenuBarItem)newMenu).Children [0].Action = - frmMenuDetails.CreateAction (newMenu, item); - - if (Key.TryParse (item.ShortcutKey, out Key key)) - { - ((MenuBarItem)newMenu).Children [0].ShortcutKey = key; - } - } - - return newMenu; - } - - void UpdateMenuItem (MenuItem currentEditMenuBarItem, DynamicMenuItem menuItem, int index) - { - currentEditMenuBarItem.Title = menuItem.Title; - currentEditMenuBarItem.Help = menuItem.Help; - currentEditMenuBarItem.CheckType = menuItem.CheckStyle; - - if (currentEditMenuBarItem.Parent is MenuBarItem parent - && parent.Children.Length == 1 - && currentEditMenuBarItem.CheckType == MenuItemCheckStyle.Radio) - { - currentEditMenuBarItem.Checked = true; - } - - if (menuItem.IsTopLevel && currentEditMenuBarItem is MenuBarItem) - { - ((MenuBarItem)currentEditMenuBarItem).Children = null; - - currentEditMenuBarItem.Action = - frmMenuDetails.CreateAction (currentEditMenuBarItem, menuItem); - - if (Key.TryParse (menuItem.ShortcutKey, out Key key)) - { - currentEditMenuBarItem.ShortcutKey = key; - } - - SetListViewSource (currentEditMenuBarItem, true); - } - else if (menuItem.HasSubMenu) - { - currentEditMenuBarItem.Action = null; - - if (currentEditMenuBarItem is MenuBarItem && ((MenuBarItem)currentEditMenuBarItem).Children == null) - { - ((MenuBarItem)currentEditMenuBarItem).Children = new MenuItem [] { }; - } - else if (currentEditMenuBarItem.Parent != null) - { - frmMenuDetails.UpdateParent (ref currentEditMenuBarItem); - } - else - { - currentEditMenuBarItem = - new MenuBarItem ( - currentEditMenuBarItem.Title, - new MenuItem [] { }, - currentEditMenuBarItem.Parent - ); - } - - SetListViewSource (currentEditMenuBarItem, true); - } - else if (currentEditMenuBarItem is MenuBarItem && currentEditMenuBarItem.Parent != null) - { - frmMenuDetails.UpdateParent (ref currentEditMenuBarItem); - - currentEditMenuBarItem = new ( - menuItem.Title, - menuItem.Help, - frmMenuDetails.CreateAction (currentEditMenuBarItem, menuItem), - null, - currentEditMenuBarItem.Parent - ); - } - else - { - if (currentEditMenuBarItem is MenuBarItem) - { - ((MenuBarItem)currentEditMenuBarItem).Children = null; - DataContext.Menus = new (); - } - - currentEditMenuBarItem.Action = - frmMenuDetails.CreateAction (currentEditMenuBarItem, menuItem); - - if (Key.TryParse (menuItem.ShortcutKey, out Key key)) - { - currentEditMenuBarItem.ShortcutKey = key; - } - } - - if (currentEditMenuBarItem.Parent == null) - { - DataContext.MenuBar = currentEditMenuBarItem.Title; - } - else - { - if (DataContext.Menus.Count == 0) - { - DataContext.Menus.Add ( - new () - { - Title = currentEditMenuBarItem.Title, MenuItem = currentEditMenuBarItem - } - ); - } - - DataContext.Menus [index] = - new () - { - Title = currentEditMenuBarItem.Title, MenuItem = currentEditMenuBarItem - }; - } - - currentEditMenuBarItem.CheckType = menuItem.CheckStyle; - SetFrameDetails (currentEditMenuBarItem); - } - - //_frmMenuDetails.Initialized += (s, e) => _frmMenuDetails.Enabled = false; - } - - public DynamicMenuItemModel DataContext { get; set; } - } - - public class DynamicMenuItem - { - public string Action { get; set; } = string.Empty; - public bool AllowNullChecked { get; set; } - public MenuItemCheckStyle CheckStyle { get; set; } - public bool HasSubMenu { get; set; } - public string Help { get; set; } = string.Empty; - public bool IsTopLevel { get; set; } - public string HotKey { get; set; } - public string ShortcutKey { get; set; } - public string Title { get; set; } = "_New"; - } - - public class DynamicMenuItemList - { - public MenuItem MenuItem { get; set; } - public string Title { get; set; } - public override string ToString () { return $"{Title}, {MenuItem.HotKey}, {MenuItem.ShortcutKey} "; } - } - - public class DynamicMenuItemModel : INotifyPropertyChanged - { - private string _menuBar; - private ObservableCollection _menus; - private string _parent; - public DynamicMenuItemModel () { Menus = []; } - - public string MenuBar - { - get => _menuBar; - set - { - if (value == _menuBar) - { - return; - } - - _menuBar = value; - - PropertyChanged?.Invoke ( - this, - new (GetPropertyName ()) - ); - } - } - - public ObservableCollection Menus - { - get => _menus; - set - { - if (value == _menus) - { - return; - } - - _menus = value; - - PropertyChanged?.Invoke ( - this, - new (GetPropertyName ()) - ); - } - } - - public string Parent - { - get => _parent; - set - { - if (value == _parent) - { - return; - } - - _parent = value; - - PropertyChanged?.Invoke ( - this, - new (GetPropertyName ()) - ); - } - } - - public event PropertyChangedEventHandler PropertyChanged; - public string GetPropertyName ([CallerMemberName] string propertyName = null) { return propertyName; } - } - - public interface IValueConverter - { - object Convert (object value, object parameter = null); - } - - public class ListWrapperConverter : IValueConverter - { - public object Convert (object value, object parameter = null) { return new ListWrapper ((ObservableCollection)value); } - } - - public class UStringValueConverter : IValueConverter - { - public object Convert (object value, object parameter = null) - { - byte [] data = Encoding.ASCII.GetBytes (value.ToString () ?? string.Empty); - - return StringExtensions.ToString (data); - } - } -} diff --git a/Examples/UICatalog/Scenarios/Editor.cs b/Examples/UICatalog/Scenarios/Editor.cs index b005fec55..3b2e13813 100644 --- a/Examples/UICatalog/Scenarios/Editor.cs +++ b/Examples/UICatalog/Scenarios/Editor.cs @@ -1,13 +1,9 @@ -using System; -using System.Collections.Generic; +#nullable enable + using System.Diagnostics; using System.Globalization; -using System.IO; -using System.Linq; using System.Text; using System.Text.RegularExpressions; -using System.Threading; -using static UICatalog.Scenarios.DynamicMenuBar; namespace UICatalog.Scenarios; @@ -21,313 +17,177 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Menus")] public class Editor : Scenario { - private Window _appWindow; - private List _cultureInfos; + private Window? _appWindow; + private List? _cultureInfos; private string _fileName = "demo.txt"; private bool _forceMinimumPosToZero = true; private bool _matchCase; private bool _matchWholeWord; - private MenuItem _miForceMinimumPosToZero; - private byte [] _originalText; + private CheckBox? _miForceMinimumPosToZeroCheckBox; + private byte []? _originalText; private bool _saved = true; - private TabView _tabView; - private string _textToFind; - private string _textToReplace; - private TextView _textView; - private FindReplaceWindow _findReplaceWindow; + private TabView? _tabView; + private string _textToFind = string.Empty; + private string _textToReplace = string.Empty; + private TextView? _textView; + private FindReplaceWindow? _findReplaceWindow; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. _appWindow = new () { - //Title = GetQuitKeyAndName (), Title = _fileName ?? "Untitled", BorderStyle = LineStyle.None }; - _cultureInfos = Application.SupportedCultures; + _cultureInfos = Application.SupportedCultures?.ToList (); _textView = new () { X = 0, Y = 1, Width = Dim.Fill (), - Height = Dim.Fill (1), + Height = Dim.Fill (1) }; - CreateDemoFile (_fileName); + CreateDemoFile (_fileName!); LoadFile (); _appWindow.Add (_textView); - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ("_New", "", () => New ()), - new ("_Open", "", () => Open ()), - new ("_Save", "", () => Save ()), - new ("_Save As", "", () => SaveAs ()), - new ("_Close", "", () => CloseFile ()), - null, - new ("_Quit", "", () => Quit ()) - } - ), - new ( - "_Edit", - new MenuItem [] - { - new ( - "_Copy", - "", - () => Copy (), - null, - null, - KeyCode.CtrlMask | KeyCode.C - ), - new ( - "C_ut", - "", - () => Cut (), - null, - null, - KeyCode.CtrlMask | KeyCode.W - ), - new ( - "_Paste", - "", - () => Paste (), - null, - null, - KeyCode.CtrlMask | KeyCode.Y - ), - null, - new ( - "_Find", - "", - () => Find (), - null, - null, - KeyCode.CtrlMask | KeyCode.S - ), - new ( - "Find _Next", - "", - () => FindNext (), - null, - null, - KeyCode.CtrlMask - | KeyCode.ShiftMask - | KeyCode.S - ), - new ( - "Find P_revious", - "", - () => FindPrevious (), - null, - null, - KeyCode.CtrlMask - | KeyCode.ShiftMask - | KeyCode.AltMask - | KeyCode.S - ), - new ( - "_Replace", - "", - () => Replace (), - null, - null, - KeyCode.CtrlMask | KeyCode.R - ), - new ( - "Replace Ne_xt", - "", - () => ReplaceNext (), - null, - null, - KeyCode.CtrlMask - | KeyCode.ShiftMask - | KeyCode.R - ), - new ( - "Replace Pre_vious", - "", - () => ReplacePrevious (), - null, - null, - KeyCode.CtrlMask - | KeyCode.ShiftMask - | KeyCode.AltMask - | KeyCode.R - ), - new ( - "Replace _All", - "", - () => ReplaceAll (), - null, - null, - KeyCode.CtrlMask - | KeyCode.ShiftMask - | KeyCode.AltMask - | KeyCode.A - ), - null, - new ( - "_Select All", - "", - () => SelectAll (), - null, - null, - KeyCode.CtrlMask | KeyCode.T - ) - } - ), - new ("_ScrollBarView", CreateKeepChecked ()), - new ("_Cursor", CreateCursorRadio ()), - new ( - "Forma_t", - new [] - { - CreateWrapChecked (), - CreateAutocomplete (), - CreateAllowsTabChecked (), - CreateReadOnlyChecked (), - CreateUseSameRuneTypeForWords (), - CreateSelectWordOnlyOnDoubleClick (), - new MenuItem ( - "Colors", - "", - () => _textView.PromptForColors (), - null, - null, - KeyCode.CtrlMask | KeyCode.L - ) - } - ), - new ( - "_Responder", - new [] { CreateCanFocusChecked (), CreateEnabledChecked (), CreateVisibleChecked () } - ), - new ( - "Conte_xtMenu", - new [] - { - _miForceMinimumPosToZero = new ( - "ForceMinimumPosTo_Zero", - "", - () => - { - //_miForceMinimumPosToZero.Checked = - // _forceMinimumPosToZero = - // !_forceMinimumPosToZero; + // MenuBar + MenuBar menu = new (); - //_textView.ContextMenu.ForceMinimumPosToZero = - // _forceMinimumPosToZero; - } - ) - { - CheckType = MenuItemCheckStyle.Checked, - Checked = _forceMinimumPosToZero - }, - new MenuBarItem ("_Languages", GetSupportedCultures ()) - } - ) - ] + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem { Title = "_New", Action = () => New () }, + new MenuItem { Title = "_Open", Action = Open }, + new MenuItem { Title = "_Save", Action = () => Save () }, + new MenuItem { Title = "_Save As", Action = () => SaveAs () }, + new MenuItem { Title = "_Close", Action = CloseFile }, + new MenuItem { Title = "_Quit", Action = Quit } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_Edit", + [ + new MenuItem { Title = "_Copy", Key = Key.C.WithCtrl, Action = Copy }, + new MenuItem { Title = "C_ut", Key = Key.W.WithCtrl, Action = Cut }, + new MenuItem { Title = "_Paste", Key = Key.Y.WithCtrl, Action = Paste }, + new MenuItem { Title = "_Find", Key = Key.S.WithCtrl, Action = Find }, + new MenuItem { Title = "Find _Next", Key = Key.S.WithCtrl.WithShift, Action = FindNext }, + new MenuItem { Title = "Find P_revious", Key = Key.S.WithCtrl.WithShift.WithAlt, Action = FindPrevious }, + new MenuItem { Title = "_Replace", Key = Key.R.WithCtrl, Action = Replace }, + new MenuItem { Title = "Replace Ne_xt", Key = Key.R.WithCtrl.WithShift, Action = ReplaceNext }, + new MenuItem { Title = "Replace Pre_vious", Key = Key.R.WithCtrl.WithShift.WithAlt, Action = ReplacePrevious }, + new MenuItem { Title = "Replace _All", Key = Key.A.WithCtrl.WithShift.WithAlt, Action = ReplaceAll }, + new MenuItem { Title = "_Select All", Key = Key.T.WithCtrl, Action = SelectAll } + ] + ) + ); + + menu.Add (new MenuBarItem ("_ScrollBars", CreateScrollBarsMenu ())); + menu.Add (new MenuBarItem ("_Cursor", CreateCursorRadio ())); + + menu.Add ( + new MenuBarItem ( + "Forma_t", + [ + CreateWrapChecked (), + CreateAutocomplete (), + CreateAllowsTabChecked (), + CreateReadOnlyChecked (), + CreateUseSameRuneTypeForWords (), + CreateSelectWordOnlyOnDoubleClick (), + new MenuItem { Title = "Colors", Key = Key.L.WithCtrl, Action = () => _textView?.PromptForColors () } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_View", + [CreateCanFocusChecked (), CreateEnabledChecked (), CreateVisibleChecked ()] + ) + ); + + _miForceMinimumPosToZeroCheckBox = new () + { + Title = "ForceMinimumPosTo_Zero", + CheckedState = _forceMinimumPosToZero ? CheckState.Checked : CheckState.UnChecked }; + _miForceMinimumPosToZeroCheckBox.CheckedStateChanging += (s, e) => + { + _forceMinimumPosToZero = e.Result == CheckState.Checked; + + // Note: PopoverMenu.ForceMinimumPosToZero property doesn't exist in v2 + // if (_textView?.ContextMenu is { }) + // { + // _textView.ContextMenu.ForceMinimumPosToZero = _forceMinimumPosToZero; + // } + }; + + menu.Add ( + new MenuBarItem ( + "Conte_xtMenu", + [ + new MenuItem { CommandView = _miForceMinimumPosToZeroCheckBox }, + new MenuBarItem ("_Languages", GetSupportedCultures ()) + ] + ) + ); + _appWindow.Add (menu); - var siCursorPosition = new Shortcut (KeyCode.Null, "", null); + Shortcut siCursorPosition = new (Key.Empty, "", null); - var statusBar = new StatusBar ( - new [] - { - new (Application.QuitKey, $"Quit", Quit), - new (Key.F2, "Open", Open), - new (Key.F3, "Save", () => Save ()), - new (Key.F4, "Save As", () => SaveAs ()), - new (Key.Empty, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null), - siCursorPosition, - } - ) + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", Quit), + new (Key.F2, "Open", Open), + new (Key.F3, "Save", () => Save ()), + new (Key.F4, "Save As", () => SaveAs ()), + new (Key.Empty, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null), + siCursorPosition + ] + ) { AlignmentModes = AlignmentModes.StartToEnd | AlignmentModes.IgnoreFirstOrLast }; _textView.VerticalScrollBar.AutoShow = false; - _textView.UnwrappedCursorPosition += (s, e) => - { - siCursorPosition.Title = $"Ln {e.Y + 1}, Col {e.X + 1}"; - }; + + _textView.UnwrappedCursorPosition += (s, e) => { siCursorPosition.Title = $"Ln {e.Y + 1}, Col {e.X + 1}"; }; _appWindow.Add (statusBar); - //_scrollBar = new (_textView, true); - - //_scrollBar.ChangedPosition += (s, e) => - // { - // _textView.TopRow = _scrollBar.Position; - - // if (_textView.TopRow != _scrollBar.Position) - // { - // _scrollBar.Position = _textView.TopRow; - // } - - // _textView.SetNeedsDraw (); - // }; - - //_scrollBar.OtherScrollBarView.ChangedPosition += (s, e) => - // { - // _textView.LeftColumn = _scrollBar.OtherScrollBarView.Position; - - // if (_textView.LeftColumn != _scrollBar.OtherScrollBarView.Position) - // { - // _scrollBar.OtherScrollBarView.Position = _textView.LeftColumn; - // } - - // _textView.SetNeedsDraw (); - // }; - - //_textView.DrawingContent += (s, e) => - // { - // _scrollBar.Size = _textView.Lines; - // _scrollBar.Position = _textView.TopRow; - - // if (_scrollBar.OtherScrollBarView != null) - // { - // _scrollBar.OtherScrollBarView.Size = _textView.Maxlength; - // _scrollBar.OtherScrollBarView.Position = _textView.LeftColumn; - // } - // }; - - _appWindow.Closed += (s, e) => Thread.CurrentThread.CurrentUICulture = new ("en-US"); CreateFindReplace (); - // Run - Start the application. Application.Run (_appWindow); _appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); - } private bool CanCloseFile () { + if (_textView is null || _originalText is null || _appWindow is null) + { + return true; + } + if (_textView.Text == Encoding.Unicode.GetString (_originalText)) { - //System.Diagnostics.Debug.Assert (!_textView.IsDirty); return true; } @@ -356,7 +216,7 @@ public class Editor : Scenario private void CloseFile () { - if (!CanCloseFile ()) + if (!CanCloseFile () || _textView is null) { return; } @@ -374,6 +234,11 @@ public class Editor : Scenario private void ContinueFind (bool next = true, bool replace = false) { + if (_textView is null) + { + return; + } + if (!replace && string.IsNullOrEmpty (_textToFind)) { Find (); @@ -383,7 +248,7 @@ public class Editor : Scenario if (replace && (string.IsNullOrEmpty (_textToFind) - || (_findReplaceWindow == null && string.IsNullOrEmpty (_textToReplace)))) + || (_findReplaceWindow is null && string.IsNullOrEmpty (_textToReplace)))) { Replace (); @@ -454,204 +319,466 @@ public class Editor : Scenario } } - private void Copy () + private void Copy () { _textView?.Copy (); } + + private MenuItem [] CreateScrollBarsMenu () { - if (_textView != null) + if (_textView is null) { - _textView.Copy (); + return []; } + + List menuItems = []; + + // Vertical ScrollBar AutoShow + CheckBox verticalAutoShowCheckBox = new () + { + Title = "_Vertical ScrollBar AutoShow", + CheckedState = _textView.VerticalScrollBar.AutoShow ? CheckState.Checked : CheckState.UnChecked + }; + + verticalAutoShowCheckBox.CheckedStateChanged += (s, e) => + { + _textView.VerticalScrollBar.AutoShow = verticalAutoShowCheckBox.CheckedState == CheckState.Checked; + }; + + MenuItem verticalItem = new () { CommandView = verticalAutoShowCheckBox }; + + verticalItem.Accepting += (s, e) => + { + verticalAutoShowCheckBox.AdvanceCheckState (); + e.Handled = true; + }; + + menuItems.Add (verticalItem); + + // Horizontal ScrollBar AutoShow + CheckBox horizontalAutoShowCheckBox = new () + { + Title = "_Horizontal ScrollBar AutoShow", + CheckedState = _textView.HorizontalScrollBar.AutoShow ? CheckState.Checked : CheckState.UnChecked + }; + + horizontalAutoShowCheckBox.CheckedStateChanged += (s, e) => + { + _textView.HorizontalScrollBar.AutoShow = horizontalAutoShowCheckBox.CheckedState == CheckState.Checked; + }; + + MenuItem horizontalItem = new () { CommandView = horizontalAutoShowCheckBox }; + + horizontalItem.Accepting += (s, e) => + { + horizontalAutoShowCheckBox.AdvanceCheckState (); + e.Handled = true; + }; + + menuItems.Add (horizontalItem); + + return [.. menuItems]; } - private MenuItem CreateAllowsTabChecked () + private MenuItem [] CreateCursorRadio () { - var item = new MenuItem { Title = "Allows Tab" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.AllowsTab; - item.Action += () => { _textView.AllowsTab = (bool)(item.Checked = !item.Checked); }; + if (_textView is null) + { + return []; + } + + List menuItems = []; + List radioGroup = []; + + void AddRadioItem (string title, CursorVisibility visibility) + { + CheckBox checkBox = new () + { + Title = title, + CheckedState = _textView.CursorVisibility == visibility ? CheckState.Checked : CheckState.UnChecked + }; + + radioGroup.Add (checkBox); + + checkBox.CheckedStateChanging += (s, e) => + { + if (e.Result == CheckState.Checked) + { + _textView.CursorVisibility = visibility; + + foreach (CheckBox cb in radioGroup) + { + if (cb != checkBox) + { + cb.CheckedState = CheckState.UnChecked; + } + } + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + menuItems.Add (item); + } + + AddRadioItem ("_Invisible", CursorVisibility.Invisible); + AddRadioItem ("_Box", CursorVisibility.Box); + AddRadioItem ("_Underline", CursorVisibility.Underline); + + menuItems.Add (new () { Title = "" }); + menuItems.Add (new () { Title = "xTerm :" }); + menuItems.Add (new () { Title = "" }); + + AddRadioItem (" _Default", CursorVisibility.Default); + AddRadioItem (" _Vertical", CursorVisibility.Vertical); + AddRadioItem (" V_ertical Fix", CursorVisibility.VerticalFix); + AddRadioItem (" B_ox Fix", CursorVisibility.BoxFix); + AddRadioItem (" U_nderline Fix", CursorVisibility.UnderlineFix); + + return [.. menuItems]; + } + + private MenuItem [] GetSupportedCultures () + { + if (_cultureInfos is null) + { + return []; + } + + List supportedCultures = []; + List allCheckBoxes = []; + int index = -1; + + void CreateCultureMenuItem (string title, string cultureName, bool isChecked) + { + CheckBox checkBox = new () + { + Title = title, + CheckedState = isChecked ? CheckState.Checked : CheckState.UnChecked + }; + + allCheckBoxes.Add (checkBox); + + checkBox.CheckedStateChanging += (s, e) => + { + if (e.Result == CheckState.Checked) + { + Thread.CurrentThread.CurrentUICulture = new (cultureName); + + foreach (CheckBox cb in allCheckBoxes) + { + cb.CheckedState = cb == checkBox ? CheckState.Checked : CheckState.UnChecked; + } + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + supportedCultures.Add (item); + } + + foreach (CultureInfo c in _cultureInfos) + { + if (index == -1) + { + CreateCultureMenuItem ("_English", "en-US", Thread.CurrentThread.CurrentUICulture.Name == "en-US"); + index++; + } + + CreateCultureMenuItem ($"_{c.Parent.EnglishName}", c.Name, Thread.CurrentThread.CurrentUICulture.Name == c.Name); + } + + return [.. supportedCultures]; + } + + private MenuItem CreateWrapChecked () + { + if (_textView is null) + { + return new () { Title = "Word Wrap" }; + } + + CheckBox checkBox = new () + { + Title = "Word Wrap", + CheckedState = _textView.WordWrap ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => { _textView.WordWrap = checkBox.CheckedState == CheckState.Checked; }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; return item; } private MenuItem CreateAutocomplete () { - var singleWordGenerator = new SingleWordSuggestionGenerator (); + if (_textView is null) + { + return new () { Title = "Autocomplete" }; + } + + SingleWordSuggestionGenerator singleWordGenerator = new (); _textView.Autocomplete.SuggestionGenerator = singleWordGenerator; - var auto = new MenuItem (); - auto.Title = "Autocomplete"; - auto.CheckType |= MenuItemCheckStyle.Checked; - auto.Checked = false; + CheckBox checkBox = new () + { + Title = "Autocomplete", + CheckedState = CheckState.UnChecked + }; - auto.Action += () => - { - if ((bool)(auto.Checked = !auto.Checked)) - { - // setup autocomplete with all words currently in the editor - singleWordGenerator.AllSuggestions = - Regex.Matches (_textView.Text, "\\w+") - .Select (s => s.Value) - .Distinct () - .ToList (); - } - else - { - singleWordGenerator.AllSuggestions.Clear (); - } - }; + checkBox.CheckedStateChanged += (s, e) => + { + if (checkBox.CheckedState == CheckState.Checked) + { + singleWordGenerator.AllSuggestions = + Regex.Matches (_textView.Text, "\\w+") + .Select (s => s.Value) + .Distinct () + .ToList (); + } + else + { + singleWordGenerator.AllSuggestions.Clear (); + } + }; - return auto; - } + MenuItem item = new () { CommandView = checkBox }; - private MenuItem CreateCanFocusChecked () - { - var item = new MenuItem { Title = "CanFocus" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.CanFocus; - - item.Action += () => - { - _textView.CanFocus = (bool)(item.Checked = !item.Checked); - - if (_textView.CanFocus) - { - _textView.SetFocus (); - } - }; + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; return item; } - private MenuItem [] CreateCursorRadio () + private MenuItem CreateAllowsTabChecked () { - List menuItems = new (); - - menuItems.Add ( - new ("_Invisible", "", () => SetCursor (CursorVisibility.Invisible)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility - == CursorVisibility.Invisible - } - ); - - menuItems.Add ( - new ("_Box", "", () => SetCursor (CursorVisibility.Box)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility == CursorVisibility.Box - } - ); - - menuItems.Add ( - new ("_Underline", "", () => SetCursor (CursorVisibility.Underline)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility - == CursorVisibility.Underline - } - ); - menuItems.Add (new ("", "", () => { }, () => false)); - menuItems.Add (new ("xTerm :", "", () => { }, () => false)); - menuItems.Add (new ("", "", () => { }, () => false)); - - menuItems.Add ( - new (" _Default", "", () => SetCursor (CursorVisibility.Default)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility - == CursorVisibility.Default - } - ); - - menuItems.Add ( - new (" _Vertical", "", () => SetCursor (CursorVisibility.Vertical)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility - == CursorVisibility.Vertical - } - ); - - menuItems.Add ( - new (" V_ertical Fix", "", () => SetCursor (CursorVisibility.VerticalFix)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility == CursorVisibility.VerticalFix - } - ); - - menuItems.Add ( - new (" B_ox Fix", "", () => SetCursor (CursorVisibility.BoxFix)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility - == CursorVisibility.BoxFix - } - ); - - menuItems.Add ( - new (" U_nderline Fix", "", () => SetCursor (CursorVisibility.UnderlineFix)) - { - CheckType = MenuItemCheckStyle.Radio, - Checked = _textView.CursorVisibility == CursorVisibility.UnderlineFix - } - ); - - void SetCursor (CursorVisibility visibility) + if (_textView is null) { - _textView.CursorVisibility = visibility; - var title = ""; - - switch (visibility) - { - case CursorVisibility.Default: - title = " _Default"; - - break; - case CursorVisibility.Invisible: - title = "_Invisible"; - - break; - case CursorVisibility.Underline: - title = "_Underline"; - - break; - case CursorVisibility.UnderlineFix: - title = " U_nderline Fix"; - - break; - case CursorVisibility.Vertical: - title = " _Vertical"; - - break; - case CursorVisibility.VerticalFix: - title = " V_ertical Fix"; - - break; - case CursorVisibility.Box: - title = "_Box"; - - break; - case CursorVisibility.BoxFix: - title = " B_ox Fix"; - - break; - } - - foreach (MenuItem menuItem in menuItems) - { - menuItem.Checked = menuItem.Title.Equals (title) && visibility == _textView.CursorVisibility; - } + return new () { Title = "Allows Tab" }; } - return menuItems.ToArray (); + CheckBox checkBox = new () + { + Title = "Allows Tab", + CheckedState = _textView.AllowsTab ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => { _textView.AllowsTab = checkBox.CheckedState == CheckState.Checked; }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateReadOnlyChecked () + { + if (_textView is null) + { + return new () { Title = "Read Only" }; + } + + CheckBox checkBox = new () + { + Title = "Read Only", + CheckedState = _textView.ReadOnly ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => { _textView.ReadOnly = checkBox.CheckedState == CheckState.Checked; }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateUseSameRuneTypeForWords () + { + if (_textView is null) + { + return new () { Title = "UseSameRuneTypeForWords" }; + } + + CheckBox checkBox = new () + { + Title = "UseSameRuneTypeForWords", + CheckedState = _textView.UseSameRuneTypeForWords ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => { _textView.UseSameRuneTypeForWords = checkBox.CheckedState == CheckState.Checked; }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateSelectWordOnlyOnDoubleClick () + { + if (_textView is null) + { + return new () { Title = "SelectWordOnlyOnDoubleClick" }; + } + + CheckBox checkBox = new () + { + Title = "SelectWordOnlyOnDoubleClick", + CheckedState = _textView.SelectWordOnlyOnDoubleClick ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => { _textView.SelectWordOnlyOnDoubleClick = checkBox.CheckedState == CheckState.Checked; }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateCanFocusChecked () + { + if (_textView is null) + { + return new () { Title = "CanFocus" }; + } + + CheckBox checkBox = new () + { + Title = "CanFocus", + CheckedState = _textView.CanFocus ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => + { + _textView.CanFocus = checkBox.CheckedState == CheckState.Checked; + + if (_textView.CanFocus) + { + _textView.SetFocus (); + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateEnabledChecked () + { + if (_textView is null) + { + return new () { Title = "Enabled" }; + } + + CheckBox checkBox = new () + { + Title = "Enabled", + CheckedState = _textView.Enabled ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => + { + _textView.Enabled = checkBox.CheckedState == CheckState.Checked; + + if (_textView.Enabled) + { + _textView.SetFocus (); + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + + private MenuItem CreateVisibleChecked () + { + if (_textView is null) + { + return new () { Title = "Visible" }; + } + + CheckBox checkBox = new () + { + Title = "Visible", + CheckedState = _textView.Visible ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => + { + _textView.Visible = checkBox.CheckedState == CheckState.Checked; + + if (_textView.Visible) + { + _textView.SetFocus (); + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; } private void CreateDemoFile (string fileName) { - var sb = new StringBuilder (); + StringBuilder sb = new (); - // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r sb.Append ("Hello world.\n"); sb.Append ("This is a test of the Emergency Broadcast System.\n"); @@ -667,341 +794,33 @@ public class Editor : Scenario sw.Close (); } - private MenuItem CreateEnabledChecked () - { - var item = new MenuItem { Title = "Enabled" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.Enabled; - - item.Action += () => - { - _textView.Enabled = (bool)(item.Checked = !item.Checked); - - if (_textView.Enabled) - { - _textView.SetFocus (); - } - }; - - return item; - } - - private class FindReplaceWindow : Window - { - private TextView _textView; - public FindReplaceWindow (TextView textView) - { - Title = "Find and Replace"; - - _textView = textView; - X = Pos.AnchorEnd () - 1; - Y = 2; - Width = 57; - Height = 11; - Arrangement = ViewArrangement.Movable; - - KeyBindings.Add (Key.Esc, Command.Cancel); - AddCommand (Command.Cancel, () => - { - Visible = false; - - return true; - }); - VisibleChanged += FindReplaceWindow_VisibleChanged; - Initialized += FindReplaceWindow_Initialized; - - //var btnCancel = new Button - //{ - // X = Pos.AnchorEnd (), - // Y = Pos.AnchorEnd (), - // Text = "Cancel" - //}; - //btnCancel.Accept += (s, e) => { Visible = false; }; - //Add (btnCancel); - } - - private void FindReplaceWindow_VisibleChanged (object sender, EventArgs e) - { - if (Visible == false) - { - _textView.SetFocus (); - } - else - { - FocusDeepest (NavigationDirection.Forward, null); - } - } - - private void FindReplaceWindow_Initialized (object sender, EventArgs e) - { - Border.LineStyle = LineStyle.Dashed; - Border.Thickness = new (0, 1, 0, 0); - } - } - - private void ShowFindReplace (bool isFind = true) - { - _findReplaceWindow.Visible = true; - _findReplaceWindow.SuperView.MoveSubViewToStart (_findReplaceWindow); - _tabView.SetFocus (); - _tabView.SelectedTab = isFind ? _tabView.Tabs.ToArray () [0] : _tabView.Tabs.ToArray () [1]; - _tabView.SelectedTab.View.FocusDeepest (NavigationDirection.Forward, null); - } - - private void CreateFindReplace () - { - _findReplaceWindow = new (_textView); - _tabView = new () - { - X = 0, Y = 0, - Width = Dim.Fill (), Height = Dim.Fill (0) - }; - - _tabView.AddTab (new () { DisplayText = "Find", View = CreateFindTab () }, true); - _tabView.AddTab (new () { DisplayText = "Replace", View = CreateReplaceTab () }, false); - _tabView.SelectedTabChanged += (s, e) => _tabView.SelectedTab.View.FocusDeepest (NavigationDirection.Forward, null); - _findReplaceWindow.Add (_tabView); - -// _tabView.SelectedTab.View.FocusLast (null); // Hack to get the first tab to be focused - _findReplaceWindow.Visible = false; - _appWindow.Add (_findReplaceWindow); - } - - private MenuItem [] CreateKeepChecked () - { - var item = new MenuItem (); - item.Title = "Keep Content Always In Viewport"; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = true; - //item.Action += () => _scrollBar.KeepContentAlwaysInViewport = (bool)(item.Checked = !item.Checked); - - return new [] { item }; - } - - private MenuItem CreateSelectWordOnlyOnDoubleClick () - { - var item = new MenuItem { Title = "SelectWordOnlyOnDoubleClick" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.SelectWordOnlyOnDoubleClick; - item.Action += () => _textView.SelectWordOnlyOnDoubleClick = (bool)(item.Checked = !item.Checked); - - return item; - } - - private MenuItem CreateUseSameRuneTypeForWords () - { - var item = new MenuItem { Title = "UseSameRuneTypeForWords" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.UseSameRuneTypeForWords; - item.Action += () => _textView.UseSameRuneTypeForWords = (bool)(item.Checked = !item.Checked); - - return item; - } - - private MenuItem CreateReadOnlyChecked () - { - var item = new MenuItem { Title = "Read Only" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.ReadOnly; - item.Action += () => _textView.ReadOnly = (bool)(item.Checked = !item.Checked); - - return item; - } - - private MenuItem CreateVisibleChecked () - { - var item = new MenuItem { Title = "Visible" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.Visible; - - item.Action += () => - { - _textView.Visible = (bool)(item.Checked = !item.Checked); - - if (_textView.Visible) - { - _textView.SetFocus (); - } - }; - - return item; - } - - private MenuItem CreateWrapChecked () - { - var item = new MenuItem { Title = "Word Wrap" }; - item.CheckType |= MenuItemCheckStyle.Checked; - item.Checked = _textView.WordWrap; - - item.Action += () => - { - _textView.WordWrap = (bool)(item.Checked = !item.Checked); - - if (_textView.WordWrap) - { - //_scrollBar.OtherScrollBarView.ShowScrollIndicator = false; - } - }; - - return item; - } - - private void Cut () - { - if (_textView != null) - { - _textView.Cut (); - } - } - - private void Find () { ShowFindReplace (true); } - private void FindNext () { ContinueFind (); } - private void FindPrevious () { ContinueFind (false); } - - private View CreateFindTab () - { - var d = new View () - { - Width = Dim.Fill (), - Height = Dim.Fill () - }; - - int lblWidth = "Replace:".Length; - - var label = new Label - { - Width = lblWidth, - TextAlignment = Alignment.End, - - Text = "Find:" - }; - d.Add (label); - - SetFindText (); - - var txtToFind = new TextField - { - X = Pos.Right (label) + 1, - Y = Pos.Top (label), - Width = Dim.Fill (1), - Text = _textToFind - }; - txtToFind.HasFocusChanging += (s, e) => txtToFind.Text = _textToFind; - d.Add (txtToFind); - - var btnFindNext = new Button - { - X = Pos.Align (Alignment.Center), - Y = Pos.AnchorEnd (), - Enabled = !string.IsNullOrEmpty (txtToFind.Text), - IsDefault = true, - - Text = "Find _Next" - }; - btnFindNext.Accepting += (s, e) => FindNext (); - d.Add (btnFindNext); - - var btnFindPrevious = new Button - { - X = Pos.Align (Alignment.Center), - Y = Pos.AnchorEnd (), - Enabled = !string.IsNullOrEmpty (txtToFind.Text), - Text = "Find _Previous" - }; - btnFindPrevious.Accepting += (s, e) => FindPrevious (); - d.Add (btnFindPrevious); - - txtToFind.TextChanged += (s, e) => - { - _textToFind = txtToFind.Text; - _textView.FindTextChanged (); - btnFindNext.Enabled = !string.IsNullOrEmpty (txtToFind.Text); - btnFindPrevious.Enabled = !string.IsNullOrEmpty (txtToFind.Text); - }; - - var ckbMatchCase = new CheckBox - { - X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase" - }; - ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.Result == CheckState.Checked; - d.Add (ckbMatchCase); - - var ckbMatchWholeWord = new CheckBox - { - X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word" - }; - ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.Result == CheckState.Checked; - d.Add (ckbMatchWholeWord); - return d; - } - - private MenuItem [] GetSupportedCultures () - { - List supportedCultures = new (); - int index = -1; - - foreach (CultureInfo c in _cultureInfos) - { - var culture = new MenuItem { CheckType = MenuItemCheckStyle.Checked }; - - if (index == -1) - { - culture.Title = "_English"; - culture.Help = "en-US"; - culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == "en-US"; - CreateAction (supportedCultures, culture); - supportedCultures.Add (culture); - index++; - culture = new () { CheckType = MenuItemCheckStyle.Checked }; - } - - culture.Title = $"_{c.Parent.EnglishName}"; - culture.Help = c.Name; - culture.Checked = Thread.CurrentThread.CurrentUICulture.Name == c.Name; - CreateAction (supportedCultures, culture); - supportedCultures.Add (culture); - } - - return supportedCultures.ToArray (); - - void CreateAction (List supportedCultures, MenuItem culture) - { - culture.Action += () => - { - Thread.CurrentThread.CurrentUICulture = new (culture.Help); - culture.Checked = true; - - foreach (MenuItem item in supportedCultures) - { - item.Checked = item.Help == Thread.CurrentThread.CurrentUICulture.Name; - } - }; - } - } - private void LoadFile () { - if (_fileName != null) + if (_fileName is null || _textView is null || _appWindow is null) { - // FIXED: BUGBUG: #452 TextView.LoadFile keeps file open and provides no way of closing it - _textView.Load (_fileName); - - //_textView.Text = System.IO.File.ReadAllText (_fileName); - _originalText = Encoding.Unicode.GetBytes (_textView.Text); - _appWindow.Title = _fileName; - _saved = true; + return; } + + _textView.Load (_fileName); + _originalText = Encoding.Unicode.GetBytes (_textView.Text); + _appWindow.Title = _fileName; + _saved = true; } private void New (bool checkChanges = true) { + if (_appWindow is null || _textView is null) + { + return; + } + if (checkChanges && !CanCloseFile ()) { return; } _appWindow.Title = "Untitled.txt"; - _fileName = null; + _fileName = null!; _originalText = new MemoryStream ().ToArray (); _textView.Text = Encoding.Unicode.GetString (_originalText); } @@ -1013,8 +832,8 @@ public class Editor : Scenario return; } - List aTypes = new () - { + List aTypes = + [ new AllowedType ( "Text", ".txt;.bin;.xml;.json", @@ -1024,8 +843,9 @@ public class Editor : Scenario ".json" ), new AllowedTypeAny () - }; - var d = new OpenDialog { Title = "Open", AllowedTypes = aTypes, AllowsMultipleSelection = false }; + ]; + + OpenDialog d = new () { Title = "Open", AllowedTypes = aTypes, AllowsMultipleSelection = false }; Application.Run (d); if (!d.Canceled && d.FilePaths.Count > 0) @@ -1037,13 +857,7 @@ public class Editor : Scenario d.Dispose (); } - private void Paste () - { - if (_textView != null) - { - _textView.Paste (); - } - } + private void Paste () { _textView?.Paste (); } private void Quit () { @@ -1059,7 +873,12 @@ public class Editor : Scenario private void ReplaceAll () { - if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && _findReplaceWindow == null)) + if (_textView is null) + { + return; + } + + if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && _findReplaceWindow is null)) { Replace (); @@ -1087,9 +906,14 @@ public class Editor : Scenario private void ReplaceNext () { ContinueFind (true, true); } private void ReplacePrevious () { ContinueFind (false, true); } - private View CreateReplaceTab () + private View CreateFindTab () { - var d = new View () + if (_textView is null) + { + return new (); + } + + View d = new () { Width = Dim.Fill (), Height = Dim.Fill () @@ -1097,7 +921,7 @@ public class Editor : Scenario int lblWidth = "Replace:".Length; - var label = new Label + Label label = new () { Width = lblWidth, TextAlignment = Alignment.End, @@ -1107,17 +931,104 @@ public class Editor : Scenario SetFindText (); - var txtToFind = new TextField + TextField txtToFind = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = Dim.Fill (1), Text = _textToFind }; - txtToFind.HasFocusChanging += (s, e) => txtToFind.Text = _textToFind; + txtToFind.HasFocusChanging += (s, e) => { txtToFind.Text = _textToFind; }; d.Add (txtToFind); - var btnFindNext = new Button + Button btnFindNext = new () + { + X = Pos.Align (Alignment.Center), + Y = Pos.AnchorEnd (), + Enabled = !string.IsNullOrEmpty (txtToFind.Text), + IsDefault = true, + Text = "Find _Next" + }; + btnFindNext.Accepting += (s, e) => { FindNext (); }; + d.Add (btnFindNext); + + Button btnFindPrevious = new () + { + X = Pos.Align (Alignment.Center), + Y = Pos.AnchorEnd (), + Enabled = !string.IsNullOrEmpty (txtToFind.Text), + Text = "Find _Previous" + }; + btnFindPrevious.Accepting += (s, e) => { FindPrevious (); }; + d.Add (btnFindPrevious); + + txtToFind.TextChanged += (s, e) => + { + _textToFind = txtToFind.Text; + _textView.FindTextChanged (); + btnFindNext.Enabled = !string.IsNullOrEmpty (txtToFind.Text); + btnFindPrevious.Enabled = !string.IsNullOrEmpty (txtToFind.Text); + }; + + CheckBox ckbMatchCase = new () + { + X = 0, + Y = Pos.Top (txtToFind) + 2, + CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, + Text = "Match c_ase" + }; + ckbMatchCase.CheckedStateChanging += (s, e) => { _matchCase = e.Result == CheckState.Checked; }; + d.Add (ckbMatchCase); + + CheckBox ckbMatchWholeWord = new () + { + X = 0, + Y = Pos.Top (ckbMatchCase) + 1, + CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, + Text = "Match _whole word" + }; + ckbMatchWholeWord.CheckedStateChanging += (s, e) => { _matchWholeWord = e.Result == CheckState.Checked; }; + d.Add (ckbMatchWholeWord); + + return d; + } + + private View CreateReplaceTab () + { + if (_textView is null) + { + return new (); + } + + View d = new () + { + Width = Dim.Fill (), + Height = Dim.Fill () + }; + + int lblWidth = "Replace:".Length; + + Label label = new () + { + Width = lblWidth, + TextAlignment = Alignment.End, + Text = "Find:" + }; + d.Add (label); + + SetFindText (); + + TextField txtToFind = new () + { + X = Pos.Right (label) + 1, + Y = Pos.Top (label), + Width = Dim.Fill (1), + Text = _textToFind + }; + txtToFind.HasFocusChanging += (s, e) => { txtToFind.Text = _textToFind; }; + d.Add (txtToFind); + + Button btnFindNext = new () { X = Pos.Align (Alignment.Center), Y = Pos.AnchorEnd (), @@ -1125,7 +1036,7 @@ public class Editor : Scenario IsDefault = true, Text = "Replace _Next" }; - btnFindNext.Accepting += (s, e) => ReplaceNext (); + btnFindNext.Accepting += (s, e) => { ReplaceNext (); }; d.Add (btnFindNext); label = new () @@ -1138,34 +1049,34 @@ public class Editor : Scenario SetFindText (); - var txtToReplace = new TextField + TextField txtToReplace = new () { X = Pos.Right (label) + 1, Y = Pos.Top (label), Width = Dim.Fill (1), Text = _textToReplace }; - txtToReplace.TextChanged += (s, e) => _textToReplace = txtToReplace.Text; + txtToReplace.TextChanged += (s, e) => { _textToReplace = txtToReplace.Text; }; d.Add (txtToReplace); - var btnFindPrevious = new Button + Button btnFindPrevious = new () { X = Pos.Align (Alignment.Center), Y = Pos.AnchorEnd (), Enabled = !string.IsNullOrEmpty (txtToFind.Text), Text = "Replace _Previous" }; - btnFindPrevious.Accepting += (s, e) => ReplacePrevious (); + btnFindPrevious.Accepting += (s, e) => { ReplacePrevious (); }; d.Add (btnFindPrevious); - var btnReplaceAll = new Button + Button btnReplaceAll = new () { X = Pos.Align (Alignment.Center), Y = Pos.AnchorEnd (), Enabled = !string.IsNullOrEmpty (txtToFind.Text), Text = "Replace _All" }; - btnReplaceAll.Accepting += (s, e) => ReplaceAll (); + btnReplaceAll.Accepting += (s, e) => { ReplaceAll (); }; d.Add (btnReplaceAll); txtToFind.TextChanged += (s, e) => @@ -1177,18 +1088,24 @@ public class Editor : Scenario btnReplaceAll.Enabled = !string.IsNullOrEmpty (txtToFind.Text); }; - var ckbMatchCase = new CheckBox + CheckBox ckbMatchCase = new () { - X = 0, Y = Pos.Top (txtToFind) + 2, CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, Text = "Match c_ase" + X = 0, + Y = Pos.Top (txtToFind) + 2, + CheckedState = _matchCase ? CheckState.Checked : CheckState.UnChecked, + Text = "Match c_ase" }; - ckbMatchCase.CheckedStateChanging += (s, e) => _matchCase = e.Result == CheckState.Checked; + ckbMatchCase.CheckedStateChanging += (s, e) => { _matchCase = e.Result == CheckState.Checked; }; d.Add (ckbMatchCase); - var ckbMatchWholeWord = new CheckBox + CheckBox ckbMatchWholeWord = new () { - X = 0, Y = Pos.Top (ckbMatchCase) + 1, CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, Text = "Match _whole word" + X = 0, + Y = Pos.Top (ckbMatchCase) + 1, + CheckedState = _matchWholeWord ? CheckState.Checked : CheckState.UnChecked, + Text = "Match _whole word" }; - ckbMatchWholeWord.CheckedStateChanging += (s, e) => _matchWholeWord = e.Result == CheckState.Checked; + ckbMatchWholeWord.CheckedStateChanging += (s, e) => { _matchWholeWord = e.Result == CheckState.Checked; }; d.Add (ckbMatchWholeWord); return d; @@ -1196,10 +1113,8 @@ public class Editor : Scenario private bool Save () { - if (_fileName != null) + if (_fileName is { } && _appWindow is { }) { - // FIXED: BUGBUG: #279 TextView does not know how to deal with \r\n, only \r - // As a result files saved on Windows and then read back will show invalid chars. return SaveFile (_appWindow.Title, _fileName); } @@ -1208,11 +1123,18 @@ public class Editor : Scenario private bool SaveAs () { - List aTypes = new () + if (_appWindow is null) { - new AllowedType ("Text Files", ".txt", ".bin", ".xml"), new AllowedTypeAny () - }; - var sd = new SaveDialog { Title = "Save file", AllowedTypes = aTypes }; + return false; + } + + List aTypes = + [ + new AllowedType ("Text Files", ".txt", ".bin", ".xml"), + new AllowedTypeAny () + ]; + + SaveDialog sd = new () { Title = "Save file", AllowedTypes = aTypes }; sd.Path = _appWindow.Title; Application.Run (sd); @@ -1251,6 +1173,11 @@ public class Editor : Scenario private bool SaveFile (string title, string file) { + if (_appWindow is null || _textView is null) + { + return false; + } + try { _appWindow.Title = title; @@ -1271,13 +1198,121 @@ public class Editor : Scenario return true; } - private void SelectAll () { _textView.SelectAll (); } + private void SelectAll () { _textView?.SelectAll (); } private void SetFindText () { + if (_textView is null) + { + return; + } + _textToFind = !string.IsNullOrEmpty (_textView.SelectedText) ? _textView.SelectedText : string.IsNullOrEmpty (_textToFind) ? "" : _textToFind; _textToReplace = string.IsNullOrEmpty (_textToReplace) ? "" : _textToReplace; } + + private void Cut () { _textView?.Cut (); } + + private void Find () { ShowFindReplace (); } + private void FindNext () { ContinueFind (); } + private void FindPrevious () { ContinueFind (false); } + + private void ShowFindReplace (bool isFind = true) + { + if (_findReplaceWindow is null || _tabView is null) + { + return; + } + + _findReplaceWindow.Visible = true; + _findReplaceWindow.SuperView?.MoveSubViewToStart (_findReplaceWindow); + _tabView.SetFocus (); + _tabView.SelectedTab = isFind ? _tabView.Tabs.ToArray () [0] : _tabView.Tabs.ToArray () [1]; + _tabView.SelectedTab?.View?.FocusDeepest (NavigationDirection.Forward, null); + } + + private void CreateFindReplace () + { + if (_textView is null || _appWindow is null) + { + return; + } + + _findReplaceWindow = new (_textView); + + _tabView = new () + { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill (0) + }; + + _tabView.AddTab (new () { DisplayText = "Find", View = CreateFindTab () }, true); + _tabView.AddTab (new () { DisplayText = "Replace", View = CreateReplaceTab () }, false); + + _tabView.SelectedTabChanged += (s, e) => { _tabView.SelectedTab?.View?.FocusDeepest (NavigationDirection.Forward, null); }; + + _findReplaceWindow.Add (_tabView); + _findReplaceWindow.Visible = false; + _appWindow.Add (_findReplaceWindow); + } + + private class FindReplaceWindow : Window + { + private readonly TextView _textView; + + public FindReplaceWindow (TextView textView) + { + Title = "Find and Replace"; + + _textView = textView; + X = Pos.AnchorEnd () - 1; + Y = 2; + Width = 57; + Height = 11; + Arrangement = ViewArrangement.Movable; + + KeyBindings.Add (Key.Esc, Command.Cancel); + + AddCommand ( + Command.Cancel, + () => + { + Visible = false; + + return true; + }); + + VisibleChanged += FindReplaceWindow_VisibleChanged; + Initialized += FindReplaceWindow_Initialized; + } + + private void FindReplaceWindow_Initialized (object? sender, EventArgs e) + { + if (Border is { }) + { + Border.LineStyle = LineStyle.Dashed; + Border.Thickness = new (0, 1, 0, 0); + } + } + + private void FindReplaceWindow_VisibleChanged (object? sender, EventArgs e) + { + if (!Visible) + { + _textView.SetFocus (); + } + else + { + FocusDeepest (NavigationDirection.Forward, null); + } + } + } } + + + + diff --git a/Examples/UICatalog/Scenarios/GraphViewExample.cs b/Examples/UICatalog/Scenarios/GraphViewExample.cs index 5dcd5df40..203896e68 100644 --- a/Examples/UICatalog/Scenarios/GraphViewExample.cs +++ b/Examples/UICatalog/Scenarios/GraphViewExample.cs @@ -1,9 +1,6 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; +#nullable enable + using System.Text; -using Application = Terminal.Gui.App.Application; namespace UICatalog.Scenarios; @@ -13,144 +10,53 @@ namespace UICatalog.Scenarios; public class GraphViewExample : Scenario { private readonly Thickness _thickness = new (1, 1, 1, 1); - private TextView _about; + private TextView? _about; private int _currentGraph; - private Action [] _graphs; - private GraphView _graphView; - private MenuItem _miDiags; - private MenuItem _miShowBorder; + private Action []? _graphs; + private GraphView? _graphView; + private CheckBox? _diagCheckBox; + private CheckBox? _showBorderCheckBox; private ViewDiagnosticFlags _viewDiagnostics; public override void Main () { Application.Init (); - Toplevel app = new (); - _graphs = new [] + Window app = new () { - () => SetupPeriodicTableScatterPlot (), //0 - () => SetupLifeExpectancyBarGraph (true), //1 - () => SetupLifeExpectancyBarGraph (false), //2 - () => SetupPopulationPyramid (), //3 - () => SetupLineGraph (), //4 - () => SetupSineWave (), //5 - () => SetupDisco (), //6 - () => MultiBarGraph () //7 + BorderStyle = LineStyle.None }; - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "Scatter _Plot", - "", - () => _graphs [_currentGraph = - 0] () - ), - new ( - "_V Bar Graph", - "", - () => _graphs [_currentGraph = - 1] () - ), - new ( - "_H Bar Graph", - "", - () => _graphs [_currentGraph = - 2] () - ), - new ( - "P_opulation Pyramid", - "", - () => _graphs [_currentGraph = - 3] () - ), - new ( - "_Line Graph", - "", - () => _graphs [_currentGraph = - 4] () - ), - new ( - "Sine _Wave", - "", - () => _graphs [_currentGraph = - 5] () - ), - new ( - "Silent _Disco", - "", - () => _graphs [_currentGraph = - 6] () - ), - new ( - "_Multi Bar Graph", - "", - () => _graphs [_currentGraph = - 7] () - ), - new ("_Quit", "", () => Quit ()) - } - ), - new ( - "_View", - new [] - { - new ("Zoom _In", "", () => Zoom (0.5f)), - new ("Zoom _Out", "", () => Zoom (2f)), - new ("MarginLeft++", "", () => Margin (true, true)), - new ("MarginLeft--", "", () => Margin (true, false)), - new ("MarginBottom++", "", () => Margin (false, true)), - new ("MarginBottom--", "", () => Margin (false, false)), - _miShowBorder = new ( - "_Enable Margin, Border, and Padding", - "", - () => ShowBorder () - ) - { - Checked = true, - CheckType = MenuItemCheckStyle - .Checked - }, - _miDiags = new ( - "_Diagnostics", - "", - () => ToggleDiagnostics () - ) - { - Checked = View.Diagnostics - == (ViewDiagnosticFlags - .Thickness - | ViewDiagnosticFlags - .Ruler), - CheckType = MenuItemCheckStyle.Checked - } - } - ) - ] - }; - app.Add (menu); + _graphs = + [ + SetupPeriodicTableScatterPlot, + () => SetupLifeExpectancyBarGraph (true), + () => SetupLifeExpectancyBarGraph (false), + SetupPopulationPyramid, + SetupLineGraph, + SetupSineWave, + SetupDisco, + MultiBarGraph + ]; + // MenuBar + MenuBar menu = new (); + + // GraphView _graphView = new () { X = 0, - Y = 1, + Y = Pos.Bottom (menu), Width = Dim.Percent (70), Height = Dim.Fill (1), BorderStyle = LineStyle.Single }; _graphView.Border!.Thickness = _thickness; _graphView.Margin!.Thickness = _thickness; - _graphView.Padding.Thickness = _thickness; + _graphView.Padding!.Thickness = _thickness; - app.Add (_graphView); - - var frameRight = new FrameView + // About TextView + FrameView frameRight = new () { X = Pos.Right (_graphView), Y = Pos.Top (_graphView), @@ -159,23 +65,24 @@ public class GraphViewExample : Scenario Title = "About" }; - frameRight.Add ( - _about = new () { Width = Dim.Fill (), Height = Dim.Fill (), ReadOnly = true } - ); + _about = new () + { + Width = Dim.Fill (), + Height = Dim.Fill (), + ReadOnly = true + }; + frameRight.Add (_about); - app.Add (frameRight); + // StatusBar + StatusBar statusBar = new ( + [ + new (Key.G.WithCtrl, "Next Graph", () => _graphs! [_currentGraph++ % _graphs.Length] ()), + new (Key.PageUp, "Zoom In", () => Zoom (0.5f)), + new (Key.PageDown, "Zoom Out", () => Zoom (2f)) + ] + ); - var statusBar = new StatusBar ( - new Shortcut [] - { - new (Key.G.WithCtrl, "Next Graph", () => _graphs [_currentGraph++ % _graphs.Length] ()), - new (Key.PageUp, "Zoom In", () => Zoom (0.5f)), - new (Key.PageDown, "Zoom Out", () => Zoom (2f)) - } - ); - app.Add (statusBar); - - var diagShortcut = new Shortcut + Shortcut? diagShortcut = new () { Key = Key.F10, CommandView = new CheckBox @@ -184,7 +91,128 @@ public class GraphViewExample : Scenario CanFocus = false } }; - statusBar.Add (diagShortcut).Accepting += DiagShortcut_Accept; + + statusBar.Add (diagShortcut); + diagShortcut.Accepting += DiagShortcut_Accept; + + // Menu setup + _showBorderCheckBox = new () + { + Title = "_Enable Margin, Border, and Padding", + CheckedState = CheckState.Checked + }; + _showBorderCheckBox.CheckedStateChanged += (s, e) => ShowBorder (); + + _diagCheckBox = new () + { + Title = "_Diagnostics", + CheckedState = View.Diagnostics == (ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler) + ? CheckState.Checked + : CheckState.UnChecked + }; + _diagCheckBox.CheckedStateChanged += (s, e) => ToggleDiagnostics (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "Scatter _Plot", + Action = () => _graphs [_currentGraph = 0] () + }, + new MenuItem + { + Title = "_V Bar Graph", + Action = () => _graphs [_currentGraph = 1] () + }, + new MenuItem + { + Title = "_H Bar Graph", + Action = () => _graphs [_currentGraph = 2] () + }, + new MenuItem + { + Title = "P_opulation Pyramid", + Action = () => _graphs [_currentGraph = 3] () + }, + new MenuItem + { + Title = "_Line Graph", + Action = () => _graphs [_currentGraph = 4] () + }, + new MenuItem + { + Title = "Sine _Wave", + Action = () => _graphs [_currentGraph = 5] () + }, + new MenuItem + { + Title = "Silent _Disco", + Action = () => _graphs [_currentGraph = 6] () + }, + new MenuItem + { + Title = "_Multi Bar Graph", + Action = () => _graphs [_currentGraph = 7] () + }, + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + Title = "Zoom _In", + Action = () => Zoom (0.5f) + }, + new MenuItem + { + Title = "Zoom _Out", + Action = () => Zoom (2f) + }, + new MenuItem + { + Title = "MarginLeft++", + Action = () => Margin (true, true) + }, + new MenuItem + { + Title = "MarginLeft--", + Action = () => Margin (true, false) + }, + new MenuItem + { + Title = "MarginBottom++", + Action = () => Margin (false, true) + }, + new MenuItem + { + Title = "MarginBottom--", + Action = () => Margin (false, false) + }, + new MenuItem + { + CommandView = _showBorderCheckBox + }, + new MenuItem + { + CommandView = _diagCheckBox + } + ] + ) + ); + + // Add views in order of visual appearance + app.Add (menu, _graphView, frameRight, statusBar); _graphs [_currentGraph++ % _graphs.Length] (); @@ -195,29 +223,31 @@ public class GraphViewExample : Scenario Application.Shutdown (); } - private void DiagShortcut_Accept (object sender, CommandEventArgs e) + private void DiagShortcut_Accept (object? sender, CommandEventArgs e) { ToggleDiagnostics (); if (sender is Shortcut shortcut && shortcut.CommandView is CheckBox checkBox) { - checkBox.CheckedState = _miDiags.Checked ?? false ? CheckState.Checked : CheckState.UnChecked; + checkBox.CheckedState = _diagCheckBox?.CheckedState ?? CheckState.UnChecked; } } private void ToggleDiagnostics () { - _miDiags.Checked = !_miDiags.Checked; - - View.Diagnostics = _miDiags.Checked == true - ? ViewDiagnosticFlags.Thickness - | ViewDiagnosticFlags.Ruler + View.Diagnostics = _diagCheckBox?.CheckedState == CheckState.Checked + ? ViewDiagnosticFlags.Thickness | ViewDiagnosticFlags.Ruler : ViewDiagnosticFlags.Off; Application.LayoutAndDraw (); } private void Margin (bool left, bool increase) { + if (_graphView is null) + { + return; + } + if (left) { _graphView.MarginLeft = (uint)Math.Max (0, _graphView.MarginLeft + (increase ? 1 : -1)); @@ -232,6 +262,11 @@ public class GraphViewExample : Scenario private void MultiBarGraph () { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = "Multi Bar"; @@ -241,14 +276,14 @@ public class GraphViewExample : Scenario Color fore = _graphView.GetAttributeForRole (VisualRole.Normal).Foreground == Color.Black ? Color.White : _graphView.GetAttributeForRole (VisualRole.Normal).Foreground; - var black = new Attribute (fore, Color.Black); - var cyan = new Attribute (Color.BrightCyan, Color.Black); - var magenta = new Attribute (Color.BrightMagenta, Color.Black); - var red = new Attribute (Color.BrightRed, Color.Black); + Attribute black = new (fore, Color.Black); + Attribute cyan = new (Color.BrightCyan, Color.Black); + Attribute magenta = new (Color.BrightMagenta, Color.Black); + Attribute red = new (Color.BrightRed, Color.Black); _graphView.GraphColor = black; - var series = new MultiBarSeries (3, 1, 0.25f, new [] { magenta, cyan, red }); + MultiBarSeries series = new (3, 1, 0.25f, [magenta, cyan, red]); Rune stiple = Glyphs.Stipple; @@ -277,20 +312,20 @@ public class GraphViewExample : Scenario _graphView.AxisY.Minimum = 0; - var legend = new LegendAnnotation (new (_graphView.Viewport.Width - 20, 0, 20, 5)); + LegendAnnotation legend = new (new (_graphView.Viewport.Width - 20, 0, 20, 5)); legend.AddEntry ( - new (stiple, series.SubSeries.ElementAt (0).OverrideBarColor), + new (stiple, series.SubSeries.ElementAt (0).OverrideBarColor ?? black), "Lower Third" ); legend.AddEntry ( - new (stiple, series.SubSeries.ElementAt (1).OverrideBarColor), + new (stiple, series.SubSeries.ElementAt (1).OverrideBarColor ?? cyan), "Middle Third" ); legend.AddEntry ( - new (stiple, series.SubSeries.ElementAt (2).OverrideBarColor), + new (stiple, series.SubSeries.ElementAt (2).OverrideBarColor ?? red), "Upper Third" ); _graphView.Annotations.Add (legend); @@ -300,6 +335,11 @@ public class GraphViewExample : Scenario private void SetupDisco () { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = "Graphic Equalizer"; @@ -308,11 +348,11 @@ public class GraphViewExample : Scenario _graphView.GraphColor = new Attribute (Color.White, Color.Black); - var stiple = new GraphCellToRender ((Rune)'\u2593'); + GraphCellToRender stiple = new ((Rune)'\u2593'); - var r = new Random (); - var series = new DiscoBarSeries (); - List bars = new (); + Random r = new (); + DiscoBarSeries series = new (); + List bars = []; Func genSample = () => { @@ -323,16 +363,13 @@ public class GraphViewExample : Scenario { bars.Add ( new (null, stiple, r.Next (0, 100)) - { - //ColorGetter = colorDelegate - } ); } - _graphView.SetNeedsDraw (); + _graphView?.SetNeedsDraw (); // while the equaliser is showing - return _graphView.Series.Contains (series); + return _graphView is { } && _graphView.Series.Contains (series); }; Application.AddTimeout (TimeSpan.FromMilliseconds (250), genSample); @@ -351,120 +388,45 @@ public class GraphViewExample : Scenario _graphView.SetNeedsDraw (); } - /* - Country,Both,Male,Female - -"Switzerland",83.4,81.8,85.1 -"South Korea",83.3,80.3,86.1 -"Singapore",83.2,81,85.5 -"Spain",83.2,80.7,85.7 -"Cyprus",83.1,81.1,85.1 -"Australia",83,81.3,84.8 -"Italy",83,80.9,84.9 -"Norway",83,81.2,84.7 -"Israel",82.6,80.8,84.4 -"France",82.5,79.8,85.1 -"Luxembourg",82.4,80.6,84.2 -"Sweden",82.4,80.8,84 -"Iceland",82.3,80.8,83.9 -"Canada",82.2,80.4,84.1 -"New Zealand",82,80.4,83.5 -"Malta,81.9",79.9,83.8 -"Ireland",81.8,80.2,83.5 -"Netherlands",81.8,80.4,83.1 -"Germany",81.7,78.7,84.8 -"Austria",81.6,79.4,83.8 -"Finland",81.6,79.2,84 -"Portugal",81.6,78.6,84.4 -"Belgium",81.4,79.3,83.5 -"United Kingdom",81.4,79.8,83 -"Denmark",81.3,79.6,83 -"Slovenia",81.3,78.6,84.1 -"Greece",81.1,78.6,83.6 -"Kuwait",81,79.3,83.9 -"Costa Rica",80.8,78.3,83.4*/ private void SetupLifeExpectancyBarGraph (bool verticalBars) { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = $"Life Expectancy - {(verticalBars ? "Vertical" : "Horizontal")}"; _about.Text = "This graph shows the life expectancy at birth of a range of countries"; - var softStiple = new GraphCellToRender ((Rune)'\u2591'); - var mediumStiple = new GraphCellToRender ((Rune)'\u2592'); + GraphCellToRender softStiple = new ((Rune)'\u2591'); + GraphCellToRender mediumStiple = new ((Rune)'\u2592'); - var barSeries = new BarSeries + BarSeries barSeries = new () { - Bars = new () - { + Bars = + [ new ("Switzerland", softStiple, 83.4f), - new ( - "South Korea", - !verticalBars - ? mediumStiple - : softStiple, - 83.3f - ), + new ("South Korea", !verticalBars ? mediumStiple : softStiple, 83.3f), new ("Singapore", softStiple, 83.2f), - new ( - "Spain", - !verticalBars - ? mediumStiple - : softStiple, - 83.2f - ), + new ("Spain", !verticalBars ? mediumStiple : softStiple, 83.2f), new ("Cyprus", softStiple, 83.1f), - new ( - "Australia", - !verticalBars - ? mediumStiple - : softStiple, - 83 - ), + new ("Australia", !verticalBars ? mediumStiple : softStiple, 83), new ("Italy", softStiple, 83), - new ( - "Norway", - !verticalBars - ? mediumStiple - : softStiple, - 83 - ), + new ("Norway", !verticalBars ? mediumStiple : softStiple, 83), new ("Israel", softStiple, 82.6f), - new ( - "France", - !verticalBars - ? mediumStiple - : softStiple, - 82.5f - ), + new ("France", !verticalBars ? mediumStiple : softStiple, 82.5f), new ("Luxembourg", softStiple, 82.4f), - new ( - "Sweden", - !verticalBars - ? mediumStiple - : softStiple, - 82.4f - ), + new ("Sweden", !verticalBars ? mediumStiple : softStiple, 82.4f), new ("Iceland", softStiple, 82.3f), - new ( - "Canada", - !verticalBars - ? mediumStiple - : softStiple, - 82.2f - ), + new ("Canada", !verticalBars ? mediumStiple : softStiple, 82.2f), new ("New Zealand", softStiple, 82), - new ( - "Malta", - !verticalBars - ? mediumStiple - : softStiple, - 81.9f - ), + new ("Malta", !verticalBars ? mediumStiple : softStiple, 81.9f), new ("Ireland", softStiple, 81.8f) - } + ] }; _graphView.Series.Add (barSeries); @@ -526,50 +488,62 @@ public class GraphViewExample : Scenario private void SetupLineGraph () { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = "Line"; _about.Text = "This graph shows random points"; - var black = new Attribute (_graphView.GetAttributeForRole (VisualRole.Normal).Foreground, Color.Black, _graphView.GetAttributeForRole (VisualRole.Normal).Style); - var cyan = new Attribute (Color.BrightCyan, Color.Black); - var magenta = new Attribute (Color.BrightMagenta, Color.Black); - var red = new Attribute (Color.BrightRed, Color.Black); + Attribute black = new ( + _graphView.GetAttributeForRole (VisualRole.Normal).Foreground, + Color.Black, + _graphView.GetAttributeForRole (VisualRole.Normal).Style); + Attribute cyan = new (Color.BrightCyan, Color.Black); + Attribute magenta = new (Color.BrightMagenta, Color.Black); + Attribute red = new (Color.BrightRed, Color.Black); _graphView.GraphColor = black; - List randomPoints = new (); + List randomPoints = []; - var r = new Random (); + Random r = new (); for (var i = 0; i < 10; i++) { randomPoints.Add (new (r.Next (100), r.Next (100))); } - var points = new ScatterSeries { Points = randomPoints }; + ScatterSeries points = new () { Points = randomPoints }; - var line = new PathAnnotation + PathAnnotation line = new () { - LineColor = cyan, Points = randomPoints.OrderBy (p => p.X).ToList (), BeforeSeries = true + LineColor = cyan, + Points = randomPoints.OrderBy (p => p.X).ToList (), + BeforeSeries = true }; _graphView.Series.Add (points); _graphView.Annotations.Add (line); - randomPoints = new (); + randomPoints = []; for (var i = 0; i < 10; i++) { randomPoints.Add (new (r.Next (100), r.Next (100))); } - var points2 = new ScatterSeries { Points = randomPoints, Fill = new ((Rune)'x', red) }; + ScatterSeries points2 = new () { Points = randomPoints, Fill = new ((Rune)'x', red) }; - var line2 = new PathAnnotation + PathAnnotation line2 = new () { - LineColor = magenta, Points = randomPoints.OrderBy (p => p.X).ToList (), BeforeSeries = true + LineColor = magenta, + Points = randomPoints.OrderBy (p => p.X).ToList (), + BeforeSeries = true }; _graphView.Series.Add (points2); @@ -609,6 +583,11 @@ public class GraphViewExample : Scenario private void SetupPeriodicTableScatterPlot () { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = "Scatter Plot"; @@ -620,8 +599,8 @@ public class GraphViewExample : Scenario _graphView.Series.Add ( new ScatterSeries { - Points = new () - { + Points = + [ new (1, 1.007f), new (2, 4.002f), new (3, 6.941f), @@ -737,7 +716,7 @@ public class GraphViewExample : Scenario new (116, 292), new (117, 295), new (118, 294) - } + ] } ); @@ -764,29 +743,10 @@ public class GraphViewExample : Scenario private void SetupPopulationPyramid () { - /* - Age,M,F -0-4,2009363,1915127 -5-9,2108550,2011016 -10-14,2022370,1933970 -15-19,1880611,1805522 -20-24,2072674,2001966 -25-29,2275138,2208929 -30-34,2361054,2345774 -35-39,2279836,2308360 -40-44,2148253,2159877 -45-49,2128343,2167778 -50-54,2281421,2353119 -55-59,2232388,2306537 -60-64,1919839,1985177 -65-69,1647391,1734370 -70-74,1624635,1763853 -75-79,1137438,1304709 -80-84,766956,969611 -85-89,438663,638892 -90-94,169952,320625 -95-99,34524,95559 -100+,3016,12818*/ + if (_graphView is null || _about is null) + { + return; + } _about.Text = "This graph shows population of each age divided by gender"; @@ -816,16 +776,16 @@ public class GraphViewExample : Scenario _graphView.AxisY.ShowLabelsEvery = 0; _graphView.AxisY.Minimum = 0; - var stiple = new GraphCellToRender (Glyphs.Stipple); + GraphCellToRender stiple = new (Glyphs.Stipple); // Bars in 2 directions // Males (negative to make the bars go left) - var malesSeries = new BarSeries + BarSeries malesSeries = new () { Orientation = Orientation.Horizontal, - Bars = new () - { + Bars = + [ new ("0-4", stiple, -2009363), new ("5-9", stiple, -2108550), new ("10-14", stiple, -2022370), @@ -847,16 +807,16 @@ public class GraphViewExample : Scenario new ("90-94", stiple, -169952), new ("95-99", stiple, -34524), new ("100+", stiple, -3016) - } + ] }; _graphView.Series.Add (malesSeries); // Females - var femalesSeries = new BarSeries + BarSeries femalesSeries = new () { Orientation = Orientation.Horizontal, - Bars = new () - { + Bars = + [ new ("0-4", stiple, 1915127), new ("5-9", stiple, 2011016), new ("10-14", stiple, 1933970), @@ -878,11 +838,11 @@ public class GraphViewExample : Scenario new ("90-94", stiple, 320625), new ("95-99", stiple, 95559), new ("100+", stiple, 12818) - } + ] }; - var softStiple = new GraphCellToRender ((Rune)'\u2591'); - var mediumStiple = new GraphCellToRender ((Rune)'\u2592'); + GraphCellToRender softStiple = new ((Rune)'\u2591'); + GraphCellToRender mediumStiple = new ((Rune)'\u2592'); for (var i = 0; i < malesSeries.Bars.Count; i++) { @@ -903,14 +863,19 @@ public class GraphViewExample : Scenario private void SetupSineWave () { + if (_graphView is null || _about is null) + { + return; + } + _graphView.Reset (); _graphView.Title = "Sine Wave"; _about.Text = "This graph shows a sine wave"; - var points = new ScatterSeries (); - var line = new PathAnnotation (); + ScatterSeries points = new (); + PathAnnotation line = new (); // Draw line first so it does not draw over top of points or axis labels line.BeforeSeries = true; @@ -950,25 +915,33 @@ public class GraphViewExample : Scenario private void ShowBorder () { - _miShowBorder.Checked = !_miShowBorder.Checked; + if (_graphView is null) + { + return; + } - if (_miShowBorder.Checked == true) + if (_showBorderCheckBox?.CheckedState == CheckState.Checked) { _graphView.BorderStyle = LineStyle.Single; _graphView.Border!.Thickness = _thickness; _graphView.Margin!.Thickness = _thickness; - _graphView.Padding.Thickness = _thickness; + _graphView.Padding!.Thickness = _thickness; } else { _graphView.BorderStyle = LineStyle.None; _graphView.Margin!.Thickness = Thickness.Empty; - _graphView.Padding.Thickness = Thickness.Empty; + _graphView.Padding!.Thickness = Thickness.Empty; } } private void Zoom (float factor) { + if (_graphView is null) + { + return; + } + _graphView.CellSize = new ( _graphView.CellSize.X * factor, _graphView.CellSize.Y * factor @@ -980,7 +953,7 @@ public class GraphViewExample : Scenario _graphView.SetNeedsDraw (); } - private class DiscoBarSeries : BarSeries + private sealed class DiscoBarSeries : BarSeries { private readonly Attribute _brightgreen; private readonly Attribute _brightred; @@ -999,35 +972,22 @@ public class GraphViewExample : Scenario protected override void DrawBarLine (GraphView graph, Point start, Point end, BarSeriesBar beingDrawn) { - IDriver driver = Application.Driver; - int x = start.X; for (int y = end.Y; y <= start.Y; y++) { float height = graph.ScreenToGraphSpace (x, y).Y; - if (height >= 85) - { - graph.SetAttribute (_red); - } - else if (height >= 66) - { - graph.SetAttribute (_brightred); - } - else if (height >= 45) - { - graph.SetAttribute (_brightyellow); - } - else if (height >= 25) - { - graph.SetAttribute (_brightgreen); - } - else - { - graph.SetAttribute (_green); - } + Attribute attr = height switch + { + >= 85 => _red, + >= 66 => _brightred, + >= 45 => _brightyellow, + >= 25 => _brightgreen, + _ => _green + }; + graph.SetAttribute (attr); graph.AddRune (x, y, beingDrawn.Fill.Rune); } } diff --git a/Examples/UICatalog/Scenarios/HexEditor.cs b/Examples/UICatalog/Scenarios/HexEditor.cs index 9b13e1656..45abe08ac 100644 --- a/Examples/UICatalog/Scenarios/HexEditor.cs +++ b/Examples/UICatalog/Scenarios/HexEditor.cs @@ -14,7 +14,7 @@ public class HexEditor : Scenario { private string? _fileName; private HexView? _hexView; - private MenuItemv2? _miReadOnly; + private MenuItem? _miReadOnly; private bool _saved = true; private Shortcut? _scAddress; private Shortcut? _scInfo; @@ -49,13 +49,13 @@ public class HexEditor : Scenario app.Add (_hexView); - var menu = new MenuBarv2 + var menu = new MenuBar { Menus = [ new ( "_File", - new MenuItemv2 [] + new MenuItem [] { new ("_New", "", New), new ("_Open", "", Open), @@ -66,7 +66,7 @@ public class HexEditor : Scenario ), new ( "_Edit", - new MenuItemv2 [] + new MenuItem [] { new ("_Copy", "", Copy), new ("C_ut", "", Cut), @@ -75,7 +75,7 @@ public class HexEditor : Scenario ), new ( "_Options", - new MenuItemv2 [] + new MenuItem [] { _miReadOnly = new ( "_Read Only", diff --git a/Examples/UICatalog/Scenarios/InteractiveTree.cs b/Examples/UICatalog/Scenarios/InteractiveTree.cs index c3b414901..a91448c10 100644 --- a/Examples/UICatalog/Scenarios/InteractiveTree.cs +++ b/Examples/UICatalog/Scenarios/InteractiveTree.cs @@ -1,4 +1,4 @@ -using System.Linq; +#nullable enable namespace UICatalog.Scenarios; @@ -7,46 +7,54 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TreeView")] public class InteractiveTree : Scenario { - private TreeView _treeView; + private TreeView? _treeView; public override void Main () { Application.Init (); - var appWindow = new Toplevel () + + Window appWindow = new () { Title = GetName (), + BorderStyle = LineStyle.None }; - var menu = new MenuBar - { - Menus = - [ - new ("_File", new MenuItem [] { new ("_Quit", "", Quit) }) - ] - }; - appWindow.Add (menu); + // MenuBar + MenuBar menu = new (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); _treeView = new () { X = 0, - Y = 1, + Y = Pos.Bottom (menu), Width = Dim.Fill (), Height = Dim.Fill (1) }; _treeView.KeyDown += TreeView_KeyPress; - appWindow.Add (_treeView); + // StatusBar + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", Quit), + new (Key.C.WithCtrl, "Add Child", AddChildNode), + new (Key.T.WithCtrl, "Add Root", AddRootNode), + new (Key.R.WithCtrl, "Rename Node", RenameNode) + ] + ); - var statusBar = new StatusBar ( - new Shortcut [] - { - new (Application.QuitKey, "Quit", Quit), - new (Key.C.WithCtrl, "Add Child", AddChildNode), - new (Key.T.WithCtrl, "Add Root", AddRootNode), - new (Key.R.WithCtrl, "Rename Node", RenameNode) - } - ); - appWindow.Add (statusBar); + appWindow.Add (menu, _treeView, statusBar); Application.Run (appWindow); appWindow.Dispose (); @@ -55,9 +63,14 @@ public class InteractiveTree : Scenario private void AddChildNode () { - ITreeNode node = _treeView.SelectedObject; + if (_treeView is null) + { + return; + } - if (node != null) + ITreeNode? node = _treeView.SelectedObject; + + if (node is { }) { if (GetText ("Text", "Enter text for node:", "", out string entered)) { @@ -69,6 +82,11 @@ public class InteractiveTree : Scenario private void AddRootNode () { + if (_treeView is null) + { + return; + } + if (GetText ("Text", "Enter text for node:", "", out string entered)) { _treeView.AddObject (new TreeNode (entered)); @@ -79,20 +97,20 @@ public class InteractiveTree : Scenario { var okPressed = false; - var ok = new Button { Text = "Ok", IsDefault = true }; + Button ok = new () { Text = "Ok", IsDefault = true }; ok.Accepting += (s, e) => - { - okPressed = true; - Application.RequestStop (); - }; - var cancel = new Button { Text = "Cancel" }; + { + okPressed = true; + Application.RequestStop (); + }; + Button cancel = new () { Text = "Cancel" }; cancel.Accepting += (s, e) => Application.RequestStop (); - var d = new Dialog { Title = title, Buttons = [ok, cancel] }; + Dialog d = new () { Title = title, Buttons = [ok, cancel] }; - var lbl = new Label { X = 0, Y = 1, Text = label }; + Label lbl = new () { X = 0, Y = 1, Text = label }; - var tf = new TextField { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; + TextField tf = new () { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; d.Add (lbl, tf); tf.SetFocus (); @@ -100,7 +118,7 @@ public class InteractiveTree : Scenario Application.Run (d); d.Dispose (); - enteredText = okPressed ? tf.Text : null; + enteredText = okPressed ? tf.Text : string.Empty; return okPressed; } @@ -109,9 +127,14 @@ public class InteractiveTree : Scenario private void RenameNode () { - ITreeNode node = _treeView.SelectedObject; + if (_treeView is null) + { + return; + } - if (node != null) + ITreeNode? node = _treeView.SelectedObject; + + if (node is { }) { if (GetText ("Text", "Enter text for node:", node.Text, out string entered)) { @@ -121,13 +144,18 @@ public class InteractiveTree : Scenario } } - private void TreeView_KeyPress (object sender, Key obj) + private void TreeView_KeyPress (object? sender, Key obj) { + if (_treeView is null) + { + return; + } + if (obj.KeyCode == Key.Delete) { - ITreeNode toDelete = _treeView.SelectedObject; + ITreeNode? toDelete = _treeView.SelectedObject; - if (toDelete == null) + if (toDelete is null) { return; } @@ -141,9 +169,9 @@ public class InteractiveTree : Scenario } else { - ITreeNode parent = _treeView.GetParent (toDelete); + ITreeNode? parent = _treeView.GetParent (toDelete); - if (parent == null) + if (parent is null) { MessageBox.ErrorQuery ( "Could not delete", diff --git a/Examples/UICatalog/Scenarios/ListColumns.cs b/Examples/UICatalog/Scenarios/ListColumns.cs index 5861acaad..8ed35942e 100644 --- a/Examples/UICatalog/Scenarios/ListColumns.cs +++ b/Examples/UICatalog/Scenarios/ListColumns.cs @@ -1,6 +1,6 @@ -using System; +#nullable enable + using System.Collections; -using System.Collections.Generic; using System.Data; namespace UICatalog.Scenarios; @@ -13,19 +13,19 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Scrolling")] public class ListColumns : Scenario { - private Scheme _alternatingScheme; - private DataTable _currentTable; - private TableView _listColView; - private MenuItem _miAlternatingColors; - private MenuItem _miAlwaysUseNormalColorForVerticalCellLines; - private MenuItem _miBottomline; - private MenuItem _miCellLines; - private MenuItem _miCursor; - private MenuItem _miExpandLastColumn; - private MenuItem _miOrientVertical; - private MenuItem _miScrollParallel; - private MenuItem _miSmoothScrolling; - private MenuItem _miTopline; + private Scheme? _alternatingScheme; + private DataTable? _currentTable; + private TableView? _listColView; + private CheckBox? _alternatingColorsCheckBox; + private CheckBox? _alwaysUseNormalColorForVerticalCellLinesCheckBox; + private CheckBox? _bottomlineCheckBox; + private CheckBox? _cellLinesCheckBox; + private CheckBox? _cursorCheckBox; + private CheckBox? _expandLastColumnCheckBox; + private CheckBox? _orientVerticalCheckBox; + private CheckBox? _scrollParallelCheckBox; + private CheckBox? _smoothScrollingCheckBox; + private CheckBox? _toplineCheckBox; /// /// Builds a simple list in which values are the index. This helps testing that scrolling etc is working @@ -35,7 +35,7 @@ public class ListColumns : Scenario /// public static IList BuildSimpleList (int items) { - List list = new (); + List list = []; for (var i = 0; i < items; i++) { @@ -47,20 +47,22 @@ public class ListColumns : Scenario public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel top = new (); Window appWindow = new () { - Title = GetQuitKeyAndName () + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None }; + // MenuBar + MenuBar menuBar = new (); + _listColView = new () { + Y = Pos.Bottom(menuBar), Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill (1), Style = new () { ShowHeaders = false, @@ -70,178 +72,43 @@ public class ListColumns : Scenario ExpandLastColumn = false } }; - var listColStyle = new ListColumnStyle (); + ListColumnStyle listColStyle = new (); - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "Open_BigListExample", - "", - () => OpenSimpleList (true) - ), - new ( - "Open_SmListExample", - "", - () => OpenSimpleList (false) - ), - new ( - "_CloseExample", - "", - () => CloseExample () - ), - new ("_Quit", "", () => Quit ()) - } - ), - new ( - "_View", - new [] - { - _miTopline = - new ("_TopLine", "", () => ToggleTopline ()) - { - Checked = _listColView.Style - .ShowHorizontalHeaderOverline, - CheckType = MenuItemCheckStyle.Checked - }, - _miBottomline = new ( - "_BottomLine", - "", - () => ToggleBottomline () - ) - { - Checked = _listColView.Style - .ShowHorizontalBottomline, - CheckType = MenuItemCheckStyle.Checked - }, - _miCellLines = new ( - "_CellLines", - "", - () => ToggleCellLines () - ) - { - Checked = _listColView.Style - .ShowVerticalCellLines, - CheckType = MenuItemCheckStyle.Checked - }, - _miExpandLastColumn = new ( - "_ExpandLastColumn", - "", - () => ToggleExpandLastColumn () - ) - { - Checked = _listColView.Style.ExpandLastColumn, - CheckType = MenuItemCheckStyle.Checked - }, - _miAlwaysUseNormalColorForVerticalCellLines = - new ( - "_AlwaysUseNormalColorForVerticalCellLines", - "", - () => - ToggleAlwaysUseNormalColorForVerticalCellLines () - ) - { - Checked = _listColView.Style - .AlwaysUseNormalColorForVerticalCellLines, - CheckType = MenuItemCheckStyle.Checked - }, - _miSmoothScrolling = new ( - "_SmoothHorizontalScrolling", - "", - () => ToggleSmoothScrolling () - ) - { - Checked = _listColView.Style - .SmoothHorizontalScrolling, - CheckType = MenuItemCheckStyle.Checked - }, - _miAlternatingColors = new ( - "Alternating Colors", - "", - () => ToggleAlternatingColors () - ) { CheckType = MenuItemCheckStyle.Checked }, - _miCursor = new ( - "Invert Selected Cell First Character", - "", - () => - ToggleInvertSelectedCellFirstCharacter () - ) - { - Checked = _listColView.Style - .InvertSelectedCellFirstCharacter, - CheckType = MenuItemCheckStyle.Checked - } - } - ), - new ( - "_List", - new [] - { - //new MenuItem ("_Hide Headers", "", HideHeaders), - _miOrientVertical = new ( - "_OrientVertical", - "", - () => ToggleVerticalOrientation () - ) - { - Checked = listColStyle.Orientation - == Orientation.Vertical, - CheckType = MenuItemCheckStyle.Checked - }, - _miScrollParallel = new ( - "_ScrollParallel", - "", - () => ToggleScrollParallel () - ) - { - Checked = listColStyle.ScrollParallel, - CheckType = MenuItemCheckStyle.Checked - }, - new ("Set _Max Cell Width", "", SetListMaxWidth), - new ("Set Mi_n Cell Width", "", SetListMinWidth) - } - ) - ] - }; - var statusBar = new StatusBar ( - new Shortcut [] - { - new (Key.F2, "OpenBigListEx", () => OpenSimpleList (true)), - new (Key.F3, "CloseExample", CloseExample), - new (Key.F4, "OpenSmListEx", () => OpenSimpleList (false)), - new (Application.QuitKey, "Quit", Quit) - } - ); - appWindow.Add (_listColView); + // Status Bar + StatusBar statusBar = new ( + [ + new (Key.F2, "OpenBigListEx", () => OpenSimpleList (true)), + new (Key.F3, "CloseExample", CloseExample), + new (Key.F4, "OpenSmListEx", () => OpenSimpleList (false)), + new (Application.QuitKey, "Quit", Quit) + ] + ); - var selectedCellLabel = new Label + // Selected cell label + Label selectedCellLabel = new () { X = 0, Y = Pos.Bottom (_listColView), Text = "0,0", - Width = Dim.Fill (), TextAlignment = Alignment.End }; - appWindow.Add (selectedCellLabel); - - _listColView.SelectedCellChanged += (s, e) => { selectedCellLabel.Text = $"{_listColView.SelectedRow},{_listColView.SelectedColumn}"; }; + _listColView.SelectedCellChanged += (s, e) => + { + if (_listColView is { }) + { + selectedCellLabel.Text = $"{_listColView.SelectedRow},{_listColView.SelectedColumn}"; + } + }; _listColView.KeyDown += TableViewKeyPress; - //SetupScrollBar (); - _alternatingScheme = new () { - Disabled = appWindow.GetAttributeForRole(VisualRole.Disabled), + Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled), HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus), - Focus = appWindow.GetAttributeForRole(VisualRole.Focus), + Focus = appWindow.GetAttributeForRole (VisualRole.Focus), Normal = new (Color.White, Color.BrightBlue) }; @@ -250,37 +117,210 @@ public class ListColumns : Scenario _listColView.KeyBindings.ReplaceCommands (Key.Space, Command.Accept); - top.Add (menu, appWindow, statusBar); - appWindow.Y = 1; - appWindow.Height = Dim.Fill(Dim.Func (_ => statusBar.Frame.Height)); + // Setup menu checkboxes + _toplineCheckBox = new () + { + Title = "_TopLine", + CheckedState = _listColView.Style.ShowHorizontalHeaderOverline ? CheckState.Checked : CheckState.UnChecked + }; + _toplineCheckBox.CheckedStateChanged += (s, e) => ToggleTopline (); - // Run - Start the application. - Application.Run (top); - top.Dispose (); + _bottomlineCheckBox = new () + { + Title = "_BottomLine", + CheckedState = _listColView.Style.ShowHorizontalBottomline ? CheckState.Checked : CheckState.UnChecked + }; + _bottomlineCheckBox.CheckedStateChanged += (s, e) => ToggleBottomline (); - // Shutdown - Calling Application.Shutdown is required. + _cellLinesCheckBox = new () + { + Title = "_CellLines", + CheckedState = _listColView.Style.ShowVerticalCellLines ? CheckState.Checked : CheckState.UnChecked + }; + _cellLinesCheckBox.CheckedStateChanged += (s, e) => ToggleCellLines (); + + _expandLastColumnCheckBox = new () + { + Title = "_ExpandLastColumn", + CheckedState = _listColView.Style.ExpandLastColumn ? CheckState.Checked : CheckState.UnChecked + }; + _expandLastColumnCheckBox.CheckedStateChanged += (s, e) => ToggleExpandLastColumn (); + + _alwaysUseNormalColorForVerticalCellLinesCheckBox = new () + { + Title = "_AlwaysUseNormalColorForVerticalCellLines", + CheckedState = _listColView.Style.AlwaysUseNormalColorForVerticalCellLines ? CheckState.Checked : CheckState.UnChecked + }; + _alwaysUseNormalColorForVerticalCellLinesCheckBox.CheckedStateChanged += (s, e) => ToggleAlwaysUseNormalColorForVerticalCellLines (); + + _smoothScrollingCheckBox = new () + { + Title = "_SmoothHorizontalScrolling", + CheckedState = _listColView.Style.SmoothHorizontalScrolling ? CheckState.Checked : CheckState.UnChecked + }; + _smoothScrollingCheckBox.CheckedStateChanged += (s, e) => ToggleSmoothScrolling (); + + _alternatingColorsCheckBox = new () + { + Title = "Alternating Colors" + }; + _alternatingColorsCheckBox.CheckedStateChanged += (s, e) => ToggleAlternatingColors (); + + _cursorCheckBox = new () + { + Title = "Invert Selected Cell First Character", + CheckedState = _listColView.Style.InvertSelectedCellFirstCharacter ? CheckState.Checked : CheckState.UnChecked + }; + _cursorCheckBox.CheckedStateChanged += (s, e) => ToggleInvertSelectedCellFirstCharacter (); + + _orientVerticalCheckBox = new () + { + Title = "_OrientVertical", + CheckedState = listColStyle.Orientation == Orientation.Vertical ? CheckState.Checked : CheckState.UnChecked + }; + _orientVerticalCheckBox.CheckedStateChanged += (s, e) => ToggleVerticalOrientation (); + + _scrollParallelCheckBox = new () + { + Title = "_ScrollParallel", + CheckedState = listColStyle.ScrollParallel ? CheckState.Checked : CheckState.UnChecked + }; + _scrollParallelCheckBox.CheckedStateChanged += (s, e) => ToggleScrollParallel (); + + menuBar.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "Open_BigListExample", + Action = () => OpenSimpleList (true) + }, + new MenuItem + { + Title = "Open_SmListExample", + Action = () => OpenSimpleList (false) + }, + new MenuItem + { + Title = "_CloseExample", + Action = CloseExample + }, + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + menuBar.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + CommandView = _toplineCheckBox + }, + new MenuItem + { + CommandView = _bottomlineCheckBox + }, + new MenuItem + { + CommandView = _cellLinesCheckBox + }, + new MenuItem + { + CommandView = _expandLastColumnCheckBox + }, + new MenuItem + { + CommandView = _alwaysUseNormalColorForVerticalCellLinesCheckBox + }, + new MenuItem + { + CommandView = _smoothScrollingCheckBox + }, + new MenuItem + { + CommandView = _alternatingColorsCheckBox + }, + new MenuItem + { + CommandView = _cursorCheckBox + } + ] + ) + ); + + menuBar.Add ( + new MenuBarItem ( + "_List", + [ + new MenuItem + { + CommandView = _orientVerticalCheckBox + }, + new MenuItem + { + CommandView = _scrollParallelCheckBox + }, + new MenuItem + { + Title = "Set _Max Cell Width", + Action = SetListMaxWidth + }, + new MenuItem + { + Title = "Set Mi_n Cell Width", + Action = SetListMinWidth + } + ] + ) + ); + + // Add views in order of visual appearance + appWindow.Add (menuBar, _listColView, selectedCellLabel, statusBar); + + Application.Run (appWindow); + appWindow.Dispose (); Application.Shutdown (); } - private void CloseExample () { _listColView.Table = null; } + private void CloseExample () + { + if (_listColView is { }) + { + _listColView.Table = null; + } + } + private void OpenSimpleList (bool big) { SetTable (BuildSimpleList (big ? 1023 : 31)); } + private void Quit () { Application.RequestStop (); } private void RunListWidthDialog (string prompt, Action setter, Func getter) { + if (_listColView is null) + { + return; + } + var accepted = false; - var ok = new Button { Text = "Ok", IsDefault = true }; + Button ok = new () { Text = "Ok", IsDefault = true }; ok.Accepting += (s, e) => - { - accepted = true; - Application.RequestStop (); - }; - var cancel = new Button { Text = "Cancel" }; + { + accepted = true; + Application.RequestStop (); + }; + Button cancel = new () { Text = "Cancel" }; cancel.Accepting += (s, e) => { Application.RequestStop (); }; - var d = new Dialog { Title = prompt, Buttons = [ok, cancel] }; + Dialog d = new () { Title = prompt, Buttons = [ok, cancel] }; - var tf = new TextField { Text = getter (_listColView).ToString (), X = 0, Y = 0, Width = Dim.Fill () }; + TextField tf = new () { Text = getter (_listColView).ToString (), X = 0, Y = 0, Width = Dim.Fill () }; d.Add (tf); tf.SetFocus (); @@ -304,63 +344,37 @@ public class ListColumns : Scenario private void SetListMaxWidth () { RunListWidthDialog ("MaxCellWidth", (s, v) => s.MaxCellWidth = v, s => s.MaxCellWidth); - _listColView.SetNeedsDraw (); + _listColView?.SetNeedsDraw (); } private void SetListMinWidth () { RunListWidthDialog ("MinCellWidth", (s, v) => s.MinCellWidth = v, s => s.MinCellWidth); - _listColView.SetNeedsDraw (); + _listColView?.SetNeedsDraw (); } private void SetTable (IList list) { + if (_listColView is null) + { + return; + } + _listColView.Table = new ListTableSource (list, _listColView); - if ((ListTableSource)_listColView.Table != null) + if (_listColView.Table is ListTableSource listTableSource) { - _currentTable = ((ListTableSource)_listColView.Table).DataTable; + _currentTable = listTableSource.DataTable; } } - //private void SetupScrollBar () - //{ - // var scrollBar = new ScrollBarView (_listColView, true); // (listColView, true, true); - - // scrollBar.ChangedPosition += (s, e) => - // { - // _listColView.RowOffset = scrollBar.Position; - - // if (_listColView.RowOffset != scrollBar.Position) - // { - // scrollBar.Position = _listColView.RowOffset; - // } - - // _listColView.SetNeedsDraw (); - // }; - // /* - // scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => { - // listColView.ColumnOffset = scrollBar.OtherScrollBarView.Position; - // if (listColView.ColumnOffset != scrollBar.OtherScrollBarView.Position) { - // scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset; - // } - // listColView.SetNeedsDraw (); - // }; - // */ - - // _listColView.DrawingContent += (s, e) => - // { - // scrollBar.Size = _listColView.Table?.Rows ?? 0; - // scrollBar.Position = _listColView.RowOffset; - - // //scrollBar.OtherScrollBarView.Size = listColView.Table?.Columns - 1 ?? 0; - // //scrollBar.OtherScrollBarView.Position = listColView.ColumnOffset; - // scrollBar.Refresh (); - // }; - //} - - private void TableViewKeyPress (object sender, Key e) + private void TableViewKeyPress (object? sender, Key e) { + if (_currentTable is null || _listColView is null) + { + return; + } + if (e.KeyCode == Key.Delete) { // set all selected cells to null @@ -376,12 +390,14 @@ public class ListColumns : Scenario private void ToggleAlternatingColors () { - //toggle menu item - _miAlternatingColors.Checked = !_miAlternatingColors.Checked; - - if (_miAlternatingColors.Checked == true) + if (_listColView is null || _alternatingColorsCheckBox is null) { - _listColView.Style.RowColorGetter = a => { return a.RowIndex % 2 == 0 ? _alternatingScheme : null; }; + return; + } + + if (_alternatingColorsCheckBox.CheckedState == CheckState.Checked) + { + _listColView.Style.RowColorGetter = a => a.RowIndex % 2 == 0 ? _alternatingScheme : null; } else { @@ -393,81 +409,106 @@ public class ListColumns : Scenario private void ToggleAlwaysUseNormalColorForVerticalCellLines () { - _miAlwaysUseNormalColorForVerticalCellLines.Checked = - !_miAlwaysUseNormalColorForVerticalCellLines.Checked; + if (_listColView is null || _alwaysUseNormalColorForVerticalCellLinesCheckBox is null) + { + return; + } _listColView.Style.AlwaysUseNormalColorForVerticalCellLines = - (bool)_miAlwaysUseNormalColorForVerticalCellLines.Checked; + _alwaysUseNormalColorForVerticalCellLinesCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleBottomline () { - _miBottomline.Checked = !_miBottomline.Checked; - _listColView.Style.ShowHorizontalBottomline = (bool)_miBottomline.Checked; + if (_listColView is null || _bottomlineCheckBox is null) + { + return; + } + + _listColView.Style.ShowHorizontalBottomline = _bottomlineCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleCellLines () { - _miCellLines.Checked = !_miCellLines.Checked; - _listColView.Style.ShowVerticalCellLines = (bool)_miCellLines.Checked; + if (_listColView is null || _cellLinesCheckBox is null) + { + return; + } + + _listColView.Style.ShowVerticalCellLines = _cellLinesCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleExpandLastColumn () { - _miExpandLastColumn.Checked = !_miExpandLastColumn.Checked; - _listColView.Style.ExpandLastColumn = (bool)_miExpandLastColumn.Checked; + if (_listColView is null || _expandLastColumnCheckBox is null) + { + return; + } + + _listColView.Style.ExpandLastColumn = _expandLastColumnCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleInvertSelectedCellFirstCharacter () { - //toggle menu item - _miCursor.Checked = !_miCursor.Checked; - _listColView.Style.InvertSelectedCellFirstCharacter = (bool)_miCursor.Checked; + if (_listColView is null || _cursorCheckBox is null) + { + return; + } + + _listColView.Style.InvertSelectedCellFirstCharacter = _cursorCheckBox.CheckedState == CheckState.Checked; _listColView.SetNeedsDraw (); } private void ToggleScrollParallel () { - _miScrollParallel.Checked = !_miScrollParallel.Checked; - - if ((ListTableSource)_listColView.Table != null) + if (_listColView?.Table is not ListTableSource listTableSource || _scrollParallelCheckBox is null) { - ((ListTableSource)_listColView.Table).Style.ScrollParallel = (bool)_miScrollParallel.Checked; - _listColView.SetNeedsDraw (); + return; } + + listTableSource.Style.ScrollParallel = _scrollParallelCheckBox.CheckedState == CheckState.Checked; + _listColView.SetNeedsDraw (); } private void ToggleSmoothScrolling () { - _miSmoothScrolling.Checked = !_miSmoothScrolling.Checked; - _listColView.Style.SmoothHorizontalScrolling = (bool)_miSmoothScrolling.Checked; + if (_listColView is null || _smoothScrollingCheckBox is null) + { + return; + } + + _listColView.Style.SmoothHorizontalScrolling = _smoothScrollingCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleTopline () { - _miTopline.Checked = !_miTopline.Checked; - _listColView.Style.ShowHorizontalHeaderOverline = (bool)_miTopline.Checked; + if (_listColView is null || _toplineCheckBox is null) + { + return; + } + + _listColView.Style.ShowHorizontalHeaderOverline = _toplineCheckBox.CheckedState == CheckState.Checked; _listColView.Update (); } private void ToggleVerticalOrientation () { - _miOrientVertical.Checked = !_miOrientVertical.Checked; - - if ((ListTableSource)_listColView.Table != null) + if (_listColView?.Table is not ListTableSource listTableSource || _orientVerticalCheckBox is null) { - ((ListTableSource)_listColView.Table).Style.Orientation = (bool)_miOrientVertical.Checked - ? Orientation.Vertical - : Orientation.Horizontal; - _listColView.SetNeedsDraw (); + return; } + + listTableSource.Style.Orientation = _orientVerticalCheckBox.CheckedState == CheckState.Checked + ? Orientation.Vertical + : Orientation.Horizontal; + _listColView.SetNeedsDraw (); } } diff --git a/Examples/UICatalog/Scenarios/Localization.cs b/Examples/UICatalog/Scenarios/Localization.cs index 229800f5e..0da975790 100644 --- a/Examples/UICatalog/Scenarios/Localization.cs +++ b/Examples/UICatalog/Scenarios/Localization.cs @@ -1,7 +1,6 @@ -using System; +#nullable enable + using System.Globalization; -using System.Linq; -using System.Threading; namespace UICatalog.Scenarios; @@ -10,11 +9,11 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Tests")] public class Localization : Scenario { - private CheckBox _allowAnyCheckBox; - private string [] _cultureInfoNameSource; - private CultureInfo [] _cultureInfoSource; + private CheckBox? _allowAnyCheckBox; + private string []? _cultureInfoNameSource; + private CultureInfo []? _cultureInfoSource; private OpenMode _currentOpenMode = OpenMode.File; - private ComboBox _languageComboBox; + private ComboBox? _languageComboBox; public CultureInfo CurrentCulture { get; private set; } = Thread.CurrentThread.CurrentUICulture; public void Quit () @@ -25,6 +24,11 @@ public class Localization : Scenario public void SetCulture (CultureInfo culture) { + if (_languageComboBox is null || _cultureInfoSource is null) + { + return; + } + if (_cultureInfoSource [_languageComboBox.SelectedItem] != culture) { _languageComboBox.SelectedItem = Array.IndexOf (_cultureInfoSource, culture); @@ -43,70 +47,67 @@ public class Localization : Scenario public override void Main () { Application.Init (); - var top = new Toplevel (); - var win = new Window { Title = GetQuitKeyAndName () }; - _cultureInfoSource = Application.SupportedCultures.Append (CultureInfo.InvariantCulture).ToArray (); - _cultureInfoNameSource = Application.SupportedCultures.Select (c => $"{c.NativeName} ({c.Name})") + Window win = new () + { + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None + }; + + _cultureInfoSource = Application.SupportedCultures!.Append (CultureInfo.InvariantCulture).ToArray (); + + _cultureInfoNameSource = Application.SupportedCultures!.Select (c => $"{c.NativeName} ({c.Name})") .Append ("Invariant") .ToArray (); - MenuItem [] languageMenus = Application.SupportedCultures - .Select ( - c => new MenuItem ( - $"{c.NativeName} ({c.Name})", - "", - () => SetCulture (c) - ) + MenuItem [] languageMenus = Application.SupportedCultures! + .Select (c => new MenuItem + { + Title = $"{c.NativeName} ({c.Name})", + Action = () => SetCulture (c) + } ) .Concat ( - new MenuItem [] - { - null, - new ( - "Invariant", - "", - () => - SetCulture ( - CultureInfo - .InvariantCulture - ) - ) - } + [ + new () + { + Title = "Invariant", + Action = () => SetCulture (CultureInfo.InvariantCulture) + } + ] ) .ToArray (); - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new MenuBarItem ( - "_Language", - languageMenus - ), - null, - new ("_Quit", "", Quit) - } - ) - ] - }; - top.Add (menu); + // MenuBar + MenuBar menu = new (); - var selectLanguageLabel = new Label + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuBarItem ( + "_Language", + languageMenus + ), + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + Label selectLanguageLabel = new () { X = 2, - Y = 1, - + Y = Pos.Bottom (menu) + 1, Width = Dim.Fill (2), Text = "Please select a language." }; win.Add (selectLanguageLabel); - _languageComboBox = new() + _languageComboBox = new () { X = 2, Y = Pos.Bottom (selectLanguageLabel) + 1, @@ -120,11 +121,10 @@ public class Localization : Scenario _languageComboBox.SelectedItemChanged += LanguageComboBox_SelectChanged; win.Add (_languageComboBox); - var textAndFileDialogLabel = new Label + Label textAndFileDialogLabel = new () { X = 2, Y = Pos.Top (_languageComboBox) + 3, - Width = Dim.Fill (2), Height = 1, Text = @@ -132,13 +132,16 @@ public class Localization : Scenario }; win.Add (textAndFileDialogLabel); - var textField = new TextView + TextView textField = new () { - X = 2, Y = Pos.Bottom (textAndFileDialogLabel) + 1, Width = Dim.Fill (32), Height = 1 + X = 2, + Y = Pos.Bottom (textAndFileDialogLabel) + 1, + Width = Dim.Fill (32), + Height = 1 }; win.Add (textField); - _allowAnyCheckBox = new() + _allowAnyCheckBox = new () { X = Pos.Right (textField) + 1, Y = Pos.Bottom (textAndFileDialogLabel) + 1, @@ -147,46 +150,53 @@ public class Localization : Scenario }; win.Add (_allowAnyCheckBox); - var openDialogButton = new Button + Button openDialogButton = new () { - X = Pos.Right (_allowAnyCheckBox) + 1, Y = Pos.Bottom (textAndFileDialogLabel) + 1, Text = "Open" + X = Pos.Right (_allowAnyCheckBox) + 1, + Y = Pos.Bottom (textAndFileDialogLabel) + 1, + Text = "Open" }; openDialogButton.Accepting += (sender, e) => ShowFileDialog (false); win.Add (openDialogButton); - var saveDialogButton = new Button + Button saveDialogButton = new () { - X = Pos.Right (openDialogButton) + 1, Y = Pos.Bottom (textAndFileDialogLabel) + 1, Text = "Save" + X = Pos.Right (openDialogButton) + 1, + Y = Pos.Bottom (textAndFileDialogLabel) + 1, + Text = "Save" }; saveDialogButton.Accepting += (sender, e) => ShowFileDialog (true); win.Add (saveDialogButton); - var wizardLabel = new Label + Label wizardLabel = new () { X = 2, Y = Pos.Bottom (textField) + 1, - Width = Dim.Fill (2), Text = "Click the button to open a wizard." }; win.Add (wizardLabel); - var wizardButton = new Button { X = 2, Y = Pos.Bottom (wizardLabel) + 1, Text = "Open _wizard" }; + Button wizardButton = new () { X = 2, Y = Pos.Bottom (wizardLabel) + 1, Text = "Open _wizard" }; wizardButton.Accepting += (sender, e) => ShowWizard (); win.Add (wizardButton); win.Unloaded += (sender, e) => Quit (); - win.Y = Pos.Bottom (menu); - top.Add (win); + win.Add (menu); - Application.Run (top); - top.Dispose (); + Application.Run (win); + win.Dispose (); Application.Shutdown (); } public void ShowFileDialog (bool isSaveFile) { + if (_allowAnyCheckBox is null) + { + return; + } + FileDialog dialog = isSaveFile ? new SaveDialog () : new OpenDialog { OpenMode = _currentOpenMode }; dialog.AllowedTypes = @@ -213,16 +223,21 @@ public class Localization : Scenario public void ShowWizard () { - var wizard = new Wizard { Height = 8, Width = 36, Title = "The wizard" }; - wizard.AddStep (new() { HelpText = "Wizard first step" }); - wizard.AddStep (new() { HelpText = "Wizard step 2", NextButtonText = ">>> (_N)" }); - wizard.AddStep (new() { HelpText = "Wizard last step" }); + Wizard wizard = new () { Height = 8, Width = 36, Title = "The wizard" }; + wizard.AddStep (new () { HelpText = "Wizard first step" }); + wizard.AddStep (new () { HelpText = "Wizard step 2", NextButtonText = ">>> (_N)" }); + wizard.AddStep (new () { HelpText = "Wizard last step" }); Application.Run (wizard); wizard.Dispose (); } - private void LanguageComboBox_SelectChanged (object sender, ListViewItemEventArgs e) + private void LanguageComboBox_SelectChanged (object? sender, ListViewItemEventArgs e) { + if (_cultureInfoNameSource is null || _cultureInfoSource is null) + { + return; + } + if (e.Value is string cultureName) { int index = Array.IndexOf (_cultureInfoNameSource, cultureName); diff --git a/Examples/UICatalog/Scenarios/MenuBarScenario.cs b/Examples/UICatalog/Scenarios/MenuBarScenario.cs deleted file mode 100644 index e61438767..000000000 --- a/Examples/UICatalog/Scenarios/MenuBarScenario.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using static System.Runtime.InteropServices.JavaScript.JSType; - -namespace UICatalog.Scenarios; - -[ScenarioMetadata ("MenuBar", "Demonstrates the MenuBar using the demo menu.")] -[ScenarioCategory ("Controls")] -[ScenarioCategory ("Menus")] -public class MenuBarScenario : Scenario -{ - private Label _currentMenuBarItem; - private Label _currentMenuItem; - private Label _focusedView; - private Label _lastAction; - private Label _lastKey; - - public override void Main () - { - // Init - Application.Init (); - - // Setup - Create a top-level application window and configure it. - Window appWindow = new () - { - Title = GetQuitKeyAndName (), - BorderStyle = LineStyle.None - }; - - MenuItem mbiCurrent = null; - MenuItem miCurrent = null; - - var label = new Label { X = 0, Y = 10, Text = "Last Key: " }; - appWindow.Add (label); - - _lastKey = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" }; - - appWindow.Add (_lastKey); - label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Current MenuBarItem: " }; - appWindow.Add (label); - - _currentMenuBarItem = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" }; - appWindow.Add (_currentMenuBarItem); - - label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Current MenuItem: " }; - appWindow.Add (label); - - _currentMenuItem = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" }; - appWindow.Add (_currentMenuItem); - - label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Last Action: " }; - appWindow.Add (label); - - _lastAction = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" }; - appWindow.Add (_lastAction); - - label = new Label { X = 0, Y = Pos.Bottom (label), Text = "Focused View: " }; - appWindow.Add (label); - - _focusedView = new Label { X = Pos.Right (label), Y = Pos.Top (label), Text = "" }; - appWindow.Add (_focusedView); - - MenuBar menuBar = new MenuBar (); - menuBar.UseKeysUpDownAsKeysLeftRight = true; - menuBar.Key = KeyCode.F9; - menuBar.Title = "TestMenuBar"; - - bool FnAction (string s) - { - _lastAction.Text = s; - - return true; - } - - // Declare a variable for the function - Func fnActionVariable = FnAction; - - menuBar.EnableForDesign (ref fnActionVariable); - - menuBar.MenuOpening += (s, e) => - { - mbiCurrent = e.CurrentMenu; - SetCurrentMenuBarItem (mbiCurrent); - SetCurrentMenuItem (miCurrent); - _lastAction.Text = string.Empty; - }; - - menuBar.MenuOpened += (s, e) => - { - miCurrent = e.MenuItem; - SetCurrentMenuBarItem (mbiCurrent); - SetCurrentMenuItem (miCurrent); - }; - - menuBar.MenuClosing += (s, e) => - { - mbiCurrent = null; - miCurrent = null; - SetCurrentMenuBarItem (mbiCurrent); - SetCurrentMenuItem (miCurrent); - }; - - Application.KeyDown += (s, e) => - { - _lastAction.Text = string.Empty; - _lastKey.Text = e.ToString (); - }; - - // There's no focus change event, so this is a bit of a hack. - menuBar.SubViewsLaidOut += (s, e) => { _focusedView.Text = appWindow.MostFocused?.ToString () ?? "None"; }; - - var openBtn = new Button { X = Pos.Center (), Y = 4, Text = "_Open Menu", IsDefault = true }; - openBtn.Accepting += (s, e) => { menuBar.OpenMenu (); }; - appWindow.Add (openBtn); - - var hideBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (openBtn), Text = "Toggle Menu._Visible" }; - hideBtn.Accepting += (s, e) => { menuBar.Visible = !menuBar.Visible; }; - appWindow.Add (hideBtn); - - var enableBtn = new Button { X = Pos.Center (), Y = Pos.Bottom (hideBtn), Text = "_Toggle Menu.Enable" }; - enableBtn.Accepting += (s, e) => { menuBar.Enabled = !menuBar.Enabled; }; - appWindow.Add (enableBtn); - - appWindow.Add (menuBar); - - // Run - Start the application. - Application.Run (appWindow); - appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. - Application.Shutdown (); - } - - private void SetCurrentMenuBarItem (MenuItem mbi) { _currentMenuBarItem.Text = mbi != null ? mbi.Title : "Closed"; } - private void SetCurrentMenuItem (MenuItem mi) { _currentMenuItem.Text = mi != null ? mi.Title : "None"; } -} diff --git a/Examples/UICatalog/Scenarios/Menus.cs b/Examples/UICatalog/Scenarios/Menus.cs index 755844715..4a5313145 100644 --- a/Examples/UICatalog/Scenarios/Menus.cs +++ b/Examples/UICatalog/Scenarios/Menus.cs @@ -189,7 +189,7 @@ public class Menus : Scenario Application.KeyBindings.Remove (Key.F5); Application.KeyBindings.Add (Key.F5, this, Command.Edit); - var menuBar = new MenuBarv2 + var menuBar = new MenuBar { Title = "MenuHost MenuBar" }; @@ -257,7 +257,7 @@ public class Menus : Scenario menuBar.Accepted += (o, args) => { - if (args.Context?.Source is MenuItemv2 mi && mi.CommandView == enableOverwriteMenuItemCb) + if (args.Context?.Source is MenuItem mi && mi.CommandView == enableOverwriteMenuItemCb) { Logging.Debug ($"menuBar.Accepted: {args.Context.Source?.Title}"); @@ -302,7 +302,7 @@ public class Menus : Scenario menuBar.Accepted += (o, args) => { - if (args.Context?.Source is MenuItemv2 mi && mi.CommandView == editModeMenuItemCb) + if (args.Context?.Source is MenuItem mi && mi.CommandView == editModeMenuItemCb) { Logging.Debug ($"menuBar.Accepted: {args.Context.Source?.Title}"); diff --git a/Examples/UICatalog/Scenarios/MultiColouredTable.cs b/Examples/UICatalog/Scenarios/MultiColouredTable.cs index cf013f86c..9b717d1ed 100644 --- a/Examples/UICatalog/Scenarios/MultiColouredTable.cs +++ b/Examples/UICatalog/Scenarios/MultiColouredTable.cs @@ -1,4 +1,5 @@ -using System; +#nullable enable + using System.Data; using System.Text; @@ -10,40 +11,49 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TableView")] public class MultiColouredTable : Scenario { - private DataTable _table; - private TableViewColors _tableView; + private DataTable? _table; + private TableViewColors? _tableView; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel appWindow = new () + Window appWindow = new () { - Title = GetQuitKeyAndName () + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None, }; - _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; + // MenuBar + var menu = new MenuBar (); - var menu = new MenuBar - { - Menus = - [ - new ("_File", new MenuItem [] { new ("_Quit", "", Quit) }) - ] - }; - appWindow.Add (menu); + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); - var statusBar = new StatusBar (new Shortcut [] { new (Application.QuitKey, "Quit", Quit) }); + _tableView = new () { X = 0, Y = Pos.Bottom (menu), Width = Dim.Fill (), Height = Dim.Fill (1) }; - appWindow.Add (statusBar); + // StatusBar + var statusBar = new StatusBar ( + [ + new (Application.QuitKey, "Quit", Quit) + ] + ); - appWindow.Add (_tableView); + appWindow.Add (menu, _tableView, statusBar); _tableView.CellActivated += EditCurrentCell; - var dt = new DataTable (); + DataTable dt = new (); dt.Columns.Add ("Col1"); dt.Columns.Add ("Col2"); @@ -54,34 +64,33 @@ public class MultiColouredTable : Scenario dt.Rows.Add (DBNull.Value, DBNull.Value); dt.Rows.Add (DBNull.Value, DBNull.Value); - _tableView.SetScheme (new () - { - Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled), - HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus), - Focus = appWindow.GetAttributeForRole (VisualRole.Focus), - Normal = new (Color.DarkGray, Color.Black) - }); + _tableView.SetScheme ( + new () + { + Disabled = appWindow.GetAttributeForRole (VisualRole.Disabled), + HotFocus = appWindow.GetAttributeForRole (VisualRole.HotFocus), + Focus = appWindow.GetAttributeForRole (VisualRole.Focus), + Normal = new (Color.DarkGray, Color.Black) + } + ); _tableView.Table = new DataTableSource (_table = dt); - // Run - Start the application. Application.Run (appWindow); appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } - private void EditCurrentCell (object sender, CellActivatedEventArgs e) + private void EditCurrentCell (object? sender, CellActivatedEventArgs e) { - if (e.Table == null) + if (e.Table is null || _table is null || _tableView is null) { return; } var oldValue = e.Table [e.Row, e.Col].ToString (); - if (GetText ("Enter new value", e.Table.ColumnNames [e.Col], oldValue, out string newText)) + if (GetText ("Enter new value", e.Table.ColumnNames [e.Col], oldValue ?? "", out string newText)) { try { @@ -101,20 +110,20 @@ public class MultiColouredTable : Scenario { var okPressed = false; - var ok = new Button { Text = "Ok", IsDefault = true }; + Button ok = new () { Text = "Ok", IsDefault = true }; ok.Accepting += (s, e) => - { - okPressed = true; - Application.RequestStop (); - }; - var cancel = new Button { Text = "Cancel" }; + { + okPressed = true; + Application.RequestStop (); + }; + Button cancel = new () { Text = "Cancel" }; cancel.Accepting += (s, e) => { Application.RequestStop (); }; - var d = new Dialog { Title = title, Buttons = [ok, cancel] }; + Dialog d = new () { Title = title, Buttons = [ok, cancel] }; - var lbl = new Label { X = 0, Y = 1, Text = label }; + Label lbl = new () { X = 0, Y = 1, Text = label }; - var tf = new TextField { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; + TextField tf = new () { Text = initialText, X = 0, Y = 2, Width = Dim.Fill () }; d.Add (lbl, tf); tf.SetFocus (); @@ -122,7 +131,7 @@ public class MultiColouredTable : Scenario Application.Run (d); d.Dispose (); - enteredText = okPressed ? tf.Text : null; + enteredText = okPressed ? tf.Text : string.Empty; return okPressed; } @@ -155,20 +164,20 @@ public class MultiColouredTable : Scenario break; case 1: SetAttribute ( - new ( - Color.BrightRed, - cellColor.Background - ) - ); + new ( + Color.BrightRed, + cellColor.Background + ) + ); break; case 2: SetAttribute ( - new ( - Color.BrightYellow, - cellColor.Background - ) - ); + new ( + Color.BrightYellow, + cellColor.Background + ) + ); break; case 3: @@ -177,29 +186,29 @@ public class MultiColouredTable : Scenario break; case 4: SetAttribute ( - new ( - Color.BrightGreen, - cellColor.Background - ) - ); + new ( + Color.BrightGreen, + cellColor.Background + ) + ); break; case 5: SetAttribute ( - new ( - Color.BrightBlue, - cellColor.Background - ) - ); + new ( + Color.BrightBlue, + cellColor.Background + ) + ); break; case 6: SetAttribute ( - new ( - Color.BrightCyan, - cellColor.Background - ) - ); + new ( + Color.BrightCyan, + cellColor.Background + ) + ); break; case 7: diff --git a/Examples/UICatalog/Scenarios/Notepad.cs b/Examples/UICatalog/Scenarios/Notepad.cs index 595604a7b..4a2f7d2e6 100644 --- a/Examples/UICatalog/Scenarios/Notepad.cs +++ b/Examples/UICatalog/Scenarios/Notepad.cs @@ -1,4 +1,5 @@ #nullable enable + namespace UICatalog.Scenarios; [ScenarioMetadata ("Notepad", "Multi-tab text editor using the TabView control.")] @@ -16,41 +17,65 @@ public class Notepad : Scenario { Application.Init (); - Toplevel top = new (); - - var menu = new MenuBar + Window top = new () { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "_New", - "", - () => New (), - null, - null, - KeyCode.N - | KeyCode.CtrlMask - | KeyCode.AltMask - ), - new ("_Open", "", Open), - new ("_Save", "", Save), - new ("Save _As", "", () => SaveAs ()), - new ("_Close", "", Close), - new ("_Quit", "", Quit) - } - ), - new ( - "_About", - "", - () => MessageBox.Query ("Notepad", "About Notepad...", "Ok") - ) - ] + BorderStyle = LineStyle.None, }; - top.Add (menu); + + // MenuBar + MenuBar menu = new (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_New", + Key = Key.N.WithCtrl.WithAlt, + Action = New + }, + new MenuItem + { + Title = "_Open", + Action = Open + }, + new MenuItem + { + Title = "_Save", + Action = Save + }, + new MenuItem + { + Title = "Save _As", + Action = () => SaveAs () + }, + new MenuItem + { + Title = "_Close", + Action = Close + }, + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_About", + [ + new MenuItem + { + Title = "_About", + Action = () => MessageBox.Query ("Notepad", "About Notepad...", "Ok") + } + ] + ) + ); _tabView = CreateNewTabView (); @@ -58,27 +83,28 @@ public class Notepad : Scenario _tabView.ApplyStyleChanges (); _tabView.X = 0; - _tabView.Y = 1; + _tabView.Y = Pos.Bottom (menu); _tabView.Width = Dim.Fill (); _tabView.Height = Dim.Fill (1); - top.Add (_tabView); LenShortcut = new (Key.Empty, "Len: ", null); - var statusBar = new StatusBar ( - [ - new (Application.QuitKey, "Quit", Quit), - new (Key.F2, "Open", Open), - new (Key.F1, "New", New), - new (Key.F3, "Save", Save), - new (Key.F6, "Close", Close), - LenShortcut - ] - ) + // StatusBar + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", Quit), + new (Key.F2, "Open", Open), + new (Key.F1, "New", New), + new (Key.F3, "Save", Save), + new (Key.F6, "Close", Close), + LenShortcut + ] + ) { AlignmentModes = AlignmentModes.IgnoreFirstOrLast }; - top.Add (statusBar); + + top.Add (menu, _tabView, statusBar); _focusedTabView = _tabView; _tabView.SelectedTabChanged += TabView_SelectedTabChanged; @@ -87,55 +113,52 @@ public class Notepad : Scenario top.Ready += (s, e) => { New (); - LenShortcut.Title = $"Len:{_focusedTabView.Text?.Length ?? 0}"; + LenShortcut.Title = $"Len:{_focusedTabView?.Text?.Length ?? 0}"; }; Application.Run (top); top.Dispose (); - Application.Shutdown (); } - public void Save () { Save (_focusedTabView!, _focusedTabView!.SelectedTab!); } + public void Save () + { + if (_focusedTabView?.SelectedTab is { }) + { + Save (_focusedTabView, _focusedTabView.SelectedTab); + } + } public void Save (TabView tabViewToSave, Tab tabToSave) { - var tab = tabToSave as OpenedFile; - - if (tab == null) + if (tabToSave is not OpenedFile tab) { return; } - if (tab.File == null) + if (tab.File is null) { SaveAs (); } + else + { + tab.Save (); + } - tab.Save (); tabViewToSave.SetNeedsDraw (); } public bool SaveAs () { - var tab = _focusedTabView!.SelectedTab as OpenedFile; - - if (tab == null) + if (_focusedTabView?.SelectedTab is not OpenedFile tab) { return false; } - var fd = new SaveDialog (); + SaveDialog fd = new (); Application.Run (fd); - if (string.IsNullOrWhiteSpace (fd.Path)) - { - fd.Dispose (); - - return false; - } - - if (fd.Canceled) + if (string.IsNullOrWhiteSpace (fd.Path) || fd.Canceled) { fd.Dispose (); @@ -151,13 +174,17 @@ public class Notepad : Scenario return true; } - private void Close () { Close (_focusedTabView!, _focusedTabView!.SelectedTab!); } + private void Close () + { + if (_focusedTabView?.SelectedTab is { }) + { + Close (_focusedTabView, _focusedTabView.SelectedTab); + } + } private void Close (TabView tv, Tab tabToClose) { - var tab = tabToClose as OpenedFile; - - if (tab == null) + if (tabToClose is not OpenedFile tab) { return; } @@ -182,7 +209,7 @@ public class Notepad : Scenario if (result == 0) { - if (tab.File == null) + if (tab.File is null) { SaveAs (); } @@ -207,7 +234,7 @@ public class Notepad : Scenario private TabView CreateNewTabView () { - var tv = new TabView { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; + TabView tv = new () { X = 0, Y = 0, Width = Dim.Fill (), Height = Dim.Fill () }; tv.TabClicked += TabView_TabClicked; tv.SelectedTabChanged += TabView_SelectedTabChanged; @@ -220,7 +247,7 @@ public class Notepad : Scenario private void Open () { - var open = new OpenDialog { Title = "Open", AllowsMultipleSelection = true }; + OpenDialog open = new () { Title = "Open", AllowsMultipleSelection = true }; Application.Run (open); @@ -246,21 +273,29 @@ public class Notepad : Scenario /// Creates a new tab with initial text /// File that was read or null if a new blank document /// - private void Open (FileInfo fileInfo, string tabName) + private void Open (FileInfo? fileInfo, string tabName) { - var tab = new OpenedFile (this) { DisplayText = tabName, File = fileInfo }; + if (_focusedTabView is null) + { + return; + } + + OpenedFile tab = new (this) { DisplayText = tabName, File = fileInfo }; tab.View = tab.CreateTextView (fileInfo); tab.SavedText = tab.View.Text; - tab.RegisterTextViewEvents (_focusedTabView!); + tab.RegisterTextViewEvents (_focusedTabView); - _focusedTabView!.AddTab (tab, true); + _focusedTabView.AddTab (tab, true); } private void Quit () { Application.RequestStop (); } private void TabView_SelectedTabChanged (object? sender, TabChangedEventArgs e) { - LenShortcut!.Title = $"Len:{e.NewTab?.View?.Text?.Length ?? 0}"; + if (LenShortcut is { }) + { + LenShortcut.Title = $"Len:{e.NewTab?.View?.Text?.Length ?? 0}"; + } e.NewTab?.View?.SetFocus (); } @@ -275,30 +310,33 @@ public class Notepad : Scenario View [] items; - if (e.Tab == null) + if (e.Tab is null) { - items = [new MenuItemv2 ("Open", "", Open)]; + items = [new MenuItem { Title = "Open", Action = Open }]; } else { var tv = (TabView)sender!; - var t = (OpenedFile)e.Tab; items = [ - new MenuItemv2 ("Save", "", () => Save (_focusedTabView!, e.Tab)), - new MenuItemv2 ("Close", "", () => Close (tv, e.Tab)) + new MenuItem { Title = "Save", Action = () => Save (_focusedTabView!, e.Tab) }, + new MenuItem { Title = "Close", Action = () => Close (tv, e.Tab) } ]; - - PopoverMenu? contextMenu = new (items); - - // 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. - tv.App!.Popover?.Register (contextMenu); - contextMenu?.MakeVisible (e.MouseEvent.ScreenPosition); - - e.MouseEvent.Handled = true; } + + PopoverMenu contextMenu = new (items); + + // 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. + if (sender is TabView tabView && tabView.App?.Popover is { }) + { + tabView.App.Popover.Register (contextMenu); + } + + contextMenu.MakeVisible (e.MouseEvent.ScreenPosition); + + e.MouseEvent.Handled = true; } private class OpenedFile (Notepad notepad) : Tab @@ -307,8 +345,8 @@ public class Notepad : Scenario public OpenedFile CloneTo (TabView other) { - var newTab = new OpenedFile (_notepad) { DisplayText = base.Text, File = File }; - newTab.View = newTab.CreateTextView (newTab.File!); + OpenedFile newTab = new (_notepad) { DisplayText = Text, File = File }; + newTab.View = newTab.CreateTextView (newTab.File); newTab.SavedText = newTab.View.Text; newTab.RegisterTextViewEvents (other); other.AddTab (newTab, true); @@ -340,7 +378,10 @@ public class Notepad : Scenario public void RegisterTextViewEvents (TabView parent) { - var textView = (TextView)View!; + if (View is not TextView textView) + { + return; + } // when user makes changes rename tab to indicate unsaved textView.ContentsChanged += (s, k) => @@ -363,25 +404,27 @@ public class Notepad : Scenario } } - _notepad.LenShortcut!.Title = $"Len:{textView.Text.Length}"; + if (_notepad.LenShortcut is { }) + { + _notepad.LenShortcut.Title = $"Len:{textView.Text.Length}"; + } }; } /// The text of the tab the last time it was saved - /// public string? SavedText { get; set; } - public bool UnsavedChanges => !string.Equals (SavedText, View!.Text); + public bool UnsavedChanges => View is { } && !string.Equals (SavedText, View.Text); internal void Save () { - string newText = View!.Text; - - if (File is null || string.IsNullOrWhiteSpace (File.FullName)) + if (View is null || File is null || string.IsNullOrWhiteSpace (File.FullName)) { return; } + string newText = View.Text; + System.IO.File.WriteAllText (File.FullName, newText); SavedText = newText; diff --git a/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs b/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs index a10fd6ca8..512a106f0 100644 --- a/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs +++ b/Examples/UICatalog/Scenarios/RuneWidthGreaterThanOne.cs @@ -1,4 +1,6 @@ -using System; +#nullable enable + +using System; namespace UICatalog.Scenarios; @@ -8,99 +10,173 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("Tests")] public class RuneWidthGreaterThanOne : Scenario { - private Button _button; - private Label _label; - private Label _labelR; - private Label _labelV; - private string _lastRunesUsed; - private TextField _text; - private Window _win; + private Button? _button; + private Label? _label; + private Label? _labelR; + private Label? _labelV; + private string? _lastRunesUsed; + private TextField? _text; + private Window? _win; public override void Main () { Application.Init (); - Toplevel topLevel = new (); - - var menu = new MenuBar + // Window (top-level) + Window win = new () { - Menus = - [ - new MenuBarItem ( - "Padding", - new MenuItem [] - { - new ( - "With Padding", - "", - () => _win.Padding.Thickness = - new Thickness (1) - ), - new ( - "Without Padding", - "", - () => _win.Padding.Thickness = - new Thickness (0) - ) - } - ), - new MenuBarItem ( - "BorderStyle", - new MenuItem [] - { - new ( - "Single", - "", - () => _win.BorderStyle = LineStyle.Single - ), - new ( - "None", - "", - () => _win.BorderStyle = LineStyle.None - ) - } - ), - new MenuBarItem ( - "Runes length", - new MenuItem [] - { - new ("Wide", "", WideRunes), - new ("Narrow", "", NarrowRunes), - new ("Mixed", "", MixedRunes) - } - ) - ] + X = 5, + Y = 5, + Width = Dim.Fill (22), + Height = Dim.Fill (5), + Arrangement = ViewArrangement.Overlapped | ViewArrangement.Movable + }; + _win = win; + + // MenuBar + MenuBar menu = new (); + + // Controls + _label = new () + { + X = Pos.Center (), + Y = 1 }; - _label = new Label + _text = new () { - X = Pos.Center (), Y = 1, + X = Pos.Center (), + Y = 3, + Width = 20 }; - _text = new TextField { X = Pos.Center (), Y = 3, Width = 20 }; - _button = new Button { X = Pos.Center (), Y = 5 }; - _labelR = new Label { X = Pos.AnchorEnd (30), Y = 18 }; - _labelV = new Label + _button = new () { - TextDirection = TextDirection.TopBottom_LeftRight, X = Pos.AnchorEnd (30), Y = Pos.Bottom (_labelR) + X = Pos.Center (), + Y = 5 }; - _win = new Window { X = 5, Y = 5, Width = Dim.Fill (22), Height = Dim.Fill (5) }; - _win.Add (_label, _text, _button, _labelR, _labelV); - topLevel.Add (menu, _win); + + _labelR = new () + { + X = Pos.AnchorEnd (30), + Y = 18 + }; + + _labelV = new () + { + TextDirection = TextDirection.TopBottom_LeftRight, + X = Pos.AnchorEnd (30), + Y = Pos.Bottom (_labelR) + }; + + menu.Add ( + new MenuBarItem ( + "Padding", + [ + new MenuItem + { + Title = "With Padding", + Action = () => + { + if (_win is { }) + { + _win.Padding!.Thickness = new (1); + } + } + }, + new MenuItem + { + Title = "Without Padding", + Action = () => + { + if (_win is { }) + { + _win.Padding!.Thickness = new (0); + } + } + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "BorderStyle", + [ + new MenuItem + { + Title = "Single", + Action = () => + { + if (_win is { }) + { + _win.BorderStyle = LineStyle.Single; + } + } + }, + new MenuItem + { + Title = "None", + Action = () => + { + if (_win is { }) + { + _win.BorderStyle = LineStyle.None; + } + } + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "Runes length", + [ + new MenuItem + { + Title = "Wide", + Action = WideRunes + }, + new MenuItem + { + Title = "Narrow", + Action = NarrowRunes + }, + new MenuItem + { + Title = "Mixed", + Action = MixedRunes + } + ] + ) + ); + + // Add views in order of visual appearance + win.Add (menu, _label, _text, _button, _labelR, _labelV); WideRunes (); - //NarrowRunes (); - //MixedRunes (); - Application.Run (topLevel); - topLevel.Dispose (); + Application.Run (win); + win.Dispose (); Application.Shutdown (); } - private void MixedMessage (object sender, EventArgs e) { MessageBox.Query ("Say Hello 你", $"Hello {_text.Text}", "Ok"); } + private void MixedMessage (object? sender, EventArgs e) + { + if (_text is { }) + { + MessageBox.Query ("Say Hello 你", $"Hello {_text.Text}", "Ok"); + } + } private void MixedRunes () { + if (_label is null || _text is null || _button is null || _labelR is null || _labelV is null || _win is null) + { + return; + } + UnsetClickedEvent (); _label.Text = "Enter your name 你:"; _text.Text = "gui.cs 你:"; @@ -117,10 +193,21 @@ public class RuneWidthGreaterThanOne : Scenario Application.LayoutAndDraw (); } - private void NarrowMessage (object sender, EventArgs e) { MessageBox.Query ("Say Hello", $"Hello {_text.Text}", "Ok"); } + private void NarrowMessage (object? sender, EventArgs e) + { + if (_text is { }) + { + MessageBox.Query ("Say Hello", $"Hello {_text.Text}", "Ok"); + } + } private void NarrowRunes () { + if (_label is null || _text is null || _button is null || _labelR is null || _labelV is null || _win is null) + { + return; + } + UnsetClickedEvent (); _label.Text = "Enter your name:"; _text.Text = "gui.cs"; @@ -139,6 +226,11 @@ public class RuneWidthGreaterThanOne : Scenario private void UnsetClickedEvent () { + if (_button is null) + { + return; + } + switch (_lastRunesUsed) { case "Narrow": @@ -156,10 +248,21 @@ public class RuneWidthGreaterThanOne : Scenario } } - private void WideMessage (object sender, EventArgs e) { MessageBox.Query ("こんにちはと言う", $"こんにちは {_text.Text}", "Ok"); } + private void WideMessage (object? sender, EventArgs e) + { + if (_text is { }) + { + MessageBox.Query ("こんにちはと言う", $"こんにちは {_text.Text}", "Ok"); + } + } private void WideRunes () { + if (_label is null || _text is null || _button is null || _labelR is null || _labelV is null || _win is null) + { + return; + } + UnsetClickedEvent (); _label.Text = "あなたの名前を入力してください:"; _text.Text = "ティラミス"; @@ -175,4 +278,4 @@ public class RuneWidthGreaterThanOne : Scenario _lastRunesUsed = "Wide"; Application.LayoutAndDraw (); } -} +} \ No newline at end of file diff --git a/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs b/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs index 92e126b84..5e795a9c0 100644 --- a/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs +++ b/Examples/UICatalog/Scenarios/SingleBackgroundWorker.cs @@ -1,8 +1,7 @@ -using System; -using System.Collections.Generic; +#nullable enable + using System.Collections.ObjectModel; using System.ComponentModel; -using System.Threading; namespace UICatalog.Scenarios; @@ -18,54 +17,52 @@ public class SingleBackgroundWorker : Scenario Application.Shutdown (); } - public class MainApp : Toplevel + public class MainApp : Window { private readonly ListView _listLog; private readonly ObservableCollection _log = []; private DateTime? _startStaging; - private BackgroundWorker _worker; + private BackgroundWorker? _worker; public MainApp () { - var menu = new MenuBar - { - Menus = - [ - new ( - "_Options", - new MenuItem [] - { - new ( - "_Run Worker", - "", - () => RunWorker (), - null, - null, - KeyCode.CtrlMask | KeyCode.R - ), - null, - new ( - "_Quit", - "", - () => Application.RequestStop (), - null, - null, - Application.QuitKey - ) - } - ) - ] - }; + BorderStyle = LineStyle.None; + // MenuBar + MenuBar menu = new (); - var statusBar = new StatusBar ( - [ - new (Application.QuitKey, "Quit", () => Application.RequestStop ()), - new (Key.R.WithCtrl, "Run Worker", RunWorker) - ]); + menu.Add ( + new MenuBarItem ( + "_Options", + [ + new MenuItem + { + Title = "_Run Worker", + Key = Key.R.WithCtrl, + Action = RunWorker + }, + new MenuItem + { + Title = "_Quit", + Key = Application.QuitKey, + Action = () => Application.RequestStop () + } + ] + ) + ); - var workerLogTop = new Toplevel + // StatusBar + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", () => Application.RequestStop ()), + new (Key.R.WithCtrl, "Run Worker", RunWorker) + ] + ); + + Window workerLogTop = new () { - Title = "Worker Log Top" + Title = "Worker Log Top", + Y = Pos.Bottom (menu), + Height = Dim.Fill (1) }; workerLogTop.Add ( @@ -82,9 +79,6 @@ public class SingleBackgroundWorker : Scenario }; workerLogTop.Add (_listLog); - workerLogTop.Y = 1; - workerLogTop.Height = Dim.Fill (Dim.Func (_ => statusBar.Frame.Height)); - Add (menu, workerLogTop, statusBar); Title = "MainApp"; } @@ -93,11 +87,11 @@ public class SingleBackgroundWorker : Scenario { _worker = new () { WorkerSupportsCancellation = true }; - var cancel = new Button { Text = "Cancel Worker" }; + Button cancel = new () { Text = "Cancel Worker" }; cancel.Accepting += (s, e) => { - if (_worker == null) + if (_worker is null) { _log.Add ($"Worker is not running at {DateTime.Now}!"); _listLog.SetNeedsDraw (); @@ -116,9 +110,10 @@ public class SingleBackgroundWorker : Scenario _log.Add ($"Worker is started at {_startStaging}.{_startStaging:fff}"); _listLog.SetNeedsDraw (); - var md = new Dialog + Dialog md = new () { - Title = $"Running Worker started at {_startStaging}.{_startStaging:fff}", Buttons = [cancel] + Title = $"Running Worker started at {_startStaging}.{_startStaging:fff}", + Buttons = [cancel] }; md.Add ( @@ -127,7 +122,7 @@ public class SingleBackgroundWorker : Scenario _worker.DoWork += (s, e) => { - List stageResult = new (); + List stageResult = []; for (var i = 0; i < 200; i++) { @@ -135,7 +130,7 @@ public class SingleBackgroundWorker : Scenario e.Result = stageResult; Thread.Sleep (1); - if (_worker.CancellationPending) + if (_worker?.CancellationPending == true) { e.Cancel = true; @@ -152,7 +147,7 @@ public class SingleBackgroundWorker : Scenario Application.RequestStop (); } - if (e.Error != null) + if (e.Error is { }) { // Failed _log.Add ( @@ -177,14 +172,22 @@ public class SingleBackgroundWorker : Scenario _listLog.SetNeedsDraw (); Application.LayoutAndDraw (); - var builderUI = - new StagingUIController (_startStaging, e.Result as ObservableCollection); - Toplevel top = Application.TopRunnable; - top.Visible = false; - Application.TopRunnable.Visible = false; + StagingUIController builderUI = + new (_startStaging, e.Result as ObservableCollection); + Toplevel? top = Application.TopRunnable; + + if (top is { }) + { + top.Visible = false; + } + builderUI.Load (); builderUI.Dispose (); - top.Visible = true; + + if (top is { }) + { + top.Visible = true; + } } _worker = null; @@ -197,13 +200,16 @@ public class SingleBackgroundWorker : Scenario public class StagingUIController : Window { - private Toplevel _top; + private Toplevel? _top; - public StagingUIController (DateTime? start, ObservableCollection list) + public StagingUIController (DateTime? start, ObservableCollection? list) { _top = new () { - Title = "_top", Width = Dim.Fill (), Height = Dim.Fill (), Modal = true + Title = "_top", + Width = Dim.Fill (), + Height = Dim.Fill (), + Modal = true }; _top.KeyDown += (s, e) => @@ -230,74 +236,78 @@ public class SingleBackgroundWorker : Scenario return n == 0; } - var menu = new MenuBar - { - Menus = - [ - new ( - "_Stage", - new MenuItem [] - { - new ( - "_Close", - "", - () => - { - if (Close ()) - { - Application.RequestStop (); - } - }, - null, - null, - KeyCode.CtrlMask | KeyCode.C - ) - } - ) - ] - }; + // MenuBar + MenuBar menu = new (); + + menu.Add ( + new MenuBarItem ( + "_Stage", + [ + new MenuItem + { + Title = "_Close", + Key = Key.C.WithCtrl, + Action = () => + { + if (Close ()) + { + Application.RequestStop (); + } + } + } + ] + ) + ); _top.Add (menu); - var statusBar = new StatusBar ( - [ - new ( - Key.C.WithCtrl, - "Close", - () => + // StatusBar + StatusBar statusBar = new ( + [ + new ( + Key.C.WithCtrl, + "Close", + () => + { + if (Close ()) { - if (Close ()) - { - Application.RequestStop (); - } + Application.RequestStop (); } - ) - ]); + } + ) + ] + ); _top.Add (statusBar); - Y = 1; + Y = Pos.Bottom (menu); Height = Dim.Fill (1); Title = $"Worker started at {start}.{start:fff}"; SchemeName = "Base"; - Add ( - new ListView - { - X = 0, - Y = 0, - Width = Dim.Fill (), - Height = Dim.Fill (), - Source = new ListWrapper (list) - } - ); + if (list is { }) + { + Add ( + new ListView + { + X = 0, + Y = 0, + Width = Dim.Fill (), + Height = Dim.Fill (), + Source = new ListWrapper (list) + } + ); + } _top.Add (this); } public void Load () { - Application.Run (_top); - _top.Dispose (); - _top = null; + if (_top is { }) + { + Application.Run (_top); + _top.Dispose (); + _top = null; + } } } } diff --git a/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs b/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs index ad9f93460..3328264ee 100644 --- a/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs +++ b/Examples/UICatalog/Scenarios/SyntaxHighlighting.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.IO; -using System.Linq; +using System.ComponentModel; using System.Reflection; using System.Text; using System.Text.Json; @@ -88,7 +84,6 @@ public class SyntaxHighlighting : Scenario private Attribute _blue; private Attribute _green; private Attribute _magenta; - private MenuItem _miWrap; private TextView _textView; private Attribute _white; @@ -99,7 +94,7 @@ public class SyntaxHighlighting : Scenario /// The type of object to read from the file. /// The file path to read the object instance from. /// Returns a new instance of the object read from the Json file. - public static T ReadFromJsonFile (string filePath) where T : new() + public static T ReadFromJsonFile (string filePath) where T : new () { TextReader reader = null; @@ -127,46 +122,26 @@ public class SyntaxHighlighting : Scenario // Setup - Create a top-level application window and configure it. Toplevel appWindow = new (); - var menu = new MenuBar - { - Menus = - [ - new ( - "_TextView", - new [] - { - _miWrap = new ( - "_Word Wrap", - "", - () => WordWrap () - ) - { - CheckType = MenuItemCheckStyle - .Checked - }, - null, - new ( - "_Syntax Highlighting", - "", - () => ApplySyntaxHighlighting () - ), - null, - new ( - "_Load Text Cells", - "", - () => ApplyLoadCells () - ), - new ( - "_Save Text Cells", - "", - () => SaveCells () - ), - null, - new ("_Quit", "", () => Quit ()) - } - ) - ] - }; + var menu = new MenuBar (); + + MenuItem wrapMenuItem = CreateWordWrapMenuItem (); + + menu.Add ( + new MenuBarItem ( + "_TextView", + [ + wrapMenuItem, + new Line (), + new MenuItem { Title = "_Syntax Highlighting", Action = ApplySyntaxHighlighting }, + new Line (), + new MenuItem { Title = "_Load Text Cells", Action = ApplyLoadCells }, + new MenuItem { Title = "_Save Text Cells", Action = SaveCells }, + new Line (), + new MenuItem { Title = "_Quit", Action = Quit } + ] + ) + ); + appWindow.Add (menu); _textView = new () @@ -192,6 +167,33 @@ public class SyntaxHighlighting : Scenario Application.Shutdown (); } + private MenuItem CreateWordWrapMenuItem () + { + CheckBox checkBox = new () + { + Title = "_Word Wrap", + CheckedState = _textView?.WordWrap == true ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => + { + if (_textView is { }) + { + _textView.WordWrap = checkBox.CheckedState == CheckState.Checked; + } + }; + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + return item; + } + /// /// Writes the given object instance to a Json file. /// Object type must have a parameterless constructor. @@ -211,7 +213,7 @@ public class SyntaxHighlighting : Scenario /// If false the file will be overwritten if it already exists. If true the contents will be appended /// to the file. /// - public static void WriteToJsonFile (string filePath, T objectToWrite, bool append = false) where T : new() + public static void WriteToJsonFile (string filePath, T objectToWrite, bool append = false) where T : new () { TextWriter writer = null; @@ -263,10 +265,10 @@ public class SyntaxHighlighting : Scenario { ClearAllEvents (); - _green = new Attribute (Color.Green, Color.Black); - _blue = new Attribute (Color.Blue, Color.Black); - _magenta = new Attribute (Color.Magenta, Color.Black); - _white = new Attribute (Color.White, Color.Black); + _green = new (Color.Green, Color.Black); + _blue = new (Color.Blue, Color.Black); + _magenta = new (Color.Magenta, Color.Black); + _white = new (Color.White, Color.Black); _textView.SetScheme (new () { Focus = _white }); _textView.Text = @@ -287,7 +289,7 @@ public class SyntaxHighlighting : Scenario _textView.InheritsPreviousAttribute = false; } - private bool ContainsPosition (Match m, int pos) { return pos >= m.Index && pos < m.Index + m.Length; } + private bool ContainsPosition (Match m, int pos) => pos >= m.Index && pos < m.Index + m.Length; private void HighlightTextBasedOnKeywords () { @@ -385,12 +387,6 @@ public class SyntaxHighlighting : Scenario List> cells = _textView.GetAllLines (); WriteToJsonFile (_path, cells); } - - private void WordWrap () - { - _miWrap.Checked = !_miWrap.Checked; - _textView.WordWrap = (bool)_miWrap.Checked; - } } public static class EventExtensions diff --git a/Examples/UICatalog/Scenarios/TabViewExample.cs b/Examples/UICatalog/Scenarios/TabViewExample.cs index 30f55d5f4..71eede3a6 100644 --- a/Examples/UICatalog/Scenarios/TabViewExample.cs +++ b/Examples/UICatalog/Scenarios/TabViewExample.cs @@ -1,4 +1,6 @@ -using System.Linq; +#nullable enable + +using System.Linq; using System.Text; namespace UICatalog.Scenarios; @@ -8,85 +10,41 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TabView")] public class TabViewExample : Scenario { - private MenuItem _miShowBorder; - private MenuItem _miShowTabViewBorder; - private MenuItem _miShowTopLine; - private MenuItem _miTabsOnBottom; - private TabView _tabView; + private CheckBox? _miShowBorderCheckBox; + private CheckBox? _miShowTabViewBorderCheckBox; + private CheckBox? _miShowTopLineCheckBox; + private CheckBox? _miTabsOnBottomCheckBox; + private TabView? _tabView; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel appWindow = new (); - - var menu = new MenuBar + Window appWindow = new () { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ("_Add Blank Tab", "", AddBlankTab), - new ( - "_Clear SelectedTab", - "", - () => _tabView.SelectedTab = null - ), - new ("_Quit", "", Quit) - } - ), - new ( - "_View", - new [] - { - _miShowTopLine = - new ("_Show Top Line", "", ShowTopLine) - { - Checked = true, CheckType = MenuItemCheckStyle.Checked - }, - _miShowBorder = - new ("_Show Border", "", ShowBorder) - { - Checked = true, CheckType = MenuItemCheckStyle.Checked - }, - _miTabsOnBottom = - new ("_Tabs On Bottom", "", SetTabsOnBottom) - { - Checked = false, CheckType = MenuItemCheckStyle.Checked - }, - _miShowTabViewBorder = - new ( - "_Show TabView Border", - "", - ShowTabViewBorder - ) { Checked = true, CheckType = MenuItemCheckStyle.Checked } - } - ) - ] + BorderStyle = LineStyle.None }; - appWindow.Add (menu); - _tabView = new() + // MenuBar + MenuBar menu = new (); + + _tabView = new () { Title = "_Tab View", X = 0, - Y = 1, + Y = Pos.Bottom (menu), Width = 60, Height = 20, BorderStyle = LineStyle.Single }; - _tabView.AddTab (new() { DisplayText = "Tab_1", View = new Label { Text = "hodor!" } }, false); - _tabView.AddTab (new() { DisplayText = "Tab_2", View = new TextField { Text = "durdur", Width = 10 } }, false); - _tabView.AddTab (new() { DisplayText = "_Interactive Tab", View = GetInteractiveTab () }, false); - _tabView.AddTab (new() { DisplayText = "Big Text", View = GetBigTextFileTab () }, false); + _tabView.AddTab (new () { DisplayText = "Tab_1", View = new Label { Text = "hodor!" } }, false); + _tabView.AddTab (new () { DisplayText = "Tab_2", View = new TextField { Text = "durdur", Width = 10 } }, false); + _tabView.AddTab (new () { DisplayText = "_Interactive Tab", View = GetInteractiveTab () }, false); + _tabView.AddTab (new () { DisplayText = "Big Text", View = GetBigTextFileTab () }, false); _tabView.AddTab ( - new() + new () { DisplayText = "Long name Tab, I mean seriously long. Like you would not believe how long this tab's name is its just too much really woooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooowwww thats long", @@ -100,15 +58,16 @@ public class TabViewExample : Scenario ); _tabView.AddTab ( - new() + new () { - DisplayText = "Les Mise" + '\u0301' + "rables", View = new Label { Text = "This tab name is unicode" } + DisplayText = "Les Mise" + '\u0301' + "rables", + View = new Label { Text = "This tab name is unicode" } }, false ); _tabView.AddTab ( - new() + new () { DisplayText = "Les Mise" + '\u0328' + '\u0301' + "rables", View = new Label @@ -123,19 +82,17 @@ public class TabViewExample : Scenario for (var i = 0; i < 100; i++) { _tabView.AddTab ( - new() { DisplayText = $"Tab{i}", View = new Label { Text = $"Welcome to tab {i}" } }, + new () { DisplayText = $"Tab{i}", View = new Label { Text = $"Welcome to tab {i}" } }, false ); } _tabView.SelectedTab = _tabView.Tabs.First (); - appWindow.Add (_tabView); - - var frameRight = new View + View frameRight = new () { X = Pos.Right (_tabView), - Y = 1, + Y = Pos.Top (_tabView), Width = Dim.Fill (), Height = Dim.Fill (1), Title = "_About", @@ -147,16 +104,15 @@ public class TabViewExample : Scenario frameRight.Add ( new TextView { - Text = "This demos the tabs control\nSwitch between tabs using cursor keys.\nThis TextView has AllowsTab = false, so tab should nav too.", + Text = + "This demos the tabs control\nSwitch between tabs using cursor keys.\nThis TextView has AllowsTab = false, so tab should nav too.", Width = Dim.Fill (), Height = Dim.Fill (), - AllowsTab = false, + AllowsTab = false } ); - appWindow.Add (frameRight); - - var frameBelow = new View + View frameBelow = new () { X = 0, Y = Pos.Bottom (_tabView), @@ -166,7 +122,6 @@ public class TabViewExample : Scenario BorderStyle = LineStyle.Single, TabStop = TabBehavior.TabStop, CanFocus = true - }; frameBelow.Add ( @@ -175,31 +130,112 @@ public class TabViewExample : Scenario Text = "This frame exists to check that you can still tab here\nand that the tab control doesn't overspill it's bounds\nAllowsTab is true.", Width = Dim.Fill (), - Height = Dim.Fill (), + Height = Dim.Fill () } ); - appWindow.Add (frameBelow); + // StatusBar + StatusBar statusBar = new ( + [ + new (Application.QuitKey, "Quit", Quit) + ] + ); - var statusBar = new StatusBar ([new (Application.QuitKey, "Quit", Quit)]); - appWindow.Add (statusBar); + // Setup menu checkboxes + _miShowTopLineCheckBox = new () + { + Title = "_Show Top Line", + CheckedState = CheckState.Checked + }; + _miShowTopLineCheckBox.CheckedStateChanged += (s, e) => ShowTopLine (); + + _miShowBorderCheckBox = new () + { + Title = "_Show Border", + CheckedState = CheckState.Checked + }; + _miShowBorderCheckBox.CheckedStateChanged += (s, e) => ShowBorder (); + + _miTabsOnBottomCheckBox = new () + { + Title = "_Tabs On Bottom" + }; + _miTabsOnBottomCheckBox.CheckedStateChanged += (s, e) => SetTabsOnBottom (); + + _miShowTabViewBorderCheckBox = new () + { + Title = "_Show TabView Border", + CheckedState = CheckState.Checked + }; + _miShowTabViewBorderCheckBox.CheckedStateChanged += (s, e) => ShowTabViewBorder (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Add Blank Tab", + Action = AddBlankTab + }, + new MenuItem + { + Title = "_Clear SelectedTab", + Action = () => + { + if (_tabView is { }) + { + _tabView.SelectedTab = null; + } + } + }, + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + CommandView = _miShowTopLineCheckBox + }, + new MenuItem + { + CommandView = _miShowBorderCheckBox + }, + new MenuItem + { + CommandView = _miTabsOnBottomCheckBox + }, + new MenuItem + { + CommandView = _miShowTabViewBorderCheckBox + } + ] + ) + ); + + appWindow.Add (menu, _tabView, frameRight, frameBelow, statusBar); - // Run - Start the application. Application.Run (appWindow); - appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } - private void AddBlankTab () { _tabView.AddTab (new (), false); } + private void AddBlankTab () { _tabView?.AddTab (new (), false); } private View GetBigTextFileTab () { - var text = new TextView { Width = Dim.Fill (), Height = Dim.Fill () }; + TextView text = new () { Width = Dim.Fill (), Height = Dim.Fill () }; - var sb = new StringBuilder (); + StringBuilder sb = new (); for (var y = 0; y < 300; y++) { @@ -218,21 +254,22 @@ public class TabViewExample : Scenario private View GetInteractiveTab () { - var interactiveTab = new View + View interactiveTab = new () { - Width = Dim.Fill (), Height = Dim.Fill (), + Width = Dim.Fill (), + Height = Dim.Fill (), CanFocus = true }; - var lblName = new Label { Text = "Name:" }; + Label lblName = new () { Text = "Name:" }; interactiveTab.Add (lblName); - var tbName = new TextField { X = Pos.Right (lblName), Width = 10 }; + TextField tbName = new () { X = Pos.Right (lblName), Width = 10 }; interactiveTab.Add (tbName); - var lblAddr = new Label { Y = 1, Text = "Address:" }; + Label lblAddr = new () { Y = 1, Text = "Address:" }; interactiveTab.Add (lblAddr); - var tbAddr = new TextField { X = Pos.Right (lblAddr), Y = 1, Width = 10 }; + TextField tbAddr = new () { X = Pos.Right (lblAddr), Y = 1, Width = 10 }; interactiveTab.Add (tbAddr); return interactiveTab; @@ -242,35 +279,47 @@ public class TabViewExample : Scenario private void SetTabsOnBottom () { - _miTabsOnBottom.Checked = !_miTabsOnBottom.Checked; + if (_tabView is null || _miTabsOnBottomCheckBox is null) + { + return; + } - _tabView.Style.TabsOnBottom = (bool)_miTabsOnBottom.Checked; + _tabView.Style.TabsOnBottom = _miTabsOnBottomCheckBox.CheckedState == CheckState.Checked; _tabView.ApplyStyleChanges (); } private void ShowBorder () { - _miShowBorder.Checked = !_miShowBorder.Checked; + if (_tabView is null || _miShowBorderCheckBox is null) + { + return; + } - _tabView.Style.ShowBorder = (bool)_miShowBorder.Checked; + _tabView.Style.ShowBorder = _miShowBorderCheckBox.CheckedState == CheckState.Checked; _tabView.ApplyStyleChanges (); } private void ShowTabViewBorder () { - _miShowTabViewBorder.Checked = !_miShowTabViewBorder.Checked; + if (_tabView is null || _miShowTabViewBorderCheckBox is null) + { + return; + } - _tabView.BorderStyle = _miShowTabViewBorder.Checked == true - ? _tabView.BorderStyle = LineStyle.Single + _tabView.BorderStyle = _miShowTabViewBorderCheckBox.CheckedState == CheckState.Checked + ? LineStyle.Single : LineStyle.None; _tabView.ApplyStyleChanges (); } private void ShowTopLine () { - _miShowTopLine.Checked = !_miShowTopLine.Checked; + if (_tabView is null || _miShowTopLineCheckBox is null) + { + return; + } - _tabView.Style.ShowTopLine = (bool)_miShowTopLine.Checked; + _tabView.Style.ShowTopLine = _miShowTopLineCheckBox.CheckedState == CheckState.Checked; _tabView.ApplyStyleChanges (); } } diff --git a/Examples/UICatalog/Scenarios/TableEditor.cs b/Examples/UICatalog/Scenarios/TableEditor.cs index 57b1f9a60..12ab5e9d8 100644 --- a/Examples/UICatalog/Scenarios/TableEditor.cs +++ b/Examples/UICatalog/Scenarios/TableEditor.cs @@ -1,4 +1,4 @@ -#nullable enable +#nullable enable using System.Data; using System.Globalization; using System.Text; @@ -458,22 +458,6 @@ public class TableEditor : Scenario private Scheme? _alternatingScheme; private DataTable? _currentTable; - private MenuItem? _miAlternatingColors; - private MenuItem? _miAlwaysShowHeaders; - private MenuItem? _miAlwaysUseNormalColorForVerticalCellLines; - private MenuItem? _miBottomline; - private MenuItem? _miCellLines; - private MenuItem? _miCheckboxes; - private MenuItem? _miCursor; - private MenuItem? _miExpandLastColumn; - private MenuItem? _miFullRowSelect; - private MenuItem? _miHeaderMidline; - private MenuItem? _miHeaderOverline; - private MenuItem? _miHeaderUnderline; - private MenuItem? _miRadioboxes; - private MenuItem? _miShowHeaders; - private MenuItem? _miShowHorizontalScrollIndicators; - private MenuItem? _miSmoothScrolling; private Scheme? _redScheme; private Scheme? _redSchemeAlt; private TableView? _tableView; @@ -519,239 +503,38 @@ public class TableEditor : Scenario _tableView = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "_OpenBigExample", - "", - () => OpenExample (true) - ), - new ( - "_OpenSmallExample", - "", - () => OpenExample (false) - ), - new ( - "OpenCharacter_Map", - "", - () => OpenUnicodeMap () - ), - new ( - "OpenTreeExample", - "", - () => OpenTreeExample () - ), - new ( - "_CloseExample", - "", - () => CloseExample () - ), - new ("_Quit", "", () => Quit ()) - } - ), - new ( - "_View", - new [] - { - _miShowHeaders = - new ( - "_ShowHeaders", - "", - () => ToggleShowHeaders () - ) - { - Checked = _tableView!.Style.ShowHeaders, - CheckType = MenuItemCheckStyle.Checked - }, - _miAlwaysShowHeaders = - new ( - "_AlwaysShowHeaders", - "", - () => ToggleAlwaysShowHeaders () - ) - { - Checked = _tableView!.Style.AlwaysShowHeaders, - CheckType = MenuItemCheckStyle.Checked - }, - _miHeaderOverline = - new ( - "_HeaderOverLine", - "", - () => ToggleOverline () - ) - { - Checked = _tableView!.Style - .ShowHorizontalHeaderOverline, - CheckType = MenuItemCheckStyle.Checked - }, - _miHeaderMidline = new ( - "_HeaderMidLine", - "", - () => ToggleHeaderMidline () - ) - { - Checked = _tableView!.Style - .ShowVerticalHeaderLines, - CheckType = MenuItemCheckStyle.Checked - }, - _miHeaderUnderline = new ( - "_HeaderUnderLine", - "", - () => ToggleUnderline () - ) - { - Checked = _tableView!.Style - .ShowHorizontalHeaderUnderline, - CheckType = MenuItemCheckStyle.Checked - }, - _miBottomline = new ( - "_BottomLine", - "", - () => ToggleBottomline () - ) - { - Checked = _tableView!.Style - .ShowHorizontalBottomline, - CheckType = MenuItemCheckStyle - .Checked - }, - _miShowHorizontalScrollIndicators = - new ( - "_HorizontalScrollIndicators", - "", - () => - ToggleHorizontalScrollIndicators () - ) - { - Checked = _tableView!.Style - .ShowHorizontalScrollIndicators, - CheckType = MenuItemCheckStyle.Checked - }, - _miFullRowSelect = new ( - "_FullRowSelect", - "", - () => ToggleFullRowSelect () - ) - { - Checked = _tableView!.FullRowSelect, - CheckType = MenuItemCheckStyle.Checked - }, - _miCellLines = new ( - "_CellLines", - "", - () => ToggleCellLines () - ) - { - Checked = _tableView!.Style - .ShowVerticalCellLines, - CheckType = MenuItemCheckStyle - .Checked - }, - _miExpandLastColumn = - new ( - "_ExpandLastColumn", - "", - () => ToggleExpandLastColumn () - ) - { - Checked = _tableView!.Style.ExpandLastColumn, - CheckType = MenuItemCheckStyle.Checked - }, - _miAlwaysUseNormalColorForVerticalCellLines = - new ( - "_AlwaysUseNormalColorForVerticalCellLines", - "", - () => - ToggleAlwaysUseNormalColorForVerticalCellLines () - ) - { - Checked = _tableView!.Style - .AlwaysUseNormalColorForVerticalCellLines, - CheckType = MenuItemCheckStyle.Checked - }, - _miSmoothScrolling = - new ( - "_SmoothHorizontalScrolling", - "", - () => ToggleSmoothScrolling () - ) - { - Checked = _tableView!.Style - .SmoothHorizontalScrolling, - CheckType = MenuItemCheckStyle.Checked - }, - new ("_AllLines", "", () => ToggleAllCellLines ()), - new ("_NoLines", "", () => ToggleNoCellLines ()), - _miCheckboxes = new ( - "_Checkboxes", - "", - () => ToggleCheckboxes (false) - ) - { - Checked = false, - CheckType = MenuItemCheckStyle.Checked - }, - _miRadioboxes = new ( - "_Radioboxes", - "", - () => ToggleCheckboxes (true) - ) - { - Checked = false, - CheckType = MenuItemCheckStyle.Checked - }, - _miAlternatingColors = - new ( - "Alternating Colors", - "", - () => ToggleAlternatingColors () - ) { CheckType = MenuItemCheckStyle.Checked }, - _miCursor = - new ( - "Invert Selected Cell First Character", - "", - () => - ToggleInvertSelectedCellFirstCharacter () - ) - { - Checked = _tableView!.Style - .InvertSelectedCellFirstCharacter, - CheckType = MenuItemCheckStyle.Checked - }, - new ( - "_ClearColumnStyles", - "", - () => ClearColumnStyles () - ), - new ("Sho_w All Columns", "", () => ShowAllColumns ()) - } - ), - new ( - "_Column", - new MenuItem [] - { - new ("_Set Max Width", "", SetMaxWidth), - new ("_Set Min Width", "", SetMinWidth), - new ( - "_Set MinAcceptableWidth", - "", - SetMinAcceptableWidth - ), - new ( - "_Set All MinAcceptableWidth=1", - "", - SetMinAcceptableWidthToOne - ) - } - ) - ] - }; + var menu = new MenuBar (); + + // File menu + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem { Title = "_OpenBigExample", Action = () => OpenExample (true) }, + new MenuItem { Title = "_OpenSmallExample", Action = () => OpenExample (false) }, + new MenuItem { Title = "OpenCharacter_Map", Action = OpenUnicodeMap }, + new MenuItem { Title = "OpenTreeExample", Action = OpenTreeExample }, + new MenuItem { Title = "_CloseExample", Action = CloseExample }, + new MenuItem { Title = "_Quit", Action = Quit } + ] + ) + ); + + // View menu - created with helper method due to complexity + menu.Add (CreateViewMenu ()); + + // Column menu + menu.Add ( + new MenuBarItem ( + "_Column", + [ + new MenuItem { Title = "_Set Max Width", Action = SetMaxWidth }, + new MenuItem { Title = "_Set Min Width", Action = SetMinWidth }, + new MenuItem { Title = "_Set MinAcceptableWidth", Action = SetMinAcceptableWidth }, + new MenuItem { Title = "_Set All MinAcceptableWidth=1", Action = SetMinAcceptableWidthToOne } + ] + ) + ); appWindow.Add (menu); @@ -828,28 +611,28 @@ public class TableEditor : Scenario // if user clicks the mouse in TableView _tableView!.MouseClick += (s, e) => - { - if (_currentTable == null) - { - return; - } + { + if (_currentTable == null) + { + return; + } - _tableView!.ScreenToCell (e.Position, out int? clickedCol); + _tableView!.ScreenToCell (e.Position, out int? clickedCol); - if (clickedCol != null) - { - if (e.Flags.HasFlag (MouseFlags.Button1Clicked)) - { - // left click in a header - SortColumn (clickedCol.Value); - } - else if (e.Flags.HasFlag (MouseFlags.Button3Clicked)) - { - // right click in a header - ShowHeaderContextMenu (clickedCol.Value, e); - } - } - }; + if (clickedCol != null) + { + if (e.Flags.HasFlag (MouseFlags.Button1Clicked)) + { + // left click in a header + SortColumn (clickedCol.Value); + } + else if (e.Flags.HasFlag (MouseFlags.Button3Clicked)) + { + // right click in a header + ShowHeaderContextMenu (clickedCol.Value, e); + } + } + }; _tableView!.KeyBindings.ReplaceCommands (Key.Space, Command.Accept); @@ -861,6 +644,261 @@ public class TableEditor : Scenario Application.Shutdown (); } + private MenuBarItem CreateViewMenu () + { + // Store checkbox references for the toggle methods to access + Dictionary checkboxes = new (); + + MenuItem CreateCheckBoxMenuItem (string key, string title, bool initialState, Action onToggle) + { + CheckBox checkBox = new () + { + Title = title, + CheckedState = initialState ? CheckState.Checked : CheckState.UnChecked + }; + + checkBox.CheckedStateChanged += (s, e) => onToggle (checkBox.CheckedState == CheckState.Checked); + + MenuItem item = new () { CommandView = checkBox }; + + item.Accepting += (s, e) => + { + checkBox.AdvanceCheckState (); + e.Handled = true; + }; + + checkboxes [key] = checkBox; + + return item; + } + + return new ( + "_View", + [ + CreateCheckBoxMenuItem ( + "ShowHeaders", + "_ShowHeaders", + _tableView!.Style.ShowHeaders, + state => + { + _tableView!.Style.ShowHeaders = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "AlwaysShowHeaders", + "_AlwaysShowHeaders", + _tableView!.Style.AlwaysShowHeaders, + state => + { + _tableView!.Style.AlwaysShowHeaders = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "HeaderOverline", + "_HeaderOverLine", + _tableView!.Style.ShowHorizontalHeaderOverline, + state => + { + _tableView!.Style.ShowHorizontalHeaderOverline = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "HeaderMidline", + "_HeaderMidLine", + _tableView!.Style.ShowVerticalHeaderLines, + state => + { + _tableView!.Style.ShowVerticalHeaderLines = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "HeaderUnderline", + "_HeaderUnderLine", + _tableView!.Style.ShowHorizontalHeaderUnderline, + state => + { + _tableView!.Style.ShowHorizontalHeaderUnderline = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "Bottomline", + "_BottomLine", + _tableView!.Style.ShowHorizontalBottomline, + state => + { + _tableView!.Style.ShowHorizontalBottomline = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "HorizontalScrollIndicators", + "_HorizontalScrollIndicators", + _tableView!.Style.ShowHorizontalScrollIndicators, + state => + { + _tableView!.Style.ShowHorizontalScrollIndicators = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "FullRowSelect", + "_FullRowSelect", + _tableView!.FullRowSelect, + state => + { + _tableView!.FullRowSelect = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "CellLines", + "_CellLines", + _tableView!.Style.ShowVerticalCellLines, + state => + { + _tableView!.Style.ShowVerticalCellLines = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "ExpandLastColumn", + "_ExpandLastColumn", + _tableView!.Style.ExpandLastColumn, + state => + { + _tableView!.Style.ExpandLastColumn = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "AlwaysUseNormalColorForVerticalCellLines", + "_AlwaysUseNormalColorForVerticalCellLines", + _tableView!.Style.AlwaysUseNormalColorForVerticalCellLines, + state => + { + _tableView!.Style.AlwaysUseNormalColorForVerticalCellLines = state; + _tableView!.Update (); + } + ), + CreateCheckBoxMenuItem ( + "SmoothScrolling", + "_SmoothHorizontalScrolling", + _tableView!.Style.SmoothHorizontalScrolling, + state => + { + _tableView!.Style.SmoothHorizontalScrolling = state; + _tableView!.Update (); + } + ), + new MenuItem + { + Title = "_AllLines", + Action = () => + { + _tableView!.Style.ShowHorizontalHeaderOverline = true; + _tableView!.Style.ShowVerticalHeaderLines = true; + _tableView!.Style.ShowHorizontalHeaderUnderline = true; + _tableView!.Style.ShowVerticalCellLines = true; + + checkboxes ["HeaderOverline"].CheckedState = CheckState.Checked; + checkboxes ["HeaderMidline"].CheckedState = CheckState.Checked; + checkboxes ["HeaderUnderline"].CheckedState = CheckState.Checked; + checkboxes ["CellLines"].CheckedState = CheckState.Checked; + + _tableView!.Update (); + } + }, + new MenuItem + { + Title = "_NoLines", + Action = () => + { + _tableView!.Style.ShowHorizontalHeaderOverline = false; + _tableView!.Style.ShowVerticalHeaderLines = false; + _tableView!.Style.ShowHorizontalHeaderUnderline = false; + _tableView!.Style.ShowVerticalCellLines = false; + + checkboxes ["HeaderOverline"].CheckedState = CheckState.UnChecked; + checkboxes ["HeaderMidline"].CheckedState = CheckState.UnChecked; + checkboxes ["HeaderUnderline"].CheckedState = CheckState.UnChecked; + checkboxes ["CellLines"].CheckedState = CheckState.UnChecked; + + _tableView!.Update (); + } + }, + CreateCheckBoxMenuItem ( + "Checkboxes", + "_Checkboxes", + false, + state => + { + if (state) + { + ToggleCheckboxes (false); + checkboxes ["Radioboxes"].CheckedState = CheckState.UnChecked; + } + else if (HasCheckboxes ()) + { + ToggleCheckboxes (false); + } + } + ), + CreateCheckBoxMenuItem ( + "Radioboxes", + "_Radioboxes", + false, + state => + { + if (state) + { + ToggleCheckboxes (true); + checkboxes ["Checkboxes"].CheckedState = CheckState.UnChecked; + } + else if (HasCheckboxes ()) + { + ToggleCheckboxes (true); + } + } + ), + CreateCheckBoxMenuItem ( + "AlternatingColors", + "Alternating Colors", + false, + state => + { + if (state) + { + _tableView!.Style.RowColorGetter = a => { return a.RowIndex % 2 == 0 ? _alternatingScheme : null; }; + } + else + { + _tableView!.Style.RowColorGetter = null; + } + + _tableView!.SetNeedsDraw (); + } + ), + CreateCheckBoxMenuItem ( + "Cursor", + "Invert Selected Cell First Character", + _tableView!.Style.InvertSelectedCellFirstCharacter, + state => + { + _tableView!.Style.InvertSelectedCellFirstCharacter = state; + _tableView!.SetNeedsDraw (); + } + ), + new MenuItem { Title = "_ClearColumnStyles", Action = ClearColumnStyles }, + new MenuItem { Title = "Sho_w All Columns", Action = ShowAllColumns } + ] + ); + } + protected override void Dispose (bool disposing) { base.Dispose (disposing); @@ -1078,7 +1116,7 @@ public class TableEditor : Scenario } private string GetUnicodeCategory (uint u) { return _ranges!.FirstOrDefault (r => u >= r.Start && u <= r.End)?.Category ?? "Unknown"; } - private bool HasCheckboxes () { return _tableView!.Table is CheckBoxTableSourceWrapperBase; } + private bool HasCheckboxes () => _tableView!.Table is CheckBoxTableSourceWrapperBase; private void HideColumn (int clickedCol) { @@ -1236,7 +1274,6 @@ public class TableEditor : Scenario // color 0 and negative values red d <= 0.0000001 ? a.RowIndex % 2 == 0 - && _miAlternatingColors!.Checked == true ? _redSchemeAlt : _redScheme : @@ -1414,7 +1451,7 @@ public class TableEditor : Scenario _tableView!.Update (); } - private string StripArrows (string columnName) { return columnName.Replace ($"{Glyphs.DownArrow}", "").Replace ($"{Glyphs.UpArrow}", ""); } + private string StripArrows (string columnName) => columnName.Replace ($"{Glyphs.DownArrow}", "").Replace ($"{Glyphs.UpArrow}", ""); private void TableViewKeyPress (object? sender, Key e) { @@ -1429,9 +1466,9 @@ public class TableEditor : Scenario { // Delete button deletes all rows when in full row mode foreach (int toRemove in _tableView!.GetAllSelectedCells () - .Select (p => p.Y) - .Distinct () - .OrderByDescending (i => i)) + .Select (p => p.Y) + .Distinct () + .OrderByDescending (i => i)) { _currentTable.Rows.RemoveAt (toRemove); } @@ -1450,70 +1487,6 @@ public class TableEditor : Scenario } } - private void ToggleAllCellLines () - { - _tableView!.Style.ShowHorizontalHeaderOverline = true; - _tableView!.Style.ShowVerticalHeaderLines = true; - _tableView!.Style.ShowHorizontalHeaderUnderline = true; - _tableView!.Style.ShowVerticalCellLines = true; - - _miHeaderOverline!.Checked = true; - _miHeaderMidline!.Checked = true; - _miHeaderUnderline!.Checked = true; - _miCellLines!.Checked = true; - - _tableView!.Update (); - } - - private void ToggleAlternatingColors () - { - //toggle menu item - _miAlternatingColors!.Checked = !_miAlternatingColors.Checked; - - if (_miAlternatingColors.Checked == true) - { - _tableView!.Style.RowColorGetter = a => { return a.RowIndex % 2 == 0 ? _alternatingScheme : null; }; - } - else - { - _tableView!.Style.RowColorGetter = null; - } - - _tableView!.SetNeedsDraw (); - } - - private void ToggleAlwaysShowHeaders () - { - _miAlwaysShowHeaders!.Checked = !_miAlwaysShowHeaders.Checked; - _tableView!.Style.AlwaysShowHeaders = (bool)_miAlwaysShowHeaders.Checked!; - _tableView!.Update (); - } - - private void ToggleAlwaysUseNormalColorForVerticalCellLines () - { - _miAlwaysUseNormalColorForVerticalCellLines!.Checked = - !_miAlwaysUseNormalColorForVerticalCellLines.Checked; - - _tableView!.Style.AlwaysUseNormalColorForVerticalCellLines = - (bool)_miAlwaysUseNormalColorForVerticalCellLines.Checked!; - - _tableView!.Update (); - } - - private void ToggleBottomline () - { - _miBottomline!.Checked = !_miBottomline.Checked; - _tableView!.Style.ShowHorizontalBottomline = (bool)_miBottomline.Checked!; - _tableView!.Update (); - } - - private void ToggleCellLines () - { - _miCellLines!.Checked = !_miCellLines.Checked; - _tableView!.Style.ShowVerticalCellLines = (bool)_miCellLines.Checked!; - _tableView!.Update (); - } - private void ToggleCheckboxes (bool radio) { if (_tableView!.Table is CheckBoxTableSourceWrapperBase wrapper) @@ -1521,9 +1494,6 @@ public class TableEditor : Scenario // unwrap it to remove check boxes _tableView!.Table = wrapper.Wrapping; - _miCheckboxes!.Checked = false; - _miRadioboxes!.Checked = false; - // if toggling off checkboxes/radio if (wrapper.UseRadioButtons == radio) { @@ -1542,7 +1512,7 @@ public class TableEditor : Scenario _checkedFileSystemInfos!.Contains, CheckOrUncheckFile ) - { UseRadioButtons = radio }; + { UseRadioButtons = radio }; } else { @@ -1550,98 +1520,6 @@ public class TableEditor : Scenario } _tableView!.Table = source; - - if (radio) - { - _miRadioboxes!.Checked = true; - _miCheckboxes!.Checked = false; - } - else - { - _miRadioboxes!.Checked = false; - _miCheckboxes!.Checked = true; - } - } - - private void ToggleExpandLastColumn () - { - _miExpandLastColumn!.Checked = !_miExpandLastColumn.Checked; - _tableView!.Style.ExpandLastColumn = (bool)_miExpandLastColumn.Checked!; - - _tableView!.Update (); - } - - private void ToggleFullRowSelect () - { - _miFullRowSelect!.Checked = !_miFullRowSelect.Checked; - _tableView!.FullRowSelect = (bool)_miFullRowSelect.Checked!; - _tableView!.Update (); - } - - private void ToggleHeaderMidline () - { - _miHeaderMidline!.Checked = !_miHeaderMidline.Checked; - _tableView!.Style.ShowVerticalHeaderLines = (bool)_miHeaderMidline.Checked!; - _tableView!.Update (); - } - - private void ToggleHorizontalScrollIndicators () - { - _miShowHorizontalScrollIndicators!.Checked = !_miShowHorizontalScrollIndicators.Checked; - _tableView!.Style.ShowHorizontalScrollIndicators = (bool)_miShowHorizontalScrollIndicators.Checked!; - _tableView!.Update (); - } - - private void ToggleInvertSelectedCellFirstCharacter () - { - //toggle menu item - _miCursor!.Checked = !_miCursor.Checked; - _tableView!.Style.InvertSelectedCellFirstCharacter = (bool)_miCursor.Checked!; - _tableView!.SetNeedsDraw (); - } - - private void ToggleNoCellLines () - { - _tableView!.Style.ShowHorizontalHeaderOverline = false; - _tableView!.Style.ShowVerticalHeaderLines = false; - _tableView!.Style.ShowHorizontalHeaderUnderline = false; - _tableView!.Style.ShowVerticalCellLines = false; - - _miHeaderOverline!.Checked = false; - _miHeaderMidline!.Checked = false; - _miHeaderUnderline!.Checked = false; - _miCellLines!.Checked = false; - - _tableView!.Update (); - } - - private void ToggleOverline () - { - _miHeaderOverline!.Checked = !_miHeaderOverline.Checked; - _tableView!.Style.ShowHorizontalHeaderOverline = (bool)_miHeaderOverline.Checked!; - _tableView!.Update (); - } - - private void ToggleShowHeaders () - { - _miShowHeaders!.Checked = !_miShowHeaders.Checked; - _tableView!.Style.ShowHeaders = (bool)_miShowHeaders.Checked!; - _tableView!.Update (); - } - - private void ToggleSmoothScrolling () - { - _miSmoothScrolling!.Checked = !_miSmoothScrolling.Checked; - _tableView!.Style.SmoothHorizontalScrolling = (bool)_miSmoothScrolling.Checked!; - - _tableView!.Update (); - } - - private void ToggleUnderline () - { - _miHeaderUnderline!.Checked = !_miHeaderUnderline.Checked; - _tableView!.Style.ShowHorizontalHeaderUnderline = (bool)_miHeaderUnderline.Checked!; - _tableView!.Update (); } private int ToTableCol (int col) @@ -1654,13 +1532,11 @@ public class TableEditor : Scenario return col; } - private string TrimArrows (string columnName) - { - return columnName.TrimEnd ( - (char)Glyphs.UpArrow.Value, - (char)Glyphs.DownArrow.Value - ); - } + private string TrimArrows (string columnName) => + columnName.TrimEnd ( + (char)Glyphs.UpArrow.Value, + (char)Glyphs.DownArrow.Value + ); public class UnicodeRange (uint start, uint end, string category) { diff --git a/Examples/UICatalog/Scenarios/TextViewAutocompletePopup.cs b/Examples/UICatalog/Scenarios/TextViewAutocompletePopup.cs index 934cc60f6..a48c9d683 100644 --- a/Examples/UICatalog/Scenarios/TextViewAutocompletePopup.cs +++ b/Examples/UICatalog/Scenarios/TextViewAutocompletePopup.cs @@ -1,4 +1,5 @@ -using System.Linq; +#nullable enable + using System.Text.RegularExpressions; namespace UICatalog.Scenarios; @@ -10,77 +11,63 @@ namespace UICatalog.Scenarios; public class TextViewAutocompletePopup : Scenario { private int _height = 10; - private MenuItem _miMultiline; - private MenuItem _miWrap; - private Shortcut _siMultiline; - private Shortcut _siWrap; - private TextView _textViewBottomLeft; - private TextView _textViewBottomRight; - private TextView _textViewCentered; - private TextView _textViewTopLeft; - private TextView _textViewTopRight; + private CheckBox? _miMultilineCheckBox; + private CheckBox? _miWrapCheckBox; + private Shortcut? _siMultiline; + private Shortcut? _siWrap; + private TextView? _textViewBottomLeft; + private TextView? _textViewBottomRight; + private TextView? _textViewCentered; + private TextView? _textViewTopLeft; + private TextView? _textViewTopRight; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel appWindow = new (); + Window appWindow = new () + { + BorderStyle = LineStyle.None + }; var width = 20; var text = " jamp jemp jimp jomp jump"; - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new [] - { - _miMultiline = - new ( - "_Multiline", - "", - () => Multiline () - ) { CheckType = MenuItemCheckStyle.Checked }, - _miWrap = new ( - "_Word Wrap", - "", - () => WordWrap () - ) { CheckType = MenuItemCheckStyle.Checked }, - new ("_Quit", "", () => Quit ()) - } - ) - ] - }; - appWindow.Add (menu); + // MenuBar + MenuBar menu = new (); - _textViewTopLeft = new() + _textViewTopLeft = new () { - Y = 1, - Width = width, Height = _height, Text = text + Y = Pos.Bottom (menu), + Width = width, + Height = _height, + Text = text }; _textViewTopLeft.DrawingContent += TextViewTopLeft_DrawContent; appWindow.Add (_textViewTopLeft); - _textViewTopRight = new() + _textViewTopRight = new () { - X = Pos.AnchorEnd (width), Y = 1, - Width = width, Height = _height, Text = text + X = Pos.AnchorEnd (width), + Y = Pos.Bottom (menu), + Width = width, + Height = _height, + Text = text }; _textViewTopRight.DrawingContent += TextViewTopRight_DrawContent; appWindow.Add (_textViewTopRight); - _textViewBottomLeft = new() + _textViewBottomLeft = new () { - Y = Pos.AnchorEnd (_height), Width = width, Height = _height, Text = text + Y = Pos.AnchorEnd (_height), + Width = width, + Height = _height, + Text = text }; _textViewBottomLeft.DrawingContent += TextViewBottomLeft_DrawContent; appWindow.Add (_textViewBottomLeft); - _textViewBottomRight = new() + _textViewBottomRight = new () { X = Pos.AnchorEnd (width), Y = Pos.AnchorEnd (_height), @@ -91,7 +78,7 @@ public class TextViewAutocompletePopup : Scenario _textViewBottomRight.DrawingContent += TextViewBottomRight_DrawContent; appWindow.Add (_textViewBottomRight); - _textViewCentered = new() + _textViewCentered = new () { X = Pos.Center (), Y = Pos.Center (), @@ -102,73 +89,170 @@ public class TextViewAutocompletePopup : Scenario _textViewCentered.DrawingContent += TextViewCentered_DrawContent; appWindow.Add (_textViewCentered); - _miMultiline.Checked = _textViewTopLeft.Multiline; - _miWrap.Checked = _textViewTopLeft.WordWrap; + // Setup menu checkboxes + _miMultilineCheckBox = new () + { + Title = "_Multiline", + CheckedState = _textViewTopLeft.Multiline ? CheckState.Checked : CheckState.UnChecked + }; + _miMultilineCheckBox.CheckedStateChanged += (s, e) => Multiline (); - var statusBar = new StatusBar ( - new [] + _miWrapCheckBox = new () + { + Title = "_Word Wrap", + CheckedState = _textViewTopLeft.WordWrap ? CheckState.Checked : CheckState.UnChecked + }; + _miWrapCheckBox.CheckedStateChanged += (s, e) => WordWrap (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem { - new ( - Application.QuitKey, - "Quit", - () => Quit () - ), - _siMultiline = new (Key.Empty, "", null), - _siWrap = new (Key.Empty, "", null) + CommandView = _miMultilineCheckBox + }, + new MenuItem + { + CommandView = _miWrapCheckBox + }, + new MenuItem + { + Title = "_Quit", + Action = Quit } - ); - appWindow.Add (statusBar); + ] + ) + ); + + // StatusBar + _siMultiline = new (Key.Empty, "", null); + _siWrap = new (Key.Empty, "", null); + + StatusBar statusBar = new ( + [ + new ( + Application.QuitKey, + "Quit", + () => Quit () + ), + _siMultiline, + _siWrap + ] + ); + + appWindow.Add (menu, statusBar); appWindow.SubViewLayout += Win_LayoutStarted; - // Run - Start the application. Application.Run (appWindow); - appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } private void Multiline () { - _miMultiline.Checked = !_miMultiline.Checked; + if (_miMultilineCheckBox is null + || _textViewTopLeft is null + || _textViewTopRight is null + || _textViewBottomLeft is null + || _textViewBottomRight is null + || _textViewCentered is null) + { + return; + } + SetMultilineStatusText (); - _textViewTopLeft.Multiline = (bool)_miMultiline.Checked; - _textViewTopRight.Multiline = (bool)_miMultiline.Checked; - _textViewBottomLeft.Multiline = (bool)_miMultiline.Checked; - _textViewBottomRight.Multiline = (bool)_miMultiline.Checked; - _textViewCentered.Multiline = (bool)_miMultiline.Checked; + _textViewTopLeft.Multiline = _miMultilineCheckBox.CheckedState == CheckState.Checked; + _textViewTopRight.Multiline = _miMultilineCheckBox.CheckedState == CheckState.Checked; + _textViewBottomLeft.Multiline = _miMultilineCheckBox.CheckedState == CheckState.Checked; + _textViewBottomRight.Multiline = _miMultilineCheckBox.CheckedState == CheckState.Checked; + _textViewCentered.Multiline = _miMultilineCheckBox.CheckedState == CheckState.Checked; } private void Quit () { Application.RequestStop (); } private void SetAllSuggestions (TextView view) { - ((SingleWordSuggestionGenerator)view.Autocomplete.SuggestionGenerator).AllSuggestions = Regex - .Matches (view.Text, "\\w+") - .Select (s => s.Value) - .Distinct () - .ToList (); + if (view.Autocomplete.SuggestionGenerator is SingleWordSuggestionGenerator generator) + { + generator.AllSuggestions = Regex + .Matches (view.Text, "\\w+") + .Select (s => s.Value) + .Distinct () + .ToList (); + } } - private void SetMultilineStatusText () { _siMultiline.Title = $"Multiline: {_miMultiline.Checked}"; } - - private void SetWrapStatusText () { _siWrap.Title = $"WordWrap: {_miWrap.Checked}"; } - private void TextViewBottomLeft_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewBottomLeft); } - private void TextViewBottomRight_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewBottomRight); } - private void TextViewCentered_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewCentered); } - private void TextViewTopLeft_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewTopLeft); } - private void TextViewTopRight_DrawContent (object sender, DrawEventArgs e) { SetAllSuggestions (_textViewTopRight); } - - private void Win_LayoutStarted (object sender, LayoutEventArgs obj) + private void SetMultilineStatusText () { - _miMultiline.Checked = _textViewTopLeft.Multiline; - _miWrap.Checked = _textViewTopLeft.WordWrap; + if (_siMultiline is { } && _miMultilineCheckBox is { }) + { + _siMultiline.Title = $"Multiline: {_miMultilineCheckBox.CheckedState == CheckState.Checked}"; + } + } + + private void SetWrapStatusText () + { + if (_siWrap is { } && _miWrapCheckBox is { }) + { + _siWrap.Title = $"WordWrap: {_miWrapCheckBox.CheckedState == CheckState.Checked}"; + } + } + + private void TextViewBottomLeft_DrawContent (object? sender, DrawEventArgs e) + { + if (_textViewBottomLeft is { }) + { + SetAllSuggestions (_textViewBottomLeft); + } + } + + private void TextViewBottomRight_DrawContent (object? sender, DrawEventArgs e) + { + if (_textViewBottomRight is { }) + { + SetAllSuggestions (_textViewBottomRight); + } + } + + private void TextViewCentered_DrawContent (object? sender, DrawEventArgs e) + { + if (_textViewCentered is { }) + { + SetAllSuggestions (_textViewCentered); + } + } + + private void TextViewTopLeft_DrawContent (object? sender, DrawEventArgs e) + { + if (_textViewTopLeft is { }) + { + SetAllSuggestions (_textViewTopLeft); + } + } + + private void TextViewTopRight_DrawContent (object? sender, DrawEventArgs e) + { + if (_textViewTopRight is { }) + { + SetAllSuggestions (_textViewTopRight); + } + } + + private void Win_LayoutStarted (object? sender, LayoutEventArgs obj) + { + if (_textViewTopLeft is null || _miMultilineCheckBox is null || _miWrapCheckBox is null || _textViewBottomLeft is null || _textViewBottomRight is null) + { + return; + } + + _miMultilineCheckBox.CheckedState = _textViewTopLeft.Multiline ? CheckState.Checked : CheckState.UnChecked; + _miWrapCheckBox.CheckedState = _textViewTopLeft.WordWrap ? CheckState.Checked : CheckState.UnChecked; SetMultilineStatusText (); SetWrapStatusText (); - if (_miMultiline.Checked == true) + if (_miMultilineCheckBox.CheckedState == CheckState.Checked) { _height = 10; } @@ -182,13 +266,22 @@ public class TextViewAutocompletePopup : Scenario private void WordWrap () { - _miWrap.Checked = !_miWrap.Checked; - _textViewTopLeft.WordWrap = (bool)_miWrap.Checked; - _textViewTopRight.WordWrap = (bool)_miWrap.Checked; - _textViewBottomLeft.WordWrap = (bool)_miWrap.Checked; - _textViewBottomRight.WordWrap = (bool)_miWrap.Checked; - _textViewCentered.WordWrap = (bool)_miWrap.Checked; - _miWrap.Checked = _textViewTopLeft.WordWrap; + if (_miWrapCheckBox is null + || _textViewTopLeft is null + || _textViewTopRight is null + || _textViewBottomLeft is null + || _textViewBottomRight is null + || _textViewCentered is null) + { + return; + } + + _textViewTopLeft.WordWrap = _miWrapCheckBox.CheckedState == CheckState.Checked; + _textViewTopRight.WordWrap = _miWrapCheckBox.CheckedState == CheckState.Checked; + _textViewBottomLeft.WordWrap = _miWrapCheckBox.CheckedState == CheckState.Checked; + _textViewBottomRight.WordWrap = _miWrapCheckBox.CheckedState == CheckState.Checked; + _textViewCentered.WordWrap = _miWrapCheckBox.CheckedState == CheckState.Checked; + _miWrapCheckBox.CheckedState = _textViewTopLeft.WordWrap ? CheckState.Checked : CheckState.UnChecked; SetWrapStatusText (); } } diff --git a/Examples/UICatalog/Scenarios/TreeUseCases.cs b/Examples/UICatalog/Scenarios/TreeUseCases.cs index 021b5b65b..a7c8772e5 100644 --- a/Examples/UICatalog/Scenarios/TreeUseCases.cs +++ b/Examples/UICatalog/Scenarios/TreeUseCases.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.Collections.Generic; using System.Linq; @@ -8,76 +10,93 @@ namespace UICatalog.Scenarios; [ScenarioCategory ("TreeView")] public class TreeUseCases : Scenario { - private View _currentTree; + private View? _currentTree; public override void Main () { - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. - Toplevel appWindow = new (); + Window appWindow = new (); - var menu = new MenuBar - { - Menus = + // MenuBar + MenuBar menu = new (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Quit", + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_Scenarios", + [ + new MenuItem + { + Title = "_Simple Nodes", + Action = LoadSimpleNodes + }, + new MenuItem + { + Title = "_Rooms", + Action = LoadRooms + }, + new MenuItem + { + Title = "_Armies With Builder", + Action = () => LoadArmies (false) + }, + new MenuItem + { + Title = "_Armies With Delegate", + Action = () => LoadArmies (true) + } + ] + ) + ); + + // StatusBar + StatusBar statusBar = new ( [ - new MenuBarItem ("_File", new MenuItem [] { new ("_Quit", "", () => Quit ()) }), - new MenuBarItem ( - "_Scenarios", - new MenuItem [] - { - new ( - "_Simple Nodes", - "", - () => LoadSimpleNodes () - ), - new ("_Rooms", "", () => LoadRooms ()), - new ( - "_Armies With Builder", - "", - () => LoadArmies (false) - ), - new ( - "_Armies With Delegate", - "", - () => LoadArmies (true) - ) - } - ) + new (Application.QuitKey, "Quit", Quit) ] - }; + ); - appWindow.Add (menu); - - var statusBar = new StatusBar ([new (Application.QuitKey, "Quit", Quit)]); - - appWindow.Add (statusBar); + appWindow.Add (menu, statusBar); appWindow.Ready += (sender, args) => - // Start with the most basic use case - LoadSimpleNodes (); + { + // Start with the most basic use case + LoadSimpleNodes (); + }; - // Run - Start the application. Application.Run (appWindow); appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); - } private void LoadArmies (bool useDelegate) { - var army1 = new Army + Army army1 = new () { Designation = "3rd Infantry", - Units = new List { new () { Name = "Orc" }, new () { Name = "Troll" }, new () { Name = "Goblin" } } + Units = [new () { Name = "Orc" }, new () { Name = "Troll" }, new () { Name = "Goblin" }] }; - if (_currentTree != null) + if (_currentTree is { }) { - Application.TopRunnable.Remove (_currentTree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Remove (_currentTree); + } + _currentTree.Dispose (); } @@ -86,18 +105,21 @@ public class TreeUseCases : Scenario if (useDelegate) { tree.TreeBuilder = new DelegateTreeBuilder ( - o => - o is Army a - ? a.Units - : Enumerable.Empty () - ); + o => + o is Army a && a.Units is { } + ? a.Units + : Enumerable.Empty () + ); } else { tree.TreeBuilder = new GameObjectTreeBuilder (); } - Application.TopRunnable.Add (tree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Add (tree); + } tree.AddObject (army1); @@ -106,24 +128,33 @@ public class TreeUseCases : Scenario private void LoadRooms () { - var myHouse = new House + House myHouse = new () { Address = "23 Nowhere Street", - Rooms = new List - { - new () { Name = "Ballroom" }, new () { Name = "Bedroom 1" }, new () { Name = "Bedroom 2" } - } + Rooms = + [ + new () { Name = "Ballroom" }, + new () { Name = "Bedroom 1" }, + new () { Name = "Bedroom 2" } + ] }; - if (_currentTree != null) + if (_currentTree is { }) { - Application.TopRunnable.Remove (_currentTree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Remove (_currentTree); + } + _currentTree.Dispose (); } - var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill(), Height = Dim.Fill (1) }; + TreeView tree = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; - Application.TopRunnable.Add (tree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Add (tree); + } tree.AddObject (myHouse); @@ -132,21 +163,28 @@ public class TreeUseCases : Scenario private void LoadSimpleNodes () { - if (_currentTree != null) + if (_currentTree is { }) { - Application.TopRunnable.Remove (_currentTree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Remove (_currentTree); + } + _currentTree.Dispose (); } - var tree = new TreeView { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; + TreeView tree = new () { X = 0, Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1) }; - Application.TopRunnable.Add (tree); + if (Application.TopRunnable is { }) + { + Application.TopRunnable.Add (tree); + } - var root1 = new TreeNode ("Root1"); + TreeNode root1 = new ("Root1"); root1.Children.Add (new TreeNode ("Child1.1")); root1.Children.Add (new TreeNode ("Child1.2")); - var root2 = new TreeNode ("Root2"); + TreeNode root2 = new ("Root2"); root2.Children.Add (new TreeNode ("Child2.1")); root2.Children.Add (new TreeNode ("Child2.2")); @@ -156,17 +194,21 @@ public class TreeUseCases : Scenario _currentTree = tree; } - private void Quit () { Application.RequestStop (); } + private void Quit () + { + Application.RequestStop (); + } private class Army : GameObject { - public string Designation { get; set; } - public List Units { get; set; } + public string Designation { get; set; } = string.Empty; + public List Units { get; set; } = []; public override string ToString () { return Designation; } } private abstract class GameObject - { } + { + } private class GameObjectTreeBuilder : ITreeBuilder { @@ -175,7 +217,7 @@ public class TreeUseCases : Scenario public IEnumerable GetChildren (GameObject model) { - if (model is Army a) + if (model is Army a && a.Units is { }) { return a.Units; } @@ -184,15 +226,12 @@ public class TreeUseCases : Scenario } } - // Your data class private class House : TreeNode { - // Your properties - public string Address { get; set; } + public string Address { get; set; } = string.Empty; - // ITreeNode member: public override IList Children => Rooms.Cast ().ToList (); - public List Rooms { get; set; } + public List Rooms { get; set; } = []; public override string Text { @@ -203,7 +242,7 @@ public class TreeUseCases : Scenario private class Room : TreeNode { - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public override string Text { @@ -214,7 +253,7 @@ public class TreeUseCases : Scenario private class Unit : GameObject { - public string Name { get; set; } + public string Name { get; set; } = string.Empty; public override string ToString () { return Name; } } } diff --git a/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs b/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs index eeb4f8b78..64102a935 100644 --- a/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs +++ b/Examples/UICatalog/Scenarios/TreeViewFileSystem.cs @@ -1,3 +1,5 @@ +#nullable enable + using System.IO.Abstractions; using System.Text; @@ -10,180 +12,51 @@ namespace UICatalog.Scenarios; public class TreeViewFileSystem : Scenario { private readonly FileSystemIconProvider _iconProvider = new (); - private DetailsFrame _detailsFrame; - private MenuItem _miArrowSymbols; - private MenuItem _miBasicIcons; - private MenuItem _miColoredSymbols; - private MenuItem _miCursor; - private MenuItem _miCustomColors; - private MenuItem _miFullPaths; - private MenuItem _miHighlightModelTextOnly; - private MenuItem _miInvertSymbols; - private MenuItem _miLeaveLastRow; - private MenuItem _miMultiSelect; - private MenuItem _miNerdIcons; - private MenuItem _miNoSymbols; - private MenuItem _miPlusMinus; - private MenuItem _miShowLines; - private MenuItem _miUnicodeIcons; + private DetailsFrame? _detailsFrame; + private CheckBox? _miArrowSymbolsCheckBox; + private CheckBox? _miBasicIconsCheckBox; + private CheckBox? _miColoredSymbolsCheckBox; + private CheckBox? _miCursorCheckBox; + private CheckBox? _miCustomColorsCheckBox; + private CheckBox? _miFullPathsCheckBox; + private CheckBox? _miHighlightModelTextOnlyCheckBox; + private CheckBox? _miInvertSymbolsCheckBox; + private CheckBox? _miLeaveLastRowCheckBox; + private CheckBox? _miMultiSelectCheckBox; + private CheckBox? _miNerdIconsCheckBox; + private CheckBox? _miNoSymbolsCheckBox; + private CheckBox? _miPlusMinusCheckBox; + private CheckBox? _miShowLinesCheckBox; + private CheckBox? _miUnicodeIconsCheckBox; /// A tree view where nodes are files and folders - private TreeView _treeViewFiles; + private TreeView? _treeViewFiles; public override void Main () { Application.Init (); - var win = new Window + Window win = new () { Title = GetName (), Y = 1, // menu Height = Dim.Fill () }; - var top = new Toplevel (); - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "_Quit", - $"{Application.QuitKey}", - () => Quit () - ) - } - ), - new ( - "_View", - new [] - { - _miFullPaths = - new ("_Full Paths", "", () => SetFullName ()) - { - Checked = false, CheckType = MenuItemCheckStyle.Checked - }, - _miMultiSelect = new ( - "_Multi Select", - "", - () => SetMultiSelect () - ) - { - Checked = true, - CheckType = MenuItemCheckStyle - .Checked - } - } - ), - new ( - "_Style", - new [] - { - _miShowLines = - new ("_Show Lines", "", () => ShowLines ()) - { - Checked = true, CheckType = MenuItemCheckStyle.Checked - }, - null /*separator*/, - _miPlusMinus = - new ( - "_Plus Minus Symbols", - "+ -", - () => SetExpandableSymbols ( - (Rune)'+', - (Rune)'-' - ) - ) { Checked = true, CheckType = MenuItemCheckStyle.Radio }, - _miArrowSymbols = - new ( - "_Arrow Symbols", - "> v", - () => SetExpandableSymbols ( - (Rune)'>', - (Rune)'v' - ) - ) { Checked = false, CheckType = MenuItemCheckStyle.Radio }, - _miNoSymbols = - new ( - "_No Symbols", - "", - () => SetExpandableSymbols ( - default (Rune), - null - ) - ) { Checked = false, CheckType = MenuItemCheckStyle.Radio }, - null /*separator*/, - _miColoredSymbols = - new ( - "_Colored Symbols", - "", - () => ShowColoredExpandableSymbols () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked }, - _miInvertSymbols = - new ( - "_Invert Symbols", - "", - () => InvertExpandableSymbols () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked }, - null /*separator*/, - _miBasicIcons = - new ("_Basic Icons", null, SetNoIcons) - { - Checked = false, CheckType = MenuItemCheckStyle.Radio - }, - _miUnicodeIcons = - new ("_Unicode Icons", null, SetUnicodeIcons) - { - Checked = false, CheckType = MenuItemCheckStyle.Radio - }, - _miNerdIcons = - new ("_Nerd Icons", null, SetNerdIcons) - { - Checked = false, CheckType = MenuItemCheckStyle.Radio - }, - null /*separator*/, - _miLeaveLastRow = - new ( - "_Leave Last Row", - "", - () => SetLeaveLastRow () - ) { Checked = true, CheckType = MenuItemCheckStyle.Checked }, - _miHighlightModelTextOnly = - new ( - "_Highlight Model Text Only", - "", - () => SetCheckHighlightModelTextOnly () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked }, - null /*separator*/, - _miCustomColors = - new ( - "C_ustom Colors Hidden Files", - "Yellow/Red", - () => SetCustomColors () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked }, - null /*separator*/, - _miCursor = new ( - "Curs_or (MultiSelect only)", - "", - () => SetCursor () - ) { Checked = false, CheckType = MenuItemCheckStyle.Checked } - } - ) - ] - }; - top.Add (menu); + // MenuBar + MenuBar menu = new (); - _treeViewFiles = new () { X = 0, Y = 0, Width = Dim.Percent (50), Height = Dim.Fill () }; + _treeViewFiles = new () { X = 0, Y = Pos.Bottom (menu), Width = Dim.Percent (50), Height = Dim.Fill () }; _treeViewFiles.DrawLine += TreeViewFiles_DrawLine; _treeViewFiles.VerticalScrollBar.AutoShow = false; _detailsFrame = new (_iconProvider) { - X = Pos.Right (_treeViewFiles), Y = 0, Width = Dim.Fill (), Height = Dim.Fill () + X = Pos.Right (_treeViewFiles), + Y = Pos.Top (_treeViewFiles), + Width = Dim.Fill (), + Height = Dim.Fill () }; win.Add (_detailsFrame); @@ -193,29 +66,214 @@ public class TreeViewFileSystem : Scenario SetupFileTree (); - win.Add (_treeViewFiles); - top.Add (win); + // Setup menu checkboxes + _miFullPathsCheckBox = new () + { + Title = "_Full Paths" + }; + _miFullPathsCheckBox.CheckedStateChanged += (s, e) => SetFullName (); + + _miMultiSelectCheckBox = new () + { + Title = "_Multi Select", + CheckedState = CheckState.Checked + }; + _miMultiSelectCheckBox.CheckedStateChanged += (s, e) => SetMultiSelect (); + + _miShowLinesCheckBox = new () + { + Title = "_Show Lines", + CheckedState = CheckState.Checked + }; + _miShowLinesCheckBox.CheckedStateChanged += (s, e) => ShowLines (); + + _miPlusMinusCheckBox = new () + { + Title = "_Plus Minus Symbols", + CheckedState = CheckState.Checked + }; + _miPlusMinusCheckBox.CheckedStateChanged += (s, e) => SetExpandableSymbols ((Rune)'+', (Rune)'-'); + + _miArrowSymbolsCheckBox = new () + { + Title = "_Arrow Symbols" + }; + _miArrowSymbolsCheckBox.CheckedStateChanged += (s, e) => SetExpandableSymbols ((Rune)'>', (Rune)'v'); + + _miNoSymbolsCheckBox = new () + { + Title = "_No Symbols" + }; + _miNoSymbolsCheckBox.CheckedStateChanged += (s, e) => SetExpandableSymbols (default (Rune), null); + + _miColoredSymbolsCheckBox = new () + { + Title = "_Colored Symbols" + }; + _miColoredSymbolsCheckBox.CheckedStateChanged += (s, e) => ShowColoredExpandableSymbols (); + + _miInvertSymbolsCheckBox = new () + { + Title = "_Invert Symbols" + }; + _miInvertSymbolsCheckBox.CheckedStateChanged += (s, e) => InvertExpandableSymbols (); + + _miBasicIconsCheckBox = new () + { + Title = "_Basic Icons" + }; + _miBasicIconsCheckBox.CheckedStateChanged += (s, e) => SetNoIcons (); + + _miUnicodeIconsCheckBox = new () + { + Title = "_Unicode Icons" + }; + _miUnicodeIconsCheckBox.CheckedStateChanged += (s, e) => SetUnicodeIcons (); + + _miNerdIconsCheckBox = new () + { + Title = "_Nerd Icons" + }; + _miNerdIconsCheckBox.CheckedStateChanged += (s, e) => SetNerdIcons (); + + _miLeaveLastRowCheckBox = new () + { + Title = "_Leave Last Row", + CheckedState = CheckState.Checked + }; + _miLeaveLastRowCheckBox.CheckedStateChanged += (s, e) => SetLeaveLastRow (); + + _miHighlightModelTextOnlyCheckBox = new () + { + Title = "_Highlight Model Text Only" + }; + _miHighlightModelTextOnlyCheckBox.CheckedStateChanged += (s, e) => SetCheckHighlightModelTextOnly (); + + _miCustomColorsCheckBox = new () + { + Title = "C_ustom Colors Hidden Files" + }; + _miCustomColorsCheckBox.CheckedStateChanged += (s, e) => SetCustomColors (); + + _miCursorCheckBox = new () + { + Title = "Curs_or (MultiSelect only)" + }; + _miCursorCheckBox.CheckedStateChanged += (s, e) => SetCursor (); + + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Quit", + Key = Application.QuitKey, + Action = Quit + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_View", + [ + new MenuItem + { + CommandView = _miFullPathsCheckBox + }, + new MenuItem + { + CommandView = _miMultiSelectCheckBox + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_Style", + [ + new MenuItem + { + CommandView = _miShowLinesCheckBox + }, + new MenuItem + { + CommandView = _miPlusMinusCheckBox + }, + new MenuItem + { + CommandView = _miArrowSymbolsCheckBox + }, + new MenuItem + { + CommandView = _miNoSymbolsCheckBox + }, + new MenuItem + { + CommandView = _miColoredSymbolsCheckBox + }, + new MenuItem + { + CommandView = _miInvertSymbolsCheckBox + }, + new MenuItem + { + CommandView = _miBasicIconsCheckBox + }, + new MenuItem + { + CommandView = _miUnicodeIconsCheckBox + }, + new MenuItem + { + CommandView = _miNerdIconsCheckBox + }, + new MenuItem + { + CommandView = _miLeaveLastRowCheckBox + }, + new MenuItem + { + CommandView = _miHighlightModelTextOnlyCheckBox + }, + new MenuItem + { + CommandView = _miCustomColorsCheckBox + }, + new MenuItem + { + CommandView = _miCursorCheckBox + } + ] + ) + ); + + win.Add (menu, _treeViewFiles); _treeViewFiles.GoToFirst (); _treeViewFiles.Expand (); - //SetupScrollBar (); - _treeViewFiles.SetFocus (); UpdateIconCheckedness (); - Application.Run (top); - top.Dispose (); + Application.Run (win); + win.Dispose (); Application.Shutdown (); } - private string AspectGetter (IFileSystemInfo f) { return (_iconProvider.GetIconWithOptionalSpace (f) + f.Name).Trim (); } + private string AspectGetter (IFileSystemInfo f) => (_iconProvider.GetIconWithOptionalSpace (f) + f.Name).Trim (); private void InvertExpandableSymbols () { - _miInvertSymbols.Checked = !_miInvertSymbols.Checked; + if (_treeViewFiles is null || _miInvertSymbolsCheckBox is null) + { + return; + } - _treeViewFiles.Style.InvertExpandSymbolColors = (bool)_miInvertSymbols.Checked; + _treeViewFiles.Style.InvertExpandSymbolColors = _miInvertSymbolsCheckBox.CheckedState == CheckState.Checked; _treeViewFiles.SetNeedsDraw (); } @@ -223,24 +281,34 @@ public class TreeViewFileSystem : Scenario private void SetCheckHighlightModelTextOnly () { - _treeViewFiles.Style.HighlightModelTextOnly = !_treeViewFiles.Style.HighlightModelTextOnly; - _miHighlightModelTextOnly.Checked = _treeViewFiles.Style.HighlightModelTextOnly; + if (_treeViewFiles is null || _miHighlightModelTextOnlyCheckBox is null) + { + return; + } + + _treeViewFiles.Style.HighlightModelTextOnly = _miHighlightModelTextOnlyCheckBox.CheckedState == CheckState.Checked; _treeViewFiles.SetNeedsDraw (); } private void SetCursor () { - _miCursor.Checked = !_miCursor.Checked; + if (_treeViewFiles is null || _miCursorCheckBox is null) + { + return; + } _treeViewFiles.CursorVisibility = - _miCursor.Checked == true ? CursorVisibility.Default : CursorVisibility.Invisible; + _miCursorCheckBox.CheckedState == CheckState.Checked ? CursorVisibility.Default : CursorVisibility.Invisible; } private void SetCustomColors () { - _miCustomColors.Checked = !_miCustomColors.Checked; + if (_treeViewFiles is null || _miCustomColorsCheckBox is null) + { + return; + } - if (_miCustomColors.Checked == true) + if (_miCustomColorsCheckBox.CheckedState == CheckState.Checked) { _treeViewFiles.ColorGetter = m => { @@ -257,8 +325,6 @@ public class TreeViewFileSystem : Scenario _treeViewFiles.GetAttributeForRole (VisualRole.Normal).Background ) }; - - ; } if (m is IFileInfo && m.Attributes.HasFlag (FileAttributes.Hidden)) @@ -274,8 +340,6 @@ public class TreeViewFileSystem : Scenario _treeViewFiles.GetAttributeForRole (VisualRole.Normal).Background ) }; - - ; } return null; @@ -291,9 +355,25 @@ public class TreeViewFileSystem : Scenario private void SetExpandableSymbols (Rune expand, Rune? collapse) { - _miPlusMinus.Checked = expand.Value == '+'; - _miArrowSymbols.Checked = expand.Value == '>'; - _miNoSymbols.Checked = expand.Value == default (int); + if (_treeViewFiles is null) + { + return; + } + + if (_miPlusMinusCheckBox is { }) + { + _miPlusMinusCheckBox.CheckedState = expand.Value == '+' ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miArrowSymbolsCheckBox is { }) + { + _miArrowSymbolsCheckBox.CheckedState = expand.Value == '>' ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miNoSymbolsCheckBox is { }) + { + _miNoSymbolsCheckBox.CheckedState = expand.Value == default (int) ? CheckState.Checked : CheckState.UnChecked; + } _treeViewFiles.Style.ExpandableSymbol = expand; _treeViewFiles.Style.CollapseableSymbol = collapse; @@ -302,9 +382,12 @@ public class TreeViewFileSystem : Scenario private void SetFullName () { - _miFullPaths.Checked = !_miFullPaths.Checked; + if (_treeViewFiles is null || _miFullPathsCheckBox is null) + { + return; + } - if (_miFullPaths.Checked == true) + if (_miFullPathsCheckBox.CheckedState == CheckState.Checked) { _treeViewFiles.AspectGetter = f => f.FullName; } @@ -318,14 +401,22 @@ public class TreeViewFileSystem : Scenario private void SetLeaveLastRow () { - _miLeaveLastRow.Checked = !_miLeaveLastRow.Checked; - _treeViewFiles.Style.LeaveLastRow = (bool)_miLeaveLastRow.Checked; + if (_treeViewFiles is null || _miLeaveLastRowCheckBox is null) + { + return; + } + + _treeViewFiles.Style.LeaveLastRow = _miLeaveLastRowCheckBox.CheckedState == CheckState.Checked; } private void SetMultiSelect () { - _miMultiSelect.Checked = !_miMultiSelect.Checked; - _treeViewFiles.MultiSelect = (bool)_miMultiSelect.Checked; + if (_treeViewFiles is null || _miMultiSelectCheckBox is null) + { + return; + } + + _treeViewFiles.MultiSelect = _miMultiSelectCheckBox.CheckedState == CheckState.Checked; } private void SetNerdIcons () @@ -349,8 +440,13 @@ public class TreeViewFileSystem : Scenario private void SetupFileTree () { + if (_treeViewFiles is null) + { + return; + } + // setup how to build tree - var fs = new FileSystem (); + FileSystem fs = new (); IEnumerable rootDirs = DriveInfo.GetDrives ().Select (d => fs.DirectoryInfo.New (d.RootDirectory.FullName)); @@ -363,52 +459,14 @@ public class TreeViewFileSystem : Scenario _iconProvider.IsOpenGetter = _treeViewFiles.IsExpanded; } - //private void SetupScrollBar () - //{ - // // When using scroll bar leave the last row of the control free (for over-rendering with scroll bar) - // _treeViewFiles.Style.LeaveLastRow = true; - - // var scrollBar = new ScrollBarView (_treeViewFiles, true); - - // scrollBar.ChangedPosition += (s, e) => - // { - // _treeViewFiles.ScrollOffsetVertical = scrollBar.Position; - - // if (_treeViewFiles.ScrollOffsetVertical != scrollBar.Position) - // { - // scrollBar.Position = _treeViewFiles.ScrollOffsetVertical; - // } - - // _treeViewFiles.SetNeedsDraw (); - // }; - - // scrollBar.OtherScrollBarView.ChangedPosition += (s, e) => - // { - // _treeViewFiles.ScrollOffsetHorizontal = scrollBar.OtherScrollBarView.Position; - - // if (_treeViewFiles.ScrollOffsetHorizontal != scrollBar.OtherScrollBarView.Position) - // { - // scrollBar.OtherScrollBarView.Position = _treeViewFiles.ScrollOffsetHorizontal; - // } - - // _treeViewFiles.SetNeedsDraw (); - // }; - - // _treeViewFiles.DrawingContent += (s, e) => - // { - // scrollBar.Size = _treeViewFiles.ContentHeight; - // scrollBar.Position = _treeViewFiles.ScrollOffsetVertical; - // scrollBar.OtherScrollBarView.Size = _treeViewFiles.GetContentWidth (true); - // scrollBar.OtherScrollBarView.Position = _treeViewFiles.ScrollOffsetHorizontal; - // scrollBar.Refresh (); - // }; - //} - private void ShowColoredExpandableSymbols () { - _miColoredSymbols.Checked = !_miColoredSymbols.Checked; + if (_treeViewFiles is null || _miColoredSymbolsCheckBox is null) + { + return; + } - _treeViewFiles.Style.ColorExpandSymbol = (bool)_miColoredSymbols.Checked; + _treeViewFiles.Style.ColorExpandSymbol = _miColoredSymbolsCheckBox.CheckedState == CheckState.Checked; _treeViewFiles.SetNeedsDraw (); } @@ -418,22 +476,31 @@ public class TreeViewFileSystem : Scenario // 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. - _detailsFrame.App?.Popover?.Register (contextMenu); + _detailsFrame?.App?.Popover?.Register (contextMenu); Application.Invoke (() => contextMenu?.MakeVisible (screenPoint)); } private void ShowLines () { - _miShowLines.Checked = !_miShowLines.Checked; + if (_treeViewFiles is null || _miShowLinesCheckBox is null) + { + return; + } - _treeViewFiles.Style.ShowBranchLines = (bool)_miShowLines.Checked!; + _treeViewFiles.Style.ShowBranchLines = _miShowLinesCheckBox.CheckedState == CheckState.Checked; _treeViewFiles.SetNeedsDraw (); } - private void ShowPropertiesOf (IFileSystemInfo fileSystemInfo) { _detailsFrame.FileInfo = fileSystemInfo; } + private void ShowPropertiesOf (IFileSystemInfo fileSystemInfo) + { + if (_detailsFrame is { }) + { + _detailsFrame.FileInfo = fileSystemInfo; + } + } - private void TreeViewFiles_DrawLine (object sender, DrawTreeViewLineEventArgs e) + private void TreeViewFiles_DrawLine (object? sender, DrawTreeViewLineEventArgs e) { // Render directory icons in yellow if (e.Model is IDirectoryInfo d) @@ -454,14 +521,19 @@ public class TreeViewFileSystem : Scenario } } - private void TreeViewFiles_KeyPress (object sender, Key obj) + private void TreeViewFiles_KeyPress (object? sender, Key obj) { + if (_treeViewFiles is null) + { + return; + } + if (obj.KeyCode == (KeyCode.R | KeyCode.CtrlMask)) { - IFileSystemInfo selected = _treeViewFiles.SelectedObject; + IFileSystemInfo? selected = _treeViewFiles.SelectedObject; // nothing is selected - if (selected == null) + if (selected is null) { return; } @@ -469,7 +541,7 @@ public class TreeViewFileSystem : Scenario int? location = _treeViewFiles.GetObjectRow (selected); //selected object is offscreen or somehow not found - if (location == null || location < 0 || location > _treeViewFiles.Frame.Height) + if (location is null || location < 0 || location > _treeViewFiles.Frame.Height) { return; } @@ -484,15 +556,20 @@ public class TreeViewFileSystem : Scenario } } - private void TreeViewFiles_MouseClick (object sender, MouseEventArgs obj) + private void TreeViewFiles_MouseClick (object? sender, MouseEventArgs obj) { + if (_treeViewFiles is null) + { + return; + } + // if user right clicks if (obj.Flags.HasFlag (MouseFlags.Button3Clicked)) { - IFileSystemInfo rightClicked = _treeViewFiles.GetObjectOnRow (obj.Position.Y); + IFileSystemInfo? rightClicked = _treeViewFiles.GetObjectOnRow (obj.Position.Y); // nothing was clicked - if (rightClicked == null) + if (rightClicked is null) { return; } @@ -507,20 +584,34 @@ public class TreeViewFileSystem : Scenario } } - private void TreeViewFiles_SelectionChanged (object sender, SelectionChangedEventArgs e) { ShowPropertiesOf (e.NewValue); } + private void TreeViewFiles_SelectionChanged (object? sender, SelectionChangedEventArgs e) { ShowPropertiesOf (e.NewValue); } private void UpdateIconCheckedness () { - _miBasicIcons.Checked = !_iconProvider.UseNerdIcons && !_iconProvider.UseUnicodeCharacters; - _miUnicodeIcons.Checked = _iconProvider.UseUnicodeCharacters; - _miNerdIcons.Checked = _iconProvider.UseNerdIcons; - _treeViewFiles.SetNeedsDraw (); + if (_miBasicIconsCheckBox is { }) + { + _miBasicIconsCheckBox.CheckedState = !_iconProvider.UseNerdIcons && !_iconProvider.UseUnicodeCharacters + ? CheckState.Checked + : CheckState.UnChecked; + } + + if (_miUnicodeIconsCheckBox is { }) + { + _miUnicodeIconsCheckBox.CheckedState = _iconProvider.UseUnicodeCharacters ? CheckState.Checked : CheckState.UnChecked; + } + + if (_miNerdIconsCheckBox is { }) + { + _miNerdIconsCheckBox.CheckedState = _iconProvider.UseNerdIcons ? CheckState.Checked : CheckState.UnChecked; + } + + _treeViewFiles?.SetNeedsDraw (); } private class DetailsFrame : FrameView { private readonly FileSystemIconProvider _iconProvider; - private IFileSystemInfo _fileInfo; + private IFileSystemInfo? _fileInfo; public DetailsFrame (FileSystemIconProvider iconProvider) { @@ -530,13 +621,13 @@ public class TreeViewFileSystem : Scenario _iconProvider = iconProvider; } - public IFileSystemInfo FileInfo + public IFileSystemInfo? FileInfo { get => _fileInfo; set { _fileInfo = value; - StringBuilder sb = null; + StringBuilder? sb = null; if (_fileInfo is IFileInfo f) { @@ -552,12 +643,12 @@ public class TreeViewFileSystem : Scenario { Title = $"{_iconProvider.GetIconWithOptionalSpace (dir)}{dir.Name}".Trim (); sb = new (); - sb.AppendLine ($"Path:\n {dir?.FullName}\n"); + sb.AppendLine ($"Path:\n {dir.FullName}\n"); sb.AppendLine ($"Modified:\n {dir.LastWriteTime}\n"); sb.AppendLine ($"Created:\n {dir.CreationTime}\n"); } - Text = sb.ToString (); + Text = sb?.ToString () ?? string.Empty; } } } diff --git a/Examples/UICatalog/Scenarios/Unicode.cs b/Examples/UICatalog/Scenarios/Unicode.cs index 412f982da..3fe347aec 100644 --- a/Examples/UICatalog/Scenarios/Unicode.cs +++ b/Examples/UICatalog/Scenarios/Unicode.cs @@ -1,5 +1,5 @@ -using System.Collections.ObjectModel; -using System.IO; +#nullable enable + using System.Text; namespace UICatalog.Scenarios; @@ -15,82 +15,75 @@ public class UnicodeInMenu : Scenario "Τὴ γλῶσσα μοῦ ἔδωσαν ἑλληνικὴ\nτὸ σπίτι φτωχικὸ στὶς ἀμμουδιὲς τοῦ Ὁμήρου.\nΜονάχη ἔγνοια ἡ γλῶσσα μου στὶς ἀμμουδιὲς τοῦ Ὁμήρου."; var gitString = - $"gui.cs 糊 (hú) { - Glyphs.IdenticalTo - } { - Glyphs.DownArrow - }18 { - Glyphs.UpArrow - }10 { - Glyphs.VerticalFourDots - }1 { - Glyphs.HorizontalEllipsis - }"; + $"gui.cs 糊 (hú) {Glyphs.IdenticalTo} {Glyphs.DownArrow}18 {Glyphs.UpArrow}10 {Glyphs.VerticalFourDots}1 {Glyphs.HorizontalEllipsis}"; - // Init Application.Init (); - // Setup - Create a top-level application window and configure it. Window appWindow = new () { - Title = GetQuitKeyAndName () + Title = GetQuitKeyAndName (), + BorderStyle = LineStyle.None }; - var menu = new MenuBar - { - Menus = - [ - new ( - "_Файл", - new MenuItem [] - { - new ( - "_Создать", - "Creates new file", - null - ), - new ("_Открыть", "", null), - new ("Со_хранить", "", null), - new ( - "_Выход", - "", - () => Application.RequestStop () - ) - } - ), - new ( - "_Edit", - new MenuItem [] - { - new ("_Copy", "", null), new ("C_ut", "", null), - new ("_糊", "hú (Paste)", null) - } - ) - ] - }; + // MenuBar + MenuBar menu = new (); + + menu.Add ( + new MenuBarItem ( + "_Файл", + [ + new MenuItem + { + Title = "_Создать", + HelpText = "Creates new file" + }, + new MenuItem + { + Title = "_Открыть" + }, + new MenuItem + { + Title = "Со_хранить" + }, + new MenuItem + { + Title = "_Выход", + Action = () => Application.RequestStop () + } + ] + ) + ); + + menu.Add ( + new MenuBarItem ( + "_Edit", + [ + new MenuItem + { + Title = "_Copy" + }, + new MenuItem + { + Title = "C_ut" + }, + new MenuItem + { + Title = "_糊", + HelpText = "hú (Paste)" + } + ] + ) + ); + appWindow.Add (menu); - var statusBar = new StatusBar ( - [ - new ( - Application.QuitKey, - "Выход", - () => Application.RequestStop () - ), - new (Key.F2, "Создать", null), - new (Key.F3, "Со_хранить", null) - ] - ); - appWindow.Add (statusBar); - - var label = new Label { X = 0, Y = 1, Text = "Label:" }; + Label label = new () { X = 0, Y = Pos.Bottom (menu), Text = "Label:" }; appWindow.Add (label); - var testlabel = new Label + Label testlabel = new () { X = 20, Y = Pos.Y (label), - Width = Dim.Percent (50), Text = gitString }; @@ -98,7 +91,8 @@ public class UnicodeInMenu : Scenario label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Label (CanFocus):" }; appWindow.Add (label); - var sb = new StringBuilder (); + + StringBuilder sb = new (); sb.Append ('e'); sb.Append ('\u0301'); sb.Append ('\u0301'); @@ -107,64 +101,59 @@ public class UnicodeInMenu : Scenario { X = 20, Y = Pos.Y (label), - Width = Dim.Percent (50), CanFocus = true, HotKeySpecifier = new ('&'), Text = $"Should be [e with two accents, but isn't due to #2616]: [{sb}]" }; appWindow.Add (testlabel); + label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "Button:" }; appWindow.Add (label); - var button = new Button { X = 20, Y = Pos.Y (label), Text = "A123456789♥♦♣♠JQK" }; + + Button button = new () { X = 20, Y = Pos.Y (label), Text = "A123456789♥♦♣♠JQK" }; appWindow.Add (button); label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 1, Text = "CheckBox:" }; appWindow.Add (label); - var checkBox = new CheckBox + CheckBox checkBox = new () { X = 20, Y = Pos.Y (label), - Width = Dim.Percent (50), Height = 1, Text = gitString }; + appWindow.Add (checkBox); - var checkBoxRight = new CheckBox + CheckBox checkBoxRight = new () { X = 20, Y = Pos.Bottom (checkBox), - Width = Dim.Percent (50), Height = 1, TextAlignment = Alignment.End, Text = $"End - {gitString}" }; - appWindow.Add (checkBox, checkBoxRight); + appWindow.Add (checkBoxRight); - //label = new () { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 1, Text = "ComboBox:" }; - //appWindow.Add (label); - //var comboBox = new ComboBox { X = 20, Y = Pos.Y (label), Width = Dim.Percent (50) }; - //comboBox.SetSource (new ObservableCollection { gitString, "Со_хранить" }); - - //appWindow.Add (comboBox); - //comboBox.Text = gitString; - - label = new () { X = Pos.X (label), Y = Pos.Bottom (label) + 2, Text = "HexView:" }; + label = new () { X = Pos.X (label), Y = Pos.Bottom (checkBoxRight) + 2, Text = "HexView:" }; appWindow.Add (label); - var hexView = new HexView (new MemoryStream (Encoding.ASCII.GetBytes (gitString + " Со_хранить"))) + HexView hexView = new (new MemoryStream (Encoding.ASCII.GetBytes (gitString + " Со_хранить"))) { - X = 20, Y = Pos.Y (label), Width = Dim.Percent (60), Height = 5 + X = 20, + Y = Pos.Y (label), + Width = Dim.Percent (60), + Height = 5 }; appWindow.Add (hexView); label = new () { X = Pos.X (label), Y = Pos.Bottom (hexView) + 1, Text = "ListView:" }; appWindow.Add (label); - var listView = new ListView + ListView listView = new () { X = 20, Y = Pos.Y (label), @@ -179,28 +168,31 @@ public class UnicodeInMenu : Scenario label = new () { X = Pos.X (label), Y = Pos.Bottom (listView) + 1, Text = "OptionSelector:" }; appWindow.Add (label); - var optionSelector = new OptionSelector + OptionSelector optionSelector = new () { X = 20, Y = Pos.Y (label), Width = Dim.Percent (60), - Labels = new [] { "item #1", gitString, "Со_хранить", "𝔽𝕆𝕆𝔹𝔸ℝ" } + Labels = ["item #1", gitString, "Со_хранить", "𝔽𝕆𝕆𝔹𝔸ℝ"] }; appWindow.Add (optionSelector); label = new () { X = Pos.X (label), Y = Pos.Bottom (optionSelector) + 1, Text = "TextField:" }; appWindow.Add (label); - var textField = new TextField + TextField textField = new () { - X = 20, Y = Pos.Y (label), Width = Dim.Percent (60), Text = gitString + " = Со_хранить" + X = 20, + Y = Pos.Y (label), + Width = Dim.Percent (60), + Text = gitString + " = Со_хранить" }; appWindow.Add (textField); label = new () { X = Pos.X (label), Y = Pos.Bottom (textField) + 1, Text = "TextView:" }; appWindow.Add (label); - var textView = new TextView + TextView textView = new () { X = 20, Y = Pos.Y (label), @@ -210,12 +202,22 @@ public class UnicodeInMenu : Scenario }; appWindow.Add (textView); - // Run - Start the application. + // StatusBar + StatusBar statusBar = new ( + [ + new ( + Application.QuitKey, + "Выход", + () => Application.RequestStop () + ), + new (Key.F2, "Создать", null), + new (Key.F3, "Со_хранить", null) + ] + ); + appWindow.Add (statusBar); + Application.Run (appWindow); - appWindow.Dispose (); - - // Shutdown - Calling Application.Shutdown is required. Application.Shutdown (); } } diff --git a/Examples/UICatalog/Scenarios/WizardAsView.cs b/Examples/UICatalog/Scenarios/WizardAsView.cs index 65f417a82..9df72b835 100644 --- a/Examples/UICatalog/Scenarios/WizardAsView.cs +++ b/Examples/UICatalog/Scenarios/WizardAsView.cs @@ -1,4 +1,5 @@ - +#nullable enable + namespace UICatalog.Scenarios; [ScenarioMetadata ("WizardAsView", "Shows using the Wizard class in an non-modal way")] @@ -9,63 +10,58 @@ public class WizardAsView : Scenario { Application.Init (); - var menu = new MenuBar - { - Menus = - [ - new MenuBarItem ( - "_File", - new MenuItem [] - { - new ( - "_Restart Configuration...", - "", - () => MessageBox.Query ( - "Wizaard", - "Are you sure you want to reset the Wizard and start over?", - "Ok", - "Cancel" - ) - ), - new ( - "Re_boot Server...", - "", - () => MessageBox.Query ( - "Wizaard", - "Are you sure you want to reboot the server start over?", - "Ok", - "Cancel" - ) - ), - new ( - "_Shutdown Server...", - "", - () => MessageBox.Query ( - "Wizaard", - "Are you sure you want to cancel setup and shutdown?", - "Ok", - "Cancel" - ) - ) - } - ) - ] - }; + // MenuBar + MenuBar menu = new (); - Toplevel topLevel = new (); - topLevel.Add (menu); + menu.Add ( + new MenuBarItem ( + "_File", + [ + new MenuItem + { + Title = "_Restart Configuration...", + Action = () => MessageBox.Query ( + "Wizard", + "Are you sure you want to reset the Wizard and start over?", + "Ok", + "Cancel" + ) + }, + new MenuItem + { + Title = "Re_boot Server...", + Action = () => MessageBox.Query ( + "Wizard", + "Are you sure you want to reboot the server start over?", + "Ok", + "Cancel" + ) + }, + new MenuItem + { + Title = "_Shutdown Server...", + Action = () => MessageBox.Query ( + "Wizard", + "Are you sure you want to cancel setup and shutdown?", + "Ok", + "Cancel" + ) + } + ] + ) + ); // No need for a Title because the border is disabled - var wizard = new Wizard + Wizard wizard = new () { X = 0, - Y = 0, + Y = Pos.Bottom (menu), Width = Dim.Fill (), Height = Dim.Fill (), ShadowStyle = ShadowStyle.None }; - // Set Mdoal to false to cause the Wizard class to render without a frame and + // Set Modal to false to cause the Wizard class to render without a frame and // behave like an non-modal View (vs. a modal/pop-up Window). wizard.Modal = false; @@ -100,7 +96,7 @@ public class WizardAsView : Scenario }; // Add 1st step - var firstStep = new WizardStep { Title = "End User License Agreement" }; + WizardStep firstStep = new () { Title = "End User License Agreement" }; wizard.AddStep (firstStep); firstStep.NextButtonText = "Accept!"; @@ -108,46 +104,50 @@ public class WizardAsView : Scenario "This is the End User License Agreement.\n\n\n\n\n\nThis is a test of the emergency broadcast system. This is a test of the emergency broadcast system.\nThis is a test of the emergency broadcast system.\n\n\nThis is a test of the emergency broadcast system.\n\nThis is a test of the emergency broadcast system.\n\n\n\nThe end of the EULA."; // Add 2nd step - var secondStep = new WizardStep { Title = "Second Step" }; + WizardStep secondStep = new () { Title = "Second Step" }; wizard.AddStep (secondStep); secondStep.HelpText = "This is the help text for the Second Step.\n\nPress the button to change the Title.\n\nIf First Name is empty the step will prevent moving to the next step."; - var buttonLbl = new Label { Text = "Second Step Button: ", X = 0, Y = 0 }; + Label buttonLbl = new () { Text = "Second Step Button: ", X = 0, Y = 0 }; - var button = new Button + Button button = new () { - Text = "Press Me to Rename Step", X = Pos.Right (buttonLbl), Y = Pos.Top (buttonLbl) + Text = "Press Me to Rename Step", + X = Pos.Right (buttonLbl), + Y = Pos.Top (buttonLbl) }; button.Accepting += (s, e) => - { - secondStep.Title = "2nd Step"; + { + secondStep.Title = "2nd Step"; - MessageBox.Query ( - "Wizard Scenario", - "This Wizard Step's title was changed to '2nd Step'", - "Ok" - ); - }; + MessageBox.Query ( + "Wizard Scenario", + "This Wizard Step's title was changed to '2nd Step'", + "Ok" + ); + }; secondStep.Add (buttonLbl, button); - var lbl = new Label { Text = "First Name: ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (buttonLbl) }; - var firstNameField = new TextField { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) }; + + Label lbl = new () { Text = "First Name: ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (buttonLbl) }; + TextField firstNameField = new () { Text = "Number", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) }; secondStep.Add (lbl, firstNameField); - lbl = new Label { Text = "Last Name: ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (lbl) }; - var lastNameField = new TextField { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) }; + lbl = new () { Text = "Last Name: ", X = Pos.Left (buttonLbl), Y = Pos.Bottom (lbl) }; + TextField lastNameField = new () { Text = "Six", Width = 30, X = Pos.Right (lbl), Y = Pos.Top (lbl) }; secondStep.Add (lbl, lastNameField); // Add last step - var lastStep = new WizardStep { Title = "The last step" }; + WizardStep lastStep = new () { Title = "The last step" }; wizard.AddStep (lastStep); lastStep.HelpText = "The wizard is complete!\n\nPress the Finish button to continue.\n\nPressing Esc will cancel."; - wizard.Y = Pos.Bottom (menu); - topLevel.Add (wizard); + Window topLevel = new (); + topLevel.Add (menu, wizard); + Application.Run (topLevel); topLevel.Dispose (); Application.Shutdown (); diff --git a/Examples/UICatalog/UICatalogTop.cs b/Examples/UICatalog/UICatalogTop.cs index 304876a87..d02a73919 100644 --- a/Examples/UICatalog/UICatalogTop.cs +++ b/Examples/UICatalog/UICatalogTop.cs @@ -98,7 +98,7 @@ public class UICatalogTop : Toplevel #region MenuBar - private readonly MenuBarv2? _menuBar; + private readonly MenuBar? _menuBar; private CheckBox? _force16ColorsMenuItemCb; private OptionSelector? _themesSelector; private OptionSelector? _topSchemesSelector; @@ -106,14 +106,14 @@ public class UICatalogTop : Toplevel private FlagSelector? _diagnosticFlagsSelector; private CheckBox? _disableMouseCb; - private MenuBarv2 CreateMenuBar () + private MenuBar CreateMenuBar () { - MenuBarv2 menuBar = new ( + MenuBar menuBar = new ( [ new ( "_File", [ - new MenuItemv2 () + new MenuItem () { Title ="_Quit", HelpText = "Quit UI Catalog", @@ -128,19 +128,19 @@ public class UICatalogTop : Toplevel new ( "_Help", [ - new MenuItemv2 ( + new MenuItem ( "_Documentation", "API docs", () => OpenUrl ("https://gui-cs.github.io/Terminal.Gui"), Key.F1 ), - new MenuItemv2 ( + new MenuItem ( "_README", "Project readme", () => OpenUrl ("https://github.com/gui-cs/Terminal.Gui"), Key.F2 ), - new MenuItemv2 ( + new MenuItem ( "_About...", "About UI Catalog", () => MessageBox.Query ( @@ -192,7 +192,7 @@ public class UICatalogTop : Toplevel }; menuItems.Add ( - new MenuItemv2 + new MenuItem { CommandView = _force16ColorsMenuItemCb }); @@ -218,7 +218,7 @@ public class UICatalogTop : Toplevel }; - var menuItem = new MenuItemv2 + var menuItem = new MenuItem { CommandView = _themesSelector, HelpText = "Cycle Through Themes", @@ -263,7 +263,7 @@ public class UICatalogTop : Toplevel } else { - menuItems.Add (new MenuItemv2 () + menuItems.Add (new MenuItem () { Title = "Configuration Manager is not Enabled", Enabled = false @@ -293,7 +293,7 @@ public class UICatalogTop : Toplevel }; menuItems.Add ( - new MenuItemv2 + new MenuItem { CommandView = _diagnosticFlagsSelector, HelpText = "View Diagnostics" @@ -313,7 +313,7 @@ public class UICatalogTop : Toplevel _disableMouseCb.CheckedStateChanged += (_, args) => { Application.IsMouseDisabled = args.Value == CheckState.Checked; }; menuItems.Add ( - new MenuItemv2 + new MenuItem { CommandView = _disableMouseCb, HelpText = "Disable Mouse" @@ -345,7 +345,7 @@ public class UICatalogTop : Toplevel }; menuItems.Add ( - new MenuItemv2 + new MenuItem { CommandView = _logLevelSelector, HelpText = "Cycle Through Log Levels", @@ -356,7 +356,7 @@ public class UICatalogTop : Toplevel menuItems.Add (new Line ()); menuItems.Add ( - new MenuItemv2 ( + new MenuItem ( "_Open Log Folder", string.Empty, () => OpenUrl (UICatalog.LOGFILE_LOCATION) diff --git a/Scripts/Run-LocalCoverage.ps1 b/Scripts/Run-LocalCoverage.ps1 index 32b88053d..710bb26ea 100644 --- a/Scripts/Run-LocalCoverage.ps1 +++ b/Scripts/Run-LocalCoverage.ps1 @@ -26,8 +26,7 @@ dotnet test Tests/UnitTests ` --no-build ` --verbosity minimal ` --collect:"XPlat Code Coverage" ` - --settings Tests/UnitTests/runsettings.coverage.xml ` - --blame-hang-timeout 60s + --settings Tests/UnitTests/runsettings.coverage.xml # ------------------------------------------------------------ # 4. Run UNIT TESTS (parallel) diff --git a/Terminal.Gui/App/ApplicationImpl.Run.cs b/Terminal.Gui/App/ApplicationImpl.Run.cs index f5b785eb2..3d03f7f60 100644 --- a/Terminal.Gui/App/ApplicationImpl.Run.cs +++ b/Terminal.Gui/App/ApplicationImpl.Run.cs @@ -16,9 +16,11 @@ public partial class ApplicationImpl #region Begin->Run->Stop->End + // TODO: This API is not used anywhere; it can be deleted /// public event EventHandler? SessionBegun; + // TODO: This API is not used anywhere; it can be deleted /// public event EventHandler? SessionEnded; @@ -151,6 +153,12 @@ public partial class ApplicationImpl /// public bool StopAfterFirstIteration { get; set; } + /// + public void RaiseIteration () + { + Iteration?.Invoke (null, new ()); + } + /// public event EventHandler? Iteration; @@ -276,6 +284,9 @@ public partial class ApplicationImpl // BUGBUG: Why layout and draw here? This causes the screen to be cleared! //LayoutAndDraw (true); + // TODO: This API is not used (correctly) anywhere; it can be deleted + // TODO: Instead, callers should use the new equivalent of Toplevel.Ready + // TODO: which will be IsRunningChanged with newIsRunning == true SessionEnded?.Invoke (this, new (CachedSessionTokenToplevel)); } @@ -305,9 +316,6 @@ public partial class ApplicationImpl top.Running = false; } - /// - public void RaiseIteration () { Iteration?.Invoke (null, new ()); } - #endregion Begin->Run->Stop->End #region Timeouts and Invoke diff --git a/Terminal.Gui/App/IApplication.cs b/Terminal.Gui/App/IApplication.cs index a14f3534b..88e1d2d1f 100644 --- a/Terminal.Gui/App/IApplication.cs +++ b/Terminal.Gui/App/IApplication.cs @@ -330,6 +330,7 @@ public interface IApplication /// bool StopAfterFirstIteration { get; set; } + // TODO: This API is not used anywhere; it can be deleted /// /// Raised when has been called and has created a new . /// @@ -340,6 +341,7 @@ public interface IApplication /// public event EventHandler? SessionBegun; + // TODO: This API is not used anywhere; it can be deleted /// /// Raised when was called and the session is stopping. The event args contain a /// reference to the diff --git a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs index e52af3f0e..3ad75c4e6 100644 --- a/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs +++ b/Terminal.Gui/App/MainLoop/ApplicationMainLoop.cs @@ -142,6 +142,11 @@ public class ApplicationMainLoop : IApplicationMainLoop /// Interface for class that handles bespoke behaviours that occur when application /// top level changes. diff --git a/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs b/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs index edb09d0e9..ee80619d8 100644 --- a/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs +++ b/Terminal.Gui/App/Toplevel/ToplevelTransitionManager.cs @@ -1,5 +1,10 @@ namespace Terminal.Gui.App; +// TODO: This whole concept is bogus and over-engineered. +// TODO: Remove it and just let subscribers use the IApplication.Iteration +// TODO: If the requirement is they know if it's the first iteration, they can +// TODO: count invocations. + /// /// Handles bespoke behaviours that occur when application top level changes. /// diff --git a/Terminal.Gui/Resources/config.json b/Terminal.Gui/Resources/config.json index 20b206e38..65283b351 100644 --- a/Terminal.Gui/Resources/config.json +++ b/Terminal.Gui/Resources/config.json @@ -748,7 +748,7 @@ "Window.DefaultBorderStyle": "Single", "MessageBox.DefaultBorderStyle": "Single", "Button.DefaultShadow": "None", - "Menuv2.DefaultBorderStyle": "Single", + "Menu.DefaultBorderStyle": "Single", "Schemes": [ { "TopLevel": { @@ -885,7 +885,7 @@ "Window.DefaultBorderStyle": "Single", "MessageBox.DefaultBorderStyle": "Single", "Button.DefaultShadow": "None", - "Menuv2.DefaultBorderStyle": "Single", + "Menu.DefaultBorderStyle": "Single", "Schemes": [ { "TopLevel": { @@ -1022,7 +1022,7 @@ "Window.DefaultBorderStyle": "Single", "MessageBox.DefaultBorderStyle": "Single", "Button.DefaultShadow": "None", - "Menuv2.DefaultBorderStyle": "None", + "Menu.DefaultBorderStyle": "None", "Glyphs.LeftBracket": "[", "Glyphs.RightBracket": "]", "Glyphs.CheckStateChecked": "X", diff --git a/Terminal.Gui/Text/TextFormatter.cs b/Terminal.Gui/Text/TextFormatter.cs index ecd1be459..f81f4c2b3 100644 --- a/Terminal.Gui/Text/TextFormatter.cs +++ b/Terminal.Gui/Text/TextFormatter.cs @@ -271,7 +271,7 @@ public class TextFormatter int size = isVertical ? screen.Height : screen.Width; int current = start + colOffset; List lastZeroWidthPos = null!; - string text = default; + string text = string.Empty; int zeroLengthCount = isVertical ? strings.EnumerateRunes ().Sum (r => r.GetColumns () == 0 ? 1 : 0) : 0; for (int idx = (isVertical ? start - y : start - x) + colOffset; diff --git a/Terminal.Gui/ViewBase/View.Layout.cs b/Terminal.Gui/ViewBase/View.Layout.cs index 76135f600..27eaaac0e 100644 --- a/Terminal.Gui/ViewBase/View.Layout.cs +++ b/Terminal.Gui/ViewBase/View.Layout.cs @@ -1187,36 +1187,10 @@ public partial class View // Layout APIs } //System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}"); - var menuVisible = false; - var statusVisible = false; + //var menuVisible = false; + //var statusVisible = false; - if (viewToMove?.SuperView is null || viewToMove == app?.TopRunnable || viewToMove?.SuperView == app?.TopRunnable) - { - menuVisible = app?.TopRunnable?.MenuBar?.Visible == true; - } - else - { - View? t = viewToMove!.SuperView; - - while (t is { } and not Toplevel) - { - t = t.SuperView; - } - - if (t is Toplevel topLevel) - { - menuVisible = topLevel.MenuBar?.Visible == true; - } - } - - if (viewToMove?.SuperView is null || viewToMove == app?.TopRunnable || viewToMove?.SuperView == app?.TopRunnable) - { - maxDimension = menuVisible ? 1 : 0; - } - else - { - maxDimension = 0; - } + maxDimension = 0; ny = Math.Max (targetY, maxDimension); @@ -1224,7 +1198,7 @@ public partial class View // Layout APIs { if (app is { }) { - maxDimension = statusVisible ? app.Screen.Height - 1 : app.Screen.Height; + maxDimension = app.Screen.Height; } else { @@ -1233,7 +1207,7 @@ public partial class View // Layout APIs } else { - maxDimension = statusVisible ? viewToMove!.SuperView.Viewport.Height - 1 : viewToMove!.SuperView.Viewport.Height; + maxDimension = viewToMove!.SuperView.Viewport.Height; } if (superView?.Margin is { } && superView == viewToMove?.SuperView) @@ -1246,13 +1220,8 @@ public partial class View // Layout APIs if (viewToMove?.Frame.Height <= maxDimension) { ny = ny + viewToMove.Frame.Height > maxDimension - ? Math.Max (maxDimension - viewToMove.Frame.Height, menuVisible ? 1 : 0) + ? Math.Max (maxDimension - viewToMove.Frame.Height, 0) : ny; - - //if (ny > viewToMove.Frame.Y + viewToMove.Frame.Height) - //{ - // ny = Math.Max (viewToMove.Frame.Bottom, 0); - //} } else { diff --git a/Terminal.Gui/Views/FileDialogs/FileDialog.cs b/Terminal.Gui/Views/FileDialogs/FileDialog.cs index 4650730aa..a24c82f0a 100644 --- a/Terminal.Gui/Views/FileDialogs/FileDialog.cs +++ b/Terminal.Gui/Views/FileDialogs/FileDialog.cs @@ -42,9 +42,11 @@ public class FileDialog : Dialog, IDesignable private readonly TextField _tbFind; private readonly TextField _tbPath; private readonly TreeView _treeView; +#if MENU_V1 private MenuBarItem? _allowedTypeMenu; private MenuBar? _allowedTypeMenuBar; private MenuItem []? _allowedTypeMenuItems; +#endif private int _currentSortColumn; private bool _currentSortIsAsc = true; private bool _disposed; @@ -466,6 +468,7 @@ public class FileDialog : Dialog, IDesignable Style.IconProvider.IsOpenGetter = _treeView.IsExpanded; _treeView.AddObjects (_treeRoots.Keys); +#if MENU_V1 // if filtering on file type is configured then create the ComboBox and establish // initial filtering by extension(s) @@ -510,6 +513,7 @@ public class FileDialog : Dialog, IDesignable Add (_allowedTypeMenuBar); } +#endif // if no path has been provided if (_tbPath.Text.Length <= 0) @@ -728,6 +732,7 @@ public class FileDialog : Dialog, IDesignable Accept (false); } } +#if MENU_V1 private void AllowedTypeMenuClicked (int idx) { @@ -748,6 +753,7 @@ public class FileDialog : Dialog, IDesignable State?.RefreshChildren (); WriteStateToTableView (); } +#endif private string AspectGetter (object o) { diff --git a/Terminal.Gui/Views/Line.cs b/Terminal.Gui/Views/Line.cs index 8da81b99d..04ba5703f 100644 --- a/Terminal.Gui/Views/Line.cs +++ b/Terminal.Gui/Views/Line.cs @@ -161,10 +161,7 @@ public class Line : View, IOrientation public event EventHandler>? OrientationChanged; #pragma warning restore CS0067 // The event is never used - /// - /// Called when has changed. - /// - /// The new orientation value. + /// public void OnOrientationChanged (Orientation newOrientation) { // Set dimensions based on new orientation: diff --git a/Terminal.Gui/Views/Menu/Menuv2.cs b/Terminal.Gui/Views/Menu/Menu.cs similarity index 90% rename from Terminal.Gui/Views/Menu/Menuv2.cs rename to Terminal.Gui/Views/Menu/Menu.cs index 2cd610f41..b9efaa91f 100644 --- a/Terminal.Gui/Views/Menu/Menuv2.cs +++ b/Terminal.Gui/Views/Menu/Menu.cs @@ -3,18 +3,18 @@ namespace Terminal.Gui.Views; /// -/// A -derived object to be used as a vertically-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 class Menu : Bar { /// - public Menuv2 () : this ([]) { } + public Menu () : this ([]) { } /// - public Menuv2 (IEnumerable? menuItems) : this (menuItems?.Cast ()) { } + public Menu (IEnumerable? menuItems) : this (menuItems?.Cast ()) { } /// - public Menuv2 (IEnumerable? shortcuts) : base (shortcuts) + public Menu (IEnumerable? shortcuts) : base (shortcuts) { // Do this to support debugging traces where Title gets set base.HotKeySpecifier = (Rune)'\xffff'; @@ -51,14 +51,14 @@ public class Menuv2 : Bar /// /// Gets or sets the menu item that opened this menu as a sub-menu. /// - public MenuItemv2? SuperMenuItem { get; set; } + public MenuItem? SuperMenuItem { get; set; } /// protected override void OnVisibleChanged () { if (Visible) { - SelectedMenuItem = SubViews.Where (mi => mi is MenuItemv2).ElementAtOrDefault (0) as MenuItemv2; + SelectedMenuItem = SubViews.Where (mi => mi is MenuItem).ElementAtOrDefault (0) as MenuItem; } } @@ -69,7 +69,7 @@ public class Menuv2 : Bar switch (view) { - case MenuItemv2 menuItem: + case MenuItem menuItem: { menuItem.CanFocus = true; @@ -176,7 +176,7 @@ public class Menuv2 : Bar { base.OnFocusedChanged (previousFocused, focused); - SelectedMenuItem = focused as MenuItemv2; + SelectedMenuItem = focused as MenuItem; RaiseSelectedMenuItemChanged (SelectedMenuItem); } @@ -184,9 +184,9 @@ public class Menuv2 : Bar /// Gets or set the currently selected menu item. This is a helper that /// tracks . /// - public MenuItemv2? SelectedMenuItem + public MenuItem? SelectedMenuItem { - get => Focused as MenuItemv2; + get => Focused as MenuItem; set { if (value == Focused) @@ -198,7 +198,7 @@ public class Menuv2 : Bar } } - internal void RaiseSelectedMenuItemChanged (MenuItemv2? selected) + internal void RaiseSelectedMenuItemChanged (MenuItem? selected) { // Logging.Debug ($"{Title} ({selected?.Title})"); @@ -210,14 +210,14 @@ public class Menuv2 : Bar /// Called when the selected menu item has changed. /// /// - protected virtual void OnSelectedMenuItemChanged (MenuItemv2? selected) + protected virtual void OnSelectedMenuItemChanged (MenuItem? selected) { } /// /// Raised when the selected menu item has changed. /// - public event EventHandler? SelectedMenuItemChanged; + public event EventHandler? SelectedMenuItemChanged; /// protected override void Dispose (bool disposing) diff --git a/Terminal.Gui/Views/Menu/MenuBarv2.cs b/Terminal.Gui/Views/Menu/MenuBar.cs similarity index 88% rename from Terminal.Gui/Views/Menu/MenuBarv2.cs rename to Terminal.Gui/Views/Menu/MenuBar.cs index 746cdb44d..67312b01e 100644 --- a/Terminal.Gui/Views/Menu/MenuBarv2.cs +++ b/Terminal.Gui/Views/Menu/MenuBar.cs @@ -5,20 +5,20 @@ using System.Diagnostics; namespace Terminal.Gui.Views; /// -/// A horizontal list of s. Each can have a -/// that is shown when the is selected. +/// A horizontal list of s. Each can have a +/// that is shown when the is selected. /// /// /// MenuBars may be hosted by any View and will, by default, be positioned the full width across the top of the View's /// Viewport. /// -public class MenuBarv2 : Menuv2, IDesignable +public class MenuBar : Menu, IDesignable { /// - public MenuBarv2 () : this ([]) { } + public MenuBar () : this ([]) { } /// - public MenuBarv2 (IEnumerable menuBarItems) : base (menuBarItems) + public MenuBar (IEnumerable menuBarItems) : base (menuBarItems) { CanFocus = false; TabStop = TabBehavior.TabGroup; @@ -45,7 +45,7 @@ public class MenuBarv2 : Menuv2, IDesignable return true; } - if (SubViews.OfType ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first) + if (SubViews.OfType ().FirstOrDefault (mbi => mbi.PopoverMenu is { }) is { } first) { Active = true; ShowItem (first); @@ -152,7 +152,7 @@ public class MenuBarv2 : Menuv2, IDesignable /// This is a convenience property to help porting from the v1 MenuBar. /// /// - public MenuBarItemv2 []? Menus + public MenuBarItem []? Menus { set { @@ -163,7 +163,7 @@ public class MenuBarv2 : Menuv2, IDesignable return; } - foreach (MenuBarItemv2 mbi in value) + foreach (MenuBarItem mbi in value) { Add (mbi); } @@ -175,7 +175,7 @@ public class MenuBarv2 : Menuv2, IDesignable { base.OnSubViewAdded (view); - if (view is MenuBarItemv2 mbi) + if (view is MenuBarItem mbi) { mbi.Accepted += OnMenuBarItemAccepted; mbi.PopoverMenuOpenChanged += OnMenuBarItemPopoverMenuOpenChanged; @@ -187,7 +187,7 @@ public class MenuBarv2 : Menuv2, IDesignable { base.OnSubViewRemoved (view); - if (view is MenuBarItemv2 mbi) + if (view is MenuBarItem mbi) { mbi.Accepted -= OnMenuBarItemAccepted; mbi.PopoverMenuOpenChanged -= OnMenuBarItemPopoverMenuOpenChanged; @@ -196,7 +196,7 @@ public class MenuBarv2 : Menuv2, IDesignable private void OnMenuBarItemPopoverMenuOpenChanged (object? sender, EventArgs e) { - if (sender is MenuBarItemv2 mbi) + if (sender is MenuBarItem mbi) { if (e.Value) { @@ -223,7 +223,7 @@ public class MenuBarv2 : Menuv2, IDesignable /// Gets whether any of the menu bar items have a visible . /// /// - public bool IsOpen () { return SubViews.OfType ().Count (sv => sv is { PopoverMenuOpen: true }) > 0; } + public bool IsOpen () { return SubViews.OfType ().Count (sv => sv is { PopoverMenuOpen: true }) > 0; } private bool _active; @@ -297,11 +297,11 @@ public class MenuBarv2 : Menuv2, IDesignable } /// - protected override void OnSelectedMenuItemChanged (MenuItemv2? selected) + protected override void OnSelectedMenuItemChanged (MenuItem? selected) { // Logging.Debug ($"{Title} ({selected?.Title}) - IsOpen: {IsOpen ()}"); - if (IsOpen () && selected is MenuBarItemv2 { PopoverMenuOpen: false } selectedMenuBarItem) + if (IsOpen () && selected is MenuBarItem { PopoverMenuOpen: false } selectedMenuBarItem) { ShowItem (selectedMenuBarItem); } @@ -319,7 +319,7 @@ public class MenuBarv2 : Menuv2, IDesignable } // TODO: This needs to be done whenever a menuitem in any MenuBarItem changes - foreach (MenuBarItemv2? mbi in SubViews.Select (s => s as MenuBarItemv2)) + foreach (MenuBarItem? mbi in SubViews.Select (s => s as MenuBarItem)) { App?.Popover?.Register (mbi?.PopoverMenu); } @@ -331,7 +331,7 @@ public class MenuBarv2 : Menuv2, IDesignable // Logging.Debug ($"{Title} ({args.Context?.Source?.Title})"); // TODO: Ensure sourceMenuBar is actually one of our bar items - if (Visible && Enabled && args.Context?.Source is MenuBarItemv2 { PopoverMenuOpen: false } sourceMenuBarItem) + if (Visible && Enabled && args.Context?.Source is MenuBarItem { PopoverMenuOpen: false } sourceMenuBarItem) { if (!CanFocus) { @@ -365,7 +365,7 @@ public class MenuBarv2 : Menuv2, IDesignable // Logging.Debug ($"{Title} ({args.Context?.Source?.Title}) Command: {args.Context?.Command}"); base.OnAccepted (args); - if (SubViews.OfType ().Contains (args.Context?.Source)) + if (SubViews.OfType ().Contains (args.Context?.Source)) { return; } @@ -377,7 +377,7 @@ public class MenuBarv2 : Menuv2, IDesignable /// Shows the specified popover, but only if the menu bar is active. /// /// - private void ShowItem (MenuBarItemv2? menuBarItem) + private void ShowItem (MenuBarItem? menuBarItem) { // Logging.Debug ($"{Title} - {menuBarItem?.Id}"); @@ -446,7 +446,7 @@ public class MenuBarv2 : Menuv2, IDesignable } } - private MenuBarItemv2? GetActiveItem () { return SubViews.OfType ().FirstOrDefault (sv => sv is { PopoverMenu: { Visible: true } }); } + private MenuBarItem? GetActiveItem () { return SubViews.OfType ().FirstOrDefault (sv => sv is { PopoverMenu: { Visible: true } }); } /// /// Hides the popover menu associated with the active menu bar item and updates the focus state. @@ -459,7 +459,7 @@ public class MenuBarv2 : Menuv2, IDesignable /// /// /// if the popover was hidden - public bool HideItem (MenuBarItemv2? activeItem) + public bool HideItem (MenuBarItem? activeItem) { // Logging.Debug ($"{Title} ({activeItem?.Title}) - Active: {Active}, CanFocus: {CanFocus}, HasFocus: {HasFocus}"); @@ -484,16 +484,16 @@ public class MenuBarv2 : Menuv2, IDesignable /// /// /// - public IEnumerable GetMenuItemsWithTitle (string title) + public IEnumerable GetMenuItemsWithTitle (string title) { - List menuItems = new (); + List menuItems = new (); if (string.IsNullOrEmpty (title)) { return menuItems; } - foreach (MenuBarItemv2 mbi in SubViews.OfType ()) + foreach (MenuBarItem mbi in SubViews.OfType ()) { if (mbi.PopoverMenu is { }) { @@ -558,15 +558,15 @@ public class MenuBarv2 : Menuv2, IDesignable }; Add ( - new MenuBarItemv2 ( + new MenuBarItem ( "_File", [ - new MenuItemv2 (targetView as View, Command.New), - new MenuItemv2 (targetView as View, Command.Open), - new MenuItemv2 (targetView as View, Command.Save), - new MenuItemv2 (targetView as View, Command.SaveAs), + new MenuItem (targetView as View, Command.New), + new MenuItem (targetView as View, Command.Open), + new MenuItem (targetView as View, Command.Save), + new MenuItem (targetView as View, Command.SaveAs), new Line (), - new MenuItemv2 + new MenuItem { Title = "_File Options", SubMenu = new ( @@ -601,25 +601,25 @@ public class MenuBarv2 : Menuv2, IDesignable ) }, new Line (), - new MenuItemv2 + new MenuItem { Title = "_Preferences", SubMenu = new ( [ - new MenuItemv2 + new MenuItem { CommandView = bordersCb, HelpText = "Toggle Menu Borders", Action = ToggleMenuBorders }, - new MenuItemv2 + new MenuItem { HelpText = "3 Mutually Exclusive Options", CommandView = mutuallyExclusiveOptionsSelector, Key = Key.F7 }, new Line (), - new MenuItemv2 + new MenuItem { HelpText = "MenuBar BG Color", CommandView = menuBgColorCp, @@ -629,7 +629,7 @@ public class MenuBarv2 : Menuv2, IDesignable ) }, new Line (), - new MenuItemv2 + new MenuItem { TargetView = targetView as View, Key = Application.QuitKey, @@ -640,16 +640,16 @@ public class MenuBarv2 : Menuv2, IDesignable ); Add ( - new MenuBarItemv2 ( + new MenuBarItem ( "_Edit", [ - new MenuItemv2 (targetView as View, Command.Cut), - new MenuItemv2 (targetView as View, Command.Copy), - new MenuItemv2 (targetView as View, Command.Paste), + new MenuItem (targetView as View, Command.Cut), + new MenuItem (targetView as View, Command.Copy), + new MenuItem (targetView as View, Command.Paste), new Line (), - new MenuItemv2 (targetView as View, Command.SelectAll), + new MenuItem (targetView as View, Command.SelectAll), new Line (), - new MenuItemv2 + new MenuItem { Title = "_Details", SubMenu = new (ConfigureDetailsSubMenu ()) @@ -659,15 +659,15 @@ public class MenuBarv2 : Menuv2, IDesignable ); Add ( - new MenuBarItemv2 ( + new MenuBarItem ( "_Help", [ - new MenuItemv2 + new MenuItem { Title = "_Online Help...", Action = () => MessageBox.Query ("Online Help", "https://gui-cs.github.io/Terminal.Gui", "Ok") }, - new MenuItemv2 + new MenuItem { Title = "About...", Action = () => MessageBox.Query ("About", "Something About Mary.", "Ok") @@ -680,14 +680,14 @@ public class MenuBarv2 : Menuv2, IDesignable void ToggleMenuBorders () { - foreach (MenuBarItemv2 mbi in SubViews.OfType ()) + foreach (MenuBarItem mbi in SubViews.OfType ()) { if (mbi is not { PopoverMenu: { } }) { continue; } - foreach (Menuv2? subMenu in mbi.PopoverMenu.GetAllSubMenus ()) + foreach (Menu? subMenu in mbi.PopoverMenu.GetAllSubMenus ()) { if (bordersCb.CheckedState == CheckState.Checked) { @@ -701,21 +701,21 @@ public class MenuBarv2 : Menuv2, IDesignable } } - MenuItemv2 [] ConfigureDetailsSubMenu () + MenuItem [] ConfigureDetailsSubMenu () { - var detail = new MenuItemv2 + var detail = new MenuItem { Title = "_Detail 1", Text = "Some detail #1" }; - var nestedSubMenu = new MenuItemv2 + var nestedSubMenu = new MenuItem { Title = "_Moar Details", SubMenu = new (ConfigureMoreDetailsSubMenu ()) }; - var editMode = new MenuItemv2 + var editMode = new MenuItem { Text = "App Binding to Command.Edit", Id = "EditMode", @@ -730,14 +730,14 @@ public class MenuBarv2 : Menuv2, IDesignable View [] ConfigureMoreDetailsSubMenu () { - var deeperDetail = new MenuItemv2 + var deeperDetail = new MenuItem { Title = "_Deeper Detail", Text = "Deeper Detail", Action = () => { MessageBox.Query ("Deeper Detail", "Lots of details", "_Ok"); } }; - var belowLineDetail = new MenuItemv2 + var belowLineDetail = new MenuItem { Title = "_Even more detail", Text = "Below the line" diff --git a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs b/Terminal.Gui/Views/Menu/MenuBarItem.cs similarity index 86% rename from Terminal.Gui/Views/Menu/MenuBarItemv2.cs rename to Terminal.Gui/Views/Menu/MenuBarItem.cs index 8d04c3e2b..fb353acbf 100644 --- a/Terminal.Gui/Views/Menu/MenuBarItemv2.cs +++ b/Terminal.Gui/Views/Menu/MenuBarItem.cs @@ -5,18 +5,18 @@ using System.Diagnostics; namespace Terminal.Gui.Views; /// -/// A -derived object to be used as items in a . +/// A -derived object to be used as items in a . /// MenuBarItems hold a instead of a . /// -public class MenuBarItemv2 : MenuItemv2 +public class MenuBarItem : MenuItem { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - public MenuBarItemv2 () : base (null, Command.NotBound) { } + public MenuBarItem () : base (null, Command.NotBound) { } /// - /// Creates a new instance of . Each MenuBarItem typically has a + /// Creates a new instance of . Each MenuBarItem typically has a /// that is /// shown when the item is selected. /// @@ -32,7 +32,7 @@ public class MenuBarItemv2 : MenuItemv2 /// /// The text to display for the command. /// The Popover Menu that will be displayed when this item is selected. - public MenuBarItemv2 (View? targetView, Command command, string? commandText, PopoverMenu? popoverMenu = null) + public MenuBarItem (View? targetView, Command command, string? commandText, PopoverMenu? popoverMenu = null) : base ( targetView, command, @@ -44,14 +44,14 @@ public class MenuBarItemv2 : MenuItemv2 } /// - /// Creates a new instance of with the specified . This is a + /// Creates a new instance of with the specified . This is a /// helper for the most common MenuBar use-cases. /// /// /// /// The text to display for the command. /// The Popover Menu that will be displayed when this item is selected. - public MenuBarItemv2 (string commandText, PopoverMenu? popoverMenu = null) + public MenuBarItem (string commandText, PopoverMenu? popoverMenu = null) : this ( null, Command.NotBound, @@ -60,7 +60,7 @@ public class MenuBarItemv2 : MenuItemv2 { } /// - /// Creates a new instance of with the automatcialy added to a + /// Creates a new instance of with the automatcialy added to a /// . /// This is a helper for the most common MenuBar use-cases. /// @@ -71,7 +71,7 @@ public class MenuBarItemv2 : MenuItemv2 /// The menu items that will be added to the Popover Menu that will be displayed when this item is /// selected. /// - public MenuBarItemv2 (string commandText, IEnumerable menuItems) + public MenuBarItem (string commandText, IEnumerable menuItems) : this ( null, Command.NotBound, @@ -83,7 +83,7 @@ public class MenuBarItemv2 : MenuItemv2 /// Do not use this property. MenuBarItem does not support SubMenu. Use instead. /// /// - public new Menuv2? SubMenu + public new Menu? SubMenu { get => null; set => throw new InvalidOperationException ("MenuBarItem does not support SubMenu. Use PopoverMenu instead."); @@ -185,7 +185,7 @@ public class MenuBarItemv2 : MenuItemv2 { // If the user presses the hotkey for a menu item that is already open, // it should close the menu item (Test: MenuBarItem_HotKey_DeActivates) - if (SuperView is MenuBarv2 { } menuBar) + if (SuperView is MenuBar { } menuBar) { menuBar.HideActiveItem (); } diff --git a/Terminal.Gui/Views/Menu/MenuItemv2.cs b/Terminal.Gui/Views/Menu/MenuItem.cs similarity index 90% rename from Terminal.Gui/Views/Menu/MenuItemv2.cs rename to Terminal.Gui/Views/Menu/MenuItem.cs index 7533881cd..01ca14922 100644 --- a/Terminal.Gui/Views/Menu/MenuItemv2.cs +++ b/Terminal.Gui/Views/Menu/MenuItem.cs @@ -4,19 +4,19 @@ using System.ComponentModel; namespace Terminal.Gui.Views; /// -/// A -derived 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 +/// A -derived 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 +public class MenuItem : Shortcut { /// - /// Creates a new instance of . + /// Creates a new instance of . /// - public MenuItemv2 () : base (Key.Empty, null, null) { } + public MenuItem () : base (Key.Empty, null, null) { } /// - /// Creates a new instance of , binding it to and + /// Creates a new instance of , binding it to and /// . The Key /// has bound to will be used as . /// @@ -34,7 +34,7 @@ public class MenuItemv2 : Shortcut /// The text to display for the command. /// The help text to display. /// The submenu to display when the user selects this menu item. - public MenuItemv2 (View? targetView, Command command, string? commandText = null, string? helpText = null, Menuv2? subMenu = null) + public MenuItem (View? targetView, Command command, string? commandText = null, string? helpText = null, Menu? subMenu = null) : base ( targetView?.HotKeyBindings.GetFirstFromCommands (command)!, string.IsNullOrEmpty (commandText) ? GlobalResources.GetString ($"cmd.{command}") : commandText, @@ -48,17 +48,17 @@ public class MenuItemv2 : Shortcut } /// - public MenuItemv2 (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null) + public MenuItem (string? commandText = null, string? helpText = null, Action? action = null, Key? key = null) : base (key ?? Key.Empty, commandText, action, helpText) { } /// - public MenuItemv2 (string commandText, Key key, Action? action = null) + public MenuItem (string commandText, Key key, Action? action = null) : base (key ?? Key.Empty, commandText, action, null) { } /// - public MenuItemv2 (string? commandText = null, string? helpText = null, Menuv2? subMenu = null) + public MenuItem (string? commandText = null, string? helpText = null, Menu? subMenu = null) : base (Key.Empty, commandText, null, helpText) { SubMenu = subMenu; @@ -171,12 +171,12 @@ public class MenuItemv2 : Shortcut // return ret is true; //} - private Menuv2? _subMenu; + private Menu? _subMenu; /// /// The submenu to display when the user selects this menu item. /// - public Menuv2? SubMenu + public Menu? SubMenu { get => _subMenu; set diff --git a/Terminal.Gui/Views/Menu/PopoverMenu.cs b/Terminal.Gui/Views/Menu/PopoverMenu.cs index c04041bf5..ef0767218 100644 --- a/Terminal.Gui/Views/Menu/PopoverMenu.cs +++ b/Terminal.Gui/Views/Menu/PopoverMenu.cs @@ -5,7 +5,7 @@ namespace Terminal.Gui.Views; /// /// Provides a cascading menu that pops over all other content. Can be used as a context menu or a drop-down /// all other content. Can be used as a context menu or a drop-down -/// menu as part of as part of . +/// menu as part of as part of . /// /// /// @@ -18,7 +18,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// /// Initializes a new instance of the class. /// - public PopoverMenu () : this ((Menuv2?)null) { } + public PopoverMenu () : this ((Menu?)null) { } /// /// Initializes a new instance of the class. If any of the elements of @@ -26,24 +26,24 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// a see will be created instead. /// public PopoverMenu (IEnumerable? menuItems) : this ( - new Menuv2 (menuItems?.Select (item => item ?? new Line ())) + new Menu (menuItems?.Select (item => item ?? new Line ())) { Title = "Popover Root" }) { } /// - public PopoverMenu (IEnumerable? menuItems) : this ( - new Menuv2 (menuItems) + public PopoverMenu (IEnumerable? menuItems) : this ( + new Menu (menuItems) { Title = "Popover Root" }) { } /// - /// Initializes a new instance of the class with the specified root . + /// Initializes a new instance of the class with the specified root . /// - public PopoverMenu (Menuv2? root) + public PopoverMenu (Menu? root) { // Do this to support debugging traces where Title gets set base.HotKeySpecifier = (Rune)'\xffff'; @@ -107,7 +107,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable return false; } - if (MostFocused is MenuItemv2 { SuperView: Menuv2 focusedMenu }) + if (MostFocused is MenuItem { SuperView: Menu focusedMenu }) { focusedMenu.SuperMenuItem?.SetFocus (); @@ -119,7 +119,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable bool? MoveRight (ICommandContext? ctx) { - if (MostFocused is MenuItemv2 { SubMenu.Visible: true } focused) + if (MostFocused is MenuItem { SubMenu.Visible: true } focused) { focused.SubMenu.SetFocus (); @@ -228,12 +228,12 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable } } - private Menuv2? _root; + private Menu? _root; /// - /// Gets or sets the that is the root of the Popover Menu. + /// Gets or sets the that is the root of the Popover Menu. /// - public Menuv2? Root + public Menu? Root { get => _root; set @@ -257,9 +257,9 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable UpdateKeyBindings (); // TODO: This needs to be done whenever any MenuItem in the menu tree changes to support dynamic menus - IEnumerable allMenus = GetAllSubMenus (); + IEnumerable allMenus = GetAllSubMenus (); - foreach (Menuv2 menu in allMenus) + foreach (Menu menu in allMenus) { menu.App = App; menu.Visible = false; @@ -272,9 +272,9 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable private void UpdateKeyBindings () { - IEnumerable all = GetMenuItemsOfAllSubMenus (); + IEnumerable all = GetMenuItemsOfAllSubMenus (); - foreach (MenuItemv2 menuItem in all.Where (mi => mi.Command != Command.NotBound)) + foreach (MenuItem menuItem in all.Where (mi => mi.Command != Command.NotBound)) { Key? key; @@ -309,9 +309,9 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable protected override bool OnKeyDownNotHandled (Key key) { // See if any of our MenuItems have this key as Key - IEnumerable all = GetMenuItemsOfAllSubMenus (); + IEnumerable all = GetMenuItemsOfAllSubMenus (); - foreach (MenuItemv2 menuItem in all) + foreach (MenuItem menuItem in all) { if (key != Application.QuitKey && menuItem.Key == key) { @@ -328,26 +328,26 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// Gets all the submenus in the PopoverMenu. /// /// - public IEnumerable GetAllSubMenus () + public IEnumerable GetAllSubMenus () { - List result = []; + List result = []; if (Root == null) { return result; } - Stack stack = new (); + Stack stack = new (); stack.Push (Root); while (stack.Count > 0) { - Menuv2 currentMenu = stack.Pop (); + Menu currentMenu = stack.Pop (); result.Add (currentMenu); foreach (View subView in currentMenu.SubViews) { - if (subView is MenuItemv2 { SubMenu: { } } menuItem) + if (subView is MenuItem { SubMenu: { } } menuItem) { stack.Push (menuItem.SubMenu); } @@ -361,15 +361,15 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// Gets all the MenuItems in the PopoverMenu. /// /// - internal IEnumerable GetMenuItemsOfAllSubMenus () + internal IEnumerable GetMenuItemsOfAllSubMenus () { - List result = []; + List result = []; - foreach (Menuv2 menu in GetAllSubMenus ()) + foreach (Menu menu in GetAllSubMenus ()) { foreach (View subView in menu.SubViews) { - if (subView is MenuItemv2 menuItem) + if (subView is MenuItem menuItem) { result.Add (menuItem); } @@ -383,16 +383,16 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// Pops up the submenu of the specified MenuItem, if there is one. /// /// - internal void ShowSubMenu (MenuItemv2? menuItem) + internal void ShowSubMenu (MenuItem? menuItem) { - var menu = menuItem?.SuperView as Menuv2; + var menu = menuItem?.SuperView as Menu; // Logging.Debug ($"{Title} - menuItem: {menuItem?.Title}, menu: {menu?.Title}"); menu?.Layout (); // If there's a visible peer, remove / hide it - if (menu?.SubViews.FirstOrDefault (v => v is MenuItemv2 { SubMenu.Visible: true }) is MenuItemv2 visiblePeer) + if (menu?.SubViews.FirstOrDefault (v => v is MenuItem { SubMenu.Visible: true }) is MenuItem visiblePeer) { HideAndRemoveSubMenu (visiblePeer.SubMenu); visiblePeer.ForceFocusColors = false; @@ -421,7 +421,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// The menu to locate. /// Ideal screen-relative location. /// - internal Point GetMostVisibleLocationForSubMenu (Menuv2 menu, Point idealLocation) + internal Point GetMostVisibleLocationForSubMenu (Menu menu, Point idealLocation) { var pos = Point.Empty; @@ -436,7 +436,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable return new (nx, ny); } - private void AddAndShowSubMenu (Menuv2? menu) + private void AddAndShowSubMenu (Menu? menu) { if (menu is { SuperView: null, Visible: false }) { @@ -462,14 +462,14 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable } } - private void HideAndRemoveSubMenu (Menuv2? menu) + private void HideAndRemoveSubMenu (Menu? menu) { if (menu is { Visible: true }) { // Logging.Debug ($"{Title} ({menu?.Title}) - menu.Visible: {menu?.Visible}"); // If there's a visible submenu, remove / hide it - if (menu.SubViews.FirstOrDefault (v => v is MenuItemv2 { SubMenu.Visible: true }) is MenuItemv2 visiblePeer) + if (menu.SubViews.FirstOrDefault (v => v is MenuItem { SubMenu.Visible: true }) is MenuItem visiblePeer) { HideAndRemoveSubMenu (visiblePeer.SubMenu); visiblePeer.ForceFocusColors = false; @@ -511,11 +511,11 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable { // Logging.Debug ($"{Title} ({e.Context?.Source?.Title}) Command: {e.Context?.Command}"); - if (e.Context?.Source is MenuItemv2 { SubMenu: null }) + if (e.Context?.Source is MenuItem { SubMenu: null }) { HideAndRemoveSubMenu (_root); } - else if (e.Context?.Source is MenuItemv2 { SubMenu: { } } menuItemWithSubMenu) + else if (e.Context?.Source is MenuItem { SubMenu: { } } menuItemWithSubMenu) { ShowSubMenu (menuItemWithSubMenu); } @@ -595,7 +595,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// public event EventHandler? Accepted; - private void MenuOnSelectedMenuItemChanged (object? sender, MenuItemv2? e) + private void MenuOnSelectedMenuItemChanged (object? sender, MenuItem? e) { // Logging.Debug ($"{Title} - e.Title: {e?.Title}"); ShowSubMenu (e); @@ -604,7 +604,7 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable /// protected override void OnSubViewAdded (View view) { - if (Root is null && (view is Menuv2 || view is MenuItemv2)) + if (Root is null && (view is Menu || view is MenuItem)) { throw new InvalidOperationException ("Do not add MenuItems or Menus directly to a PopoverMenu. Use the Root property."); } @@ -617,9 +617,9 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable { if (disposing) { - IEnumerable allMenus = GetAllSubMenus (); + IEnumerable allMenus = GetAllSubMenus (); - foreach (Menuv2 menu in allMenus) + foreach (Menu menu in allMenus) { menu.Accepting -= MenuOnAccepting; menu.Accepted -= MenuAccepted; @@ -641,13 +641,13 @@ public class PopoverMenu : PopoverBaseImpl, IDesignable Root = new ( [ - new MenuItemv2 (targetView as View, Command.Cut), - new MenuItemv2 (targetView as View, Command.Copy), - new MenuItemv2 (targetView as View, Command.Paste), + new MenuItem (targetView as View, Command.Cut), + new MenuItem (targetView as View, Command.Copy), + new MenuItem (targetView as View, Command.Paste), new Line (), - new MenuItemv2 (targetView as View, Command.SelectAll), + new MenuItem (targetView as View, Command.SelectAll), new Line (), - new MenuItemv2 (targetView as View, Command.Quit) + new MenuItem (targetView as View, Command.Quit) ]) { Title = "Popover Demo Root" diff --git a/Terminal.Gui/Views/Menuv1/Menu.cs b/Terminal.Gui/Views/Menuv1/Menu.cs deleted file mode 100644 index 61b58649a..000000000 --- a/Terminal.Gui/Views/Menuv1/Menu.cs +++ /dev/null @@ -1,1009 +0,0 @@ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - - -namespace Terminal.Gui.Views; - -#pragma warning disable CS0618 // Type or member is obsolete - -/// -/// An internal class used to represent a menu pop-up menu. Created and managed by . -/// -internal sealed class Menu : View -{ - public Menu () - { - if (Application.TopRunnable is { }) - { - Application.TopRunnable.DrawComplete += Top_DrawComplete; - Application.TopRunnable.SizeChanging += Current_TerminalResized; - } - - Application.MouseEvent += Application_RootMouseEvent; - Application.Mouse.UnGrabbedMouse += Application_UnGrabbedMouse; - - // Things this view knows how to do - AddCommand (Command.Up, () => MoveUp ()); - AddCommand (Command.Down, () => MoveDown ()); - - AddCommand ( - Command.Left, - () => - { - _host!.PreviousMenu (true); - - return true; - } - ); - - AddCommand ( - Command.Cancel, - () => - { - CloseAllMenus (); - - return true; - } - ); - - AddCommand ( - Command.Accept, - () => - { - RunSelected (); - - return true; - } - ); - - AddCommand ( - Command.Select, - ctx => - { - if (ctx is not CommandContext keyCommandContext) - { - return false; - } - - return _host?.SelectItem ((keyCommandContext.Binding.Data as MenuItem)!); - }); - - AddCommand ( - Command.Toggle, - ctx => - { - if (ctx is not CommandContext keyCommandContext) - { - return false; - } - - return ExpandCollapse ((keyCommandContext.Binding.Data as MenuItem)!); - }); - - AddCommand ( - Command.HotKey, - ctx => - { - if (ctx is not CommandContext keyCommandContext) - { - return false; - } - - return _host?.SelectItem ((keyCommandContext.Binding.Data as MenuItem)!); - }); - - // Default key bindings for this view - KeyBindings.Add (Key.CursorUp, Command.Up); - KeyBindings.Add (Key.CursorDown, Command.Down); - KeyBindings.Add (Key.CursorLeft, Command.Left); - KeyBindings.Add (Key.CursorRight, Command.Right); - KeyBindings.Add (Key.Esc, Command.Cancel); - } - - internal int _currentChild; - internal View? _previousSubFocused; - private readonly MenuBarItem? _barItems; - private readonly MenuBar _host; - - public override void BeginInit () - { - base.BeginInit (); - - Rectangle frame = MakeFrame (Frame.X, Frame.Y, _barItems!.Children!, Parent); - - if (Frame.X != frame.X) - { - X = frame.X; - } - - if (Frame.Y != frame.Y) - { - Y = frame.Y; - } - - Width = frame.Width; - Height = frame.Height; - - if (_barItems.Children is { }) - { - foreach (MenuItem? menuItem in _barItems.Children) - { - if (menuItem is { }) - { - menuItem._menuBar = Host; - - if (menuItem.ShortcutKey != Key.Empty) - { - KeyBinding keyBinding = new ([Command.Select], this, data: menuItem); - - // Remove an existent ShortcutKey - menuItem._menuBar.HotKeyBindings.Remove (menuItem.ShortcutKey!); - menuItem._menuBar.HotKeyBindings.Add (menuItem.ShortcutKey!, keyBinding); - } - } - } - } - - if (_barItems is { IsTopLevel: true }) - { - // This is a standalone MenuItem on a MenuBar - SetScheme (_host.GetScheme ()); - CanFocus = true; - } - else - { - _currentChild = -1; - - for (var i = 0; i < _barItems.Children?.Length; i++) - { - if (_barItems.Children [i]?.IsEnabled () == true) - { - _currentChild = i; - - break; - } - } - - SetScheme (_host.GetScheme ()); - CanFocus = true; - WantMousePositionReports = _host.WantMousePositionReports; - } - - BorderStyle = _host.MenusBorderStyle; - - AddCommand ( - Command.Right, - () => - { - _host.NextMenu ( - !_barItems.IsTopLevel - || (_barItems.Children is { Length: > 0 } - && _currentChild > -1 - && _currentChild < _barItems.Children.Length - && _barItems.Children [_currentChild]!.IsFromSubMenu), - _barItems.Children is { Length: > 0 } - && _currentChild > -1 - && _host.UseSubMenusSingleFrame - && _barItems.SubMenu ( - _barItems.Children [_currentChild]! - ) - != null! - ); - - return true; - } - ); - - AddKeyBindingsHotKey (_barItems); - } - - public override Point? PositionCursor () - { - if (_host.IsMenuOpen) - { - if (_barItems!.IsTopLevel) - { - return _host.PositionCursor (); - } - - Move (2, 1 + _currentChild); - - return null; // Don't show the cursor - } - - return _host.PositionCursor (); - } - - public void Run (Action? action) - { - if (action is null) - { - return; - } - - Application.Mouse.UngrabMouse (); - _host.CloseAllMenus (); - Application.LayoutAndDraw (true); - - _host.Run (action); - } - - protected override void Dispose (bool disposing) - { - RemoveKeyBindingsHotKey (_barItems); - - if (Application.TopRunnable is { }) - { - Application.TopRunnable.DrawComplete -= Top_DrawComplete; - Application.TopRunnable.SizeChanging -= Current_TerminalResized; - } - - Application.MouseEvent -= Application_RootMouseEvent; - Application.Mouse.UnGrabbedMouse -= Application_UnGrabbedMouse; - base.Dispose (disposing); - } - - protected override void OnHasFocusChanged (bool newHasFocus, View? previousFocusedView, View? view) - { - if (!newHasFocus) - { - _host.LostFocus (previousFocusedView!); - } - } - - /// - protected override bool OnKeyDownNotHandled (Key keyEvent) - { - // We didn't handle the key, pass it on to host - return _host.InvokeCommandsBoundToHotKey (keyEvent) is true; - } - - protected override bool OnMouseEvent (MouseEventArgs me) - { - if (!_host._handled && !_host.HandleGrabView (me, this)) - { - return false; - } - - _host._handled = false; - bool disabled; - - if (me.Flags == MouseFlags.Button1Clicked) - { - disabled = false; - - if (me.Position.Y < 0) - { - return me.Handled = true; - } - - if (me.Position.Y >= _barItems!.Children!.Length) - { - return me.Handled = true; - } - - MenuItem item = _barItems.Children [me.Position.Y]!; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (item is null || !item.IsEnabled ()) - { - disabled = true; - } - - if (disabled) - { - return me.Handled = true; - } - - _currentChild = me.Position.Y; - RunSelected (); - - return me.Handled = true; - } - - if (me.Flags != MouseFlags.Button1Pressed - && me.Flags != MouseFlags.Button1DoubleClicked - && me.Flags != MouseFlags.Button1TripleClicked - && me.Flags != MouseFlags.ReportMousePosition - && !me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) - { - return false; - } - - { - disabled = false; - - if (me.Position.Y < 0 || me.Position.Y >= _barItems!.Children!.Length) - { - return me.Handled = true; - } - - MenuItem item = _barItems.Children [me.Position.Y]!; - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (item is null) - { - return me.Handled = true; - } - - if (item.IsEnabled () != true) - { - disabled = true; - } - - if (!disabled) - { - _currentChild = me.Position.Y; - } - - if (_host.UseSubMenusSingleFrame || !CheckSubMenu ()) - { - SetNeedsDraw (); - SetParentSetNeedsDisplay (); - - return me.Handled = true; - } - - _host.OnMenuOpened (); - - return me.Handled = true; - } - } - - /// - protected override void OnVisibleChanged () - { - base.OnVisibleChanged (); - - if (Visible) - { - Application.MouseEvent += Application_RootMouseEvent; - } - else - { - Application.MouseEvent -= Application_RootMouseEvent; - } - } - - internal required MenuBarItem? BarItems - { - get => _barItems!; - init - { - ArgumentNullException.ThrowIfNull (value); - _barItems = value; - - // Debugging aid so ToString() is helpful - Text = _barItems.Title; - } - } - - internal bool CheckSubMenu () - { - if (_currentChild == -1 || _barItems?.Children? [_currentChild] is null) - { - return true; - } - - MenuBarItem? subMenu = _barItems.SubMenu (_barItems.Children [_currentChild]!); - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (subMenu is { }) - { - int pos = -1; - - if (_host._openSubMenu is { }) - { - pos = _host._openSubMenu.FindIndex (o => o._barItems == subMenu); - } - - if (pos == -1 - && this != _host.OpenCurrentMenu - && subMenu.Children != _host.OpenCurrentMenu!._barItems!.Children - && !_host.CloseMenu (false, true)) - { - return false; - } - - _host.Activate (_host._selected, pos, subMenu); - } - else if (_host._openSubMenu?.Count == 0 || _host._openSubMenu?.Last ()._barItems!.IsSubMenuOf (_barItems.Children [_currentChild]!) == false) - { - return _host.CloseMenu (false, true); - } - else - { - SetNeedsDraw (); - SetParentSetNeedsDisplay (); - } - - return true; - } - - internal Attribute DetermineSchemeFor (MenuItem? item, int index) - { - if (item is null) - { - return GetAttributeForRole (VisualRole.Normal); - } - - if (index == _currentChild) - { - return GetAttributeForRole (VisualRole.Focus); - } - - return !item.IsEnabled () ? GetAttributeForRole (VisualRole.Disabled) : GetAttributeForRole (VisualRole.Normal); - } - - internal required MenuBar Host - { - get => _host; - init - { - ArgumentNullException.ThrowIfNull (value); - _host = value; - } - } - - internal static Rectangle MakeFrame (int x, int y, MenuItem? []? items, Menu? parent = null) - { - if (items is null || items.Length == 0) - { - return Rectangle.Empty; - } - - int minX = x; - int minY = y; - const int borderOffset = 2; // This 2 is for the space around - int maxW = (items.Max (z => z?.Width) ?? 0) + borderOffset; - int maxH = items.Length + borderOffset; - - if (parent is { } && x + maxW > Application.Screen.Width) - { - minX = Math.Max (parent.Frame.Right - parent.Frame.Width - maxW, 0); - } - - if (y + maxH > Application.Screen.Height) - { - minY = Math.Max (Application.Screen.Height - maxH, 0); - } - - return new (minX, minY, maxW, maxH); - } - - internal Menu? Parent { get; init; } - - private void AddKeyBindingsHotKey (MenuBarItem? menuBarItem) - { - if (menuBarItem is null || menuBarItem.Children is null) - { - return; - } - - IEnumerable menuItems = menuBarItem.Children.Where (m => m is { })!; - - foreach (MenuItem menuItem in menuItems) - { - KeyBinding keyBinding = new ([Command.Toggle], this, data: menuItem); - - if (menuItem.HotKey != Key.Empty) - { - HotKeyBindings.Remove (menuItem.HotKey!); - HotKeyBindings.Add (menuItem.HotKey!, keyBinding); - HotKeyBindings.Remove (menuItem.HotKey!.WithAlt); - HotKeyBindings.Add (menuItem.HotKey.WithAlt, keyBinding); - } - } - } - - private void Application_RootMouseEvent (object? sender, MouseEventArgs a) - { - if (a.View is { } and (MenuBar or not Menu)) - { - return; - } - - if (!Visible) - { - throw new InvalidOperationException ("This shouldn't running on a invisible menu!"); - } - - View view = a.View ?? this; - - Point boundsPoint = view.ScreenToViewport (new (a.Position.X, a.Position.Y)); - - var me = new MouseEventArgs - { - Position = boundsPoint, - Flags = a.Flags, - ScreenPosition = a.Position, - View = view - }; - - if (view.NewMouseEvent (me) == true || a.Flags == MouseFlags.Button1Pressed || a.Flags == MouseFlags.Button1Released) - { - a.Handled = true; - } - } - - private void Application_UnGrabbedMouse (object? sender, ViewEventArgs a) - { - if (_host is { IsMenuOpen: true }) - { - _host.CloseAllMenus (); - } - } - - private void CloseAllMenus () - { - Application.Mouse.UngrabMouse (); - _host.CloseAllMenus (); - } - - private void Current_TerminalResized (object? sender, SizeChangedEventArgs e) - { - if (_host.IsMenuOpen) - { - _host.CloseAllMenus (); - } - } - - /// Called when a key bound to Command.ToggleExpandCollapse is pressed. This means a hot key was pressed. - /// - private bool ExpandCollapse (MenuItem? menuItem) - { - if (!IsInitialized || !Visible) - { - return true; - } - - for (var c = 0; c < _barItems!.Children!.Length; c++) - { - if (_barItems.Children [c] == menuItem) - { - _currentChild = c; - - break; - } - } - - if (menuItem is { }) - { - var m = menuItem as MenuBarItem; - - if (m?.Children?.Length > 0) - { - MenuItem? item = _barItems.Children [_currentChild]; - - if (item is null) - { - return true; - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - bool disabled = item is null || !item.IsEnabled (); - - if (!disabled && (_host.UseSubMenusSingleFrame || !CheckSubMenu ())) - { - SetNeedsDraw (); - SetParentSetNeedsDisplay (); - - return true; - } - - if (!disabled) - { - _host.OnMenuOpened (); - } - } - else - { - _host.SelectItem (menuItem); - } - } - else if (_host.IsMenuOpen) - { - _host.CloseAllMenus (); - } - else - { - _host.OpenMenu (); - } - - return true; - } - - private bool MoveDown () - { - if (_barItems!.IsTopLevel) - { - return true; - } - - bool disabled; - - do - { - _currentChild++; - - if (_currentChild >= _barItems?.Children?.Length) - { - _currentChild = 0; - } - - if (this != _host.OpenCurrentMenu && _barItems?.Children? [_currentChild]?.IsFromSubMenu == true && _host._selectedSub > -1) - { - _host.PreviousMenu (true); - _host.SelectEnabledItem (_barItems.Children!, _currentChild, out _currentChild); - _host.OpenCurrentMenu = this; - } - - MenuItem? item = _barItems?.Children? [_currentChild]; - - if (item?.IsEnabled () != true) - { - disabled = true; - } - else - { - disabled = false; - } - - if (_host is { UseSubMenusSingleFrame: false, UseKeysUpDownAsKeysLeftRight: true } - && _barItems?.SubMenu (_barItems?.Children? [_currentChild]!) != null - && !disabled - && _host.IsMenuOpen) - { - if (!CheckSubMenu ()) - { - return false; - } - - break; - } - - if (!_host.IsMenuOpen) - { - _host.OpenMenu (_host._selected); - } - } - while (_barItems?.Children? [_currentChild] is null || disabled); - - SetNeedsDraw (); - SetParentSetNeedsDisplay (); - - if (!_host.UseSubMenusSingleFrame) - { - _host.OnMenuOpened (); - } - - return true; - } - - private bool MoveUp () - { - if (_barItems!.IsTopLevel || _currentChild == -1) - { - return true; - } - - bool disabled; - - do - { - _currentChild--; - - if (_host.UseKeysUpDownAsKeysLeftRight && !_host.UseSubMenusSingleFrame) - { - if ((_currentChild == -1 || this != _host.OpenCurrentMenu) - && _barItems.Children! [_currentChild + 1]!.IsFromSubMenu - && _host._selectedSub > -1) - { - _currentChild++; - _host.PreviousMenu (true); - - if (_currentChild > 0) - { - _currentChild--; - _host.OpenCurrentMenu = this; - } - - break; - } - } - - if (_currentChild < 0) - { - _currentChild = _barItems.Children!.Length - 1; - } - - if (!_host.SelectEnabledItem (_barItems.Children!, _currentChild, out _currentChild, false)) - { - _currentChild = 0; - - if (!_host.SelectEnabledItem (_barItems.Children!, _currentChild, out _currentChild) && !_host.CloseMenu ()) - { - return false; - } - - break; - } - - MenuItem item = _barItems.Children! [_currentChild]!; - disabled = item.IsEnabled () != true; - - if (_host.UseSubMenusSingleFrame - || !_host.UseKeysUpDownAsKeysLeftRight - || _barItems.SubMenu (_barItems.Children [_currentChild]!) == null! - || disabled - || !_host.IsMenuOpen) - { - continue; - } - - if (!CheckSubMenu ()) - { - return false; - } - - break; - } - while (_barItems.Children [_currentChild] is null || disabled); - - SetNeedsDraw (); - SetParentSetNeedsDisplay (); - - if (!_host.UseSubMenusSingleFrame) - { - _host.OnMenuOpened (); - } - - return true; - } - - private void RemoveKeyBindingsHotKey (MenuBarItem? menuBarItem) - { - if (menuBarItem is null || menuBarItem.Children is null) - { - return; - } - - IEnumerable menuItems = menuBarItem.Children.Where (m => m is { })!; - - foreach (MenuItem menuItem in menuItems) - { - if (menuItem.HotKey != Key.Empty) - { - KeyBindings.Remove (menuItem.HotKey!); - KeyBindings.Remove (menuItem.HotKey!.WithAlt); - } - } - } - - private void RunSelected () - { - if (_barItems!.IsTopLevel) - { - Run (_barItems.Action); - } - else - { - switch (_currentChild) - { - case > -1 when _barItems.Children! [_currentChild]!.Action != null!: - Run (_barItems.Children [_currentChild]?.Action); - - break; - case 0 when _host.UseSubMenusSingleFrame && _barItems.Children [_currentChild]?.Parent!.Parent != null: - _host.PreviousMenu (_barItems.Children [_currentChild]!.Parent!.IsFromSubMenu, true); - - break; - case > -1 when _barItems.SubMenu (_barItems.Children [_currentChild]) != null!: - CheckSubMenu (); - - break; - } - } - } - - private void SetParentSetNeedsDisplay () - { - if (_host._openSubMenu is { }) - { - foreach (Menu menu in _host._openSubMenu) - { - menu.SetNeedsDraw (); - } - } - - _host._openMenu?.SetNeedsDraw (); - _host.SetNeedsDraw (); - } - - // By doing this we draw last, over everything else. - private void Top_DrawComplete (object? sender, DrawEventArgs e) - { - if (!Visible) - { - return; - } - - if (_barItems!.Children is null) - { - return; - } - - DrawAdornments (); - RenderLineCanvas (); - - // BUGBUG: Views should not change the clip. Doing so is an indcation of poor design or a bug in the framework. - Region? savedClip = SetClipToScreen (); - - SetAttribute (GetAttributeForRole (VisualRole.Normal)); - - for (int i = Viewport.Y; i < _barItems!.Children.Length; i++) - { - if (i < 0) - { - continue; - } - - if (ViewportToScreen (Viewport).Y + i >= Application.Screen.Height) - { - break; - } - - MenuItem? item = _barItems.Children [i]; - - SetAttribute ( - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - item is null ? GetAttributeForRole (VisualRole.Normal) : - i == _currentChild ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal) - ); - - if (item is null && BorderStyle != LineStyle.None) - { - Move (-1, i); - AddRune (Glyphs.LeftTee); - } - else if (Frame.X < Application.Screen.Width) - { - Move (0, i); - } - - SetAttribute (DetermineSchemeFor (item, i)); - - for (int p = Viewport.X; p < Frame.Width - 2; p++) - { - // This - 2 is for the border - if (p < 0) - { - continue; - } - - if (ViewportToScreen (Viewport).X + p >= Application.Screen.Width) - { - break; - } - - if (item is null) - { - AddRune (Glyphs.HLine); - } - else if (i == 0 && p == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { }) - { - AddRune (Glyphs.LeftArrow); - } - - // This `- 3` is left border + right border + one row in from right - else if (p == Frame.Width - 3 && _barItems?.SubMenu (_barItems.Children [i]!) is { }) - { - AddRune (Glyphs.RightArrow); - } - else - { - AddRune ((Rune)' '); - } - } - - if (item is null) - { - if (BorderStyle != LineStyle.None && SuperView?.Frame.Right - Frame.X > Frame.Width) - { - Move (Frame.Width - 2, i); - AddRune (Glyphs.RightTee); - } - - continue; - } - - string? textToDraw; - Rune nullCheckedChar = Glyphs.CheckStateNone; - Rune checkChar = Glyphs.Selected; - Rune uncheckedChar = Glyphs.UnSelected; - - if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked)) - { - checkChar = Glyphs.CheckStateChecked; - uncheckedChar = Glyphs.CheckStateUnChecked; - } - - // Support Checked even though CheckType wasn't set - if (item is { CheckType: MenuItemCheckStyle.Checked, Checked: null }) - { - textToDraw = $"{nullCheckedChar} {item.Title}"; - } - else if (item.Checked == true) - { - textToDraw = $"{checkChar} {item.Title}"; - } - else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) - { - textToDraw = $"{uncheckedChar} {item.Title}"; - } - else - { - textToDraw = item.Title; - } - - Point screen = ViewportToScreen (new Point (0, i)); - - if (screen.X < Application.Screen.Width) - { - Move (1, i); - - if (!item.IsEnabled ()) - { - DrawHotString (textToDraw, GetAttributeForRole (VisualRole.Disabled), GetAttributeForRole (VisualRole.Disabled)); - } - else if (i == 0 && _host.UseSubMenusSingleFrame && item.Parent!.Parent is { }) - { - var tf = new TextFormatter - { - ConstrainToWidth = Frame.Width - 3, - ConstrainToHeight = 1, - Alignment = Alignment.Center, HotKeySpecifier = MenuBar.HotKeySpecifier, Text = textToDraw - }; - - // The -3 is left/right border + one space (not sure what for) - tf.Draw ( - driver: Driver, - screen: ViewportToScreen (new Rectangle (1, i, Frame.Width - 3, 1)), - normalColor: i == _currentChild ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal), - hotColor: i == _currentChild ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), - maximum: SuperView?.ViewportToScreen (SuperView.Viewport) ?? Rectangle.Empty); - } - else - { - DrawHotString ( - textToDraw, - i == _currentChild ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal), - i == _currentChild ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal) - ); - } - - // The help string - int l = item.ShortcutTag.GetColumns () == 0 - ? item.Help.GetColumns () - : item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2; - int col = Frame.Width - l - 3; - screen = ViewportToScreen (new Point (col, i)); - - if (screen.X < Application.Screen.Width) - { - Move (col, i); - AddStr (item.Help); - - // The shortcut tag string - if (!string.IsNullOrEmpty (item.ShortcutTag)) - { - Move (col + l - item.ShortcutTag.GetColumns (), i); - AddStr (item.ShortcutTag); - } - } - } - } - - SetClip (savedClip); - } -} diff --git a/Terminal.Gui/Views/Menuv1/MenuBar.cs b/Terminal.Gui/Views/Menuv1/MenuBar.cs deleted file mode 100644 index 2c09e6c56..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuBar.cs +++ /dev/null @@ -1,1854 +0,0 @@ - - -namespace Terminal.Gui.Views; - -/// -/// Provides a menu bar that spans the top of a View with drop-down and cascading menus. -/// -/// By default, any sub-sub-menus (sub-menus of the s added to s) -/// are displayed in a cascading manner, where each sub-sub-menu pops out of the sub-menu frame (either to the -/// right or left, depending on where the sub-menu is relative to the edge of the screen). By setting -/// to , this behavior can be changed such that all -/// sub-sub-menus are drawn within a single frame below the MenuBar. -/// -/// -/// -/// -/// The appears on the first row of the SuperView and uses the full -/// width. -/// -/// 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 -/// item's hot key (both alone AND with AltMask) and shortcut, if defined. -/// -/// -/// If a key press matches any of the menu item's hot keys or shortcuts, the menu item's action is invoked or -/// sub-menu opened. -/// -/// -/// * If the menu bar is not open * Any shortcut defined within the menu will be invoked * Only hot keys defined -/// for the menu bar items will be invoked, and only if Alt is pressed too. * If the menu bar is open * Un-shifted -/// hot keys defined for the menu bar items will be invoked, only if the menu they belong to is open (the menu bar -/// item's text is visible). * Alt-shifted hot keys defined for the menu bar items will be invoked, only if the -/// menu they belong to is open (the menu bar item's text is visible). * If there is a visible hot key that -/// duplicates a shortcut (e.g. _File and Alt-F), the hot key wins. -/// -/// -[Obsolete ("Use MenuBarv2 instead.", false)] -public class MenuBar : View, IDesignable -{ - // Spaces before the Title - private static readonly int _leftPadding = 1; - - // Spaces after the submenu Title, before Help - private static readonly int _parensAroundHelp = 3; - - // Spaces after the Title - private static readonly int _rightPadding = 1; - - // The column where the MenuBar starts - private static readonly int _xOrigin = 0; - internal bool _isMenuClosing; - internal bool _isMenuOpening; - - internal Menu? _openMenu; - internal List? _openSubMenu; - internal int _selected; - internal int _selectedSub; - - private bool _initialCanFocus; - private bool _isCleaning; - private View? _lastFocused; - private Menu? _ocm; - private View? _previousFocused; - private bool _reopen; - private bool _useSubMenusSingleFrame; - - /// Initializes a new instance of the . - public MenuBar () - { - TabStop = TabBehavior.NoStop; - X = 0; - Y = 0; - Width = Dim.Fill (); - Height = 1; // BUGBUG: Views should avoid setting Height as doing so implies Frame.Size == GetContentSize (). - Menus = []; - - //CanFocus = true; - _selected = -1; - _selectedSub = -1; - SchemeName = SchemeManager.SchemesToSchemeName (Schemes.Menu); - WantMousePositionReports = true; - IsMenuOpen = false; - - SuperViewChanged += MenuBar_SuperViewChanged; - - // Things this view knows how to do - AddCommand ( - Command.Left, - () => - { - MoveLeft (); - - return true; - } - ); - - AddCommand ( - Command.Right, - () => - { - MoveRight (); - - return true; - } - ); - - AddCommand ( - Command.Cancel, - () => - { - if (IsMenuOpen) - { - CloseMenuBar (); - - return true; - } - - return false; - } - ); - - AddCommand ( - Command.Accept, - (ctx) => - { - if (Menus.Length > 0) - { - ProcessMenu (_selected, Menus [_selected]); - } - - return RaiseAccepting (ctx); - } - ); - AddCommand (Command.Toggle, ctx => - { - CloseOtherOpenedMenuBar (); - - if (ctx is not CommandContext keyCommandContext) - { - return false; - } - return Select (Menus.IndexOf (keyCommandContext.Binding.Data)); - }); - AddCommand (Command.Select, ctx => - { - if (ctx is not CommandContext keyCommandContext) - { - return false; - } - if (keyCommandContext.Binding.Data is MouseEventArgs) - { - // HACK: Work around the fact that View.MouseClick always invokes Select - return false; - } - var res = Run ((keyCommandContext.Binding.Data as MenuItem)?.Action!); - CloseAllMenus (); - - return res; - }); - - // Default key bindings for this view - KeyBindings.Add (Key.CursorLeft, Command.Left); - KeyBindings.Add (Key.CursorRight, Command.Right); - KeyBindings.Add (Key.Esc, Command.Cancel); - KeyBindings.Add (Key.CursorDown, Command.Accept); - - KeyBinding keyBinding = new ([Command.Toggle], this, data: -1); // -1 indicates Key was used - HotKeyBindings.Add (Key, keyBinding); - - // TODO: Why do we have two keybindings for opening the menu? Ctrl-Space and Key? - HotKeyBindings.Add (Key.Space.WithCtrl, keyBinding); - // This is needed for macOS because Key.Space.WithCtrl doesn't work - HotKeyBindings.Add (Key.Space.WithAlt, keyBinding); - - // TODO: Figure out how to make Alt work (on Windows) - //KeyBindings.Add (Key.WithAlt, keyBinding); - } - - /// if the menu is open; otherwise . - public bool IsMenuOpen { get; protected set; } - - /// Gets the view that was last focused before opening the menu. - public View? LastFocused { get; private set; } - - /// - /// Gets or sets the array of s for the menu. Only set this after the - /// is visible. - /// - /// The menu array. - public MenuBarItem [] Menus - { - get => _menus; - set - { - _menus = value; - - if (Menus is []) - { - return; - } - - // TODO: Hotkeys should not work for sub-menus if they are not visible! - for (var i = 0; i < Menus.Length; i++) - { - MenuBarItem menuBarItem = Menus [i]; - - if (menuBarItem.HotKey != Key.Empty) - { - HotKeyBindings.Remove (menuBarItem.HotKey!); - KeyBinding keyBinding = new ([Command.Toggle], this, menuBarItem); - HotKeyBindings.Add (menuBarItem.HotKey!, keyBinding); - HotKeyBindings.Remove (menuBarItem.HotKey!.WithAlt); - keyBinding = new ([Command.Toggle], this, data: menuBarItem); - HotKeyBindings.Add (menuBarItem.HotKey.WithAlt, keyBinding); - } - - if (menuBarItem.ShortcutKey != Key.Empty) - { - // Technically this will never run because MenuBarItems don't have shortcuts - // unless the IsTopLevel is true - HotKeyBindings.Remove (menuBarItem.ShortcutKey!); - KeyBinding keyBinding = new ([Command.Select], this, data: menuBarItem); - HotKeyBindings.Add (menuBarItem.ShortcutKey!, keyBinding); - } - - menuBarItem.AddShortcutKeyBindings (this); - } - } - } - - /// - /// The default for 's border. The default is - /// . - /// - public LineStyle MenusBorderStyle { get; set; } = LineStyle.Single; - - /// - /// Gets or sets if the sub-menus must be displayed in a single or multiple frames. - /// - /// By default, any sub-sub-menus (sub-menus of the main s) are displayed in a cascading - /// manner, where each sub-sub-menu pops out of the sub-menu frame (either to the right or left, depending on where - /// the sub-menu is relative to the edge of the screen). By setting to - /// , this behavior can be changed such that all sub-sub-menus are drawn within a single - /// frame below the MenuBar. - /// - /// - public bool UseSubMenusSingleFrame - { - get => _useSubMenusSingleFrame; - set - { - _useSubMenusSingleFrame = value; - - if (value && UseKeysUpDownAsKeysLeftRight) - { - _useKeysUpDownAsKeysLeftRight = false; - SetNeedsDraw (); - } - } - } - - /// - public override bool Visible - { - get => base.Visible; - set - { - base.Visible = value; - - if (!value) - { - CloseAllMenus (); - } - } - } - - internal Menu? OpenCurrentMenu - { - get => _ocm; - set - { - if (_ocm != value) - { - _ocm = value!; - - if (_ocm is { _currentChild: > -1 }) - { - OnMenuOpened (); - } - } - } - } - - /// Closes the Menu programmatically if open and not canceled (as though F9 were pressed). - public bool CloseMenu (bool ignoreUseSubMenusSingleFrame = false) { return CloseMenu (false, false, ignoreUseSubMenusSingleFrame); } - - /// Raised when all the menu is closed. - public event EventHandler? MenuAllClosed; - - /// Raised when a menu is closing passing . - public event EventHandler? MenuClosing; - - /// Raised when a menu is opened. - public event EventHandler? MenuOpened; - - /// Raised as a menu is opening. - public event EventHandler? MenuOpening; - - /// - protected override bool OnDrawingContent () - { - var pos = 0; - - for (var i = 0; i < Menus.Length; i++) - { - MenuBarItem menu = Menus [i]; - Move (pos, 0); - Attribute hotColor, normalColor; - - if (i == _selected && IsMenuOpen) - { - hotColor = i == _selected ? GetAttributeForRole (VisualRole.HotFocus) : GetAttributeForRole (VisualRole.HotNormal); - normalColor = i == _selected ? GetAttributeForRole (VisualRole.Focus) : GetAttributeForRole (VisualRole.Normal); - } - else - { - hotColor = GetAttributeForRole (VisualRole.HotNormal); - normalColor = GetAttributeForRole (VisualRole.Normal); - } - - // Note Help on MenuBar is drawn with parens around it - DrawHotString ( - string.IsNullOrEmpty (menu.Help) ? $" {menu.Title} " : $" {menu.Title} ({menu.Help}) ", - hotColor, - normalColor - ); - - pos += _leftPadding - + menu.TitleLength - + (menu.Help.GetColumns () > 0 - ? _leftPadding + menu.Help.GetColumns () + _parensAroundHelp - : 0) - + _rightPadding; - } - - //PositionCursor (); - return true; - } - - /// Virtual method that will invoke the . - public virtual void OnMenuAllClosed () { MenuAllClosed?.Invoke (this, EventArgs.Empty); } - - /// Virtual method that will invoke the . - /// The current menu to be closed. - /// Whether the current menu will be reopened. - /// Whether is a sub-menu or not. - public virtual MenuClosingEventArgs OnMenuClosing (MenuBarItem currentMenu, bool reopen, bool isSubMenu) - { - var ev = new MenuClosingEventArgs (currentMenu, reopen, isSubMenu); - MenuClosing?.Invoke (this, ev); - - return ev; - } - - /// Virtual method that will invoke the event if it's defined. - public virtual void OnMenuOpened () - { - MenuItem? mi = null; - MenuBarItem? parent; - - if (OpenCurrentMenu?.BarItems?.Children is { Length: > 0 } - && OpenCurrentMenu?._currentChild > -1) - { - parent = OpenCurrentMenu.BarItems; - mi = parent.Children [OpenCurrentMenu._currentChild]; - } - else if (OpenCurrentMenu!.BarItems!.IsTopLevel) - { - parent = null; - mi = OpenCurrentMenu.BarItems; - } - else - { - parent = _openMenu?.BarItems; - - if (OpenCurrentMenu?._currentChild > -1) - { - mi = parent?.Children?.Length > 0 ? parent.Children [_openMenu!._currentChild] : null; - } - } - - MenuOpened?.Invoke (this, new (parent, mi)); - } - - /// Virtual method that will invoke the event if it's defined. - /// The current menu to be replaced. - /// Returns the - public virtual MenuOpeningEventArgs OnMenuOpening (MenuBarItem currentMenu) - { - var ev = new MenuOpeningEventArgs (currentMenu); - MenuOpening?.Invoke (this, ev); - - return ev; - } - - /// Opens the Menu programatically, as though the F9 key were pressed. - public void OpenMenu () - { - MenuBar? mbar = GetMouseGrabViewInstance (this); - - mbar?.CleanUp (); - - CloseOtherOpenedMenuBar (); - - if (!Enabled || _openMenu is { }) - { - return; - } - - _selected = 0; - SetNeedsDraw (); - - _previousFocused = (SuperView is null ? Application.TopRunnable?.Focused : SuperView.Focused)!; - OpenMenu (_selected); - - if (!SelectEnabledItem ( - OpenCurrentMenu?.BarItems?.Children, - OpenCurrentMenu!._currentChild, - out OpenCurrentMenu._currentChild - ) - && !CloseMenu ()) - { - return; - } - - if (!OpenCurrentMenu.CheckSubMenu ()) - { - return; - } - - if (_isContextMenuLoading) - { - Application.Mouse.GrabMouse (_openMenu); - _isContextMenuLoading = false; - } - else - { - Application.Mouse.GrabMouse (this); - } - } - - /// - public override Point? PositionCursor () - { - if (_selected == -1 && HasFocus && Menus.Length > 0) - { - _selected = 0; - } - - var pos = 0; - - for (var i = 0; i < Menus.Length; i++) - { - if (i == _selected) - { - pos++; - Move (pos + 1, 0); - - return null; // Don't show the cursor - } - - pos += _leftPadding - + Menus [i].TitleLength - + (Menus [i].Help.GetColumns () > 0 - ? Menus [i].Help.GetColumns () + _parensAroundHelp - : 0) - + _rightPadding; - } - - return null; // Don't show the cursor - } - - // Activates the menu, handles either first focus, or activating an entry when it was already active - // For mouse events. - internal void Activate (int idx, int sIdx = -1, MenuBarItem? subMenu = null!) - { - _selected = idx; - _selectedSub = sIdx; - - if (_openMenu is null) - { - _previousFocused = (SuperView is null ? Application.TopRunnable?.Focused ?? null : SuperView.Focused)!; - } - - OpenMenu (idx, sIdx, subMenu); - SetNeedsDraw (); - } - - internal void CleanUp () - { - if (_isCleaning) - { - return; - } - - _isCleaning = true; - - if (_openMenu is { }) - { - CloseAllMenus (); - } - - _openedByAltKey = false; - IsMenuOpen = false; - _selected = -1; - CanFocus = _initialCanFocus; - - if (_lastFocused is { }) - { - _lastFocused.SetFocus (); - } - - SetNeedsDraw (); - - if (Application.Mouse.MouseGrabView is { } && Application.Mouse.MouseGrabView is MenuBar && Application.Mouse.MouseGrabView != this) - { - var menuBar = Application.Mouse.MouseGrabView as MenuBar; - - if (menuBar!.IsMenuOpen) - { - menuBar.CleanUp (); - } - } - Application.Mouse.UngrabMouse (); - _isCleaning = false; - } - - internal void CloseAllMenus () - { - if (!_isMenuOpening && !_isMenuClosing) - { - if (_openSubMenu is { } && !CloseMenu (false, true, true)) - { - return; - } - - if (!CloseMenu ()) - { - return; - } - - if (LastFocused is { } && LastFocused != this) - { - _selected = -1; - } - - Application.Mouse.UngrabMouse (); - } - - if (OpenCurrentMenu is { }) - { - OpenCurrentMenu = null; - } - - IsMenuOpen = false; - _openedByAltKey = false; - OnMenuAllClosed (); - - CloseOtherOpenedMenuBar (); - } - - private void CloseOtherOpenedMenuBar () - { - if (SuperView is { }) - { - // Close others menu bar opened - Menu? menu = SuperView.SubViews.FirstOrDefault (v => v is Menu m && m.Host != this && m.Host.IsMenuOpen) as Menu; - menu?.Host.CleanUp (); - } - } - - internal bool CloseMenu (bool reopen, bool isSubMenu, bool ignoreUseSubMenusSingleFrame = false) - { - MenuBarItem? mbi = isSubMenu ? OpenCurrentMenu!.BarItems : _openMenu?.BarItems; - - if (UseSubMenusSingleFrame && mbi is { } && !ignoreUseSubMenusSingleFrame && mbi.Parent is { }) - { - return false; - } - - _isMenuClosing = true; - _reopen = reopen; - MenuClosingEventArgs args = OnMenuClosing (mbi!, reopen, isSubMenu); - - if (args.Cancel) - { - _isMenuClosing = false; - - if (args.CurrentMenu.Parent is { } && _openMenu is { }) - { - _openMenu._currentChild = - ((MenuBarItem)args.CurrentMenu.Parent).Children.IndexOf (args.CurrentMenu); - } - - return false; - } - - switch (isSubMenu) - { - case false: - if (_openMenu is { }) - { - SuperView?.Remove (_openMenu); - } - - SetNeedsDraw (); - - if (_previousFocused is Menu && _openMenu is { } && _previousFocused.ToString () != OpenCurrentMenu!.ToString ()) - { - _previousFocused.SetFocus (); - } - - if (Application.Mouse.MouseGrabView == _openMenu) - { - Application.Mouse.UngrabMouse (); - } - _openMenu?.Dispose (); - _openMenu = null; - - if (_lastFocused is Menu or MenuBar) - { - _lastFocused = null; - } - - LastFocused = _lastFocused; - _lastFocused = null; - - if (LastFocused is { CanFocus: true }) - { - if (!reopen) - { - _selected = -1; - } - - if (_openSubMenu is { }) - { - _openSubMenu = null; - } - - if (OpenCurrentMenu is { }) - { - SuperView?.Remove (OpenCurrentMenu); - if (Application.Mouse.MouseGrabView == OpenCurrentMenu) - { - Application.Mouse.UngrabMouse (); - } - OpenCurrentMenu.Dispose (); - OpenCurrentMenu = null; - } - - LastFocused.SetFocus (); - } - else if (_openSubMenu is null || _openSubMenu.Count == 0) - { - CloseAllMenus (); - } - else - { - SetFocus (); - } - - IsMenuOpen = false; - - break; - - case true: - _selectedSub = -1; - SetNeedsDraw (); - RemoveAllOpensSubMenus (); - OpenCurrentMenu!._previousSubFocused!.SetFocus (); - _openSubMenu = null; - IsMenuOpen = true; - - break; - } - - _reopen = false; - _isMenuClosing = false; - - return true; - } - - /// Gets the superview location offset relative to the location. - /// The location offset. - internal Point GetScreenOffset () - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (Application.Screen.Height == 0) - { - return Point.Empty; - } - - Rectangle superViewFrame = SuperView?.Frame ?? Application.Screen; - View? sv = SuperView ?? Application.TopRunnable; - - if (sv is null) - { - // Support Unit Tests - return Point.Empty; - } - - Point viewportOffset = sv.GetViewportOffsetFromFrame (); - - return new ( - superViewFrame.X - sv.Frame.X - viewportOffset.X, - superViewFrame.Y - sv.Frame.Y - viewportOffset.Y - ); - } - - internal void NextMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false) - { - switch (isSubMenu) - { - case false: - if (_selected == -1) - { - _selected = 0; - } - else if (_selected + 1 == Menus.Length) - { - _selected = 0; - } - else - { - _selected++; - } - - if (_selected > -1 && !CloseMenu (true, ignoreUseSubMenusSingleFrame)) - { - return; - } - - if (_selected == -1) - { - return; - } - - OpenMenu (_selected); - - SelectEnabledItem ( - OpenCurrentMenu?.BarItems?.Children, - OpenCurrentMenu!._currentChild, - out OpenCurrentMenu._currentChild - ); - - break; - case true: - if (UseKeysUpDownAsKeysLeftRight) - { - if (CloseMenu (false, true, ignoreUseSubMenusSingleFrame)) - { - NextMenu (false, ignoreUseSubMenusSingleFrame); - } - } - else - { - MenuBarItem? subMenu = OpenCurrentMenu!._currentChild > -1 && OpenCurrentMenu.BarItems?.Children!.Length > 0 - ? OpenCurrentMenu.BarItems.SubMenu ( - OpenCurrentMenu.BarItems.Children? [OpenCurrentMenu._currentChild]! - ) - : null; - - if ((_selectedSub == -1 || _openSubMenu is null || _openSubMenu?.Count - 1 == _selectedSub) && subMenu is null) - { - if (_openSubMenu is { } && !CloseMenu (false, true)) - { - return; - } - - NextMenu (false, ignoreUseSubMenusSingleFrame); - } - else if (subMenu != null - || (OpenCurrentMenu._currentChild > -1 - && !OpenCurrentMenu.BarItems! - .Children! [OpenCurrentMenu._currentChild]! - .IsFromSubMenu)) - { - _selectedSub++; - OpenCurrentMenu.CheckSubMenu (); - } - else - { - if (CloseMenu (false, true, ignoreUseSubMenusSingleFrame)) - { - NextMenu (false, ignoreUseSubMenusSingleFrame); - } - - return; - } - - SetNeedsDraw (); - - if (UseKeysUpDownAsKeysLeftRight) - { - OpenCurrentMenu.CheckSubMenu (); - } - } - - break; - } - } - - internal void OpenMenu (int index, int sIndex = -1, MenuBarItem? subMenu = null!) - { - _isMenuOpening = true; - MenuOpeningEventArgs newMenu = OnMenuOpening (Menus [index]); - - if (newMenu.Cancel) - { - _isMenuOpening = false; - - return; - } - - if (newMenu.NewMenuBarItem is { }) - { - Menus [index] = newMenu.NewMenuBarItem; - } - - var pos = 0; - - switch (subMenu) - { - case null: - // Open a submenu below a MenuBar - _lastFocused ??= SuperView is null ? Application.TopRunnable?.MostFocused : SuperView.MostFocused; - - if (_openSubMenu is { } && !CloseMenu (false, true)) - { - return; - } - - if (_openMenu is { }) - { - SuperView?.Remove (_openMenu); - if (Application.Mouse.MouseGrabView == _openMenu) - { - Application.Mouse.UngrabMouse (); - } - _openMenu.Dispose (); - _openMenu = null; - } - - // This positions the submenu horizontally aligned with the first character of the - // text belonging to the menu - for (var i = 0; i < index; i++) - { - pos += Menus [i].TitleLength + (Menus [i].Help.GetColumns () > 0 ? Menus [i].Help.GetColumns () + 2 : 0) + _leftPadding + _rightPadding; - } - - - - - _openMenu = new () - { - Host = this, - X = Frame.X + pos, - Y = Frame.Y + 1, - BarItems = Menus [index], - Parent = null - }; - OpenCurrentMenu = _openMenu; - OpenCurrentMenu._previousSubFocused = _openMenu; - - if (SuperView is { }) - { - SuperView.Add (_openMenu); - // _openMenu.SetRelativeLayout (Application.Screen.Size); - } - else - { - _openMenu.BeginInit (); - _openMenu.EndInit (); - } - - _openMenu.SetFocus (); - - break; - default: - // Opens a submenu next to another submenu (openSubMenu) - if (_openSubMenu is null) - { - _openSubMenu = new (); - } - - if (sIndex > -1) - { - RemoveSubMenu (sIndex); - } - else - { - Menu? last = _openSubMenu.Count > 0 ? _openSubMenu.Last () : _openMenu; - - if (!UseSubMenusSingleFrame) - { - OpenCurrentMenu = new () - { - Host = this, - X = last!.Frame.Left + last.Frame.Width, - Y = last.Frame.Top + last._currentChild + 1, - BarItems = subMenu, - Parent = last - }; - } - else - { - Menu? first = _openSubMenu.Count > 0 ? _openSubMenu.First () : _openMenu; - - // 2 is for the parent and the separator - MenuItem? [] mbi = new MenuItem [2 + subMenu.Children!.Length]; - mbi [0] = new () { Title = subMenu.Title, Parent = subMenu }; - mbi [1] = null; - - for (var j = 0; j < subMenu.Children.Length; j++) - { - mbi [j + 2] = subMenu.Children [j]; - } - - var newSubMenu = new MenuBarItem (mbi!) { Parent = subMenu }; - - OpenCurrentMenu = new () - { - Host = this, X = first!.Frame.Left, Y = first.Frame.Top, BarItems = newSubMenu - }; - last!.Visible = false; - Application.Mouse.GrabMouse (OpenCurrentMenu); - } - - OpenCurrentMenu._previousSubFocused = last._previousSubFocused; - _openSubMenu.Add (OpenCurrentMenu); - SuperView?.Add (OpenCurrentMenu); - - if (!OpenCurrentMenu.IsInitialized) - { - // Supports unit tests - OpenCurrentMenu.BeginInit (); - OpenCurrentMenu.EndInit (); - } - } - - _selectedSub = _openSubMenu.Count - 1; - - if (_selectedSub > -1 - && SelectEnabledItem ( - OpenCurrentMenu!.BarItems!.Children, - OpenCurrentMenu._currentChild, - out OpenCurrentMenu._currentChild - )) - { - OpenCurrentMenu.SetFocus (); - } - - break; - } - - _isMenuOpening = false; - IsMenuOpen = true; - } - - internal void PreviousMenu (bool isSubMenu = false, bool ignoreUseSubMenusSingleFrame = false) - { - switch (isSubMenu) - { - case false: - if (_selected <= 0) - { - _selected = Menus.Length - 1; - } - else - { - _selected--; - } - - if (_selected > -1 && !CloseMenu (true, false, ignoreUseSubMenusSingleFrame)) - { - return; - } - - if (_selected == -1) - { - return; - } - - OpenMenu (_selected); - - if (!SelectEnabledItem ( - OpenCurrentMenu?.BarItems?.Children, - OpenCurrentMenu!._currentChild, - out OpenCurrentMenu._currentChild, - false - )) - { - OpenCurrentMenu._currentChild = 0; - } - - break; - case true: - if (_selectedSub > -1) - { - _selectedSub--; - RemoveSubMenu (_selectedSub, ignoreUseSubMenusSingleFrame); - SetNeedsDraw (); - } - else - { - PreviousMenu (); - } - - break; - } - } - - internal void RemoveAllOpensSubMenus () - { - if (_openSubMenu is { }) - { - foreach (Menu item in _openSubMenu) - { - SuperView?.Remove (item); - if (Application.Mouse.MouseGrabView == item) - { - Application.Mouse.UngrabMouse (); - } - item.Dispose (); - } - } - } - - internal bool Run (Action? action) - { - if (action is null) - { - return false; - } - - Application.AddTimeout (TimeSpan.Zero, - () => - { - action (); - - return false; - } - ); - - return true; - } - - internal bool SelectEnabledItem ( - MenuItem? []? children, - int current, - out int newCurrent, - bool forward = true - ) - { - if (children is null) - { - newCurrent = -1; - - return true; - } - - IEnumerable childMenuItems = forward ? children : children.Reverse (); - - int count; - - IEnumerable menuItems = childMenuItems as MenuItem [] ?? childMenuItems.ToArray (); - - if (forward) - { - count = -1; - } - else - { - count = menuItems.Count (); - } - - foreach (MenuItem? child in menuItems) - { - if (forward) - { - if (++count < current) - { - continue; - } - } - else - { - if (--count > current) - { - continue; - } - } - - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (child is null || !child.IsEnabled ()) - { - if (forward) - { - current++; - } - else - { - current--; - } - } - else - { - newCurrent = current; - - return true; - } - } - - newCurrent = -1; - - return false; - } - - /// Called when an item is selected; Runs the action. - /// - internal bool SelectItem (MenuItem? item) - { - if (item?.Action is null) - { - return false; - } - - Application.Mouse.UngrabMouse (); - CloseAllMenus (); - Application.LayoutAndDraw (true); - _openedByAltKey = true; - - return Run (item.Action); - } - - private void CloseMenuBar () - { - if (!CloseMenu ()) - { - return; - } - - if (_openedByAltKey) - { - _openedByAltKey = false; - LastFocused?.SetFocus (); - } - - SetNeedsDraw (); - } - - private Point GetLocationOffset () - { - if (MenusBorderStyle != LineStyle.None) - { - return new (0, 1); - } - - return new (-2, 0); - } - - private void MenuBar_SuperViewChanged (object? sender, SuperViewChangedEventArgs _) - { - _initialCanFocus = CanFocus; - SuperViewChanged -= MenuBar_SuperViewChanged; - } - - private void MoveLeft () - { - _selected--; - - if (_selected < 0) - { - _selected = Menus.Length - 1; - } - - OpenMenu (_selected); - SetNeedsDraw (); - } - - private void MoveRight () - { - _selected = (_selected + 1) % Menus.Length; - OpenMenu (_selected); - SetNeedsDraw (); - } - - private bool ProcessMenu (int i, MenuBarItem mi) - { - if (_selected < 0 && IsMenuOpen) - { - return false; - } - - if (mi.IsTopLevel) - { - Point screen = ViewportToScreen (new Point (0, i)); - var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = mi }; - menu.Run (mi.Action); - if (Application.Mouse.MouseGrabView == menu) - { - Application.Mouse.UngrabMouse (); - } - menu.Dispose (); - } - else - { - Application.Mouse.GrabMouse (this); - _selected = i; - OpenMenu (i); - - if (!SelectEnabledItem ( - OpenCurrentMenu?.BarItems?.Children, - OpenCurrentMenu!._currentChild, - out OpenCurrentMenu._currentChild - ) - && !CloseMenu ()) - { - return true; - } - - if (!OpenCurrentMenu.CheckSubMenu ()) - { - return true; - } - } - - SetNeedsDraw (); - - return true; - } - - private void RemoveSubMenu (int index, bool ignoreUseSubMenusSingleFrame = false) - { - if (_openSubMenu == null - || (UseSubMenusSingleFrame - && !ignoreUseSubMenusSingleFrame - && _openSubMenu.Count == 0)) - { - return; - } - - for (int i = _openSubMenu.Count - 1; i > index; i--) - { - _isMenuClosing = true; - Menu? menu; - - if (_openSubMenu!.Count - 1 > 0) - { - menu = _openSubMenu [i - 1]; - } - else - { - menu = _openMenu; - } - - if (!menu!.Visible) - { - menu.Visible = true; - } - - OpenCurrentMenu = menu; - OpenCurrentMenu.SetFocus (); - - if (_openSubMenu is { }) - { - menu = _openSubMenu [i]; - SuperView!.Remove (menu); - _openSubMenu.Remove (menu); - - if (Application.Mouse.MouseGrabView == menu) - { - Application.Mouse.GrabMouse (this); - } - - menu.Dispose (); - } - - RemoveSubMenu (i, ignoreUseSubMenusSingleFrame); - } - - if (_openSubMenu!.Count > 0) - { - OpenCurrentMenu = _openSubMenu.Last (); - } - - _isMenuClosing = false; - } - - #region Keyboard handling - - private Key _key = Key.F9; - - /// - /// The used to activate or close the menu bar by keyboard. The default is - /// . - /// - /// - /// - /// If the user presses any s defined in the s, the menu - /// bar will be activated and the sub-menu will be opened. - /// - /// will close the menu bar and any open sub-menus. - /// - public Key Key - { - get => _key; - set - { - if (_key == value) - { - return; - } - - HotKeyBindings.Remove (_key); - KeyBinding keyBinding = new ([Command.Toggle], this, data: -1); // -1 indicates Key was used - HotKeyBindings.Add (value, keyBinding); - _key = value; - } - } - - private bool _useKeysUpDownAsKeysLeftRight; - - /// Used for change the navigation key style. - public bool UseKeysUpDownAsKeysLeftRight - { - get => _useKeysUpDownAsKeysLeftRight; - set - { - _useKeysUpDownAsKeysLeftRight = value; - - if (value && UseSubMenusSingleFrame) - { - UseSubMenusSingleFrame = false; - SetNeedsDraw (); - } - } - } - - /// The specifier character for the hot keys. - public new static Rune HotKeySpecifier => (Rune)'_'; - - // TODO: This doesn't actually work. Figure out why. - private bool _openedByAltKey; - - /// - /// Called when a key bound to Command.Select is pressed. Either activates the menu item or runs it, depending on - /// whether it has a sub-menu. If the menu is open, it will close the menu bar. - /// - /// The index of the menu bar item to select. -1 if the selection was via . - /// - private bool Select (int index) - { - if (!IsInitialized || !Visible) - { - return true; - } - - // If the menubar is open and the menu that's open is 'index' then close it. Otherwise activate it. - if (IsMenuOpen) - { - if (index == -1) - { - CloseAllMenus (); - - return true; - } - - // Find the index of the open submenu and close the menu if it matches - for (var i = 0; i < Menus.Length; i++) - { - MenuBarItem open = Menus [i]; - - if (open == OpenCurrentMenu!.BarItems && i == index) - { - CloseAllMenus (); - return true; - } - } - } - - if (index == -1) - { - OpenMenu (); - } - else if (Menus [index].IsTopLevel) - { - Run (Menus [index].Action); - } - else - { - Activate (index); - } - - return true; - } - - #endregion Keyboard handling - - #region Mouse Handling - - internal void LostFocus (View view) - { - if (view is not MenuBar && view is not Menu && !_isCleaning && !_reopen) - { - CleanUp (); - } - } - - /// - protected override bool OnMouseEvent (MouseEventArgs me) - { - if (!_handled && !HandleGrabView (me, this)) - { - return false; - } - - _handled = false; - - if (me.Flags == MouseFlags.Button1Pressed - || me.Flags == MouseFlags.Button1DoubleClicked - || me.Flags == MouseFlags.Button1TripleClicked - || me.Flags == MouseFlags.Button1Clicked - || (me.Flags == MouseFlags.ReportMousePosition && _selected > -1) - || (me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && _selected > -1)) - { - int pos = _xOrigin; - Point locationOffset = default; - - if (SuperView is { }) - { - locationOffset.X += SuperView.Border!.Thickness.Left; - locationOffset.Y += SuperView.Border!.Thickness.Top; - } - - int cx = me.Position.X - locationOffset.X; - - for (var i = 0; i < Menus.Length; i++) - { - if (cx >= pos && cx < pos + _leftPadding + Menus [i].TitleLength + Menus [i].Help.GetColumns () + _rightPadding) - { - if (me.Flags == MouseFlags.Button1Clicked) - { - if (Menus [i].IsTopLevel) - { - Point screen = ViewportToScreen (new Point (0, i)); - var menu = new Menu { Host = this, X = screen.X, Y = screen.Y, BarItems = Menus [i] }; - menu.Run (Menus [i].Action); - if (Application.Mouse.MouseGrabView == menu) - { - Application.Mouse.UngrabMouse (); - } - - menu.Dispose (); - } - else if (!IsMenuOpen) - { - Activate (i); - } - } - else if (me.Flags.HasFlag (MouseFlags.Button1Pressed) - || me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) - || me.Flags.HasFlag (MouseFlags.Button1TripleClicked)) - { - if (IsMenuOpen && !Menus [i].IsTopLevel) - { - CloseAllMenus (); - } - else if (!Menus [i].IsTopLevel) - { - Activate (i); - } - } - else if (_selected != i - && _selected > -1 - && (me.Flags == MouseFlags.ReportMousePosition - || (me.Flags is MouseFlags.Button1Pressed && me.Flags == MouseFlags.ReportMousePosition))) - { - if (IsMenuOpen) - { - if (!CloseMenu (true, false)) - { - return me.Handled = true; - } - - Activate (i); - } - } - else if (IsMenuOpen) - { - if (!UseSubMenusSingleFrame - || (UseSubMenusSingleFrame - && OpenCurrentMenu is { BarItems.Parent: { } } - && OpenCurrentMenu.BarItems.Parent.Parent != Menus [i])) - { - Activate (i); - } - } - - return me.Handled = true; - } - - if (i == Menus.Length - 1 && me.Flags == MouseFlags.Button1Clicked) - { - if (IsMenuOpen && !Menus [i].IsTopLevel) - { - CloseAllMenus (); - - return me.Handled = true; - } - } - - pos += _leftPadding + Menus [i].TitleLength + _rightPadding; - } - } - - return false; - } - - internal bool _handled; - internal bool _isContextMenuLoading; - private MenuBarItem [] _menus = []; - - internal bool HandleGrabView (MouseEventArgs me, View current) - { - if (Application.Mouse.MouseGrabView is { }) - { - if (me.View is MenuBar or Menu) - { - MenuBar? mbar = GetMouseGrabViewInstance (me.View); - - if (mbar is { }) - { - if (me.Flags == MouseFlags.Button1Clicked) - { - mbar.CleanUp (); - Application.Mouse.GrabMouse (me.View); - } - else - { - _handled = false; - - return false; - } - } - - if (Application.Mouse.MouseGrabView != me.View) - { - View v = me.View; - Application.Mouse.GrabMouse (v); - - return true; - } - - if (me.View != current) - { - View v = me.View; - Application.Mouse.GrabMouse (v); - MouseEventArgs nme; - - if (me.Position.Y > -1) - { - Point frameLoc = v.ScreenToFrame (me.Position); - - nme = new () - { - Position = frameLoc, - Flags = me.Flags, - View = v - }; - } - else - { - nme = new () - { - Position = new (me.Position.X + current.Frame.X, me.Position.Y + current.Frame.Y), - Flags = me.Flags, View = v - }; - } - - v.NewMouseEvent (nme); - - return false; - } - } - else if (!(me.View is MenuBar || me.View is Menu) - && me.Flags != MouseFlags.ReportMousePosition - && me.Flags != 0) - { - Application.Mouse.UngrabMouse (); - - if (IsMenuOpen) - { - CloseAllMenus (); - } - - _handled = false; - - return false; - } - else - { - _handled = false; - - return false; - } - } - else if (!IsMenuOpen - && (me.Flags == MouseFlags.Button1Pressed - || me.Flags == MouseFlags.Button1DoubleClicked - || me.Flags == MouseFlags.Button1TripleClicked - || me.Flags.HasFlag ( - MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition - ))) - { - Application.Mouse.GrabMouse (current); - } - else if (IsMenuOpen && (me.View is MenuBar || me.View is Menu)) - { - Application.Mouse.GrabMouse (me.View); - } - else - { - _handled = false; - - return false; - } - - _handled = true; - - return true; - } - - private MenuBar? GetMouseGrabViewInstance (View? view) - { - if (view is null || Application.Mouse.MouseGrabView is null) - { - return null; - } - - MenuBar? hostView = null; - - if (view is MenuBar) - { - hostView = (MenuBar)view; - } - else if (view is Menu) - { - hostView = ((Menu)view).Host; - } - - View grabView = Application.Mouse.MouseGrabView; - MenuBar? hostGrabView = null; - - if (grabView is MenuBar bar) - { - hostGrabView = bar; - } - else if (grabView is Menu menu) - { - hostGrabView = menu.Host; - } - - return hostView != hostGrabView ? hostGrabView : null; - } - - #endregion Mouse Handling - - - /// - public bool EnableForDesign (ref TContext targetView) where TContext : notnull - { - if (targetView is not Func actionFn) - { - actionFn = (_) => true; - } - - Menus = - [ - new MenuBarItem ( - "_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/Menuv1/MenuBarItem.cs b/Terminal.Gui/Views/Menuv1/MenuBarItem.cs deleted file mode 100644 index fe96c1589..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuBarItem.cs +++ /dev/null @@ -1,265 +0,0 @@ - - -namespace Terminal.Gui.Views; - -/// -/// is a menu item on . MenuBarItems do not support -/// . -/// -[Obsolete ("Use MenuBarItemv2 instead.", false)] -public class MenuBarItem : MenuItem -{ - /// Initializes a new as a . - /// Title for the menu item. - /// Help text to display. Will be displayed next to the Title surrounded by parentheses. - /// Action to invoke when the menu item is activated. - /// Function to determine if the action can currently be executed. - /// The parent of this if any. - public MenuBarItem ( - string title, - string help, - Action action, - Func? canExecute = null, - MenuItem? parent = null - ) : base (title, help, action, canExecute, parent) - { - SetInitialProperties (title, null, null, true); - } - - /// Initializes a new . - /// Title for the menu item. - /// The items in the current menu. - /// The parent of this if any. - public MenuBarItem (string title, MenuItem [] children, MenuItem? parent = null) { SetInitialProperties (title, children, parent); } - - /// Initializes a new with separate list of items. - /// Title for the menu item. - /// The list of items in the current menu. - /// The parent of this if any. - public MenuBarItem (string title, List children, MenuItem? parent = null) { SetInitialProperties (title, children, parent); } - - /// Initializes a new . - /// The items in the current menu. - public MenuBarItem (MenuItem [] children) : this ("", children) { } - - /// Initializes a new . - public MenuBarItem () : this ([]) { } - - /// - /// Gets or sets an array of objects that are the children of this - /// - /// - /// The children. - public MenuItem? []? Children { get; set; } - - internal bool IsTopLevel => Parent is null && (Children is null || Children.Length == 0) && Action != null; - - /// Get the index of a child . - /// - /// Returns a greater than -1 if the is a child. - public int GetChildrenIndex (MenuItem children) - { - var i = 0; - - if (Children is null) - { - return -1; - } - - foreach (MenuItem? child in Children) - { - if (child == children) - { - return i; - } - - i++; - } - - return -1; - } - - /// Check if a is a submenu of this MenuBar. - /// - /// Returns true if it is a submenu. false otherwise. - public bool IsSubMenuOf (MenuItem menuItem) - { - return Children!.Any (child => child == menuItem && child.Parent == menuItem.Parent); - } - - /// Check if a is a . - /// - /// Returns a or null otherwise. - public MenuBarItem? SubMenu (MenuItem? menuItem) { return menuItem as MenuBarItem; } - - internal void AddShortcutKeyBindings (MenuBar menuBar) - { - if (Children is null) - { - return; - } - - _menuBar = menuBar; - - IEnumerable menuItems = Children.Where (m => m is { })!; - - foreach (MenuItem menuItem in menuItems) - { - // Initialize MenuItem _menuBar - menuItem._menuBar = menuBar; - - // For MenuBar only add shortcuts for submenus - if (menuItem.ShortcutKey != Key.Empty) - { - menuItem.AddShortcutKeyBinding (menuBar, Key.Empty); - } - - SubMenu (menuItem)?.AddShortcutKeyBindings (menuBar); - } - } - - // ReSharper disable once ParameterOnlyUsedForPreconditionCheck.Local - private void SetInitialProperties (string title, object? children, MenuItem? parent = null, bool isTopLevel = false) - { - if (!isTopLevel && children is null) - { - throw new ArgumentNullException ( - nameof (children), - @"The parameter cannot be null. Use an empty array instead." - ); - } - - SetTitle (title); - - if (parent is { }) - { - Parent = parent; - } - - switch (children) - { - case List childrenList: - { - MenuItem [] newChildren = []; - - foreach (MenuItem [] grandChild in childrenList) - { - foreach (MenuItem child in grandChild) - { - SetParent (grandChild); - Array.Resize (ref newChildren, newChildren.Length + 1); - newChildren [^1] = child; - } - } - - Children = newChildren; - - break; - } - case MenuItem [] items: - SetParent (items); - Children = items; - - break; - default: - Children = null; - - break; - } - } - - private void SetParent (MenuItem [] children) - { - foreach (MenuItem child in children) - { - if (child is { Parent: null }) - { - child.Parent = this; - } - } - } - - private void SetTitle (string? title) - { - title ??= string.Empty; - Title = title; - } - - /// - /// Add a dynamically into the .Menus. - /// - /// - /// - public void AddMenuBarItem (MenuBar menuBar, MenuItem? menuItem = null) - { - ArgumentNullException.ThrowIfNull (menuBar); - - _menuBar = menuBar; - - if (menuItem is null) - { - MenuBarItem [] menus = _menuBar.Menus; - Array.Resize (ref menus, menus.Length + 1); - menus [^1] = this; - _menuBar.Menus = menus; - } - else - { - MenuItem [] childrens = (Children ?? [])!; - Array.Resize (ref childrens, childrens.Length + 1); - menuItem._menuBar = menuBar; - childrens [^1] = menuItem; - Children = childrens; - } - } - - /// - public override void RemoveMenuItem () - { - if (Children is { }) - { - foreach (MenuItem? menuItem in Children) - { - if (menuItem?.ShortcutKey != Key.Empty) - { - // Remove an existent ShortcutKey - _menuBar?.HotKeyBindings.Remove (menuItem?.ShortcutKey!); - } - } - } - - if (ShortcutKey != Key.Empty) - { - // Remove an existent ShortcutKey - _menuBar?.HotKeyBindings.Remove (ShortcutKey!); - } - - var index = _menuBar!.Menus.IndexOf (this); - if (index > -1) - { - if (_menuBar.Menus [index].HotKey != Key.Empty) - { - // Remove an existent HotKey - _menuBar.HotKeyBindings.Remove (HotKey!.WithAlt); - } - - _menuBar.Menus [index] = null!; - } - - var i = 0; - - foreach (MenuBarItem m in _menuBar.Menus) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (m != null) - { - _menuBar.Menus [i] = m; - i++; - } - } - - MenuBarItem [] menus = _menuBar.Menus; - Array.Resize (ref menus, menus.Length - 1); - _menuBar.Menus = menus; - } -} diff --git a/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs deleted file mode 100644 index e223893c8..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuClosingEventArgs.cs +++ /dev/null @@ -1,34 +0,0 @@ -#nullable disable -namespace Terminal.Gui.Views; - -#pragma warning disable CS0618 // Type or member is obsolete - -/// An which allows passing a cancelable menu closing event. -public class MenuClosingEventArgs : EventArgs -{ - /// Initializes a new instance of . - /// The current parent. - /// Whether the current menu will reopen. - /// Indicates whether it is a sub-menu. - public MenuClosingEventArgs (MenuBarItem currentMenu, bool reopen, bool isSubMenu) - { - CurrentMenu = currentMenu; - Reopen = reopen; - IsSubMenu = isSubMenu; - } - - /// - /// Flag that allows the cancellation of the event. If set to in the event handler, the - /// event will be canceled. - /// - public bool Cancel { get; set; } - - /// The current parent. - public MenuBarItem CurrentMenu { get; } - - /// Indicates whether the current menu is a sub-menu. - public bool IsSubMenu { get; } - - /// Indicates whether the current menu will reopen. - public bool Reopen { get; } -} diff --git a/Terminal.Gui/Views/Menuv1/MenuItem.cs b/Terminal.Gui/Views/Menuv1/MenuItem.cs deleted file mode 100644 index 9d66c1c45..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuItem.cs +++ /dev/null @@ -1,387 +0,0 @@ -#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. - - -namespace Terminal.Gui.Views; - -/// -/// A has title, an associated help text, and an action to execute on activation. MenuItems -/// can also have a checked indicator (see ). -/// -[Obsolete ("Use MenuItemv2 instead.", false)] - -public class MenuItem -{ - internal MenuBar _menuBar; - - /// Initializes a new instance of - public MenuItem (Key? shortcutKey = null) : this ("", "", null, null, null, shortcutKey) { } - - /// Initializes a new instance of . - /// Title for the menu item. - /// Help text to display. - /// Action to invoke when the menu item is activated. - /// Function to determine if the action can currently be executed. - /// The of this menu item. - /// The keystroke combination. - public MenuItem ( - string? title, - string? help, - Action? action, - Func? canExecute = null, - MenuItem? parent = null, - Key? shortcutKey = null - ) - { - Title = title ?? ""; - Help = help ?? ""; - Action = action!; - CanExecute = canExecute!; - Parent = parent!; - - if (Parent is { } && Parent.ShortcutKey != Key.Empty) - { - Parent.ShortcutKey = Key.Empty; - } - // Setter will ensure Key.Empty if it's null - ShortcutKey = shortcutKey!; - } - - private bool _allowNullChecked; - private MenuItemCheckStyle _checkType; - - private string _title; - - /// Gets or sets the action to be invoked when the menu item is triggered. - /// Method to invoke. - public Action? Action { get; set; } - - /// - /// Used only if is of type. If - /// allows to be null, true or false. If only - /// allows to be true or false. - /// - public bool AllowNullChecked - { - get => _allowNullChecked; - set - { - _allowNullChecked = value; - Checked ??= false; - } - } - - /// - /// Gets or sets the action to be invoked to determine if the menu can be triggered. If - /// returns the menu item will be enabled. Otherwise, it will be disabled. - /// - /// Function to determine if the action is can be executed or not. - public Func? CanExecute { get; set; } - - /// - /// Sets or gets whether the shows a check indicator or not. See - /// . - /// - public bool? Checked { set; get; } - - /// - /// Sets or gets the of a menu item where is set to - /// . - /// - public MenuItemCheckStyle CheckType - { - get => _checkType; - set - { - _checkType = value; - - if (_checkType == MenuItemCheckStyle.Checked && !_allowNullChecked && Checked is null) - { - Checked = false; - } - } - } - - /// Gets or sets arbitrary data for the menu item. - /// This property is not used internally. - public object Data { get; set; } - - /// Gets or sets the help text for the menu item. The help text is drawn to the right of the . - /// The help text. - public string Help { get; set; } - - /// - /// Returns if the menu item is enabled. This method is a wrapper around - /// . - /// - public bool IsEnabled () { return CanExecute?.Invoke () ?? true; } - - /// Gets the parent for this . - /// The parent. - public MenuItem? Parent { get; set; } - - /// Gets or sets the title of the menu item . - /// The title. - public string Title - { - get => _title; - set - { - if (_title == value) - { - return; - } - - _title = value; - GetHotKey (); - } - } - - /// - /// Toggle the between three states if is - /// or between two states if is . - /// - public void ToggleChecked () - { - if (_checkType != MenuItemCheckStyle.Checked) - { - throw new InvalidOperationException ("This isn't a Checked MenuItemCheckStyle!"); - } - - bool? previousChecked = Checked; - - if (AllowNullChecked) - { - Checked = previousChecked switch - { - null => true, - true => false, - false => null - }; - } - else - { - Checked = !Checked; - } - } - - /// Merely a debugging aid to see the interaction with main. - internal bool GetMenuBarItem () { return IsFromSubMenu; } - - /// Merely a debugging aid to see the interaction with main. - internal MenuItem GetMenuItem () { return this; } - - /// Gets if this is from a sub-menu. - internal bool IsFromSubMenu => Parent != null; - - internal int TitleLength => GetMenuBarItemLength (Title); - - // - // ┌─────────────────────────────┐ - // │ Quit Quit UI Catalog Ctrl+Q │ - // └─────────────────────────────┘ - // ┌─────────────────┐ - // │ ◌ TopLevel Alt+T │ - // └─────────────────┘ - // TODO: Replace the `2` literals with named constants - internal int Width => 1 - + // space before Title - TitleLength - + 2 - + // space after Title - BUGBUG: This should be 1 - (Checked == true || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) - ? 2 - : 0) - + // check glyph + space - (Help.GetColumns () > 0 ? 2 + Help.GetColumns () : 0) - + // Two spaces before Help - (ShortcutTag.GetColumns () > 0 - ? 2 + ShortcutTag.GetColumns () - : 0); // Pad two spaces before shortcut tag (which are also aligned right) - - private static int GetMenuBarItemLength (string title) - { - return title.EnumerateRunes () - .Where (ch => ch != MenuBar.HotKeySpecifier) - .Sum (ch => Math.Max (ch.GetColumns (), 1)); - } - - #region Keyboard Handling - - private Key _hotKey = Key.Empty; - - /// - /// The HotKey is used to activate a with the keyboard. HotKeys are defined by prefixing the - /// of a MenuItem with an underscore ('_'). - /// - /// Pressing Alt-Hotkey for a (menu items on the menu bar) works even if the menu is - /// not active. Once a menu has focus and is active, pressing just the HotKey will activate the MenuItem. - /// - /// - /// For example for a MenuBar with a "_File" MenuBarItem that contains a "_New" MenuItem, Alt-F will open the - /// File menu. Pressing the N key will then activate the New MenuItem. - /// - /// See also which enable global key-bindings to menu items. - /// - public Key? HotKey - { - get => _hotKey; - private set - { - var oldKey = _hotKey; - _hotKey = value ?? Key.Empty; - UpdateHotKeyBinding (oldKey); - } - } - - private void GetHotKey () - { - var nextIsHot = false; - - foreach (char x in _title) - { - if (x == MenuBar.HotKeySpecifier.Value) - { - nextIsHot = true; - } - else if (nextIsHot) - { - HotKey = char.ToLower (x); - - return; - } - } - - HotKey = Key.Empty; - } - - private Key _shortcutKey = Key.Empty; - - /// - /// Shortcut defines a key binding to the MenuItem that will invoke the MenuItem's action globally for the - /// that is the parent of the this - /// . - /// - /// The will be drawn on the MenuItem to the right of the and - /// text. See . - /// - /// - public Key? ShortcutKey - { - get => _shortcutKey; - set - { - var oldKey = _shortcutKey; - _shortcutKey = value ?? Key.Empty; - UpdateShortcutKeyBinding (oldKey); - } - } - - /// Gets the text describing the keystroke combination defined by . - public string ShortcutTag => ShortcutKey != Key.Empty ? ShortcutKey!.ToString () : string.Empty; - - internal void AddShortcutKeyBinding (MenuBar menuBar, Key key) - { - ArgumentNullException.ThrowIfNull (menuBar); - - _menuBar = menuBar; - - AddOrUpdateShortcutKeyBinding (key); - } - - private void AddOrUpdateShortcutKeyBinding (Key key) - { - if (key != Key.Empty) - { - _menuBar.HotKeyBindings.Remove (key); - } - - if (ShortcutKey != Key.Empty) - { - KeyBinding keyBinding = new ([Command.Select], null, data: this); - // Remove an existent ShortcutKey - _menuBar.HotKeyBindings.Remove (ShortcutKey!); - _menuBar.HotKeyBindings.Add (ShortcutKey!, keyBinding); - } - } - - private void UpdateHotKeyBinding (Key oldKey) - { - if (_menuBar is null or { IsInitialized: false }) - { - return; - } - - if (oldKey != Key.Empty) - { - var index = _menuBar.Menus.IndexOf (this); - - if (index > -1) - { - _menuBar.HotKeyBindings.Remove (oldKey.WithAlt); - } - } - - if (HotKey != Key.Empty) - { - var index = _menuBar.Menus.IndexOf (this); - - if (index > -1) - { - _menuBar.HotKeyBindings.Remove (HotKey!.WithAlt); - KeyBinding keyBinding = new ([Command.Toggle], null, data: this); - _menuBar.HotKeyBindings.Add (HotKey.WithAlt, keyBinding); - } - } - } - - private void UpdateShortcutKeyBinding (Key oldKey) - { - // ReSharper disable once ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract - if (_menuBar is null) - { - return; - } - - AddOrUpdateShortcutKeyBinding (oldKey); - } - - #endregion Keyboard Handling - - /// - /// Removes a dynamically from the . - /// - public virtual void RemoveMenuItem () - { - if (Parent is { }) - { - MenuItem? []? childrens = ((MenuBarItem)Parent).Children; - var i = 0; - - foreach (MenuItem? c in childrens!) - { - if (c != this) - { - childrens [i] = c; - i++; - } - } - - Array.Resize (ref childrens, childrens.Length - 1); - - if (childrens.Length == 0) - { - ((MenuBarItem)Parent).Children = null; - } - else - { - ((MenuBarItem)Parent).Children = childrens; - } - } - - if (ShortcutKey != Key.Empty) - { - // Remove an existent ShortcutKey - _menuBar.HotKeyBindings.Remove (ShortcutKey!); - } - } -} diff --git a/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs b/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs deleted file mode 100644 index ad189e373..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuItemCheckStyle.cs +++ /dev/null @@ -1,16 +0,0 @@ -#nullable disable -namespace Terminal.Gui.Views; - -/// Specifies how a shows selection state. -[Flags] -public enum MenuItemCheckStyle -{ - /// The menu item will be shown normally, with no check indicator. The default. - NoCheck = 0b_0000_0000, - - /// The menu item will indicate checked/un-checked state (see ). - Checked = 0b_0000_0001, - - /// The menu item is part of a menu radio group (see ) and will indicate selected state. - Radio = 0b_0000_0010 -} diff --git a/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs deleted file mode 100644 index 581b9b04c..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuOpenedEventArgs.cs +++ /dev/null @@ -1,22 +0,0 @@ -#nullable disable -namespace Terminal.Gui.Views; -#pragma warning disable CS0618 // Type or member is obsolete - -/// Defines arguments for the event -public class MenuOpenedEventArgs : EventArgs -{ - /// Creates a new instance of the class - /// - /// - public MenuOpenedEventArgs (MenuBarItem parent, MenuItem menuItem) - { - Parent = parent; - MenuItem = menuItem; - } - - /// Gets the being opened. - public MenuItem MenuItem { get; } - - /// The parent of . Will be null if menu opening is the root. - public MenuBarItem Parent { get; } -} diff --git a/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs b/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs deleted file mode 100644 index c2e10d6d4..000000000 --- a/Terminal.Gui/Views/Menuv1/MenuOpeningEventArgs.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable disable -namespace Terminal.Gui.Views; - -#pragma warning disable CS0618 // Type or member is obsolete - -/// -/// An which allows passing a cancelable menu opening event or replacing with a new -/// . -/// -public class MenuOpeningEventArgs : EventArgs -{ - /// Initializes a new instance of . - /// The current parent. - public MenuOpeningEventArgs (MenuBarItem currentMenu) { CurrentMenu = currentMenu; } - - /// - /// Flag that allows the cancellation of the event. If set to in the event handler, the - /// event will be canceled. - /// - public bool Cancel { get; set; } - - /// The current parent. - public MenuBarItem CurrentMenu { get; } - - /// The new to be replaced. - public MenuBarItem NewMenuBarItem { get; set; } -} diff --git a/Terminal.Gui/Views/TextInput/TextField.cs b/Terminal.Gui/Views/TextInput/TextField.cs index 38d123c19..c4aa19754 100644 --- a/Terminal.Gui/Views/TextInput/TextField.cs +++ b/Terminal.Gui/Views/TextInput/TextField.cs @@ -1233,7 +1233,7 @@ public class TextField : View, IDesignable DisposeContextMenu (); PopoverMenu menu = new ( - new List + new List { new (this, Command.SelectAll, Strings.ctxSelectAll), new (this, Command.DeleteAll, Strings.ctxDeleteAll), diff --git a/Terminal.Gui/Views/TextInput/TextView.cs b/Terminal.Gui/Views/TextInput/TextView.cs index c2ad7d463..5e2124d38 100644 --- a/Terminal.Gui/Views/TextInput/TextView.cs +++ b/Terminal.Gui/Views/TextInput/TextView.cs @@ -2370,13 +2370,13 @@ public class TextView : View, IDesignable PopoverMenu menu = new ( new List { - 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) + new MenuItem (this, Command.SelectAll, Strings.ctxSelectAll), + new MenuItem (this, Command.DeleteAll, Strings.ctxDeleteAll), + new MenuItem (this, Command.Copy, Strings.ctxCopy), + new MenuItem (this, Command.Cut, Strings.ctxCut), + new MenuItem (this, Command.Paste, Strings.ctxPaste), + new MenuItem (this, Command.Undo, Strings.ctxUndo), + new MenuItem (this, Command.Redo, Strings.ctxRedo) }); menu.KeyChanged += ContextMenu_KeyChanged; diff --git a/Terminal.Gui/Views/Toplevel.cs b/Terminal.Gui/Views/Toplevel.cs index 6f9bb2268..e3feb8ef9 100644 --- a/Terminal.Gui/Views/Toplevel.cs +++ b/Terminal.Gui/Views/Toplevel.cs @@ -68,13 +68,9 @@ public partial class Toplevel : View #region SubViews - // TODO: Deprecate - Any view can host a menubar in v2 - /// Gets the latest added into this Toplevel. - public MenuBar? MenuBar => (MenuBar?)SubViews?.LastOrDefault (s => s is MenuBar); - - //// TODO: Deprecate - Any view can host a statusbar in v2 - ///// Gets the latest added into this Toplevel. - //public StatusBar? StatusBar => (StatusBar?)SubViews?.LastOrDefault (s => s is StatusBar); + //// TODO: Deprecate - Any view can host a menubar in v2 + ///// Gets the latest added into this Toplevel. + //public MenuBar? MenuBar => (MenuBar?)SubViews?.LastOrDefault (s => s is MenuBar); #endregion @@ -85,7 +81,7 @@ public partial class Toplevel : View /// Setting this property directly is discouraged. Use instead. public bool Running { get; set; } - // TODO: IRunnable: Re-implement in IRunnable + // TODO: Deprecate. Other than a few tests, this is not used anywhere. /// /// if was already loaded by the /// , otherwise. diff --git a/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs b/Tests/IntegrationTests/FluentTests/MenuBarvTests.cs similarity index 89% rename from Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs rename to Tests/IntegrationTests/FluentTests/MenuBarvTests.cs index e0ca97b45..499957f12 100644 --- a/Tests/IntegrationTests/FluentTests/MenuBarv2Tests.cs +++ b/Tests/IntegrationTests/FluentTests/MenuBarvTests.cs @@ -7,13 +7,13 @@ using Xunit.Abstractions; namespace IntegrationTests.FluentTests; /// -/// Tests for the MenuBarv2 class +/// Tests for the MenuBar class /// -public class MenuBarv2Tests +public class MenuBarTests { private readonly TextWriter _out; - public MenuBarv2Tests (ITestOutputHelper outputHelper) + public MenuBarTests (ITestOutputHelper outputHelper) { CultureInfo.CurrentUICulture = CultureInfo.InvariantCulture; _out = new TestOutputWriter (outputHelper); @@ -27,11 +27,11 @@ public class MenuBarv2Tests .Then ((_) => { // Create a menu bar with no items - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); Assert.Empty (menuBar.SubViews); Assert.False (menuBar.CanFocus); Assert.Equal (Orientation.Horizontal, menuBar.Orientation); - Assert.Equal (Key.F9, MenuBarv2.DefaultKey); + Assert.Equal (Key.F9, MenuBar.DefaultKey); }); } @@ -39,7 +39,7 @@ public class MenuBarv2Tests [ClassData (typeof (TestDrivers))] public void Initializes_WithItems (TestDriver d) { - MenuBarItemv2 [] menuItems = []; + MenuBarItem [] menuItems = []; using GuiTestContext c = With.A (80, 25, d, _out) .Then ((_) => @@ -50,25 +50,25 @@ public class MenuBarv2Tests new ( "_File", [ - new MenuItemv2 ("_Open", "Opens a file", () => { }) + new MenuItem ("_Open", "Opens a file", () => { }) ]), new ( "_Edit", [ - new MenuItemv2 ("_Copy", "Copies selection", () => { }) + new MenuItem ("_Copy", "Copies selection", () => { }) ]) ]; - var menuBar = new MenuBarv2 (menuItems); + var menuBar = new MenuBar (menuItems); Assert.Equal (2, menuBar.SubViews.Count); // First item should be the File menu - var fileMenu = menuBar.SubViews.ElementAt (0) as MenuBarItemv2; + var fileMenu = menuBar.SubViews.ElementAt (0) as MenuBarItem; Assert.NotNull (fileMenu); Assert.Equal ("_File", fileMenu.Title); // Second item should be the Edit menu - var editMenu = menuBar.SubViews.ElementAt (1) as MenuBarItemv2; + var editMenu = menuBar.SubViews.ElementAt (1) as MenuBarItem; Assert.NotNull (editMenu); Assert.Equal ("_Edit", editMenu.Title); }); @@ -81,7 +81,7 @@ public class MenuBarv2Tests using GuiTestContext c = With.A (80, 25, d, _out) .Then ((_) => { - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); // Set items through Menus property menuBar.Menus = @@ -102,7 +102,7 @@ public class MenuBarv2Tests using GuiTestContext c = With.A (80, 25, d, _out) .Then ((_) => { - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); var oldKeyValue = Key.Empty; var newKeyValue = Key.Empty; @@ -136,13 +136,13 @@ public class MenuBarv2Tests [ClassData (typeof (TestDrivers))] public void DefaultKey_Activates (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; Toplevel? top = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((app) => { - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); top = app.TopRunnable!; top.Add ( @@ -156,11 +156,11 @@ public class MenuBarv2Tests app.TopRunnable!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (top?.App?.Navigation!.GetFocused ()) + .AssertIsNotType (top?.App?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .WaitIteration () - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .AssertEqual ("_New file", top?.App?.Navigation!.GetFocused ()!.Title) .AssertTrue (top?.App?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()); @@ -171,13 +171,13 @@ public class MenuBarv2Tests [ClassData (typeof (TestDrivers))] public void DefaultKey_DeActivates (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel top = app.TopRunnable!; top.Add ( @@ -191,14 +191,14 @@ public class MenuBarv2Tests app.TopRunnable!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .EnqueueKeyEvent (MenuBar.DefaultKey) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) - .AssertIsNotType (app?.Navigation!.GetFocused ()); + .EnqueueKeyEvent (MenuBar.DefaultKey) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) + .AssertIsNotType (app?.Navigation!.GetFocused ()); } [Theory] @@ -211,14 +211,14 @@ public class MenuBarv2Tests { app = a; // Create a menu bar with items that have submenus - var fileMenuItem = new MenuBarItemv2 ( + var fileMenuItem = new MenuBarItem ( "_File", [ - new MenuItemv2 ("_Open", string.Empty, null), - new MenuItemv2 ("_Save", string.Empty, null) + new MenuItem ("_Open", string.Empty, null), + new MenuItem ("_Save", string.Empty, null) ]); - var menuBar = new MenuBarv2 ([fileMenuItem]) { App = app }; + var menuBar = new MenuBar ([fileMenuItem]) { App = app }; // Initially, no menu should be open Assert.False (menuBar.IsOpen ()); @@ -229,12 +229,12 @@ public class MenuBarv2Tests menuBar.EndInit (); // Simulate showing a popover menu by manipulating the first menu item - MethodInfo? showPopoverMethod = typeof (MenuBarv2).GetMethod ( + MethodInfo? showPopoverMethod = typeof (MenuBar).GetMethod ( "ShowPopover", BindingFlags.NonPublic | BindingFlags.Instance); // Set menu bar to active state using reflection - FieldInfo? activeField = typeof (MenuBarv2).GetField ( + FieldInfo? activeField = typeof (MenuBar).GetField ( "_active", BindingFlags.NonPublic | BindingFlags.Instance); activeField?.SetValue (menuBar, true); @@ -265,7 +265,7 @@ public class MenuBarv2Tests using GuiTestContext c = With.A (80, 25, d, _out) .Then ((app) => { - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); app.TopRunnable!.Add (menuBar); // Call EnableForDesign @@ -279,40 +279,40 @@ public class MenuBarv2Tests Assert.True (menuBar.SubViews.Count > 0); // Should have File, Edit and Help menus - View? fileMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_File"); - View? editMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_Edit"); - View? helpMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItemv2)?.Title == "_Help"); + View? fileMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_File"); + View? editMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_Edit"); + View? helpMenu = menuBar.SubViews.FirstOrDefault (v => (v as MenuBarItem)?.Title == "_Help"); Assert.NotNull (fileMenu); Assert.NotNull (editMenu); Assert.NotNull (helpMenu); }) - .ScreenShot ("MenuBarv2 EnableForDesign", _out); + .ScreenShot ("MenuBar EnableForDesign", _out); } [Theory] [ClassData (typeof (TestDrivers))] public void Navigation_Left_Right_Wraps (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel top = app.TopRunnable!; menuBar.EnableForDesign (ref top); app.TopRunnable!.Add (menuBar); }) .WaitIteration () .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .EnqueueKeyEvent (Key.CursorRight) .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .ScreenShot ("After right arrow", _out) @@ -334,14 +334,14 @@ public class MenuBarv2Tests [ClassData (typeof (TestDrivers))] public void MenuBarItem_With_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel top = app.TopRunnable!; top.Add ( @@ -354,24 +354,24 @@ public class MenuBarv2Tests menuBar.EnableForDesign (ref top); app.TopRunnable!.Add (menuBar); }) - .AssertIsNotType (app!.Navigation!.GetFocused ()) + .AssertIsNotType (app!.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title) .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) .AssertEqual ("_New file", app?.Navigation?.GetFocused ()!.Title) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (app!.Navigation!.GetFocused ()); + .AssertIsNotType (app!.Navigation!.GetFocused ()); } [Theory] [ClassData (typeof (TestDrivers))] public void MenuBarItem_Without_QuitKey_Open_QuitKey_Restores_Focus_Correctly (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) @@ -385,38 +385,38 @@ public class MenuBarv2Tests .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel? toplevel = app.TopRunnable; menuBar.EnableForDesign (ref toplevel!); app.TopRunnable!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .EnqueueKeyEvent (Key.CursorRight) .AssertEqual ("Cu_t", app?.Navigation!.GetFocused ()!.Title) .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (menuBar?.IsOpen ()) .AssertEqual ("Cu_t", app?.Navigation?.GetFocused ()!.Title) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (app?.Navigation?.GetFocused ()); + .AssertIsNotType (app?.Navigation?.GetFocused ()); } [Theory] [ClassData (typeof (TestDrivers))] public void MenuBarItem_With_QuitKey_Open_QuitKey_Does_Not_Quit_App (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel top = app.TopRunnable!; top.Add ( @@ -430,12 +430,12 @@ public class MenuBarv2Tests app.TopRunnable!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (app!.Navigation!.GetFocused ()) + .AssertIsNotType (app!.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .AssertEqual ("_New file", app.Navigation!.GetFocused ()!.Title) .AssertTrue (app?.TopRunnable!.Running) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (app!.TopRunnable!.Running); @@ -445,14 +445,14 @@ public class MenuBarv2Tests [ClassData (typeof (TestDrivers))] public void MenuBarItem_Without_QuitKey_Open_QuitKey_Does_Not_Quit_MenuBar_SuperView (TestDriver d) { - MenuBarv2? menuBar = null; + MenuBar? menuBar = null; IApplication? app = null; using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { app = a; - menuBar = new MenuBarv2 (); + menuBar = new MenuBar (); Toplevel top = app.TopRunnable!; top.Add ( @@ -463,9 +463,9 @@ public class MenuBarv2Tests }); menuBar.EnableForDesign (ref top); - IEnumerable items = menuBar.GetMenuItemsWithTitle ("_Quit"); + IEnumerable items = menuBar.GetMenuItemsWithTitle ("_Quit"); - foreach (MenuItemv2 item in items) + foreach (MenuItem item in items) { item.Key = Key.Empty; } @@ -473,11 +473,11 @@ public class MenuBarv2Tests app.TopRunnable!.Add (menuBar); }) .WaitIteration () - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("MenuBar initial state", _out) - .EnqueueKeyEvent (MenuBarv2.DefaultKey) + .EnqueueKeyEvent (MenuBar.DefaultKey) .AssertEqual ("_New file", app?.Navigation!.GetFocused ()!.Title) - .ScreenShot ($"After {MenuBarv2.DefaultKey}", _out) + .ScreenShot ($"After {MenuBar.DefaultKey}", _out) .EnqueueKeyEvent (Application.QuitKey) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) .AssertTrue (app?.TopRunnable!.Running); @@ -505,7 +505,7 @@ public class MenuBarv2Tests using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); Toplevel top = a.TopRunnable!; menuBar.EnableForDesign (ref top); a.TopRunnable!.Add (menuBar); @@ -539,7 +539,7 @@ public class MenuBarv2Tests using GuiTestContext c = With.A (50, 20, d, _out) .Then ((a) => { - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); Toplevel top = a.TopRunnable!; menuBar.EnableForDesign (ref top); a.TopRunnable!.Add (menuBar); diff --git a/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs b/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs index 09ce0417d..079411148 100644 --- a/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs +++ b/Tests/IntegrationTests/FluentTests/PopverMenuTests.cs @@ -86,7 +86,7 @@ public class PopoverMenuTests view.SetFocus (); }) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("PopoverMenu initial state", _out) .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("After Show", _out) @@ -179,15 +179,15 @@ public class PopoverMenuTests }) .ScreenShot ("PopoverMenu initial state", _out) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("After Show", _out) .AssertTrue (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsType (app?.Navigation!.GetFocused ()) + .AssertIsType (app?.Navigation!.GetFocused ()) .EnqueueKeyEvent (Application.QuitKey) .ScreenShot ($"After {Application.QuitKey}", _out) .AssertFalse (app?.Popover?.GetActivePopover () is PopoverMenu) - .AssertIsNotType (app?.Navigation!.GetFocused ()); + .AssertIsNotType (app?.Navigation!.GetFocused ()); } [Theory] @@ -226,7 +226,7 @@ public class PopoverMenuTests view.SetFocus (); }) - .AssertIsNotType (app?.Navigation!.GetFocused ()) + .AssertIsNotType (app?.Navigation!.GetFocused ()) .ScreenShot ("PopoverMenu initial state", _out) .Then ((_) => app?.Popover!.Show (app?.Popover.Popovers.First ())) .ScreenShot ("PopoverMenu after Show", _out) @@ -361,7 +361,7 @@ public class PopoverMenuTests { var clicked = false; - MenuItemv2 [] menuItems = [new ("_New File", string.Empty, () => { clicked = true; })]; + MenuItem [] menuItems = [new ("_New File", string.Empty, () => { clicked = true; })]; IApplication? app = null; using GuiTestContext c = With.A (40, 10, d, _out) @@ -390,7 +390,7 @@ public class PopoverMenuTests { var clicked = false; - MenuItemv2 [] menuItems = + MenuItem [] menuItems = [ new ("One", "", null), new ("Two", "", null), diff --git a/Tests/UnitTests/Application/ApplicationTests.cs b/Tests/UnitTests/Application/ApplicationTests.cs index 52d9276da..4ff8ecb2b 100644 --- a/Tests/UnitTests/Application/ApplicationTests.cs +++ b/Tests/UnitTests/Application/ApplicationTests.cs @@ -661,7 +661,7 @@ public class ApplicationTests void OnApplicationOnIteration (object s, IterationEventArgs a) { Assert.True (top.Running); - top.Running = false; + top.RequestStop (); } } diff --git a/Tests/UnitTests/View/SubviewTests.cs b/Tests/UnitTests/View/SubviewTests.cs index 2da6f6065..21c320986 100644 --- a/Tests/UnitTests/View/SubviewTests.cs +++ b/Tests/UnitTests/View/SubviewTests.cs @@ -4,104 +4,104 @@ namespace UnitTests.ViewTests; public class SubViewTests { - private readonly ITestOutputHelper _output; - public SubViewTests (ITestOutputHelper output) { _output = output; } + //private readonly ITestOutputHelper _output; + //public SubViewTests (ITestOutputHelper output) { _output = output; } - // TODO: This is a poor unit tests. Not clear what it's testing. Refactor. - [Fact] - [AutoInitShutdown] - public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically () - { - var t = new Toplevel { Id = "0" }; + //// TODO: This is a poor unit tests. Not clear what it's testing. Refactor. + //[Fact] + //[AutoInitShutdown] + //public void Initialized_Event_Will_Be_Invoked_When_Added_Dynamically () + //{ + // var t = new Toplevel { Id = "0" }; - var w = new Window { Id = "t", Width = Dim.Fill (), Height = Dim.Fill () }; - var v1 = new View { Id = "v1", Width = Dim.Fill (), Height = Dim.Fill () }; - var v2 = new View { Id = "v2", Width = Dim.Fill (), Height = Dim.Fill () }; + // var w = new Window { Id = "t", Width = Dim.Fill (), Height = Dim.Fill () }; + // var v1 = new View { Id = "v1", Width = Dim.Fill (), Height = Dim.Fill () }; + // var v2 = new View { Id = "v2", Width = Dim.Fill (), Height = Dim.Fill () }; - int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0; + // int tc = 0, wc = 0, v1c = 0, v2c = 0, sv1c = 0; - t.Initialized += (s, e) => - { - tc++; - Assert.Equal (1, tc); - Assert.Equal (1, wc); - Assert.Equal (1, v1c); - Assert.Equal (1, v2c); - Assert.Equal (0, sv1c); // Added after t in the Application.Iteration. + // t.Initialized += (s, e) => + // { + // tc++; + // Assert.Equal (1, tc); + // Assert.Equal (1, wc); + // Assert.Equal (1, v1c); + // Assert.Equal (1, v2c); + // Assert.Equal (0, sv1c); // Added after t in the Application.Iteration. - Assert.True (t.CanFocus); - Assert.True (w.CanFocus); - Assert.False (v1.CanFocus); - Assert.False (v2.CanFocus); + // Assert.True (t.CanFocus); + // Assert.True (w.CanFocus); + // Assert.False (v1.CanFocus); + // Assert.False (v2.CanFocus); - Application.LayoutAndDraw (); - }; + // Application.LayoutAndDraw (); + // }; - w.Initialized += (s, e) => - { - wc++; - Assert.Equal (t.Viewport.Width, w.Frame.Width); - Assert.Equal (t.Viewport.Height, w.Frame.Height); - }; + // w.Initialized += (s, e) => + // { + // wc++; + // Assert.Equal (t.Viewport.Width, w.Frame.Width); + // Assert.Equal (t.Viewport.Height, w.Frame.Height); + // }; - v1.Initialized += (s, e) => - { - v1c++; + // v1.Initialized += (s, e) => + // { + // v1c++; - //Assert.Equal (t.Viewport.Width, v1.Frame.Width); - //Assert.Equal (t.Viewport.Height, v1.Frame.Height); - }; + // //Assert.Equal (t.Viewport.Width, v1.Frame.Width); + // //Assert.Equal (t.Viewport.Height, v1.Frame.Height); + // }; - v2.Initialized += (s, e) => - { - v2c++; + // v2.Initialized += (s, e) => + // { + // v2c++; - //Assert.Equal (t.Viewport.Width, v2.Frame.Width); - //Assert.Equal (t.Viewport.Height, v2.Frame.Height); - }; - w.Add (v1, v2); - t.Add (w); + // //Assert.Equal (t.Viewport.Width, v2.Frame.Width); + // //Assert.Equal (t.Viewport.Height, v2.Frame.Height); + // }; + // w.Add (v1, v2); + // t.Add (w); - Application.Iteration += OnApplicationOnIteration; + // Application.Iteration += OnApplicationOnIteration; - Application.Run (t); - Application.Iteration -= OnApplicationOnIteration; + // Application.Run (t); + // Application.Iteration -= OnApplicationOnIteration; - t.Dispose (); - Application.Shutdown (); + // t.Dispose (); + // Application.Shutdown (); - Assert.Equal (1, tc); - Assert.Equal (1, wc); - Assert.Equal (1, v1c); - Assert.Equal (1, v2c); - Assert.Equal (1, sv1c); + // Assert.Equal (1, tc); + // Assert.Equal (1, wc); + // Assert.Equal (1, v1c); + // Assert.Equal (1, v2c); + // Assert.Equal (1, sv1c); - Assert.True (t.CanFocus); - Assert.True (w.CanFocus); - Assert.False (v1.CanFocus); - Assert.False (v2.CanFocus); + // Assert.True (t.CanFocus); + // Assert.True (w.CanFocus); + // Assert.False (v1.CanFocus); + // Assert.False (v2.CanFocus); - return; + // return; - void OnApplicationOnIteration (object s, IterationEventArgs a) - { - var sv1 = new View { Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill () }; + // void OnApplicationOnIteration (object s, IterationEventArgs a) + // { + // var sv1 = new View { Id = "sv1", Width = Dim.Fill (), Height = Dim.Fill () }; - sv1.Initialized += (s, e) => - { - sv1c++; - Assert.NotEqual (t.Frame.Width, sv1.Frame.Width); - Assert.NotEqual (t.Frame.Height, sv1.Frame.Height); - Assert.False (sv1.CanFocus); + // sv1.Initialized += (s, e) => + // { + // sv1c++; + // Assert.NotEqual (t.Frame.Width, sv1.Frame.Width); + // Assert.NotEqual (t.Frame.Height, sv1.Frame.Height); + // Assert.False (sv1.CanFocus); - //Assert.Throws (() => sv1.CanFocus = true); - Assert.False (sv1.CanFocus); - }; + // //Assert.Throws (() => sv1.CanFocus = true); + // Assert.False (sv1.CanFocus); + // }; - v1.Add (sv1); + // v1.Add (sv1); - Application.LayoutAndDraw (); - t.Running = false; - } - } + // Application.LayoutAndDraw (); + // t.Running = false; + // } + //} } diff --git a/Tests/UnitTests/Views/MenuBarTests.cs b/Tests/UnitTests/Views/MenuBarTests.cs index b77ebc1ad..b15501a07 100644 --- a/Tests/UnitTests/Views/MenuBarTests.cs +++ b/Tests/UnitTests/Views/MenuBarTests.cs @@ -15,12 +15,12 @@ public class MenuBarTests () App = ApplicationImpl.Instance }; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; top.Add (menuBar); - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBar.Add (menuBarItem); @@ -41,7 +41,7 @@ public class MenuBarTests () Assert.False (menuBar.Active); // Act - Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); + Application.RaiseKeyDownEvent (MenuBar.DefaultKey); Assert.True (menuBar.Active); Assert.True (menuBar.IsOpen ()); Assert.True (menuBar.HasFocus); @@ -59,7 +59,7 @@ public class MenuBarTests () { // Arrange var top = new Toplevel () { App = ApplicationImpl.Instance }; - MenuBarv2 menuBar = new MenuBarv2 () { App = ApplicationImpl.Instance }; + MenuBar menuBar = new MenuBar () { App = ApplicationImpl.Instance }; menuBar.EnableForDesign (ref top); top.Add (menuBar); @@ -67,10 +67,10 @@ public class MenuBarTests () Assert.False (menuBar.Active); // Act - Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); + Application.RaiseKeyDownEvent (MenuBar.DefaultKey); Assert.True (menuBar.IsOpen ()); - Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); + Application.RaiseKeyDownEvent (MenuBar.DefaultKey); Assert.False (menuBar.Active); Assert.False (menuBar.IsOpen ()); Assert.False (menuBar.HasFocus); @@ -85,13 +85,13 @@ public class MenuBarTests () public void QuitKey_Deactivates () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "Menu_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_MenuBarItem" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "Menu_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_MenuBarItem" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -99,7 +99,7 @@ public class MenuBarTests () top.Add (menuBar); SessionToken rs = Application.Begin (top); - Application.RaiseKeyDownEvent (MenuBarv2.DefaultKey); + Application.RaiseKeyDownEvent (MenuBar.DefaultKey); Assert.True (menuBar.Active); Assert.True (menuBar.IsOpen ()); Assert.True (menuBarItem.PopoverMenu.Visible); @@ -123,13 +123,13 @@ public class MenuBarTests () public void MenuBarItem_HotKey_Activates_And_Opens () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -155,13 +155,13 @@ public class MenuBarTests () public void MenuBarItem_HotKey_Deactivates () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -194,13 +194,13 @@ public class MenuBarTests () { // Arrange int action = 0; - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "Menu_Item", Action = () => action++ }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_MenuBarItem" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "Menu_Item", Action = () => action++ }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_MenuBarItem" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -227,13 +227,13 @@ public class MenuBarTests () public void MenuItems_HotKey_Deactivates () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "Menu_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_MenuBarItem" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "Menu_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_MenuBarItem" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -264,13 +264,13 @@ public class MenuBarTests () public void HotKey_Makes_PopoverMenu_Visible_Only_Once () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -301,21 +301,21 @@ public class MenuBarTests () public void WhenOpen_Other_MenuBarItem_HotKey_Activates_And_Opens () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuItem2 = new MenuItemv2 { Id = "menuItem2", Title = "_Copy" }; - var menu2 = new Menuv2 ([menuItem2]) { Id = "menu2" }; - var menuBarItem2 = new MenuBarItemv2 () { Id = "menuBarItem2", Title = "_Edit" }; + var menuItem2 = new MenuItem { Id = "menuItem2", Title = "_Copy" }; + var menu2 = new Menu ([menuItem2]) { Id = "menu2" }; + var menuBarItem2 = new MenuBarItem () { Id = "menuBarItem2", Title = "_Edit" }; var menuBarItemPopover2 = new PopoverMenu () { Id = "menuBarItemPopover2" }; menuBarItem2.PopoverMenu = menuBarItemPopover2; menuBarItemPopover2.Root = menu2; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); menuBar.Add (menuBarItem2); @@ -346,13 +346,13 @@ public class MenuBarTests () public void Mouse_Enter_Activates_But_Does_Not_Open () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -383,7 +383,7 @@ public class MenuBarTests () { // Arrange var top = new Toplevel () { App = ApplicationImpl.Instance }; - MenuBarv2 menuBar = new MenuBarv2 () { App = ApplicationImpl.Instance }; + MenuBar menuBar = new MenuBar () { App = ApplicationImpl.Instance }; menuBar.EnableForDesign (ref top); top.Add (menuBar); @@ -414,13 +414,13 @@ public class MenuBarTests () { // Arrange // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -461,13 +461,13 @@ public class MenuBarTests () { // Arrange int action = 0; - var menuItem = new MenuItemv2 { Title = "_Item", Action = () => action++ }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Title = "_New" }; + var menuItem = new MenuItem { Title = "_Item", Action = () => action++ }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 (); + var menuBar = new MenuBar (); menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -503,13 +503,13 @@ public class MenuBarTests () public void Disabled_MenuBar_Is_Not_Activated () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -534,13 +534,13 @@ public class MenuBarTests () public void MenuBarItem_Disabled_MenuBarItem_HotKey_No_Activate_Or_Open () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -566,13 +566,13 @@ public class MenuBarTests () public void MenuBarItem_Disabled_Popover_Is_Activated () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -604,13 +604,13 @@ public class MenuBarTests () public void Update_MenuBarItem_HotKey_Works () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -653,13 +653,13 @@ public class MenuBarTests () public void Visible_False_HotKey_Does_Not_Activate () { // Arrange - var menuItem = new MenuItemv2 { Id = "menuItem", Title = "_Item" }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menuItem = new MenuItem { Id = "menuItem", Title = "_Item" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); @@ -688,19 +688,19 @@ public class MenuBarTests () { // Arrange int action = 0; - var menuItem = new MenuItemv2 () + var menuItem = new MenuItem () { Id = "menuItem", Title = "_Item", Key = Key.F1, Action = () => action++ }; - var menu = new Menuv2 ([menuItem]) { Id = "menu" }; - var menuBarItem = new MenuBarItemv2 { Id = "menuBarItem", Title = "_New" }; + var menu = new Menu ([menuItem]) { Id = "menu" }; + var menuBarItem = new MenuBarItem { Id = "menuBarItem", Title = "_New" }; var menuBarItemPopover = new PopoverMenu (); menuBarItem.PopoverMenu = menuBarItemPopover; menuBarItemPopover.Root = menu; - var menuBar = new MenuBarv2 () { Id = "menuBar" }; + var menuBar = new MenuBar () { Id = "menuBar" }; menuBar.Add (menuBarItem); Assert.Single (menuBar.SubViews); Assert.Single (menuBarItem.SubViews); diff --git a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs b/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs deleted file mode 100644 index c51389bbc..000000000 --- a/Tests/UnitTests/Views/Menuv1/MenuBarv1Tests.cs +++ /dev/null @@ -1,3410 +0,0 @@ -using UnitTests; -using Xunit.Abstractions; - -namespace UnitTests.ViewsTests; - -#pragma warning disable CS0618 // Type or member is obsolete -public class MenuBarv1Tests (ITestOutputHelper output) -{ - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void AddMenuBarItem_RemoveMenuItem_Dynamically () - { - var menuBar = new MenuBar (); - var menuBarItem = new MenuBarItem { Title = "_New" }; - var action = ""; - var menuItem = new MenuItem { Title = "_Item", Action = () => action = "I", Parent = menuBarItem }; - Assert.Equal ("n", menuBarItem.HotKey); - Assert.Equal ("i", menuItem.HotKey); - Assert.Empty (menuBar.Menus); - menuBarItem.AddMenuBarItem (menuBar, menuItem); - menuBar.Menus = [menuBarItem]; - Assert.Single (menuBar.Menus); - Assert.Single (menuBar.Menus [0].Children!); - - Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _)); - Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _)); - - var top = new Toplevel (); - top.Add (menuBar); - Application.Begin (top); - - top.NewKeyDownEvent (Key.N.WithAlt); - AutoInitShutdownAttribute.RunIteration (); - - Assert.True (menuBar.IsMenuOpen); - Assert.Equal ("", action); - - top.NewKeyDownEvent (Key.I); - AutoInitShutdownAttribute.RunIteration (); - - Assert.False (menuBar.IsMenuOpen); - Assert.Equal ("I", action); - - menuItem.RemoveMenuItem (); - Assert.Single (menuBar.Menus); - Assert.Null (menuBar.Menus [0].Children); - Assert.True (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _)); - Assert.False (menuBar.HotKeyBindings.TryGet (Key.I, out _)); - - menuBarItem.RemoveMenuItem (); - Assert.Empty (menuBar.Menus); - Assert.False (menuBar.HotKeyBindings.TryGet (Key.N.WithAlt, out _)); - - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void AllowNullChecked_Get_Set () - { - var mi = new MenuItem ("Check this out 你", "", null) { CheckType = MenuItemCheckStyle.Checked }; - mi.Action = mi.ToggleChecked; - - var menu = new MenuBar - { - Menus = - [ - new ("Nullable Checked", new [] { mi }) - ] - }; - - //new CheckBox (); - Toplevel top = new (); - top.Add (menu); - Application.Begin (top); - - Assert.False (mi.Checked); - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter)); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (mi.Checked); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - - Assert.True ( - menu._openMenu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu } - ) - ); - AutoInitShutdownAttribute.RunIteration (); - Assert.False (mi.Checked); - - mi.AllowNullChecked = true; - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter)); - AutoInitShutdownAttribute.RunIteration (); - Assert.Null (mi.Checked); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @$" - Nullable Checked -┌──────────────────────┐ -│ {Glyphs.CheckStateNone} Check this out 你 │ -└──────────────────────┘", - output - ); - - Assert.True ( - menu._openMenu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu } - ) - ); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (mi.Checked); - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu._openMenu.NewKeyDownEvent (Key.Enter)); - AutoInitShutdownAttribute.RunIteration (); - Assert.False (mi.Checked); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - - Assert.True ( - menu._openMenu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Clicked, View = menu._openMenu } - ) - ); - AutoInitShutdownAttribute.RunIteration (); - Assert.Null (mi.Checked); - - mi.AllowNullChecked = false; - Assert.False (mi.Checked); - - mi.CheckType = MenuItemCheckStyle.NoCheck; - Assert.Throws (mi.ToggleChecked); - - mi.CheckType = MenuItemCheckStyle.Radio; - Assert.Throws (mi.ToggleChecked); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void CanExecute_False_Does_Not_Throws () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] - { - new ("New", "", null, () => false), - null, - new ("Quit", "", null) - }) - ] - }; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void CanExecute_HotKey () - { - Window win = null; - - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ("_New", "", New, CanExecuteNew), - new ( - "_Close", - "", - Close, - CanExecuteClose - ) - } - ) - ] - }; - Toplevel top = new (); - top.Add (menu); - - bool CanExecuteNew () { return win == null; } - - void New () { win = new (); } - - bool CanExecuteClose () { return win != null; } - - void Close () { win = null; } - - Application.Begin (top); - - Assert.Null (win); - Assert.True (CanExecuteNew ()); - Assert.False (CanExecuteClose ()); - - Assert.True (top.NewKeyDownEvent (Key.F.WithAlt)); - Assert.True (top.NewKeyDownEvent (Key.N.WithAlt)); - AutoInitShutdownAttribute.RunIteration (); - Assert.NotNull (win); - Assert.False (CanExecuteNew ()); - Assert.True (CanExecuteClose ()); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Click_Another_View_Close_An_Open_Menu () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }) - ] - }; - - var btnClicked = false; - var btn = new Button { Y = 4, Text = "Test" }; - btn.Accepting += (s, e) => btnClicked = true; - var top = new Toplevel (); - top.Add (menu, btn); - Application.Begin (top); - - Application.RaiseMouseEvent (new () { ScreenPosition = new (0, 4), Flags = MouseFlags.Button1Clicked }); - Assert.True (btnClicked); - top.Dispose (); - } - - // TODO: Lots of tests in here really test Menu and MenuItem - Move them to MenuTests.cs - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void Constructors_Defaults () - { - var menuBar = new MenuBar (); - Assert.Equal (KeyCode.F9, menuBar.Key); - var menu = new Menu { Host = menuBar, X = 0, Y = 0, BarItems = new () }; - Assert.False (menu.HasScheme); - Assert.False (menu.IsInitialized); - menu.BeginInit (); - menu.EndInit (); - Assert.True (menu.CanFocus); - Assert.False (menu.WantContinuousButtonPressed); - Assert.Equal (LineStyle.Single, menuBar.MenusBorderStyle); - - menuBar = new (); - Assert.Equal (0, menuBar.X); - Assert.Equal (0, menuBar.Y); - Assert.IsType (menuBar.Width); - Assert.Equal (1, menuBar.Height); - Assert.Empty (menuBar.Menus); - Assert.True (menuBar.WantMousePositionReports); - Assert.False (menuBar.IsMenuOpen); - - menuBar = new () { Menus = [] }; - Assert.Equal (0, menuBar.X); - Assert.Equal (0, menuBar.Y); - Assert.IsType (menuBar.Width); - Assert.Equal (1, menuBar.Height); - Assert.Empty (menuBar.Menus); - Assert.True (menuBar.WantMousePositionReports); - Assert.False (menuBar.IsMenuOpen); - - var menuBarItem = new MenuBarItem (); - Assert.Equal ("", menuBarItem.Title); - Assert.Null (menuBarItem.Parent); - Assert.Empty (menuBarItem.Children); - - menuBarItem = new (new MenuBarItem [] { }); - Assert.Equal ("", menuBarItem.Title); - Assert.Null (menuBarItem.Parent); - Assert.Empty (menuBarItem.Children); - - menuBarItem = new ("Test", new MenuBarItem [] { }); - Assert.Equal ("Test", menuBarItem.Title); - Assert.Null (menuBarItem.Parent); - Assert.Empty (menuBarItem.Children); - - menuBarItem = new ("Test", new List ()); - Assert.Equal ("Test", menuBarItem.Title); - Assert.Null (menuBarItem.Parent); - Assert.Empty (menuBarItem.Children); - - menuBarItem = new ("Test", "Help", null); - Assert.Equal ("Test", menuBarItem.Title); - Assert.Equal ("Help", menuBarItem.Help); - Assert.Null (menuBarItem.Action); - Assert.Null (menuBarItem.CanExecute); - Assert.Null (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Disabled_MenuBar_Is_Never_Opened () - { - Toplevel top = new (); - - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }) - ] - }; - top.Add (menu); - Application.Begin (top); - Assert.True (menu.Enabled); - menu.OpenMenu (); - Assert.True (menu.IsMenuOpen); - - menu.Enabled = false; - menu.CloseAllMenus (); - menu.OpenMenu (); - Assert.False (menu.IsMenuOpen); - top.Dispose (); - } - - [Fact (Skip = "#3798 Broke. Will fix in #2975")] - [AutoInitShutdown] - public void Disabled_MenuItem_Is_Never_Selected () - { - var menu = new MenuBar - { - Menus = - [ - new ( - "Menu", - new MenuItem [] - { - new ("Enabled 1", "", null), - new ("Disabled", "", null, () => false), - null, - new ("Enabled 2", "", null) - } - ) - ] - }; - - Toplevel top = new (); - top.Add (menu); - Application.Begin (top); - - Attribute [] attributes = - { - // 0 - menu.GetAttributeForRole(VisualRole.Normal), - - // 1 - menu.GetAttributeForRole(VisualRole.Focus), - - // 2 - menu.GetAttributeForRole(VisualRole.Disabled) - }; - - DriverAssert.AssertDriverAttributesAre ( - @" -00000000000000", - output, - Application.Driver, - attributes - ); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (0, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - top.Draw (); - - DriverAssert.AssertDriverAttributesAre ( - @" -11111100000000 -00000000000000 -01111111111110 -02222222222220 -00000000000000 -00000000000000 -00000000000000", - output, - Application.Driver, - attributes - ); - - Assert.True ( - top.SubViews.ElementAt (1) - .NewMouseEvent ( - new () { Position = new (0, 2), Flags = MouseFlags.Button1Clicked, View = top.SubViews.ElementAt (1) } - ) - ); - top.SubViews.ElementAt (1).Layout (); - top.SubViews.ElementAt (1).Draw (); - - DriverAssert.AssertDriverAttributesAre ( - @" -11111100000000 -00000000000000 -01111111111110 -02222222222220 -00000000000000 -00000000000000 -00000000000000", - output, - Application.Driver, - attributes - ); - - Assert.True ( - top.SubViews.ElementAt (1) - .NewMouseEvent ( - new () { Position = new (0, 2), Flags = MouseFlags.ReportMousePosition, View = top.SubViews.ElementAt (1) } - ) - ); - top.SubViews.ElementAt (1).Draw (); - - DriverAssert.AssertDriverAttributesAre ( - @" -11111100000000 -00000000000000 -01111111111110 -02222222222220 -00000000000000 -00000000000000 -00000000000000", - output, - Application.Driver, - attributes - ); - top.Dispose (); - } - - [Fact (Skip = "#3798 Broke. Will fix in #2975")] - [AutoInitShutdown] - public void Draw_A_Menu_Over_A_Dialog () - { - // Override CM - Window.DefaultBorderStyle = LineStyle.Single; - Dialog.DefaultButtonAlignment = Alignment.Center; - Dialog.DefaultBorderStyle = LineStyle.Single; - Dialog.DefaultShadow = ShadowStyle.None; - Button.DefaultShadow = ShadowStyle.None; - - Toplevel top = new (); - Window win = new (); - top.Add (win); - SessionToken rsTop = Application.Begin (top); - Application.Driver!.SetScreenSize (40, 15); - - Assert.Equal (new (0, 0, 40, 15), win.Frame); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - List items = new () - { - "New", - "Open", - "Close", - "Save", - "Save As", - "Delete" - }; - Dialog dialog = new () { X = 2, Y = 2, Width = 15, Height = 4 }; - MenuBar menu = new () { X = Pos.Center (), Width = 10 }; - - menu.Menus = new MenuBarItem [] - { - new ( - "File", - new MenuItem [] - { - new ( - items [0], - "Create a new file", - () => ChangeMenuTitle ("New"), - null, - null, - KeyCode.CtrlMask | KeyCode.N - ), - new ( - items [1], - "Open a file", - () => ChangeMenuTitle ("Open"), - null, - null, - KeyCode.CtrlMask | KeyCode.O - ), - new ( - items [2], - "Close a file", - () => ChangeMenuTitle ("Close"), - null, - null, - KeyCode.CtrlMask | KeyCode.C - ), - new ( - items [3], - "Save a file", - () => ChangeMenuTitle ("Save"), - null, - null, - KeyCode.CtrlMask | KeyCode.S - ), - new ( - items [4], - "Save a file as", - () => ChangeMenuTitle ("Save As"), - null, - null, - KeyCode.CtrlMask | KeyCode.A - ), - new ( - items [5], - "Delete a file", - () => ChangeMenuTitle ("Delete"), - null, - null, - KeyCode.CtrlMask | KeyCode.A - ) - } - ) - }; - dialog.Add (menu); - - void ChangeMenuTitle (string title) - { - menu.Menus [0].Title = title; - menu.SetNeedsDraw (); - } - - SessionToken rsDialog = Application.Begin (dialog); - AutoInitShutdownAttribute.RunIteration (); - - Assert.Equal (new (2, 2, 15, 4), dialog.Frame); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ │ -│ ┌─────────────┐ │ -│ │ File │ │ -│ │ │ │ -│ └─────────────┘ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.Equal ("File", menu.Menus [0].Title); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ │ -│ ┌─────────────┐ │ -│ │ File │ │ -│ │ ┌──────────────────────────────────┐ -│ └─│ New Create a new file Ctrl+N │ -│ │ Open Open a file Ctrl+O │ -│ │ Close Close a file Ctrl+C │ -│ │ Save Save a file Ctrl+S │ -│ │ Save As Save a file as Ctrl+A │ -│ │ Delete Delete a file Ctrl+A │ -│ └──────────────────────────────────┘ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked }); - - // Need to fool MainLoop into thinking it's running - AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (items [0], menu.Menus [0].Title); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ │ -│ ┌─────────────┐ │ -│ │ New │ │ -│ │ │ │ -│ └─────────────┘ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - for (var i = 0; i < items.Count; i++) - { - menu.OpenMenu (); - - Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked }); - - AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (items [i], menu.Menus [0].Title); - } - - Application.Driver!.SetScreenSize (20, 15); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────┐ -│ │ -│ ┌─────────────┐ │ -│ │ Delete │ │ -│ │ ┌─────────────── -│ └─│ New Create -│ │ Open O -│ │ Close Cl -│ │ Save S -│ │ Save As Save -│ │ Delete Del -│ └─────────────── -│ │ -│ │ -└──────────────────┘", - output - ); - - Application.End (rsDialog); - Application.End (rsTop); - top.Dispose (); - } - - [Fact (Skip = "#3798 Broke. Will fix in #2975")] - [AutoInitShutdown] - public void Draw_A_Menu_Over_A_Top_Dialog () - { - Application.Driver!.SetScreenSize (40, 15); - - // Override CM - Window.DefaultBorderStyle = LineStyle.Single; - Dialog.DefaultButtonAlignment = Alignment.Center; - Dialog.DefaultBorderStyle = LineStyle.Single; - Dialog.DefaultShadow = ShadowStyle.None; - Button.DefaultShadow = ShadowStyle.None; - - Assert.Equal (new (0, 0, 40, 15), Application.TopRunnable!.GetClip ()!.GetBounds ()); - DriverAssert.AssertDriverContentsWithFrameAre (@"", output); - - List items = new () - { - "New", - "Open", - "Close", - "Save", - "Save As", - "Delete" - }; - var dialog = new Dialog { X = 2, Y = 2, Width = 15, Height = 4 }; - var menu = new MenuBar { X = Pos.Center (), Width = 10 }; - - menu.Menus = new MenuBarItem [] - { - new ( - "File", - new MenuItem [] - { - new ( - items [0], - "Create a new file", - () => ChangeMenuTitle ("New"), - null, - null, - KeyCode.CtrlMask | KeyCode.N - ), - new ( - items [1], - "Open a file", - () => ChangeMenuTitle ("Open"), - null, - null, - KeyCode.CtrlMask | KeyCode.O - ), - new ( - items [2], - "Close a file", - () => ChangeMenuTitle ("Close"), - null, - null, - KeyCode.CtrlMask | KeyCode.C - ), - new ( - items [3], - "Save a file", - () => ChangeMenuTitle ("Save"), - null, - null, - KeyCode.CtrlMask | KeyCode.S - ), - new ( - items [4], - "Save a file as", - () => ChangeMenuTitle ("Save As"), - null, - null, - KeyCode.CtrlMask | KeyCode.A - ), - new ( - items [5], - "Delete a file", - () => ChangeMenuTitle ("Delete"), - null, - null, - KeyCode.CtrlMask | KeyCode.A - ) - } - ) - }; - dialog.Add (menu); - - void ChangeMenuTitle (string title) - { - menu.Menus [0].Title = title; - menu.SetNeedsDraw (); - } - - SessionToken rs = Application.Begin (dialog); - AutoInitShutdownAttribute.RunIteration (); - - Assert.Equal (new (2, 2, 15, 4), dialog.Frame); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - ┌─────────────┐ - │ File │ - │ │ - └─────────────┘", - output - ); - - Assert.Equal ("File", menu.Menus [0].Title); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - ┌─────────────┐ - │ File │ - │ ┌──────────────────────────────────┐ - └─│ New Create a new file Ctrl+N │ - │ Open Open a file Ctrl+O │ - │ Close Close a file Ctrl+C │ - │ Save Save a file Ctrl+S │ - │ Save As Save a file as Ctrl+A │ - │ Delete Delete a file Ctrl+A │ - └──────────────────────────────────┘", - output - ); - - Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5), Flags = MouseFlags.Button1Clicked }); - - // Need to fool MainLoop into thinking it's running - AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (items [0], menu.Menus [0].Title); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - ┌─────────────┐ - │ New │ - │ │ - └─────────────┘", - output - ); - - for (var i = 1; i < items.Count; i++) - { - menu.OpenMenu (); - - Application.RaiseMouseEvent (new () { ScreenPosition = new (20, 5 + i), Flags = MouseFlags.Button1Clicked }); - - AutoInitShutdownAttribute.RunIteration (); - Assert.Equal (items [i], menu.Menus [0].Title); - } - - Application.Driver!.SetScreenSize (20, 15); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - ┌─────────────┐ - │ Delete │ - │ ┌─────────────── - └─│ New Create - │ Open O - │ Close Cl - │ Save S - │ Save As Save - │ Delete Del - └───────────────", - output - ); - - Application.End (rs); - dialog.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void DrawFrame_With_Negative_Positions () - { - var menu = new MenuBar - { - X = -1, - Y = -1, - Menus = - [ - new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) }) - ] - }; - menu.Layout (); - - Assert.Equal (new (-1, -1), new Point (menu.Frame.X, menu.Frame.Y)); - - Toplevel top = new (); - Application.Begin (top); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - var expected = @" -──────┐ - One │ - Two │ -──────┘ -"; - - Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (0, 0, 7, 4), pos); - - menu.CloseAllMenus (); - menu.Frame = new (-1, -2, menu.Frame.Width, menu.Frame.Height); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" - One │ - Two │ -──────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 7, 3), pos); - - menu.CloseAllMenus (); - menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - Application.Driver!.SetScreenSize (7, 5); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" -┌────── -│ One -│ Two -└────── -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (0, 1, 7, 4), pos); - - menu.CloseAllMenus (); - menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - Application.Driver!.SetScreenSize (7, 3); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" -┌────── -│ One -│ Two -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (0, 0, 7, 3), pos); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void DrawFrame_With_Negative_Positions_Disabled_Border () - { - var menu = new MenuBar - { - X = -2, - Y = -1, - MenusBorderStyle = LineStyle.None, - Menus = - [ - new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) }) - ] - }; - menu.Layout (); - - Assert.Equal (new (-2, -1), new Point (menu.Frame.X, menu.Frame.Y)); - - Toplevel top = new (); - Application.Begin (top); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - var expected = @" -ne -wo -"; - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - menu.CloseAllMenus (); - menu.Frame = new (-2, -2, menu.Frame.Width, menu.Frame.Height); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" -wo -"; - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - menu.CloseAllMenus (); - menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - Application.Driver!.SetScreenSize (3, 2); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" - On - Tw -"; - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - menu.CloseAllMenus (); - menu.Frame = new (0, 0, menu.Frame.Width, menu.Frame.Height); - Application.Driver!.SetScreenSize (3, 1); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - expected = @" - On -"; - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void DrawFrame_With_Positive_Positions () - { - var menu = new MenuBar - { - Menus = - [ - new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) }) - ] - }; - - Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y)); - - Toplevel top = new (); - Application.Begin (top); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - var expected = @" -┌──────┐ -│ One │ -│ Two │ -└──────┘ -"; - - Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (0, 1, 8, 4), pos); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void DrawFrame_With_Positive_Positions_Disabled_Border () - { - var menu = new MenuBar - { - MenusBorderStyle = LineStyle.None, - Menus = - [ - new (new MenuItem [] { new ("One", "", null), new ("Two", "", null) }) - ] - }; - - Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y)); - - Toplevel top = new (); - Application.Begin (top); - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - var expected = @" - One - Two -"; - - _ = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void Exceptions () - { - Assert.Throws (() => new MenuBarItem ("Test", (MenuItem [])null)); - Assert.Throws (() => new MenuBarItem ("Test", (List)null)); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void HotKey_MenuBar_OnKeyDown_OnKeyUp_ProcessKeyPressed () - { - var newAction = false; - var copyAction = false; - - var menu = new MenuBar - { - Menus = - [ - new ("_File", new MenuItem [] { new ("_New", "", () => newAction = true) }), - new ( - "_Edit", - new MenuItem [] { new ("_Copy", "", () => copyAction = true) } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.False (newAction); - Assert.False (copyAction); - -#if SUPPORT_ALT_TO_ACTIVATE_MENU - Assert.False (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.False (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.TopRunnable.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); - Assert.True (menu.IsMenuOpen); - Application.TopRunnable.Draw (); - - string expected = @" - File Edit -"; - - var pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 11, 1), pos); - - Assert.True (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.N))); - AutoInitShutdownAttribute.RunIteration (); - Assert.False (newAction); // not yet, hot keys don't work if the item is not visible - - Assert.True (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.F))); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.N))); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (newAction); - Application.TopRunnable.Draw (); - - expected = @" - File Edit -"; - - Assert.False (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.TopRunnable.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); - Assert.True (Application.TopRunnable.ProcessKeyUp (new KeyEventArgs (Key.AltMask))); - Assert.True (menu.IsMenuOpen); - Application.TopRunnable.Draw (); - - expected = @" - File Edit -"; - - pos = DriverAsserts.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 11, 1), pos); - - Assert.True (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.CursorRight))); - Assert.True (Application.TopRunnable.ProcessKeyDown (new KeyEventArgs (Key.C))); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (copyAction); -#endif - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void HotKey_MenuBar_ProcessKeyPressed_Menu_ProcessKey () - { - var newAction = false; - var copyAction = false; - - // Define the expected menu - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ( - "Edit", - new MenuItem [] { new ("Copy", "", null) } - ) - ] - }; - - // The real menu - var menu = new MenuBar - { - Menus = - [ - new ( - "_" + expectedMenu.Menus [0].Title, - new MenuItem [] - { - new ( - "_" + expectedMenu.Menus [0].Children [0].Title, - "", - () => newAction = true - ) - } - ), - new ( - "_" + expectedMenu.Menus [1].Title, - new MenuItem [] - { - new ( - "_" - + expectedMenu.Menus [1] - .Children [0] - .Title, - "", - () => copyAction = true - ) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.False (newAction); - Assert.False (copyAction); - - Assert.True (menu.NewKeyDownEvent (Key.F.WithAlt)); - Assert.True (menu.IsMenuOpen); - Application.TopRunnable.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - Assert.True (Application.TopRunnable.SubViews.ElementAt (1).NewKeyDownEvent (Key.N)); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (newAction); - - Assert.True (menu.NewKeyDownEvent (Key.E.WithAlt)); - Assert.True (menu.IsMenuOpen); - Application.TopRunnable.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - Assert.True (Application.TopRunnable.SubViews.ElementAt (1).NewKeyDownEvent (Key.C)); - AutoInitShutdownAttribute.RunIteration (); - Assert.True (copyAction); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Key_Open_And_Close_The_MenuBar () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }) - ] - }; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (top.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - Assert.True (top.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - - menu.Key = Key.F10.WithShift; - Assert.False (top.NewKeyDownEvent (Key.F9)); - Assert.False (menu.IsMenuOpen); - - Assert.True (top.NewKeyDownEvent (Key.F10.WithShift)); - Assert.True (menu.IsMenuOpen); - Assert.True (top.NewKeyDownEvent (Key.F10.WithShift)); - Assert.False (menu.IsMenuOpen); - top.Dispose (); - } - - [Theory (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - [InlineData ("_File", "_New", "", KeyCode.Space | KeyCode.CtrlMask)] - [InlineData ("Closed", "None", "", KeyCode.Space | KeyCode.CtrlMask, KeyCode.Space | KeyCode.CtrlMask)] - [InlineData ("_File", "_New", "", KeyCode.F9)] - [InlineData ("Closed", "None", "", KeyCode.F9, KeyCode.F9)] - [InlineData ("_File", "_Open", "", KeyCode.F9, KeyCode.CursorDown)] - [InlineData ("_File", "_Save", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown)] - [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorDown, KeyCode.CursorDown)] - [InlineData ( - "_File", - "_New", - "", - KeyCode.F9, - KeyCode.CursorDown, - KeyCode.CursorDown, - KeyCode.CursorDown, - KeyCode.CursorDown - )] - [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorDown, KeyCode.CursorUp)] - [InlineData ("_File", "_Quit", "", KeyCode.F9, KeyCode.CursorUp)] - [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorUp, KeyCode.CursorDown)] - [InlineData ("Closed", "None", "Open", KeyCode.F9, KeyCode.CursorDown, KeyCode.Enter)] - [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorRight)] - [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorLeft)] - [InlineData ("_Edit", "_Copy", "", KeyCode.F9, KeyCode.CursorLeft, KeyCode.CursorLeft)] - [InlineData ("_Edit", "_Select All", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorUp)] - [InlineData ("_File", "_New", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorDown, KeyCode.CursorLeft)] - [InlineData ("_About", "_About", "", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight)] - [InlineData ("Closed", "None", "New", KeyCode.F9, KeyCode.Enter)] - [InlineData ("Closed", "None", "Quit", KeyCode.F9, KeyCode.CursorUp, KeyCode.Enter)] - [InlineData ("Closed", "None", "Copy", KeyCode.F9, KeyCode.CursorRight, KeyCode.Enter)] - [InlineData ( - "Closed", - "None", - "Find", - KeyCode.F9, - KeyCode.CursorRight, - KeyCode.CursorUp, - KeyCode.CursorUp, - KeyCode.Enter - )] - [InlineData ( - "Closed", - "None", - "Replace", - KeyCode.F9, - KeyCode.CursorRight, - KeyCode.CursorUp, - KeyCode.CursorUp, - KeyCode.CursorDown, - KeyCode.Enter - )] - [InlineData ( - "_Edit", - "F_ind", - "", - KeyCode.F9, - KeyCode.CursorRight, - KeyCode.CursorUp, - KeyCode.CursorUp, - KeyCode.CursorLeft, - KeyCode.Enter - )] - [InlineData ("Closed", "None", "About", KeyCode.F9, KeyCode.CursorRight, KeyCode.CursorRight, KeyCode.Enter)] - - //// Hotkeys - [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F)] - [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.ShiftMask | KeyCode.F)] - [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.Esc)] - [InlineData ("Closed", "None", "", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.F)] - [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.O)] - [InlineData ("_File", "_New", "", KeyCode.AltMask | KeyCode.F, KeyCode.ShiftMask | KeyCode.O)] - [InlineData ("Closed", "None", "Open", KeyCode.AltMask | KeyCode.F, KeyCode.AltMask | KeyCode.O)] - [InlineData ("_Edit", "_Copy", "", KeyCode.AltMask | KeyCode.E)] - [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.F)] - [InlineData ("_Edit", "F_ind", "", KeyCode.AltMask | KeyCode.E, KeyCode.AltMask | KeyCode.F)] - [InlineData ("Closed", "None", "Replace", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.R)] - [InlineData ("Closed", "None", "Copy", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.C)] - [InlineData ("_Edit", "_1st", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3)] - [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D1)] - [InlineData ("Closed", "None", "1", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.Enter)] - [InlineData ("Closed", "None", "2", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D2)] - [InlineData ("_Edit", "_5th", "", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D3, KeyCode.D4)] - [InlineData ("Closed", "None", "5", KeyCode.AltMask | KeyCode.E, KeyCode.F, KeyCode.D4, KeyCode.D5)] - [InlineData ("Closed", "None", "About", KeyCode.AltMask | KeyCode.A)] - public void KeyBindings_Navigation_Commands ( - string expectedBarTitle, - string expectedItemTitle, - string expectedAction, - params KeyCode [] keys - ) - { - var miAction = ""; - MenuItem mbiCurrent = null; - MenuItem miCurrent = null; - - var menu = new MenuBar (); - - Func fn = s => - { - miAction = s as string; - - return true; - }; - menu.EnableForDesign (ref fn); - - menu.Key = KeyCode.F9; - menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu; - menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; }; - - menu.MenuClosing += (s, e) => - { - mbiCurrent = null; - miCurrent = null; - }; - menu.UseKeysUpDownAsKeysLeftRight = true; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - foreach (Key key in keys) - { - top.NewKeyDownEvent (key); - AutoInitShutdownAttribute.RunIteration (); - } - - Assert.Equal (expectedBarTitle, mbiCurrent != null ? mbiCurrent.Title : "Closed"); - Assert.Equal (expectedItemTitle, miCurrent != null ? miCurrent.Title : "None"); - Assert.Equal (expectedAction, miAction); - top.Dispose (); - } - - [Theory (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - [InlineData ("New", KeyCode.CtrlMask | KeyCode.N)] - [InlineData ("Quit", KeyCode.CtrlMask | KeyCode.Q)] - [InlineData ("Copy", KeyCode.CtrlMask | KeyCode.C)] - [InlineData ("Replace", KeyCode.CtrlMask | KeyCode.H)] - [InlineData ("1", KeyCode.F1)] - [InlineData ("5", KeyCode.CtrlMask | KeyCode.D5)] - public void KeyBindings_Shortcut_Commands (string expectedAction, params KeyCode [] keys) - { - var miAction = ""; - MenuItem mbiCurrent = null; - MenuItem miCurrent = null; - - var menu = new MenuBar (); - - bool FnAction (string s) - { - miAction = s; - - return true; - } - - // Declare a variable for the function - Func fnActionVariable = FnAction; - - menu.EnableForDesign (ref fnActionVariable); - - menu.Key = KeyCode.F9; - menu.MenuOpening += (s, e) => mbiCurrent = e.CurrentMenu; - menu.MenuOpened += (s, e) => { miCurrent = e.MenuItem; }; - - menu.MenuClosing += (s, e) => - { - mbiCurrent = null; - miCurrent = null; - }; - menu.UseKeysUpDownAsKeysLeftRight = true; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - foreach (KeyCode key in keys) - { - Assert.True (top.NewKeyDownEvent (new (key))); - AutoInitShutdownAttribute.RunIteration (); - } - - Assert.Equal (expectedAction, miAction); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Menu_With_Separator () - { - var menu = new MenuBar - { - Menus = - [ - new ( - "File", - new MenuItem [] - { - new ( - "_Open", - "Open a file", - () => { }, - null, - null, - KeyCode.CtrlMask | KeyCode.O - ), - null, - new ("_Quit", "", null) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - File -┌────────────────────────────┐ -│ Open Open a file Ctrl+O │ -├────────────────────────────┤ -│ Quit │ -└────────────────────────────┘", - output - ); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Menu_With_Separator_Disabled_Border () - { - var menu = new MenuBar - { - MenusBorderStyle = LineStyle.None, - Menus = - [ - new ( - "File", - new MenuItem [] - { - new ( - "_Open", - "Open a file", - () => { }, - null, - null, - KeyCode.CtrlMask | KeyCode.O - ), - null, - new ("_Quit", "", null) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - menu.OpenMenu (); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - File - Open Open a file Ctrl+O -──────────────────────────── - Quit ", - output - ); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_ButtonPressed_Open_The_Menu_ButtonPressed_Again_Close_The_Menu () - { - // Define the expected menu - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("Open", "", null) }), - new ( - "Edit", - new MenuItem [] { new ("Copy", "", null) } - ) - ] - }; - - // Test without HotKeys first - var menu = new MenuBar - { - Menus = - [ - new ( - "_" + expectedMenu.Menus [0].Title, - new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) } - ), - new ( - "_" + expectedMenu.Menus [1].Title, - new MenuItem [] - { - new ( - "_" - + expectedMenu.Menus [1] - .Children [0] - .Title, - "", - null - ) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu })); - Assert.True (menu.IsMenuOpen); - top.Draw (); - - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu })); - Assert.False (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_In_Window_Without_Other_Views_With_Top_Init () - { - var win = new Window (); - - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ( - "Edit", - new MenuItem [] - { - new MenuBarItem ( - "Delete", - new MenuItem [] - { new ("All", "", null), new ("Selected", "", null) } - ) - } - ) - ] - }; - win.Add (menu); - Toplevel top = new (); - top.Add (win); - Application.Begin (top); - Application.Driver!.SetScreenSize (40, 8); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (win.NewKeyDownEvent (menu.Key)); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu.NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│ │ -│ └─────────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│┌───────────┐ │ -│ └─────────┘│ All │ │ -│ │ Selected │ │ -│ └───────────┘ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - top.SetClipToScreen (); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_In_Window_Without_Other_Views_With_Top_Init_With_Parameterless_Run () - { - var win = new Window (); - - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ( - "Edit", - new MenuItem [] - { - new MenuBarItem ( - "Delete", - new MenuItem [] - { new ("All", "", null), new ("Selected", "", null) } - ) - } - ) - ] - }; - win.Add (menu); - Toplevel top = new (); - top.Add (win); - - Application.AddTimeout (TimeSpan.Zero, () => - { - Application.Driver!.SetScreenSize (40, 8); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (win.NewKeyDownEvent (menu.Key)); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu.NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│ │ -│ └─────────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│┌───────────┐ │ -│ └─────────┘│ All │ │ -│ │ Selected │ │ -│ └───────────┘ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - top.SetClipToScreen (); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Application.RequestStop (); - - return false; - }); - - Application.Run (top); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init () - { - var win = new Window (); - - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ( - "Edit", - new MenuItem [] - { - new MenuBarItem ( - "Delete", - new MenuItem [] - { new ("All", "", null), new ("Selected", "", null) } - ) - } - ) - ] - }; - win.Add (menu); - Application.Driver!.SetScreenSize (40, 8); - SessionToken rs = Application.Begin (win); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (win.NewKeyDownEvent (menu.Key)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu.NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│ │ -│ └─────────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│┌───────────┐ │ -│ └─────────┘│ All │ │ -│ │ Selected │ │ -│ └───────────┘ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - win.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_In_Window_Without_Other_Views_Without_Top_Init_With_Run_T () - { - Application.Driver!.SetScreenSize (40, 8); - - Application.AddTimeout (TimeSpan.Zero, () => - { - Toplevel top = Application.TopRunnable; - - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ │ -│ │ -│ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (top.NewKeyDownEvent (Key.F9)); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True (top.SubViews.ElementAt (0).NewKeyDownEvent (Key.CursorRight)); - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│ │ -│ └─────────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True ( - ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight) - ); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│ ┌─────────┐ │ -│ │ Delete ►│┌───────────┐ │ -│ └─────────┘│ All │ │ -│ │ Selected │ │ -│ └───────────┘ │ -└──────────────────────────────────────┘", - output - ); - - Assert.True ( - ((MenuBar)top.SubViews.ElementAt (0))._openMenu.NewKeyDownEvent (Key.CursorRight) - ); - top.SetClipToScreen (); - top.Draw (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌──────┐ │ -││ New │ │ -│└──────┘ │ -│ │ -│ │ -└──────────────────────────────────────┘", - output - ); - - Application.RequestStop (); - - return false; - }); - - Application.Run ().Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_Position_And_Size_With_HotKeys_Is_The_Same_As_Without_HotKeys () - { - // Define the expected menu - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("12", "", null) }), - new ( - "Edit", - new MenuItem [] { new ("Copy", "", null) } - ) - ] - }; - - // Test without HotKeys first - var menu = new MenuBar - { - Menus = - [ - new ( - expectedMenu.Menus [0].Title, - new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) } - ), - new ( - expectedMenu.Menus [1].Title, - new MenuItem [] - { - new ( - expectedMenu.Menus [1].Children [0].Title, - "", - null - ) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - // Open first - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - // Open second - Assert.True (Application.TopRunnable.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight)); - Assert.True (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - // Close menu - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - - top.Remove (menu); - - // Now test WITH HotKeys - menu = new () - { - Menus = - [ - new ( - "_" + expectedMenu.Menus [0].Title, - new MenuItem [] { new ("_" + expectedMenu.Menus [0].Children [0].Title, "", null) } - ), - new ( - "_" + expectedMenu.Menus [1].Title, - new MenuItem [] - { - new ( - "_" + expectedMenu.Menus [1].Children [0].Title, - "", - null - ) - } - ) - ] - }; - - top.Add (menu); - - // Open first - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - // Open second - Assert.True (top.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorRight)); - Assert.True (menu.IsMenuOpen); - top.SetClipToScreen (); - Application.TopRunnable.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - // Close menu - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_Submenus_Alignment_Correct () - { - // Define the expected menu - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ( - "File", - new MenuItem [] - { - new ( - "Really Long Sub Menu", - "", - null - ) - } - ), - new ( - "123", - new MenuItem [] { new ("Copy", "", null) } - ), - new ( - "Format", - new MenuItem [] { new ("Word Wrap", "", null) } - ), - new ( - "Help", - new MenuItem [] { new ("About", "", null) } - ), - new ( - "1", - new MenuItem [] { new ("2", "", null) } - ), - new ( - "3", - new MenuItem [] { new ("2", "", null) } - ), - new ( - "Last one", - new MenuItem [] { new ("Test", "", null) } - ) - ] - }; - - MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length]; - - for (var i = 0; i < expectedMenu.Menus.Length; i++) - { - items [i] = new ( - expectedMenu.Menus [i].Title, - new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) } - ); - } - - var menu = new MenuBar { Menus = items }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - - for (var i = 0; i < expectedMenu.Menus.Length; i++) - { - menu.OpenMenu (i); - Assert.True (menu.IsMenuOpen); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (i), output); - } - - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBar_With_Action_But_Without_MenuItems_Not_Throw () - { - var menu = new MenuBar - { - Menus = - [ - new () { Title = "Test 1", Action = () => { } }, - - new () { Title = "Test 2", Action = () => { } } - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - -#if SUPPORT_ALT_TO_ACTIVATE_MENU - Assert.True ( - Application.OnKeyUp ( - new KeyEventArgs ( - Key.AltMask - ) - ) - ); // changed to true because Alt activates menu bar -#endif - Assert.True (menu.NewKeyDownEvent (Key.CursorRight)); - Assert.True (menu.NewKeyDownEvent (Key.CursorRight)); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuBarItem_Children_Null_Does_Not_Throw () - { - var menu = new MenuBar - { - Menus = - [ - new ("Test", "", null) - ] - }; - var top = new Toplevel (); - top.Add (menu); - - Exception exception = Record.Exception (() => menu.NewKeyDownEvent (Key.Space)); - Assert.Null (exception); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuOpened_On_Disabled_MenuItem () - { - MenuItem parent = null; - MenuItem miCurrent = null; - Menu mCurrent = null; - - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new MenuBarItem ( - "_New", - new MenuItem [] - { - new ( - "_New doc", - "Creates new doc.", - null, - () => false - ) - } - ), - null, - new ("_Save", "Saves the file.", null) - } - ) - ] - }; - - menu.MenuOpened += (s, e) => - { - parent = e.Parent; - miCurrent = e.MenuItem; - mCurrent = menu._openMenu; - }; - menu.UseKeysUpDownAsKeysLeftRight = true; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - // open the menu - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Equal ("_File", miCurrent.Parent.Title); - Assert.Equal ("_New", miCurrent.Title); - - Assert.True ( - mCurrent.NewMouseEvent ( - new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Equal ("_File", miCurrent.Parent.Title); - Assert.Equal ("_New", miCurrent.Title); - - Assert.True ( - mCurrent.NewMouseEvent ( - new () { Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = mCurrent } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Equal ("_File", miCurrent.Parent.Title); - Assert.Equal ("_New", miCurrent.Title); - - Assert.True ( - mCurrent.NewMouseEvent ( - new () { Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Equal ("_File", miCurrent.Parent.Title); - Assert.Equal ("_Save", miCurrent.Title); - - // close the menu - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ) - ); - Assert.False (menu.IsMenuOpen); - - // open the menu - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - - // The _New doc is enabled but the sub-menu isn't enabled. Is show but can't be selected and executed - Assert.Equal ("_New", parent.Title); - Assert.Equal ("_New", miCurrent.Parent.Title); - Assert.Equal ("_New doc", miCurrent.Title); - - Assert.True (mCurrent.NewKeyDownEvent (Key.CursorDown)); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Equal ("_File", miCurrent.Parent.Title); - Assert.Equal ("_Save", miCurrent.Title); - - Assert.True (mCurrent.NewKeyDownEvent (Key.CursorUp)); - Assert.True (menu.IsMenuOpen); - Assert.Equal ("_File", parent.Title); - Assert.Null (miCurrent); - - // close the menu - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void MenuOpening_MenuOpened_MenuClosing_Events () - { - var miAction = ""; - var isMenuClosed = true; - var cancelClosing = false; - - var menu = new MenuBar - { - Menus = - [ - new ("_File", new MenuItem [] { new ("_New", "Creates new file.", New) }) - ] - }; - - menu.MenuOpening += (s, e) => - { - Assert.Equal ("_File", e.CurrentMenu.Title); - Assert.Equal ("_New", e.CurrentMenu.Children [0].Title); - Assert.Equal ("Creates new file.", e.CurrentMenu.Children [0].Help); - Assert.Equal (New, e.CurrentMenu.Children [0].Action); - e.CurrentMenu.Children [0].Action (); - Assert.Equal ("New", miAction); - - e.NewMenuBarItem = new ( - "_Edit", - new MenuItem [] { new ("_Copy", "Copies the selection.", Copy) } - ); - }; - - menu.MenuOpened += (s, e) => - { - MenuItem mi = e.MenuItem; - - Assert.Equal ("_Edit", mi.Parent.Title); - Assert.Equal ("_Copy", mi.Title); - Assert.Equal ("Copies the selection.", mi.Help); - Assert.Equal (Copy, mi.Action); - mi.Action (); - Assert.Equal ("Copy", miAction); - }; - - menu.MenuClosing += (s, e) => - { - Assert.False (isMenuClosed); - - if (cancelClosing) - { - e.Cancel = true; - isMenuClosed = false; - } - else - { - isMenuClosed = true; - } - }; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - isMenuClosed = !menu.IsMenuOpen; - Assert.False (isMenuClosed); - top.Draw (); - - var expected = @" -Edit -┌──────────────────────────────┐ -│ Copy Copies the selection. │ -└──────────────────────────────┘ -"; - DriverAssert.AssertDriverContentsAre (expected, output); - - cancelClosing = true; - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - Assert.False (isMenuClosed); - top.SetClipToScreen (); - top.Draw (); - - expected = @" -Edit -┌──────────────────────────────┐ -│ Copy Copies the selection. │ -└──────────────────────────────┘ -"; - DriverAssert.AssertDriverContentsAre (expected, output); - - cancelClosing = false; - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - Assert.True (isMenuClosed); - top.SetClipToScreen (); - top.Draw (); - - expected = @" -Edit -"; - DriverAssert.AssertDriverContentsAre (expected, output); - - void New () { miAction = "New"; } - - void Copy () { miAction = "Copy"; } - - top.Dispose (); - } - - [Fact (Skip = "See Issue #4370. Not gonna try to fix menu v1.")] - [AutoInitShutdown] - public void MouseEvent_Test () - { - MenuItem miCurrent = null; - Menu mCurrent = null; - - var menuBar = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] { new ("_New", "", null), new ("_Open", "", null), new ("_Save", "", null) } - ), - new ( - "_Edit", - new MenuItem [] { new ("_Copy", "", null), new ("C_ut", "", null), new ("_Paste", "", null) } - ) - ] - }; - - menuBar.MenuOpened += (s, e) => - { - miCurrent = e.MenuItem; - mCurrent = menuBar.OpenCurrentMenu; - }; - var top = new Toplevel (); - top.Add (menuBar); - Application.Begin (top); - - // Click on Edit - Assert.True ( - menuBar.NewMouseEvent ( - new () { Position = new (10, 0), Flags = MouseFlags.Button1Pressed, View = menuBar } - ) - ); - Assert.True (menuBar.IsMenuOpen); - Assert.Equal ("_Edit", miCurrent.Parent.Title); - Assert.Equal ("_Copy", miCurrent.Title); - - // Click on Paste - Assert.True ( - mCurrent.NewMouseEvent ( - new () { Position = new (10, 2), Flags = MouseFlags.ReportMousePosition, View = mCurrent } - ) - ); - Assert.True (menuBar.IsMenuOpen); - Assert.Equal ("_Edit", miCurrent.Parent.Title); - Assert.Equal ("_Paste", miCurrent.Title); - - for (var i = 4; i >= -1; i--) - { - Application.RaiseMouseEvent ( - new () { ScreenPosition = new (10, i), Flags = MouseFlags.ReportMousePosition } - ); - - Assert.True (menuBar.IsMenuOpen); - Menu menu = (Menu)top.SubViews.First (v => v is Menu); - - if (i is < 0 or > 0) - { - Assert.Equal (menu, Application.Mouse.MouseGrabView); - } - else - { - Assert.Equal (menuBar, Application.Mouse.MouseGrabView); - } - - Assert.Equal ("_Edit", miCurrent.Parent.Title); - - if (i == 4) - { - Assert.Equal ("_Paste", miCurrent.Title); - } - else if (i == 3) - { - Assert.Equal ("C_ut", miCurrent.Title); - } - else if (i == 2) - { - Assert.Equal ("_Copy", miCurrent.Title); - } - else - { - Assert.Equal ("_Copy", miCurrent.Title); - } - } - - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Keyboard () - { - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ("Edit", Array.Empty ()), - new ( - "Format", - new MenuItem [] { new ("Wrap", "", null) } - ) - ] - }; - - MenuBarItem [] items = new MenuBarItem [expectedMenu.Menus.Length]; - - for (var i = 0; i < expectedMenu.Menus.Length; i++) - { - items [i] = new ( - expectedMenu.Menus [i].Title, - expectedMenu.Menus [i].Children.Length > 0 - ? new MenuItem [] { new (expectedMenu.Menus [i].Children [0].Title, "", null) } - : Array.Empty () - ); - } - - var menu = new MenuBar { Menus = items }; - - var tf = new TextField { Y = 2, Width = 10 }; - var top = new Toplevel (); - top.Add (menu, tf); - - Application.Begin (top); - Assert.True (tf.HasFocus); - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - // Right - Edit has no sub menu; this tests that no sub menu shows - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - Assert.Equal (1, menu._selected); - Assert.Equal (-1, menu._selectedSub); - Assert.Null (menu._openSubMenu); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - // Right - Format - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorRight)); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output); - - // Left - Edit - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft)); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - Assert.True (menu._openMenu.NewKeyDownEvent (Key.CursorLeft)); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - Assert.True (Application.RaiseKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - Assert.True (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Parent_MenuItem_Stay_Focused_If_Child_MenuItem_Is_Empty_By_Mouse () - { - // File Edit Format - //┌──────┐ ┌───────┐ - //│ New │ │ Wrap │ - //└──────┘ └───────┘ - - // Define the expected menu - var expectedMenu = new ExpectedMenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ("Edit", new MenuItem [] { }), - new ( - "Format", - new MenuItem [] { new ("Wrap", "", null) } - ) - ] - }; - - var menu = new MenuBar - { - Menus = - [ - new ( - expectedMenu.Menus [0].Title, - new MenuItem [] { new (expectedMenu.Menus [0].Children [0].Title, "", null) } - ), - new (expectedMenu.Menus [1].Title, new MenuItem [] { }), - new ( - expectedMenu.Menus [2].Title, - new MenuItem [] - { - new ( - expectedMenu.Menus [2].Children [0].Title, - "", - null - ) - } - ) - ] - }; - - var tf = new TextField { Y = 2, Width = 10 }; - var top = new Toplevel (); - top.Add (menu, tf); - Application.Begin (top); - - Assert.True (tf.HasFocus); - Assert.True (menu.NewMouseEvent (new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu })); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (1), output); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (15, 0), Flags = MouseFlags.ReportMousePosition, View = menu } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (2), output); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (8, 0), Flags = MouseFlags.ReportMousePosition, View = menu } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - - Assert.True ( - menu.NewMouseEvent ( - new () { Position = new (1, 0), Flags = MouseFlags.ReportMousePosition, View = menu } - ) - ); - Assert.True (menu.IsMenuOpen); - Assert.False (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ExpectedSubMenuOpen (0), output); - - Assert.True (menu.NewMouseEvent (new () { Position = new (8, 0), Flags = MouseFlags.Button1Pressed, View = menu })); - Assert.False (menu.IsMenuOpen); - Assert.True (tf.HasFocus); - top.SetClipToScreen (); - top.Draw (); - DriverAssert.AssertDriverContentsAre (expectedMenu.ClosedMenuText, output); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void RemoveAndThenAddMenuBar_ShouldNotChangeWidth () - { - MenuBar menuBar; - MenuBar menuBar2; - - // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, - // TODO: Change this to Window - var w = new View (); - menuBar2 = new (); - menuBar = new (); - w.Width = Dim.Fill (); - w.Height = Dim.Fill (); - w.X = 0; - w.Y = 0; - - w.Visible = true; - - // TODO: When https: //github.com/gui-cs/Terminal.Gui/issues/3136 is fixed, - // TODO: uncomment this. - //w.Modal = false; - w.Title = ""; - menuBar.Width = Dim.Fill (); - menuBar.Height = 1; - menuBar.X = 0; - menuBar.Y = 0; - menuBar.Visible = true; - w.Add (menuBar); - - menuBar2.Width = Dim.Fill (); - menuBar2.Height = 1; - menuBar2.X = 0; - menuBar2.Y = 4; - menuBar2.Visible = true; - w.Add (menuBar2); - - MenuBar [] menuBars = w.SubViews.OfType ().ToArray (); - Assert.Equal (2, menuBars.Length); - - Assert.Equal (Dim.Fill (), menuBars [0].Width); - Assert.Equal (Dim.Fill (), menuBars [1].Width); - - // Goes wrong here - w.Remove (menuBar); - w.Remove (menuBar2); - - w.Add (menuBar); - w.Add (menuBar2); - - // These assertions fail - Assert.Equal (Dim.Fill (), menuBars [0].Width); - Assert.Equal (Dim.Fill (), menuBars [1].Width); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Resizing_Close_Menus () - { - var menu = new MenuBar - { - Menus = - [ - new ( - "File", - new MenuItem [] - { - new ( - "Open", - "Open a file", - () => { }, - null, - null, - KeyCode.CtrlMask | KeyCode.O - ) - } - ) - ] - }; - var top = new Toplevel (); - top.Add (menu); - SessionToken rs = Application.Begin (top); - - menu.OpenMenu (); - - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - File -┌────────────────────────────┐ -│ Open Open a file Ctrl+O │ -└────────────────────────────┘", - output - ); - - Application.Driver!.SetScreenSize (20, 15); - - AutoInitShutdownAttribute.RunIteration (); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" - File", - output - ); - - Application.End (rs); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void Separator_Does_Not_Throws_Pressing_Menu_Hotkey () - { - var menu = new MenuBar - { - Menus = - [ - new ( - "File", - new MenuItem [] { new ("_New", "", null), null, new ("_Quit", "", null) } - ) - ] - }; - Assert.False (menu.NewKeyDownEvent (Key.Q.WithAlt)); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void SetMenus_With_Same_HotKey_Does_Not_Throws () - { - var mb = new MenuBar (); - - var i1 = new MenuBarItem ("_heey", "fff", () => { }, () => true); - - mb.Menus = new [] { i1 }; - mb.Menus = new [] { i1 }; - - Assert.Equal (Key.H, mb.Menus [0].HotKey); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void ShortCut_Activates () - { - var saveAction = false; - - var menu = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ( - "_Save", - "Saves the file.", - () => { saveAction = true; }, - null, - null, - (KeyCode)Key.S.WithCtrl - ) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Application.RaiseKeyDownEvent (Key.S.WithCtrl); - AutoInitShutdownAttribute.RunIteration (); - - Assert.True (saveAction); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void Update_ShortcutKey_KeyBindings_Old_ShortcutKey_Is_Removed () - { - var menuBar = new MenuBar - { - Menus = - [ - new ( - "_File", - new MenuItem [] - { - new ("New", "Create New", null, null, null, Key.A.WithCtrl) - } - ) - ] - }; - - Assert.True (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _)); - - menuBar.Menus [0].Children! [0].ShortcutKey = Key.B.WithCtrl; - - Assert.False (menuBar.HotKeyBindings.TryGet (Key.A.WithCtrl, out _)); - Assert.True (menuBar.HotKeyBindings.TryGet (Key.B.WithCtrl, out _)); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - public void UseKeysUpDownAsKeysLeftRight_And_UseSubMenusSingleFrame_Cannot_Be_Both_True () - { - var menu = new MenuBar (); - Assert.False (menu.UseKeysUpDownAsKeysLeftRight); - Assert.False (menu.UseSubMenusSingleFrame); - - menu.UseKeysUpDownAsKeysLeftRight = true; - Assert.True (menu.UseKeysUpDownAsKeysLeftRight); - Assert.False (menu.UseSubMenusSingleFrame); - - menu.UseSubMenusSingleFrame = true; - Assert.False (menu.UseKeysUpDownAsKeysLeftRight); - Assert.True (menu.UseSubMenusSingleFrame); - } - - [Fact (Skip = "#3798 Broke. Will fix in #2975")] - [AutoInitShutdown] - public void UseSubMenusSingleFrame_False_By_Keyboard () - { - var menu = new MenuBar - { - Menus = new MenuBarItem [] - { - new ( - "Numbers", - new MenuItem [] - { - new ("One", "", null), - new MenuBarItem ( - "Two", - new MenuItem [] - { - new ("Sub-Menu 1", "", null), - new ("Sub-Menu 2", "", null) - } - ), - new ("Three", "", null) - } - ) - } - }; - menu.UseKeysUpDownAsKeysLeftRight = true; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y)); - Assert.False (menu.UseSubMenusSingleFrame); - - top.Draw (); - - var expected = @" - Numbers -"; - - Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - Assert.True (menu.NewKeyDownEvent (menu.Key)); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│ -│ Three │ -└────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - Assert.True (Application.TopRunnable.SubViews.ElementAt (1).NewKeyDownEvent (Key.CursorDown)); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│┌─────────────┐ -│ Three ││ Sub-Menu 1 │ -└────────┘│ Sub-Menu 2 │ - └─────────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - Assert.True (Application.TopRunnable.SubViews.ElementAt (2).NewKeyDownEvent (Key.CursorLeft)); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│ -│ Three │ -└────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - - Assert.True (Application.TopRunnable.SubViews.ElementAt (1).NewKeyDownEvent (Key.Esc)); - top.Draw (); - - expected = @" - Numbers -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - top.Dispose (); - } - - [Fact (Skip = "#3798 Broke. Will fix in #2975")] - [AutoInitShutdown] - public void UseSubMenusSingleFrame_False_By_Mouse () - { - var menu = new MenuBar - { - Menus = - [ - new ( - "Numbers", - new MenuItem [] - { - new ("One", "", null), - new MenuBarItem ( - "Two", - new MenuItem [] - { - new ( - "Sub-Menu 1", - "", - null - ), - new ( - "Sub-Menu 2", - "", - null - ) - } - ), - new ("Three", "", null) - } - ) - ] - }; - - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.Equal (Point.Empty, new (menu.Frame.X, menu.Frame.Y)); - Assert.False (menu.UseSubMenusSingleFrame); - - top.Draw (); - - var expected = @" - Numbers -"; - - Rectangle pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 8, 1), pos); - - menu.NewMouseEvent ( - new () { Position = new (1, 0), Flags = MouseFlags.Button1Pressed, View = menu } - ); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│ -│ Three │ -└────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 10, 6), pos); - - menu.NewMouseEvent ( - new () - { - Position = new (1, 2), Flags = MouseFlags.ReportMousePosition, View = Application.TopRunnable.SubViews.ElementAt (1) - } - ); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│┌─────────────┐ -│ Three ││ Sub-Menu 1 │ -└────────┘│ Sub-Menu 2 │ - └─────────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 25, 7), pos); - - Assert.False ( - menu.NewMouseEvent ( - new () - { - Position = new (1, 1), Flags = MouseFlags.ReportMousePosition, View = Application.TopRunnable.SubViews.ElementAt (1) - } - ) - ); - top.Draw (); - - expected = @" - Numbers -┌────────┐ -│ One │ -│ Two ►│ -│ Three │ -└────────┘ -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 10, 6), pos); - - menu.NewMouseEvent ( - new () { Position = new (70, 2), Flags = MouseFlags.Button1Clicked, View = Application.TopRunnable } - ); - top.Draw (); - - expected = @" - Numbers -"; - - pos = DriverAssert.AssertDriverContentsWithFrameAre (expected, output); - Assert.Equal (new (1, 0, 8, 1), pos); - top.Dispose (); - } - - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void Visible_False_Key_Does_Not_Open_And_Close_All_Opened_Menus () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }) - ] - }; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (menu.Visible); - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - - menu.Visible = false; - Assert.False (menu.IsMenuOpen); - - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.False (menu.IsMenuOpen); - top.Dispose (); - } - - [Fact (Skip = "v2 fake driver broke. Menu still works; disabling tests.")] - [AutoInitShutdown] - public void CanFocus_True_Key_Esc_Exit_Toplevel_If_IsMenuOpen_False () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }) - ], - CanFocus = true - }; - var top = new Toplevel (); - top.Add (menu); - Application.Begin (top); - - Assert.True (menu.CanFocus); - Assert.True (menu.NewKeyDownEvent (menu.Key)); - Assert.True (menu.IsMenuOpen); - - Assert.True (menu.NewKeyDownEvent (Key.Esc)); - Assert.False (menu.IsMenuOpen); - - Assert.False (menu.NewKeyDownEvent (Key.Esc)); - Assert.False (menu.IsMenuOpen); - top.Dispose (); - } - - // Defines the expected strings for a Menu. Currently supports - // - MenuBar with any number of MenuItems - // - Each top-level MenuItem can have a SINGLE sub-menu - // - // TODO: Enable multiple sub-menus - // TODO: Enable checked sub-menus - // TODO: Enable sub-menus with sub-menus (perhaps better to put this in a separate class with focused unit tests?) - // - // E.g: - // - // File Edit - // New Copy - public class ExpectedMenuBar : MenuBar - { - - // The expected strings when the menu is closed - public string ClosedMenuText => MenuBarText + "\n"; - - public string ExpectedBottomRow (int i) - { - return $"{Glyphs.LLCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.LRCorner} \n"; - } - - // The 3 spaces at end are a result of Menu.cs line 1062 where `pos` is calculated (` + spacesAfterTitle`) - public string ExpectedMenuItemRow (int i) { return $"{Glyphs.VLine} {Menus [i].Children [0].Title} {Glyphs.VLine} \n"; } - - // The full expected string for an open sub menu - public string ExpectedSubMenuOpen (int i) - { - return ClosedMenuText - + (Menus [i].Children.Length > 0 - ? ExpectedPadding (i) - + ExpectedTopRow (i) - + ExpectedPadding (i) - + ExpectedMenuItemRow (i) - + ExpectedPadding (i) - + ExpectedBottomRow (i) - : ""); - } - - // Define expected menu frame - // "┌──────┐" - // "│ New │" - // "└──────┘" - // - // The width of the Frame is determined in Menu.cs line 144, where `Width` is calculated - // 1 space before the Title and 2 spaces after the Title/Check/Help - public string ExpectedTopRow (int i) - { - return $"{Glyphs.ULCorner}{new (Glyphs.HLine.ToString () [0], Menus [i].Children [0].TitleLength + 3)}{Glyphs.URCorner} \n"; - } - - // Each MenuBar title has a 1 space pad on each side - // See `static int leftPadding` and `static int rightPadding` on line 1037 of Menu.cs - public string MenuBarText - { - get - { - var txt = string.Empty; - - foreach (MenuBarItem m in Menus) - { - txt += " " + m.Title + " "; - } - - return txt; - } - } - - // Padding for the X of the sub menu Frame - // Menu.cs - Line 1239 in `internal void OpenMenu` is where the Menu is created - private string ExpectedPadding (int i) - { - var n = 0; - - while (i > 0) - { - n += Menus [i - 1].TitleLength + 2; - i--; - } - - return new (' ', n); - } - } - - private class CustomWindow : Window - { - public CustomWindow () - { - var menu = new MenuBar - { - Menus = - [ - new ("File", new MenuItem [] { new ("New", "", null) }), - new ( - "Edit", - new MenuItem [] - { - new MenuBarItem ( - "Delete", - new MenuItem [] - { new ("All", "", null), new ("Selected", "", null) } - ) - } - ) - ] - }; - Add (menu); - } - } -} -#pragma warning restore CS0618 // Type or member is obsolete diff --git a/Tests/UnitTests/Views/Menuv1/Menuv1Tests.cs b/Tests/UnitTests/Views/Menuv1/Menuv1Tests.cs deleted file mode 100644 index 86f8bfc32..000000000 --- a/Tests/UnitTests/Views/Menuv1/Menuv1Tests.cs +++ /dev/null @@ -1,111 +0,0 @@ -using Xunit.Abstractions; - -//using static Terminal.Gui.ViewTests.MenuTests; - -namespace UnitTests.ViewsTests; - -#pragma warning disable CS0618 // Type or member is obsolete -public class Menuv1Tests -{ - private readonly ITestOutputHelper _output; - public Menuv1Tests (ITestOutputHelper output) { _output = output; } - - // TODO: Create more low-level unit tests for Menu and MenuItem - - [Fact] - public void Menu_Constructors_Defaults () - { - Assert.Throws (() => new Menu { Host = null, BarItems = new MenuBarItem () }); - Assert.Throws (() => new Menu { Host = new MenuBar (), BarItems = null }); - - var menu = new Menu { Host = new MenuBar (), X = 0, Y = 0, BarItems = new MenuBarItem () }; - Assert.Empty (menu.Title); - Assert.Empty (menu.Text); - } - - [Fact] - public void MenuItem_Constructors_Defaults () - { - var menuItem = new MenuItem (); - Assert.Equal ("", menuItem.Title); - Assert.Equal ("", menuItem.Help); - Assert.Null (menuItem.Action); - Assert.Null (menuItem.CanExecute); - Assert.Null (menuItem.Parent); - Assert.Equal (Key.Empty, menuItem.ShortcutKey); - - menuItem = new MenuItem ("Test", "Help", Run, () => { return true; }, new MenuItem (), KeyCode.F1); - Assert.Equal ("Test", menuItem.Title); - Assert.Equal ("Help", menuItem.Help); - Assert.Equal (Run, menuItem.Action); - Assert.NotNull (menuItem.CanExecute); - Assert.NotNull (menuItem.Parent); - Assert.Equal (KeyCode.F1, menuItem.ShortcutKey); - - void Run () { } - } - - [Fact] - public void MenuBarItem_SubMenu_Can_Return_Null () - { - var menuItem = new MenuItem (); - var menuBarItem = new MenuBarItem (); - Assert.Null (menuBarItem.SubMenu (menuItem)); - } - - [Fact] - public void MenuBarItem_Constructors_Defaults () - { - var menuBarItem = new MenuBarItem (); - Assert.Equal ("", menuBarItem.Title); - Assert.Equal ("", menuBarItem.Help); - Assert.Null (menuBarItem.Action); - Assert.Null (menuBarItem.CanExecute); - Assert.Null (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - Assert.Equal ([], menuBarItem.Children); - Assert.False (menuBarItem.IsTopLevel); - - menuBarItem = new MenuBarItem (null!, null!, Run, () => true, new ()); - Assert.Equal ("", menuBarItem.Title); - Assert.Equal ("", menuBarItem.Help); - Assert.Equal (Run, menuBarItem.Action); - Assert.NotNull (menuBarItem.CanExecute); - Assert.NotNull (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - Assert.Null (menuBarItem.Children); - Assert.False (menuBarItem.IsTopLevel); - - menuBarItem = new MenuBarItem (null!, Array.Empty (), new ()); - Assert.Equal ("", menuBarItem.Title); - Assert.Equal ("", menuBarItem.Help); - Assert.Null (menuBarItem.Action); - Assert.Null (menuBarItem.CanExecute); - Assert.NotNull (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - Assert.Equal ([], menuBarItem.Children); - Assert.False (menuBarItem.IsTopLevel); - - menuBarItem = new MenuBarItem (null!, new List (), new ()); - Assert.Equal ("", menuBarItem.Title); - Assert.Equal ("", menuBarItem.Help); - Assert.Null (menuBarItem.Action); - Assert.Null (menuBarItem.CanExecute); - Assert.NotNull (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - Assert.Equal ([], menuBarItem.Children); - Assert.False (menuBarItem.IsTopLevel); - - menuBarItem = new MenuBarItem ([]); - Assert.Equal ("", menuBarItem.Title); - Assert.Equal ("", menuBarItem.Help); - Assert.Null (menuBarItem.Action); - Assert.Null (menuBarItem.CanExecute); - Assert.Null (menuBarItem.Parent); - Assert.Equal (Key.Empty, menuBarItem.ShortcutKey); - Assert.Equal ([], menuBarItem.Children); - Assert.False (menuBarItem.IsTopLevel); - - void Run () { } - } -} diff --git a/Tests/UnitTests/Views/ToplevelTests.cs b/Tests/UnitTests/Views/ToplevelTests.cs index f01ad988e..55a426e3d 100644 --- a/Tests/UnitTests/Views/ToplevelTests.cs +++ b/Tests/UnitTests/Views/ToplevelTests.cs @@ -12,7 +12,6 @@ public class ToplevelTests Assert.Equal ("Fill(Absolute(0))", top.Height.ToString ()); Assert.False (top.Running); Assert.False (top.Modal); - Assert.Null (top.MenuBar); //Assert.Null (top.StatusBar); } @@ -42,32 +41,6 @@ public class ToplevelTests top.OnUnloaded (); Assert.Equal ("Unloaded", eventInvoked); - top.Add (new MenuBar ()); - Assert.NotNull (top.MenuBar); - - //top.Add (new StatusBar ()); - //Assert.NotNull (top.StatusBar); - MenuBar menuBar = top.MenuBar; - top.Remove (top.MenuBar); - Assert.Null (top.MenuBar); - Assert.NotNull (menuBar); - - //var statusBar = top.StatusBar; - //top.Remove (top.StatusBar); - //Assert.Null (top.StatusBar); - //Assert.NotNull (statusBar); -#if DEBUG_IDISPOSABLE - Assert.False (menuBar.WasDisposed); - - //Assert.False (statusBar.WasDisposed); - menuBar.Dispose (); - - //statusBar.Dispose (); - Assert.True (menuBar.WasDisposed); - - //Assert.True (statusBar.WasDisposed); -#endif - Application.Begin (top); Assert.Equal (top, Application.TopRunnable); @@ -76,50 +49,18 @@ public class ToplevelTests Assert.Equal (Application.TopRunnable, supView); Assert.Equal (0, nx); Assert.Equal (0, ny); - - //Assert.Null (sb); - - top.Add (new MenuBar ()); - Assert.NotNull (top.MenuBar); - - // Application.TopRunnable with a menu and without status bar. + // Application.Current with a menu and without status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - Assert.Equal (1, ny); - - //Assert.Null (sb); - - //top.Add (new StatusBar ()); - //Assert.NotNull (top.StatusBar); - + Assert.Equal (0, ny); // Application.TopRunnable with a menu and status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - // The available height is lower than the Application.TopRunnable height minus - // the menu bar and status bar, then the top can go beyond the bottom - // Assert.Equal (2, ny); - //Assert.NotNull (sb); - - menuBar = top.MenuBar; - top.Remove (top.MenuBar); - Assert.Null (top.MenuBar); - Assert.NotNull (menuBar); - - // Application.TopRunnable without a menu and with a status bar. + // Application.TopRunnable without a menu and with a status bar. View.GetLocationEnsuringFullVisibility (top, 2, 2, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); - // The available height is lower than the Application.TopRunnable height minus - // the status bar, then the top can go beyond the bottom - // Assert.Equal (2, ny); - //Assert.NotNull (sb); - - //statusBar = top.StatusBar; - //top.Remove (top.StatusBar); - //Assert.Null (top.StatusBar); - //Assert.NotNull (statusBar); - Assert.Null (top.MenuBar); var win = new Window { Width = Dim.Fill (), Height = Dim.Fill () }; top.Add (win); @@ -135,108 +76,10 @@ public class ToplevelTests View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); Assert.Equal (0, nx); Assert.Equal (0, ny); - - //Assert.Null (sb); - - top.Add (new MenuBar ()); - Assert.NotNull (top.MenuBar); - - // Application.TopRunnable with a menu and without status bar. - View.GetLocationEnsuringFullVisibility (win, 2, 2, out nx, out ny /*, out sb*/); - Assert.Equal (0, nx); - Assert.Equal (1, ny); - - //Assert.Null (sb); - - top.Add (new StatusBar ()); - - //Assert.NotNull (top.StatusBar); - - // Application.TopRunnable with a menu and status bar. - View.GetLocationEnsuringFullVisibility (win, 30, 20, out nx, out ny /*, out sb*/); - Assert.Equal (0, nx); - - // The available height is lower than the Application.TopRunnable height minus - // the menu bar and status bar, then the top can go beyond the bottom - //Assert.Equal (20, ny); - //Assert.NotNull (sb); - - menuBar = top.MenuBar; - - //statusBar = top.StatusBar; - top.Remove (top.MenuBar); - Assert.Null (top.MenuBar); - Assert.NotNull (menuBar); - - //top.Remove (top.StatusBar); - //Assert.Null (top.StatusBar); - //Assert.NotNull (statusBar); - top.Remove (win); win = new () { Width = 60, Height = 15 }; top.Add (win); - - // Application.TopRunnable without menu and status bar. - View.GetLocationEnsuringFullVisibility (win, 0, 0, out nx, out ny /*, out sb*/); - Assert.Equal (0, nx); - Assert.Equal (0, ny); - - //Assert.Null (sb); - - top.Add (new MenuBar ()); - Assert.NotNull (top.MenuBar); - - // Application.TopRunnable with a menu and without status bar. - View.GetLocationEnsuringFullVisibility (win, 2, 2, out nx, out ny /*, out sb*/); - Assert.Equal (2, nx); - Assert.Equal (2, ny); - - //Assert.Null (sb); - - top.Add (new StatusBar ()); - - //Assert.NotNull (top.StatusBar); - - // Application.TopRunnable with a menu and status bar. - View.GetLocationEnsuringFullVisibility (win, 30, 20, out nx, out ny /*, out sb*/); - Assert.Equal (20, nx); // 20+60=80 - - //Assert.Equal (9, ny); // 9+15+1(mb)=25 - //Assert.NotNull (sb); - - //Assert.Null (Toplevel._dragPosition); - win.NewMouseEvent (new () { Position = new (6, 0), Flags = MouseFlags.Button1Pressed }); - - // Assert.Equal (new Point (6, 0), Toplevel._dragPosition); - win.NewMouseEvent (new () { Position = new (6, 0), Flags = MouseFlags.Button1Released }); - - //Assert.Null (Toplevel._dragPosition); - win.CanFocus = false; - win.NewMouseEvent (new () { Position = new (6, 0), Flags = MouseFlags.Button1Pressed }); - - //Assert.Null (Toplevel._dragPosition); -#if DEBUG_IDISPOSABLE - - Assert.False (top.MenuBar.WasDisposed); - - //Assert.False (top.StatusBar.WasDisposed); -#endif - menuBar = top.MenuBar; - - //statusBar = top.StatusBar; - top.Dispose (); - Assert.Null (top.MenuBar); - - //Assert.Null (top.StatusBar); - Assert.NotNull (menuBar); - - //Assert.NotNull (statusBar); -#if DEBUG_IDISPOSABLE - Assert.True (menuBar.WasDisposed); - - //Assert.True (statusBar.WasDisposed); -#endif } [Fact] @@ -714,28 +557,6 @@ public class ToplevelTests window.Dispose (); } - [Fact] - [AutoInitShutdown] - public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw () - { - var menu = new MenuBar - { - Menus = - [ - new ("Child", new MenuItem [] { new ("_Create Child", "", null) }) - ] - }; - var topChild = new Toplevel (); - topChild.Add (menu); - var top = new Toplevel (); - top.Add (topChild); - Application.Begin (top); - - Exception exception = Record.Exception (() => topChild.NewKeyDownEvent (KeyCode.AltMask)); - Assert.Null (exception); - top.Dispose (); - } - [Fact] public void Multi_Thread_Toplevels () { @@ -870,33 +691,4 @@ public class ToplevelTests t.Dispose (); Application.Shutdown (); } - - [Fact] - public void Remove_Do_Not_Dispose_MenuBar_Or_StatusBar () - { - var mb = new MenuBar (); - var sb = new StatusBar (); - var tl = new Toplevel (); - -#if DEBUG - Assert.False (mb.WasDisposed); - Assert.False (sb.WasDisposed); -#endif - tl.Add (mb, sb); - Assert.NotNull (tl.MenuBar); - - //Assert.NotNull (tl.StatusBar); -#if DEBUG - Assert.False (mb.WasDisposed); - Assert.False (sb.WasDisposed); -#endif - tl.RemoveAll (); - Assert.Null (tl.MenuBar); - - //Assert.Null (tl.StatusBar); -#if DEBUG - Assert.False (mb.WasDisposed); - Assert.False (sb.WasDisposed); -#endif - } } diff --git a/Tests/UnitTests/Views/WindowTests.cs b/Tests/UnitTests/Views/WindowTests.cs index 32d524549..9ad07b547 100644 --- a/Tests/UnitTests/Views/WindowTests.cs +++ b/Tests/UnitTests/Views/WindowTests.cs @@ -3,117 +3,8 @@ using Xunit.Abstractions; namespace UnitTests.ViewsTests; -public class WindowTests (ITestOutputHelper output) +public class WindowTests () { - [Fact] - [AutoInitShutdown] - public void Activating_MenuBar_By_Alt_Key_Does_Not_Throw () - { - var menu = new MenuBar - { - Menus = - [ - new MenuBarItem ("Child", new MenuItem [] { new ("_Create Child", "", null) }) - ] - }; - var win = new Window (); - win.Add (menu); - var top = new Toplevel (); - top.Add (win); - Application.Begin (top); - - Exception exception = Record.Exception (() => win.NewKeyDownEvent (KeyCode.AltMask)); - Assert.Null (exception); - top.Dispose (); - } - - [Fact] - [AutoInitShutdown] - public void MenuBar_And_StatusBar_Inside_Window () - { - var menu = new MenuBar - { - Menus = - [ - new MenuBarItem ("File", new MenuItem [] { new ("Open", "", null), new ("Quit", "", null) }), - new MenuBarItem ( - "Edit", - new MenuItem [] { new ("Copy", "", null) } - ) - ] - }; - - var sb = new StatusBar (); - - var fv = new FrameView { Y = 1, Width = Dim.Fill (), Height = Dim.Fill (1), Title = "Frame View", BorderStyle = LineStyle.Single }; - var win = new Window (); - win.Add (menu, sb, fv); - Toplevel top = new (); - top.Add (win); - Application.Begin (top); - Application.Driver!.SetScreenSize (20, 10); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────┐ -│ File Edit │ -│┌┤Frame View├────┐│ -││ ││ -││ ││ -││ ││ -││ ││ -│└────────────────┘│ -│ │ -└──────────────────┘", - output - ); - - Application.Driver!.SetScreenSize (40, 20); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────────────────────────┐ -│ File Edit │ -│┌┤Frame View├────────────────────────┐│ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -││ ││ -│└────────────────────────────────────┘│ -│ │ -└──────────────────────────────────────┘", - output - ); - - Application.Driver!.SetScreenSize (20, 10); - - DriverAssert.AssertDriverContentsWithFrameAre ( - @" -┌──────────────────┐ -│ File Edit │ -│┌┤Frame View├────┐│ -││ ││ -││ ││ -││ ││ -││ ││ -│└────────────────┘│ -│ │ -└──────────────────┘", - output - ); - top.Dispose (); - } - [Fact] public void New_Initializes () { diff --git a/Tests/UnitTestsParallelizable/Views/BarTests.cs b/Tests/UnitTestsParallelizable/Views/BarTests.cs index 259c97abd..4454c08b0 100644 --- a/Tests/UnitTestsParallelizable/Views/BarTests.cs +++ b/Tests/UnitTestsParallelizable/Views/BarTests.cs @@ -107,7 +107,7 @@ public class BarTests public void GetAttributeForRole_DoesNotDeferToSuperView_WhenSchemeNameIsSet () { // This test would fail before the fix that checks SchemeName in GetAttributeForRole - // StatusBar and MenuBarv2 set SchemeName = "Menu", and should use Menu scheme + // StatusBar and MenuBar set SchemeName = "Menu", and should use Menu scheme // instead of deferring to parent's customized attributes var parentView = new View { SchemeName = "Base" }; diff --git a/Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs b/Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs index 75ac7a9a5..f4405628f 100644 --- a/Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs +++ b/Tests/UnitTestsParallelizable/Views/MenuBarItemTests.cs @@ -9,11 +9,11 @@ public class MenuBarItemTests () [Fact] public void Constructors_Defaults () { - var menuBarItem = new MenuBarItemv2 (); + var menuBarItem = new MenuBarItem (); Assert.Null (menuBarItem.PopoverMenu); Assert.Null (menuBarItem.TargetView); - menuBarItem = new MenuBarItemv2 (targetView: null, command: Command.NotBound, commandText: null, popoverMenu: null); + menuBarItem = new MenuBarItem (targetView: null, command: Command.NotBound, commandText: null, popoverMenu: null); Assert.Null (menuBarItem.PopoverMenu); Assert.Null (menuBarItem.TargetView); diff --git a/Tests/UnitTestsParallelizable/Views/MenuItemTests.cs b/Tests/UnitTestsParallelizable/Views/MenuItemTests.cs deleted file mode 100644 index b48502cd9..000000000 --- a/Tests/UnitTestsParallelizable/Views/MenuItemTests.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Xunit.Abstractions; - -//using static Terminal.Gui.ViewTests.MenuTests; - -namespace UnitTests_Parallelizable.ViewsTests; - -public class MenuItemTests () -{ - [Fact] - public void Constructors_Defaults () - { - - } -} diff --git a/Tests/UnitTestsParallelizable/Views/MenuTests.cs b/Tests/UnitTestsParallelizable/Views/MenuTests.cs index f83dd490e..a20f7d53f 100644 --- a/Tests/UnitTestsParallelizable/Views/MenuTests.cs +++ b/Tests/UnitTestsParallelizable/Views/MenuTests.cs @@ -9,7 +9,7 @@ public class MenuTests () [Fact] public void Constructors_Defaults () { - var menu = new Menuv2 { }; + var menu = new Menu { }; Assert.Empty (menu.Title); Assert.Empty (menu.Text); } diff --git a/docfx/docs/Popovers.md b/docfx/docs/Popovers.md index bfbe549dd..54cc24d10 100644 --- a/docfx/docs/Popovers.md +++ b/docfx/docs/Popovers.md @@ -15,4 +15,4 @@ A `Popover` is any View that meets these characteristics: - Is Transparent (`ViewportSettings = ViewportSettings.Transparent | ViewportSettings.TransparentMouse` - Sets `Visible = false` when it receives `Application.QuitKey` -@Terminal.Gui.Views.PopoverMenu provides a sophisticated implementation that can be used as a context menu and is the basis for @Terminal.Gui.MenuBarv2. \ No newline at end of file +@Terminal.Gui.Views.PopoverMenu provides a sophisticated implementation that can be used as a context menu and is the basis for @Terminal.Gui.MenuBar. \ No newline at end of file diff --git a/docfx/docs/cancellable-work-pattern.md b/docfx/docs/cancellable-work-pattern.md index b39262d69..aa5f7263a 100644 --- a/docfx/docs/cancellable-work-pattern.md +++ b/docfx/docs/cancellable-work-pattern.md @@ -181,7 +181,7 @@ protected bool? RaiseAccepting(ICommandContext? ctx) #### Propagation Challenge -- `Command.Activate` is local, limiting hierarchical coordination (e.g., `MenuBarv2` popovers). A proposed `PropagatedCommands` property addresses this, as detailed in the appendix. +- `Command.Activate` is local, limiting hierarchical coordination (e.g., `MenuBar` popovers). A proposed `PropagatedCommands` property addresses this, as detailed in the appendix. ### 4. Application.Keyboard: Application-Level Keyboard Input diff --git a/docfx/docs/command.md b/docfx/docs/command.md index 9e6779ca6..82f30e4c9 100644 --- a/docfx/docs/command.md +++ b/docfx/docs/command.md @@ -10,7 +10,7 @@ The `Command` system in Terminal.Gui provides a standardized framework for defining and executing actions that views can perform, such as selecting items, accepting input, or navigating content. Implemented primarily through the `View.Command` APIs, this system integrates tightly with input handling (e.g., keyboard and mouse events) and leverages the *Cancellable Work Pattern* to ensure extensibility, cancellation, and decoupling. Central to this system are the `Selecting` and `Accepting` events, which encapsulate common user interactions: `Selecting` for changing a view’s state or preparing it for interaction (e.g., toggling a checkbox, focusing a menu item), and `Accepting` for confirming an action or state (e.g., executing a menu command, submitting a dialog). -This deep dive explores the `Command` and `View.Command` APIs, focusing on the `Selecting` and `Accepting` concepts, their implementation, and their propagation behavior. It critically evaluates the need for additional events (`Selected`/`Accepted`) and the propagation of `Selecting` events, drawing on insights from `Menuv2`, `MenuItemv2`, `MenuBarv2`, `CheckBox`, and `FlagSelector`. These implementations highlight the system’s application in hierarchical (menus) and stateful (checkboxes, flag selectors) contexts. The document reflects the current implementation, including the `Cancel` property in `CommandEventArgs` and local handling of `Command.Select`. An appendix briefly summarizes proposed changes from a filed issue to rename `Command.Select` to `Command.Activate`, replace `Cancel` with `Handled`, and introduce a propagation mechanism, addressing limitations in the current system. +This deep dive explores the `Command` and `View.Command` APIs, focusing on the `Selecting` and `Accepting` concepts, their implementation, and their propagation behavior. It critically evaluates the need for additional events (`Selected`/`Accepted`) and the propagation of `Selecting` events, drawing on insights from `Menu`, `MenuItemv2`, `MenuBar`, `CheckBox`, and `FlagSelector`. These implementations highlight the system’s application in hierarchical (menus) and stateful (checkboxes, flag selectors) contexts. The document reflects the current implementation, including the `Cancel` property in `CommandEventArgs` and local handling of `Command.Select`. An appendix briefly summarizes proposed changes from a filed issue to rename `Command.Select` to `Command.Activate`, replace `Cancel` with `Handled`, and introduce a propagation mechanism, addressing limitations in the current system. ## Overview of the Command System @@ -89,7 +89,7 @@ public bool? InvokeCommand(Command command, ICommandContext? ctx) ### Command Routing Most commands route directly to the target view. `Command.Select` and `Command.Accept` have special routing: -- `Command.Select`: Handled locally, with no propagation to superviews, relying on view-specific events (e.g., `SelectedMenuItemChanged` in `Menuv2`) for hierarchical coordination. +- `Command.Select`: Handled locally, with no propagation to superviews, relying on view-specific events (e.g., `SelectedMenuItemChanged` in `Menu`) for hierarchical coordination. - `Command.Accept`: Propagates to a default button (if `IsDefault = true`), superview, or `SuperMenuItem` (in menus). **Example**: `Command.Accept` in `RaiseAccepting`: @@ -170,7 +170,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in } ``` - **OptionSelector**: Selecting an OpitonSelector option raises `Selecting` to update the selected option. - - **Menuv2** and **MenuBarv2**: Selecting a `MenuItemv2` (e.g., via mouse enter or arrow keys) sets focus, tracked by `SelectedMenuItem` and raising `SelectedMenuItemChanged`: + - **Menu** and **MenuBar**: Selecting a `MenuItemv2` (e.g., via mouse enter or arrow keys) sets focus, tracked by `SelectedMenuItem` and raising `SelectedMenuItemChanged`: ```csharp protected override void OnFocusedChanged(View? previousFocused, View? focused) { @@ -196,7 +196,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in ``` - **Views without State**: For views like `Button`, `Selecting` typically sets focus but does not change state, making it less relevant. -- **Propagation**: `Command.Select` is handled locally by the target view. If the command is unhandled (`null` or `false`), processing stops without propagating to the superview or other views. This is evident in `Menuv2`, where `SelectedMenuItemChanged` is used for hierarchical coordination, and in `CheckBox` and `FlagSelector`, where state changes are internal. +- **Propagation**: `Command.Select` is handled locally by the target view. If the command is unhandled (`null` or `false`), processing stops without propagating to the superview or other views. This is evident in `Menu`, where `SelectedMenuItemChanged` is used for hierarchical coordination, and in `CheckBox` and `FlagSelector`, where state changes are internal. ### Accepting - **Definition**: `Accepting` represents a user action that confirms or finalizes a view’s state or triggers an action, such as submitting a dialog, activating a button, or confirming a selection in a list. It is associated with `Command.Accept`, typically triggered by the Enter key or double-click. @@ -211,7 +211,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in - **Button**: Pressing Enter raises `Accepting` to activate the button (e.g., submit a dialog). - **ListView**: Double-clicking or pressing Enter raises `Accepting` to confirm the selected item(s). - **TextField**: Pressing Enter raises `Accepting` to submit the input. - - **Menuv2** and **MenuBarv2**: Pressing Enter on a `MenuItemv2` raises `Accepting` to execute a command or open a submenu, followed by the `Accepted` event to hide the menu or deactivate the menu bar: + - **Menu** and **MenuBar**: Pressing Enter on a `MenuItemv2` raises `Accepting` to execute a command or open a submenu, followed by the `Accepted` event to hide the menu or deactivate the menu bar: ```csharp protected void RaiseAccepted(ICommandContext? ctx) { @@ -230,7 +230,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in - **Propagation**: `Command.Accept` propagates to: - A default button (if present in the superview with `IsDefault = true`). - The superview, enabling hierarchical handling (e.g., a dialog processes `Accept` if no button handles it). - - In `Menuv2`, propagation extends to the `SuperMenuItem` for submenus in popovers, as seen in `OnAccepting`: + - In `Menu`, propagation extends to the `SuperMenuItem` for submenus in popovers, as seen in `OnAccepting`: ```csharp protected override bool OnAccepting(CommandEventArgs args) { @@ -245,7 +245,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in return false; } ``` - - Similarly, `MenuBarv2` customizes propagation to show popovers: + - Similarly, `MenuBar` customizes propagation to show popovers: ```csharp protected override bool OnAccepting(CommandEventArgs args) { @@ -278,7 +278,7 @@ These concepts are opinionated, reflecting Terminal.Gui’s view that most UI in | **Event** | `Selecting` | `Accepting` | | **Virtual Method** | `OnSelecting` | `OnAccepting` | | **Propagation** | Local to the view | Propagates to default button, superview, or SuperMenuItem (in menus) | -| **Use Cases** | `Menuv2`, `MenuBarv2`, `CheckBox`, `FlagSelector`, `ListView`, `Button` | `Menuv2`, `MenuBarv2`, `CheckBox`, `FlagSelector`, `Button`, `ListView`, `Dialog` | +| **Use Cases** | `Menu`, `MenuBar`, `CheckBox`, `FlagSelector`, `ListView`, `Button` | `Menu`, `MenuBar`, `CheckBox`, `FlagSelector`, `Button`, `ListView`, `Dialog` | | **State Dependency** | Often stateful, but includes focus for stateless views | May be stateless (triggers action) | ### Critical Evaluation: Selecting vs. Accepting @@ -287,9 +287,9 @@ The distinction between `Selecting` and `Accepting` is clear in theory: - `Accepting` is about finalizing an action, such as submitting a selection or activating a button. However, practical challenges arise: -- **Overlapping Triggers**: In `ListView`, pressing Enter might both select an item (`Selecting`) and confirm it (`Accepting`), depending on the interaction model, potentially confusing developers. Similarly, in `Menuv2`, navigation (e.g., arrow keys) triggers `Selecting`, while Enter triggers `Accepting`, but the overlap in user intent can blur the lines. +- **Overlapping Triggers**: In `ListView`, pressing Enter might both select an item (`Selecting`) and confirm it (`Accepting`), depending on the interaction model, potentially confusing developers. Similarly, in `Menu`, navigation (e.g., arrow keys) triggers `Selecting`, while Enter triggers `Accepting`, but the overlap in user intent can blur the lines. - **Stateless Views**: For views like `Button` or `MenuItemv2`, `Selecting` is limited to setting focus, which dilutes its purpose as a state-changing action and may confuse developers expecting a more substantial state change. -- **Propagation Limitations**: The local handling of `Command.Select` restricts hierarchical coordination. For example, `MenuBarv2` relies on `SelectedMenuItemChanged` to manage `PopoverMenu` visibility, which is view-specific and not generalizable. This highlights a need for a propagation mechanism that maintains subview-superview decoupling. +- **Propagation Limitations**: The local handling of `Command.Select` restricts hierarchical coordination. For example, `MenuBar` relies on `SelectedMenuItemChanged` to manage `PopoverMenu` visibility, which is view-specific and not generalizable. This highlights a need for a propagation mechanism that maintains subview-superview decoupling. - **FlagSelector Design Flaw**: In `FlagSelector`, the `CheckBox.Selecting` handler incorrectly triggers both `Selecting` and `Accepting`, conflating state changes (toggling flags) with action confirmation (submitting the flag set). This violates the intended separation and requires a design fix to ensure `Selecting` is limited to subview state changes and `Accepting` is reserved for parent-level confirmation. **Recommendation**: Enhance documentation to clarify the `Selecting`/`Accepting` model: @@ -299,13 +299,13 @@ However, practical challenges arise: ## Evaluating Selected/Accepted Events -The need for `Selected` and `Accepted` events is under consideration, with `Accepted` showing utility in specific views (`Menuv2`, `MenuBarv2`) but not universally required across all views. These events would serve as post-events, notifying that a `Selecting` or `Accepting` action has completed, similar to other *Cancellable Work Pattern* post-events like `ClearedViewport` in `View.Draw` or `OrientationChanged` in `OrientationHelper`. +The need for `Selected` and `Accepted` events is under consideration, with `Accepted` showing utility in specific views (`Menu`, `MenuBar`) but not universally required across all views. These events would serve as post-events, notifying that a `Selecting` or `Accepting` action has completed, similar to other *Cancellable Work Pattern* post-events like `ClearedViewport` in `View.Draw` or `OrientationChanged` in `OrientationHelper`. ### Need for Selected/Accepted Events - **Selected Event**: - **Purpose**: A `Selected` event would notify that a `Selecting` action has completed, indicating that a state change or preparatory action (e.g., a new item highlighted, a checkbox toggled) has taken effect. - **Use Cases**: - - **Menuv2** and **MenuBarv2**: Notify when a new `MenuItemv2` is focused, currently handled by the `SelectedMenuItemChanged` event, which tracks focus changes: + - **Menu** and **MenuBar**: Notify when a new `MenuItemv2` is focused, currently handled by the `SelectedMenuItemChanged` event, which tracks focus changes: ```csharp protected override void OnFocusedChanged(View? previousFocused, View? focused) { @@ -366,7 +366,7 @@ The need for `Selected` and `Accepted` events is under consideration, with `Acce ``` - **ListView**: Notify when a new item is selected, typically handled by `SelectedItemChanged` or similar custom events. - **Button**: Less relevant, as `Selecting` typically only sets focus, and no state change occurs to warrant a `Selected` notification. - - **Current Approach**: Views like `Menuv2`, `CheckBox`, and `FlagSelector` use custom events (`SelectedMenuItemChanged`, `CheckedStateChanged`, `ValueChanged`) to signal state changes, bypassing a generic `Selected` event. These view-specific events provide context (e.g., the selected `MenuItemv2`, the new `CheckedState`, or the updated `Value`) that a generic `Selected` event would struggle to convey without additional complexity. + - **Current Approach**: Views like `Menu`, `CheckBox`, and `FlagSelector` use custom events (`SelectedMenuItemChanged`, `CheckedStateChanged`, `ValueChanged`) to signal state changes, bypassing a generic `Selected` event. These view-specific events provide context (e.g., the selected `MenuItemv2`, the new `CheckedState`, or the updated `Value`) that a generic `Selected` event would struggle to convey without additional complexity. - **Pros**: - A standardized `Selected` event could unify state change notifications across views, reducing the need for custom events in some cases. - Aligns with the *Cancellable Work Pattern*’s post-event phase, providing a consistent way to react to completed `Selecting` actions. @@ -376,13 +376,13 @@ The need for `Selected` and `Accepted` events is under consideration, with `Acce - Less relevant for stateless views like `Button`, where `Selecting` only sets focus, leading to inconsistent usage across view types. - Adds complexity to the base `View` class, potentially bloating the API for a feature not universally needed. - Requires developers to handle generic `Selected` events with less specific information, which could lead to more complex event handling logic compared to targeted view-specific events. - - **Context Insight**: The use of `SelectedMenuItemChanged` in `Menuv2` and `MenuBarv2`, `CheckedStateChanged` in `CheckBox`, and `ValueChanged` in `FlagSelector` suggests that view-specific events are preferred for their specificity and context. These events are tailored to the view’s state (e.g., `MenuItemv2` instance, `CheckState`, or `Value`), making them more intuitive for developers than a generic `Selected` event. The absence of a `Selected` event in the current implementation indicates that it hasn’t been necessary for most use cases, as view-specific events adequately cover state change notifications. - - **Verdict**: A generic `Selected` event could provide a standardized way to notify state changes, but its benefits are outweighed by the effectiveness of view-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged`. These events offer richer context and are sufficient for current use cases across `Menuv2`, `CheckBox`, `FlagSelector`, and other views. Adding `Selected` to the base `View` class is not justified at this time, as it would add complexity without significant advantages over existing mechanisms. + - **Context Insight**: The use of `SelectedMenuItemChanged` in `Menu` and `MenuBar`, `CheckedStateChanged` in `CheckBox`, and `ValueChanged` in `FlagSelector` suggests that view-specific events are preferred for their specificity and context. These events are tailored to the view’s state (e.g., `MenuItemv2` instance, `CheckState`, or `Value`), making them more intuitive for developers than a generic `Selected` event. The absence of a `Selected` event in the current implementation indicates that it hasn’t been necessary for most use cases, as view-specific events adequately cover state change notifications. + - **Verdict**: A generic `Selected` event could provide a standardized way to notify state changes, but its benefits are outweighed by the effectiveness of view-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged`. These events offer richer context and are sufficient for current use cases across `Menu`, `CheckBox`, `FlagSelector`, and other views. Adding `Selected` to the base `View` class is not justified at this time, as it would add complexity without significant advantages over existing mechanisms. - **Accepted Event**: - **Purpose**: An `Accepted` event would notify that an `Accepting` action has completed (i.e., was not canceled via `args.Cancel`), indicating that the action has taken effect, aligning with the *Cancellable Work Pattern*’s post-event phase. - **Use Cases**: - - **Menuv2** and **MenuBarv2**: The `Accepted` event is critical for signaling that a menu command has been executed or a submenu action has completed, triggering actions like hiding the menu or deactivating the menu bar. In `Menuv2`, it’s raised by `RaiseAccepted` and used hierarchically: + - **Menu** and **MenuBar**: The `Accepted` event is critical for signaling that a menu command has been executed or a submenu action has completed, triggering actions like hiding the menu or deactivating the menu bar. In `Menu`, it’s raised by `RaiseAccepted` and used hierarchically: ```csharp protected void RaiseAccepted(ICommandContext? ctx) { @@ -391,7 +391,7 @@ The need for `Selected` and `Accepted` events is under consideration, with `Acce Accepted?.Invoke(this, args); } ``` - In `MenuBarv2`, it deactivates the menu bar: + In `MenuBar`, it deactivates the menu bar: ```csharp protected override void OnAccepted(CommandEventArgs args) { @@ -408,16 +408,16 @@ The need for `Selected` and `Accepted` events is under consideration, with `Acce - **Button**: Could notify that the button was activated, typically handled by a custom event like `Clicked`. - **ListView**: Could notify that a selection was confirmed (e.g., Enter pressed), often handled by custom events. - **Dialog**: Could notify that an action was completed (e.g., OK button clicked), useful for hierarchical scenarios. - - **Current Approach**: `Menuv2` and `MenuItemv2` implement `Accepted` to signal action completion, with hierarchical handling via subscriptions (e.g., `MenuItemv2.Accepted` triggers `Menuv2.RaiseAccepted`, which triggers `MenuBarv2.OnAccepted`). Other views like `CheckBox` and `FlagSelector` rely on the completion of the `Accepting` event (i.e., not canceled) or custom events (e.g., `Button.Clicked`) to indicate action completion, without a generic `Accepted` event. + - **Current Approach**: `Menu` and `MenuItemv2` implement `Accepted` to signal action completion, with hierarchical handling via subscriptions (e.g., `MenuItemv2.Accepted` triggers `Menu.RaiseAccepted`, which triggers `MenuBar.OnAccepted`). Other views like `CheckBox` and `FlagSelector` rely on the completion of the `Accepting` event (i.e., not canceled) or custom events (e.g., `Button.Clicked`) to indicate action completion, without a generic `Accepted` event. - **Pros**: - - Provides a standardized way to react to confirmed actions, particularly valuable in composite or hierarchical views like `Menuv2`, `MenuBarv2`, and `Dialog`, where superviews need to respond to action completion (e.g., closing a menu or dialog). + - Provides a standardized way to react to confirmed actions, particularly valuable in composite or hierarchical views like `Menu`, `MenuBar`, and `Dialog`, where superviews need to respond to action completion (e.g., closing a menu or dialog). - Aligns with the *Cancellable Work Pattern*’s post-event phase, offering a consistent mechanism for post-action notifications. - Simplifies hierarchical scenarios by providing a unified event for action completion, reducing reliance on view-specific events in some cases. - **Cons**: - - May duplicate existing view-specific events (e.g., `Button.Clicked`, `Menuv2.Accepted`), leading to redundancy in views where custom events are already established. + - May duplicate existing view-specific events (e.g., `Button.Clicked`, `Menu.Accepted`), leading to redundancy in views where custom events are already established. - Adds complexity to the base `View` class, especially for views like `CheckBox` or `FlagSelector` where `Accepting`’s completion is often sufficient without a post-event. - Requires clear documentation to distinguish `Accepted` from `Accepting` and to clarify when it should be used over view-specific events. - - **Context Insight**: The implementation of `Accepted` in `Menuv2` and `MenuBarv2` demonstrates its utility in hierarchical contexts, where it facilitates actions like menu closure or menu bar deactivation. For example, `MenuItemv2` raises `Accepted` to trigger `Menuv2`’s `RaiseAccepted`, which propagates to `MenuBarv2`: + - **Context Insight**: The implementation of `Accepted` in `Menu` and `MenuBar` demonstrates its utility in hierarchical contexts, where it facilitates actions like menu closure or menu bar deactivation. For example, `MenuItemv2` raises `Accepted` to trigger `Menu`’s `RaiseAccepted`, which propagates to `MenuBar`: ```csharp protected void RaiseAccepted(ICommandContext? ctx) { @@ -427,16 +427,16 @@ The need for `Selected` and `Accepted` events is under consideration, with `Acce } ``` In contrast, `CheckBox` and `FlagSelector` do not use `Accepted`, relying on `Accepting`’s completion or view-specific events like `CheckedStateChanged` or `ValueChanged`. This suggests that `Accepted` is particularly valuable in composite views with hierarchical interactions but not universally needed across all views. The absence of `Accepted` in `CheckBox` and `FlagSelector` indicates that `Accepting` is often sufficient for simple confirmation scenarios, but the hierarchical use in menus and potential dialog applications highlight its potential for broader adoption in specific contexts. - - **Verdict**: The `Accepted` event is highly valuable in composite and hierarchical views like `Menuv2`, `MenuBarv2`, and potentially `Dialog`, where it supports coordinated action completion (e.g., closing menus or dialogs). However, adding it to the base `View` class is premature without broader validation across more view types, as many views (e.g., `CheckBox`, `FlagSelector`) function effectively without it, using `Accepting` or custom events. Implementing `Accepted` in specific views or base classes like `Bar` or `Toplevel` (e.g., for menus and dialogs) and reassessing its necessity for the base `View` class later is a prudent approach. This balances the demonstrated utility in hierarchical scenarios with the need to avoid unnecessary complexity in simpler views. + - **Verdict**: The `Accepted` event is highly valuable in composite and hierarchical views like `Menu`, `MenuBar`, and potentially `Dialog`, where it supports coordinated action completion (e.g., closing menus or dialogs). However, adding it to the base `View` class is premature without broader validation across more view types, as many views (e.g., `CheckBox`, `FlagSelector`) function effectively without it, using `Accepting` or custom events. Implementing `Accepted` in specific views or base classes like `Bar` or `Toplevel` (e.g., for menus and dialogs) and reassessing its necessity for the base `View` class later is a prudent approach. This balances the demonstrated utility in hierarchical scenarios with the need to avoid unnecessary complexity in simpler views. **Recommendation**: Avoid adding `Selected` or `Accepted` events to the base `View` class for now. Instead: -- Continue using view-specific events (e.g., `Menuv2.SelectedMenuItemChanged`, `CheckBox.CheckedStateChanged`, `FlagSelector.ValueChanged`, `ListView.SelectedItemChanged`, `Button.Clicked`) for their contextual specificity and clarity. -- Maintain and potentially formalize the use of `Accepted` in views like `Menuv2`, `MenuBarv2`, and `Dialog`, tracking its utility to determine if broader adoption in a base class like `Bar` or `Toplevel` is warranted. +- Continue using view-specific events (e.g., `Menu.SelectedMenuItemChanged`, `CheckBox.CheckedStateChanged`, `FlagSelector.ValueChanged`, `ListView.SelectedItemChanged`, `Button.Clicked`) for their contextual specificity and clarity. +- Maintain and potentially formalize the use of `Accepted` in views like `Menu`, `MenuBar`, and `Dialog`, tracking its utility to determine if broader adoption in a base class like `Bar` or `Toplevel` is warranted. - If `Selected` or `Accepted` events are added in the future, ensure they fire only when their respective events (`Selecting`, `Accepting`) are not canceled (i.e., `args.Cancel` is `false`), maintaining consistency with the *Cancellable Work Pattern*’s post-event phase. ## Propagation of Selecting -The current implementation of `Command.Select` is local, but `MenuBarv2` requires propagation to manage `PopoverMenu` visibility, highlighting a limitation in the system’s ability to support hierarchical coordination without view-specific mechanisms. +The current implementation of `Command.Select` is local, but `MenuBar` requires propagation to manage `PopoverMenu` visibility, highlighting a limitation in the system’s ability to support hierarchical coordination without view-specific mechanisms. ### Current Behavior - **Selecting**: `Command.Select` is handled locally by the target view, with no propagation to the superview or other views. If the command is unhandled (returns `null` or `false`), processing stops without further routing. @@ -457,7 +457,7 @@ The current implementation of `Command.Select` is local, but `MenuBarv2` require } ``` - **Context Across Views**: - - In `Menuv2`, `Selecting` sets focus and raises `SelectedMenuItemChanged` to track changes, but this is a view-specific mechanism: + - In `Menu`, `Selecting` sets focus and raises `SelectedMenuItemChanged` to track changes, but this is a view-specific mechanism: ```csharp protected override void OnFocusedChanged(View? previousFocused, View? focused) { @@ -466,7 +466,7 @@ The current implementation of `Command.Select` is local, but `MenuBarv2` require RaiseSelectedMenuItemChanged(SelectedMenuItem); } ``` - - In `MenuBarv2`, `SelectedMenuItemChanged` is used to manage `PopoverMenu` visibility, but this relies on custom event handling rather than a generic propagation model: + - In `MenuBar`, `SelectedMenuItemChanged` is used to manage `PopoverMenu` visibility, but this relies on custom event handling rather than a generic propagation model: ```csharp protected override void OnSelectedMenuItemChanged(MenuItemv2? selected) { @@ -481,7 +481,7 @@ The current implementation of `Command.Select` is local, but `MenuBarv2` require - In `Button`, `Selecting` sets focus, which is inherently local. - **Accepting**: `Command.Accept` propagates to a default button (if present), the superview, or a `SuperMenuItem` (in menus), enabling hierarchical handling. - - **Rationale**: `Accepting` often involves actions that affect the broader UI context (e.g., closing a dialog, executing a menu command), requiring coordination with parent views. This is evident in `Menuv2`’s propagation to `SuperMenuItem` and `MenuBarv2`’s handling of `Accepted`: + - **Rationale**: `Accepting` often involves actions that affect the broader UI context (e.g., closing a dialog, executing a menu command), requiring coordination with parent views. This is evident in `Menu`’s propagation to `SuperMenuItem` and `MenuBar`’s handling of `Accepted`: ```csharp protected override void OnAccepting(CommandEventArgs args) { @@ -498,10 +498,10 @@ The current implementation of `Command.Select` is local, but `MenuBarv2` require ``` ### Should Selecting Propagate? -The local handling of `Command.Select` is sufficient for many views, but `MenuBarv2`’s need to manage `PopoverMenu` visibility highlights a gap in the current design, where hierarchical coordination relies on view-specific events like `SelectedMenuItemChanged`. +The local handling of `Command.Select` is sufficient for many views, but `MenuBar`’s need to manage `PopoverMenu` visibility highlights a gap in the current design, where hierarchical coordination relies on view-specific events like `SelectedMenuItemChanged`. - **Arguments For Propagation**: - - **Hierarchical Coordination**: In `MenuBarv2`, propagation would allow the menu bar to react to `MenuItemv2` selections (e.g., focusing a menu item via arrow keys or mouse enter) to show or hide popovers, streamlining the interaction model. Without propagation, `MenuBarv2` depends on `SelectedMenuItemChanged`, which is specific to `Menuv2` and not reusable for other hierarchical components. + - **Hierarchical Coordination**: In `MenuBar`, propagation would allow the menu bar to react to `MenuItemv2` selections (e.g., focusing a menu item via arrow keys or mouse enter) to show or hide popovers, streamlining the interaction model. Without propagation, `MenuBar` depends on `SelectedMenuItemChanged`, which is specific to `Menu` and not reusable for other hierarchical components. - **Consistency with Accepting**: `Command.Accept`’s propagation model supports hierarchical actions (e.g., dialog submission, menu command execution), suggesting that `Command.Select` could benefit from a similar approach to enable broader UI coordination, particularly in complex views like menus or dialogs. - **Future-Proofing**: Propagation could support other hierarchical components, such as `TabView` (coordinating tab selection) or nested dialogs (tracking subview state changes), enhancing the `Command` system’s flexibility for future use cases. @@ -530,7 +530,7 @@ The local handling of `Command.Select` is sufficient for many views, but `MenuBa }; ``` - **Performance and Complexity**: Propagation increases event handling overhead and complicates the API, as superviews must process or ignore `Selecting` events. This could lead to performance issues in deeply nested view hierarchies or views with frequent state changes. - - **Existing Alternatives**: View-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged` already provide mechanisms for superview coordination, negating the need for generic propagation in many cases. For instance, `MenuBarv2` uses `SelectedMenuItemChanged` to manage popovers, albeit in a view-specific way: + - **Existing Alternatives**: View-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged` already provide mechanisms for superview coordination, negating the need for generic propagation in many cases. For instance, `MenuBar` uses `SelectedMenuItemChanged` to manage popovers, albeit in a view-specific way: ```csharp protected override void OnSelectedMenuItemChanged(MenuItemv2? selected) { @@ -543,21 +543,21 @@ The local handling of `Command.Select` is sufficient for many views, but `MenuBa Similarly, `CheckBox` and `FlagSelector` use `CheckedStateChanged` and `ValueChanged` to notify superviews or external code of state changes, which is sufficient for most scenarios. - **Semantics of `Cancel`**: Propagation would occur only if `args.Cancel` is `false`, implying an unhandled selection, which is counterintuitive since `Selecting` typically completes its action (e.g., setting focus or toggling a state) within the view. This could confuse developers expecting propagation to occur for all `Selecting` events. -- **Context Insight**: The `MenuBarv2` implementation demonstrates a clear need for propagation to manage `PopoverMenu` visibility, as it must react to `MenuItemv2` selections (e.g., focus changes) across its submenu hierarchy. The reliance on `SelectedMenuItemChanged` works but is specific to `Menuv2`, limiting its applicability to other hierarchical components. In contrast, `CheckBox` and `FlagSelector` show that local handling is adequate for most stateful views, where state changes are self-contained or communicated via view-specific events. `ListView` similarly operates locally, with `SelectedItemChanged` or similar events handling external notifications. `Button`’s focus-based `Selecting` is inherently local, requiring no propagation. This dichotomy suggests that while propagation is critical for certain hierarchical scenarios (e.g., menus), it’s unnecessary for many views, and any propagation mechanism must avoid coupling subviews to superviews to maintain encapsulation. +- **Context Insight**: The `MenuBar` implementation demonstrates a clear need for propagation to manage `PopoverMenu` visibility, as it must react to `MenuItemv2` selections (e.g., focus changes) across its submenu hierarchy. The reliance on `SelectedMenuItemChanged` works but is specific to `Menu`, limiting its applicability to other hierarchical components. In contrast, `CheckBox` and `FlagSelector` show that local handling is adequate for most stateful views, where state changes are self-contained or communicated via view-specific events. `ListView` similarly operates locally, with `SelectedItemChanged` or similar events handling external notifications. `Button`’s focus-based `Selecting` is inherently local, requiring no propagation. This dichotomy suggests that while propagation is critical for certain hierarchical scenarios (e.g., menus), it’s unnecessary for many views, and any propagation mechanism must avoid coupling subviews to superviews to maintain encapsulation. -- **Verdict**: The local handling of `Command.Select` is sufficient for most views, including `CheckBox`, `FlagSelector`, `ListView`, and `Button`, where state changes or preparatory actions are internal or communicated via view-specific events. However, `MenuBarv2`’s requirement for hierarchical coordination to manage `PopoverMenu` visibility highlights a gap in the current design, where view-specific events like `SelectedMenuItemChanged` are used as a workaround. A generic propagation model would enhance flexibility for hierarchical components, but it must ensure that subviews (e.g., `MenuItemv2`) remain decoupled from superviews (e.g., `MenuBarv2`) to avoid implementation-specific dependencies. The current lack of propagation is a limitation, particularly for menus, but adding it requires careful design to avoid overcomplicating the API or impacting performance for views that don’t need it. +- **Verdict**: The local handling of `Command.Select` is sufficient for most views, including `CheckBox`, `FlagSelector`, `ListView`, and `Button`, where state changes or preparatory actions are internal or communicated via view-specific events. However, `MenuBar`’s requirement for hierarchical coordination to manage `PopoverMenu` visibility highlights a gap in the current design, where view-specific events like `SelectedMenuItemChanged` are used as a workaround. A generic propagation model would enhance flexibility for hierarchical components, but it must ensure that subviews (e.g., `MenuItemv2`) remain decoupled from superviews (e.g., `MenuBar`) to avoid implementation-specific dependencies. The current lack of propagation is a limitation, particularly for menus, but adding it requires careful design to avoid overcomplicating the API or impacting performance for views that don’t need it. -**Recommendation**: Maintain the local handling of `Command.Select` for now, as it meets the needs of most views like `CheckBox`, `FlagSelector`, and `ListView`. For `MenuBarv2`, continue using `SelectedMenuItemChanged` as a temporary solution, but prioritize developing a generic propagation mechanism that supports hierarchical coordination without coupling subviews to superviews. This mechanism should allow superviews to opt-in to receiving `Selecting` events from subviews, ensuring encapsulation (see appendix for a proposed solution). +**Recommendation**: Maintain the local handling of `Command.Select` for now, as it meets the needs of most views like `CheckBox`, `FlagSelector`, and `ListView`. For `MenuBar`, continue using `SelectedMenuItemChanged` as a temporary solution, but prioritize developing a generic propagation mechanism that supports hierarchical coordination without coupling subviews to superviews. This mechanism should allow superviews to opt-in to receiving `Selecting` events from subviews, ensuring encapsulation (see appendix for a proposed solution). ## Recommendations for Refining the Design -Based on the analysis of the current `Command` and `View.Command` system, as implemented in `Menuv2`, `MenuBarv2`, `CheckBox`, and `FlagSelector`, the following recommendations aim to refine the system’s clarity, consistency, and flexibility while addressing identified limitations: +Based on the analysis of the current `Command` and `View.Command` system, as implemented in `Menu`, `MenuBar`, `CheckBox`, and `FlagSelector`, the following recommendations aim to refine the system’s clarity, consistency, and flexibility while addressing identified limitations: 1. **Clarify Selecting/Accepting in Documentation**: - Explicitly define `Selecting` as state changes or interaction preparation (e.g., toggling a `CheckBox`, focusing a `MenuItemv2`, selecting a `ListView` item) and `Accepting` as action confirmations (e.g., executing a menu command, submitting a dialog). - Emphasize that `Command.Select` may set focus in stateless views (e.g., `Button`, `MenuItemv2`) but is primarily intended for state changes, to reduce confusion for developers. - - Provide examples for each view type (e.g., `Menuv2`, `CheckBox`, `FlagSelector`, `ListView`, `Button`) to illustrate their distinct roles. For instance: - - `Menuv2`: “`Selecting` focuses a `MenuItemv2` via arrow keys, while `Accepting` executes the selected command.” + - Provide examples for each view type (e.g., `Menu`, `CheckBox`, `FlagSelector`, `ListView`, `Button`) to illustrate their distinct roles. For instance: + - `Menu`: “`Selecting` focuses a `MenuItemv2` via arrow keys, while `Accepting` executes the selected command.” - `CheckBox`: “`Selecting` toggles the `CheckedState`, while `Accepting` confirms the current state.” - `FlagSelector`: “`Selecting` toggles a subview flag, while `Accepting` confirms the entire flag set.” - Document the `Cancel` property’s role in `CommandEventArgs`, noting its current limitation (implying negation rather than completion) and the planned replacement with `Handled` to align with input events like `Key.Handled`. @@ -577,7 +577,7 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp - This ensures `Selecting` only propagates state changes to the parent `FlagSelector` via `RaiseSelecting`, and `Accepting` is triggered separately (e.g., via Enter on the `FlagSelector` itself) to confirm the `Value`. 3. **Enhance ICommandContext with View-Specific State**: - - Enrich `ICommandContext` with a `State` property to include view-specific data (e.g., the selected `MenuItemv2` in `Menuv2`, the new `CheckedState` in `CheckBox`, the updated `Value` in `FlagSelector`). This enables more informed event handlers without requiring view-specific subscriptions. + - Enrich `ICommandContext` with a `State` property to include view-specific data (e.g., the selected `MenuItemv2` in `Menu`, the new `CheckedState` in `CheckBox`, the updated `Value` in `FlagSelector`). This enables more informed event handlers without requiring view-specific subscriptions. - Proposed interface update: ```csharp public interface ICommandContext @@ -588,7 +588,7 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp object? State { get; } // View-specific state (e.g., selected item, CheckState) } ``` - - Example: In `Menuv2`, include the `SelectedMenuItem` in `ICommandContext.State` for `Selecting` handlers: + - Example: In `Menu`, include the `SelectedMenuItem` in `ICommandContext.State` for `Selecting` handlers: ```csharp protected bool? RaiseSelecting(ICommandContext? ctx) { @@ -605,19 +605,19 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp - This enhances the flexibility of event handlers, allowing external code to react to state changes without subscribing to view-specific events like `SelectedMenuItemChanged` or `CheckedStateChanged`. 4. **Monitor Use Cases for Propagation Needs**: - - Track the usage of `Selecting` and `Accepting` in real-world applications, particularly in `Menuv2`, `MenuBarv2`, `CheckBox`, and `FlagSelector`, to identify scenarios where propagation of `Selecting` events could simplify hierarchical coordination. - - Collect feedback on whether the reliance on view-specific events (e.g., `SelectedMenuItemChanged` in `Menuv2`) is sufficient or if a generic propagation model would reduce complexity for hierarchical components like `MenuBarv2`. This will inform the design of a propagation mechanism that maintains subview-superview decoupling (see appendix). + - Track the usage of `Selecting` and `Accepting` in real-world applications, particularly in `Menu`, `MenuBar`, `CheckBox`, and `FlagSelector`, to identify scenarios where propagation of `Selecting` events could simplify hierarchical coordination. + - Collect feedback on whether the reliance on view-specific events (e.g., `SelectedMenuItemChanged` in `Menu`) is sufficient or if a generic propagation model would reduce complexity for hierarchical components like `MenuBar`. This will inform the design of a propagation mechanism that maintains subview-superview decoupling (see appendix). - Example focus areas: - - `MenuBarv2`: Assess whether `SelectedMenuItemChanged` adequately handles `PopoverMenu` visibility or if propagation would streamline the interaction model. + - `MenuBar`: Assess whether `SelectedMenuItemChanged` adequately handles `PopoverMenu` visibility or if propagation would streamline the interaction model. - `Dialog`: Evaluate whether `Selecting` propagation could enhance subview coordination (e.g., tracking checkbox toggles within a dialog). - `TabView`: Consider potential needs for tab selection coordination if implemented in the future. 5. **Improve Propagation for Hierarchical Views**: - - Recognize the limitation in `Command.Select`’s local handling for hierarchical components like `MenuBarv2`, where superviews need to react to subview selections (e.g., focusing a `MenuItemv2` to manage popovers). The current reliance on `SelectedMenuItemChanged` is effective but view-specific, limiting reusability. + - Recognize the limitation in `Command.Select`’s local handling for hierarchical components like `MenuBar`, where superviews need to react to subview selections (e.g., focusing a `MenuItemv2` to manage popovers). The current reliance on `SelectedMenuItemChanged` is effective but view-specific, limiting reusability. - Develop a propagation mechanism that allows superviews to opt-in to receiving `Selecting` events from subviews without requiring subviews to know superview details, ensuring encapsulation. This could involve a new event or property in `View` to enable propagation while maintaining decoupling (see appendix for a proposed solution). - - Example: For `MenuBarv2`, a propagation mechanism could allow it to handle `Selecting` events from `MenuItemv2` subviews to show or hide popovers, replacing the need for `SelectedMenuItemChanged`: + - Example: For `MenuBar`, a propagation mechanism could allow it to handle `Selecting` events from `MenuItemv2` subviews to show or hide popovers, replacing the need for `SelectedMenuItemChanged`: ```csharp - // Current workaround in MenuBarv2 + // Current workaround in MenuBar protected override void OnSelectedMenuItemChanged(MenuItemv2? selected) { if (IsOpen() && selected is MenuBarItemv2 { PopoverMenuOpen: false } selectedMenuBarItem) @@ -628,7 +628,7 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp ``` 6. **Standardize Hierarchical Handling for Accepting**: - - Refine the propagation model for `Command.Accept` to reduce reliance on view-specific logic, such as `Menuv2`’s use of `SuperMenuItem` for submenu propagation. The current approach, while functional, introduces coupling: + - Refine the propagation model for `Command.Accept` to reduce reliance on view-specific logic, such as `Menu`’s use of `SuperMenuItem` for submenu propagation. The current approach, while functional, introduces coupling: ```csharp if (SuperView is null && SuperMenuItem is {}) { @@ -636,9 +636,9 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp } ``` - Explore a more generic mechanism, such as allowing superviews to subscribe to `Accepting` events from subviews, to streamline propagation and improve encapsulation. This could be addressed in conjunction with `Selecting` propagation (see appendix). - - Example: In `Menuv2`, a subscription-based model could replace `SuperMenuItem` logic: + - Example: In `Menu`, a subscription-based model could replace `SuperMenuItem` logic: ```csharp - // Hypothetical subscription in Menuv2 + // Hypothetical subscription in Menu SubViewAdded += (sender, args) => { if (args.View is MenuItemv2 menuItem) @@ -650,15 +650,15 @@ Based on the analysis of the current `Command` and `View.Command` system, as imp ## Conclusion -The `Command` and `View.Command` system in Terminal.Gui provides a robust framework for handling view actions, with `Selecting` and `Accepting` serving as opinionated mechanisms for state changes/preparation and action confirmations. The system is effectively implemented across `Menuv2`, `MenuBarv2`, `CheckBox`, and `FlagSelector`, supporting a range of stateful and stateless interactions. However, limitations in terminology (`Select`’s ambiguity), cancellation semantics (`Cancel`’s misleading implication), and propagation (local `Selecting` handling) highlight areas for improvement. +The `Command` and `View.Command` system in Terminal.Gui provides a robust framework for handling view actions, with `Selecting` and `Accepting` serving as opinionated mechanisms for state changes/preparation and action confirmations. The system is effectively implemented across `Menu`, `MenuBar`, `CheckBox`, and `FlagSelector`, supporting a range of stateful and stateless interactions. However, limitations in terminology (`Select`’s ambiguity), cancellation semantics (`Cancel`’s misleading implication), and propagation (local `Selecting` handling) highlight areas for improvement. -The `Selecting`/`Accepting` distinction is clear in principle but requires careful documentation to avoid confusion, particularly for stateless views where `Selecting` is focus-driven and for views like `FlagSelector` where implementation flaws conflate the two concepts. View-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged` are sufficient for post-selection notifications, negating the need for a generic `Selected` event. The `Accepted` event is valuable in hierarchical views like `Menuv2` and `MenuBarv2` but not universally required, suggesting inclusion in `Bar` or `Toplevel` rather than `View`. +The `Selecting`/`Accepting` distinction is clear in principle but requires careful documentation to avoid confusion, particularly for stateless views where `Selecting` is focus-driven and for views like `FlagSelector` where implementation flaws conflate the two concepts. View-specific events like `SelectedMenuItemChanged`, `CheckedStateChanged`, and `ValueChanged` are sufficient for post-selection notifications, negating the need for a generic `Selected` event. The `Accepted` event is valuable in hierarchical views like `Menu` and `MenuBar` but not universally required, suggesting inclusion in `Bar` or `Toplevel` rather than `View`. -By clarifying terminology, fixing implementation flaws (e.g., `FlagSelector`), enhancing `ICommandContext`, and developing a decoupled propagation model, Terminal.Gui can enhance the `Command` system’s clarity and flexibility, particularly for hierarchical components like `MenuBarv2`. The appendix summarizes proposed changes to address these limitations, aligning with a filed issue to guide future improvements. +By clarifying terminology, fixing implementation flaws (e.g., `FlagSelector`), enhancing `ICommandContext`, and developing a decoupled propagation model, Terminal.Gui can enhance the `Command` system’s clarity and flexibility, particularly for hierarchical components like `MenuBar`. The appendix summarizes proposed changes to address these limitations, aligning with a filed issue to guide future improvements. ## Appendix: Summary of Proposed Changes to Command System -A filed issue proposes enhancements to the `Command` system to address limitations in terminology, cancellation semantics, and propagation, informed by `Menuv2`, `MenuBarv2`, `CheckBox`, and `FlagSelector`. These changes are not yet implemented but aim to improve clarity, consistency, and flexibility. +A filed issue proposes enhancements to the `Command` system to address limitations in terminology, cancellation semantics, and propagation, informed by `Menu`, `MenuBar`, `CheckBox`, and `FlagSelector`. These changes are not yet implemented but aim to improve clarity, consistency, and flexibility. ### Proposed Changes 1. **Rename `Command.Select` to `Command.Activate`**: @@ -672,9 +672,9 @@ A filed issue proposes enhancements to the `Command` system to address limitatio - Impact: Clarifies semantics, requires updating event handlers. 3. **Introduce `PropagateActivating` Event**: - - Add `event EventHandler? PropagateActivating` to `View`, allowing superviews (e.g., `MenuBarv2`) to subscribe to subview propagation requests. - - Rationale: Enables hierarchical coordination (e.g., `MenuBarv2` managing `PopoverMenu` visibility) without coupling subviews to superviews, addressing the current reliance on view-specific events like `SelectedMenuItemChanged`. - - Impact: Enhances flexibility for hierarchical views, requires subscription management in superviews like `MenuBarv2`. + - Add `event EventHandler? PropagateActivating` to `View`, allowing superviews (e.g., `MenuBar`) to subscribe to subview propagation requests. + - Rationale: Enables hierarchical coordination (e.g., `MenuBar` managing `PopoverMenu` visibility) without coupling subviews to superviews, addressing the current reliance on view-specific events like `SelectedMenuItemChanged`. + - Impact: Enhances flexibility for hierarchical views, requires subscription management in superviews like `MenuBar`. ### Benefits - **Clarity**: `Activate` improves terminology for all views. @@ -685,7 +685,7 @@ A filed issue proposes enhancements to the `Command` system to address limitatio ### Implementation Notes - Update `Command` enum, `View`, and derived classes for the rename. - Modify `CommandEventArgs` for `Handled`. -- Implement `PropagateActivating` and test in `MenuBarv2`. +- Implement `PropagateActivating` and test in `MenuBar`. - Revise documentation to reflect changes. For details, refer to the filed issue in the Terminal.Gui repository. \ No newline at end of file diff --git a/docfx/docs/events.md b/docfx/docs/events.md index 52f6363d7..88e7a457c 100644 --- a/docfx/docs/events.md +++ b/docfx/docs/events.md @@ -350,7 +350,7 @@ TG follows the *naming* advice provided in [.NET Naming Guidelines - Names of Ev ### Proposed Enhancement: Command Propagation -The *Cancellable Work Pattern* in `View.Command` currently supports local `Command.Activate` and propagating `Command.Accept`. To address hierarchical coordination needs (e.g., `MenuBarv2` popovers, `Dialog` closing), a `PropagatedCommands` property is proposed (Issue #4050): +The *Cancellable Work Pattern* in `View.Command` currently supports local `Command.Activate` and propagating `Command.Accept`. To address hierarchical coordination needs (e.g., `MenuBar` popovers, `Dialog` closing), a `PropagatedCommands` property is proposed (Issue #4050): - **Change**: Add `IReadOnlyList PropagatedCommands` to `View`, defaulting to `[Command.Accept]`. `Raise*` methods propagate if the command is in `SuperView?.PropagatedCommands` and `args.Handled` is `false`. - **Example**: @@ -373,7 +373,7 @@ The *Cancellable Work Pattern* in `View.Command` currently supports local `Comma } ``` -- **Impact**: Enables `Command.Activate` propagation for `MenuBarv2` while preserving `Command.Accept` propagation, maintaining decoupling and avoiding noise from irrelevant commands. +- **Impact**: Enables `Command.Activate` propagation for `MenuBar` while preserving `Command.Accept` propagation, maintaining decoupling and avoiding noise from irrelevant commands. ### **Conflation in FlagSelector**: - **Issue**: `CheckBox.Activating` triggers `Accepting`, conflating state change and confirmation. @@ -389,7 +389,7 @@ The *Cancellable Work Pattern* in `View.Command` currently supports local `Comma ``` ### **Propagation Limitations**: - - **Issue**: Local `Command.Activate` restricts `MenuBarv2` coordination; `Command.Accept` uses hacks (#3925). + - **Issue**: Local `Command.Activate` restricts `MenuBar` coordination; `Command.Accept` uses hacks (#3925). - **Recommendation**: Adopt `PropagatedCommands` to enable targeted propagation, as proposed. ### **Complexity in Multi-Phase Workflows**: diff --git a/docfx/docs/newinv2.md b/docfx/docs/newinv2.md index 43dec8e87..570507950 100644 --- a/docfx/docs/newinv2.md +++ b/docfx/docs/newinv2.md @@ -173,14 +173,14 @@ See the [Views Overview](views.md) for a complete catalog of all built-in views. v2 introduces many new View subclasses that were not present in v1: -- **Bar**: A foundational view for horizontal or vertical layouts of `Shortcut` or other items, used in `StatusBar`, `MenuBarv2`, and `PopoverMenu`. +- **Bar**: A foundational view for horizontal or vertical layouts of `Shortcut` or other items, used in `StatusBar`, `MenuBar`, and `PopoverMenu`. - **CharMap**: A scrollable, searchable Unicode character map with support for the Unicode Character Database (UCD) API, enabling users to browse and select from all Unicode codepoints with detailed character information. See [Character Map Deep Dive](CharacterMap.md). - **ColorPicker**: Leverages TrueColor for a comprehensive color selection experience, supporting multiple color models (HSV, RGB, HSL, Grayscale) with interactive color bars. - **DatePicker**: Provides a calendar-based date selection UI with month/year navigation, leveraging v2's improved drawing and navigation systems. - **FlagSelector**: Enables selection of non-mutually-exclusive flags with checkbox-based UI, supporting both dictionary-based and enum-based flag definitions. - **GraphView**: Displays graphs (bar charts, scatter plots, line graphs) with flexible axes, labels, scaling, scrolling, and annotations - bringing data visualization to the terminal. - **Line**: Draws single horizontal or vertical lines using the `LineCanvas` system with automatic intersection handling and multiple line styles (Single, Double, Heavy, Rounded, Dashed, Dotted). -- **Menuv2 System** (MenuBarv2, PopoverMenu): A completely redesigned menu system built on the `Bar` infrastructure, providing a more flexible and visually appealing menu experience. +- **Menu System** (MenuBar, PopoverMenu): A completely redesigned menu system built on the `Bar` infrastructure, providing a more flexible and visually appealing menu experience. - **NumericUpDown**: Type-safe numeric input with increment/decrement buttons, supporting `int`, `long`, `float`, `double`, and `decimal` types. - **OptionSelector**: Displays a list of mutually-exclusive options with checkbox-style UI (radio button equivalent), supporting both horizontal and vertical orientations. - **Shortcut**: An opinionated view for displaying commands with key bindings, simplifying status bar and toolbar creation with consistent visual presentation. diff --git a/docfx/schemas/tui-config-schema.json b/docfx/schemas/tui-config-schema.json index 52b7d7f38..8eb937141 100644 --- a/docfx/schemas/tui-config-schema.json +++ b/docfx/schemas/tui-config-schema.json @@ -572,7 +572,7 @@ ], "description": "Default shadow style for Button controls, often used for 3D effect." }, - "Menuv2.DefaultBorderStyle": { + "Menu.DefaultBorderStyle": { "type": "string", "enum": [ "None", @@ -581,9 +581,9 @@ "Heavy", "Rounded" ], - "description": "Default border style for the newer Menuv2 control and its sub-menus." + "description": "Default border style for the newer Menu control and its sub-menus." }, - "MenuBarv2.DefaultBorderStyle": { + "MenuBar.DefaultBorderStyle": { "type": "string", "enum": [ "None", @@ -592,7 +592,7 @@ "Heavy", "Rounded" ], - "description": "Default border style for the MenuBarv2 control." + "description": "Default border style for the MenuBar control." }, "StatusBar.DefaultSeparatorLineStyle": { "type": "string",