Browse Source

MenuItems on MenuBar (#388)

This PR enhances MenuBar with the ability to have top-level (on the MenuBar itself), clickable, actionable, MenuItems.

Along the way it fixes a few MenuBar bugs and...

alt activates/closes the menu just like on Windows (alt and f9 work the same).
Cursor shows consistently across the MenuBar as the focus changes
Top-level menu items show accelerator keys.
demo.cs has been enhanced to include an example top-level menuitems. I also tested top-level menuitem as the first menu item as well as in the middle. I didn't want to over complicate demo.cs with tons of examples, FWIW.
Charlie Kindel 5 years ago
parent
commit
c7ce619092

+ 45 - 14
Example/demo.cs

@@ -93,10 +93,10 @@ static class Demo {
 		int i = 0;
 		int i = 0;
 		string txt = "Hello world, how are you doing today";
 		string txt = "Hello world, how are you doing today";
 		container.Add (
 		container.Add (
-				new Label(new Rect(0, 1, 40, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
-				new Label(new Rect(0, 3, 40, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
-				new Label(new Rect(0, 5, 40, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
-				new Label(new Rect(0, 7, 40, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
+				new Label (new Rect (0, 1, 40, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
+				new Label (new Rect (0, 3, 40, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
+				new Label (new Rect (0, 5, 40, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
+				new Label (new Rect (0, 7, 40, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
 			);
 			);
 
 
 		Application.Run (container);
 		Application.Run (container);
@@ -408,6 +408,34 @@ static class Demo {
 	#endregion
 	#endregion
 
 
 
 
+	#region OnKeyDown / OnKeyUp Demo
+	private static void OnKeyDownUpDemo ()
+	{
+		var container = new Dialog (
+			"OnKeyDown & OnKeyUp demo", 50, 20,
+			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+
+		var kl = new Label (new Rect (3, 3, 40, 1), "Keyboard: ");
+		container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, kl, "Down");
+		container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, kl, "Up");
+		container.Add (kl);
+		Application.Run (container);
+	}
+
+	private static void KeyUpDown (KeyEvent keyEvent, Label kl, string updown)
+	{
+		kl.TextColor = Colors.TopLevel.Normal;
+		if ((keyEvent.Key & Key.CtrlMask) != 0) {
+			kl.Text = $"Keyboard: Ctrl Key{updown}";
+		} else if ((keyEvent.Key & Key.AltMask) != 0) {
+			kl.Text = $"Keyboard: Alt Key{updown}";
+		} else {
+			kl.Text = $"Keyboard: {(char)keyEvent.KeyValue} Key{updown}";
+		}
+	}
+#endregion
+
 	public static Label ml;
 	public static Label ml;
 	public static MenuBar menu;
 	public static MenuBar menu;
 	public static CheckBox menuKeysStyle;
 	public static CheckBox menuKeysStyle;
@@ -430,7 +458,7 @@ static class Demo {
 			X = 1,
 			X = 1,
 			Y = 1,
 			Y = 1,
 			Width = Dim.Fill (),
 			Width = Dim.Fill (),
-			Height = Dim.Fill ()-1
+			Height = Dim.Fill () - 1
 		};
 		};
 #else
 #else
 		var tframe = top.Frame;
 		var tframe = top.Frame;
@@ -451,7 +479,7 @@ static class Demo {
 
 
 		menu = new MenuBar (new MenuBarItem [] {
 		menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
-				new MenuItem ("Text Editor Demo", "", () => { Editor (top); }),
+				new MenuItem ("Text _Editor Demo", "", () => { Editor (top); }),
 				new MenuItem ("_New", "Creates new file", NewFile),
 				new MenuItem ("_New", "Creates new file", NewFile),
 				new MenuItem ("_Open", "", Open),
 				new MenuItem ("_Open", "", Open),
 				new MenuItem ("_Hex", "", () => ShowHex (top)),
 				new MenuItem ("_Hex", "", () => ShowHex (top)),
@@ -469,18 +497,19 @@ static class Demo {
 				menuItems[3]
 				menuItems[3]
 			}),
 			}),
 			new MenuBarItem ("_List Demos", new MenuItem [] {
 			new MenuBarItem ("_List Demos", new MenuItem [] {
-				new MenuItem ("Select Multiple Items", "", () => ListSelectionDemo (true)),
-				new MenuItem ("Select Single Item", "", () => ListSelectionDemo (false)),
+				new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)),
+				new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)),
 			}),
 			}),
-			new MenuBarItem ("Assorted", new MenuItem [] {
-				new MenuItem ("Show text alignments", "", () => ShowTextAlignments ())
+			new MenuBarItem ("A_ssorted", new MenuItem [] {
+				new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()),
+				new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ())
 			}),
 			}),
-			new MenuBarItem ("Test Menu and SubMenus", new MenuItem [] {
-				new MenuItem ("SubMenu1Item1",
+			new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
+				new MenuItem ("SubMenu1Item_1",
 					new MenuBarItem (new MenuItem[] {
 					new MenuBarItem (new MenuItem[] {
-						new MenuItem ("SubMenu2Item1",
+						new MenuItem ("SubMenu2Item_1",
 							new MenuBarItem (new MenuItem [] {
 							new MenuBarItem (new MenuItem [] {
-								new MenuItem ("SubMenu3Item1",
+								new MenuItem ("SubMenu3Item_1",
 									new MenuBarItem (new MenuItem [] { menuItems [2] })
 									new MenuBarItem (new MenuItem [] { menuItems [2] })
 								)
 								)
 							})
 							})
@@ -488,6 +517,7 @@ static class Demo {
 					})
 					})
 				)
 				)
 			}),
 			}),
+			new MenuBarItem ("_About...", "Demonstrates top-level menu item", () =>  MessageBox.ErrorQuery (50, 7, "About Demo", "This is a demo app for gui.cs", "Ok")),
 		});
 		});
 
 
 		menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
 		menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
@@ -535,6 +565,7 @@ static class Demo {
 		};
 		};
 #endif
 #endif
 
 
+
 		top.Add (win);
 		top.Add (win);
 		//top.Add (menu);
 		//top.Add (menu);
 		top.Add (menu, statusBar, ml);
 		top.Add (menu, statusBar, ml);

+ 81 - 1
Terminal.Gui/Core.cs

@@ -121,6 +121,27 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		/// <summary>
+		/// Method invoked when a key is pressed.
+		/// </summary>
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <returns>true if the event was handled</returns>
+		public virtual bool KeyDown (KeyEvent keyEvent)
+		{
+			return false;
+		}
+
+		/// <summary>
+		/// Method invoked when a key is released.
+		/// </summary>
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		/// <returns>true if the event was handled</returns>
+		public virtual bool KeyUp (KeyEvent keyEvent)
+		{
+			return false;
+		}
+
+
 		/// <summary>
 		/// <summary>
 		/// Method invoked when a mouse event is generated
 		/// Method invoked when a mouse event is generated
 		/// </summary>
 		/// </summary>
@@ -1011,6 +1032,41 @@ namespace Terminal.Gui {
 			return false;
 			return false;
 		}
 		}
 
 
+		/// <summary>
+		/// Invoked when a key is pressed
+		/// </summary>
+		public Action<KeyEvent> OnKeyDown;
+
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		public override bool KeyDown (KeyEvent keyEvent)
+		{
+			OnKeyDown?.Invoke (keyEvent);
+			if (subviews == null || subviews.Count == 0)
+				return false;
+			foreach (var view in subviews)
+				if (view.KeyDown (keyEvent))
+					return true;
+
+			return false;
+		}
+
+		/// <summary>
+		/// Invoked when a key is released
+		/// </summary>
+		public Action<KeyEvent> OnKeyUp;
+
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		public override bool KeyUp (KeyEvent keyEvent)
+		{
+			OnKeyUp?.Invoke (keyEvent);
+			if (subviews == null || subviews.Count == 0)
+				return false;
+			foreach (var view in subviews)
+				if (view.KeyUp (keyEvent))
+					return true;
+
+			return false;
+		}
 		/// <summary>
 		/// <summary>
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
 		/// </summary>
 		/// </summary>
@@ -1943,6 +1999,7 @@ namespace Terminal.Gui {
 
 
 		static void ProcessKeyEvent (KeyEvent ke)
 		static void ProcessKeyEvent (KeyEvent ke)
 		{
 		{
+		
 			var chain = toplevels.ToList();
 			var chain = toplevels.ToList();
 			foreach (var topLevel in chain) {
 			foreach (var topLevel in chain) {
 				if (topLevel.ProcessHotKey (ke))
 				if (topLevel.ProcessHotKey (ke))
@@ -1967,6 +2024,29 @@ namespace Terminal.Gui {
 			}
 			}
 		}
 		}
 
 
+		static void ProcessKeyDownEvent (KeyEvent ke)
+		{
+			var chain = toplevels.ToList ();
+			foreach (var topLevel in chain) {
+				if (topLevel.KeyDown (ke))
+					return;
+				if (topLevel.Modal)
+					break;
+			}
+		}
+
+		
+		static void ProcessKeyUpEvent (KeyEvent ke)
+		{
+			var chain = toplevels.ToList ();
+			foreach (var topLevel in chain) {
+				if (topLevel.KeyUp (ke))
+					return;
+				if (topLevel.Modal)
+					break;
+			}
+		}
+
 		static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
 		static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
 		{
 		{
 			var startFrame = start.Frame;
 			var startFrame = start.Frame;
@@ -2092,7 +2172,7 @@ namespace Terminal.Gui {
 			}
 			}
 			toplevels.Push (toplevel);
 			toplevels.Push (toplevel);
 			Current = toplevel;
 			Current = toplevel;
-			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessMouseEvent);
+			Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
 			if (toplevel.LayoutStyle == LayoutStyle.Computed)
 			if (toplevel.LayoutStyle == LayoutStyle.Computed)
 				toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 				toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			toplevel.LayoutSubviews ();
 			toplevel.LayoutSubviews ();

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

@@ -469,7 +469,7 @@ namespace Terminal.Gui {
 		/// <param name="mainLoop"></param>
 		/// <param name="mainLoop"></param>
 		/// <param name="keyHandler"></param>
 		/// <param name="keyHandler"></param>
 		/// <param name="mouseHandler"></param>
 		/// <param name="mouseHandler"></param>
-		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler);
+		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler);
 
 
 		/// <summary>
 		/// <summary>
 		/// Updates the screen to reflect all the changes that have been done to the display buffer
 		/// Updates the screen to reflect all the changes that have been done to the display buffer

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

@@ -203,8 +203,9 @@ namespace Terminal.Gui {
 				keyHandler (new KeyEvent ((Key)wch));
 				keyHandler (new KeyEvent ((Key)wch));
 		}
 		}
 
 
-		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
+		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 (-1);
 			Curses.timeout (-1);
 
 
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {

+ 2 - 1
Terminal.Gui/Drivers/NetDriver.cs

@@ -320,8 +320,9 @@ namespace Terminal.Gui {
 			return (Key)(0xffffffff);
 			return (Key)(0xffffffff);
 		}
 		}
 
 
-		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
+		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
+			// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
 			(mainLoop.Driver as NetMainLoop).WindowsKeyPressed = delegate (ConsoleKeyInfo consoleKey) {
 			(mainLoop.Driver as NetMainLoop).WindowsKeyPressed = delegate (ConsoleKeyInfo consoleKey) {
 				var map = MapKey (consoleKey);
 				var map = MapKey (consoleKey);
 				if (map == (Key)0xffffffff)
 				if (map == (Key)0xffffffff)

+ 74 - 43
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -26,6 +26,7 @@
 // SOFTWARE.
 // SOFTWARE.
 //
 //
 using System;
 using System;
+using System.CodeDom;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading;
@@ -102,7 +103,7 @@ namespace Terminal.Gui {
 			}
 			}
 
 
 			if (ScreenBuffer != IntPtr.Zero)
 			if (ScreenBuffer != IntPtr.Zero)
-				CloseHandle(ScreenBuffer);
+				CloseHandle (ScreenBuffer);
 
 
 			ScreenBuffer = IntPtr.Zero;
 			ScreenBuffer = IntPtr.Zero;
 		}
 		}
@@ -358,8 +359,8 @@ namespace Terminal.Gui {
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		[DllImport ("kernel32.dll", SetLastError = true)]
 		static extern IntPtr GetStdHandle (int nStdHandle);
 		static extern IntPtr GetStdHandle (int nStdHandle);
 
 
-		[DllImport("kernel32.dll", SetLastError = true)]
-		static extern bool CloseHandle(IntPtr handle);
+		[DllImport ("kernel32.dll", SetLastError = true)]
+		static extern bool CloseHandle (IntPtr handle);
 
 
 		[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
 		[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
 		public static extern bool ReadConsoleInput (
 		public static extern bool ReadConsoleInput (
@@ -424,8 +425,8 @@ namespace Terminal.Gui {
 
 
 	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
 	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
 		static bool sync;
 		static bool sync;
-		ManualResetEventSlim eventReady = new ManualResetEventSlim(false);
-		ManualResetEventSlim waitForProbe = new ManualResetEventSlim(false);
+		ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
+		ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
 		MainLoop mainLoop;
 		MainLoop mainLoop;
 		WindowsConsole.CharInfo [] OutputBuffer;
 		WindowsConsole.CharInfo [] OutputBuffer;
 		int cols, rows;
 		int cols, rows;
@@ -456,17 +457,17 @@ namespace Terminal.Gui {
 			Task.Run ((Action)WindowsInputHandler);
 			Task.Run ((Action)WindowsInputHandler);
 		}
 		}
 
 
-		[StructLayout(LayoutKind.Sequential)]
+		[StructLayout (LayoutKind.Sequential)]
 		public struct ConsoleKeyInfoEx {
 		public struct ConsoleKeyInfoEx {
 			public ConsoleKeyInfo consoleKeyInfo;
 			public ConsoleKeyInfo consoleKeyInfo;
 			public bool CapsLock;
 			public bool CapsLock;
 			public bool NumLock;
 			public bool NumLock;
 
 
-			public ConsoleKeyInfoEx(ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
+			public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
 			{
 			{
-			this.consoleKeyInfo = consoleKeyInfo;
-			CapsLock = capslock;
-			NumLock = numlock;
+				this.consoleKeyInfo = consoleKeyInfo;
+				CapsLock = capslock;
+				NumLock = numlock;
 			}
 			}
 		}
 		}
 
 
@@ -476,8 +477,8 @@ namespace Terminal.Gui {
 		void WindowsInputHandler ()
 		void WindowsInputHandler ()
 		{
 		{
 			while (true) {
 			while (true) {
-				waitForProbe.Wait();
-				waitForProbe.Reset();
+				waitForProbe.Wait ();
+				waitForProbe.Reset ();
 
 
 				uint numberEventsRead = 0;
 				uint numberEventsRead = 0;
 
 
@@ -487,7 +488,7 @@ namespace Terminal.Gui {
 				else
 				else
 					result = records;
 					result = records;
 
 
-				eventReady.Set();
+				eventReady.Set ();
 			}
 			}
 		}
 		}
 
 
@@ -498,7 +499,7 @@ namespace Terminal.Gui {
 
 
 		void IMainLoopDriver.Wakeup ()
 		void IMainLoopDriver.Wakeup ()
 		{
 		{
-			tokenSource.Cancel();
+			tokenSource.Cancel ();
 		}
 		}
 
 
 		bool IMainLoopDriver.EventsPending (bool wait)
 		bool IMainLoopDriver.EventsPending (bool wait)
@@ -517,31 +518,35 @@ namespace Terminal.Gui {
 				waitTimeout = 0;
 				waitTimeout = 0;
 
 
 			result = null;
 			result = null;
-			waitForProbe.Set();
+			waitForProbe.Set ();
 
 
 			try {
 			try {
-				if(!tokenSource.IsCancellationRequested)
-					eventReady.Wait(waitTimeout, tokenSource.Token);
+				if (!tokenSource.IsCancellationRequested)
+					eventReady.Wait (waitTimeout, tokenSource.Token);
 			} catch (OperationCanceledException) {
 			} catch (OperationCanceledException) {
 				return true;
 				return true;
 			} finally {
 			} finally {
-				eventReady.Reset();
+				eventReady.Reset ();
 			}
 			}
 
 
 			if (!tokenSource.IsCancellationRequested)
 			if (!tokenSource.IsCancellationRequested)
 				return result != null;
 				return result != null;
 
 
-			tokenSource.Dispose();
-			tokenSource = new CancellationTokenSource();
+			tokenSource.Dispose ();
+			tokenSource = new CancellationTokenSource ();
 			return true;
 			return true;
 		}
 		}
 
 
 		Action<KeyEvent> keyHandler;
 		Action<KeyEvent> keyHandler;
+		Action<KeyEvent> keyDownHandler;
+		Action<KeyEvent> keyUpHandler;
 		Action<MouseEvent> mouseHandler;
 		Action<MouseEvent> mouseHandler;
 
 
-		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
+		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 		{
 			this.keyHandler = keyHandler;
 			this.keyHandler = keyHandler;
+			this.keyDownHandler = keyDownHandler;
+			this.keyUpHandler = keyUpHandler;
 			this.mouseHandler = mouseHandler;
 			this.mouseHandler = mouseHandler;
 		}
 		}
 
 
@@ -554,12 +559,37 @@ namespace Terminal.Gui {
 			var inputEvent = result [0];
 			var inputEvent = result [0];
 			switch (inputEvent.EventType) {
 			switch (inputEvent.EventType) {
 			case WindowsConsole.EventType.Key:
 			case WindowsConsole.EventType.Key:
-				if (inputEvent.KeyEvent.bKeyDown == false)
-					return;
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
 				var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
-				if (inputEvent.KeyEvent.UnicodeChar == 0 && map == (Key)0xffffffff)
-					return;
-				keyHandler (new KeyEvent (map));
+				if (map == (Key)0xffffffff) {
+					KeyEvent key;
+					// Shift = VK_SHIFT = 0x10
+					// Ctrl = VK_CONTROL = 0x11
+					// Alt = VK_MENU = 0x12
+					switch (inputEvent.KeyEvent.wVirtualKeyCode) {
+					case 0x11:
+						key = new KeyEvent (Key.CtrlMask);
+						break;
+					case 0x12:
+						key = new KeyEvent (Key.AltMask);
+						break;
+					default:
+						key = new KeyEvent (Key.Unknown);
+						break;
+					}
+
+					if (inputEvent.KeyEvent.bKeyDown)
+						keyDownHandler (key);
+					else
+						keyUpHandler (key);
+				} else {
+					if (inputEvent.KeyEvent.bKeyDown) {
+						// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+						keyDownHandler (new KeyEvent (map));
+						keyHandler (new KeyEvent (map));
+					} else {
+						keyUpHandler (new KeyEvent (map));
+					}
+				}
 				break;
 				break;
 
 
 			case WindowsConsole.EventType.Mouse:
 			case WindowsConsole.EventType.Mouse:
@@ -571,7 +601,7 @@ namespace Terminal.Gui {
 				rows = inputEvent.WindowBufferSizeEvent.size.Y;
 				rows = inputEvent.WindowBufferSizeEvent.size.Y;
 				ResizeScreen ();
 				ResizeScreen ();
 				UpdateOffScreen ();
 				UpdateOffScreen ();
-				TerminalResized?.Invoke();
+				TerminalResized?.Invoke ();
 				break;
 				break;
 			}
 			}
 			result = null;
 			result = null;
@@ -734,8 +764,8 @@ namespace Terminal.Gui {
 			bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
 			bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
 			bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
 			bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
 
 
-			var ConsoleKeyInfo = new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-			return new ConsoleKeyInfoEx(ConsoleKeyInfo, capslock, numlock);
+			var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
+			return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
 		}
 		}
 
 
 		public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
 		public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
@@ -837,6 +867,7 @@ namespace Terminal.Gui {
 
 
 				return (Key)((int)Key.F1 + delta);
 				return (Key)((int)Key.F1 + delta);
 			}
 			}
+
 			return (Key)(0xffffffff);
 			return (Key)(0xffffffff);
 		}
 		}
 
 
@@ -871,7 +902,7 @@ namespace Terminal.Gui {
 			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
 			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
 			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
 			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
 			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
 			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
-			Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
+			Colors.Menu.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Cyan);
 
 
 			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
 			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
@@ -925,10 +956,10 @@ namespace Terminal.Gui {
 			}
 			}
 
 
 			ccol++;
 			ccol++;
-			var runeWidth = Rune.ColumnWidth(rune);
+			var runeWidth = Rune.ColumnWidth (rune);
 			if (runeWidth > 1) {
 			if (runeWidth > 1) {
 				for (int i = 1; i < runeWidth; i++) {
 				for (int i = 1; i < runeWidth; i++) {
-					AddStr(" ");
+					AddStr (" ");
 				}
 				}
 			}
 			}
 			if (ccol == Cols) {
 			if (ccol == Cols) {
@@ -947,7 +978,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		int currentAttribute;
 		int currentAttribute;
-		CancellationTokenSource tokenSource = new CancellationTokenSource();
+		CancellationTokenSource tokenSource = new CancellationTokenSource ();
 
 
 		public override void SetAttribute (Attribute c)
 		public override void SetAttribute (Attribute c)
 		{
 		{
@@ -995,39 +1026,39 @@ namespace Terminal.Gui {
 			if (damageRegion.Left == -1)
 			if (damageRegion.Left == -1)
 				return;
 				return;
 
 
-			var bufferCoords = new WindowsConsole.Coord (){
+			var bufferCoords = new WindowsConsole.Coord () {
 				X = (short)Clip.Width,
 				X = (short)Clip.Width,
 				Y = (short)Clip.Height
 				Y = (short)Clip.Height
 			};
 			};
 
 
-			var window = new WindowsConsole.SmallRect (){
+			var window = new WindowsConsole.SmallRect () {
 				Top = 0,
 				Top = 0,
 				Left = 0,
 				Left = 0,
 				Right = (short)Clip.Right,
 				Right = (short)Clip.Right,
 				Bottom = (short)Clip.Bottom
 				Bottom = (short)Clip.Bottom
 			};
 			};
 
 
-			UpdateCursor();
+			UpdateCursor ();
 			winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
 			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);
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 		}
 		}
 
 
-		public override void UpdateCursor()
+		public override void UpdateCursor ()
 		{
 		{
-			var position = new WindowsConsole.Coord(){
+			var position = new WindowsConsole.Coord () {
 				X = (short)ccol,
 				X = (short)ccol,
 				Y = (short)crow
 				Y = (short)crow
 			};
 			};
-			winConsole.SetCursorPosition(position);
+			winConsole.SetCursorPosition (position);
 		}
 		}
 
 
 		public override void End ()
 		public override void End ()
 		{
 		{
-			winConsole.Cleanup();
+			winConsole.Cleanup ();
 		}
 		}
 
 
-#region Unused
+		#region Unused
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
 		{
 		{
 		}
 		}
@@ -1055,7 +1086,7 @@ namespace Terminal.Gui {
 		public override void CookMouse ()
 		public override void CookMouse ()
 		{
 		{
 		}
 		}
-#endregion
+		#endregion
 
 
 	}
 	}
 
 

+ 7 - 1
Terminal.Gui/Event.cs

@@ -178,6 +178,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		AltMask = 0x80000000,
 		AltMask = 0x80000000,
 
 
+		/// <summary>
+		///   When this value is set, the Key encodes the sequence Ctrl-KeyValue.
+		///   And the actual value must be extracted by removing the CtrlMask.
+		/// </summary>
+		CtrlMask = 0x40000000,
+
 		/// <summary>
 		/// <summary>
 		/// Backspace key.
 		/// Backspace key.
 		/// </summary>
 		/// </summary>
@@ -297,7 +303,7 @@ namespace Terminal.Gui {
 		public bool IsAlt => (Key & Key.AltMask) != 0;
 		public bool IsAlt => (Key & Key.AltMask) != 0;
 
 
 		/// <summary>
 		/// <summary>
-		/// Determines whether the value is a control key
+		/// Determines whether the value is a control key (and NOT just the ctrl key)
 		/// </summary>
 		/// </summary>
 		/// <value><c>true</c> if is ctrl; otherwise, <c>false</c>.</value>
 		/// <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);

+ 1096 - 998
Terminal.Gui/Views/Menu.cs

@@ -1,998 +1,1096 @@
-//
-// Menu.cs: application menus and submenus
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//
-// TODO:
-//   Add accelerator support, but should also support chords (ShortCut in MenuItem)
-//   Allow menus inside menus
-
-using System;
-using NStack;
-using System.Linq;
-using System.Collections.Generic;
-
-namespace Terminal.Gui {
-
-	/// <summary>
-	/// A menu item has a title, an associated help text, and an action to execute on activation.
-	/// </summary>
-	public class MenuItem {
-
-		/// <summary>
-		/// Initializes a new <see cref="T:Terminal.Gui.MenuItem"/>.
-		/// </summary>
-		/// <param name="title">Title for the menu item.</param>
-		/// <param name="help">Help text to display.</param>
-		/// <param name="action">Action to invoke when the menu item is activated.</param>
-		/// <param name="canExecute">Function to determine if the action can currently be executred.</param>
-		public MenuItem (ustring title, string help, Action action, Func<bool> canExecute = null)
-		{
-			Title = title ?? "";
-			Help = help ?? "";
-			Action = action;
-			CanExecute = canExecute;
-			bool nextIsHot = false;
-			foreach (var x in Title) {
-				if (x == '_')
-					nextIsHot = true;
-				else {
-					if (nextIsHot) {
-						HotKey = Char.ToUpper ((char)x);
-						break;
-					}
-					nextIsHot = false;
-				}
-			}
-		}
-
-		/// <summary>
-		/// Initializes a new <see cref="T:Terminal.Gui.MenuItem"/>.
-		/// </summary>
-		/// <param name="title">Title for the menu item.</param>
-		/// <param name="subMenu">The menu sub-menu.</param>
-		public MenuItem (ustring title, MenuBarItem subMenu) : this (title, "", null)
-		{
-			SubMenu = subMenu;
-			IsFromSubMenu = true;
-		}
-
-		//
-		//
-
-		/// <summary>
-		/// The hotkey is used when the menu is active, the shortcut can be triggered when the menu is not active.
-		/// For example HotKey would be "N" when the File Menu is open (assuming there is a "_New" entry
-		/// if the ShortCut is set to "Control-N", this would be a global hotkey that would trigger as well
-		/// </summary>
-		public Rune HotKey;
-
-		/// <summary>
-		/// This is the global setting that can be used as a global shortcut to invoke the action on the menu.
-		/// </summary>
-		public Key ShortCut;
-
-		/// <summary>
-		/// Gets or sets the title.
-		/// </summary>
-		/// <value>The title.</value>
-		public ustring Title { get; set; }
-
-		/// <summary>
-		/// Gets or sets the help text for the menu item.
-		/// </summary>
-		/// <value>The help text.</value>
-		public ustring Help { get; set; }
-
-		/// <summary>
-		/// Gets or sets the action to be invoked when the menu is triggered
-		/// </summary>
-		/// <value>Method to invoke.</value>
-		public Action Action { get; set; }
-
-		/// <summary>
-		/// Gets or sets the action to be invoked if the menu can be triggered
-		/// </summary>
-		/// <value>Function to determine if action is ready to be executed.</value>
-		public Func<bool> CanExecute { get; set; }
-
-		/// <summary>
-		/// Shortcut to check if the menu item is enabled
-		/// </summary>
-		public bool IsEnabled ()
-		{
-			return CanExecute == null ? true : CanExecute ();
-		}
-
-		internal int Width => Title.Length + Help.Length + 1 + 2;
-
-		/// <summary>
-		/// Gets or sets the parent for this MenuBarItem
-		/// </summary>
-		/// <value>The parent.</value>
-		internal MenuBarItem SubMenu { get; set; }
-		internal bool IsFromSubMenu { get; set; }
-
-		/// <summary>
-		/// Merely a debugging aid to see the interaction with main
-		/// </summary>
-		public MenuItem GetMenuItem ()
-		{
-			return this;
-		}
-
-		/// <summary>
-		/// Merely a debugging aid to see the interaction with main
-		/// </summary>
-		public bool GetMenuBarItem ()
-		{
-			return IsFromSubMenu;
-		}
-	}
-
-	/// <summary>
-	/// A menu bar item contains other menu items.
-	/// </summary>
-	public class MenuBarItem {
-		/// <summary>
-		/// Initializes a new <see cref="T:Terminal.Gui.MenuBarItem"/>.
-		/// </summary>
-		/// <param name="title">Title for the menu item.</param>
-		/// <param name="children">The items in the current menu.</param>
-		public MenuBarItem (ustring title, MenuItem [] children)
-		{
-			SetTitle (title ?? "");
-			Children = children;
-		}
-
-		/// <summary>
-		/// Initializes a new <see cref="T:Terminal.Gui.MenuBarItem"/>.
-		/// </summary>
-		/// <param name="children">The items in the current menu.</param>
-		public MenuBarItem (MenuItem[] children) : this (new string (' ', GetMaxTitleLength (children)), children)
-		{
-		}
-
-		static int GetMaxTitleLength (MenuItem[] children)
-		{
-			int maxLength = 0;
-			foreach (var item in children) {
-				int len = GetMenuBarItemLength (item.Title);
-				if (len > maxLength)
-					maxLength = len;
-				item.IsFromSubMenu = true;
-			}
-
-			return maxLength;
-		}
-
-		void SetTitle (ustring title)
-		{
-			if (title == null)
-				title = "";
-			Title = title;
-			TitleLength = GetMenuBarItemLength(Title);
-		}
-
-		static int GetMenuBarItemLength(ustring title)
-		{
-			int len = 0;
-			foreach (var ch in title) {
-				if (ch == '_')
-					continue;
-				len++;
-			}
-
-			return len;
-		}
-
-		/// <summary>
-		/// Gets or sets the title to display.
-		/// </summary>
-		/// <value>The title.</value>
-		public ustring Title { get; set; }
-
-		/// <summary>
-		/// Gets or sets the children for this MenuBarItem
-		/// </summary>
-		/// <value>The children.</value>
-		public MenuItem [] Children { get; set; }
-		internal int TitleLength { get; private set; }
-	}
-
-	class Menu : View {
-		internal MenuBarItem barItems;
-		MenuBar host;
-		internal int current;
-		internal View previousSubFocused;
-
-		static Rect MakeFrame (int x, int y, MenuItem [] items)
-		{
-			int maxW = items.Max(z => z?.Width) ?? 0;
-
-			return new Rect (x, y, maxW + 2, items.Length + 2);
-		}
-
-		public Menu (MenuBar host, int x, int y, MenuBarItem barItems) : base (MakeFrame (x, y, barItems.Children))
-		{
-			this.barItems = barItems;
-			this.host = host;
-			current = -1;
-			for (int i = 0; i < barItems.Children.Length; i++) {
-				if (barItems.Children[i] != null) {
-					current = i;
-					break;
-				}
-			}
-			ColorScheme = Colors.Menu;
-			CanFocus = true;
-			WantMousePositionReports = host.WantMousePositionReports;
-		}
-
-		internal Attribute DetermineColorSchemeFor (MenuItem item, int index)
-		{
-			if (item != null) {
-				if (index == current) return ColorScheme.Focus;
-				if (!item.IsEnabled ()) return ColorScheme.Disabled;
-			}
-			return ColorScheme.Normal;
-		}
-
-		public override void Redraw (Rect region)
-		{
-			Driver.SetAttribute (ColorScheme.Normal);
-			DrawFrame (region, padding: 0, fill: true);
-
-			for (int i = 0; i < barItems.Children.Length; i++) {
-				var item = barItems.Children [i];
-				Driver.SetAttribute (item == null ? ColorScheme.Normal : i == current ? ColorScheme.Focus : ColorScheme.Normal);
-				if (item == null) {
-					Move (0, i + 1);
-					Driver.AddRune (Driver.LeftTee);
-				} else
-					Move (1, i + 1);
-
-				Driver.SetAttribute (DetermineColorSchemeFor (item, i));
-				for (int p = 0; p < Frame.Width - 2; p++)
-					if (item == null)
-						Driver.AddRune (Driver.HLine);
-					else if (p == Frame.Width - 3 && barItems.Children [i].SubMenu != null)
-						Driver.AddRune ('>');
-					else
-						Driver.AddRune (' ');
-
-				if (item == null) {
-					Move (Frame.Right - 1, i + 1);
-					Driver.AddRune (Driver.RightTee);
-					continue;
-				}
-
-				Move (2, i + 1);
-				if (!item.IsEnabled ())
-					DrawHotString (item.Title, ColorScheme.Disabled, ColorScheme.Disabled);
-				else
-					DrawHotString (item.Title,
-					       i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
-					       i == current ? ColorScheme.Focus : ColorScheme.Normal);
-
-				// The help string
-				var l = item.Help.Length;
-				Move (Frame.Width - l - 2, 1 + i);
-				Driver.AddStr (item.Help);
-			}
-			PositionCursor ();
-		}
-
-		public override void PositionCursor ()
-		{
-			if (!host.isMenuClosed)
-				Move (2, 1 + current);
-			else
-				host.PositionCursor ();
-		}
-
-		void Run (Action action)
-		{
-			if (action == null)
-				return;
-
-			Application.UngrabMouse ();
-			host.CloseAllMenus ();
-			Application.Refresh ();
-
-			Application.MainLoop.AddIdle (() => {
-				action ();
-				return false;
-			});
-		}
-
-		public override bool ProcessKey (KeyEvent kb)
-		{
-			bool disabled;
-			switch (kb.Key) {
-			case Key.CursorUp:
-				if (current == -1)
-					break;
-				do {
-					disabled = false;
-					current--;
-					if (host.UseKeysUpDownAsKeysLeftRight) {
-						if (current == -1 && barItems.Children [current + 1].IsFromSubMenu && host.selectedSub > -1) {
-							current++;
-							host.PreviousMenu (true);
-							break;
-						}
-					}
-					if (current < 0)
-						current = barItems.Children.Length - 1;
-					var item = barItems.Children [current];
-					if (item == null || !item.IsEnabled ()) disabled = true;
-				} while (barItems.Children [current] == null || disabled);
-				SetNeedsDisplay ();
-				break;
-			case Key.CursorDown:
-				do {
-					current++;
-					disabled = false;
-					if (current == barItems.Children.Length)
-						current = 0;
-					var item = barItems.Children [current];
-					if (item == null || !item.IsEnabled ()) disabled = true;
-					if (host.UseKeysUpDownAsKeysLeftRight && barItems.Children [current]?.SubMenu != null &&
-						!disabled && !host.isMenuClosed) {
-						CheckSubMenu ();
-						break;
-					}
-					if (host.isMenuClosed)
-						host.OpenMenu (host.selected);
-				} while (barItems.Children [current] == null || disabled);
-				SetNeedsDisplay ();
-				break;
-			case Key.CursorLeft:
-				host.PreviousMenu (true);
-				break;
-			case Key.CursorRight:
-				host.NextMenu (barItems.Children [current].IsFromSubMenu ? true : false);
-				break;
-			case Key.Esc:
-				Application.UngrabMouse ();
-				host.CloseAllMenus ();
-				break;
-			case Key.Enter:
-				CheckSubMenu ();
-				Run (barItems.Children [current].Action);
-				break;
-			default:
-				// TODO: rune-ify
-				if (Char.IsLetterOrDigit ((char)kb.KeyValue)) {
-					var x = Char.ToUpper ((char)kb.KeyValue);
-
-					foreach (var item in barItems.Children) {
-						if (item == null) continue;
-						if (item.IsEnabled () && item.HotKey == x) {
-							host.CloseMenu ();
-							Run (item.Action);
-							return true;
-						}
-					}
-				}
-				break;
-			}
-			return true;
-		}
-
-		public override bool MouseEvent(MouseEvent me)
-		{
-			if (!host.handled && !host.HandleGrabView (me, this)) {
-				return false;
-			}
-			host.handled = false;
-			bool disabled;
-			if (me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1Released) {
-				disabled = false;
-				if (me.Y < 1)
-					return true;
-				var meY = me.Y - 1;
-				if (meY >= barItems.Children.Length)
-					return true;
-				var item = barItems.Children [meY];
-				if (item == null || !item.IsEnabled ()) disabled = true;
-				if (item != null && !disabled)
-					Run (barItems.Children [meY].Action);
-				return true;
-			} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.ReportMousePosition) {
-				disabled = false;
-				if (me.Y < 1)
-					return true;
-				if (me.Y - 1 >= barItems.Children.Length)
-					return true;
-				var item = barItems.Children [me.Y - 1];
-				if (item == null || !item.IsEnabled ()) disabled = true;
-				if (item != null && !disabled)
-					current = me.Y - 1;
-				HasFocus = true;
-				SetNeedsDisplay ();
-				CheckSubMenu ();
-				return true;
-			}
-			return false;
-		}
-
-		internal void CheckSubMenu ()
-		{
-			if (barItems.Children [current] == null)
-				return;
-			var subMenu = barItems.Children [current].SubMenu;
-			if (subMenu != null) {
-				int pos = -1;
-				if (host.openSubMenu != null)
-					pos = host.openSubMenu.FindIndex (o => o?.barItems == subMenu);
-				host.Activate (host.selected, pos, subMenu);
-			} else if (host.openSubMenu != null && !barItems.Children [current].IsFromSubMenu)
-				host.CloseMenu (false, true);
-		}
-
-		int GetSubMenuIndex (MenuBarItem subMenu)
-		{
-			int pos = -1;
-			if (this != null && Subviews.Count > 0) {
-				Menu v = null;
-				foreach (var menu in Subviews) {
-					if (((Menu)menu).barItems == subMenu)
-						v = (Menu)menu;
-				}
-				if (v != null)
-					pos = Subviews.IndexOf (v);
-			}
-
-			return pos;
-		}
-	}
-
-
-
-	/// <summary>
-	/// A menu bar for your application.
-	/// </summary>
-	public class MenuBar : View {
-		/// <summary>
-		/// The menus that were defined when the menubar was created.   This can be updated if the menu is not currently visible.
-		/// </summary>
-		/// <value>The menu array.</value>
-		public MenuBarItem [] Menus { get; set; }
-		internal int selected;
-		internal int selectedSub;
-		Action action;
-
-		/// <summary>
-		/// Used for change the navigation key style.
-		/// </summary>
-		public bool UseKeysUpDownAsKeysLeftRight { get; set; } = true;
-
-		/// <summary>
-		/// Initializes a new instance of the <see cref="T:Terminal.Gui.MenuBar"/> class with the specified set of toplevel menu items.
-		/// </summary>
-		/// <param name="menus">Individual menu items, if one of those contains a null, then a separator is drawn.</param>
-		public MenuBar (MenuBarItem [] menus) : base ()
-		{
-			X = 0;
-			Y = 0;
-			Width = Dim.Fill ();
-			Height = 1;
-			Menus = menus;
-			CanFocus = false;
-			selected = -1;
-			selectedSub = -1;
-			ColorScheme = Colors.Menu;
-			WantMousePositionReports = true;
-			isMenuClosed = true;
-		}
-
-		public override void Redraw (Rect region)
-		{
-			Move (0, 0);
-			Driver.SetAttribute (Colors.Menu.Normal);
-			for (int i = 0; i < Frame.Width; i++)
-				Driver.AddRune (' ');
-
-			Move (1, 0);
-			int pos = 1;
-
-			for (int i = 0; i < Menus.Length; i++) {
-				var menu = Menus [i];
-				Move (pos, 0);
-				Attribute hotColor, normalColor;
-				if (i == selected) {
-					hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
-					normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
-				} else {
-					hotColor = Colors.Base.Focus;
-					normalColor = Colors.Base.Focus;
-				}
-				DrawHotString (" " + menu.Title + " " + "   ", hotColor, normalColor);
-				pos += menu.TitleLength + 3;
-			}
-			PositionCursor ();
-		}
-
-		public override void PositionCursor ()
-		{
-			int pos = 0;
-			for (int i = 0; i < Menus.Length; i++) {
-				if (i == selected) {
-					pos++;
-					if (!isMenuClosed)
-						Move (pos, 0);
-					else
-						Move (pos + 1, 0);
-					return;
-				} else {
-					if (!isMenuClosed)
-						pos += Menus [i].TitleLength + 4;
-					else
-						pos += 2 + Menus [i].TitleLength + 1;
-				}
-			}
-			Move (0, 0);
-		}
-
-		void Selected (MenuItem item)
-		{
-			// TODO: Running = false;
-			action = item.Action;
-		}
-
-		public event EventHandler OnOpenMenu;
-		public event EventHandler OnCloseMenu;
-		internal Menu openMenu;
-		Menu openCurrentMenu;
-		internal List<Menu> openSubMenu;
-		View previousFocused;
-		internal bool isMenuOpening;
-		internal bool isMenuClosing;
-		internal bool isMenuClosed;
-		public bool MenuOpen;
-		View lastFocused;
-
-		/// <summary>
-		/// Get the lasted focused view before open the menu.
-		/// </summary>
-		public View LastFocused { get; private set; }
-
-		internal void OpenMenu (int index, int sIndex = -1, MenuBarItem subMenu = null)
-		{
-			isMenuOpening = true;
-			OnOpenMenu?.Invoke (this, null);
-			int pos = 0;
-			switch (subMenu) {
-			case null:
-				lastFocused = lastFocused ?? SuperView.MostFocused;
-				if (openSubMenu != null)
-					CloseMenu (false, true);
-				if (openMenu != null)
-					SuperView.Remove (openMenu);
-
-				for (int i = 0; i < index; i++)
-					pos += Menus [i].Title.Length + 2;
-				openMenu = new Menu (this, pos, 1, Menus [index]);
-				openCurrentMenu = openMenu;
-				openCurrentMenu.previousSubFocused = openMenu;
-				SuperView.Add (openMenu);
-				SuperView.SetFocus (openMenu);
-				break;
-			default:
-				if (openSubMenu == null)
-					openSubMenu = new List<Menu> ();
-				if (sIndex > -1) {
-					RemoveSubMenu (sIndex);
-				} else {
-					var last = openSubMenu.Count > 0 ? openSubMenu.Last () : openMenu;
-					openCurrentMenu = new Menu (this, last.Frame.Left + last.Frame.Width, last.Frame.Top + 1 + last.current, subMenu);
-					openCurrentMenu.previousSubFocused = last.previousSubFocused;
-					openSubMenu.Add (openCurrentMenu);
-					SuperView.Add (openCurrentMenu);
-				}
-				selectedSub = openSubMenu.Count - 1;
-				SuperView?.SetFocus (openCurrentMenu);
-				break;
-			}
-			isMenuOpening = false;
-			isMenuClosed = false;
-			MenuOpen = true;
-		}
-
-		// Starts the menu from a hotkey
-		void StartMenu ()
-		{
-			if (openMenu != null)
-				return;
-			selected = 0;
-			SetNeedsDisplay ();
-
-			previousFocused = SuperView.Focused;
-			OpenMenu (selected);
-			Application.GrabMouse (this);
-		}
-
-		// Activates the menu, handles either first focus, or activating an entry when it was already active
-		// For mouse events.
-		internal void Activate (int idx, int sIdx = -1, MenuBarItem subMenu = null)
-		{
-			selected = idx;
-			selectedSub = sIdx;
-			if (openMenu == null)
-				previousFocused = SuperView.Focused;
-
-			OpenMenu (idx, sIdx, subMenu);
-			SetNeedsDisplay ();
-		}
-
-		internal void CloseMenu (bool reopen = false, bool isSubMenu = false)
-		{
-			isMenuClosing = true;
-			OnCloseMenu?.Invoke (this, null);
-			switch (isSubMenu) {
-			case false:
-				if (openMenu != null)
-					SuperView.Remove (openMenu);
-				SetNeedsDisplay ();
-				if (previousFocused != null && openMenu != null && previousFocused.ToString () != openCurrentMenu.ToString ())
-					previousFocused?.SuperView?.SetFocus (previousFocused);
-				openMenu = null;
-				if (lastFocused is Menu) {
-					lastFocused = null;
-				}
-				LastFocused = lastFocused;
-				lastFocused = null;
-				if (LastFocused != null) {
-					if (!reopen)
-						selected = -1;
-					LastFocused.SuperView?.SetFocus (LastFocused);
-				} else {
-					SuperView.SetFocus (this);
-					isMenuClosed = true;
-					PositionCursor ();
-				}
-				isMenuClosed = true;
-				break;
-
-			case true:
-				selectedSub = -1;
-				SetNeedsDisplay ();
-				RemoveAllOpensSubMenus ();
-				openCurrentMenu.previousSubFocused?.SuperView?.SetFocus (openCurrentMenu.previousSubFocused);
-				openSubMenu = null;
-				break;
-			}
-			isMenuClosing = false;
-			MenuOpen = false;
-		}
-
-		void RemoveSubMenu (int index)
-		{
-			if (openSubMenu == null)
-				return;
-			for (int i = openSubMenu.Count - 1; i > index; i--) {
-				isMenuClosing = true;
-				if (openSubMenu.Count - 1 > 0)
-					SuperView.SetFocus (openSubMenu [i - 1]);
-				else
-					SuperView.SetFocus (openMenu);
-				if (openSubMenu != null) {
-					SuperView.Remove (openSubMenu [i]);
-					openSubMenu.Remove (openSubMenu [i]);
-				}
-				RemoveSubMenu (i);
-			}
-			if (openSubMenu.Count > 0)
-				openCurrentMenu = openSubMenu.Last ();
-
-			//if (openMenu.Subviews.Count == 0)
-			//	return;
-			//if (index == 0) {
-			//	//SuperView.SetFocus (previousSubFocused);
-			//	FocusPrev ();
-			//	return;
-			//}
-
-			//for (int i = openMenu.Subviews.Count - 1; i > index; i--) {
-			//	isMenuClosing = true;
-			//	if (openMenu.Subviews.Count - 1 > 0)
-			//		SuperView.SetFocus (openMenu.Subviews [i - 1]);
-			//	else
-			//		SuperView.SetFocus (openMenu);
-			//	if (openMenu != null) {
-			//		Remove (openMenu.Subviews [i]);
-			//		openMenu.Remove (openMenu.Subviews [i]);
-			//	}
-			//	RemoveSubMenu (i);
-			//}
-			isMenuClosing = false;
-		}
-
-		internal void RemoveAllOpensSubMenus ()
-		{
-			if (openSubMenu != null) {
-				foreach (var item in openSubMenu) {
-					SuperView.Remove (item);
-				}
-			}
-		}
-
-		internal void CloseAllMenus ()
-		{
-			if (!isMenuOpening && !isMenuClosing) {
-				if (openSubMenu != null)
-					CloseMenu (false, true);
-				CloseMenu ();
-				if (LastFocused != null && LastFocused != this)
-					selected = -1;
-			}
-			isMenuClosed = true;
-		}
-
-		View FindDeepestMenu (View view, ref int count)
-		{
-			count = count > 0 ? count : 0;
-			foreach (var menu in view.Subviews) {
-				if (menu is Menu) {
-					count++;
-					return FindDeepestMenu ((Menu)menu, ref count);
-				}
-			}
-			return view;
-		}
-
-		internal void PreviousMenu (bool isSubMenu = false)
-		{
-			switch (isSubMenu) {
-			case false:
-				if (selected <= 0)
-					selected = Menus.Length - 1;
-				else
-					selected--;
-
-				if (selected > -1)
-					CloseMenu (true, false);
-				OpenMenu (selected);
-				break;
-			case true:
-				if (selectedSub > -1) {
-					selectedSub--;
-					RemoveSubMenu (selectedSub);
-					SetNeedsDisplay ();
-				} else
-					PreviousMenu ();
-
-				break;
-			}
-		}
-
-		internal void NextMenu (bool isSubMenu = false)
-		{
-			switch (isSubMenu) {
-			case false:
-				if (selected == -1)
-					selected = 0;
-				else if (selected + 1 == Menus.Length)
-					selected = 0;
-				else
-					selected++;
-
-				if (selected > -1)
-					CloseMenu (true);
-				OpenMenu (selected);
-				break;
-			case true:
-				if (UseKeysUpDownAsKeysLeftRight) {
-					CloseMenu (false, true);
-					NextMenu ();
-				} else {
-					if ((selectedSub == -1 || openSubMenu == null || openSubMenu?.Count == selectedSub) && openCurrentMenu.barItems.Children [openCurrentMenu.current].SubMenu == null) {
-						if (openSubMenu != null)
-							CloseMenu (false, true);
-						NextMenu ();
-					} else if (openCurrentMenu.barItems.Children [openCurrentMenu.current].SubMenu != null ||
-						!openCurrentMenu.barItems.Children [openCurrentMenu.current].IsFromSubMenu)
-						selectedSub++;
-					else
-						return;
-					SetNeedsDisplay ();
-					openCurrentMenu.CheckSubMenu ();
-				}
-				break;
-			}
-		}
-
-                internal bool FindAndOpenMenuByHotkey(KeyEvent kb)
-                {
-                    int pos = 0;
-                    var c = ((uint)kb.Key & (uint)Key.CharMask);
-	            for (int i = 0; i < Menus.Length; i++)
-                    {
-			    // TODO: this code is duplicated, hotkey should be part of the MenuBarItem
-                            var mi = Menus[i];
-                            int p = mi.Title.IndexOf('_');
-                            if (p != -1 && p + 1 < mi.Title.Length) {
-                                    if (mi.Title[p + 1] == c) {
-						Application.GrabMouse (this);
-						selected = i;
-						OpenMenu (i);
-			                    return true;
-                                    }
-                            }
-                    }
-	            return false;
-                }
-
-	        public override bool ProcessHotKey (KeyEvent kb)
-		{
-			if (kb.Key == Key.F9) {
-				StartMenu ();
-				return true;
-			}
-
-                        if (kb.IsAlt)
-                        {
-                            if (FindAndOpenMenuByHotkey(kb)) return true;
-                        }
-			var kc = kb.KeyValue;
-
-			return base.ProcessHotKey (kb);
-		}
-
-		public override bool ProcessKey (KeyEvent kb)
-		{
-			switch (kb.Key) {
-			case Key.CursorLeft:
-				selected--;
-				if (selected < 0)
-					selected = Menus.Length - 1;
-				break;
-			case Key.CursorRight:
-				selected = (selected + 1) % Menus.Length;
-				break;
-
-			case Key.Esc:
-			case Key.ControlC:
-				//TODO: Running = false;
-				CloseMenu ();
-				break;
-
-			default:
-				var key = kb.KeyValue;
-				if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
-					char c = Char.ToUpper ((char)key);
-
-					if (Menus [selected].Children == null)
-						return false;
-
-					foreach (var mi in Menus [selected].Children) {
-						int p = mi.Title.IndexOf ('_');
-						if (p != -1 && p + 1 < mi.Title.Length) {
-							if (mi.Title [p + 1] == c) {
-								Selected (mi);
-								return true;
-							}
-						}
-					}
-				}
-
-				return false;
-			}
-			SetNeedsDisplay ();
-			return true;
-		}
-
-		public override bool MouseEvent(MouseEvent me)
-		{
-			if (!handled && !HandleGrabView (me, this)) {
-				return false;
-			}
-			handled = false;
-
-			if (me.Flags == MouseFlags.Button1Clicked ||
-				(me.Flags == 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.Button1Clicked && !isMenuClosed) {
-							Application.UngrabMouse ();
-							CloseMenu ();
-						} else if (me.Flags == MouseFlags.Button1Clicked && isMenuClosed) {
-							Activate (i);
-						}
-						else if (selected != i && selected > -1 && me.Flags == MouseFlags.ReportMousePosition) {
-							if (!isMenuClosed) {
-								CloseMenu ();
-								Activate (i);
-							}
-						} else {
-							if (!isMenuClosed)
-								Activate (i);
-						}
-						return true;
-					}
-					pos += 2 + Menus [i].TitleLength + 1;
-				}
-			}
-			return false;
-		}
-
-		internal bool handled;
-
-		internal bool HandleGrabView (MouseEvent me, View current)
-		{
-			if (Application.mouseGrabView != null) {
-				if (me.View is MenuBar || me.View is Menu) {
-					if(me.View != current) {
-						Application.UngrabMouse ();
-						Application.GrabMouse (me.View);
-						me.View.MouseEvent (me);
-					}
-				} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
-					Application.UngrabMouse ();
-					CloseAllMenus ();
-					handled = false;
-					return false;
-				} else {
-					handled = false;
-					return false;
-				}
-			} else if (isMenuClosed && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
-				Application.GrabMouse (current);
-			} else {
-				handled = false;
-				return false;
-			}
-			//if (me.View != this && me.Flags != MouseFlags.Button1Clicked)
-			//	return true;
-			//else if (me.View != this && me.Flags == MouseFlags.Button1Clicked) {
-			//	Application.UngrabMouse ();
-			//	host.CloseAllMenus ();
-			//	return true;
-			//}
-
-
-			//if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Clicked)
-			//	return false;
-
-			//if (Application.mouseGrabView != null) {
-			//	if (me.View is MenuBar || me.View is Menu) {
-			//		me.X -= me.OfX;
-			//		me.Y -= me.OfY;
-			//		me.View.MouseEvent (me);
-			//		return true;
-			//	} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags == MouseFlags.Button1Clicked) {
-			//		Application.UngrabMouse ();
-			//		CloseAllMenus ();
-			//	}
-			//} else if (!isMenuClosed && selected == -1 && me.Flags == MouseFlags.Button1Clicked) {
-			//	Application.GrabMouse (this);
-			//	return true;
-			//}
-
-			//if (Application.mouseGrabView != null) {
-			//	if (Application.mouseGrabView == me.View && me.View == current) {
-			//		me.X -= me.OfX;
-			//		me.Y -= me.OfY;
-			//	} else if (me.View != current && me.View is MenuBar && me.View is Menu) {
-			//		Application.UngrabMouse ();
-			//		Application.GrabMouse (me.View);
-			//	} else if (me.Flags == MouseFlags.Button1Clicked) {
-			//		Application.UngrabMouse ();
-			//		CloseMenu ();
-			//	}
-			//} else if ((!isMenuClosed && selected > -1)) {
-			//	Application.GrabMouse (current);
-			//}
-
-			handled = true;
-
-			return true;
-		}
-	}
-
-}
+//
+// Menu.cs: application menus and submenus
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+// TODO:
+//   Add accelerator support, but should also support chords (ShortCut in MenuItem)
+//   Allow menus inside menus
+
+using System;
+using NStack;
+using System.Linq;
+using System.Collections.Generic;
+using System.Reflection;
+
+namespace Terminal.Gui {
+
+	/// <summary>
+	/// A menu item has a title, an associated help text, and an action to execute on activation.
+	/// </summary>
+	public class MenuItem {
+
+		public MenuItem ()
+		{
+			Title = "";
+			Help = "";
+		}
+
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.MenuItem"/>.
+		/// </summary>
+		/// <param name="title">Title for the menu item.</param>
+		/// <param name="help">Help text to display.</param>
+		/// <param name="action">Action to invoke when the menu item is activated.</param>
+		/// <param name="canExecute">Function to determine if the action can currently be executred.</param>
+		public MenuItem (ustring title, string help, Action action, Func<bool> canExecute = null)
+		{
+			Title = title ?? "";
+			Help = help ?? "";
+			Action = action;
+			CanExecute = canExecute;
+			bool nextIsHot = false;
+			foreach (var x in Title) {
+				if (x == '_')
+					nextIsHot = true;
+				else {
+					if (nextIsHot) {
+						HotKey = Char.ToUpper ((char)x);
+						break;
+					}
+					nextIsHot = false;
+				}
+			}
+		}
+
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.MenuItem"/>.
+		/// </summary>
+		/// <param name="title">Title for the menu item.</param>
+		/// <param name="subMenu">The menu sub-menu.</param>
+		public MenuItem (ustring title, MenuBarItem subMenu) : this (title, "", null)
+		{
+			SubMenu = subMenu;
+			IsFromSubMenu = true;
+		}
+
+		//
+		//
+
+		/// <summary>
+		/// The hotkey is used when the menu is active, the shortcut can be triggered when the menu is not active.
+		/// For example HotKey would be "N" when the File Menu is open (assuming there is a "_New" entry
+		/// if the ShortCut is set to "Control-N", this would be a global hotkey that would trigger as well
+		/// </summary>
+		public Rune HotKey;
+
+		/// <summary>
+		/// This is the global setting that can be used as a global shortcut to invoke the action on the menu.
+		/// </summary>
+		public Key ShortCut;
+
+		/// <summary>
+		/// Gets or sets the title.
+		/// </summary>
+		/// <value>The title.</value>
+		public ustring Title { get; set; }
+
+		/// <summary>
+		/// Gets or sets the help text for the menu item.
+		/// </summary>
+		/// <value>The help text.</value>
+		public ustring Help { get; set; }
+
+		/// <summary>
+		/// Gets or sets the action to be invoked when the menu is triggered
+		/// </summary>
+		/// <value>Method to invoke.</value>
+		public Action Action { get; set; }
+
+		/// <summary>
+		/// Gets or sets the action to be invoked if the menu can be triggered
+		/// </summary>
+		/// <value>Function to determine if action is ready to be executed.</value>
+		public Func<bool> CanExecute { get; set; }
+
+		/// <summary>
+		/// Shortcut to check if the menu item is enabled
+		/// </summary>
+		public bool IsEnabled ()
+		{
+			return CanExecute == null ? true : CanExecute ();
+		}
+
+		internal int Width => Title.Length + Help.Length + 1 + 2;
+
+		/// <summary>
+		/// Gets or sets the parent for this MenuBarItem
+		/// </summary>
+		/// <value>The parent.</value>
+		internal MenuBarItem SubMenu { get; set; }
+		internal bool IsFromSubMenu { get; set; }
+
+		/// <summary>
+		/// Merely a debugging aid to see the interaction with main
+		/// </summary>
+		public MenuItem GetMenuItem ()
+		{
+			return this;
+		}
+
+		/// <summary>
+		/// Merely a debugging aid to see the interaction with main
+		/// </summary>
+		public bool GetMenuBarItem ()
+		{
+			return IsFromSubMenu;
+		}
+	}
+
+	/// <summary>
+	/// A menu bar item contains other menu items.
+	/// </summary>
+	public class MenuBarItem : MenuItem {
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.MenuBarItem"/> as a <see cref="T:Terminal.Gui.MenuItem"/>.
+		/// </summary>
+		/// <param name="title">Title for the menu item.</param>
+		/// <param name="help">Help text to display.</param>
+		/// <param name="action">Action to invoke when the menu item is activated.</param>
+		/// <param name="canExecute">Function to determine if the action can currently be executred.</param>
+		public MenuBarItem (ustring title, string help, Action action, Func<bool> canExecute = null) : base (title, help, action, canExecute)
+		{
+			SetTitle (title ?? "");
+			Children = null;
+		}
+
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.MenuBarItem"/>.
+		/// </summary>
+		/// <param name="title">Title for the menu item.</param>
+		/// <param name="children">The items in the current menu.</param>
+		public MenuBarItem (ustring title, MenuItem [] children)
+		{
+			SetTitle (title ?? "");
+			Children = children;
+		}
+
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.MenuBarItem"/>.
+		/// </summary>
+		/// <param name="children">The items in the current menu.</param>
+		public MenuBarItem (MenuItem [] children) : this (new string (' ', GetMaxTitleLength (children)), children)
+		{
+		}
+
+		static int GetMaxTitleLength (MenuItem [] children)
+		{
+			int maxLength = 0;
+			foreach (var item in children) {
+				int len = GetMenuBarItemLength (item.Title);
+				if (len > maxLength)
+					maxLength = len;
+				item.IsFromSubMenu = true;
+			}
+
+			return maxLength;
+		}
+
+		void SetTitle (ustring title)
+		{
+			if (title == null)
+				title = "";
+			Title = title;
+			TitleLength = GetMenuBarItemLength (Title);
+		}
+
+		static int GetMenuBarItemLength (ustring title)
+		{
+			int len = 0;
+			foreach (var ch in title) {
+				if (ch == '_')
+					continue;
+				len++;
+			}
+
+			return len;
+		}
+
+		/// <summary>
+		/// Gets or sets the title to display.
+		/// </summary>
+		/// <value>The title.</value>
+		//public ustring Title { get; set; }
+
+		/// <summary>
+		/// Gets or sets the children for this MenuBarItem
+		/// </summary>
+		/// <value>The children.</value>
+		public MenuItem [] Children { get; set; }
+		internal int TitleLength { get; private set; }
+
+		internal bool IsTopLevel { get => (Children == null || Children.Length == 0); }
+
+	}
+
+	class Menu : View {
+		internal MenuBarItem barItems;
+		MenuBar host;
+		internal int current;
+		internal View previousSubFocused;
+
+		static Rect MakeFrame (int x, int y, MenuItem [] items)
+		{
+			if (items == null || items.Length == 0) {
+				return new Rect ();
+			}
+			int maxW = items.Max (z => z?.Width) ?? 0;
+
+			return new Rect (x, y, maxW + 2, items.Length + 2);
+		}
+
+		public Menu (MenuBar host, int x, int y, MenuBarItem barItems) : base (MakeFrame (x, y, barItems.Children))
+		{
+			this.barItems = barItems;
+			this.host = host;
+			if (barItems.IsTopLevel) {
+				// This is a standalone MenuItem on a MenuBar
+				ColorScheme = Colors.Menu;
+				CanFocus = true;
+			} else {
+
+				current = -1;
+				for (int i = 0; i < barItems.Children.Length; i++) {
+					if (barItems.Children [i] != null) {
+						current = i;
+						break;
+					}
+				}
+				ColorScheme = Colors.Menu;
+				CanFocus = true;
+				WantMousePositionReports = host.WantMousePositionReports;
+			}
+
+		}
+
+		internal Attribute DetermineColorSchemeFor (MenuItem item, int index)
+		{
+			if (item != null) {
+				if (index == current) return ColorScheme.Focus;
+				if (!item.IsEnabled ()) return ColorScheme.Disabled;
+			}
+			return ColorScheme.Normal;
+		}
+
+		public override void Redraw (Rect region)
+		{
+			Driver.SetAttribute (ColorScheme.Normal);
+			DrawFrame (region, padding: 0, fill: true);
+
+			for (int i = 0; i < barItems.Children.Length; i++) {
+				var item = barItems.Children [i];
+				Driver.SetAttribute (item == null ? ColorScheme.Normal : i == current ? ColorScheme.Focus : ColorScheme.Normal);
+				if (item == null) {
+					Move (0, i + 1);
+					Driver.AddRune (Driver.LeftTee);
+				} else
+					Move (1, i + 1);
+
+				Driver.SetAttribute (DetermineColorSchemeFor (item, i));
+				for (int p = 0; p < Frame.Width - 2; p++)
+					if (item == null)
+						Driver.AddRune (Driver.HLine);
+					else if (p == Frame.Width - 3 && barItems.Children [i].SubMenu != null)
+						Driver.AddRune ('>');
+					else
+						Driver.AddRune (' ');
+
+				if (item == null) {
+					Move (Frame.Right - 1, i + 1);
+					Driver.AddRune (Driver.RightTee);
+					continue;
+				}
+
+				Move (2, i + 1);
+				if (!item.IsEnabled ())
+					DrawHotString (item.Title, ColorScheme.Disabled, ColorScheme.Disabled);
+				else
+					DrawHotString (item.Title,
+					       i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
+					       i == current ? ColorScheme.Focus : ColorScheme.Normal);
+
+				// The help string
+				var l = item.Help.Length;
+				Move (Frame.Width - l - 2, 1 + i);
+				Driver.AddStr (item.Help);
+			}
+			PositionCursor ();
+		}
+
+		public override void PositionCursor ()
+		{
+			if (host == null || !host.isMenuClosed)
+				if (barItems.IsTopLevel) {
+					host.PositionCursor ();
+				} else
+					Move (2, 1 + current);
+			else
+				host.PositionCursor ();
+		}
+
+		public void Run (Action action)
+		{
+			if (action == null)
+				return;
+
+			Application.UngrabMouse ();
+			host.CloseAllMenus ();
+			Application.Refresh ();
+
+			Application.MainLoop.AddIdle (() => {
+				action ();
+				return false;
+			});
+		}
+		public override bool ProcessKey (KeyEvent kb)
+		{
+			bool disabled;
+			switch (kb.Key) {
+			case Key.CursorUp:
+				if (barItems.IsTopLevel || current == -1)
+					break;
+				do {
+					disabled = false;
+					current--;
+					if (host.UseKeysUpDownAsKeysLeftRight) {
+						if (current == -1 && barItems.Children [current + 1].IsFromSubMenu && host.selectedSub > -1) {
+							current++;
+							host.PreviousMenu (true);
+							break;
+						}
+					}
+					if (current < 0)
+						current = barItems.Children.Length - 1;
+					var item = barItems.Children [current];
+					if (item == null || !item.IsEnabled ()) disabled = true;
+				} while (barItems.Children [current] == null || disabled);
+				SetNeedsDisplay ();
+				break;
+			case Key.CursorDown:
+				if (barItems.IsTopLevel) {
+					break;
+				}
+
+				do {
+					current++;
+					disabled = false;
+					if (current == barItems.Children.Length)
+						current = 0;
+					var item = barItems.Children [current];
+					if (item == null || !item.IsEnabled ()) disabled = true;
+					if (host.UseKeysUpDownAsKeysLeftRight && barItems.Children [current]?.SubMenu != null &&
+						!disabled && !host.isMenuClosed) {
+						CheckSubMenu ();
+						break;
+					}
+					if (host.isMenuClosed)
+						host.OpenMenu (host.selected);
+				} while (barItems.Children [current] == null || disabled);
+				SetNeedsDisplay ();
+				break;
+			case Key.CursorLeft:
+				host.PreviousMenu (true);
+				break;
+			case Key.CursorRight:
+				host.NextMenu (barItems.IsTopLevel || barItems.Children [current].IsFromSubMenu ? true : false);
+				break;
+			case Key.Esc:
+				Application.UngrabMouse ();
+				host.CloseAllMenus ();
+				break;
+			case Key.Enter:
+				if (barItems.IsTopLevel) {
+					Run (barItems.Action);
+				} else {
+					CheckSubMenu ();
+					Run (barItems.Children [current].Action);
+				}
+				break;
+			default:
+				// TODO: rune-ify
+				if (barItems.Children != null && Char.IsLetterOrDigit ((char)kb.KeyValue)) {
+					var x = Char.ToUpper ((char)kb.KeyValue);
+					foreach (var item in barItems.Children) {
+						if (item == null) continue;
+						if (item.IsEnabled () && item.HotKey == x) {
+							host.CloseMenu ();
+							Run (item.Action);
+							return true;
+						}
+					}
+				}
+				break;
+			}
+			return true;
+		}
+
+		public override bool MouseEvent (MouseEvent me)
+		{
+			if (!host.handled && !host.HandleGrabView (me, this)) {
+				return false;
+			}
+			host.handled = false;
+			bool disabled;
+			if (me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1Released) {
+				disabled = false;
+				if (me.Y < 1)
+					return true;
+				var meY = me.Y - 1;
+				if (meY >= barItems.Children.Length)
+					return true;
+				var item = barItems.Children [meY];
+				if (item == null || !item.IsEnabled ()) disabled = true;
+				if (item != null && !disabled)
+					Run (barItems.Children [meY].Action);
+				return true;
+			} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.ReportMousePosition) {
+				disabled = false;
+				if (me.Y < 1)
+					return true;
+				if (me.Y - 1 >= barItems.Children.Length)
+					return true;
+				var item = barItems.Children [me.Y - 1];
+				if (item == null || !item.IsEnabled ()) disabled = true;
+				if (item != null && !disabled)
+					current = me.Y - 1;
+				HasFocus = true;
+				SetNeedsDisplay ();
+				CheckSubMenu ();
+				return true;
+			}
+			return false;
+		}
+
+		internal void CheckSubMenu ()
+		{
+			if (barItems.Children [current] == null)
+				return;
+			var subMenu = barItems.Children [current].SubMenu;
+			if (subMenu != null) {
+				int pos = -1;
+				if (host.openSubMenu != null)
+					pos = host.openSubMenu.FindIndex (o => o?.barItems == subMenu);
+				host.Activate (host.selected, pos, subMenu);
+			} else if (host.openSubMenu != null && !barItems.Children [current].IsFromSubMenu)
+				host.CloseMenu (false, true);
+		}
+
+		int GetSubMenuIndex (MenuBarItem subMenu)
+		{
+			int pos = -1;
+			if (this != null && Subviews.Count > 0) {
+				Menu v = null;
+				foreach (var menu in Subviews) {
+					if (((Menu)menu).barItems == subMenu)
+						v = (Menu)menu;
+				}
+				if (v != null)
+					pos = Subviews.IndexOf (v);
+			}
+
+			return pos;
+		}
+	}
+
+
+
+	/// <summary>
+	/// A menu bar for your application.
+	/// </summary>
+	public class MenuBar : View {
+		/// <summary>
+		/// The menus that were defined when the menubar was created.   This can be updated if the menu is not currently visible.
+		/// </summary>
+		/// <value>The menu array.</value>
+		public MenuBarItem [] Menus { get; set; }
+		internal int selected;
+		internal int selectedSub;
+
+		Action action;
+
+		/// <summary>
+		/// Used for change the navigation key style.
+		/// </summary>
+		public bool UseKeysUpDownAsKeysLeftRight { get; set; } = true;
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.MenuBar"/> class with the specified set of toplevel menu items.
+		/// </summary>
+		/// <param name="menus">Individual menu items, if one of those contains a null, then a separator is drawn.</param>
+		public MenuBar (MenuBarItem [] menus) : base ()
+		{
+			X = 0;
+			Y = 0;
+			Width = Dim.Fill ();
+			Height = 1;
+			Menus = menus;
+			CanFocus = true;
+			selected = -1;
+			selectedSub = -1;
+			ColorScheme = Colors.Menu;
+			WantMousePositionReports = true;
+			isMenuClosed = true;
+		}
+
+		public override bool KeyDown (KeyEvent keyEvent)
+		{
+			if (keyEvent.IsAlt) {
+				openedByHotKey = false;
+			}
+			return false;
+		}
+
+		/// <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. 
+		/// </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 
+					// that faults.
+
+					Activate (0);
+					SetNeedsDisplay ();
+				} else {
+					// There's an open menu. If this Alt key-up is a pre-cursor to an acellerator
+					// we don't want to close the menu because it'll flash.
+					// How to deal with that?
+					if (!openedByHotKey) {
+						CloseAllMenus ();
+					}
+				}
+				return true;
+			}
+			return false;
+		}
+
+		public override void Redraw (Rect region)
+		{
+			Move (0, 0);
+			Driver.SetAttribute (Colors.Menu.Normal);
+			for (int i = 0; i < Frame.Width; i++)
+				Driver.AddRune (' ');
+
+			Move (1, 0);
+			int pos = 1;
+
+			for (int i = 0; i < Menus.Length; i++) {
+				var menu = Menus [i];
+				Move (pos, 0);
+				Attribute hotColor, normalColor;
+				if (i == selected) {
+					hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
+					normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
+				} else {
+					hotColor = ColorScheme.HotNormal;
+					normalColor = ColorScheme.Normal;
+				}
+				DrawHotString ($" {menu.Title}  ", hotColor, normalColor);
+				pos += 1 + menu.TitleLength + 2;
+			}
+			PositionCursor ();
+		}
+
+		public override void PositionCursor ()
+		{
+			int pos = 0;
+			for (int i = 0; i < Menus.Length; i++) {
+				if (i == selected) {
+					pos++;
+					if (!isMenuClosed)
+						Move (pos + 1, 0);
+					else
+						Move (pos + 1, 0);
+					return;
+				} else {
+					if (!isMenuClosed)
+						pos += 1 + Menus [i].TitleLength + 2;
+					else
+						pos += 2 + Menus [i].TitleLength + 1;
+				}
+			}
+			//Move (0, 0);
+		}
+
+		void Selected (MenuItem item)
+		{
+			// TODO: Running = false;
+			action = item.Action;
+		}
+
+		public event EventHandler OnOpenMenu;
+		public event EventHandler OnCloseMenu;
+		internal Menu openMenu;
+		Menu openCurrentMenu;
+		internal List<Menu> openSubMenu;
+		View previousFocused;
+		internal bool isMenuOpening;
+		internal bool isMenuClosing;
+		internal bool isMenuClosed;
+		public bool MenuOpen;
+		View lastFocused;
+
+		/// <summary>
+		/// Get the lasted focused view before open the menu.
+		/// </summary>
+		public View LastFocused { get; private set; }
+
+		internal void OpenMenu (int index, int sIndex = -1, MenuBarItem subMenu = null)
+		{
+			isMenuOpening = true;
+			OnOpenMenu?.Invoke (this, null);
+			int pos = 0;
+			switch (subMenu) {
+			case null:
+				lastFocused = lastFocused ?? SuperView.MostFocused;
+				if (openSubMenu != null)
+					CloseMenu (false, true);
+				if (openMenu != null)
+					SuperView.Remove (openMenu);
+
+				for (int i = 0; i < index; i++)
+					pos += Menus [i].Title.Length + 2;
+				openMenu = new Menu (this, pos, 1, Menus [index]);
+				openCurrentMenu = openMenu;
+				openCurrentMenu.previousSubFocused = openMenu;
+
+				SuperView.Add (openMenu);
+				SuperView.SetFocus (openMenu);
+				break;
+			default:
+				if (openSubMenu == null)
+					openSubMenu = new List<Menu> ();
+				if (sIndex > -1) {
+					RemoveSubMenu (sIndex);
+				} else {
+					var last = openSubMenu.Count > 0 ? openSubMenu.Last () : openMenu;
+					openCurrentMenu = new Menu (this, last.Frame.Left + last.Frame.Width, last.Frame.Top + 1 + last.current, subMenu);
+					openCurrentMenu.previousSubFocused = last.previousSubFocused;
+					openSubMenu.Add (openCurrentMenu);
+					SuperView.Add (openCurrentMenu);
+				}
+				selectedSub = openSubMenu.Count - 1;
+				SuperView?.SetFocus (openCurrentMenu);
+				break;
+			}
+			isMenuOpening = false;
+			isMenuClosed = false;
+			MenuOpen = true;
+		}
+
+		// Starts the menu from a hotkey
+		void StartMenu ()
+		{
+			if (openMenu != null)
+				return;
+			selected = 0;
+			SetNeedsDisplay ();
+
+			previousFocused = SuperView.Focused;
+			OpenMenu (selected);
+			Application.GrabMouse (this);
+		}
+
+		// Activates the menu, handles either first focus, or activating an entry when it was already active
+		// For mouse events.
+		internal void Activate (int idx, int sIdx = -1, MenuBarItem subMenu = null)
+		{
+			selected = idx;
+			selectedSub = sIdx;
+			if (openMenu == null)
+				previousFocused = SuperView.Focused;
+
+			OpenMenu (idx, sIdx, subMenu);
+			SetNeedsDisplay ();
+		}
+
+		internal void CloseMenu (bool reopen = false, bool isSubMenu = false)
+		{
+			isMenuClosing = true;
+			OnCloseMenu?.Invoke (this, null);
+			switch (isSubMenu) {
+			case false:
+				if (openMenu != null)
+					SuperView.Remove (openMenu);
+				SetNeedsDisplay ();
+				if (previousFocused != null && openMenu != null && previousFocused.ToString () != openCurrentMenu.ToString ())
+					previousFocused?.SuperView?.SetFocus (previousFocused);
+				openMenu = null;
+				if (lastFocused is Menu) {
+					lastFocused = null;
+				}
+				LastFocused = lastFocused;
+				lastFocused = null;
+				if (LastFocused != null) {
+					if (!reopen)
+						selected = -1;
+					LastFocused.SuperView?.SetFocus (LastFocused);
+				} else {
+					SuperView.SetFocus (this);
+					isMenuClosed = true;
+					PositionCursor ();
+				}
+				isMenuClosed = true;
+				break;
+
+			case true:
+				selectedSub = -1;
+				SetNeedsDisplay ();
+				RemoveAllOpensSubMenus ();
+				openCurrentMenu.previousSubFocused?.SuperView?.SetFocus (openCurrentMenu.previousSubFocused);
+				openSubMenu = null;
+				break;
+			}
+			isMenuClosing = false;
+			MenuOpen = false;
+		}
+
+		void RemoveSubMenu (int index)
+		{
+			if (openSubMenu == null)
+				return;
+			for (int i = openSubMenu.Count - 1; i > index; i--) {
+				isMenuClosing = true;
+				if (openSubMenu.Count - 1 > 0)
+					SuperView.SetFocus (openSubMenu [i - 1]);
+				else
+					SuperView.SetFocus (openMenu);
+				if (openSubMenu != null) {
+					SuperView.Remove (openSubMenu [i]);
+					openSubMenu.Remove (openSubMenu [i]);
+				}
+				RemoveSubMenu (i);
+			}
+			if (openSubMenu.Count > 0)
+				openCurrentMenu = openSubMenu.Last ();
+
+			//if (openMenu.Subviews.Count == 0)
+			//	return;
+			//if (index == 0) {
+			//	//SuperView.SetFocus (previousSubFocused);
+			//	FocusPrev ();
+			//	return;
+			//}
+
+			//for (int i = openMenu.Subviews.Count - 1; i > index; i--) {
+			//	isMenuClosing = true;
+			//	if (openMenu.Subviews.Count - 1 > 0)
+			//		SuperView.SetFocus (openMenu.Subviews [i - 1]);
+			//	else
+			//		SuperView.SetFocus (openMenu);
+			//	if (openMenu != null) {
+			//		Remove (openMenu.Subviews [i]);
+			//		openMenu.Remove (openMenu.Subviews [i]);
+			//	}
+			//	RemoveSubMenu (i);
+			//}
+			isMenuClosing = false;
+		}
+
+		internal void RemoveAllOpensSubMenus ()
+		{
+			if (openSubMenu != null) {
+				foreach (var item in openSubMenu) {
+					SuperView.Remove (item);
+				}
+			}
+		}
+
+		internal void CloseAllMenus ()
+		{
+			if (!isMenuOpening && !isMenuClosing) {
+				if (openSubMenu != null)
+					CloseMenu (false, true);
+				CloseMenu ();
+				if (LastFocused != null && LastFocused != this)
+					selected = -1;
+			}
+			isMenuClosed = true;
+			openedByHotKey = false;
+		}
+
+		View FindDeepestMenu (View view, ref int count)
+		{
+			count = count > 0 ? count : 0;
+			foreach (var menu in view.Subviews) {
+				if (menu is Menu) {
+					count++;
+					return FindDeepestMenu ((Menu)menu, ref count);
+				}
+			}
+			return view;
+		}
+
+		internal void PreviousMenu (bool isSubMenu = false)
+		{
+			switch (isSubMenu) {
+			case false:
+				if (selected <= 0)
+					selected = Menus.Length - 1;
+				else
+					selected--;
+
+				if (selected > -1)
+					CloseMenu (true, false);
+				OpenMenu (selected);
+				break;
+			case true:
+				if (selectedSub > -1) {
+					selectedSub--;
+					RemoveSubMenu (selectedSub);
+					SetNeedsDisplay ();
+				} else
+					PreviousMenu ();
+
+				break;
+			}
+		}
+
+		internal void NextMenu (bool isSubMenu = false)
+		{
+			switch (isSubMenu) {
+			case false:
+				if (selected == -1)
+					selected = 0;
+				else if (selected + 1 == Menus.Length)
+					selected = 0;
+				else
+					selected++;
+
+				if (selected > -1)
+					CloseMenu (true);
+				OpenMenu (selected);
+				break;
+			case true:
+				if (UseKeysUpDownAsKeysLeftRight) {
+					CloseMenu (false, true);
+					NextMenu ();
+				} else {
+					if ((selectedSub == -1 || openSubMenu == null || openSubMenu?.Count == selectedSub) && openCurrentMenu.barItems.Children [openCurrentMenu.current].SubMenu == null) {
+						if (openSubMenu != null)
+							CloseMenu (false, true);
+						NextMenu ();
+					} else if (openCurrentMenu.barItems.Children [openCurrentMenu.current].SubMenu != null ||
+						!openCurrentMenu.barItems.Children [openCurrentMenu.current].IsFromSubMenu)
+						selectedSub++;
+					else
+						return;
+					SetNeedsDisplay ();
+					openCurrentMenu.CheckSubMenu ();
+				}
+				break;
+			}
+		}
+
+		bool openedByHotKey = false;
+		internal bool FindAndOpenMenuByHotkey (KeyEvent kb)
+		{
+			int pos = 0;
+			var c = ((uint)kb.Key & (uint)Key.CharMask);
+			for (int i = 0; i < Menus.Length; i++) {
+				// TODO: this code is duplicated, hotkey should be part of the MenuBarItem
+				var mi = Menus [i];
+				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);
+						}
+						return true;
+					}
+				}
+			}
+			return false;
+		}
+
+		public override bool ProcessHotKey (KeyEvent kb)
+		{
+			if (kb.Key == Key.F9) {
+				StartMenu ();
+				return true;
+			}
+
+			if (kb.IsAlt) {
+				if (FindAndOpenMenuByHotkey (kb)) return true;
+			}
+			var kc = kb.KeyValue;
+
+			return base.ProcessHotKey (kb);
+		}
+
+		public override bool ProcessKey (KeyEvent kb)
+		{
+			switch (kb.Key) {
+			case Key.CursorLeft:
+				selected--;
+				if (selected < 0)
+					selected = Menus.Length - 1;
+				break;
+			case Key.CursorRight:
+				selected = (selected + 1) % Menus.Length;
+				break;
+
+			case Key.Esc:
+			case Key.ControlC:
+				//TODO: Running = false;
+				CloseMenu ();
+				break;
+
+			default:
+				var key = kb.KeyValue;
+				if ((key >= 'a' && key <= 'z') || (key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) {
+					char c = Char.ToUpper ((char)key);
+
+					if (Menus [selected].IsTopLevel)
+						return false;
+
+					foreach (var mi in Menus [selected].Children) {
+						int p = mi.Title.IndexOf ('_');
+						if (p != -1 && p + 1 < mi.Title.Length) {
+							if (mi.Title [p + 1] == c) {
+								Selected (mi);
+								return true;
+							}
+						}
+					}
+				}
+
+				return false;
+			}
+			SetNeedsDisplay ();
+			return true;
+		}
+
+		public override bool MouseEvent (MouseEvent me)
+		{
+			if (!handled && !HandleGrabView (me, this)) {
+				return false;
+			}
+			handled = false;
+
+			if (me.Flags == MouseFlags.Button1Clicked ||
+				(me.Flags == 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.Button1Clicked && !isMenuClosed) {
+							Application.UngrabMouse ();
+							if (Menus [i].IsTopLevel) {
+								var menu = new Menu (this, i, 0, Menus [i]);
+								menu.Run (Menus [i].Action);
+							} else {
+								CloseMenu ();
+							}
+						} else if (me.Flags == MouseFlags.Button1Clicked && isMenuClosed) {
+							if (Menus [i].IsTopLevel) {
+								var menu = new Menu (this, i, 0, Menus [i]);
+								menu.Run (Menus [i].Action);
+							} else {
+								Activate (i);
+							}
+						} else if (selected != i && selected > -1 && me.Flags == MouseFlags.ReportMousePosition) {
+							if (!isMenuClosed) {
+								CloseMenu ();
+								Activate (i);
+							}
+						} else {
+							if (!isMenuClosed)
+								Activate (i);
+						}
+						return true;
+					}
+					pos += 2 + Menus [i].TitleLength + 1;
+				}
+			}
+			return false;
+		}
+
+		internal bool handled;
+
+		internal bool HandleGrabView (MouseEvent me, View current)
+		{
+			if (Application.mouseGrabView != null) {
+				if (me.View is MenuBar || me.View is Menu) {
+					if (me.View != current) {
+						Application.UngrabMouse ();
+						Application.GrabMouse (me.View);
+						me.View.MouseEvent (me);
+					}
+				} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
+					Application.UngrabMouse ();
+					CloseAllMenus ();
+					handled = false;
+					return false;
+				} else {
+					handled = false;
+					return false;
+				}
+			} else if (isMenuClosed && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
+				Application.GrabMouse (current);
+			} else {
+				handled = false;
+				return false;
+			}
+			//if (me.View != this && me.Flags != MouseFlags.Button1Clicked)
+			//	return true;
+			//else if (me.View != this && me.Flags == MouseFlags.Button1Clicked) {
+			//	Application.UngrabMouse ();
+			//	host.CloseAllMenus ();
+			//	return true;
+			//}
+
+
+			//if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Clicked)
+			//	return false;
+
+			//if (Application.mouseGrabView != null) {
+			//	if (me.View is MenuBar || me.View is Menu) {
+			//		me.X -= me.OfX;
+			//		me.Y -= me.OfY;
+			//		me.View.MouseEvent (me);
+			//		return true;
+			//	} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags == MouseFlags.Button1Clicked) {
+			//		Application.UngrabMouse ();
+			//		CloseAllMenus ();
+			//	}
+			//} else if (!isMenuClosed && selected == -1 && me.Flags == MouseFlags.Button1Clicked) {
+			//	Application.GrabMouse (this);
+			//	return true;
+			//}
+
+			//if (Application.mouseGrabView != null) {
+			//	if (Application.mouseGrabView == me.View && me.View == current) {
+			//		me.X -= me.OfX;
+			//		me.Y -= me.OfY;
+			//	} else if (me.View != current && me.View is MenuBar && me.View is Menu) {
+			//		Application.UngrabMouse ();
+			//		Application.GrabMouse (me.View);
+			//	} else if (me.Flags == MouseFlags.Button1Clicked) {
+			//		Application.UngrabMouse ();
+			//		CloseMenu ();
+			//	}
+			//} else if ((!isMenuClosed && selected > -1)) {
+			//	Application.GrabMouse (current);
+			//}
+
+			handled = true;
+
+			return true;
+		}
+	}
+
+}

