Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 3f3d2b5

Browse files
committedMar 16, 2025·
Cascading mostly working
1 parent 33d4f3d commit 3f3d2b5

File tree

8 files changed

+192
-89
lines changed

8 files changed

+192
-89
lines changed
 

‎Terminal.Gui/Application/Application.Popover.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ public static partial class Application // Popover handling
1717
/// If the user clicks anywhere not occulded by a SubView of the PopoverHost, the PopoverHost will be hidden.
1818
/// </para>
1919
/// </remarks>
20-
public static PopoverHost? PopoverHost { get; internal set; }
20+
public static PopoverHost? PopoverHost { get; set; }
2121
}

‎Terminal.Gui/View/View.Keyboard.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -604,7 +604,7 @@ internal bool InvokeCommandsBoundToHotKey (Key hotKey, ref bool? handled)
604604
}
605605

606606
// Now, process any HotKey bindings in the subviews
607-
foreach (View subview in InternalSubViews)
607+
foreach (View subview in InternalSubViews.ToList())
608608
{
609609
if (subview == Focused)
610610
{

‎Terminal.Gui/View/View.Layout.cs

+8-4
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ out int ny
10481048
int maxDimension;
10491049
View? superView;
10501050

1051-
if (viewToMove is not Toplevel || viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
1051+
if (viewToMove?.SuperView is null || viewToMove == Application.Top || viewToMove?.SuperView == Application.Top)
10521052
{
10531053
maxDimension = Application.Screen.Width;
10541054
superView = Application.Top;
@@ -1077,7 +1077,7 @@ out int ny
10771077
}
10781078
else
10791079
{
1080-
nx = targetX;
1080+
nx = 0;//targetX;
10811081
}
10821082

10831083
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
@@ -1141,10 +1141,14 @@ out int ny
11411141
ny = Math.Max (viewToMove.Frame.Bottom, 0);
11421142
}
11431143
}
1144+
else
1145+
{
1146+
ny = 0;
1147+
}
11441148

1145-
//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
1149+
//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
11461150

1147-
return superView!;
1151+
return superView!;
11481152
}
11491153

11501154
#endregion Utilities

‎Terminal.Gui/Views/Bar.cs

-52
Original file line numberDiff line numberDiff line change
@@ -32,58 +32,6 @@ public Bar (IEnumerable<Shortcut>? shortcuts)
3232
// Initialized += Bar_Initialized;
3333
MouseEvent += OnMouseEvent;
3434

35-
AddCommand (Command.Right, MoveRight);
36-
37-
bool? MoveRight (ICommandContext? ctx)
38-
{
39-
if (Orientation == Orientation.Vertical)
40-
{
41-
return false;
42-
}
43-
44-
return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
45-
}
46-
47-
AddCommand (Command.Left, MoveLeft);
48-
49-
bool? MoveLeft (ICommandContext? ctx)
50-
{
51-
if (Orientation == Orientation.Vertical)
52-
{
53-
return false;
54-
}
55-
56-
return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
57-
}
58-
59-
AddCommand (Command.Down, MoveDown);
60-
61-
bool? MoveDown (ICommandContext? ctx)
62-
{
63-
if (Orientation == Orientation.Horizontal)
64-
{
65-
return false;
66-
}
67-
68-
return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
69-
}
70-
71-
AddCommand (Command.Up, MoveUp);
72-
73-
bool? MoveUp (ICommandContext? ctx)
74-
{
75-
if (Orientation == Orientation.Horizontal)
76-
{
77-
return false;
78-
}
79-
80-
return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
81-
}
82-
83-
KeyBindings.Add (Key.CursorRight, Command.Right);
84-
KeyBindings.Add (Key.CursorDown, Command.Down);
85-
KeyBindings.Add (Key.CursorLeft, Command.Left);
86-
KeyBindings.Add (Key.CursorUp, Command.Up);
8735

