浏览代码

Merge branch 'master' of https://github.com/migueldeicaza/gui.cs into feature/TextFieldAutoComplete

Ross Ferguson 5 年之前
父节点
当前提交
6228bffcbf

+ 0 - 26
.github/workflows/dotnetcore.yml

@@ -1,26 +0,0 @@
-name: .NET Core
-
-on:
-  push:
-    branches: [ master ]
-  pull_request:
-    branches: [ master ]
-
-jobs:
-  build:
-
-    runs-on: ubuntu-latest
-
-# Commented out until we resolve how to build the project with just netstandard2.1
-#    steps:
-#    - uses: actions/checkout@v2
-#   - name: Setup .NET Core
-#      uses: actions/setup-dotnet@v1
-#      with:
-#        dotnet-version: 3.1.101
-#    - name: Install dependencies
-#      run: dotnet restore
-#    - name: Build
-#      run: dotnet build --configuration Release --no-restore
-#    - name: Test
-#      run: dotnet test --no-restore --verbosity normal

+ 8 - 8
Example/demo.cs

@@ -92,8 +92,8 @@ static class Demo {
 			Width = Dim.Fill (),
 			Height = Dim.Fill ()
 		};
-		container.OnKeyUp += (KeyEvent ke) => {
-			if (ke.Key == Key.Esc)
+		container.KeyUp += (sender, e) => {
+			if (e.KeyEvent.Key == Key.Esc)
 				container.Running = false;
 		};
 
@@ -436,11 +436,11 @@ static class Demo {
 	#endregion
 
 
-	#region OnKeyDown / OnKeyPress / OnKeyUp Demo
+	#region KeyDown / KeyPress / KeyUp Demo
 	private static void OnKeyDownPressUpDemo ()
 	{
 		var container = new Dialog (
-			"OnKeyDown & OnKeyPress & OnKeyUp demo", 80, 20,
+			"KeyDown & KeyPress & KeyUp demo", 80, 20,
 			new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
 			Width = Dim.Fill (),
 			Height = Dim.Fill (),
@@ -492,9 +492,9 @@ static class Demo {
 		}
 
 
-		container.OnKeyDown += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Down");
-		container.OnKeyPress += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Press");
-		container.OnKeyUp += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Up");
+		container.KeyDown += (o, e) => KeyDownPressUp (e.KeyEvent, "Down");
+		container.KeyPress += (o, e) => KeyDownPressUp (e.KeyEvent, "Press");
+		container.KeyUp += (o, e) => KeyDownPressUp (e.KeyEvent, "Up");
 		Application.Run (container);
 	}
 	#endregion
@@ -628,7 +628,7 @@ static class Demo {
 		var bottom2 = new Label ("This should go on the bottom of another top-level!");
 		top.Add (bottom2);
 
-		Application.OnLoad = () => {
+		Application.Loaded += (sender, e) => {
 			bottom.X = win.X;
 			bottom.Y = Pos.Bottom (win) - Pos.Top (win) - margin;
 			bottom2.X = Pos.Left (win);

+ 71 - 99
README.md

@@ -2,59 +2,84 @@
 [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui)
 [![Downloads](https://img.shields.io/nuget/dt/Terminal.Gui)](https://www.nuget.org/packages/Terminal.Gui)
 [![License](https://img.shields.io/github/license/migueldeicaza/gui.cs.svg)](LICENSE)
+[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mono/mono?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - The Mono Channel room
 
-[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/mono/mono?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) - This is the Mono Channel room
+# Terminal.Gui - Terminal UI toolkit for .NET
 
-# Gui.cs - Terminal UI toolkit for .NET
+A simple UI toolkit for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
 
-This is a simple UI toolkit for .NET, .NET Core and Mono and works on
-both Windows and Linux/Unix.
+![Sample app](https://raw.githubusercontent.com/migueldeicaza/gui.cs/master/docfx/sample.gif)
 
-![Sample app](https://raw.githubusercontent.com/migueldeicaza/gui.cs/master/docfx/sample.png)
+## Controls & Features
 
-A presentation of this was part of the [Retro.NET](https://channel9.msdn.com/Events/dotnetConf/2018/S313) talk at .NET Conf 2018 [Slides](https://tirania.org/Retro.pdf)
-
-The toolkit contains various controls for building text user interfaces:
+The *Terminal.Gui* toolkit contains various controls for building text user interfaces:
 
 * [Buttons](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Button.html) 
+* [Checkboxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.CheckBox.html)
+* [Dialog boxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Dialog.html)
+* [Frames](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.FrameView.html)
+* [Hex viewer/editor](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.HexView.html)
 * [Labels](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Label.html)
+* [ListViews](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ListView.html)
+* [Menus](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.MenuBar.html)
+* [Message boxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.MessageBox.html)
+* [ProgressBars](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ProgressBar.html)
+* [Time editing field](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.TimeField.html)
 * [Text entry](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.TextField.html)
 * [Text view](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.TextView.html)
-* [Time editing field](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.TimeField.html)
+* [Scroll views](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ScrollView.html)
+* [Scrollbars](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ScrollBarView.html)
+* [Status bars]()
 * [Radio buttons](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.RadioGroup.html)
-* [Checkboxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.CheckBox.html)
-* [Dialog boxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Dialog.html)
-  * [Message boxes](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.MessageBox.html)
 * [Windows](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Window.html)
-* [Menus](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.MenuBar.html)
-* [ListViews](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ListView.html)
-* [Frames](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.FrameView.html)
-* [ProgressBars](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ProgressBar.html)
-* [Scroll views](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ScrollView.html) and [Scrollbars](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.ScrollBarView.html)
-* Hexadecimal viewer/editor (HexView)
-* Terminal Emulator - a complete Xterm/Vt100 terminal emulator that you can embed is now part of [XtermSharp](https://github.com/migueldeicaza/XtermSharp/blob/master/GuiCsHost/TerminalView.cs) - you just need to pull the `TerminalView` linked here into your project.
 
-All visible UI elements are subclasses of the
-[View](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.View.html),
-and these in turn can contain an arbitrary number of subviews.   
+In addition, a complete Xterm/Vt100 terminal emulator that you can embed is now part of [XtermSharp](https://github.com/migueldeicaza/XtermSharp/blob/master/GuiCsHost/TerminalView.cs) - you just need to pull the `TerminalView` linked here into your project.
 
-It comes with a
-[mainloop](https://migueldeicaza.github.io/gui.cs/api/Mono.Terminal/Mono.Terminal.MainLoop.html)
-to process events, process idle handlers, timers and monitoring file
+### Features
+
+* **Cross Platform** - Terminal drivers for Curses, [Windows Console](https://github.com/migueldeicaza/gui.cs/issues/27), and the .NET Console mean **Terminal.Gui** works well on both color and monochrome terminals and has mouse support on terminal emulators that support it.
+* **Keyboard and Mouse Input** - Both keyboard and mouse input are supported, including limited support for drag & drop.
+* **[Flexible Layout](https://migueldeicaza.github.io/gui.cs/articles/overview.html#layout)** - **Terminal.Gui** supports both *Absolute layout* and an innovative UI layout system referred to as *Computed Layout*. *Computed Layout* makes it easy to layout controls relative to each other and enables dynamic console GUIs.
+* **Clipboard support** - Cut, Copy, and Paste of text provided through the [`Clipboard`](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.Clipboard.html) class.
+* **[Arbitrary Views](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui/Terminal.Gui.View.html)** - All visible UI elements are subclasses of the `View` class, and these in turn can contain an arbitrary number of sub-views.
+* **Advanced App Features** - The [Mainloop](https://migueldeicaza.github.io/gui.cs/api/Mono.Terminal/Mono.Terminal.MainLoop.html) supports processing events, idle handlers, timers, and monitoring file
 descriptors.
 
-It is designed to work on Curses and the [Windows Console](https://github.com/migueldeicaza/gui.cs/issues/27), 
-works well on both color and monochrome terminals and has mouse support on
-terminal emulators that support it.
+### Keyboard Input Handling
+
+The input handling of **Terminal.Gui** is similar in some ways to Emacs and the Midnight Commander, so you can expect some of the special key combinations to be active.
+
+The key `ESC` can act as an Alt modifier (or Meta in Emacs parlance), to allow input on terminals that do not have an alt key.  So to produce the sequence `Alt-F`, you can press either `Alt-F`, or `ESC` followed by the key `F`.
+
+To enter the key `ESC`, you can either press `ESC` and wait 100 milliseconds, or you can press `ESC` twice.
+
+`ESC-0`, and `ESC-1` through `ESC-9` have a special meaning, they map to `F10`, and `F1` to `F9` respectively.
 
-# Documentation
+**Terminal.Gui** respects common Mac and Windows keyboard idoms as well. For example, clipboard operations use the familiar `Control/Command-C, X, V` model.
 
-* [API documentation](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui.html) for details.
+`CTRL-Q` is used for exiting views (and apps).
 
-* [Overview](https://migueldeicaza.github.io/gui.cs/articles/overview.html) contains the conceptual
-  documentation and a walkthrough of the core concepts of `gui.cs`
+### Driver model
 
-# Sample Usage
+Currently **Terminal.Gui** has support for [ncurses](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/CursesDriver.cs), [`System.Console`](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/NetDriver.cs), and a full [Win32 Console](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Drivers/WindowsDriver.cs) front-end.
+
+`ncurses` is used on Mac/Linux/Unix with color support based on what your library is compiled with; the Windows driver supports full color and mouse, and an easy-to-debug `System.Console` can be used on Windows and Unix, but lacks mouse support.
+
+You can force the use of `System.Console` on Unix as well; see `Core.cs`.
+
+## Showcase & Examples
+
+* **UI Catalog** - The [UI Catalog project](https://github.com/migueldeicaza/gui.cs/tree/master/UICatalog) provides an easy to use and extend sample illustrating the capabilities of **Terminal.Gui**. Run `dotnet run` in the `UICatalog` directory to run the UI Catalog.
+* **Example (aka `demo.cs`)** - Run `dotnet run` in the `Example` directory to run the simple demo.
+* **Standalone Example** - A trivial .NET core sample application can be found in the `StandaloneExample` directory. Run `dotnet run` in directory to test.
+
+## Documentation
+
+* [API documentation](https://migueldeicaza.github.io/gui.cs/api/Terminal.Gui.html)
+
+* [Overview](https://migueldeicaza.github.io/gui.cs/articles/overview.html) contains the conceptual documentation and a walkthrough of the core concepts of **Terminal.Gui**.
+
+### Sample Usage
 
 ```csharp
 using Terminal.Gui;
@@ -125,8 +150,7 @@ class Demo {
 }
 ```
 
-Alternatively, you can encapsulate the app behavior in a new `Window`-derived class, 
-say `App.cs` containing the code above, and simplify your `Main` method to:
+Alternatively, you can encapsulate the app behavior in a new `Window`-derived class, say `App.cs` containing the code above, and simplify your `Main` method to:
 
 ```csharp
 using Terminal.Gui;
@@ -139,78 +163,26 @@ class Demo {
 }
 ```
 
-The example above shows how to add views, two styles are used, a very
-nice layout system that I have no name for, but that [is
-documented](https://migueldeicaza.github.io/gui.cs/articles/overview.html#layout),
-and the absolute positioning.
-
-# Installing it
-
-If you want to try Gui.cs, use NuGet to install the `Terminal.Gui` NuGet package:
-
-https://www.nuget.org/packages/Terminal.Gui
+The example above shows how to add views using both styles of layout supported by **Terminal.Gui**: **Absolute layout** and **[Computed layout](https://migueldeicaza.github.io/gui.cs/articles/overview.html#layout)**.
 
-# Running and Building
+## Installing
 
-You can find a trivial .NET core sample application in the
-"StandaloneExample" directory.   You can execute it by running
-`dotnet run` in that directory.
+Use NuGet to install the `Terminal.Gui` NuGet package: https://www.nuget.org/packages/Terminal.Gui
 
-That sample relies on the distributed NuGet package, if you want to
-to use the code on GitHub, you can open the Example program which 
-references the library built out of this tree.
+## Running and Building
 
-# Input Handling
+* *`Terminal.Gui`* - Build and run using the .NET SDK command line tools (`doetnet build` in the root directory) or with Visual Studio 2019.
 
-The input handling of gui.cs is similar in some ways to Emacs and the
-Midnight Commander, so you can expect some of the special key
-combinations to be active.
+## Contributing
 
-The key `ESC` can act as an Alt modifier (or Meta in Emacs parlance), to
-allow input on terminals that do not have an alt key.  So to produce
-the sequence `Alt-F`, you can press either `Alt-F`, or `ESC` followed by the key `F`.
+See [Issues](https://github.com/migueldeicaza/gui.cs/issues) for a list of open bugs and enhancements.
 
-To enter the key `ESC`, you can either press `ESC` and wait 100
-milliseconds, or you can press `ESC` twice.
+## History
 
-`ESC-0`, and `ESC-1` through `ESC-9` have a special meaning, they map to
-`F10`, and `F1` to `F9` respectively.
+This is an updated version of [gui.cs](http://tirania.org/blog/archive/2007/Apr-16.html) that Miguel wrote for [mono-curses](https://github.com/mono/mono-curses) in 2007.
 
-# Driver model
+The original **gui.cs** was a UI toolkit in a single file and tied to curses. This version tries to be console-agnostic and instead of having a container/widget model, only uses Views (which can contain subviews) and changes the rendering model to rely on damage regions instead of burdening each view with the details.
 
-Currently gui.cs has support for ncurses, `System.Console` and a full
-Win32 console front-end.
-
-ncurses is used on Unix with color support based on what your library
-is compiled with;   The windows driver supports full color and mouse, and
-an easy-to-debug `System.Console` can be used on Windows and Unix, but
-lacks mouse support.
-
-You can force the use of `System.Console` on Unix as
-well, see `Core.cs`.   
-
-# Tasks
-
-There are some tasks in the github issues, and some others are being
-tracked in the TODO.md file.
-
-# History
-
-This is an updated version of
-[gui.cs](http://tirania.org/blog/archive/2007/Apr-16.html) that
-I wrote for [mono-curses](https://github.com/mono/mono-curses) in 2007.
-
-The original gui.cs was a UI toolkit in a single file and tied to
-curses.  This version tries to be console-agnostic and instead of
-having a container/widget model, only uses Views (which can contain
-subviews) and changes the rendering model to rely on damage regions
-instead of burderning each view with the details.
-
-# Releases
-
-Recently, I setup VSTS to do the releases, for now, this requires a
-branch to be pushed with the name release/XXX, do this after the NuGet
-package version has been updated on the
-Terminal.Gui/Terminal.Gui.csproj, and push.
+A presentation of this was part of the [Retro.NET](https://channel9.msdn.com/Events/dotnetConf/2018/S313) talk at .NET Conf 2018 [Slides](https://tirania.org/Retro.pdf)
 
-Then once the package is built, VSTS will request an approval.
+Release history can be found in the [Terminal.Gui.csproj](https://github.com/migueldeicaza/gui.cs/blob/master/Terminal.Gui/Terminal.Gui.csproj) file.

+ 2 - 5
StandaloneExample/README.md

@@ -1,5 +1,4 @@
-This is just a simple standalone sample that shows how to consume
-the gui.cs from a NuGet package and .NET Core project.
+This is just a simple standalone sample that shows how to consume Terinal.Gui from a NuGet package and .NET Core project.
 
 Simply run:
 
@@ -7,6 +6,4 @@ Simply run:
 $ dotnet run
 ```
 
-To launch the application.   
-
-Or use Visual Studio, open the solution and run.
+To launch the application. Or use Visual Studio, open the solution and run.

+ 0 - 48
TODO.md

@@ -1,48 +0,0 @@
-
-# Things missing
-
-## Color System
-
-Topics to debate.
-
-Given that we need pairs of foreground/background to be set when
-operating on a view, should we surface the values independently, or
-should we surface the attribute?
-
-Currently views hardcode the colors to Colors.Base.SOmething for
-example, perhaps these should be set with styles instead, or even
-inheriting them.
-
-The reason why the Colors definition is useful is because it comes with
-defaults that work for both color and black and white and other limited
-terminals.  Setting foreground/background independently tends to break
-the black and white scenarios.
-
-## Color and Dialogs
-
-Replaces `Colors.Base.Normal` with `Attributes.Normal`, and perhaps attributes
-points to the container.
-
-## Views
-
-Wanted:
-- HotLabels (should be labelsw ith a hotkey that take a focus view as an argument)
-- Shell/Process?
-- Submenus in menus.
-- Make windows draggable
-- View + Attribute for SolidFills?
-
-Should Views support Padding/Margin/Border?   Would make it simpler for Forms backend and perhaps
-adopt the Forms CSS as-is
-
-## Layout manager
-
-Unclear what to do about that right now.  Perhaps use Flex?
-
-Will at least need the protocol for sizing 
-
-# Merge Responder into View
-
-For now it is split, in case we want to introduce formal view
-controllers.  But the design becomes very ugly.
-

+ 115 - 69
Terminal.Gui/Core.cs

@@ -126,7 +126,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
 		/// <returns>true if the event was handled</returns>
-		public virtual bool KeyDown (KeyEvent keyEvent)
+		public virtual bool OnKeyDown (KeyEvent keyEvent)
 		{
 			return false;
 		}
@@ -136,7 +136,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
 		/// <returns>true if the event was handled</returns>
-		public virtual bool KeyUp (KeyEvent keyEvent)
+		public virtual bool OnKeyUp (KeyEvent keyEvent)
 		{
 			return false;
 		}
@@ -607,7 +607,7 @@ namespace Terminal.Gui {
 				return;
 
 			while (subviews.Count > 0) {
-				Remove (subviews[0]);
+				Remove (subviews [0]);
 			}
 		}
 
@@ -705,9 +705,9 @@ namespace Terminal.Gui {
 		{
 			PerformActionForSubview (subview, x => {
 				var idx = subviews.IndexOf (x);
-				if (idx+1 < subviews.Count) {
+				if (idx + 1 < subviews.Count) {
 					subviews.Remove (x);
-					subviews.Insert (idx+1, x);
+					subviews.Insert (idx + 1, x);
 				}
 			});
 		}
@@ -910,7 +910,7 @@ namespace Terminal.Gui {
 						OnEnter ();
 					else
 						OnLeave ();
-					SetNeedsDisplay ();
+				SetNeedsDisplay ();
 				base.HasFocus = value;
 
 				// Remove focus down the chain of subviews if focus is removed
@@ -1062,18 +1062,33 @@ namespace Terminal.Gui {
 			focused.EnsureFocus ();
 
 			// Send focus upwards
-			SuperView?.SetFocus(this);
+			SuperView?.SetFocus (this);
+		}
+
+		/// <summary>
+		/// Specifies the event arguments for <see cref="KeyEvent"/>
+		/// </summary>
+		public class KeyEventEventArgs : EventArgs {
+			/// <summary>
+			/// Constructs.
+			/// </summary>
+			/// <param name="ke"></param>
+			public KeyEventEventArgs(KeyEvent ke) => KeyEvent = ke;
+			/// <summary>
+			/// The <see cref="KeyEvent"/> for the event.
+			/// </summary>
+			public KeyEvent KeyEvent { get; set; }
 		}
 
 		/// <summary>
 		/// Invoked when a character key is pressed and occurs after the key up event.
 		/// </summary>
-		public Action<KeyEvent> OnKeyPress;
+		public event EventHandler<KeyEventEventArgs> KeyPress;
 
 		/// <inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		{
-			OnKeyPress?.Invoke (keyEvent);
+			KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent));
 			if (Focused?.ProcessKey (keyEvent) == true)
 				return true;
 
@@ -1083,6 +1098,7 @@ namespace Terminal.Gui {
 		/// <inheritdoc cref="ProcessHotKey"/>
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
+			KeyPress?.Invoke (this, new KeyEventEventArgs (keyEvent));
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
@@ -1094,6 +1110,7 @@ namespace Terminal.Gui {
 		/// <inheritdoc cref="ProcessColdKey"/>
 		public override bool ProcessColdKey (KeyEvent keyEvent)
 		{
+			KeyPress?.Invoke (this, new KeyEventEventArgs(keyEvent));
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
@@ -1105,16 +1122,16 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invoked when a key is pressed
 		/// </summary>
-		public Action<KeyEvent> OnKeyDown;
+		public event EventHandler<KeyEventEventArgs> KeyDown;
 
-		/// <inheritdoc cref="KeyDown"/>
-		public override bool KeyDown (KeyEvent keyEvent)
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
-			OnKeyDown?.Invoke (keyEvent);
+			KeyDown?.Invoke (this, new KeyEventEventArgs (keyEvent));
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
-				if (view.KeyDown (keyEvent))
+				if (view.OnKeyDown (keyEvent))
 					return true;
 
 			return false;
@@ -1123,16 +1140,16 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Invoked when a key is released
 		/// </summary>
-		public Action<KeyEvent> OnKeyUp;
+		public event EventHandler<KeyEventEventArgs> KeyUp;
 
-		/// <inheritdoc cref="KeyUp"/>
-		public override bool KeyUp (KeyEvent keyEvent)
+		/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
+		public override bool OnKeyUp (KeyEvent keyEvent)
 		{
-			OnKeyUp?.Invoke (keyEvent);
+			KeyUp?.Invoke (this, new KeyEventEventArgs (keyEvent));
 			if (subviews == null || subviews.Count == 0)
 				return false;
 			foreach (var view in subviews)
-				if (view.KeyUp (keyEvent))
+				if (view.OnKeyUp (keyEvent))
 					return true;
 
 			return false;
@@ -1174,7 +1191,7 @@ namespace Terminal.Gui {
 		public void FocusLast ()
 		{
 			if (subviews == null) {
-				SuperView?.SetFocus(this);
+				SuperView?.SetFocus (this);
 				return;
 			}
 
@@ -1221,7 +1238,7 @@ namespace Terminal.Gui {
 						w.FocusLast ();
 
 					SetFocus (w);
-				return true;
+					return true;
 				}
 			}
 			if (focused != null) {
@@ -1538,12 +1555,12 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Check id current toplevel has menu bar
 		/// </summary>
-		public bool HasMenuBar { get; set; }
+		public MenuBar MenuBar { get; set; }
 
 		/// <summary>
 		/// Check id current toplevel has status bar
 		/// </summary>
-		public bool HasStatusBar { get; set; }
+		public StatusBar StatusBar { get; set; }
 
 		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
@@ -1602,9 +1619,9 @@ namespace Terminal.Gui {
 		{
 			if (this == Application.Top) {
 				if (view is MenuBar)
-					HasMenuBar = true;
+					MenuBar = view as MenuBar;
 				if (view is StatusBar)
-					HasStatusBar = true;
+					StatusBar = view as StatusBar;
 			}
 			base.Add (view);
 		}
@@ -1614,9 +1631,9 @@ namespace Terminal.Gui {
 		{
 			if (this == Application.Top) {
 				if (view is MenuBar)
-					HasMenuBar = true;
+					MenuBar = null;
 				if (view is StatusBar)
-					HasStatusBar = true;
+					StatusBar = null;
 			}
 			base.Remove (view);
 		}
@@ -1625,8 +1642,8 @@ namespace Terminal.Gui {
 		public override void RemoveAll ()
 		{
 			if (this == Application.Top) {
-				HasMenuBar = false;
-				HasStatusBar = false;
+				MenuBar = null;
+				StatusBar = null;
 			}
 			base.RemoveAll ();
 		}
@@ -1634,21 +1651,21 @@ namespace Terminal.Gui {
 		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;
+			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;
+			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
+				m = Application.Top.MenuBar != null;
 			else
-				m = ((Toplevel)SuperView).HasMenuBar;
+				m = ((Toplevel)SuperView).MenuBar != null;
 			int l = m ? 1 : 0;
 			ny = Math.Max (y, l);
-			if (SuperView == null)
-				s = Application.Top.HasStatusBar;
+			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
+				s = Application.Top.StatusBar != null;
 			else
-				s = ((Toplevel)SuperView).HasStatusBar;
+				s = ((Toplevel)SuperView).StatusBar != null;
 			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;
+			ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
 		}
 
 		internal void PositionToplevels ()
@@ -1667,9 +1684,15 @@ namespace Terminal.Gui {
 							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;
+						if (StatusBar != null) {
+							if (ny + top.Frame.Height > Driver.Rows - 1) {
+								if (top.Height is Dim.DimFill)
+									top.Height = Dim.Fill () - 1;
+							}
+							if (StatusBar.Frame.Y != Driver.Rows - 1) {
+								StatusBar.Y = Driver.Rows - 1;
+								SetNeedsDisplay ();
+							}
 						}
 					}
 				}
@@ -1681,7 +1704,7 @@ namespace Terminal.Gui {
 		{
 			Application.CurrentView = this;
 
-			if (this == Application.Top) {
+			if (this == Application.Top || this == Application.Current) {
 				if (!NeedDisplay.IsEmpty) {
 					Driver.SetAttribute (Colors.TopLevel.Normal);
 					Clear (region);
@@ -1796,7 +1819,7 @@ namespace Terminal.Gui {
 			this.Title = title;
 			int wb = 1 + padding;
 			this.padding = padding;
- 			contentView = new ContentView () {
+			contentView = new ContentView () {
 				X = wb,
 				Y = wb,
 				Width = Dim.Fill (wb),
@@ -2008,7 +2031,7 @@ namespace Terminal.Gui {
 		/// <remarks>
 		///   See also <see cref="Timeout"/>
 		/// </remarks>
-		static public event EventHandler Iteration;
+		public static event EventHandler Iteration;
 
 		/// <summary>
 		/// Returns a rectangle that is centered in the screen for the provided size.
@@ -2079,7 +2102,7 @@ namespace Terminal.Gui {
 			if (UseSystemConsole) {
 				mainLoopDriver = new Mono.Terminal.NetMainLoop ();
 				Driver = new NetDriver ();
-			} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows){
+			} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
 				var windowsDriver = new WindowsDriver ();
 				mainLoopDriver = windowsDriver;
 				Driver = windowsDriver;
@@ -2137,7 +2160,7 @@ namespace Terminal.Gui {
 		static void ProcessKeyEvent (KeyEvent ke)
 		{
 
-			var chain = toplevels.ToList();
+			var chain = toplevels.ToList ();
 			foreach (var topLevel in chain) {
 				if (topLevel.ProcessHotKey (ke))
 					return;
@@ -2165,7 +2188,7 @@ namespace Terminal.Gui {
 		{
 			var chain = toplevels.ToList ();
 			foreach (var topLevel in chain) {
-				if (topLevel.KeyDown (ke))
+				if (topLevel.OnKeyDown (ke))
 					return;
 				if (topLevel.Modal)
 					break;
@@ -2177,7 +2200,7 @@ namespace Terminal.Gui {
 		{
 			var chain = toplevels.ToList ();
 			foreach (var topLevel in chain) {
-				if (topLevel.KeyUp (ke))
+				if (topLevel.OnKeyUp (ke))
 					return;
 				if (topLevel.Modal)
 					break;
@@ -2194,7 +2217,7 @@ namespace Terminal.Gui {
 				return null;
 			}
 
-			if (start.InternalSubviews != null){
+			if (start.InternalSubviews != null) {
 				int count = start.InternalSubviews.Count;
 				if (count > 0) {
 					var rx = x - startFrame.X;
@@ -2210,8 +2233,8 @@ namespace Terminal.Gui {
 					}
 				}
 			}
-			resx = x-startFrame.X;
-			resy = y-startFrame.Y;
+			resx = x - startFrame.X;
+			resy = y - startFrame.Y;
 			return start;
 		}
 
@@ -2242,7 +2265,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Merely a debugging aid to see the raw mouse events
 		/// </summary>
-		static public Action<MouseEvent> RootMouseEvent;
+		public static Action<MouseEvent> RootMouseEvent;
 
 		internal static View wantContinuousButtonPressedView;
 		static View lastMouseOwnerView;
@@ -2267,8 +2290,12 @@ namespace Terminal.Gui {
 					OfY = me.Y - newxy.Y,
 					View = view
 				};
-				mouseGrabView.MouseEvent (nme);
-				return;
+				if (OutsideFrame (new Point (nme.X, nme.Y), mouseGrabView.Frame))
+					lastMouseOwnerView.OnMouseLeave (me);
+				if (mouseGrabView != null) {
+					mouseGrabView.MouseEvent (nme);
+					return;
+				}
 			}
 
 			if (view != null) {
@@ -2303,10 +2330,16 @@ namespace Terminal.Gui {
 			}
 		}
 
+		static bool OutsideFrame (Point p, Rect r)
+		{
+			return p.X < 0 || p.X > r.Width - 1 || p.Y < 0 || p.Y > r.Height - 1;
+		}
+
 		/// <summary>
-		/// Action that is invoked once at beginning.
+		/// This event is fired once when the application is first loaded. The dimensions of the
+		/// terminal are provided.
 		/// </summary>
-		static public Action OnLoad;
+		public static event EventHandler<ResizedEventArgs> Loaded;
 
 		/// <summary>
 		/// Building block API: Prepares the provided toplevel for execution.
@@ -2321,7 +2354,7 @@ namespace Terminal.Gui {
 		///  the <see cref="RunLoop"/> method, and then the <see cref="End(RunState)"/> method upon termination which will
 		///   undo these changes.
 		/// </remarks>
-		static public RunState Begin (Toplevel toplevel)
+		public static RunState Begin (Toplevel toplevel)
 		{
 			if (toplevel == null)
 				throw new ArgumentNullException (nameof (toplevel));
@@ -2330,11 +2363,11 @@ namespace Terminal.Gui {
 			Init ();
 			if (toplevel is ISupportInitializeNotification initializableNotification &&
 			    !initializableNotification.IsInitialized) {
-				initializableNotification.BeginInit();
-				initializableNotification.EndInit();
+				initializableNotification.BeginInit ();
+				initializableNotification.EndInit ();
 			} else if (toplevel is ISupportInitialize initializable) {
-				initializable.BeginInit();
-				initializable.EndInit();
+				initializable.BeginInit ();
+				initializable.EndInit ();
 			}
 			toplevels.Push (toplevel);
 			Current = toplevel;
@@ -2342,7 +2375,7 @@ namespace Terminal.Gui {
 			if (toplevel.LayoutStyle == LayoutStyle.Computed)
 				toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
 			toplevel.LayoutSubviews ();
-			OnLoad?.Invoke ();
+			Loaded?.Invoke (null, new ResizedEventArgs () { Rows = Driver.Rows, Cols = Driver.Cols } );
 			toplevel.WillPresent ();
 			Redraw (toplevel);
 			toplevel.PositionCursor ();
@@ -2355,7 +2388,7 @@ namespace Terminal.Gui {
 		/// 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)
+		public static void End (RunState runState)
 		{
 			if (runState == null)
 				throw new ArgumentNullException (nameof (runState));
@@ -2409,9 +2442,8 @@ namespace Terminal.Gui {
 			toplevels.Pop ();
 			if (toplevels.Count == 0)
 				Shutdown ();
-			else
-			{
-				Current = toplevels.Peek();
+			else {
+				Current = toplevels.Peek ();
 				Refresh ();
 			}
 		}
@@ -2480,7 +2512,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public static void Run<T> () where T : Toplevel, new()
 		{
-			Init (() => new T());
+			Init (() => new T ());
 			Run (Top);
 		}
 
@@ -2525,14 +2557,28 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Invoked when the terminal was resized.
+		/// Event arguments for the <see cref="T:Terminal.Gui.Application.Resized"/> event.
+		/// </summary>
+		public class ResizedEventArgs : EventArgs {
+			/// <summary>
+			/// The number of rows in the resized terminal.
+			/// </summary>
+			public int Rows { get; set; }
+			/// <summary>
+			/// The number of columns in the resized terminal.
+			/// </summary>
+			public int Cols { get; set; }
+		}
+
+		/// <summary>
+		/// Invoked when the terminal was resized. The new size of the terminal is provided.
 		/// </summary>
-		static public Action OnResized;
+		public static event EventHandler<ResizedEventArgs> Resized;
 
 		static void TerminalResized ()
 		{
-			OnResized?.Invoke ();
 			var full = new Rect (0, 0, Driver.Cols, Driver.Rows);
+			Resized?.Invoke (null, new ResizedEventArgs () { Cols = full.Width, Rows = full.Height });
 			Driver.Clip = full;
 			foreach (var t in toplevels) {
 				t.PositionToplevels ();

+ 190 - 11
Terminal.Gui/Drivers/CursesDriver.cs

@@ -7,6 +7,7 @@
 using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
+using System.Threading.Tasks;
 using Mono.Terminal;
 using NStack;
 using Unix.Terminal;
@@ -183,12 +184,185 @@ namespace Terminal.Gui {
 			}
 		}
 
-		static MouseEvent ToDriverMouse (Curses.MouseEvent cev)
+		Curses.Event? LastMouseButtonPressed = null;
+		bool IsButtonPressed = false;
+		bool cancelButtonClicked = false;
+		Point point;
+
+		MouseEvent ToDriverMouse (Curses.MouseEvent cev)
+		{
+			MouseFlags mouseFlag = MouseFlags.AllEvents;
+
+			if (LastMouseButtonPressed != null && cev.ButtonState != Curses.Event.ReportMousePosition) {
+				LastMouseButtonPressed = null;
+				IsButtonPressed = false;
+			}
+
+
+			if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked ||
+				cev.ButtonState == Curses.Event.Button3Clicked) &&
+				LastMouseButtonPressed == null) {
+
+				IsButtonPressed = false;
+				mouseFlag = ProcessButtonClickedEvent (cev, mouseFlag);
+
+			} else if (((cev.ButtonState == Curses.Event.Button1Pressed || cev.ButtonState == Curses.Event.Button2Pressed ||
+				cev.ButtonState == Curses.Event.Button3Pressed) && LastMouseButtonPressed == null) ||
+				IsButtonPressed && cev.ButtonState == Curses.Event.ReportMousePosition) {
+
+				mouseFlag = (MouseFlags)cev.ButtonState;
+				if (cev.ButtonState != Curses.Event.ReportMousePosition)
+					LastMouseButtonPressed = cev.ButtonState;
+				IsButtonPressed = true;
+
+				if (cev.ButtonState == Curses.Event.ReportMousePosition) {
+					mouseFlag = (MouseFlags)LastMouseButtonPressed | MouseFlags.ReportMousePosition;
+					point = new Point ();
+					cancelButtonClicked = true;
+				} else {
+					point = new Point () {
+						X = cev.X,
+						Y = cev.Y
+					};
+				}
+
+				if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+					Task.Run (async () => {
+						while (IsButtonPressed && LastMouseButtonPressed != null) {
+							await Task.Delay (200);
+							var me = new MouseEvent () {
+								X = cev.X,
+								Y = cev.Y,
+								Flags = mouseFlag
+							};
+
+							var view = Application.wantContinuousButtonPressedView;
+							if (view == null)
+								break;
+							if (IsButtonPressed && LastMouseButtonPressed != null && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+								mouseHandler (me);
+								mainLoop.Driver.Wakeup ();
+							}
+						}
+					});
+				}
+
+
+			} else if ((cev.ButtonState == Curses.Event.Button1Released || cev.ButtonState == Curses.Event.Button2Released ||
+				cev.ButtonState == Curses.Event.Button3Released)) {
+
+				mouseFlag = ProcessButtonReleasedEvent (cev, mouseFlag);
+				IsButtonPressed = false;
+
+			} else if (cev.ButtonState == Curses.Event.Button4Pressed) {
+
+				mouseFlag = MouseFlags.WheeledUp;
+
+			} else if (cev.ButtonState == Curses.Event.ReportMousePosition && cev.X == point.X && cev.Y == point.Y) {
+
+				mouseFlag = MouseFlags.WheeledDown;
+
+			}
+			else if (cev.ButtonState == Curses.Event.ReportMousePosition) {
+
+				mouseFlag = MouseFlags.ReportMousePosition;
+			} else {
+				mouseFlag = (MouseFlags)cev.ButtonState;
+			}
+
+			point = new Point () {
+				X = cev.X,
+				Y = cev.Y
+			};
+
+
+
+			if (cev.ID != 0)
+				mouseFlag = MouseFlags.WheeledDown;
+
+			return new MouseEvent () {
+				X = cev.X,
+				Y = cev.Y,
+				//Flags = (MouseFlags)cev.ButtonState
+				Flags = mouseFlag
+			};
+		}
+
+		private MouseFlags ProcessButtonClickedEvent (Curses.MouseEvent cev, MouseFlags mf)
+		{
+			LastMouseButtonPressed = cev.ButtonState;
+			mf = GetButtonState (cev, true);
+			mouseHandler (ProcessButtonState (cev, mf));
+			if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) {
+				mf = GetButtonState (cev, false);
+				mouseHandler (ProcessButtonState (cev, mf));
+				if (LastMouseButtonPressed != null && LastMouseButtonPressed == cev.ButtonState) {
+					mf = (MouseFlags)cev.ButtonState;
+				}
+			}
+			LastMouseButtonPressed = null;
+			return mf;
+		}
+
+		private MouseFlags ProcessButtonReleasedEvent (Curses.MouseEvent cev, MouseFlags mf)
+		{			
+			mf = (MouseFlags)cev.ButtonState;
+			mouseHandler (ProcessButtonState (cev, mf));
+			if (!cancelButtonClicked && LastMouseButtonPressed == null)
+				mf = GetButtonState (cev);
+			else
+				cancelButtonClicked = false;
+			return mf;
+		}
+
+		MouseFlags GetButtonState (Curses.MouseEvent cev, bool pressed = false)
+		{
+			MouseFlags mf = default;
+			switch (cev.ButtonState) {
+			case Curses.Event.Button1Clicked:
+				if (pressed)
+					mf = MouseFlags.Button1Pressed;
+				else
+					mf = MouseFlags.Button1Released;
+				break;
+
+			case Curses.Event.Button2Clicked:
+				if (pressed)
+					mf = MouseFlags.Button2Pressed;
+				else
+					mf = MouseFlags.Button2Released;
+				break;
+
+			case Curses.Event.Button3Clicked:
+				if (pressed)
+					mf = MouseFlags.Button3Pressed;
+				else
+					mf = MouseFlags.Button3Released;
+				break;
+
+			case Curses.Event.Button1Released:
+				mf = MouseFlags.Button1Clicked;
+				break;
+
+			case Curses.Event.Button2Released:
+				mf = MouseFlags.Button2Clicked;
+				break;
+
+			case Curses.Event.Button3Released:
+				mf = MouseFlags.Button3Clicked;
+				break;
+
+
+			}
+			return mf;
+		}
+
+		MouseEvent ProcessButtonState (Curses.MouseEvent cev, MouseFlags mf)
 		{
 			return new MouseEvent () {
 				X = cev.X,
 				Y = cev.Y,
-				Flags = (MouseFlags)cev.ButtonState
+				Flags = mf
 			};
 		}
 
@@ -252,10 +426,15 @@ namespace Terminal.Gui {
 			keyUpHandler (new KeyEvent ((Key)wch));
 		}
 
+		Action<MouseEvent> mouseHandler;
+		MainLoop mainLoop;
+
 		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
 		{
 			// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
-			Curses.timeout (-1);
+			Curses.timeout (0);
+			this.mouseHandler = mouseHandler;
+			this.mainLoop = mainLoop;
 
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
 				ProcessInput (keyHandler, keyUpHandler, mouseHandler);
@@ -425,21 +604,21 @@ namespace Terminal.Gui {
 			Console.Out.Flush ();
 		}
 
-		int lastMouseInterval;
-		bool mouseGrabbed;
+		//int lastMouseInterval;
+		//bool mouseGrabbed;
 
 		public override void UncookMouse ()
 		{
-			if (mouseGrabbed)
-				return;
-			lastMouseInterval = Curses.mouseinterval (0);
-			mouseGrabbed = true;
+			//if (mouseGrabbed)
+			//	return;
+			//lastMouseInterval = Curses.mouseinterval (0);
+			//mouseGrabbed = true;
 		}
 
 		public override void CookMouse ()
 		{
-			mouseGrabbed = false;
-			Curses.mouseinterval (lastMouseInterval);
+			//mouseGrabbed = false;
+			//Curses.mouseinterval (lastMouseInterval);
 		}
 	}
 

+ 2 - 2
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -673,10 +673,10 @@ namespace Terminal.Gui {
 						keyUpHandler (key);
 				} else {
 					if (inputEvent.KeyEvent.bKeyDown) {
+						// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
+						keyHandler (new KeyEvent (map));
 						keyDownHandler (new KeyEvent (map));
 					} else {
-						// Key Up - Fire KeyDown Event and KeyStroke (ProcessKey) Event
-						keyHandler (new KeyEvent (map));
 						keyUpHandler (new KeyEvent (map));
 					}
 				}

+ 35 - 24
Terminal.Gui/MonoCurses/constants.cs

@@ -2,6 +2,8 @@
  * This file is autogenerated by the attrib.c program, do not edit
  */
 
+#define XTERM1006
+
 using System;
 
 namespace Unix.Terminal {
@@ -77,6 +79,15 @@ namespace Unix.Terminal {
 			ReportMousePosition = unchecked((int)0x8000000),
 			AllEvents = unchecked((int)0x7ffffff),
 		}
+#if XTERM1006
+		public const int LeftRightUpNPagePPage= unchecked((int)0x8);
+		public const int DownEnd = unchecked((int)0x6);
+		public const int Home = unchecked((int)0x7);
+#else
+		public const int LeftRightUpNPagePPage= unchecked((int)0x0);
+		public const int DownEnd = unchecked((int)0x0);
+		public const int Home = unchecked((int)0x0);
+#endif
 		public const int ERR = unchecked((int)0xffffffff);
 		public const int KeyBackspace = unchecked((int)0x107);
 		public const int KeyUp = unchecked((int)0x103);
@@ -110,30 +121,30 @@ namespace Unix.Terminal {
 		public const int ShiftKeyPPage = unchecked((int)0x18e);
 		public const int ShiftKeyHome = unchecked((int)0x187);
 		public const int ShiftKeyEnd = unchecked((int)0x182);
-		public const int AltKeyUp = unchecked((int)0x234);
-		public const int AltKeyDown = unchecked((int)0x20b);
-		public const int AltKeyLeft = unchecked((int)0x21f);
-		public const int AltKeyRight = unchecked((int)0x22e);
-		public const int AltKeyNPage = unchecked((int)0x224);
-		public const int AltKeyPPage = unchecked((int)0x229);
-		public const int AltKeyHome = unchecked((int)0x215);
-		public const int AltKeyEnd = unchecked((int)0x210);
-		public const int CtrlKeyUp = unchecked((int)0x236);
-		public const int CtrlKeyDown = unchecked((int)0x20d);
-		public const int CtrlKeyLeft = unchecked((int)0x221);
-		public const int CtrlKeyRight = unchecked((int)0x230);
-		public const int CtrlKeyNPage = unchecked((int)0x226);
-		public const int CtrlKeyPPage = unchecked((int)0x22b);
-		public const int CtrlKeyHome = unchecked((int)0x217);
-		public const int CtrlKeyEnd = unchecked((int)0x212);
-		public const int ShiftCtrlKeyUp = unchecked((int)0x237);
-		public const int ShiftCtrlKeyDown = unchecked((int)0x20e);
-		public const int ShiftCtrlKeyLeft = unchecked((int)0x222);
-		public const int ShiftCtrlKeyRight = unchecked((int)0x231);
-		public const int ShiftCtrlKeyNPage = unchecked((int)0x227);
-		public const int ShiftCtrlKeyPPage = unchecked((int)0x22c);
-		public const int ShiftCtrlKeyHome = unchecked((int)0x218);
-		public const int ShiftCtrlKeyEnd = unchecked((int)0x213);
+		public const int AltKeyUp = unchecked((int)0x234 + LeftRightUpNPagePPage);
+		public const int AltKeyDown = unchecked((int)0x20b + DownEnd);
+		public const int AltKeyLeft = unchecked((int)0x21f + LeftRightUpNPagePPage);
+		public const int AltKeyRight = unchecked((int)0x22e + LeftRightUpNPagePPage);
+		public const int AltKeyNPage = unchecked((int)0x224 + LeftRightUpNPagePPage);
+		public const int AltKeyPPage = unchecked((int)0x229 + LeftRightUpNPagePPage);
+		public const int AltKeyHome = unchecked((int)0x215 + Home);
+		public const int AltKeyEnd = unchecked((int)0x210 + DownEnd);
+		public const int CtrlKeyUp = unchecked((int)0x236 + LeftRightUpNPagePPage);
+		public const int CtrlKeyDown = unchecked((int)0x20d + DownEnd);
+		public const int CtrlKeyLeft = unchecked((int)0x221 + LeftRightUpNPagePPage);
+		public const int CtrlKeyRight = unchecked((int)0x230 + LeftRightUpNPagePPage);
+		public const int CtrlKeyNPage = unchecked((int)0x226 + LeftRightUpNPagePPage);
+		public const int CtrlKeyPPage = unchecked((int)0x22b + LeftRightUpNPagePPage);
+		public const int CtrlKeyHome = unchecked((int)0x217 + Home);
+		public const int CtrlKeyEnd = unchecked((int)0x212 + DownEnd);
+		public const int ShiftCtrlKeyUp = unchecked((int)0x237 + LeftRightUpNPagePPage);
+		public const int ShiftCtrlKeyDown = unchecked((int)0x20e + DownEnd);
+		public const int ShiftCtrlKeyLeft = unchecked((int)0x222 + LeftRightUpNPagePPage);
+		public const int ShiftCtrlKeyRight = unchecked((int)0x231 + LeftRightUpNPagePPage);
+		public const int ShiftCtrlKeyNPage = unchecked((int)0x227 + LeftRightUpNPagePPage);
+		public const int ShiftCtrlKeyPPage = unchecked((int)0x22c + LeftRightUpNPagePPage);
+		public const int ShiftCtrlKeyHome = unchecked((int)0x218 + Home);
+		public const int ShiftCtrlKeyEnd = unchecked((int)0x213 + DownEnd);
 
 		public const int LC_ALL = 6;
 		static public int ColorPair(int n){

+ 141 - 61
Terminal.Gui/Views/ListView.cs

@@ -63,6 +63,12 @@ namespace Terminal.Gui {
 		/// <param name="item">Item index.</param>
 		/// <param name="value">If set to <c>true</c> value.</param>
 		void SetMark (int item, bool value);
+
+		/// <summary>
+		/// Return the source as IList.
+		/// </summary>
+		/// <returns></returns>
+		IList ToList ();
 	}
 
 	/// <summary>
@@ -257,7 +263,7 @@ namespace Terminal.Gui {
 		/// Redraws the ListView
 		/// </summary>
 		/// <param name="region">Region.</param>
-		public override void Redraw(Rect region)
+		public override void Redraw (Rect region)
 		{
 			var current = ColorScheme.Focus;
 			Driver.SetAttribute (current);
@@ -279,12 +285,12 @@ namespace Terminal.Gui {
 				Move (0, row);
 				if (source == null || item >= source.Count) {
 					for (int c = 0; c < f.Width; c++)
-						Driver.AddRune(' ');
+						Driver.AddRune (' ');
 				} else {
 					if (allowsMarking) {
 						Driver.AddStr (source.IsMarked (item) ? (AllowsMultipleSelection ? "[x] " : "(o)") : (AllowsMultipleSelection ? "[ ] " : "( )"));
 					}
-					Source.Render(this, Driver, isSelected, item, col, row, f.Width-col);
+					Source.Render (this, Driver, isSelected, item, col, row, f.Width - col);
 				}
 			}
 		}
@@ -292,12 +298,12 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// This event is raised when the cursor selection has changed.
 		/// </summary>
-		public event Action SelectedChanged;
+		public event EventHandler<ListViewItemEventArgs> SelectedChanged;
 
 		/// <summary>
 		/// This event is raised on Enter key or Double Click to open the selected item.
 		/// </summary>
-		public event EventHandler OpenSelectedItem;
+		public event EventHandler<ListViewItemEventArgs> OpenSelectedItem;
 
 		/// <summary>
 		/// Handles cursor movement for this view, passes all other events.
@@ -312,27 +318,27 @@ namespace Terminal.Gui {
 			switch (kb.Key) {
 			case Key.CursorUp:
 			case Key.ControlP:
-				return MoveUp();
+				return MoveUp ();
 
 			case Key.CursorDown:
 			case Key.ControlN:
-				return MoveDown();
+				return MoveDown ();
 
 			case Key.ControlV:
 			case Key.PageDown:
-				return MovePageDown();
+				return MovePageDown ();
 
 			case Key.PageUp:
-				return MovePageUp();
+				return MovePageUp ();
 
 			case Key.Space:
-				if (MarkUnmarkRow())
+				if (MarkUnmarkRow ())
 					return true;
 				else
 					break;
 
 			case Key.Enter:
-				OpenSelectedItem?.Invoke (this, new EventArgs ());
+				OnOpenSelectedItem ();
 				break;
 
 			}
@@ -340,7 +346,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// 
+		/// Prevents marking if it's not allowed mark and if it's not allows multiple selection.
 		/// </summary>
 		/// <returns></returns>
 		public virtual bool AllowsAll ()
@@ -359,13 +365,14 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// 
+		/// Marks an unmarked row.
 		/// </summary>
 		/// <returns></returns>
-		public virtual bool MarkUnmarkRow(){
+		public virtual bool MarkUnmarkRow ()
+		{
 			if (AllowsAll ()) {
-				Source.SetMark(SelectedItem, !Source.IsMarked(SelectedItem));
-				SetNeedsDisplay();
+				Source.SetMark (SelectedItem, !Source.IsMarked (SelectedItem));
+				SetNeedsDisplay ();
 				return true;
 			}
 
@@ -373,84 +380,114 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// 
+		/// Moves to the next page.
 		/// </summary>
 		/// <returns></returns>
-		public virtual bool MovePageUp(){
+		public virtual bool MovePageUp ()
+		{
 			int n = (selected - Frame.Height);
 			if (n < 0)
 				n = 0;
-			if (n != selected){
+			if (n != selected) {
 				selected = n;
 				top = selected;
-				if (SelectedChanged != null)
-					SelectedChanged();
-				SetNeedsDisplay();
+				OnSelectedChanged ();
+				SetNeedsDisplay ();
 			}
 
 			return true;
 		}
 
 		/// <summary>
-		/// 
+		/// Moves to the previous page.
 		/// </summary>
 		/// <returns></returns>
-		public virtual bool MovePageDown(){
+		public virtual bool MovePageDown ()
+		{
 			var n = (selected + Frame.Height);
 			if (n > source.Count)
 				n = source.Count - 1;
-			if (n != selected){
+			if (n != selected) {
 				selected = n;
 				if (source.Count >= Frame.Height)
 					top = selected;
 				else
 					top = 0;
-				if (SelectedChanged != null)
-					SelectedChanged();
-				SetNeedsDisplay();
+				OnSelectedChanged ();
+				SetNeedsDisplay ();
 			}
 
 			return true;
 		}
 
 		/// <summary>
-		/// 
+		/// Moves to the next row.
 		/// </summary>
 		/// <returns></returns>
-		public virtual bool MoveDown(){
-			if (selected + 1 < source.Count){
+		public virtual bool MoveDown ()
+		{
+			if (selected + 1 < source.Count) {
 				selected++;
 				if (selected >= top + Frame.Height)
 					top++;
-				if (SelectedChanged != null)
-					SelectedChanged();
-				SetNeedsDisplay();
+				OnSelectedChanged ();
+				SetNeedsDisplay ();
 			}
 
 			return true;
 		}
 
 		/// <summary>
-		/// 
+		/// Moves to the previous row.
 		/// </summary>
 		/// <returns></returns>
-		public virtual bool MoveUp(){
-			if (selected > 0){
+		public virtual bool MoveUp ()
+		{
+			if (selected > 0) {
 				selected--;
 				if (selected < top)
 					top = selected;
-				if (SelectedChanged != null)
-					SelectedChanged();
-				SetNeedsDisplay();
+				OnSelectedChanged ();
+				SetNeedsDisplay ();
 			}
 
 			return true;
 		}
 
+		int lastSelectedItem = -1;
+
+		/// <summary>
+		/// Invokes the SelectedChanged event if it is defined.
+		/// </summary>
+		/// <returns></returns>
+		public virtual bool OnSelectedChanged ()
+		{
+			if (selected != lastSelectedItem) {
+				var value = source.ToList () [selected];
+				SelectedChanged?.Invoke (this, new ListViewItemEventArgs (selected, value));
+				lastSelectedItem = selected;
+				return true;
+			}
+
+			return false;
+		}
+
+		/// <summary>
+		/// Invokes the OnOpenSelectedItem event if it is defined.
+		/// </summary>
+		/// <returns></returns>
+		public virtual bool OnOpenSelectedItem ()
+		{
+			var value = source.ToList () [selected];
+			OpenSelectedItem?.Invoke (this, new ListViewItemEventArgs (selected, value));
+
+			return true;
+		}
+
 		/// <summary>
 		/// Positions the cursor in this view
 		/// </summary>
-		public override void PositionCursor()
+		public override void PositionCursor ()
 		{
 			if (allowsMarking)
 				Move (1, selected - top);
@@ -461,7 +498,8 @@ namespace Terminal.Gui {
 		///<inheritdoc cref="MouseEvent(Gui.MouseEvent)"/>
 		public override bool MouseEvent(MouseEvent me)
 		{
-			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked))
+			if (!me.Flags.HasFlag (MouseFlags.Button1Clicked) && !me.Flags.HasFlag (MouseFlags.Button1DoubleClicked) &&
+				me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp)
 				return false;
 
 			if (!HasFocus)
@@ -470,6 +508,14 @@ namespace Terminal.Gui {
 			if (source == null)
 				return false;
 
+			if (me.Flags == MouseFlags.WheeledDown) {
+				MoveDown ();
+				return true;
+			} else if (me.Flags == MouseFlags.WheeledUp) {
+				MoveUp ();
+				return true;
+			}
+
 			if (me.Y + top >= source.Count)
 				return true;
 
@@ -479,10 +525,10 @@ namespace Terminal.Gui {
 				SetNeedsDisplay ();
 				return true;
 			}
-			SelectedChanged?.Invoke ();
+			OnSelectedChanged ();
 			SetNeedsDisplay ();
 			if (me.Flags == MouseFlags.Button1DoubleClicked)
-				OpenSelectedItem?.Invoke (this, new EventArgs ());
+				OnOpenSelectedItem ();
 			return true;
 		}
 	}
@@ -497,7 +543,7 @@ namespace Terminal.Gui {
 		int count;
 
 		/// <summary>
-		/// constructor
+		/// Constructor based on a source.
 		/// </summary>
 		/// <param name="source"></param>
 		public ListWrapper (IList source)
@@ -508,7 +554,7 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Count of items.
+		/// Returns the amount of items in the source.
 		/// </summary>
 		public int Count => src.Count;
 
@@ -519,7 +565,7 @@ namespace Terminal.Gui {
 			for (int i = 0; i < byteLen;) {
 				(var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen);
 				var count = Rune.ColumnWidth (rune);
-				if (used+count >= width)
+				if (used + count >= width)
 					break;
 				driver.AddRune (rune);
 				used += count;
@@ -531,15 +577,15 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Renders an item in the the list.
+		/// Method that render to the appropriate type based on the type of the item passed to it.
 		/// </summary>
-		/// <param name="container"></param>
-		/// <param name="driver"></param>
-		/// <param name="marked"></param>
-		/// <param name="item"></param>
-		/// <param name="col"></param>
-		/// <param name="line"></param>
-		/// <param name="width"></param>
+		/// <param name="container">The ListView.</param>
+		/// <param name="driver">The driver used by the caller.</param>
+		/// <param name="marked">Informs if it's marked or not.</param>
+		/// <param name="item">The item.</param>
+		/// <param name="col">The col where to move.</param>
+		/// <param name="line">The line where to move.</param>
+		/// <param name="width">The item width.</param>
 		public void Render (ListView container, ConsoleDriver driver, bool marked, int item, int col, int line, int width)
 		{
 			container.Move (col, line);
@@ -553,10 +599,10 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Returns true of the item is marked. false if not.
+		/// Returns true if the item is marked, false otherwise.
 		/// </summary>
-		/// <param name="item"></param>
-		/// <returns></returns>
+		/// <param name="item">The item.</param>
+		/// <returns><c>true</c>If is marked.<c>false</c>otherwise.</returns>
 		public bool IsMarked (int item)
 		{
 			if (item >= 0 && item < count)
@@ -565,14 +611,48 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Sets the marked state of an item.
+		/// Sets the item as marked or unmarked based on the value is true or false, respectively.
 		/// </summary>
-		/// <param name="item"></param>
-		/// <param name="value"></param>
+		/// <param name="item">The item</param>
+		/// <param name="value"><true>Marks the item.</true><false>Unmarked the item.</false>The value.</param>
 		public void SetMark (int item, bool value)
 		{
 			if (item >= 0 && item < count)
 				marks [item] = value;
 		}
+
+		/// <summary>
+		/// Returns the source as IList.
+		/// </summary>
+		/// <returns></returns>
+		public IList ToList ()
+		{
+			return src;
+		}
+	}
+
+	/// <summary>
+	/// Gets the item and value to use in an event handler.
+	/// </summary>
+	public class ListViewItemEventArgs : EventArgs {
+		/// <summary>
+		/// The item.
+		/// </summary>
+		public int Item { get; }
+		/// <summary>
+		/// The item value.
+		/// </summary>
+		public object Value { get; }
+
+		/// <summary>
+		/// Constructor to sets the item and value passed from.
+		/// </summary>
+		/// <param name="item">The item.</param>
+		/// <param name="value">The item value</param>
+		public ListViewItemEventArgs (int item, object value)
+		{
+			Item = item;
+			Value = value;
+		}
 	}
 }

+ 10 - 15
Terminal.Gui/Views/Menu.cs

@@ -347,7 +347,7 @@ namespace Terminal.Gui {
 			});
 		}
 
-		public override bool KeyDown (KeyEvent keyEvent)
+		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
 				host.CloseAllMenus ();
@@ -360,9 +360,9 @@ namespace Terminal.Gui {
 		public override bool ProcessHotKey (KeyEvent keyEvent)
 		{
 			// To ncurses simulate a AltMask key pressing Alt+Space because
-			// it can´t detect an alone special key down was pressed.
+			// it can�t detect an alone special key down was pressed.
 			if (keyEvent.IsAlt && keyEvent.Key == Key.AltMask) {
-				KeyDown (keyEvent);
+				OnKeyDown (keyEvent);
 				return true;
 			}
 
@@ -563,8 +563,8 @@ namespace Terminal.Gui {
 
 		bool openedByAltKey;
 
-		///<inheritdoc cref="KeyDown"/>
-		public override bool KeyDown (KeyEvent keyEvent)
+		///<inheritdoc cref="OnKeyDown"/>
+		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
 				openedByAltKey = true;
@@ -574,13 +574,8 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-		/// <summary>
-		/// Track Alt key-up events. On Windows, when a user releases Alt (without another key), the menu gets focus but doesn't open.
-		/// We mimic that behavior here.
-		/// </summary>
-		/// <param name="keyEvent"></param>
-		/// <returns></returns>
-		public override bool KeyUp (KeyEvent keyEvent)
+		///<inheritdoc cref="OnKeyUp"/>
+		public override bool OnKeyUp (KeyEvent keyEvent)
 		{
 			if (keyEvent.IsAlt) {
 				// User pressed Alt - this may be a precursor to a menu accelerator (e.g. Alt-F)
@@ -1001,10 +996,10 @@ namespace Terminal.Gui {
 			}
 
 			// To ncurses simulate a AltMask key pressing Alt+Space because
-			// it can´t detect an alone special key down was pressed.
+			// it can�t detect an alone special key down was pressed.
 			if (kb.IsAlt && kb.Key == Key.AltMask && openMenu == null) {
-				KeyDown (kb);
-				KeyUp (kb);
+				OnKeyDown (kb);
+				OnKeyUp (kb);
 				return true;
 			} else if (kb.IsAlt) {
 				if (FindAndOpenMenuByHotkey (kb)) return true;

+ 43 - 2
Terminal.Gui/Views/ScrollView.cs

@@ -14,6 +14,8 @@
 // - Perhaps allow an option to not display the scrollbar arrow indicators?
 
 using System;
+using System.Reflection;
+
 namespace Terminal.Gui {
 	/// <summary>
 	/// ScrollBarViews are views that display a 1-character scrollbar, either horizontal or vertical
@@ -73,7 +75,7 @@ namespace Terminal.Gui {
 		/// <param name="rect">Frame for the scrollbar.</param>
 		/// <param name="size">The size that this scrollbar represents.</param>
 		/// <param name="position">The position within this scrollbar.</param>
-		/// <param name="isVertical">If set to <c>true</c> this is a vertical scrollbar, otherwize, the scrollbar is horizontal.</param>
+		/// <param name="isVertical">If set to <c>true</c> this is a vertical scrollbar, otherwise, the scrollbar is horizontal.</param>
 		public ScrollBarView (Rect rect, int size, int position, bool isVertical) : base (rect)
 		{
 			vertical = isVertical;
@@ -314,9 +316,35 @@ namespace Terminal.Gui {
 		/// <param name="view">The view to add to the scrollview.</param>
 		public override void Add (View view)
 		{
+			if (!IsOverridden (view)) {
+				view.MouseEnter += View_MouseEnter;
+				view.MouseLeave += View_MouseLeave;
+				vertical.MouseEnter += View_MouseEnter;
+				vertical.MouseLeave += View_MouseLeave;
+				horizontal.MouseEnter += View_MouseEnter;
+				horizontal.MouseLeave += View_MouseLeave;
+			}
 			contentView.Add (view);
 		}
 
+		void View_MouseLeave (object sender, MouseEvent e)
+		{
+			Application.UngrabMouse ();
+		}
+
+		void View_MouseEnter (object sender, MouseEvent e)
+		{
+			Application.GrabMouse (this);
+		}
+
+		bool IsOverridden (View view)
+		{
+			Type t = view.GetType ();
+			MethodInfo m = t.GetMethod ("MouseEvent");
+
+			return m.DeclaringType == t && m.GetBaseDefinition ().DeclaringType == typeof (Responder);
+		}
+
 		/// <summary>
 		/// Gets or sets the visibility for the horizontal scroll indicator.
 		/// </summary>
@@ -463,7 +491,7 @@ namespace Terminal.Gui {
 			switch (kb.Key) {
 			case Key.CursorUp:
 				return ScrollUp (1);
-			case (Key) 'v' | Key.AltMask:
+			case (Key)'v' | Key.AltMask:
 			case Key.PageUp:
 				return ScrollUp (Bounds.Height);
 
@@ -489,5 +517,18 @@ namespace Terminal.Gui {
 			}
 			return false;
 		}
+
+		public override bool MouseEvent (MouseEvent me)
+		{
+			if (me.Flags != MouseFlags.WheeledDown && me.Flags != MouseFlags.WheeledUp)
+				return false;
+
+			if (me.Flags == MouseFlags.WheeledDown)
+				ScrollDown (1);
+			else if (me.Flags == MouseFlags.WheeledUp)
+				ScrollUp (1);
+
+			return true;
+		}
 	}
 }

+ 11 - 7
Terminal.Gui/Views/StatusBar.cs

@@ -107,8 +107,12 @@ namespace Terminal.Gui {
 			Items = items;
 			CanFocus = false;
 			ColorScheme = Colors.Menu;
+			X = 0;
+			Y = Driver.Rows - 1;
+			Width = Dim.Fill ();
+			Height = 1;
 
-			Application.OnLoad += () => {
+			Application.Loaded += (sender, e) => {
 				X = 0;
 				Height = 1;
 #if SNAP_TO_TOP
@@ -120,7 +124,7 @@ namespace Terminal.Gui {
 				case StatusBarStyle.SnapToBottom:
 #endif
 					if (Parent == null) {
-						Y = Application.Driver.Rows - 1; // TODO: using internals of Application
+						Y = e.Rows - 1; 
 					} else {
 						Y = Pos.Bottom (Parent);
 					}
@@ -141,11 +145,11 @@ namespace Terminal.Gui {
 		///<inheritdoc cref="Redraw"/>
 		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 ();
-			}
+			//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);

+ 44 - 6
Terminal.Gui/Views/TextField.cs

@@ -28,6 +28,11 @@ namespace Terminal.Gui {
 		/// </summary>
 		public bool Used { get => used; set { used = value; } }
 
+		/// <summary>
+		/// If set to true its not allow any changes in the text.
+		/// </summary>
+		public bool ReadOnly { get; set; } = false;
+
 		/// <summary>
 		///   Changed event, raised when the text has clicked.
 		/// </summary>
@@ -95,7 +100,7 @@ namespace Terminal.Gui {
 			set {
 				base.Frame = value;
 				var w = base.Frame.Width;
-				//first = point > w ? point - w : 0;
+				first = point > w ? point - w : 0;
 				Adjust ();
 			}
 		}
@@ -115,6 +120,9 @@ namespace Terminal.Gui {
 			}
 
 			set {
+				if (ReadOnly)
+					return;
+
 				var oldText = ustring.Make (text);
 				text = TextModel.ToRunes (value);
 				if (!Secret && !isFromHistory) {
@@ -184,13 +192,16 @@ namespace Terminal.Gui {
 			int col = 0;
 			int width = Frame.Width;
 			var tcount = text.Count;
+			var roc = new Attribute (Color.DarkGray, Color.Gray);
 			for (int idx = 0; idx < tcount; idx++){
 				var rune = text [idx];
 				if (idx < p)
 					continue;
 				var cols = Rune.ColumnWidth (rune);
-				if (col == point && HasFocus && !Used && SelectedLength == 0)
+				if (col == point && HasFocus && !Used && SelectedLength == 0 && !ReadOnly)
 					Driver.SetAttribute (Colors.Menu.HotFocus);
+				else if (ReadOnly)
+					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : roc);
 				else
 					Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? color.Focus : ColorScheme.Focus);
 				if (col + cols <= width)
@@ -261,6 +272,9 @@ namespace Terminal.Gui {
 			switch (kb.Key) {
 			case Key.DeleteChar:
 			case Key.ControlD:
+				if (ReadOnly)
+					return true;
+
 				if (SelectedLength == 0) {
 					if (text.Count == 0 || text.Count == point)
 						return true;
@@ -275,6 +289,9 @@ namespace Terminal.Gui {
 
 			case Key.Delete:
 			case Key.Backspace:
+				if (ReadOnly)
+					return true;
+
 				if (SelectedLength == 0) {
 					if (point == 0)
 						return true;
@@ -373,6 +390,9 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.ControlK: // kill-to-end
+				if (ReadOnly)
+					return true;
+
 				ClearAllSelection ();
 				if (point >= text.Count)
 					return true;
@@ -383,6 +403,9 @@ namespace Terminal.Gui {
 
 			// Undo
 			case Key.ControlZ:
+				if (ReadOnly)
+					return true;
+
 				if (historyText != null && historyText.Count > 0) {
 					isFromHistory = true;
 					if (idxhistoryText > 0)
@@ -396,6 +419,9 @@ namespace Terminal.Gui {
 
 			//Redo
 			case Key.ControlY: // Control-y, yank
+				if (ReadOnly)
+					return true;
+
 				if (historyText != null && historyText.Count > 0) {
 					isFromHistory = true;
 					if (idxhistoryText < historyText.Count - 1) {
@@ -455,6 +481,9 @@ namespace Terminal.Gui {
 				break;
 
 			case Key.ControlX:
+				if (ReadOnly)
+					return true;
+
 				Cut ();
 				break;
 
@@ -472,6 +501,9 @@ namespace Terminal.Gui {
 				if (kb.Key < Key.Space || kb.Key > Key.CharMask)
 					return false;
 
+				if (ReadOnly)
+					return true;
+
 				if (SelectedLength != 0) {
 					DeleteSelectedText ();
 					oldCursorPos = point;
@@ -639,7 +671,7 @@ namespace Terminal.Gui {
 				point = text.Count;
 			if (point < first)
 				point = 0;
-			return x;
+			return point;
 		}
 
 		void PrepareSelection (int x, int direction = 0)
@@ -682,8 +714,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Copy the selected text to the clipboard.
 		/// </summary>
-		public void Copy ()
+		public virtual void Copy ()
 		{
+			if (Secret)
+				return;
+
 			if (SelectedLength != 0) {
 				Clipboard.Contents = SelectedText;
 			}
@@ -692,7 +727,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Cut the selected text to the clipboard.
 		/// </summary>
-		public void Cut ()
+		public virtual void Cut ()
 		{
 			if (SelectedLength != 0) {
 				Clipboard.Contents = SelectedText;
@@ -715,8 +750,11 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Paste the selected text from the clipboard.
 		/// </summary>
-		public void Paste ()
+		public virtual void Paste ()
 		{
+			if (ReadOnly)
+				return;
+
 			string actualText = Text.ToString ();
 			int start = SelectedStart == -1 ? CursorPosition : SelectedStart;
 			ustring cbTxt = Clipboard.Contents?.ToString () ?? "";

+ 28 - 0
Terminal.Gui/Views/TextView.cs

@@ -39,6 +39,7 @@ namespace Terminal.Gui {
 			if (file == null)
 				throw new ArgumentNullException (nameof (file));
 			try {
+				FilePath = file;
 				var stream = File.OpenRead (file);
 			} catch {
 				return false;
@@ -47,6 +48,19 @@ namespace Terminal.Gui {
 			return true;
 		}
 
+		public bool CloseFile ()
+		{
+			if (FilePath == null)
+				throw new ArgumentNullException (nameof (FilePath));
+			try {
+				FilePath = null;
+				lines = new List<List<Rune>> ();
+			} catch {
+				return false;
+			}
+			return true;
+		}
+
 		// Turns the ustring into runes, this does not split the 
 		// contents on a newline if it is present.
 		internal static List<Rune> ToRunes (ustring str)
@@ -120,6 +134,8 @@ namespace Terminal.Gui {
 			return sb.ToString ();
 		}
 
+		public string FilePath { get; set; }
+
 		/// <summary>
 		/// The number of text lines in the model
 		/// </summary>
@@ -351,6 +367,18 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
+		/// <summary>
+		/// Closes the contents of the stream into the TextView.
+		/// </summary>
+		/// <returns><c>true</c>, if stream was closed, <c>false</c> otherwise.</returns>
+		public bool CloseFile()
+		{
+			ResetPosition ();
+			var res = model.CloseFile ();
+			SetNeedsDisplay ();
+			return res;
+		}
+
 		/// <summary>
 		///    The current cursor row.
 		/// </summary>

+ 11 - 8
Terminal.sln

@@ -1,13 +1,14 @@
-
 Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio 2012
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30114.128
+MinimumVisualStudioVersion = 10.0.40219.1
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{B0A602CD-E176-449D-8663-64238D54F857}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
 EndProject
 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
 EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}"
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UICatalog", "UICatalog\UICatalog.csproj", "{88979F89-9A42-448F-AE3E-3060145F6375}"
 EndProject
 Global
 	GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -27,15 +28,17 @@ Global
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.Build.0 = Debug|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
 		{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = Release|x86
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|Any CPU.Build.0 = Debug|Any CPU
 		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.ActiveCfg = Debug|Any CPU
 		{88979F89-9A42-448F-AE3E-3060145F6375}.Debug|x86.Build.0 = Debug|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.ActiveCfg = Release|Any CPU
-		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|Any CPU.Build.0 = Release|Any CPU
 		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.ActiveCfg = Release|Any CPU
 		{88979F89-9A42-448F-AE3E-3060145F6375}.Release|x86.Build.0 = Release|Any CPU
 	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {9F8F8A4D-7B8D-4C2A-AC5E-CD7117F74C03}
+	EndGlobalSection
 	GlobalSection(MonoDevelopProperties) = preSolution
 		Policies = $0
 		$0.TextStylePolicy = $1

+ 16 - 7
UICatalog/Program.cs

@@ -1,5 +1,6 @@
 using NStack;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Globalization;
@@ -62,7 +63,7 @@ namespace UICatalog {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "", () => Application.RequestStop() )
 				}),
-				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query (0, 6, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
+				new MenuBarItem ("_About...", "About this app", () =>  MessageBox.Query (0, 10, "About UI Catalog", "UI Catalog is a comprehensive sample library for Terminal.Gui", "Ok")),
 			});
 
 			_leftPane = new Window ("Categories") {
@@ -119,7 +120,7 @@ namespace UICatalog {
 			_rightPane.Add (_scenarioListView);
 
 			_categoryListView.SelectedItem = 0;
-			CategoryListView_SelectedChanged ();
+			_categoryListView.OnSelectedChanged ();
 
 			_statusBar = new StatusBar (new StatusItem [] {
 				//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
@@ -148,7 +149,9 @@ namespace UICatalog {
 			}
 
 			_top = Application.Top;
-			_top.OnKeyUp += KeyUpHandler;
+
+			_top.KeyUp += KeyUpHandler;
+
 			_top.Add (_menu);
 			_top.Add (_leftPane);
 			_top.Add (_rightPane);
@@ -227,6 +230,12 @@ namespace UICatalog {
 					used++;
 				}
 			}
+
+			public IList ToList ()
+			{
+				return Scenarios;
+			}
+
 		}
 
 		/// <summary>
@@ -235,7 +244,7 @@ namespace UICatalog {
 		/// to not be impacted. Same as for tabs.
 		/// </summary>
 		/// <param name="ke"></param>
-		private static void KeyUpHandler (KeyEvent ke)
+		private static void KeyUpHandler (object sender, View.KeyEventEventArgs a)
 		{
 			if (_runningScenario != null) {
 				//switch (ke.Key) {
@@ -244,8 +253,8 @@ namespace UICatalog {
 				//	break;
 				//case Key.Enter:
 				//	break;
-				//}
-			} else if (ke.Key == Key.Tab || ke.Key == Key.BackTab) {
+				//}<
+			} else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.Key == Key.BackTab) {
 				// BUGBUG: Work around Issue #434 by implementing our own TAB navigation
 				if (_top.MostFocused == _categoryListView)
 					_top.SetFocus (_rightPane);
@@ -254,7 +263,7 @@ namespace UICatalog {
 			}
 		}
 
-		private static void CategoryListView_SelectedChanged ()
+		private static void CategoryListView_SelectedChanged (object sender, ListViewItemEventArgs e)
 		{
 			var item = _categories [_categoryListView.SelectedItem];
 			List<Type> newlist;

+ 0 - 1
UICatalog/Properties/launchSettings.json

@@ -2,7 +2,6 @@
   "profiles": {
     "UICatalog": {
       "commandName": "Project"
-
     }
   }
 }

+ 1 - 1
UICatalog/README.md

@@ -119,4 +119,4 @@ For complete control, the `Init` and `Run` overrides can be implemented. The `ba
 - Use the `Bug Rero` Category for `Scnarios` that reproduce bugs. 
 	- Include the Github Issue # in the Description.
 	- Once the bug has been fixed in `master` submit another PR to remove the `Scenario` (or modify it to provide a good regression test).
-- Tag bugs or suggestions for `UI Catalog` in the main `Terminal.Gui` Github Issues with "UICatalog: ".
+- Tag bugs or suggestions for `UI Catalog` as [`Terminal.Gui` Github Issues](https://github.com/migueldeicaza/gui.cs/issues) with "UICatalog: ".

+ 23 - 1
UICatalog/Scenario.cs

@@ -17,7 +17,9 @@ namespace UICatalog {
 	/// The Main program uses reflection to find all sceanarios and adds them to the
 	/// ListViews. Press ENTER to run the selected sceanrio. Press CTRL-Q to exit it.
 	/// </summary>
-	public class Scenario {
+	public class Scenario : IDisposable {
+		private bool _disposedValue;
+
 		/// <summary>
 		/// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
 		/// </summary>
@@ -177,5 +179,25 @@ namespace UICatalog {
 			}
 			return objects;
 		}
+
+		protected virtual void Dispose (bool disposing)
+		{
+			if (!_disposedValue) {
+				if (disposing) {
+					// TODO: dispose managed state (managed objects)
+				}
+
+				// TODO: free unmanaged resources (unmanaged objects) and override finalizer
+				// TODO: set large fields to null
+				_disposedValue = true;
+			}
+		}
+
+		public void Dispose ()
+		{
+			// Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method
+			Dispose (disposing: true);
+			GC.SuppressFinalize (this);
+		}
 	}
 }

+ 155 - 0
UICatalog/Scenarios/ComputedLayout.cs

@@ -0,0 +1,155 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using Terminal.Gui;
+
+namespace UICatalog {
+	/// <summary>
+	/// This Scenario demonstrates how to use Termina.gui's Dim and Pos Layout System. 
+	/// [x] - Using Dim.Fill to fill a window
+	/// [x] - Using Dim.Fill and Dim.Pos to automatically align controls based on an initial control
+	/// [ ] - ...
+	/// </summary>
+	[ScenarioMetadata (Name: "Computed Layout", Description: "Demonstrates using the Computed (Dim and Pos) Layout System")]
+	[ScenarioCategory ("Layout")]
+	class ComputedLayout : Scenario {
+
+		public override void Setup ()
+		{
+			//Top.LayoutStyle = LayoutStyle.Computed;
+			// Demonstrate using Dim to create a horizontal ruler that always measures the parent window's width
+			// BUGBUG: Dim.Fill returns too big a value sometimes.
+			const string rule = "|123456789";
+			var horizontalRuler = new Label ("") {
+				X = 0,
+				Y = 0,
+				Width = Dim.Fill (1),  // BUGBUG: I don't think this should be needed; DimFill() should respect container's frame. X does.
+				ColorScheme = Colors.Error
+			};
+
+			Application.Resized += (sender, a) => {
+				horizontalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(horizontalRuler.Bounds.Width) / (double)rule.Length)) [0..(horizontalRuler.Bounds.Width)];
+			};
+
+			Win.Add (horizontalRuler);
+
+			// Demonstrate using Dim to create a vertical ruler that always measures the parent window's height
+			// TODO: Either build a custom control for this or implement linewrap in Label #352
+			//var verticalRuler = new Label ("") {
+			//	X = 0,
+			//	Y = 0,
+			//	Width = 1,
+			//	Height = Dim.Fill (),
+			//	ColorScheme = Colors.Error
+			//};
+
+			//Application.OnResized += () => {
+			//	verticalRuler.Text = rule.Repeat ((int)Math.Ceiling ((double)(verticalRuler.Bounds.Height) / (double)rule.Length)) [0..(verticalRuler.Bounds.Height)];
+			//};
+
+			//Win.Add (verticalRuler);
+
+
+			// Demonstrate using Dim to create a window that fills the parent with a margin
+			int margin = 10;
+			var subWin = new Window ($"Centered Sub Window with {margin} character margin") {
+				X = Pos.Center(),
+				Y = 2,
+				Width = Dim.Fill (margin),
+				Height = 7
+			};
+			Win.Add (subWin);
+
+			int i = 1;
+			string txt = "Resize the terminal to see computed layout in action.";
+			var labelList = new List<Label> ();
+			labelList.Add (new Label ($"The lines below show different TextAlignments"));
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
+			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()), ColorScheme = Colors.Dialog });
+
+			subWin.Add (labelList.ToArray ());
+
+			// Demonstrate Dim & Pos using percentages - a TextField that is 20% height and 80% wide
+			var textView= new TextView () {
+				X = Pos.Center (),
+				Y = Pos.Percent (50),
+				Width = Dim.Percent (80),
+				Height = Dim.Percent (20),
+				ColorScheme = Colors.TopLevel,
+			};
+			textView.Text = "This text view should be half-way down the terminal,\n20% of its height, and 80% of its width.";
+			Win.Add (textView);
+
+			//// Demonstrate AnchorEnd - Button anchored to bottom of textView
+			//var clearButton = new Button ("Clear") {
+			//	X = Pos.AnchorEnd (),
+			//	Y = Pos.AnchorEnd (),
+			//	Width = 15,
+			//	Height = 1
+			//};
+			//Win.Add (clearButton);
+
+			// Demonstrate At - Absolute Layout using Pos
+			var absoluteButton = new Button ("At(10,10)") {
+				X = Pos.At(10),
+				Y = Pos.At(10)
+			};
+			Win.Add (absoluteButton);
+
+			// Centering multiple controls horizontally. 
+			// This is intentionally convoluted to illustrate potential bugs.
+			var bottomLabel = new Label ("This should be the last line (Bug #xxx).") {
+				TextAlignment = Terminal.Gui.TextAlignment.Centered,
+				ColorScheme = Colors.TopLevel,
+				Width = Dim.Fill (),
+				X = Pos.Center (),
+				Y = Pos.Bottom (Win) - 3  // BUGBUG: -1 should be just above border; but it has to be -3
+			};
+
+			var centerButton = new Button ("Center") {
+				X = Pos.Center (),
+				Y = Pos.Top(bottomLabel) - 1
+			};
+			var leftButton = new Button ("Left") {
+				Y = Pos.Top (bottomLabel) - 1
+			};
+			var rightButton = new Button ("Right") {
+				Y = Pos.Top (bottomLabel) - 1
+			};
+
+			leftButton.X = Pos.Left (centerButton) - leftButton.Frame.Width - 5;
+			rightButton.X = Pos.Right (centerButton) + 5;
+
+			Win.Add (bottomLabel);
+			Win.Add (leftButton);
+			Win.Add (centerButton);
+			Win.Add (rightButton);
+
+		}
+
+		public override void Run ()
+		{
+			base.Run ();
+		}
+	}
+
+	public static class StringExtensions {
+		public static string Repeat (this string instr, int n)
+		{
+			if (n <= 0) {
+				return null;
+			}
+
+			if (string.IsNullOrEmpty (instr) || n == 1) {
+				return instr;
+			}
+
+			return new StringBuilder (instr.Length * n)
+				.Insert (0, instr, n)
+				.ToString ();
+		}
+	}
+}

+ 0 - 82
UICatalog/Scenarios/DimAndPosLayout.cs

@@ -1,82 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using Terminal.Gui;
-
-namespace UICatalog {
-	/// <summary>
-	/// This Scenario demonstrates how to use Termina.gui's Dim and Pos Layout System. 
-	/// [x] - Using Dim.Fill to fill a window
-	/// [x] - Using Dim.Fill and Dim.Pos to automatically align controls based on an initial control
-	/// [ ] - ...
-	/// </summary>
-	[ScenarioMetadata (Name: "DimAndPosLayout", Description: "Demonstrates using the Dim and Pos Layout System")]
-	[ScenarioCategory ("Layout")]
-	class DimAndPosLayout : Scenario {
-
-		public override void Setup ()
-		{
-			Top.LayoutStyle = LayoutStyle.Computed;
-			// Demonstrate using Dim to create a ruler that always measures the top-level window's width
-			// BUGBUG: Dim.Fill returns too big a value sometimes.
-			//const string rule = "|123456789";
-			//var labelRuler = new Label ("ruler") {
-			//	X = 0,
-			//	Y = 0,
-			//	Width = Dim.Fill (1),  // BUGBUG: I don't think this should be needed; DimFill() should respect container's frame. X does.
-			//	ColorScheme = Colors.Error
-			//};
-
-			//Application.OnResized += () => {
-			//	labelRuler.Text = rule.Repeat ((int)Math.Ceiling((double)(labelRuler.Bounds.Width) / (double)rule.Length))[0..(labelRuler.Bounds.Width)];
-			//};
-
-			//win.Add (labelRuler);
-
-			// Demonstrate using Dim to create a window that fills the parent with a margin
-			int margin = 20;
-			var subWin = new Window ($"Sub Windoww with {margin} character margin") {
-				X = margin,
-				Y = 2,
-				Width = Dim.Fill (margin),
-				Height = Dim.Fill ()
-			};
-			Win.Add (subWin);
-
-			int i = 1;
-			string txt = "Hello world, how are you doing today";
-			var labelList = new List<Label> ();
-			labelList.Add (new Label ($"Label:"));
-			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Left, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
-			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Right, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
-			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Centered, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
-			labelList.Add (new Label ($"{i++}-{txt}") { TextAlignment = Terminal.Gui.TextAlignment.Justified, Width = Dim.Fill (1), X = 0, Y = Pos.Bottom (labelList.LastOrDefault ()) + 1, ColorScheme = Colors.Dialog });
-
-			subWin.Add (labelList.ToArray ());
-			//subWin.LayoutSubviews ();
-		}
-
-		public override void Run ()
-		{
-			base.Run ();
-		}
-	}
-
-	public static class StringExtensions {
-		public static string Repeat (this string instr, int n)
-		{
-			if (n <= 0) {
-				return null;
-			}
-
-			if (string.IsNullOrEmpty (instr) || n == 1) {
-				return instr;
-			}
-
-			return new StringBuilder (instr.Length * n)
-				.Insert (0, instr, n)
-				.ToString ();
-		}
-	}
-}

+ 5 - 5
UICatalog/Scenarios/Keys.cs

@@ -43,8 +43,8 @@ namespace UICatalog {
 			public override bool ProcessColdKey (KeyEvent keyEvent)
 			{
 				_processColdKeyList.Add (keyEvent.ToString ());
-				return base.ProcessHotKey (keyEvent);
 
+				return base.ProcessColdKey (keyEvent);
 			}
 		}
 
@@ -92,7 +92,7 @@ namespace UICatalog {
 			};
 			Win.Add (labelKeypress);
 
-			Win.OnKeyPress += (KeyEvent keyEvent) => labelKeypress.Text = keyEvent.ToString ();
+			Win.KeyPress += (sender, a) => labelKeypress.Text = a.KeyEvent.ToString ();
 
 			// Key stroke log:
 			var keyLogLabel = new Label ("Key stroke log:") {
@@ -119,9 +119,9 @@ namespace UICatalog {
 				keyStrokeListView.MoveDown ();
 			}
 
-			Win.OnKeyDown += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Down");
-			Win.OnKeyPress += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Press");
-			Win.OnKeyUp += (KeyEvent keyEvent) => KeyDownPressUp (keyEvent, "Up");
+			Win.KeyDown += (sender, a) => KeyDownPressUp (a.KeyEvent, "Down");
+			Win.KeyPress += (sender, a) => KeyDownPressUp (a.KeyEvent, "Press");
+			Win.KeyUp += (sender, a) => KeyDownPressUp (a.KeyEvent, "Up");
 
 			// ProcessKey log:
 			// BUGBUG: Label is not positioning right with Pos, so using TextField instead

+ 66 - 11
UICatalog/Scenarios/Progress.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Threading;
 using Terminal.Gui;
 
 namespace UICatalog {
@@ -9,39 +10,93 @@ namespace UICatalog {
 	[ScenarioCategory ("Controls")]
 	class Progress : Scenario {
 
-		private ProgressBar _progressBar;
+		private ProgressBar _activityProgressBar;
+		private ProgressBar _pulseProgressBar;
+		private Timer _timer;
+		private object _timeoutToken;
+
 		public override void Setup ()
 		{
-			Win.Add (new Button ("Start") {
-				X = Pos.Center () - 20,
+			var pulseButton = new Button ("Pulse") {
+				X = Pos.Center (),
 				Y = Pos.Center () - 5,
+				Clicked = () => Pulse ()
+			};
+
+			Win.Add (new Button ("Start Timer") {
+				X = Pos.Left(pulseButton) - 20,
+				Y = Pos.Y(pulseButton),
 				Clicked = () => Start ()
-			}); ;
+			});
 
-			Win.Add (new Button ("Stop") {
-				X = Pos.Center () + 10,
-				Y = Pos.Center () - 5,
+			Win.Add (new Button ("Stop Timer") {
+				X = Pos.Right (pulseButton) + 20, // BUGBUG: Right is somehow adding additional width
+				Y = Pos.Y (pulseButton),
 				Clicked = () => Stop()
 			});
 
-			_progressBar = new ProgressBar () {
+			Win.Add (pulseButton);
+
+			_activityProgressBar = new ProgressBar () {
 				X = Pos.Center (),
 				// BUGBUG: If you remove the +1 below the control is drawn at top?!?!
 				Y = Pos.Center ()+1,
 				Width = 30,
 				Fraction = 0.25F,
 			};
-			Win.Add (_progressBar);
+			Win.Add (_activityProgressBar);
+
+			_pulseProgressBar = new ProgressBar () {
+				X = Pos.Center (),
+				// BUGBUG: If you remove the +1 below the control is drawn at top?!?!
+				Y = Pos.Center () + 3,
+				Width = 30,
+			};
+			Win.Add (_pulseProgressBar);
+		}
+
+		protected override void Dispose (bool disposing)
+		{
+			_timer?.Dispose ();
+			_timer = null;
+			Application.MainLoop.RemoveTimeout (_timeoutToken);
+			base.Dispose (disposing);
+		}
+
+		private void Pulse ()
+		{
+			if (_activityProgressBar.Fraction + 0.01F >= 1) {
+				_activityProgressBar.Fraction = 0F;
+			} else {
+				_activityProgressBar.Fraction += 0.01F;
+			}
+			_pulseProgressBar.Pulse ();
 		}
 
 		private void Start ()
 		{
-			_progressBar.Fraction = 0F;
+			_timer?.Dispose ();
+			_timer = null;
+
+			_activityProgressBar.Fraction = 0F;
+			_pulseProgressBar.Fraction = 0F;
+
+			_timer = new Timer ((o) => Application.MainLoop.Invoke (() => Pulse ()), null, 0, 10);
+
+			// BUGBUG: This timeout does nothing but return true, however it trigger the Application.MainLoop
+			// to run the Action. Without this timeout, the display updates are random, 
+			// or triggered by user interaction with the UI. See #155
+			_timeoutToken = Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (10), loop => true);
 		}
 
 		private void Stop ()
 		{
-			_progressBar.Fraction = 1F;
+			_timer?.Dispose ();
+			_timer = null;
+			Application.MainLoop.RemoveTimeout (_timeoutToken);
+
+			_activityProgressBar.Fraction = 1F;
+			_pulseProgressBar.Fraction = 1F;
 		}
 	}
 }

+ 45 - 11
UICatalog/Scenarios/TimeAndDate.cs

@@ -8,22 +8,56 @@ namespace UICatalog {
 	class TimeAndDate : Scenario {
 		public override void Setup ()
 		{
-			// NOTE: The TimeField control is not ready for prime-time.
+			// NOTE: The TimeField control is not ready for prime-time. See #246
 
-			Win.Add (new TimeField (0, 0, DateTime.Now, isShort: false) {
+			var longTime = new TimeField (0, 0, DateTime.Now, isShort: false) {
 				// BUGBUG: TimeField does not support Computed Layout
-				//X = Pos.Center (),
-				//Y = Pos.Center () - 1,
-				X = 10,
+				X = Pos.Center (),
 				Y = 2,
-			});
+				ReadOnly = false,
+			};
+			Win.Add (longTime);
+
+			var shortTime = new TimeField (0, 2, DateTime.Now, isShort: true) {
+				// BUGBUG: TimeField does not support Computed Layout
+				X = Pos.Center (),
+				Y = Pos.Bottom(longTime) + 1,
+				ReadOnly = true,
+			};
+			Win.Add (shortTime);
+
+			var shortDate = new DateField (0, 2, DateTime.Now, isShort: true) {
+				// BUGBUG: TimeField does not support Computed Layout
+				X = Pos.Center (),
+				Y = Pos.Bottom (shortTime) + 1,
+				ReadOnly = true,
+			};
+			Win.Add (shortDate);
 
-			Win.Add (new TimeField (0, 2, DateTime.Now, isShort: true) {
+			var longDate = new TimeField (0, 2, DateTime.Now, isShort: true) {
 				// BUGBUG: TimeField does not support Computed Layout
-				//X = Pos.Center (),
-				//Y = Pos.Center () + 1,
-				X = 10,
-				Y = 3,
+				X = Pos.Center (),
+				Y = Pos.Bottom (shortDate) + 1,
+				ReadOnly = true,
+			};
+			Win.Add (longDate);
+
+			Win.Add (new Button ("Swap Long/Short & Read/Read Only") {
+				X = Pos.Center (),
+				Y = Pos.Bottom (Win) - 5,
+				Clicked = () => {
+					longTime.ReadOnly = !longTime.ReadOnly;
+					shortTime.ReadOnly = !shortTime.ReadOnly;
+
+					//longTime.IsShortFormat = !longTime.IsShortFormat;
+					//shortTime.IsShortFormat = !shortTime.IsShortFormat;
+
+					longDate.ReadOnly = !longDate.ReadOnly;
+					shortDate.ReadOnly = !shortDate.ReadOnly;
+
+					//longDate.IsShortFormat = !longDate.IsShortFormat;
+					//shortDate.IsShortFormat = !shortDate.IsShortFormat;
+				}
 			});
 		}
 	}

二进制
UICatalog/screenshot.png


+ 1 - 1
docfx/articles/index.md

@@ -1,5 +1,5 @@
 # Conceptual Documentation
 
-* [Gui.cs Overview](overview.html)
+* [Terminal.Gui Overview](overview.html)
 * [Keyboard Event Processing](keyboard.html)
 * [Event Processing and the Application Main Loop](mainloop.md)

+ 1 - 1
docfx/articles/mainloop.md

@@ -12,7 +12,7 @@ class.
 
 Mainloops are a common idiom in many user interface toolkits so many
 of the concepts will be familiar to you if you have used other
-toolkits before.   
+toolkits before.
 
 This class provides the following capabilities:
 

二进制
docfx/sample.gif


二进制
docfx/sample.png