Explorar o código

refactored core.cs to see how it feels

Charlie Kindel %!s(int64=5) %!d(string=hai) anos
pai
achega
040422b71d
Modificáronse 3 ficheiros con 544 adicións e 525 borrados
  1. 1 525
      Terminal.Gui/Core.cs
  2. 291 0
      Terminal.Gui/Views/Toplevel.cs
  3. 252 0
      Terminal.Gui/Views/Window.cs

+ 1 - 525
Terminal.Gui/Core.cs

@@ -1,5 +1,5 @@
 //
-// Core.cs: The core engine for gui.cs
+// Core.cs: The core engine for Terminal.Gui (Application, Responder, & View)
 //
 // Authors:
 //   Miguel de Icaza ([email protected])
@@ -21,7 +21,6 @@ using NStack;
 using System.ComponentModel;
 
 namespace Terminal.Gui {
-
 	/// <summary>
 	/// Responder base class implemented by objects that want to participate on keyboard and mouse input.
 	/// </summary>
@@ -1482,529 +1481,6 @@ namespace Terminal.Gui {
 		}
 	}
 
-	/// <summary>
-	/// Toplevel views can be modally executed.
-	/// </summary>
-	/// <remarks>
-	///   <para>
-	///     Toplevels can be modally executing views, and they return control
-	///     to the caller when the "Running" property is set to false, or
-	///     by calling <see cref="M:Terminal.Gui.Application.RequestStop()"/>
-	///   </para>
-	///   <para>
-	///     There will be a toplevel created for you on the first time use
-	///     and can be accessed from the property <see cref="P:Terminal.Gui.Application.Top"/>,
-	///     but new toplevels can be created and ran on top of it.   To run, create the
-	///     toplevel and then invoke <see cref="M:Terminal.Gui.Application.Run"/> with the
-	///     new toplevel.
-	///   </para>
-	///   <para>
-	///     TopLevels can also opt-in to more sophisticated initialization
-	///     by implementing <see cref="ISupportInitialize"/>. When they do
-	///     so, the <see cref="ISupportInitialize.BeginInit"/> and
-	///     <see cref="ISupportInitialize.EndInit"/> methods will be called
-	///     before running the view.
-	///     If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
-	///     can be implemented too, in which case the <see cref="ISupportInitialize"/>
-	///     methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
-	///     is <see langword="false"/>. This allows proper View inheritance hierarchies
-	///     to override base class layout code optimally by doing so only on first run,
-	///     instead of on every run.
-	///   </para>
-	/// </remarks>
-	public class Toplevel : View {
-		/// <summary>
-		/// Gets or sets whether the Mainloop for this <see cref="Toplevel"/> is running or not. Setting
-		/// this property to false will cause the MainLoop to exit. 
-		/// </summary>
-		public bool Running { get; set; }
-
-		/// <summary>
-		/// Fired once the Toplevel's MainLoop has started it's first iteration. 
-		/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
-		/// changes. A Ready event handler is a good place to finalize initialization after calling `<see cref="Application.Run()"/>(topLevel)`. 
-		/// </summary>
-		public event EventHandler Ready;
-
-		/// <summary>
-		/// Called from Application.RunLoop after the <see cref="Toplevel"/> has entered it's first iteration of the loop. 
-		/// </summary>
-		internal virtual void OnReady ()
-		{
-			Ready?.Invoke (this, EventArgs.Empty);
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Toplevel"/> class with the specified absolute layout.
-		/// </summary>
-		/// <param name="frame">Frame.</param>
-		public Toplevel (Rect frame) : base (frame)
-		{
-			Initialize ();
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Toplevel"/> class with Computed layout, defaulting to <see langword="async"/> full screen.
-		/// </summary>
-		public Toplevel () : base ()
-		{
-			Initialize ();
-			Width = Dim.Fill ();
-			Height = Dim.Fill ();
-		}
-
-		void Initialize ()
-		{
-			ColorScheme = Colors.Base;
-		}
-
-		/// <summary>
-		/// Convenience factory method that creates a new toplevel with the current terminal dimensions.
-		/// </summary>
-		/// <returns>The create.</returns>
-		public static Toplevel Create ()
-		{
-			return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
-		}
-
-		/// <summary>
-		/// Gets or sets a value indicating whether this <see cref="Toplevel"/> can focus.
-		/// </summary>
-		/// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
-		public override bool CanFocus {
-			get => true;
-		}
-
-		/// <summary>
-		/// Determines whether the <see cref="Toplevel"/> is modal or not.
-		/// Causes <see cref="ProcessKey(KeyEvent)"/> to propagate keys upwards
-		/// by default unless set to <see langword="true"/>.
-		/// </summary>
-		public bool Modal { get; set; }
-
-		/// <summary>
-		/// Check id current toplevel has menu bar
-		/// </summary>
-		public MenuBar MenuBar { get; set; }
-
-		/// <summary>
-		/// Check id current toplevel has status bar
-		/// </summary>
-		public StatusBar StatusBar { get; set; }
-
-		///<inheritdoc cref="ProcessKey"/>
-		public override bool ProcessKey (KeyEvent keyEvent)
-		{
-			if (base.ProcessKey (keyEvent))
-				return true;
-
-			switch (keyEvent.Key) {
-			case Key.ControlQ:
-				// FIXED: stop current execution of this container
-				Application.RequestStop ();
-				break;
-			case Key.ControlZ:
-				Driver.Suspend ();
-				return true;
-
-#if false
-			case Key.F5:
-				Application.DebugDrawBounds = !Application.DebugDrawBounds;
-				SetNeedsDisplay ();
-				return true;
-#endif
-			case Key.Tab:
-			case Key.CursorRight:
-			case Key.CursorDown:
-			case Key.ControlI: // Unix
-				var old = Focused;
-				if (!FocusNext ())
-					FocusNext ();
-				if (old != Focused) {
-					old?.SetNeedsDisplay ();
-					Focused?.SetNeedsDisplay ();
-				}
-				return true;
-			case Key.CursorLeft:
-			case Key.CursorUp:
-			case Key.BackTab:
-				old = Focused;
-				if (!FocusPrev ())
-					FocusPrev ();
-				if (old != Focused) {
-					old?.SetNeedsDisplay ();
-					Focused?.SetNeedsDisplay ();
-				}
-				return true;
-
-			case Key.ControlL:
-				Application.Refresh ();
-				return true;
-			}
-			return false;
-		}
-
-		///<inheritdoc cref="Add"/>
-		public override void Add (View view)
-		{
-			if (this == Application.Top) {
-				if (view is MenuBar)
-					MenuBar = view as MenuBar;
-				if (view is StatusBar)
-					StatusBar = view as StatusBar;
-			}
-			base.Add (view);
-		}
-
-		///<inheritdoc cref="Remove"/>
-		public override void Remove (View view)
-		{
-			if (this == Application.Top) {
-				if (view is MenuBar)
-					MenuBar = null;
-				if (view is StatusBar)
-					StatusBar = null;
-			}
-			base.Remove (view);
-		}
-
-		///<inheritdoc cref="RemoveAll"/>
-		public override void RemoveAll ()
-		{
-			if (this == Application.Top) {
-				MenuBar = null;
-				StatusBar = null;
-			}
-			base.RemoveAll ();
-		}
-
-		internal void EnsureVisibleBounds (Toplevel top, int x, int y, out int nx, out int ny)
-		{
-			nx = Math.Max (x, 0);
-			nx = nx + top.Frame.Width > Driver.Cols ? Math.Max (Driver.Cols - top.Frame.Width, 0) : nx;
-			bool m, s;
-			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
-				m = Application.Top.MenuBar != null;
-			else
-				m = ((Toplevel)SuperView).MenuBar != null;
-			int l = m ? 1 : 0;
-			ny = Math.Max (y, l);
-			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
-				s = Application.Top.StatusBar != null;
-			else
-				s = ((Toplevel)SuperView).StatusBar != null;
-			l = s ? Driver.Rows - 1 : Driver.Rows;
-			ny = Math.Min (ny, l);
-			ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
-		}
-
-		internal void PositionToplevels ()
-		{
-			if (this != Application.Top) {
-				EnsureVisibleBounds (this, Frame.X, Frame.Y, out int nx, out int ny);
-				if ((nx != Frame.X || ny != Frame.Y) && LayoutStyle != LayoutStyle.Computed) {
-					X = nx;
-					Y = ny;
-				}
-			} else {
-				foreach (var top in Subviews) {
-					if (top is Toplevel) {
-						EnsureVisibleBounds ((Toplevel)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) {
-							top.X = nx;
-							top.Y = ny;
-						}
-						if (StatusBar != null) {
-							if (ny + top.Frame.Height > Driver.Rows - 1) {
-								if (top.Height is Dim.DimFill)
-									top.Height = Dim.Fill () - 1;
-							}
-							if (StatusBar.Frame.Y != Driver.Rows - 1) {
-								StatusBar.Y = Driver.Rows - 1;
-								SetNeedsDisplay ();
-							}
-						}
-					}
-				}
-			}
-		}
-
-		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect region)
-		{
-			Application.CurrentView = this;
-
-			if (IsCurrentTop) {
-				if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
-					Driver.SetAttribute (Colors.TopLevel.Normal);
-					Clear (region);
-					Driver.SetAttribute (Colors.Base.Normal);
-				}
-				foreach (var view in Subviews) {
-					if (view.Frame.IntersectsWith (region)) {
-						view.SetNeedsLayout ();
-						view.SetNeedsDisplay (view.Bounds);
-					}
-				}
-
-				ClearNeedsDisplay ();
-			}
-
-			base.Redraw (base.Bounds);
-		}
-
-		/// <summary>
-		/// This method is invoked by Application.Begin as part of the Application.Run after
-		/// the views have been laid out, and before the views are drawn for the first time.
-		/// </summary>
-		public virtual void WillPresent ()
-		{
-			FocusFirst ();
-		}
-	}
-
-	/// <summary>
-	/// A <see cref="Toplevel"/> <see cref="View"/> that draws a frame around its region and has a "ContentView" subview where the contents are added.
-	/// </summary>
-	public class Window : Toplevel, IEnumerable {
-		View contentView;
-		ustring title;
-
-		/// <summary>
-		/// The title to be displayed for this window.
-		/// </summary>
-		/// <value>The title.</value>
-		public ustring Title {
-			get => title;
-			set {
-				title = value;
-				SetNeedsDisplay ();
-			}
-		}
-
-		class ContentView : View {
-			public ContentView (Rect frame) : base (frame) { }
-			public ContentView () : base () { }
-#if false
-			public override void Redraw (Rect region)
-			{
-				Driver.SetAttribute (ColorScheme.Focus);
-
-				for (int y = 0; y < Frame.Height; y++) {
-					Move (0, y);
-					for (int x = 0; x < Frame.Width; x++) {
-
-						Driver.AddRune ('x');
-					}
-				}
-			}
-#endif
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Gui.Window"/> class with an optional title and a set frame.
-		/// </summary>
-		/// <param name="frame">Frame.</param>
-		/// <param name="title">Title.</param>
-		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
-		{
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> class with an optional title.
-		/// </summary>
-		/// <param name="title">Title.</param>
-		public Window (ustring title = null) : this (title, padding: 0)
-		{
-		}
-
-		int padding;
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> with
-		/// the specified frame for its location, with the specified border
-		/// an optional title.
-		/// </summary>
-		/// <param name="frame">Frame.</param>
-		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
-		/// <param name="title">Title.</param>
-		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
-		{
-			this.Title = title;
-			int wb = 2 * (1 + padding);
-			this.padding = padding;
-			var cFrame = new Rect (1 + padding, 1 + padding, frame.Width - wb, frame.Height - wb);
-			contentView = new ContentView (cFrame);
-			base.Add (contentView);
-		}
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="Window"/> with
-		/// the specified frame for its location, with the specified border
-		/// an optional title.
-		/// </summary>
-		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
-		/// <param name="title">Title.</param>
-		public Window (ustring title = null, int padding = 0) : base ()
-		{
-			this.Title = title;
-			int wb = 1 + padding;
-			this.padding = padding;
-			contentView = new ContentView () {
-				X = wb,
-				Y = wb,
-				Width = Dim.Fill (wb),
-				Height = Dim.Fill (wb)
-			};
-			base.Add (contentView);
-		}
-
-		/// <summary>
-		/// Enumerates the various <see cref="View"/>s in the embedded <see cref="ContentView"/>.
-		/// </summary>
-		/// <returns>The enumerator.</returns>
-		public new IEnumerator GetEnumerator ()
-		{
-			return contentView.GetEnumerator ();
-		}
-
-		void DrawFrame (bool fill = true)
-		{
-			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: fill);
-		}
-
-		/// <summary>
-		/// Add the specified view to the <see cref="ContentView"/>.
-		/// </summary>
-		/// <param name="view">View to add to the window.</param>
-		public override void Add (View view)
-		{
-			contentView.Add (view);
-			if (view.CanFocus)
-				CanFocus = true;
-		}
-
-
-		/// <summary>
-		///   Removes a widget from this container.
-		/// </summary>
-		/// <remarks>
-		/// </remarks>
-		public override void Remove (View view)
-		{
-			if (view == null)
-				return;
-
-			SetNeedsDisplay ();
-			var touched = view.Frame;
-			contentView.Remove (view);
-
-			if (contentView.InternalSubviews.Count < 1)
-				this.CanFocus = false;
-		}
-
-		/// <summary>
-		///   Removes all widgets from this container.
-		/// </summary>
-		/// <remarks>
-		/// </remarks>
-		public override void RemoveAll ()
-		{
-			contentView.RemoveAll ();
-		}
-
-		///<inheritdoc cref="Redraw"/>
-		public override void Redraw (Rect bounds)
-		{
-			Application.CurrentView = this;
-
-			if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
-				DrawFrameWindow ();
-			}
-			contentView.Redraw (contentView.Bounds);
-			ClearNeedsDisplay ();
-			DrawFrameWindow (false);
-
-			void DrawFrameWindow (bool fill = true)
-			{
-				Driver.SetAttribute (ColorScheme.Normal);
-				DrawFrame (fill);
-				if (HasFocus)
-					Driver.SetAttribute (ColorScheme.HotNormal);
-				var width = Frame.Width - (padding + 2) * 2;
-				if (Title != null && width > 4) {
-					Move (1 + padding, padding);
-					Driver.AddRune (' ');
-					var str = Title.Length >= width ? Title [0, width - 2] : Title;
-					Driver.AddStr (str);
-					Driver.AddRune (' ');
-				}
-				Driver.SetAttribute (ColorScheme.Normal);
-			}
-		}
-
-		//
-		// 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 cref="MouseEvent(Gui.MouseEvent)"/>
-		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 ((mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
-				mouseEvent.Flags == MouseFlags.Button3Pressed)) {
-				if (dragPosition.HasValue) {
-					if (SuperView == null) {
-						Application.Top.SetNeedsDisplay (Frame);
-						Application.Top.Redraw (Frame);
-					} else {
-						SuperView.SetNeedsDisplay (Frame);
-					}
-					EnsureVisibleBounds (this, mouseEvent.X + mouseEvent.OfX - start.X,
-						mouseEvent.Y + mouseEvent.OfY, out nx, out ny);
-
-					dragPosition = new Point (nx, ny);
-					Frame = new Rect (nx, ny, Frame.Width, Frame.Height);
-					X = nx;
-					Y = ny;
-					//Demo.ml2.Text = $"{dx},{dy}";
-
-					// FIXED: optimize, only SetNeedsDisplay on the before/after regions.
-					SetNeedsDisplay ();
-					return true;
-				} else {
-					// 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);
-					}
-
-					//Demo.ml2.Text = $"Starting at {dragPosition}";
-					return true;
-				}
-			}
-
-			if (mouseEvent.Flags == MouseFlags.Button1Released && dragPosition.HasValue) {
-				Application.UngrabMouse ();
-				Driver.UncookMouse ();
-				dragPosition = null;
-			}
-
-			//Demo.ml.Text = me.ToString ();
-			return false;
-		}
-
-	}
-
 	/// <summary>
 	/// The application driver for Terminal.Gui.
 	/// </summary>

