Browse Source

Improved a better clipped screen. Fixes some bugs with ScrollView, Menu. Added some virtual methods. (#410)

* Improved a better clipped screen. Fixes some bugs with ScrollView, Menu. Added some virtual methods.

* Added some more key features, like shift. Cleaning and updating some stuffs .Added more features to TextField.

* Closes the menu even in a button pressed in another view than menu.

* Added a OnKeyPress action. Now all the keys events are properly mapped to the keys modifiers. Fixed a issue which keys like (ã, á, â) aren't correctly written.

* Fixed an issue with the shift flag for keys ControlA_Z.

* Ensures quiting the terminal if no other option is provided by pressing Ctrl-Q

* Ensures the exception is thrown before ordered.Reverse.

* Changed Button4 To Button3 for CursesDriver compatibility.

* Added support for word selection through keyboard and the mouse. With triple click all text is selected.

* Changed AllowNewLine to AllowWrap.

* Fix topological sort in view class (#413)

* AllowWrap removed and keys issues fixed.

* Removing ordered.Reverse (); Something went wrong.

* Fixes FrameView title.

* Reverted some MapKeyModifiers that prevented the display of some characters.

* Avoiding open the menu-bar every time we typing (€@£§). Alt key now only highlight the menu-bar without open it.

* Fixes hot-key issue preventing menu closing after opened.

* Curses now supports hot-keys and simulates AltMask with Alt+Space.  Also supports shift and ctrl combinations to use with text selection.

* Maintains the menu highlighted while focused.

* Removed the IsOutBounds method. This feature is for a future presentation.

Co-authored-by: En3Tho <[email protected]>
BDisp 5 years ago
parent
commit
e6c5b2599a

+ 68 - 8
Designer/Program.cs

@@ -15,19 +15,45 @@ namespace Designer {
 			MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
 		}
 
+		static void Copy ()
+		{
+			TextField textField = menu.LastFocused as TextField;
+			if (textField != null && textField.SelectedLength != 0) {
+				textField.Copy ();
+			}
+		}
+
+		static void Cut ()
+		{
+			TextField textField = menu.LastFocused as TextField;
+			if (textField != null && textField.SelectedLength != 0) {
+				textField.Cut ();
+			}
+		}
+
+		static void Paste ()
+		{
+			TextField textField = menu.LastFocused as TextField;
+			if (textField != null) {
+				textField.Paste ();
+			}
+		}
+
+		public static MenuBar menu;
+
 		public static void Main (string [] args)
 		{
 			Application.Init ();
 
-			var menu = new MenuBar (new MenuBarItem [] {
+			menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Close", "", () => Close ()),
 					new MenuItem ("_Quit", "", () => { Application.RequestStop (); })
 				}),
 				new MenuBarItem ("_Edit", new MenuItem [] {
-					new MenuItem ("_Copy", "", null),
-					new MenuItem ("C_ut", "", null),
-					new MenuItem ("_Paste", "", null)
+					new MenuItem ("_Copy", "", Copy),
+					new MenuItem ("C_ut", "", Cut),
+					new MenuItem ("_Paste", "", Paste)
 				}),
 			});
 
@@ -36,18 +62,22 @@ namespace Designer {
 				X = Pos.Left (login),
 				Y = Pos.Bottom (login) + 1
 			};
+			var test = new Label ("Test: ") {
+				X = Pos.Left (login),
+				Y = Pos.Bottom (password) + 1
+			};
 
 			var surface = new Surface () {
 				X = 0,
 				Y = 1,
-				Width = Dim.Percent (80),
-				Height = Dim.Fill ()
+				Width = Dim.Percent (50),
+				Height = Dim.Percent (50)
 			};
 
 			var loginText = new TextField("") {
 				X = Pos.Right(password),
 				Y = Pos.Top(login),
-				Width = 40,
+				Width = Dim.Percent(90),
 				ColorScheme = new ColorScheme() {
 					Focus = Attribute.Make(Color.BrightYellow, Color.DarkGray),
 					Normal = Attribute.Make(Color.Green, Color.BrightYellow),
@@ -55,6 +85,10 @@ namespace Designer {
 					HotNormal = Attribute.Make(Color.Red, Color.BrightRed),
 				},
 			};
+			loginText.MouseEnter += LoginText_MouseEnter;
+			loginText.MouseLeave += LoginText_MouseLeave;
+			loginText.Enter += LoginText_Enter;
+			loginText.Leave += LoginText_Leave;
 
 			var passText = new TextField ("") {
 				Secret = true,
@@ -63,9 +97,35 @@ namespace Designer {
 				Width = Dim.Width (loginText)
 			};
 
-			surface.Add (login, password, loginText, passText);
+			var testText = new TextField ("") {
+				X = Pos.Left (loginText),
+				Y = Pos.Top (test),
+				Width = Dim.Width (loginText)
+			};
+
+			surface.Add (login, password, test, loginText, passText, testText);
 			Application.Top.Add (menu, surface);
 			Application.Run ();
 		}
+
+		private static void LoginText_Leave (object sender, EventArgs e)
+		{
+			((TextField)sender).Text = $"Leaving from: {sender}";
+		}
+
+		private static void LoginText_Enter (object sender, EventArgs e)
+		{
+			((TextField)sender).Text = $"Entering in: {sender}";
+		}
+
+		private static void LoginText_MouseLeave (object sender, MouseEvent e)
+		{
+			((TextField)sender).Text = $"Mouse leave at X: {e.X}; Y: {e.Y} HasFocus: {e.View.HasFocus}";
+		}
+
+		private static void LoginText_MouseEnter (object sender, MouseEvent e)
+		{
+			((TextField)sender).Text = $"Mouse enter at X: {e.X}; Y: {e.Y} HasFocus: {e.View.HasFocus}";
+		}
 	}
 }

+ 39 - 14
Example/demo.cs

