소스 검색

updated scenarios

Charlie Kindel 5 년 전
부모
커밋
9b0fd2625f

+ 611 - 0
Example/demo.cs.orig

@@ -0,0 +1,611 @@
+using Terminal.Gui;
+using System;
+using Mono.Terminal;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Globalization;
+using System.Reflection;
+using NStack;
+using System.Text;
+
+static class Demo {
+	//class Box10x : View, IScrollView {
+	class Box10x : View {
+		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 < h; y++) {
+				Move (0, y);
+				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);
+		}
+	}
+
+	class Filler : View {
+		public Filler (Rect rect) : base (rect)
+		{
+		}
+
+		public override void Redraw (Rect region)
+		{
+			Driver.SetAttribute (ColorScheme.Focus);
+			var f = Frame;
+
+			for (int y = 0; y < f.Width; y++) {
+				Move (0, y);
+				for (int x = 0; x < f.Height; x++) {
+					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:
+						r = 'o';
+						break;
+					default:
+						r = 'O';
+						break;
+					}
+					Driver.AddRune (r);
+				}
+			}
+		}
+	}
+
+	static void ShowTextAlignments ()
+	{
+<<<<<<< HEAD
+		var container = new Window ($"Show Text Alignments") {
+			X = 0,
+			Y = 0,
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
+		};
+		container.OnKeyUp += (KeyEvent ke) => {
+			if (ke.Key == Key.Esc)
+				container.Running = false;
+		};
+=======
+		var container = new Dialog (
+			"Text Alignments", 70, 20,
+			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+
+>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f
+
+		int i = 0;
+		string txt = "Hello world, how are you doing today?";
+		container.Add (
+<<<<<<< HEAD
+				new Label ($"{i+1}-{txt}") { TextAlignment = TextAlignment.Left,      Y = 3, Width = Dim.Fill () },
+				new Label ($"{i+2}-{txt}") { TextAlignment = TextAlignment.Right,     Y = 5, Width = Dim.Fill () },
+				new Label ($"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered,  Y = 7, Width = Dim.Fill () },
+				new Label ($"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified, Y = 9, Width = Dim.Fill () }
+=======
+				new Label (new Rect (0, 1, 50, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
+				new Label (new Rect (0, 3, 50, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
+				new Label (new Rect (0, 5, 50, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
+				new Label (new Rect (0, 7, 50, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
+>>>>>>> cb40c5c2491a559658481d20dd4b6a3343c0183f
+			);
+
+		Application.Run (container);
+	}
+
+	static void ShowEntries (View container)
+	{
+		var scrollView = new ScrollView (new Rect (50, 10, 20, 8)) {
+			ContentSize = new Size (20, 50),
+			//ContentOffset = new Point (0, 0),
+			ShowVerticalScrollIndicator = true,
+			ShowHorizontalScrollIndicator = true
+		};
+#if false
+		scrollView.Add (new Box10x (0, 0));
+#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)) {
+			ContentSize = new Size (100, 100),
+			ShowVerticalScrollIndicator = true,
+			ShowHorizontalScrollIndicator = true
+		};
+		scrollView2.Add (new Box10x (0, 0));
+		var progress = new ProgressBar (new Rect (68, 1, 10, 1));
+		bool timer (MainLoop caller)
+		{
+			progress.Pulse ();
+			return true;
+		}
+
+		Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+
+
+		// A little convoluted, this is because I am using this to test the
+		// layout based on referencing elements of another view:
+
+		var login = new Label ("Login: ") { X = 3, Y = 6 };
+		var password = new Label ("Password: ") {
+			X = Pos.Left (login),
+			Y = Pos.Bottom (login) + 1
+		};
+		var loginText = new TextField ("") {
+			X = Pos.Right (password),
+			Y = Pos.Top (login),
+			Width = 40
+		};
+
+		var passText = new TextField ("") {
+			Secret = true,
+			X = Pos.Left (loginText),
+			Y = Pos.Top (password),
+			Width = Dim.Width (loginText)
+		};
+
+		var tf = new Button (3, 19, "Ok");
+		// Add some content
+		container.Add (
+			login,
+			loginText,
+			password,
+			passText,
+			new FrameView (new Rect (3, 10, 25, 6), "Options"){
+				new CheckBox (1, 0, "Remember me"),
+				new RadioGroup (1, 2, new [] { "_Personal", "_Company" }),
+			},
+			new ListView (new Rect (59, 6, 16, 4), new string [] {
+				"First row",
+				"<>",
+				"This is a very long row that should overflow what is shown",
+				"4th",
+				"There is an empty slot on the second row",
+				"Whoa",
+				"This is so cool"
+			}),
+			scrollView,
+			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, 24, "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"),
+			menuKeysStyle,
+			menuAutoMouseNav
+
+		);
+		container.SendSubviewToBack (tf);
+	}
+
+	public static Label ml2;
+
+	static void NewFile ()
+	{
+		var d = new Dialog (
+			"New File", 50, 20,
+			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+		ml2 = new Label (1, 1, "Mouse Debug Line");
+		d.Add (ml2);
+		Application.Run (d);
+	}
+
+	//
+	// Creates a nested editor
+	static void Editor (Toplevel top)
+	{
+		var tframe = top.Frame;
+		var ntop = new Toplevel (tframe);
+		var menu = new MenuBar (new MenuBarItem [] {
+			new MenuBarItem ("_File", new MenuItem [] {
+				new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
+			}),
+			new MenuBarItem ("_Edit", new MenuItem [] {
+				new MenuItem ("_Copy", "", null),
+				new MenuItem ("C_ut", "", null),
+				new MenuItem ("_Paste", "", null)
+			}),
+		});
+		ntop.Add (menu);
+
+		string fname = null;
+		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") {
+			X = 0,
+			Y = 1,
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
+		};
+		ntop.Add (win);
+
+		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);
+
+		Application.Run (ntop);
+	}
+
+	static bool Quit ()
+	{
+		var n = MessageBox.Query (50, 7, "Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No");
+		return n == 0;
+	}
+
+	static void Close ()
+	{
+		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
+	// it introduces a new brace instead, and does not indent.  Then watch me fight
+	// the editor as more oddities happen.
+
+	public static void Open ()
+	{
+		var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = true };
+		Application.Run (d);
+
+		if (!d.Canceled)
+			MessageBox.Query (50, 7, "Selected File", string.Join (", ", d.FilePaths), "Ok");
+	}
+
+	public static void ShowHex (Toplevel top)
+	{
+		var tframe = top.Frame;
+		var ntop = new Toplevel (tframe);
+		var menu = new MenuBar (new MenuBarItem [] {
+			new MenuBarItem ("_File", new MenuItem [] {
+				new MenuItem ("_Close", "", () => {Application.RequestStop ();}),
+			}),
+		});
+		ntop.Add (menu);
+
+		var win = new Window ("/etc/passwd") {
+			X = 0,
+			Y = 1,
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
+		};
+		ntop.Add (win);
+
+		var source = System.IO.File.OpenRead ("/etc/passwd");
+		var hex = new HexView (source) {
+			X = 0,
+			Y = 0,
+			Width = Dim.Fill (),
+			Height = Dim.Fill ()
+		};
+		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
+
+	static void ListSelectionDemo (bool multiple)
+	{
+		var d = new Dialog ("Selection Demo", 60, 20,
+			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
+			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+
+		var animals = new List<string> () { "Alpaca", "Llama", "Lion", "Shark", "Goat" };
+		var msg = new Label ("Use space bar or control-t to toggle selection") {
+			X = 1,
+			Y = 1,
+			Width = Dim.Fill () - 1,
+			Height = 1
+		};
+
+		var list = new ListView (animals) {
+			X = 1,
+			Y = 3,
+			Width = Dim.Fill () - 4,
+			Height = Dim.Fill () - 4,
+			AllowsMarking = true,
+			AllowsMultipleSelection = multiple
+		};
+		d.Add (msg, list);
+		Application.Run (d);
+
+		var result = "";
+		for (int i = 0; i < animals.Count; i++) {
+			if (list.Source.IsMarked (i)) {
+				result += animals [i] + " ";
+			}
+		}
+		MessageBox.Query (60, 10, "Selected Animals", result == "" ? "No animals selected" : result, "Ok");
+	}
+	#endregion
+
+
+	#region OnKeyDown / OnKeyUp Demo
+	private static void OnKeyDownUpDemo ()
+	{
+		var container = new Dialog (
+			"OnKeyDown & OnKeyUp demo", 80, 20,
+			new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
+			Width = Dim.Fill (),
+			Height = Dim.Fill (),
+		};
+
+		var list = new List<string> ();
+		var listView = new ListView (list) {
+			X = 0,
+			Y = 0,
+			Width = Dim.Fill () - 1,
+			Height = Dim.Fill () - 2,
+		};
+		listView.ColorScheme = Colors.TopLevel;
+		container.Add (listView);
+
+		void KeyUpDown (KeyEvent keyEvent, string updown)
+		{
+			if ((keyEvent.Key & Key.CtrlMask) != 0) {
+				list.Add ($"Key{updown,-4}: Ctrl ");
+			} else if ((keyEvent.Key & Key.AltMask) != 0) {
+				list.Add ($"Key{updown,-4}: Alt ");
+			} else {
+				list.Add ($"Key{updown,-4}: {(((uint)keyEvent.KeyValue & (uint)Key.CharMask) > 26 ? $"{(char)keyEvent.KeyValue}" : $"{keyEvent.Key}")}");
+			}
+			listView.MoveDown ();
+		}
+
+		container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Down");
+		container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, "Up");
+		Application.Run (container);
+	}
+	#endregion
+
+	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;
+
+		//Open ();
+#if true
+		int margin = 3;
+		var win = new Window ("Hello") {
+			X = 1,
+			Y = 1,
+
+			Width = Dim.Fill () - margin,
+			Height = Dim.Fill () - margin
+		};
+#else
+		var tframe = top.Frame;
+
+		var win = new Window (new Rect (0, 1, tframe.Width, tframe.Height - 1), "Hello");
+#endif
+		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", "", 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 MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)),
+				new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)),
+			}),
+			new MenuBarItem ("A_ssorted", new MenuItem [] {
+				new MenuItem ("_Show text alignments", "", () => ShowTextAlignments ()),
+				new MenuItem ("_OnKeyDown/Up", "", () => OnKeyDownUpDemo ())
+			}),
+			new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
+				new MenuItem ("SubMenu1Item_1",
+					new MenuBarItem (new MenuItem[] {
+						new MenuItem ("SubMenu2Item_1",
+							new MenuBarItem (new MenuItem [] {
+								new MenuItem ("SubMenu3Item_1",
+									new MenuBarItem (new MenuItem [] { menuItems [2] })
+								)
+							})
+						)
+					})
+				)
+			}),
+			new MenuBarItem ("_About...", "Demonstrates top-level menu item", () =>  MessageBox.ErrorQuery (50, 7, "About Demo", "This is a demo app for gui.cs", "Ok")),
+		});
+
+		menuKeysStyle = new CheckBox (3, 25, "UseKeysUpDownAsKeysLeftRight", true);
+		menuKeysStyle.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: ");
+		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);
+
+		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", () => { if (Quit ()) top.Running = false; }),
+		}) {
+			Parent = null,
+		};
+
+		win.Add (drag, dragText);
+#if true
+		// FIXED: This currently causes a stack overflow, because it is referencing a window that has not had its size allocated yet
+
+		var bottom = new Label ("This should go on the bottom of the same top-level!");
+		win.Add (bottom);
+		var bottom2 = new Label ("This should go on the bottom of another top-level!");
+		top.Add (bottom2);
+
+		Application.OnLoad = () => {
+			bottom.X = win.X;
+			bottom.Y = Pos.Bottom (win) - Pos.Top (win) - margin;
+			bottom2.X = Pos.Left (win);
+			bottom2.Y = Pos.Bottom (win);
+		};
+#endif
+
+
+		top.Add (win);
+		//top.Add (menu);
+		top.Add (menu, statusBar);
+		Application.Run ();
+	}
+}

