Browse Source

Merge branch 'master' of tig:migueldeicaza/gui.cs

Charlie Kindel 4 years ago
parent
commit
214d7d5504

+ 1 - 1
README.md

@@ -71,7 +71,7 @@ To enter the key `ESC`, you can either press `ESC` and wait 100 milliseconds, or
 
 ### Driver model
 
-**Terminal.Gui** has support for [ncurses](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/CursesDriver.cs), [`System.Console`](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/NetDriver.cs), and a full [Win32 Console](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/WindowsDriver.cs) front-end.
+**Terminal.Gui** has support for [ncurses](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs), [`System.Console`](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/ConsoleDrivers/NetDriver.cs), and a full [Win32 Console](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs) front-end.
 
 `ncurses` is used on Mac/Linux/Unix with color support based on what your library is compiled with; the Windows driver supports full color and mouse, and an easy-to-debug `System.Console` can be used on Windows and Unix, but lacks mouse support.
 

+ 17 - 5
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -255,10 +255,18 @@ namespace Terminal.Gui {
 			case 91:
 				ConsoleKeyInfo [] cki = new ConsoleKeyInfo [] { consoleKeyInfo };
 				ConsoleModifiers mod = consoleKeyInfo.Modifiers;
-				while (Console.KeyAvailable) {
-					var result = Console.ReadKey (true);
-					Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1);
-					cki [cki.Length - 1] = result;
+				int delay = 0;
+				while (delay < 100) {
+					if (Console.KeyAvailable) {
+						do {
+							var result = Console.ReadKey (true);
+							Array.Resize (ref cki, cki == null ? 1 : cki.Length + 1);
+							cki [cki.Length - 1] = result;
+						} while (Console.KeyAvailable);
+						break;
+					}
+					Thread.Sleep (50);
+					delay += 50;
 				}
 				SplitCSI (cki, ref inputResult, ref newConsoleKeyInfo, ref key, ref mouseEvent, ref mod);
 				return;
@@ -744,7 +752,8 @@ namespace Terminal.Gui {
 				MouseEvent = mouseEvent
 			});
 