@@ -413,11 +413,11 @@ static class Demo {
 	#endregion
 
 
-	#region OnKeyDown / OnKeyUp Demo
-	private static void OnKeyDownUpDemo ()
+	#region OnKeyDown / OnKeyPress / OnKeyUp Demo
+	private static void OnKeyDownPressUpDemo ()
 	{
 		var container = new Dialog (
-			"OnKeyDown & OnKeyUp demo", 80, 20,
+			"OnKeyDown & OnKeyPress & OnKeyUp demo", 80, 20,
 			new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
 			Width = Dim.Fill (),
 			Height = Dim.Fill (),
@@ -433,20 +433,45 @@ static class Demo {
 		listView.ColorScheme = Colors.TopLevel;
 		container.Add (listView);
 
-		void KeyUpDown (KeyEvent keyEvent, string updown)
+		void KeyDownPressUp (KeyEvent keyEvent, string updown)
 		{
-			if ((keyEvent.Key & Key.CtrlMask) != 0) {
-				list.Add ($"Key{updown,-4}: Ctrl ");
-			} else if ((keyEvent.Key & Key.AltMask) != 0) {
-				list.Add ($"Key{updown,-4}: Alt ");
-			} else {
-				list.Add ($"Key{updown,-4}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
+			const int ident = -5;
+			switch (updown) {
+			case "Down":
+			case "Up":
+			case "Press":
+				var msg = $"Key{updown,ident}: ";
+				if ((keyEvent.Key & Key.ShiftMask) != 0)
+					msg += "Shift ";
+				if ((keyEvent.Key & Key.CtrlMask) != 0)
+					msg += "Ctrl ";
+				if ((keyEvent.Key & Key.AltMask) != 0)
+					msg += "Alt ";
+				msg += $"{(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}";
+				list.Add (msg);
+
+				break;
+
+			default:
+				if ((keyEvent.Key & Key.ShiftMask) != 0) {
+					list.Add ($"Key{updown,ident}: Shift ");
+				} else if ((keyEvent.Key & Key.CtrlMask) != 0) {
+					list.Add ($"Key{updown,ident}: Ctrl ");
+				} else if ((keyEvent.Key & Key.AltMask) != 0) {
+					list.Add ($"Key{updown,ident}: Alt ");
+				} else {
+					list.Add ($"Key{updown,ident}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
+				}
+
+				break;
 			}
 			listView.MoveDown ();
 		}
 
-		container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Down");
-		container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Up");
+
+		container.OnKeyDown += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Down");
+		container.OnKeyPress += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Press");
+		container.OnKeyUp += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Up");
 		Application.Run (container);
 	}
 	#endregion
@@ -518,7 +543,7 @@ static class Demo {
 			}),
 			new MenuBarItem ("A_ssorted", new MenuItem [] {
 				new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()),
-				new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ())
+				new MenuItem ("_OnKeyDown/Press/Up", "", () => OnKeyDownPressUpDemo ())
 			}),
 			new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
 				new MenuItem ("SubMenu1Item_1",
@@ -565,7 +590,7 @@ static class Demo {
 			new StatusItem(Key.F1, "~F1~ Help", () => Help()),
 			new StatusItem(Key.F2, "~F2~ Load", null),
 			new StatusItem(Key.F3, "~F3~ Save", null),
-			new StatusItem(Key.ControlX, "~^X~ Quit", () => { if (Quit ()) top.Running = false; }),
+			new StatusItem(Key.ControlQ, "~^Q~ Quit", () => { if (Quit ()) top.Running = false; }),
 		}) {
 			Parent = null,
 		};

+ 2 - 2
StandaloneExample/Program.cs

@@ -111,7 +111,7 @@ class Demo {
 			Y = Pos.Top (password),
 			Width = Dim.Width (loginText)
 		};
-		
+
 		// Add some content
 		container.Add (
 			login,
@@ -162,7 +162,7 @@ class Demo {
 
 	static void Close ()
 	{
-		MessageBox.ErrorQuery (50, 5, "Error", "There is nothing to close", "Ok");
+		MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
 	}
 
 	public static Label ml;

+ 1 - 1
StandaloneExample/StandaloneExample.csproj

@@ -7,6 +7,6 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Terminal.Gui" Version="0.17.0" />
+    <PackageReference Include="Terminal.Gui" Version="0.81.0" />
   </ItemGroup>
 </Project>

+ 25 - 0
StandaloneExample/StandaloneExample.sln

@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30011.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "StandaloneExample", "StandaloneExample.csproj", "{8DC768EF-530D-4261-BD35-FC41E13B041E}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{8DC768EF-530D-4261-BD35-FC41E13B041E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{8DC768EF-530D-4261-BD35-FC41E13B041E}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{8DC768EF-530D-4261-BD35-FC41E13B041E}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{8DC768EF-530D-4261-BD35-FC41E13B041E}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {C9C434AC-B7E4-43AB-834A-F9489766FDFF}
+	EndGlobalSection
+EndGlobal

+ 161 - 25
Terminal.Gui/Core.cs

@@ -151,6 +151,44 @@ namespace Terminal.Gui {
 		{
 			return false;
 		}
+
+		/// <summary>
+		/// Method invoked when a mouse event is generated for the first time.
+		/// </summary>
+		/// <param name="mouseEvent"></param>
+		/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
+		public virtual bool OnMouseEnter (MouseEvent mouseEvent)
+		{
+			return false;
+		}
+
+		/// <summary>
+		/// Method invoked when a mouse event is generated for the last time.
+		/// </summary>
+		/// <param name="mouseEvent"></param>
+		/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
+		public virtual bool OnMouseLeave (MouseEvent mouseEvent)
+		{
+			return false;
+		}
+
+		/// <summary>
+		/// Method invoked when a view gets focus.
+		/// </summary>
+		/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
+		public virtual bool OnEnter ()
+		{
+			return false;
+		}
+
+		/// <summary>
+		/// Method invoked when a view loses focus.
+		/// </summary>
+		/// <returns><c>true</c>, if the event was handled, <c>false</c> otherwise.</returns>
+		public virtual bool OnLeave ()
+		{
+			return false;
+		}
 	}
 
 	/// <summary>
@@ -253,12 +291,22 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Event fired when the view get focus.
 		/// </summary>
-		public event EventHandler OnEnter;
+		public event EventHandler Enter;
 
 		/// <summary>
 		/// Event fired when the view lost focus.
 		/// </summary>
-		public event EventHandler OnLeave;
+		public event EventHandler Leave;
+
+		/// <summary>
+		/// Event fired when the view receives the mouse event for the first time.
+		/// </summary>
+		public event EventHandler<MouseEvent> MouseEnter;
+
+		/// <summary>
+		/// Event fired when the view loses mouse event for the last time.
+		/// </summary>
+		public event EventHandler<MouseEvent> MouseLeave;
 
 		internal Direction FocusDirection {
 			get => SuperView?.FocusDirection ?? focusDirection;
@@ -308,6 +356,10 @@ namespace Terminal.Gui {
 		/// <value><c>true</c> if want mouse position reports; otherwise, <c>false</c>.</value>
 		public virtual bool WantMousePositionReports { get; set; } = false;
 
+		/// <summary>
+		/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.View"/> want continuous button pressed event.
+		/// </summary>
+		public virtual bool WantContinuousButtonPressed { get; set; } = false;
 		/// <summary>
 		/// Gets or sets the frame for the view.
 		/// </summary>
@@ -857,22 +909,34 @@ namespace Terminal.Gui {
 			}
 			internal set {
 				if (base.HasFocus != value)
-					if (value == true)
-						OnEnter?.Invoke (this, new EventArgs ());
+					if (value)
+						OnEnter ();
 					else
-						OnLeave?.Invoke (this, new EventArgs ());
+						OnLeave ();
 					SetNeedsDisplay ();
 				base.HasFocus = value;
 
 				// Remove focus down the chain of subviews if focus is removed
-				if (value == false && focused != null) {
-					OnLeave?.Invoke (focused, new EventArgs ());
+				if (!value && focused != null) {
+					focused.OnLeave ();
 					focused.HasFocus = false;
 					focused = null;
 				}
 			}
 		}
 
+		public override bool OnEnter ()
+		{
+			Enter?.Invoke (this, new EventArgs ());
+			return base.OnEnter ();
+		}
+
+		public override bool OnLeave ()
+		{
+			Leave?.Invoke (this, new EventArgs ());
+			return base.OnLeave ();
+		}
+
 		/// <summary>
 		/// Returns the currently focused view inside this view, or null if nothing is focused.
 		/// </summary>
@@ -958,6 +1022,7 @@ namespace Terminal.Gui {
 							// FIXED: optimize this by computing the intersection of region and view.Bounds
 							if (view.layoutNeeded)
 								view.LayoutSubviews ();
+							Application.CurrentView = view;
 							view.Redraw (view.Bounds);
 						}
 						view.NeedDisplay = Rect.Empty;
@@ -1001,9 +1066,15 @@ namespace Terminal.Gui {
 			SuperView?.SetFocus(this);
 		}
 
+		/// <summary>
+		/// Invoked when a character key is pressed and occurs after the key down event.
+		/// </summary>
+		public Action<KeyEvent> OnKeyPress;
+
 		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
+			OnKeyPress?.Invoke (keyEvent);
 			if (Focused?.ProcessKey (keyEvent) == true)
 				return true;
 
@@ -1013,6 +1084,7 @@ namespace Terminal.Gui {
 		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
+			OnKeyPress?.Invoke (keyEvent);
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
@@ -1024,6 +1096,7 @@ namespace Terminal.Gui {
 		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
 		public override bool ProcessColdKey (KeyEvent keyEvent)
 		{
+			OnKeyPress?.Invoke (keyEvent);
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
@@ -1345,6 +1418,24 @@ namespace Terminal.Gui {
 		{
 			return $"{GetType ().Name}({Id})({Frame})";
 		}
+
+		public override bool OnMouseEnter (MouseEvent mouseEvent)
+		{
+			if (!base.OnMouseEnter (mouseEvent)) {
+				MouseEnter?.Invoke (this, mouseEvent);
+				return false;
+			}
+			return true;
+		}
+
+		public override bool OnMouseLeave (MouseEvent mouseEvent)
+		{
+			if (!base.OnMouseLeave (mouseEvent)) {
+				MouseLeave?.Invoke (this, mouseEvent);
+				return false;
+			}
+			return true;
+		}
 	}
 
 	/// <summary>
@@ -1444,8 +1535,9 @@ namespace Terminal.Gui {
 				return true;
 
 			switch (keyEvent.Key) {
-			case Key.ControlC:
-				// TODO: stop current execution of this container
+			case Key.ControlQ:
+				// FIXED: stop current execution of this container
+				Application.RequestStop ();
 				break;
 			case Key.ControlZ:
 				Driver.Suspend ();
@@ -1460,6 +1552,7 @@ namespace Terminal.Gui {
 			case Key.Tab:
 			case Key.CursorRight:
 			case Key.CursorDown:
+			case Key.ControlI: // Unix
 				var old = Focused;
 				if (!FocusNext ())
 					FocusNext ();
@@ -1565,6 +1658,8 @@ namespace Terminal.Gui {
 
 		public override void Redraw (Rect region)
 		{
+			Application.CurrentView = this;
+
 			if (this == Application.Top) {
 				if (!NeedDisplay.IsEmpty) {
 					Driver.SetAttribute (Colors.TopLevel.Normal);
@@ -1698,9 +1793,9 @@ namespace Terminal.Gui {
 			return contentView.GetEnumerator ();
 		}
 
-		void DrawFrame ()
+		void DrawFrame (bool fill = true)
 		{
-			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: true);
+			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), padding, fill: fill);
 		}
 
 		/// <summary>
@@ -1745,23 +1840,31 @@ namespace Terminal.Gui {
 
 		public override void Redraw (Rect bounds)
 		{
+			Application.CurrentView = this;
+
 			if (!NeedDisplay.IsEmpty) {
+				DrawFrameWindow ();
+			}
+			contentView.Redraw (contentView.Bounds);
+			ClearNeedsDisplay ();
+			DrawFrameWindow (false);
+
+			void DrawFrameWindow (bool fill = true)
+			{
 				Driver.SetAttribute (ColorScheme.Normal);
-				DrawFrame ();
+				DrawFrame (fill);
 				if (HasFocus)
-					Driver.SetAttribute (ColorScheme.Normal);
-				var width = Frame.Width;
+					Driver.SetAttribute (ColorScheme.HotNormal);
+				var width = Frame.Width - (padding + 2) * 2;
 				if (Title != null && width > 4) {
-					Move (1+padding, padding);
+					Move (1 + padding, padding);
 					Driver.AddRune (' ');
-					var str = Title.Length > width ? Title [0, width-4] : Title;
+					var str = Title.Length >= width ? Title [0, width - 2] : Title;
 					Driver.AddStr (str);
 					Driver.AddRune (' ');
 				}
 				Driver.SetAttribute (ColorScheme.Normal);
 			}
-			contentView.Redraw (contentView.Bounds);
-			ClearNeedsDisplay ();
 		}
 
 		//
@@ -1778,7 +1881,7 @@ namespace Terminal.Gui {
 
 			int nx, ny;
 			if ((mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
-				mouseEvent.Flags == MouseFlags.Button4Pressed)) {
+				mouseEvent.Flags == MouseFlags.Button3Pressed)) {
 				if (dragPosition.HasValue) {
 					if (SuperView == null) {
 						Application.Top.SetNeedsDisplay (Frame);
@@ -1856,11 +1959,17 @@ namespace Terminal.Gui {
 		public static Toplevel Top { get; private set; }
 
 		/// <summary>
-		/// The current toplevel object.   This is updated when Application.Run enters and leaves and points to the current toplevel.
+		/// The current toplevel object. This is updated when Application.Run enters and leaves and points to the current toplevel.
 		/// </summary>
 		/// <value>The current.</value>
 		public static Toplevel Current { get; private set; }
 
+		/// <summary>
+		/// TThe current view object being redrawn.
+		/// </summary>
+		/// /// <value>The current.</value>
+		public static View CurrentView { get; set; }
+
 		/// <summary>
 		/// The mainloop driver for the applicaiton
 		/// </summary>
@@ -1960,6 +2069,7 @@ namespace Terminal.Gui {
 			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			Top = topLevelFactory ();
 			Current = Top;
+			CurrentView = Top;
 			_initialized = true;
 		}
 
@@ -2003,7 +2113,7 @@ namespace Terminal.Gui {
 
 		static void ProcessKeyEvent (KeyEvent ke)
 		{
-		
+
 			var chain = toplevels.ToList();
 			foreach (var topLevel in chain) {
 				if (topLevel.ProcessHotKey (ke))
@@ -2039,7 +2149,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		
+
 		static void ProcessKeyUpEvent (KeyEvent ke)
 		{
 			var chain = toplevels.ToList ();
@@ -2111,9 +2221,18 @@ namespace Terminal.Gui {
 		/// </summary>
 		static public Action<MouseEvent> RootMouseEvent;
 
+		internal static View wantContinuousButtonPressedView;
+		static View lastMouseOwnerView;
+
 		static void ProcessMouseEvent (MouseEvent me)
 		{
 			var view = FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
+
+			if (view != null && view.WantContinuousButtonPressed)
+				wantContinuousButtonPressedView = view;
+			else
+				wantContinuousButtonPressedView = null;
+
 			RootMouseEvent?.Invoke (me);
 			if (mouseGrabView != null) {
 				var newxy = mouseGrabView.ScreenToView (me.X, me.Y);
@@ -2130,9 +2249,6 @@ namespace Terminal.Gui {
 			}
 
 			if (view != null) {
-				if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
-					return;
-
 				var nme = new MouseEvent () {
 					X = rx,
 					Y = ry,
@@ -2141,6 +2257,24 @@ namespace Terminal.Gui {
 					OfY = ry,
 					View = view
 				};
+
+				if (lastMouseOwnerView == null) {
+					lastMouseOwnerView = view;
+					view.OnMouseEnter (nme);
+				} else if (lastMouseOwnerView != view) {
+					lastMouseOwnerView.OnMouseLeave (nme);
+					view.OnMouseEnter (nme);
+					lastMouseOwnerView = view;
+				}
+
+				if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
+					return;
+
+				if (view.WantContinuousButtonPressed)
+					wantContinuousButtonPressedView = view;
+				else
+					wantContinuousButtonPressedView = null;
+
 				// Should we bubbled up the event, if it is not handled?
 				view.MouseEvent (nme);
 			}
@@ -2217,6 +2351,8 @@ namespace Terminal.Gui {
 
 		static void Redraw (View view)
 		{
+			Application.CurrentView = view;
+
 			view.Redraw (view.Bounds);
 			Driver.Refresh ();
 		}

+ 41 - 15
Terminal.Gui/Drivers/ConsoleDriver.cs

@@ -540,47 +540,73 @@ namespace Terminal.Gui {
 			Move (region.X, region.Y);
 			if (padding > 0) {
 				for (int l = 0; l < padding; l++)
-					for (b = 0; b < width; b++)
+					for (b = region.X; b < region.X + width; b++) {
 						AddRune (' ');
+						Move (b + 1, region.Y);
+					}
 			}
 			Move (region.X, region.Y + padding);
-			for (int c = 0; c < padding; c++)
+			for (int c = 0; c < padding; c++) {
 				AddRune (' ');
+				Move (region.X + 1, region.Y + padding);
+			}
 			AddRune (ULCorner);
-			for (b = 0; b < fwidth - 2; b++)
+			for (b = region.X; b < region.X + fwidth - 2; b++) {
 				AddRune (HLine);
+				Move (b + (padding > 0 ? padding + 2 : 2), region.Y + padding);
+			}
 			AddRune (URCorner);
-			for (int c = 0; c < padding; c++)
+			for (int c = 0; c < padding; c++) {
 				AddRune (' ');
-
+				Move (region.X + 1, region.Y + padding);
+			}
 			for (b = 1 + padding; b < fheight; b++) {
 				Move (region.X, region.Y + b);
-				for (int c = 0; c < padding; c++)
+				for (int c = 0; c < padding; c++) {
 					AddRune (' ');
+					Move (region.X + 1, region.Y + b);
+				}
 				AddRune (VLine);
 				if (fill) {
-					for (int x = 1; x < fwidth - 1; x++)
+					for (int x = region.X + 1; x < region.X + fwidth - 1; x++) {
 						AddRune (' ');
-				} else
-					Move (region.X + fwidth - 1, region.Y + b);
+						Move (x + (padding > 0 ? padding + 1 : 1), region.Y + b);
+					}
+				} else {
+					if (padding > 0)
+						Move (region.X + fwidth, region.Y + b);
+					else
+						Move (region.X + fwidth - 1, region.Y + b);
+				}
 				AddRune (VLine);
-				for (int c = 0; c < padding; c++)
+				for (int c = 0; c < padding; c++) {
 					AddRune (' ');
+					Move (region.X + 1, region.Y + b);
+				}
 			}
 			Move (region.X, region.Y + fheight);
-			for (int c = 0; c < padding; c++)
+			for (int c = 0; c < padding; c++) {
 				AddRune (' ');
+				Move (region.X + 1, region.Y + b);
+			}
 			AddRune (LLCorner);
-			for (b = 0; b < fwidth - 2; b++)
+			for (b = region.X; b < region.X + fwidth - 2; b++) {
 				AddRune (HLine);
+				Move (b + (padding > 0 ? padding + 2 : 2), region.Y + fheight);
+			}
 			AddRune (LRCorner);
-			for (int c = 0; c < padding; c++)
+			for (int c = 0; c < padding; c++) {
 				AddRune (' ');
+				Move (region.X + 1, region.Y);
+			}
 			if (padding > 0) {
 				Move (region.X, region.Y + height - padding);
-				for (int l = 0; l < padding; l++)
-					for (b = 0; b < width; b++)
+				for (int l = 0; l < padding; l++) {
+					for (b = region.X; b < region.X + width; b++) {
 						AddRune (' ');
+						Move (b + 1, region.Y + height - padding);
+					}
+				}
 			}
 		}
 

+ 38 - 1
Terminal.Gui/Drivers/CursesDriver.cs

@@ -141,6 +141,38 @@ namespace Terminal.Gui {
 			case Curses.KeyInsertChar: return Key.InsertChar;
 			case Curses.KeyBackTab: return Key.BackTab;
 			case Curses.KeyBackspace: return Key.Backspace;
+			case Curses.ShiftKeyUp: return Key.CursorUp | Key.ShiftMask;
+			case Curses.ShiftKeyDown: return Key.CursorDown | Key.ShiftMask;
+			case Curses.ShiftKeyLeft: return Key.CursorLeft | Key.ShiftMask;
+			case Curses.ShiftKeyRight: return Key.CursorRight | Key.ShiftMask;
+			case Curses.ShiftKeyHome: return Key.Home | Key.ShiftMask;
+			case Curses.ShiftKeyEnd: return Key.End | Key.ShiftMask;
+			case Curses.ShiftKeyNPage: return Key.PageDown | Key.ShiftMask;
+			case Curses.ShiftKeyPPage: return Key.PageUp | Key.ShiftMask;
+			case Curses.AltKeyUp: return Key.CursorUp | Key.AltMask;
+			case Curses.AltKeyDown: return Key.CursorDown | Key.AltMask;
+			case Curses.AltKeyLeft: return Key.CursorLeft | Key.AltMask;
+			case Curses.AltKeyRight: return Key.CursorRight | Key.AltMask;
+			case Curses.AltKeyHome: return Key.Home | Key.AltMask;
+			case Curses.AltKeyEnd: return Key.End | Key.AltMask;
+			case Curses.AltKeyNPage: return Key.PageDown | Key.AltMask;
+			case Curses.AltKeyPPage: return Key.PageUp | Key.AltMask;
+			case Curses.CtrlKeyUp: return Key.CursorUp | Key.CtrlMask;
+			case Curses.CtrlKeyDown: return Key.CursorDown | Key.CtrlMask;
+			case Curses.CtrlKeyLeft: return Key.CursorLeft | Key.CtrlMask;
+			case Curses.CtrlKeyRight: return Key.CursorRight | Key.CtrlMask;
+			case Curses.CtrlKeyHome: return Key.Home | Key.CtrlMask;
+			case Curses.CtrlKeyEnd: return Key.End | Key.CtrlMask;
+			case Curses.CtrlKeyNPage: return Key.PageDown | Key.CtrlMask;
+			case Curses.CtrlKeyPPage: return Key.PageUp | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyUp: return Key.CursorUp | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyDown: return Key.CursorDown | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyLeft: return Key.CursorLeft | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyRight: return Key.CursorRight | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyHome: return Key.Home | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyEnd: return Key.End | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyNPage: return Key.PageDown | Key.ShiftMask | Key.CtrlMask;
+			case Curses.ShiftCtrlKeyPPage: return Key.PageUp | Key.ShiftMask | Key.CtrlMask;
 			default: return Key.Unknown;
 			}
 		}
@@ -188,7 +220,12 @@ namespace Terminal.Gui {
 					KeyEvent key;
 
 					// The ESC-number handling, debatable.
-					if (wch >= '1' && wch <= '9')
+					// Simulates the AltMask itself by pressing Alt + Space.
+					if (wch == (int)Key.Space)
+						key = new KeyEvent (Key.AltMask);
+					else if (wch - (int)Key.Space >= 'A' && wch - (int)Key.Space <= 'Z')
+						key = new KeyEvent ((Key)((uint)Key.AltMask + (wch - (int)Key.Space)));
+					else if (wch >= '1' && wch <= '9')
 						key = new KeyEvent ((Key)((int)Key.F1 + (wch - '0' - 1)));
 					else if (wch == '0')
 						key = new KeyEvent (Key.F10);

+ 5 - 5
Terminal.Gui/Drivers/NetDriver.cs

@@ -81,11 +81,11 @@ namespace Terminal.Gui {
 			} else
 				needMove = true;
 			ccol++;
-			if (ccol == Cols) {
-				ccol = 0;
-				if (crow + 1 < Rows)
-					crow++;
-			}
+			//if (ccol == Cols) {
+			//	ccol = 0;
+			//	if (crow + 1 < Rows)
+			//		crow++;
+			//}
 			if (sync)
 				UpdateScreen ();
 		}

+ 83 - 32
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -53,6 +53,7 @@ namespace Terminal.Gui {
 			var newConsoleMode = originalConsoleMode;
 			newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
 			newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
+			newConsoleMode &= ~(uint)ConsoleModes.EnableProcessedInput;
 			ConsoleMode = newConsoleMode;
 		}
 
@@ -124,6 +125,7 @@ namespace Terminal.Gui {
 
 		[Flags]
 		public enum ConsoleModes : uint {
+			EnableProcessedInput = 1,
 			EnableMouseInput = 16,
 			EnableQuickEditMode = 64,
 			EnableExtendedFlags = 128,
@@ -552,7 +554,6 @@ namespace Terminal.Gui {
 			this.mouseHandler = mouseHandler;
 		}
 
-
 		void IMainLoopDriver.MainIteration ()
 		{
 			if (result == null)
@@ -563,19 +564,50 @@ namespace Terminal.Gui {
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				if (map == (Key)0xffffffff) {
-					KeyEvent key;
+					KeyEvent key = default;
 					// Shift = VK_SHIFT = 0x10
 					// Ctrl = VK_CONTROL = 0x11
 					// Alt = VK_MENU = 0x12
-					switch (inputEvent.KeyEvent.wVirtualKeyCode) {
-					case 0x11:
-						key = new KeyEvent (Key.CtrlMask);
+
+					switch (inputEvent.KeyEvent.dwControlKeyState) {
+					case WindowsConsole.ControlKeyState.RightAltPressed:
+					case WindowsConsole.ControlKeyState.RightAltPressed |
+						WindowsConsole.ControlKeyState.LeftControlPressed |
+						WindowsConsole.ControlKeyState.EnhancedKey:
+					case WindowsConsole.ControlKeyState.EnhancedKey:
+						key = new KeyEvent (Key.CtrlMask | Key.AltMask);
 						break;
-					case 0x12:
+					case WindowsConsole.ControlKeyState.LeftAltPressed:
 						key = new KeyEvent (Key.AltMask);
 						break;
+					case WindowsConsole.ControlKeyState.RightControlPressed:
+					case WindowsConsole.ControlKeyState.LeftControlPressed:
+						key = new KeyEvent (Key.CtrlMask);
+						break;
+					case WindowsConsole.ControlKeyState.ShiftPressed:
+						key = new KeyEvent (Key.ShiftMask);
+						break;
+					case WindowsConsole.ControlKeyState.NumlockOn:
+						break;
+					case WindowsConsole.ControlKeyState.ScrolllockOn:
+						break;
+					case WindowsConsole.ControlKeyState.CapslockOn:
+						break;
 					default:
-						key = new KeyEvent (Key.Unknown);
+						switch (inputEvent.KeyEvent.wVirtualKeyCode) {
+						case 0x10:
+							key = new KeyEvent (Key.ShiftMask);
+							break;
+						case 0x11:
+							key = new KeyEvent (Key.CtrlMask);
+							break;
+						case 0x12:
+							key = new KeyEvent (Key.AltMask);
+							break;
+						default:
+							key = new KeyEvent (Key.Unknown);
+							break;
+						}
 						break;
 					}
 
@@ -652,7 +684,7 @@ namespace Terminal.Gui {
 					break;
 
 				case WindowsConsole.ButtonState.RightmostButtonPressed:
-					mouseFlag = MouseFlags.Button4Pressed;
+					mouseFlag = MouseFlags.Button3Pressed;
 					break;
 				}
 
@@ -679,6 +711,9 @@ namespace Terminal.Gui {
 								Flags = mouseFlag
 							};
 
+							var view = Application.wantContinuousButtonPressedView;
+							if (view == null)
+								break;
 							if (IsButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
 								mouseHandler (me);
 								mainLoop.Driver.Wakeup ();
@@ -699,7 +734,7 @@ namespace Terminal.Gui {
 					break;
 
 				case WindowsConsole.ButtonState.RightmostButtonPressed:
-					mouseFlag = MouseFlags.Button4Released;
+					mouseFlag = MouseFlags.Button3Released;
 					break;
 				}
 				IsButtonPressed = false;
@@ -721,7 +756,7 @@ namespace Terminal.Gui {
 						break;
 
 					case WindowsConsole.ButtonState.RightmostButtonPressed:
-						mouseFlag = MouseFlags.Button4Clicked;
+						mouseFlag = MouseFlags.Button3Clicked;
 						break;
 					}
 				} else {
@@ -740,7 +775,7 @@ namespace Terminal.Gui {
 					break;
 
 				case WindowsConsole.ButtonState.RightmostButtonPressed:
-					mouseFlag = MouseFlags.Button4DoubleClicked;
+					mouseFlag = MouseFlags.Button3DoubleClicked;
 					break;
 				}
 				IsButtonDoubleClicked = true;
@@ -755,7 +790,7 @@ namespace Terminal.Gui {
 					break;
 
 				case WindowsConsole.ButtonState.RightmostButtonPressed:
-					mouseFlag = MouseFlags.Button4TripleClicked;
+					mouseFlag = MouseFlags.Button3TripleClicked;
 					break;
 				}
 				IsButtonDoubleClicked = false;
@@ -817,33 +852,35 @@ namespace Terminal.Gui {
 			var keyInfo = keyInfoEx.consoleKeyInfo;
 			switch (keyInfo.Key) {
 			case ConsoleKey.Escape:
-				return Key.Esc;
+				return MapKeyModifiers (keyInfo, Key.Esc);
 			case ConsoleKey.Tab:
 				return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
 			case ConsoleKey.Home:
-				return Key.Home;
+				return MapKeyModifiers (keyInfo, Key.Home);
 			case ConsoleKey.End:
-				return Key.End;
+				return MapKeyModifiers (keyInfo, Key.End);
 			case ConsoleKey.LeftArrow:
-				return Key.CursorLeft;
+				return MapKeyModifiers (keyInfo, Key.CursorLeft);
 			case ConsoleKey.RightArrow:
-				return Key.CursorRight;
+				return MapKeyModifiers (keyInfo, Key.CursorRight);
 			case ConsoleKey.UpArrow:
-				return Key.CursorUp;
+				return MapKeyModifiers (keyInfo, Key.CursorUp);
 			case ConsoleKey.DownArrow:
-				return Key.CursorDown;
+				return MapKeyModifiers (keyInfo, Key.CursorDown);
 			case ConsoleKey.PageUp:
-				return Key.PageUp;
+				return MapKeyModifiers (keyInfo, Key.PageUp);
 			case ConsoleKey.PageDown:
-				return Key.PageDown;
+				return MapKeyModifiers (keyInfo, Key.PageDown);
 			case ConsoleKey.Enter:
-				return Key.Enter;
+				return MapKeyModifiers (keyInfo, Key.Enter);
 			case ConsoleKey.Spacebar:
-				return Key.Space;
+				return MapKeyModifiers (keyInfo, Key.Space);
 			case ConsoleKey.Backspace:
-				return Key.Backspace;
+				return MapKeyModifiers (keyInfo, Key.Backspace);
 			case ConsoleKey.Delete:
-				return Key.DeleteChar;
+				return MapKeyModifiers (keyInfo, Key.DeleteChar);
+			case ConsoleKey.Insert:
+				return MapKeyModifiers (keyInfo, Key.InsertChar);
 
 			case ConsoleKey.NumPad0:
 				return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar;
@@ -883,7 +920,7 @@ namespace Terminal.Gui {
 			}
 
 			var key = keyInfo.Key;
-			var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
+			//var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
 
 			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
 				var delta = key - ConsoleKey.A;
@@ -897,7 +934,8 @@ namespace Terminal.Gui {
 					else
 						return (Key)((uint)keyInfo.KeyChar);
 				}
-				return (Key)((uint)alphaBase + delta);
+				//return (Key)((uint)alphaBase + delta);
+				return (Key)((uint)keyInfo.KeyChar);
 			}
 			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
 				var delta = key - ConsoleKey.D0;
@@ -915,6 +953,19 @@ namespace Terminal.Gui {
 			return (Key)(0xffffffff);
 		}
 
+		private static Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		{
+			Key keyMod = new Key ();
+			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Shift))
+				keyMod = Key.ShiftMask;
+			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Control))
+				keyMod |= Key.CtrlMask;
+			if (keyInfo.Modifiers.HasFlag (ConsoleModifiers.Alt))
+				keyMod |= Key.AltMask;
+
+			return keyMod != Key.ControlSpace ? keyMod | key : key;
+		}
+
 		public override void Init (Action terminalResized)
 		{
 			TerminalResized = terminalResized;
@@ -1006,11 +1057,11 @@ namespace Terminal.Gui {
 					AddStr (" ");
 				}
 			}
-			if (ccol == Cols) {
-				ccol = 0;
-				if (crow + 1 < Rows)
-					crow++;
-			}
+			//if (ccol == Cols) {
+			//	ccol = 0;
+			//	if (crow + 1 < Rows)
+			//		crow++;
+			//}
 			if (sync)
 				UpdateScreen ();
 		}

+ 22 - 9
Terminal.Gui/Event.cs

@@ -24,9 +24,10 @@ namespace Terminal.Gui {
 	///   Unicode runes are also stored here, the letter 'A" for example is encoded as a value 65 (not surfaced in the enum).
 	/// </para>
 	/// </remarks>
+	[Flags]
 	public enum Key : uint {
 		/// <summary>
-		/// Mask that indictes that this is a character value, values outside this range
+		/// Mask that indicates that this is a character value, values outside this range
 		/// indicate special characters like Alt-key combinations or special keys on the
 		/// keyboard like function keys, arrows keys and so on.
 		/// </summary>
@@ -42,7 +43,7 @@ namespace Terminal.Gui {
 		/// The key code for the user pressing Control-spacebar
 		/// </summary>
 		ControlSpace = 0,
-			
+
 		/// <summary>
 	        /// The key code for the user pressing Control-A
 		/// </summary>
@@ -80,10 +81,6 @@ namespace Terminal.Gui {
 		/// </summary>
 		ControlI,
 		/// <summary>
-		/// The key code for the user pressing the tab key (same as pressing Control-I).
-		/// </summary>
-		Tab = ControlI,
-		/// <summary>
 		/// The key code for the user pressing Control-J
 		/// </summary>
 		ControlJ,
@@ -151,7 +148,7 @@ namespace Terminal.Gui {
 		/// The key code for the user pressing Control-Z
 		/// </summary>
 		ControlZ,
-			
+
 		/// <summary>
 		/// The key code for the user pressing the escape key
 		/// </summary>
@@ -172,6 +169,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		Delete = 127,
 
+		/// <summary>
+		/// When this value is set, the Key encodes the sequence Shift-KeyValue.
+		/// </summary>
+		ShiftMask = 0x10000000,
+
 		/// <summary>
 		///   When this value is set, the Key encodes the sequence Alt-KeyValue.
 		///   And the actual value must be extracted by removing the AltMask.
@@ -270,6 +272,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		F10,
 		/// <summary>
+		/// The key code for the user pressing the tab key (forwards tab key).
+		/// </summary>
+		Tab,
+		/// <summary>
 		/// Shift-tab key (backwards tab key).
 		/// </summary>
 		BackTab,
@@ -290,12 +296,18 @@ namespace Terminal.Gui {
 		public Key Key;
 
 		/// <summary>
-		///   The key value cast to an integer, you will typicall use this for
+		///   The key value cast to an integer, you will typical use this for
 		///   extracting the Unicode rune value out of a key, when none of the
 		///   symbolic options are in use.
 		/// </summary>
 		public int KeyValue => (int)Key;
 
+		/// <summary>
+		/// Gets a value indicating whether the Shift key was pressed.
+		/// </summary>
+		/// <value><c>true</c> if is shift; otherwise, <c>false</c>.</value>
+		public bool IsShift => (Key & Key.ShiftMask) != 0;
+
 		/// <summary>
 		/// Gets a value indicating whether the Alt key was pressed (real or synthesized)
 		/// </summary>
@@ -306,7 +318,8 @@ namespace Terminal.Gui {
 		/// Determines whether the value is a control key (and NOT just the ctrl key)
 		/// </summary>
 		/// <value><c>true</c> if is ctrl; otherwise, <c>false</c>.</value>
-		public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
+		//public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
+		public bool IsCtrl => (Key & Key.CtrlMask) != 0;
 
 		/// <summary>
 		///   Constructs a new KeyEvent from the provided Key value - can be a rune cast into a Key value

+ 33 - 1
Terminal.Gui/MonoCurses/constants.cs

@@ -1,5 +1,5 @@
 /*
- * This file is autogenerated by the attrib.c program, do not edit 
+ * This file is autogenerated by the attrib.c program, do not edit
  */
 
 using System;
@@ -101,6 +101,38 @@ namespace Unix.Terminal {
 		public const int KeyF9 = unchecked((int)0x111);
 		public const int KeyF10 = unchecked((int)0x112);
 		public const int KeyResize = unchecked((int)0x19a);
+		public const int ShiftKeyUp = unchecked((int)0x151);
+		public const int ShiftKeyDown = unchecked((int)0x150);
+		public const int ShiftKeyLeft = unchecked((int)0x189);
+		public const int ShiftKeyRight = unchecked((int)0x192);
+		public const int ShiftKeyNPage = unchecked((int)0x18c);
+		public const int ShiftKeyPPage = unchecked((int)0x18e);
+		public const int ShiftKeyHome = unchecked((int)0x187);
+		public const int ShiftKeyEnd = unchecked((int)0x182);
+		public const int AltKeyUp = unchecked((int)0x234);
+		public const int AltKeyDown = unchecked((int)0x20b);
+		public const int AltKeyLeft = unchecked((int)0x21f);
+		public const int AltKeyRight = unchecked((int)0x22e);
+		public const int AltKeyNPage = unchecked((int)0x224);
+		public const int AltKeyPPage = unchecked((int)0x229);
+		public const int AltKeyHome = unchecked((int)0x215);
+		public const int AltKeyEnd = unchecked((int)0x210);
+		public const int CtrlKeyUp = unchecked((int)0x236);
+		public const int CtrlKeyDown = unchecked((int)0x20d);
+		public const int CtrlKeyLeft = unchecked((int)0x221);
+		public const int CtrlKeyRight = unchecked((int)0x230);
+		public const int CtrlKeyNPage = unchecked((int)0x226);
+		public const int CtrlKeyPPage = unchecked((int)0x22b);
+		public const int CtrlKeyHome = unchecked((int)0x217);
+		public const int CtrlKeyEnd = unchecked((int)0x212);
+		public const int ShiftCtrlKeyUp = unchecked((int)0x237);
+		public const int ShiftCtrlKeyDown = unchecked((int)0x20e);
+		public const int ShiftCtrlKeyLeft = unchecked((int)0x222);
+		public const int ShiftCtrlKeyRight = unchecked((int)0x231);
+		public const int ShiftCtrlKeyNPage = unchecked((int)0x227);
+		public const int ShiftCtrlKeyPPage = unchecked((int)0x22c);
+		public const int ShiftCtrlKeyHome = unchecked((int)0x218);
+		public const int ShiftCtrlKeyEnd = unchecked((int)0x213);
 
 		public const int LC_ALL = 6;
 		static public int ColorPair(int n){

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

@@ -43,6 +43,7 @@ namespace Terminal.Gui {
 		public FrameView (Rect frame, ustring title) : base (frame)
 		{
 			var cFrame = new Rect (1, 1 , frame.Width - 2, frame.Height - 2);
+			this.title = title;
 			contentView = new ContentView (cFrame);
 			Initialize ();
 		}
@@ -69,6 +70,7 @@ namespace Terminal.Gui {
 		/// <param name="title">Title.</param>
 		public FrameView (ustring title)
 		{
+			this.title = title;
 			contentView = new ContentView () {
 				X = 1,
 				Y = 1,
@@ -81,7 +83,6 @@ namespace Terminal.Gui {
 		void Initialize ()
 		{
 			base.Add (contentView);
-			Title = title;
 		}
 
 		void DrawFrame ()

+ 102 - 31
Terminal.Gui/Views/Menu.cs

@@ -343,6 +343,29 @@ namespace Terminal.Gui {
 				return false;
 			});
 		}
+
+		public override bool KeyDown (KeyEvent keyEvent)
+		{
+			if (keyEvent.IsAlt) {
+				host.CloseAllMenus ();
+				return true;
+			}
+
+			return false;
+		}
+
+		public override bool ProcessHotKey (KeyEvent keyEvent)
+		{
+			// To ncurses simulate a AltMask key pressing Alt+Space because
+			// it can´t detect an alone special key down was pressed.
+			if (keyEvent.IsAlt && keyEvent.Key == Key.AltMask) {
+				KeyDown (keyEvent);
+				return true;
+			}
+
+			return false;
+		}
+
 		public override bool ProcessKey (KeyEvent kb)
 		{
 			bool disabled;
@@ -445,7 +468,8 @@ namespace Terminal.Gui {
 					Run (barItems.Children [meY].Action);
 				return true;
 			} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
-				me.Flags == MouseFlags.ReportMousePosition) {
+				me.Flags == MouseFlags.ReportMousePosition ||
+				me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
 				disabled = false;
 				if (me.Y < 1)
 					return true;
@@ -534,9 +558,12 @@ namespace Terminal.Gui {
 			isMenuClosed = true;
 		}
 
+		bool openedByAltKey;
 		public override bool KeyDown (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
+				openedByAltKey = true;
+				SetNeedsDisplay ();
 				openedByHotKey = false;
 			}
 			return false;
@@ -544,29 +571,45 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Track Alt key-up events. On Windows, when a user releases Alt (without another key), the menu gets focus but doesn't open.
-		/// We mimic that behavior here. 
+		/// We mimic that behavior here.
 		/// </summary>
 		/// <param name="keyEvent"></param>
 		/// <returns></returns>
 		public override bool KeyUp (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
-				// User pressed Alt - this may be a precursor to a menu accellerator (e.g. Alt-F)
-				if (openMenu == null) {
-					// There's no open menu, the first menu item should be highlight. 
-					// The right way to do this is to SetFocus(MenuBar), but for some reason 
+				// User pressed Alt - this may be a precursor to a menu accelerator (e.g. Alt-F)
+				if (!keyEvent.IsCtrl && openedByAltKey && isMenuClosed && openMenu == null && ((uint)keyEvent.Key & (uint)Key.CharMask) == 0) {
+					// There's no open menu, the first menu item should be highlight.
+					// The right way to do this is to SetFocus(MenuBar), but for some reason
 					// that faults.
 
-					Activate (0);
+					//Activate (0);
+					//StartMenu ();
+					isMenuClosed = false;
+					selected = 0;
+					CanFocus = true;
+					lastFocused = SuperView.MostFocused;
+					SuperView.SetFocus (this);
 					SetNeedsDisplay ();
-				} else {
-					// There's an open menu. If this Alt key-up is a pre-cursor to an acellerator
+					Application.GrabMouse (this);
+				} else if (!openedByHotKey) {
+					// There's an open menu. If this Alt key-up is a pre-cursor to an accelerator
 					// we don't want to close the menu because it'll flash.
 					// How to deal with that?
-					if (!openedByHotKey) {
+
+					if (openMenu != null)
 						CloseAllMenus ();
-					}
+					openedByAltKey = false;
+					isMenuClosed = true;
+					selected = -1;
+					CanFocus = false;
+					if (lastFocused != null)
+						SuperView?.SetFocus (lastFocused);
+					SetNeedsDisplay ();
+					Application.UngrabMouse ();
 				}
+
 				return true;
 			}
 			return false;
@@ -589,9 +632,12 @@ namespace Terminal.Gui {
 				if (i == selected) {
 					hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
 					normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
-				} else {
+				} else if (openedByAltKey) {
 					hotColor = ColorScheme.HotNormal;
 					normalColor = ColorScheme.Normal;
+				} else {
+					hotColor = ColorScheme.Normal;
+					normalColor = ColorScheme.Normal;
 				}
 				DrawHotString ($" {menu.Title}  ", hotColor, normalColor);
 				pos += 1 + menu.TitleLength + 2;
@@ -815,6 +861,7 @@ namespace Terminal.Gui {
 			}
 			isMenuClosed = true;
 			openedByHotKey = false;
+			openedByAltKey = false;
 		}
 
 		View FindDeepestMenu (View view, ref int count)
@@ -890,7 +937,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		bool openedByHotKey = false;
+		bool openedByHotKey;
 		internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
 		{
 			int pos = 0;
@@ -901,15 +948,7 @@ namespace Terminal.Gui {
 				int p = mi.Title.IndexOf ('_');
 				if (p != -1 && p + 1 < mi.Title.Length) {
 					if (Char.ToUpperInvariant ((char)mi.Title [p + 1]) == c) {
-						if (mi.IsTopLevel) {
-							var menu = new Menu (this, i, 0, mi);
-							menu.Run (mi.Action);
-						} else {
-							openedByHotKey = true;
-							Application.GrabMouse (this);
-							selected = i;
-							OpenMenu (i);
-						}
+						ProcessMenu (i, mi);
 						return true;
 					}
 				}
@@ -917,6 +956,19 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		private void ProcessMenu (int i, MenuBarItem mi)
+		{
+			if (mi.IsTopLevel) {
+				var menu = new Menu (this, i, 0, mi);
+				menu.Run (mi.Action);
+			} else {
+				openedByHotKey = true;
+				Application.GrabMouse (this);
+				selected = i;
+				OpenMenu (i);
+			}
+		}
+
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 			if (kb.Key == Key.F9) {
@@ -927,10 +979,16 @@ namespace Terminal.Gui {
 				return true;
 			}
 
-			if (kb.IsAlt) {
+			// To ncurses simulate a AltMask key pressing Alt+Space because
+			// it can´t detect an alone special key down was pressed.
+			if (kb.IsAlt && kb.Key == Key.AltMask && openMenu == null) {
+				KeyDown (kb);
+				KeyUp (kb);
+				return true;
+			} else if (kb.IsAlt) {
 				if (FindAndOpenMenuByHotkey (kb)) return true;
 			}
-			var kc = kb.KeyValue;
+			//var kc = kb.KeyValue;
 
 			return base.ProcessHotKey (kb);
 		}
@@ -951,6 +1009,15 @@ namespace Terminal.Gui {
 			case Key.ControlC:
 				//TODO: Running = false;
 				CloseMenu ();
+				if (openedByAltKey) {
+					openedByAltKey = false;
+					LastFocused.SuperView?.SetFocus (LastFocused);
+				}
+				break;
+
+			case Key.CursorDown:
+			case Key.Enter:
+				ProcessMenu (selected, Menus [selected]);
 				break;
 
 			default:
@@ -958,10 +1025,12 @@ namespace Terminal.Gui {
 				if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
 					char c = Char.ToUpper ((char)key);
 
-					if (Menus [selected].IsTopLevel)
+					if (selected == -1 || Menus [selected].IsTopLevel)
 						return false;
 
 					foreach (var mi in Menus [selected].Children) {
+						if (mi == null)
+							continue;
 						int p = mi.Title.IndexOf ('_');
 						if (p != -1 && p + 1 < mi.Title.Length) {
 							if (mi.Title [p + 1] == c) {
@@ -985,14 +1054,15 @@ namespace Terminal.Gui {
 			}
 			handled = false;
 
-			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
-				(me.Flags == MouseFlags.ReportMousePosition && selected > -1)) {
+			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1DoubleClicked ||
+				(me.Flags == MouseFlags.ReportMousePosition && selected > -1) ||
+				(me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) && selected > -1)) {
 				int pos = 1;
 				int cx = me.X;
 				for (int i = 0; i < Menus.Length; i++) {
 					if (cx > pos && me.X < pos + 1 + Menus [i].TitleLength) {
 						if (selected == i && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) &&
-							!isMenuClosed) {
+											!isMenuClosed) {
 							Application.UngrabMouse ();
 							if (Menus [i].IsTopLevel) {
 								var menu = new Menu (this, i, 0, Menus [i]);
@@ -1036,8 +1106,8 @@ namespace Terminal.Gui {
 						Application.GrabMouse (me.View);
 						me.View.MouseEvent (me);
 					}
-				} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Pressed) ||
-					me.Flags == MouseFlags.Button1DoubleClicked)) {
+				} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Clicked) ||
+					me.Flags == MouseFlags.Button1DoubleClicked || me.Flags == MouseFlags.Button1Pressed)) {
 					Application.UngrabMouse ();
 					CloseAllMenus ();
 					handled = false;
@@ -1046,7 +1116,8 @@ namespace Terminal.Gui {
 					handled = false;
 					return false;
 				}
-			} else if (isMenuClosed && (me.Flags.HasFlag (MouseFlags.Button1Pressed) || me.Flags == MouseFlags.Button1DoubleClicked)) {
+			} else if (isMenuClosed && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
+				me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))) {
 				Application.GrabMouse (current);
 			} else {
 				handled = false;

+ 1 - 0
Terminal.Gui/Views/ScrollView.cs

@@ -79,6 +79,7 @@ namespace Terminal.Gui {
 			vertical = isVertical;
 			this.position = position;
 			this.size = size;
+			WantContinuousButtonPressed = true;
 		}
 
 		/// <summary>

+ 163 - 42
Terminal.Gui/Views/TextField.cs

@@ -78,15 +78,16 @@ namespace Terminal.Gui {
 			CanFocus = true;
 			Used = true;
 			WantMousePositionReports = true;
-			OnLeave += TextField_OnLeave;
 		}
 
-		void TextField_OnLeave (object sender, EventArgs e)
+		public override bool OnLeave ()
 		{
 			if (Application.mouseGrabView != null && Application.mouseGrabView == this)
 				Application.UngrabMouse ();
 			if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar))
 				ClearAllSelection ();
+
+			return base.OnLeave ();
 		}
 
 		public override Rect Frame {
@@ -94,10 +95,15 @@ namespace Terminal.Gui {
 			set {
 				base.Frame = value;
 				var w = base.Frame.Width;
-				first = point > w ? point - w : 0;
+				//first = point > w ? point - w : 0;
+				Adjust ();
 			}
 		}
 
+		List<ustring> historyText;
+		int idxhistoryText;
+		bool isFromHistory;
+
 		/// <summary>
 		///   Sets or gets the text in the entry.
 		/// </summary>
@@ -109,15 +115,24 @@ namespace Terminal.Gui {
 			}
 
 			set {
-				ustring oldText = ustring.Make (text);
+				var oldText = ustring.Make (text);
 				text = TextModel.ToRunes (value);
+				if (!Secret && !isFromHistory) {
+					if (historyText == null)
+						historyText = new List<ustring> () { oldText };
+					if (idxhistoryText > 0 && idxhistoryText + 1 < historyText.Count)
+						historyText.RemoveRange (idxhistoryText + 1, historyText.Count - idxhistoryText - 1);
+					historyText.Add (ustring.Make (text));
+					idxhistoryText++;
+				}
 				Changed?.Invoke (this, oldText);
 
 				if (point > text.Count)
-					point = Math.Max (text.Count-1, 0);
+					point = Math.Max (DisplaySize (text, 0) - 1, 0);
 
 				// FIXME: this needs to be updated to use Rune.ColumnWidth
-				first = point > Frame.Width ? point - Frame.Width : 0;
+				//first = point > Frame.Width ? point - Frame.Width : 0;
+				Adjust ();
 				SetNeedsDisplay ();
 			}
 		}
@@ -171,14 +186,14 @@ namespace Terminal.Gui {
 			var tcount = text.Count;
 			for (int idx = 0; idx < tcount; idx++){
 				var rune = text [idx];
-				if (idx < first)
+				if (idx < p)
 					continue;
 				var cols = Rune.ColumnWidth (rune);
 				if (col == point && HasFocus && !Used && SelectedLength == 0)
 					Driver.SetAttribute (Colors.Menu.HotFocus);
 				else
 					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
-				if (col + cols < width)
+				if (col + cols <= width)
 					Driver.AddRune ((Rune)(Secret ? '*' : rune));
 				col += cols;
 			}
@@ -204,10 +219,13 @@ namespace Terminal.Gui {
 
 		void Adjust ()
 		{
+			int offB = 0;
+			if (SuperView != null && SuperView.Frame.Right - Frame.Right < 0)
+				offB = SuperView.Frame.Right - Frame.Right - 1;
 			if (point < first)
 				first = point;
-			else if (first + point >= Frame.Width) {
-				first = point - (Frame.Width - 1);
+			else if (first + point >= Frame.Width + offB) {
+				first = point - (Frame.Width - 1 + offB);
 			}
 			SetNeedsDisplay ();
 		}
@@ -269,6 +287,22 @@ namespace Terminal.Gui {
 				}
 				break;
 
+			case Key.Home | Key.ShiftMask:
+				if (point > 0) {
+					int x = point;
+					point = 0;
+					PrepareSelection (x, point - x);
+				}
+				break;
+
+			case Key.End | Key.ShiftMask:
+				if (point < text.Count) {
+					int x = point;
+					point = text.Count;
+					PrepareSelection (x, point - x);
+				}
+				break;
+
 			// Home, C-A
 			case Key.Home:
 			case Key.ControlA:
@@ -277,6 +311,42 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
+			case Key.CursorLeft | Key.ShiftMask:
+			case Key.CursorUp | Key.ShiftMask:
+				if (point > 0) {
+					PrepareSelection (point--, -1);
+				}
+				break;
+
+			case Key.CursorRight | Key.ShiftMask:
+			case Key.CursorDown | Key.ShiftMask:
+				if (point < text.Count) {
+					PrepareSelection (point++, 1);
+				}
+				break;
+
+			case Key.CursorLeft | Key.ShiftMask | Key.CtrlMask:
+			case Key.CursorUp | Key.ShiftMask | Key.CtrlMask:
+				if (point > 0) {
+					int x = start > -1 ? start : point;
+					int sbw = WordBackward (point);
+					if (sbw != -1)
+						point = sbw;
+					PrepareSelection (x, sbw - x);
+				}
+				break;
+
+			case Key.CursorRight | Key.ShiftMask | Key.CtrlMask:
+			case Key.CursorDown | Key.ShiftMask | Key.CtrlMask:
+				if (point < text.Count) {
+					int x = start > -1 ? start : point;
+					int sfw = WordForward (point);
+					if (sfw != -1)
+						point = sfw;
+					PrepareSelection (x, sfw - x);
+				}
+				break;
+
 			case Key.CursorLeft:
 			case Key.ControlB:
 				ClearAllSelection ();
@@ -311,23 +381,53 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
-			case Key.ControlY: // Control-y, yank
-				if (Clipboard.Contents == null)
-					return true;
-				var clip = TextModel.ToRunes (Clipboard.Contents);
-				if (clip == null)
-					return true;
-
-				if (point == text.Count) {
+			// Undo
+			case Key.ControlZ:
+				if (historyText != null && historyText.Count > 0) {
+					isFromHistory = true;
+					if (idxhistoryText > 0)
+						idxhistoryText--;
+					if (idxhistoryText > -1)
+						Text = historyText [idxhistoryText];
 					point = text.Count;
-					SetText(text.Concat(clip).ToList());
-				} else {
-					point += clip.Count;
-					SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
+					isFromHistory = false;
 				}
-				Adjust ();
 				break;
 
+			//Redo
+			case Key.ControlY: // Control-y, yank
+				if (historyText != null && historyText.Count > 0) {
+					isFromHistory = true;
+					if (idxhistoryText < historyText.Count - 1) {
+						idxhistoryText++;
+						if (idxhistoryText < historyText.Count) {
+							Text = historyText [idxhistoryText];
+						} else if (idxhistoryText == historyText.Count - 1) {
+							Text = historyText [historyText.Count - 1];
+						}
+						point = text.Count;
+					}
+					isFromHistory = false;
+				}
+
+				//if (Clipboard.Contents == null)
+				//	return true;
+				//var clip = TextModel.ToRunes (Clipboard.Contents);
+				//if (clip == null)
+				//	return true;
+
+				//if (point == text.Count) {
+				//	point = text.Count;
+				//	SetText(text.Concat(clip).ToList());
+				//} else {
+				//	point += clip.Count;
+				//	SetText(text.GetRange(0, oldCursorPos).Concat(clip).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
+				//}
+				//Adjust ();
+
+				break;
+
+			case Key.CursorLeft | Key.CtrlMask:
 			case (Key)((int)'b' + Key.AltMask):
 				ClearAllSelection ();
 				int bw = WordBackward (point);
@@ -336,6 +436,7 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
+			case Key.CursorRight | Key.CtrlMask:
 			case (Key)((int)'f' + Key.AltMask):
 				ClearAllSelection ();
 				int fw = WordForward (point);
@@ -344,20 +445,20 @@ namespace Terminal.Gui {
 				Adjust ();
 				break;
 
-			case Key.AltMask | Key.ControlI:
+			case Key.InsertChar:
 				Used = !Used;
 				SetNeedsDisplay ();
 				break;
 
-			case Key.AltMask | Key.ControlC:
+			case Key.ControlC:
 				Copy ();
 				break;
 
-			case Key.AltMask | Key.ControlX:
+			case Key.ControlX:
 				Cut ();
 				break;
 
-			case Key.AltMask | Key.ControlV:
+			case Key.ControlV:
 				Paste ();
 				break;
 
@@ -378,7 +479,7 @@ namespace Terminal.Gui {
 				var kbstr = TextModel.ToRunes (ustring.Make ((uint)kb.Key));
 				if (used) {
 					point++;
-					if (point == text.Count) {
+					if (point == text.Count + 1) {
 						SetText (text.Concat (kbstr).ToList ());
 					} else {
 						SetText (text.GetRange (0, oldCursorPos).Concat (kbstr).Concat (text.GetRange (oldCursorPos, Math.Min (text.Count - oldCursorPos, text.Count))));
@@ -476,7 +577,8 @@ namespace Terminal.Gui {
 		public override bool MouseEvent (MouseEvent ev)
 		{
 			if (!ev.Flags.HasFlag (MouseFlags.Button1Pressed) && !ev.Flags.HasFlag (MouseFlags.ReportMousePosition) &&
-				!ev.Flags.HasFlag (MouseFlags.Button1Released))
+				!ev.Flags.HasFlag (MouseFlags.Button1Released) && !ev.Flags.HasFlag (MouseFlags.Button1DoubleClicked) &&
+				!ev.Flags.HasFlag (MouseFlags.Button1TripleClicked))
 				return false;
 
 			if (ev.Flags == MouseFlags.Button1Pressed) {
@@ -493,13 +595,25 @@ namespace Terminal.Gui {
 				if (Application.mouseGrabView == null) {
 					Application.GrabMouse (this);
 				}
-			} else if (ev.Flags == MouseFlags.Button1Pressed) {
-				int x = PositionCursor (ev);
-				if (SelectedLength != 0)
-					ClearAllSelection ();
 			} else if (ev.Flags == MouseFlags.Button1Released) {
 				isButtonReleased = true;
 				Application.UngrabMouse ();
+			} else if (ev.Flags == MouseFlags.Button1DoubleClicked) {
+				int x = PositionCursor (ev);
+				int sbw = x;
+				if (x > 0 && (char)Text [x - 1] != ' ')
+					sbw = WordBackward (x);
+				if (sbw != -1) {
+					x = sbw;
+					PositionCursor (x);
+				}
+				int sfw = WordForward (x);
+				ClearAllSelection ();
+				PrepareSelection (sbw, sfw - sbw);
+			} else if (ev.Flags == MouseFlags.Button1TripleClicked) {
+				PositionCursor (0);
+				ClearAllSelection ();
+				PrepareSelection (0, text.Count);
 			}
 
 			SetNeedsDisplay ();
@@ -510,12 +624,16 @@ namespace Terminal.Gui {
 		{
 			// We could also set the cursor position.
 			int x;
-			if (Application.mouseGrabView == null) {
+			if (text.Count == 0)
+				x = ev.X - ev.OfX;
+			else
 				x = ev.X;
-			} else {
-				x = ev.X;// - (text.Count > Frame.Width ? text.Count - Frame.Width : 0);
-			}
 
+			return PositionCursor (x);
+		}
+
+		private int PositionCursor (int x)
+		{
 			point = first + x;
 			if (point > text.Count)
 				point = text.Count;
@@ -524,12 +642,12 @@ namespace Terminal.Gui {
 			return x;
 		}
 
-		void PrepareSelection (int x)
+		void PrepareSelection (int x, int direction = 0)
 		{
-			x = x + first < 0 ? 0 : x + first;
+			x = x + first < 0 ? 0 : x;
 			SelectedStart = SelectedStart == -1 && text.Count > 0 && x >= 0 && x <= text.Count ? x : SelectedStart;
 			if (SelectedStart > -1) {
-				SelectedLength = x <= text.Count ? x - SelectedStart : text.Count - SelectedStart;
+				SelectedLength = x + direction <= text.Count ? x + direction - SelectedStart : text.Count - SelectedStart;
 				SetSelectedStartSelectedLength ();
 				SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
 					start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
@@ -542,7 +660,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public void ClearAllSelection ()
 		{
-			if (SelectedLength == 0)
+			if (SelectedStart == -1 && SelectedLength == 0)
 				return;
 			SelectedStart = -1;
 			SelectedLength = 0;
@@ -601,10 +719,13 @@ namespace Terminal.Gui {
 		{
 			string actualText = Text.ToString ();
 			int start = SelectedStart == -1 ? CursorPosition : SelectedStart;
+			ustring cbTxt = Clipboard.Contents?.ToString () ?? "";
 			Text = actualText.Substring (0, start) +
-				Clipboard.Contents?.ToString () +
+				cbTxt +
 				actualText.Substring (start + SelectedLength, actualText.Length - start - SelectedLength);
+			point = start + cbTxt.Length;
 			SelectedLength = 0;
+			ClearAllSelection ();
 			SetNeedsDisplay ();
 		}