|
@@ -27,12 +27,45 @@ public partial class Toplevel : View
|
|
|
/// </summary>
|
|
|
public Toplevel ()
|
|
|
{
|
|
|
+ CanFocus = true;
|
|
|
Arrangement = ViewArrangement.Fixed;
|
|
|
Width = Dim.Fill ();
|
|
|
Height = Dim.Fill ();
|
|
|
|
|
|
ColorScheme = Colors.ColorSchemes ["TopLevel"];
|
|
|
|
|
|
+ ConfigureKeyBindings ();
|
|
|
+
|
|
|
+ MouseClick += Toplevel_MouseClick;
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: IRunnable: Re-implement - Modal means IRunnable, ViewArrangement.Overlapped where modalView.Z > allOtherViews.Max (v = v.Z).
|
|
|
+ /// <summary>
|
|
|
+ /// Determines whether the <see cref="Toplevel"/> is modal or not. If set to <c>false</c> (the default):
|
|
|
+ /// <list type="bullet">
|
|
|
+ /// <item>
|
|
|
+ /// <description><see cref="View.OnKeyDown"/> events will propagate keys upwards.</description>
|
|
|
+ /// </item>
|
|
|
+ /// <item>
|
|
|
+ /// <description>The Toplevel will act as an embedded view (not a modal/pop-up).</description>
|
|
|
+ /// </item>
|
|
|
+ /// </list>
|
|
|
+ /// If set to <c>true</c>:
|
|
|
+ /// <list type="bullet">
|
|
|
+ /// <item>
|
|
|
+ /// <description><see cref="View.OnKeyDown"/> events will NOT propagate keys upwards.</description>
|
|
|
+ /// </item>
|
|
|
+ /// <item>
|
|
|
+ /// <description>The Toplevel will and look like a modal (pop-up) (e.g. see <see cref="Dialog"/>.</description>
|
|
|
+ /// </item>
|
|
|
+ /// </list>
|
|
|
+ /// </summary>
|
|
|
+ public bool Modal { get; set; }
|
|
|
+
|
|
|
+ #region Keyboard & Mouse
|
|
|
+
|
|
|
+ private void ConfigureKeyBindings ()
|
|
|
+ {
|
|
|
// Things this view knows how to do
|
|
|
AddCommand (
|
|
|
Command.QuitToplevel,
|
|
@@ -134,107 +167,19 @@ public partial class Toplevel : View
|
|
|
KeyBindings.Add (Key.I.WithCtrl, Command.NextView); // Unix
|
|
|
KeyBindings.Add (Key.B.WithCtrl, Command.PreviousView); // Unix
|
|
|
#endif
|
|
|
- MouseClick += Toplevel_MouseClick;
|
|
|
-
|
|
|
- CanFocus = true;
|
|
|
- }
|
|
|
-
|
|
|
- private void Toplevel_MouseClick (object sender, MouseEventEventArgs e)
|
|
|
- {
|
|
|
- e.Handled = InvokeCommand (Command.HotKey) == true;
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
|
|
|
- /// <see langword="false"/>, otherwise.
|
|
|
- /// </summary>
|
|
|
- public bool IsLoaded { get; private set; }
|
|
|
-
|
|
|
- /// <summary>Gets or sets the menu for this Toplevel.</summary>
|
|
|
- public virtual MenuBar MenuBar { get; set; }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Determines whether the <see cref="Toplevel"/> is modal or not. If set to <c>false</c> (the default):
|
|
|
- /// <list type="bullet">
|
|
|
- /// <item>
|
|
|
- /// <description><see cref="View.OnKeyDown"/> events will propagate keys upwards.</description>
|
|
|
- /// </item>
|
|
|
- /// <item>
|
|
|
- /// <description>The Toplevel will act as an embedded view (not a modal/pop-up).</description>
|
|
|
- /// </item>
|
|
|
- /// </list>
|
|
|
- /// If set to <c>true</c>:
|
|
|
- /// <list type="bullet">
|
|
|
- /// <item>
|
|
|
- /// <description><see cref="View.OnKeyDown"/> events will NOT propagate keys upwards.</description>
|
|
|
- /// </item>
|
|
|
- /// <item>
|
|
|
- /// <description>The Toplevel will and look like a modal (pop-up) (e.g. see <see cref="Dialog"/>.</description>
|
|
|
- /// </item>
|
|
|
- /// </list>
|
|
|
- /// </summary>
|
|
|
- public bool Modal { get; set; }
|
|
|
-
|
|
|
- /// <summary>Gets or sets whether the main loop for this <see cref="Toplevel"/> is running or not.</summary>
|
|
|
- /// <remarks>Setting this property directly is discouraged. Use <see cref="Application.RequestStop"/> instead.</remarks>
|
|
|
- public bool Running { get; set; }
|
|
|
-
|
|
|
- /// <summary>Gets or sets the status bar for this Toplevel.</summary>
|
|
|
- public virtual StatusBar StatusBar { get; set; }
|
|
|
-
|
|
|
- /// <summary>Invoked when the Toplevel <see cref="RunState"/> becomes the <see cref="Application.Current"/> Toplevel.</summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> Activate;
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public override View Add (View view)
|
|
|
- {
|
|
|
- CanFocus = true;
|
|
|
- AddMenuStatusBar (view);
|
|
|
- return base.Add (view);
|
|
|
- }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from by
|
|
|
- /// <see cref="Application.End(RunState)"/>.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler AllChildClosed;
|
|
|
+ private void Toplevel_MouseClick (object sender, MouseEventEventArgs e) { e.Handled = InvokeCommand (Command.HotKey) == true; }
|
|
|
|
|
|
+ // TODO: Deprecate - No need for this at View level; having at Application is sufficient.
|
|
|
/// <summary>Invoked when the <see cref="Application.AlternateBackwardKey"/> is changed.</summary>
|
|
|
public event EventHandler<KeyChangedEventArgs> AlternateBackwardKeyChanged;
|
|
|
|
|
|
+ // TODO: Deprecate - No need for this at View level; having at Application is sufficient.
|
|
|
/// <summary>Invoked when the <see cref="Application.AlternateForwardKey"/> is changed.</summary>
|
|
|
public event EventHandler<KeyChangedEventArgs> AlternateForwardKeyChanged;
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Invoked when a child of the Toplevel <see cref="RunState"/> is closed by
|
|
|
- /// <see cref="Application.End(RunState)"/>.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> ChildClosed;
|
|
|
-
|
|
|
- /// <summary>Invoked when a child Toplevel's <see cref="RunState"/> has been loaded.</summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> ChildLoaded;
|
|
|
-
|
|
|
- /// <summary>Invoked when a cjhild Toplevel's <see cref="RunState"/> has been unloaded.</summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> ChildUnloaded;
|
|
|
-
|
|
|
- /// <summary>Invoked when the Toplevel's <see cref="RunState"/> is closed by <see cref="Application.End(RunState)"/>.</summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> Closed;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Invoked when the Toplevel's <see cref="RunState"/> is being closed by
|
|
|
- /// <see cref="Application.RequestStop(Toplevel)"/>.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler<ToplevelClosingEventArgs> Closing;
|
|
|
-
|
|
|
- /// <summary>Invoked when the Toplevel<see cref="RunState"/> ceases to be the <see cref="Application.Current"/> Toplevel.</summary>
|
|
|
- public event EventHandler<ToplevelEventArgs> Deactivate;
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// Invoked when the <see cref="Toplevel"/> <see cref="RunState"/> has begun to be loaded. A Loaded event handler
|
|
|
- /// is a good place to finalize initialization before calling <see cref="Application.RunLoop(RunState)"/>.
|
|
|
- /// </summary>
|
|
|
- public event EventHandler Loaded;
|
|
|
-
|
|
|
+ // TODO: Deprecate - No need for this at View level; having at Application is sufficient.
|
|
|
/// <summary>Virtual method to invoke the <see cref="AlternateBackwardKeyChanged"/> event.</summary>
|
|
|
/// <param name="e"></param>
|
|
|
public virtual void OnAlternateBackwardKeyChanged (KeyChangedEventArgs e)
|
|
@@ -243,6 +188,7 @@ public partial class Toplevel : View
|
|
|
AlternateBackwardKeyChanged?.Invoke (this, e);
|
|
|
}
|
|
|
|
|
|
+ // TODO: Deprecate - No need for this at View level; having at Application is sufficient.
|
|
|
/// <summary>Virtual method to invoke the <see cref="AlternateForwardKeyChanged"/> event.</summary>
|
|
|
/// <param name="e"></param>
|
|
|
public virtual void OnAlternateForwardKeyChanged (KeyChangedEventArgs e)
|
|
@@ -251,189 +197,171 @@ public partial class Toplevel : View
|
|
|
AlternateForwardKeyChanged?.Invoke (this, e);
|
|
|
}
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
- public override void OnDrawContent (Rectangle viewport)
|
|
|
+ /// <summary>Virtual method to invoke the <see cref="QuitKeyChanged"/> event.</summary>
|
|
|
+ /// <param name="e"></param>
|
|
|
+ public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
|
|
|
{
|
|
|
- if (!Visible)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
+ KeyBindings.Replace (e.OldKey, e.NewKey);
|
|
|
+ QuitKeyChanged?.Invoke (this, e);
|
|
|
+ }
|
|
|
|
|
|
- if (NeedsDisplay || SubViewNeedsDisplay /*|| LayoutNeeded*/)
|
|
|
- {
|
|
|
- Clear ();
|
|
|
- //LayoutSubviews ();
|
|
|
- //PositionToplevels ();
|
|
|
+ /// <summary>Invoked when the <see cref="Application.QuitKey"/> is changed.</summary>
|
|
|
+ public event EventHandler<KeyChangedEventArgs> QuitKeyChanged;
|
|
|
|
|
|
- //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 ();
|
|
|
- // }
|
|
|
- // }
|
|
|
- // }
|
|
|
- //}
|
|
|
+ #endregion
|
|
|
|
|
|
- // 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))
|
|
|
- {
|
|
|
- //view.SetNeedsLayout ();
|
|
|
- view.SetNeedsDisplay ();
|
|
|
- view.SetSubViewNeedsDisplay ();
|
|
|
- }
|
|
|
- }
|
|
|
+ #region Subviews
|
|
|
|
|
|
- base.OnDrawContent (viewport);
|
|
|
- }
|
|
|
- }
|
|
|
+ // TODO: Deprecate - Any view can host a menubar in v2
|
|
|
+ /// <summary>Gets or sets the menu for this Toplevel.</summary>
|
|
|
+ public virtual MenuBar MenuBar { get; set; }
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
- public override bool OnEnter (View view) { return MostFocused?.OnEnter (view) ?? base.OnEnter (view); }
|
|
|
+ // TODO: Deprecate - Any view can host a statusbar in v2
|
|
|
+ /// <summary>Gets or sets the status bar for this Toplevel.</summary>
|
|
|
+ public virtual StatusBar StatusBar { get; set; }
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
- public override bool OnLeave (View view) { return MostFocused?.OnLeave (view) ?? base.OnLeave (view); }
|
|
|
-
|
|
|
- /// <summary>
|
|
|
- /// 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 ()
|
|
|
+ public override View Add (View view)
|
|
|
{
|
|
|
- IsLoaded = true;
|
|
|
-
|
|
|
- foreach (Toplevel tl in Subviews.Where (v => v is Toplevel))
|
|
|
- {
|
|
|
- tl.OnLoaded ();
|
|
|
- }
|
|
|
+ CanFocus = true;
|
|
|
+ AddMenuStatusBar (view);
|
|
|
|
|
|
- Loaded?.Invoke (this, EventArgs.Empty);
|
|
|
+ return base.Add (view);
|
|
|
}
|
|
|
|
|
|
- /// <summary>Virtual method to invoke the <see cref="QuitKeyChanged"/> event.</summary>
|
|
|
- /// <param name="e"></param>
|
|
|
- public virtual void OnQuitKeyChanged (KeyChangedEventArgs e)
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public override View Remove (View view)
|
|
|
{
|
|
|
- KeyBindings.Replace (e.OldKey, e.NewKey);
|
|
|
- QuitKeyChanged?.Invoke (this, e);
|
|
|
+ if (this is Toplevel { MenuBar: { } })
|
|
|
+ {
|
|
|
+ RemoveMenuStatusBar (view);
|
|
|
+ }
|
|
|
+
|
|
|
+ return base.Remove (view);
|
|
|
}
|
|
|
|
|
|
/// <inheritdoc/>
|
|
|
- public override Point? PositionCursor ()
|
|
|
+ public override void RemoveAll ()
|
|
|
{
|
|
|
- if (!IsOverlappedContainer)
|
|
|
+ if (this == Application.Top)
|
|
|
{
|
|
|
- if (Focused is null)
|
|
|
- {
|
|
|
- EnsureFocus ();
|
|
|
- }
|
|
|
-
|
|
|
- return null;
|
|
|
+ MenuBar?.Dispose ();
|
|
|
+ MenuBar = null;
|
|
|
+ StatusBar?.Dispose ();
|
|
|
+ StatusBar = null;
|
|
|
}
|
|
|
|
|
|
- // This code path only happens when the Toplevel is an Overlapped container
|
|
|
+ base.RemoveAll ();
|
|
|
+ }
|
|
|
|
|
|
- if (Focused is null)
|
|
|
+ internal void AddMenuStatusBar (View view)
|
|
|
+ {
|
|
|
+ if (view is MenuBar)
|
|
|
{
|
|
|
- // TODO: this is an Overlapped hack
|
|
|
- foreach (Toplevel top in Application.OverlappedChildren)
|
|
|
- {
|
|
|
- if (top != this && top.Visible)
|
|
|
- {
|
|
|
- top.SetFocus ();
|
|
|
+ MenuBar = view as MenuBar;
|
|
|
+ }
|
|
|
|
|
|
- return null;
|
|
|
- }
|
|
|
- }
|
|
|
+ if (view is StatusBar)
|
|
|
+ {
|
|
|
+ StatusBar = view as StatusBar;
|
|
|
}
|
|
|
+ }
|
|
|
|
|
|
- var cursor2 = base.PositionCursor ();
|
|
|
+ internal void RemoveMenuStatusBar (View view)
|
|
|
+ {
|
|
|
+ if (view is MenuBar)
|
|
|
+ {
|
|
|
+ MenuBar?.Dispose ();
|
|
|
+ MenuBar = null;
|
|
|
+ }
|
|
|
|
|
|
- return null;
|
|
|
+ if (view is StatusBar)
|
|
|
+ {
|
|
|
+ StatusBar?.Dispose ();
|
|
|
+ StatusBar = null;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ // TODO: Overlapped - Rename to AllSubviewsClosed - Move to View?
|
|
|
/// <summary>
|
|
|
- /// Adjusts the location and size of <paramref name="top"/> within this Toplevel. Virtual method enabling
|
|
|
- /// implementation of specific positions for inherited <see cref="Toplevel"/> views.
|
|
|
+ /// Invoked when the last child of the Toplevel <see cref="RunState"/> is closed from by
|
|
|
+ /// <see cref="Application.End(RunState)"/>.
|
|
|
/// </summary>
|
|
|
- /// <param name="top">The Toplevel to adjust.</param>
|
|
|
- public virtual void PositionToplevel (Toplevel top)
|
|
|
- {
|
|
|
+ public event EventHandler AllChildClosed;
|
|
|
|
|
|
- View superView = GetLocationEnsuringFullVisibility (
|
|
|
- top,
|
|
|
- top.Frame.X,
|
|
|
- top.Frame.Y,
|
|
|
- out int nx,
|
|
|
- out int ny,
|
|
|
- out StatusBar sb
|
|
|
- );
|
|
|
+ // TODO: Overlapped - Rename to *Subviews* - Move to View?
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked when a child of the Toplevel <see cref="RunState"/> is closed by
|
|
|
+ /// <see cref="Application.End(RunState)"/>.
|
|
|
+ /// </summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> ChildClosed;
|
|
|
|
|
|
- if (superView is null)
|
|
|
- {
|
|
|
- return;
|
|
|
- }
|
|
|
+ // TODO: Overlapped - Rename to *Subviews* - Move to View?
|
|
|
+ /// <summary>Invoked when a child Toplevel's <see cref="RunState"/> has been loaded.</summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> ChildLoaded;
|
|
|
|
|
|
- var layoutSubviews = false;
|
|
|
- var maxWidth = 0;
|
|
|
+ // TODO: Overlapped - Rename to *Subviews* - Move to View?
|
|
|
+ /// <summary>Invoked when a cjhild Toplevel's <see cref="RunState"/> has been unloaded.</summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> ChildUnloaded;
|
|
|
|
|
|
- if (superView.Margin is { } && superView == top.SuperView)
|
|
|
- {
|
|
|
- maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
|
|
|
- }
|
|
|
+ #endregion
|
|
|
|
|
|
- if ((superView != top || top?.SuperView is { } || (top != Application.Top && top.Modal) || (top?.SuperView is null && top.IsOverlapped))
|
|
|
- && (top.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y))
|
|
|
- {
|
|
|
- if ((top.X is null || top.X is PosAbsolute) && top.Frame.X != nx)
|
|
|
- {
|
|
|
- top.X = nx;
|
|
|
- layoutSubviews = true;
|
|
|
- }
|
|
|
+ #region Life Cycle
|
|
|
|
|
|
- if ((top.Y is null || top.Y is PosAbsolute) && top.Frame.Y != ny)
|
|
|
- {
|
|
|
- top.Y = ny;
|
|
|
- layoutSubviews = true;
|
|
|
- }
|
|
|
- }
|
|
|
+ // TODO: IRunnable: Re-implement as a property on IRunnable
|
|
|
+ /// <summary>Gets or sets whether the main loop for this <see cref="Toplevel"/> is running or not.</summary>
|
|
|
+ /// <remarks>Setting this property directly is discouraged. Use <see cref="Application.RequestStop"/> instead.</remarks>
|
|
|
+ public bool Running { get; set; }
|
|
|
|
|
|
- // TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
|
|
|
- if (sb != null
|
|
|
- && !top.Subviews.Contains (sb)
|
|
|
- && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
|
|
|
- && top.Height is DimFill
|
|
|
- && -top.Height.GetAnchor (0) < 1)
|
|
|
- {
|
|
|
- top.Height = Dim.Fill (sb.Visible ? 1 : 0);
|
|
|
- layoutSubviews = true;
|
|
|
- }
|
|
|
+ // TODO: IRunnable: Re-implement in IRunnable
|
|
|
+ /// <summary>
|
|
|
+ /// <see langword="true"/> if was already loaded by the <see cref="Application.Begin(Toplevel)"/>
|
|
|
+ /// <see langword="false"/>, otherwise.
|
|
|
+ /// </summary>
|
|
|
+ public bool IsLoaded { get; private set; }
|
|
|
|
|
|
- if (superView.LayoutNeeded || layoutSubviews)
|
|
|
- {
|
|
|
- superView.LayoutSubviews ();
|
|
|
- }
|
|
|
+ // TODO: IRunnable: Re-implement as an event on IRunnable; IRunnable.Activating/Activate
|
|
|
+ /// <summary>Invoked when the Toplevel <see cref="RunState"/> becomes the <see cref="Application.Current"/> Toplevel.</summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> Activate;
|
|
|
+
|
|
|
+ // TODO: IRunnable: Re-implement as an event on IRunnable; IRunnable.Deactivating/Deactivate?
|
|
|
+ /// <summary>Invoked when the Toplevel<see cref="RunState"/> ceases to be the <see cref="Application.Current"/> Toplevel.</summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> Deactivate;
|
|
|
+
|
|
|
+ /// <summary>Invoked when the Toplevel's <see cref="RunState"/> is closed by <see cref="Application.End(RunState)"/>.</summary>
|
|
|
+ public event EventHandler<ToplevelEventArgs> Closed;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked when the Toplevel's <see cref="RunState"/> is being closed by
|
|
|
+ /// <see cref="Application.RequestStop(Toplevel)"/>.
|
|
|
+ /// </summary>
|
|
|
+ public event EventHandler<ToplevelClosingEventArgs> Closing;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked when the <see cref="Toplevel"/> <see cref="RunState"/> has begun to be loaded. A Loaded event handler
|
|
|
+ /// is a good place to finalize initialization before calling <see cref="Application.RunLoop(RunState)"/>.
|
|
|
+ /// </summary>
|
|
|
+ public event EventHandler Loaded;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 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;
|
|
|
|
|
|
- if (LayoutNeeded)
|
|
|
+ foreach (Toplevel tl in Subviews.Where (v => v is Toplevel))
|
|
|
{
|
|
|
- LayoutSubviews ();
|
|
|
+ tl.OnLoaded ();
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- /// <summary>Invoked when the <see cref="Application.QuitKey"/> is changed.</summary>
|
|
|
- public event EventHandler<KeyChangedEventArgs> QuitKeyChanged;
|
|
|
+ Loaded?.Invoke (this, EventArgs.Empty);
|
|
|
+ }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Invoked when the <see cref="Toplevel"/> main loop has started it's first iteration. Subscribe to this event to
|
|
@@ -445,31 +373,6 @@ public partial class Toplevel : View
|
|
|
/// </summary>
|
|
|
public event EventHandler Ready;
|
|
|
|
|
|
- /// <inheritdoc/>
|
|
|
- public override View Remove (View view)
|
|
|
- {
|
|
|
- if (this is Toplevel { MenuBar: { } })
|
|
|
- {
|
|
|
- RemoveMenuStatusBar (view);
|
|
|
- }
|
|
|
-
|
|
|
- return base.Remove (view);
|
|
|
- }
|
|
|
-
|
|
|
- /// <inheritdoc/>
|
|
|
- public override void RemoveAll ()
|
|
|
- {
|
|
|
- if (this == Application.Top)
|
|
|
- {
|
|
|
- MenuBar?.Dispose ();
|
|
|
- MenuBar = null;
|
|
|
- StatusBar?.Dispose ();
|
|
|
- StatusBar = null;
|
|
|
- }
|
|
|
-
|
|
|
- base.RemoveAll ();
|
|
|
- }
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Stops and closes this <see cref="Toplevel"/>. If this Toplevel is the top-most Toplevel,
|
|
|
/// <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to exit.
|
|
@@ -527,37 +430,22 @@ public partial class Toplevel : View
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- /// <summary>
|
|
|
- /// Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is
|
|
|
- /// the top-most Toplevel, <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to
|
|
|
- /// exit.
|
|
|
- /// </summary>
|
|
|
- /// <param name="top">The Toplevel to request stop.</param>
|
|
|
- public virtual void RequestStop (Toplevel top) { top.RequestStop (); }
|
|
|
-
|
|
|
- /// <summary>Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.</summary>
|
|
|
- public event EventHandler<SizeChangedEventArgs> SizeChanging;
|
|
|
-
|
|
|
/// <summary>
|
|
|
/// Invoked when the Toplevel <see cref="RunState"/> has been unloaded. A Unloaded event handler is a good place
|
|
|
/// to dispose objects after calling <see cref="Application.End(RunState)"/>.
|
|
|
/// </summary>
|
|
|
public event EventHandler Unloaded;
|
|
|
|
|
|
- internal void AddMenuStatusBar (View view)
|
|
|
- {
|
|
|
- if (view is MenuBar)
|
|
|
- {
|
|
|
- MenuBar = view as MenuBar;
|
|
|
- }
|
|
|
+ internal virtual void OnActivate (Toplevel deactivated) { Activate?.Invoke (this, new (deactivated)); }
|
|
|
|
|
|
- if (view is StatusBar)
|
|
|
- {
|
|
|
- StatusBar = view as StatusBar;
|
|
|
- }
|
|
|
- }
|
|
|
+ /// <summary>
|
|
|
+ /// Stops and closes the <see cref="Toplevel"/> specified by <paramref name="top"/>. If <paramref name="top"/> is
|
|
|
+ /// the top-most Toplevel, <see cref="Application.RequestStop(Toplevel)"/> will be called, causing the application to
|
|
|
+ /// exit.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="top">The Toplevel to request stop.</param>
|
|
|
+ public virtual void RequestStop (Toplevel top) { top.RequestStop (); }
|
|
|
|
|
|
- internal virtual void OnActivate (Toplevel deactivated) { Activate?.Invoke (this, new ToplevelEventArgs (deactivated)); }
|
|
|
internal virtual void OnAllChildClosed () { AllChildClosed?.Invoke (this, EventArgs.Empty); }
|
|
|
|
|
|
internal virtual void OnChildClosed (Toplevel top)
|
|
@@ -567,12 +455,12 @@ public partial class Toplevel : View
|
|
|
SetSubViewNeedsDisplay ();
|
|
|
}
|
|
|
|
|
|
- ChildClosed?.Invoke (this, new ToplevelEventArgs (top));
|
|
|
+ ChildClosed?.Invoke (this, new (top));
|
|
|
}
|
|
|
|
|
|
- internal virtual void OnChildLoaded (Toplevel top) { ChildLoaded?.Invoke (this, new ToplevelEventArgs (top)); }
|
|
|
- internal virtual void OnChildUnloaded (Toplevel top) { ChildUnloaded?.Invoke (this, new ToplevelEventArgs (top)); }
|
|
|
- internal virtual void OnClosed (Toplevel top) { Closed?.Invoke (this, new ToplevelEventArgs (top)); }
|
|
|
+ internal virtual void OnChildLoaded (Toplevel top) { ChildLoaded?.Invoke (this, new (top)); }
|
|
|
+ internal virtual void OnChildUnloaded (Toplevel top) { ChildUnloaded?.Invoke (this, new (top)); }
|
|
|
+ internal virtual void OnClosed (Toplevel top) { Closed?.Invoke (this, new (top)); }
|
|
|
|
|
|
internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
|
|
|
{
|
|
@@ -581,7 +469,7 @@ public partial class Toplevel : View
|
|
|
return ev.Cancel;
|
|
|
}
|
|
|
|
|
|
- internal virtual void OnDeactivate (Toplevel activated) { Deactivate?.Invoke (this, new ToplevelEventArgs (activated)); }
|
|
|
+ internal virtual void OnDeactivate (Toplevel activated) { Deactivate?.Invoke (this, new (activated)); }
|
|
|
|
|
|
/// <summary>
|
|
|
/// Called from <see cref="Application.RunLoop"/> after the <see cref="Toplevel"/> has entered the first iteration
|
|
@@ -597,9 +485,6 @@ public partial class Toplevel : View
|
|
|
Ready?.Invoke (this, EventArgs.Empty);
|
|
|
}
|
|
|
|
|
|
- // TODO: Make cancelable?
|
|
|
- internal virtual void OnSizeChanging (SizeChangedEventArgs size) { SizeChanging?.Invoke (this, size); }
|
|
|
-
|
|
|
/// <summary>Called from <see cref="Application.End(RunState)"/> before the <see cref="Toplevel"/> is disposed.</summary>
|
|
|
internal virtual void OnUnloaded ()
|
|
|
{
|
|
@@ -611,35 +496,79 @@ public partial class Toplevel : View
|
|
|
Unloaded?.Invoke (this, EventArgs.Empty);
|
|
|
}
|
|
|
|
|
|
- // TODO: v2 - Not sure this is needed anymore.
|
|
|
- internal void PositionToplevels ()
|
|
|
+ private void QuitToplevel ()
|
|
|
{
|
|
|
- PositionToplevel (this);
|
|
|
-
|
|
|
- foreach (View top in Subviews)
|
|
|
+ if (Application.OverlappedTop is { })
|
|
|
{
|
|
|
- if (top is Toplevel)
|
|
|
- {
|
|
|
- PositionToplevel ((Toplevel)top);
|
|
|
- }
|
|
|
+ RequestStop (this);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Application.RequestStop ();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- internal void RemoveMenuStatusBar (View view)
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Draw
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public override void OnDrawContent (Rectangle viewport)
|
|
|
{
|
|
|
- if (view is MenuBar)
|
|
|
+ if (!Visible)
|
|
|
{
|
|
|
- MenuBar?.Dispose ();
|
|
|
- MenuBar = null;
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- if (view is StatusBar)
|
|
|
+ if (NeedsDisplay || SubViewNeedsDisplay /*|| LayoutNeeded*/)
|
|
|
{
|
|
|
- StatusBar?.Dispose ();
|
|
|
- StatusBar = null;
|
|
|
+ Clear ();
|
|
|
+
|
|
|
+ //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 ();
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ // }
|
|
|
+ //}
|
|
|
+
|
|
|
+ // 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))
|
|
|
+ {
|
|
|
+ //view.SetNeedsLayout ();
|
|
|
+ view.SetNeedsDisplay ();
|
|
|
+ view.SetSubViewNeedsDisplay ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ base.OnDrawContent (viewport);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Focus
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public override bool OnEnter (View view) { return MostFocused?.OnEnter (view) ?? base.OnEnter (view); }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public override bool OnLeave (View view) { return MostFocused?.OnLeave (view) ?? base.OnLeave (view); }
|
|
|
+
|
|
|
private void FocusNearestView (IEnumerable<View> views, NavigationDirection direction)
|
|
|
{
|
|
|
if (views is null)
|
|
@@ -785,6 +714,117 @@ public partial class Toplevel : View
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ #endregion
|
|
|
+
|
|
|
+ #region Size / Position Management
|
|
|
+
|
|
|
+ // TODO: Make cancelable?
|
|
|
+ internal virtual void OnSizeChanging (SizeChangedEventArgs size) { SizeChanging?.Invoke (this, size); }
|
|
|
+
|
|
|
+ /// <inheritdoc/>
|
|
|
+ public override Point? PositionCursor ()
|
|
|
+ {
|
|
|
+ if (!IsOverlappedContainer)
|
|
|
+ {
|
|
|
+ if (Focused is null)
|
|
|
+ {
|
|
|
+ EnsureFocus ();
|
|
|
+ }
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ // This code path only happens when the Toplevel is an Overlapped container
|
|
|
+
|
|
|
+ if (Focused is null)
|
|
|
+ {
|
|
|
+ // TODO: this is an Overlapped hack
|
|
|
+ foreach (Toplevel top in Application.OverlappedChildren)
|
|
|
+ {
|
|
|
+ if (top != this && top.Visible)
|
|
|
+ {
|
|
|
+ top.SetFocus ();
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ Point? cursor2 = base.PositionCursor ();
|
|
|
+
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Adjusts the location and size of <paramref name="top"/> within this Toplevel. Virtual method enabling
|
|
|
+ /// implementation of specific positions for inherited <see cref="Toplevel"/> views.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="top">The Toplevel to adjust.</param>
|
|
|
+ public virtual void PositionToplevel (Toplevel top)
|
|
|
+ {
|
|
|
+ View superView = GetLocationEnsuringFullVisibility (
|
|
|
+ top,
|
|
|
+ top.Frame.X,
|
|
|
+ top.Frame.Y,
|
|
|
+ out int nx,
|
|
|
+ out int ny,
|
|
|
+ out StatusBar sb
|
|
|
+ );
|
|
|
+
|
|
|
+ if (superView is null)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ var layoutSubviews = false;
|
|
|
+ var maxWidth = 0;
|
|
|
+
|
|
|
+ if (superView.Margin is { } && superView == top.SuperView)
|
|
|
+ {
|
|
|
+ maxWidth -= superView.GetAdornmentsThickness ().Left + superView.GetAdornmentsThickness ().Right;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((superView != top || top?.SuperView is { } || (top != Application.Top && top.Modal) || (top?.SuperView is null && top.IsOverlapped))
|
|
|
+ && (top.Frame.X + top.Frame.Width > maxWidth || ny > top.Frame.Y))
|
|
|
+ {
|
|
|
+ if ((top.X is null || top.X is PosAbsolute) && top.Frame.X != nx)
|
|
|
+ {
|
|
|
+ top.X = nx;
|
|
|
+ layoutSubviews = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if ((top.Y is null || top.Y is PosAbsolute) && top.Frame.Y != ny)
|
|
|
+ {
|
|
|
+ top.Y = ny;
|
|
|
+ layoutSubviews = true;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // TODO: v2 - This is a hack to get the StatusBar to be positioned correctly.
|
|
|
+ if (sb != null
|
|
|
+ && !top.Subviews.Contains (sb)
|
|
|
+ && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
|
|
|
+ && top.Height is DimFill
|
|
|
+ && -top.Height.GetAnchor (0) < 1)
|
|
|
+ {
|
|
|
+ top.Height = Dim.Fill (sb.Visible ? 1 : 0);
|
|
|
+ layoutSubviews = true;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (superView.LayoutNeeded || layoutSubviews)
|
|
|
+ {
|
|
|
+ superView.LayoutSubviews ();
|
|
|
+ }
|
|
|
+
|
|
|
+ if (LayoutNeeded)
|
|
|
+ {
|
|
|
+ LayoutSubviews ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>Invoked when the terminal has been resized. The new <see cref="Size"/> of the terminal is provided.</summary>
|
|
|
+ public event EventHandler<SizeChangedEventArgs> SizeChanging;
|
|
|
+
|
|
|
private bool OutsideTopFrame (Toplevel top)
|
|
|
{
|
|
|
if (top.Frame.X > Driver.Cols || top.Frame.Y > Driver.Rows)
|
|
@@ -795,17 +835,21 @@ public partial class Toplevel : View
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- private void QuitToplevel ()
|
|
|
+ // TODO: v2 - Not sure this is needed anymore.
|
|
|
+ internal void PositionToplevels ()
|
|
|
{
|
|
|
- if (Application.OverlappedTop is { })
|
|
|
- {
|
|
|
- RequestStop (this);
|
|
|
- }
|
|
|
- else
|
|
|
+ PositionToplevel (this);
|
|
|
+
|
|
|
+ foreach (View top in Subviews)
|
|
|
{
|
|
|
- Application.RequestStop ();
|
|
|
+ if (top is Toplevel)
|
|
|
+ {
|
|
|
+ PositionToplevel ((Toplevel)top);
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ #endregion
|
|
|
}
|
|
|
|
|
|
/// <summary>
|