8836
if (shortcuts is { })
8937
{

‎Terminal.Gui/Views/Menu/MenuItemv2.cs

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public MenuItemv2 (View targetView, Command command, string commandText, string?
5353
{
5454
// TODO: This is a temporary hack - add a flag or something instead
5555
KeyView.Text = $"{Glyphs.RightArrow}";
56+
subMenu.SuperMenuItem = this;
5657
}
5758

5859
SubMenu = subMenu;

‎Terminal.Gui/Views/Menu/Menuv2.cs

+4-20
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ public Menuv2 (IEnumerable<Shortcut> shortcuts) : base (shortcuts)
2121
Height = Dim.Auto (DimAutoStyle.Content, 1);
2222
Initialized += Menuv2_Initialized;
2323
VisibleChanged += OnVisibleChanged;
24+
2425
}
2526

27+
public MenuItemv2 SuperMenuItem { get; set; }
28+
2629
private void OnVisibleChanged (object? sender, EventArgs e)
2730
{
2831
if (Visible)
@@ -150,7 +153,7 @@ internal void RaiseSelectedMenuItemChanged (MenuItemv2? selected)
150153
{
151154
//Logging.Trace ($"RaiseSelectedMenuItemChanged: {selected?.Title}");
152155

153-
ShowSubMenu (selected);
156+
//ShowSubMenu (selected);
154157
OnSelectedMenuItemChanged (selected);
155158

156159
SelectedMenuItemChanged?.Invoke (this, selected);
@@ -169,23 +172,4 @@ protected virtual void OnSelectedMenuItemChanged (MenuItemv2? selected)
169172
/// </summary>
170173
public event EventHandler<MenuItemv2?>? SelectedMenuItemChanged;
171174

172-
public void ShowSubMenu (MenuItemv2? menuItem)
173-
{
174-
// Hide any other submenus that might be visible
175-
foreach (MenuItemv2 mi in SubViews.Where (v => v is MenuItemv2 { SubMenu.Visible: true }).Cast<MenuItemv2> ())
176-
{
177-
mi.ForceFocusColors = false;
178-
mi.SubMenu!.Visible = false;
179-
SuperView?.Remove (mi.SubMenu);
180-
}
181-
182-
if (menuItem is { SubMenu: {} })
183-
{
184-
SuperView?.Add (menuItem.SubMenu);
185-
menuItem.SubMenu.X = Frame.X + Frame.Width;
186-
menuItem.SubMenu.Y = Frame.Y + menuItem.Frame.Y;
187-
menuItem.SubMenu.Visible = true;
188-
menuItem.ForceFocusColors = true;
189-
}
190-
}
191175
}

‎Terminal.Gui/Views/Menu/PopoverMenu.cs

+112
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
#nullable enable
2+
using Microsoft.CodeAnalysis;
3+
24
namespace Terminal.Gui;
35

46
/// <summary>
@@ -28,6 +30,63 @@ public PopoverMenu (Menuv2? root)
2830

2931
Root = root;
3032

33+
AddCommand (Command.Right, MoveRight);
34+
bool? MoveRight (ICommandContext? ctx)
35+
{
36+
MenuItemv2? focused = MostFocused as MenuItemv2;
37+
38+
if (focused is { SubMenu.Visible: true })
39+
{
40+
focused.SubMenu.SetFocus ();
41+
42+
return true;
43+
}
44+
45+
return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
46+
}
47+
KeyBindings.Add (Key.CursorRight, Command.Right);
48+
49+
AddCommand (Command.Left, MoveLeft);
50+
bool? MoveLeft (ICommandContext? ctx)
51+
{
52+
if (MostFocused is MenuItemv2 { SuperView: Menuv2 focusedMenu })
53+
{
54+
focusedMenu.SuperMenuItem?.SetFocus ();
55+
56+
return true;
57+
}
58+
return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
59+
}
60+
KeyBindings.Add (Key.CursorLeft, Command.Left);
61+
62+
//AddCommand (Command.Down, MoveDown);
63+
64+
//bool? MoveDown (ICommandContext? ctx)
65+
//{
66+
// if (Orientation == Orientation.Horizontal)
67+
// {
68+
// return false;
69+
// }
70+
71+
// return AdvanceFocus (NavigationDirection.Forward, TabBehavior.TabStop);
72+
//}
73+
74+
//AddCommand (Command.Up, MoveUp);
75+
76+
//bool? MoveUp (ICommandContext? ctx)
77+
//{
78+
// if (Orientation == Orientation.Horizontal)
79+
// {
80+
// return false;
81+
// }
82+
83+
// return AdvanceFocus (NavigationDirection.Backward, TabBehavior.TabStop);
84+
//}
85+
86+
87+
//KeyBindings.Add (Key.CursorDown, Command.Down);
88+
//KeyBindings.Add (Key.CursorUp, Command.Up);
89+
3190
}
3291

3392
private Menuv2? _root;
@@ -50,6 +109,7 @@ public Menuv2? Root
50109
base.Remove (_root);
51110
_root.Accepting -= RootOnAccepting;
52111
_root.MenuItemCommandInvoked -= RootOnMenuItemCommandInvoked;
112+
_root.SelectedMenuItemChanged -= RootOnSelectedMenuItemChanged;
53113
}
54114

