Skip to content

Commit cabe411

Browse files
committed
Fixes gui-cs#3691 - Adds ViewArrangement.Popover (gui-cs#3852)
* Added Applicaton.Popover. Refactored FindDeepestView * Popover prototype * Testing highlight * Fixed click outside issue * Fixed DialogTests * Fixed click outside issue (agbain) * Enabled mouse wheel in Bar * Enabled mouse wheel in Bar * Progress. Broke arrangement * Added popover tests. Fixed a bunch more CM issues related ot unreliable unit tests. Updated config.json to include Glyphs. * Can't set ForceDriver to empty in Resources/config.json. * added BUGBUG * Made Position/ScreenPosition clear * Added View.IsInHierarchy tests * Added Contextmenuv2 scenario. * Implemented CM2 in TextView * Removed unneeded CM stuff from testhelpers * Shortcut API docs * Fixed keybinding unit tests * Fixed mouse handling * Fighting with CM related unit test failures * Unit tests pass. I think. * Shortcut code cleanup * TextView uses new CM2 * Starting on OnSelect etc... * Starting on OnSelect etc... * Fixed ContextMenuv2 * ContextMenu is working again. * Ugh. ANd fixed button api docs * Fixed DrawHorizontalShadowTransparent (vertical was already fixed). * Made Scenarios compatible with #nullable enable * Undid some keybinding stuff * Fixed stuff * Sped up unit tests * Sped up unit tests 2 * Sped up unit tests 3 * Messing with menus * merged latest v2_develop * Added more Popover unit tests * Added more Popover unit tests2 * Fixed positioning bug * Fixed mouse bug * Fixed Bar draw issue * WIP * merge v2_develop * CM2 sorta works * Enabled Bar subclasses to have IDesignable * Added ViewportSettings.Transparent * Region -> nullable enable * Added ViewportSettigs Editor * merged v2_develop part 2 * merged v2_develop part 3 * WIP: GetViewsUnderMouse * WIP: More GetViewsUnderMouse work * Bars works again * Added unit tests * CM now works * MenuItemv2 POC * SubMenu POC * CommandNotBound * More POC * Optimize Margin to not defer draw if there's no shadow * Logger cleanup * Reverted Generic * Cascading mostly working * fixed layout bug * API docs * API docs * Fixed cascade * Events basically work * code cleanup * Fixed IsDefault bug; * Enabled hotkey support * Made context-menu-like * Improved usability * Refactored ApplicationPopover again * Cleanup * Menuv2 POC basically complete * Code Cleanup * Made menu API simpler * Fixed Strings bugs * Got old ContextMenu scenario mostly working * ContextMenu scenario now works * ContextMenu fixes * ContextMenu fixes * Tons of menu cleanup * ContextMenu works in TextView * Fixed unit tes * Added unit tests * Fixed tests * code cleanup * More code cleanup * Deep dive * scenario * typos * Demo colorpicker in a Menu * Added Region tests proving Region is broken in some Union cases * fixed v2win/net
1 parent e089108 commit cabe411

File tree

89 files changed

+4438
-911
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+4438
-911
lines changed

Terminal.Gui/Application/Application.Initialization.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ internal static void InternalInit (
8383
}
8484

8585
Navigation = new ();
86+
Popover = new ();
8687

8788
// For UnitTests
8889
if (driver is { })
@@ -162,6 +163,12 @@ internal static void InternalInit (
162163

163164
SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext ());
164165

166+
// TODO: This is probably not needed
167+
if (Popover.GetActivePopover () is View popover)
168+
{
169+
popover.Visible = false;
170+
}
171+
165172
MainThreadId = Thread.CurrentThread.ManagedThreadId;
166173
bool init = Initialized = true;
167174
InitializedChanged?.Invoke (null, new (init));
@@ -265,6 +272,6 @@ internal static void UnsubscribeDriverEvents ()
265272
/// </summary>
266273
internal static void OnInitializedChanged (object sender, EventArgs<bool> e)
267274
{
268-
Application.InitializedChanged?.Invoke (sender,e);
275+
Application.InitializedChanged?.Invoke (sender, e);
269276
}
270277
}

