|
@@ -16,11 +16,11 @@ namespace Terminal.Gui {
|
|
|
/// <remarks>
|
|
|
/// <para>
|
|
|
/// Toplevels can be modally executing views, started by calling <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
|
|
|
- /// They return control to the caller when <see cref="Application.RequestStop()"/> has
|
|
|
+ /// They return control to the caller when <see cref="Application.RequestStop(Toplevel)"/> has
|
|
|
/// been called (which sets the <see cref="Toplevel.Running"/> property to false).
|
|
|
/// </para>
|
|
|
/// <para>
|
|
|
- /// A Toplevel is created when an application initialzies Terminal.Gui by callling <see cref="Application.Init(ConsoleDriver, IMainLoopDriver)"/>.
|
|
|
+ /// A Toplevel is created when an application initializes Terminal.Gui by calling <see cref="Application.Init(ConsoleDriver, IMainLoopDriver)"/>.
|
|
|
/// 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 <see cref="Application.Run(Toplevel, Func{Exception, bool})"/>.
|
|
@@ -67,6 +67,90 @@ namespace Terminal.Gui {
|
|
|
/// </summary>
|
|
|
public event Action Unloaded;
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the Toplevel's <see cref="Application.RunState"/> becomes the <see cref="Application.Current"/>.
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> Activate;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the Toplevel's <see cref="Application.RunState"/> ceases to be the <see cref="Application.Current"/>.
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> Deactivate;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the child Toplevel's <see cref="Application.RunState"/> is closed from the <see cref="Application.End(View)"/>
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> ChildClosed;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the last child Toplevel's <see cref="Application.RunState"/> is closed from the <see cref="Application.End(View)"/>
|
|
|
+ /// </summary>
|
|
|
+ public event Action AllChildClosed;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the Toplevel's <see cref="Application.RunState"/> is being closing from the <see cref="Application.RequestStop(Toplevel)"/>
|
|
|
+ /// </summary>
|
|
|
+ public event Action<ToplevelClosingEventArgs> Closing;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the Toplevel's <see cref="Application.RunState"/> is closed from the <see cref="Application.End(View)"/>
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> Closed;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the child Toplevel's <see cref="Application.RunState"/> has begin loaded.
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> ChildLoaded;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Invoked once the child Toplevel's <see cref="Application.RunState"/> has begin unloaded.
|
|
|
+ /// </summary>
|
|
|
+ public event Action<Toplevel> ChildUnloaded;
|
|
|
+
|
|
|
+ internal virtual void OnChildUnloaded (Toplevel top)
|
|
|
+ {
|
|
|
+ ChildUnloaded?.Invoke (top);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnChildLoaded (Toplevel top)
|
|
|
+ {
|
|
|
+ ChildLoaded?.Invoke (top);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnClosed (Toplevel top)
|
|
|
+ {
|
|
|
+ Closed?.Invoke (top);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual bool OnClosing (ToplevelClosingEventArgs ev)
|
|
|
+ {
|
|
|
+ Closing?.Invoke (ev);
|
|
|
+ return ev.Cancel;
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnAllChildClosed ()
|
|
|
+ {
|
|
|
+ AllChildClosed?.Invoke ();
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnChildClosed (Toplevel top)
|
|
|
+ {
|
|
|
+ if (IsMdiContainer) {
|
|
|
+ SetChildNeedsDisplay ();
|
|
|
+ }
|
|
|
+ ChildClosed?.Invoke (top);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnDeactivate (Toplevel activated)
|
|
|
+ {
|
|
|
+ Deactivate?.Invoke (activated);
|
|
|
+ }
|
|
|
+
|
|
|
+ internal virtual void OnActivate (Toplevel deactivated)
|
|
|
+ {
|
|
|
+ Activate?.Invoke (deactivated);
|
|
|
+ }
|
|
|
+
|
|
|
/// <summary>
|
|
|
/// Called from <see cref="Application.Begin(Toplevel)"/> before the <see cref="Toplevel"/> is redraws for the first time.
|
|
|
/// </summary>
|
|
@@ -149,6 +233,20 @@ namespace Terminal.Gui {
|
|
|
/// </summary>
|
|
|
public virtual StatusBar StatusBar { get; set; }
|
|
|
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets if this Toplevel is a Mdi container.
|
|
|
+ /// </summary>
|
|
|
+ public bool IsMdiContainer { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets or sets if this Toplevel is a Mdi child.
|
|
|
+ /// </summary>
|
|
|
+ public bool IsMdiChild {
|
|
|
+ get {
|
|
|
+ return Application.MdiTop != null && Application.MdiTop != this && !Modal;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
///<inheritdoc/>
|
|
|
public override bool OnKeyDown (KeyEvent keyEvent)
|
|
|
{
|
|
@@ -198,7 +296,11 @@ namespace Terminal.Gui {
|
|
|
switch (ShortcutHelper.GetModifiersKey (keyEvent)) {
|
|
|
case Key.Q | Key.CtrlMask:
|
|
|
// FIXED: stop current execution of this container
|
|
|
- Application.RequestStop ();
|
|
|
+ if (Application.MdiTop != null) {
|
|
|
+ Application.MdiTop.RequestStop ();
|
|
|
+ } else {
|
|
|
+ Application.RequestStop ();
|
|
|
+ }
|
|
|
break;
|
|
|
case Key.Z | Key.CtrlMask:
|
|
|
Driver.Suspend ();
|
|
@@ -239,16 +341,26 @@ namespace Terminal.Gui {
|
|
|
return true;
|
|
|
case Key.Tab | Key.CtrlMask:
|
|
|
case Key key when key == Application.AlternateForwardKey: // Needed on Unix
|
|
|
- Application.Top.FocusNext ();
|
|
|
- if (Application.Top.Focused == null) {
|
|
|
+ if (Application.MdiTop == null) {
|
|
|
Application.Top.FocusNext ();
|
|
|
+ if (Application.Top.Focused == null) {
|
|
|
+ Application.Top.FocusNext ();
|
|
|
+ }
|
|
|
+ Application.Top.SetNeedsDisplay ();
|
|
|
+ } else {
|
|
|
+ MoveNext ();
|
|
|
}
|
|
|
return true;
|
|
|
case Key.Tab | Key.ShiftMask | Key.CtrlMask:
|
|
|
case Key key when key == Application.AlternateBackwardKey: // Needed on Unix
|
|
|
- Application.Top.FocusPrev ();
|
|
|
- if (Application.Top.Focused == null) {
|
|
|
+ if (Application.MdiTop == null) {
|
|
|
Application.Top.FocusPrev ();
|
|
|
+ if (Application.Top.Focused == null) {
|
|
|
+ Application.Top.FocusPrev ();
|
|
|
+ }
|
|
|
+ Application.Top.SetNeedsDisplay ();
|
|
|
+ } else {
|
|
|
+ MovePrevious ();
|
|
|
}
|
|
|
return true;
|
|
|
case Key.L | Key.CtrlMask:
|
|
@@ -383,7 +495,7 @@ namespace Terminal.Gui {
|
|
|
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
|
|
|
bool m, s;
|
|
|
if (SuperView == null || SuperView.GetType () != typeof (Toplevel)) {
|
|
|
- m = Application.Top.MenuBar != null;
|
|
|
+ m = Application.MdiTop?.MenuBar != null || Application.Top.MenuBar != null;
|
|
|
} else {
|
|
|
m = ((Toplevel)SuperView).MenuBar != null;
|
|
|
}
|
|
@@ -394,7 +506,9 @@ namespace Terminal.Gui {
|
|
|
}
|
|
|
ny = Math.Max (y, l);
|
|
|
if (SuperView == null || SuperView.GetType () != typeof (Toplevel)) {
|
|
|
- s = Application.Top.StatusBar != null && Application.Top.StatusBar.Visible;
|
|
|
+ s = (Application.MdiTop != null && Application.MdiTop.StatusBar != null
|
|
|
+ && Application.MdiTop.StatusBar.Visible)
|
|
|
+ || (Application.Top.StatusBar != null && Application.Top.StatusBar.Visible);
|
|
|
} else {
|
|
|
s = ((Toplevel)SuperView).StatusBar != null && ((Toplevel)SuperView).StatusBar.Visible;
|
|
|
}
|
|
@@ -429,7 +543,8 @@ namespace Terminal.Gui {
|
|
|
public virtual void PositionToplevel (Toplevel top)
|
|
|
{
|
|
|
EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
|
|
|
- if (top?.SuperView != null && (nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
|
|
|
+ if ((top?.SuperView != null || Application.MdiTop != null && top != Application.MdiTop)
|
|
|
+ && (nx > top.Frame.X || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
|
|
|
if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
|
|
|
top.X = nx;
|
|
|
}
|
|
@@ -437,16 +552,28 @@ namespace Terminal.Gui {
|
|
|
top.Y = ny;
|
|
|
}
|
|
|
}
|
|
|
- var statusBar = top?.SuperView != null && top.SuperView is Toplevel toplevel
|
|
|
- ? toplevel.StatusBar : null;
|
|
|
|
|
|
+ View superView = null;
|
|
|
+ StatusBar statusBar = null;
|
|
|
+
|
|
|
+ if (top != Application.MdiTop && Application.MdiTop != null && Application.MdiTop.StatusBar != null) {
|
|
|
+ superView = Application.MdiTop;
|
|
|
+ statusBar = Application.MdiTop.StatusBar;
|
|
|
+ } else if (top?.SuperView != null && top.SuperView is Toplevel toplevel) {
|
|
|
+ superView = top.SuperView;
|
|
|
+ statusBar = toplevel.StatusBar;
|
|
|
+ }
|
|
|
if (statusBar != null) {
|
|
|
- if (ny + top.Frame.Height != top.SuperView.Frame.Height - (statusBar.Visible ? 1 : 0)) {
|
|
|
+ if (ny + top.Frame.Height >= superView.Frame.Height - (statusBar.Visible ? 1 : 0)) {
|
|
|
if (top.Height is Dim.DimFill) {
|
|
|
top.Height = Dim.Fill (statusBar.Visible ? 1 : 0);
|
|
|
}
|
|
|
}
|
|
|
- top.SuperView.LayoutSubviews ();
|
|
|
+ if (superView == Application.MdiTop) {
|
|
|
+ top.SetRelativeLayout (superView.Frame);
|
|
|
+ } else {
|
|
|
+ superView.LayoutSubviews ();
|
|
|
+ }
|
|
|
}
|
|
|
if (top.StatusBar != null) {
|
|
|
if (top.StatusBar.Frame.Y != top.Frame.Height - (top.StatusBar.Visible ? 1 : 0)) {
|
|
@@ -460,30 +587,151 @@ namespace Terminal.Gui {
|
|
|
///<inheritdoc/>
|
|
|
public override void Redraw (Rect bounds)
|
|
|
{
|
|
|
- if (IsCurrentTop || this == Application.Top || Application.Current.GetType ().BaseType == typeof (Toplevel)) {
|
|
|
- if (!NeedDisplay.IsEmpty || LayoutNeeded) {
|
|
|
- Driver.SetAttribute (Colors.TopLevel.Normal);
|
|
|
+ if (this == Application.MdiTop) {
|
|
|
+ RedrawMdi (bounds);
|
|
|
+ } else {
|
|
|
+ if (!CanBeVisible (this)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) {
|
|
|
+ Driver.SetAttribute (ColorScheme.Normal);
|
|
|
|
|
|
// This is the Application.Top. Clear just the region we're being asked to redraw
|
|
|
// (the bounds passed to us).
|
|
|
// Must be the screen-relative region to clear, not the bounds.
|
|
|
Clear (Frame);
|
|
|
Driver.SetAttribute (Colors.Base.Normal);
|
|
|
- PositionToplevels ();
|
|
|
+ }
|
|
|
+ }
|
|
|
|
|
|
- foreach (var view in Subviews) {
|
|
|
- if (view.Frame.IntersectsWith (bounds)) {
|
|
|
- view.SetNeedsLayout ();
|
|
|
- view.SetNeedsDisplay (view.Bounds);
|
|
|
+ if (LayoutStyle == LayoutStyle.Computed)
|
|
|
+ SetRelativeLayout (Bounds);
|
|
|
+ PositionToplevels ();
|
|
|
+ LayoutSubviews ();
|
|
|
+
|
|
|
+ foreach (var view in Subviews) {
|
|
|
+ if (view.Frame.IntersectsWith (bounds) && !OutsideTopFrame (this)) {
|
|
|
+ view.SetNeedsLayout ();
|
|
|
+ view.SetNeedsDisplay (view.Bounds);
|
|
|
+ view.Redraw (view.Bounds);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ ClearLayoutNeeded ();
|
|
|
+ ClearNeedsDisplay ();
|
|
|
+ }
|
|
|
+
|
|
|
+ void RedrawMdi (Rect bounds)
|
|
|
+ {
|
|
|
+ if (!IsMdiContainer) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) {
|
|
|
+ Driver.SetAttribute (ColorScheme.Normal);
|
|
|
+
|
|
|
+ Clear (Frame);
|
|
|
+
|
|
|
+ Driver.SetAttribute (Colors.Base.Normal);
|
|
|
+
|
|
|
+ if (LayoutStyle == LayoutStyle.Computed)
|
|
|
+ SetRelativeLayout (Bounds);
|
|
|
+ PositionToplevels ();
|
|
|
+ LayoutSubviews ();
|
|
|
+
|
|
|
+ foreach (var top in Application.MdiChildes.AsEnumerable ().Reverse ()) {
|
|
|
+ if (top.Frame.IntersectsWith (bounds)) {
|
|
|
+ if (top != this && !top.IsCurrentTop && !OutsideTopFrame (top) && top.Visible) {
|
|
|
+ top.SetNeedsLayout ();
|
|
|
+ top.SetNeedsDisplay (top.Bounds);
|
|
|
+ top.Redraw (top.Bounds);
|
|
|
}
|
|
|
}
|
|
|
+ }
|
|
|
+
|
|
|
+ ClearLayoutNeeded ();
|
|
|
+ ClearNeedsDisplay ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ bool OutsideTopFrame (Toplevel top)
|
|
|
+ {
|
|
|
+ if (top.Frame.X > Driver.Cols || top.Frame.Y > Driver.Rows) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ //
|
|
|
+ // FIXED:It does not look like the event is raised on clicked-drag
|
|
|
+ // need to figure that out.
|
|
|
+ //
|
|
|
+ internal static Point? dragPosition;
|
|
|
+ Point start;
|
|
|
+
|
|
|
+ ///<inheritdoc/>
|
|
|
+ public override bool MouseEvent (MouseEvent mouseEvent)
|
|
|
+ {
|
|
|
+ // FIXED:The code is currently disabled, because the
|
|
|
+ // Driver.UncookMouse does not seem to have an effect if there is
|
|
|
+ // a pending mouse event activated.
|
|
|
+
|
|
|
+ int nx, ny;
|
|
|
+ if (!dragPosition.HasValue && mouseEvent.Flags == (MouseFlags.Button1Pressed)) {
|
|
|
+ // Only start grabbing if the user clicks on the title bar.
|
|
|
+ if (mouseEvent.Y == 0) {
|
|
|
+ start = new Point (mouseEvent.X, mouseEvent.Y);
|
|
|
+ dragPosition = new Point ();
|
|
|
+ nx = mouseEvent.X - mouseEvent.OfX;
|
|
|
+ ny = mouseEvent.Y - mouseEvent.OfY;
|
|
|
+ dragPosition = new Point (nx, ny);
|
|
|
+ Application.GrabMouse (this);
|
|
|
+ }
|
|
|
+
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"Starting at {dragPosition}");
|
|
|
+ return true;
|
|
|
+ } else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
|
|
|
+ mouseEvent.Flags == MouseFlags.Button3Pressed) {
|
|
|
+ if (dragPosition.HasValue) {
|
|
|
+ if (SuperView == null) {
|
|
|
+ // Redraw the entire app window using just our Frame. Since we are
|
|
|
+ // Application.Top, and our Frame always == our Bounds (Location is always (0,0))
|
|
|
+ // our Frame is actually view-relative (which is what Redraw takes).
|
|
|
+ // We need to pass all the view bounds because since the windows was
|
|
|
+ // moved around, we don't know exactly what was the affected region.
|
|
|
+ Application.Top.SetNeedsDisplay ();
|
|
|
+ } else {
|
|
|
+ SuperView.SetNeedsDisplay ();
|
|
|
+ }
|
|
|
+ EnsureVisibleBounds (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - start.X : Frame.X - start.X),
|
|
|
+ mouseEvent.Y + (SuperView == null ? mouseEvent.OfY : Frame.Y), out nx, out ny);
|
|
|
+
|
|
|
+ dragPosition = new Point (nx, ny);
|
|
|
+ LayoutSubviews ();
|
|
|
+ Frame = new Rect (nx, ny, Frame.Width, Frame.Height);
|
|
|
+ if (X == null || X is Pos.PosAbsolute) {
|
|
|
+ X = nx;
|
|
|
+ }
|
|
|
+ if (Y == null || Y is Pos.PosAbsolute) {
|
|
|
+ Y = ny;
|
|
|
+ }
|
|
|
+ //System.Diagnostics.Debug.WriteLine ($"nx:{nx},ny:{ny}");
|
|
|
|
|
|
- ClearLayoutNeeded ();
|
|
|
- ClearNeedsDisplay ();
|
|
|
+ // FIXED: optimize, only SetNeedsDisplay on the before/after regions.
|
|
|
+ SetNeedsDisplay ();
|
|
|
+ return true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- base.Redraw (base.Bounds);
|
|
|
+ if (mouseEvent.Flags == MouseFlags.Button1Released && dragPosition.HasValue) {
|
|
|
+ Application.UngrabMouse ();
|
|
|
+ Driver.UncookMouse ();
|
|
|
+ dragPosition = null;
|
|
|
+ }
|
|
|
+
|
|
|
+ //System.Diagnostics.Debug.WriteLine (mouseEvent.ToString ());
|
|
|
+ return false;
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
@@ -494,5 +742,209 @@ namespace Terminal.Gui {
|
|
|
{
|
|
|
FocusFirst ();
|
|
|
}
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Move to the next Mdi child from the <see cref="Application.MdiTop"/>.
|
|
|
+ /// </summary>
|
|
|
+ public virtual void MoveNext ()
|
|
|
+ {
|
|
|
+ Application.MoveNext ();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Move to the previous Mdi child from the <see cref="Application.MdiTop"/>.
|
|
|
+ /// </summary>
|
|
|
+ public virtual void MovePrevious ()
|
|
|
+ {
|
|
|
+ Application.MovePrevious ();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Stops running this <see cref="Toplevel"/>.
|
|
|
+ /// </summary>
|
|
|
+ public virtual void RequestStop ()
|
|
|
+ {
|
|
|
+ if (IsMdiContainer && Running
|
|
|
+ && (Application.Current == this
|
|
|
+ || Application.Current?.Modal == false
|
|
|
+ || Application.Current?.Modal == true && Application.Current?.Running == false)) {
|
|
|
+
|
|
|
+ foreach (var child in Application.MdiChildes) {
|
|
|
+ var ev = new ToplevelClosingEventArgs (this);
|
|
|
+ if (child.OnClosing (ev)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ child.Running = false;
|
|
|
+ Application.RequestStop (child);
|
|
|
+ }
|
|
|
+ Running = false;
|
|
|
+ Application.RequestStop (this);
|
|
|
+ } else if (IsMdiContainer && Running && Application.Current?.Modal == true && Application.Current?.Running == true) {
|
|
|
+ var ev = new ToplevelClosingEventArgs (Application.Current);
|
|
|
+ if (OnClosing (ev)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Application.RequestStop (Application.Current);
|
|
|
+ } else if (!IsMdiContainer && Running && (!Modal || (Modal && Application.Current != this))) {
|
|
|
+ var ev = new ToplevelClosingEventArgs (this);
|
|
|
+ if (OnClosing (ev)) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ Running = false;
|
|
|
+ Application.RequestStop (this);
|
|
|
+ } else {
|
|
|
+ Application.RequestStop (Application.Current);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Stops running the <paramref name="top"/> <see cref="Toplevel"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="top">The toplevel to request stop.</param>
|
|
|
+ public virtual void RequestStop (Toplevel top)
|
|
|
+ {
|
|
|
+ top.RequestStop ();
|
|
|
+ }
|
|
|
+
|
|
|
+ ///<inheritdoc/>
|
|
|
+ public override void PositionCursor ()
|
|
|
+ {
|
|
|
+ if (!IsMdiContainer) {
|
|
|
+ base.PositionCursor ();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (Focused == null) {
|
|
|
+ foreach (var top in Application.MdiChildes) {
|
|
|
+ if (top != this && top.Visible) {
|
|
|
+ top.SetFocus ();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ base.PositionCursor ();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Gets the current visible toplevel Mdi child that match the arguments pattern.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="type">The type.</param>
|
|
|
+ /// <param name="exclude">The strings to exclude.</param>
|
|
|
+ /// <returns>The matched view.</returns>
|
|
|
+ public View GetTopMdiChild (Type type = null, string [] exclude = null)
|
|
|
+ {
|
|
|
+ if (Application.MdiTop == null) {
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (var top in Application.MdiChildes) {
|
|
|
+ if (type != null && top.GetType () == type
|
|
|
+ && exclude?.Contains (top.Data.ToString ()) == false) {
|
|
|
+ return top;
|
|
|
+ } else if ((type != null && top.GetType () != type)
|
|
|
+ || (exclude?.Contains (top.Data.ToString ()) == true)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ return top;
|
|
|
+ }
|
|
|
+ return null;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Shows the Mdi child indicated by the <paramref name="top"/> setting as <see cref="Application.Current"/>.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="top">The toplevel.</param>
|
|
|
+ /// <returns><see langword="true"/> if the toplevel can be showed.<see langword="false"/> otherwise.</returns>
|
|
|
+ public virtual bool ShowChild (Toplevel top = null)
|
|
|
+ {
|
|
|
+ if (Application.MdiTop != null) {
|
|
|
+ return Application.ShowChild (top == null ? this : top);
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Implements the <see cref="IEqualityComparer{T}"/> to comparing two <see cref="Toplevel"/> used by <see cref="StackExtensions"/>.
|
|
|
+ /// </summary>
|
|
|
+ public class ToplevelEqualityComparer : IEqualityComparer<Toplevel> {
|
|
|
+ /// <summary>Determines whether the specified objects are equal.</summary>
|
|
|
+ /// <param name="x">The first object of type <see cref="Toplevel" /> to compare.</param>
|
|
|
+ /// <param name="y">The second object of type <see cref="Toplevel" /> to compare.</param>
|
|
|
+ /// <returns>
|
|
|
+ /// <see langword="true" /> if the specified objects are equal; otherwise, <see langword="false" />.</returns>
|
|
|
+ public bool Equals (Toplevel x, Toplevel y)
|
|
|
+ {
|
|
|
+ if (y == null && x == null)
|
|
|
+ return true;
|
|
|
+ else if (x == null || y == null)
|
|
|
+ return false;
|
|
|
+ else if (x.Id == y.Id)
|
|
|
+ return true;
|
|
|
+ else
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>Returns a hash code for the specified object.</summary>
|
|
|
+ /// <param name="obj">The <see cref="Toplevel" /> for which a hash code is to be returned.</param>
|
|
|
+ /// <returns>A hash code for the specified object.</returns>
|
|
|
+ /// <exception cref="ArgumentNullException">The type of <paramref name="obj" /> is a reference type and <paramref name="obj" /> is <see langword="null" />.</exception>
|
|
|
+ public int GetHashCode (Toplevel obj)
|
|
|
+ {
|
|
|
+ if (obj == null)
|
|
|
+ throw new ArgumentNullException ();
|
|
|
+
|
|
|
+ int hCode = 0;
|
|
|
+ if (int.TryParse (obj.Id.ToString (), out int result)) {
|
|
|
+ hCode = result;
|
|
|
+ }
|
|
|
+ return hCode.GetHashCode ();
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Implements the <see cref="IComparer{T}"/> to sort the <see cref="Toplevel"/> from the <see cref="Application.MdiChildes"/> if needed.
|
|
|
+ /// </summary>
|
|
|
+ public sealed class ToplevelComparer : IComparer<Toplevel> {
|
|
|
+ /// <summary>Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.</summary>
|
|
|
+ /// <param name="x">The first object to compare.</param>
|
|
|
+ /// <param name="y">The second object to compare.</param>
|
|
|
+ /// <returns>A signed integer that indicates the relative values of <paramref name="x" /> and <paramref name="y" />, as shown in the following table.Value Meaning Less than zero
|
|
|
+ /// <paramref name="x" /> is less than <paramref name="y" />.Zero
|
|
|
+ /// <paramref name="x" /> equals <paramref name="y" />.Greater than zero
|
|
|
+ /// <paramref name="x" /> is greater than <paramref name="y" />.</returns>
|
|
|
+ public int Compare (Toplevel x, Toplevel y)
|
|
|
+ {
|
|
|
+ if (ReferenceEquals (x, y))
|
|
|
+ return 0;
|
|
|
+ else if (x == null)
|
|
|
+ return -1;
|
|
|
+ else if (y == null)
|
|
|
+ return 1;
|
|
|
+ else
|
|
|
+ return string.Compare (x.Id.ToString (), y.Id.ToString ());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// <see cref="EventArgs"/> implementation for the <see cref="Toplevel.Closing"/> event.
|
|
|
+ /// </summary>
|
|
|
+ public class ToplevelClosingEventArgs : EventArgs {
|
|
|
+ /// <summary>
|
|
|
+ /// The toplevel requesting stop.
|
|
|
+ /// </summary>
|
|
|
+ public View RequestingTop { get; }
|
|
|
+ /// <summary>
|
|
|
+ /// Provides an event cancellation option.
|
|
|
+ /// </summary>
|
|
|
+ public bool Cancel { get; set; }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// Initializes the event arguments with the requesting toplevel.
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="requestingTop">The <see cref="RequestingTop"/>.</param>
|
|
|
+ public ToplevelClosingEventArgs (Toplevel requestingTop)
|
|
|
+ {
|
|
|
+ RequestingTop = requestingTop;
|
|
|
+ }
|
|
|
}
|
|
|
}
|