+ 3 - 4
README.md

@@ -2,14 +2,13 @@
 [![Version](https://img.shields.io/nuget/v/Terminal.Gui.svg)](https://www.nuget.org/packages/Terminal.Gui)
 [![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)
 [![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)
 [![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) - This is 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) - The Mono Channel room
 
 
 # Terminal.Gui - Terminal UI toolkit for .NET
 # Terminal.Gui - Terminal UI toolkit for .NET
 
 
 A simple UI toolkit for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
 A simple UI toolkit for .NET, .NET Core, and Mono that works on Windows, the Mac, and Linux/Unix.
 
 
-![Sample app](https://raw.githubusercontent.com/migueldeicaza/gui.cs/master/docfx/sample.png)
+![Sample app](https://raw.githubusercontent.com/migueldeicaza/gui.cs/master/docfx/sample.gif)
 
 
 ## Controls & Features
 ## Controls & Features
 
 
@@ -41,7 +40,7 @@ In addition, a complete Xterm/Vt100 terminal emulator that you can embed is now
 * **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.
 * **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.
 * **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.
 * **[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.
+* **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.
 * **[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
 * **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.
 descriptors.

+ 33 - 17
Terminal.Gui/Core.cs

@@ -1065,8 +1065,18 @@ namespace Terminal.Gui {
 			SuperView?.SetFocus (this);
 			SuperView?.SetFocus (this);
 		}
 		}
 
 
+		/// <summary>
+		/// Specifies the event arguments for <see cref="KeyEvent"/>
+		/// </summary>
 		public class KeyEventEventArgs : EventArgs {
 		public class KeyEventEventArgs : EventArgs {
+			/// <summary>
+			/// Constructs.
+			/// </summary>
+			/// <param name="ke"></param>
 			public KeyEventEventArgs(KeyEvent ke) => KeyEvent = ke;
 			public KeyEventEventArgs(KeyEvent ke) => KeyEvent = ke;
+			/// <summary>
+			/// The <see cref="KeyEvent"/> for the event.
+			/// </summary>
 			public KeyEvent KeyEvent { get; set; }
 			public KeyEvent KeyEvent { get; set; }
 		}
 		}
 
 
@@ -1545,12 +1555,12 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// <summary>
 		/// Check id current toplevel has menu bar
 		/// Check id current toplevel has menu bar
 		/// </summary>
 		/// </summary>
-		public bool HasMenuBar { get; set; }
+		public MenuBar MenuBar { get; set; }
 
 
 		/// <summary>
 		/// <summary>
 		/// Check id current toplevel has status bar
 		/// Check id current toplevel has status bar
 		/// </summary>
 		/// </summary>
-		public bool HasStatusBar { get; set; }
+		public StatusBar StatusBar { get; set; }
 
 
 		///<inheritdoc cref="ProcessKey"/>
 		///<inheritdoc cref="ProcessKey"/>
 		public override bool ProcessKey (KeyEvent keyEvent)
 		public override bool ProcessKey (KeyEvent keyEvent)
@@ -1609,9 +1619,9 @@ namespace Terminal.Gui {
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
 				if (view is MenuBar)
 				if (view is MenuBar)
-					HasMenuBar = true;
+					MenuBar = view as MenuBar;
 				if (view is StatusBar)
 				if (view is StatusBar)
-					HasStatusBar = true;
+					StatusBar = view as StatusBar;
 			}
 			}
 			base.Add (view);
 			base.Add (view);
 		}
 		}
@@ -1621,9 +1631,9 @@ namespace Terminal.Gui {
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
 				if (view is MenuBar)
 				if (view is MenuBar)
-					HasMenuBar = true;
+					MenuBar = null;
 				if (view is StatusBar)
 				if (view is StatusBar)
-					HasStatusBar = true;
+					StatusBar = null;
 			}
 			}
 			base.Remove (view);
 			base.Remove (view);
 		}
 		}
@@ -1632,8 +1642,8 @@ namespace Terminal.Gui {
 		public override void RemoveAll ()
 		public override void RemoveAll ()
 		{
 		{
 			if (this == Application.Top) {
 			if (this == Application.Top) {
-				HasMenuBar = false;
-				HasStatusBar = false;
+				MenuBar = null;
+				StatusBar = null;
 			}
 			}
 			base.RemoveAll ();
 			base.RemoveAll ();
 		}
 		}
@@ -1644,15 +1654,15 @@ namespace Terminal.Gui {
 			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;
 			bool m, s;
 			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
 			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
-				m = Application.Top.HasMenuBar;
+				m = Application.Top.MenuBar != null;
 			else
 			else
-				m = ((Toplevel)SuperView).HasMenuBar;
+				m = ((Toplevel)SuperView).MenuBar != null;
 			int l = m ? 1 : 0;
 			int l = m ? 1 : 0;
 			ny = Math.Max (y, l);
 			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
 			else
-				s = ((Toplevel)SuperView).HasStatusBar;
+				s = ((Toplevel)SuperView).StatusBar != null;
 			l = s ? Driver.Rows - 1 : Driver.Rows;
 			l = s ? Driver.Rows - 1 : Driver.Rows;
 			ny = Math.Min (ny, l);
 			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;
@@ -1674,9 +1684,15 @@ namespace Terminal.Gui {
 							top.X = nx;
 							top.X = nx;
 							top.Y = ny;
 							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 ();
+							}
 						}
 						}
 					}
 					}
 				}
 				}