+ 291 - 0
Terminal.Gui/Views/Toplevel.cs

@@ -0,0 +1,291 @@
+//
+// Toplevel.cs: Toplevel views can be modally executed
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+using System;
+using System.ComponentModel;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// Toplevel views can be modally executed.
+	/// </summary>
+	/// <remarks>
+	///   <para>
+	///     Toplevels can be modally executing views, and they return control
+	///     to the caller when the "Running" property is set to false, or
+	///     by calling <see cref="M:Terminal.Gui.Application.RequestStop()"/>
+	///   </para>
+	///   <para>
+	///     There will be a toplevel created for you on the first time use
+	///     and can be accessed from the property <see cref="P:Terminal.Gui.Application.Top"/>,
+	///     but new toplevels can be created and ran on top of it.   To run, create the
+	///     toplevel and then invoke <see cref="M:Terminal.Gui.Application.Run"/> with the
+	///     new toplevel.
+	///   </para>
+	///   <para>
+	///     TopLevels can also opt-in to more sophisticated initialization
+	///     by implementing <see cref="ISupportInitialize"/>. When they do
+	///     so, the <see cref="ISupportInitialize.BeginInit"/> and
+	///     <see cref="ISupportInitialize.EndInit"/> methods will be called
+	///     before running the view.
+	///     If first-run-only initialization is preferred, the <see cref="ISupportInitializeNotification"/>
+	///     can be implemented too, in which case the <see cref="ISupportInitialize"/>
+	///     methods will only be called if <see cref="ISupportInitializeNotification.IsInitialized"/>
+	///     is <see langword="false"/>. This allows proper View inheritance hierarchies
+	///     to override base class layout code optimally by doing so only on first run,
+	///     instead of on every run.
+	///   </para>
+	/// </remarks>
+	public class Toplevel : View {
+		/// <summary>
+		/// Gets or sets whether the Mainloop for this <see cref="Toplevel"/> is running or not. Setting
+		/// this property to false will cause the MainLoop to exit. 
+		/// </summary>
+		public bool Running { get; set; }
+
+		/// <summary>
+		/// Fired once the Toplevel's MainLoop has started it's first iteration. 
+		/// Subscribe to this event to perform tasks when the <see cref="Toplevel"/> has been laid out and focus has been set.
+		/// changes. A Ready event handler is a good place to finalize initialization after calling `<see cref="Application.Run()"/>(topLevel)`. 
+		/// </summary>
+		public event EventHandler Ready;
+
+		/// <summary>
+		/// Called from Application.RunLoop after the <see cref="Toplevel"/> has entered it's first iteration of the loop. 
+		/// </summary>
+		internal virtual void OnReady ()
+		{
+			Ready?.Invoke (this, EventArgs.Empty);
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Toplevel"/> class with the specified absolute layout.
+		/// </summary>
+		/// <param name="frame">Frame.</param>
+		public Toplevel (Rect frame) : base (frame)
+		{
+			Initialize ();
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Toplevel"/> class with Computed layout, defaulting to <see langword="async"/> full screen.
+		/// </summary>
+		public Toplevel () : base ()
+		{
+			Initialize ();
+			Width = Dim.Fill ();
+			Height = Dim.Fill ();
+		}
+
+		void Initialize ()
+		{
+			ColorScheme = Colors.Base;
+		}
+
+		/// <summary>
+		/// Convenience factory method that creates a new toplevel with the current terminal dimensions.
+		/// </summary>
+		/// <returns>The create.</returns>
+		public static Toplevel Create ()
+		{
+			return new Toplevel (new Rect (0, 0, Driver.Cols, Driver.Rows));
+		}
+
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="Toplevel"/> can focus.
+		/// </summary>
+		/// <value><c>true</c> if can focus; otherwise, <c>false</c>.</value>
+		public override bool CanFocus {
+			get => true;
+		}
+
+		/// <summary>
+		/// Determines whether the <see cref="Toplevel"/> is modal or not.
+		/// Causes <see cref="ProcessKey(KeyEvent)"/> to propagate keys upwards
+		/// by default unless set to <see langword="true"/>.
+		/// </summary>
+		public bool Modal { get; set; }
+
+		/// <summary>
+		/// Check id current toplevel has menu bar
+		/// </summary>
+		public MenuBar MenuBar { get; set; }
+
+		/// <summary>
+		/// Check id current toplevel has status bar
+		/// </summary>
+		public StatusBar StatusBar { get; set; }
+
+		///<inheritdoc cref="ProcessKey"/>
+		public override bool ProcessKey (KeyEvent keyEvent)
+		{
+			if (base.ProcessKey (keyEvent))
+				return true;
+
+			switch (keyEvent.Key) {
+			case Key.ControlQ:
+				// FIXED: stop current execution of this container
+				Application.RequestStop ();
+				break;
+			case Key.ControlZ:
+				Driver.Suspend ();
+				return true;
+
+#if false
+			case Key.F5:
+				Application.DebugDrawBounds = !Application.DebugDrawBounds;
+				SetNeedsDisplay ();
+				return true;
+#endif
+			case Key.Tab:
+			case Key.CursorRight:
+			case Key.CursorDown:
+			case Key.ControlI: // Unix
+				var old = Focused;
+				if (!FocusNext ())
+					FocusNext ();
+				if (old != Focused) {
+					old?.SetNeedsDisplay ();
+					Focused?.SetNeedsDisplay ();
+				}
+				return true;
+			case Key.CursorLeft:
+			case Key.CursorUp:
+			case Key.BackTab:
+				old = Focused;
+				if (!FocusPrev ())
+					FocusPrev ();
+				if (old != Focused) {
+					old?.SetNeedsDisplay ();
+					Focused?.SetNeedsDisplay ();
+				}
+				return true;
+
+			case Key.ControlL:
+				Application.Refresh ();
+				return true;
+			}
+			return false;
+		}
+
+		///<inheritdoc cref="Add"/>
+		public override void Add (View view)
+		{
+			if (this == Application.Top) {
+				if (view is MenuBar)
+					MenuBar = view as MenuBar;
+				if (view is StatusBar)
+					StatusBar = view as StatusBar;
+			}
+			base.Add (view);
+		}
+
+		///<inheritdoc cref="Remove"/>
+		public override void Remove (View view)
+		{
+			if (this == Application.Top) {
+				if (view is MenuBar)
+					MenuBar = null;
+				if (view is StatusBar)
+					StatusBar = null;
+			}
+			base.Remove (view);
+		}
+
+		///<inheritdoc cref="RemoveAll"/>
+		public override void RemoveAll ()
+		{
+			if (this == Application.Top) {
+				MenuBar = null;
+				StatusBar = null;
+			}
+			base.RemoveAll ();
+		}
+
+		internal void EnsureVisibleBounds (Toplevel top, int x, int y, out int nx, out int ny)
+		{
+			nx = Math.Max (x, 0);
+			nx = nx + top.Frame.Width > Driver.Cols ? Math.Max (Driver.Cols - top.Frame.Width, 0) : nx;
+			bool m, s;
+			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
+				m = Application.Top.MenuBar != null;
+			else
+				m = ((Toplevel)SuperView).MenuBar != null;
+			int l = m ? 1 : 0;
+			ny = Math.Max (y, l);
+			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
+				s = Application.Top.StatusBar != null;
+			else
+				s = ((Toplevel)SuperView).StatusBar != null;
+			l = s ? Driver.Rows - 1 : Driver.Rows;
+			ny = Math.Min (ny, l);
+			ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
+		}
+
+		internal void PositionToplevels ()
+		{
+			if (this != Application.Top) {
+				EnsureVisibleBounds (this, Frame.X, Frame.Y, out int nx, out int ny);
+				if ((nx != Frame.X || ny != Frame.Y) && LayoutStyle != LayoutStyle.Computed) {
+					X = nx;
+					Y = ny;
+				}
+			} else {
+				foreach (var top in Subviews) {
+					if (top is Toplevel) {
+						EnsureVisibleBounds ((Toplevel)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) {
+							top.X = nx;
+							top.Y = ny;
+						}
+						if (StatusBar != null) {
+							if (ny + top.Frame.Height > Driver.Rows - 1) {
+								if (top.Height is Dim.DimFill)
+									top.Height = Dim.Fill () - 1;
+							}
+							if (StatusBar.Frame.Y != Driver.Rows - 1) {
+								StatusBar.Y = Driver.Rows - 1;
+								SetNeedsDisplay ();
+							}
+						}
+					}
+				}
+			}
+		}
+
+		///<inheritdoc cref="Redraw"/>
+		public override void Redraw (Rect region)
+		{
+			Application.CurrentView = this;
+
+			if (IsCurrentTop) {
+				if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
+					Driver.SetAttribute (Colors.TopLevel.Normal);
+					Clear (region);
+					Driver.SetAttribute (Colors.Base.Normal);
+				}
+				foreach (var view in Subviews) {
+					if (view.Frame.IntersectsWith (region)) {
+						view.SetNeedsLayout ();
+						view.SetNeedsDisplay (view.Bounds);
+					}
+				}
+
+				ClearNeedsDisplay ();
+			}
+
+			base.Redraw (base.Bounds);
+		}
+
+		/// <summary>
+		/// This method is invoked by Application.Begin as part of the Application.Run after
+		/// the views have been laid out, and before the views are drawn for the first time.
+		/// </summary>
+		public virtual void WillPresent ()
+		{
+			FocusFirst ();
+		}
+	}
+}