-			if (!isButtonClicked && lastMouseEvent.Position != default && lastMouseEvent.Position == point
+			if (!isButtonClicked && !lastMouseEvent.ButtonState.HasFlag (MouseButtonState.ReportMousePosition)
+				&& lastMouseEvent.Position != default && lastMouseEvent.Position == point
 				&& ((buttonState & MouseButtonState.Button1Released) != 0
 				|| (buttonState & MouseButtonState.Button2Released) != 0
 				|| (buttonState & MouseButtonState.Button3Released) != 0)) {
@@ -960,6 +969,9 @@ namespace Terminal.Gui {
 			case '1':
 				key = ConsoleKey.F10;
 				break;
+			case '2':
+				key = ConsoleKey.Insert;
+				break;
 			case '3':
 				if (length == 5) {
 					key = ConsoleKey.F11;

+ 3 - 0
Terminal.Gui/Core/Application.cs

@@ -760,6 +760,9 @@ 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;
 			Resized?.Invoke (new ResizedEventArgs () { Cols = full.Width, Rows = full.Height });
 			Driver.Clip = full;
 			foreach (var t in toplevels) {

+ 1 - 1
Terminal.Gui/Core/ConsoleDriver.cs

@@ -124,7 +124,7 @@ namespace Terminal.Gui {
 		/// <param name="background">Background</param>
 		public Attribute (Color foreground = new Color (), Color background = new Color ())
 		{
-			Value = (int)foreground | ((int)background << 4);
+			Value = Make (foreground, background).Value;
 			Foreground = foreground;
 			Background = background;
 		}

+ 5 - 5
Terminal.Gui/Core/PosDim.cs

@@ -36,7 +36,7 @@ namespace Terminal.Gui {
 			return 0;
 		}
 
-		class PosFactor : Pos {
+		internal class PosFactor : Pos {
 			float factor;
 
 			public PosFactor (float n)
@@ -82,7 +82,7 @@ namespace Terminal.Gui {
 
 		static PosAnchorEnd endNoMargin;
 
-		class PosAnchorEnd : Pos {
+		internal class PosAnchorEnd : Pos {
 			int n;
 
 			public PosAnchorEnd (int n)
@@ -205,8 +205,8 @@ namespace Terminal.Gui {
 			return new PosAbsolute (n);
 		}
 
-		class PosCombine : Pos {
-			Pos left, right;
+		internal class PosCombine : Pos {
+			internal Pos left, right;
 			bool add;
 			public PosCombine (bool add, Pos left, Pos right)
 			{
@@ -500,7 +500,7 @@ namespace Terminal.Gui {
 		}
 
 		internal class DimCombine : Dim {
-			Dim left, right;
+			internal Dim left, right;
 			bool add;
 			public DimCombine (bool add, Dim left, Dim right)
 			{

+ 2 - 1
Terminal.Gui/Core/TextFormatter.cs

@@ -625,7 +625,8 @@ namespace Terminal.Gui {
 						Application.Driver?.AddRune (rune);
 					}
 					col += Rune.ColumnWidth (rune);
-					if (idx + 1 < runes.Length && col + Rune.ColumnWidth (runes [idx + 1]) > bounds.Width) {
+					if (idx + 1 > - 1 && idx + 1 < runes.Length && col
+						+ Rune.ColumnWidth (runes [idx + 1]) > bounds.Width) {
 						break;
 					}
 				}

+ 136 - 60
Terminal.Gui/Core/View.cs

@@ -8,7 +8,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;
@@ -47,9 +47,9 @@ namespace Terminal.Gui {
 	/// </para>
 	/// <para>
 	///    Views supports two layout styles: Absolute or Computed. The choice as to which layout style is used by the View 
-	///    is determined when the View is initizlied. To create a View using Absolute layout, call a constructor that takes a
+	///    is determined when the View is initialized. To create a View using Absolute layout, call a constructor that takes a
 	///    Rect parameter to specify the absolute position and size (the <c>View.<see cref="Frame "/></c>)/. To create a View 
-	///    using Computed layout use a constructor that does not take a Rect parametr and set the X, Y, Width and Height 
+	///    using Computed layout use a constructor that does not take a Rect parameter and set 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>
@@ -69,7 +69,7 @@ namespace Terminal.Gui {
 	/// </para>
 	/// <para>
 	///    Absolute layout requires specifying coordinates and sizes of Views explicitly, and the
-	///    View will typcialy stay in a fixed position and size. To change the position and size use the
+	///    View will typically stay in a fixed position and size. To change the position and size use the
 	///    <see cref="Frame"/> property.
 	/// </para>
 	/// <para>
@@ -463,7 +463,7 @@ namespace Terminal.Gui {
 		Pos x, y;
 
 		/// <summary>
-		/// Gets or sets the X position for the view (the column). Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
+		/// Gets or sets the X position for the view (the column). Only used the <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The X Position.</value>
 		/// <remarks>
@@ -483,7 +483,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets or sets the Y position for the view (the row). Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
+		/// Gets or sets the Y position for the view (the row). Only used the <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The y position (line).</value>
 		/// <remarks>
@@ -505,7 +505,7 @@ namespace Terminal.Gui {
 		Dim width, height;
 
 		/// <summary>
-		/// Gets or sets the width of the view. Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
+		/// Gets or sets the width of the view. Only used the <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The width.</value>
 		/// <remarks>
@@ -525,7 +525,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets or sets the height of the view. Only used whe <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
+		/// Gets or sets the height of the view. Only used the <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Computed"/>.
 		/// </summary>
 		/// <value>The height.</value>
 		/// If <see cref="LayoutStyle"/> is <see cref="LayoutStyle.Absolute"/> changing this property has no effect and its value is indeterminate. 
@@ -567,18 +567,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="frame">The region covered by this view.</param>
 		/// <remarks>
-		/// This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use <see cref="View()"/> to 
+		/// This constructor initialize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Absolute"/>. Use <see cref="View()"/> to 
 		/// initialize a View with  <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/> 
 		/// </remarks>
 		public View (Rect frame)
 		{
-			textFormatter = new TextFormatter ();
-			this.Text = ustring.Empty;
-
-			shortcutHelper = new ShortcutHelper ();
-
-			this.Frame = frame;
-			LayoutStyle = LayoutStyle.Absolute;
+			Initialize (ustring.Empty, frame, LayoutStyle.Absolute);
 		}
 
 		/// <summary>
@@ -595,13 +589,12 @@ namespace Terminal.Gui {
 		///   If <c>Height</c> is greater than one, word wrapping is provided.
 		/// </para>
 		/// <para>
-		///   This constructor intitalize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
+		///   This constructor initialize a View with a <see cref="LayoutStyle"/> of <see cref="LayoutStyle.Computed"/>. 
 		///   Use <see cref="X"/>, <see cref="Y"/>, <see cref="Width"/>, and <see cref="Height"/> properties to dynamically control the size and location of the view.
 		/// </para>
 		/// </remarks>
 		public View () : this (text: string.Empty) { }
 
-
 		/// <summary>
 		///   Initializes a new instance of <see cref="View"/> using <see cref="LayoutStyle.Absolute"/> layout.
 		/// </summary>
@@ -635,12 +628,9 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="rect">Location.</param>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
-		public View (Rect rect, ustring text) : this (rect)
+		public View (Rect rect, ustring text)
 		{
-			textFormatter = new TextFormatter ();
-			this.Text = text;
-
-			shortcutHelper = new ShortcutHelper ();
+			Initialize (text, rect, LayoutStyle.Absolute);
 		}
 
 		/// <summary>
@@ -658,6 +648,11 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
 		public View (ustring text)
+		{
+			Initialize (text, Rect.Empty);
+		}
+
+		void Initialize (ustring text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed)
 		{
 			textFormatter = new TextFormatter ();
 			Text = text;
@@ -667,13 +662,20 @@ namespace Terminal.Gui {
 			CanFocus = false;
 			TabIndex = -1;
 			TabStop = false;
-			LayoutStyle = LayoutStyle.Computed;
+			LayoutStyle = layoutStyle;
 			// BUGBUG: CalcRect doesn't account for line wrapping
-			var r = TextFormatter.CalcRect (0, 0, text);
-			x = Pos.At (0);
-			y = Pos.At (0);
+			Rect r;
+			if (rect.IsEmpty) {
+				r = TextFormatter.CalcRect (0, 0, text);
+			} else {
+				r = rect;
+			}
+			x = Pos.At (r.X);
+			y = Pos.At (r.Y);
 			Width = r.Width;
 			Height = r.Height;
+
+			Frame = r;
 		}
 
 		/// <summary>
@@ -1022,7 +1024,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Sets the <see cref="ConsoleDriver"/>'s clip region to the current View's <see cref="Bounds"/>.
 		/// </summary>
-		/// <returns>The existing driver's clip region, which can be then re-eapplied by setting <c><see cref="Driver"/>.Clip</c> (<see cref="ConsoleDriver.Clip"/>).</returns>
+		/// <returns>The existing driver's clip region, which can be then re-applied by setting <c><see cref="Driver"/>.Clip</c> (<see cref="ConsoleDriver.Clip"/>).</returns>
 		/// <remarks>
 		/// <see cref="Bounds"/> is View-relative.
 		/// </remarks>
@@ -1084,7 +1086,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Utility function to draw strings that contains a hotkey using a <see cref="ColorScheme"/> and the "focused" state.
 		/// </summary>
-		/// <param name="text">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
+		/// <param name="text">String to display, the underscore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="focused">If set to <c>true</c> this uses the focused colors from the color scheme, otherwise the regular ones.</param>
 		/// <param name="scheme">The color scheme to use.</param>
 		public void DrawHotString (ustring text, bool focused, ColorScheme scheme)
@@ -1751,7 +1753,7 @@ namespace Terminal.Gui {
 			var result = new List<View> ();
 
 			// Set of all nodes with no incoming edges
-			var S = new HashSet<View> (nodes.Where (n => edges.All (e => e.To.Equals (n) == false)));
+			var S = new HashSet<View> (nodes.Where (n => edges.All (e => !e.To.Equals (n))));
 
 			while (S.Any ()) {
 				//  remove a node n from S
@@ -1770,15 +1772,15 @@ namespace Terminal.Gui {
 					edges.Remove (e);
 
 					// if m has no other incoming edges then
-					if (edges.All (me => me.To.Equals (m) == false) && m != this?.SuperView) {
+					if (edges.All (me => !me.To.Equals (m)) && m != this?.SuperView) {
 						// insert m into S
 						S.Add (m);
 					}
 				}
 			}
 
-			if (edges.Any ()) {
-				if (!object.ReferenceEquals (edges.First ().From, edges.First ().To)) {
+			if (edges.Any () && edges.First ().From != Application.Top) {
+				if (!ReferenceEquals (edges.First ().From, edges.First ().To)) {
 					throw new InvalidOperationException ($"TopologicalSort (for Pos/Dim) cannot find {edges.First ().From}. Did you forget to add it to {this}?");
 				} else {
 					throw new InvalidOperationException ("TopologicalSort encountered a recursive cycle in the relative Pos/Dim in the views of " + this);
@@ -1861,20 +1863,60 @@ namespace Terminal.Gui {
 			var nodes = new HashSet<View> ();
 			var edges = new HashSet<(View, View)> ();
 
-			foreach (var v in InternalSubviews) {
-				nodes.Add (v);
-				if (v.LayoutStyle == LayoutStyle.Computed) {
-					if (v.X is Pos.PosView vX)
-						edges.Add ((vX.Target, v));
-					if (v.Y is Pos.PosView vY)
-						edges.Add ((vY.Target, v));
-					if (v.Width is Dim.DimView vWidth)
-						edges.Add ((vWidth.Target, v));
-					if (v.Height is Dim.DimView vHeight)
-						edges.Add ((vHeight.Target, v));
+			void CollectPos (Pos pos, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+			{
+				if (pos is Pos.PosView pv) {
+					if (pv.Target != this) {
+						nEdges.Add ((pv.Target, from));
+					}
+					foreach (var v in from.InternalSubviews) {
+						CollectAll (v, ref nNodes, ref nEdges);
+					}
+					return;
+				}
+				if (pos is Pos.PosCombine pc) {
+					foreach (var v in from.InternalSubviews) {
+						CollectPos (pc.left, from, ref nNodes, ref nEdges);
+						CollectPos (pc.right, from, ref nNodes, ref nEdges);
+					}
+				}
+			}
+
+			void CollectDim (Dim dim, View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+			{
+				if (dim is Dim.DimView dv) {
+					if (dv.Target != this) {
+						nEdges.Add ((dv.Target, from));
+					}
+					foreach (var v in from.InternalSubviews) {
+						CollectAll (v, ref nNodes, ref nEdges);
+					}
+					return;
+				}
+				if (dim is Dim.DimCombine dc) {
+					foreach (var v in from.InternalSubviews) {
+						CollectDim (dc.left, from, ref nNodes, ref nEdges);
+						CollectDim (dc.right, from, ref nNodes, ref nEdges);
+					}
 				}
 			}
 
+			void CollectAll (View from, ref HashSet<View> nNodes, ref HashSet<(View, View)> nEdges)
+			{
+				foreach (var v in from.InternalSubviews) {
+					nNodes.Add (v);
+					if (v.layoutStyle != LayoutStyle.Computed) {
+						continue;
+					}
+					CollectPos (v.X, v, ref nNodes, ref nEdges);
+					CollectPos (v.Y, v, ref nNodes, ref nEdges);
+					CollectDim (v.Width, v, ref nNodes, ref nEdges);
+					CollectDim (v.Height, v, ref nNodes, ref nEdges);
+				}
+			}
+
+			CollectAll (this, ref nodes, ref edges);
+
 			var ordered = TopologicalSort (nodes, edges);
 
 			foreach (var v in ordered) {
@@ -1884,7 +1926,6 @@ namespace Terminal.Gui {
 
 				v.LayoutSubviews ();
 				v.LayoutNeeded = false;
-
 			}
 
 			if (SuperView == Application.Top && LayoutNeeded && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
@@ -2067,7 +2108,6 @@ namespace Terminal.Gui {
 			if (MouseEvent (mouseEvent))
 				return true;
 
-
 			if (mouseEvent.Flags == MouseFlags.Button1Clicked) {
 				if (CanFocus && !HasFocus && SuperView != null) {
 					SuperView.SetFocus (this);
@@ -2148,19 +2188,15 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		/// <summary>
-		/// Calculate the width based on the <see cref="Width"/> settings.
-		/// </summary>
-		/// <param name="desiredWidth">The desired width.</param>
-		/// <param name="resultWidth">The real result width.</param>
-		/// <returns>True if the width can be directly assigned, false otherwise.</returns>
-		public bool SetWidth (int desiredWidth, out int resultWidth)
+		bool CanSetWidth (int desiredWidth, out int resultWidth, out int currentWidth)
 		{
 			int w = desiredWidth;
+			currentWidth = Width != null ? Width.Anchor (0) : 0;
 			bool canSetWidth;
 			if (Width is Dim.DimCombine || Width is Dim.DimView || Width is Dim.DimFill) {
 				// It's a Dim.DimCombine and so can't be assigned. Let it have it's width anchored.
 				w = Width.Anchor (w);
+				currentWidth = Width.Anchor (w);
 				canSetWidth = false;
 			} else if (Width is Dim.DimFactor factor) {
 				// Tries to get the SuperView width otherwise the view width.
@@ -2169,6 +2205,7 @@ namespace Terminal.Gui {
 					sw -= Frame.X;
 				}
 				w = Width.Anchor (sw);
+				currentWidth = Width.Anchor (sw);
 				canSetWidth = false;
 			} else {
 				canSetWidth = true;
@@ -2178,19 +2215,15 @@ namespace Terminal.Gui {
 			return canSetWidth;
 		}
 
-		/// <summary>
-		/// Calculate the height based on the <see cref="Height"/> settings.
-		/// </summary>
-		/// <param name="desiredHeight">The desired height.</param>
-		/// <param name="resultHeight">The real result height.</param>
-		/// <returns>True if the height can be directly assigned, false otherwise.</returns>
-		public bool SetHeight (int desiredHeight, out int resultHeight)
+		bool CanSetHeight (int desiredHeight, out int resultHeight, out int currentHeight)
 		{
 			int h = desiredHeight;
+			currentHeight = Height != null ? Height.Anchor (0) : 0;
 			bool canSetHeight;
 			if (Height is Dim.DimCombine || Height is Dim.DimView || Height is Dim.DimFill) {
 				// It's a Dim.DimCombine and so can't be assigned. Let it have it's height anchored.
 				h = Height.Anchor (h);
+				currentHeight = Height.Anchor (h);
 				canSetHeight = false;
 			} else if (Height is Dim.DimFactor factor) {
 				// Tries to get the SuperView height otherwise the view height.
@@ -2199,6 +2232,7 @@ namespace Terminal.Gui {
 					sh -= Frame.Y;
 				}
 				h = Height.Anchor (sh);
+				currentHeight = Height.Anchor (sh);
 				canSetHeight = false;
 			} else {
 				canSetHeight = true;
@@ -2207,5 +2241,47 @@ namespace Terminal.Gui {
 
 			return canSetHeight;
 		}
+
+		/// <summary>
+		/// Calculate the width based on the <see cref="Width"/> settings.
+		/// </summary>
+		/// <param name="desiredWidth">The desired width.</param>
+		/// <param name="resultWidth">The real result width.</param>
+		/// <returns><c>true</c> if the width can be directly assigned, <c>false</c> otherwise.</returns>
+		public bool SetWidth (int desiredWidth, out int resultWidth)
+		{
+			return CanSetWidth (desiredWidth, out resultWidth, out _);
+		}
+
+		/// <summary>
+		/// Calculate the height based on the <see cref="Height"/> settings.
+		/// </summary>
+		/// <param name="desiredHeight">The desired height.</param>
+		/// <param name="resultHeight">The real result height.</param>
+		/// <returns><c>true</c> if the height can be directly assigned, <c>false</c> otherwise.</returns>
+		public bool SetHeight (int desiredHeight, out int resultHeight)
+		{
+			return CanSetHeight (desiredHeight, out resultHeight, out _);
+		}
+
+		/// <summary>
+		/// Gets the current width based on the <see cref="Width"/> settings.
+		/// </summary>
+		/// <param name="currentWidth">The real current width.</param>
+		/// <returns><c>true</c> if the width can be directly assigned, <c>false</c> otherwise.</returns>
+		public bool GetCurrentWidth (out int currentWidth)
+		{
+			return CanSetWidth (0, out _, out currentWidth);
+		}
+
+		/// <summary>
+		/// Calculate the height based on the <see cref="Height"/> settings.
+		/// </summary>
+		/// <param name="currentHeight">The real current height.</param>
+		/// <returns><c>true</c> if the height can be directly assigned, <c>false</c> otherwise.</returns>
+		public bool GetCurrentHeight (out int currentHeight)
+		{
+			return CanSetHeight (0, out _, out currentHeight);
+		}
 	}
 }

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

@@ -146,10 +146,14 @@ namespace Terminal.Gui {
 				base.Text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket);
 
 			int w = base.Text.RuneCount - (base.Text.Contains (HotKeySpecifier) ? 1 : 0);
-			if (SetWidth (w, out int rWidth)) {
+			GetCurrentWidth (out int cWidth);
+			var canSetWidth = SetWidth (w, out int rWidth);
+			if (canSetWidth && (cWidth < rWidth || AutoSize)) {
 				Width = rWidth;
+				w = rWidth;
+			} else if (!canSetWidth || !AutoSize) {
+				w = cWidth;
 			}
-			w = rWidth;
 			var layout = LayoutStyle;
 			bool layoutChanged = false;
 			if (!(Height is Dim.DimAbsolute)) {

+ 20 - 2
Terminal.Gui/Views/Checkbox.cs

@@ -100,8 +100,11 @@ namespace Terminal.Gui {
 				hot_pos = -1;
 				hot_key = (char)0;
 				foreach (Rune c in text) {
-					if (Rune.IsUpper (c)) {
-						hot_key = c;
+					//if (Rune.IsUpper (c)) {
+					if (c == '_') {
+						hot_key = text [i + 1];
+						HotKey = (Key)(char)hot_key.ToString ().ToUpper () [0];
+						text = text.ToString ().Replace ("_", "");
 						hot_pos = i;
 						break;
 					}
@@ -145,6 +148,21 @@ namespace Terminal.Gui {
 			return base.ProcessKey (kb);
 		}
 
+		///<inheritdoc/>
+		public override bool ProcessHotKey (KeyEvent ke)
+		{
+			if (ke.Key == (Key.AltMask | HotKey)) {
+				SetFocus ();
+				var previousChecked = Checked;
+				Checked = !Checked;
+				OnToggled (previousChecked);
+				SetNeedsDisplay ();
+				return true;
+			}
+
+			return false;
+		}
+
 		///<inheritdoc/>
 		public override bool MouseEvent (MouseEvent me)
 		{

+ 4 - 1
Terminal.Gui/Views/ListView.cs

@@ -401,8 +401,11 @@ namespace Terminal.Gui {
 			case Key.Home:
 				return MoveHome ();
 
+			default:
+				return false;
 			}
-			return base.ProcessKey (kb);
+
+			return true;
 		}
 
 		/// <summary>

+ 101 - 62
Terminal.Gui/Views/TextField.cs

@@ -20,6 +20,8 @@ namespace Terminal.Gui {
 	public class TextField : View {
 		List<Rune> text;
 		int first, point;
+		int selectedStart = -1; // -1 represents there is no text selection.
+		ustring selectedText;
 
 		/// <summary>
 		/// Tracks whether the text field should be considered "used", that is, that the user has moved in the entry, so new input should be appended at the cursor position, rather than clearing the entry
@@ -104,8 +106,8 @@ namespace Terminal.Gui {
 		{
 			if (Application.mouseGrabView != null && Application.mouseGrabView == this)
 				Application.UngrabMouse ();
-			if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar))
-				ClearAllSelection ();
+			//if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar))
+			//	ClearAllSelection ();
 
 			return base.OnLeave (view);
 		}
@@ -177,9 +179,14 @@ namespace Terminal.Gui {
 		public int CursorPosition {
 			get { return point; }
 			set {
-				point = value;
-				Adjust ();
-				SetNeedsDisplay ();
+				if (value < 0) {
+					point = 0;
+				} else if (value > text.Count) {
+					point = text.Count;
+				} else {
+					point = value;
+				}
+				PrepareSelection (selectedStart, point - selectedStart);
 			}
 		}
 
@@ -201,7 +208,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		{
-			ColorScheme color = Colors.Menu;
+			var selColor = new Attribute (ColorScheme.Focus.Background, ColorScheme.Focus.Foreground);
 			SetSelectedStartSelectedLength ();
 
 			Driver.SetAttribute (ColorScheme.Focus);
@@ -215,12 +222,14 @@ namespace Terminal.Gui {
 			for (int idx = p; idx < tcount; idx++) {
 				var rune = text [idx];
 				var cols = Rune.ColumnWidth (rune);
-				if (idx == point && HasFocus && !Used && SelectedLength == 0 && !ReadOnly) {
-					Driver.SetAttribute (Colors.Menu.HotFocus);
+				if (idx == point && HasFocus && !Used && length == 0 && !ReadOnly) {
+					Driver.SetAttribute (selColor);
 				} else if (ReadOnly) {
-					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : roc);
+					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? selColor : roc);
+				} else if (!HasFocus) {
+					Driver.SetAttribute (ColorScheme.Focus);
 				} else {
-					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
+					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? selColor : ColorScheme.Focus);
 				}
 				if (col + cols <= width) {
 					Driver.AddRune ((Rune)(Secret ? '*' : rune));
@@ -316,13 +325,12 @@ namespace Terminal.Gui {
 				if (ReadOnly)
 					return true;
 
-				if (SelectedLength == 0) {
+				if (length == 0) {
 					if (text.Count == 0 || text.Count == point)
 						return true;
 
 					SetText (text.GetRange (0, point).Concat (text.GetRange (point + 1, text.Count - (point + 1))));
 					Adjust ();
-
 				} else {
 					DeleteSelectedText ();
 				}
@@ -332,7 +340,7 @@ namespace Terminal.Gui {
 				if (ReadOnly)
 					return true;
 
-				if (SelectedLength == 0) {
+				if (length == 0) {
 					if (point == 0)
 						return true;
 
@@ -391,7 +399,7 @@ namespace Terminal.Gui {
 			case Key.CursorUp | Key.ShiftMask | Key.CtrlMask:
 			case (Key)((int)'B' + Key.ShiftMask | Key.AltMask):
 				if (point > 0) {
-					int x = start > -1 && start > point ? start : point;
+					int x = Math.Min (start > -1 && start > point ? start : point, text.Count - 1);
 					if (x > 0) {
 						int sbw = WordBackward (x);
 						if (sbw != -1)
@@ -556,7 +564,7 @@ namespace Terminal.Gui {
 				if (ReadOnly)
 					return true;
 
-				if (SelectedLength != 0) {
+				if (length > 0) {
 					DeleteSelectedText ();
 					oldCursorPos = point;
 				}
@@ -586,27 +594,30 @@ namespace Terminal.Gui {
 			if (p >= text.Count)
 				return -1;
 
-			int i = p;
-			if (Rune.IsPunctuation (text [p]) || Rune.IsWhiteSpace (text [p])) {
+			int i = p + 1;
+			if (i == text.Count)
+				return text.Count;
+
+			var ti = text [i];
+			if (Rune.IsPunctuation (ti) || Rune.IsSymbol (ti) || Rune.IsWhiteSpace (ti)) {
 				for (; i < text.Count; i++) {
-					var r = text [i];
-					if (Rune.IsLetterOrDigit (r))
-						break;
+					if (Rune.IsLetterOrDigit (text [i]))
+						return i;
 				}
+			} else {
 				for (; i < text.Count; i++) {
-					var r = text [i];
-					if (!Rune.IsLetterOrDigit (r))
+					if (!Rune.IsLetterOrDigit (text [i]))
 						break;
 				}
-			} else {
 				for (; i < text.Count; i++) {
-					var r = text [i];
-					if (!Rune.IsLetterOrDigit (r))
+					if (Rune.IsLetterOrDigit (text [i]))
 						break;
 				}
 			}
+
 			if (i != p)
-				return i + 1;
+				return Math.Min (i, text.Count);
+
 			return -1;
 		}
 
@@ -620,25 +631,38 @@ namespace Terminal.Gui {
 				return 0;
 
 			var ti = text [i];
+			var lastValidCol = -1;
 			if (Rune.IsPunctuation (ti) || Rune.IsSymbol (ti) || Rune.IsWhiteSpace (ti)) {
 				for (; i >= 0; i--) {
-					if (Rune.IsLetterOrDigit (text [i]))
+					if (Rune.IsLetterOrDigit (text [i])) {
+						lastValidCol = i;
 						break;
+					}
+					if (i - 1 > 0 && !Rune.IsWhiteSpace (text [i]) && Rune.IsWhiteSpace (text [i - 1])) {
+						return i;
+					}
 				}
 				for (; i >= 0; i--) {
 					if (!Rune.IsLetterOrDigit (text [i]))
 						break;
+					lastValidCol = i;
+				}
+				if (lastValidCol > -1) {
+					return lastValidCol;
 				}
 			} else {
 				for (; i >= 0; i--) {
 					if (!Rune.IsLetterOrDigit (text [i]))
 						break;
+					lastValidCol = i;
+				}
+				if (lastValidCol > -1) {
+					return lastValidCol;
 				}
 			}
-			i++;
 
 			if (i != p)
-				return i;
+				return Math.Max (i, 0);
 
 			return -1;
 		}
@@ -646,17 +670,32 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Start position of the selected text.
 		/// </summary>
-		public int SelectedStart { get; set; } = -1;
+		public int SelectedStart {
+			get => selectedStart;
+			set {
+				if (value < -1) {
+					selectedStart = -1;
+				} else if (value > text.Count) {
+					selectedStart = text.Count;
+				} else {
+					selectedStart = value;
+				}
+				PrepareSelection (selectedStart, point - selectedStart);
+			}
+		}
 
 		/// <summary>
 		/// Length of the selected text.
 		/// </summary>
-		public int SelectedLength { get; set; } = 0;
+		public int SelectedLength { get => length; }
 
 		/// <summary>
 		/// The selected text.
 		/// </summary>
-		public ustring SelectedText { get; set; }
+		public ustring SelectedText {
+			get => Secret ? null : selectedText;
+			private set => selectedText = value;
+		}
 
 		int start, length;
 		bool isButtonPressed;
@@ -707,6 +746,9 @@ namespace Terminal.Gui {
 				}
 				int sfw = WordForward (x);
 				ClearAllSelection ();
+				if (sfw != -1 && sbw != -1) {
+					point = sfw;
+				}
 				PrepareSelection (sbw, sfw - sbw);
 			} else if (ev.Flags == MouseFlags.Button1TripleClicked) {
 				PositionCursor (0);
@@ -750,20 +792,20 @@ namespace Terminal.Gui {
 
 		void PrepareSelection (int x, int direction = 0)
 		{
-			x = x + first < 0 ? 0 : x;
-			SelectedStart = SelectedStart == -1 && text.Count > 0 && x >= 0 && x <= text.Count ? x : SelectedStart;
-			if (SelectedStart > -1) {
-				SelectedLength = x + direction <= text.Count ? x + direction - SelectedStart : text.Count - SelectedStart;
+			x = x + first < -1 ? 0 : x;
+			selectedStart = selectedStart == -1 && text.Count > 0 && x >= 0 && x <= text.Count ? x : selectedStart;
+			if (selectedStart > -1) {
+				length = Math.Abs (x + direction <= text.Count ? x + direction - selectedStart : text.Count - selectedStart);
 				SetSelectedStartSelectedLength ();
-				if (start > -1) {
-					SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
+				if (start > -1 && length > 0) {
+					selectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
 						start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
 					if (first > start) {
 						first = start;
 					}
-				} else {
-					ClearAllSelection ();
 				}
+			} else if (length > 0) {
+				ClearAllSelection ();
 			}
 			Adjust ();
 		}
@@ -773,11 +815,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void ClearAllSelection ()
 		{
-			if (SelectedStart == -1 && SelectedLength == 0)
+			if (selectedStart == -1 && length == 0)
 				return;
-			SelectedStart = -1;
-			SelectedLength = 0;
-			SelectedText = "";
+
+			selectedStart = -1;
+			length = 0;
+			selectedText = null;
 			start = 0;
 			length = 0;
 			SetNeedsDisplay ();
@@ -785,12 +828,10 @@ namespace Terminal.Gui {
 
 		void SetSelectedStartSelectedLength ()
 		{
-			if (SelectedLength < 0) {
-				start = SelectedLength + SelectedStart;
-				length = Math.Abs (SelectedLength);
+			if (SelectedStart > -1 && point < SelectedStart) {
+				start = point - SelectedStart + SelectedStart;
 			} else {
 				start = SelectedStart;
-				length = SelectedLength;
 			}
 		}
 
@@ -799,12 +840,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void Copy ()
 		{
-			if (Secret)
+			if (Secret || length == 0)
 				return;
 
-			if (SelectedLength != 0) {
-				Clipboard.Contents = SelectedText;
-			}
+			Clipboard.Contents = SelectedText;
 		}
 
 		/// <summary>
@@ -812,20 +851,20 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void Cut ()
 		{
-			if (SelectedLength != 0) {
-				Clipboard.Contents = SelectedText;
-				DeleteSelectedText ();
-			}
+			if (Secret || length == 0)
+				return;
+
+			Clipboard.Contents = SelectedText;
+			DeleteSelectedText ();
 		}
 
 		void DeleteSelectedText ()
 		{
 			ustring actualText = Text;
-			int selStart = SelectedLength < 0 ? SelectedLength + SelectedStart : SelectedStart;
-			int selLength = Math.Abs (SelectedLength);
+			int selStart = point < SelectedStart ? SelectedStart - point + SelectedStart : SelectedStart;
 			(var _, var len) = TextModel.DisplaySize (text, 0, selStart, false);
-			(var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + selLength, false);
-			(var _, var len3) = TextModel.DisplaySize (text, selStart + selLength, actualText.RuneCount, false);
+			(var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + length, false);
+			(var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.RuneCount, false);
 			Text = actualText [0, len] +
 				actualText [len + len2, len + len2 + len3];
 			ClearAllSelection ();
@@ -838,7 +877,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void Paste ()
 		{
-			if (ReadOnly) {
+			if (ReadOnly || Clipboard.Contents == null) {
 				return;
 			}
 
@@ -869,7 +908,7 @@ namespace Terminal.Gui {
 			return ev;
 		}
 
-		private CursorVisibility desiredCursorVisibility = CursorVisibility.Default;
+		CursorVisibility desiredCursorVisibility = CursorVisibility.Default;
 
 		/// <summary>
 		/// Get / Set the wished cursor when the field is focused

+ 634 - 63
Terminal.Gui/Views/TextView.cs

@@ -279,6 +279,184 @@ namespace Terminal.Gui {
 			}
 			return col;
 		}
+
+		(Point startPointToFind, Point currentPointToFind, bool found) toFind;
+
+		internal (Point current, bool found) FindNextText (ustring text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
+		{
+			if (text == null || lines.Count == 0) {
+				gaveFullTurn = false;
+				return (Point.Empty, false);
+			}
+
+			if (toFind.found) {
+				toFind.currentPointToFind.X++;
+			}
+			var foundPos = GetFoundNextTextPoint (text, lines.Count, matchCase, matchWholeWord, toFind.currentPointToFind);
+			if (!foundPos.found && toFind.currentPointToFind != toFind.startPointToFind) {
+				foundPos = GetFoundNextTextPoint (text, toFind.startPointToFind.Y + 1, matchCase, matchWholeWord, Point.Empty);
+			}
+			gaveFullTurn = ApplyToFind (foundPos);
+
+			return foundPos;
+		}
+
+		internal (Point current, bool found) FindPreviousText (ustring text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
+		{
+			if (text == null || lines.Count == 0) {
+				gaveFullTurn = false;
+				return (Point.Empty, false);
+			}
+
+			if (toFind.found) {
+				toFind.currentPointToFind.X++;
+			}
+			var foundPos = GetFoundPreviousTextPoint (text, toFind.currentPointToFind.Y, matchCase, matchWholeWord, toFind.currentPointToFind);
+			if (!foundPos.found && toFind.currentPointToFind != toFind.startPointToFind) {
+				foundPos = GetFoundPreviousTextPoint (text, lines.Count - 1, matchCase, matchWholeWord,
+					new Point (lines [lines.Count - 1].Count, lines.Count));
+			}
+			gaveFullTurn = ApplyToFind (foundPos);
+
+			return foundPos;
+		}
+
+		internal (Point current, bool found) ReplaceAllText (ustring text, bool matchCase = false, bool matchWholeWord = false, ustring textToReplace = null)
+		{
+			bool found = false;
+			Point pos = Point.Empty;
+
+			for (int i = 0; i < lines.Count; i++) {
+				var x = lines [i];
+				var txt = ustring.Make (x).ToString ();
+				if (!matchCase) {
+					txt = txt.ToUpper ();
+				}
+				var matchText = !matchCase ? text.ToUpper ().ToString () : text.ToString ();
+				var col = txt.IndexOf (matchText);
+				if (col > -1 && matchWholeWord && !MatchWholeWord (txt, matchText, col)) {
+					continue;
+				}
+				if (col > -1) {
+					if (!found) {
+						found = true;
+					}
+					pos = new Point (col, i);
+					lines [i] = ReplaceText (x, textToReplace, matchText, col).ToRuneList ();
+					i--;
+				}
+			}
+
+			return (pos, found);
+		}
+
+		ustring ReplaceText (List<Rune> source, ustring textToReplace, string matchText, int col)
+		{
+			var origTxt = ustring.Make (source);
+			(int _, int len) = TextModel.DisplaySize (source, 0, col, false);
+			(var _, var len2) = TextModel.DisplaySize (source, col, col + matchText.Length, false);
+			(var _, var len3) = TextModel.DisplaySize (source, col + matchText.Length, origTxt.RuneCount, false);
+
+			return origTxt [0, len] +
+				textToReplace.ToString () +
+				origTxt [len + len2, len + len2 + len3];
+		}
+
+		bool ApplyToFind ((Point current, bool found) foundPos)
+		{
+			bool gaveFullTurn = false;
+			if (foundPos.found) {
+				toFind.currentPointToFind = foundPos.current;
+				if (toFind.found && toFind.currentPointToFind == toFind.startPointToFind) {
+					gaveFullTurn = true;
+				}
+				if (!toFind.found) {
+					toFind.startPointToFind = toFind.currentPointToFind = foundPos.current;
+					toFind.found = foundPos.found;
+				}
+			}
+
+			return gaveFullTurn;
+		}
+
+		(Point current, bool found) GetFoundNextTextPoint (ustring text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
+		{
+			for (int i = start.Y; i < linesCount; i++) {
+				var x = lines [i];
+				var txt = ustring.Make (x).ToString ();
+				if (!matchCase) {
+					txt = txt.ToUpper ();
+				}
+				var matchText = !matchCase ? text.ToUpper ().ToString () : text.ToString ();
+				var col = txt.IndexOf (matchText, Math.Min (start.X, txt.Length));
+				if (col > -1 && matchWholeWord && !MatchWholeWord (txt, matchText, col)) {
+					continue;
+				}
+				if (col > -1 && ((i == start.Y && col >= start.X)
+					|| i > start.Y)
+					&& txt.Contains (matchText)) {
+					return (new Point (col, i), true);
+				} else if (col == -1 && start.X > 0) {
+					start.X = 0;
+				}
+			}
+
+			return (Point.Empty, false);
+		}
+
+		(Point current, bool found) GetFoundPreviousTextPoint (ustring text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
+		{
+			for (int i = linesCount; i >= 0; i--) {
+				var x = lines [i];
+				var txt = ustring.Make (x).ToString ();
+				if (!matchCase) {
+					txt = txt.ToUpper ();
+				}
+				if (start.Y != i) {
+					start.X = Math.Max (x.Count - 1, 0);
+				}
+				var matchText = !matchCase ? text.ToUpper ().ToString () : text.ToString ();
+				var col = txt.LastIndexOf (matchText, start.X);
+				if (col > -1 && matchWholeWord && !MatchWholeWord (txt, matchText, col)) {
+					continue;
+				}
+				if (col > -1 && ((i == linesCount && col <= start.X)
+					|| i < start.Y)
+					&& txt.Contains (matchText)) {
+					return (new Point (col, i), true);
+				}
+			}
+
+			return (Point.Empty, false);
+		}
+
+		bool MatchWholeWord (string source, string matchText, int index = 0)
+		{
+			if (string.IsNullOrEmpty (source) || string.IsNullOrEmpty (matchText)) {
+				return false;
+			}
+
+			var txt = matchText.Trim ();
+			var start = index > 0 ? index - 1 : 0;
+			var end = index + txt.Length;
+
+			if ((start == 0 || Rune.IsWhiteSpace (source [start]))
+				&& (end == source.Length || Rune.IsWhiteSpace (source [end]))) {
+				return true;
+			}
+
+			return false;
+		}
+
+		/// <summary>
+		/// Redefine column and line tracking.
+		/// </summary>
+		/// <param name="point">Contains the column and line.</param>
+		internal void ResetContinuousFind (Point point)
+		{
+			toFind.startPointToFind = toFind.currentPointToFind = point;
+			toFind.found = false;
+		}
 	}
 
 	class WordWrapManager {
@@ -645,9 +823,9 @@ namespace Terminal.Gui {
 		int currentColumn;
 		int selectionStartColumn, selectionStartRow;
 		bool selecting;
-		//bool used;
 		bool wordWrap;
 		WordWrapManager wrapManager;
+		bool continuousFind;
 
 		/// <summary>
 		/// Raised when the <see cref="Text"/> of the <see cref="TextView"/> changes.
@@ -671,7 +849,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		public TextView (Rect frame) : base (frame)
 		{
-			CanFocus = true;
+			Initialize ();
 		}
 
 		/// <summary>
@@ -679,10 +857,22 @@ namespace Terminal.Gui {
 		///   with dimensions controlled with the X, Y, Width and Height properties.
 		/// </summary>
 		public TextView () : base ()
+		{
+			Initialize ();
+		}
+
+		void Initialize ()
 		{
 			CanFocus = true;
+			Used = true;
 		}
 
+		/// <summary>
+		/// Tracks whether the text view should be considered "used", that is, that the user has moved in the entry,
+		/// so new input should be appended at the cursor position, rather than clearing the entry
+		/// </summary>
+		public bool Used { get; set; }
+
 		void ResetPosition ()
 		{
 			topRow = leftColumn = currentRow = currentColumn = 0;
@@ -763,6 +953,79 @@ namespace Terminal.Gui {
 		/// </summary>
 		public int Lines => model.Count;
 
+		/// <summary>
+		///    Sets or gets the current cursor position.
+		/// </summary>
+		public Point CursorPosition {
+			get => new Point (currentColumn, currentRow);
+			set {
+				var line = model.GetLine (Math.Max (Math.Min (value.Y, model.Count - 1), 0));
+				currentColumn = value.X < 0 ? 0 : value.X > line.Count ? line.Count : value.X;
+				currentRow = value.Y < 0 ? 0 : value.Y > model.Count - 1
+					? Math.Max (model.Count - 1, 0) : value.Y;
+				SetNeedsDisplay ();
+				Adjust ();
+			}
+		}
+
+		/// <summary>
+		/// Start column position of the selected text.
+		/// </summary>
+		public int SelectionStartColumn {
+			get => selectionStartColumn;
+			set {
+				var line = model.GetLine (currentRow);
+				selectionStartColumn = value < 0 ? 0 : value > line.Count ? line.Count : value;
+				selecting = true;
+				SetNeedsDisplay ();
+				Adjust ();
+			}
+		}
+
+		/// <summary>
+		/// Start row position of the selected text.
+		/// </summary>
+		public int SelectionStartRow {
+			get => selectionStartRow;
+			set {
+				selectionStartRow = value < 0 ? 0 : value > model.Count - 1
+					? Math.Max (model.Count - 1, 0) : value;
+				selecting = true;
+				SetNeedsDisplay ();
+				Adjust ();
+			}
+		}
+
+		/// <summary>
+		/// The selected text.
+		/// </summary>
+		public ustring SelectedText {
+			get {
+				if (!selecting || (model.Count == 1 && model.GetLine (0).Count == 0)) {
+					return ustring.Empty;
+				}
+
+				SetWrapModel ();
+				var sel = GetRegion ();
+				UpdateWrapModel ();
+				Adjust ();
+
+				return sel;
+			}
+		}
+
+		/// <summary>
+		/// Length of the selected text.
+		/// </summary>
+		public int SelectedLength => GetSelectedLength ();
+
+		/// <summary>
+		/// Get or sets the selecting.
+		/// </summary>
+		public bool Selecting {
+			get => selecting;
+			set => selecting = value;
+		}
 		/// <summary>
 		/// Allows word wrap the to fit the available container width.
 		/// </summary>
@@ -776,7 +1039,7 @@ namespace Terminal.Gui {
 				ResetPosition ();
 				if (wordWrap) {
 					wrapManager = new WordWrapManager (model);
-					model = wrapManager.WrapModel (Frame.Width - 2, out _, out _, out _, out _);
+					model = wrapManager.WrapModel (Math.Max (Frame.Width - 2, 0), out _, out _, out _, out _);
 				} else if (!wordWrap && wrapManager != null) {
 					model = wrapManager.Model;
 				}
@@ -796,6 +1059,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public int RightOffset { get; set; }
 
+		int GetSelectedLength ()
+		{
+			return SelectedText.Length;
+		}
+
 		CursorVisibility savedCursorVisibility = CursorVisibility.Default;
 
 		void SaveCursorVisibility ()
@@ -884,7 +1152,7 @@ namespace Terminal.Gui {
 			var col = 0;
 			if (line.Count > 0) {
 				retreat = Math.Max (SpecialRune (line [Math.Min (Math.Max (currentColumn - leftColumn - 1, 0), line.Count - 1)])
-				? 1 : 0, 0);
+					? 1 : 0, 0);
 				for (int idx = leftColumn < 0 ? 0 : leftColumn; idx < line.Count; idx++) {
 					if (idx == currentColumn)
 						break;
@@ -892,7 +1160,7 @@ namespace Terminal.Gui {
 					col += cols - 1;
 				}
 			}
-			var ccol = currentColumn - leftColumn - retreat + col;
+			var ccol = currentColumn - leftColumn + retreat + col;
 			if (leftColumn <= currentColumn && ccol < Frame.Width
 				&& topRow <= currentRow && currentRow - topRow < Frame.Height) {
 				ResetCursorVisibility ();
@@ -921,6 +1189,11 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (ColorScheme.Focus);
 		}
 
+		void ColorUsed ()
+		{
+			Driver.SetAttribute (ColorScheme.HotFocus);
+		}
+
 		bool isReadOnly = false;
 
 		/// <summary>
@@ -1050,6 +1323,145 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
+		/// <summary>
+		/// Select all text.
+		/// </summary>
+		public void SelectAll ()
+		{
+			if (model.Count == 0) {
+				return;
+			}
+
+			StartSelecting ();
+			selectionStartColumn = 0;
+			selectionStartRow = 0;
+			currentColumn = model.GetLine (model.Count - 1).Count;
+			currentRow = model.Count - 1;
+			SetNeedsDisplay ();
+		}
+
+		/// <summary>
+		/// Find the next text based on the match case with the option to replace it.
+		/// </summary>
+		/// <param name="textToFind">The text to find.</param>
+		/// <param name="gaveFullTurn"><c>true</c>If all the text was forward searched.<c>false</c>otherwise.</param>
+		/// <param name="matchCase">The match case setting.</param>
+		/// <param name="matchWholeWord">The match whole word setting.</param>
+		/// <param name="textToReplace">The text to replace.</param>
+		/// <param name="replace"><c>true</c>If is replacing.<c>false</c>otherwise.</param>
+		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
+		public bool FindNextText (ustring textToFind, out bool gaveFullTurn, bool matchCase = false,
+			bool matchWholeWord = false, ustring textToReplace = null, bool replace = false)
+		{
+			if (model.Count == 0) {
+				gaveFullTurn = false;
+				return false;
+			}
+
+			SetWrapModel ();
+			ResetContinuousFind ();
+			var foundPos = model.FindNextText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
+
+			return SetFoundText (textToFind, foundPos, textToReplace, replace);
+		}
+
+		/// <summary>
+		/// Find the previous text based on the match case with the option to replace it.
+		/// </summary>
+		/// <param name="textToFind">The text to find.</param>
+		/// <param name="gaveFullTurn"><c>true</c>If all the text was backward searched.<c>false</c>otherwise.</param>
+		/// <param name="matchCase">The match case setting.</param>
+		/// <param name="matchWholeWord">The match whole word setting.</param>
+		/// <param name="textToReplace">The text to replace.</param>
+		/// <param name="replace"><c>true</c>If the text was found.<c>false</c>otherwise.</param>
+		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
+		public bool FindPreviousText (ustring textToFind, out bool gaveFullTurn, bool matchCase = false,
+			bool matchWholeWord = false, ustring textToReplace = null, bool replace = false)
+		{
+			if (model.Count == 0) {
+				gaveFullTurn = false;
+				return false;
+			}
+
+			SetWrapModel ();
+			ResetContinuousFind ();
+			var foundPos = model.FindPreviousText (textToFind, out gaveFullTurn, matchCase, matchWholeWord);
+
+			return SetFoundText (textToFind, foundPos, textToReplace, replace);
+		}
+
+		/// <summary>
+		/// Reset the flag to stop continuous find.
+		/// </summary>
+		public void FindTextChanged ()
+		{
+			continuousFind = false;
+		}
+
+		/// <summary>
+		/// Replaces all the text based on the match case.
+		/// </summary>
+		/// <param name="textToFind">The text to find.</param>
+		/// <param name="matchCase">The match case setting.</param>
+		/// <param name="matchWholeWord">The match whole word setting.</param>
+		/// <param name="textToReplace">The text to replace.</param>
+		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
+		public bool ReplaceAllText (ustring textToFind, bool matchCase = false, bool matchWholeWord = false,
+			ustring textToReplace = null)
+		{
+			if (isReadOnly || model.Count == 0) {
+				return false;
+			}
+
+			SetWrapModel ();
+			ResetContinuousFind ();
+			var foundPos = model.ReplaceAllText (textToFind, matchCase, matchWholeWord, textToReplace);
+
+			return SetFoundText (textToFind, foundPos, textToReplace, false, true);
+		}
+
+		bool SetFoundText (ustring text, (Point current, bool found) foundPos,
+			ustring textToReplace = null, bool replace = false, bool replaceAll = false)
+		{
+			if (foundPos.found) {
+				StartSelecting ();
+				selectionStartColumn = foundPos.current.X;
+				selectionStartRow = foundPos.current.Y;
+				if (!replaceAll) {
+					currentColumn = selectionStartColumn + text.RuneCount;
+				} else {
+					currentColumn = selectionStartColumn + textToReplace.RuneCount;
+				}
+				currentRow = foundPos.current.Y;
+				if (!isReadOnly && replace) {
+					Adjust ();
+					ClearSelectedRegion ();
+					InsertText (textToReplace);
+					StartSelecting ();
+					selectionStartColumn = currentColumn - textToReplace.RuneCount;
+				} else {
+					UpdateWrapModel ();
+					SetNeedsDisplay ();
+					Adjust ();
+				}
+				continuousFind = true;
+				return foundPos.found;
+			}
+			UpdateWrapModel ();
+			continuousFind = false;
+
+			return foundPos.found;
+		}
+
+		void ResetContinuousFind ()
+		{
+			if (!continuousFind) {
+				var col = selecting ? selectionStartColumn : currentColumn;
+				var row = selecting ? selectionStartRow : currentRow;
+				model.ResetContinuousFind (new Point (col, row));
+			}
+		}
+
 		/// <summary>
 		/// Restore from original model.
 		/// </summary>
@@ -1088,7 +1500,7 @@ namespace Terminal.Gui {
 			ColorNormal ();
 
 			int bottom = bounds.Bottom;
-			int right = bounds.Right + 1;
+			int right = bounds.Right;
 			for (int row = bounds.Top; row < bottom; row++) {
 				int textLine = topRow + row;
 				if (textLine >= model.Count) {
@@ -1111,14 +1523,27 @@ namespace Terminal.Gui {
 					var cols = Rune.ColumnWidth (rune);
 					if (lineCol < line.Count && selecting && PointInSelection (idx + leftColumn, row + topRow)) {
 						ColorSelection ();
+					} else if (lineCol == currentColumn && textLine == currentRow && !selecting && !Used
+						&& HasFocus && lineCol < lineRuneCount) {
+						ColorUsed ();
 					} else {
 						ColorNormal ();
 					}
 
 					if (!SpecialRune (rune)) {
 						AddRune (col, row, rune);
+					} else {
+						col++;
 					}
 					col = TextModel.SetCol (col, bounds.Right, cols);
+					if (idx + 1 < lineRuneCount && col + Rune.ColumnWidth (line [idx + 1]) > right) {
+						break;
+					} else if (idx == lineRuneCount - 1) {
+						ColorNormal ();
+						for (int i = col; i < right; i++) {
+							Driver.AddRune (' ');
+						}
+					}
 				}
 			}
 			PositionCursor ();
@@ -1156,9 +1581,21 @@ namespace Terminal.Gui {
 		void Insert (Rune rune)
 		{
 			var line = GetCurrentLine ();
-			line.Insert (Math.Min (currentColumn, line.Count), rune);
+			if (Used) {
+				line.Insert (Math.Min (currentColumn, line.Count), rune);
+			} else {
+				if (currentColumn < line.Count) {
+					line.RemoveAt (currentColumn);
+				}
+				line.Insert (Math.Min (currentColumn, line.Count), rune);
+			}
 			if (wordWrap) {
-				wrapNeeded = wrapManager.Insert (currentRow, currentColumn, rune);
+				if (Used) {
+					wrapNeeded = wrapManager.Insert (currentRow, currentColumn, rune);
+				} else {
+					wrapNeeded = wrapManager.RemoveAt (currentRow, currentColumn);
+					wrapNeeded = wrapManager.Insert (currentRow, currentColumn, rune);
+				}
 				if (wrapNeeded) {
 					SetNeedsDisplay ();
 				}
@@ -1211,7 +1648,7 @@ namespace Terminal.Gui {
 				if (wordWrap) {
 					SetNeedsDisplay ();
 				} else {
-					SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, currentRow - topRow + 1));
+					SetNeedsDisplay (new Rect (0, currentRow - topRow, Frame.Width, Math.Max (currentRow - topRow + 1, 0)));
 				}
 				return;
 			}
@@ -1348,9 +1785,35 @@ namespace Terminal.Gui {
 			case Key.P | Key.CtrlMask:
 			case Key.CursorUp:
 				lastWasKill = false;
+				continuousFind = false;
 				break;
 			case Key.K | Key.CtrlMask:
 				break;
+			case Key.F | Key.CtrlMask:
+			case Key.B | Key.CtrlMask:
+			case (Key)((int)'B' + Key.AltMask):
+			case Key.A | Key.CtrlMask:
+			case Key.E | Key.CtrlMask:
+			case Key.CursorRight:
+			case Key.CursorLeft:
+			case Key.CursorRight | Key.CtrlMask:
+			case Key.CursorLeft | Key.CtrlMask:
+			case Key.CursorRight | Key.ShiftMask:
+			case Key.CursorLeft | Key.ShiftMask:
+			case Key.CursorRight | Key.CtrlMask | Key.ShiftMask:
+			case Key.CursorLeft | Key.CtrlMask | Key.ShiftMask:
+			case Key.Home:
+			case Key.Home | Key.CtrlMask:
+			case Key.Home | Key.ShiftMask:
+			case Key.Home | Key.CtrlMask | Key.ShiftMask:
+			case Key.End:
+			case Key.End | Key.CtrlMask:
+			case Key.End | Key.ShiftMask:
+			case Key.End | Key.CtrlMask | Key.ShiftMask:
+				lastWasKill = false;
+				columnTrack = -1;
+				continuousFind = false;
+				break;
 			default:
 				lastWasKill = false;
 				columnTrack = -1;
@@ -1371,7 +1834,9 @@ namespace Terminal.Gui {
 				if (currentRow < model.Count) {
 					if (columnTrack == -1)
 						columnTrack = currentColumn;
-					currentRow = (currentRow + nPageDnShift) > model.Count ? model.Count : currentRow + nPageDnShift;
+					currentRow = (currentRow + nPageDnShift) > model.Count
+						? model.Count > 0 ? model.Count - 1 : 0
+						: currentRow + nPageDnShift;
 					if (topRow < currentRow - nPageDnShift) {
 						topRow = currentRow >= model.Count ? currentRow - nPageDnShift : topRow + nPageDnShift;
 						SetNeedsDisplay ();
@@ -1478,7 +1943,7 @@ namespace Terminal.Gui {
 				if (isReadOnly)
 					break;
 				if (selecting) {
-					Cut ();
+					ClearSelectedRegion ();
 					return true;
 				}
 				if (currentColumn > 0) {
@@ -1533,7 +1998,7 @@ namespace Terminal.Gui {
 				if (isReadOnly)
 					break;
 				if (selecting) {
-					Cut ();
+					ClearSelectedRegion ();
 					return true;
 				}
 				currentLine = GetCurrentLine ();
@@ -1733,6 +2198,15 @@ namespace Terminal.Gui {
 				MoveHome ();
 				break;
 
+			case Key.T | Key.CtrlMask:
+				SelectAll ();
+				break;
+
+			case Key.InsertChar:
+				Used = !Used;
+				SetNeedsDisplay ();
+				break;
+
 			default:
 				// Ignore control characters and other special keys
 				if (kb.Key < Key.Space || kb.Key > Key.CharMask)
@@ -1741,13 +2215,18 @@ namespace Terminal.Gui {
 				if (isReadOnly)
 					return true;
 				if (selecting) {
-					Cut ();
+					ClearSelectedRegion ();
 				}
-				Insert ((uint)kb.Key);
-				currentColumn++;
-				if (currentColumn >= leftColumn + Frame.Width) {
-					leftColumn++;
-					SetNeedsDisplay ();
+				if (Used) {
+					Insert ((uint)kb.Key);
+					currentColumn++;
+					if (currentColumn >= leftColumn + Frame.Width) {
+						leftColumn++;
+						SetNeedsDisplay ();
+					}
+				} else {
+					Insert ((uint)kb.Key);
+					currentColumn++;
 				}
 				break;
 			}
@@ -1838,6 +2317,17 @@ namespace Terminal.Gui {
 			selecting = false;
 		}
 
+		void ClearSelectedRegion ()
+		{
+			SetWrapModel ();
+			if (!isReadOnly) {
+				ClearRegion ();
+			}
+			UpdateWrapModel ();
+			selecting = false;
+			DoNeededAction ();
+		}
+
 		void MoveUp ()
 		{
 			if (currentRow > 0) {
@@ -1895,7 +2385,11 @@ namespace Terminal.Gui {
 		Rune RuneAt (int col, int row)
 		{
 			var line = model.GetLine (row);
-			return line [col > line.Count - 1 ? line.Count - 1 : col];
+			if (line.Count > 0) {
+				return line [col > line.Count - 1 ? line.Count - 1 : col];
+			} else {
+				return 0;
+			}
 		}
 
 		/// <summary>
@@ -1927,6 +2421,9 @@ namespace Terminal.Gui {
 			if (col + 1 < line.Count) {
 				col++;
 				rune = line [col];
+				if (col + 1 == line.Count) {
+					col++;
+				}
 				return true;
 			}
 			while (row + 1 < model.Count) {
@@ -1975,27 +2472,45 @@ namespace Terminal.Gui {
 			try {
 				var rune = RuneAt (col, row);
 
-				var srow = row;
-				if (Rune.IsPunctuation (rune) || Rune.IsWhiteSpace (rune)) {
-					while (MoveNext (ref col, ref row, out rune)) {
-						if (Rune.IsLetterOrDigit (rune))
-							break;
-					}
-					if (row != fromRow && Rune.IsLetterOrDigit (rune)) {
-						return (col, row);
-					}
-					while (MoveNext (ref col, ref row, out rune)) {
-						if (!Rune.IsLetterOrDigit (rune))
-							break;
-					}
-				} else {
-					while (MoveNext (ref col, ref row, out rune)) {
-						if (!Rune.IsLetterOrDigit (rune))
-							break;
+				void ProcMoveNext (ref int nCol, ref int nRow, Rune nRune)
+				{
+					if (Rune.IsSymbol (nRune) || Rune.IsWhiteSpace (nRune)) {
+						while (MoveNext (ref nCol, ref nRow, out nRune)) {
+							if (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune))
+								return;
+						}
+						if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune))) {
+							return;
+						}
+						while (MoveNext (ref nCol, ref nRow, out nRune)) {
+							if (!Rune.IsLetterOrDigit (nRune) && !Rune.IsPunctuation (nRune))
+								break;
+						}
+					} else {
+						if (!MoveNext (ref nCol, ref nRow, out nRune)) {
+							return;
+						}
+
+						var line = model.GetLine (fromRow);
+						if ((nRow != fromRow && fromCol < line.Count)
+							|| (nRow == fromRow && nCol == line.Count - 1)) {
+							nCol = line.Count;
+							nRow = fromRow;
+							return;
+						} else if (nRow != fromRow && fromCol == line.Count) {
+							line = model.GetLine (nRow);
+							if (Rune.IsLetterOrDigit (line [nCol]) || Rune.IsPunctuation (line [nCol])) {
+								return;
+							}
+						}
+						ProcMoveNext (ref nCol, ref nRow, nRune);
 					}
 				}
+
+				ProcMoveNext (ref col, ref row, rune);
+
 				if (fromCol != col || fromRow != row)
-					return (col + 1, row);
+					return (col, row);
 				return null;
 			} catch (Exception) {
 				return null;
@@ -2011,36 +2526,54 @@ namespace Terminal.Gui {
 			var row = fromRow;
 			try {
 				var rune = RuneAt (col, row);
+				int lastValidCol = Rune.IsLetterOrDigit (rune) || Rune.IsPunctuation (rune) ? col : -1;
+
+				void ProcMovePrev (ref int nCol, ref int nRow, Rune nRune)
+				{
+					if (Rune.IsSymbol (nRune) || Rune.IsWhiteSpace (nRune)) {
+						while (MovePrev (ref nCol, ref nRow, out nRune)) {
+							if (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune)) {
+								lastValidCol = nCol;
+								break;
+							}
+						}
+						if (nRow != fromRow && (Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune))) {
+							return;
+						}
+						while (MovePrev (ref nCol, ref nRow, out nRune)) {
+							if (!Rune.IsLetterOrDigit (nRune) && !Rune.IsPunctuation (nRune))
+								break;
+							lastValidCol = nCol;
+						}
+						if (lastValidCol > -1) {
+							nCol = lastValidCol;
+						}
+					} else {
+						if (!MovePrev (ref nCol, ref nRow, out nRune)) {
+							return;
+						}
 
-				if (Rune.IsPunctuation (rune) || Rune.IsSymbol (rune) || Rune.IsWhiteSpace (rune)) {
-					while (MovePrev (ref col, ref row, out rune)) {
-						if (Rune.IsLetterOrDigit (rune))
-							break;
-					}
-					int lastValidCol = -1;
-					while (MovePrev (ref col, ref row, out rune)) {
-						if (col == 0 && Rune.IsLetterOrDigit (rune)) {
-							return (col, row);
-						} else if (col == 0 && !Rune.IsLetterOrDigit (rune) && lastValidCol > -1) {
-							col = lastValidCol;
-							return (col, row);
+						var line = model.GetLine (nRow);
+						if (nCol == 0 && nRow == fromRow && (Rune.IsLetterOrDigit (line [0]) || Rune.IsPunctuation (line [0]))) {
+							return;
 						}
-						if (!Rune.IsLetterOrDigit (rune)) {
-							break;
+						lastValidCol = Rune.IsLetterOrDigit (nRune) || Rune.IsPunctuation (nRune) ? nCol : lastValidCol;
+						if (lastValidCol > -1 && (Rune.IsSymbol (nRune) || Rune.IsWhiteSpace (nRune))) {
+							nCol = lastValidCol;
+							return;
 						}
-						lastValidCol = Rune.IsLetterOrDigit (rune) ? col : -1;
-					}
-				} else {
-					while (MovePrev (ref col, ref row, out rune)) {
-						if (!Rune.IsLetterOrDigit (rune))
-							break;
+						if (fromRow != nRow) {
+							nCol = line.Count;
+							return;
+						}
+						ProcMovePrev (ref nCol, ref nRow, nRune);
 					}
 				}
-				if (fromCol != col && fromRow == row) {
-					return (col == 0 ? col : col + 1, row);
-				} else if (fromCol != col && fromRow != row) {
-					return (col + 1, row);
-				}
+
+				ProcMovePrev (ref col, ref row, rune);
+
+				if (fromCol != col || fromRow != row)
+					return (col, row);
 				return null;
 			} catch (Exception) {
 				return null;
@@ -2054,7 +2587,9 @@ namespace Terminal.Gui {
 				&& !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)
 				&& !ev.Flags.HasFlag (MouseFlags.Button1Released)
 				&& !ev.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ButtonShift)
-				&& !ev.Flags.HasFlag (MouseFlags.WheeledDown) && !ev.Flags.HasFlag (MouseFlags.WheeledUp)) {
+				&& !ev.Flags.HasFlag (MouseFlags.WheeledDown) && !ev.Flags.HasFlag (MouseFlags.WheeledUp)
+				&& !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked)
+				&& !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked | MouseFlags.ButtonShift)) {
 				return false;
 			}
 
@@ -2066,6 +2601,8 @@ namespace Terminal.Gui {
 				SetFocus ();
 			}
 
+			continuousFind = false;
+
 			if (ev.Flags == MouseFlags.Button1Clicked) {
 				if (shiftSelecting) {
 					shiftSelecting = false;
@@ -2143,6 +2680,40 @@ namespace Terminal.Gui {
 				}
 			} else if (ev.Flags.HasFlag (MouseFlags.Button1Released)) {
 				Application.UngrabMouse ();
+			} else if (ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked)) {
+				if (ev.Flags.HasFlag (MouseFlags.ButtonShift)) {
+					if (!selecting) {
+						StartSelecting ();
+					}
+				} else if (selecting) {
+					StopSelecting ();
+				}
+				ProcessMouseClick (ev, out List<Rune> line);
+				(int col, int row)? newPos = null;
+				if (currentColumn > 0 && line [currentColumn - 1] != ' ') {
+					newPos = WordBackward (currentColumn, currentRow);
+					if (newPos.HasValue) {
+						currentColumn = newPos.Value.col;
+						currentRow = newPos.Value.row;
+					}
+				}
+				if (!selecting) {
+					StartSelecting ();
+				}
+				if (currentRow < selectionStartRow || currentRow == selectionStartRow && currentColumn < selectionStartColumn) {
+					if (currentColumn > 0 && line [currentColumn - 1] != ' ') {
+						newPos = WordBackward (currentColumn, currentRow);
+					}
+				} else {
+					newPos = WordForward (currentColumn, currentRow);
+				}
+				if (newPos != null && newPos.HasValue) {
+					currentColumn = newPos.Value.col;
+					currentRow = newPos.Value.row;
+				}
+				PositionCursor ();
+				lastWasKill = false;
+				columnTrack = currentColumn;
 			}
 
 			return true;

+ 2 - 7
UICatalog/Scenarios/Dialogs.cs

@@ -93,13 +93,8 @@ namespace UICatalog {
 			};
 			frame.Add (numButtonsEdit);
 
-			void Top_Loaded ()
-			{
-				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit)
-					+ Dim.Height (numButtonsEdit) + 2;
-				Top.Loaded -= Top_Loaded;
-			}
-			Top.Loaded += Top_Loaded;
+			frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit)
+				+ Dim.Height (numButtonsEdit) + 2;
 
 			label = new Label ("Button Pressed:") {
 				X = Pos.Center (),

+ 368 - 1
UICatalog/Scenarios/Editor.cs

@@ -15,6 +15,11 @@ namespace UICatalog {
 		private bool _saved = true;
 		private ScrollBarView _scrollBar;
 		private byte [] _originalText;
+		private string _textToFind;
+		private string _textToReplace;
+		private bool _matchCase;
+		private bool _matchWholeWord;
+		Window winDialog;
 
 		public override void Init (Toplevel top, ColorScheme colorScheme)
 		{
@@ -36,7 +41,17 @@ namespace UICatalog {
 				new MenuBarItem ("_Edit", new MenuItem [] {
 					new MenuItem ("_Copy", "", () => Copy(),null,null, Key.CtrlMask | Key.C),
 					new MenuItem ("C_ut", "", () => Cut(),null,null, Key.CtrlMask | Key.W),
-					new MenuItem ("_Paste", "", () => Paste(),null,null, Key.CtrlMask | Key.Y)
+					new MenuItem ("_Paste", "", () => Paste(),null,null, Key.CtrlMask | Key.Y),
+					null,
+					new MenuItem ("_Find", "", () => Find(),null,null, Key.CtrlMask | Key.S),
+					new MenuItem ("Find _Next", "", () => FindNext(),null,null, Key.CtrlMask | Key.ShiftMask | Key.S),
+					new MenuItem ("Find P_revious", "", () => FindPrevious(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.S),
+					new MenuItem ("_Replace", "", () => Replace(),null,null, Key.CtrlMask | Key.R),
+					new MenuItem ("Replace Ne_xt", "", () => ReplaceNext(),null,null, Key.CtrlMask | Key.ShiftMask | Key.R),
+					new MenuItem ("Replace Pre_vious", "", () => ReplacePrevious(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.R),
+					new MenuItem ("Replace _All", "", () => ReplaceAll(),null,null, Key.CtrlMask | Key.ShiftMask | Key.AltMask | Key.A),
+					null,
+					new MenuItem ("_Select All", "", () => SelectAll(),null,null, Key.CtrlMask | Key.T)
 				}),
 				new MenuBarItem ("_ScrollBarView", CreateKeepChecked ()),
 				new MenuBarItem ("_Cursor", new MenuItem [] {
@@ -115,6 +130,20 @@ namespace UICatalog {
 				_scrollBar.LayoutSubviews ();
 				_scrollBar.Refresh ();
 			};
+
+			Win.KeyPress += (e) => {
+				if (winDialog != null && (e.KeyEvent.Key == Key.Esc
+					|| e.KeyEvent.Key.HasFlag (Key.Q | Key.CtrlMask))) {
+					DisposeWinDialog ();
+				}
+			};
+		}
+
+		private void DisposeWinDialog ()
+		{
+			winDialog.Dispose ();
+			Win.Remove (winDialog);
+			winDialog = null;
 		}
 
 		public override void Setup ()
@@ -166,6 +195,91 @@ namespace UICatalog {
 			}
 		}
 
+		private void SelectAll ()
+		{
+			_textView.SelectAll ();
+		}
+
+		private void Find ()
+		{
+			CreateFindReplace ();
+		}
+
+		private void FindNext ()
+		{
+			ContinueFind ();
+		}
+
+		private void FindPrevious ()
+		{
+			ContinueFind (false);
+		}
+
+		private void ContinueFind (bool next = true, bool replace = false)
+		{
+			if (!replace && string.IsNullOrEmpty (_textToFind)) {
+				Find ();
+				return;
+			} else if (replace && (string.IsNullOrEmpty (_textToFind)
+				|| (winDialog == null && string.IsNullOrEmpty (_textToReplace)))) {
+				Replace ();
+				return;
+			}
+
+			bool found;
+			bool gaveFullTurn;
+
+			if (next) {
+				if (!replace) {
+					found = _textView.FindNextText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord);
+				} else {
+					found = _textView.FindNextText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord,
+						_textToReplace, true);
+				}
+			} else {
+				if (!replace) {
+					found = _textView.FindPreviousText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord);
+				} else {
+					found = _textView.FindPreviousText (_textToFind, out gaveFullTurn, _matchCase, _matchWholeWord,
+						_textToReplace, true);
+				}
+			}
+			if (!found) {
+				MessageBox.Query ("Find", $"The following specified text was not found: '{_textToFind}'", "Ok");
+			} else if (gaveFullTurn) {
+				MessageBox.Query ("Find", $"No more occurrences were found for the following specified text: '{_textToFind}'", "Ok");
+			}
+		}
+
+		private void Replace ()
+		{
+			CreateFindReplace (false);
+		}
+
+		private void ReplaceNext ()
+		{
+			ContinueFind (true, true);
+		}
+
+		private void ReplacePrevious ()
+		{
+			ContinueFind (false, true);
+		}
+
+		private void ReplaceAll ()
+		{
+			if (string.IsNullOrEmpty (_textToFind) || (string.IsNullOrEmpty (_textToReplace) && winDialog == null)) {
+				Replace ();
+				return;
+			}
+
+			if (_textView.ReplaceAllText (_textToFind, _matchCase, _matchWholeWord, _textToReplace)) {
+				MessageBox.Query ("Replace All", $"All occurrences were replaced for the following specified text: '{_textToReplace}'", "Ok");
+			} else {
+				MessageBox.Query ("Replace All", $"None of the following specified text was found: '{_textToFind}'", "Ok");
+			}
+		}
+
 		private void SetCursor (CursorVisibility visibility)
 		{
 			_textView.DesiredCursorVisibility = visibility;
@@ -307,6 +421,259 @@ namespace UICatalog {
 			return new MenuItem [] { item };
 		}
 
+		private void CreateFindReplace (bool isFind = true)
+		{
+			winDialog = new Window (isFind ? "Find" : "Replace") {
+				X = Win.Bounds.Width / 2 - 30,
+				Y = Win.Bounds.Height / 2 - 10,
+				ColorScheme = Colors.Menu
+			};
+
+			var tabView = new TabView () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+
+			tabView.AddTab (new Tab ("Find", FindTab ()), isFind);
+			var replace = ReplaceTab ();
+			tabView.AddTab (new Tab ("Replace", replace), !isFind);
+			tabView.SelectedTabChanged += (s, e) => tabView.SelectedTab.View.FocusFirst ();
+			winDialog.Add (tabView);
+
+			Win.Add (winDialog);
+
+			winDialog.Width = replace.Width + 4;
+			winDialog.Height = replace.Height + 4;
+
+			winDialog.SuperView.BringSubviewToFront (winDialog);
+			winDialog.SetFocus ();
+		}
+
+		private void SetFindText ()
+		{
+			_textToFind = !_textView.SelectedText.IsEmpty
+				? _textView.SelectedText.ToString ()
+				: string.IsNullOrEmpty (_textToFind) ? "" : _textToFind;
+
+			_textToReplace = string.IsNullOrEmpty (_textToReplace) ? "" : _textToReplace;
+		}
+
+		private View FindTab ()
+		{
+			var d = new View () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			d.DrawContent += (e) => {
+				foreach (var v in d.Subviews) {
+					v.SetNeedsDisplay ();
+				}
+			};
+
+			var lblWidth = "Replace:".Length;
+
+			var label = new Label (0, 1, "Find:") {
+				Width = lblWidth,
+				TextAlignment = TextAlignment.Right,
+				LayoutStyle = LayoutStyle.Computed
+			};
+			d.Add (label);
+
+			SetFindText ();
+			var txtToFind = new TextField (_textToFind) {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 20
+			};
+			txtToFind.Enter += (_) => txtToFind.Text = _textToFind;
+			d.Add (txtToFind);
+
+			var btnFindNext = new Button ("Find _Next") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (label),
+				Width = 20,
+				CanFocus = !txtToFind.Text.IsEmpty,
+				TextAlignment = TextAlignment.Centered,
+				IsDefault = true
+			};
+			btnFindNext.Clicked += () => FindNext ();
+			d.Add (btnFindNext);
+
+			var btnFindPrevious = new Button ("Find _Previous") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (btnFindNext) + 1,
+				Width = 20,
+				CanFocus = !txtToFind.Text.IsEmpty,
+				TextAlignment = TextAlignment.Centered
+			};
+			btnFindPrevious.Clicked += () => FindPrevious ();
+			d.Add (btnFindPrevious);
+
+			txtToFind.TextChanged += (e) => {
+				_textToFind = txtToFind.Text.ToString ();
+				_textView.FindTextChanged ();
+				btnFindNext.CanFocus = !txtToFind.Text.IsEmpty;
+				btnFindPrevious.CanFocus = !txtToFind.Text.IsEmpty;
+			};
+
+			var btnCancel = new Button ("Cancel") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (btnFindPrevious) + 2,
+				Width = 20,
+				TextAlignment = TextAlignment.Centered
+			};
+			btnCancel.Clicked += () => {
+				DisposeWinDialog ();
+			};
+			d.Add (btnCancel);
+
+			var ckbMatchCase = new CheckBox ("Match c_ase") {
+				X = 0,
+				Y = Pos.Top (txtToFind) + 2,
+				Checked = _matchCase
+			};
+			ckbMatchCase.Toggled += (e) => _matchCase = ckbMatchCase.Checked;
+			d.Add (ckbMatchCase);
+
+			var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
+				X = 0,
+				Y = Pos.Top (ckbMatchCase) + 1,
+				Checked = _matchWholeWord
+			};
+			ckbMatchWholeWord.Toggled += (e) => _matchWholeWord = ckbMatchWholeWord.Checked;
+			d.Add (ckbMatchWholeWord);
+
+			d.Width = label.Width + txtToFind.Width + btnFindNext.Width + 2;
+			d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
+
+			return d;
+		}
+
+		private View ReplaceTab ()
+		{
+			var d = new View () {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (),
+				Height = Dim.Fill ()
+			};
+			d.DrawContent += (e) => {
+				foreach (var v in d.Subviews) {
+					v.SetNeedsDisplay ();
+				}
+			};
+
+			var lblWidth = "Replace:".Length;
+
+			var label = new Label (0, 1, "Find:") {
+				Width = lblWidth,
+				TextAlignment = TextAlignment.Right,
+				LayoutStyle = LayoutStyle.Computed
+			};
+			d.Add (label);
+
+			SetFindText ();
+			var txtToFind = new TextField (_textToFind) {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 20
+			};
+			txtToFind.Enter += (_) => txtToFind.Text = _textToFind;
+			d.Add (txtToFind);
+
+			var btnFindNext = new Button ("Replace _Next") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (label),
+				Width = 20,
+				CanFocus = !txtToFind.Text.IsEmpty,
+				TextAlignment = TextAlignment.Centered,
+				IsDefault = true
+			};
+			btnFindNext.Clicked += () => ReplaceNext ();
+			d.Add (btnFindNext);
+
+			label = new Label ("Replace:") {
+				X = Pos.Left (label),
+				Y = Pos.Top (label) + 1,
+				Width = lblWidth,
+				TextAlignment = TextAlignment.Right
+			};
+			d.Add (label);
+
+			SetFindText ();
+			var txtToReplace = new TextField (_textToReplace) {
+				X = Pos.Right (label) + 1,
+				Y = Pos.Top (label),
+				Width = 20
+			};
+			txtToReplace.TextChanged += (e) => _textToReplace = txtToReplace.Text.ToString ();
+			d.Add (txtToReplace);
+
+			var btnFindPrevious = new Button ("Replace _Previous") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (btnFindNext) + 1,
+				Width = 20,
+				CanFocus = !txtToFind.Text.IsEmpty,
+				TextAlignment = TextAlignment.Centered
+			};
+			btnFindPrevious.Clicked += () => ReplacePrevious ();
+			d.Add (btnFindPrevious);
+
+			var btnReplaceAll = new Button ("Replace _All") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (btnFindPrevious) + 1,
+				Width = 20,
+				CanFocus = !txtToFind.Text.IsEmpty,
+				TextAlignment = TextAlignment.Centered
+			};
+			btnReplaceAll.Clicked += () => ReplaceAll ();
+			d.Add (btnReplaceAll);
+
+			txtToFind.TextChanged += (e) => {
+				_textToFind = txtToFind.Text.ToString ();
+				_textView.FindTextChanged ();
+				btnFindNext.CanFocus = !txtToFind.Text.IsEmpty;
+				btnFindPrevious.CanFocus = !txtToFind.Text.IsEmpty;
+				btnReplaceAll.CanFocus = !txtToFind.Text.IsEmpty;
+			};
+
+			var btnCancel = new Button ("Cancel") {
+				X = Pos.Right (txtToFind) + 1,
+				Y = Pos.Top (btnReplaceAll) + 1,
+				Width = 20,
+				TextAlignment = TextAlignment.Centered
+			};
+			btnCancel.Clicked += () => {
+				DisposeWinDialog ();
+			};
+			d.Add (btnCancel);
+
+			var ckbMatchCase = new CheckBox ("Match c_ase") {
+				X = 0,
+				Y = Pos.Top (txtToFind) + 2,
+				Checked = _matchCase
+			};
+			ckbMatchCase.Toggled += (e) => _matchCase = ckbMatchCase.Checked;
+			d.Add (ckbMatchCase);
+
+			var ckbMatchWholeWord = new CheckBox ("Match _whole word") {
+				X = 0,
+				Y = Pos.Top (ckbMatchCase) + 1,
+				Checked = _matchWholeWord
+			};
+			ckbMatchWholeWord.Toggled += (e) => _matchWholeWord = ckbMatchWholeWord.Checked;
+			d.Add (ckbMatchWholeWord);
+
+			d.Width = lblWidth + txtToFind.Width + btnFindNext.Width + 2;
+			d.Height = btnFindNext.Height + btnFindPrevious.Height + btnCancel.Height + 4;
+
+			return d;
+		}
+
 		public override void Run ()
 		{
 			base.Run ();

+ 2 - 7
UICatalog/Scenarios/MessageBoxes.cs

@@ -140,13 +140,8 @@ namespace UICatalog {
 			};
 			frame.Add (styleRadioGroup);
 
-			void Top_Loaded ()
-			{
-				frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + Dim.Height (messageEdit)
-					+ Dim.Height (numButtonsEdit) + Dim.Height (defaultButtonEdit) + Dim.Height (styleRadioGroup) + 2;
-				Top.Loaded -= Top_Loaded;
-			}
-			Top.Loaded += Top_Loaded;
+			frame.Height = Dim.Height (widthEdit) + Dim.Height (heightEdit) + Dim.Height (titleEdit) + Dim.Height (messageEdit)
+				+ Dim.Height (numButtonsEdit) + Dim.Height(defaultButtonEdit) + Dim.Height (styleRadioGroup) + 2;
 
 			label = new Label ("Button Pressed:") {
 				X = Pos.Center (),

+ 83 - 1
UnitTests/DimTests.cs

@@ -483,6 +483,10 @@ namespace Terminal.Gui {
 				Assert.Equal (1, v3.Frame.Height); // 1 because is Dim.DimAbsolute
 
 				v4.Text = "Button4";
+				v4.AutoSize = false;
+				Assert.Equal ("Dim.Absolute(50)", v4.Width.ToString ());
+				Assert.Equal ("Dim.Absolute(1)", v4.Height.ToString ());
+				v4.AutoSize = true;
 				Assert.Equal ("Dim.Absolute(11)", v4.Width.ToString ());
 				Assert.Equal ("Dim.Absolute(1)", v4.Height.ToString ());
 				Assert.Equal (11, v4.Frame.Width); // 11 is the text length and because is Dim.DimAbsolute
@@ -507,6 +511,84 @@ namespace Terminal.Gui {
 			Application.Shutdown ();
 		}
 
-		// TODO: Test operators
+		// DONE: Test operators
+		[Fact]
+		public void DimCombine_Do_Not_Throws ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var t = Application.Top;
+
+			var w = new Window ("w") {
+				Width = Dim.Width (t) - 2,
+				Height = Dim.Height (t) - 2
+			};
+			var f = new FrameView ("f");
+			var v1 = new View ("v1") {
+				Width = Dim.Width (w) - 2,
+				Height = Dim.Height (w) - 2
+			};
+			var v2 = new View ("v2") {
+				Width = Dim.Width (v1) - 2,
+				Height = Dim.Height (v1) - 2
+			};
+
+			f.Add (v1, v2);
+			w.Add (f);
+			t.Add (w);
+
+			f.Width = Dim.Width (t) - Dim.Width (v2);
+			f.Height = Dim.Height (t) - Dim.Height (v2);
+
+			t.Ready += () => {
+				Assert.Equal (80, t.Frame.Width);
+				Assert.Equal (25, t.Frame.Height);
+				Assert.Equal (78, w.Frame.Width);
+				Assert.Equal (23, w.Frame.Height);
+				Assert.Equal (6, f.Frame.Width);
+				Assert.Equal (6, f.Frame.Height);
+				Assert.Equal (76, v1.Frame.Width);
+				Assert.Equal (21, v1.Frame.Height);
+				Assert.Equal (74, v2.Frame.Width);
+				Assert.Equal (19, v2.Frame.Height);
+			};
+
+			Application.Iteration += () => Application.RequestStop ();
+
+			Application.Run ();
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void PosCombine_Will_Throws ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var t = Application.Top;
+
+			var w = new Window ("w") {
+				Width = Dim.Width (t) - 2,
+				Height = Dim.Height (t) - 2
+			};
+			var f = new FrameView ("f");
+			var v1 = new View ("v1") {
+				Width = Dim.Width (w) - 2,
+				Height = Dim.Height (w) - 2
+			};
+			var v2 = new View ("v2") {
+				Width = Dim.Width (v1) - 2,
+				Height = Dim.Height (v1) - 2
+			};
+
+			f.Add (v1); // v2 not added
+			w.Add (f);
+			t.Add (w);
+
+			f.Width = Dim.Width (t) - Dim.Width (v2);
+			f.Height = Dim.Height (t) - Dim.Height (v2);
+
+			Assert.Throws<InvalidOperationException> (() => Application.Run ());
+			Application.Shutdown ();
+		}
 	}
 }

+ 81 - 5
UnitTests/PosTests.cs

@@ -79,7 +79,7 @@ namespace Terminal.Gui {
 			Assert.Equal (pos1, pos2);
 		}
 
-		[Fact] 
+		[Fact]
 		public void SetSide_Null_Throws ()
 		{
 			var pos = Pos.Left (null);
@@ -91,7 +91,7 @@ namespace Terminal.Gui {
 			pos = Pos.Top (null);
 			Assert.Throws<NullReferenceException> (() => pos.ToString ());
 
-			pos = Pos.Y(null);
+			pos = Pos.Y (null);
 			Assert.Throws<NullReferenceException> (() => pos.ToString ());
 
 			pos = Pos.Bottom (null);
@@ -112,7 +112,7 @@ namespace Terminal.Gui {
 			string side; // used in format string
 			var testRect = Rect.Empty;
 			var testInt = 0;
-			Pos pos; 
+			Pos pos;
 
 			// Pos.Left
 			side = "x";
@@ -456,9 +456,85 @@ namespace Terminal.Gui {
 			Application.Shutdown ();
 		}
 
-		// TODO: Test PosCombine
+		// DONE: Test PosCombine
+		// DONE: Test operators
+		[Fact]
+		public void PosCombine_Do_Not_Throws ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
 
+			var t = Application.Top;
 
-		// TODO: Test operators
+			var w = new Window ("w") {
+				X = Pos.Left (t) + 2,
+				Y = Pos.Top (t) + 2
+			};
+			var f = new FrameView ("f");
+			var v1 = new View ("v1") {
+				X = Pos.Left (w) + 2,
+				Y = Pos.Top (w) + 2
+			};
+			var v2 = new View ("v2") {
+				X = Pos.Left (v1) + 2,
+				Y = Pos.Top (v1) + 2
+			};
+
+			f.Add (v1, v2);
+			w.Add (f);
+			t.Add (w);
+
+			f.X = Pos.X (t) + Pos.X (v2) - Pos.X (v1);
+			f.Y = Pos.Y (t) + Pos.Y (v2) - Pos.Y (v1);
+
+			t.Ready += () => {
+				Assert.Equal (0, t.Frame.X);
+				Assert.Equal (0, t.Frame.Y);
+				Assert.Equal (2, w.Frame.X);
+				Assert.Equal (2, w.Frame.Y);
+				Assert.Equal (2, f.Frame.X);
+				Assert.Equal (2, f.Frame.Y);
+				Assert.Equal (4, v1.Frame.X);
+				Assert.Equal (4, v1.Frame.Y);
+				Assert.Equal (6, v2.Frame.X);
+				Assert.Equal (6, v2.Frame.Y);
+			};
+
+			Application.Iteration += () => Application.RequestStop ();
+
+			Application.Run ();
+			Application.Shutdown ();
+		}
+
+		[Fact]
+		public void PosCombine_Will_Throws ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			var t = Application.Top;
+
+			var w = new Window ("w") {
+				X = Pos.Left (t) + 2,
+				Y = Pos.Top (t) + 2
+			};
+			var f = new FrameView ("f");
+			var v1 = new View ("v1") {
+				X = Pos.Left (w) + 2,
+				Y = Pos.Top (w) + 2
+			};
+			var v2 = new View ("v2") {
+				X = Pos.Left (v1) + 2,
+				Y = Pos.Top (v1) + 2
+			};
+
+			f.Add (v1); // v2 not added
+			w.Add (f);
+			t.Add (w);
+
+			f.X = Pos.X (v2) - Pos.X (v1);
+			f.Y = Pos.Y (v2) - Pos.Y (v1);
+
+			Assert.Throws<InvalidOperationException> (() => Application.Run ());
+			Application.Shutdown ();
+		}
 	}
 }

+ 659 - 0
UnitTests/TextFieldTests.cs

@@ -0,0 +1,659 @@
+using Xunit;
+
+namespace Terminal.Gui {
+	public class TextFieldTests {
+		private TextField _textField;
+
+		public TextFieldTests ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			//                                     1         2         3 
+			//                           01234567890123456789012345678901=32 (Length)
+			_textField = new TextField ("TAB to jump between text fields.");
+		}
+
+		[Fact]
+		public void Changing_SelectedStart_Or_CursorPosition_Update_SelectedLength_And_SelectedText ()
+		{
+			_textField.SelectedStart = 2;
+			Assert.Equal (32, _textField.CursorPosition);
+			Assert.Equal (30, _textField.SelectedLength);
+			Assert.Equal ("B to jump between text fields.", _textField.SelectedText);
+			_textField.CursorPosition = 20;
+			Assert.Equal (2, _textField.SelectedStart);
+			Assert.Equal (18, _textField.SelectedLength);
+			Assert.Equal ("B to jump between ", _textField.SelectedText);
+		}
+
+		[Fact]
+		public void SelectedStart_With_Value_Less_Than_Minus_One_Changes_To_Minus_One ()
+		{
+			_textField.SelectedStart = -2;
+			Assert.Equal (-1, _textField.SelectedStart);
+			Assert.Equal (0, _textField.SelectedLength);
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void SelectedStart_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length ()
+		{
+			_textField.CursorPosition = 2;
+			_textField.SelectedStart = 33;
+			Assert.Equal (32, _textField.SelectedStart);
+			Assert.Equal (30, _textField.SelectedLength);
+			Assert.Equal ("B to jump between text fields.", _textField.SelectedText);
+		}
+
+		[Fact]
+		public void SelectedStart_And_CursorPosition_With_Value_Greater_Than_Text_Length_Changes_Both_To_Text_Length ()
+		{
+			_textField.CursorPosition = 33;
+			_textField.SelectedStart = 33;
+			Assert.Equal (32, _textField.CursorPosition);
+			Assert.Equal (32, _textField.SelectedStart);
+			Assert.Equal (0, _textField.SelectedLength);
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero ()
+		{
+			_textField.CursorPosition = -1;
+			Assert.Equal (0, _textField.CursorPosition);
+			Assert.Equal (0, _textField.SelectedLength);
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length ()
+		{
+			_textField.CursorPosition = 33;
+			Assert.Equal (32, _textField.CursorPosition);
+			Assert.Equal (0, _textField.SelectedLength);
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void WordForward_With_No_Selection ()
+		{
+			_textField.CursorPosition = 0;
+			var iteration = 0;
+
+			while (_textField.CursorPosition < _textField.Text.Length) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (4, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (7, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (20, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (32, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_No_Selection ()
+		{
+			_textField.CursorPosition = _textField.Text.Length;
+			var iteration = 0;
+
+			while (_textField.CursorPosition > 0) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (7, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (4, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (0, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_Selection ()
+		{
+			_textField.CursorPosition = 0;
+			_textField.SelectedStart = 0;
+			var iteration = 0;
+
+			while (_textField.CursorPosition < _textField.Text.Length) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (4, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (4, _textField.SelectedLength);
+					Assert.Equal ("TAB ", _textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (7, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (7, _textField.SelectedLength);
+					Assert.Equal ("TAB to ", _textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (12, _textField.SelectedLength);
+					Assert.Equal ("TAB to jump ", _textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (20, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (20, _textField.SelectedLength);
+					Assert.Equal ("TAB to jump between ", _textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (25, _textField.SelectedLength);
+					Assert.Equal ("TAB to jump between text ", _textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (32, _textField.CursorPosition);
+					Assert.Equal (0, _textField.SelectedStart);
+					Assert.Equal (32, _textField.SelectedLength);
+					Assert.Equal ("TAB to jump between text fields.", _textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_Selection ()
+		{
+			_textField.CursorPosition = _textField.Text.Length;
+			_textField.SelectedStart = _textField.Text.Length;
+			var iteration = 0;
+
+			while (_textField.CursorPosition > 0) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (7, _textField.SelectedLength);
+					Assert.Equal ("fields.", _textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (12, _textField.SelectedLength);
+					Assert.Equal ("text fields.", _textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (20, _textField.SelectedLength);
+					Assert.Equal ("between text fields.", _textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (7, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (25, _textField.SelectedLength);
+					Assert.Equal ("jump between text fields.", _textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (4, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (28, _textField.SelectedLength);
+					Assert.Equal ("to jump between text fields.", _textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (0, _textField.CursorPosition);
+					Assert.Equal (32, _textField.SelectedStart);
+					Assert.Equal (32, _textField.SelectedLength);
+					Assert.Equal ("TAB to jump between text fields.", _textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text ()
+		{
+			_textField.CursorPosition = 10;
+			_textField.SelectedStart = 10;
+			var iteration = 0;
+
+			while (_textField.CursorPosition < _textField.Text.Length) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (2, _textField.SelectedLength);
+					Assert.Equal ("p ", _textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (10, _textField.SelectedLength);
+					Assert.Equal ("p between ", _textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (15, _textField.SelectedLength);
+					Assert.Equal ("p between text ", _textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (32, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (22, _textField.SelectedLength);
+					Assert.Equal ("p between text fields.", _textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text ()
+		{
+			_textField.CursorPosition = 10;
+			_textField.SelectedStart = 10;
+			var iteration = 0;
+
+			while (_textField.CursorPosition > 0) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (7, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (3, _textField.SelectedLength);
+					Assert.Equal ("jum", _textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (4, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (6, _textField.SelectedLength);
+					Assert.Equal ("to jum", _textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (0, _textField.CursorPosition);
+					Assert.Equal (10, _textField.SelectedStart);
+					Assert.Equal (10, _textField.SelectedLength);
+					Assert.Equal ("TAB to jum", _textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter ()
+		{
+			//                           1         2         3         4         5    
+			//                 0123456789012345678901234567890123456789012345678901234=55 (Length)
+			_textField.Text = "TAB   t  o  jump         b  etween    t ext   f ields .";
+			_textField.CursorPosition = 0;
+			var iteration = 0;
+
+			while (_textField.CursorPosition < _textField.Text.Length) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (6, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (9, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (28, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (38, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 6:
+					Assert.Equal (40, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 7:
+					Assert.Equal (46, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 8:
+					Assert.Equal (48, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 9:
+					Assert.Equal (55, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter ()
+		{
+			//                           1         2         3         4         5    
+			//                 0123456789012345678901234567890123456789012345678901234=55 (Length)
+			_textField.Text = "TAB   t  o  jump         b  etween    t ext   f ields .";
+			_textField.CursorPosition = _textField.Text.Length;
+			var iteration = 0;
+
+			while (_textField.CursorPosition > 0) {
+				_textField.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (54, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (48, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (46, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (40, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (38, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (28, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 6:
+					Assert.Equal (25, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 7:
+					Assert.Equal (12, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 8:
+					Assert.Equal (9, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 9:
+					Assert.Equal (6, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				case 10:
+					Assert.Equal (0, _textField.CursorPosition);
+					Assert.Equal (-1, _textField.SelectedStart);
+					Assert.Equal (0, _textField.SelectedLength);
+					Assert.Null (_textField.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_Null_If_No_Selection ()
+		{
+			_textField.SelectedStart = -1;
+			_textField.Copy ();
+			Assert.Null (_textField.SelectedText);
+			_textField.Cut ();
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_Not_Null_If_Has_Selection ()
+		{
+			_textField.SelectedStart = 20;
+			_textField.CursorPosition = 24;
+			_textField.Copy ();
+			Assert.Equal ("text", _textField.SelectedText);
+			_textField.Cut ();
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_And_Paste_With_Selection ()
+		{
+			_textField.SelectedStart = 20;
+			_textField.CursorPosition = 24;
+			_textField.Copy ();
+			Assert.Equal ("text", _textField.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.Paste ();
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.SelectedStart = 20;
+			_textField.Cut ();
+			_textField.Paste ();
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_And_Paste_With_No_Selection ()
+		{
+			_textField.SelectedStart = 20;
+			_textField.CursorPosition = 24;
+			_textField.Copy ();
+			Assert.Equal ("text", _textField.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.SelectedStart = -1;
+			_textField.Paste ();
+			Assert.Equal ("TAB to jump between texttext fields.", _textField.Text);
+			_textField.SelectedStart = 24;
+			_textField.Cut ();
+			Assert.Null (_textField.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.SelectedStart = -1;
+			_textField.Paste ();
+			Assert.Equal ("TAB to jump between texttext fields.", _textField.Text);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut__Not_Allowed_If_Secret_Is_True ()
+		{
+			_textField.Secret = true;
+			_textField.SelectedStart = 20;
+			_textField.CursorPosition = 24;
+			_textField.Copy ();
+			Assert.Null (_textField.SelectedText);
+			_textField.Cut ();
+			Assert.Null (_textField.SelectedText);
+			_textField.Secret = false;
+			_textField.Copy ();
+			Assert.Equal ("text", _textField.SelectedText);
+			_textField.Cut ();
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void Paste_Always_Clear_The_SelectedText ()
+		{
+			_textField.SelectedStart = 20;
+			_textField.CursorPosition = 24;
+			_textField.Copy ();
+			Assert.Equal ("text", _textField.SelectedText);
+			_textField.Paste ();
+			Assert.Null (_textField.SelectedText);
+		}
+
+		[Fact]
+		public void TextChanging_Event ()
+		{
+			bool cancel = true;
+
+			_textField.TextChanging += (e) => {
+				Assert.Equal ("changing", e.NewText);
+				if (cancel) {
+					e.Cancel = true;
+				}
+			};
+
+			_textField.Text = "changing";
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			cancel = false;
+			_textField.Text = "changing";
+			Assert.Equal ("changing", _textField.Text);
+		}
+
+		[Fact]
+		public void TextChanged_Event ()
+		{
+			_textField.TextChanged += (e) => {
+				Assert.Equal ("TAB to jump between text fields.", e);
+			};
+
+			_textField.Text = "changed";
+			Assert.Equal ("changed", _textField.Text);
+		}
+
+		[Fact]
+		public void Used_Is_True_By_Default ()
+		{
+			_textField.CursorPosition = 10;
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u
+			Assert.Equal ("TAB to jumup between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s
+			Assert.Equal ("TAB to jumusp between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e
+			Assert.Equal ("TAB to jumusep between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d
+			Assert.Equal ("TAB to jumusedp between text fields.", _textField.Text);
+
+		}
+
+		[Fact]
+		public void Used_Is_False ()
+		{
+			_textField.Used = false;
+			_textField.CursorPosition = 10;
+			Assert.Equal ("TAB to jump between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u
+			Assert.Equal ("TAB to jumu between text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s
+			Assert.Equal ("TAB to jumusbetween text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e
+			Assert.Equal ("TAB to jumuseetween text fields.", _textField.Text);
+			_textField.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d
+			Assert.Equal ("TAB to jumusedtween text fields.", _textField.Text);
+
+		}
+	}
+}

+ 797 - 0
UnitTests/TextViewTests.cs

@@ -0,0 +1,797 @@
+using Xunit;
+
+namespace Terminal.Gui {
+	public class TextViewTests {
+		private TextView _textView;
+
+		public TextViewTests ()
+		{
+			Application.Init (new FakeDriver (), new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+			//                   1         2         3 
+			//         01234567890123456789012345678901=32 (Length)
+			var txt = "TAB to jump between text fields.";
+			var buff = new byte [txt.Length];
+			for (int i = 0; i < txt.Length; i++) {
+				buff [i] = (byte)txt [i];
+			}
+			var ms = new System.IO.MemoryStream (buff).ToArray ();
+			_textView = new TextView () { Width = 30, Height = 10 };
+			_textView.Text = ms;
+		}
+
+		[Fact]
+		public void Changing_Selection_Or_CursorPosition_Update_SelectedLength_And_SelectedText ()
+		{
+			_textView.SelectionStartColumn = 2;
+			_textView.SelectionStartRow = 0;
+			Assert.Equal (0, _textView.CursorPosition.X);
+			Assert.Equal (0, _textView.CursorPosition.Y);
+			Assert.Equal (2, _textView.SelectedLength);
+			Assert.Equal ("TA", _textView.SelectedText);
+			_textView.CursorPosition = new Point (20, 0);
+			Assert.Equal (2, _textView.SelectionStartColumn);
+			Assert.Equal (0, _textView.SelectionStartRow);
+			Assert.Equal (18, _textView.SelectedLength);
+			Assert.Equal ("B to jump between ", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Selection_With_Value_Less_Than_Zero_Changes_To_Zero ()
+		{
+			_textView.SelectionStartColumn = -2;
+			_textView.SelectionStartRow = -2;
+			Assert.Equal (0, _textView.SelectionStartColumn);
+			Assert.Equal (0, _textView.SelectionStartRow);
+			Assert.Equal (0, _textView.SelectedLength);
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Selection_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length ()
+		{
+			_textView.CursorPosition = new Point (2, 0);
+			_textView.SelectionStartColumn = 33;
+			_textView.SelectionStartRow = 1;
+			Assert.Equal (32, _textView.SelectionStartColumn);
+			Assert.Equal (0, _textView.SelectionStartRow);
+			Assert.Equal (30, _textView.SelectedLength);
+			Assert.Equal ("B to jump between text fields.", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Selection_With_Empty_Text ()
+		{
+			_textView = new TextView ();
+			_textView.CursorPosition = new Point (2, 0);
+			_textView.SelectionStartColumn = 33;
+			_textView.SelectionStartRow = 1;
+			Assert.Equal (0, _textView.SelectionStartColumn);
+			Assert.Equal (0, _textView.SelectionStartRow);
+			Assert.Equal (0, _textView.SelectedLength);
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Selection_And_CursorPosition_With_Value_Greater_Than_Text_Length_Changes_Both_To_Text_Length ()
+		{
+			_textView.CursorPosition = new Point (33, 2);
+			_textView.SelectionStartColumn = 33;
+			_textView.SelectionStartRow = 33;
+			Assert.Equal (32, _textView.CursorPosition.X);
+			Assert.Equal (0, _textView.CursorPosition.Y);
+			Assert.Equal (32, _textView.SelectionStartColumn);
+			Assert.Equal (0, _textView.SelectionStartRow);
+			Assert.Equal (0, _textView.SelectedLength);
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void CursorPosition_With_Value_Less_Than_Zero_Changes_To_Zero ()
+		{
+			_textView.CursorPosition = new Point (-1, -1);
+			Assert.Equal (0, _textView.CursorPosition.X);
+			Assert.Equal (0, _textView.CursorPosition.Y);
+			Assert.Equal (0, _textView.SelectedLength);
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void CursorPosition_With_Value_Greater_Than_Text_Length_Changes_To_Text_Length ()
+		{
+			_textView.CursorPosition = new Point (33, 1);
+			Assert.Equal (32, _textView.CursorPosition.X);
+			Assert.Equal (0, _textView.CursorPosition.Y);
+			Assert.Equal (0, _textView.SelectedLength);
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void WordForward_With_No_Selection ()
+		{
+			_textView.CursorPosition = new Point (0, 0);
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X < _textView.Text.Length) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (4, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (7, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (20, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (32, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_No_Selection ()
+		{
+			_textView.CursorPosition = new Point (_textView.Text.Length, 0);
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X > 0) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (7, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (4, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (0, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_Selection ()
+		{
+			_textView.CursorPosition = new Point (0, 0);
+			_textView.SelectionStartColumn = 0;
+			_textView.SelectionStartRow = 0;
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X < _textView.Text.Length) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (4, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (4, _textView.SelectedLength);
+					Assert.Equal ("TAB ", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (7, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (7, _textView.SelectedLength);
+					Assert.Equal ("TAB to ", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (12, _textView.SelectedLength);
+					Assert.Equal ("TAB to jump ", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (20, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (20, _textView.SelectedLength);
+					Assert.Equal ("TAB to jump between ", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (25, _textView.SelectedLength);
+					Assert.Equal ("TAB to jump between text ", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (32, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (32, _textView.SelectedLength);
+					Assert.Equal ("TAB to jump between text fields.", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_Selection ()
+		{
+			_textView.CursorPosition = new Point (_textView.Text.Length, 0);
+			_textView.SelectionStartColumn = _textView.Text.Length;
+			_textView.SelectionStartRow = 0;
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X > 0) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (7, _textView.SelectedLength);
+					Assert.Equal ("fields.", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (12, _textView.SelectedLength);
+					Assert.Equal ("text fields.", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (20, _textView.SelectedLength);
+					Assert.Equal ("between text fields.", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (7, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (25, _textView.SelectedLength);
+					Assert.Equal ("jump between text fields.", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (4, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (28, _textView.SelectedLength);
+					Assert.Equal ("to jump between text fields.", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (0, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (32, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (32, _textView.SelectedLength);
+					Assert.Equal ("TAB to jump between text fields.", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text ()
+		{
+			_textView.CursorPosition = new Point (10, 0);
+			_textView.SelectionStartColumn = 10;
+			_textView.SelectionStartRow = 0;
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X < _textView.Text.Length) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (2, _textView.SelectedLength);
+					Assert.Equal ("p ", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (20, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (10, _textView.SelectedLength);
+					Assert.Equal ("p between ", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (15, _textView.SelectedLength);
+					Assert.Equal ("p between text ", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (32, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (22, _textView.SelectedLength);
+					Assert.Equal ("p between text fields.", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_The_Same_Values_For_SelectedStart_And_CursorPosition_And_Not_Starting_At_Beginning_Of_The_Text ()
+		{
+			_textView.CursorPosition = new Point (10, 0);
+			_textView.SelectionStartColumn = 10;
+			_textView.SelectionStartRow = 0;
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X > 0) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask | Key.ShiftMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (7, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (3, _textView.SelectedLength);
+					Assert.Equal ("jum", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (4, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (6, _textView.SelectedLength);
+					Assert.Equal ("to jum", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (0, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (10, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (10, _textView.SelectedLength);
+					Assert.Equal ("TAB to jum", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordForward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter ()
+		{
+			//                          1         2         3         4         5    
+			//                0123456789012345678901234567890123456789012345678901234=55 (Length)
+			_textView.Text = "TAB   t  o  jump         b  etween    t ext   f ields .";
+			_textView.CursorPosition = new Point (0, 0);
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X < _textView.Text.Length) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorRight | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (6, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (9, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (28, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (38, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 6:
+					Assert.Equal (40, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 7:
+					Assert.Equal (46, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 8:
+					Assert.Equal (48, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 9:
+					Assert.Equal (55, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void WordBackward_With_No_Selection_And_With_More_Than_Only_One_Whitespace_And_With_Only_One_Letter ()
+		{
+			//                          1         2         3         4         5    
+			//                0123456789012345678901234567890123456789012345678901234=55 (Length)
+			_textView.Text = "TAB   t  o  jump         b  etween    t ext   f ields .";
+			_textView.CursorPosition = new Point (_textView.Text.Length, 0);
+			var iteration = 0;
+
+			while (_textView.CursorPosition.X > 0) {
+				_textView.ProcessKey (new KeyEvent (Key.CursorLeft | Key.CtrlMask, new KeyModifiers ()));
+				switch (iteration) {
+				case 0:
+					Assert.Equal (54, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 1:
+					Assert.Equal (48, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 2:
+					Assert.Equal (46, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 3:
+					Assert.Equal (40, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 4:
+					Assert.Equal (38, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 5:
+					Assert.Equal (28, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 6:
+					Assert.Equal (25, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 7:
+					Assert.Equal (12, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 8:
+					Assert.Equal (9, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 9:
+					Assert.Equal (6, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				case 10:
+					Assert.Equal (0, _textView.CursorPosition.X);
+					Assert.Equal (0, _textView.CursorPosition.Y);
+					Assert.Equal (0, _textView.SelectionStartColumn);
+					Assert.Equal (0, _textView.SelectionStartRow);
+					Assert.Equal (0, _textView.SelectedLength);
+					Assert.Equal ("", _textView.SelectedText);
+					break;
+				}
+				iteration++;
+			}
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_Null_If_No_Selection ()
+		{
+			_textView.SelectionStartColumn = 0;
+			_textView.SelectionStartRow = 0;
+			_textView.Copy ();
+			Assert.Equal ("", _textView.SelectedText);
+			_textView.Cut ();
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_Not_Null_If_Has_Selection ()
+		{
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.CursorPosition = new Point (24, 0);
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			_textView.Cut ();
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_And_Paste_With_Selection ()
+		{
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.CursorPosition = new Point (24, 0);
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.Paste ();
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.Cut ();
+			_textView.Paste ();
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+		}
+
+		[Fact]
+		public void Copy_Or_Cut_And_Paste_With_No_Selection ()
+		{
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.CursorPosition = new Point (24, 0);
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.SelectionStartColumn = 0;
+			_textView.SelectionStartRow = 0;
+			Assert.True (_textView.Selecting);
+			_textView.Selecting = false;
+			_textView.Paste ();
+			Assert.Equal ("TAB to jump between texttext fields.", _textView.Text);
+			_textView.SelectionStartColumn = 24;
+			_textView.SelectionStartRow = 0;
+			_textView.Cut ();
+			Assert.Equal ("", _textView.SelectedText);
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.SelectionStartColumn = 0;
+			_textView.SelectionStartRow = 0;
+			Assert.True (_textView.Selecting);
+			_textView.Selecting = false;
+			_textView.Paste ();
+			Assert.Equal ("TAB to jump between texttext fields.", _textView.Text);
+		}
+
+		[Fact]
+		public void Cut_Not_Allowed_If_ReadOnly_Is_True ()
+		{
+			_textView.ReadOnly = true;
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.CursorPosition = new Point (24, 0);
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			_textView.Cut (); // Selecting is set to false after Cut.
+			Assert.Equal ("", _textView.SelectedText);
+			_textView.ReadOnly = false;
+			Assert.False (_textView.Selecting);
+			_textView.Selecting = true; // Needed to set Selecting to true.
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			_textView.Cut ();
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void Paste_Always_Clear_The_SelectedText ()
+		{
+			_textView.SelectionStartColumn = 20;
+			_textView.SelectionStartRow = 0;
+			_textView.CursorPosition = new Point (24, 0);
+			_textView.Copy ();
+			Assert.Equal ("text", _textView.SelectedText);
+			_textView.Paste ();
+			Assert.Equal ("", _textView.SelectedText);
+		}
+
+		[Fact]
+		public void TextChanged_Event ()
+		{
+			_textView.TextChanged += () => {
+				if (_textView.Text == "changing") {
+					Assert.Equal ("changing", _textView.Text);
+					_textView.Text = "changed";
+				}
+			};
+
+			_textView.Text = "changing";
+			Assert.Equal ("changed", _textView.Text);
+		}
+
+		[Fact]
+		public void Used_Is_True_By_Default ()
+		{
+			_textView.CursorPosition = new Point (10, 0);
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u
+			Assert.Equal ("TAB to jumup between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s
+			Assert.Equal ("TAB to jumusp between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e
+			Assert.Equal ("TAB to jumusep between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d
+			Assert.Equal ("TAB to jumusedp between text fields.", _textView.Text);
+		}
+
+		[Fact]
+		public void Used_Is_False ()
+		{
+			_textView.Used = false;
+			_textView.CursorPosition = new Point (10, 0);
+			Assert.Equal ("TAB to jump between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x75, new KeyModifiers ())); // u
+			Assert.Equal ("TAB to jumu between text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x73, new KeyModifiers ())); // s
+			Assert.Equal ("TAB to jumusbetween text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x65, new KeyModifiers ())); // e
+			Assert.Equal ("TAB to jumuseetween text fields.", _textView.Text);
+			_textView.ProcessKey (new KeyEvent ((Key)0x64, new KeyModifiers ())); // d
+			Assert.Equal ("TAB to jumusedtween text fields.", _textView.Text);
+		}
+	}
+}

+ 12 - 13
UnitTests/ViewTests.cs

@@ -49,10 +49,10 @@ namespace Terminal.Gui {
 			Assert.Equal (new Rect (0, 0, 0, 0), r.Frame);
 			Assert.Null (r.Focused);
 			Assert.Null (r.ColorScheme);
-			Assert.Null (r.Height);
-			Assert.Null (r.Height);
-			Assert.Null (r.X);
-			Assert.Null (r.Y);
+			Assert.NotNull (r.Width);	// All view Dim are initialized now,
+			Assert.NotNull (r.Height);	// avoiding Dim errors.
+			Assert.NotNull (r.X);		// All view Pos are initialized now,
+			Assert.NotNull (r.Y);		// avoiding Pos errors.
 			Assert.False (r.IsCurrentTop);
 			Assert.Empty (r.Id);
 			Assert.Empty (r.Subviews);
@@ -72,10 +72,10 @@ namespace Terminal.Gui {
 			Assert.Equal (new Rect (1, 2, 3, 4), r.Frame);
 			Assert.Null (r.Focused);
 			Assert.Null (r.ColorScheme);
-			Assert.Null (r.Height);
-			Assert.Null (r.Height);
-			Assert.Null (r.X);
-			Assert.Null (r.Y);
+			Assert.NotNull (r.Width);
+			Assert.NotNull (r.Height);
+			Assert.NotNull (r.X);
+			Assert.NotNull (r.Y);
 			Assert.False (r.IsCurrentTop);
 			Assert.Empty (r.Id);
 			Assert.Empty (r.Subviews);
@@ -83,7 +83,6 @@ namespace Terminal.Gui {
 			Assert.False (r.WantMousePositionReports);
 			Assert.Null (r.SuperView);
 			Assert.Null (r.MostFocused);
-
 		}
 
 		[Fact]
@@ -1057,10 +1056,10 @@ namespace Terminal.Gui {
 
 			// Constructor
 			view = new View (1, 2, "");
-			Assert.Null (view.X);
-			Assert.Null (view.Y);
-			Assert.Null (view.Width);
-			Assert.Null (view.Height);
+			Assert.NotNull (view.X);
+			Assert.NotNull (view.Y);
+			Assert.NotNull (view.Width);
+			Assert.NotNull (view.Height);
 			Assert.False (view.Frame.IsEmpty);
 			Assert.True (view.Bounds.IsEmpty);