Terminal.Gui/Application/Application.Keyboard.cs

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,19 @@ public static partial class Application // Keyboard handling
1313
/// <returns><see langword="true"/> if the key was handled.</returns>
1414
public static bool RaiseKeyDownEvent (Key key)
1515
{
16+
// TODO: This should match standard event patterns
1617
KeyDown?.Invoke (null, key);
1718

1819
if (key.Handled)
1920
{
2021
return true;
2122
}
2223

24+
if (Popover?.DispatchKeyDown (key) is true)
25+
{
26+
return true;
27+
}
28+
2329
if (Top is null)
2430
{
2531
foreach (Toplevel topLevel in TopLevels.ToList ())
@@ -43,6 +49,27 @@ public static bool RaiseKeyDownEvent (Key key)
4349
}
4450
}
4551

52+
bool? commandHandled = InvokeCommandsBoundToKey (key);
53+
if(commandHandled is true)
54+
{
55+
return true;
56+
}
57+
58+
return false;
59+
}
60+
61+
/// <summary>
62+
/// Invokes any commands bound at the Application-level to <paramref name="key"/>.
63+
/// </summary>
64+
/// <param name="key"></param>
65+
/// <returns>
66+
/// <see langword="null"/> if no command was found; input processing should continue.
67+
/// <see langword="false"/> if the command was invoked and was not handled (or cancelled); input processing should continue.
68+
/// <see langword="true"/> if the command was invoked the command was handled (or cancelled); input processing should stop.
69+
/// </returns>
70+
public static bool? InvokeCommandsBoundToKey (Key key)
71+
{
72+
bool? handled = null;
4673
// Invoke any Application-scoped KeyBindings.
4774
// The first view that handles the key will stop the loop.
4875
// foreach (KeyValuePair<Key, KeyBinding> binding in KeyBindings.GetBindings (key))
@@ -52,22 +79,17 @@ public static bool RaiseKeyDownEvent (Key key)
5279
{
5380
if (!binding.Target.Enabled)
5481
{
55-
return false;
82+
return null;
5683
}
5784

58-
bool? handled = binding.Target?.InvokeCommands (binding.Commands, binding);
59-
60-
if (handled != null && (bool)handled)
61-
{
62-
return true;
63-
}
85+
handled = binding.Target?.InvokeCommands (binding.Commands, binding);
6486
}
6587
else
6688
{
6789
// BUGBUG: this seems unneeded.
6890
if (!KeyBindings.TryGet (key, out KeyBinding keybinding))
6991
{
70-
return false;
92+
return null;
7193
}
7294

7395
bool? toReturn = null;
@@ -77,30 +99,42 @@ public static bool RaiseKeyDownEvent (Key key)
7799
toReturn = InvokeCommand (command, key, keybinding);
78100
}
79101

80-
return toReturn ?? true;
102+
handled = toReturn ?? true;
81103
}
82104
}
83105

84-
return false;
106+
return handled;
107+
}
85108

