Browse Source

Merge branch 'main' into dependabot/nuget/ReactiveUI.Fody-14.1.1

Charlie Kindel 4 years ago
parent
commit
7fe82531d4

+ 457 - 31
Terminal.Gui/Core/Application.cs

@@ -10,7 +10,7 @@
 //   - "Colors" type or "Attributes" type?
 //   - What to surface as "BackgroundCOlor" when clearing a window, an attribute or colors?
 //
-// Optimziations
+// Optimizations
 //   - Add rendering limitation to the exposed area
 using System;
 using System.Collections;
@@ -23,7 +23,7 @@ using System.ComponentModel;
 namespace Terminal.Gui {
 
 	/// <summary>
-	/// A static, singelton class provding the main application driver for Terminal.Gui apps. 
+	/// A static, singleton class providing the main application driver for Terminal.Gui apps. 
 	/// </summary>
 	/// <example>
 	/// <code>
@@ -55,11 +55,43 @@ namespace Terminal.Gui {
 	///   </para>
 	/// </remarks>
 	public static class Application {
+		static Stack<Toplevel> toplevels = new Stack<Toplevel> ();
+
 		/// <summary>
 		/// The current <see cref="ConsoleDriver"/> in use.
 		/// </summary>
 		public static ConsoleDriver Driver;
-		
+
+		/// <summary>
+		/// Gets all the Mdi childes which represent all the not modal <see cref="Toplevel"/> from the <see cref="MdiTop"/>.
+		/// </summary>
+		public static List<Toplevel> MdiChildes {
+			get {
+				if (MdiTop != null) {
+					List<Toplevel> mdiChildes = new List<Toplevel> ();
+					foreach (var top in toplevels) {
+						if (top != MdiTop && !top.Modal) {
+							mdiChildes.Add (top);
+						}
+					}
+					return mdiChildes;
+				}
+				return null;
+			}
+		}
+
+		/// <summary>
+		/// The <see cref="Toplevel"/> object used for the application on startup which <see cref="Toplevel.IsMdiContainer"/> is true.
+		/// </summary>
+		public static Toplevel MdiTop {
+			get {
+				if (Top.IsMdiContainer) {
+					return Top;
+				}
+				return null;
+			}
+		}
+
 		/// <summary>
 		/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
 		/// </summary>
@@ -125,8 +157,6 @@ namespace Terminal.Gui {
 		/// <value>The main loop.</value>
 		public static MainLoop MainLoop { get; private set; }
 
-		static Stack<Toplevel> toplevels = new Stack<Toplevel> ();
-
 		/// <summary>
 		///   This event is raised on each iteration of the <see cref="MainLoop"/> 
 		/// </summary>
@@ -349,6 +379,72 @@ namespace Terminal.Gui {
 			}
 		}
 
+		static View FindDeepestTop (Toplevel start, int x, int y, out int resx, out int resy)
+		{
+			var startFrame = start.Frame;
+
+			if (!startFrame.Contains (x, y)) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+
+			if (toplevels != null) {
+				int count = toplevels.Count;
+				if (count > 0) {
+					var rx = x - startFrame.X;
+					var ry = y - startFrame.Y;
+					foreach (var t in toplevels) {
+						if (t != Current) {
+							if (t != start && t.Visible && t.Frame.Contains (rx, ry)) {
+								start = t;
+								break;
+							}
+						}
+					}
+				}
+			}
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
+			return start;
+		}
+
+		static View FindDeepestMdiView (View start, int x, int y, out int resx, out int resy)
+		{
+			if (start.GetType ().BaseType != typeof (Toplevel)
+				&& !((Toplevel)start).IsMdiContainer) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+
+			var startFrame = start.Frame;
+
+			if (!startFrame.Contains (x, y)) {
+				resx = 0;
+				resy = 0;
+				return null;
+			}
+
+			int count = toplevels.Count;
+			for (int i = count - 1; i >= 0; i--) {
+				foreach (var top in toplevels) {
+					var rx = x - startFrame.X;
+					var ry = y - startFrame.Y;
+					if (top.Visible && top.Frame.Contains (rx, ry)) {
+						var deep = FindDeepestView (top, rx, ry, out resx, out resy);
+						if (deep == null)
+							return FindDeepestMdiView (top, rx, ry, out resx, out resy);
+						if (deep != MdiTop)
+							return deep;
+					}
+				}
+			}
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
+			return start;
+		}
+
 		static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
 		{
 			var startFrame = start.Frame;
@@ -380,6 +476,18 @@ namespace Terminal.Gui {
 			return start;
 		}
 
+		static View FindTopFromView (View view)
+		{
+			View top = view?.SuperView != null ? view.SuperView : view;
+
+			while (top?.SuperView != null) {
+				if (top?.SuperView != null) {
+					top = top.SuperView;
+				}
+			}
+			return top;
+		}
+
 		internal static View mouseGrabView;
 
 		/// <summary>
@@ -441,6 +549,17 @@ namespace Terminal.Gui {
 				}
 			}
 
+			if ((view == null || view == MdiTop) && !Current.Modal && MdiTop != null
+				&& me.Flags != MouseFlags.ReportMousePosition && me.Flags != 0) {
+
+				var top = FindDeepestTop (Top, me.X, me.Y, out _, out _);
+				view = FindDeepestView (top, me.X, me.Y, out rx, out ry);
+
+				if (view != null && view != MdiTop && top != Current) {
+					MoveCurrent ((Toplevel)top);
+				}
+			}
+
 			if (view != null) {
 				var nme = new MouseEvent () {
 					X = rx,
@@ -473,6 +592,56 @@ namespace Terminal.Gui {
 			}
 		}
 
+		// Only return true if the Current has changed.
+		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 (MdiTop != null && top != MdiTop && top != Current && Current?.Modal == true && !toplevels.Peek ().Modal) {
+				lock (toplevels) {
+					toplevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
+				}
+				var index = 0;
+				var savedToplevels = toplevels.ToArray ();
+				foreach (var 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 (MdiTop != null && top != MdiTop && top != Current && Current?.Running == false && !top.Running) {
+				lock (toplevels) {
+					toplevels.MoveTo (Current, 0, new ToplevelEqualityComparer ());
+				}
+				var index = 0;
+				foreach (var t in toplevels.ToArray ()) {
+					if (!t.Running && t != Current && index > 0) {
+						lock (toplevels) {
+							toplevels.MoveTo (top, index - 1, new ToplevelEqualityComparer ());
+						}
+					}
+					index++;
+				}
+				return false;
+			}
+			if ((MdiTop != null && top?.Modal == true && toplevels.Peek () != top)
+				|| (MdiTop != null && Current != MdiTop && Current?.Modal == false && top == MdiTop)
+				|| (MdiTop != null && Current?.Modal == false && top != Current)
+				|| (MdiTop != null && Current?.Modal == true && top == MdiTop)) {
+				lock (toplevels) {
+					toplevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
+					Current = top;
+				}
+			}
+			return true;
+		}
+
 		static bool OutsideFrame (Point p, Rect r)
 		{
 			return p.X < 0 || p.X > r.Width - 1 || p.Y < 0 || p.Y > r.Height - 1;
@@ -493,8 +662,12 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public static RunState Begin (Toplevel toplevel)
 		{
-			if (toplevel == null)
+			if (toplevel == null) {
 				throw new ArgumentNullException (nameof (toplevel));
+			} else if (toplevel.IsMdiContainer && MdiTop != null) {
+				throw new InvalidOperationException ("Only one Mdi Container is allowed.");
+			}
+
 			var rs = new RunState (toplevel);
 
 			Init ();
@@ -506,17 +679,67 @@ namespace Terminal.Gui {
 				initializable.BeginInit ();
 				initializable.EndInit ();
 			}
-			toplevels.Push (toplevel);
-			Current = toplevel;
+
+			lock (toplevels) {
+				if (string.IsNullOrEmpty (toplevel.Id.ToString ())) {
+					var count = 1;
+					var id = (toplevels.Count + count).ToString ();
+					while (toplevels.Count > 0 && toplevels.FirstOrDefault (x => x.Id.ToString () == id) != null) {
+						count++;
+						id = (toplevels.Count + count).ToString ();
+					}
+					toplevel.Id = (toplevels.Count + count).ToString ();
+
+					toplevels.Push (toplevel);
+				} else {
+					var dup = toplevels.FirstOrDefault (x => x.Id.ToString () == toplevel.Id);
+					if (dup == null) {
+						toplevels.Push (toplevel);
+					}
+				}
+
+				if (toplevels.FindDuplicates (new ToplevelEqualityComparer ()).Count > 0) {
+					throw new ArgumentException ("There are duplicates toplevels Id's");
+				}
+			}
+			if (toplevel.IsMdiContainer) {
+				Top = toplevel;
+			}
+
+			var refreshDriver = true;
+			if (MdiTop == null || toplevel.IsMdiContainer || (Current?.Modal == false && toplevel.Modal)
+				|| (Current?.Modal == false && !toplevel.Modal) || (Current?.Modal == true && toplevel.Modal)) {
+
+				if (toplevel.Visible) {
+					Current = toplevel;
+					SetCurrentAsTop ();
+				} else {
+					refreshDriver = false;
+				}
+			} else if ((MdiTop != null && toplevel != MdiTop && Current?.Modal == true && !toplevels.Peek ().Modal)
+				|| (MdiTop != null && toplevel != MdiTop && Current?.Running == false)) {
+				refreshDriver = false;
+				MoveCurrent (toplevel);
+			} else {
+				refreshDriver = false;
+				MoveCurrent (Current);
+			}
+
 			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
 			if (toplevel.LayoutStyle == LayoutStyle.Computed)
 				toplevel.SetRelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
+			toplevel.PositionToplevels ();
 			toplevel.LayoutSubviews ();
 			toplevel.WillPresent ();
-			toplevel.OnLoaded ();
-			Redraw (toplevel);
-			toplevel.PositionCursor ();
-			Driver.Refresh ();
+			if (refreshDriver) {
+				if (MdiTop != null) {
+					MdiTop.OnChildLoaded (toplevel);
+				}
+				toplevel.OnLoaded ();
+				Redraw (toplevel);
+				toplevel.PositionCursor ();
+				Driver.Refresh ();
+			}
 
 			return rs;
 		}
@@ -530,7 +753,11 @@ namespace Terminal.Gui {
 			if (runState == null)
 				throw new ArgumentNullException (nameof (runState));
 
-			runState.Toplevel.OnUnloaded ();
+			if (MdiTop != null) {
+				MdiTop.OnChildUnloaded (runState.Toplevel);
+			} else {
+				runState.Toplevel.OnUnloaded ();
+			}
 			runState.Dispose ();
 		}
 
@@ -544,9 +771,10 @@ namespace Terminal.Gui {
 
 		// Encapsulate all setting of initial state for Application; Having
 		// this in a function like this ensures we don't make mistakes in
-		// guranteeing that the state of this singleton is deterministic when Init
+		// guaranteeing that the state of this singleton is deterministic when Init
 		// starts running and after Shutdown returns.
-		static void ResetState () {
+		static void ResetState ()
+		{
 			// Shutdown is the bookend for Init. As such it needs to clean up all resources
 			// Init created. Apps that do any threading will need to code defensively for this.
 			// e.g. see Issue #537
@@ -595,8 +823,10 @@ namespace Terminal.Gui {
 			Driver.UpdateScreen ();
 			View last = null;
 			foreach (var v in toplevels.Reverse ()) {
-				v.SetNeedsDisplay ();
-				v.Redraw (v.Bounds);
+				if (v.Visible) {
+					v.SetNeedsDisplay ();
+					v.Redraw (v.Bounds);
+				}
 				last = v;
 			}
 			last?.PositionCursor ();
@@ -609,10 +839,21 @@ namespace Terminal.Gui {
 				throw new ArgumentException ("The view that you end with must be balanced");
 			toplevels.Pop ();
 
+			(view as Toplevel)?.OnClosed ((Toplevel)view);
+
+			if (MdiTop != null && !((Toplevel)view).Modal && view != MdiTop) {
+				MdiTop.OnChildClosed (view as Toplevel);
+			}
+
 			if (toplevels.Count == 0) {
 				Current = null;
 			} else {
 				Current = toplevels.Peek ();
+				if (toplevels.Count == 1 && Current == MdiTop) {
+					MdiTop.OnAllChildClosed ();
+				} else {
+					SetCurrentAsTop ();
+				}
 				Refresh ();
 			}
 		}
@@ -644,18 +885,29 @@ namespace Terminal.Gui {
 
 					MainLoop.MainIteration ();
 					Iteration?.Invoke ();
-					
+
+					EnsureModalAlwaysOnTop (state.Toplevel);
+					if ((state.Toplevel != Current && Current?.Modal == true)
+						|| (state.Toplevel != Current && Current?.Modal == false)) {
+						MdiTop?.OnDeactivate (state.Toplevel);
+						state.Toplevel = Current;
+						MdiTop?.OnActivate (state.Toplevel);
+						Top.SetChildNeedsDisplay ();
+						Refresh ();
+					}
 					if (Driver.EnsureCursorVisibility ()) {
 						state.Toplevel.SetNeedsDisplay ();
 					}
 				} else if (!wait) {
 					return;
 				}
-				if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+				if (state.Toplevel != Top
+					&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
 					Top.Redraw (Top.Bounds);
 					state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 				}
-				if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded) {
+				if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded
+					|| MdiChildNeedsDisplay ()) {
 					state.Toplevel.Redraw (state.Toplevel.Bounds);
 					if (DebugDrawBounds) {
 						DrawBounds (state.Toplevel);
@@ -665,7 +917,40 @@ namespace Terminal.Gui {
 				} else {
 					Driver.UpdateCursor ();
 				}
+				if (state.Toplevel != Top && !state.Toplevel.Modal
+					&& (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
+					Top.Redraw (Top.Bounds);
+				}
+			}
+		}
+
+		static void EnsureModalAlwaysOnTop (Toplevel toplevel)
+		{
+			if (!toplevel.Running || toplevel == Current || MdiTop == null || toplevels.Peek ().Modal) {
+				return;
+			}
+
+			foreach (var top in toplevels.Reverse ()) {
+				if (top.Modal && top != Current) {
+					MoveCurrent (top);
+					return;
+				}
+			}
+		}
+
+		static bool MdiChildNeedsDisplay ()
+		{
+			if (MdiTop == null) {
+				return false;
+			}
+
+			foreach (var top in toplevels) {
+				if (top != Current && top.Visible && (!top.NeedDisplay.IsEmpty || top.ChildNeedsDisplay || top.LayoutNeeded)) {
+					MdiTop.SetChildNeedsDisplay ();
+					return true;
+				}
 			}
+			return false;
 		}
 
 		internal static bool DebugDrawBounds = false;
@@ -692,8 +977,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static void Run<T> (Func<Exception, bool> errorHandler = null) where T : Toplevel, new()
 		{
-			Init (() => new T ());
-			Run (Top, errorHandler);
+			if (_initialized && Driver != null) {
+				var top = new T ();
+				if (top.GetType ().BaseType != typeof (Toplevel)) {
+					throw new ArgumentException (top.GetType ().BaseType.Name);
+				}
+				Run (top, errorHandler);
+			} else {
+				Init (() => new T ());
+				Run (Top, errorHandler);
+			}
 		}
 
 		/// <summary>
@@ -723,7 +1016,7 @@ namespace Terminal.Gui {
 		///     When <paramref name="errorHandler"/> is null the exception is rethrown, when it returns true the application is resumed and when false method exits gracefully.
 		///   </para>
 		/// </remarks>
-		/// <param name="view">The <see cref="Toplevel"/> tu run modally.</param>
+		/// <param name="view">The <see cref="Toplevel"/> to run modally.</param>
 		/// <param name="errorHandler">Handler for any unhandled exceptions (resumes when returns true, rethrows when null).</param>
 		public static void Run (Toplevel view, Func<Exception, bool> errorHandler = null)
 		{
@@ -751,19 +1044,74 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Stops running the most recent <see cref="Toplevel"/>. 
+		/// Stops running the most recent <see cref="Toplevel"/> or the <paramref name="top"/> if provided.
 		/// </summary>
+		/// <param name="top">The toplevel to request stop.</param>
 		/// <remarks>
 		///   <para>
 		///   This will cause <see cref="Application.Run(Func{Exception, bool})"/> to return.
 		///   </para>
 		///   <para>
-		///     Calling <see cref="Application.RequestStop"/> is equivalent to setting the <see cref="Toplevel.Running"/> property on the curently running <see cref="Toplevel"/> to false.
+		///     Calling <see cref="Application.RequestStop"/> is equivalent to setting the <see cref="Toplevel.Running"/> property on the currently running <see cref="Toplevel"/> to false.
 		///   </para>
 		/// </remarks>
-		public static void RequestStop ()
+		public static void RequestStop (Toplevel top = null)
 		{
-			Current.Running = false;
+			if (MdiTop == null || top == null || (MdiTop == null && top != null)) {
+				top = Current;
+			}
+
+			if (MdiTop != null && top.IsMdiContainer && top?.Running == true
+				&& (Current?.Modal == false || (Current?.Modal == true && Current?.Running == false))) {
+
+				MdiTop.RequestStop ();
+			} else if (MdiTop != null && top != Current && Current?.Running == true && Current?.Modal == true
+				&& top.Modal && top.Running) {
+
+				var ev = new ToplevelClosingEventArgs (Current);
+				Current.OnClosing (ev);
+				if (ev.Cancel) {
+					return;
+				}
+				ev = new ToplevelClosingEventArgs (top);
+				top.OnClosing (ev);
+				if (ev.Cancel) {
+					return;
+				}
+				Current.Running = false;
+				top.Running = false;
+			} else if ((MdiTop != null && top != MdiTop && top != Current && Current?.Modal == false
+				&& Current?.Running == true && !top.Running)
+				|| (MdiTop != null && top != MdiTop && top != Current && Current?.Modal == false
+				&& Current?.Running == false && !top.Running && toplevels.ToArray () [1].Running)) {
+
+				MoveCurrent (top);
+			} else if (MdiTop != null && Current != top && Current?.Running == true && !top.Running
+				&& Current?.Modal == true && top.Modal) {
+				// The Current and the top are both modal so needed to set the Current.Running to false too.
+				Current.Running = false;
+			} else if (MdiTop != null && Current == top && MdiTop?.Running == true && Current?.Running == true && top.Running
+				&& Current?.Modal == true && top.Modal) {
+				// The MdiTop was requested to stop inside a modal toplevel which is the Current and top,
+				// both are the same, so needed to set the Current.Running to false too.
+				Current.Running = false;
+			} else {
+				Toplevel currentTop;
+				if (top == Current || (Current?.Modal == true && !top.Modal)) {
+					currentTop = Current;
+				} else {
+					currentTop = top;
+				}
+				if (!currentTop.Running) {
+					return;
+				}
+				var ev = new ToplevelClosingEventArgs (currentTop);
+				currentTop.OnClosing (ev);
+				if (ev.Cancel) {
+					return;
+				}
+				currentTop.Running = false;
+			}
 		}
 
 		/// <summary>
@@ -788,17 +1136,95 @@ namespace Terminal.Gui {
 		static void TerminalResized ()
 		{
 			var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
-			Top.Frame = full;
-			Top.Width = full.Width;
-			Top.Height = full.Height;
+			SetToplevelsSize (full);
 			Resized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height });
 			Driver.Clip = full;
 			foreach (var t in toplevels) {
-				t.PositionToplevels ();
 				t.SetRelativeLayout (full);
+				t.PositionToplevels ();
 				t.LayoutSubviews ();
 			}
 			Refresh ();
 		}
+
+		static void SetToplevelsSize (Rect full)
+		{
+			if (MdiTop == null) {
+				foreach (var t in toplevels) {
+					if (t?.SuperView == null && !t.Modal) {
+						t.Frame = full;
+						t.Width = full.Width;
+						t.Height = full.Height;
+					}
+				}
+			} else {
+				Top.Frame = full;
+				Top.Width = full.Width;
+				Top.Height = full.Height;
+			}
+		}
+
+		static bool SetCurrentAsTop ()
+		{
+			if (MdiTop == null && Current != Top && Current?.SuperView == null && Current?.Modal == false) {
+				Top = Current;
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Move to the next Mdi child from the <see cref="MdiTop"/>.
+		/// </summary>
+		public static void MoveNext ()
+		{
+			if (MdiTop != null && !Current.Modal) {
+				lock (toplevels) {
+					toplevels.MoveNext ();
+					while (toplevels.Peek () == MdiTop || !toplevels.Peek ().Visible) {
+						toplevels.MoveNext ();
+					}
+					Current = toplevels.Peek ();
+				}
+			}
+		}
+
+		/// <summary>
+		/// Move to the previous Mdi child from the <see cref="MdiTop"/>.
+		/// </summary>
+		public static void MovePrevious ()
+		{
+			if (MdiTop != null && !Current.Modal) {
+				lock (toplevels) {
+					toplevels.MovePrevious ();
+					while (toplevels.Peek () == MdiTop || !toplevels.Peek ().Visible) {
+						lock (toplevels) {
+							toplevels.MovePrevious ();
+						}
+					}
+					Current = toplevels.Peek ();
+				}
+			}
+		}
+
+		internal static bool ShowChild (Toplevel top)
+		{
+			if (top.Visible && MdiTop != null && Current?.Modal == false) {
+				lock (toplevels) {
+					toplevels.MoveTo (top, 0, new ToplevelEqualityComparer ());
+					Current = top;
+				}
+				return true;
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Wakes up the mainloop that might be waiting on input, must be thread safe.
+		/// </summary>
+		public static void DoEvents ()
+		{
+			MainLoop.Driver.Wakeup ();
+		}
 	}
 }

+ 196 - 0
Terminal.Gui/Core/StackExtensions.cs

@@ -0,0 +1,196 @@
+using System;
+using System.Collections.Generic;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Extension of <see cref="Stack{T}"/> helper to work with specific <see cref="IEqualityComparer{T}"/>
+	/// </summary>
+	public static class StackExtensions {
+		/// <summary>
+		/// Replaces an stack object values that match with the value to replace.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		/// <param name="valueToReplace">Value to replace.</param>
+		/// <param name="valueToReplaceWith">Value to replace with to what matches the value to replace.</param>
+		/// <param name="comparer">The comparison object.</param>
+		public static void Replace<T> (this Stack<T> stack, T valueToReplace,
+			T valueToReplaceWith, IEqualityComparer<T> comparer = null)
+		{
+			comparer = comparer ?? EqualityComparer<T>.Default;
+
+			var temp = new Stack<T> ();
+			while (stack.Count > 0) {
+				var value = stack.Pop ();
+				if (comparer.Equals (value, valueToReplace)) {
+					stack.Push (valueToReplaceWith);
+					break;
+				}
+				temp.Push (value);
+			}
+
+			while (temp.Count > 0)
+				stack.Push (temp.Pop ());
+		}
+
+		/// <summary>
+		/// Swap two stack objects values that matches with the both values.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		/// <param name="valueToSwapFrom">Value to swap from.</param>
+		/// <param name="valueToSwapTo">Value to swap to.</param>
+		/// <param name="comparer">The comparison object.</param>
+		public static void Swap<T> (this Stack<T> stack, T valueToSwapFrom,
+			T valueToSwapTo, IEqualityComparer<T> comparer = null)
+		{
+			comparer = comparer ?? EqualityComparer<T>.Default;
+
+			int index = stack.Count - 1;
+			T [] stackArr = new T [stack.Count];
+			while (stack.Count > 0) {
+				var value = stack.Pop ();
+				if (comparer.Equals (value, valueToSwapFrom)) {
+					stackArr [index] = valueToSwapTo;
+				} else if (comparer.Equals (value, valueToSwapTo)) {
+					stackArr [index] = valueToSwapFrom;
+				} else {
+					stackArr [index] = value;
+				}
+				index--;
+			}
+
+			for (int i = 0; i < stackArr.Length; i++)
+				stack.Push (stackArr [i]);
+		}
+
+		/// <summary>
+		/// Move the first stack object value to the end.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		public static void MoveNext<T> (this Stack<T> stack)
+		{
+			var temp = new Stack<T> ();
+			var last = stack.Pop ();
+			while (stack.Count > 0) {
+				var value = stack.Pop ();
+				temp.Push (value);
+			}
+			temp.Push (last);
+
+			while (temp.Count > 0)
+				stack.Push (temp.Pop ());
+		}
+
+		/// <summary>
+		/// Move the last stack object value to the top.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		public static void MovePrevious<T> (this Stack<T> stack)
+		{
+			var temp = new Stack<T> ();
+			T first = default;
+			while (stack.Count > 0) {
+				var value = stack.Pop ();
+				temp.Push (value);
+				if (stack.Count == 1) {
+					first = stack.Pop ();
+				}
+			}
+
+			while (temp.Count > 0)
+				stack.Push (temp.Pop ());
+			stack.Push (first);
+		}
+
+		/// <summary>
+		/// Find all duplicates stack objects values.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		/// <param name="comparer">The comparison object.</param>
+		/// <returns>The duplicates stack object.</returns>
+		public static Stack<T> FindDuplicates<T> (this Stack<T> stack, IEqualityComparer<T> comparer = null)
+		{
+			comparer = comparer ?? EqualityComparer<T>.Default;
+
+			var dup = new Stack<T> ();
+			T [] stackArr = stack.ToArray ();
+			for (int i = 0; i < stackArr.Length; i++) {
+				var value = stackArr [i];
+				for (int j = i + 1; j < stackArr.Length; j++) {
+					var valueToFind = stackArr [j];
+					if (comparer.Equals (value, valueToFind) && !Contains (dup, valueToFind)) {
+						dup.Push (value);
+					}
+				}
+			}
+
+			return dup;
+		}
+
+		/// <summary>
+		/// Check if the stack object contains the value to find.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		/// <param name="valueToFind">Value to find.</param>
+		/// <param name="comparer">The comparison object.</param>
+		/// <returns><c>true</c> If the value was found.<c>false</c> otherwise.</returns>
+		public static bool Contains<T> (this Stack<T> stack, T valueToFind, IEqualityComparer<T> comparer = null)
+		{
+			comparer = comparer ?? EqualityComparer<T>.Default;
+
+			foreach (T obj in stack) {
+				if (comparer.Equals (obj, valueToFind)) {
+					return true;
+				}
+			}
+			return false;
+		}
+
+		/// <summary>
+		/// Move the stack object value to the index.
+		/// </summary>
+		/// <typeparam name="T">The stack object type.</typeparam>
+		/// <param name="stack">The stack object.</param>
+		/// <param name="valueToMove">Value to move.</param>
+		/// <param name="index">The index where to move.</param>
+		/// <param name="comparer">The comparison object.</param>
+		public static void MoveTo<T> (this Stack<T> stack, T valueToMove, int index = 0,
+			IEqualityComparer<T> comparer = null)
+		{
+			if (index < 0) {
+				return;
+			}
+
+			comparer = comparer ?? EqualityComparer<T>.Default;
+
+			var temp = new Stack<T> ();
+			var toMove = default (T);
+			var stackCount = stack.Count;
+			var count = 0;
+			while (stack.Count > 0) {
+				var value = stack.Pop ();
+				if (comparer.Equals (value, valueToMove)) {
+					toMove = value;
+					break;
+				}
+				temp.Push (value);
+				count++;
+			}
+
+			int idx = 0;
+			while (stack.Count < stackCount) {
+				if (count - idx == index) {
+					stack.Push (toMove);
+				} else {
+					stack.Push (temp.Pop ());
+				}
+				idx++;
+			}
+		}
+	}
+}

+ 474 - 37
Terminal.Gui/Core/Toplevel.cs

@@ -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>
@@ -112,7 +196,7 @@ namespace Terminal.Gui {
 
 		void Initialize ()
 		{
-			ColorScheme = Colors.Base;
+			ColorScheme = Colors.TopLevel;
 		}
 
 		/// <summary>
@@ -142,12 +226,26 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets or sets the menu for this Toplevel
 		/// </summary>
-		public MenuBar MenuBar { get; set; }
+		public virtual MenuBar MenuBar { get; set; }
 
 		/// <summary>
 		/// Gets or sets the status bar for this Toplevel
 		/// </summary>
-		public StatusBar StatusBar { get; set; }
+		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 ();
@@ -234,21 +336,31 @@ namespace Terminal.Gui {
 					old?.SetNeedsDisplay ();
 					Focused?.SetNeedsDisplay ();
 				} else {
-					FocusNearestView (SuperView?.TabIndexes?.Reverse(), Direction.Backward);
+					FocusNearestView (SuperView?.TabIndexes?.Reverse (), Direction.Backward);
 				}
 				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:
@@ -265,7 +377,7 @@ namespace Terminal.Gui {
 				return true;
 			}
 
-			if (ShortcutHelper.FindAndOpenByShortcut(keyEvent, this)) {
+			if (ShortcutHelper.FindAndOpenByShortcut (keyEvent, this)) {
 				return true;
 			}
 			return false;
@@ -319,9 +431,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Add (View view)
 		{
-			if (this == Application.Top) {
-				AddMenuStatusBar (view);
-			}
+			AddMenuStatusBar (view);
 			base.Add (view);
 		}
 
@@ -424,10 +534,15 @@ namespace Terminal.Gui {
 			}
 		}
 
-		private void PositionToplevel (Toplevel top)
+		/// <summary>
+		/// Virtual method which allow to be overridden to implement specific positions for inherited <see cref="Toplevel"/>.
+		/// </summary>
+		/// <param name="top">The toplevel.</param>
+		public virtual void PositionToplevel (Toplevel top)
 		{
 			EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
-			if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
+			if ((top?.SuperView != null || top != Application.Top)
+				&& (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;
 				}
@@ -435,11 +550,30 @@ namespace Terminal.Gui {
 					top.Y = ny;
 				}
 			}
-			if (top.StatusBar != null) {
-				if (ny + top.Frame.Height > top.Frame.Height - (top.StatusBar.Visible ? 1 : 0)) {
-					if (top.Height is Dim.DimFill)
-						top.Height = Dim.Fill () - (top.StatusBar.Visible ? 1 : 0);
+
+			View superView = null;
+			StatusBar statusBar = null;
+
+			if (top != Application.Top && Application.Top.StatusBar != null) {
+				superView = Application.Top;
+				statusBar = Application.Top.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 >= superView.Frame.Height - (statusBar.Visible ? 1 : 0)) {
+					if (top.Height is Dim.DimFill) {
+						top.Height = Dim.Fill (statusBar.Visible ? 1 : 0);
+					}
+				}
+				if (superView == Application.Top) {
+					top.SetRelativeLayout (superView.Frame);
+				} else {
+					superView.LayoutSubviews ();
 				}
+			}
+			if (top.StatusBar != null) {
 				if (top.StatusBar.Frame.Y != top.Frame.Height - (top.StatusBar.Visible ? 1 : 0)) {
 					top.StatusBar.Y = top.Frame.Height - (top.StatusBar.Visible ? 1 : 0);
 					top.LayoutSubviews ();
@@ -451,29 +585,128 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
-			if (IsCurrentTop || this == Application.Top) {
-				if (!NeedDisplay.IsEmpty || LayoutNeeded) {
-					Driver.SetAttribute (Colors.TopLevel.Normal);
-
-					// This is the Application.Top. Clear just the region we're being asked to redraw 
-					// (the bounds passed to us).
-					Clear (bounds);
-					Driver.SetAttribute (Colors.Base.Normal);
-					PositionToplevels ();
+			if (!Visible) {
+				return;
+			}
 
-					foreach (var view in Subviews) {
-						if (view.Frame.IntersectsWith (bounds)) {
-							view.SetNeedsLayout ();
-							view.SetNeedsDisplay (view.Bounds);
+			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);
+
+				if (LayoutStyle == LayoutStyle.Computed)
+					SetRelativeLayout (Bounds);
+				PositionToplevels ();
+				LayoutSubviews ();
+
+				if (this == Application.MdiTop) {
+					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);
+							}
 						}
 					}
+				}
+
+				foreach (var view in Subviews) {
+					if (view.Frame.IntersectsWith (bounds) && !OutsideTopFrame (this)) {
+						view.SetNeedsLayout ();
+						view.SetNeedsDisplay (view.Bounds);
+						//view.Redraw (view.Bounds);
+					}
+				}
+
+				ClearLayoutNeeded ();
+				ClearNeedsDisplay ();
+			}
+
+			base.Redraw (Bounds);
+		}
+
+		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>
@@ -484,5 +717,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;
+		}
 	}
 }

+ 15 - 10
Terminal.Gui/Core/View.cs

@@ -268,7 +268,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		private int GetTabIndex (int idx)
+		int GetTabIndex (int idx)
 		{
 			int i = 0;
 			foreach (var v in SuperView.tabIndexes) {
@@ -280,7 +280,7 @@ namespace Terminal.Gui {
 			return Math.Min (i, idx);
 		}
 
-		private void SetTabIndex ()
+		void SetTabIndex ()
 		{
 			int i = 0;
 			foreach (var v in SuperView.tabIndexes) {
@@ -989,8 +989,8 @@ namespace Terminal.Gui {
 		internal void ViewToScreen (int col, int row, out int rcol, out int rrow, bool clipped = true)
 		{
 			// Computes the real row, col relative to the screen.
-			rrow = row + frame.Y;
-			rcol = col + frame.X;
+			rrow = Math.Max (row + frame.Y, 0);
+			rcol = Math.Max (col + frame.X, 0);
 			var ccontainer = container;
 			while (ccontainer != null) {
 				rrow += ccontainer.frame.Y;
@@ -1342,7 +1342,7 @@ namespace Terminal.Gui {
 				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			}
 
-			if (!ustring.IsNullOrEmpty (Text)) {
+			if (!ustring.IsNullOrEmpty (Text) || (this is Label && !AutoSize)) {
 				Clear ();
 				// Draw any Text
 				if (textFormatter != null) {
@@ -1957,8 +1957,9 @@ namespace Terminal.Gui {
 				v.LayoutNeeded = false;
 			}
 
-			if (SuperView == Application.Top && LayoutNeeded && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
-				SetRelativeLayout (Frame);
+			if (SuperView != null && SuperView == Application.Top && LayoutNeeded
+				&& ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
+				SetRelativeLayout (SuperView.Frame);
 			}
 
 			LayoutNeeded = false;
@@ -1988,8 +1989,10 @@ namespace Terminal.Gui {
 			get => textFormatter.Text;
 			set {
 				textFormatter.Text = value;
-				ResizeView (autoSize);
-				if (textFormatter.Size != Bounds.Size) {
+				var canResize = ResizeView (autoSize);
+				if (canResize && textFormatter.Size != Bounds.Size) {
+					Bounds = new Rect (new Point (Bounds.X, Bounds.Y), textFormatter.Size);
+				} else if (!canResize && textFormatter.Size != Bounds.Size) {
 					textFormatter.Size = Bounds.Size;
 				}
 				SetNeedsLayout ();
@@ -2085,7 +2088,9 @@ namespace Terminal.Gui {
 
 			var aSize = autoSize;
 			Rect nBounds = TextFormatter.CalcRect (Bounds.X, Bounds.Y, Text, textFormatter.Direction);
-
+			if (textFormatter.Size != nBounds.Size) {
+				textFormatter.Size = nBounds.Size;
+			}
 			if ((textFormatter.Size != Bounds.Size || textFormatter.Size != nBounds.Size)
 				&& (((width == null || width is Dim.DimAbsolute) && (Bounds.Width == 0
 				|| autoSize && Bounds.Width != nBounds.Width))

+ 0 - 71
Terminal.Gui/Core/Window.cs

@@ -209,77 +209,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		//
-		// 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
-				|| mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) {
-				// 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) {
-						Application.Top.SetNeedsDisplay (Frame);
-						// 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).
-						Application.Top.Redraw (Frame);
-					} else {
-						SuperView.SetNeedsDisplay (Frame);
-					}
-					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}");
-
-					// FIXED: optimize, only SetNeedsDisplay on the before/after regions.
-					SetNeedsDisplay ();
-					return true;
-				}
-			}
-
-			if (mouseEvent.Flags == MouseFlags.Button1Released && dragPosition.HasValue) {
-				Application.UngrabMouse ();
-				Driver.UncookMouse ();
-				dragPosition = null;
-			}
-
-			//System.Diagnostics.Debug.WriteLine (mouseEvent.ToString ());
-			return false;
-		}
-
 		/// <summary>
 		///   The text displayed by the <see cref="Label"/>.
 		/// </summary>

+ 49 - 11
Terminal.Gui/Views/Menu.cs

@@ -166,7 +166,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets if this <see cref="MenuItem"/> is from a sub-menu.
 		/// </summary>
-		internal bool IsFromSubMenu { get {return Parent != null; } }
+		internal bool IsFromSubMenu { get { return Parent != null; } }
 
 		/// <summary>
 		/// Merely a debugging aid to see the interaction with main
@@ -274,7 +274,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Initializes a new <see cref="MenuBarItem"/>.
 		/// </summary>
-		public MenuBarItem () : this (children: new MenuItem [] { }) {  }
+		public MenuBarItem () : this (children: new MenuItem [] { }) { }
 
 		//static int GetMaxTitleLength (MenuItem [] children)
 		//{
@@ -447,7 +447,7 @@ namespace Terminal.Gui {
 				for (int p = 0; p < Frame.Width - 2; p++)
 					if (item == null)
 						Driver.AddRune (Driver.HLine);
-					else if (p == Frame.Width - 3 && barItems.SubMenu(barItems.Children [i]) != null)
+					else if (p == Frame.Width - 3 && barItems.SubMenu (barItems.Children [i]) != null)
 						Driver.AddRune (Driver.RightArrow);
 					else
 						Driver.AddRune (' ');
@@ -916,7 +916,7 @@ namespace Terminal.Gui {
 				var menu = Menus [i];
 				Move (pos, 0);
 				Attribute hotColor, normalColor;
-				if (i == selected) {
+				if (i == selected && IsMenuOpen) {
 					hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
 					normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
 				} else if (openedByAltKey) {
@@ -966,7 +966,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Raised as a menu is opening.
 		/// </summary>
-		public event Action MenuOpening;
+		public event Action<MenuOpeningEventArgs> MenuOpening;
 
 		/// <summary>
 		/// Raised when a menu is closing.
@@ -986,11 +986,15 @@ namespace Terminal.Gui {
 		public bool IsMenuOpen { get; protected set; }
 
 		/// <summary>
-		/// Virtual method that will invoke the <see cref="MenuOpening"/>
+		/// Virtual method that will invoke the <see cref="MenuOpening"/> event if it's defined.
 		/// </summary>
-		public virtual void OnMenuOpening ()
+		/// <param name="currentMenu">The current menu to be replaced.</param>
+		/// /// <returns>Returns the <see cref="MenuOpeningEventArgs"/></returns>
+		public virtual MenuOpeningEventArgs OnMenuOpening (MenuBarItem currentMenu)
 		{
-			MenuOpening?.Invoke ();
+			var ev = new MenuOpeningEventArgs (currentMenu);
+			MenuOpening?.Invoke (ev);
+			return ev;
 		}
 
 		/// <summary>
@@ -1011,11 +1015,17 @@ namespace Terminal.Gui {
 		internal void OpenMenu (int index, int sIndex = -1, MenuBarItem subMenu = null)
 		{
 			isMenuOpening = true;
-			OnMenuOpening ();
+			var newMenu = OnMenuOpening (Menus [index]);
+			if (newMenu.Cancel) {
+				return;
+			}
+			if (newMenu.NewMenuBarItem != null && Menus [index].Title == newMenu.NewMenuBarItem.Title) {
+				Menus [index] = newMenu.NewMenuBarItem;
+			}
 			int pos = 0;
 			switch (subMenu) {
 			case null:
-				lastFocused = lastFocused ?? SuperView.MostFocused;
+				lastFocused = lastFocused ?? SuperView?.MostFocused;
 				if (openSubMenu != null)
 					CloseMenu (false, true);
 				if (openMenu != null) {
@@ -1460,7 +1470,7 @@ namespace Terminal.Gui {
 			case Key.CursorDown:
 			case Key.Enter:
 				if (selected > -1) {
-					ProcessMenu (selected, Menus [selected]); 
+					ProcessMenu (selected, Menus [selected]);
 				}
 				break;
 
@@ -1638,4 +1648,32 @@ namespace Terminal.Gui {
 			return base.OnEnter (view);
 		}
 	}
+
+	/// <summary>
+	/// An <see cref="EventArgs"/> which allows passing a cancelable menu opening event or replacing with a new <see cref="MenuBarItem"/>.
+	/// </summary>
+	public class MenuOpeningEventArgs : EventArgs {
+		/// <summary>
+		/// The current <see cref="MenuBarItem"/> parent.
+		/// </summary>
+		public MenuBarItem CurrentMenu { get; }
+
+		/// <summary>
+		/// The new <see cref="MenuBarItem"/> to be replaced.
+		/// </summary>
+		public MenuBarItem NewMenuBarItem { get; set; }
+		/// <summary>
+		/// Flag that allows you to cancel the opening of the menu.
+		/// </summary>
+		public bool Cancel { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of <see cref="MenuOpeningEventArgs"/>
+		/// </summary>
+		/// <param name="currentMenu">The current <see cref="MenuBarItem"/> parent.</param>
+		public MenuOpeningEventArgs (MenuBarItem currentMenu)
+		{
+			CurrentMenu = currentMenu;
+		}
+	}
 }

+ 7 - 3
Terminal.Gui/Views/ScrollBarView.cs

@@ -298,11 +298,15 @@ namespace Terminal.Gui {
 			}
 
 			var pending = CheckBothScrollBars (this);
-			CheckBothScrollBars (otherScrollBarView, pending);
+			if (otherScrollBarView != null) {
+				CheckBothScrollBars (otherScrollBarView, pending);
+			}
 
 			SetWidthHeight ();
 			SetRelativeLayout (Bounds);
-			OtherScrollBarView.SetRelativeLayout (OtherScrollBarView.Bounds);
+			if (otherScrollBarView != null) {
+				OtherScrollBarView.SetRelativeLayout (OtherScrollBarView.Bounds);
+			}
 
 			if (showBothScrollIndicator) {
 				if (contentBottomRightCorner != null) {
@@ -321,7 +325,7 @@ namespace Terminal.Gui {
 			if (showScrollIndicator) {
 				Redraw (Bounds);
 			}
-			if (otherScrollBarView.showScrollIndicator) {
+			if (otherScrollBarView != null && otherScrollBarView.showScrollIndicator) {
 				otherScrollBarView.Redraw (otherScrollBarView.Bounds);
 			}
 		}

+ 2 - 1
Terminal.Gui/Views/TabView.cs

@@ -179,6 +179,7 @@ namespace Terminal.Gui {
 
 			if (Tabs.Any ()) {
 				tabsBar.Redraw (tabsBar.Bounds);
+				contentView.SetNeedsDisplay();
 				contentView.Redraw (contentView.Bounds);
 			}
 		}
@@ -840,4 +841,4 @@ namespace Terminal.Gui {
 		}
 		#endregion
 	}
-}
+}

+ 13 - 2
UICatalog/Scenarios/AllViewsTester.cs

@@ -228,6 +228,8 @@ namespace UICatalog {
 
 			Top.Add (_leftPane, _settingsPane, _hostPane);
 
+			Top.LayoutSubviews ();
+
 			_curView = CreateClass (_viewClasses.First ().Value);
 		}
 
@@ -236,7 +238,12 @@ namespace UICatalog {
 			if (view == null) {
 				return;
 			}
+
+			var layout = view.LayoutStyle;
+
 			try {
+				view.LayoutStyle = LayoutStyle.Absolute;
+
 				switch (_xRadioGroup.SelectedItem) {
 				case 0:
 					view.X = Pos.Percent (_xVal);
@@ -292,6 +299,8 @@ namespace UICatalog {
 				}
 			} catch (Exception e) {
 				MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+			} finally {
+				view.LayoutStyle = layout;
 			}
 			UpdateTitle (view);
 		}
@@ -366,8 +375,10 @@ namespace UICatalog {
 			view.Width = Dim.Percent(75);
 			view.Height = Dim.Percent (75);
 
-			// Set the colorscheme to make it stand out
-			view.ColorScheme = Colors.Base;
+			// Set the colorscheme to make it stand out if is null by default
+			if (view.ColorScheme == null) {
+				view.ColorScheme = Colors.Base;
+			}
 
 			// If the view supports a Text property, set it so we have something to look at
 			if (view.GetType ().GetProperty ("Text") != null) {

+ 393 - 0
UICatalog/Scenarios/BackgroundWorkerCollection.cs

@@ -0,0 +1,393 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading;
+using System.Threading.Tasks;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "BackgroundWorker Collection", Description: "A persisting multi Toplevel BackgroundWorker threading")]
+	[ScenarioCategory ("Threading")]
+	[ScenarioCategory ("TopLevel")]
+	[ScenarioCategory ("Dialogs")]
+	[ScenarioCategory ("Controls")]
+	class BackgroundWorkerCollection : Scenario {
+		public override void Init (Toplevel top, ColorScheme colorScheme)
+		{
+			Application.Top.Dispose ();
+
+			Application.Run<MdiMain> ();
+
+			Application.Top.Dispose ();
+		}
+
+		public override void Run ()
+		{
+		}
+
+		class MdiMain : Toplevel {
+			private WorkerApp workerApp;
+			private bool canOpenWorkerApp;
+			MenuBar menu;
+
+			public MdiMain ()
+			{
+				Data = "MdiMain";
+
+				IsMdiContainer = true;
+
+				workerApp = new WorkerApp () { Visible = false };
+
+				menu = new MenuBar (new MenuBarItem [] {
+					new MenuBarItem ("_Options", new MenuItem [] {
+						new MenuItem ("_Run Worker", "", () => workerApp.RunWorker(), null, null, Key.CtrlMask | Key.R),
+						new MenuItem ("_Cancel Worker", "", () => workerApp.CancelWorker(), null, null, Key.CtrlMask | Key.C),
+						null,
+						new MenuItem ("_Quit", "", () => Quit(), null, null, Key.CtrlMask | Key.Q)
+					}),
+					new MenuBarItem ("_View", new MenuItem [] { }),
+					new MenuBarItem ("_Window", new MenuItem [] { })
+				});
+				menu.MenuOpening += Menu_MenuOpening;
+				Add (menu);
+
+				var statusBar = new StatusBar (new [] {
+					new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Quit()),
+					new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Worker", () => workerApp.RunWorker()),
+					new StatusItem(Key.CtrlMask | Key.C, "~^C~ Cancel Worker", () => workerApp.CancelWorker())
+				});
+				Add (statusBar);
+
+				Activate += MdiMain_Activate;
+				Deactivate += MdiMain_Deactivate;
+
+				Closed += MdiMain_Closed;
+
+				Application.Iteration += () => {
+					if (canOpenWorkerApp && !workerApp.Running && Application.MdiTop.Running) {
+						Application.Run (workerApp);
+					}
+				};
+			}
+
+			private void MdiMain_Closed (Toplevel obj)
+			{
+				workerApp.Dispose ();
+				Dispose ();
+			}
+
+			private void Menu_MenuOpening (MenuOpeningEventArgs menu)
+			{
+				if (!canOpenWorkerApp) {
+					canOpenWorkerApp = true;
+					return;
+				}
+				if (menu.CurrentMenu.Title == "_Window") {
+					menu.NewMenuBarItem = OpenedWindows ();
+				} else if (menu.CurrentMenu.Title == "_View") {
+					menu.NewMenuBarItem = View ();
+				}
+			}
+
+			private void MdiMain_Deactivate (Toplevel top)
+			{
+				workerApp.WriteLog ($"{top.Data} deactivate.");
+			}
+
+			private void MdiMain_Activate (Toplevel top)
+			{
+				workerApp.WriteLog ($"{top.Data} activate.");
+			}
+
+			private MenuBarItem View ()
+			{
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				var item = new MenuItem () {
+					Title = "WorkerApp",
+					CheckType = MenuItemCheckStyle.Checked
+				};
+				var top = Application.MdiChildes?.Find ((x) => x.Data.ToString () == "WorkerApp");
+				if (top != null) {
+					item.Checked = top.Visible;
+				}
+				item.Action += () => {
+					var top = Application.MdiChildes.Find ((x) => x.Data.ToString () == "WorkerApp");
+					item.Checked = top.Visible = !item.Checked;
+					if (top.Visible) {
+						top.ShowChild ();
+					} else {
+						Application.MdiTop.SetNeedsDisplay ();
+					}
+				};
+				menuItems.Add (item);
+				return new MenuBarItem ("_View",
+					new List<MenuItem []> () { menuItems.Count == 0 ? new MenuItem [] { } : menuItems.ToArray () });
+			}
+
+			private MenuBarItem OpenedWindows ()
+			{
+				var index = 1;
+				List<MenuItem> menuItems = new List<MenuItem> ();
+				var sortedChildes = Application.MdiChildes;
+				sortedChildes.Sort (new ToplevelComparer ());
+				foreach (var top in sortedChildes) {
+					if (top.Data.ToString () == "WorkerApp" && !top.Visible) {
+						continue;
+					}
+					var item = new MenuItem ();
+					item.Title = top is Window ? $"{index} {((Window)top).Title}" : $"{index} {top.Data}";
+					index++;
+					item.CheckType |= MenuItemCheckStyle.Checked;
+					var topTitle = top is Window ? ((Window)top).Title : top.Data.ToString ();
+					var itemTitle = item.Title.Substring (index.ToString ().Length + 1);
+					if (top == top.GetTopMdiChild () && topTitle == itemTitle) {
+						item.Checked = true;
+					} else {
+						item.Checked = false;
+					}
+					item.Action += () => {
+						top.ShowChild ();
+					};
+					menuItems.Add (item);
+				}
+				if (menuItems.Count == 0) {
+					return new MenuBarItem ("_Window", "", null);
+				} else {
+					return new MenuBarItem ("_Window", new List<MenuItem []> () { menuItems.ToArray () });
+				}
+			}
+
+			private void Quit ()
+			{
+				RequestStop ();
+			}
+		}
+
+		class WorkerApp : Toplevel {
+			private List<string> log = new List<string> ();
+			private ListView listLog;
+			private Dictionary<Staging, BackgroundWorker> stagingWorkers;
+			private List<StagingUIController> stagingsUI;
+
+			public WorkerApp ()
+			{
+				Data = "WorkerApp";
+
+				Width = Dim.Percent (80);
+				Height = Dim.Percent (50);
+
+				ColorScheme = Colors.Base;
+
+				var label = new Label ("Worker collection Log") {
+					X = Pos.Center (),
+					Y = 0
+				};
+				Add (label);
+
+				listLog = new ListView (log) {
+					X = 0,
+					Y = Pos.Bottom (label),
+					Width = Dim.Fill (),
+					Height = Dim.Fill ()
+				};
+				Add (listLog);
+			}
+
+			public void RunWorker ()
+			{
+				var stagingUI = new StagingUIController () { Modal = true };
+
+				Staging staging = null;
+				var worker = new BackgroundWorker () { WorkerSupportsCancellation = true };
+
+				worker.DoWork += (s, e) => {
+					var stageResult = new List<string> ();
+					for (int i = 0; i < 500; i++) {
+						stageResult.Add (
+							$"Worker {i} started at {DateTime.Now}");
+						e.Result = stageResult;
+						Thread.Sleep (1);
+						if (worker.CancellationPending) {
+							e.Cancel = true;
+							return;
+						}
+					}
+				};
+
+				worker.RunWorkerCompleted += (s, e) => {
+					if (e.Error != null) {
+						// Failed
+						WriteLog ($"Exception occurred {e.Error.Message} on Worker {staging.StartStaging}.{staging.StartStaging:fff} at {DateTime.Now}");
+					} else if (e.Cancelled) {
+						// Canceled
+						WriteLog ($"Worker {staging.StartStaging}.{staging.StartStaging:fff} was canceled at {DateTime.Now}!");
+					} else {
+						// Passed
+						WriteLog ($"Worker {staging.StartStaging}.{staging.StartStaging:fff} was completed at {DateTime.Now}.");
+						Application.Refresh ();
+
+						var stagingUI = new StagingUIController (staging, e.Result as List<string>) {
+							Modal = false,
+							Title = $"Worker started at {staging.StartStaging}.{staging.StartStaging:fff}",
+							Data = $"{staging.StartStaging}.{staging.StartStaging:fff}"
+						};
+
+						stagingUI.ReportClosed += StagingUI_ReportClosed;
+
+						if (stagingsUI == null) {
+							stagingsUI = new List<StagingUIController> ();
+						}
+						stagingsUI.Add (stagingUI);
+						stagingWorkers.Remove (staging);
+
+						stagingUI.Run ();
+					}
+				};
+
+				Application.Run (stagingUI);
+
+				if (stagingUI.Staging != null && stagingUI.Staging.StartStaging != null) {
+					staging = new Staging (stagingUI.Staging.StartStaging);
+					WriteLog ($"Worker is started at {staging.StartStaging}.{staging.StartStaging:fff}");
+					if (stagingWorkers == null) {
+						stagingWorkers = new Dictionary<Staging, BackgroundWorker> ();
+					}
+					stagingWorkers.Add (staging, worker);
+					worker.RunWorkerAsync ();
+					stagingUI.Dispose ();
+				}
+			}
+
+			private void StagingUI_ReportClosed (StagingUIController obj)
+			{
+				WriteLog ($"Report {obj.Staging.StartStaging}.{obj.Staging.StartStaging:fff} closed.");
+				stagingsUI.Remove (obj);
+			}
+
+			public void CancelWorker ()
+			{
+				if (stagingWorkers == null || stagingWorkers.Count == 0) {
+					WriteLog ($"Worker is not running at {DateTime.Now}!");
+					return;
+				}
+
+				foreach (var sw in stagingWorkers) {
+					var key = sw.Key;
+					var value = sw.Value;
+					if (!key.Completed) {
+						value.CancelAsync ();
+					}
+					WriteLog ($"Worker {key.StartStaging}.{key.StartStaging:fff} is canceling at {DateTime.Now}!");
+
+					stagingWorkers.Remove (sw.Key);
+				}
+			}
+
+			public void WriteLog (string msg)
+			{
+				log.Add (msg);
+				listLog.MoveEnd ();
+			}
+		}
+
+		class StagingUIController : Window {
+			private Label label;
+			private ListView listView;
+			private Button start;
+			private Button close;
+			public Staging Staging { get; private set; }
+
+			public event Action<StagingUIController> ReportClosed;
+
+			public StagingUIController (Staging staging, List<string> list) : this ()
+			{
+				Staging = staging;
+				label.Text = "Work list:";
+				listView.SetSource (list);
+				start.Visible = false;
+				Id = "";
+			}
+
+			public StagingUIController ()
+			{
+				X = Pos.Center ();
+				Y = Pos.Center ();
+				Width = Dim.Percent (85);
+				Height = Dim.Percent (85);
+
+				ColorScheme = Colors.Dialog;
+
+				Title = "Run Worker";
+
+				label = new Label ("Press start to do the work or close to exit.") {
+					X = Pos.Center (),
+					Y = 1,
+					ColorScheme = Colors.Dialog
+				};
+				Add (label);
+
+				listView = new ListView () {
+					X = 0,
+					Y = 2,
+					Width = Dim.Fill (),
+					Height = Dim.Fill (2)
+				};
+				Add (listView);
+
+				start = new Button ("Start") { IsDefault = true };
+				start.Clicked += () => {
+					Staging = new Staging (DateTime.Now);
+					RequestStop ();
+				};
+				Add (start);
+
+				close = new Button ("Close");
+				close.Clicked += OnReportClosed;
+				Add (close);
+
+				KeyPress += (e) => {
+					if (e.KeyEvent.Key == Key.Esc) {
+						OnReportClosed ();
+					}
+				};
+
+				LayoutStarted += (_) => {
+					var btnsWidth = start.Bounds.Width + close.Bounds.Width + 2 - 1;
+					var shiftLeft = Math.Max ((Bounds.Width - btnsWidth) / 2 - 2, 0);
+
+					shiftLeft += close.Bounds.Width + 1;
+					close.X = Pos.AnchorEnd (shiftLeft);
+					close.Y = Pos.AnchorEnd (1);
+
+					shiftLeft += start.Bounds.Width + 1;
+					start.X = Pos.AnchorEnd (shiftLeft);
+					start.Y = Pos.AnchorEnd (1);
+				};
+			}
+
+			private void OnReportClosed ()
+			{
+				if (Staging.StartStaging != null) {
+					ReportClosed?.Invoke (this);
+				}
+				RequestStop ();
+			}
+
+			public void Run ()
+			{
+				Application.Run (this);
+			}
+		}
+
+		class Staging {
+			public DateTime? StartStaging { get; private set; }
+			public bool Completed { get; }
+
+			public Staging (DateTime? startStaging, bool completed = false)
+			{
+				StartStaging = startStaging;
+				Completed = completed;
+			}
+		}
+	}
+}

+ 7 - 2
UICatalog/Scenarios/Buttons.cs

@@ -103,8 +103,13 @@ namespace UICatalog {
 				ColorScheme = Colors.Error
 			};
 			Win.Add (removeButton);
-			// This in intresting test case because `moveBtn` and below are laid out relative to this one!
-			removeButton.Clicked += () => Win.Remove (removeButton);
+			// This in interesting test case because `moveBtn` and below are laid out relative to this one!
+			removeButton.Clicked += () => {
+				// Now this throw a InvalidOperationException on the TopologicalSort method as is expected.
+				//Win.Remove (removeButton);
+
+				removeButton.Visible = false;
+			};
 
 			var computedFrame = new FrameView ("Computed Layout") {
 				X = 0,

+ 187 - 0
UICatalog/Scenarios/SingleBackgroundWorker.cs

@@ -0,0 +1,187 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Threading;
+using Terminal.Gui;
+
+namespace UICatalog {
+	[ScenarioMetadata (Name: "Single BackgroundWorker", Description: "A single BackgroundWorker threading opening another Toplevel")]
+	[ScenarioCategory ("Threading")]
+	[ScenarioCategory ("TopLevel")]
+	[ScenarioCategory ("Dialogs")]
+	[ScenarioCategory ("Controls")]
+	class SingleBackgroundWorker : Scenario {
+		public override void Run ()
+		{
+			Top.Dispose ();
+
+			Application.Run<MainApp> ();
+
+			Top.Dispose ();
+		}
+
+		public class MainApp : Toplevel {
+			private BackgroundWorker worker;
+			private List<string> log = new List<string> ();
+			private DateTime? startStaging;
+			private ListView listLog;
+
+			public MainApp ()
+			{
+				var menu = new MenuBar (new MenuBarItem [] {
+					new MenuBarItem ("_Options", new MenuItem [] {
+						new MenuItem ("_Run Worker", "", () => RunWorker(), null, null, Key.CtrlMask | Key.R),
+						null,
+						new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.CtrlMask | Key.Q)
+					})
+				});
+				Add (menu);
+
+				var statusBar = new StatusBar (new [] {
+					new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Exit", () => Application.RequestStop()),
+					new StatusItem(Key.CtrlMask | Key.P, "~^R~ Run Worker", () => RunWorker())
+				});
+				Add (statusBar);
+
+				var top = new Toplevel ();
+
+				top.Add (new Label ("Worker Log") {
+					X = Pos.Center (),
+					Y = 0
+				});
+
+				listLog = new ListView (log) {
+					X = 0,
+					Y = 2,
+					Width = Dim.Fill (),
+					Height = Dim.Fill ()
+				};
+				top.Add (listLog);
+				Add (top);
+			}
+
+			public void Load ()
+			{
+				Application.Run (this);
+			}
+
+			private void RunWorker ()
+			{
+				worker = new BackgroundWorker () { WorkerSupportsCancellation = true };
+
+				var cancel = new Button ("Cancel Worker");
+				cancel.Clicked += () => {
+					if (worker == null) {
+						log.Add ($"Worker is not running at {DateTime.Now}!");
+						listLog.SetNeedsDisplay ();
+						return;
+					}
+
+					log.Add ($"Worker {startStaging}.{startStaging:fff} is canceling at {DateTime.Now}!");
+					listLog.SetNeedsDisplay ();
+					worker.CancelAsync ();
+				};
+
+				startStaging = DateTime.Now;
+				log.Add ($"Worker is started at {startStaging}.{startStaging:fff}");
+				listLog.SetNeedsDisplay ();
+
+				var md = new Dialog ($"Running Worker started at {startStaging}.{startStaging:fff}", cancel);
+
+				worker.DoWork += (s, e) => {
+					var stageResult = new List<string> ();
+					for (int i = 0; i < 500; i++) {
+						stageResult.Add ($"Worker {i} started at {DateTime.Now}");
+						e.Result = stageResult;
+						Thread.Sleep (1);
+						if (worker.CancellationPending) {
+							e.Cancel = true;
+							return;
+						}
+					}
+				};
+
+				worker.RunWorkerCompleted += (s, e) => {
+					if (md.IsCurrentTop) {
+						//Close the dialog
+						Application.RequestStop ();
+					}
+
+					if (e.Error != null) {
+						// Failed
+						log.Add ($"Exception occurred {e.Error.Message} on Worker {startStaging}.{startStaging:fff} at {DateTime.Now}");
+						listLog.SetNeedsDisplay ();
+					} else if (e.Cancelled) {
+						// Canceled
+						log.Add ($"Worker {startStaging}.{startStaging:fff} was canceled at {DateTime.Now}!");
+						listLog.SetNeedsDisplay ();
+					} else {
+						// Passed
+						log.Add ($"Worker {startStaging}.{startStaging:fff} was completed at {DateTime.Now}.");
+						listLog.SetNeedsDisplay ();
+						Application.Refresh ();
+						var builderUI = new StagingUIController (startStaging, e.Result as List<string>);
+						builderUI.Load ();
+					}
+					worker = null;
+				};
+				worker.RunWorkerAsync ();
+				Application.Run (md);
+			}
+		}
+
+		public class StagingUIController : Window {
+			Toplevel top;
+
+			public StagingUIController (DateTime? start, List<string> list)
+			{
+				top = new Toplevel (Application.Top.Frame);
+				top.KeyPress += (e) => {
+					// Prevents Ctrl+Q from closing this.
+					// Only Ctrl+C is allowed.
+					if (e.KeyEvent.Key == (Key.Q | Key.CtrlMask)) {
+						e.Handled = true;
+					}
+				};
+
+				bool Close ()
+				{
+					var n = MessageBox.Query (50, 7, "Close Window.", "Are you sure you want to close this window?", "Yes", "No");
+					return n == 0;
+				}
+
+				var menu = new MenuBar (new MenuBarItem [] {
+					new MenuBarItem ("_Stage", new MenuItem [] {
+						new MenuItem ("_Close", "", () => { if (Close()) { Application.RequestStop(); } }, null, null, Key.CtrlMask | Key.C)
+					})
+				});
+				top.Add (menu);
+
+				var statusBar = new StatusBar (new [] {
+					new StatusItem(Key.CtrlMask | Key.C, "~^C~ Close", () => { if (Close()) { Application.RequestStop(); } }),
+				});
+				top.Add (statusBar);
+
+				Title = $"Worker started at {start}.{start:fff}";
+				Y = 1;
+				Height = Dim.Fill (1);
+
+				ColorScheme = Colors.Base;
+
+				Add (new ListView (list) {
+					X = 0,
+					Y = 0,
+					Width = Dim.Fill (),
+					Height = Dim.Fill ()
+				});
+
+				top.Add (this);
+			}
+
+			public void Load ()
+			{
+				Application.Run (top);
+			}
+		}
+	}
+}

+ 1 - 1
UICatalog/Scenarios/Threading.cs

@@ -89,7 +89,7 @@ namespace UICatalog {
 			var _btnClearData = new Button (80, 20, "Clear Data");
 			_btnClearData.Clicked += () => { _itemsList.Source = null; LogJob ("Cleaning Data"); };
 			var _btnQuit = new Button (80, 22, "Quit");
-			_btnQuit.Clicked += Application.RequestStop;
+			_btnQuit.Clicked += () => Application.RequestStop ();
 
 			Win.Add (_itemsList, _btnActionCancel, _logJob, text, _btnAction, _btnLambda, _btnHandler, _btnSync, _btnMethod, _btnClearData, _btnQuit);
 

+ 19 - 6
UICatalog/UICatalog.cs

@@ -65,6 +65,7 @@ namespace UICatalog {
 		private static ConsoleDriver.DiagnosticFlags _diagnosticFlags;
 		private static bool _heightAsBuffer = false;
 		private static bool _alwaysSetPosition;
+		private static bool _isFirstRunning = true;
 
 		static void Main (string [] args)
 		{
@@ -108,13 +109,15 @@ namespace UICatalog {
 				scenario.Setup ();
 				scenario.Run ();
 
-				static void LoadedHandler ()
-				{
-					_rightPane.SetFocus ();
-					_top.Loaded -= LoadedHandler;
-				}
+				//static void LoadedHandler ()
+				//{
+				//	_rightPane.SetFocus ();
+				//	_top.Loaded -= LoadedHandler;
+				//}
 
-				_top.Loaded += LoadedHandler;
+				//_top.Loaded += LoadedHandler;
+
+				Application.Shutdown ();
 
 #if DEBUG_IDISPOSABLE
 				// After the scenario runs, validate all Responder-based instances
@@ -269,11 +272,21 @@ namespace UICatalog {
 			_top.Add (_leftPane);
 			_top.Add (_rightPane);
 			_top.Add (_statusBar);
+
 			_top.Loaded += () => {
 				if (_runningScenario != null) {
 					_runningScenario = null;
+					_isFirstRunning = false;
 				}
 			};
+			void ReadyHandler ()
+			{
+				if (!_isFirstRunning) {
+					_rightPane.SetFocus ();
+				}
+				_top.Ready -= ReadyHandler;
+			}
+			_top.Ready += ReadyHandler;
 
 			Application.Run (_top);
 			return _runningScenario;

+ 755 - 0
UnitTests/ApplicationTests.cs

@@ -372,5 +372,760 @@ namespace Terminal.Gui.Core {
 			// Shutdown must be called to safely clean up Application if Init has been called
 			Application.Shutdown ();
 		}
+
+		[Fact]
+		public void Application_RequestStop_With_Params_On_A_Not_MdiContainer_Always_Use_The_Application_Current ()
+		{
+			Init ();
+
+			var top1 = new Toplevel ();
+			var top2 = new Toplevel ();
+			var top3 = new Window ();
+			var top4 = new Window ();
+			var d = new Dialog ();
+
+			// top1, top2, top3, d1 = 4
+			var iterations = 4;
+
+			top1.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top2);
+			};
+			top2.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top3);
+			};
+			top3.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (top4);
+			};
+			top4.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				Application.Run (d);
+			};
+
+			d.Ready += () => {
+				Assert.Null (Application.MdiChildes);
+				// This will close the d because on a not MdiContainer the Application.Current it always used.
+				Application.RequestStop (top1);
+				Assert.True (Application.Current == d);
+			};
+
+			d.Closed += (e) => Application.RequestStop (top1);
+
+			Application.Iteration += () => {
+				Assert.Null (Application.MdiChildes);
+				if (iterations == 4) {
+					Assert.True (Application.Current == d);
+				} else if (iterations == 3) {
+					Assert.True (Application.Current == top4);
+				} else if (iterations == 2) {
+					Assert.True (Application.Current == top3);
+				} else if (iterations == 1) {
+					Assert.True (Application.Current == top2);
+				} else {
+					Assert.True (Application.Current == top1);
+				}
+				Application.RequestStop (top1);
+				iterations--;
+			};
+
+			Application.Run (top1);
+
+			Assert.Null (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		class Mdi : Toplevel {
+			public Mdi ()
+			{
+				IsMdiContainer = true;
+			}
+		}
+
+		[Fact]
+		public void MdiContainer_With_Toplevel_RequestStop_Balanced ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			// More easy because the Mdi Container handles all at once
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// This will not close the MdiContainer because d is a modal toplevel and will be closed.
+				mdi.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog was not closed before and will be closed now.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void MdiContainer_With_Application_RequestStop_MdiTop_With_Params ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			// Also easy because the Mdi Container handles all at once
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// This will not close the MdiContainer because d is a modal toplevel
+				Application.RequestStop (mdi);
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => Application.RequestStop (mdi);
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog was not closed before and will be closed now.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void MdiContainer_With_Application_RequestStop_MdiTop_Without_Params ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			// MdiChild = c1, c2, c3 = 3
+			// d1 = 1
+			var iterations = 4;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d);
+			};
+
+			//More harder because it's sequential.
+			d.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				// Close the Dialog
+				Application.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d.Closed += (e) => Application.RequestStop (mdi);
+
+			Application.Iteration += () => {
+				if (iterations == 4) {
+					// The Dialog still is the current top and we can't request stop to MdiContainer
+					// because we are not using parameter calls.
+					Assert.True (Application.Current == d);
+					Assert.False (d.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void IsMdiChild_Testing ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d = new Dialog ();
+
+			Application.Iteration += () => {
+				Assert.False (mdi.IsMdiChild);
+				Assert.True (c1.IsMdiChild);
+				Assert.True (c2.IsMdiChild);
+				Assert.True (c3.IsMdiChild);
+				Assert.False (d.IsMdiChild);
+
+				mdi.RequestStop ();
+			};
+
+			Application.Run (mdi);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void Modal_Toplevel_Can_Open_Another_Modal_Toplevel_But_RequestStop_To_The_Caller_Also_Sets_Current_Running_To_False_Too ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d1 = new Dialog ();
+			var d2 = new Dialog ();
+
+			// MdiChild = c1, c2, c3 = 3
+			// d1, d2 = 2
+			var iterations = 5;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d1);
+			};
+			d1.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d2);
+			};
+
+			d2.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Assert.True (Application.Current == d2);
+				Assert.True (Application.Current.Running);
+				// Trying to close the Dialog1
+				d1.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d1.Closed += (e) => {
+				Assert.True (Application.Current == d1);
+				Assert.False (Application.Current.Running);
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 5) {
+					// The Dialog2 still is the current top and we can't request stop to MdiContainer
+					// because Dialog2 and Dialog1 must be closed first.
+					// Dialog2 will be closed in this iteration.
+					Assert.True (Application.Current == d2);
+					Assert.False (Application.Current.Running);
+					Assert.False (d1.Running);
+				} else if (iterations == 4) {
+					// Dialog1 will be closed in this iteration.
+					Assert.True (Application.Current == d1);
+					Assert.False (Application.Current.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + 1).ToString (), Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void Modal_Toplevel_Can_Open_Another_Not_Modal_Toplevel_But_RequestStop_To_The_Caller_Also_Sets_Current_Running_To_False_Too ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+			var d1 = new Dialog ();
+			var c4 = new Toplevel ();
+
+			// MdiChild = c1, c2, c3, c4 = 4
+			// d1 = 1
+			var iterations = 5;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (d1);
+			};
+			d1.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				Application.Run (c4);
+			};
+
+			c4.Ready += () => {
+				Assert.Equal (4, Application.MdiChildes.Count);
+				// Trying to close the Dialog1
+				d1.RequestStop ();
+			};
+
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			d1.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+
+			Application.Iteration += () => {
+				if (iterations == 5) {
+					// The Dialog2 still is the current top and we can't request stop to MdiContainer
+					// because Dialog2 and Dialog1 must be closed first.
+					// Using request stop here will call the Dialog again without need
+					Assert.True (Application.Current == d1);
+					Assert.False (Application.Current.Running);
+					Assert.True (c4.Running);
+				} else {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					for (int i = 0; i < iterations; i++) {
+						Assert.Equal ((iterations - i + (iterations == 4 && i == 0 ? 2 : 1)).ToString (),
+							Application.MdiChildes [i].Id);
+					}
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void MoveCurrent_Returns_False_If_The_Current_And_Top_Parameter_Are_Both_With_Running_Set_To_False ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+
+			// MdiChild = c1, c2, c3
+			var iterations = 3;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				c3.RequestStop ();
+				c1.RequestStop ();
+			};
+			// Now this will close the MdiContainer propagating through the MdiChildes.
+			c1.Closed += (e) => {
+				mdi.RequestStop ();
+			};
+			Application.Iteration += () => {
+				if (iterations == 3) {
+					// The Current still is c3 because Current.Running is false.
+					Assert.True (Application.Current == c3);
+					Assert.False (Application.Current.Running);
+					// But the childes order were reorder by Running = false
+					Assert.True (Application.MdiChildes [0] == c3);
+					Assert.True (Application.MdiChildes [1] == c1);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else if (iterations == 2) {
+					// The Current is c1 and Current.Running is false.
+					Assert.True (Application.Current == c1);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [0] == c1);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else if (iterations == 1) {
+					// The Current is c2 and Current.Running is false.
+					Assert.True (Application.Current == c2);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [^1] == c2);
+				} else {
+					// The Current is mdi.
+					Assert.True (Application.Current == mdi);
+					Assert.Empty (Application.MdiChildes);
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void MdiContainer_Throws_If_More_Than_One ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var mdi2 = new Mdi ();
+
+			mdi.Ready += () => {
+				Assert.Throws<InvalidOperationException> (() => Application.Run (mdi2));
+				mdi.RequestStop ();
+			};
+
+			Application.Run (mdi);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void MdiContainer_Open_And_Close_Modal_And_Open_Not_Modal_Toplevels_Randomly ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var logger = new Toplevel ();
+
+			var iterations = 1; // The logger
+			var running = true;
+			var stageCompleted = true;
+			var allStageClosed = false;
+			var mdiRequestStop = false;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (logger);
+			};
+
+			logger.Ready += () => Assert.Single (Application.MdiChildes);
+
+			Application.Iteration += () => {
+				if (stageCompleted && running) {
+					stageCompleted = false;
+					var stage = new Window () { Modal = true };
+
+					stage.Ready += () => {
+						Assert.Equal (iterations, Application.MdiChildes.Count);
+						stage.RequestStop ();
+					};
+
+					stage.Closed += (_) => {
+						if (iterations == 11) {
+							allStageClosed = true;
+						}
+						Assert.Equal (iterations, Application.MdiChildes.Count);
+						if (running) {
+							stageCompleted = true;
+
+							var rpt = new Window ();
+
+							rpt.Ready += () => {
+								iterations++;
+								Assert.Equal (iterations, Application.MdiChildes.Count);
+							};
+
+							Application.Run (rpt);
+						}
+					};
+
+					Application.Run (stage);
+
+				} else if (iterations == 11 && running) {
+					running = false;
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+
+				} else if (!mdiRequestStop && running && !allStageClosed) {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+
+				} else if (!mdiRequestStop && !running && allStageClosed) {
+					Assert.Equal (iterations, Application.MdiChildes.Count);
+					mdiRequestStop = true;
+					mdi.RequestStop ();
+				} else {
+					Assert.Empty (Application.MdiChildes);
+				}
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void AllChildClosed_Event_Test ()
+		{
+			Init ();
+
+			var mdi = new Mdi ();
+			var c1 = new Toplevel ();
+			var c2 = new Window ();
+			var c3 = new Window ();
+
+			// MdiChild = c1, c2, c3
+			var iterations = 3;
+
+			mdi.Ready += () => {
+				Assert.Empty (Application.MdiChildes);
+				Application.Run (c1);
+			};
+			c1.Ready += () => {
+				Assert.Single (Application.MdiChildes);
+				Application.Run (c2);
+			};
+			c2.Ready += () => {
+				Assert.Equal (2, Application.MdiChildes.Count);
+				Application.Run (c3);
+			};
+			c3.Ready += () => {
+				Assert.Equal (3, Application.MdiChildes.Count);
+				c3.RequestStop ();
+				c2.RequestStop ();
+				c1.RequestStop ();
+			};
+			// Now this will close the MdiContainer when all MdiChildes was closed
+			mdi.AllChildClosed += () => {
+				mdi.RequestStop ();
+			};
+			Application.Iteration += () => {
+				if (iterations == 3) {
+					// The Current still is c3 because Current.Running is false.
+					Assert.True (Application.Current == c3);
+					Assert.False (Application.Current.Running);
+					// But the childes order were reorder by Running = false
+					Assert.True (Application.MdiChildes [0] == c3);
+					Assert.True (Application.MdiChildes [1] == c2);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else if (iterations == 2) {
+					// The Current is c2 and Current.Running is false.
+					Assert.True (Application.Current == c2);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [0] == c2);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else if (iterations == 1) {
+					// The Current is c1 and Current.Running is false.
+					Assert.True (Application.Current == c1);
+					Assert.False (Application.Current.Running);
+					Assert.True (Application.MdiChildes [^1] == c1);
+				} else {
+					// The Current is mdi.
+					Assert.True (Application.Current == mdi);
+					Assert.False (Application.Current.Running);
+					Assert.Empty (Application.MdiChildes);
+				}
+				iterations--;
+			};
+
+			Application.Run (mdi);
+
+			Assert.Empty (Application.MdiChildes);
+
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void SetCurrentAsTop_Run_A_Not_Modal_Toplevel_Make_It_The_Current_Application_Top ()
+		{
+			Init ();
+
+			var t1 = new Toplevel ();
+			var t2 = new Toplevel ();
+			var t3 = new Toplevel ();
+			var d = new Dialog ();
+			var t4 = new Toplevel ();
+
+			// t1, t2, t3, d, t4
+			var iterations = 5;
+
+			t1.Ready += () => {
+				Assert.Equal (t1, Application.Top);
+				Application.Run (t2);
+			};
+			t2.Ready += () => {
+				Assert.Equal (t2, Application.Top);
+				Application.Run (t3);
+			};
+			t3.Ready += () => {
+				Assert.Equal (t3, Application.Top);
+				Application.Run (d);
+			};
+			d.Ready += () => {
+				Assert.Equal (t3, Application.Top);
+				Application.Run (t4);
+			};
+			t4.Ready += () => {
+				Assert.Equal (t4, Application.Top);
+				t4.RequestStop ();
+				d.RequestStop ();
+				t3.RequestStop ();
+				t2.RequestStop ();
+			};
+			// Now this will close the MdiContainer when all MdiChildes was closed
+			t2.Closed += (_) => {
+				t1.RequestStop ();
+			};
+			Application.Iteration += () => {
+				if (iterations == 5) {
+					// The Current still is t4 because Current.Running is false.
+					Assert.Equal (t4, Application.Current);
+					Assert.False (Application.Current.Running);
+					Assert.Equal (t4, Application.Top);
+				} else if (iterations == 4) {
+					// The Current is d and Current.Running is false.
+					Assert.Equal (d, Application.Current);
+					Assert.False (Application.Current.Running);
+					Assert.Equal (t4, Application.Top);
+				} else if (iterations == 3) {
+					// The Current is t3 and Current.Running is false.
+					Assert.Equal (t3, Application.Current);
+					Assert.False (Application.Current.Running);
+					Assert.Equal (t3, Application.Top);
+				} else if (iterations == 2) {
+					// The Current is t2 and Current.Running is false.
+					Assert.Equal (t2, Application.Current);
+					Assert.False (Application.Current.Running);
+					Assert.Equal (t2, Application.Top);
+				} else {
+					// The Current is t1.
+					Assert.Equal (t1, Application.Current);
+					Assert.False (Application.Current.Running);
+					Assert.Equal (t1, Application.Top);
+				}
+				iterations--;
+			};
+
+			Application.Run (t1);
+
+			Assert.Equal (t1, Application.Top);
+
+			Application.Shutdown ();
+
+			Assert.Null (Application.Top);
+		}
 	}
 }

+ 1 - 1
UnitTests/AssemblyInfo.cs

@@ -7,7 +7,7 @@ using Xunit;
 // Since Application is a singleton we can't run tests in parallel
 [assembly: CollectionBehavior (DisableTestParallelization = true)]
 
-// This class enables test functions annotaed with the [AutoInitShutdown] attribute to 
+// This class enables test functions annotated with the [AutoInitShutdown] attribute to 
 // automatically call Application.Init before called and Application.Shutdown after
 // 
 // This is necessary because a) Application is a singleton and Init/Shutdown must be called

+ 50 - 0
UnitTests/GraphViewTests.cs

@@ -1382,6 +1382,56 @@ namespace Terminal.Gui.Views {
 			// Shutdown must be called to safely clean up Application if Init has been called
 			Application.Shutdown ();
 		}
+
+		[Theory]
+		[InlineData (true)]
+		[InlineData (false)]
+		public void LabelChangeText_RendersCorrectly (bool useFill)
+		{
+			var driver = new FakeDriver ();
+			Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+			driver.Init (() => { });
+
+			// create a wide window
+			var mount = new View () {
+				Width = 100,
+				Height = 100
+			};
+
+			try {
+				// Create a label with a short text 
+				var lbl1 = new Label ("ff");
+
+				// Specify that the label should be very wide
+				if (useFill) {
+					lbl1.Width = Dim.Fill ();
+				} else {
+					lbl1.Width = 100;
+				}
+
+				//put label into view
+				mount.Add (lbl1);
+
+				// render view
+				lbl1.ColorScheme = new ColorScheme ();
+				Assert.Equal (1, lbl1.Height);
+				mount.Redraw (mount.Bounds);
+
+				// should have the initial text
+				GraphViewTests.AssertDriverContentsAre ("ff", null);
+
+				// change the text and redraw
+				lbl1.Text = "ff1234";
+				mount.Redraw (mount.Bounds);
+
+				// should have the new text rendered
+				GraphViewTests.AssertDriverContentsAre ("ff1234", null);
+
+
+			} finally {
+				Application.Shutdown ();
+			}
+		}
 	}
 
 		public class AxisIncrementToRenderTests {

+ 12 - 6
UnitTests/PosTests.cs

@@ -296,37 +296,43 @@ namespace Terminal.Gui.Core {
 			var app = setup ();
 			app.button.Y = Pos.Left (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.X (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Top (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Y (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Bottom (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 
 			app = setup ();
 			app.button.Y = Pos.Right (app.win);
 			rs = Application.Begin (Application.Top);
-			Application.Run ();
+			// If Application.RunState is used then we must use Application.RunLoop with the rs parameter
+			Application.RunLoop (rs);
 			cleanup (rs);
 		}
 

+ 427 - 5
UnitTests/ScenarioTests.cs

@@ -1,6 +1,8 @@
+using NStack;
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Reflection;
 using Terminal.Gui;
 using UICatalog;
 using Xunit;
@@ -72,10 +74,12 @@ namespace Terminal.Gui {
 				var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
 				scenario.Init (Application.Top, Colors.Base);
 				scenario.Setup ();
-				var rs = Application.Begin (Application.Top);
+				// There is no need to call Application.Begin because Init already creates the Application.Top
+				// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
+				//var rs = Application.Begin (Application.Top);
 				scenario.Run ();
 
-				Application.End (rs);
+				//Application.End (rs);
 
 				// Shutdown must be called to safely clean up Application if Init has been called
 				Application.Shutdown ();
@@ -100,7 +104,7 @@ namespace Terminal.Gui {
 			Assert.NotEmpty (scenarioClasses);
 
 			var item = scenarioClasses.FindIndex (t => Scenario.ScenarioMetadata.GetName (t).Equals ("Generic", StringComparison.OrdinalIgnoreCase));
-			var scenarioClass = scenarioClasses[item];
+			var scenarioClass = scenarioClasses [item];
 			// Setup some fake keypresses 
 			// Passing empty string will cause just a ctrl-q to be fired
 			int stackSize = CreateInput ("");
@@ -132,10 +136,12 @@ namespace Terminal.Gui {
 			var scenario = (Scenario)Activator.CreateInstance (scenarioClass);
 			scenario.Init (Application.Top, Colors.Base);
 			scenario.Setup ();
-			var rs = Application.Begin (Application.Top);
+			// There is no need to call Application.Begin because Init already creates the Application.Top
+			// If Application.RunState is used then the Application.RunLoop must also be used instead Application.Run.
+			//var rs = Application.Begin (Application.Top);
 			scenario.Run ();
 
-			Application.End (rs);
+			//Application.End (rs);
 
 			Assert.Equal (0, abortCount);
 			// # of key up events should match # of iterations
@@ -153,5 +159,421 @@ namespace Terminal.Gui {
 			Responder.Instances.Clear ();
 #endif
 		}
+
+		[Fact]
+		public void Run_All_Views_Tester_Scenario ()
+		{
+			Window _leftPane;
+			ListView _classListView;
+			FrameView _hostPane;
+
+			Dictionary<string, Type> _viewClasses;
+			View _curView = null;
+
+			// Settings
+			FrameView _settingsPane;
+			CheckBox _computedCheckBox;
+			FrameView _locationFrame;
+			RadioGroup _xRadioGroup;
+			TextField _xText;
+			int _xVal = 0;
+			RadioGroup _yRadioGroup;
+			TextField _yText;
+			int _yVal = 0;
+
+			FrameView _sizeFrame;
+			RadioGroup _wRadioGroup;
+			TextField _wText;
+			int _wVal = 0;
+			RadioGroup _hRadioGroup;
+			TextField _hText;
+			int _hVal = 0;
+			List<string> posNames = new List<String> { "Factor", "AnchorEnd", "Center", "Absolute" };
+			List<string> dimNames = new List<String> { "Factor", "Fill", "Absolute" };
+
+
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var Top = Application.Top;
+
+			_viewClasses = GetAllViewClassesCollection ()
+				.OrderBy (t => t.Name)
+				.Select (t => new KeyValuePair<string, Type> (t.Name, t))
+				.ToDictionary (t => t.Key, t => t.Value);
+
+			_leftPane = new Window ("Classes") {
+				X = 0,
+				Y = 0,
+				Width = 15,
+				Height = Dim.Fill (1), // for status bar
+				CanFocus = false,
+				ColorScheme = Colors.TopLevel,
+			};
+
+			_classListView = new ListView (_viewClasses.Keys.ToList ()) {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (0),
+				Height = Dim.Fill (0),
+				AllowsMarking = false,
+				ColorScheme = Colors.TopLevel,
+			};
+			_leftPane.Add (_classListView);
+
+			_settingsPane = new FrameView ("Settings") {
+				X = Pos.Right (_leftPane),
+				Y = 0, // for menu
+				Width = Dim.Fill (),
+				Height = 10,
+				CanFocus = false,
+				ColorScheme = Colors.TopLevel,
+			};
+			_computedCheckBox = new CheckBox ("Computed Layout", true) { X = 0, Y = 0 };
+			_settingsPane.Add (_computedCheckBox);
+
+			var radioItems = new ustring [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
+			_locationFrame = new FrameView ("Location (Pos)") {
+				X = Pos.Left (_computedCheckBox),
+				Y = Pos.Bottom (_computedCheckBox),
+				Height = 3 + radioItems.Length,
+				Width = 36,
+			};
+			_settingsPane.Add (_locationFrame);
+
+			var label = new Label ("x:") { X = 0, Y = 0 };
+			_locationFrame.Add (label);
+			_xRadioGroup = new RadioGroup (radioItems) {
+				X = 0,
+				Y = Pos.Bottom (label),
+			};
+			_xText = new TextField ($"{_xVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+			_locationFrame.Add (_xText);
+
+			_locationFrame.Add (_xRadioGroup);
+
+			radioItems = new ustring [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
+			label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
+			_locationFrame.Add (label);
+			_yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+			_locationFrame.Add (_yText);
+			_yRadioGroup = new RadioGroup (radioItems) {
+				X = Pos.X (label),
+				Y = Pos.Bottom (label),
+			};
+			_locationFrame.Add (_yRadioGroup);
+
+			_sizeFrame = new FrameView ("Size (Dim)") {
+				X = Pos.Right (_locationFrame),
+				Y = Pos.Y (_locationFrame),
+				Height = 3 + radioItems.Length,
+				Width = 40,
+			};
+
+			radioItems = new ustring [] { "Percent(width)", "Fill(width)", "Sized(width)" };
+			label = new Label ("width:") { X = 0, Y = 0 };
+			_sizeFrame.Add (label);
+			_wRadioGroup = new RadioGroup (radioItems) {
+				X = 0,
+				Y = Pos.Bottom (label),
+			};
+			_wText = new TextField ($"{_wVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+			_sizeFrame.Add (_wText);
+			_sizeFrame.Add (_wRadioGroup);
+
+			radioItems = new ustring [] { "Percent(height)", "Fill(height)", "Sized(height)" };
+			label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
+			_sizeFrame.Add (label);
+			_hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
+			_sizeFrame.Add (_hText);
+
+			_hRadioGroup = new RadioGroup (radioItems) {
+				X = Pos.X (label),
+				Y = Pos.Bottom (label),
+			};
+			_sizeFrame.Add (_hRadioGroup);
+
+			_settingsPane.Add (_sizeFrame);
+
+			_hostPane = new FrameView ("") {
+				X = Pos.Right (_leftPane),
+				Y = Pos.Bottom (_settingsPane),
+				Width = Dim.Fill (),
+				Height = Dim.Fill (1), // + 1 for status bar
+				ColorScheme = Colors.Dialog,
+			};
+
+			_classListView.OpenSelectedItem += (a) => {
+				_settingsPane.SetFocus ();
+			};
+			_classListView.SelectedItemChanged += (args) => {
+				ClearClass (_curView);
+				_curView = CreateClass (_viewClasses.Values.ToArray () [_classListView.SelectedItem]);
+			};
+
+			_computedCheckBox.Toggled += (previousState) => {
+				if (_curView != null) {
+					_curView.LayoutStyle = previousState ? LayoutStyle.Absolute : LayoutStyle.Computed;
+					_hostPane.LayoutSubviews ();
+				}
+			};
+
+			_xRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView);
+
+			_xText.TextChanged += (args) => {
+				try {
+					_xVal = int.Parse (_xText.Text.ToString ());
+					DimPosChanged (_curView);
+				} catch {
+
+				}
+			};
+
+			_yText.TextChanged += (args) => {
+				try {
+					_yVal = int.Parse (_yText.Text.ToString ());
+					DimPosChanged (_curView);
+				} catch {
+
+				}
+			};
+
+			_yRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView);
+
+			_wRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView);
+
+			_wText.TextChanged += (args) => {
+				try {
+					_wVal = int.Parse (_wText.Text.ToString ());
+					DimPosChanged (_curView);
+				} catch {
+
+				}
+			};
+
+			_hText.TextChanged += (args) => {
+				try {
+					_hVal = int.Parse (_hText.Text.ToString ());
+					DimPosChanged (_curView);
+				} catch {
+
+				}
+			};
+
+			_hRadioGroup.SelectedItemChanged += (selected) => DimPosChanged (_curView);
+
+			Top.Add (_leftPane, _settingsPane, _hostPane);
+
+			Top.LayoutSubviews ();
+
+			_curView = CreateClass (_viewClasses.First ().Value);
+
+			int iterations = 0;
+
+			Application.Iteration += () => {
+				iterations++;
+
+				if (iterations < _viewClasses.Count) {
+					_classListView.MoveDown ();
+					Assert.Equal (_curView.GetType ().Name,
+						_viewClasses.Values.ToArray () [_classListView.SelectedItem].Name);
+				} else {
+					Application.RequestStop ();
+				}
+			};
+
+			Application.Run ();
+
+			Assert.Equal (_viewClasses.Count, iterations);
+
+			Application.Shutdown ();
+
+
+			void DimPosChanged (View view)
+			{
+				if (view == null) {
+					return;
+				}
+
+				var layout = view.LayoutStyle;
+
+				try {
+					view.LayoutStyle = LayoutStyle.Absolute;
+
+					switch (_xRadioGroup.SelectedItem) {
+					case 0:
+						view.X = Pos.Percent (_xVal);
+						break;
+					case 1:
+						view.X = Pos.AnchorEnd (_xVal);
+						break;
+					case 2:
+						view.X = Pos.Center ();
+						break;
+					case 3:
+						view.X = Pos.At (_xVal);
+						break;
+					}
+
+					switch (_yRadioGroup.SelectedItem) {
+					case 0:
+						view.Y = Pos.Percent (_yVal);
+						break;
+					case 1:
+						view.Y = Pos.AnchorEnd (_yVal);
+						break;
+					case 2:
+						view.Y = Pos.Center ();
+						break;
+					case 3:
+						view.Y = Pos.At (_yVal);
+						break;
+					}
+
+					switch (_wRadioGroup.SelectedItem) {
+					case 0:
+						view.Width = Dim.Percent (_wVal);
+						break;
+					case 1:
+						view.Width = Dim.Fill (_wVal);
+						break;
+					case 2:
+						view.Width = Dim.Sized (_wVal);
+						break;
+					}
+
+					switch (_hRadioGroup.SelectedItem) {
+					case 0:
+						view.Height = Dim.Percent (_hVal);
+						break;
+					case 1:
+						view.Height = Dim.Fill (_hVal);
+						break;
+					case 2:
+						view.Height = Dim.Sized (_hVal);
+						break;
+					}
+				} catch (Exception e) {
+					MessageBox.ErrorQuery ("Exception", e.Message, "Ok");
+				} finally {
+					view.LayoutStyle = layout;
+				}
+				UpdateTitle (view);
+			}
+
+			void UpdateSettings (View view)
+			{
+				var x = view.X.ToString ();
+				var y = view.Y.ToString ();
+				_xRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ());
+				_yRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ());
+				_xText.Text = $"{view.Frame.X}";
+				_yText.Text = $"{view.Frame.Y}";
+
+				var w = view.Width.ToString ();
+				var h = view.Height.ToString ();
+				_wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ());
+				_hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ());
+				_wText.Text = $"{view.Frame.Width}";
+				_hText.Text = $"{view.Frame.Height}";
+			}
+
+			void UpdateTitle (View view)
+			{
+				_hostPane.Title = $"{view.GetType ().Name} - {view.X.ToString ()}, {view.Y.ToString ()}, {view.Width.ToString ()}, {view.Height.ToString ()}";
+			}
+
+			List<Type> GetAllViewClassesCollection ()
+			{
+				List<Type> types = new List<Type> ();
+				foreach (Type type in typeof (View).Assembly.GetTypes ()
+				 .Where (myType => myType.IsClass && !myType.IsAbstract && myType.IsPublic && myType.IsSubclassOf (typeof (View)))) {
+					types.Add (type);
+				}
+				return types;
+			}
+
+			void ClearClass (View view)
+			{
+				// Remove existing class, if any
+				if (view != null) {
+					view.LayoutComplete -= LayoutCompleteHandler;
+					_hostPane.Remove (view);
+					view.Dispose ();
+					_hostPane.Clear ();
+				}
+			}
+
+			View CreateClass (Type type)
+			{
+				// If we are to create a generic Type
+				if (type.IsGenericType) {
+
+					// For each of the <T> arguments
+					List<Type> typeArguments = new List<Type> ();
+
+					// use <object>
+					foreach (var arg in type.GetGenericArguments ()) {
+						typeArguments.Add (typeof (object));
+					}
+
+					// And change what type we are instantiating from MyClass<T> to MyClass<object>
+					type = type.MakeGenericType (typeArguments.ToArray ());
+				}
+				// Instantiate view
+				var view = (View)Activator.CreateInstance (type);
+
+				//_curView.X = Pos.Center ();
+				//_curView.Y = Pos.Center ();
+				view.Width = Dim.Percent (75);
+				view.Height = Dim.Percent (75);
+
+				// Set the colorscheme to make it stand out if is null by default
+				if (view.ColorScheme == null) {
+					view.ColorScheme = Colors.Base;
+				}
+
+				// If the view supports a Text property, set it so we have something to look at
+				if (view.GetType ().GetProperty ("Text") != null) {
+					try {
+						view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Text") });
+					} catch (TargetInvocationException e) {
+						MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
+						view = null;
+					}
+				}
+
+				// If the view supports a Title property, set it so we have something to look at
+				if (view != null && view.GetType ().GetProperty ("Title") != null) {
+					view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Title") });
+				}
+
+				// If the view supports a Source property, set it so we have something to look at
+				if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (Terminal.Gui.IListDataSource)) {
+					var source = new ListWrapper (new List<ustring> () { ustring.Make ("Test Text #1"), ustring.Make ("Test Text #2"), ustring.Make ("Test Text #3") });
+					view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source });
+				}
+
+				// Set Settings
+				_computedCheckBox.Checked = view.LayoutStyle == LayoutStyle.Computed;
+
+				// Add
+				_hostPane.Add (view);
+				//DimPosChanged ();
+				_hostPane.LayoutSubviews ();
+				_hostPane.Clear ();
+				_hostPane.SetNeedsDisplay ();
+				UpdateSettings (view);
+				UpdateTitle (view);
+
+				view.LayoutComplete += LayoutCompleteHandler;
+
+				return view;
+			}
+
+			void LayoutCompleteHandler (View.LayoutEventArgs args)
+			{
+				UpdateTitle (_curView);
+			}
+		}
 	}
 }

+ 73 - 2
UnitTests/ScrollBarViewTests.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.Generic;
 using System.Diagnostics;
 using System.Reflection;
 using Xunit;
@@ -6,7 +7,7 @@ using Xunit;
 namespace Terminal.Gui.Views {
 	public class ScrollBarViewTests {
 
-		// This class enables test functions annoated with the [InitShutdown] attribute
+		// This class enables test functions annotated with the [InitShutdown] attribute
 		// to have a function called before the test function is called and after.
 		// 
 		// This is necessary because a) Application is a singleton and Init/Shutdown must be called
@@ -55,7 +56,7 @@ namespace Terminal.Gui.Views {
 
 		private static HostView _hostView;
 		private ScrollBarView _scrollBar;
-		private  bool _added;
+		private bool _added;
 
 		private void AddHandlers ()
 		{
@@ -444,5 +445,75 @@ namespace Terminal.Gui.Views {
 			Assert.Equal ("Dim.Absolute(1)", _scrollBar.OtherScrollBarView.Height.ToString ());
 			Assert.Equal (1, _scrollBar.OtherScrollBarView.Bounds.Height);
 		}
+
+		[Fact]
+		public void Constructor_ShowBothScrollIndicator_False_Refresh_Does_Not_Throws_An_Object_Null_Exception ()
+		{
+			var exception = Record.Exception (() => {
+				Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+				var top = Application.Top;
+
+				var win = new Window () {
+					X = 0,
+					Y = 0,
+					Width = Dim.Fill (),
+					Height = Dim.Fill ()
+				};
+
+				List<string> source = new List<string> ();
+
+				for (int i = 0; i < 50; i++) {
+					source.Add ($"item {i}");
+				}
+
+				var listView = new ListView (source) {
+					X = 0,
+					Y = 0,
+					Width = Dim.Fill (),
+					Height = Dim.Fill ()
+				};
+				win.Add (listView);
+
+				var newScrollBarView = new ScrollBarView (listView, true, false) {
+					KeepContentAlwaysInViewport = true
+				};
+				win.Add (newScrollBarView);
+
+				newScrollBarView.ChangedPosition += () => {
+					listView.TopItem = newScrollBarView.Position;
+					if (listView.TopItem != newScrollBarView.Position) {
+						newScrollBarView.Position = listView.TopItem;
+					}
+					Assert.Equal (newScrollBarView.Position, listView.TopItem);
+					listView.SetNeedsDisplay ();
+				};
+
+				listView.DrawContent += (e) => {
+					newScrollBarView.Size = listView.Source.Count - 1;
+					Assert.Equal (newScrollBarView.Size, listView.Source.Count);
+					newScrollBarView.Position = listView.TopItem;
+					Assert.Equal (newScrollBarView.Position, listView.TopItem);
+					newScrollBarView.Refresh ();
+				};
+
+				top.Ready += () => {
+					newScrollBarView.Position = 45;
+					Assert.Equal (newScrollBarView.Position, newScrollBarView.Size - listView.TopItem + (listView.TopItem - listView.Bounds.Height));
+					Assert.Equal (newScrollBarView.Position, listView.TopItem);
+					Assert.Equal (27, newScrollBarView.Position);
+					Assert.Equal (27, listView.TopItem);
+					Application.RequestStop ();
+				};
+
+				top.Add (win);
+
+				Application.Run ();
+
+				Application.Shutdown ();
+
+			});
+			Assert.Null (exception);
+		}
 	}
 }

+ 192 - 0
UnitTests/StackExtensionsTests.cs

@@ -0,0 +1,192 @@
+using System;
+using System.Collections.Generic;
+using Xunit;
+
+namespace Terminal.Gui.Core {
+	public class StackExtensionsTests {
+		[Fact]
+		public void Stack_Toplevels_CreateToplevels ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			int index = toplevels.Count - 1;
+			foreach (var top in toplevels) {
+				if (top.GetType () == typeof (Toplevel)) {
+					Assert.Equal ("Top", top.Id);
+				} else {
+					Assert.Equal ($"w{index}", top.Id);
+				}
+				index--;
+			}
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("w4", tops [0].Id);
+			Assert.Equal ("w3", tops [1].Id);
+			Assert.Equal ("w2", tops [2].Id);
+			Assert.Equal ("w1", tops [3].Id);
+			Assert.Equal ("Top", tops [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_Replace ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			var valueToReplace = new Window () { Id = "w1" };
+			var valueToReplaceWith = new Window () { Id = "new" };
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+
+			toplevels.Replace (valueToReplace, valueToReplaceWith, comparer);
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("w4", tops [0].Id);
+			Assert.Equal ("w3", tops [1].Id);
+			Assert.Equal ("w2", tops [2].Id);
+			Assert.Equal ("new", tops [3].Id);
+			Assert.Equal ("Top", tops [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_Swap ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			var valueToSwapFrom = new Window () { Id = "w3" };
+			var valueToSwapTo = new Window () { Id = "w1" };
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+			toplevels.Swap (valueToSwapFrom, valueToSwapTo, comparer);
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("w4", tops [0].Id);
+			Assert.Equal ("w1", tops [1].Id);
+			Assert.Equal ("w2", tops [2].Id);
+			Assert.Equal ("w3", tops [3].Id);
+			Assert.Equal ("Top", tops [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_MoveNext ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			toplevels.MoveNext ();
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("w3", tops [0].Id);
+			Assert.Equal ("w2", tops [1].Id);
+			Assert.Equal ("w1", tops [2].Id);
+			Assert.Equal ("Top", tops [3].Id);
+			Assert.Equal ("w4", tops [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_MovePrevious ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			toplevels.MovePrevious ();
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("Top", tops [0].Id);
+			Assert.Equal ("w4", tops [1].Id);
+			Assert.Equal ("w3", tops [2].Id);
+			Assert.Equal ("w2", tops [3].Id);
+			Assert.Equal ("w1", tops [^1].Id);
+		}
+
+		[Fact]
+		public void ToplevelEqualityComparer_GetHashCode ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			// Only allows unique keys
+			HashSet<int> hCodes = new HashSet<int> ();
+
+			foreach (var top in toplevels) {
+				Assert.True (hCodes.Add (top.GetHashCode ()));
+			}
+		}
+
+		[Fact]
+		public void Stack_Toplevels_FindDuplicates ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+
+			toplevels.Push (new Toplevel () { Id = "w4" });
+			toplevels.Push (new Toplevel () { Id = "w1" });
+
+			var dup = toplevels.FindDuplicates (comparer).ToArray ();
+
+			Assert.Equal ("w4", dup [0].Id);
+			Assert.Equal ("w1", dup [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_Contains ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+
+			Assert.True (toplevels.Contains (new Window () { Id = "w2" }, comparer));
+			Assert.False (toplevels.Contains (new Toplevel () { Id = "top2" }, comparer));
+		}
+
+		[Fact]
+		public void Stack_Toplevels_MoveTo ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			var valueToMove = new Window () { Id = "w1" };
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+
+			toplevels.MoveTo (valueToMove, 1, comparer);
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("w4", tops [0].Id);
+			Assert.Equal ("w1", tops [1].Id);
+			Assert.Equal ("w3", tops [2].Id);
+			Assert.Equal ("w2", tops [3].Id);
+			Assert.Equal ("Top", tops [^1].Id);
+		}
+
+		[Fact]
+		public void Stack_Toplevels_MoveTo_From_Last_To_Top ()
+		{
+			Stack<Toplevel> toplevels = CreateToplevels ();
+
+			var valueToMove = new Window () { Id = "Top" };
+			ToplevelEqualityComparer comparer = new ToplevelEqualityComparer ();
+
+			toplevels.MoveTo (valueToMove, 0, comparer);
+
+			var tops = toplevels.ToArray ();
+
+			Assert.Equal ("Top", tops [0].Id);
+			Assert.Equal ("w4", tops [1].Id);
+			Assert.Equal ("w3", tops [2].Id);
+			Assert.Equal ("w2", tops [3].Id);
+			Assert.Equal ("w1", tops [^1].Id);
+		}
+
+
+		private Stack<Toplevel> CreateToplevels ()
+		{
+			Stack<Toplevel> toplevels = new Stack<Toplevel> ();
+
+			toplevels.Push (new Toplevel () { Id = "Top" });
+			toplevels.Push (new Window () { Id = "w1" });
+			toplevels.Push (new Window () { Id = "w2" });
+			toplevels.Push (new Window () { Id = "w3" });
+			toplevels.Push (new Window () { Id = "w4" });
+
+			return toplevels;
+		}
+	}
+}

+ 1 - 1
UnitTests/UnitTests.csproj

@@ -16,7 +16,7 @@
 
   <ItemGroup>
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.10.0" />
-    <PackageReference Include="ReportGenerator" Version="4.8.9" />
+    <PackageReference Include="ReportGenerator" Version="4.8.11" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

+ 38 - 0
UnitTests/ViewTests.cs

@@ -1332,5 +1332,43 @@ namespace Terminal.Gui.Views {
 			Assert.True (label.AutoSize);
 			Assert.Equal ("{X=0,Y=0,Width=28,Height=2}", label.Bounds.ToString ());
 		}
+
+		[Theory]
+		[InlineData (1)]
+		[InlineData (2)]
+		[InlineData (3)]
+		public void LabelChangeText_RendersCorrectly_Constructors (int choice)
+		{
+			var driver = new FakeDriver ();
+			Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			try {
+				// Create a label with a short text 
+				Label lbl;
+				var text = "test";
+
+				if (choice == 1) {
+					// An object initializer should call the default constructor.
+					lbl = new Label { Text = text };
+				} else if (choice == 2) {
+					// Calling the default constructor followed by the object initializer.
+					lbl = new Label () { Text = text };
+				} else {
+					// Calling the Text constructor.
+					lbl = new Label (text);
+				}
+				lbl.ColorScheme = new ColorScheme ();
+				lbl.Redraw (lbl.Bounds);
+
+				// should have the initial text
+				Assert.Equal ('t', driver.Contents [0, 0, 0]);
+				Assert.Equal ('e', driver.Contents [0, 1, 0]);
+				Assert.Equal ('s', driver.Contents [0, 2, 0]);
+				Assert.Equal ('t', driver.Contents [0, 3, 0]);
+				Assert.Equal (' ', driver.Contents [0, 4, 0]);
+			} finally {
+				Application.Shutdown ();
+			}
+		}
 	}
 }