+ 252 - 0
Terminal.Gui/Views/Window.cs

@@ -0,0 +1,252 @@
+//
+// Window.cs: Window is a TopLevel View with a frame
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+using System.Collections;
+using NStack;
+
+namespace Terminal.Gui {
+	/// <summary>
+	/// A <see cref="Toplevel"/> <see cref="View"/> that draws a frame around its region and has a "ContentView" subview where the contents are added.
+	/// </summary>
+	public class Window : Toplevel, IEnumerable {
+		View contentView;
+		ustring title;
+
+		/// <summary>
+		/// The title to be displayed for this window.
+		/// </summary>
+		/// <value>The title.</value>
+		public ustring Title {
+			get => title;
+			set {
+				title = value;
+				SetNeedsDisplay ();
+			}
+		}
+
+		class ContentView : View {
+			public ContentView (Rect frame) : base (frame) { }
+			public ContentView () : base () { }
+#if false
+			public override void Redraw (Rect region)
+			{
+				Driver.SetAttribute (ColorScheme.Focus);
+
+				for (int y = 0; y < Frame.Height; y++) {
+					Move (0, y);
+					for (int x = 0; x < Frame.Width; x++) {
+
+						Driver.AddRune ('x');
+					}
+				}
+			}
+#endif
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Gui.Window"/> class with an optional title and a set frame.
+		/// </summary>
+		/// <param name="frame">Frame.</param>
+		/// <param name="title">Title.</param>
+		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
+		{
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Window"/> class with an optional title.
+		/// </summary>
+		/// <param name="title">Title.</param>
+		public Window (ustring title = null) : this (title, padding: 0)
+		{
+		}
+
+		int padding;
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Window"/> with
+		/// the specified frame for its location, with the specified border
+		/// an optional title.
+		/// </summary>
+		/// <param name="frame">Frame.</param>
+		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
+		/// <param name="title">Title.</param>
+		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
+		{
+			this.Title = title;
+			int wb = 2 * (1 + padding);
+			this.padding = padding;
+			var cFrame = new Rect (1 + padding, 1 + padding, frame.Width - wb, frame.Height - wb);
+			contentView = new ContentView (cFrame);
+			base.Add (contentView);
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="Window"/> with
+		/// the specified frame for its location, with the specified border
+		/// an optional title.
+		/// </summary>
+		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
+		/// <param name="title">Title.</param>
+		public Window (ustring title = null, int padding = 0) : base ()
+		{
+			this.Title = title;
+			int wb = 1 + padding;
+			this.padding = padding;
+			contentView = new ContentView () {
+				X = wb,
+				Y = wb,
+				Width = Dim.Fill (wb),
+				Height = Dim.Fill (wb)
+			};
+			base.Add (contentView);
+		}
+
+		/// <summary>
+		/// Enumerates the various <see cref="View"/>s in the embedded <see cref="ContentView"/>.
+		/// </summary>
+		/// <returns>The enumerator.</returns>
+		public new IEnumerator GetEnumerator ()
+		{
+			return contentView.GetEnumerator ();
+		}
+
+		void DrawFrame (bool fill = true)
+		{
+			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: fill);
+		}
+
+		/// <summary>
+		/// Add the specified view to the <see cref="ContentView"/>.
+		/// </summary>
+		/// <param name="view">View to add to the window.</param>
+		public override void Add (View view)
+		{
+			contentView.Add (view);
+			if (view.CanFocus)
+				CanFocus = true;
+		}
+
+
+		/// <summary>
+		///   Removes a widget from this container.
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		public override void Remove (View view)
+		{
+			if (view == null)
+				return;
+
+			SetNeedsDisplay ();
+			var touched = view.Frame;
+			contentView.Remove (view);
+
+			if (contentView.InternalSubviews.Count < 1)
+				this.CanFocus = false;
+		}
+
+		/// <summary>
+		///   Removes all widgets from this container.
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		public override void RemoveAll ()
+		{
+			contentView.RemoveAll ();
+		}
+
+		///<inheritdoc cref="Redraw"/>
+		public override void Redraw (Rect bounds)
+		{
+			Application.CurrentView = this;
+
+			if (NeedDisplay != null && !NeedDisplay.IsEmpty) {
+				DrawFrameWindow ();
+			}
+			contentView.Redraw (contentView.Bounds);
+			ClearNeedsDisplay ();
+			DrawFrameWindow (false);
+
+			void DrawFrameWindow (bool fill = true)
+			{
+				Driver.SetAttribute (ColorScheme.Normal);
+				DrawFrame (fill);
+				if (HasFocus)
+					Driver.SetAttribute (ColorScheme.HotNormal);
+				var width = Frame.Width - (padding + 2) * 2;
+				if (Title != null && width > 4) {
+					Move (1 + padding, padding);
+					Driver.AddRune (' ');
+					var str = Title.Length >= width ? Title [0, width - 2] : Title;
+					Driver.AddStr (str);
+					Driver.AddRune (' ');
+				}
+				Driver.SetAttribute (ColorScheme.Normal);
+			}
+		}
+
+		//
+		// 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 cref="MouseEvent(Gui.MouseEvent)"/>
+		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 ((mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
+				mouseEvent.Flags == MouseFlags.Button3Pressed)) {
+				if (dragPosition.HasValue) {
+					if (SuperView == null) {
+						Application.Top.SetNeedsDisplay (Frame);
+						Application.Top.Redraw (Frame);
+					} else {
+						SuperView.SetNeedsDisplay (Frame);
+					}
+					EnsureVisibleBounds (this, mouseEvent.X + mouseEvent.OfX - start.X,
+						mouseEvent.Y + mouseEvent.OfY, out nx, out ny);
+
+					dragPosition = new Point (nx, ny);
+					Frame = new Rect (nx, ny, Frame.Width, Frame.Height);
+					X = nx;
+					Y = ny;
+					//Demo.ml2.Text = $"{dx},{dy}";
+
+					// FIXED: optimize, only SetNeedsDisplay on the before/after regions.
+					SetNeedsDisplay ();
+					return true;
+				} else {
+					// 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);
+					}
+
+					//Demo.ml2.Text = $"Starting at {dragPosition}";
+					return true;
+				}
+			}
+
+			if (mouseEvent.Flags == MouseFlags.Button1Released && dragPosition.HasValue) {
+				Application.UngrabMouse ();
+				Driver.UncookMouse ();
+				dragPosition = null;
+			}
+
+			//Demo.ml.Text = me.ToString ();
+			return false;
+		}
+
+	}
+}