86-
static bool? InvokeCommand (Command command, Key key, KeyBinding binding)
109+
/// <summary>
110+
/// Invokes an Application-bound commmand.
111+
/// </summary>
112+
/// <param name="command">The Command to invoke</param>
113+
/// <param name="key">The Application-bound Key that was pressed.</param>
114+
/// <param name="binding">Describes the binding.</param>
115+
/// <returns>
116+
/// <see langword="null"/> if no command was found; input processing should continue.
117+
/// <see langword="false"/> if the command was invoked and was not handled (or cancelled); input processing should continue.
118+
/// <see langword="true"/> if the command was invoked the command was handled (or cancelled); input processing should stop.
119+
/// </returns>
120+
/// <exception cref="NotSupportedException"></exception>
121+
public static bool? InvokeCommand (Command command, Key key, KeyBinding binding)
122+
{
123+
if (!_commandImplementations!.ContainsKey (command))
87124
{
88-
if (!_commandImplementations!.ContainsKey (command))
89-
{
90-
throw new NotSupportedException (
91-
@$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by Application."
92-
);
93-
}
94-
95-
if (_commandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
96-
{
97-
CommandContext<KeyBinding> context = new (command, binding); // Create the context here
125+
throw new NotSupportedException (
126+
@$"A KeyBinding was set up for the command {command} ({key}) but that command is not supported by Application."
127+
);
128+
}
98129

99-
return implementation (context);
100-
}
130+
if (_commandImplementations.TryGetValue (command, out View.CommandImplementation? implementation))
131+
{
132+
CommandContext<KeyBinding> context = new (command, null, binding); // Create the context here
101133

102-
return false;
134+
return implementation (context);
103135
}
136+
137+
return null;
104138
}
105139

106140
/// <summary>
@@ -167,7 +201,7 @@ internal static void AddKeyBindings ()
167201
{
168202
_commandImplementations.Clear ();
169203

170-
// Things this view knows how to do
204+
// Things Application knows how to do
171205
AddCommand (
172206
Command.Quit,
173207
static () =>
@@ -213,7 +247,7 @@ internal static void AddKeyBindings ()
213247
);
214248

215249
AddCommand (
216-
Command.Edit,
250+
Command.Arrange,
217251
static () =>
218252
{
219253
View? viewToArrange = Navigation?.GetFocused ();
@@ -249,7 +283,7 @@ internal static void AddKeyBindings ()
249283
KeyBindings.Add (PrevTabKey, Command.PreviousTabStop);
250284
KeyBindings.Add (NextTabGroupKey, Command.NextTabGroup);
251285
KeyBindings.Add (PrevTabGroupKey, Command.PreviousTabGroup);
252-
KeyBindings.Add (ArrangeKey, Command.Edit);
286+
KeyBindings.Add (ArrangeKey, Command.Arrange);
253287

254288
KeyBindings.Add (Key.CursorRight, Command.NextTabStop);
255289
KeyBindings.Add (Key.CursorDown, Command.NextTabStop);

Terminal.Gui/Application/Application.Mouse.cs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#nullable enable
22
using System.ComponentModel;
3+
using System.Diagnostics;
34

45
namespace Terminal.Gui;
56

@@ -168,6 +169,20 @@ internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
168169
return;
169170
}
170171

172+
// Dismiss the Popover if the user presses mouse outside of it
173+
if (mouseEvent.IsPressed
174+
&& Popover?.GetActivePopover () as View is { Visible: true } visiblePopover
175+
&& View.IsInHierarchy (visiblePopover, deepestViewUnderMouse, includeAdornments: true) is false)
176+
{
177+
178+
visiblePopover.Visible = false;
179+
180+
// Recurse once so the event can be handled below the popover
181+
RaiseMouseEvent (mouseEvent);
182+
183+
return;
184+
}
185+
171186
if (HandleMouseGrab (deepestViewUnderMouse, mouseEvent))
172187
{
173188
return;
@@ -216,6 +231,7 @@ internal static void RaiseMouseEvent (MouseEventArgs mouseEvent)
216231
else
217232
{
218233
// The mouse was outside any View's Viewport.
234+
//Debug.Fail ("this should not happen.");
219235

220236
// Debug.Fail ("This should never happen. If it does please file an Issue!!");
221237

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#nullable enable
2+
3+
namespace Terminal.Gui;
4+
5+
public static partial class Application // Popover handling
6+
{
7+
/// <summary>Gets the Application <see cref="Popover"/> manager.</summary>
8+
public static ApplicationPopover? Popover { get; internal set; }
9+
}

Terminal.Gui/Application/Application.Run.cs

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,7 +337,7 @@ public static Toplevel Run (Func<Exception, bool>? errorHandler = null, IConsole
337337
[RequiresUnreferencedCode ("AOT")]
338338
[RequiresDynamicCode ("AOT")]
339339
public static T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriver? driver = null)
340-
where T : Toplevel, new ()
340+
where T : Toplevel, new()
341341
{
342342
return ApplicationImpl.Instance.Run<T> (errorHandler, driver);
343343
}
@@ -426,7 +426,16 @@ public static T Run<T> (Func<Exception, bool>? errorHandler = null, IConsoleDriv
426426

427427
internal static void LayoutAndDrawImpl (bool forceDraw = false)
428428
{
429-
bool neededLayout = View.Layout (TopLevels.Reverse (), Screen.Size);
429+
List<View> tops = [..TopLevels];
430+
431+
if (Popover?.GetActivePopover () as View is { Visible: true } visiblePopover)
432+
{
433+
visiblePopover.SetNeedsDraw ();
434+
visiblePopover.SetNeedsLayout ();
435+
tops.Insert (0, visiblePopover);
436+
}
437+
438+
bool neededLayout = View.Layout (tops.ToArray ().Reverse (), Screen.Size);
430439

431440
if (ClearScreenNextIteration)
432441
{
@@ -440,7 +449,7 @@ internal static void LayoutAndDrawImpl (bool forceDraw = false)
440449
}
441450

442451
View.SetClipToScreen ();
443-
View.Draw (TopLevels, neededLayout || forceDraw);
452+
View.Draw (tops, neededLayout || forceDraw);
444453
View.SetClipToScreen ();
445454
Driver?.Refresh ();
446455
}
@@ -555,6 +564,8 @@ public static void End (RunState runState)
555564
{
556565
ArgumentNullException.ThrowIfNull (runState);
557566

567+
Popover?.HidePopover (Popover?.GetActivePopover ());
568+
558569
runState.Toplevel.OnUnloaded ();
559570

560571
// End the RunState.Toplevel

Terminal.Gui/Application/Application.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ internal static List<CultureInfo> GetAvailableCulturesFromEmbeddedResources ()
103103
.ToList ();
104104
}
105105

106+
// BUGBUG: This does not return en-US even though it's supported by default
106107
internal static List<CultureInfo> GetSupportedCultures ()
107108
{
108109
CultureInfo [] cultures = CultureInfo.GetCultures (CultureTypes.AllCultures);
@@ -148,6 +149,12 @@ internal static void ResetState (bool ignoreDisposed = false)
148149
t!.Running = false;
149150
}
150151

152+
if (Popover?.GetActivePopover () is View popover)
153+
{
154+
popover.Visible = false;
155+
}
156+
Popover = null;
157+
151158
TopLevels.Clear ();
152159
#if DEBUG_IDISPOSABLE
153160

@@ -197,7 +204,9 @@ internal static void ResetState (bool ignoreDisposed = false)
197204
Initialized = false;
198205

199206
// Mouse
200-
_lastMousePosition = null;
207+
// Do not clear _lastMousePosition; Popover's require it to stay set with
208+
// last mouse pos.
209+
//_lastMousePosition = null;
201210
_cachedViewsUnderMouse.Clear ();
202211
WantContinuousButtonPressedView = null;
203212
MouseEvent = null;

Terminal.Gui/Application/ApplicationNavigation.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ internal void SetFocused (View? value)
104104
/// </returns>
105105
public bool AdvanceFocus (NavigationDirection direction, TabBehavior? behavior)
106106
{
107+
if (Application.Popover?.GetActivePopover () as View is { Visible: true } visiblePopover)
108+
{
109+
return visiblePopover.AdvanceFocus (direction, behavior);
110+
}
107111
return Application.Top is { } && Application.Top.AdvanceFocus (direction, behavior);
108112
}
109113
}

0 commit comments

Comments
 (0)