55115
_root = value;
@@ -59,6 +119,9 @@ public Menuv2? Root
59119
base.Add (_root);
60120
_root.Accepting += RootOnAccepting;
61121
_root.MenuItemCommandInvoked += RootOnMenuItemCommandInvoked;
122+
_root.SelectedMenuItemChanged += RootOnSelectedMenuItemChanged;
123+
124+
62125
}
63126

64127
return;
@@ -72,6 +135,55 @@ void RootOnAccepting (object? sender, CommandEventArgs e)
72135
{
73136
Logging.Trace ($"RootOnAccepting: {e.Context}");
74137
}
138+
139+
void RootOnSelectedMenuItemChanged (object? sender, MenuItemv2? e)
140+
{
141+
Logging.Trace ($"RootOnSelectedMenuItemChanged: {e.Title}");
142+
ShowSubMenu (e);
143+
}
144+
75145
}
76146
}
147+
public void ShowSubMenu (MenuItemv2? menuItem)
148+
{
149+
// Hide any other submenus that might be visible
150+
foreach (MenuItemv2 mi in menuItem.SuperView.SubViews.Where (v => v is MenuItemv2 { SubMenu.Visible: true }).Cast<MenuItemv2> ())
151+
{
152+
mi.ForceFocusColors = false;
153+
mi.SubMenu!.Visible = false;
154+
Remove (mi.SubMenu);
155+
}
156+
157+
if (menuItem is { SubMenu: { Visible: false } })
158+
{
159+
Add (menuItem.SubMenu);
160+
Point pos = GetMostVisibleLocationForSubMenu (menuItem);
161+
menuItem.SubMenu.X = pos.X;
162+
menuItem.SubMenu.Y = pos.Y;
163+
164+
menuItem.SubMenu.Visible = true;
165+
menuItem.ForceFocusColors = true;
166+
}
167+
}
168+
169+
/// <summary>
170+
/// Given a <see cref="MenuItemv2"/>, returns the most visible location for the submenu.
171+
/// The location is relative to the Frame.
172+
/// </summary>
173+
/// <param name="menuItem"></param>
174+
/// <returns></returns>
175+
internal Point GetMostVisibleLocationForSubMenu (MenuItemv2 menuItem)
176+
{
177+
Point pos = Point.Empty;
178+
179+
// Calculate the initial position to the right of the menu item
180+
pos.X = menuItem.SuperView!.Frame.X + menuItem.Frame.Width;
181+
pos.Y = menuItem.SuperView.Frame.Y + menuItem.Frame.Y;
182+
183+
GetLocationEnsuringFullVisibility (menuItem.SubMenu, pos.X, pos.Y, out int nx, out int ny);
184+
185+
186+
return new (nx,ny);
187+
}
188+
77189
}

‎UICatalog/Scenarios/MenusV2.cs

+65-11
Original file line numberDiff line numberDiff line change
@@ -41,8 +41,10 @@
4141
Id = "frame",
4242
Title = "Cascading Menu...",
4343

44-
Width = Dim.Fill ()! - Dim.Width (eventLog),
45-
Height = Dim.Fill (),
44+
X = 4,
45+
Y = 4,
46+
Width = Dim.Fill (8)! - Dim.Width (eventLog),
47+
Height = Dim.Fill (8),
4648
BorderStyle = LineStyle.Dotted
4749
};
4850
app.Add (frame);
@@ -53,15 +55,25 @@
5355
};
5456
ConfigureRootMenu (frame, rootMenu);
5557

56-
var subMenu = new Menuv2
58+
var optionsSubMenu = new Menuv2
5759
{
58-
Id = "subMenu",
60+
Id = "optionsSubMenu",
5961
Visible = false
6062
};
61-
ConfigureSubMenu1 (frame, subMenu);
63+
ConfigureOptionsSubMenu (frame, optionsSubMenu);
6264