+ 25 - 25
Terminal.Gui/Views/StatusBar.cs

@@ -6,27 +6,24 @@
 //
 //
 // TODO:
 // TODO:
 //   Add mouse support
 //   Add mouse support
-//   Uses internals of Application
 using System;
 using System;
 using NStack;
 using NStack;
 
 
-namespace Terminal.Gui
-{
+namespace Terminal.Gui {
 	/// <summary>
 	/// <summary>
 	/// A statusbar item has a title, a shortcut aka hotkey, and an action to execute on activation.
 	/// A statusbar item has a title, a shortcut aka hotkey, and an action to execute on activation.
 	/// Such an item is ment to be as part of the global hotkeys of the application, which are available in the current context of the screen.
 	/// Such an item is ment to be as part of the global hotkeys of the application, which are available in the current context of the screen.
 	/// The colour of the text will be changed after each ~. Having an statusbar item with a text of `~F1~ Help` will draw *F1* as shortcut and 
 	/// The colour of the text will be changed after each ~. Having an statusbar item with a text of `~F1~ Help` will draw *F1* as shortcut and 
 	/// *Help* as standard text.
 	/// *Help* as standard text.
 	/// </summary>
 	/// </summary>
-	public class StatusItem
-	{
+	public class StatusItem {
 		/// <summary>
 		/// <summary>
 		/// Initializes a new <see cref="T:Terminal.Gui.StatusItem"/>.
 		/// Initializes a new <see cref="T:Terminal.Gui.StatusItem"/>.
 		/// </summary>
 		/// </summary>
 		/// <param name="shortcut">Shortcut to activate the item.</param>
 		/// <param name="shortcut">Shortcut to activate the item.</param>
 		/// <param name="title">Title for the statusbar item.</param>
 		/// <param name="title">Title for the statusbar item.</param>
 		/// <param name="action">Action to invoke when the staturbar item is activated.</param>
 		/// <param name="action">Action to invoke when the staturbar item is activated.</param>
-		public StatusItem(Key shortcut, ustring title, Action action) 
+		public StatusItem (Key shortcut, ustring title, Action action)
 		{
 		{
 			Title = title ?? "";
 			Title = title ?? "";
 			Shortcut = shortcut;
 			Shortcut = shortcut;
@@ -57,8 +54,7 @@ namespace Terminal.Gui
 	/// be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. 
 	/// be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog to ask a file to load is executed, the remaining commands will probably be ~F1~ Help. 
 	/// So for each context must be a new instance of a statusbar.  
 	/// So for each context must be a new instance of a statusbar.  
 	/// </summary>
 	/// </summary>
-	public class StatusBar : View
-	{
+	public class StatusBar : View {
 		public StatusItem [] Items { get; set; }
 		public StatusItem [] Items { get; set; }
 
 
 		/// <summary>
 		/// <summary>
@@ -66,25 +62,29 @@ namespace Terminal.Gui
 		/// It will be drawn in the lowest column of the terminal.
 		/// It will be drawn in the lowest column of the terminal.
 		/// </summary>
 		/// </summary>
 		/// <param name="items">A list of statusbar items.</param>
 		/// <param name="items">A list of statusbar items.</param>
-		public StatusBar(StatusItem [] items) : base()
+		public StatusBar (StatusItem [] items) : base ()
 		{
 		{
-			X = 0;
-			Y = Application.Driver.Rows - 1; // TODO: using internals of Application
 			Width = Dim.Fill ();
 			Width = Dim.Fill ();
 			Height = 1;
 			Height = 1;
 			Items = items;
 			Items = items;
 			CanFocus = false;
 			CanFocus = false;
 			ColorScheme = Colors.Menu;
 			ColorScheme = Colors.Menu;
+
+			Application.OnResized += () => {
+				this.X = Pos.Left (Application.Top);
+				this.Y = Pos.Bottom (Application.Top);
+			};
 		}
 		}
 
 
-		Attribute ToggleScheme(Attribute scheme)
+		Attribute ToggleScheme (Attribute scheme)
 		{
 		{
-			var result = scheme==ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
-			Driver.SetAttribute(result);
+			var result = scheme == ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
+			Driver.SetAttribute (result);
 			return result;
 			return result;
 		}
 		}
 
 
-		public override void Redraw(Rect region) {
+		public override void Redraw (Rect region)
+		{
 			if (Frame.Y != Driver.Rows - 1) {
 			if (Frame.Y != Driver.Rows - 1) {
 				Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);
 				Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);
 				Y = Driver.Rows - 1;
 				Y = Driver.Rows - 1;
@@ -98,15 +98,15 @@ namespace Terminal.Gui
 
 
 			Move (1, 0);
 			Move (1, 0);
 			var scheme = ColorScheme.Normal;
 			var scheme = ColorScheme.Normal;
-			Driver.SetAttribute(scheme);
-			for(int i=0; i<Items.Length; i++) {
-				var title = Items[i].Title;
-				for(int n=0; n<title.Length; n++) {
-					if(title[n]=='~') {
-						scheme = ToggleScheme(scheme);
+			Driver.SetAttribute (scheme);
+			for (int i = 0; i < Items.Length; i++) {
+				var title = Items [i].Title;
+				for (int n = 0; n < title.Length; n++) {
+					if (title [n] == '~') {
+						scheme = ToggleScheme (scheme);
 						continue;
 						continue;
 					}
 					}
-					Driver.AddRune(title[n]);
+					Driver.AddRune (title [n]);
 				}
 				}
 				Driver.AddRune (' ');
 				Driver.AddRune (' ');
 			}
 			}
@@ -114,9 +114,9 @@ namespace Terminal.Gui
 
 
 		public override bool ProcessHotKey (KeyEvent kb)
 		public override bool ProcessHotKey (KeyEvent kb)
 		{
 		{
-			foreach(var item in Items) {
-				if(kb.Key==item.Shortcut) {
-					if( item.Action!=null ) item.Action();
+			foreach (var item in Items) {
+				if (kb.Key == item.Shortcut) {
+					if (item.Action != null) item.Action ();
 					return true;
 					return true;
 				}
 				}
 			}
 			}