mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Toplevel cleanup
This commit is contained in:
@@ -40,7 +40,6 @@ public static partial class Application // Initialization (Init/Shutdown)
|
||||
internal static bool _initialized;
|
||||
internal static int _mainThreadId = -1;
|
||||
|
||||
|
||||
// INTERNAL function for initializing an app with a Toplevel factory object, driver, and mainloop.
|
||||
//
|
||||
// Called from:
|
||||
|
||||
@@ -25,6 +25,7 @@ public static partial class Application // Keyboard handling
|
||||
|
||||
private static void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
|
||||
{
|
||||
// TODO: The fact Top has it's own AlternateForwardKey and events is needlessly complex. Remove it.
|
||||
foreach (Toplevel top in _topLevels.ToArray ())
|
||||
{
|
||||
top.OnAlternateForwardKeyChanged (e);
|
||||
@@ -52,6 +53,7 @@ public static partial class Application // Keyboard handling
|
||||
|
||||
private static void OnAlternateBackwardKeyChanged (KeyChangedEventArgs oldKey)
|
||||
{
|
||||
// TODO: The fact Top has it's own AlternateBackwardKey and events is needlessly complex. Remove it.
|
||||
foreach (Toplevel top in _topLevels.ToArray ())
|
||||
{
|
||||
top.OnAlternateBackwardKeyChanged (oldKey);
|
||||
@@ -79,6 +81,7 @@ public static partial class Application // Keyboard handling
|
||||
|
||||
private static void OnQuitKeyChanged (KeyChangedEventArgs e)
|
||||
{
|
||||
// TODO: The fact Top has it's own QuitKey and events is needlessly complex. Remove it.
|
||||
// Duplicate the list so if it changes during enumeration we're safe
|
||||
foreach (Toplevel top in _topLevels.ToArray ())
|
||||
{
|
||||
|
||||
@@ -137,232 +137,14 @@ public static partial class Application
|
||||
SynchronizationContext.SetSynchronizationContext (null);
|
||||
}
|
||||
|
||||
// When `End ()` is called, it is possible `RunState.Toplevel` is a different object than `Top`.
|
||||
// This field is set in `End` in this case so that `Begin` correctly sets `Top`.
|
||||
|
||||
// TODO: Determine if this is really needed. The only code that calls WakeUp I can find
|
||||
// is ProgressBarStyles, and it's not clear it needs to.
|
||||
|
||||
#region Toplevel handling
|
||||
|
||||
/// <summary>Holds the stack of TopLevel views.</summary>
|
||||
|
||||
// BUGBUG: Technically, this is not the full lst of TopLevels. There be dragons here, e.g. see how Toplevel.Id is used. What
|
||||
// about TopLevels that are just a SubView of another View?
|
||||
internal static readonly Stack<Toplevel> _topLevels = new ();
|
||||
|
||||
/// <summary>The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)</summary>
|
||||
/// <value>The top.</value>
|
||||
public static Toplevel? Top { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current <see cref="Toplevel"/> object. This is updated in <see cref="Application.Begin"/> enters and leaves to
|
||||
/// point to the current
|
||||
/// <see cref="Toplevel"/> .
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Only relevant in scenarios where <see cref="Toplevel.IsOverlappedContainer"/> is <see langword="true"/>.
|
||||
/// </remarks>
|
||||
/// <value>The current.</value>
|
||||
public static Toplevel? Current { get; private set; }
|
||||
|
||||
private static void EnsureModalOrVisibleAlwaysOnTop (Toplevel topLevel)
|
||||
{
|
||||
if (!topLevel.Running
|
||||
|| (topLevel == Current && topLevel.Visible)
|
||||
|| OverlappedTop == null
|
||||
|| _topLevels.Peek ().Modal)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (Toplevel? top in _topLevels.Reverse ())
|
||||
{
|
||||
if (top.Modal && top != Current)
|
||||
{
|
||||
MoveCurrent (top);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!topLevel.Visible && topLevel == Current)
|
||||
{
|
||||
OverlappedMoveNext ();
|
||||
}
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
private static Toplevel? FindDeepestTop (Toplevel start, in Point location)
|
||||
{
|
||||
if (!start.Frame.Contains (location))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_topLevels is { Count: > 0 })
|
||||
{
|
||||
int rx = location.X - start.Frame.X;
|
||||
int ry = location.Y - start.Frame.Y;
|
||||
|
||||
foreach (Toplevel? t in _topLevels)
|
||||
{
|
||||
if (t != Current)
|
||||
{
|
||||
if (t != start && t.Visible && t.Frame.Contains (rx, ry))
|
||||
{
|
||||
start = t;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
private static View FindTopFromView (View view)
|
||||
{
|
||||
View top = view?.SuperView is { } && view?.SuperView != Top
|
||||
? view.SuperView
|
||||
: view;
|
||||
|
||||
while (top?.SuperView is { } && top?.SuperView != Top)
|
||||
{
|
||||
top = top.SuperView;
|
||||
}
|
||||
|
||||
return top;
|
||||
}
|
||||
|
||||
#nullable enable
|
||||
|
||||
// Only return true if the Current has changed.
|
||||
private static bool MoveCurrent (Toplevel top)
|
||||
{
|
||||
// The Current is modal and the top is not modal Toplevel then
|
||||
// the Current must be moved above the first not modal Toplevel.
|
||||
if (OverlappedTop is { }
|
||||
&& top != OverlappedTop
|
||||
&& top != Current
|
||||
&& Current?.Modal == true
|
||||
&& !_topLevels.Peek ().Modal)
|
||||
{
|
||||
lock (_topLevels)
|
||||
{
|
||||
_topLevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
Toplevel? [] savedToplevels = _topLevels.ToArray ();
|
||||
|
||||
foreach (Toplevel? t in savedToplevels)
|
||||
{
|
||||
if (!t!.Modal && t != Current && t != top && t != savedToplevels [index])
|
||||
{
|
||||
lock (_topLevels)
|
||||
{
|
||||
_topLevels.MoveTo (top, index, new ToplevelEqualityComparer ());
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// The Current and the top are both not running Toplevel then
|
||||
// the top must be moved above the first not running Toplevel.
|
||||
if (OverlappedTop is { }
|
||||
&& top != OverlappedTop
|
||||
&& top != Current
|
||||
&& Current?.Running == false
|
||||
&& top?.Running == false)
|
||||
{
|
||||
lock (_topLevels)
|
||||
{
|
||||
_topLevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
|
||||
}
|
||||
|
||||
var index = 0;
|
||||
|
||||
foreach (Toplevel? t in _topLevels.ToArray ())
|
||||
{
|
||||
if (!t.Running && t != Current && index > 0)
|
||||
{
|
||||
lock (_topLevels)
|
||||
{
|
||||
_topLevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
|
||||
}
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((OverlappedTop is { } && top?.Modal == true && _topLevels.Peek () != top)
|
||||
|| (OverlappedTop is { } && Current != OverlappedTop && Current?.Modal == false && top == OverlappedTop)
|
||||
|| (OverlappedTop is { } && Current?.Modal == false && top != Current)
|
||||
|| (OverlappedTop is { } && Current?.Modal == true && top == OverlappedTop))
|
||||
{
|
||||
lock (_topLevels)
|
||||
{
|
||||
_topLevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
|
||||
Current = top;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#nullable restore
|
||||
|
||||
/// <summary>Invoked when the terminal's size changed. The new size of the terminal is provided.</summary>
|
||||
/// <remarks>
|
||||
/// Event handlers can set <see cref="SizeChangedEventArgs.Cancel"/> to <see langword="true"/> to prevent
|
||||
/// <see cref="Application"/> from changing it's size to match the new terminal size.
|
||||
/// </remarks>
|
||||
public static event EventHandler<SizeChangedEventArgs> SizeChanging;
|
||||
|
||||
/// <summary>
|
||||
/// Called when the application's size changes. Sets the size of all <see cref="Toplevel"/>s and fires the
|
||||
/// <see cref="SizeChanging"/> event.
|
||||
/// </summary>
|
||||
/// <param name="args">The new size.</param>
|
||||
/// <returns><see lanword="true"/>if the size was changed.</returns>
|
||||
public static bool OnSizeChanging (SizeChangedEventArgs args)
|
||||
{
|
||||
SizeChanging?.Invoke (null, args);
|
||||
|
||||
if (args.Cancel || args.Size is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach (Toplevel t in _topLevels)
|
||||
{
|
||||
t.SetRelativeLayout (args.Size.Value);
|
||||
t.LayoutSubviews ();
|
||||
t.PositionToplevels ();
|
||||
t.OnSizeChanging (new (args.Size));
|
||||
|
||||
if (PositionCursor (t))
|
||||
{
|
||||
Driver.UpdateCursor ();
|
||||
}
|
||||
}
|
||||
|
||||
Refresh ();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion Toplevel handling
|
||||
|
||||
/// <summary>
|
||||
/// Gets a string representation of the Application as rendered by <see cref="Driver"/>.
|
||||
/// </summary>
|
||||
|
||||
@@ -6,7 +6,7 @@ namespace Terminal.Gui;
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// Toplevels can run as modal (popup) views, started by calling
|
||||
/// Toplevel views can run as modal (popup) views, started by calling
|
||||
/// <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>. They return control to the caller when
|
||||
/// <see cref="Application.RequestStop(Toplevel)"/> has been called (which sets the <see cref="Toplevel.Running"/>
|
||||
/// property to <c>false</c>).
|
||||
@@ -14,7 +14,7 @@ namespace Terminal.Gui;
|
||||
/// <para>
|
||||
/// A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init"/>.
|
||||
/// The application Toplevel can be accessed via <see cref="Application.Top"/>. Additional Toplevels can be created
|
||||
/// and run (e.g. <see cref="Dialog"/>s. To run a Toplevel, create the <see cref="Toplevel"/> and call
|
||||
/// and run (e.g. <see cref="Dialog"/>s). To run a Toplevel, create the <see cref="Toplevel"/> and call
|
||||
/// <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
@@ -259,32 +259,30 @@ public partial class Toplevel : View
|
||||
return;
|
||||
}
|
||||
|
||||
if (NeedsDisplay || SubViewNeedsDisplay || LayoutNeeded)
|
||||
if (NeedsDisplay || SubViewNeedsDisplay /*|| LayoutNeeded*/)
|
||||
{
|
||||
//Driver.SetAttribute (GetNormalColor ());
|
||||
// TODO: It's bad practice for views to always clear. Defeats the purpose of clipping etc...
|
||||
Clear ();
|
||||
LayoutSubviews ();
|
||||
PositionToplevels ();
|
||||
//LayoutSubviews ();
|
||||
//PositionToplevels ();
|
||||
|
||||
if (this == Application.OverlappedTop)
|
||||
{
|
||||
foreach (Toplevel top in Application.OverlappedChildren.AsEnumerable ().Reverse ())
|
||||
{
|
||||
if (top.Frame.IntersectsWith (Viewport))
|
||||
{
|
||||
if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible)
|
||||
{
|
||||
top.SetNeedsLayout ();
|
||||
top.SetNeedsDisplay (top.Viewport);
|
||||
top.Draw ();
|
||||
top.OnRenderLineCanvas ();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//if (this == Application.OverlappedTop)
|
||||
//{
|
||||
// foreach (Toplevel top in Application.OverlappedChildren.AsEnumerable ().Reverse ())
|
||||
// {
|
||||
// if (top.Frame.IntersectsWith (Viewport))
|
||||
// {
|
||||
// if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible)
|
||||
// {
|
||||
// top.SetNeedsLayout ();
|
||||
// top.SetNeedsDisplay (top.Viewport);
|
||||
// top.Draw ();
|
||||
// top.OnRenderLineCanvas ();
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// This should not be here, but in base
|
||||
// BUGBUG: This appears to be a hack to get ScrollBarViews to render correctly.
|
||||
foreach (View view in Subviews)
|
||||
{
|
||||
if (view.Frame.IntersectsWith (Viewport) && !OutsideTopFrame (this))
|
||||
@@ -296,12 +294,6 @@ public partial class Toplevel : View
|
||||
}
|
||||
|
||||
base.OnDrawContent (viewport);
|
||||
|
||||
// This is causing the menus drawn incorrectly if UseSubMenusSingleFrame is true
|
||||
//if (this.MenuBar is { } && this.MenuBar.IsMenuOpen && this.MenuBar.openMenu is { }) {
|
||||
// // TODO: Hack until we can get compositing working right.
|
||||
// this.MenuBar.openMenu.Redraw (this.MenuBar.openMenu.Viewport);
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,6 +307,9 @@ public partial class Toplevel : View
|
||||
/// Called from <see cref="Application.Begin(Toplevel)"/> before the <see cref="Toplevel"/> redraws for the first
|
||||
/// time.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Overrides must call base.OnLoaded() to ensure any Toplevel subviews are initialized properly and the <see cref="Loaded"/> event is raised.
|
||||
/// </remarks>
|
||||
public virtual void OnLoaded ()
|
||||
{
|
||||
IsLoaded = true;
|
||||
|
||||
Reference in New Issue
Block a user