63-
var cascadeShortcut = new MenuItemv2 (frame, Command.Accept, "_Options", "File options", subMenu);
64-
rootMenu.Add (cascadeShortcut);
65+
var optionsSubMenuItem = new MenuItemv2 (frame, Command.Accept, "O_ptions", "File options", optionsSubMenu);
66+
rootMenu.Add (optionsSubMenuItem);
67+
68+
var detailsSubMenu = new Menuv2
69+
{
70+
Id = "detailsSubMenu",
71+
Visible = false
72+
};
73+
ConfigureDetialsSubMenu (frame, detailsSubMenu);
74+
75+
var detailsSubMenuItem = new MenuItemv2 (frame, Command.Accept, "_Details", "File details", detailsSubMenu);
76+
rootMenu.Add (detailsSubMenuItem);
6577

6678
var popoverMenu = new PopoverMenu (rootMenu)
6779
{
@@ -93,7 +105,7 @@
93105

94106
popoverMenu.Accepting += (o, args) =>
95107
{
96108
Logging.Trace ($"Accepting: {popoverMenu!.Id} {args.Context.Command}");
97109
//eventSource.Add ($"Accepting: {menu!.Id} {args.Context.Command}");
98110
//eventLog.MoveDown ();
99111
//args.Cancel = true;
@@ -101,7 +113,7 @@
101113

102114
popoverMenu.Selecting += (o, args) =>
103115
{
104116
Logging.Trace ($"Selecting: {popoverMenu!.Id} {args.Context.Command}");
105117
//eventSource.Add ($"Selecting: {menu!.Id} {args.Context.Command}");
106118
//eventLog.MoveDown ();
107119
//args.Cancel = false;
@@ -121,7 +133,7 @@
121133
//// };
122134

123135

124136
foreach (View view2 in popoverMenu.Root.SubViews.Where (s => s is MenuItemv2)!)
125137
{
126138
var sh = (MenuItemv2)view2;
127139

@@ -219,20 +231,20 @@
219231
}
220232

221233

222-
private void ConfigureSubMenu1 (View targetView, Menuv2 menu)
234+
private void ConfigureOptionsSubMenu (View targetView, Menuv2 menu)
223235
{
224236
var shortcut2 = new MenuItemv2
225237
{
226-
Title = "Za_G",
227-
Text = "Gonna zag",
238+
Title = "_Option 1",
239+
Text = "Some option #1",
228240
Key = Key.G.WithAlt
229241
};
230242

231243
var shortcut3 = new MenuItemv2
232244
{
233245
Title = "_Three",
234246
Text = "The 3rd item",
235-
Key = Key.D3.WithAlt
247+
Key = Key.T.WithAlt
236248
};
237249

238250
var line = new Line
@@ -245,9 +257,51 @@
245257
{
246258
Title = "_Four",
247259
Text = "Below the line",
260+
Key = Key.D7.WithAlt
261+
};
262+
263+
shortcut4.CommandView = new CheckBox
264+
{
265+
Title = shortcut4.Title,
266+
HighlightStyle = HighlightStyle.None,
267+
CanFocus = false
268+
};
269+
270+
// This ensures the checkbox state toggles when the hotkey of Title is pressed.
271+
//shortcut4.Accepting += (sender, args) => args.Cancel = true;
272+
273+
menu.Add (shortcut2, shortcut3, line, shortcut4);
274+
}
275+
276+
private void ConfigureDetialsSubMenu (View targetView, Menuv2 menu)
277+
{
278+
var shortcut2 = new MenuItemv2
279+
{
280+
Title = "_Detail 1",
281+
Text = "Some detail #1",
282+
Key = Key.G.WithAlt
283+
};
284+
285+
var shortcut3 = new MenuItemv2
286+
{
287+
Title = "_Three",
288+
Text = "The 3rd item",
248289
Key = Key.D3.WithAlt
249290
};
250291

292+
var line = new Line
293+
{
294+
X = -1,
295+
Width = Dim.Fill ()! + 1
296+
};
297+
298+
var shortcut4 = new MenuItemv2
299+
{
300+
Title = "_Four",
301+
Text = "Below the line",
302+
Key = Key.D8.WithAlt
303+
};
304+
251305
shortcut4.CommandView = new CheckBox
252306
{
253307
Title = shortcut4.Title,

0 commit comments

Comments
 (0)
Please sign in to comment.