瀏覽代碼

New layout engine

miguel 7 年之前
父節點
當前提交
f383ffb056

+ 237 - 7
Terminal.Gui/Core.cs

@@ -119,6 +119,24 @@ namespace Terminal.Gui {
 		}
 	}
 
+	/// <summary>
+	/// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the
+	/// value from the Frame will be used, if the value is Computer, then the Frame 
+	/// will be updated from the X, Y Pos objets and the Width and Heigh Dim objects.
+	/// </summary>
+	public enum LayoutStyle {
+		/// <summary>
+		/// The position and size of the view are based on the Frame value.
+		/// </summary>
+		Absolute,
+
+		/// <summary>
+		/// The position and size of the view will be computed based on the
+		/// X, Y, Width and Height properties and set on the Frame.
+		/// </summary>
+		Computed
+	}
+
 	/// <summary>
 	/// View is the base class for all views on the screen and represents a visible element that can render itself and contains zero or more nested views.
 	/// </summary>
@@ -128,8 +146,26 @@ namespace Terminal.Gui {
 	///    can contain one or more subviews, can respond to user input and render themselves on the screen.
 	/// </para>
 	/// <para>
-	///    Views are created with a specified rectangle region (the frame) that is relative to the container
-	///    that they are added into.   
+	///    Views can either be created with an absolute position, by calling the constructor that takes a
+	///    Rect parameter to specify the absolute position and size (the Frame of the View) or by setting the
+	///    X, Y, Width and Height properties on the view.    Both approaches use coordinates that are relative 
+	///    to the container they are being added to.
+	/// </para>
+	/// <para>
+	///    When you do not specify a Rect frame you can use the more flexible 
+	///    Dim and Pos objects that can dynamically update the position of a view.   
+	///    The X and Y properties are of type <see cref="T:Terminal.Gui.Pos"/>
+	///    and you can use either absolute positions, percentages or anchor
+	///    points.   The Width and Height properties are of type 
+	///    <see cref="T:Terminal.Gui.Dim"/> and can use absolute position, 
+	///    percentages and anchors.  These are useful as they will take
+	///    care of repositioning your views if your view's frames are resized
+	///    or if the terminal size changes.
+	/// </para>
+	/// <para>
+	///    When you specify the Rect parameter to a view, you are setting the LayoutStyle to Absolute, and the 
+	///    view will always stay in the position that you placed it.   To change the position change the 
+	///    Frame property to the new position.
 	/// </para>
 	/// <para>
 	///    Subviews can be added to a View by calling the Add method.   The container of a view is the 
@@ -163,6 +199,12 @@ namespace Terminal.Gui {
 	///    the last focused view.   So views should make sure that they place the cursor
 	///    in a visually sensible place.
 	/// </para>
+	/// <para>
+	///    The metnod LayoutSubviews is invoked when the size or layout of a view has
+	///    changed.   The default processing system will keep the size and dimensions
+	///    for views that use the LayoutKind.Absolute, and will recompute the
+	///    frames for the vies that use LayoutKind.Computed.
+	/// </para>
 	/// </remarks>
 	public class View : Responder, IEnumerable {
 		View container = null;
@@ -207,7 +249,7 @@ namespace Terminal.Gui {
 		///    Altering the Frame of a view will trigger the redrawing of the 
 		///    view as well as the redrawing of the affected regions in the superview.
 		/// </remarks>
-		public Rect Frame {
+		public virtual Rect Frame {
 			get => frame;
 			set {
 				if (SuperView != null) {
@@ -216,6 +258,7 @@ namespace Terminal.Gui {
 				}
 				frame = value;
 
+				SetNeedsLayout ();
 				SetNeedsDisplay (frame);
 			}
 		}
@@ -230,6 +273,22 @@ namespace Terminal.Gui {
 				yield return v;
 		}
 
+		LayoutStyle layoutStyle;
+
+		/// <summary>
+		/// Controls how the view's Frame is computed during the LayoutSubviews method, if Absolute, then
+		/// LayoutSubviews does not change the Frame properties, otherwise the Frame is updated from the
+		/// values in X, Y, Width and Height properties.
+		/// </summary>
+		/// <value>The layout style.</value>
+		public LayoutStyle LayoutStyle {
+			get => layoutStyle;
+			set {
+				layoutStyle = value;
+				SetNeedsLayout ();
+			}
+		}
+
 		/// <summary>
 		/// The bounds represent the View-relative rectangle used for this view.   Updates to the Bounds update the Frame, and has the same side effects as updating the frame.
 		/// </summary>
@@ -241,6 +300,61 @@ namespace Terminal.Gui {
 			}
 		}
 
+		Pos x, y;
+		/// <summary>
+		/// Gets or sets the X position for the view (the column).  This is only used when the LayoutStyle is Computed, if the
+		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// </summary>
+		/// <value>The X Position.</value>
+		public Pos X {
+			get => x;
+			set {
+				x = value;
+				SetNeedsLayout ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the Y position for the view (line).  This is only used when the LayoutStyle is Computed, if the
+		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// </summary>
+		/// <value>The y position (line).</value>
+		public Pos Y {
+			get => y;
+			set {
+				y = value;
+				SetNeedsLayout ();
+			}
+		}
+
+		Dim width, height;
+
+		/// <summary>
+		/// Gets or sets the width for the view. This is only used when the LayoutStyle is Computed, if the
+		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// </summary>
+		/// <value>The width.</value>
+		public Dim Width {
+			get => width;
+			set {
+				width = value;
+				SetNeedsLayout ();
+			}
+		}
+
+		/// <summary>
+		/// Gets or sets the height for the view. This is only used when the LayoutStyle is Computed, if the
+		/// LayoutStyle is set to Absolute, this value is ignored.
+		/// </summary>
+		/// <value>The height.</value>
+		public Dim Height {
+			get => height;
+			set {
+				height = value;
+				SetNeedsLayout ();
+			}
+		}
+
 		/// <summary>
 		/// Returns the container for this view, or null if this view has not been added to a container.
 		/// </summary>
@@ -248,13 +362,27 @@ namespace Terminal.Gui {
 		public View SuperView => container;
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="T:Terminal.Gui.View"/> class with the specified frame.   This is the default constructor.
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.View"/> class with the absolute
+		/// dimensions specified in the frame.   If you want to have Views that can be positioned with
+		/// Pos and Dim properties on X, Y, Width and Height, use the empty constructor.
 		/// </summary>
 		/// <param name="frame">The region covered by this view.</param>
 		public View (Rect frame)
 		{
 			this.Frame = frame;
 			CanFocus = false;
+			LayoutStyle = LayoutStyle.Absolute;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.View"/> class and sets the
+		/// view up for Computed layout, which will use the values in X, Y, Width and Height to 
+		/// compute the View's Frame.
+		/// </summary>
+		public View ()
+		{
+			CanFocus = false;
+			LayoutStyle = LayoutStyle.Computed;
 		}
 
 		/// <summary>
@@ -266,6 +394,18 @@ namespace Terminal.Gui {
 			SetNeedsDisplay (Bounds);
 		}
 
+		bool layoutNeeded = true;
+
+		internal void SetNeedsLayout ()
+		{
+			if (layoutNeeded)
+				return;
+			layoutNeeded = true;
+			if (SuperView == null)
+				return;
+			SuperView.layoutNeeded = true;
+		}
+
 		/// <summary>
 		/// Flags the specified rectangle region on this view as needing to be repainted.
 		/// </summary>
@@ -864,6 +1004,43 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		/// <summary>
+		/// Computes the RelativeLayout for the view, given the frame for its container.
+		/// </summary>
+		/// <param name="hostFrame">The Frame for the host.</param>
+		internal void RelativeLayout (Rect hostFrame)
+		{
+			int w, h, _x, _y;
+			if (width == null)
+				w = hostFrame.Width;
+			else
+				w = width.Anchor (hostFrame.Width);
+
+			if (x == null)
+				_x = 0;
+			else {
+				if (x is Pos.PosCenter)
+					_x = x.Anchor (hostFrame.Width - w);
+				else
+					_x = x.Anchor (hostFrame.Width);
+			}
+			if (height == null)
+				h = hostFrame.Height;
+			else
+				h = height.Anchor (hostFrame.Height);
+
+			if (y == null)
+				_y = 0;
+			else {
+				if (y is Pos.PosCenter)
+					_y = y.Anchor (hostFrame.Height - h);
+				else
+					_y = y.Anchor (hostFrame.Height);
+			}
+
+			Frame = new Rect (_x, _y, w, h);
+		}
+
 		/// <summary>
 		/// This virtual method is invoked when a view starts executing or 
 		/// when the dimensions of the view have changed, for example in 
@@ -871,6 +1048,17 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void LayoutSubviews ()
 		{
+			if (!layoutNeeded)
+				return;
+			
+			foreach (var v in Subviews) {
+				if (v.LayoutStyle == LayoutStyle.Absolute)
+					continue;
+
+				v.RelativeLayout (Frame);
+				v.layoutNeeded = false;
+			}
+			layoutNeeded = false;
 		}
 
 		/// <summary>
@@ -908,7 +1096,7 @@ namespace Terminal.Gui {
 		public bool Running;
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class.
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class with the specified absolute layout.
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		public Toplevel (Rect frame) : base (frame)
@@ -916,6 +1104,16 @@ namespace Terminal.Gui {
 			ColorScheme = Colors.Base;
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Toplevel"/> class with Computed layout, defaulting to <see langword="async"/> full screen.
+		/// </summary>
+		public Toplevel () : base ()
+		{
+			ColorScheme = Colors.Base;
+			Width = Dim.Fill ();
+			Height = Dim.Fill ();
+		}
+
 		/// <summary>
 		/// Convenience factory method that creates a new toplevel with the current terminal dimensions.
 		/// </summary>
@@ -1000,10 +1198,11 @@ namespace Terminal.Gui {
 
 		class ContentView : View {
 			public ContentView (Rect frame) : base (frame) { }
+			public ContentView () : base () { }
 		}
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Gui.Window"/> class with an optioanl title
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Gui.Window"/> class with an optional title and a set frame.
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
@@ -1011,6 +1210,14 @@ namespace Terminal.Gui {
 		{
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.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="T:Terminal.Gui.Window"/> with
@@ -1030,6 +1237,27 @@ namespace Terminal.Gui {
 			base.Add (contentView);
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.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 = 2 * (1 + padding);
+			this.padding = padding;
+			contentView = new ContentView () {
+				X = 1 + padding,
+				Y = 1 + padding,
+				Width = Dim.Fill (wb),
+				Height = Dim.Fill (wb)
+			};
+			base.Add (contentView);
+		}
+
 		/// <summary>
 		/// Enumerates the various views in the ContentView.
 		/// </summary>
@@ -1295,7 +1523,7 @@ namespace Terminal.Gui {
 			internal Toplevel Toplevel;
 
 			/// <summary>
-			/// Releases all resource used by the <see cref="T:Terminal.Gui.Application.RunState"/> object.
+			/// Releases alTop = l resource used by the <see cref="T:Terminal.Gui.Application.RunState"/> object.
 			/// </summary>
 			/// <remarks>Call <see cref="Dispose()"/> when you are finished using the <see cref="T:Terminal.Gui.Application.RunState"/>. The
 			/// <see cref="Dispose()"/> method leaves the <see cref="T:Terminal.Gui.Application.RunState"/> in an unusable state. After
@@ -1448,6 +1676,8 @@ namespace Terminal.Gui {
 			toplevels.Push (toplevel);
 			Current = toplevel;
 			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessMouseEvent);
+			if (toplevel.LayoutStyle == LayoutStyle.Computed)
+				toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			toplevel.LayoutSubviews ();
 			toplevel.FocusFirst ();
 			Redraw (toplevel);

+ 5 - 1
Terminal.Gui/Dialogs/Dialog.cs

@@ -25,8 +25,12 @@ namespace Terminal.Gui {
 		/// <param name="width">Width for the dialog.</param>
 		/// <param name="height">Height for the dialog.</param>
 		/// <param name="buttons">Optional buttons to lay out at the bottom of the dialog.</param>
-		public Dialog (ustring title, int width, int height, params Button [] buttons) : base (Application.MakeCenteredRect (new Size (width, height)), title, padding: padding)
+		public Dialog (ustring title, int width, int height, params Button [] buttons) : base (title, padding: padding)
 		{
+			X = Pos.Center ();
+			Y = Pos.Center ();
+			Width = width;
+			Height = height;
 			ColorScheme = Colors.Dialog;
 
 			if (buttons != null) {

+ 12 - 10
Terminal.Gui/MonoCurses/mainloop.cs

@@ -388,16 +388,18 @@ namespace Mono.Terminal {
 				RunTimers ();
 
 			if (useUnix) {
-				foreach (var p in pollmap) {
-					Watch watch;
-
-					if (p.revents == 0)
-						continue;
-
-					if (!descriptorWatchers.TryGetValue (p.fd, out watch))
-						continue;
-					if (!watch.Callback (this))
-						descriptorWatchers.Remove (p.fd);
+				if (pollmap != null) {
+					foreach (var p in pollmap) {
+						Watch watch;
+
+						if (p.revents == 0)
+							continue;
+
+						if (!descriptorWatchers.TryGetValue (p.fd, out watch))
+							continue;
+						if (!watch.Callback (this))
+							descriptorWatchers.Remove (p.fd);
+					}
 				}
 			} else {
 				if (windowsKeyResult.HasValue) {

+ 351 - 0
Terminal.Gui/Types/PosDim.cs

@@ -0,0 +1,351 @@
+//
+// PosDim.cs: Pos and Dim objects for view dimensions.
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+
+using System;
+namespace Terminal.Gui {
+	/// <summary>
+	/// Describes a position which can be an absolute value, a percentage, centered, or 
+	/// relative to the ending dimension.   Integer values are implicitly convertible to
+	/// an absolute Pos.    These objects are created using the static methods Percent,
+	/// AnchorEnd and Center.   The Pos objects can be combined with the addition and 
+	/// subtraction operators.
+	/// </summary>
+	/// <remarks>
+	///   <para>
+	///     Use the Pos objects on the X or Y properties of a view to control the position.
+	///   </para>
+	///   <para>
+	///     These can be used to set the absolute position, when merely assigning an
+	///     integer value (via the implicit integer to Pos conversion), and they can be combined
+	///     to produce more useful layouts, like: Pos.Center - 3, which would shift the postion
+	///     of the view 3 characters to the left after centering for example.
+	///   </para>
+	/// </remarks>
+	public class Pos {
+		internal virtual int Anchor (int width)
+		{
+			return 0;
+		}
+
+		class PosFactor : Pos {
+			float factor;
+
+			public PosFactor (float n)
+			{
+				this.factor = n;
+			}
+
+			internal override int Anchor (int width)
+			{
+				return (int)(width * factor);
+			}
+
+			public override string ToString ()
+			{
+				return $"Pos.Factor({factor})";
+			}
+		}
+
+		/// <summary>
+		/// Creates a percentage Pos object
+		/// </summary>
+		/// <returns>The percent Pos object.</returns>
+		/// <param name="n">A value between 0 and 100 representing the percentage.</param>
+		public static Pos Percent (float n)
+		{
+			if (n < 0 || n > 100)
+				throw new ArgumentException ("Percent value must be between 0 and 100");
+
+			return new PosFactor (n / 100);
+		}
+
+		static PosAnchorEnd endNoMargin;
+
+		class PosAnchorEnd : Pos {
+			int n;
+
+			public PosAnchorEnd (int n)
+			{
+				this.n = n;
+			}
+
+			internal override int Anchor (int width)
+			{
+				return width - n;
+			}
+
+			public override string ToString ()
+			{
+				return $"Pos.AnchorEnd(margin={n})";
+			}
+		}
+
+		/// <summary>
+		/// Creates a Pos object that is anchored to the end of the dimension, useful to flush 
+		/// the layout from the end.
+		/// </summary>
+		/// <returns>The Pos object anchored to the end (the bottom or the right side).</returns>
+		/// <param name="margin">Optional margin to set aside.</param>
+		public static Pos AnchorEnd (int margin = 0)
+		{
+			if (margin < 0)
+				throw new ArgumentException ("Margin must be positive");
+
+			if (margin == 0) {
+				if (endNoMargin == null)
+					endNoMargin = new PosAnchorEnd (0);
+				return endNoMargin;
+			}
+			return new PosAnchorEnd (margin);
+		}
+
+		internal class PosCenter : Pos {
+			internal override int Anchor (int width)
+			{
+				return width / 2;
+			}
+
+			public override string ToString ()
+			{
+				return "Pos.Center";
+			}
+		}
+
+		static PosCenter pCenter;
+
+		/// <summary>
+		/// Returns a Pos object that can be used to center the views.
+		/// </summary>
+		/// <returns>The center Pos.</returns>
+		public static Pos Center ()
+		{
+			if (pCenter == null)
+				pCenter = new PosCenter ();
+			return pCenter;
+		}
+
+		class PosAbsolute : Pos {
+			int n;
+			public PosAbsolute (int n) { this.n = n; }
+
+			public override string ToString ()
+			{
+				return $"Pos.Absolute({n})";
+			}
+
+			internal override int Anchor (int width)
+			{
+				return n;
+			}
+		}
+
+		/// <summary>
+		/// Creates an Absolute Pos from the specified integer value.
+		/// </summary>
+		/// <returns>The Absolute Pos.</returns>
+		/// <param name="n">The value to convert to the pos.</param>
+		public static implicit operator Pos (int n)
+		{
+			return new PosAbsolute (n);
+		}
+
+		class PosCombine : Pos {
+			Pos left, right;
+			bool add;
+			public PosCombine (bool add, Pos left, Pos right)
+			{
+				this.left = left;
+				this.right = right;
+				this.add = add;
+			}
+
+			internal override int Anchor (int width)
+			{
+				var la = left.Anchor (width);
+				var ra = right.Anchor (width);
+				if (add)
+					return la + ra;
+				else
+					return la - ra;
+			}
+		}
+
+		/// <summary>
+		/// Adds a <see cref="Terminal.Gui.Pos"/> to a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="T:Terminal.Gui.Pos"/>.
+		/// </summary>
+		/// <param name="left">The first <see cref="Terminal.Gui.Pos"/> to add.</param>
+		/// <param name="right">The second <see cref="Terminal.Gui.Pos"/> to add.</param>
+		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the sum of the values of <c>left</c> and <c>right</c>.</returns>
+		public static Pos operator + (Pos left, Pos right)
+		{
+			return new PosCombine (true, left, right);
+		}
+
+		/// <summary>
+		/// Subtracts a <see cref="Terminal.Gui.Pos"/> from a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="T:Terminal.Gui.Pos"/>.
+		/// </summary>
+		/// <param name="left">The <see cref="Terminal.Gui.Pos"/> to subtract from (the minuend).</param>
+		/// <param name="right">The <see cref="Terminal.Gui.Pos"/> to subtract (the subtrahend).</param>
+		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the <c>left</c> minus <c>right</c>.</returns>
+		public static Pos operator - (Pos left, Pos right)
+		{
+			return new PosCombine (false, left, right);
+		}
+	}
+
+	/// <summary>
+	/// </summary>
+	/// <remarks>
+	///   <para>
+	///     Use the Dim objects on the Width or Height properties of a view to control the position.
+	///   </para>
+	///   <para>
+	///     These can be used to set the absolute position, when merely assigning an
+	///     integer value (via the implicit integer to Pos conversion), and they can be combined
+	///     to produce more useful layouts, like: Pos.Center - 3, which would shift the postion
+	///     of the view 3 characters to the left after centering for example.
+	///   </para>
+	/// </remarks>
+	public class Dim {
+		internal virtual int Anchor (int width)
+		{
+			return 0;
+		}
+
+		class DimFactor : Dim {
+			float factor;
+
+			public DimFactor (float n)
+			{
+				this.factor = n;
+			}
+
+			internal override int Anchor (int width)
+			{
+				return (int)(width * factor);
+			}
+
+			public override string ToString ()
+			{
+				return $"Dim.Factor({factor})";
+			}
+		}
+
+		/// <summary>
+		/// Creates a percentage Dim object
+		/// </summary>
+		/// <returns>The percent Dim object.</returns>
+		/// <param name="n">A value between 0 and 100 representing the percentage.</param>
+		public static Dim Percent (float n)
+		{
+			if (n < 0 || n > 100)
+				throw new ArgumentException ("Percent value must be between 0 and 100");
+
+			return new DimFactor (n / 100);
+		}
+
+		class DimAbsolute : Dim {
+			int n;
+			public DimAbsolute (int n) { this.n = n; }
+
+			public override string ToString ()
+			{
+				return $"Dim.Absolute({n})";
+			}
+
+			internal override int Anchor (int width)
+			{
+				return n;
+			}
+		}
+
+		class DimFill : Dim {
+			int margin;
+			public DimFill (int margin) { this.margin = margin; }
+
+			public override string ToString ()
+			{
+				return $"Dim.Fill(margin={margin})";
+			}
+
+			internal override int Anchor (int width)
+			{
+				return width-margin;
+			}			
+		}
+
+		static DimFill zeroMargin;
+
+		/// <summary>
+		/// Creates a Dim object that fills the dimension, but leaves the specified number of colums for a margin.
+		/// </summary>
+		/// <returns>The Fill dimension.</returns>
+		/// <param name="margin">Margin to use.</param>
+		public static Dim Fill (int margin = 0)
+		{
+			if (margin == 0) {
+				if (zeroMargin == null)
+					zeroMargin = new DimFill (0);
+				return zeroMargin;
+			}
+			return new DimFill (margin);
+		}
+
+		/// <summary>
+		/// Creates an Absolute Pos from the specified integer value.
+		/// </summary>
+		/// <returns>The Absolute Pos.</returns>
+		/// <param name="n">The value to convert to the pos.</param>
+		public static implicit operator Dim (int n)
+		{
+			return new DimAbsolute (n);
+		}
+
+		class DimCombine : Dim {
+			Dim left, right;
+			bool add;
+			public DimCombine (bool add, Dim left, Dim right)
+			{
+				this.left = left;
+				this.right = right;
+				this.add = add;
+			}
+
+			internal override int Anchor (int width)
+			{
+				var la = left.Anchor (width);
+				var ra = right.Anchor (width);
+				if (add)
+					return la + ra;
+				else
+					return la - ra;
+			}
+		}
+
+		/// <summary>
+		/// Adds a <see cref="Terminal.Gui.Pos"/> to a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="T:Terminal.Gui.Pos"/>.
+		/// </summary>
+		/// <param name="left">The first <see cref="Terminal.Gui.Pos"/> to add.</param>
+		/// <param name="right">The second <see cref="Terminal.Gui.Pos"/> to add.</param>
+		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the sum of the values of <c>left</c> and <c>right</c>.</returns>
+		public static Dim operator + (Dim left, Dim right)
+		{
+			return new DimCombine (true, left, right);
+		}
+
+		/// <summary>
+		/// Subtracts a <see cref="Terminal.Gui.Pos"/> from a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="T:Terminal.Gui.Pos"/>.
+		/// </summary>
+		/// <param name="left">The <see cref="Terminal.Gui.Pos"/> to subtract from (the minuend).</param>
+		/// <param name="right">The <see cref="Terminal.Gui.Pos"/> to subtract (the subtrahend).</param>
+		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the <c>left</c> minus <c>right</c>.</returns>
+		public static Dim operator - (Dim left, Dim right)
+		{
+			return new DimCombine (false, left, right);
+		}
+	}
+}

+ 6 - 13
Terminal.Gui/Views/Button.cs

@@ -62,20 +62,13 @@ namespace Terminal.Gui {
 		///   text length.   This button is not a default button.
 		/// </remarks>
 		/// <param name="text">The button's text</param>
-		public Button (ustring text) : this (0, 0, text) { }
-
-		/// <summary>
-		///   Public constructor, creates a button based on
-		///   the given text.
-		/// </summary>
-		/// <remarks>
-		///   If the value for is_default is true, a special
-		///   decoration is used, and the enter key on a
-		///   dialog would implicitly activate this button.
-		/// </remarks>
-		/// <param name="text">The button's text</param>
 		/// <param name="is_default">If set, this makes the button the default button in the current view, which means that if the user presses return on a view that does not handle return, it will be treated as if he had clicked on the button</param>
-		public Button (ustring text, bool is_default) : this (0, 0, text, is_default) { }
+		public Button (ustring text, bool is_default = false) : base ()
+		{
+			Text = text;
+			Width = text.Length + 4 + (is_default ? 2 : 0);
+			Height = 1;
+		}
 
 		/// <summary>
 		///   Public constructor, creates a button based on

+ 15 - 1
Terminal.Gui/Views/Checkbox.cs

@@ -27,9 +27,23 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public event EventHandler Toggled;
 
+		/// <summary>
+		/// Public constructor, creates a CheckButton based on the given text, uses Computed layout and sets the height and width.
+		/// </summary>
+		/// <param name="s">S.</param>
+		/// <param name="is_checked">If set to <c>true</c> is checked.</param>
+		public CheckBox (ustring s, bool is_checked = false) : base ()
+		{
+			Checked = is_checked;
+			Text = s;
+			CanFocus = true;
+			Height = 1;
+			Width = s.Length + 4;
+		}
+
 		/// <summary>
 		///   Public constructor, creates a CheckButton based on
-		///   the given text at the given position.
+		///   the given text at an absolute position.
 		/// </summary>
 		/// <remarks>
 		///   The size of CheckButton is computed based on the

+ 20 - 1
Terminal.Gui/Views/FrameView.cs

@@ -31,11 +31,12 @@ namespace Terminal.Gui {
 
 		class ContentView : View {
 			public ContentView (Rect frame) : base (frame) { }
+			public ContentView () : base () { }
 		}
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Gui.FrameView"/> class with
-		/// a title.
+		/// an absolute position and a title.
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
@@ -47,6 +48,24 @@ namespace Terminal.Gui {
 			Title = title;
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Gui.FrameView"/> class with
+		/// a title and the result is suitable to have its X, Y, Width and Height properties computed.
+		/// </summary>
+		/// <param name="title">Title.</param>
+		public FrameView (ustring title)
+		{
+			contentView = new ContentView () {
+				X = 1,
+				Y = 1,
+				Width = Dim.Fill (2),
+				Height = Dim.Fill (2)
+			};
+			base.Add (contentView);
+			Title = title;
+		}
+
+
 		void DrawFrame ()
 		{
 			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), 0, fill: true);

+ 12 - 0
Terminal.Gui/Views/Label.cs

@@ -80,6 +80,18 @@ namespace Terminal.Gui {
 			this.text = text;
 		}
 
+		/// <summary>
+		/// Public constructor: creates a label and configures the default Width and Height based on the text, the result is suitable for Computed layout.
+		/// </summary>
+		/// <param name="text">Text.</param>
+		public Label (ustring text) : base ()
+		{
+			this.text = text;
+			var r = CalcRect (0, 0, text);
+			Width = r.Width;
+			Height = r.Height;
+		}
+
 		static char [] whitespace = new char [] { ' ', '\t' };
 
 		static ustring ClipAndJustify (ustring str, int width, TextAlignment talign)

+ 24 - 2
Terminal.Gui/Views/ListView.cs

@@ -233,7 +233,29 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Initializes a new ListView that will display the contents of the object implementing the IList interface.
+		/// Initializes a new ListView that will display the contents of the object implementing the IList interface, with relative positioning
+		/// </summary>
+		/// <param name="source">An IList data source, if the elements of the IList are strings or ustrings, the string is rendered, otherwise the ToString() method is invoked on the result.</param>
+		public ListView (IList source) : this (MakeWrapper (source))
+		{
+			((ListWrapper)(Source)).Container = this;
+			((ListWrapper)(Source)).Driver = Driver;
+		}
+
+		/// <summary>
+		/// Initializes a new ListView that will display the provided data source, uses relative positioning.
+		/// </summary>
+		/// <param name="source">IListDataSource object that provides a mechanism to render the data. The number of elements on the collection should not change, if you must change, set the "Source" property to reset the internal settings of the ListView.</param>
+		public ListView (IListDataSource source) : base ()
+		{
+			if (source == null)
+				throw new ArgumentNullException (nameof (source));
+			Source = source;
+			CanFocus = true;
+		}
+
+		/// <summary>
+		/// Initializes a new ListView that will display the contents of the object implementing the IList interface with an absolute position.
 		/// </summary>
 		/// <param name="rect">Frame for the listview.</param>
 		/// <param name="source">An IList data source, if the elements of the IList are strings or ustrings, the string is rendered, otherwise the ToString() method is invoked on the result.</param>
@@ -244,7 +266,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Initializes a new ListView that will display the provided data source.
+		/// Initializes a new ListView that will display the provided data source  with an absolute position
 		/// </summary>
 		/// <param name="rect">Frame for the listview.</param>
 		/// <param name="source">IListDataSource object that provides a mechanism to render the data. The number of elements on the collection should not change, if you must change, set the "Source" property to reset the internal settings of the ListView.</param>

+ 5 - 1
Terminal.Gui/Views/Menu.cs

@@ -275,8 +275,12 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.MenuBar"/> class with the specified set of toplevel menu items.
 		/// </summary>
 		/// <param name="menus">Menus.</param>
-		public MenuBar (MenuBarItem [] menus) : base (new Rect (0, 0, Application.Driver.Cols, 1))
+		public MenuBar (MenuBarItem [] menus) : base ()
 		{
+			X = 0;
+			Y = 0;
+			Width = Dim.Fill ();
+			Height = 1;
 			Menus = menus;
 			CanFocus = false;
 			selected = -1;

+ 10 - 1
Terminal.Gui/Views/ProgressBar.cs

@@ -20,7 +20,7 @@ namespace Terminal.Gui {
 		int activityPos, delta;
 
 		/// <summary>
-		/// Initializes a new instance of the <see cref="T:Terminal.Gui.ProgressBar"/> class, starts in percentage mode.
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.ProgressBar"/> class, starts in percentage mode with an absolute position and size.
 		/// </summary>
 		/// <param name="rect">Rect.</param>
 		public ProgressBar (Rect rect) : base (rect)
@@ -29,6 +29,15 @@ namespace Terminal.Gui {
 			fraction = 0;
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.ProgressBar"/> class, starts in percentage mode and uses relative layout.
+		/// </summary>
+		public ProgressBar () : base ()
+		{
+			CanFocus = false;
+			fraction = 0;
+		}
+
 		float fraction;
 
 		/// <summary>

+ 19 - 1
Terminal.Gui/Views/RadioGroup.cs

@@ -8,7 +8,8 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.RadioGroup"/> class
-		/// setting up the initial set of radio labels and the item that should be selected.
+		/// setting up the initial set of radio labels and the item that should be selected and uses
+		/// an absolute layout for the result.
 		/// </summary>
 		/// <param name="rect">Boundaries for the radio group.</param>
 		/// <param name="radioLabels">Radio labels, the strings can contain hotkeys using an undermine before the letter.</param>
@@ -20,6 +21,23 @@ namespace Terminal.Gui {
 			CanFocus = true;
 		}
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.RadioGroup"/> class
+		/// setting up the initial set of radio labels and the item that should be selected.
+		/// </summary>
+		/// <param name="radioLabels">Radio labels, the strings can contain hotkeys using an undermine before the letter.</param>
+		/// <param name="selected">The item to be selected, the value is clamped to the number of items.</param>
+		public RadioGroup (string [] radioLabels, int selected = 0) : base ()
+		{
+			var r = MakeRect (0, 0, radioLabels);
+			Width = r.Width;
+			Height = radioLabels.Length;
+
+			this.selected = selected;
+			this.radioLabels = radioLabels;
+			CanFocus = true;
+		}
+
 		static Rect MakeRect (int x, int y, string [] radioLabels)
 		{
 			int width = 0;

+ 33 - 8
Terminal.Gui/Views/TextField.cs

@@ -33,21 +33,46 @@ namespace Terminal.Gui {
 		public event EventHandler Changed;
 
 		/// <summary>
-		///   Public constructor.
+		///    Public constructor that creates a text field, with layout controlled with X, Y, Width and Height.
 		/// </summary>
-		/// <remarks>
-		/// </remarks>
-		public TextField (int x, int y, int w, string s) : base (new Rect (x, y, w, 1))
+		/// <param name="text">Initial text contents.</param>
+		public TextField (ustring text)
 		{
-			if (s == null)
-				s = "";
+			if (text == null)
+				text = "";
 
-			text = s;
-			point = s.Length;
+			this.text = text;
+			point = text.Length;
+			CanFocus = true;
+		}
+
+		/// <summary>
+		///    Public constructor that creates a text field at an absolute position and size.
+		/// </summary>
+		/// <param name="x">The x coordinate.</param>
+		/// <param name="y">The y coordinate.</param>
+		/// <param name="w">The width.</param>
+		/// <param name="text">Initial text contents.</param>
+		public TextField (int x, int y, int w, ustring text) : base (new Rect (x, y, w, 1))
+		{
+			if (text == null)
+				text = "";
+
+			this.text = text;
+			point = text.Length;
 			first = point > w ? point - w : 0;
 			CanFocus = true;
 		}
 
+		public override Rect Frame {
+			get => base.Frame;
+			set {
+				base.Frame = value;
+				var w = base.Frame.Width;
+				first = point > w ? point - w : 0;
+			}
+		}
+
 		/// <summary>
 		///   Sets or gets the text in the entry.
 		/// </summary>

+ 9 - 1
Terminal.Gui/Views/TextView.cs

@@ -276,7 +276,7 @@ namespace Terminal.Gui {
 		public event EventHandler Changed;
 #endif
 		/// <summary>
-		///   Public constructor, creates a view on the specfied area
+		///   Public constructor, creates a view on the specified area, with absolute position and size.
 		/// </summary>
 		/// <remarks>
 		/// </remarks>
@@ -285,6 +285,14 @@ namespace Terminal.Gui {
 			CanFocus = true;
 		}
 
+		/// <summary>
+		/// Public constructor, creates a view on the specified area, with dimensions controlled with the X, Y, Width and Height properties.
+		/// </summary>
+		public TextView () : base ()
+		{
+			CanFocus = true;
+		}
+
 		void ResetPosition ()
 		{
 			topRow = leftColumn = currentRow = currentColumn = 0;