Ver Fonte

updated scenarios

Charlie Kindel há 5 anos atrás
pai
commit
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)
 [![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) - 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
 
 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
 
@@ -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.
 * **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.
+* **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.

+ 33 - 17
Terminal.Gui/Core.cs

@@ -1065,8 +1065,18 @@ namespace Terminal.Gui {
 			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; }
 		}
 
@@ -1545,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)
@@ -1609,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);
 		}
@@ -1621,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);
 		}
@@ -1632,8 +1642,8 @@ namespace Terminal.Gui {
 		public override void RemoveAll ()
 		{
 			if (this == Application.Top) {
-				HasMenuBar = false;
-				HasStatusBar = false;
+				MenuBar = null;
+				StatusBar = null;
 			}
 			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;
 			bool m, s;
 			if (SuperView == null || SuperView.GetType() != typeof(Toplevel))
-				m = Application.Top.HasMenuBar;
+				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;
@@ -1674,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 ();
+							}
 						}
 					}
 				}
@@ -1688,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);

+ 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);
 		}
 	}
 

+ 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){

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

@@ -247,6 +247,7 @@ namespace Terminal.Gui {
 				case 1: return Target.Frame.Y;
 				case 2: return Target.Frame.Right;
 				case 3: return Target.Frame.Bottom;
+				case 4: return Target.Frame.Right - Target.Frame.Left;
 				default:
 					return 0;
 				}
@@ -260,6 +261,7 @@ namespace Terminal.Gui {
 				case 1: tside = "y"; break;
 				case 2: tside = "right"; break;
 				case 3: tside = "bottom"; break;
+				case 4: tside = "width"; break;
 				default: tside = "unknown"; break;
 				}
 				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>
 		/// <param name="view">The view that will be tracked.</param>
 		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>

+ 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;
+		}
 	}
 }

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

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

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

@@ -107,6 +107,10 @@ namespace Terminal.Gui {
 			Items = items;
 			CanFocus = false;
 			ColorScheme = Colors.Menu;
+			X = 0;
+			Y = Driver.Rows - 1;
+			Width = Dim.Fill ();
+			Height = 1;
 
 			Application.Loaded += (sender, e) => {
 				X = 0;
@@ -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>

+ 13 - 4
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.KeyUp += KeyUpHandler;
+
 			_top.Add (_menu);
 			_top.Add (_leftPane);
 			_top.Add (_rightPane);
@@ -227,6 +230,12 @@ namespace UICatalog {
 					used++;
 				}
 			}
+
+			public IList ToList ()
+			{
+				return Scenarios;
+			}
+
 		}
 
 		/// <summary>
@@ -244,7 +253,7 @@ namespace UICatalog {
 				//	break;
 				//case Key.Enter:
 				//	break;
-				//}
+				//}<
 			} 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)
@@ -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;

+ 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);
+		}
 	}
 }

+ 2 - 3
UICatalog/Scenarios/Buttons.cs

@@ -24,9 +24,8 @@ namespace UICatalog {
 			// the scenario will quit
 			var defaultButton = new Button ("Quit") {
 				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,
 				Clicked = () => Application.RequestStop (),
 			};

+ 59 - 18
UICatalog/Scenarios/ComputedLayout.cs

@@ -50,6 +50,12 @@ namespace UICatalog {
 
 			//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
 			int margin = 10;
@@ -72,55 +78,90 @@ namespace UICatalog {
 
 			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 () {
 				X = Pos.Center (),
 				Y = Pos.Percent (50),
 				Width = Dim.Percent (80),
-				Height = Dim.Percent (20),
+				Height = Dim.Percent (30),
 				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 ("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. 
 			// This is intentionally convoluted to illustrate potential bugs.
 			var bottomLabel = new Label ("This should be the 2nd to last line (Bug #xxx).") {
 				TextAlignment = Terminal.Gui.TextAlignment.Centered,
-				ColorScheme = Colors.TopLevel,
+				ColorScheme = Colors.Menu,
 				Width = Dim.Fill (),
 				X = Pos.Center (),
 				Y = Pos.Bottom (Win) - 4  // BUGBUG: -2 should be two lines above border; but it has to be -4
 			};
 			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") {
-				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") {
 				X = Pos.Center (),
 				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") {
-				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;
 
 			Win.Add (leftButton);

+ 1 - 1
UICatalog/Scenarios/Keys.cs

@@ -43,8 +43,8 @@ namespace UICatalog {
 			public override bool ProcessColdKey (KeyEvent keyEvent)
 			{
 				_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 {
 		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;
+				}
 			});
 		}
 	}

BIN
UICatalog/screenshot.png


BIN
docfx/sample.gif