Browse Source

Merge branch 'master' into tree-view

tznind 4 years ago
parent
commit
edf46b6f6d

+ 1 - 1
Example/Example.csproj

@@ -6,7 +6,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="NStack.Core" Version="0.15.0" />
+    <PackageReference Include="NStack.Core" Version="0.16.0" />
   </ItemGroup>
 
   <ItemGroup>

+ 3 - 1
Example/demo.cs

@@ -562,9 +562,11 @@ static class Demo {
 		if (Debugger.IsAttached)
 			CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
 
-		//Application.UseSystemConsole = true;
+		Application.UseSystemConsole = true;
 
 		Application.Init();
+		Application.HeightAsBuffer = true;
+		//ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler;
 
 		var top = Application.Top;
 

+ 4 - 4
ReactiveExample/ReactiveExample.csproj

@@ -4,10 +4,10 @@
         <TargetFramework>netcoreapp3.1</TargetFramework>
     </PropertyGroup>
     <ItemGroup>
-        <PackageReference Include="Pharmacist.MsBuild" Version="1.8.1" PrivateAssets="all" />
-        <PackageReference Include="Pharmacist.Common" Version="1.8.1" />
+        <PackageReference Include="Pharmacist.MsBuild" Version="2.0.6" PrivateAssets="all" />
+        <PackageReference Include="Pharmacist.Common" Version="2.0.6" />
         <PackageReference Include="Terminal.Gui" Version="1.0.0-pre.*" />
-        <PackageReference Include="ReactiveUI.Fody" Version="11.5.35" />
-        <PackageReference Include="ReactiveUI" Version="11.5.35" />
+        <PackageReference Include="ReactiveUI.Fody" Version="13.0.1" />
+        <PackageReference Include="ReactiveUI" Version="13.0.1" />
     </ItemGroup>
 </Project>

+ 25 - 6
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -20,6 +20,7 @@ namespace Terminal.Gui {
 		public override int Cols => Curses.Cols;
 		public override int Rows => Curses.Lines;
 		public override int Top => 0;
+		public override bool HeightAsBuffer { get; set; }
 
 		// Current row, and current col, tracked by Move/AddRune only
 		int ccol, crow;
@@ -39,7 +40,7 @@ namespace Terminal.Gui {
 		}
 
 		static bool sync = false;
-		public override void AddRune (Rune rune) 
+		public override void AddRune (Rune rune)
 		{
 			if (Clip.Contains (ccol, crow)) {
 				if (needMove) {
@@ -70,6 +71,7 @@ namespace Terminal.Gui {
 		public override void Refresh () {
 			Curses.refresh ();
 			if (Curses.CheckWinChange ()) {
+				Clip = new Rect (0, 0, Cols, Rows);
 				TerminalResized?.Invoke ();
 			}
 		}
@@ -89,7 +91,14 @@ namespace Terminal.Gui {
 		}
 
 		public override void UpdateScreen () => window.redrawwin ();
-		public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
+
+		int currentAttribute;
+
+		public override void SetAttribute (Attribute c) {
+			currentAttribute = c.Value;
+			Curses.attrset (currentAttribute);
+		}
+
 		public Curses.Window window;
 
 		static short last_color_pair = 16;
@@ -103,7 +112,11 @@ namespace Terminal.Gui {
 		public static Attribute MakeColor (short foreground, short background)
 		{
 			Curses.InitColorPair (++last_color_pair, foreground, background);
-			return new Attribute () { value = Curses.ColorPair (last_color_pair) };
+			return new Attribute (
+				value: Curses.ColorPair (last_color_pair),
+				foreground: (Color)foreground,
+				background: (Color)background
+				);
 		}
 
 		int [,] colorPairs = new int [16, 16];
@@ -633,6 +646,7 @@ namespace Terminal.Gui {
 				}
 				keyDownHandler (new KeyEvent (k, MapKeyModifiers (k)));
 				keyHandler (new KeyEvent (k, MapKeyModifiers (k)));
+				keyUpHandler (new KeyEvent (k, MapKeyModifiers (k)));
 			}
 			// Cause OnKeyUp and OnKeyPressed. Note that the special handling for ESC above 
 			// will not impact KeyUp.
@@ -645,16 +659,16 @@ namespace Terminal.Gui {
 		}
 
 		Action<MouseEvent> mouseHandler;
-		MainLoop mainLoop;
 
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			Curses.timeout (0);
 			this.mouseHandler = mouseHandler;
-			this.mainLoop = mainLoop;
 
-			(mainLoop.Driver as UnixMainLoop).AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
+			var mLoop = mainLoop.Driver as UnixMainLoop;
+
+			mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
 				ProcessInput (keyHandler, keyDownHandler, keyUpHandler, mouseHandler);
 				return true;
 			});
@@ -846,6 +860,11 @@ namespace Terminal.Gui {
 			//mouseGrabbed = false;
 			//Curses.mouseinterval (lastMouseInterval);
 		}
+
+		public override Attribute GetAttribute ()
+		{
+			return currentAttribute;
+		}
 	}
 
 	internal static class Platform {

+ 1 - 2
Terminal.Gui/ConsoleDrivers/CursesDriver/handles.cs

@@ -28,9 +28,8 @@
 using System;
 
 namespace Unix.Terminal {
-
-	public partial class Curses {
 #pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+	public partial class Curses {
 		public class Window {
 			public readonly IntPtr Handle;
 			static Window curscr;

+ 14 - 3
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -20,6 +20,7 @@ namespace Terminal.Gui {
 		public override int Cols => cols;
 		public override int Rows => rows;
 		public override int Top => 0;
+		public override bool HeightAsBuffer { get; set; }
 
 		// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
 		int [,,] contents;
@@ -112,7 +113,11 @@ namespace Terminal.Gui {
 		static Attribute MakeColor (ConsoleColor f, ConsoleColor b)
 		{
 			// Encode the colors into the int value.
-			return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
+			return new Attribute (
+				value: ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff),
+				foreground: (Color)f,
+				background: (Color)b
+				);
 		}
 
 		public override void Init (Action terminalResized)
@@ -248,7 +253,7 @@ namespace Terminal.Gui {
 		int currentAttribute;
 		public override void SetAttribute (Attribute c)
 		{
-			currentAttribute = c.value;
+			currentAttribute = c.Value;
 		}
 
 		Key MapKey (ConsoleKeyInfo keyInfo)
@@ -389,9 +394,14 @@ namespace Terminal.Gui {
 			};
 		}
 
+		public override Attribute GetAttribute ()
+		{
+			return currentAttribute;
+		}
+
+		#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
-			throw new NotImplementedException ();
 		}
 
 		public override void SetColors (short foregroundColorId, short backgroundColorId)
@@ -406,6 +416,7 @@ namespace Terminal.Gui {
 		public override void UncookMouse ()
 		{
 		}
+		#endregion
 #pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
 	}
 }

File diff suppressed because it is too large
+ 870 - 52
Terminal.Gui/ConsoleDrivers/NetDriver.cs


+ 527 - 256
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -59,31 +59,40 @@ namespace Terminal.Gui {
 		public bool WriteToConsole (CharInfo [] charInfoBuffer, Coord coords, SmallRect window)
 		{
 			if (ScreenBuffer == IntPtr.Zero) {
-				ScreenBuffer = CreateConsoleScreenBuffer (
-					DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
-					ShareMode.FileShareRead | ShareMode.FileShareWrite,
-					IntPtr.Zero,
-					1,
-					IntPtr.Zero
-				);
-				if (ScreenBuffer == INVALID_HANDLE_VALUE) {
-					var err = Marshal.GetLastWin32Error ();
-
-					if (err != 0)
-						throw new System.ComponentModel.Win32Exception (err);
-				}
+				window = ReadFromConsoleOutput (new Size (Console.WindowWidth, Console.WindowHeight), coords, window);
+			}
 
-				if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
-					var err = Marshal.GetLastWin32Error ();
+			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
+		}
+
+		public SmallRect ReadFromConsoleOutput (Size size, Coord coords, SmallRect window)
+		{
+			ScreenBuffer = CreateConsoleScreenBuffer (
+				DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
+				ShareMode.FileShareRead | ShareMode.FileShareWrite,
+				IntPtr.Zero,
+				1,
+				IntPtr.Zero
+			);
+			if (ScreenBuffer == INVALID_HANDLE_VALUE) {
+				var err = Marshal.GetLastWin32Error ();
+
+				if (err != 0 && HeightAsBuffer) {
 					throw new System.ComponentModel.Win32Exception (err);
 				}
+			}
 
-				OriginalStdOutChars = new CharInfo [Console.WindowHeight * Console.WindowWidth];
-
-				ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
+			if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
+				var err = Marshal.GetLastWin32Error ();
+				if (HeightAsBuffer) {
+					throw new System.ComponentModel.Win32Exception (err);
+				}
 			}
 
-			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
+			OriginalStdOutChars = new CharInfo [size.Height * size.Width];
+			ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
+
+			return window;
 		}
 
 		public bool SetCursorPosition (Coord position)
@@ -120,6 +129,8 @@ namespace Terminal.Gui {
 			}
 		}
 
+		public bool HeightAsBuffer { get; set; }
+
 		[Flags]
 		public enum ConsoleModes : uint {
 			EnableProcessedInput = 1,
@@ -207,7 +218,7 @@ namespace Terminal.Gui {
 			public override string ToString () => $"({X},{Y})";
 		};
 
-		internal struct WindowBufferSizeRecord {
+		public struct WindowBufferSizeRecord {
 			public Coordinate size;
 
 			public WindowBufferSizeRecord (short x, short y)
@@ -355,6 +366,20 @@ namespace Terminal.Gui {
 			}
 		}
 
+		[StructLayout (LayoutKind.Sequential)]
+		public struct ConsoleKeyInfoEx {
+			public ConsoleKeyInfo consoleKeyInfo;
+			public bool CapsLock;
+			public bool NumLock;
+
+			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
+			{
+				this.consoleKeyInfo = consoleKeyInfo;
+				CapsLock = capslock;
+				NumLock = numlock;
+			}
+		}
+
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		static extern IntPtr GetStdHandle (int nStdHandle);
 
@@ -431,231 +456,147 @@ namespace Terminal.Gui {
 
 				return numberEventsRead == 0
 					? null
-					: new [] {Marshal.PtrToStructure<InputRecord> (pRecord)};
+					: new [] { Marshal.PtrToStructure<InputRecord> (pRecord) };
 			} catch (Exception) {
 				return null;
 			} finally {
 				Marshal.FreeHGlobal (pRecord);
 			}
 		}
-#if false	// Not needed on the constructor. Perhaps could be used on resizing. To study.
-		[DllImport ("kernel32.dll", ExactSpelling = true)]
-		private static extern IntPtr GetConsoleWindow ();
-
-		[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
-		private static extern bool ShowWindow (IntPtr hWnd, int nCmdShow);
 
-		public const int HIDE = 0;
-		public const int MAXIMIZE = 3;
-		public const int MINIMIZE = 6;
-		public const int RESTORE = 9;
+		// Not needed on the constructor. Perhaps could be used on resizing. To study.
+		[DllImport ("kernel32.dll", ExactSpelling = true)]
+		static extern IntPtr GetConsoleWindow ();
 
-		internal void ShowWindow (int state)
+		internal IntPtr GetConsole ()
 		{
-			IntPtr thisConsole = GetConsoleWindow ();
-			ShowWindow (thisConsole, state);
-		}
+			return GetConsoleWindow ();
+		}
+
+		[DllImport ("user32.dll")]
+		[return: MarshalAs (UnmanagedType.Bool)]
+		static extern bool GetWindowPlacement (IntPtr hWnd, ref WindowPlacement lpwndpl);
+
+		[DllImport ("user32.dll", SetLastError = true)]
+		[return: MarshalAs (UnmanagedType.Bool)]
+		static extern bool SetWindowPlacement (IntPtr hWnd, [In] ref WindowPlacement lpwndpl);
+
+		internal struct WindowPlacement {
+			public int length;
+			public int flags;
+			public int showCmd;
+			public System.Drawing.Point ptMinPosition;
+			public System.Drawing.Point ptMaxPosition;
+			public System.Drawing.Rectangle rcNormalPosition;
+#if _MAC
+			public System.Drawing.Rectangle rcDevice;
 #endif
-#if false // See: https://github.com/migueldeicaza/gui.cs/issues/357
-		[StructLayout (LayoutKind.Sequential)]
-		public struct SMALL_RECT {
-			public short Left;
-			public short Top;
-			public short Right;
-			public short Bottom;
-
-			public SMALL_RECT (short Left, short Top, short Right, short Bottom)
-			{
-				this.Left = Left;
-				this.Top = Top;
-				this.Right = Right;
-				this.Bottom = Bottom;
-			}
 		}
 
-		[StructLayout (LayoutKind.Sequential)]
-		public struct CONSOLE_SCREEN_BUFFER_INFO {
-			public int dwSize;
-			public int dwCursorPosition;
-			public short wAttributes;
-			public SMALL_RECT srWindow;
-			public int dwMaximumWindowSize;
-		}
+		// flags
+		public const int WPF_SET_MIN_POSITION = 1;
+		public const int WPF_RESTORE_TO_MAXIMIZED = 2;
+		public const int WPF_ASYNC_WINDOWPLACEMENT = 4;
 
-		[DllImport ("kernel32.dll", SetLastError = true)]
-		static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo);
+		// showCmd
+		public const int HIDE = 0;
+		public const int SHOW_NORMAL = 1;
+		public const int SHOW_MINIMIZED = 2;
+		public const int SHOW_MAXIMIZED = 3;
+		public const int SHOW_NOACTIVATE = 4;
+		public const int SHOW = 5;
+		public const int MINIMIZE = 6;
+		public const int SHOW_MIN_NOACTIVE = 7;
+		public const int SHOW_NA = 8;
+		public const int RESTORE = 9;
+		public const int SHOW_DEFAULT = 10;
+		public const int FORCE_MINIMIZE = 11;
 
-		// Theoretically GetConsoleScreenBuffer height should give the console Windoww size
-		// It does not work, however, and always returns the size the window was initially created at
-		internal Size GetWindowSize ()
+		internal bool GetWindow (IntPtr handle, ref WindowPlacement placement)
 		{
-			var consoleScreenBufferInfo = new CONSOLE_SCREEN_BUFFER_INFO ();
-			//consoleScreenBufferInfo.dwSize = Marshal.SizeOf (typeof (CONSOLE_SCREEN_BUFFER_INFO));
-			GetConsoleScreenBufferInfo (OutputHandle, out consoleScreenBufferInfo);
-			return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left,
-				consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top);
+			placement = new WindowPlacement {
+				length = Marshal.SizeOf (typeof (WindowPlacement))
+			};
+			return GetWindowPlacement (handle, ref placement);
 		}
-#endif
-	}
-
-	internal class WindowsDriver : ConsoleDriver, IMainLoopDriver {
-		static bool sync = false;
-		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
-		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
-		MainLoop mainLoop;
-		WindowsConsole.CharInfo [] OutputBuffer;
-		int cols, rows;
-		WindowsConsole winConsole;
-		WindowsConsole.SmallRect damageRegion;
-
-		public override int Cols => cols;
-		public override int Rows => rows;
-		public override int Top => 0;
 
-		public WindowsDriver ()
+		internal bool SetWindow (IntPtr handle, ref WindowPlacement placement)
 		{
-			winConsole = new WindowsConsole ();
-
-			SetupColorsAndBorders ();
-
-			cols = Console.WindowWidth;
-			rows = Console.WindowHeight;
-#if false
-			winConsole.ShowWindow (WindowsConsole.RESTORE);
-#endif
-			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
-
-			ResizeScreen ();
-			UpdateOffScreen ();
-
-			Task.Run ((Action)WindowsInputHandler);
+			return SetWindowPlacement (handle, ref placement);
 		}
 
-		private void SetupColorsAndBorders ()
-		{
-			Colors.TopLevel = new ColorScheme ();
-			Colors.Base = new ColorScheme ();
-			Colors.Dialog = new ColorScheme ();
-			Colors.Menu = new ColorScheme ();
-			Colors.Error = new ColorScheme ();
-
-			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
-			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
-			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
-			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
-
-			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
-			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
-			Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+		[DllImport ("user32.dll", SetLastError = true)]
+		static extern bool GetWindowRect (IntPtr hwnd, out System.Drawing.Rectangle lpRect);
 
-			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
-			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
-			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
-			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
-			Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
-
-			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
-			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
-			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
-
-			Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
-			Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
-			Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
-			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
+		internal bool GetRect (IntPtr handle, out System.Drawing.Rectangle lpRect)
+		{
+			return GetWindowRect (handle, out lpRect);
 		}
 
-		[StructLayout (LayoutKind.Sequential)]
-		public struct ConsoleKeyInfoEx {
-			public ConsoleKeyInfo consoleKeyInfo;
-			public bool CapsLock;
-			public bool NumLock;
+#if false
+		// size of a device name string
+		private const int CCHDEVICENAME = 32;
 
-			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
-			{
-				this.consoleKeyInfo = consoleKeyInfo;
-				CapsLock = capslock;
-				NumLock = numlock;
-			}
+		[StructLayout (LayoutKind.Sequential, CharSet = CharSet.Auto)]
+		internal struct MonitorInfoEx {
+			public uint cbSize;
+			public System.Drawing.Rectangle rcMonitor;
+			public System.Drawing.Rectangle rcWork;
+			public int dwFlags;
+			[MarshalAs (UnmanagedType.ByValTStr, SizeConst = CCHDEVICENAME)]
+			public string szDevice;
 		}
 
-		// The records that we keep fetching
-		WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1];
+		[DllImport ("user32.dll", CharSet = CharSet.Auto)]
+		static extern bool GetMonitorInfo (IntPtr hMonitor, ref MonitorInfoEx lpmi);
 
-		void WindowsInputHandler ()
+		internal bool GetMonitor(IntPtr hMonitor, ref MonitorInfoEx minfo)
 		{
-			while (true) {
-				waitForProbe.Wait ();
-				waitForProbe.Reset ();
+			minfo.cbSize = (uint)Marshal.SizeOf (minfo);
+			return GetMonitorInfo (hMonitor, ref minfo);
+		}
 
-				result = winConsole.ReadConsoleInput ();
+		[DllImport ("user32.dll")]
+		static extern IntPtr MonitorFromWindow (IntPtr hwnd, uint dwFlags);
 
-				eventReady.Set ();
-			}
-		}
+		public const int MONITOR_DEFAULTTONULL = 0;
+		public const int MONITOR_DEFAULTTOPRIMARY = 1;
+		public const int MONITOR_DEFAULTTONEAREST = 2;
 
-		void IMainLoopDriver.Setup (MainLoop mainLoop)
+		internal IntPtr GetMonitorWindow (IntPtr hwnd, uint dwFlag)
 		{
-			this.mainLoop = mainLoop;
+			return MonitorFromWindow (hwnd, dwFlag);
 		}
 
-		void IMainLoopDriver.Wakeup ()
-		{
-			//tokenSource.Cancel ();
-			eventReady.Reset ();
-			eventReady.Set ();
-		}
+		[DllImport ("kernel32.dll", SetLastError = true)]
+		static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out ConsoleScreenBufferInfo ConsoleScreenBufferInfo);
 
-		bool IMainLoopDriver.EventsPending (bool wait)
+		// Theoretically GetConsoleScreenBuffer height should give the console Window size, but the Top is always 0.
+		// It does not work, however, and always returns the size the window was initially created at
+		internal Size GetWindowSize (IntPtr handle)
 		{
-			if (CheckTimers (wait, out var waitTimeout)) {
-				return true;
-			}
-
-			result = null;
-			waitForProbe.Set ();
-
-			try {
-				if (!tokenSource.IsCancellationRequested) {
-					eventReady.Wait (waitTimeout, tokenSource.Token);
-				}
-			} catch (OperationCanceledException) {
-				return true;
-			} finally {
-				eventReady.Reset ();
-			}
-
-			if (!tokenSource.IsCancellationRequested) {
-				return result != null || CheckTimers (wait, out waitTimeout);
-			}
-
-			tokenSource.Dispose ();
-			tokenSource = new CancellationTokenSource ();
-			return true;
+			GetConsoleScreenBufferInfo (handle, out ConsoleScreenBufferInfo consoleScreenBufferInfo);
+			return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left + 1,
+				consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top + 1);
 		}
+#endif
+	}
 
-		bool CheckTimers (bool wait, out int waitTimeout)
-		{
-			long now = DateTime.UtcNow.Ticks;
-
-			if (mainLoop.timeouts.Count > 0) {
-				waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
-				if (waitTimeout < 0)
-					return true;
-			} else {
-				waitTimeout = -1;
-			}
-
-			if (!wait)
-				waitTimeout = 0;
+	internal class WindowsDriver : ConsoleDriver {
+		static bool sync = false;
+		WindowsConsole.CharInfo [] OutputBuffer;
+		int cols, rows, top;
+		WindowsConsole winConsole;
+		WindowsConsole.SmallRect damageRegion;
 
-			int ic;
-			lock (mainLoop.idleHandlers) {
-				ic = mainLoop.idleHandlers.Count;
-			}
+		public override int Cols => cols;
+		public override int Rows => rows;
+		public override int Top => top;
+		public override bool HeightAsBuffer { get; set; }
 
-			return ic > 0;
+		public WindowsConsole WinConsole {
+			get => winConsole;
+			private set => winConsole = value;
 		}
 
 		Action<KeyEvent> keyHandler;
@@ -663,20 +604,58 @@ namespace Terminal.Gui {
 		Action<KeyEvent> keyUpHandler;
 		Action<MouseEvent> mouseHandler;
 
+		public WindowsDriver ()
+		{
+			winConsole = new WindowsConsole () {
+				HeightAsBuffer = this.HeightAsBuffer
+			};
+		}
+
+		MainLoop mainLoop;
+
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 			this.keyHandler = keyHandler;
 			this.keyDownHandler = keyDownHandler;
 			this.keyUpHandler = keyUpHandler;
 			this.mouseHandler = mouseHandler;
+			this.mainLoop = mainLoop;
+
+			var mLoop = mainLoop.Driver as WindowsMainLoop;
+
+			mLoop.ProcessInput = (e) => ProcessInput (e);
+
+			mLoop.WinChanged = (e) => ChangeWin (e);
 		}
 
-		void IMainLoopDriver.MainIteration ()
+		bool winChanging;
+		bool wasChangeWin;
+
+		void ChangeWin (Size size)
 		{
-			if (result == null)
-				return;
+			if (!HeightAsBuffer) {
+				winChanging = true;
+				top = 0;
+				cols = size.Width;
+				rows = size.Height;
+				ResizeScreen ();
+				UpdateOffScreen ();
+				var bufferCoords = new WindowsConsole.Coord () {
+					X = (short)cols,
+					Y = (short)rows
+				};
+				winConsole.ReadFromConsoleOutput (size, bufferCoords, damageRegion);
+				if (!winChanging) {
+					TerminalResized.Invoke ();
+				}
+				wasChangeWin = true;
+			}
+		}
+
+		bool isFromRestore;
 
-			var inputEvent = result [0];
+		void ProcessInput (WindowsConsole.InputRecord inputEvent)
+		{
 			switch (inputEvent.EventType) {
 			case WindowsConsole.EventType.Key:
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
@@ -766,20 +745,30 @@ namespace Terminal.Gui {
 				break;
 
 			case WindowsConsole.EventType.WindowBufferSize:
-				cols = inputEvent.WindowBufferSizeEvent.size.X;
-				rows = inputEvent.WindowBufferSizeEvent.size.Y;
-				ResizeScreen ();
-				UpdateOffScreen ();
-				TerminalResized?.Invoke ();
+				if (HeightAsBuffer) {
+					cols = inputEvent.WindowBufferSizeEvent.size.X;
+					rows = inputEvent.WindowBufferSizeEvent.size.Y;
+					ResizeScreen ();
+					UpdateOffScreen ();
+					TerminalResized?.Invoke ();
+				} else if (!HeightAsBuffer && !wasChangeWin && !(mainLoop.Driver as WindowsMainLoop).Maximized
+					&& !isFromRestore) {
+					ChangeWin (new Size (inputEvent.WindowBufferSizeEvent.size.X,
+						inputEvent.WindowBufferSizeEvent.size.Y));
+				} else if (!HeightAsBuffer && wasChangeWin && (mainLoop.Driver as WindowsMainLoop).Restored) {
+					(mainLoop.Driver as WindowsMainLoop).Restored = false;
+					isFromRestore = true;
+				} else if (!HeightAsBuffer && wasChangeWin && !(mainLoop.Driver as WindowsMainLoop).Maximized
+					&& !(mainLoop.Driver as WindowsMainLoop).Restored && !isFromRestore) {
+					wasChangeWin = false;
+				} else if (isFromRestore) {
+					isFromRestore = false;
+				}
 				break;
 
 			case WindowsConsole.EventType.Focus:
 				break;
-
-			default:
-				break;
 			}
-			result = null;
 		}
 
 		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
@@ -871,23 +860,23 @@ namespace Terminal.Gui {
 					Y = mouseEvent.MousePosition.Y
 				};
 				//if (p == point) {
-					switch (LastMouseButtonPressed) {
-					case WindowsConsole.ButtonState.Button1Pressed:
-						mouseFlag = MouseFlags.Button1Clicked;
-						break;
+				switch (LastMouseButtonPressed) {
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1Clicked;
+					break;
 
-					case WindowsConsole.ButtonState.Button2Pressed:
-						mouseFlag = MouseFlags.Button2Clicked;
-						break;
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2Clicked;
+					break;
 
-					case WindowsConsole.ButtonState.RightmostButtonPressed:
-						mouseFlag = MouseFlags.Button3Clicked;
-						break;
-					}
-					point = new Point () {
-						X = mouseEvent.MousePosition.X,
-						Y = mouseEvent.MousePosition.Y
-					};
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button3Clicked;
+					break;
+				}
+				point = new Point () {
+					X = mouseEvent.MousePosition.X,
+					Y = mouseEvent.MousePosition.Y
+				};
 				//} else {
 				//	mouseFlag = 0;
 				//}
@@ -1009,7 +998,7 @@ namespace Terminal.Gui {
 
 		KeyModifiers keyModifiers;
 
-		public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
+		public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
 		{
 			var state = keyEvent.dwControlKeyState;
 
@@ -1036,10 +1025,10 @@ namespace Terminal.Gui {
 				keyModifiers.Scrolllock = scrolllock;
 
 			var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-			return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
+			return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
 		}
 
-		public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
+		public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
 		{
 			var keyInfo = keyInfoEx.consoleKeyInfo;
 			switch (keyInfo.Key) {
@@ -1163,7 +1152,7 @@ namespace Terminal.Gui {
 			return (Key)(0xffffffff);
 		}
 
-		private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
+		Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
 		{
 			Key keyMod = new Key ();
 			if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
@@ -1179,7 +1168,48 @@ namespace Terminal.Gui {
 		public override void Init (Action terminalResized)
 		{
 			TerminalResized = terminalResized;
-			SetupColorsAndBorders ();
+
+			cols = Console.WindowWidth;
+			rows = Console.WindowHeight;
+#if false
+			winConsole.ShowWindow (WindowsConsole.RESTORE);
+#endif
+			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
+
+			ResizeScreen ();
+			UpdateOffScreen ();
+
+			Colors.TopLevel = new ColorScheme ();
+			Colors.Base = new ColorScheme ();
+			Colors.Dialog = new ColorScheme ();
+			Colors.Menu = new ColorScheme ();
+			Colors.Error = new ColorScheme ();
+
+			Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
+			Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
+			Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
+			Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+
+			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
+			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
+			Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+
+			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
+			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
+			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
+			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+			Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
+
+			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
+			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
+			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
+			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+
+			Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
+			Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
+			Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
+			Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
 		}
 
 		void ResizeScreen ()
@@ -1196,12 +1226,15 @@ namespace Terminal.Gui {
 
 		void UpdateOffScreen ()
 		{
-			for (int row = 0; row < rows; row++)
+			for (int row = 0; row < rows; row++) {
 				for (int col = 0; col < cols; col++) {
 					int position = row * cols + col;
 					OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
 					OutputBuffer [position].Char.UnicodeChar = ' ';
 				}
+			}
+
+			winChanging = false;
 		}
 
 		int ccol, crow;
@@ -1245,21 +1278,20 @@ namespace Terminal.Gui {
 		}
 
 		int currentAttribute;
-		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 		public override void SetAttribute (Attribute c)
 		{
-			currentAttribute = c.value;
+			currentAttribute = c.Value;
 		}
 
 		Attribute MakeColor (ConsoleColor f, ConsoleColor b)
 		{
 			// Encode the colors into the int value.
-			return new Attribute () {
-				value = ((int)f | (int)b << 4),
-				foreground = (Color)f,
-				background = (Color)b
-			};
+			return new Attribute (
+				value: ((int)f | (int)b << 4),
+				foreground: (Color)f,
+				background: (Color)b
+			);
 		}
 
 		public override Attribute MakeAttribute (Color fore, Color back)
@@ -1298,16 +1330,16 @@ namespace Terminal.Gui {
 				Y = (short)Clip.Height
 			};
 
-			var window = new WindowsConsole.SmallRect () {
-				Top = 0,
-				Left = 0,
-				Right = (short)Clip.Right,
-				Bottom = (short)Clip.Bottom
-			};
+			//var window = new WindowsConsole.SmallRect () {
+			//	Top = 0,
+			//	Left = 0,
+			//	Right = (short)Clip.Right,
+			//	Bottom = (short)Clip.Bottom
+			//};
 
 			UpdateCursor ();
 			winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
-			//			System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
+			//System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 		}
 
@@ -1325,7 +1357,12 @@ namespace Terminal.Gui {
 			winConsole.Cleanup ();
 		}
 
-#region Unused
+		public override Attribute GetAttribute ()
+		{
+			return currentAttribute;
+		}
+
+		#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		}
@@ -1353,8 +1390,242 @@ namespace Terminal.Gui {
 		public override void CookMouse ()
 		{
 		}
-#endregion
-
+		#endregion
 	}
 
+	/// <summary>
+	/// Mainloop intended to be used with the <see cref="WindowsDriver"/>, and can
+	/// only be used on Windows.
+	/// </summary>
+	/// <remarks>
+	/// This implementation is used for WindowsDriver.
+	/// </remarks>
+	internal class WindowsMainLoop : IMainLoopDriver {
+		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
+		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
+		ManualResetEventSlim winChange = new ManualResetEventSlim (false);
+		MainLoop mainLoop;
+		ConsoleDriver consoleDriver;
+		WindowsConsole winConsole;
+		bool winChanged;
+		Size windowSize;
+		CancellationTokenSource tokenSource = new CancellationTokenSource ();
+
+		// The records that we keep fetching
+		WindowsConsole.InputRecord [] result = new WindowsConsole.InputRecord [1];
+
+		/// <summary>
+		/// Invoked when a Key is pressed or released.
+		/// </summary>
+		public Action<WindowsConsole.InputRecord> ProcessInput;
+
+		/// <summary>
+		/// Invoked when the window is changed.
+		/// </summary>
+		public Action<Size> WinChanged;
+
+		public bool Maximized;
+		public bool Restored;
+
+		public WindowsMainLoop (ConsoleDriver consoleDriver = null)
+		{
+			if (consoleDriver == null) {
+				throw new ArgumentNullException ("Console driver instance must be provided.");
+			}
+			this.consoleDriver = consoleDriver;
+			winConsole = ((WindowsDriver)consoleDriver).WinConsole;
+		}
+
+		void IMainLoopDriver.Setup (MainLoop mainLoop)
+		{
+			this.mainLoop = mainLoop;
+			Task.Run (WindowsInputHandler);
+			Task.Run (CheckWinChange);
+		}
+
+		void WindowsInputHandler ()
+		{
+			while (true) {
+				waitForProbe.Wait ();
+				waitForProbe.Reset ();
+
+				result = winConsole.ReadConsoleInput ();
+
+				eventReady.Set ();
+			}
+		}
+
+		void CheckWinChange ()
+		{
+			while (true) {
+				winChange.Wait ();
+				winChange.Reset ();
+				WaitWinChange ();
+				winChanged = true;
+				eventReady.Set ();
+			}
+		}
+
+		const int Width_Divider = 8;
+		const int Height_Divider = 18;
+		bool docked;
+
+		void WaitWinChange ()
+		{
+			var handle = winConsole.GetConsole ();
+
+			while (!consoleDriver.HeightAsBuffer) {
+				WindowsConsole.WindowPlacement windowPlacement = new WindowsConsole.WindowPlacement ();
+				winConsole.GetWindow (handle, ref windowPlacement);
+
+				if (windowPlacement.rcNormalPosition.Size.Height > -1) {
+					windowSize = SetWindowSize (windowPlacement.rcNormalPosition);
+
+					if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED && !Maximized && !Restored && !docked
+						&& (windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows)) {
+						docked = false;
+						return;
+					} else if (windowPlacement.showCmd == WindowsConsole.SHOW_MAXIMIZED && !Maximized
+						&& (Console.LargestWindowWidth != consoleDriver.Cols || Console.LargestWindowHeight != consoleDriver.Rows)) {
+						windowSize = new Size (Console.LargestWindowWidth, Console.LargestWindowHeight);
+						Maximized = true;
+						docked = false;
+						return;
+					} else if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED && Maximized) {
+						windowPlacement = new WindowsConsole.WindowPlacement () {
+							showCmd = WindowsConsole.RESTORE
+						};
+						winConsole.SetWindow (handle, ref windowPlacement);
+						Restored = true;
+						Maximized = false;
+						docked = false;
+						return;
+					} else if (!Maximized && IsDockedToMonitor (handle, windowPlacement)) {
+						return;
+					}
+				}
+			}
+
+			Size SetWindowSize (System.Drawing.Rectangle rect)
+			{
+				return new Size (Math.Max (((rect.Width - rect.X) / Width_Divider) - 2, 0),
+					Math.Max (((rect.Height - rect.Y) / Height_Divider) - 2, 0));
+			}
+
+			bool IsDockedToMonitor (IntPtr hWnd, WindowsConsole.WindowPlacement placement)
+			{
+				System.Drawing.Rectangle rc;
+				winConsole.GetRect (hWnd, out rc);
+
+				var changed = placement.showCmd == WindowsConsole.SHOW_NORMAL
+				    && (rc.Left != placement.rcNormalPosition.Left ||
+					rc.Top != placement.rcNormalPosition.Top ||
+					rc.Right != placement.rcNormalPosition.Right ||
+					rc.Bottom != placement.rcNormalPosition.Bottom);
+
+				if (changed) {
+					var pSize = new Size (placement.rcNormalPosition.Size.Width - placement.rcNormalPosition.X,
+						placement.rcNormalPosition.Size.Height - placement.rcNormalPosition.Y);
+					var rSize = new Size (rc.Width - rc.X,
+						rc.Height - rc.Y);
+					windowSize = SetWindowSize (rc);
+
+					if ((rc.X < 0) || (rc.Y == 0) || (rc.Y == 0 && rc.X < 0)
+						|| (rc.Y == 0 && rc.Right / Width_Divider >= Console.LargestWindowWidth)
+						|| (rc.X < 0 && rc.Bottom / Height_Divider >= Console.LargestWindowHeight)
+						|| (rc.X / Width_Divider >= Console.LargestWindowWidth / 2 - 1 && rc.Bottom / Height_Divider >= Console.LargestWindowHeight)) {
+						if (!docked || consoleDriver.Cols != windowSize.Width
+							|| consoleDriver.Rows != windowSize.Height) {
+							docked = true;
+						} else {
+							changed = false;
+						}
+					} else {
+						if (!docked && (pSize == rSize || rSize.Width / Width_Divider >= Console.LargestWindowWidth
+							|| rSize.Height / Height_Divider >= Console.LargestWindowHeight)) {
+							changed = false;
+						}
+						docked = false;
+					}
+				} else {
+					docked = false;
+				}
+
+				return changed;
+			}
+		}
+
+		void IMainLoopDriver.Wakeup ()
+		{
+			//tokenSource.Cancel ();
+			eventReady.Set ();
+		}
+
+		bool IMainLoopDriver.EventsPending (bool wait)
+		{
+			if (CheckTimers (wait, out var waitTimeout)) {
+				return true;
+			}
+
+			//result = null;
+			waitForProbe.Set ();
+			if (!consoleDriver.HeightAsBuffer) {
+				winChange.Set ();
+			}
+
+			try {
+				if (!tokenSource.IsCancellationRequested) {
+					eventReady.Wait (waitTimeout, tokenSource.Token);
+				}
+			} catch (OperationCanceledException) {
+				return true;
+			} finally {
+				eventReady.Reset ();
+			}
+
+			if (!tokenSource.IsCancellationRequested) {
+				return result != null || CheckTimers (wait, out _) || winChanged;
+			}
+
+			tokenSource.Dispose ();
+			tokenSource = new CancellationTokenSource ();
+			return true;
+		}
+
+		bool CheckTimers (bool wait, out int waitTimeout)
+		{
+			long now = DateTime.UtcNow.Ticks;
+
+			if (mainLoop.timeouts.Count > 0) {
+				waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
+				if (waitTimeout < 0)
+					return true;
+			} else {
+				waitTimeout = -1;
+			}
+
+			if (!wait)
+				waitTimeout = 0;
+
+			int ic;
+			lock (mainLoop.idleHandlers) {
+				ic = mainLoop.idleHandlers.Count;
+			}
+
+			return ic > 0;
+		}
+
+		void IMainLoopDriver.MainIteration ()
+		{
+			if (result != null) {
+				var inputEvent = result [0];
+				result = null;
+				ProcessInput?.Invoke (inputEvent);
+			}
+			if (winChanged) {
+				winChanged = false;
+				WinChanged?.Invoke (windowSize);
+			}
+		}
+	}
 }

+ 28 - 8
Terminal.Gui/Core/Application.cs

@@ -59,7 +59,7 @@ namespace Terminal.Gui {
 		/// The current <see cref="ConsoleDriver"/> in use.
 		/// </summary>
 		public static ConsoleDriver Driver;
-
+		
 		/// <summary>
 		/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
 		/// </summary>
@@ -73,11 +73,32 @@ namespace Terminal.Gui {
 		public static Toplevel Current { get; private set; }
 
 		/// <summary>
-		/// TThe current <see cref="View"/> object being redrawn.
+		/// The current <see cref="View"/> object being redrawn.
 		/// </summary>
 		/// /// <value>The current.</value>
 		public static View CurrentView { get; set; }
 
+		/// <summary>
+		/// The current <see cref="ConsoleDriver.HeightAsBuffer"/> used in the terminal.
+		/// </summary>
+		public static bool HeightAsBuffer {
+			get {
+				if (Driver == null) {
+					throw new ArgumentNullException ("The driver must be initialized first.");
+				}
+				return Driver.HeightAsBuffer;
+			}
+			set {
+				if (Driver == null) {
+					throw new ArgumentNullException ("The driver must be initialized first.");
+				}
+				if (Driver.HeightAsBuffer != value) {
+					Driver.HeightAsBuffer = value;
+					Driver.Refresh ();
+				}
+			}
+		}
+
 		/// <summary>
 		/// The <see cref="MainLoop"/>  driver for the application
 		/// </summary>
@@ -167,7 +188,7 @@ namespace Terminal.Gui {
 		static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null)
 		{
 			if (_initialized && driver == null) return;
-
+			
 			// Used only for start debugging on Unix.
 //#if DEBUG
 //			while (!System.Diagnostics.Debugger.IsAttached) {
@@ -193,9 +214,8 @@ namespace Terminal.Gui {
 					Driver = new NetDriver ();
 					mainLoopDriver = new NetMainLoop (Driver);
 				} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
-					var windowsDriver = new WindowsDriver ();
-					mainLoopDriver = windowsDriver;
-					Driver = windowsDriver;
+					Driver = new WindowsDriver ();
+					mainLoopDriver = new WindowsMainLoop (Driver);
 				} else {
 					mainLoopDriver = new UnixMainLoop ();
 					Driver = new CursesDriver ();
@@ -588,11 +608,11 @@ namespace Terminal.Gui {
 				} else if (!wait) {
 					return;
 				}
-				if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.childNeedsDisplay)) {
+				if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
 					Top.Redraw (Top.Bounds);
 					state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
 				}
-				if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay) {
+				if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded) {
 					state.Toplevel.Redraw (state.Toplevel.Bounds);
 					if (DebugDrawBounds) {
 						DrawBounds (state.Toplevel);

+ 96 - 68
Terminal.Gui/Core/ConsoleDriver.cs

@@ -61,7 +61,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		BrightGreen,
 		/// <summary>
-		/// The brigh cyan color.
+		/// The bright cyan color.
 		/// </summary>
 		BrighCyan,
 		/// <summary>
@@ -91,9 +91,18 @@ namespace Terminal.Gui {
 	///   class to define color schemes that can be used in your application.
 	/// </remarks>
 	public struct Attribute {
-		internal int value;
-		internal Color foreground;
-		internal Color background;
+		/// <summary>
+		/// The color attribute value.
+		/// </summary>
+		public int Value { get; }
+		/// <summary>
+		/// The foreground color.
+		/// </summary>
+		public Color Foreground { get; }
+		/// <summary>
+		/// The background color.
+		/// </summary>
+		public Color Background { get; }
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="Attribute"/> struct.
@@ -103,9 +112,9 @@ namespace Terminal.Gui {
 		/// <param name="background">Background</param>
 		public Attribute (int value, Color foreground = new Color (), Color background = new Color ())
 		{
-			this.value = value;
-			this.foreground = foreground;
-			this.background = background;
+			Value = value;
+			Foreground = foreground;
+			Background = background;
 		}
 
 		/// <summary>
@@ -115,9 +124,9 @@ namespace Terminal.Gui {
 		/// <param name="background">Background</param>
 		public Attribute (Color foreground = new Color (), Color background = new Color ())
 		{
-			this.value = value = ((int)foreground | (int)background << 4);
-			this.foreground = foreground;
-			this.background = background;
+			Value = (int)foreground | ((int)background << 4);
+			Foreground = foreground;
+			Background = background;
 		}
 
 		/// <summary>
@@ -125,7 +134,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <returns>The integer value stored in the attribute.</returns>
 		/// <param name="c">The attribute to convert</param>
-		public static implicit operator int (Attribute c) => c.value;
+		public static implicit operator int (Attribute c) => c.Value;
 
 		/// <summary>
 		/// Implicitly convert an integer value into an <see cref="Attribute"/>
@@ -146,6 +155,17 @@ namespace Terminal.Gui {
 				throw new InvalidOperationException ("The Application has not been initialized");
 			return Application.Driver.MakeAttribute (foreground, background);
 		}
+
+		/// <summary>
+		/// Gets the current <see cref="Attribute"/> from the driver.
+		/// </summary>
+		/// <returns>The current attribute.</returns>
+		public static Attribute Get ()
+		{
+			if (Application.Driver == null)
+				throw new InvalidOperationException ("The Application has not been initialized");
+			return Application.Driver.GetAttribute ();
+		}
 	}
 
 	/// <summary>
@@ -201,18 +221,18 @@ namespace Terminal.Gui {
 			case "TopLevel":
 				switch (callerMemberName) {
 				case "Normal":
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
 					break;
 				case "Focus":
-					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.Foreground, attribute.Background);
 					break;
 				case "HotNormal":
-					HotFocus = Application.Driver.MakeAttribute (attribute.foreground, HotFocus.background);
+					HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus.Background);
 					break;
 				case "HotFocus":
-					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
-					if (Focus.foreground != attribute.background)
-						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal.Background);
+					if (Focus.Foreground != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (Focus.Foreground, attribute.Background);
 					break;
 				}
 				break;
@@ -220,19 +240,19 @@ namespace Terminal.Gui {
 			case "Base":
 				switch (callerMemberName) {
 				case "Normal":
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
 					break;
 				case "Focus":
-					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.Foreground, attribute.Background);
 					break;
 				case "HotNormal":
-					HotFocus = Application.Driver.MakeAttribute (attribute.foreground, HotFocus.background);
-					Normal = Application.Driver.MakeAttribute (Normal.foreground, attribute.background);
+					HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus.Background);
+					Normal = Application.Driver.MakeAttribute (Normal.Foreground, attribute.Background);
 					break;
 				case "HotFocus":
-					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
-					if (Focus.foreground != attribute.background)
-						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal.Background);
+					if (Focus.Foreground != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (Focus.Foreground, attribute.Background);
 					break;
 				}
 				break;
@@ -240,57 +260,56 @@ namespace Terminal.Gui {
 			case "Menu":
 				switch (callerMemberName) {
 				case "Normal":
-					if (Focus.background != attribute.background)
-						Focus = Application.Driver.MakeAttribute (attribute.foreground, Focus.background);
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
-					Disabled = Application.Driver.MakeAttribute (Disabled.foreground, attribute.background);
+					if (Focus.Background != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus.Background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
+					Disabled = Application.Driver.MakeAttribute (Disabled.Foreground, attribute.Background);
 					break;
 				case "Focus":
-					Normal = Application.Driver.MakeAttribute (attribute.foreground, Normal.background);
-					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal.Background);
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.Foreground, attribute.Background);
 					break;
 				case "HotNormal":
-					if (Focus.background != attribute.background)
-						HotFocus = Application.Driver.MakeAttribute (attribute.foreground, HotFocus.background);
-					Normal = Application.Driver.MakeAttribute (Normal.foreground, attribute.background);
-					Disabled = Application.Driver.MakeAttribute (Disabled.foreground, attribute.background);
+					if (Focus.Background != attribute.Background)
+						HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus.Background);
+					Normal = Application.Driver.MakeAttribute (Normal.Foreground, attribute.Background);
+					Disabled = Application.Driver.MakeAttribute (Disabled.Foreground, attribute.Background);
 					break;
 				case "HotFocus":
-					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
-					if (Focus.foreground != attribute.background)
-						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal.Background);
+					if (Focus.Foreground != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (Focus.Foreground, attribute.Background);
 					break;
 				case "Disabled":
-					if (Focus.background != attribute.background)
-						HotFocus = Application.Driver.MakeAttribute (attribute.foreground, HotFocus.background);
-					Normal = Application.Driver.MakeAttribute (Normal.foreground, attribute.background);
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					if (Focus.Background != attribute.Background)
+						HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus.Background);
+					Normal = Application.Driver.MakeAttribute (Normal.Foreground, attribute.Background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
 					break;
-
 				}
 				break;
 
 			case "Dialog":
 				switch (callerMemberName) {
 				case "Normal":
-					if (Focus.background != attribute.background)
-						Focus = Application.Driver.MakeAttribute (attribute.foreground, Focus.background);
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					if (Focus.Background != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (attribute.Foreground, Focus.Background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
 					break;
 				case "Focus":
-					Normal = Application.Driver.MakeAttribute (attribute.foreground, Normal.background);
-					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					Normal = Application.Driver.MakeAttribute (attribute.Foreground, Normal.Background);
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.Foreground, attribute.Background);
 					break;
 				case "HotNormal":
-					if (Focus.background != attribute.background)
-						HotFocus = Application.Driver.MakeAttribute (attribute.foreground, HotFocus.background);
-					if (Normal.foreground != attribute.background)
-						Normal = Application.Driver.MakeAttribute (Normal.foreground, attribute.background);
+					if (Focus.Background != attribute.Background)
+						HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, HotFocus.Background);
+					if (Normal.Foreground != attribute.Background)
+						Normal = Application.Driver.MakeAttribute (Normal.Foreground, attribute.Background);
 					break;
 				case "HotFocus":
-					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
-					if (Focus.foreground != attribute.background)
-						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (attribute.Foreground, HotNormal.Background);
+					if (Focus.Foreground != attribute.Background)
+						Focus = Application.Driver.MakeAttribute (Focus.Foreground, attribute.Background);
 					break;
 				}
 				break;
@@ -298,17 +317,16 @@ namespace Terminal.Gui {
 			case "Error":
 				switch (callerMemberName) {
 				case "Normal":
-					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
-					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.Foreground, attribute.Background);
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.Foreground, attribute.Background);
 					break;
 				case "HotNormal":
 				case "HotFocus":
-					HotFocus = Application.Driver.MakeAttribute (attribute.foreground, attribute.background);
-					Normal = Application.Driver.MakeAttribute (Normal.foreground, attribute.background);
+					HotFocus = Application.Driver.MakeAttribute (attribute.Foreground, attribute.Background);
+					Normal = Application.Driver.MakeAttribute (Normal.Foreground, attribute.Background);
 					break;
 				}
 				break;
-
 			}
 			preparingScheme = false;
 			return attribute;
@@ -383,7 +401,7 @@ namespace Terminal.Gui {
 	public static class Colors {
 		static Colors ()
 		{
-			// Use reflection to dynamically create the default set of ColorSchemes from the list defiined 
+			// Use reflection to dynamically create the default set of ColorSchemes from the list defined 
 			// by the class. 
 			ColorSchemes = typeof (Colors).GetProperties ()
 				.Where (p => p.PropertyType == typeof (ColorScheme))
@@ -546,6 +564,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public abstract int Top { get; }
 
+		/// <summary>
+		/// If false height is measured by the window height and thus no scrolling.
+		/// If true then height is measured by the buffer height, enabling scrolling.
+		/// </summary>
+		public abstract bool HeightAsBuffer { get; set; }
+
 		/// <summary>
 		/// Initializes the driver
 		/// </summary>
@@ -570,12 +594,10 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public static Rune MakePrintable (Rune c)
 		{
-			if (c <= 0x1F) {
-				// ASCII (C0) control characters. 
-				return new Rune (c + 0x2400);
-			} else if (c >= 0x80 && c <= 0x9F) {
+			if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
+				// ASCII (C0) control characters.
 				// C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1)
-				return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □: 
+				return new Rune (c + 0x2400);
 			} else {
 				return c;
 			}
@@ -657,7 +679,7 @@ namespace Terminal.Gui {
 		/// <param name="paddingTop">Number of rows to pad on the top (if 0 the border and title will not appear on the top).</param>
 		/// <param name="paddingRight">Number of columns to pad on the right (if 0 the border will not appear on the right).</param>
 		/// <param name="paddingBottom">Number of rows to pad on the bottom (if 0 the border will not appear on the bottom).</param>
-		/// <param name="textAlignment">Not yet immplemented.</param>
+		/// <param name="textAlignment">Not yet implemented.</param>
 		/// <remarks></remarks>
 		public virtual void DrawWindowTitle (Rect region, ustring title, int paddingLeft, int paddingTop, int paddingRight, int paddingBottom, TextAlignment textAlignment = TextAlignment.Left)
 		{
@@ -672,7 +694,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Enables diagnostic funcions
+		/// Enables diagnostic functions
 		/// </summary>
 		[Flags]
 		public enum DiagnosticFlags : uint {
@@ -863,7 +885,7 @@ namespace Terminal.Gui {
 		/// <param name="region">Screen relative region where the frame will be drawn.</param>
 		/// <param name="padding">Padding to add on the sides.</param>
 		/// <param name="fill">If set to <c>true</c> it will clear the contents with the current color, otherwise the contents will be left untouched.</param>
-		/// <remarks>This API has been superceded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
+		/// <remarks>This API has been superseded by <see cref="DrawWindowFrame(Rect, int, int, int, int, bool, bool)"/>.</remarks>
 		/// <remarks>This API is equivalent to calling <c>DrawWindowFrame(Rect, p - 1, p - 1, p - 1, p - 1)</c>. In other words,
 		/// A padding value of 0 means there is actually a one cell border.
 		/// </remarks>
@@ -1049,5 +1071,11 @@ namespace Terminal.Gui {
 		/// <param name="back">Background.</param>
 		/// <returns></returns>
 		public abstract Attribute MakeAttribute (Color fore, Color back);
+
+		/// <summary>
+		/// Gets the current <see cref="Attribute"/>.
+		/// </summary>
+		/// <returns>The current attribute.</returns>
+		public abstract Attribute GetAttribute ();
 	}
 }

+ 6 - 2
Terminal.Gui/Core/Toplevel.cs

@@ -158,6 +158,8 @@ namespace Terminal.Gui {
 
 			switch (keyEvent.Key) {
 			case Key.AltMask:
+			case Key.AltMask | Key.Space:
+			case Key.CtrlMask | Key.Space:
 				if (MenuBar != null && MenuBar.OnKeyDown (keyEvent)) {
 					return true;
 				}
@@ -176,6 +178,8 @@ namespace Terminal.Gui {
 
 			switch (keyEvent.Key) {
 			case Key.AltMask:
+			case Key.AltMask | Key.Space:
+			case Key.CtrlMask | Key.Space:
 				if (MenuBar != null && MenuBar.OnKeyUp (keyEvent)) {
 					return true;
 				}
@@ -401,10 +405,10 @@ namespace Terminal.Gui {
 		{
 			EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
 			if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
-				if (top.X is Pos.PosAbsolute && top.Bounds.X != nx) {
+				if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
 					top.X = nx;
 				}
-				if (top.Y is Pos.PosAbsolute && top.Bounds.Y != ny) {
+				if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Bounds.Y != ny) {
 					top.Y = ny;
 				}
 			}

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

@@ -721,7 +721,7 @@ namespace Terminal.Gui {
 				NeedDisplay = new Rect (x, y, w, h);
 			}
 			if (container != null)
-				container.ChildNeedsDisplay ();
+				container.SetChildNeedsDisplay ();
 			if (subviews == null)
 				return;
 			foreach (var view in subviews)
@@ -733,16 +733,16 @@ namespace Terminal.Gui {
 				}
 		}
 
-		internal bool childNeedsDisplay;
+		internal bool ChildNeedsDisplay { get; private set; }
 
 		/// <summary>
 		/// Indicates that any child views (in the <see cref="Subviews"/> list) need to be repainted.
 		/// </summary>
-		public void ChildNeedsDisplay ()
+		public void SetChildNeedsDisplay ()
 		{
-			childNeedsDisplay = true;
+			ChildNeedsDisplay = true;
 			if (container != null)
-				container.ChildNeedsDisplay ();
+				container.SetChildNeedsDisplay ();
 		}
 
 		internal bool addingView = false;
@@ -1288,7 +1288,7 @@ namespace Terminal.Gui {
 		protected void ClearNeedsDisplay ()
 		{
 			NeedDisplay = Rect.Empty;
-			childNeedsDisplay = false;
+			ChildNeedsDisplay = false;
 		}
 
 		/// <summary>
@@ -1314,10 +1314,13 @@ namespace Terminal.Gui {
 				return;
 			}
 
+			Application.CurrentView = this;
+
 			var clipRect = new Rect (Point.Empty, frame.Size);
 
-			if (ColorScheme != null)
+			if (ColorScheme != null) {
 				Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
+			}
 
 			if (!ustring.IsNullOrEmpty (Text)) {
 				Clear ();
@@ -1333,23 +1336,24 @@ namespace Terminal.Gui {
 
 			if (subviews != null) {
 				foreach (var view in subviews) {
-					if (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay) {
+					if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
 						if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
 							if (view.LayoutNeeded)
 								view.LayoutSubviews ();
 							Application.CurrentView = view;
 
 							// Draw the subview
-							// Use the view's bounds (view-relative; Location will always be (0,0) because
+							// Use the view's bounds (view-relative; Location will always be (0,0)
 							if (view.Visible) {
 								view.Redraw (view.Bounds);
 							}
 						}
 						view.NeedDisplay = Rect.Empty;
-						view.childNeedsDisplay = false;
+						view.ChildNeedsDisplay = false;
 					}
 				}
 			}
+			ClearLayoutNeeded ();
 			ClearNeedsDisplay ();
 		}
 

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

@@ -193,6 +193,16 @@ namespace Terminal.Gui {
 				Driver.SetAttribute (ColorScheme.HotNormal);
 			Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
 			Driver.SetAttribute (ColorScheme.Normal);
+
+			// Checks if there are any SuperView view which intersect with this window.
+			if (SuperView != null) {
+				foreach (var view in SuperView.Subviews) {
+					if (view != this && view.Frame.IntersectsWith (Bounds)) {
+						view.SetNeedsLayout ();
+						view.SetNeedsDisplay (view.Bounds);
+					}
+				}
+			}
 		}
 
 		//

+ 1 - 1
Terminal.Gui/Terminal.Gui.csproj

@@ -167,7 +167,7 @@
   </PropertyGroup>-->
   <ItemGroup>
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" PrivateAssets="true" />
-    <PackageReference Include="NStack.Core" Version="0.15.0" />
+    <PackageReference Include="NStack.Core" Version="0.16.0" />
   </ItemGroup>
   <!--<ItemGroup>
     <Reference Include="NStack">

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

@@ -239,6 +239,32 @@ namespace Terminal.Gui {
 			Children = children;
 		}
 
+		/// <summary>
+		/// Initializes a new <see cref="MenuBarItem"/> with separate list of items.
+		/// </summary>
+		/// <param name="title">Title for the menu item.</param>
+		/// <param name="children">The list of items in the current menu.</param>
+		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
+		public MenuBarItem (ustring title, List<MenuItem []> children, MenuItem parent = null)
+		{
+			if (children == null) {
+				throw new ArgumentNullException (nameof (children), "The parameter cannot be null. Use an empty array instead.");
+			}
+			SetTitle (title ?? "");
+			if (parent != null) {
+				Parent = parent;
+			}
+			MenuItem [] childrens = new MenuItem [] { };
+			foreach (var item in children) {
+				for (int i = 0; i < item.Length; i++) {
+					SetChildrensParent (item);
+					Array.Resize (ref childrens, childrens.Length + 1);
+					childrens [childrens.Length - 1] = item [i];
+				}
+			}
+			Children = childrens;
+		}
+
 		/// <summary>
 		/// Initializes a new <see cref="MenuBarItem"/>.
 		/// </summary>
@@ -808,7 +834,7 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
-			if (keyEvent.IsAlt) {
+			if (keyEvent.IsAlt || (keyEvent.IsCtrl && keyEvent.Key == (Key.CtrlMask | Key.Space))) {
 				openedByAltKey = true;
 				SetNeedsDisplay ();
 				openedByHotKey = false;
@@ -819,9 +845,10 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		public override bool OnKeyUp (KeyEvent keyEvent)
 		{
-			if (keyEvent.IsAlt) {
+			if (keyEvent.IsAlt || (keyEvent.IsCtrl && keyEvent.Key == (Key.CtrlMask | Key.Space))) {
 				// User pressed Alt - this may be a precursor to a menu accelerator (e.g. Alt-F)
-				if (!keyEvent.IsCtrl && openedByAltKey && !IsMenuOpen && openMenu == null && ((uint)keyEvent.Key & (uint)Key.CharMask) == 0) {
+				if (openedByAltKey && !IsMenuOpen && openMenu == null && (((uint)keyEvent.Key & (uint)Key.CharMask) == 0
+					|| ((uint)keyEvent.Key & (uint)Key.CharMask) == (uint)Key.Space)) {
 					// 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.
@@ -1415,7 +1442,7 @@ namespace Terminal.Gui {
 				CloseMenu ();
 				if (openedByAltKey) {
 					openedByAltKey = false;
-					LastFocused.SetFocus ();
+					LastFocused?.SetFocus ();
 				}
 				break;
 

+ 2 - 2
Terminal.Gui/Views/TextField.cs

@@ -211,9 +211,9 @@ namespace Terminal.Gui {
 
 			int p = first;
 			int col = 0;
-			int width = Frame.Width;
+			int width = Frame.Width + OffSetBackground ();
 			var tcount = text.Count;
-			var roc = new Attribute (Color.DarkGray, Color.Gray);
+			var roc = Colors.Menu.Disabled;
 			for (int idx = p; idx < tcount; idx++) {
 				var rune = text [idx];
 				var cols = Rune.ColumnWidth (rune);

+ 23 - 4
Terminal.Gui/Views/TextView.cs

@@ -419,7 +419,13 @@ namespace Terminal.Gui {
 
 				SetNeedsDisplay (new Rect (0, minRow, Frame.Width, maxRow));
 			}
-			Move (CurrentColumn - leftColumn, CurrentRow - topRow);
+			var line = model.GetLine (currentRow);
+			var retreat = 0;
+			if (line.Count > 0) {
+				retreat = Math.Max ((SpecialRune (line [Math.Max (CurrentColumn - leftColumn - 1, 0)])
+				? 1 : 0), 0);
+			}
+			Move (CurrentColumn - leftColumn - retreat, CurrentRow - topRow);
 		}
 
 		void ClearRegion (int left, int top, int right, int bottom)
@@ -566,17 +572,30 @@ namespace Terminal.Gui {
 				for (int col = bounds.Left; col < right; col++) {
 					var lineCol = leftColumn + col;
 					var rune = lineCol >= lineRuneCount ? ' ' : line [lineCol];
-					if (selecting && PointInSelection (col, row))
+					if (selecting && PointInSelection (col, row)) {
 						ColorSelection ();
-					else
+					} else {
 						ColorNormal ();
+					}
 
-					AddRune (col, row, rune);
+					if (!SpecialRune (rune)) {
+						AddRune (col, row, rune);
+					}
 				}
 			}
 			PositionCursor ();
 		}
 
+		bool SpecialRune (Rune rune)
+		{
+			switch (rune) {
+			case (uint)Key.Enter:
+			case 0xd:
+				return true;
+			default:
+				return false;			}
+		}
+
 		///<inheritdoc/>
 		public override bool CanFocus {
 			get => base.CanFocus;

+ 27 - 14
UICatalog/Scenarios/CharacterMap.cs

@@ -1,4 +1,7 @@
-using NStack;
+#define DRAW_CONTENT
+//#define BASE_DRAW_CONTENT
+
+using NStack;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -58,15 +61,20 @@ namespace UICatalog {
 				CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal),
 			};
 
-			var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ());
-			jumpList.X = Pos.X (label);
-			jumpList.Y = Pos.Bottom (label);
-			jumpList.Width = Dim.Fill ();
+			var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()) {
+				X = Pos.X (label),
+				Y = Pos.Bottom (label),
+				Width = Dim.Fill (),
+				SelectedItem = 8
+			};
 			jumpList.SelectedItemChanged += (args) => {
-				_charMap.Start = radioItems[args.SelectedItem].start;
+				_charMap.Start = radioItems [args.SelectedItem].start;
 			};
 
 			Win.Add (jumpList);
+
+			jumpList.Refresh ();
+			jumpList.SetFocus ();
 		}
 
 		public override void Run ()
@@ -91,11 +99,14 @@ namespace UICatalog {
 		}
 		int _start = 0x2500;
 
+		public const int H_SPACE = 2;
+		public const int V_SPACE = 2;
+
 		public static int MaxCodePointVal => 0xE0FFF;
 
 		// Row Header + space + (space + char + space)
 		public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
-		public static int RowWidth => RowHeaderWidth + (" c".Length * 16);
+		public static int RowWidth => RowHeaderWidth + (H_SPACE * 16);
 
 		public CharMap ()
 		{
@@ -109,10 +120,12 @@ namespace UICatalog {
 					ShowHorizontalScrollIndicator = false;
 				}
 			};
+#if DRAW_CONTENT
 
 			DrawContent += CharMap_DrawContent;
+#endif
 		}
-#if true
+
 		private void CharMap_DrawContent (Rect viewport)
 		{
 			//Rune ReplaceNonPrintables (Rune c)
@@ -125,10 +138,10 @@ namespace UICatalog {
 			//}
 
 			for (int header = 0; header < 16; header++) {
-				Move (viewport.X + RowHeaderWidth + (header * 2), 0);
+				Move (viewport.X + RowHeaderWidth + (header * H_SPACE), 0);
 				Driver.AddStr ($" {header:x} ");
 			}
-			for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) {
+			for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y+= V_SPACE) {
 				int val = (-viewport.Y + row) * 16;
 				if (val < MaxCodePointVal) {
 					var rowLabel = $"U+{val / 16:x4}x";
@@ -137,17 +150,17 @@ namespace UICatalog {
 					var prevColWasWide = false;
 					for (int col = 0; col < 16; col++) {
 						var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col));
-						Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1);
+						Move (viewport.X + RowHeaderWidth + (col * H_SPACE) + (prevColWasWide ? 0 : 1), y + 1);
 						Driver.AddRune (rune);
-						//prevColWasWide = Rune.ColumnWidth(rune) > 1;
+						//prevColWasWide = Rune.ColumnWidth (rune) > 1;
 					}
 				}
 			}
 		}
-#else
+#if BASE_DRAW_CONTENT
 		public override void OnDrawContent (Rect viewport)
 		{
-			CharMap_DrawContent(this, viewport);
+			CharMap_DrawContent (viewport);
 			base.OnDrawContent (viewport);
 		}
 #endif

+ 34 - 3
UICatalog/UICatalog.cs

@@ -253,7 +253,7 @@ namespace UICatalog {
 					_leftPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
 					_rightPane.Height = Dim.Fill(_statusBar.Visible ? 1 : 0);
 					_top.LayoutSubviews();
-					_top.ChildNeedsDisplay();
+					_top.SetChildNeedsDisplay();
 				}),
 			};
 
@@ -274,7 +274,33 @@ namespace UICatalog {
 			return _runningScenario;
 		}
 
-		static MenuItem [] CreateDiagnosticMenuItems ()
+		static List<MenuItem []> CreateDiagnosticMenuItems ()
+		{
+			List<MenuItem []> menuItems = new List<MenuItem []> ();
+			menuItems.Add (CreateDiagnosticFlagsMenuItems ());
+			menuItems.Add (new MenuItem [] { null });
+			menuItems.Add (CreateSizeStyle ());
+			return menuItems;
+		}
+
+		static MenuItem [] CreateSizeStyle ()
+		{
+			List<MenuItem> menuItems = new List<MenuItem> ();
+			var item = new MenuItem ();
+			item.Title = "_Height As Buffer";
+			item.Shortcut = Key.CtrlMask | Key.AltMask | (Key)item.Title.ToString ().Substring (1, 1) [0];
+			item.CheckType |= MenuItemCheckStyle.Checked;
+			item.Checked = Application.HeightAsBuffer;
+			item.Action += () => {
+				item.Checked = !item.Checked;
+				Application.HeightAsBuffer = item.Checked;
+			};
+			menuItems.Add (item);
+
+			return menuItems.ToArray ();
+		}
+
+		static MenuItem [] CreateDiagnosticFlagsMenuItems ()
 		{
 			const string OFF = "Diagnostics: _Off";
 			const string FRAME_RULER = "Diagnostics: Frame _Ruler";
@@ -288,7 +314,12 @@ namespace UICatalog {
 				item.Shortcut = Key.AltMask + index.ToString () [0];
 				index++;
 				item.CheckType |= MenuItemCheckStyle.Checked;
-				item.Checked = _diagnosticFlags.HasFlag (diag);
+				if (GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off) == item.Title) {
+					item.Checked = (_diagnosticFlags & (ConsoleDriver.DiagnosticFlags.FramePadding
+					| ConsoleDriver.DiagnosticFlags.FrameRuler)) == 0;
+				} else {
+					item.Checked = _diagnosticFlags.HasFlag (diag);
+				}
 				item.Action += () => {
 					var t = GetDiagnosticsTitle (ConsoleDriver.DiagnosticFlags.Off);
 					if (item.Title == t && !item.Checked) {

+ 1 - 1
UnitTests/UnitTests.csproj

@@ -14,7 +14,7 @@
   </PropertyGroup>
 
   <ItemGroup>
-    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.7.1" />
+    <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" />
     <PackageReference Include="System.Collections" Version="4.3.0" />
     <PackageReference Include="xunit" Version="2.4.1" />
     <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">

Some files were not shown because too many files changed in this diff