浏览代码

All my pull-request at once (#345)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Fixes culture info of DataField from pr #250

* Fixes the rectangle drawing issue

* Fixes #290 issue "Redraw issue when setting coordinates of label"

* Added sub menus into menu bar with mouse and key navigation

* Needed to assume color for the Disable attribute

* Added Colors.Menu.Disabled to CursesDriver.cs

* Mouse text selection with cut, copy and paste on text fields

* Change sepChar from char to string in DateField

* Adding a disabled menu item in the demo file

* Adding a disabled menu item in the demo file

* Fixes Button repainting issue when changing the text length to one smaller

* Fixes #290 issue "Redraw issue when setting coordinates of label"

* Only demonstration of issue # 308 that even though the cursor is gray on a gray background can be viewed.

* Fixes issue #163 "ScrollView does not render some content"

* Fixed bug in Button that caused a loop redraw calling TerminalResized

* Fixes #282 "Repaint Issue"

* Removed white space

* Mouse features added to FileDialog including wheel support.

* Forget to delete this commented method.

* Changing back to MouseFlags.AllEvents in case some mouse event is not triggering.

* Add documentation on ISupportInitialize/ISupportInitializeNotification (#286)

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Fixed key events traversal for modal dialogs (#288)

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Add documentation on ISupportInitialize/ISupportInitializeNotification (#286)

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Fixed key events traversal for modal dialogs (#288)

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Added sub menus into menu bar with mouse and key navigation

* Fetch from upstream/master

* Fetch from upstream/master

* Fetch from upstream/master

* Fetch from upstream/master

* Fetch from upstream/master

* Add documentation on ISupportInitialize/ISupportInitializeNotification (#286)

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Fixed key events traversal for modal dialogs (#288)

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Fetch from upstream/master

* Fetch from upstream/master

* Fetch from upstream/master

* Add documentation on ISupportInitialize/ISupportInitializeNotification (#286)

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Fixed key events traversal for modal dialogs (#288)

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Fetch from upstream/master

* Switch netcoreapp target to netstandard2.0 (#284)

Fixes #283 

Instead of adding another target framework to the list, switch from netcoreapp2.0 to netstandard2.0, as ns2.0 is a subset of netcoreapp.

* Added TextView.TextChanged event (#264)

* Prepare for 0.25

* Remove travis link

* Revert Daniel's change 00c5997daaa40f0ee73c8b7d7eccf7b4dfd04194 as it prevents the solution from building on Mac

* Prepare for 0.26

* Restore some files that were deleted by Daniel's commit that I had not restored

* Fixed out of range exception and text redraw when navigate backward (#320)

* Typo fix (#321)

* Fixes issue #306 async/await hang (#312)

* Fixed async/await hang

* Fixed async/await hang with calling Wakeup

* Moved Wake Up into lock statement

* Support menu items that are null so they can be drawn as a menu separator (#304)

* Fixed and Enabled Library reinitialization (#291)

- Replaced static driver initialization with property getter for reference passing in Core.cs::View class, this allows the library to be reinitialized at any time.
- Made the Shutdown method on Core.cs::Application class public, since there is no reason to keep it private. Applications can shutdown the library and revert the console to the initial stage by calling it.
- Fixed a memory-leak on Drivers/WindowsDriver class by destroying the generated screen buffers at library shutdown by calling CloseHandle.
- Minor change to Core.cs::Application.Init(Func<Toplevel>) for better initialization status tracking, via backend property instead of relying on the Top field.

* Moved `ListView.ListWrapper` out of `ListView` migueldeicaza/gui.cs#313` (#315)

* Resizing the MessageBox width to accommodate all message text (#299)

* Fixed key events traversal for modal dialogs

The key must be propagated initially to the first chain element
before evaluating if we should stop because of the handled or
the Modal condition. Otherwise the modal dialog
won't get any process key notification.

This fixes https://github.com/migueldeicaza/gui.cs/commit/c072e29a684068af50e1b9e284213b3839dad804

* Resizing the MessageBox width to accommodate all message text

Co-authored-by: Adrian Alonso <[email protected]>

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* extract methods on ListView to make it controlable from other controls (#310)

* moveup

* MoveDown

* MovePageDown

* MovePageUp

* MarkUnmarkRow

* Allowing list items selection (#302)

* Fetch from upstream/master

* Fixes #342 and improves color change interaction.
Usage:
Colors.Base.Normal = new Terminal.Gui.Attribute (Color.Green, Color.Black);

* Inserted new line at the end  of file .
Changed method name to SetAttribute in the  ColorScheme class.

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Prepare for 0.70

* Timefield format with bounds values (#303)

* Implemented lower and upper bounds to TimeField

* Passing old text to the Changed event handler

* Change sepChar from char to string in TimeField

* Changing comparison from ':' to sepChar.ToCharArray () [0]

* Prepare for 0.70

* Removed duplicated Attribute Disabled property

* Fixed some bugs with the mouse event and text selection, copy, cut and paste. There is still a random failure in the mouse events that lock on button released and only trigger button clicked after moving the mouse.

* Failure behavior solved. It was a threading safe issue. Driver.Wakeup () moved to the Post method on MainLoopSyncContext class solved it.

* Changed the default for RightmostButtonPressed to Button4 and enabled  clicked-drag

* Added support for Button Triple Clicked too.  FileDialog changed to deal with ButtonClicked.

* Fixed a bug with the timer when dragging.

* Fixes #343 - Added AllowsMultipleSelection to the ListView

* Fixes #346 issue with enhancers characters, but it only could be apply after the pull requests at NStack are merged because of the Rune.ColumnWidth error.

* Fixes code format.

* Enabled Button Pressed with ReportMousePosition simultaneously.

* Dragging is already working. TODO: optimize, only SetNeedsDisplay on the before/after regions.

* Fixes the extra characters that remains in case the new text length is smaller than the older.

* Fixes #349 TextField user typed input no longer fires Changed event.

* Includes ControlKeyState for all the buttons events.

* Added SetSourceAsync to ListView

* Menu enhancement that works well, even if the top level has no other views. Working in further feature that if clicked outside of the menu it will closed.

* Some more features in mouse and in core.

* Added more mouse events flags, drag features, toplevel color and more...

* Remove unnecessary SetNeedsDisplay.

* Fixes a bug in the label

* Added StatusBar from pr #201 with a  little change.

* Added features to TextField like mouse selection with copy, cut and paste shortcut keys. Now it's possible to use the combination of the Alt+Control+delta keys. It also be possible use special characters like €.

* Simplifying the menu with better performance.

* Private keyword dropped in all files and added some documentation.

* Changed demo to reflect the added and changes features.

* Added csproj and config files to verify if it won't trigger errors from Travis.

* Demo with the StatusBar.

* Patch for position of the StatusBar

* Removed unnecessary nugget packages.

* It looks like packages.config files are obsolete. Travis verification test.

* Update Designer.csproj

Use Stack 0.14

* Use NStack 0.14

* Use NStack 0.14

* Use NStack 0.14

* Use NStack 0.14

* Changed the NStack.Core and System.ValueTuple versions.

* Added System.ValueTuple to Example project.

* Remove System.ValueTuple and added NETStandard.Library to Example project.

* Try to restore the nuget packages.

* Revert "Try to restore the nuget packages."

This reverts commit 3957e022c391ac3d3ebbbb1dc5f6262d3d2a23d5.

* Added NETStandard.Library ti the root packages.config

* Upgrade to "Microsoft.NETCore.Platforms" version="
2.0.1"

* Added <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>

* Targeting framework 472.

* Removed "System.ValueTuple" Version="4.5.0" from Terminal.Gui project.

* More cleaning to the projects.

* I guess you don't need this.

Co-authored-by: Adrian Alonso <[email protected]>
Co-authored-by: Daniel Cazzulino <[email protected]>
Co-authored-by: Marius Ungureanu <[email protected]>
Co-authored-by: miguel <[email protected]>
Co-authored-by: Miguel de Icaza <[email protected]>
Co-authored-by: imaras <[email protected]>
Co-authored-by: Kasper B. Graversen <[email protected]>
Co-authored-by: Fabian R <[email protected]>
Co-authored-by: Timothy <[email protected]>
BDisp 5 年之前
父节点
当前提交
49cd29853f

+ 3 - 0
.editorconfig

@@ -17,3 +17,6 @@ csharp_preserve_single_line_blocks = true
 dotnet_style_require_accessibility_modifiers = never
 csharp_style_var_when_type_is_apparent = true
 csharp_prefer_braces = false
+csharp_space_before_open_square_brackets = true
+csharp_space_between_method_call_name_and_opening_parenthesis = true
+csharp_space_between_method_declaration_name_and_open_parenthesis = true

+ 2 - 1
.gitignore

@@ -4,4 +4,5 @@ obj
 *.userprefs
 *~
 packages
-.vs
+.vs
+*.csproj.user

+ 5 - 9
Designer/Designer.csproj

@@ -7,7 +7,7 @@
     <OutputType>Exe</OutputType>
     <RootNamespace>Designer</RootNamespace>
     <AssemblyName>Designer</AssemblyName>
-    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
     <TargetFrameworkProfile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
@@ -30,13 +30,10 @@
     <PlatformTarget>x86</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="NStack">
-      <HintPath>..\packages\NStack.Core.0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
-    </Reference>
-    <Reference Include="NStack">
-      <HintPath>..\packages\NStack.Core.0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
+    <Reference Include="NStack, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\NStack.Core.0.14.0\lib\netstandard2.0\NStack.dll</HintPath>
     </Reference>
+    <Reference Include="System" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="Program.cs" />
@@ -48,8 +45,7 @@
     </ProjectReference>
   </ItemGroup>
   <ItemGroup>
-    <None Include="app.config" />
     <None Include="packages.config" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-</Project>
+</Project>

+ 28 - 2
Designer/Program.cs

@@ -1,5 +1,6 @@
 using System;
 using Terminal.Gui;
+using Attribute = Terminal.Gui.Attribute;
 
 namespace Designer {
 	class Surface : Window {
@@ -9,12 +10,18 @@ namespace Designer {
 	}
 
 	class MainClass {
+		static void Close ()
+		{
+			MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
+		}
+
 		public static void Main (string [] args)
 		{
 			Application.Init ();
 
 			var menu = new MenuBar (new MenuBarItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
+					new MenuItem ("_Close", "", () => Close ()),
 					new MenuItem ("_Quit", "", () => { Application.RequestStop (); })
 				}),
 				new MenuBarItem ("_Edit", new MenuItem [] {
@@ -37,8 +44,27 @@ namespace Designer {
 				Height = Dim.Fill ()
 			};
 
-			//Application.Top.Add (menu);
-			Application.Top.Add (login, password);
+			var loginText = new TextField("") {
+				X = Pos.Right(password),
+				Y = Pos.Top(login),
+				Width = 40,
+				ColorScheme = new ColorScheme() {
+					Focus = Attribute.Make(Color.BrightYellow, Color.DarkGray),
+					Normal = Attribute.Make(Color.Green, Color.BrightYellow),
+					HotFocus = Attribute.Make(Color.BrightBlue, Color.Brown),
+					HotNormal = Attribute.Make(Color.Red, Color.BrightRed),
+				},
+			};
+
+			var passText = new TextField ("") {
+				Secret = true,
+				X = Pos.Left (loginText),
+				Y = Pos.Top (password),
+				Width = Dim.Width (loginText)
+			};
+
+			surface.Add (login, password, loginText, passText);
+			Application.Top.Add (menu, surface);
 			Application.Run ();
 		}
 	}

+ 0 - 3
Designer/app.config

@@ -1,3 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<configuration>
-<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1"/></startup></configuration>

+ 3 - 3
Designer/packages.config

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="NStack.Core" version="0.11.0" targetFramework="net461" />
-</packages>
+  <package id="NStack.Core" version="0.14.0" targetFramework="net472" />
+</packages>

+ 9 - 10
Example/Example.csproj

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <PropertyGroup>
     <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
@@ -7,7 +7,10 @@
     <OutputType>Exe</OutputType>
     <RootNamespace>Terminal</RootNamespace>
     <AssemblyName>Terminal</AssemblyName>
-    <TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
+    <TargetFrameworkVersion>v4.7.2</TargetFrameworkVersion>
+    <NuGetPackageImportStamp>
+    </NuGetPackageImportStamp>
+    <TargetFrameworkProfile />
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
     <DebugSymbols>true</DebugSymbols>
@@ -30,14 +33,10 @@
     <PlatformTarget>x86</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
-    <Reference Include="System" />
-    <Reference Include="NStack">
-      <HintPath>..\packages\NStack.Core.0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
-      <Private>False</Private>
-    </Reference>
-    <Reference Include="NStack">
-      <HintPath>..\packages\NStack.Core.0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
+    <Reference Include="NStack, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
+      <HintPath>..\packages\NStack.Core.0.14.0\lib\netstandard2.0\NStack.dll</HintPath>
     </Reference>
+    <Reference Include="System" />
   </ItemGroup>
   <ItemGroup>
     <Compile Include="demo.cs" />
@@ -52,4 +51,4 @@
     <None Include="packages.config" />
   </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
-</Project>
+</Project>

+ 223 - 56
Example/demo.cs

@@ -1,28 +1,49 @@
 using Terminal.Gui;
 using System;
 using Mono.Terminal;
-using System.Collections;
 using System.Collections.Generic;
-
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using NStack;
 
 static class Demo {
+	//class Box10x : View, IScrollView {
 	class Box10x : View {
-		public Box10x (int x, int y) : base (new Rect (x, y, 10, 10))
+		int w = 40;
+		int h = 50;
+
+		public bool WantCursorPosition { get; set; } = false;
+
+		public Box10x (int x, int y) : base (new Rect (x, y, 20, 10))
+		{
+		}
+
+		public Size GetContentSize ()
 		{
+			return new Size (w, h);
+		}
+
+		public void SetCursorPosition (Point pos)
+		{
+			throw new NotImplementedException ();
 		}
 
 		public override void Redraw (Rect region)
 		{
+			//Point pos = new Point (region.X, region.Y);
 			Driver.SetAttribute (ColorScheme.Focus);
 
-			for (int y = 0; y < 10; y++) {
+			for (int y = 0; y < h; y++) {
 				Move (0, y);
-				for (int x = 0; x < 10; x++) {
-
-					Driver.AddRune ((Rune)('0' + (x + y) % 10));
+				Driver.AddStr (y.ToString ());
+				for (int x = 0; x < w - y.ToString ().Length; x++) {
+					//Driver.AddRune ((Rune)('0' + (x + y) % 10));
+					if (y.ToString ().Length < w)
+						Driver.AddStr (" ");
 				}
 			}
-
+			//Move (pos.X, pos.Y);
 		}
 	}
 
@@ -42,6 +63,9 @@ static class Demo {
 					Rune r;
 					switch (x % 3) {
 					case 0:
+						Driver.AddRune (y.ToString ().ToCharArray (0, 1) [0]);
+						if (y > 9)
+							Driver.AddRune (y.ToString ().ToCharArray (1, 1) [0]);
 						r = '.';
 						break;
 					case 1:
@@ -60,24 +84,30 @@ static class Demo {
 
 	static void ShowTextAlignments (View container)
 	{
+		int i = 0;
+		string txt = "Hello world, how are you doing today";
 		container.Add (
-			new Label (new Rect (0, 0, 40, 3), "1-Hello world, how are you doing today") { TextAlignment = TextAlignment.Left },
-			new Label (new Rect (0, 4, 40, 3), "2-Hello world, how are you doing today") { TextAlignment = TextAlignment.Right },
-			new Label (new Rect (0, 8, 40, 3), "3-Hello world, how are you doing today") { TextAlignment = TextAlignment.Centered },
-			new Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified });
+			new FrameView (new Rect (75, 3, txt.Length + 6, 20), "Text Alignments") {
+				new Label(new Rect(0, 1, 40, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
+				new Label(new Rect(0, 5, 40, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
+				new Label(new Rect(0, 9, 40, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
+				new Label(new Rect(0, 13, 40, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
+			});
 	}
 
 	static void ShowEntries (View container)
 	{
 		var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) {
-			ContentSize = new Size (100, 100),
-			ContentOffset = new Point (-1, -1),
+			ContentSize = new Size (20, 50),
+			//ContentOffset = new Point (0, 0),
 			ShowVerticalScrollIndicator = true,
 			ShowHorizontalScrollIndicator = true
 		};
-
+#if false
 		scrollView.Add (new Box10x (0, 0));
-		//scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
+#else
+		scrollView.Add (new Filler (new Rect (0, 0, 40, 40)));
+#endif
 
 		// This is just to debug the visuals of the scrollview when small
 		var scrollView2 = new ScrollView (new Rect (72, 10, 3, 3)) {
@@ -93,7 +123,7 @@ static class Demo {
 			return true;
 		}
 
-		//Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+		Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
 
 
 		// A little convoluted, this is because I am using this to test the
@@ -117,6 +147,7 @@ static class Demo {
 			Width = Dim.Width (loginText)
 		};
 
+		var tf = new Button (3, 19, "Ok");
 		// Add some content
 		container.Add (
 			login,
@@ -127,7 +158,7 @@ static class Demo {
 				new CheckBox (1, 0, "Remember me"),
 				new RadioGroup (1, 2, new [] { "_Personal", "_Company" }),
 			},
-			new ListView (new Rect (60, 6, 16, 4), new string [] {
+			new ListView (new Rect (59, 6, 16, 4), new string [] {
 				"First row",
 				"<>",
 				"This is a very long row that should overflow what is shown",
@@ -137,16 +168,20 @@ static class Demo {
 				"This is so cool"
 			}),
 			scrollView,
-			//scrollView2,
-			new Button (3, 19, "Ok"),
+			scrollView2,
+			tf,
 			new Button (10, 19, "Cancel"),
 			new TimeField (3, 20, DateTime.Now),
 			new TimeField (23, 20, DateTime.Now, true),
+			new DateField (3, 22, DateTime.Now),
+			new DateField (23, 22, DateTime.Now, true),
 			progress,
-			new Label (3, 22, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar")
+			new Label (3, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"),
+			menuKeysStyle,
+			menuAutoMouseNav
 
 		);
-
+		container.SendSubviewToBack (tf);
 	}
 
 	public static Label ml2;
@@ -162,12 +197,13 @@ static class Demo {
 		Application.Run (d);
 	}
 
-	// 
+	//
 	// Creates a nested editor
-	static void Editor(Toplevel top) {
+	static void Editor (Toplevel top)
+	{
 		var tframe = top.Frame;
-		var ntop = new Toplevel(tframe);
-		var menu = new MenuBar(new MenuBarItem[] {
+		var ntop = new Toplevel (tframe);
+		var menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
 				new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
 			}),
@@ -177,25 +213,25 @@ static class Demo {
 				new MenuItem ("_Paste", "", null)
 			}),
 		});
-		ntop.Add(menu);
+		ntop.Add (menu);
 
 		string fname = null;
-		foreach (var s in new[] { "/etc/passwd", "c:\\windows\\win.ini" })
-			if (System.IO.File.Exists(s)) {
+		foreach (var s in new [] { "/etc/passwd", "c:\\windows\\win.ini" })
+			if (System.IO.File.Exists (s)) {
 				fname = s;
 				break;
 			}
 
-		var win = new Window(fname ?? "Untitled") {
+		var win = new Window (fname ?? "Untitled") {
 			X = 0,
 			Y = 1,
-			Width = Dim.Fill(),
-			Height = Dim.Fill()
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
 		};
-		ntop.Add(win);
+		ntop.Add (win);
+
+		var text = new TextView (new Rect (0, 0, tframe.Width - 2, tframe.Height - 3));
 
-		var text = new TextView(new Rect(0, 0, tframe.Width - 2, tframe.Height - 3));
-		
 		if (fname != null)
 			text.Text = System.IO.File.ReadAllText (fname);
 		win.Add (text);
@@ -211,7 +247,7 @@ static class Demo {
 
 	static void Close ()
 	{
-		MessageBox.ErrorQuery (50, 5, "Error", "There is nothing to close", "Ok");
+		MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok");
 	}
 
 	// Watch what happens when I try to introduce a newline after the first open brace
@@ -220,10 +256,11 @@ static class Demo {
 
 	public static void Open ()
 	{
-		var d = new OpenDialog ("Open", "Open a file");
+		var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = true };
 		Application.Run (d);
 
-		MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok");
+		if (!d.Canceled)
+			MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok");
 	}
 
 	public static void ShowHex (Toplevel top)
@@ -254,7 +291,76 @@ static class Demo {
 		};
 		win.Add (hex);
 		Application.Run (ntop);
-			
+
+	}
+
+	public class MenuItemDetails : MenuItem {
+		ustring title;
+		string help;
+		Action action;
+
+		public MenuItemDetails (ustring title, string help, Action action) : base (title, help, action)
+		{
+			this.title = title;
+			this.help = help;
+			this.action = action;
+		}
+
+		public static MenuItemDetails Instance (MenuItem mi)
+		{
+			return (MenuItemDetails)mi.GetMenuItem ();
+		}
+	}
+
+	public delegate MenuItem MenuItemDelegate (MenuItemDetails menuItem);
+
+	public static void ShowMenuItem (MenuItem mi)
+	{
+		BindingFlags flags = BindingFlags.Public | BindingFlags.Static;
+		MethodInfo minfo = typeof (MenuItemDetails).GetMethod ("Instance", flags);
+		MenuItemDelegate mid = (MenuItemDelegate)Delegate.CreateDelegate (typeof (MenuItemDelegate), minfo);
+		MessageBox.Query (70, 7, mi.Title.ToString (),
+			$"{mi.Title.ToString ()} selected. Is from submenu: {mi.GetMenuBarItem ()}", "Ok");
+	}
+
+	static void MenuKeysStyle_Toggled (object sender, EventArgs e)
+	{
+		menu.UseKeysUpDownAsKeysLeftRight = menuKeysStyle.Checked;
+	}
+
+	static void MenuAutoMouseNav_Toggled (object sender, EventArgs e)
+	{
+		menu.WantMousePositionReports = menuAutoMouseNav.Checked;
+	}
+
+
+	static void Copy ()
+	{
+		TextField textField = menu.LastFocused as TextField;
+		if (textField != null && textField.SelectedLength != 0) {
+			textField.Copy ();
+		}
+	}
+
+	static void Cut ()
+	{
+		TextField textField = menu.LastFocused as TextField;
+		if (textField != null && textField.SelectedLength != 0) {
+			textField.Cut ();
+		}
+	}
+
+	static void Paste ()
+	{
+		TextField textField = menu.LastFocused as TextField;
+		if (textField != null) {
+			textField.Paste ();
+		}
+	}
+
+	static void Help ()
+	{
+		MessageBox.Query (50, 7, "Help", "This is a small help\nBe kind.", "Ok");
 	}
 
 	#region Selection Demo
@@ -278,7 +384,8 @@ static class Demo {
 			Y = 3,
 			Width = Dim.Fill () - 4,
 			Height = Dim.Fill () - 4,
-			AllowsMarking = true
+			AllowsMarking = true,
+			AllowsMultipleSelection = false
 		};
 		d.Add (msg, list);
 		Application.Run (d);
@@ -295,60 +402,120 @@ static class Demo {
 
 
 	public static Label ml;
+	public static MenuBar menu;
+	public static CheckBox menuKeysStyle;
+	public static CheckBox menuAutoMouseNav;
 	static void Main ()
 	{
+		if (Debugger.IsAttached)
+			CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
+
 		//Application.UseSystemConsole = true;
 		Application.Init ();
-		
+
 		var top = Application.Top;
-		
-		var tframe = top.Frame;
+
 		//Open ();
 #if true
 		var win = new Window ("Hello") {
 			X = 0,
-			Y = 1,
+			Y = 0,
 			Width = Dim.Fill (),
 			Height = Dim.Fill ()
 		};
 #else
+		var tframe = top.Frame;
+
 		var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height - 1), "Hello");
 #endif
-		var menu = new MenuBar (new MenuBarItem [] {
+		MenuItemDetails [] menuItems = {
+			new MenuItemDetails ("F_ind", "", null),
+			new MenuItemDetails ("_Replace", "", null),
+			new MenuItemDetails ("_Item1", "", null),
+			new MenuItemDetails ("_Not From Sub Menu", "", null)
+		};
+
+		menuItems [0].Action = () => ShowMenuItem (menuItems [0]);
+		menuItems [1].Action = () => ShowMenuItem (menuItems [1]);
+		menuItems [2].Action = () => ShowMenuItem (menuItems [2]);
+		menuItems [3].Action = () => ShowMenuItem (menuItems [3]);
+
+		menu = new MenuBar (new MenuBarItem [] {
 			new MenuBarItem ("_File", new MenuItem [] {
 				new MenuItem ("Text Editor Demo", "", () => { Editor (top); }),
 				new MenuItem ("_New", "Creates new file", NewFile),
 				new MenuItem ("_Open", "", Open),
 				new MenuItem ("_Hex", "", () => ShowHex (top)),
 				new MenuItem ("_Close", "", () => Close ()),
+				new MenuItem ("_Disabled", "", () => { }, () => false),
+				null,
 				new MenuItem ("_Quit", "", () => { if (Quit ()) top.Running = false; })
 			}),
 			new MenuBarItem ("_Edit", new MenuItem [] {
-				new MenuItem ("_Copy", "", null),
-				new MenuItem ("C_ut", "", null),
-				new MenuItem ("_Paste", "", null)
+				new MenuItem ("_Copy", "", Copy),
+				new MenuItem ("C_ut", "", Cut),
+				new MenuItem ("_Paste", "", Paste),
+				new MenuItem ("_Find and Replace",
+					new MenuBarItem (new MenuItem[] {menuItems [0], menuItems [1] })),
+				menuItems[3]
 			}),
-			 new MenuBarItem ("_List Demos", new MenuItem [] {
+			new MenuBarItem ("_List Demos", new MenuItem [] {
 				new MenuItem ("Select Items", "", ListSelectionDemo),
 			}),
+			new MenuBarItem ("Test Menu and SubMenus", new MenuItem [] {
+				new MenuItem ("SubMenu1Item1",
+					new MenuBarItem (new MenuItem[] {
+						new MenuItem ("SubMenu2Item1",
+							new MenuBarItem (new MenuItem [] {
+								new MenuItem ("SubMenu3Item1",
+									new MenuBarItem (new MenuItem [] { menuItems [2] })
+								)
+							})
+						)
+					})
+				)
+			}),
 		});
 
+		menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
+		menuKeysStyle.Toggled += MenuKeysStyle_Toggled;
+		menuAutoMouseNav = new CheckBox (40, 25, "UseMenuAutoNavigation", true);
+		menuAutoMouseNav.Toggled += MenuAutoMouseNav_Toggled;
+
 		ShowEntries (win);
 
 		int count = 0;
-		ml = new Label (new Rect (3, 17, 47, 1), "Mouse: ");
+		ml = new Label (new Rect (3, 18, 47, 1), "Mouse: ");
 		Application.RootMouseEvent += delegate (MouseEvent me) {
-
+			ml.TextColor = Colors.TopLevel.Normal;
 			ml.Text = $"Mouse: ({me.X},{me.Y}) - {me.Flags} {count++}";
 		};
-		
+
 		var test = new Label (3, 18, "Se iniciará el análisis");
 		win.Add (test);
 		win.Add (ml);
-		
-		// ShowTextAlignments (win);
+
+		ShowTextAlignments (win);
+
+		var drag = new Label ("Drag: ") { X = 70, Y = 24 };
+		var dragText = new TextField ("") {
+			X = Pos.Right (drag),
+			Y = Pos.Top (drag),
+			Width = 40
+		};
+
+		var statusBar = new StatusBar (new StatusItem [] {
+			new StatusItem(Key.F1, "~F1~ Help", () => Help()),
+			new StatusItem(Key.F2, "~F2~ Load", null),
+			new StatusItem(Key.F3, "~F3~ Save", null),
+			new StatusItem(Key.ControlX, "~^X~ Quit", () => Quit()),
+		});
+
+		win.Add (drag, dragText);
+
 		top.Add (win);
-		top.Add (menu);
+		//top.Add (menu);
+		top.Add (menu, statusBar, ml);
 		Application.Run ();
 	}
-}
+}

+ 3 - 3
Example/packages.config

@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="NStack.Core" version="0.11.0" targetFramework="net461" />
-</packages>
+  <package id="NStack.Core" version="0.14.0" targetFramework="net472" />
+</packages>

+ 190 - 54
Terminal.Gui/Core.cs

@@ -134,8 +134,8 @@ namespace Terminal.Gui {
 
 	/// <summary>
 	/// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the
-	/// value from the Frame will be used, if the value is Computer, then the Frame 
-	/// will be updated from the X, Y Pos objets and the Width and Heigh Dim objects.
+	/// value from the Frame will be used, if the value is Computer, then the Frame
+	/// will be updated from the X, Y Pos objects and the Width and Height Dim objects.
 	/// </summary>
 	public enum LayoutStyle {
 		/// <summary>
@@ -229,6 +229,16 @@ namespace Terminal.Gui {
 		View focused = null;
 		Direction focusDirection;
 
+		/// <summary>
+		/// Event fired when the view get focus.
+		/// </summary>
+		public event EventHandler OnEnter;
+
+		/// <summary>
+		/// Event fired when the view lost focus.
+		/// </summary>
+		public event EventHandler OnLeave;
+
 		internal Direction FocusDirection {
 			get => SuperView?.FocusDirection ?? focusDirection;
 			set {
@@ -430,7 +440,7 @@ namespace Terminal.Gui {
 			SetNeedsDisplay (Bounds);
 		}
 
-		bool layoutNeeded = true;
+		internal bool layoutNeeded = true;
 
 		internal void SetNeedsLayout ()
 		{
@@ -439,7 +449,7 @@ namespace Terminal.Gui {
 			layoutNeeded = true;
 			if (SuperView == null)
 				return;
-			SuperView.layoutNeeded = true;
+			SuperView.SetNeedsLayout ();
 		}
 
 		/// <summary>
@@ -649,14 +659,14 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		///   Clears the specfied rectangular region with the current color
+		///   Clears the specified rectangular region with the current color
 		/// </summary>
 		public void Clear (Rect r)
 		{
 			var h = r.Height;
 			var w = r.Width;
-			for (int line = 0; line < h; line++) {
-				Move (0, line);
+			for (int line = r.Y; line < r.Y + h; line++) {
+				Driver.Move (r.X, line);
 				for (int col = 0; col < w; col++)
 					Driver.AddRune (' ');
 			}
@@ -826,11 +836,16 @@ namespace Terminal.Gui {
 			}
 			internal set {
 				if (base.HasFocus != value)
+					if (value == true)
+						OnEnter?.Invoke (this, new EventArgs ());
+					else
+						OnLeave?.Invoke (this, new EventArgs ());
 					SetNeedsDisplay ();
 				base.HasFocus = value;
 
 				// Remove focus down the chain of subviews if focus is removed
 				if (value == false && focused != null) {
+					OnLeave?.Invoke (focused, new EventArgs ());
 					focused.HasFocus = false;
 					focused = null;
 				}
@@ -919,7 +934,9 @@ namespace Terminal.Gui {
 					if (!view.NeedDisplay.IsEmpty || view.childNeedsDisplay) {
 						if (view.Frame.IntersectsWith (clipRect) && view.Frame.IntersectsWith (region)) {
 
-							// TODO: optimize this by computing the intersection of region and view.Bounds
+							// FIXED: optimize this by computing the intersection of region and view.Bounds
+							if (view.layoutNeeded)
+								view.LayoutSubviews ();
 							view.Redraw (view.Bounds);
 						}
 						view.NeedDisplay = Rect.Empty;
@@ -1310,7 +1327,7 @@ namespace Terminal.Gui {
 		/// <param name="frame">Frame.</param>
 		public Toplevel (Rect frame) : base (frame)
 		{
-			ColorScheme = Colors.Base;
+			Initialize ();
 		}
 
 		/// <summary>
@@ -1318,11 +1335,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Toplevel () : base ()
 		{
-			ColorScheme = Colors.Base;
+			Initialize ();
 			Width = Dim.Fill ();
 			Height = Dim.Fill ();
 		}
 
+		void Initialize ()
+		{
+			ColorScheme = Colors.Base;
+		}
+
 		/// <summary>
 		/// Convenience factory method that creates a new toplevel with the current terminal dimensions.
 		/// </summary>
@@ -1343,6 +1365,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool Modal { get; set; }
 
+		/// <summary>
+		/// Check id current toplevel has menu bar
+		/// </summary>
+		public bool HasMenuBar { get; set; }
+
+		/// <summary>
+		/// Check id current toplevel has status bar
+		/// </summary>
+		public bool HasStatusBar { get; set; }
+
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 			if (base.ProcessKey (keyEvent))
@@ -1392,6 +1424,103 @@ namespace Terminal.Gui {
 			return false;
 		}
 
+		public override void Add (View view)
+		{
+			if (this == Application.Top) {
+				if (view is MenuBar)
+					HasMenuBar = true;
+				if (view is StatusBar)
+					HasStatusBar = true;
+			}
+			base.Add (view);
+		}
+
+		public override void Remove (View view)
+		{
+			if (this == Application.Top) {
+				if (view is MenuBar)
+					HasMenuBar = true;
+				if (view is StatusBar)
+					HasStatusBar = true;
+			}
+			base.Remove (view);
+		}
+
+		public override void RemoveAll ()
+		{
+			if (this == Application.Top) {
+				HasMenuBar = false;
+				HasStatusBar = false;
+			}
+			base.RemoveAll ();
+		}
+
+		internal void EnsureVisibleBounds (Toplevel top, int x, int y, out int nx, out int ny)
+		{
+			nx = Math.Max (x, 0);
+			nx = nx + top.Frame.Width > Driver.Cols ? Math.Max(Driver.Cols - top.Frame.Width, 0) : nx;
+			bool m, s;
+			if (SuperView == null)
+				m = Application.Top.HasMenuBar;
+			else
+				m = ((Toplevel)SuperView).HasMenuBar;
+			int l = m ? 1 : 0;
+			ny = Math.Max (y, l);
+			if (SuperView == null)
+				s = Application.Top.HasStatusBar;
+			else
+				s = ((Toplevel)SuperView).HasStatusBar;
+			l = s ? Driver.Rows - 1 : Driver.Rows;
+			ny = Math.Min (ny, l);
+			ny = ny + top.Frame.Height > l ? Math.Max(l - top.Frame.Height, m ? 1 : 0) : ny;
+		}
+
+		internal void PositionToplevels ()
+		{
+			if (this != Application.Top) {
+				EnsureVisibleBounds (this, Frame.X, Frame.Y, out int nx, out int ny);
+				if (nx != Frame.X || ny != Frame.Y) {
+					X = nx;
+					Y = ny;
+				}
+			} else {
+				foreach (var top in Subviews) {
+					if (top is Toplevel) {
+						EnsureVisibleBounds ((Toplevel)top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
+						if (nx != top.Frame.X || ny != top.Frame.Y) {
+							top.X = nx;
+							top.Y = ny;
+						}
+						if (HasStatusBar && ny + top.Frame.Height > Driver.Rows - 1) {
+							if (top.Height is Dim.DimFill)
+								top.Height = Dim.Fill () - 1;
+						}
+					}
+				}
+			}
+		}
+
+		public override void Redraw (Rect region)
+		{
+			if (this == Application.Top) {
+				if (!NeedDisplay.IsEmpty) {
+					Driver.SetAttribute (Colors.TopLevel.Normal);
+					Clear (region);
+					Driver.SetAttribute (Colors.Base.Normal);
+				}
+				foreach (var view in Subviews) {
+					if (view.Frame.IntersectsWith (region)) {
+						//view.SetNeedsLayout ();
+						view.SetNeedsDisplay (view.Bounds);
+					}
+				}
+
+				ClearNeedsDisplay ();
+			}
+
+			base.Redraw (base.Bounds);
+		}
+
 		/// <summary>
 		/// This method is invoked by Application.Begin as part of the Application.Run after
 		/// the views have been laid out, and before the views are drawn for the first time.
@@ -1572,48 +1701,48 @@ namespace Terminal.Gui {
 			ClearNeedsDisplay ();
 		}
 
-#if true
-		// 
-		// It does not look like the event is raised on clicked-drag
+		//
+		// FIXED:It does not look like the event is raised on clicked-drag
 		// need to figure that out.
 		//
-		Point? dragPosition;
-		public override bool MouseEvent(MouseEvent mouseEvent)
+		internal static Point? dragPosition;
+		Point start;
+		public override bool MouseEvent (MouseEvent mouseEvent)
 		{
-			// The code is currently disabled, because the 
-			// Driver.UncookMouse does not seem to have an effect if there is 
+			// FIXED:The code is currently disabled, because the
+			// Driver.UncookMouse does not seem to have an effect if there is
 			// a pending mouse event activated.
-			if (true)
-				return false;
-			
-			if ((mouseEvent.Flags == MouseFlags.Button1Pressed|| mouseEvent.Flags == MouseFlags.Button4Pressed)){
-				
-				if (dragPosition.HasValue) {
-					var dx = mouseEvent.X - dragPosition.Value.X;
-					var dy = mouseEvent.Y - dragPosition.Value.Y;
 
-					var nx = Frame.X + dx;
-					var ny = Frame.Y + dy;
-					if (nx < 0)
-						nx = 0;
-					if (ny < 0)
-						ny = 0;
+			int nx, ny;
+			if ((mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition) ||
+				mouseEvent.Flags == MouseFlags.Button4Pressed)) {
+				if (dragPosition.HasValue) {
+					if (SuperView == null) {
+						Application.Top.SetNeedsDisplay (Frame);
+						Application.Top.Redraw (Frame);
+					} else {
+						SuperView.SetNeedsDisplay (Frame);
+					}
+					EnsureVisibleBounds (this, mouseEvent.X + mouseEvent.OfX - start.X,
+						mouseEvent.Y + mouseEvent.OfY, out nx, out ny);
 
+					dragPosition = new Point (nx, ny);
+					Frame = new Rect (nx, ny, Frame.Width, Frame.Height);
+					X = nx;
+					Y = ny;
 					//Demo.ml2.Text = $"{dx},{dy}";
-					dragPosition = new Point (mouseEvent.X, mouseEvent.Y);
 
-					// TODO: optimize, only SetNeedsDisplay on the before/after regions.
-					if (SuperView == null)
-						Application.Refresh ();
-					else
-						SuperView.SetNeedsDisplay ();
-					Frame = new Rect (nx, ny, Frame.Width, Frame.Height);
+					// FIXED: optimize, only SetNeedsDisplay on the before/after regions.
 					SetNeedsDisplay ();
 					return true;
 				} else {
 					// Only start grabbing if the user clicks on the title bar.
 					if (mouseEvent.Y == 0) {
-						dragPosition = new Point (mouseEvent.X, mouseEvent.Y);
+						start = new Point (mouseEvent.X, mouseEvent.Y);
+						dragPosition = new Point ();
+						nx = mouseEvent.X - mouseEvent.OfX;
+						ny = mouseEvent.Y - mouseEvent.OfY;
+						dragPosition = new Point (nx, ny);
 						Application.GrabMouse (this);
 					}
 
@@ -1622,18 +1751,16 @@ namespace Terminal.Gui {
 				}
 			}
 
-			if (mouseEvent.Flags == MouseFlags.Button1Released) {
+			if (mouseEvent.Flags == MouseFlags.Button1Released && dragPosition.HasValue) {
 				Application.UngrabMouse ();
 				Driver.UncookMouse ();
-
 				dragPosition = null;
-				//Driver.StopReportingMouseMoves ();
 			}
 
 			//Demo.ml.Text = me.ToString ();
 			return false;
 		}
-#endif
+
 	}
 
 	/// <summary>
@@ -1721,6 +1848,7 @@ namespace Terminal.Gui {
 					d (state);
 					return false;
 				});
+				mainLoop.Driver.Wakeup ();
 			}
 
 			public override void Send (SendOrPostCallback d, object state)
@@ -1741,7 +1869,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static void Init () => Init (() => Toplevel.Create ());
 
-		static bool _initialized = false;
+		internal static bool _initialized = false;
 
 		/// <summary>
 		/// Initializes the Application
@@ -1749,7 +1877,6 @@ namespace Terminal.Gui {
 		static void Init (Func<Toplevel> topLevelFactory)
 		{
 			if (_initialized) return;
-			_initialized = true;
 
 			var p = Environment.OSVersion.Platform;
 			Mono.Terminal.IMainLoopDriver mainLoopDriver;
@@ -1770,6 +1897,7 @@ namespace Terminal.Gui {
 			SynchronizationContext.SetSynchronizationContext (new MainLoopSyncContext (MainLoop));
 			Top = topLevelFactory ();
 			Current = Top;
+			_initialized = true;
 		}
 
 		/// <summary>
@@ -1867,7 +1995,7 @@ namespace Terminal.Gui {
 			return start;
 		}
 
-		static View mouseGrabView;
+		internal static View mouseGrabView;
 
 		/// <summary>
 		/// Grabs the mouse, forcing all mouse events to be routed to the specified view until UngrabMouse is called.
@@ -1898,20 +2026,22 @@ namespace Terminal.Gui {
 
 		static void ProcessMouseEvent (MouseEvent me)
 		{
+			var view = FindDeepestView (Current, me.X, me.Y, out int rx, out int ry);
 			RootMouseEvent?.Invoke (me);
 			if (mouseGrabView != null) {
 				var newxy = mouseGrabView.ScreenToView (me.X, me.Y);
 				var nme = new MouseEvent () {
 					X = newxy.X,
 					Y = newxy.Y,
-					Flags = me.Flags
+					Flags = me.Flags,
+					OfX = me.X - newxy.X,
+					OfY = me.Y - newxy.Y,
+					View = view
 				};
-				mouseGrabView.MouseEvent (me);
+				mouseGrabView.MouseEvent (nme);
 				return;
 			}
 
-			int rx, ry;
-			var view = FindDeepestView (Current, me.X, me.Y, out rx, out ry);
 			if (view != null) {
 				if (!view.WantMousePositionReports && me.Flags == MouseFlags.ReportMousePosition)
 					return;
@@ -1919,7 +2049,10 @@ namespace Terminal.Gui {
 				var nme = new MouseEvent () {
 					X = rx,
 					Y = ry,
-					Flags = me.Flags
+					Flags = me.Flags,
+					OfX = rx,
+					OfY = ry,
+					View = view
 				};
 				// Should we bubbled up the event, if it is not handled?
 				view.MouseEvent (nme);
@@ -1969,7 +2102,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Building block API: completes the exection of a Toplevel that was started with Begin.
+		/// Building block API: completes the execution of a Toplevel that was started with Begin.
 		/// </summary>
 		/// <param name="runState">The runstate returned by the <see cref="Begin(Toplevel)"/> method.</param>
 		static public void End (RunState runState)
@@ -1980,6 +2113,9 @@ namespace Terminal.Gui {
 			runState.Dispose ();
 		}
 
+		/// <summary>
+		/// Finalize the driver.
+		/// </summary>
 		public static void Shutdown ()
 		{
 			Driver.End ();
@@ -2047,8 +2183,7 @@ namespace Terminal.Gui {
 			for (state.Toplevel.Running = true; state.Toplevel.Running;) {
 				if (MainLoop.EventsPending (wait)) {
 					MainLoop.MainIteration ();
-					if (Iteration != null)
-						Iteration (null, EventArgs.Empty);
+					Iteration?.Invoke (null, EventArgs.Empty);
 				} else if (wait == false)
 					return;
 				if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.childNeedsDisplay) {
@@ -2135,6 +2270,7 @@ namespace Terminal.Gui {
 			var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
 			Driver.Clip = full;
 			foreach (var t in toplevels) {
+				t.PositionToplevels ();
 				t.RelativeLayout (full);
 				t.LayoutSubviews ();
 			}

+ 216 - 48
Terminal.Gui/Dialogs/FileDialog.cs

@@ -23,11 +23,13 @@ namespace Terminal.Gui {
 		internal bool canChooseFiles = true;
 		internal bool canChooseDirectories = false;
 		internal bool allowsMultipleSelection = false;
+		FileDialog host;
 
-		public DirListView ()
+		public DirListView (FileDialog host)
 		{
 			infos = new List<(string,bool,bool)> ();
 			CanFocus = true;
+			this.host = host;
 		}
 
 		bool IsAllowed (FileSystemInfo fsi)
@@ -71,6 +73,84 @@ namespace Terminal.Gui {
 			Move (0, selected - top);
 		}
 
+		int lastSelected;
+		bool shiftOnWheel;
+		public override bool MouseEvent (MouseEvent me)
+		{
+			if ((me.Flags & (MouseFlags.Button1Clicked | MouseFlags.Button1DoubleClicked |
+				MouseFlags.WheeledUp | MouseFlags.WheeledDown)) == 0)
+				return false;
+
+			if (!HasFocus)
+				SuperView.SetFocus (this);
+
+			if (infos == null)
+				return false;
+
+			if (me.Y + top >= infos.Count)
+				return true;
+
+			int lastSelectedCopy = shiftOnWheel ? lastSelected : selected;
+
+			switch (me.Flags) {
+			case MouseFlags.Button1Clicked:
+				SetSelected (me);
+				SelectionChanged ();
+				SetNeedsDisplay ();
+				break;
+			case MouseFlags.Button1DoubleClicked:
+				SetSelected (me);
+				if (ExecuteSelection ()) {
+					host.canceled = false;
+					Application.RequestStop ();
+				}
+				return true;
+			case MouseFlags.Button1Clicked | MouseFlags.ButtonShift:
+				SetSelected (me);
+				if (shiftOnWheel)
+					lastSelected = lastSelectedCopy;
+				shiftOnWheel = false;
+				PerformMultipleSelection (lastSelected);
+				return true;
+			case MouseFlags.Button1Clicked | MouseFlags.ButtonCtrl:
+				SetSelected (me);
+				PerformMultipleSelection ();
+				return true;
+			case MouseFlags.WheeledUp:
+				SetSelected (me);
+				selected = lastSelected;
+				MoveUp ();
+				return true;
+			case MouseFlags.WheeledDown:
+				SetSelected (me);
+				selected = lastSelected;
+				MoveDown ();
+				return true;
+			case MouseFlags.WheeledUp | MouseFlags.ButtonShift:
+				SetSelected (me);
+				selected = lastSelected;
+				lastSelected = lastSelectedCopy;
+				shiftOnWheel = true;
+				MoveUp ();
+				return true;
+			case MouseFlags.WheeledDown | MouseFlags.ButtonShift:
+				SetSelected (me);
+				selected = lastSelected;
+				lastSelected = lastSelectedCopy;
+				shiftOnWheel = true;
+				MoveDown ();
+				return true;
+			}
+
+			return true;
+		}
+
+		void SetSelected (MouseEvent me)
+		{
+			lastSelected = selected;
+			selected = top + me.Y;
+		}
+
 		void DrawString (int line, string str)
 		{
 			var f = Frame;
@@ -91,7 +171,7 @@ namespace Terminal.Gui {
 			}
 			for (; used < width; used++) {
 				Driver.AddRune (' ');
-			}			
+			}
 		}
 
 		public override void Redraw (Rect region)
@@ -123,7 +203,7 @@ namespace Terminal.Gui {
 
 				if (allowsMultipleSelection)
 					Driver.AddRune (fi.Item3 ? '*' : ' ');
-				
+
 				if (fi.Item2)
 					Driver.AddRune ('/');
 				else
@@ -138,35 +218,38 @@ namespace Terminal.Gui {
 
 		void SelectionChanged ()
 		{
+			if (FilePaths.Count > 0)
+				FileChanged?.Invoke (string.Join (", ", GetFilesName (FilePaths)));
+			else
+				FileChanged?.Invoke (infos [selected].Item2 ? "" : Path.GetFileName (infos [selected].Item1));
 			if (SelectedChanged != null) {
 				var sel = infos [selected];
 				SelectedChanged ((sel.Item1, sel.Item2));
 			}
 		}
 
+		List<string> GetFilesName (IReadOnlyList<string> files)
+		{
+			List<string> filesName = new List<string> ();
+
+			foreach (var file in files) {
+				filesName.Add (Path.GetFileName (file));
+			}
+
+			return filesName;
+		}
+
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
 			switch (keyEvent.Key) {
 			case Key.CursorUp:
 			case Key.ControlP:
-				if (selected > 0) {
-					selected--;
-					if (selected < top)
-						top = selected;
-					SelectionChanged ();
-					SetNeedsDisplay ();
-				}
+				MoveUp ();
 				return true;
 
 			case Key.CursorDown:
 			case Key.ControlN:
-				if (selected + 1 < infos.Count) {
-					selected++;
-					if (selected >= top + Frame.Height)
-						top++;
-					SelectionChanged ();
-					SetNeedsDisplay ();
-				}
+				MoveDown ();
 				return true;
 
 			case Key.ControlV:
@@ -187,22 +270,10 @@ namespace Terminal.Gui {
 				return true;
 
 			case Key.Enter:
-				var isDir = infos [selected].Item2;
-
-				if (isDir) {
-					Directory = Path.GetFullPath (Path.Combine (Path.GetFullPath (Directory.ToString ()), infos [selected].Item1));
-					if (DirectoryChanged != null)
-						DirectoryChanged (Directory);
-				} else {
-					if (FileChanged != null)
-						FileChanged (infos [selected].Item1);
-					if (canChooseFiles) {
-						// Let the OK handler take it over
-						return false;
-					}
-					// No files allowed, do not let the default handler take it.
-				}
-				return true;
+				if (ExecuteSelection ())
+					return false;
+				else
+					return true;
 
 			case Key.PageUp:
 				n = (selected - Frame.Height);
@@ -217,21 +288,97 @@ namespace Terminal.Gui {
 				return true;
 
 			case Key.Space:
-			case Key.ControlT: 
-				if (allowsMultipleSelection) {
-					if ((canChooseFiles && infos [selected].Item2 == false) ||
-					    (canChooseDirectories && infos [selected].Item2 &&
-					     infos [selected].Item1 != "..")){
-						infos [selected] = (infos [selected].Item1, infos [selected].Item2, !infos [selected].Item3);
-						SelectionChanged ();
-						SetNeedsDisplay ();
-					}
-				}
+			case Key.ControlT:
+				PerformMultipleSelection ();
+				return true;
+
+			case Key.Home:
+				MoveFirst ();
+				return true;
+
+			case Key.End:
+				MoveLast ();
 				return true;
 			}
 			return base.ProcessKey (keyEvent);
 		}
 
+		void MoveLast ()
+		{
+			selected = infos.Count - 1;
+			top = infos.Count () - 1;
+			SelectionChanged ();
+			SetNeedsDisplay ();
+		}
+
+		void MoveFirst ()
+		{
+			selected = 0;
+			top = 0;
+			SelectionChanged ();
+			SetNeedsDisplay ();
+		}
+
+		void MoveDown ()
+		{
+			if (selected + 1 < infos.Count) {
+				selected++;
+				if (selected >= top + Frame.Height)
+					top++;
+				SelectionChanged ();
+				SetNeedsDisplay ();
+			}
+		}
+
+		void MoveUp ()
+		{
+			if (selected > 0) {
+				selected--;
+				if (selected < top)
+					top = selected;
+				SelectionChanged ();
+				SetNeedsDisplay ();
+			}
+		}
+
+		internal bool ExecuteSelection ()
+		{
+			var isDir = infos [selected].Item2;
+
+			if (isDir) {
+				Directory = Path.GetFullPath (Path.Combine (Path.GetFullPath (Directory.ToString ()), infos [selected].Item1));
+				DirectoryChanged?.Invoke (Directory);
+			} else {
+				FileChanged?.Invoke (infos [selected].Item1);
+				if (canChooseFiles) {
+					// Ensures that at least one file is selected.
+					if (FilePaths.Count == 0)
+						PerformMultipleSelection ();
+					// Let the OK handler take it over
+					return true;
+				}
+				// No files allowed, do not let the default handler take it.
+			}
+			return false;
+		}
+
+		void PerformMultipleSelection (int? firstSelected = null)
+		{
+			if (allowsMultipleSelection) {
+				int first = Math.Min (firstSelected ?? selected, selected);
+				int last = Math.Max (selected, firstSelected ?? selected);
+				for (int i = first; i <= last; i++) {
+					if ((canChooseFiles && infos [i].Item2 == false) ||
+					    (canChooseDirectories && infos [i].Item2 &&
+					     infos [i].Item1 != "..")) {
+						infos [i] = (infos [i].Item1, infos [i].Item2, !infos [i].Item3);
+					}
+				}
+				SelectionChanged ();
+				SetNeedsDisplay ();
+			}
+		}
+
 		string [] allowedFileTypes;
 		public string [] AllowedFileTypes {
 			get => allowedFileTypes;
@@ -278,6 +425,13 @@ namespace Terminal.Gui {
 		TextField dirEntry, nameEntry;
 		internal DirListView dirListView;
 
+		/// <summary>
+		/// Constructor for the OpenDialog and the SaveDialog.
+		/// </summary>
+		/// <param name="title">The title.</param>
+		/// <param name="prompt">The prompt.</param>
+		/// <param name="nameFieldLabel">The name field label.</param>
+		/// <param name="message">The message.</param>
 		public FileDialog (ustring title, ustring prompt, ustring nameFieldLabel, ustring message) : base (title, Driver.Cols - 20, Driver.Rows - 5, null)
 		{
 			this.message = new Label (Rect.Empty, "MESSAGE" + message);
@@ -306,18 +460,16 @@ namespace Terminal.Gui {
 			};
 			Add (this.nameFieldLabel, nameEntry);
 
-			dirListView = new DirListView () {
+			dirListView = new DirListView (this) {
 				X = 1,
 				Y = 3 + msgLines + 2,
-				Width = Dim.Fill () - 2,
+				Width = Dim.Fill () - 3,
 				Height = Dim.Fill () - 2,
 			};
 			DirectoryPath = Path.GetFullPath (Environment.CurrentDirectory);
 			Add (dirListView);
 			dirListView.DirectoryChanged = (dir) => dirEntry.Text = dir;
-			dirListView.FileChanged = (file) => {
-				nameEntry.Text = file;
-			};
+			dirListView.FileChanged = (file) => nameEntry.Text = file;
 
 			this.cancel = new Button ("Cancel");
 			this.cancel.Clicked += () => {
@@ -330,6 +482,7 @@ namespace Terminal.Gui {
 				IsDefault = true,
 			};
 			this.prompt.Clicked += () => {
+				dirListView.ExecuteSelection ();
 				canceled = false;
 				Application.RequestStop ();
 			};
@@ -430,6 +583,11 @@ namespace Terminal.Gui {
 				nameEntry.Text = Path.GetFileName(value.ToString());
 			}
 		}
+
+		/// <summary>
+		/// Check if the dialog was or not canceled.
+		/// </summary>
+		public bool Canceled { get => canceled; }
 	}
 
 	/// <summary>
@@ -445,6 +603,11 @@ namespace Terminal.Gui {
 	/// </para>
 	/// </remarks>
 	public class SaveDialog : FileDialog {
+		/// <summary>
+		/// Constructor of the save dialog.
+		/// </summary>
+		/// <param name="title">The title.</param>
+		/// <param name="message">The message.</param>
 		public SaveDialog (ustring title, ustring message) : base (title, prompt: "Save", nameFieldLabel: "Save as:", message: message)
 		{
 		}
@@ -482,6 +645,11 @@ namespace Terminal.Gui {
 	/// </para>
 	/// </remarks>
 	public class OpenDialog : FileDialog {
+		/// <summary>
+		/// Constructor of the Open Dialog.
+		/// </summary>
+		/// <param name="title"></param>
+		/// <param name="message"></param>
 		public OpenDialog (ustring title, ustring message) : base (title, prompt: "Open", nameFieldLabel: "Open", message: message)
 		{
 		}

+ 222 - 16
Terminal.Gui/Drivers/ConsoleDriver.cs

@@ -6,6 +6,7 @@
 //
 using System;
 using System.Collections.Generic;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Mono.Terminal;
 using NStack;
@@ -93,14 +94,32 @@ namespace Terminal.Gui {
 	/// </remarks>
 	public struct Attribute {
 		internal int value;
+		internal Color foreground;
+		internal Color background;
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Attribute"/> struct.
 		/// </summary>
 		/// <param name="value">Value.</param>
-		public Attribute (int value)
+		/// <param name="foreground">Foreground</param>
+		/// <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;
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Attribute"/> struct.
+		/// </summary>
+		/// <param name="foreground">Foreground</param>
+		/// <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;
 		}
 
 		/// <summary>
@@ -137,50 +156,207 @@ namespace Terminal.Gui {
 	/// views contained inside.
 	/// </summary>
 	public class ColorScheme {
+		Attribute _normal;
+		Attribute _focus;
+		Attribute _hotNormal;
+		Attribute _hotFocus;
+		Attribute _disabled;
+		internal string caller = "";
+
 		/// <summary>
 		/// The default color for text, when the view is not focused.
 		/// </summary>
-		public Attribute Normal;
+		public Attribute Normal { get { return _normal; } set { _normal = SetAttribute (value); } }
+
 		/// <summary>
 		/// The color for text when the view has the focus.
 		/// </summary>
-		public Attribute Focus;
+		public Attribute Focus { get { return _focus; } set { _focus = SetAttribute (value); } }
 
 		/// <summary>
 		/// The color for the hotkey when a view is not focused
 		/// </summary>
-		public Attribute HotNormal;
+		public Attribute HotNormal { get { return _hotNormal; } set { _hotNormal = SetAttribute (value); } }
 
 		/// <summary>
 		/// The color for the hotkey when the view is focused.
 		/// </summary>
-		public Attribute HotFocus;
+		public Attribute HotFocus { get { return _hotFocus; } set { _hotFocus = SetAttribute (value); } }
+
+		/// <summary>
+		/// The default color for text, when the view is disabled.
+		/// </summary>
+		public Attribute Disabled { get { return _disabled; } set { _disabled = SetAttribute (value); } }
+
+		bool preparingScheme = false;
+
+		Attribute SetAttribute (Attribute attribute, [CallerMemberName]string callerMemberName = null)
+		{
+			if (!Application._initialized && !preparingScheme)
+				return attribute;
+
+			if (preparingScheme)
+				return attribute;
+
+			preparingScheme = true;
+			switch (caller) {
+			case "TopLevel":
+				switch (callerMemberName) {
+				case "Normal":
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					break;
+				case "Focus":
+					HotFocus = Application.Driver.MakeAttribute (HotFocus.foreground, attribute.background);
+					break;
+				case "HotNormal":
+					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);
+					break;
+				}
+				break;
+
+			case "Base":
+				switch (callerMemberName) {
+				case "Normal":
+					HotNormal = Application.Driver.MakeAttribute (HotNormal.foreground, attribute.background);
+					break;
+				case "Focus":
+					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);
+					break;
+				case "HotFocus":
+					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
+					if (Focus.foreground != attribute.background)
+						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					break;
+				}
+				break;
+
+			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);
+					break;
+				case "Focus":
+					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);
+					break;
+				case "HotFocus":
+					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);
+					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);
+					break;
+				case "Focus":
+					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);
+					break;
+				case "HotFocus":
+					HotNormal = Application.Driver.MakeAttribute (attribute.foreground, HotNormal.background);
+					if (Focus.foreground != attribute.background)
+						Focus = Application.Driver.MakeAttribute (Focus.foreground, attribute.background);
+					break;
+				}
+				break;
+
+			case "Error":
+				switch (callerMemberName) {
+				case "Normal":
+					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);
+					break;
+				}
+				break;
+
+			}
+			preparingScheme = false;
+			return attribute;
+		}
 	}
 
 	/// <summary>
 	/// The default ColorSchemes for the application.
 	/// </summary>
 	public static class Colors {
+		static ColorScheme _toplevel;
+		static ColorScheme _base;
+		static ColorScheme _dialog;
+		static ColorScheme _menu;
+		static ColorScheme _error;
+
+		/// <summary>
+		/// The application toplevel color scheme, for the default toplevel views.
+		/// </summary>
+		public static ColorScheme TopLevel { get { return _toplevel; } set { _toplevel = SetColorScheme (value); } }
+
 		/// <summary>
 		/// The base color scheme, for the default toplevel views.
 		/// </summary>
-		public static ColorScheme Base;
-		
+		public static ColorScheme Base { get { return _base; } set { _base = SetColorScheme (value); } }
+
 		/// <summary>
 		/// The dialog color scheme, for standard popup dialog boxes
 		/// </summary>
-		public static ColorScheme Dialog;
+		public static ColorScheme Dialog { get { return _dialog; } set { _dialog = SetColorScheme (value); } }
 
 		/// <summary>
 		/// The menu bar color
 		/// </summary>
-		public static ColorScheme Menu;
+		public static ColorScheme Menu { get { return _menu; } set { _menu = SetColorScheme (value); } }
 
 		/// <summary>
 		/// The color scheme for showing errors.
 		/// </summary>
-		public static ColorScheme Error;
+		public static ColorScheme Error { get { return _error; } set { _error = SetColorScheme (value); } }
 
+		static ColorScheme SetColorScheme (ColorScheme colorScheme, [CallerMemberName]string callerMemberName = null)
+		{
+			colorScheme.caller = callerMemberName;
+			return colorScheme;
+		}
 	}
 
 	/// <summary>
@@ -238,7 +414,7 @@ namespace Terminal.Gui {
 		RightTee,
 
 		/// <summary>
-		/// Top tee 
+		/// Top tee
 		/// </summary>
 		TopTee,
 
@@ -253,6 +429,9 @@ namespace Terminal.Gui {
 	/// ConsoleDriver is an abstract class that defines the requirements for a console driver.   One implementation if the CursesDriver, and another one uses the .NET Console one.
 	/// </summary>
 	public abstract class ConsoleDriver {
+		/// <summary>
+		/// The handler fired when the terminal is resized.
+		/// </summary>
 		protected Action TerminalResized;
 
 		/// <summary>
@@ -280,10 +459,16 @@ namespace Terminal.Gui {
 		/// <param name="rune">Rune to add.</param>
 		public abstract void AddRune (Rune rune);
 		/// <summary>
-		/// Adds the specified 
+		/// Adds the specified
 		/// </summary>
 		/// <param name="str">String.</param>
 		public abstract void AddStr (ustring str);
+		/// <summary>
+		/// Prepare the driver and set the key and mouse events handlers.
+		/// </summary>
+		/// <param name="mainLoop"></param>
+		/// <param name="keyHandler"></param>
+		/// <param name="mouseHandler"></param>
 		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler);
 
 		/// <summary>
@@ -312,19 +497,27 @@ namespace Terminal.Gui {
 		/// <param name="c">C.</param>
 		public abstract void SetAttribute (Attribute c);
 
-		// Set Colors from limit sets of colors
+		/// <summary>
+		/// Set Colors from limit sets of colors.
+		/// </summary>
+		/// <param name="foreground">Foreground.</param>
+		/// <param name="background">Background.</param>
 		public abstract void SetColors (ConsoleColor foreground, ConsoleColor background);
 
 		// Advanced uses - set colors to any pre-set pairs, you would need to init_color
 		// that independently with the R, G, B values.
 		/// <summary>
-		/// Advanced uses - set colors to any pre-set pairs, you would need to init_color 
+		/// Advanced uses - set colors to any pre-set pairs, you would need to init_color
 		/// that independently with the R, G, B values.
 		/// </summary>
 		/// <param name="foregroundColorId">Foreground color identifier.</param>
 		/// <param name="backgroundColorId">Background color identifier.</param>
 		public abstract void SetColors (short foregroundColorId, short backgroundColorId);
 
+		/// <summary>
+		/// Set the handler when the terminal is resized.
+		/// </summary>
+		/// <param name="terminalResized"></param>
 		public void SetTerminalResized(Action terminalResized)
 		{
 			TerminalResized = terminalResized;
@@ -388,7 +581,7 @@ namespace Terminal.Gui {
 				for (int l = 0; l < padding; l++)
 					for (b = 0; b < width; b++)
 						AddRune (' ');
-			}			
+			}
 		}
 
 
@@ -408,7 +601,14 @@ namespace Terminal.Gui {
 			set => this.clip = value;
 		}
 
+		/// <summary>
+		/// Start of mouse moves.
+		/// </summary>
 		public abstract void StartReportingMouseMoves ();
+
+		/// <summary>
+		/// Stop reporting mouses moves.
+		/// </summary>
 		public abstract void StopReportingMouseMoves ();
 
 		/// <summary>
@@ -472,7 +672,7 @@ namespace Terminal.Gui {
 		public Rune RightTee;
 
 		/// <summary>
-		/// Top tee 
+		/// Top tee
 		/// </summary>
 		public Rune TopTee;
 
@@ -481,6 +681,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		public Rune BottomTee;
 
+		/// <summary>
+		/// Make the attribute for the foreground and background colors.
+		/// </summary>
+		/// <param name="fore">Foreground.</param>
+		/// <param name="back">Background.</param>
+		/// <returns></returns>
 		public abstract Attribute MakeAttribute (Color fore, Color back);
 	}
 }

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

@@ -255,7 +255,7 @@ namespace Terminal.Gui {
 				Colors.Base.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLUE);
 				Colors.Base.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
 
-				// Focused, 
+				// Focused,
 				//    Selected, Hot: Yellow on Black
 				//    Selected, text: white on black
 				//    Unselected, hot: yellow on cyan
@@ -264,6 +264,7 @@ namespace Terminal.Gui {
 				Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
 				Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
 				Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
+				Colors.Menu.Disabled = MakeColor(Curses.COLOR_WHITE, Curses.COLOR_CYAN);
 
 				Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
 				Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
@@ -410,7 +411,7 @@ namespace Terminal.Gui {
 					suspendSignal = 18;
 					break;
 				case "Linux":
-					// TODO: should fetch the machine name and 
+					// TODO: should fetch the machine name and
 					// if it is MIPS return 24
 					suspendSignal = 20;
 					break;

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

@@ -135,7 +135,7 @@ namespace Terminal.Gui {
 			Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
 			Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
 
-			// Focused, 
+			// Focused,
 			//    Selected, Hot: Yellow on Black
 			//    Selected, text: white on black
 			//    Unselected, hot: yellow on cyan
@@ -144,6 +144,7 @@ namespace Terminal.Gui {
 			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
 			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
 			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
+			Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
 
 			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
@@ -348,7 +349,7 @@ namespace Terminal.Gui {
 		}
 
 		//
-		// These are for the .NET driver, but running natively on Windows, wont run 
+		// These are for the .NET driver, but running natively on Windows, wont run
 		// on the Mono emulation
 		//
 

+ 162 - 55
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -1,5 +1,5 @@
 //
-// WindowsDriver.cs: Windows specific driver 
+// WindowsDriver.cs: Windows specific driver
 //
 // Authors:
 //   Miguel de Icaza ([email protected])
@@ -13,10 +13,10 @@
 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 // copies of the Software, and to permit persons to whom the Software is
 // furnished to do so, subject to the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be included in all
 // copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
@@ -107,7 +107,7 @@ namespace Terminal.Gui {
 			ScreenBuffer = IntPtr.Zero;
 		}
 
-		private bool ContinueListeningForConsoleEvents = true;
+		bool ContinueListeningForConsoleEvents = true;
 
 		public uint ConsoleMode {
 			get {
@@ -151,7 +151,8 @@ namespace Terminal.Gui {
 			Button3Pressed = 8,
 			Button4Pressed = 16,
 			RightmostButtonPressed = 2,
-
+			WheeledUp = unchecked((int)0x780000),
+			WheeledDown = unchecked((int)0xFF880000),
 		}
 
 		[Flags]
@@ -436,10 +437,17 @@ namespace Terminal.Gui {
 
 		public WindowsDriver ()
 		{
+			Colors.TopLevel = 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.DarkYellow, ConsoleColor.DarkCyan);
+
 			winConsole = new WindowsConsole ();
 
 			cols = Console.WindowWidth;
-			rows = Console.WindowHeight - 1;
+			rows = Console.WindowHeight;
 			WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
 
 			ResizeScreen ();
@@ -519,7 +527,6 @@ namespace Terminal.Gui {
 			} finally {
 				eventReady.Reset();
 			}
-			Debug.WriteLine("Events ready");
 
 			if (!tokenSource.IsCancellationRequested)
 				return result != null;
@@ -561,7 +568,7 @@ namespace Terminal.Gui {
 
 			case WindowsConsole.EventType.WindowBufferSize:
 				cols = inputEvent.WindowBufferSizeEvent.size.X;
-				rows = inputEvent.WindowBufferSizeEvent.size.Y - 1;
+				rows = inputEvent.WindowBufferSizeEvent.size.Y;
 				ResizeScreen ();
 				UpdateOffScreen ();
 				TerminalResized?.Invoke();
@@ -570,22 +577,34 @@ namespace Terminal.Gui {
 			result = null;
 		}
 
-		private WindowsConsole.ButtonState? LastMouseButtonPressed = null;
+		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
+		bool IsButtonReleased = false;
+		bool IsButtonDoubleClicked = false;
 
-		private MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
+		MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
 		{
 			MouseFlags mouseFlag = MouseFlags.AllEvents;
 
+			if (IsButtonDoubleClicked) {
+				Task.Run (async () => {
+					await Task.Delay (300);
+					_ = new Action (() => IsButtonDoubleClicked = false);
+				});
+			}
+
 			// The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
 			// This will tell when a mouse button is pressed. When the button is released this event will
 			// be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
 			// To map to the correct driver events we save the last pressed mouse button so we can
 			// map to the correct clicked event.
-			if (LastMouseButtonPressed != null && mouseEvent.ButtonState != 0) {
+			if ((LastMouseButtonPressed != null || IsButtonReleased) && mouseEvent.ButtonState != 0) {
 				LastMouseButtonPressed = null;
+				IsButtonReleased = false;
 			}
 
-			if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null) {
+			if ((mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null && !IsButtonDoubleClicked) ||
+				(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved &&
+				mouseEvent.ButtonState != 0 && !IsButtonDoubleClicked)) {
 				switch (mouseEvent.ButtonState) {
 				case WindowsConsole.ButtonState.Button1Pressed:
 					mouseFlag = MouseFlags.Button1Pressed;
@@ -595,12 +614,32 @@ namespace Terminal.Gui {
 					mouseFlag = MouseFlags.Button2Pressed;
 					break;
 
-				case WindowsConsole.ButtonState.Button3Pressed:
-					mouseFlag = MouseFlags.Button3Pressed;
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button4Pressed;
 					break;
 				}
+
+				if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
+					mouseFlag |= MouseFlags.ReportMousePosition;
 				LastMouseButtonPressed = mouseEvent.ButtonState;
-			} else if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed != null) {
+			} else if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed != null && !IsButtonReleased &&
+				!IsButtonDoubleClicked) {
+				switch (LastMouseButtonPressed) {
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1Released;
+					break;
+
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2Released;
+					break;
+
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button4Released;
+					break;
+				}
+				IsButtonReleased = true;
+			} else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
+				IsButtonReleased) {
 				switch (LastMouseButtonPressed) {
 				case WindowsConsole.ButtonState.Button1Pressed:
 					mouseFlag = MouseFlags.Button1Clicked;
@@ -610,15 +649,59 @@ namespace Terminal.Gui {
 					mouseFlag = MouseFlags.Button2Clicked;
 					break;
 
-				case WindowsConsole.ButtonState.Button3Pressed:
-					mouseFlag = MouseFlags.Button3Clicked;
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button4Clicked;
 					break;
 				}
 				LastMouseButtonPressed = null;
+				IsButtonReleased = false;
+			} else if (mouseEvent.EventFlags.HasFlag (WindowsConsole.EventFlags.DoubleClick)) {
+				switch (mouseEvent.ButtonState) {
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1DoubleClicked;
+					break;
+
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2DoubleClicked;
+					break;
+
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button4DoubleClicked;
+					break;
+				}
+				IsButtonDoubleClicked = true;
+			} else if (mouseEvent.EventFlags == 0 && mouseEvent.ButtonState != 0 && IsButtonDoubleClicked) {
+				switch (mouseEvent.ButtonState) {
+				case WindowsConsole.ButtonState.Button1Pressed:
+					mouseFlag = MouseFlags.Button1TripleClicked;
+					break;
+
+				case WindowsConsole.ButtonState.Button2Pressed:
+					mouseFlag = MouseFlags.Button2TripleClicked;
+					break;
+
+				case WindowsConsole.ButtonState.RightmostButtonPressed:
+					mouseFlag = MouseFlags.Button4TripleClicked;
+					break;
+				}
+				IsButtonDoubleClicked = false;
+			} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseWheeled) {
+				switch (mouseEvent.ButtonState) {
+				case WindowsConsole.ButtonState.WheeledUp:
+					mouseFlag = MouseFlags.WheeledUp;
+					break;
+
+				case WindowsConsole.ButtonState.WheeledDown:
+					mouseFlag = MouseFlags.WheeledDown;
+					break;
+				}
+
 			} else if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
 				mouseFlag = MouseFlags.ReportMousePosition;
 			}
 
+			mouseFlag = SetControlKeyStates (mouseEvent, mouseFlag);
+
 			return new MouseEvent () {
 				X = mouseEvent.MousePosition.X,
 				Y = mouseEvent.MousePosition.Y,
@@ -626,6 +709,21 @@ namespace Terminal.Gui {
 			};
 		}
 
+		static MouseFlags SetControlKeyStates (WindowsConsole.MouseEventRecord mouseEvent, MouseFlags mouseFlag)
+		{
+			if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightControlPressed) ||
+				mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftControlPressed))
+				mouseFlag |= MouseFlags.ButtonCtrl;
+
+			if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.ShiftPressed))
+				mouseFlag |= MouseFlags.ButtonShift;
+
+			if (mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.RightAltPressed) ||
+				 mouseEvent.ControlKeyState.HasFlag (WindowsConsole.ControlKeyState.LeftAltPressed))
+				mouseFlag |= MouseFlags.ButtonAlt;
+			return mouseFlag;
+		}
+
 		public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
 		{
 			var state = keyEvent.dwControlKeyState;
@@ -676,23 +774,23 @@ namespace Terminal.Gui {
 			case ConsoleKey.NumPad0:
 				return keyInfoEx.NumLock ? (Key)(uint)'0' : Key.InsertChar;
 			case ConsoleKey.NumPad1:
-			        return keyInfoEx.NumLock ? (Key)(uint)'1' : Key.End;
+				return keyInfoEx.NumLock ? (Key)(uint)'1' : Key.End;
 			case ConsoleKey.NumPad2:
-			        return keyInfoEx.NumLock ? (Key)(uint)'2' : Key.CursorDown;
+				return keyInfoEx.NumLock ? (Key)(uint)'2' : Key.CursorDown;
 			case ConsoleKey.NumPad3:
 				return keyInfoEx.NumLock ? (Key)(uint)'3' : Key.PageDown;
 			case ConsoleKey.NumPad4:
-			        return keyInfoEx.NumLock ? (Key)(uint)'4' : Key.CursorLeft;
+				return keyInfoEx.NumLock ? (Key)(uint)'4' : Key.CursorLeft;
 			case ConsoleKey.NumPad5:
-			        return keyInfoEx.NumLock ? (Key)(uint)'5' : (Key)((uint)keyInfo.KeyChar);
+				return keyInfoEx.NumLock ? (Key)(uint)'5' : (Key)((uint)keyInfo.KeyChar);
 			case ConsoleKey.NumPad6:
-			        return keyInfoEx.NumLock ? (Key)(uint)'6' : Key.CursorRight;
+				return keyInfoEx.NumLock ? (Key)(uint)'6' : Key.CursorRight;
 			case ConsoleKey.NumPad7:
-			        return keyInfoEx.NumLock ? (Key)(uint)'7' : Key.Home;
+				return keyInfoEx.NumLock ? (Key)(uint)'7' : Key.Home;
 			case ConsoleKey.NumPad8:
-			        return keyInfoEx.NumLock ? (Key)(uint)'8' : Key.CursorUp;
+				return keyInfoEx.NumLock ? (Key)(uint)'8' : Key.CursorUp;
 			case ConsoleKey.NumPad9:
-			        return keyInfoEx.NumLock ? (Key)(uint)'9' : Key.PageUp;
+				return keyInfoEx.NumLock ? (Key)(uint)'9' : Key.PageUp;
 
 			case ConsoleKey.Oem1:
 			case ConsoleKey.Oem2:
@@ -708,33 +806,39 @@ namespace Terminal.Gui {
 			case ConsoleKey.OemPlus:
 			case ConsoleKey.OemMinus:
 				return (Key)((uint)keyInfo.KeyChar);
-		}
-
-		var key = keyInfo.Key;
-		var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
+			}
 
-		if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
-				    var delta = key - ConsoleKey.A;
-				    if (keyInfo.Modifiers == ConsoleModifiers.Control)
-					    return (Key)((uint)Key.ControlA + delta);
-				    if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					    return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
-				    return (Key)((uint)alphaBase + delta);
-		}
-		if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
-		var delta = key - ConsoleKey.D0;
-		if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-			return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
+			var key = keyInfo.Key;
+			var alphaBase = ((keyInfo.Modifiers == ConsoleModifiers.Shift) ^ (keyInfoEx.CapsLock)) ? 'A' : 'a';
+
+			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
+				var delta = key - ConsoleKey.A;
+				if (keyInfo.Modifiers == ConsoleModifiers.Control)
+					return (Key)((uint)Key.ControlA + delta);
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
+					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
+				if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
+					if (keyInfo.KeyChar == 0)
+						return (Key)(((uint)Key.AltMask) + ((uint)Key.ControlA + delta));
+					else
+						return (Key)((uint)keyInfo.KeyChar);
+				}
+				return (Key)((uint)alphaBase + delta);
+			}
+			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
+				var delta = key - ConsoleKey.D0;
+				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
+					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
 
-		return (Key)((uint)keyInfo.KeyChar);
-		}
-		if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
-		var delta = key - ConsoleKey.F1;
+				return (Key)((uint)keyInfo.KeyChar);
+			}
+			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
+				var delta = key - ConsoleKey.F1;
 
-		return (Key)((int)Key.F1 + delta);
+				return (Key)((int)Key.F1 + delta);
+			}
+			return (Key)(0xffffffff);
 		}
-		return (Key)(0xffffffff);
-	}
 
 		public override void Init (Action terminalResized)
 		{
@@ -763,10 +867,11 @@ namespace Terminal.Gui {
 			Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
 			Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
 
-			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
 			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
 			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
+			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
+			Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
 
 			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
 			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
@@ -797,7 +902,7 @@ namespace Terminal.Gui {
 			for (int row = 0; row < rows; row++)
 				for (int col = 0; col < cols; col++) {
 					int position = row * cols + col;
-					OutputBuffer [position].Attributes = (ushort)MakeColor (ConsoleColor.White, ConsoleColor.Blue);
+					OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
 					OutputBuffer [position].Char.UnicodeChar = ' ';
 				}
 		}
@@ -843,11 +948,13 @@ namespace Terminal.Gui {
 			currentAttribute = c.value;
 		}
 
-		private Attribute MakeColor (ConsoleColor f, ConsoleColor b)
+		Attribute MakeColor (ConsoleColor f, ConsoleColor b)
 		{
 			// Encode the colors into the int value.
 			return new Attribute () {
-				value = ((int)f | (int)b << 4)
+				value = ((int)f | (int)b << 4),
+				foreground = (Color)f,
+				background = (Color)b
 			};
 		}
 
@@ -881,7 +988,7 @@ namespace Terminal.Gui {
 		{
 			if (damageRegion.Left == -1)
 				return;
-			
+
 			var bufferCoords = new WindowsConsole.Coord (){
 				X = (short)Clip.Width,
 				Y = (short)Clip.Height
@@ -908,6 +1015,7 @@ namespace Terminal.Gui {
 			};
 			winConsole.SetCursorPosition(position);
 		}
+
 		public override void End ()
 		{
 			winConsole.Cleanup();
@@ -945,5 +1053,4 @@ namespace Terminal.Gui {
 
 	}
 
-	
 }

+ 30 - 7
Terminal.Gui/Event.cs

@@ -336,7 +336,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		Button1DoubleClicked = unchecked((int)0x8),
 		/// <summary>
-		/// The first mouse button was tripple-clicked.
+		/// The first mouse button was triple-clicked.
 		/// </summary>
 		Button1TripleClicked = unchecked((int)0x10),
 		/// <summary>
@@ -356,9 +356,9 @@ namespace Terminal.Gui {
 		/// </summary>
 		Button2DoubleClicked = unchecked((int)0x200),
 		/// <summary>
-		/// The second mouse button was tripple-clicked.
+		/// The second mouse button was triple-clicked.
 		/// </summary>
-		Button2TrippleClicked = unchecked((int)0x400),
+		Button2TripleClicked = unchecked((int)0x400),
 		/// <summary>
 		/// The third mouse button was pressed.
 		/// </summary>
@@ -376,7 +376,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		Button3DoubleClicked = unchecked((int)0x8000),
 		/// <summary>
-		/// The third mouse button was tripple-clicked.  
+		/// The third mouse button was triple-clicked.
 		/// </summary>
 		Button3TripleClicked = unchecked((int)0x10000),
 		/// <summary>
@@ -396,15 +396,15 @@ namespace Terminal.Gui {
 		/// </summary>
 		Button4DoubleClicked = unchecked((int)0x200000),
 		/// <summary>
-		/// The fourth button was tripple-clicked.
+		/// The fourth button was triple-clicked.
 		/// </summary>
 		Button4TripleClicked = unchecked((int)0x400000),
 		/// <summary>
-		/// The fourth button was pressed.
+		/// Flag: the shift key was pressed when the mouse button took place.
 		/// </summary>
 		ButtonShift = unchecked((int)0x2000000),
 		/// <summary>
-		/// Flag: the shift key was pressed when the mouse button took place.
+		/// Flag: the ctrl key was pressed when the mouse button took place.
 		/// </summary>
 		ButtonCtrl = unchecked((int)0x1000000),
 		/// <summary>
@@ -416,6 +416,14 @@ namespace Terminal.Gui {
 		/// </summary>
 		ReportMousePosition = unchecked((int)0x8000000),
 		/// <summary>
+		/// Vertical button wheeled up.
+		/// </summary>
+		WheeledUp = unchecked((int)0x10000000),
+		/// <summary>
+		/// Vertical button wheeled up.
+		/// </summary>
+		WheeledDown = unchecked((int)0x20000000),
+		/// <summary>
 		/// Mask that captures all the events.
 		/// </summary>
 		AllEvents = unchecked((int)0x7ffffff),
@@ -440,6 +448,21 @@ namespace Terminal.Gui {
 		/// </summary>
 		public MouseFlags Flags;
 
+		/// <summary>
+		/// The offset X (column) location for the mouse event.
+		/// </summary>
+		public int OfX;
+
+		/// <summary>
+		/// The offset Y (column) location for the mouse event.
+		/// </summary>
+		public int OfY;
+
+		/// <summary>
+		/// The current view at the location for the mouse event.
+		/// </summary>
+		public View View;
+
 		/// <summary>
 		/// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:Terminal.Gui.MouseEvent"/>.
 		/// </summary>

+ 7 - 7
Terminal.Gui/MonoCurses/UnmanagedLibrary.cs

@@ -172,7 +172,7 @@ namespace Mono.Terminal.Internal {
 		/// <summary>
 		/// Loads library in a platform specific way.
 		/// </summary>
-		private static IntPtr PlatformSpecificLoadLibrary (string libraryPath)
+		static IntPtr PlatformSpecificLoadLibrary (string libraryPath)
 		{
 			if (IsWindows) {
 				return Windows.LoadLibrary (libraryPath);
@@ -192,7 +192,7 @@ namespace Mono.Terminal.Internal {
 			throw new InvalidOperationException ("Unsupported platform.");
 		}
 
-		private static string FirstValidLibraryPath (string [] libraryPathAlternatives)
+		static string FirstValidLibraryPath (string [] libraryPathAlternatives)
 		{
 			foreach (var path in libraryPathAlternatives) {
 				if (File.Exists (path)) {
@@ -204,7 +204,7 @@ namespace Mono.Terminal.Internal {
 				string.Join (",", libraryPathAlternatives)));
 		}
 
-		private static class Windows
+		static class Windows
 		{
 			[DllImport ("kernel32.dll")]
 			internal static extern IntPtr LoadLibrary (string filename);
@@ -213,7 +213,7 @@ namespace Mono.Terminal.Internal {
 			internal static extern IntPtr GetProcAddress (IntPtr hModule, string procName);
 		}
 
-		private static class Linux
+		static class Linux
 		{
 			[DllImport ("libdl.so")]
 			internal static extern IntPtr dlopen (string filename, int flags);
@@ -222,7 +222,7 @@ namespace Mono.Terminal.Internal {
 			internal static extern IntPtr dlsym (IntPtr handle, string symbol);
 		}
 
-		private static class MacOSX
+		static class MacOSX
 		{
 			[DllImport ("libSystem.dylib")]
 			internal static extern IntPtr dlopen (string filename, int flags);
@@ -238,7 +238,7 @@ namespace Mono.Terminal.Internal {
 		/// dlopen and dlsym from the current process as on Linux
 		/// Mono sure is linked against these symbols.
 		/// </summary>
-		private static class Mono
+		static class Mono
 		{
 			[DllImport ("__Internal")]
 			internal static extern IntPtr dlopen (string filename, int flags);
@@ -252,7 +252,7 @@ namespace Mono.Terminal.Internal {
 		/// dlopen and dlsym from the "libcoreclr.so",
 		/// to avoid the dependency on libc-dev Linux.
 		/// </summary>
-		private static class CoreCLR
+		static class CoreCLR
 		{
 			[DllImport ("libcoreclr.so")]
 			internal static extern IntPtr dlopen (string filename, int flags);

+ 2 - 3
Terminal.Gui/MonoCurses/mainloop.cs

@@ -354,10 +354,9 @@ namespace Mono.Terminal {
 		/// </summary>
 		public Func<bool> AddIdle (Func<bool> idleHandler)
 		{
-			lock (idleHandlers) {
+			lock (idleHandlers)
 				idleHandlers.Add (idleHandler);
-				driver.Wakeup ();
-			}
+
 			return idleHandler;
 		}
 

+ 5 - 10
Terminal.Gui/Terminal.Gui.csproj

@@ -1,6 +1,6 @@
 <Project Sdk="Microsoft.NET.Sdk">
   <PropertyGroup>
-    <TargetFrameworks>net461;netstandard2.0</TargetFrameworks>
+    <TargetFrameworks>net472;netstandard2.0</TargetFrameworks>
     <RootNamespace>Terminal.Gui</RootNamespace>
     <AssemblyName>Terminal.Gui</AssemblyName>
     <DocumentationFile>bin\Release\Terminal.Gui.xml</DocumentationFile>
@@ -57,23 +57,18 @@
     <DebugType></DebugType>
   </PropertyGroup>
   <ItemGroup>
-    <PackageReference Include="NStack.Core" Version="0.11.0" />
-    <PackageReference Include="System.ValueTuple" Version="4.4.0" />
-    <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.0" />
+    <PackageReference Include="NStack.Core" Version="0.14.0" />
   </ItemGroup>
   <ItemGroup>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Dialogs\" />
   </ItemGroup>
-  <ItemGroup>
-    <Reference Include="NStack">
-      <HintPath>..\..\..\Users\miguel\.nuget\packages\nstack.core\0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
-    </Reference>
+  <!--<ItemGroup>
     <Reference Include="NStack">
-      <HintPath>..\..\..\Users\miguel\.nuget\packages\nstack.core\0.11.0\lib\netstandard1.5\NStack.dll</HintPath>
+      <HintPath>..\..\..\Users\miguel\.nuget\packages\nstack.core\0.14.0\lib\netstandard2.0\NStack.dll</HintPath>
     </Reference>
-  </ItemGroup>
+  </ItemGroup>-->
   <ItemGroup>
     <None Remove="Drivers\#ConsoleDriver.cs#" />
   </ItemGroup>

+ 19 - 4
Terminal.Gui/Types/PosDim.cs

@@ -187,8 +187,16 @@ namespace Terminal.Gui {
 				else
 					return la - ra;
 			}
+
+			public override string ToString ()
+			{
+				return $"{((PosView)left).Target.ToString ()},{right.ToString ()}";
+			}
+
 		}
 
+		static PosCombine posCombine;
+
 		/// <summary>
 		/// Adds a <see cref="Terminal.Gui.Pos"/> to a <see cref="Terminal.Gui.Pos"/>, yielding a new <see cref="T:Terminal.Gui.Pos"/>.
 		/// </summary>
@@ -197,7 +205,10 @@ namespace Terminal.Gui {
 		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the sum of the values of <c>left</c> and <c>right</c>.</returns>
 		public static Pos operator + (Pos left, Pos right)
 		{
-			return new PosCombine (true, left, right);
+			PosCombine newPos = new PosCombine (true, left, right);
+			if (posCombine?.ToString () != newPos.ToString ())
+				((PosView)left).Target.SetNeedsLayout ();
+			return posCombine = newPos;
 		}
 
 		/// <summary>
@@ -208,7 +219,10 @@ namespace Terminal.Gui {
 		/// <returns>The <see cref="T:Terminal.Gui.Pos"/> that is the <c>left</c> minus <c>right</c>.</returns>
 		public static Pos operator - (Pos left, Pos right)
 		{
-			return new PosCombine (false, left, right);
+			PosCombine newPos = new PosCombine (false, left, right);
+			if (posCombine?.ToString () != newPos.ToString ())
+				((PosView)left).Target.SetNeedsLayout ();
+			return posCombine = newPos;
 		}
 
 		internal class PosView : Pos {
@@ -276,6 +290,7 @@ namespace Terminal.Gui {
 	}
 
 	/// <summary>
+	/// Dim properties of a view to control the position.
 	/// </summary>
 	/// <remarks>
 	///   <para>
@@ -331,7 +346,7 @@ namespace Terminal.Gui {
 			return new DimFactor (n / 100);
 		}
 
-		class DimAbsolute : Dim {
+		internal class DimAbsolute : Dim {
 			int n;
 			public DimAbsolute (int n) { this.n = n; }
 
@@ -351,7 +366,7 @@ namespace Terminal.Gui {
 
 		}
 
-		class DimFill : Dim {
+		internal class DimFill : Dim {
 			int margin;
 			public DimFill (int margin) { this.margin = margin; }
 

+ 26 - 27
Terminal.Gui/Types/Rect.cs

@@ -43,7 +43,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	An uninitialized Rectangle Structure.
 		/// </remarks>
-		
+
 		public static readonly Rect Empty;
 
 		/// <summary>
@@ -54,7 +54,7 @@ namespace Terminal.Gui
 		///	Produces a Rectangle structure from left, top, right
 		///	and bottom coordinates.
 		/// </remarks>
-		
+
 		public static Rect FromLTRB (int left, int top,
 						  int right, int bottom)
 		{
@@ -70,7 +70,7 @@ namespace Terminal.Gui
 		///	Produces a new Rectangle by inflating an existing 
 		///	Rectangle by the specified coordinate values.
 		/// </remarks>
-		
+
 		public static Rect Inflate (Rect rect, int x, int y)
 		{
 			Rect r = new Rect (rect.Location, rect.Size);
@@ -85,7 +85,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Inflates the Rectangle by a specified width and height.
 		/// </remarks>
-		
+
 		public void Inflate (int width, int height)
 		{
 			Inflate (new Size (width, height));
@@ -98,7 +98,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Inflates the Rectangle by a specified Size.
 		/// </remarks>
-		
+
 		public void Inflate (Size size)
 		{
 			X -= size.Width;
@@ -115,7 +115,7 @@ namespace Terminal.Gui
 		///	Produces a new Rectangle by intersecting 2 existing 
 		///	Rectangles. Returns null if there is no	intersection.
 		/// </remarks>
-		
+
 		public static Rect Intersect (Rect a, Rect b)
 		{
 			// MS.NET returns a non-empty rectangle if the two rectangles
@@ -138,7 +138,7 @@ namespace Terminal.Gui
 		///	Replaces the Rectangle with the intersection of itself
 		///	and another Rectangle.
 		/// </remarks>
-		
+
 		public void Intersect (Rect rect)
 		{
 			this = Rect.Intersect (this, rect);
@@ -152,7 +152,7 @@ namespace Terminal.Gui
 		///	Produces a new Rectangle from the union of 2 existing 
 		///	Rectangles.
 		/// </remarks>
-		
+
 		public static Rect Union (Rect a, Rect b)
 		{
 			return FromLTRB (Math.Min (a.Left, b.Left),
@@ -176,7 +176,7 @@ namespace Terminal.Gui
 			return ((left.Location == right.Location) && 
 				(left.Size == right.Size));
 		}
-		
+
 		/// <summary>
 		///	Inequality Operator
 		/// </summary>
@@ -192,7 +192,6 @@ namespace Terminal.Gui
 			return ((left.Location != right.Location) || 
 				(left.Size != right.Size));
 		}
-		
 
 		// -----------------------
 		// Public Constructors
@@ -205,7 +204,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Creates a Rectangle from Point and Size values.
 		/// </remarks>
-		
+
 		public Rect (Point location, Size size)
 		{
 			X = location.X;
@@ -222,7 +221,7 @@ namespace Terminal.Gui
 		///	Creates a Rectangle from a specified x,y location and
 		///	width and height values.
 		/// </remarks>
-		
+
 		public Rect (int x, int y, int width, int height)
 		{
 			this.X = x;
@@ -268,7 +267,7 @@ namespace Terminal.Gui
 		///	The X coordinate of the left edge of the Rectangle.
 		///	Read only.
 		/// </remarks>
-		
+
 		public int Left {
 			get {
 				return X;
@@ -282,7 +281,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	The Location of the top-left corner of the Rectangle.
 		/// </remarks>
-		
+
 		public Point Location {
 			get {
 				return new Point (X, Y);
@@ -301,7 +300,7 @@ namespace Terminal.Gui
 		///	The X coordinate of the right edge of the Rectangle.
 		///	Read only.
 		/// </remarks>
-		
+
 		public int Right {
 			get {
 				return X + Width;
@@ -315,7 +314,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	The Size of the Rectangle.
 		/// </remarks>
-		
+
 		public Size Size {
 			get {
 				return new Size (Width, Height);
@@ -334,7 +333,7 @@ namespace Terminal.Gui
 		///	The Y coordinate of the top edge of the Rectangle.
 		///	Read only.
 		/// </remarks>
-		
+
 		public int Top {
 			get {
 				return Y;
@@ -348,7 +347,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Checks if an x,y coordinate lies within this Rectangle.
 		/// </remarks>
-		
+
 		public bool Contains (int x, int y)
 		{
 			return ((x >= Left) && (x < Right) && 
@@ -362,7 +361,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Checks if a Point lies within this Rectangle.
 		/// </remarks>
-		
+
 		public bool Contains (Point pt)
 		{
 			return Contains (pt.X, pt.Y);
@@ -376,7 +375,7 @@ namespace Terminal.Gui
 		///	Checks if a Rectangle lies entirely within this 
 		///	Rectangle.
 		/// </remarks>
-		
+
 		public bool Contains (Rect rect)
 		{
 			return (rect == Intersect (this, rect));
@@ -389,7 +388,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Checks equivalence of this Rectangle and another object.
 		/// </remarks>
-		
+
 		public override bool Equals (object obj)
 		{
 			if (!(obj is Rect))
@@ -405,7 +404,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Calculates a hashing value.
 		/// </remarks>
-		
+
 		public override int GetHashCode ()
 		{
 			return (Height + Width) ^ X + Y;
@@ -418,14 +417,14 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Checks if a Rectangle intersects with this one.
 		/// </remarks>
-		
+
 		public bool IntersectsWith (Rect rect)
 		{
 			return !((Left >= rect.Right) || (Right <= rect.Left) ||
 			    (Top >= rect.Bottom) || (Bottom <= rect.Top));
 		}
 
-		private bool IntersectsWithInclusive (Rect r)
+		bool IntersectsWithInclusive (Rect r)
 		{
 			return !((Left > r.Right) || (Right < r.Left) ||
 			    (Top > r.Bottom) || (Bottom < r.Top));
@@ -444,7 +443,7 @@ namespace Terminal.Gui
 			this.X += x;
 			this.Y += y;
 		}
-		
+
 		/// <summary>
 		///	Offset Method
 		/// </summary>
@@ -458,7 +457,7 @@ namespace Terminal.Gui
 			X += pos.X;
 			Y += pos.Y;
 		}
-		
+
 		/// <summary>
 		///	ToString Method
 		/// </summary>
@@ -466,7 +465,7 @@ namespace Terminal.Gui
 		/// <remarks>
 		///	Formats the Rectangle as a string in (x,y,w,h) notation.
 		/// </remarks>
-		
+
 		public override string ToString ()
 		{
 			return String.Format ("{{X={0},Y={1},Width={2},Height={3}}}",

+ 21 - 15
Terminal.Gui/Types/Size.cs

@@ -15,8 +15,8 @@ namespace Terminal.Gui {
 	/// Stores an ordered pair of integers, which specify a Height and Width.
 	/// </summary>
 	public struct Size
-	{ 
-		private int width, height;
+	{
+		int width, height;
 
 		/// <summary>
 		/// Gets a Size structure that has a Height and Width value of 0.
@@ -36,7 +36,7 @@ namespace Terminal.Gui {
 			return new Size (sz1.Width + sz2.Width, 
 					 sz1.Height + sz2.Height);
 		}
-		
+
 		/// <summary>
 		///	Equality Operator
 		/// </summary>
@@ -52,7 +52,7 @@ namespace Terminal.Gui {
 			return ((sz1.Width == sz2.Width) && 
 				(sz1.Height == sz2.Height));
 		}
-		
+
 		/// <summary>
 		///	Inequality Operator
 		/// </summary>
@@ -68,7 +68,7 @@ namespace Terminal.Gui {
 			return ((sz1.Width != sz2.Width) || 
 				(sz1.Height != sz2.Height));
 		}
-		
+
 		/// <summary>
 		///	Subtraction Operator
 		/// </summary>
@@ -82,7 +82,7 @@ namespace Terminal.Gui {
 			return new Size (sz1.Width - sz2.Width, 
 					 sz1.Height - sz2.Height);
 		}
-		
+
 		/// <summary>
 		///	Size to Point Conversion
 		/// </summary>
@@ -104,7 +104,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Creates a Size from a Point value.
 		/// </remarks>
-		
+
 		public Size (Point pt)
 		{
 			width = pt.X;
@@ -118,7 +118,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Creates a Size from specified dimensions.
 		/// </remarks>
-		
+
 		public Size (int width, int height)
 		{
 			this.width = width;
@@ -132,7 +132,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Indicates if both Width and Height are zero.
 		/// </remarks>
-		
+
 		public bool IsEmpty {
 			get {
 				return ((width == 0) && (height == 0));
@@ -146,7 +146,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	The Width coordinate of the Size.
 		/// </remarks>
-		
+
 		public int Width {
 			get {
 				return width;
@@ -163,7 +163,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	The Height coordinate of the Size.
 		/// </remarks>
-		
+
 		public int Height {
 			get {
 				return height;
@@ -180,7 +180,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Checks equivalence of this Size and another object.
 		/// </remarks>
-		
+
 		public override bool Equals (object obj)
 		{
 			if (!(obj is Size))
@@ -196,7 +196,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Calculates a hashing value.
 		/// </remarks>
-		
+
 		public override int GetHashCode ()
 		{
 			return width^height;
@@ -209,7 +209,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///	Formats the Size as a string in coordinate notation.
 		/// </remarks>
-		
+
 		public override string ToString ()
 		{
 			return String.Format ("{{Width={0}, Height={1}}}", width, height);
@@ -227,7 +227,13 @@ namespace Terminal.Gui {
 					 sz1.Height + sz2.Height);
 
 		}
-		
+
+		/// <summary>
+		/// Subtracts the width and height of one Size structure to the width and height of another Size structure.
+		/// </summary>
+		/// <returns>The subtract.</returns>
+		/// <param name="sz1">The first Size structure to subtract.</param>
+		/// <param name="sz2">The second Size structure to subtract.</param>
 		public static Size Subtract (Size sz1, Size sz2)
 		{
 			return new Size (sz1.Width - sz2.Width, 

+ 11 - 3
Terminal.Gui/Views/Button.cs

@@ -68,10 +68,16 @@ namespace Terminal.Gui {
 			CanFocus = true;
 			this.IsDefault = is_default;
 			Text = text;
+			int w = SetWidthHeight (text, is_default);
+			Frame = new Rect (0, 0, w, 1);
+		}
+
+		int SetWidthHeight (ustring text, bool is_default)
+		{
 			int w = text.Length + 4 + (is_default ? 2 : 0);
 			Width = w;
 			Height = 1;
-			Frame = new Rect (0, 0, w, 1);
+			return w;
 		}
 
 		/// <summary>
@@ -96,6 +102,9 @@ namespace Terminal.Gui {
 			}
 
 			set {
+				if (text?.Length != value?.Length) {
+					SetWidthHeight (value, is_default);
+				}
 				text = value;
 				Update ();
 			}
@@ -166,8 +175,7 @@ namespace Terminal.Gui {
 		{
 			if (Char.ToUpper ((char)key.KeyValue) == hot_key) {
 				this.SuperView.SetFocus (this);
-				if (Clicked != null)
-					Clicked ();
+				Clicked?.Invoke ();
 				return true;
 			}
 			return false;

+ 241 - 0
Terminal.Gui/Views/DateField.cs

@@ -0,0 +1,241 @@
+//
+// DateField.cs: text entry for date
+//
+// Author: Barry Nolte
+//
+// Licensed under the MIT license
+//
+using System;
+using System.Globalization;
+using System.Linq;
+using NStack;
+
+namespace Terminal.Gui {
+
+	/// <summary>
+	///   Date edit widget
+	/// </summary>
+	/// <remarks>
+	///   This widget provides date editing functionality, and mouse support.
+	/// </remarks>
+	public class DateField : TextField {
+		bool isShort;
+
+		int longFieldLen = 10;
+		int shortFieldLen = 8;
+		int FieldLen { get { return isShort ? shortFieldLen : longFieldLen; } }
+		string sepChar;
+		string longFormat;
+		string shortFormat;
+		string Format { get { return isShort ? shortFormat : longFormat; } }
+
+
+		/// <summary>
+		///    Public constructor that creates a date edit field at an absolute position and fixed size.
+		/// </summary>
+		/// <param name="x">The x coordinate.</param>
+		/// <param name="y">The y coordinate.</param>
+		/// <param name="date">Initial date contents.</param>
+		/// <param name="isShort">If true, shows only two digits for the year.</param>
+		public DateField(int x, int y, DateTime date, bool isShort = false) : base(x, y, isShort ? 10 : 12, "")
+		{
+			CultureInfo cultureInfo = CultureInfo.CurrentCulture;
+			sepChar = cultureInfo.DateTimeFormat.DateSeparator;
+			longFormat = $" {cultureInfo.DateTimeFormat.ShortDatePattern}";
+			shortFormat = GetShortFormat(longFormat);
+			this.isShort = isShort;
+			CursorPosition = 1;
+			Date = date;
+			Changed += DateField_Changed;
+		}
+
+		void DateField_Changed(object sender, ustring e)
+		{
+			if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result))
+				Text = e;
+		}
+
+		string GetShortFormat(string lf)
+		{
+			return lf.Replace("yyyy", "yy");
+		}
+
+		/// <summary>
+		///   Gets or sets the date in the widget.
+		/// </summary>
+		/// <remarks>
+		/// </remarks>
+		public DateTime Date {
+			get {
+				if (!DateTime.TryParseExact(Text.ToString(), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result)) return new DateTime();
+				return result;
+			}
+			set {
+				this.Text = value.ToString(Format);
+			}
+		}
+
+		bool SetText(Rune key)
+		{
+			var text = TextModel.ToRunes(Text);
+			var newText = text.GetRange(0, CursorPosition);
+			newText.Add(key);
+			if (CursorPosition < FieldLen)
+				newText = newText.Concat(text.GetRange(CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList();
+			return SetText(ustring.Make(newText));
+		}
+
+		bool SetText(ustring text)
+		{
+			ustring[] vals = text.Split(ustring.Make(sepChar));
+			ustring[] frm = ustring.Make(Format).Split(ustring.Make(sepChar));
+			bool isValidDate = true;
+			int idx = GetFormatIndex(frm, "y");
+			int year = Int32.Parse(vals[idx].ToString());
+			int month;
+			int day;
+			idx = GetFormatIndex(frm, "M");
+			if (Int32.Parse(vals[idx].ToString()) < 1) {
+				isValidDate = false;
+				month = 1;
+				vals[idx] = "1";
+			} else if (Int32.Parse(vals[idx].ToString()) > 12) {
+				isValidDate = false;
+				month = 12;
+				vals[idx] = "12";
+			} else
+				month = Int32.Parse(vals[idx].ToString());
+			idx = GetFormatIndex(frm, "d");
+			if (Int32.Parse(vals[idx].ToString()) < 1) {
+				isValidDate = false;
+				day = 1;
+				vals[idx] = "1";
+			} else if (Int32.Parse(vals[idx].ToString()) > 31) {
+				isValidDate = false;
+				day = DateTime.DaysInMonth(year, month);
+				vals[idx] = day.ToString();
+			} else
+				day = Int32.Parse(vals[idx].ToString());
+			string date = GetData(month, day, year, frm);
+			Text = date;
+
+			if (!DateTime.TryParseExact(date, Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result) ||
+				!isValidDate)
+				return false;
+			return true;
+		}
+
+		string GetData(int month, int day, int year, ustring[] fm)
+		{
+			string data = " ";
+			for (int i = 0; i < fm.Length; i++) {
+				if (fm[i].Contains("M"))
+					data += $"{month,2:00}";
+				else if (fm[i].Contains("d"))
+					data += $"{day,2:00}";
+				else
+					data += isShort ? $"{year,2:00}" : $"{year,4:0000}";
+				if (i < 2)
+					data += $"{sepChar}";
+			}
+			return data;
+		}
+
+		int GetFormatIndex(ustring[] fm, string t)
+		{
+			int idx = -1;
+			for (int i = 0; i < fm.Length; i++) {
+				if (fm[i].Contains(t)) {
+					idx = i;
+					break;
+				}
+			}
+			return idx;
+		}
+
+		void IncCursorPosition()
+		{
+			if (CursorPosition == FieldLen)
+				return;
+			if (Text[++CursorPosition] == sepChar.ToCharArray()[0])
+				CursorPosition++;
+		}
+
+		void DecCursorPosition()
+		{
+			if (CursorPosition == 1)
+				return;
+			if (Text[--CursorPosition] == sepChar.ToCharArray()[0])
+				CursorPosition--;
+		}
+
+		void AdjCursorPosition()
+		{
+			if (Text[CursorPosition] == sepChar.ToCharArray()[0])
+				CursorPosition++;
+		}
+
+		public override bool ProcessKey(KeyEvent kb)
+		{
+			switch (kb.Key) {
+			case Key.DeleteChar:
+			case Key.ControlD:
+				SetText('0');
+				break;
+
+			case Key.Delete:
+			case Key.Backspace:
+				SetText('0');
+				DecCursorPosition();
+				break;
+
+			// Home, C-A
+			case Key.Home:
+			case Key.ControlA:
+				CursorPosition = 1;
+				break;
+
+			case Key.CursorLeft:
+			case Key.ControlB:
+				DecCursorPosition();
+				break;
+
+			case Key.End:
+			case Key.ControlE: // End
+				CursorPosition = FieldLen;
+				break;
+
+			case Key.CursorRight:
+			case Key.ControlF:
+				IncCursorPosition();
+				break;
+
+			default:
+				// Ignore non-numeric characters.
+				if (kb.Key < (Key)((int)'0') || kb.Key > (Key)((int)'9'))
+					return false;
+				if (SetText(TextModel.ToRunes(ustring.Make((uint)kb.Key)).First()))
+					IncCursorPosition();
+				return true;
+			}
+			return true;
+		}
+
+		public override bool MouseEvent(MouseEvent ev)
+		{
+			if (!ev.Flags.HasFlag(MouseFlags.Button1Clicked))
+				return false;
+			if (!HasFocus)
+				SuperView.SetFocus(this);
+
+			var point = ev.X;
+			if (point > FieldLen)
+				point = FieldLen;
+			if (point < 1)
+				point = 1;
+			CursorPosition = point;
+			AdjCursorPosition();
+			return true;
+		}
+	}
+}

+ 1 - 1
Terminal.Gui/Views/Label.cs

@@ -248,7 +248,7 @@ namespace Terminal.Gui {
 		Attribute textColor = -1;
 		/// <summary>
 		///   The color used for the label
-		/// </summary>        
+		/// </summary>
 		public Attribute TextColor {
 			get => textColor;
 			set {

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

@@ -20,6 +20,8 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
 using NStack;
 
 namespace Terminal.Gui {
@@ -123,6 +125,21 @@ namespace Terminal.Gui {
 			}
 		}
 
+		/// <summary>
+		/// Sets the source to an IList value asynchronously, if you want to set a full IListDataSource, use the Source property.
+		/// </summary>
+		/// <value>An item implementing the IList interface.</value>
+		public Task SetSourceAsync (IList source)
+		{
+			return Task.Factory.StartNew (() => {
+				if (source == null)
+					Source = null;
+				else
+					Source = MakeWrapper (source);
+				return source;
+			}, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
+		}
+
 		bool allowsMarking;
 		/// <summary>
 		/// Gets or sets a value indicating whether this <see cref="T:Terminal.Gui.ListView"/> allows items to be marked. 
@@ -142,6 +159,8 @@ namespace Terminal.Gui {
 			}
 		}
 
+		public bool AllowsMultipleSelection { get; set; } = true;
+
 		/// <summary>
 		/// Gets or sets the item that is displayed at the top of the listview
 		/// </summary>
@@ -305,8 +324,21 @@ namespace Terminal.Gui {
 			return base.ProcessKey (kb);
 		}
 
+		public virtual bool AllowsAll ()
+		{
+			if (!allowsMarking)
+				return false;
+			if (!AllowsMultipleSelection) {
+				for (int i = 0; i < Source.Count; i++) {
+					if (Source.IsMarked (i) && i != selected)
+						return false;
+				}
+			}
+			return true;
+		}
+
 		public virtual bool MarkUnmarkRow(){
-			if (allowsMarking){
+			if (AllowsAll ()) {
 				Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem));
 				SetNeedsDisplay();
 				return true;
@@ -400,7 +432,7 @@ namespace Terminal.Gui {
 				return true;
 
 			selected = top + me.Y;
-			if (allowsMarking) {
+			if (AllowsAll ()) {
 				Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
 				SetNeedsDisplay ();
 				return true;

+ 988 - 510
Terminal.Gui/Views/Menu.cs

@@ -1,515 +1,993 @@
-//
-// 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;
-
-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>
-		public MenuItem (ustring title, string help, Action action)
-		{
-			Title = title ?? "";
-			Help = help ?? "";
-			Action = action;
-			bool nextIsHot = false;
-			foreach (var x in Title) {
-				if (x == '_')
-					nextIsHot = true;
-				else {
-					if (nextIsHot) {
-						HotKey = Char.ToUpper ((char)x);
-						break;
-					}
-					nextIsHot = false;
-				}
-			}
-		}
-
-		// 
-		// 
-
-		/// <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; }
-		internal int Width => Title.Length + Help.Length + 1 + 2;
-	}
-
-	/// <summary>
-	/// A menu bar item contains other menu items.
-	/// </summary>
-	public class MenuBarItem {
-		public MenuBarItem (ustring title, MenuItem [] children)
-		{
-			SetTitle (title ?? "");
-			Children = children;
-		}
-
-		void SetTitle (ustring title)
-		{
-			if (title == null)
-				title = "";
-			Title = title;
-			int len = 0;
-			foreach (var ch in Title) {
-				if (ch == '_')
-					continue;
-				len++;
-			}
-			TitleLength = 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 {
-		MenuBarItem barItems;
-		MenuBar host;
-		int current;
-
-		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;
-		}
-
-		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];
-				Move (1, i+1);
-				Driver.SetAttribute (item == null ? Colors.Base.Focus : i == current ? ColorScheme.Focus : ColorScheme.Normal);
-				for (int p = 0; p < Frame.Width-2; p++)
-					if (item == null)
-						Driver.AddRune (Driver.HLine);
-					else
-						Driver.AddRune (' ');
-
-				if (item == null)
-					continue;
-
-				Move (2, i + 1);
-				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);
-			}
-		}
-
-		public override void PositionCursor ()
-		{
-			Move (2, 1 + current);
-		}
-
-		void Run (Action action)
-		{
-			if (action == null)
-				return;
-			
-			Application.MainLoop.AddIdle (() => {
-				action ();
-				return false;
-			});
-		}
-
-		public override bool ProcessKey (KeyEvent kb)
-		{
-			switch (kb.Key) {
-			case Key.CursorUp:
-				if (current == -1)
-					break;
-				do {
-					current--;
-					if (current < 0)
-						current = barItems.Children.Length - 1;
-				} while (barItems.Children [current] == null);
-				SetNeedsDisplay ();
-				break;
-			case Key.CursorDown:
-				do {
-					current++;
-					if (current == barItems.Children.Length)
-						current = 0;
-				} while (barItems.Children [current] == null);
-				SetNeedsDisplay ();
-				break;
-			case Key.CursorLeft:
-				host.PreviousMenu ();
-				break;
-			case Key.CursorRight:
-				host.NextMenu ();
-				break;
-			case Key.Esc:
-				host.CloseMenu ();
-				break;
-			case Key.Enter:
-				host.CloseMenu ();
-				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.HotKey == x) {
-							host.CloseMenu ();
-							Run (item.Action);
-							return true;
-						}
-					}
-				}
-				break;
-			}
-			return true;
-		}
-
-		public override bool MouseEvent(MouseEvent me)
-		{
-			if (me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1Released) {
-				if (me.Y < 1)
-					return true;
-				var item = me.Y - 1;
-				if (item >= barItems.Children.Length)
-					return true;
-				host.CloseMenu ();
-				Run (barItems.Children [item].Action);
-				return true;
-			}
-			if (me.Flags == MouseFlags.Button1Pressed) {
-				if (me.Y < 1)
-					return true;
-				if (me.Y - 1 >= barItems.Children.Length)
-					return true;
-				current = me.Y - 1;
-				SetNeedsDisplay ();
-				return true;
-			}
-			return false;
-		}
-	}
-
-	/// <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; }
-		int selected;
-		Action action;
-
-
-		/// <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;
-			ColorScheme = Colors.Menu;
-		}
-
-		public override void Redraw (Rect region)
-		{
-			Move (0, 0);
-			Driver.SetAttribute (Colors.Base.Focus);
-			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++;
-					Move (pos, 0);
-					return;
-				} else {
-					pos += Menus [i].TitleLength + 4;
-				}
-			}
-			Move (0, 0);
-		}
-
-		void Selected (MenuItem item)
-		{
-			// TODO: Running = false;
-			action = item.Action;
-		}
-
-		public event EventHandler OnOpenMenu;
-		Menu openMenu;
-		View previousFocused;
-
-		void OpenMenu (int index)
-		{
-			OnOpenMenu?.Invoke(this, null);
-			if (openMenu != null)
-				SuperView.Remove (openMenu);
-			
-			int pos = 0;
-			for (int i = 0; i < index; i++) 
-				pos += Menus [i].Title.Length + 3;
-
-			openMenu = new Menu (this, pos, 1, Menus [index]);
-
-			SuperView.Add (openMenu);
-			SuperView.SetFocus (openMenu);
-		}
-
-		// Starts the menu from a hotkey
-		void StartMenu ()
-		{
-			if (openMenu != null)
-				return;
-			selected = 0;
-			SetNeedsDisplay ();
-
-			previousFocused = SuperView.Focused;
-			OpenMenu (selected);
-		}
-
-		// Activates the menu, handles either first focus, or activating an entry when it was already active
-		// For mouse events.
-		void Activate (int idx)
-		{
-			selected = idx;
-			if (openMenu == null) 
-				previousFocused = SuperView.Focused;
-			
-			OpenMenu (idx);
-			SetNeedsDisplay ();
-		}
-
-		internal void CloseMenu ()
-		{
-			selected = -1;
-			SetNeedsDisplay ();
-			SuperView.Remove (openMenu);
-			previousFocused?.SuperView?.SetFocus (previousFocused);
-			openMenu = null;
-		}
-
-		internal void PreviousMenu ()
-		{
-			if (selected <= 0)
-				selected = Menus.Length - 1;
-			else
-				selected--;
-
-			OpenMenu (selected);				
-		}
-
-		internal void NextMenu ()
-		{
-			if (selected == -1)
-				selected = 0;
-			else if (selected + 1 == Menus.Length)
-				selected = 0;
-			else
-				selected++;
-			OpenMenu (selected);
-		}
-
-                internal bool FindAndOpenMenuByHotkey(KeyEvent kb)
-                {
-                    int pos = 0;
+//
+// 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;
+		internal Menu openMenu;
+		Menu openCurrentMenu;
+		internal List<Menu> openSubMenu;
+		View previousFocused;
+		internal bool isMenuOpening;
+		internal bool isMenuClosing;
+		internal bool isMenuClosed;
+		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;
+		}
+
+		// 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;
+			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;
+		}
+
+		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) {
+	            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) {
-			                    OpenMenu(i);
-			                    return true;
-                                    }
-                            }
+						Application.GrabMouse (this);
+						selected = i;
+						OpenMenu (i);
+			                    return true;
+                                    }
+                            }
                     }
-	            return false;
+	            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;
-				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 (me.Flags == MouseFlags.Button1Clicked) {
- 				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) {
-						Activate (i);
-						return true;
-					}
-					pos += 2 + Menus [i].TitleLength + 1;
-				}
-			}
-			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;
+		}
+	}
+
+}

+ 21 - 3
Terminal.Gui/Views/RadioGroup.cs

@@ -42,15 +42,20 @@ namespace Terminal.Gui {
 		/// <param name="selected">The item to be selected, the value is clamped to the number of items.</param>
 		public RadioGroup (string [] radioLabels, int selected = 0) : base ()
 		{
-			var r = MakeRect (0, 0, radioLabels);
-			Width = r.Width;
-			Height = radioLabels.Length;
+			SetWidthHeight(radioLabels);
 
 			this.selected = selected;
 			this.radioLabels = radioLabels;
 			CanFocus = true;
 		}
 
+		void SetWidthHeight(string[] radioLabels)
+		{
+			var r = MakeRect(0, 0, radioLabels);
+			Width = r.Width;
+			Height = radioLabels.Length;
+		}
+
 		static Rect MakeRect (int x, int y, string [] radioLabels)
 		{
 			int width = 0;
@@ -81,6 +86,7 @@ namespace Terminal.Gui {
 		public string [] RadioLabels { 
 			get => radioLabels;
 			set {
+				Update(value);
 				radioLabels = value;
 				selected = 0;
 				cursor = 0;
@@ -88,6 +94,18 @@ namespace Terminal.Gui {
 			}
 		}
 
+		void Update(string [] newRadioLabels)
+		{
+			for (int i = 0; i < radioLabels.Length; i++) {
+				Move(0, i);
+				Driver.SetAttribute(ColorScheme.Normal);
+				Driver.AddStr(new string(' ', radioLabels[i].Length + 4));
+			}
+			if (newRadioLabels.Length != radioLabels.Length) {
+				SetWidthHeight(newRadioLabels);
+			}
+		}
+
 		public override void Redraw (Rect region)
 		{
 			base.Redraw (region);

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

@@ -355,6 +355,7 @@ namespace Terminal.Gui {
 
 		public override void Redraw(Rect region)
 		{
+			SetViewsNeedsDisplay ();
 			var oldClip = ClipToBounds ();
 			Driver.SetAttribute (ColorScheme.Normal);
 			Clear ();
@@ -363,6 +364,13 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (ColorScheme.Normal);
 		}
 
+		void SetViewsNeedsDisplay ()
+		{
+			foreach (View view in contentView) {
+				view.SetNeedsDisplay ();
+			}
+		}
+
 		public override void PositionCursor()
 		{
 			if (InternalSubviews.Count == 0)

+ 126 - 0
Terminal.Gui/Views/StatusBar.cs

@@ -0,0 +1,126 @@
+//
+// StatusBar.cs: a statusbar for an application
+//
+// Authors:
+//   Miguel de Icaza ([email protected])
+//
+// TODO:
+//   Add mouse support
+//   Uses internals of Application
+using System;
+using NStack;
+
+namespace Terminal.Gui
+{
+	/// <summary>
+	/// 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.
+	/// 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.
+	/// </summary>
+	public class StatusItem
+	{
+		/// <summary>
+		/// Initializes a new <see cref="T:Terminal.Gui.StatusItem"/>.
+		/// </summary>
+		/// <param name="shortcut">Shortcut to activate the item.</param>
+		/// <param name="title">Title for the statusbar item.</param>
+		/// <param name="action">Action to invoke when the staturbar item is activated.</param>
+		public StatusItem(Key shortcut, ustring title, Action action) 
+		{
+			Title = title ?? "";
+			Shortcut = shortcut;
+			Action = action;
+		}
+
+		/// <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 { get; }
+
+		/// <summary>
+		/// Gets or sets the title.
+		/// </summary>
+		/// <value>The title.</value>
+		public ustring Title { get; }
+
+		/// <summary>
+		/// Gets or sets the action to be invoked when the statusbar item is triggered
+		/// </summary>
+		/// <value>Method to invoke.</value>
+		public Action Action { get; }
+	};
+
+	/// <summary>
+	/// A statusbar for your application.  
+	/// The statusbar should be context sensitive. This means, if the main menu and an open text editor are visible, the items probably shown will
+	/// 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.  
+	/// </summary>
+	public class StatusBar : View
+	{
+		public StatusItem [] Items { get; set; }
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.StatusBar"/> class with the specified set of statusbar items.
+		/// It will be drawn in the lowest column of the terminal.
+		/// </summary>
+		/// <param name="items">A list of statusbar items.</param>
+		public StatusBar(StatusItem [] items) : base()
+		{
+			X = 0;
+			Y = Application.Driver.Rows - 1; // TODO: using internals of Application
+			Width = Dim.Fill ();
+			Height = 1;
+			Items = items;
+			CanFocus = false;
+			ColorScheme = Colors.Menu;
+		}
+
+		Attribute ToggleScheme(Attribute scheme)
+		{
+			var result = scheme==ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
+			Driver.SetAttribute(result);
+			return result;
+		}
+
+		public override void Redraw(Rect region) {
+			if (Frame.Y != Driver.Rows - 1) {
+				Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);
+				Y = Driver.Rows - 1;
+				SetNeedsDisplay ();
+			}
+
+			Move (0, 0);
+			Driver.SetAttribute (ColorScheme.Normal);
+			for (int i = 0; i < Frame.Width; i++)
+				Driver.AddRune (' ');
+
+			Move (1, 0);
+			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);
+						continue;
+					}
+					Driver.AddRune(title[n]);
+				}
+				Driver.AddRune (' ');
+			}
+		}
+
+		public override bool ProcessHotKey (KeyEvent kb)
+		{
+			foreach(var item in Items) {
+				if(kb.Key==item.Shortcut) {
+					if( item.Action!=null ) item.Action();
+					return true;
+				}
+			}
+			return false;
+		}
+	}
+}

+ 223 - 41
Terminal.Gui/Views/TextField.cs

@@ -52,12 +52,7 @@ namespace Terminal.Gui {
 		/// <param name="text">Initial text contents.</param>
 		public TextField (ustring text)
 		{
-			if (text == null)
-				text = "";
-
-			this.text = TextModel.ToRunes (text);
-			point = text.Length;
-			CanFocus = true;
+			Initialize (text, Frame.Width);
 		}
 
 		/// <summary>
@@ -68,6 +63,11 @@ namespace Terminal.Gui {
 		/// <param name="w">The width.</param>
 		/// <param name="text">Initial text contents.</param>
 		public TextField (int x, int y, int w, ustring text) : base (new Rect (x, y, w, 1))
+		{
+			Initialize (text, w);
+		}
+
+		void Initialize (ustring text, int w)
 		{
 			if (text == null)
 				text = "";
@@ -76,6 +76,15 @@ namespace Terminal.Gui {
 			point = text.Length;
 			first = point > w ? point - w : 0;
 			CanFocus = true;
+			Used = true;
+			WantMousePositionReports = true;
+			OnLeave += TextField_OnLeave;
+		}
+
+		void TextField_OnLeave (object sender, EventArgs e)
+		{
+			if (Application.mouseGrabView != null && Application.mouseGrabView == this)
+				Application.UngrabMouse ();
 		}
 
 		public override Rect Frame {
@@ -98,9 +107,9 @@ namespace Terminal.Gui {
 			}
 
 			set {
-				ustring oldText = ustring.Make(text);
+				ustring oldText = ustring.Make (text);
 				text = TextModel.ToRunes (value);
-				Changed?.Invoke(this, oldText);
+				Changed?.Invoke (this, oldText);
 
 				if (point > text.Count)
 					point = Math.Max (text.Count-1, 0);
@@ -137,7 +146,7 @@ namespace Terminal.Gui {
 		public override void PositionCursor ()
 		{
 			var col = 0;
-			for (int idx = first; idx < text.Count; idx++) {
+			for (int idx = first < 0 ? 0 : first; idx < text.Count; idx++) {
 				if (idx == point)
 					break;
 				var cols = Rune.ColumnWidth (text [idx]);
@@ -148,6 +157,9 @@ namespace Terminal.Gui {
 
 		public override void Redraw (Rect region)
 		{
+			ColorScheme color = Colors.Menu;
+			SetSelectedStartSelectedLength ();
+
 			Driver.SetAttribute (ColorScheme.Focus);
 			Move (0, 0);
 
@@ -160,11 +172,16 @@ namespace Terminal.Gui {
 				if (idx < first)
 					continue;
 				var cols = Rune.ColumnWidth (rune);
+				if (col == point && HasFocus && !Used && SelectedLength == 0)
+					Driver.SetAttribute (Colors.Menu.HotFocus);
+				else
+					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
 				if (col + cols < width)
 					Driver.AddRune ((Rune)(Secret ? '*' : rune));
 				col += cols;
 			}
 
+			Driver.SetAttribute (ColorScheme.Focus);
 			for (int i = col; i < Frame.Width; i++)
 				Driver.AddRune (' ');
 
@@ -175,9 +192,9 @@ namespace Terminal.Gui {
 		int DisplaySize (List<Rune> t, int start)
 		{
 			int size = 0;
-			int tcount = text.Count;
+			int tcount = t.Count;
 			for (int i = start; i < tcount; i++) {
-				var rune = text [i];
+				var rune = t [i];
 				size += Rune.ColumnWidth (rune);
 			}
 			return size;
@@ -195,7 +212,7 @@ namespace Terminal.Gui {
 
 		void SetText (List<Rune> newText)
 		{
-			text = newText;
+			Text = ustring.Make (newText);
 		}
 
 		void SetText (IEnumerable<Rune> newText)
@@ -217,39 +234,50 @@ namespace Terminal.Gui {
 		public override bool ProcessKey (KeyEvent kb)
 		{
 			// remember current cursor position
-			// because the new calculated cursor position is needed to be set BEFORE the change event is triggert
+			// because the new calculated cursor position is needed to be set BEFORE the change event is triggest
 			// Needed for the Elmish Wrapper issue https://github.com/DieselMeister/Terminal.Gui.Elmish/issues/2
 			var oldCursorPos = point;
-			
+
 			switch (kb.Key) {
 			case Key.DeleteChar:
 			case Key.ControlD:
-				if (text.Count == 0 || text.Count== point)
-					return true;
+				if (SelectedLength == 0) {
+					if (text.Count == 0 || text.Count == point)
+						return true;
 
-				SetText (text.GetRange (0, point).Concat (text.GetRange (point + 1, text.Count - (point + 1))));
-				Adjust ();
+					SetText (text.GetRange (0, point).Concat (text.GetRange (point + 1, text.Count - (point + 1))));
+					Adjust ();
+
+				} else {
+					DeleteSelectedText ();
+				}
 				break;
 
 			case Key.Delete:
 			case Key.Backspace:
-				if (point == 0)
-					return true;
+				if (SelectedLength == 0) {
+					if (point == 0)
+						return true;
 
-				point--;
-				SetText(text.GetRange(0, oldCursorPos - 1).Concat(text.GetRange(oldCursorPos, text.Count - (oldCursorPos))));
-				Adjust ();
+					point--;
+					SetText (text.GetRange (0, oldCursorPos - 1).Concat (text.GetRange (oldCursorPos, text.Count - (oldCursorPos))));
+					Adjust ();
+				} else {
+					DeleteSelectedText ();
+				}
 				break;
 
 			// Home, C-A
 			case Key.Home:
 			case Key.ControlA:
+				ClearAllSelection ();
 				point = 0;
 				Adjust ();
 				break;
 
 			case Key.CursorLeft:
 			case Key.ControlB:
+				ClearAllSelection ();
 				if (point > 0) {
 					point--;
 					Adjust ();
@@ -258,12 +286,14 @@ namespace Terminal.Gui {
 
 			case Key.End:
 			case Key.ControlE: // End
+				ClearAllSelection ();
 				point = text.Count;
 				Adjust ();
 				break;
 
 			case Key.CursorRight:
 			case Key.ControlF:
+				ClearAllSelection ();
 				if (point == text.Count)
 					break;
 				point++;
@@ -271,6 +301,7 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.ControlK: // kill-to-end
+				ClearAllSelection ();
 				if (point >= text.Count)
 					return true;
 				SetClipboard (text.GetRange (point, text.Count - point));
@@ -296,6 +327,7 @@ namespace Terminal.Gui {
 				break;
 
 			case (Key)((int)'b' + Key.AltMask):
+				ClearAllSelection ();
 				int bw = WordBackward (point);
 				if (bw != -1)
 					point = bw;
@@ -303,40 +335,59 @@ namespace Terminal.Gui {
 				break;
 
 			case (Key)((int)'f' + Key.AltMask):
+				ClearAllSelection ();
 				int fw = WordForward (point);
 				if (fw != -1)
 					point = fw;
 				Adjust ();
 				break;
 
-				// MISSING:
-				// Alt-D, Alt-backspace
-				// Alt-Y
-				// Delete adding to kill buffer
+			case Key.AltMask | Key.ControlI:
+				Used = !Used;
+				SetNeedsDisplay ();
+				break;
+
+			case Key.AltMask | Key.ControlC:
+				Copy ();
+				break;
+
+			case Key.AltMask | Key.ControlX:
+				Cut ();
+				break;
+
+			case Key.AltMask | Key.ControlV:
+				Paste ();
+				break;
+
+			// MISSING:
+			// Alt-D, Alt-backspace
+			// Alt-Y
+			// Delete adding to kill buffer
 
 			default:
 				// Ignore other control characters.
 				if (kb.Key < Key.Space || kb.Key > Key.CharMask)
 					return false;
 
+				if (SelectedLength != 0) {
+					DeleteSelectedText ();
+					oldCursorPos = point;
+				}
 				var kbstr = TextModel.ToRunes (ustring.Make ((uint)kb.Key));
 				if (used) {
 					point++;
 					if (point == text.Count) {
 						SetText (text.Concat (kbstr).ToList ());
 					} else {
-						SetText(text.GetRange(0, oldCursorPos).Concat(kbstr).Concat(text.GetRange(oldCursorPos, text.Count - oldCursorPos)));
-					}					
+						SetText (text.GetRange (0, oldCursorPos).Concat (kbstr).Concat (text.GetRange (oldCursorPos, Math.Min (text.Count - oldCursorPos, text.Count))));
+					}
 				} else {
-					point = 1;
-					SetText (kbstr);
-					first = 0;					
+					SetText (text.GetRange (0, oldCursorPos).Concat (kbstr).Concat (text.GetRange (Math.Min (oldCursorPos + 1, text.Count), Math.Max (text.Count - oldCursorPos - 1, 0))));
+					point++;
 				}
-				used = true;
 				Adjust ();
 				return true;
 			}
-			used = true;
 			return true;
 		}
 
@@ -402,25 +453,156 @@ namespace Terminal.Gui {
 			return -1;
 		}
 
-        	public override bool MouseEvent (MouseEvent ev)
+		/// <summary>
+		/// Start position of the selected text.
+		/// </summary>
+		public int SelectedStart { get; set; } = -1;
+
+		/// <summary>
+		/// Length of the selected text.
+		/// </summary>
+		public int SelectedLength { get; set; } = 0;
+
+		/// <summary>
+		/// The selected text.
+		/// </summary>
+		public ustring SelectedText { get; set; }
+
+		int start, length;
+		bool isButtonReleased = true;
+
+		public override bool MouseEvent (MouseEvent ev)
 		{
-			if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked))
+			if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked) && !ev.Flags.HasFlag (MouseFlags.Button1Pressed) &&
+				!ev.Flags.HasFlag (MouseFlags.ReportMousePosition))
 				return false;
 
-			if (!HasFocus)
-				SuperView.SetFocus (this);
+			if (ev.Flags == MouseFlags.Button1Clicked) {
+				if (!HasFocus)
+					SuperView.SetFocus (this);
+				int x = PositionCursor (ev);
+				if (isButtonReleased)
+					ClearAllSelection ();
+				isButtonReleased = true;
+				Application.UngrabMouse ();
+			} else if (ev.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
+				int x = PositionCursor (ev);
+				isButtonReleased = false;
+				PrepareSelection (x);
+				if (Application.mouseGrabView == null) {
+					Application.GrabMouse (this);
+				}
+			} else if (ev.Flags == MouseFlags.Button1Pressed) {
+				int x = PositionCursor (ev);
+				if (SelectedLength != 0)
+					ClearAllSelection ();
+			}
+
+			SetNeedsDisplay ();
+			return true;
+		}
 
+		int PositionCursor (MouseEvent ev)
+		{
 			// We could also set the cursor position.
-			point = first + ev.X;
+			int x;
+			if (Application.mouseGrabView == null) {
+				x = ev.X;
+			} else {
+				x = ev.X;// - (text.Count > Frame.Width ? text.Count - Frame.Width : 0);
+			}
+
+			point = first + x;
 			if (point > text.Count)
 				point = text.Count;
 			if (point < first)
 				point = 0;
+			return x;
+		}
 
+		void PrepareSelection (int x)
+		{
+			x = x + first < 0 ? 0 : x + first;
+			SelectedStart = SelectedStart == -1 && text.Count > 0 && x >= 0 && x <= text.Count ? x : SelectedStart;
+			if (SelectedStart > -1) {
+				SelectedLength = x <= text.Count ? x - SelectedStart : text.Count - SelectedStart;
+				SetSelectedStartSelectedLength ();
+				SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
+					start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
+			}
+			Adjust ();
+		}
+
+		/// <summary>
+		/// Clear the selected text.
+		/// </summary>
+		public void ClearAllSelection ()
+		{
+			if (SelectedLength == 0)
+				return;
+			SelectedStart = -1;
+			SelectedLength = 0;
+			SelectedText = "";
+			start = 0;
+		}
+
+		void SetSelectedStartSelectedLength ()
+		{
+			if (SelectedLength < 0) {
+				start = SelectedLength + SelectedStart;
+				length = Math.Abs (SelectedLength);
+			} else {
+				start = SelectedStart;
+				length = SelectedLength;
+			}
+		}
+
+		/// <summary>
+		/// Copy the selected text to the clipboard.
+		/// </summary>
+		public void Copy ()
+		{
+			if (SelectedLength != 0) {
+				Clipboard.Contents = SelectedText;
+			}
+		}
+
+		/// <summary>
+		/// Cut the selected text to the clipboard.
+		/// </summary>
+		public void Cut ()
+		{
+			if (SelectedLength != 0) {
+				Clipboard.Contents = SelectedText;
+				DeleteSelectedText ();
+			}
+		}
+
+		void DeleteSelectedText ()
+		{
+			string actualText = Text.ToString ();
+			int selStart = SelectedLength < 0 ? SelectedLength + SelectedStart : SelectedStart;
+			int selLength = Math.Abs (SelectedLength);
+			Text = actualText.Substring (0, selStart) +
+				actualText.Substring (selStart + selLength, actualText.Length - selStart - selLength);
+			ClearAllSelection ();
+			CursorPosition = selStart >= Text.Length ? Text.Length : selStart;
 			SetNeedsDisplay ();
-			return true;
 		}
-	}
 
+		/// <summary>
+		/// Paste the selected text from the clipboard.
+		/// </summary>
+		public void Paste ()
+		{
+			string actualText = Text.ToString ();
+			int start = SelectedStart == -1 ? CursorPosition : SelectedStart;
+			Text = actualText.Substring (0, start) +
+				Clipboard.Contents?.ToString () +
+				actualText.Substring (start + SelectedLength, actualText.Length - start - SelectedLength);
+			SelectedLength = 0;
+			SetNeedsDisplay ();
+		}
 
+	}
 }

+ 1 - 1
Terminal.Gui/Views/TimeField.cs

@@ -50,7 +50,7 @@ namespace Terminal.Gui {
 			Changed += TimeField_Changed;
 		}
 
-		private void TimeField_Changed (object sender, ustring e)
+		void TimeField_Changed (object sender, ustring e)
 		{
 			if (!DateTime.TryParseExact (Text.ToString (), Format, CultureInfo.CurrentCulture, DateTimeStyles.None, out DateTime result))
 				Text = e;

+ 1 - 2
Terminal.Gui/packages.config

@@ -1,5 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="NStack.Core" version="0.11.0" targetFramework="net461" />
-  <package id="System.ValueTuple" version="4.4.0" targetFramework="net461" />
+  <package id="NStack.Core" version="0.14.0" targetFramework="net472" />
 </packages>

+ 1 - 1
packages.config

@@ -1,4 +1,4 @@
 <?xml version="1.0" encoding="utf-8"?>
 <packages>
-  <package id="NStack.Core" version="0.11.0" targetFramework="net461" />
+  <package id="NStack.Core" version="0.14.0" targetFramework="net472" />
 </packages>