@@ -1688,7 +1704,7 @@ namespace Terminal.Gui {
 		{
 		{
 			Application.CurrentView = this;
 			Application.CurrentView = this;
 
 
-			if (this == Application.Top) {
+			if (this == Application.Top || this == Application.Current) {
 				if (!NeedDisplay.IsEmpty) {
 				if (!NeedDisplay.IsEmpty) {
 					Driver.SetAttribute (Colors.TopLevel.Normal);
 					Driver.SetAttribute (Colors.TopLevel.Normal);
 					Clear (region);
 					Clear (region);

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

@@ -7,6 +7,7 @@
 using System;
 using System;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Runtime.InteropServices;
+using System.Threading.Tasks;
 using Mono.Terminal;
 using Mono.Terminal;
 using NStack;
 using NStack;
 using Unix.Terminal;
 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 () {
 			return new MouseEvent () {
 				X = cev.X,
 				X = cev.X,
 				Y = cev.Y,
 				Y = cev.Y,
-				Flags = (MouseFlags)cev.ButtonState
+				Flags = mf
 			};
 			};
 		}
 		}
 
 
@@ -252,10 +426,15 @@ namespace Terminal.Gui {
 			keyUpHandler (new KeyEvent ((Key)wch));
 			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)
 		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
 			// 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 => {
 			(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
 				ProcessInput (keyHandler, keyUpHandler, mouseHandler);
 				ProcessInput (keyHandler, keyUpHandler, mouseHandler);
@@ -425,21 +604,21 @@ namespace Terminal.Gui {
 			Console.Out.Flush ();
 			Console.Out.Flush ();
 		}
 		}
 
 
-		int lastMouseInterval;
-		bool mouseGrabbed;
+		//int lastMouseInterval;
+		//bool mouseGrabbed;
 
 
 		public override void UncookMouse ()
 		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 ()
 		public override void CookMouse ()
 		{
 		{
-			mouseGrabbed = false;
-			Curses.mouseinterval (lastMouseInterval);
+			//mouseGrabbed = false;
+			//Curses.mouseinterval (lastMouseInterval);
 		}
 		}
 	}
 	}
 
 

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

@@ -2,6 +2,8 @@
  * This file is autogenerated by the attrib.c program, do not edit
  * This file is autogenerated by the attrib.c program, do not edit
  */
  */
 
 
+#define XTERM1006
+
 using System;
 using System;
 
 
 namespace Unix.Terminal {
 namespace Unix.Terminal {
@@ -77,6 +79,15 @@ namespace Unix.Terminal {
 			ReportMousePosition = unchecked((int)0x8000000),
 			ReportMousePosition = unchecked((int)0x8000000),
 			AllEvents = unchecked((int)0x7ffffff),
 			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 ERR = unchecked((int)0xffffffff);
 		public const int KeyBackspace = unchecked((int)0x107);
 		public const int KeyBackspace = unchecked((int)0x107);
 		public const int KeyUp = unchecked((int)0x103);
 		public const int KeyUp = unchecked((int)0x103);
@@ -110,30 +121,30 @@ namespace Unix.Terminal {
 		public const int ShiftKeyPPage = unchecked((int)0x18e);
 		public const int ShiftKeyPPage = unchecked((int)0x18e);
 		public const int ShiftKeyHome = unchecked((int)0x187);
 		public const int ShiftKeyHome = unchecked((int)0x187);
 		public const int ShiftKeyEnd = unchecked((int)0x182);
 		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;
 		public const int LC_ALL = 6;
 		static public int ColorPair(int n){
 		static public int ColorPair(int n){

+ 9 - 0
Terminal.Gui/Types/PosDim.cs

@@ -247,6 +247,7 @@ namespace Terminal.Gui {
 				case 1: return Target.Frame.Y;
 				case 1: return Target.Frame.Y;
 				case 2: return Target.Frame.Right;
 				case 2: return Target.Frame.Right;
 				case 3: return Target.Frame.Bottom;
 				case 3: return Target.Frame.Bottom;
+				case 4: return Target.Frame.Right - Target.Frame.Left;
 				default:
 				default:
 					return 0;
 					return 0;
 				}
 				}
@@ -260,6 +261,7 @@ namespace Terminal.Gui {
 				case 1: tside = "y"; break;
 				case 1: tside = "y"; break;
 				case 2: tside = "right"; break;
 				case 2: tside = "right"; break;
 				case 3: tside = "bottom"; break;
 				case 3: tside = "bottom"; break;
+				case 4: tside = "width"; break;
 				default: tside = "unknown"; break;
 				default: tside = "unknown"; break;
 				}
 				}
 				return $"Pos.View(side={tside}, target={Target.ToString()}";
 				return $"Pos.View(side={tside}, target={Target.ToString()}";
@@ -307,6 +309,13 @@ namespace Terminal.Gui {
 		/// <returns>The Position that depends on the other view.</returns>
 		/// <returns>The Position that depends on the other view.</returns>
 		/// <param name="view">The view that will be tracked.</param>
 		/// <param name="view">The view that will be tracked.</param>
 		public static Pos Bottom (View view) => new PosView (view, 3);
 		public static Pos Bottom (View view) => new PosView (view, 3);
+
+		/// <summary>
+		/// Returns a Pos object tracks the Width (Right-Left) of the specified view.
+		/// </summary>
+		/// <returns>The Position that depends on the other view.</returns>
+		/// <param name="view">The view that will be tracked.</param>
+		public static Pos Width (View view) => new PosView (view, 4);
 	}
 	}
 
 
 	/// <summary>
 	/// <summary>

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

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

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

@@ -562,6 +562,7 @@ namespace Terminal.Gui {
 		}
 		}
 
 
 		bool openedByAltKey;
 		bool openedByAltKey;
+
 		///<inheritdoc cref="OnKeyDown"/>
 		///<inheritdoc cref="OnKeyDown"/>
 		public override bool OnKeyDown (KeyEvent keyEvent)
 		public override bool OnKeyDown (KeyEvent keyEvent)
 		{
 		{

+ 9 - 5
Terminal.Gui/Views/StatusBar.cs

@@ -107,6 +107,10 @@ namespace Terminal.Gui {
 			Items = items;
 			Items = items;
 			CanFocus = false;
 			CanFocus = false;
 			ColorScheme = Colors.Menu;
 			ColorScheme = Colors.Menu;
+			X = 0;
+			Y = Driver.Rows - 1;
+			Width = Dim.Fill ();
+			Height = 1;
 
 
 			Application.Loaded += (sender, e) => {
 			Application.Loaded += (sender, e) => {
 				X = 0;
 				X = 0;
@@ -141,11 +145,11 @@ namespace Terminal.Gui {
 		///<inheritdoc cref="Redraw"/>
 		///<inheritdoc cref="Redraw"/>
 		public override void Redraw (Rect region)
 		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);
 			Move (0, 0);
 			Driver.SetAttribute (ColorScheme.Normal);
 			Driver.SetAttribute (ColorScheme.Normal);

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

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

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

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

+ 13 - 4
UICatalog/Program.cs

@@ -1,5 +1,6 @@
 using NStack;
 using NStack;
 using System;
 using System;
+using System.Collections;
 using System.Collections.Generic;
 using System.Collections.Generic;
 using System.Diagnostics;
 using System.Diagnostics;
 using System.Globalization;
 using System.Globalization;
@@ -62,7 +63,7 @@ namespace UICatalog {
 				new MenuBarItem ("_File", new MenuItem [] {
 				new MenuBarItem ("_File", new MenuItem [] {
 					new MenuItem ("_Quit", "", () => Application.RequestStop() )
 					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") {
 			_leftPane = new Window ("Categories") {
@@ -119,7 +120,7 @@ namespace UICatalog {
 			_rightPane.Add (_scenarioListView);
 			_rightPane.Add (_scenarioListView);
 
 
 			_categoryListView.SelectedItem = 0;
 			_categoryListView.SelectedItem = 0;
-			CategoryListView_SelectedChanged ();
+			_categoryListView.OnSelectedChanged ();
 
 
 			_statusBar = new StatusBar (new StatusItem [] {
 			_statusBar = new StatusBar (new StatusItem [] {
 				//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
 				//new StatusItem(Key.F1, "~F1~ Help", () => Help()),
@@ -148,7 +149,9 @@ namespace UICatalog {
 			}
 			}
 
 
 			_top = Application.Top;
 			_top = Application.Top;
+
 			_top.KeyUp += KeyUpHandler;
 			_top.KeyUp += KeyUpHandler;
+
 			_top.Add (_menu);
 			_top.Add (_menu);
 			_top.Add (_leftPane);
 			_top.Add (_leftPane);
 			_top.Add (_rightPane);
 			_top.Add (_rightPane);
@@ -227,6 +230,12 @@ namespace UICatalog {
 					used++;
 					used++;
 				}
 				}
 			}
 			}
+
+			public IList ToList ()
+			{
+				return Scenarios;
+			}
+
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
@@ -244,7 +253,7 @@ namespace UICatalog {
 				//	break;
 				//	break;
 				//case Key.Enter:
 				//case Key.Enter:
 				//	break;
 				//	break;
-				//}
+				//}<
 			} else if (a.KeyEvent.Key == Key.Tab || a.KeyEvent.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
 				// BUGBUG: Work around Issue #434 by implementing our own TAB navigation
 				if (_top.MostFocused == _categoryListView)
 				if (_top.MostFocused == _categoryListView)
@@ -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];
 			var item = _categories [_categoryListView.SelectedItem];
 			List<Type> newlist;
 			List<Type> newlist;

+ 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. 
 - Use the `Bug Rero` Category for `Scnarios` that reproduce bugs. 
 	- Include the Github Issue # in the Description.
 	- 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).
 	- 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
 	/// 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.
 	/// ListViews. Press ENTER to run the selected sceanrio. Press CTRL-Q to exit it.
 	/// </summary>
 	/// </summary>
-	public class Scenario {
+	public class Scenario : IDisposable {
+		private bool _disposedValue;
+
 		/// <summary>
 		/// <summary>
 		/// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
 		/// The Top level for the Scenario. This should be set to `Application.Top` in most cases.
 		/// </summary>
 		/// </summary>
@@ -177,5 +179,25 @@ namespace UICatalog {
 			}
 			}
 			return objects;
 			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);
+		}
 	}
 	}
 }
 }

+ 2 - 3
UICatalog/Scenarios/Buttons.cs

@@ -24,9 +24,8 @@ namespace UICatalog {
 			// the scenario will quit
 			// the scenario will quit
 			var defaultButton = new Button ("Quit") {
 			var defaultButton = new Button ("Quit") {
 				X = Pos.Center (),
 				X = Pos.Center (),
-				// BUGBUG: Throws an exception
-				//Y= Pos.Bottom(Win),
-				Y = 20,
+				//TODO: Change to use Pos.AnchorEnd()
+				Y= Pos.Bottom(Win) - 3,
 				IsDefault = true,
 				IsDefault = true,
 				Clicked = () => Application.RequestStop (),
 				Clicked = () => Application.RequestStop (),
 			};
 			};

+ 59 - 18
UICatalog/Scenarios/ComputedLayout.cs

@@ -50,6 +50,12 @@ namespace UICatalog {
 
 
 			//Win.Add (verticalRuler);
 			//Win.Add (verticalRuler);
 
 
+			// Demonstrate At - Absolute Layout using Pos
+			var absoluteButton = new Button ("Absolute At(2,1)") {
+				X = Pos.At (2),
+				Y = Pos.At (1)
+			};
+			Win.Add (absoluteButton);
 
 
 			// Demonstrate using Dim to create a window that fills the parent with a margin
 			// Demonstrate using Dim to create a window that fills the parent with a margin
 			int margin = 10;
 			int margin = 10;
@@ -72,55 +78,90 @@ namespace UICatalog {
 
 
 			subWin.Add (labelList.ToArray ());
 			subWin.Add (labelList.ToArray ());
 
 
-			// Demonstrate Dim & Pos using percentages - a TextField that is 20% height and 80% wide
+			// Demonstrate Dim & Pos using percentages - a TextField that is 30% height and 80% wide
 			var textView= new TextView () {
 			var textView= new TextView () {
 				X = Pos.Center (),
 				X = Pos.Center (),
 				Y = Pos.Percent (50),
 				Y = Pos.Percent (50),
 				Width = Dim.Percent (80),
 				Width = Dim.Percent (80),
-				Height = Dim.Percent (20),
+				Height = Dim.Percent (30),
 				ColorScheme = Colors.TopLevel,
 				ColorScheme = Colors.TopLevel,
 			};
 			};
 			textView.Text = "This text view should be half-way down the terminal,\n20% of its height, and 80% of its width.";
 			textView.Text = "This text view should be half-way down the terminal,\n20% of its height, and 80% of its width.";
 			Win.Add (textView);
 			Win.Add (textView);
 
 
-			// Demonstrate AnchorEnd - Button anchored to bottom of textView
-			var clearButton = new Button ("Anchor End") {
-				Y = Pos.AnchorEnd () - 1
+			// Demonstrate AnchorEnd - Button is anchored to bottom/right
+			var anchorButton = new Button ("Anchor End") {
+				Y = Pos.AnchorEnd () - 1,
 			};
 			};
-			clearButton.X = Pos.AnchorEnd () - clearButton.Text.Length + 4;
-
-			Win.Add (clearButton);
-
-			// Demonstrate At - Absolute Layout using Pos
-			var absoluteButton = new Button ("At(10,10)") {
-				X = Pos.At(10),
-				Y = Pos.At(10)
+			// TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
+			anchorButton.X = Pos.AnchorEnd () - (Pos.Right (anchorButton) - Pos.Left (anchorButton));
+			anchorButton.Clicked = () => {
+				// Ths demonstrates how to have a dynamically sized button
+				// Each time the button is clicked the button's text gets longer
+				// The call to Win.LayoutSubviews causes the Computed layout to
+				// get updated. 
+				anchorButton.Text += "!";
+				Win.LayoutSubviews ();
 			};
 			};
-			Win.Add (absoluteButton);
+			Win.Add (anchorButton);
+
 
 
 			// Centering multiple controls horizontally. 
 			// Centering multiple controls horizontally. 
 			// This is intentionally convoluted to illustrate potential bugs.
 			// This is intentionally convoluted to illustrate potential bugs.
 			var bottomLabel = new Label ("This should be the 2nd to last line (Bug #xxx).") {
 			var bottomLabel = new Label ("This should be the 2nd to last line (Bug #xxx).") {
 				TextAlignment = Terminal.Gui.TextAlignment.Centered,
 				TextAlignment = Terminal.Gui.TextAlignment.Centered,
-				ColorScheme = Colors.TopLevel,
+				ColorScheme = Colors.Menu,
 				Width = Dim.Fill (),
 				Width = Dim.Fill (),
 				X = Pos.Center (),
 				X = Pos.Center (),
 				Y = Pos.Bottom (Win) - 4  // BUGBUG: -2 should be two lines above border; but it has to be -4
 				Y = Pos.Bottom (Win) - 4  // BUGBUG: -2 should be two lines above border; but it has to be -4
 			};
 			};
 			Win.Add (bottomLabel);
 			Win.Add (bottomLabel);
 
 
+			// Show positioning vertically using Pos.Bottom 
+			// BUGBUG: -1 should be just above border; but it has to be -3
 			var leftButton = new Button ("Left") {
 			var leftButton = new Button ("Left") {
-				Y = Pos.Bottom (Win) - 2
+				Y = Pos.Bottom (Win) - 3
 			};
 			};
+			leftButton.Clicked = () => {
+				// Ths demonstrates how to have a dynamically sized button
+				// Each time the button is clicked the button's text gets longer
+				// The call to Win.LayoutSubviews causes the Computed layout to
+				// get updated. 
+				leftButton.Text += "!";
+				Win.LayoutSubviews ();
+			};
+
+
+			// show positioning vertically using Pos.AnchorEnd
 			var centerButton = new Button ("Center") {
 			var centerButton = new Button ("Center") {
 				X = Pos.Center (),
 				X = Pos.Center (),
 				Y = Pos.AnchorEnd () - 1
 				Y = Pos.AnchorEnd () - 1
 			};
 			};
+			centerButton.Clicked = () => {
+				// Ths demonstrates how to have a dynamically sized button
+				// Each time the button is clicked the button's text gets longer
+				// The call to Win.LayoutSubviews causes the Computed layout to
+				// get updated. 
+				centerButton.Text += "!";
+				Win.LayoutSubviews ();
+			};
+
+			// show positioning vertically using another window and Pos.Bottom
 			var rightButton = new Button ("Right") {
 			var rightButton = new Button ("Right") {
-				Y = Pos.Y(centerButton)
+				Y = Pos.Y (centerButton)
+			};
+			rightButton.Clicked = () => {
+				// Ths demonstrates how to have a dynamically sized button
+				// Each time the button is clicked the button's text gets longer
+				// The call to Win.LayoutSubviews causes the Computed layout to
+				// get updated. 
+				rightButton.Text += "!";
+				Win.LayoutSubviews ();
 			};
 			};
 
 
-			leftButton.X = Pos.Left (centerButton) - leftButton.Frame.Width - 5;
+			// Center three buttons with 5 spaces between them
+			// TODO: Use Pos.Width instead of (Right-Left) when implemented (#502)
+			leftButton.X = Pos.Left (centerButton) - (Pos.Right(leftButton) - Pos.Left (leftButton)) - 5;
 			rightButton.X = Pos.Right (centerButton) + 5;
 			rightButton.X = Pos.Right (centerButton) + 5;
 
 
 			Win.Add (leftButton);
 			Win.Add (leftButton);

+ 1 - 1
UICatalog/Scenarios/Keys.cs

@@ -43,8 +43,8 @@ namespace UICatalog {
 			public override bool ProcessColdKey (KeyEvent keyEvent)
 			public override bool ProcessColdKey (KeyEvent keyEvent)
 			{
 			{
 				_processColdKeyList.Add (keyEvent.ToString ());
 				_processColdKeyList.Add (keyEvent.ToString ());
-				return base.ProcessColdKey (keyEvent);
 
 
+				return base.ProcessColdKey (keyEvent);
 			}
 			}
 		}
 		}
 
 

+ 45 - 11
UICatalog/Scenarios/TimeAndDate.cs

@@ -8,22 +8,56 @@ namespace UICatalog {
 	class TimeAndDate : Scenario {
 	class TimeAndDate : Scenario {
 		public override void Setup ()
 		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
 				// BUGBUG: TimeField does not support Computed Layout
-				//X = Pos.Center (),
-				//Y = Pos.Center () - 1,
-				X = 10,
+				X = Pos.Center (),
 				Y = 2,
 				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
 				// 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;
+				}
 			});
 			});
 		}
 		}
 	}
 	}

BIN
UICatalog/screenshot.png


BIN
docfx/sample.gif