Преглед изворни кода

Merge branch 'master' of tig:migueldeicaza/gui.cs

Charlie Kindel пре 5 година
родитељ
комит
917f02a124

+ 1 - 0
.editorconfig

@@ -6,6 +6,7 @@ csharp_new_line_before_open_brace = methods,local_functions
 csharp_new_line_before_else = false
 csharp_new_line_before_catch = false
 csharp_new_line_before_finally = false
+end_of_line = crlf
 
 csharp_indent_case_contents = true
 csharp_indent_switch_labels = false

+ 0 - 3
.gitattributes

@@ -5,9 +5,6 @@
 # native line endings on checkout.
 *.cs text
 
-# Convert to CRLF line endings on checkout.
-*.sln text eol=crlf
-
 # Convert to LF line endings on checkout.
 *.sh text eol=lf
 

+ 3 - 2
.gitignore

@@ -1,8 +1,9 @@
 bin
 obj
-*~
+~$*
 *.userprefs
 *~
 packages
 .vs
-*.csproj.user
+# User-specific files
+*.user

+ 49 - 31
Example/demo.cs

@@ -6,6 +6,7 @@ using System.Diagnostics;
 using System.Globalization;
 using System.Reflection;
 using NStack;
+using System.Text;
 
 static class Demo {
 	//class Box10x : View, IScrollView {
@@ -81,22 +82,26 @@ static class Demo {
 		}
 	}
 
-
 	static void ShowTextAlignments ()
 	{
-		var container = new Dialog (
-			"Text Alignments", 50, 20,
-			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
-			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
-
+		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;
+		};
 
 		int i = 0;
-		string txt = "Hello world, how are you doing today";
+		string txt = "Hello world, how are you doing today?";
 		container.Add (
-				new Label (new Rect (0, 1, 40, 3), $"{i+1}-{txt}") { TextAlignment = TextAlignment.Left },
-				new Label (new Rect (0, 3, 40, 3), $"{i+2}-{txt}") { TextAlignment = TextAlignment.Right },
-				new Label (new Rect (0, 5, 40, 3), $"{i+3}-{txt}") { TextAlignment = TextAlignment.Centered },
-				new Label (new Rect (0, 7, 40, 3), $"{i+4}-{txt}") { TextAlignment = TextAlignment.Justified }
+				new Label ($"{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 () }
 			);
 
 		Application.Run (container);
@@ -412,29 +417,39 @@ static class Demo {
 	private static void OnKeyDownUpDemo ()
 	{
 		var container = new Dialog (
-			"OnKeyDown & OnKeyUp demo", 50, 20,
-			new Button ("Ok", is_default: true) { Clicked = () => { Application.RequestStop (); } },
-			new Button ("Cancel") { Clicked = () => { Application.RequestStop (); } });
+			"OnKeyDown & OnKeyUp demo", 80, 20,
+			new Button ("Close") { Clicked = () => { Application.RequestStop (); } }) {
+			Width = Dim.Fill (),
+			Height = Dim.Fill (),
+		};
 
-		var kl = new Label (new Rect (3, 3, 40, 1), "Keyboard: ");
-		container.OnKeyDown += (KeyEvent keyEvent) => KeyUpDown (keyEvent, kl, "Down");
-		container.OnKeyUp += (KeyEvent keyEvent) => KeyUpDown (keyEvent, kl, "Up");
-		container.Add (kl);
-		Application.Run (container);
-	}
+		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);
 
-	private static void KeyUpDown (KeyEvent keyEvent, Label kl, string updown)
-	{
-		kl.TextColor = Colors.TopLevel.Normal;
-		if ((keyEvent.Key & Key.CtrlMask) != 0) {
-			kl.Text = $"Keyboard: Ctrl Key{updown}";
-		} else if ((keyEvent.Key & Key.AltMask) != 0) {
-			kl.Text = $"Keyboard: Alt Key{updown}";
-		} else {
-			kl.Text = $"Keyboard: {(char)keyEvent.KeyValue} Key{updown}";
+		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
+	#endregion
 
 	public static Label ml;
 	public static MenuBar menu;
@@ -551,7 +566,9 @@ static class Demo {
 			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
@@ -570,6 +587,7 @@ static class Demo {
 		};
 #endif
 
+
 		top.Add (win);
 		//top.Add (menu);
 		top.Add (menu, statusBar);

+ 4 - 0
FSharpExample/.editorconfig

@@ -0,0 +1,4 @@
+[*.fs]
+indent_style = space
+indent_size = 4
+tab_width = 4

+ 5 - 1
FSharpExample/FSharpExample.fsproj

@@ -10,7 +10,11 @@
   </ItemGroup>
 
   <ItemGroup>
-    <PackageReference Include="Terminal.Gui" Version="0.9.0" />
+    <ProjectReference Include="..\Terminal.Gui\Terminal.Gui.csproj" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <PackageReference Update="FSharp.Core" Version="4.7.1" />
   </ItemGroup>
 
 </Project>

+ 31 - 0
FSharpExample/FSharpExample.sln

@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.30011.22
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "FSharpExample", "FSharpExample.fsproj", "{6E4DF691-FA5F-4D7C-8DBC-8656103C5CB1}"
+EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Terminal.Gui", "..\Terminal.Gui\Terminal.Gui.csproj", "{FA48E777-1308-489D-95A0-89DE46B65A93}"
+EndProject
+Global
+	GlobalSection(SolutionConfigurationPlatforms) = preSolution
+		Debug|Any CPU = Debug|Any CPU
+		Release|Any CPU = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(ProjectConfigurationPlatforms) = postSolution
+		{6E4DF691-FA5F-4D7C-8DBC-8656103C5CB1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{6E4DF691-FA5F-4D7C-8DBC-8656103C5CB1}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{6E4DF691-FA5F-4D7C-8DBC-8656103C5CB1}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{6E4DF691-FA5F-4D7C-8DBC-8656103C5CB1}.Release|Any CPU.Build.0 = Release|Any CPU
+		{FA48E777-1308-489D-95A0-89DE46B65A93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+		{FA48E777-1308-489D-95A0-89DE46B65A93}.Debug|Any CPU.Build.0 = Debug|Any CPU
+		{FA48E777-1308-489D-95A0-89DE46B65A93}.Release|Any CPU.ActiveCfg = Release|Any CPU
+		{FA48E777-1308-489D-95A0-89DE46B65A93}.Release|Any CPU.Build.0 = Release|Any CPU
+	EndGlobalSection
+	GlobalSection(SolutionProperties) = preSolution
+		HideSolutionNode = FALSE
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+		SolutionGuid = {A023D2E3-EF0F-4986-8E6C-323F967788B7}
+	EndGlobalSection
+EndGlobal

+ 435 - 31
FSharpExample/Program.fs

@@ -1,37 +1,441 @@
 // Learn more about F# at http://fsharp.org
 
-open System
 open Terminal.Gui
+open System
+open Mono.Terminal
+open System.Collections.Generic
+open System.Diagnostics
+open System.Globalization
+open System.Reflection
 open NStack
 
+type Demo() = class end
+    let ustr (x:string) = ustring.Make(x)
+    let mutable ml2 = Unchecked.defaultof<Label>
+    let mutable ml = Unchecked.defaultof<Label>
+    let mutable menu = Unchecked.defaultof<MenuBar>
+    let mutable menuKeysStyle = Unchecked.defaultof<CheckBox>
+    let mutable menuAutoMouseNav = Unchecked.defaultof<CheckBox>
+
+    type Box10x() =
+        inherit View()
+        member val w = 40 with get, set
+        member val h = 50 with get, set
+        member val WantCursorPosition = Unchecked.defaultof<System.Boolean> with get, set
+        new(x : int, y : int) as this =
+            (Box10x())
+            then
+            ()
+        member this.GetContentSize() =
+            new Size(this.w, this.h)
+        member this.SetCursorPosition(pos : Point) =
+            raise (new NotImplementedException())
+        override this.Redraw(region : Rect) =
+            Application.Driver.SetAttribute (Application.Current.ColorScheme.Focus)
+            do
+            let mutable (y : int) = 0
+            while (y < this.h) do
+            this.Move (0, y)
+            Application.Driver.AddStr (ustr (y.ToString()))
+            do
+            let mutable (x : int) = 0
+            while (x < this.w - (y.ToString ()).Length) do
+                if (y.ToString ()).Length < this.w
+                then Application.Driver.AddStr (ustr " ")
+                x <- x + 1
+            x
+            y <- y + 1
+            y
+            ()
+
+    type Filler() =
+        inherit View()
+        new(rect : Rect) as this =
+            (Filler ())
+            then
+            ()
+        override this.Redraw(region : Rect) =
+            Application.Driver.SetAttribute (Application.Current.ColorScheme.Focus)
+            let mutable f = this.Frame
+            do
+            let mutable (y : int) = 0
+            while (y < f.Width) do
+            this.Move (0, y)
+            do
+            let mutable (x : int) = 0
+            while (x < f.Height) do
+                let mutable (r : Rune) = Unchecked.defaultof<Rune>
+                match (x % 3) with
+                | 0 ->
+                    Application.Driver.AddRune ((Rune ((y.ToString ()).ToCharArray (0, 1)).[0]))
+                    if y > 9
+                    then Application.Driver.AddRune ((Rune ((y.ToString ()).ToCharArray (1, 1)).[0]))
+                    r <- (Rune '.')
+                | 1 ->
+                    r <- (Rune 'o')
+                | _ ->
+                    r <- (Rune 'O')
+                Application.Driver.AddRune (r)
+                x <- x + 1
+            x
+            y <- y + 1
+            y
+            ()
+
+    let ShowTextAlignments() =
+        let mutable container = new Dialog(
+            ustr "Text Alignments", 50, 20,
+            new Button (ustr "Ok", true, Clicked = Action(Application.RequestStop)),
+            new Button (ustr "Cancel", true, Clicked = Action(Application.RequestStop))
+            )
+        let mutable (i : int) = 0
+        let mutable (txt : string) = "Hello world, how are you doing today"
+        container.Add (
+            new Label (new Rect (0, 1, 40, 3), ustr ((sprintf "%O-%O" (i + 1)) txt), TextAlignment = TextAlignment.Left),
+            new Label (new Rect (0, 3, 40, 3), ustr ((sprintf "%O-%O" (i + 2)) txt), TextAlignment = TextAlignment.Right),
+            new Label (new Rect (0, 5, 40, 3), ustr ((sprintf "%O-%O" (i + 3)) txt), TextAlignment = TextAlignment.Centered),
+            new Label (new Rect (0, 7, 40, 3), ustr ((sprintf "%O-%O" (i + 4)) txt), TextAlignment = TextAlignment.Justified)
+            )
+        Application.Run (container)
+
+    let ShowEntries(container : View) =
+        let mutable scrollView = new ScrollView (new Rect (50, 10, 20, 8),
+            ContentSize = new Size (20, 50),
+            ShowVerticalScrollIndicator = true,
+            ShowHorizontalScrollIndicator = true
+            )
+        scrollView.Add (new Filler(new Rect(0, 0, 40, 40)))
+        let mutable scrollView2 = new ScrollView (new Rect (72, 10, 3, 3),
+            ContentSize = new Size (100, 100),
+            ShowVerticalScrollIndicator = true,
+            ShowHorizontalScrollIndicator = true
+            )
+        scrollView2.Add (new Box10x(0, 0))
+        let mutable progress = new ProgressBar(new Rect(68, 1, 10, 1))
+        let timer = Func<MainLoop, bool> (fun (caller) ->
+            progress.Pulse ();
+            true)
+
+        Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300.0), timer) |> ignore
+
+        let mutable login = Label (ustr "Login: ",
+            X = Pos.At(3),
+            Y = Pos.At(6)
+            )
+        let mutable password = new Label (ustr "Password: ",
+            X = Pos.Left (login),
+            Y = Pos.Bottom (login) + Pos.At(1)
+            )
+        let mutable loginText = new TextField (ustr "",
+            X = Pos.Right (password),
+            Y = Pos.Top (login),
+            Width = Dim.op_Implicit(40)
+            )
+        let mutable passText = new TextField (ustr "",
+            Secret = true,
+            X = Pos.Left (loginText),
+            Y = Pos.Top (password),
+            Width = Dim.Width (loginText)
+            )
+        let mutable tf = new Button(3, 19, ustr "Ok")
+        container.Add (login, loginText, password, passText,
+            new FrameView (new Rect (3, 10, 25, 6), ustr "Options",
+                [|new CheckBox (1, 0, ustr "Remember me");
+                new RadioGroup (1, 2, [|"_Personal"; "_Company"|])|]
+                ),
+            new ListView (new Rect(59, 6, 16, 4),
+                    [|"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, ustr "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, ustr "Press F9 (on Unix, ESC+9 is an alias) to activate the menubar"),
+            menuKeysStyle,
+            menuAutoMouseNav
+        )
+        container.SendSubviewToBack (tf)
+        ()
+    let NewFile() =
+        let mutable d = new Dialog (ustr "New File", 50, 20,
+                            new Button (ustr "Ok", true, Clicked = Action(Application.RequestStop)),
+                            new Button (ustr "Cancel", true, Clicked = Action(Application.RequestStop))
+        )
+        ml2 <- new Label(1, 1, ustr "Mouse Debug Line")
+        d.Add (ml2)
+        Application.Run (d)
+
+    let Editor(top : Toplevel) =
+        let mutable tframe = top.Frame
+        let mutable ntop = new Toplevel(tframe)
+        let mutable menu = new MenuBar([|new MenuBarItem(ustr "_File",
+            [|new MenuItem(ustr "_Close", "", (fun () -> Application.RequestStop ()))|]);
+            new MenuBarItem(ustr "_Edit", [|new MenuItem(ustr "_Copy", "", Unchecked.defaultof<_>);
+            new MenuItem(ustr "C_ut", "", Unchecked.defaultof<_>);
+            new MenuItem(ustr "_Paste", "", Unchecked.defaultof<_>)|])|]
+            )
+        ntop.Add (menu)
+        let mutable (fname : string) = Unchecked.defaultof<_>
+        for s in [|"/etc/passwd"; "c:\\windows\\win.ini"|] do
+            if System.IO.File.Exists (s)
+            then
+                fname <- s
+        let mutable win = new Window (ustr(if fname <> null then fname else "Untitled"),
+            X = Pos.At(0),
+            Y = Pos.At(1),
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+        )
+        ntop.Add (win)
+        let mutable text = new TextView(new Rect(0, 0, (tframe.Width - 2), (tframe.Height - 3)))
+        if fname <> Unchecked.defaultof<_>
+        then text.Text <- ustr (System.IO.File.ReadAllText (fname))
+        win.Add (text)
+        Application.Run (ntop)
+
+    let Quit() =
+        let mutable n = MessageBox.Query (50, 7, "Quit Demo", "Are you sure you want to quit this demo?", "Yes", "No")
+        n = 0
+
+    let Close() =
+        MessageBox.ErrorQuery (50, 7, "Error", "There is nothing to close", "Ok")
+        |> ignore
+
+    let Open() =
+        let mutable d = new OpenDialog (ustr "Open", ustr "Open a file", AllowsMultipleSelection = true)
+        Application.Run (d)
+        if not d.Canceled
+            then MessageBox.Query (50, 7, "Selected File", (String.Join (", ", d.FilePaths)), "Ok") |> ignore
+
+    let ShowHex(top : Toplevel) =
+        let mutable tframe = top.Frame
+        let mutable ntop = new Toplevel(tframe)
+        let mutable menu = new MenuBar([|new MenuBarItem(ustr "_File",
+            [|new MenuItem(ustr "_Close", "", (fun () -> Application.RequestStop ()))|])|])
+        ntop.Add (menu)
+        let mutable win = new Window (ustr "/etc/passwd",
+            X = Pos.At(0),
+            Y = Pos.At(1),
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+            )
+        ntop.Add (win)
+        let mutable source = System.IO.File.OpenRead ("/etc/passwd")
+        let mutable hex = new HexView (source,
+            X = Pos.At(0),
+            Y = Pos.At(0),
+            Width = Dim.Fill (),
+            Height = Dim.Fill ()
+            )
+        win.Add (hex)
+        Application.Run (ntop)
+
+    type MenuItemDetails() =
+        inherit MenuItem()
+        new(title : ustring, help : string, action : Action) as this =
+            (MenuItemDetails ())
+            then
+                this.Title <- title
+                this.Help <- ustr help
+                this.Action <- action
+        static member Instance(mi : MenuItem) =
+            (mi.GetMenuItem ()) :?> MenuItemDetails
+
+    type MenuItemDelegate = delegate of MenuItemDetails -> MenuItem
+
+    let ShowMenuItem(mi : MenuItemDetails) =
+        let mutable (flags : BindingFlags) = BindingFlags.Public ||| BindingFlags.Static
+        let mutable (minfo : MethodInfo) = typeof<MenuItemDetails>.GetMethod ("Instance", flags)
+        let mutable (mid : Delegate) = Delegate.CreateDelegate (typeof<MenuItemDelegate>, minfo)
+        MessageBox.Query (70, 7, (mi.Title.ToString ()),
+            ((sprintf "%O selected. Is from submenu: %O" (mi.Title.ToString ())) (mi.GetMenuBarItem ())), "Ok")
+        |> ignore
+
+    let MenuKeysStyle_Toggled(e : EventArgs) =
+        menu.UseKeysUpDownAsKeysLeftRight <- menuKeysStyle.Checked
+
+    let MenuAutoMouseNav_Toggled(e : EventArgs) =
+        menu.WantMousePositionReports <- menuAutoMouseNav.Checked
+
+    let Copy() =
+        let mutable (textField : TextField) = menu.LastFocused :?> TextField
+        if textField <> Unchecked.defaultof<_> && textField.SelectedLength <> 0
+        then textField.Copy ()
+        ()
+
+    let Cut() =
+        let mutable (textField : TextField) = menu.LastFocused :?> TextField
+        if textField <> Unchecked.defaultof<_> && textField.SelectedLength <> 0
+        then textField.Cut ()
+        ()
+
+    let Paste() =
+        let mutable (textField : TextField) = menu.LastFocused :?> TextField
+        if textField <> Unchecked.defaultof<_>
+        then textField.Paste ()
+        ()
+
+    let Help() =
+        MessageBox.Query (50, 7, "Help", "This is a small help\nBe kind.", "Ok")
+        |> ignore
+
+    let ListSelectionDemo(multiple : System.Boolean) =
+        let mutable d = new Dialog (ustr "Selection Demo", 60, 20,
+            new Button (ustr "Ok", true, Clicked = fun () -> Application.RequestStop ()),
+            new Button (ustr "Cancel", Clicked = fun () -> Application.RequestStop ())
+            )
+        let mutable animals = new List<string> ()
+        animals.AddRange([|"Alpaca"; "Llama"; "Lion"; "Shark"; "Goat"|])
+        let mutable msg = new Label (ustr "Use space bar or control-t to toggle selection",
+            X = Pos.At(1),
+            Y = Pos.At(1),
+            Width = Dim.Fill () - Dim.op_Implicit(1),
+            Height = Dim.op_Implicit(1)
+            )
+        let mutable list = new ListView (animals,
+            X = Pos.At(1),
+            Y = Pos.At(3),
+            Width = Dim.Fill () - Dim.op_Implicit(4),
+            Height = Dim.Fill () - Dim.op_Implicit(4),
+            AllowsMarking = true,
+            AllowsMultipleSelection = multiple
+            )
+        d.Add (msg, list)
+        Application.Run (d)
+        let mutable result = ""
+        do
+            let mutable (i : int) = 0
+            while (i < animals.Count) do
+            if list.Source.IsMarked (i)
+            then result <- result + animals.[i] + " "
+            i <- i + 1
+            i
+            ()
+        MessageBox.Query (60, 10, "Selected Animals", (if result = "" then "No animals selected" else result), "Ok") |> ignore
+
+    let KeyUpDown(keyEvent : KeyEvent, kl : Label, updown : string) =
+        kl.TextColor <- Colors.TopLevel.Normal
+        if keyEvent.Key &&& Key.CtrlMask <> Key.Unknown
+        then kl.Text <- ustr (sprintf "Keyboard: Ctrl Key%O" updown)
+        else
+            if keyEvent.Key &&& Key.AltMask <> Key.Unknown
+            then kl.Text <- ustr (sprintf "Keyboard: Alt Key%O" updown)
+            else kl.Text <- ustr (sprintf "Keyboard: %O Key%O" (char keyEvent.KeyValue) updown)
+
+    let OnKeyDownUpDemo() =
+        let mutable container = new Dialog(ustr "OnKeyDown & OnKeyUp demo", 50, 20,
+            new Button (ustr "Ok", true, Clicked = fun () -> Application.RequestStop ()),
+            new Button (ustr "Cancel", Clicked = fun () -> Application.RequestStop ())
+            )
+        let mutable kl = new Label(new Rect(3, 3, 40, 1), ustr "Keyboard: ")
+        container.OnKeyDown <- Action<KeyEvent>(fun (keyEvent : KeyEvent) -> KeyUpDown (keyEvent, kl, "Down"))
+        container.OnKeyUp <- Action<KeyEvent>(fun (keyEvent : KeyEvent) -> KeyUpDown (keyEvent, kl, "Up"))
+        container.Add (kl)
+        Application.Run (container)
+
+    let Main() =
+        if Debugger.IsAttached
+        then CultureInfo.DefaultThreadCurrentUICulture <- CultureInfo.GetCultureInfo ("en-US")
+        Application.Init ()
+        let mutable top = Application.Top
+        let mutable (margin : int) = 3
+        let mutable win = new Window (ustr "Hello",
+            X = Pos.At(1),
+            Y = Pos.At(1),
+
+            Width = Dim.Fill () - Dim.op_Implicit(margin),
+            Height = Dim.Fill () - Dim.op_Implicit(margin)
+            )
+        let mutable (menuItems : MenuItemDetails[]) = [|new MenuItemDetails(ustr "F_ind", "", Unchecked.defaultof<_>);
+            new MenuItemDetails(ustr "_Replace", "", Unchecked.defaultof<_>);
+            new MenuItemDetails(ustr "_Item1", "", Unchecked.defaultof<_>);
+            new MenuItemDetails(ustr "_Not From Sub Menu", "", Unchecked.defaultof<_>)|]
+        menuItems.[0].Action <- fun () -> ShowMenuItem (menuItems.[0])
+        menuItems.[1].Action <- fun () -> ShowMenuItem (menuItems.[1])
+        menuItems.[2].Action <- fun () -> ShowMenuItem (menuItems.[2])
+        menuItems.[3].Action <- fun () -> ShowMenuItem (menuItems.[3])
+        menu <-
+            new MenuBar ([|new MenuBarItem(ustr "_File",
+                [|new MenuItem (ustr "Text _Editor Demo", "", (fun () -> Editor (top)));
+                    new MenuItem (ustr "_New", "Creates new file", fun () -> NewFile());
+                    new MenuItem (ustr "_Open", "", fun () -> Open());
+                    new MenuItem (ustr "_Hex", "", (fun () -> ShowHex (top)));
+                    new MenuItem (ustr "_Close", "", (fun () -> Close()));
+                    new MenuItem (ustr "_Disabled", "", (fun () -> ()), (fun () -> false));
+                    Unchecked.defaultof<_>;
+                    new MenuItem (ustr "_Quit", "", (fun () -> if Quit() then top.Running <- false))|]);
+                new MenuBarItem (ustr "_Edit", [|new MenuItem(ustr "_Copy", "", fun () -> Copy());
+                    new MenuItem(ustr "C_ut", "", fun () -> Cut()); new MenuItem(ustr "_Paste", "", fun () -> Paste());
+                    new MenuItem(ustr "_Find and Replace", new MenuBarItem([|(menuItems.[0]);
+                    (menuItems.[1])|])); (menuItems.[3])|]);
+                new MenuBarItem(ustr "_List Demos", [|new MenuItem(ustr "Select _Multiple Items", "", (fun () -> ListSelectionDemo (true)));
+                    new MenuItem(ustr "Select _Single Item", "", (fun () -> ListSelectionDemo (false)))|]);
+                    new MenuBarItem(ustr "A_ssorted", [|new MenuItem(ustr "_Show text alignments", "", (fun () -> ShowTextAlignments ()));
+                new MenuItem(ustr "_OnKeyDown/Up", "", (fun () -> OnKeyDownUpDemo ()))|]);
+                new MenuBarItem(ustr "_Test Menu and SubMenus",
+                    [|new MenuItem(ustr "SubMenu1Item_1", new MenuBarItem([|new MenuItem(ustr "SubMenu2Item_1",
+                    new MenuBarItem([|new MenuItem(ustr "SubMenu3Item_1", new MenuBarItem([|(menuItems.[2])|]))|]))|]))|]);
+                new MenuBarItem(ustr "_About...", "Demonstrates top-level menu item",
+                    (fun () -> MessageBox.ErrorQuery (50, 7, "About Demo", "This is a demo app for gui.cs", "Ok") |> ignore))|])
+        menuKeysStyle <- new CheckBox(3, 25, ustr "UseKeysUpDownAsKeysLeftRight", true)
+        menuKeysStyle.Toggled.Add(MenuKeysStyle_Toggled)
+        menuAutoMouseNav <- new CheckBox(40, 25, ustr "UseMenuAutoNavigation", true)
+        menuAutoMouseNav.Toggled.Add(MenuAutoMouseNav_Toggled)
+        ShowEntries (win)
+        let mutable (count : int) = 0
+        ml <- new Label(new Rect(3, 17, 47, 1), ustr "Mouse: ")
+        Application.RootMouseEvent <- Action<MouseEvent> (
+                fun (me : MouseEvent) ->
+                    ml.TextColor <- Colors.TopLevel.Normal
+                    ml.Text <- ustr (
+                         (((sprintf "Mouse: (%O,%O) - %O %O" me.X) me.Y) me.Flags) (
+                            count <- count + 1
+                            count))
+                            )
+        let mutable test = new Label(3, 18, ustr "Se iniciará el análisis")
+        win.Add (test)
+        win.Add (ml)
+        let mutable drag = new Label (ustr "Drag: ", X = Pos.At(70), Y = Pos.At(24))
+        let mutable dragText = new TextField (ustr "",
+            X = Pos.Right (drag),
+            Y = Pos.Top (drag),
+            Width = Dim.op_Implicit(40)
+            )
+        let mutable statusBar = new StatusBar ([|
+            new StatusItem(Key.F1, ustr "~F1~ Help", Action(Help));
+            new StatusItem(Key.F2, ustr "~F2~ Load", null);
+            new StatusItem(Key.F3, ustr "~F3~ Save", null);
+            new StatusItem(Key.ControlX, ustr "~^X~ Quit", fun () -> if (Quit ()) then top.Running <- false)
+            |],
+            Parent = null
+            )
+        win.Add (drag, dragText)
+        let mutable bottom = new Label(ustr "This should go on the bottom of the same top-level!")
+        win.Add (bottom)
+        let mutable bottom2 = new Label(ustr "This should go on the bottom of another top-level!")
+        top.Add (bottom2)
+        Application.OnLoad <- Action (
+            fun () ->
+                bottom.X <- win.X
+                bottom.Y <- Pos.Bottom (win) - Pos.Top (win) - Pos.At(margin)
+                bottom2.X <- Pos.Left (win)
+                bottom2.Y <- Pos.Bottom (win)
+                )
+        top.Add (win)
+        top.Add (menu, statusBar)
+        Application.Run ()
 
-let ustr (x:string) = ustring.Make(x)
-
-let stop = Action Application.RequestStop
-
-let newFile() =
-    let ok = Button (ustr "Ok", true, Clicked=stop)
-    let cancel = Button (ustr "Cancel", Clicked=stop)
-    let d = Dialog (ustr ("New File"), 50, 20, ok, cancel)
-    Application.Run (d)
-
-let quit() =
-    if MessageBox.Query (50, 7, "Quit demo", "Are you sure you want to quit the demo?", "Yes", "No") = 0 then
-       Application.Top.Running <- false
-
-let buildMenu() =
-    MenuBar ([|
-        MenuBarItem (ustr ("File"), 
-            [| MenuItem(ustr("_New"), "Creates a new file", System.Action newFile);
-               MenuItem(ustr("_Quit"), null, System.Action quit)
-             |])|])
-
-[<EntryPoint>]
-let main argv =
-    Application.Init ()
-    let top = Application.Top
-    let win = Window (ustr "Hello", X=Pos.op_Implicit(0), Y=Pos.op_Implicit(1), Width=Dim.Fill(), Height=Dim.Fill())
-    top.Add (buildMenu())
-    top.Add (win)
-    Application.Run ()
-    0 // return an integer exit code
+module Demo__run =
+    [<EntryPoint>]
+    let main argv =
+        Main ()
+        0

+ 62 - 18
Terminal.Gui/Drivers/WindowsDriver.cs

@@ -499,7 +499,9 @@ namespace Terminal.Gui {
 
 		void IMainLoopDriver.Wakeup ()
 		{
-			tokenSource.Cancel ();
+			//tokenSource.Cancel ();
+			eventReady.Reset ();
+			eventReady.Set ();
 		}
 
 		bool IMainLoopDriver.EventsPending (bool wait)
@@ -594,6 +596,8 @@ namespace Terminal.Gui {
 
 			case WindowsConsole.EventType.Mouse:
 				mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
+				if (IsButtonReleased)
+					mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
 				break;
 
 			case WindowsConsole.EventType.WindowBufferSize:
@@ -608,8 +612,10 @@ namespace Terminal.Gui {
 		}
 
 		WindowsConsole.ButtonState? LastMouseButtonPressed = null;
+		bool IsButtonPressed = false;
 		bool IsButtonReleased = false;
 		bool IsButtonDoubleClicked = false;
+		Point point;
 
 		MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
 		{
@@ -617,8 +623,8 @@ namespace Terminal.Gui {
 
 			if (IsButtonDoubleClicked) {
 				Task.Run (async () => {
-					await Task.Delay (300);
-					_ = new Action (() => IsButtonDoubleClicked = false);
+					await Task.Delay (100);
+					IsButtonDoubleClicked = false;
 				});
 			}
 
@@ -629,12 +635,13 @@ namespace Terminal.Gui {
 			// map to the correct clicked event.
 			if ((LastMouseButtonPressed != null || IsButtonReleased) && mouseEvent.ButtonState != 0) {
 				LastMouseButtonPressed = null;
+				IsButtonPressed = false;
 				IsButtonReleased = false;
 			}
 
 			if ((mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null && !IsButtonDoubleClicked) ||
 				(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved &&
-				mouseEvent.ButtonState != 0 && !IsButtonDoubleClicked)) {
+				mouseEvent.ButtonState != 0 && !IsButtonReleased && !IsButtonDoubleClicked)) {
 				switch (mouseEvent.ButtonState) {
 				case WindowsConsole.ButtonState.Button1Pressed:
 					mouseFlag = MouseFlags.Button1Pressed;
@@ -649,11 +656,39 @@ namespace Terminal.Gui {
 					break;
 				}
 
-				if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
+				if (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) {
 					mouseFlag |= MouseFlags.ReportMousePosition;
+					point = new Point ();
+					IsButtonReleased = false;
+				} else {
+					point = new Point () {
+						X = mouseEvent.MousePosition.X,
+						Y = mouseEvent.MousePosition.Y
+					};
+				}
 				LastMouseButtonPressed = mouseEvent.ButtonState;
-			} else if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed != null && !IsButtonReleased &&
-				!IsButtonDoubleClicked) {
+				IsButtonPressed = true;
+
+				if ((mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+					Task.Run (async () => {
+						while (IsButtonPressed) {
+							await Task.Delay (200);
+							var me = new MouseEvent () {
+								X = mouseEvent.MousePosition.X,
+								Y = mouseEvent.MousePosition.Y,
+								Flags = mouseFlag
+							};
+
+							if (IsButtonPressed && (mouseFlag & MouseFlags.ReportMousePosition) == 0) {
+								mouseHandler (me);
+								mainLoop.Driver.Wakeup ();
+							}
+						}
+					});
+				}
+
+			} else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
+				LastMouseButtonPressed != null && !IsButtonReleased && !IsButtonDoubleClicked) {
 				switch (LastMouseButtonPressed) {
 				case WindowsConsole.ButtonState.Button1Pressed:
 					mouseFlag = MouseFlags.Button1Released;
@@ -667,21 +702,30 @@ namespace Terminal.Gui {
 					mouseFlag = MouseFlags.Button4Released;
 					break;
 				}
+				IsButtonPressed = false;
 				IsButtonReleased = true;
 			} else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
-				IsButtonReleased) {
-				switch (LastMouseButtonPressed) {
-				case WindowsConsole.ButtonState.Button1Pressed:
-					mouseFlag = MouseFlags.Button1Clicked;
-					break;
+				  IsButtonReleased) {
+				var p = new Point () {
+					X = mouseEvent.MousePosition.X,
+					Y = mouseEvent.MousePosition.Y
+				};
+				if (p == point) {
+					switch (LastMouseButtonPressed) {
+					case WindowsConsole.ButtonState.Button1Pressed:
+						mouseFlag = MouseFlags.Button1Clicked;
+						break;
 
-				case WindowsConsole.ButtonState.Button2Pressed:
-					mouseFlag = MouseFlags.Button2Clicked;
-					break;
+					case WindowsConsole.ButtonState.Button2Pressed:
+						mouseFlag = MouseFlags.Button2Clicked;
+						break;
 
-				case WindowsConsole.ButtonState.RightmostButtonPressed:
-					mouseFlag = MouseFlags.Button4Clicked;
-					break;
+					case WindowsConsole.ButtonState.RightmostButtonPressed:
+						mouseFlag = MouseFlags.Button4Clicked;
+						break;
+					}
+				} else {
+					mouseFlag = 0;
 				}
 				LastMouseButtonPressed = null;
 				IsButtonReleased = false;

+ 0 - 840
Terminal.Gui/Drivers/WindowsDriver.cs.orig

@@ -1,840 +0,0 @@
-//
-// WindowsDriver.cs: Windows specific driver 
-//
-// Authors:
-//   Miguel de Icaza ([email protected])
-//   Nick Van Dyck ([email protected])
-//
-// Copyright (c) 2018
-//
-// Permission is hereby granted, free of charge, to any person obtaining a copy
-// of this software and associated documentation files (the "Software"), to deal
-// in the Software without restriction, including without limitation the rights
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-// copies of the Software, and to permit persons to whom the Software is
-// furnished to do so, subject to the following conditions:
-// 
-// The above copyright notice and this permission notice shall be included in all
-// copies or substantial portions of the Software.
-// 
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-// SOFTWARE.
-//
-using System;
-using System.Runtime.InteropServices;
-using System.Threading;
-using System.Threading.Tasks;
-using Mono.Terminal;
-using NStack;
-
-namespace Terminal.Gui {
-
-	internal class WindowsConsole {
-		public const int STD_OUTPUT_HANDLE = -11;
-		public const int STD_INPUT_HANDLE = -10;
-		public const int STD_ERROR_HANDLE = -12;
-
-		internal IntPtr InputHandle, OutputHandle;
-		IntPtr ScreenBuffer;
-		uint originalConsoleMode;
-
-		public WindowsConsole ()
-		{
-			InputHandle = GetStdHandle (STD_INPUT_HANDLE);
-			OutputHandle = GetStdHandle (STD_OUTPUT_HANDLE);
-			originalConsoleMode = ConsoleMode;
-			var newConsoleMode = originalConsoleMode;
-			newConsoleMode |= (uint)(ConsoleModes.EnableMouseInput | ConsoleModes.EnableExtendedFlags);
-			newConsoleMode &= ~(uint)ConsoleModes.EnableQuickEditMode;
-			ConsoleMode = newConsoleMode;
-		}
-
-		public CharInfo[] OriginalStdOutChars;
-
-		public bool WriteToConsole (CharInfo[] charInfoBuffer, Coord coords, SmallRect window)
-		{
-			if (ScreenBuffer == IntPtr.Zero)
-			{
-				ScreenBuffer = CreateConsoleScreenBuffer (
-					DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
-					ShareMode.FileShareRead | ShareMode.FileShareWrite,
-					IntPtr.Zero,
-					1,
-					IntPtr.Zero
-				);
-				if (ScreenBuffer == INVALID_HANDLE_VALUE){
-					var err = Marshal.GetLastWin32Error ();
-
-					if (err != 0)
-					throw new System.ComponentModel.Win32Exception(err);
-				}
-
-				if (!SetConsoleActiveScreenBuffer (ScreenBuffer)){
-					var err = Marshal.GetLastWin32Error();
-					throw new System.ComponentModel.Win32Exception(err);
-				}
-
-				OriginalStdOutChars = new CharInfo[Console.WindowHeight * Console.WindowWidth];
-
-				ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
-			}
-
-			return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = 0, Y = 0 }, ref window);
-		}
-
-		public bool SetCursorPosition(Coord position)
-		{
-			return SetConsoleCursorPosition (ScreenBuffer, position);
-		}
-
-		public void Cleanup ()
-		{
-			ContinueListeningForConsoleEvents = false;
-			if (!SetConsoleActiveScreenBuffer (OutputHandle)){
-				var err = Marshal.GetLastWin32Error ();
-				Console.WriteLine("Error: {0}", err);
-			}
-		}
-
-		private bool ContinueListeningForConsoleEvents = true;
-
-		public uint ConsoleMode {
-			get {
-				uint v;
-				GetConsoleMode (InputHandle, out v);
-				return v;
-			}
-
-			set {
-				SetConsoleMode (InputHandle, value);
-			}
-		}
-
-		[Flags]
-		public enum ConsoleModes : uint
-		{
-			EnableMouseInput = 16,
-			EnableQuickEditMode = 64,
-			EnableExtendedFlags = 128,
-		}
-
-		[StructLayout (LayoutKind.Explicit, CharSet = CharSet.Unicode)]
-		public struct KeyEventRecord {
-			[FieldOffset (0), MarshalAs (UnmanagedType.Bool)]
-			public bool bKeyDown;
-			[FieldOffset (4), MarshalAs (UnmanagedType.U2)]
-			public ushort wRepeatCount;
-			[FieldOffset (6), MarshalAs (UnmanagedType.U2)]
-			public ushort wVirtualKeyCode;
-			[FieldOffset (8), MarshalAs (UnmanagedType.U2)]
-			public ushort wVirtualScanCode;
-			[FieldOffset (10)]
-			public char UnicodeChar;
-			[FieldOffset (12), MarshalAs (UnmanagedType.U4)]
-			public ControlKeyState dwControlKeyState;
-		}
-
-		[Flags]
-		public enum ButtonState {
-			Button1Pressed = 1,
-			Button2Pressed = 4,
-			Button3Pressed = 8,
-			Button4Pressed = 16,
-			RightmostButtonPressed = 2,
-
-		}
-
-		[Flags]
-		public enum ControlKeyState {
-			RightAltPressed = 1,
-			LeftAltPressed = 2,
-			RightControlPressed = 4,
-			LeftControlPressed = 8,
-			ShiftPressed = 16,
-			NumlockOn = 32,
-			ScrolllockOn = 64,
-			CapslockOn = 128,
-			EnhancedKey = 256
-		}
-
-		[Flags]
-		public enum EventFlags {
-			MouseMoved = 1,
-			DoubleClick = 2,
-			MouseWheeled = 4,
-			MouseHorizontalWheeled = 8
-		}
-
-		[StructLayout (LayoutKind.Explicit)]
-		public struct MouseEventRecord {
-			[FieldOffset (0)]
-			public Coordinate MousePosition;
-			[FieldOffset (4)]
-			public ButtonState ButtonState;
-			[FieldOffset (8)]
-			public ControlKeyState ControlKeyState;
-			[FieldOffset (12)]
-			public EventFlags EventFlags;
-
-			public override string ToString ()
-			{
-				return $"[Mouse({MousePosition},{ButtonState},{ControlKeyState},{EventFlags}";
-			}
-		}
-
-		[StructLayout (LayoutKind.Sequential)]
-		public struct Coordinate {
-			public short X;
-			public short Y;
-
-			public Coordinate (short X, short Y)
-			{
-				this.X = X;
-				this.Y = Y;
-			}
-
-			public override string ToString () => $"({X},{Y})";
-		};
-
-		internal struct WindowBufferSizeRecord {
-			public Coordinate size;
-
-			public WindowBufferSizeRecord (short x, short y)
-			{
-				this.size = new Coordinate (x, y);
-			}
-
-			public override string ToString () => $"[WindowBufferSize{size}";
-		}
-
-		[StructLayout (LayoutKind.Sequential)]
-		public struct MenuEventRecord {
-			public uint dwCommandId;
-		}
-
-		[StructLayout (LayoutKind.Sequential)]
-		public struct FocusEventRecord {
-			public uint bSetFocus;
-		}
-
-		public enum EventType {
-			Focus = 0x10,
-			Key = 0x1,
-			Menu = 0x8,
-			Mouse = 2,
-			WindowBufferSize = 4
-		}
-
-		[StructLayout (LayoutKind.Explicit)]
-		public struct InputRecord {
-			[FieldOffset (0)]
-			public EventType EventType;
-			[FieldOffset (4)]
-			public KeyEventRecord KeyEvent;
-			[FieldOffset (4)]
-			public MouseEventRecord MouseEvent;
-			[FieldOffset (4)]
-			public WindowBufferSizeRecord WindowBufferSizeEvent;
-			[FieldOffset (4)]
-			public MenuEventRecord MenuEvent;
-			[FieldOffset (4)]
-			public FocusEventRecord FocusEvent;
-
-			public override string ToString ()
-			{
-				switch (EventType) {
-				case EventType.Focus:
-					return FocusEvent.ToString ();
-				case EventType.Key:
-					return KeyEvent.ToString ();
-				case EventType.Menu:
-					return MenuEvent.ToString ();
-				case EventType.Mouse:
-					return MouseEvent.ToString ();
-				case EventType.WindowBufferSize:
-					return WindowBufferSizeEvent.ToString ();
-				default:
-					return "Unknown event type: " + EventType;
-				}
-			}
-		};
-
-		[Flags]
-		enum ShareMode : uint
-		{
-			FileShareRead = 1,
-			FileShareWrite = 2,
-		}
-
-		[Flags]
-		enum DesiredAccess : uint
-		{
-			GenericRead = 2147483648,
-			GenericWrite = 1073741824,
-		}
-
-		[StructLayout(LayoutKind.Sequential)]
-		public struct ConsoleScreenBufferInfo
-		{
-			public Coord dwSize;
-			public Coord dwCursorPosition;
-			public ushort wAttributes;
-			public SmallRect srWindow;
-			public Coord dwMaximumWindowSize;
-		}
-
-		[StructLayout(LayoutKind.Sequential)]
-		public struct Coord
-		{
-			public short X;
-			public short Y;
-
-			public Coord(short X, short Y)
-			{
-				this.X = X;
-				this.Y = Y;
-			}
-		};
-
-		[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
-		public struct CharUnion
-		{
-			[FieldOffset(0)] public char UnicodeChar;
-			[FieldOffset(0)] public byte AsciiChar;
-		}
-
-		[StructLayout(LayoutKind.Explicit, CharSet=CharSet.Unicode)]
-		public struct CharInfo
-		{
-			[FieldOffset(0)] public CharUnion Char;
-			[FieldOffset(2)] public ushort Attributes;
-		}
-
-		[StructLayout(LayoutKind.Sequential)]
-		public struct SmallRect
-		{
-			public short Left;
-			public short Top;
-			public short Right;
-			public short Bottom;
-		}
-
-		[DllImport ("kernel32.dll", SetLastError = true)]
-		static extern IntPtr GetStdHandle (int nStdHandle);
-
-		[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
-		public static extern bool ReadConsoleInput (
-			IntPtr hConsoleInput,
-			[Out] InputRecord [] lpBuffer,
-			uint nLength,
-			out uint lpNumberOfEventsRead);
-
-		[DllImport("kernel32.dll", SetLastError=true, CharSet=CharSet.Unicode)]
-		static extern bool ReadConsoleOutput(
-			IntPtr hConsoleOutput,
-			[Out] CharInfo[] lpBuffer,
-			Coord dwBufferSize,
-			Coord dwBufferCoord,
-			ref SmallRect lpReadRegion
-		);
-		
-		[DllImport("kernel32.dll", EntryPoint="WriteConsoleOutput", SetLastError=true, CharSet=CharSet.Unicode)]
-		static extern bool WriteConsoleOutput(
-			IntPtr hConsoleOutput,
-			CharInfo[] lpBuffer,
-			Coord dwBufferSize,
-			Coord dwBufferCoord,
-			ref SmallRect lpWriteRegion
-		);
-
-		[DllImport ("kernel32.dll")]
-		static extern bool SetConsoleCursorPosition(IntPtr hConsoleOutput, Coord dwCursorPosition);
-
-		[DllImport ("kernel32.dll")]
-		static extern bool GetConsoleMode (IntPtr hConsoleHandle, out uint lpMode);
-
-
-		[DllImport ("kernel32.dll")]
-		static extern bool SetConsoleMode (IntPtr hConsoleHandle, uint dwMode);
-
-		[DllImport("kernel32.dll", SetLastError = true)]
-		static extern IntPtr CreateConsoleScreenBuffer(
-			DesiredAccess dwDesiredAccess,
-			ShareMode dwShareMode,
-			IntPtr secutiryAttributes,
-			UInt32 flags,
-			IntPtr screenBufferData
-		);
-
-		internal static IntPtr INVALID_HANDLE_VALUE = new IntPtr (-1);
-
-
-		[DllImport("kernel32.dll", SetLastError = true)]
-		static extern bool SetConsoleActiveScreenBuffer(IntPtr Handle);
-
-		[DllImport ("kernel32.dll", SetLastError = true)]
-		static extern bool GetNumberOfConsoleInputEvents (IntPtr handle, out uint lpcNumberOfEvents);
-		public uint InputEventCount {
-			get {
-				uint v;
-				GetNumberOfConsoleInputEvents (InputHandle, out v);
-				return v;
-			}
-		}
-	}
-
-	internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
-		static bool sync;
-		AutoResetEvent eventReady = new AutoResetEvent (false);
-		AutoResetEvent waitForProbe = new AutoResetEvent (false);
-		MainLoop mainLoop;
-		Action TerminalResized;
-		WindowsConsole.CharInfo[] OutputBuffer;
-		int cols, rows;
-		WindowsConsole winConsole;
-
-		public override int Cols => cols;
-		public override int Rows => rows;
-
-		public WindowsDriver ()
-		{
-			winConsole = new WindowsConsole();
-
-			cols = Console.WindowWidth;
-			rows = Console.WindowHeight - 1;
-
-			ResizeScreen ();
-			UpdateOffScreen ();
-
-			Task.Run ((Action)WindowsInputHandler);
-		}
-
-		// The records that we keep fetching
-		WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1];
-
-		void WindowsInputHandler () 
-		{
-			while (true) {
-				waitForProbe.WaitOne ();
-
-				uint numberEventsRead = 0;
-
-				WindowsConsole.ReadConsoleInput (winConsole.InputHandle, records, 1, out numberEventsRead);
-				if (numberEventsRead == 0)
-					result = null;
-				else
-					result = records;
-				
-				eventReady.Set ();
-			}
-		}
-
-		void IMainLoopDriver.Setup (MainLoop mainLoop)
-		{
-			this.mainLoop = mainLoop;
-		}
-
-		void IMainLoopDriver.Wakeup ()
-		{
-		}
-
-		bool IMainLoopDriver.EventsPending (bool wait)
-		{
-			long now = DateTime.UtcNow.Ticks;
-
-			int waitTimeout;
-			if (mainLoop.timeouts.Count > 0) {
-				waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
-				if (waitTimeout < 0)
-					return true;
-			} else
-				waitTimeout = -1;
-
-			if (!wait)
-				waitTimeout = 0;
-
-			result = null;
-			waitForProbe.Set ();
-			eventReady.WaitOne (waitTimeout);
-			return result != null;
-		}
-
-		Action<KeyEvent> keyHandler;
-		Action<MouseEvent> mouseHandler;
-
-		public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
-		{
-			this.keyHandler = keyHandler;
-			this.mouseHandler = mouseHandler;
-		}
-		
-
-		void IMainLoopDriver.MainIteration ()
-		{
-			if (result == null)
-				return;
-
-			var inputEvent = result [0];
-			switch (inputEvent.EventType) {
-			case WindowsConsole.EventType.Key:
-				if (inputEvent.KeyEvent.bKeyDown == false)
-					return;
-				var map = MapKey (ToConsoleKeyInfo (inputEvent.KeyEvent));
-				if (map == (Key)0xffffffff)
-					return;
-				keyHandler (new KeyEvent (map));
-				break;
-
-			case WindowsConsole.EventType.Mouse:
-				mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
-				break;
-
-			case WindowsConsole.EventType.WindowBufferSize:
-				cols = inputEvent.WindowBufferSizeEvent.size.X;
-				rows = inputEvent.WindowBufferSizeEvent.size.Y - 1;
-				ResizeScreen ();
-				UpdateOffScreen ();
-				TerminalResized ();
-				break;
-			}
-			result = null;
-		}
-
-		private WindowsConsole.ButtonState? LastMouseButtonPressed = null;
-
-		private MouseEvent ToDriverMouse(WindowsConsole.MouseEventRecord mouseEvent)
-		{
-			MouseFlags mouseFlag = MouseFlags.AllEvents;
-
-			// The ButtonState member of the MouseEvent structure has bit corresponding to each mouse button.
-			// This will tell when a mouse button is pressed. When the button is released this event will
-			// be fired with it's bit set to 0. So when the button is up ButtonState will be 0.
-			// To map to the correct driver events we save the last pressed mouse button so we can
-			// map to the correct clicked event.
-			if (LastMouseButtonPressed != null && mouseEvent.ButtonState != 0)
-			{
-				LastMouseButtonPressed = null;
-			}
-
-			if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed == null){
-				switch (mouseEvent.ButtonState){
-				case WindowsConsole.ButtonState.Button1Pressed:
-					mouseFlag = MouseFlags.Button1Pressed;
-					break;
-
-				case WindowsConsole.ButtonState.Button2Pressed:
-					mouseFlag = MouseFlags.Button2Pressed;
-					break;
-
-				case WindowsConsole.ButtonState.Button3Pressed:
-					mouseFlag = MouseFlags.Button3Pressed;
-					break;
-				}
-				LastMouseButtonPressed = mouseEvent.ButtonState;
-			} else if (mouseEvent.EventFlags == 0 && LastMouseButtonPressed != null){
-				switch (LastMouseButtonPressed){
-				case WindowsConsole.ButtonState.Button1Pressed:
-					mouseFlag = MouseFlags.Button1Clicked;
-					break;
-
-				case WindowsConsole.ButtonState.Button2Pressed:
-					mouseFlag = MouseFlags.Button2Clicked;
-					break;
-
-				case WindowsConsole.ButtonState.Button3Pressed:
-					mouseFlag = MouseFlags.Button3Clicked;
-					break;
-				}
-				LastMouseButtonPressed = null;
-			} else if(mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved){
-				mouseFlag = MouseFlags.ReportMousePosition;
-			}
-
-			return new MouseEvent () {
-				X = mouseEvent.MousePosition.X,
-				Y = mouseEvent.MousePosition.Y,
-				Flags = mouseFlag
-			};
-		}
-
-		private ConsoleKeyInfo ToConsoleKeyInfo (WindowsConsole.KeyEventRecord keyEvent)
-		{
-			var state = keyEvent.dwControlKeyState;
-
-			bool shift = (state & WindowsConsole.ControlKeyState.ShiftPressed) != 0;
-			bool alt = (state & (WindowsConsole.ControlKeyState.LeftAltPressed | WindowsConsole.ControlKeyState.RightAltPressed)) != 0;
-			bool control = (state & (WindowsConsole.ControlKeyState.LeftControlPressed | WindowsConsole.ControlKeyState.RightControlPressed)) != 0;
-
-			return new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
-		}
-
-		public Key MapKey (ConsoleKeyInfo keyInfo)
-		{
-			switch (keyInfo.Key) {
-			case ConsoleKey.Escape:
-				return Key.Esc;
-			case ConsoleKey.Tab:
-				return Key.Tab;
-			case ConsoleKey.Home:
-				return Key.Home;
-			case ConsoleKey.End:
-				return Key.End;
-			case ConsoleKey.LeftArrow:
-				return Key.CursorLeft;
-			case ConsoleKey.RightArrow:
-				return Key.CursorRight;
-			case ConsoleKey.UpArrow:
-				return Key.CursorUp;
-			case ConsoleKey.DownArrow:
-				return Key.CursorDown;
-			case ConsoleKey.PageUp:
-				return Key.PageUp;
-			case ConsoleKey.PageDown:
-				return Key.PageDown;
-			case ConsoleKey.Enter:
-				return Key.Enter;
-			case ConsoleKey.Spacebar:
-				return Key.Space;
-			case ConsoleKey.Backspace:
-				return Key.Backspace;
-			case ConsoleKey.Delete:
-				return Key.DeleteChar;
-
-			case ConsoleKey.Oem1:
-			case ConsoleKey.Oem2:
-			case ConsoleKey.Oem3:
-			case ConsoleKey.Oem4:
-			case ConsoleKey.Oem5:
-			case ConsoleKey.Oem6:
-			case ConsoleKey.Oem7:
-			case ConsoleKey.Oem8:
-			case ConsoleKey.Oem102:
-			case ConsoleKey.OemPeriod:
-			case ConsoleKey.OemComma:
-			case ConsoleKey.OemPlus:
-			case ConsoleKey.OemMinus:
-				return (Key)((uint)keyInfo.KeyChar);
-			}
-
-			var key = keyInfo.Key;
-			if (key >= ConsoleKey.A && key <= ConsoleKey.Z) {
-				var delta = key - ConsoleKey.A;
-				if (keyInfo.Modifiers == ConsoleModifiers.Control)
-					return (Key)((uint)Key.ControlA + delta);
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'A' + delta));
-				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
-					return (Key)((uint)'A' + delta);
-				else
-					return (Key)((uint)'a' + delta);
-			}
-			if (key >= ConsoleKey.D0 && key <= ConsoleKey.D9) {
-				var delta = key - ConsoleKey.D0;
-				if (keyInfo.Modifiers == ConsoleModifiers.Alt)
-					return (Key)(((uint)Key.AltMask) | ((uint)'0' + delta));
-				if (keyInfo.Modifiers == ConsoleModifiers.Shift)
-					return (Key)((uint)keyInfo.KeyChar);
-				return (Key)((uint)'0' + delta);
-			}
-			if (key >= ConsoleKey.F1 && key <= ConsoleKey.F10) {
-				var delta = key - ConsoleKey.F1;
-
-				return (Key)((int)Key.F1 + delta);
-			}
-			return (Key)(0xffffffff);
-		}
-
-		public override void Init (Action terminalResized)
-		{
-			TerminalResized = terminalResized;
-
-			Colors.Base = new ColorScheme ();
-			Colors.Dialog = new ColorScheme ();
-			Colors.Menu = new ColorScheme ();
-			Colors.Error = new ColorScheme ();
-
-			HLine = '\u2500';
-			VLine = '\u2502';
-			Stipple = '\u2592';
-			Diamond = '\u25c6';
-			ULCorner = '\u250C';
-			LLCorner = '\u2514';
-			URCorner = '\u2510';
-			LRCorner = '\u2518';
-			LeftTee = '\u251c';
-			RightTee = '\u2524';
-			TopTee = '\u22a4';
-			BottomTee = '\u22a5';
-
-			Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Blue);
-			Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
-			Colors.Base.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Blue);
-			Colors.Base.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-
-			Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
-			Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
-			Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
-			Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Cyan);
-
-			Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
-			Colors.Dialog.HotNormal = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
-			Colors.Dialog.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Cyan);
-
-			Colors.Error.Normal = MakeColor (ConsoleColor.White, ConsoleColor.Red);
-			Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
-			Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
-			Colors.Error.HotFocus = Colors.Error.HotNormal;
-			Console.Clear ();
-		}
-
-		void ResizeScreen ()
-		{
-			OutputBuffer = new WindowsConsole.CharInfo[Rows * Cols];
-			Clip = new Rect (0, 0, Cols, Rows);
-		}
-
-		void UpdateOffScreen ()
-		{
-			for (int row = 0; row < rows; row++)
-				for (int col = 0; col < cols; col++){
-					int position = row * cols + col;
-					OutputBuffer[position].Attributes = (ushort)MakeColor(ConsoleColor.White, ConsoleColor.Blue);
-					OutputBuffer[position].Char.UnicodeChar = ' ';
-				}
-		}
-
-		int ccol, crow;
-		public override void Move (int col, int row)
-		{
-			ccol = col;
-			crow = row;
-		}
-
-		public override void AddRune (Rune rune)
-		{
-			var position = crow * Cols + ccol;
-
-			if (Clip.Contains (ccol, crow)){
-				OutputBuffer[position].Attributes = (ushort)currentAttribute;
-				OutputBuffer[position].Char.UnicodeChar = (char)rune;
-			}
-
-			ccol++;
-			if (ccol == Cols) {
-				ccol = 0;
-				if (crow + 1 < Rows)
-					crow++;
-			}
-			if (sync)
-				UpdateScreen ();
-		}
-
-		public override void AddStr (ustring str)
-		{
-			foreach (var rune in str)
-				AddRune (rune);
-		}
-
-		int currentAttribute;
-		public override void SetAttribute (Attribute c)
-		{
-			currentAttribute = c.value;
-		}
-
-		private Attribute MakeColor (ConsoleColor f, ConsoleColor b)
-		{
-			// Encode the colors into the int value.
-			return new Attribute (){
-				value = ((int)f | (int)b << 4)
-			};
-		}
-
-		public override void Refresh()
-		{
-			var bufferCoords = new WindowsConsole.Coord (){
-				X = (short)Clip.Width,
-				Y = (short)Clip.Height
-			};
-
-			var window = new WindowsConsole.SmallRect (){
-				Top = 0,
-				Left = 0,
-				Right = (short)Clip.Right,
-				Bottom = (short)Clip.Bottom
-			};
-
-			UpdateCursor();
-			winConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
-		}
-
-		public override void UpdateScreen ()
-		{
-			var bufferCoords = new WindowsConsole.Coord (){
-				X = (short)Clip.Width,
-				Y = (short)Clip.Height
-			};
-
-			var window = new WindowsConsole.SmallRect (){
-				Top = 0,
-				Left = 0,
-				Right = (short)Clip.Right,
-				Bottom = (short)Clip.Bottom
-			};
-
-			UpdateCursor();
-			winConsole.WriteToConsole (OutputBuffer, bufferCoords, window);
-		}
-
-		public override void UpdateCursor()
-		{
-			var position = new WindowsConsole.Coord(){
-				X = (short)ccol,
-				Y = (short)crow
-			};
-			winConsole.SetCursorPosition(position);
-		}
-		public override void End ()
-		{
-			winConsole.Cleanup();
-		}
-
-		#region Unused
-		public override void SetColors (ConsoleColor foreground, ConsoleColor background)
-		{
-		}
-
-		public override void SetColors (short foregroundColorId, short backgroundColorId)
-		{
-		}
-
-		public override void Suspend ()
-		{
-		}
-
-		public override void StartReportingMouseMoves ()
-		{
-		}
-
-		public override void StopReportingMouseMoves ()
-		{
-		}
-
-		public override void UncookMouse ()
-		{
-		}
-
-		public override void CookMouse ()
-		{
-		}
-		#endregion
-
-	}
-
-	
-}

+ 17 - 18
Terminal.Gui/MonoCurses/mainloop.cs

@@ -13,10 +13,10 @@
 // distribute, sublicense, and/or sell copies of the Software, and to
 // permit persons to whom the Software is furnished to do so, subject to
 // the following conditions:
-// 
+//
 // The above copyright notice and this permission notice shall be
 // included in all copies or substantial portions of the Software.
-// 
+//
 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -138,7 +138,7 @@ namespace Mono.Terminal {
 			AddWatch (wakeupPipes [0], Condition.PollIn, ml => {
 				read (wakeupPipes [0], ignore, (IntPtr)1);
 				return true;
-			});			
+			});
 		}
 
 		/// <summary>
@@ -192,7 +192,7 @@ namespace Mono.Terminal {
 			}
 		}
 
-		bool IMainLoopDriver.EventsPending (bool wait) 
+		bool IMainLoopDriver.EventsPending (bool wait)
 		{
 			long now = DateTime.UtcNow.Ticks;
 
@@ -214,10 +214,10 @@ namespace Mono.Terminal {
 			int ic;
 			lock (mainLoop.idleHandlers)
 				ic = mainLoop.idleHandlers.Count;
-			return n > 0 || mainLoop.timeouts.Count > 0 && ((mainLoop.timeouts.Keys [0] - DateTime.UtcNow.Ticks) < 0) || ic > 0;			
+			return n > 0 || mainLoop.timeouts.Count > 0 && ((mainLoop.timeouts.Keys [0] - DateTime.UtcNow.Ticks) < 0) || ic > 0;
 		}
 
-		void IMainLoopDriver.MainIteration () 
+		void IMainLoopDriver.MainIteration ()
 		{
 			if (pollmap != null) {
 				foreach (var p in pollmap) {
@@ -231,7 +231,7 @@ namespace Mono.Terminal {
 					if (!watch.Callback (this.mainLoop))
 						descriptorWatchers.Remove (p.fd);
 				}
-			}			
+			}
 		}
 	}
 
@@ -247,7 +247,7 @@ namespace Mono.Terminal {
 		public Action<ConsoleKeyInfo> WindowsKeyPressed;
 		MainLoop mainLoop;
 
-		public NetMainLoop () 
+		public NetMainLoop ()
 		{
 		}
 
@@ -258,16 +258,16 @@ namespace Mono.Terminal {
 				windowsKeyResult = Console.ReadKey (true);
 				keyReady.Set ();
 			}
-		}		
+		}
 
 		void IMainLoopDriver.Setup (MainLoop mainLoop)
 		{
 			this.mainLoop = mainLoop;
 			Thread readThread = new Thread (WindowsKeyReader);
-			readThread.Start ();			
+			readThread.Start ();
 		}
 
-		void IMainLoopDriver.Wakeup () 
+		void IMainLoopDriver.Wakeup ()
 		{
 		}
 
@@ -298,7 +298,7 @@ namespace Mono.Terminal {
 				if (WindowsKeyPressed!= null)
 					WindowsKeyPressed (windowsKeyResult.Value);
 				windowsKeyResult = null;
-			}			
+			}
 		}
 	}
 
@@ -346,7 +346,6 @@ namespace Mono.Terminal {
 				action ();
 				return false;
 			});
-			driver.Wakeup ();
 		}
 
 		/// <summary>
@@ -373,7 +372,7 @@ namespace Mono.Terminal {
 		{
 			timeouts.Add ((DateTime.UtcNow + time).Ticks, timeout);
 		}
-		
+
 		/// <summary>
 		///   Adds a timeout to the mainloop.
 		/// </summary>
@@ -440,9 +439,9 @@ namespace Mono.Terminal {
 						idleHandlers.Add (idle);
 			}
 		}
-		
+
 		bool running;
-		
+
 		/// <summary>
 		///   Stops the mainloop.
 		/// </summary>
@@ -458,7 +457,7 @@ namespace Mono.Terminal {
 		/// <remarks>
 		///   You can use this method if you want to probe if events are pending.
 		///   Typically used if you need to flush the input queue while still
-		///   running some of your own code in your main thread. 
+		///   running some of your own code in your main thread.
 		/// </remarks>
 		public bool EventsPending (bool wait = false)
 		{
@@ -486,7 +485,7 @@ namespace Mono.Terminal {
 					RunIdle();
 			}
 		}
-		
+
 		/// <summary>
 		///   Runs the mainloop.
 		/// </summary>

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

@@ -44,8 +44,22 @@ namespace Terminal.Gui {
 		{
 			var cFrame = new Rect (1, 1 , frame.Width - 2, frame.Height - 2);
 			contentView = new ContentView (cFrame);
-			base.Add (contentView);
-			Title = title;
+			Initialize ();
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="T:Terminal.Gui.Gui.FrameView"/> class with
+		/// an absolute position, a title and views.
+		/// </summary>
+		/// <param name="frame">Frame.</param>
+		/// <param name="title">Title.</param>
+		/// /// <param name="views">Views.</param>
+		public FrameView (Rect frame, ustring title, View[] views) : this (frame, title)
+		{
+			foreach (var view in views) {
+				contentView.Add (view);
+			}
+			Initialize ();
 		}
 
 		/// <summary>
@@ -61,11 +75,15 @@ namespace Terminal.Gui {
 				Width = Dim.Fill (2),
 				Height = Dim.Fill (2)
 			};
+			Initialize ();
+		}
+
+		void Initialize ()
+		{
 			base.Add (contentView);
 			Title = title;
 		}
 
-
 		void DrawFrame ()
 		{
 			DrawFrame (new Rect (0, 0, Frame.Width, Frame.Height), 0, fill: true);

+ 20 - 14
Terminal.Gui/Views/Menu.cs

@@ -432,7 +432,7 @@ namespace Terminal.Gui {
 			}
 			host.handled = false;
 			bool disabled;
-			if (me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1Released) {
+			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
 				disabled = false;
 				if (me.Y < 1)
 					return true;
@@ -444,7 +444,8 @@ namespace Terminal.Gui {
 				if (item != null && !disabled)
 					Run (barItems.Children [meY].Action);
 				return true;
-			} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.ReportMousePosition) {
+			} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
+				me.Flags == MouseFlags.ReportMousePosition) {
 				disabled = false;
 				if (me.Y < 1)
 					return true;
@@ -525,7 +526,7 @@ namespace Terminal.Gui {
 			Width = Dim.Fill ();
 			Height = 1;
 			Menus = menus;
-			CanFocus = true;
+			//CanFocus = true;
 			selected = -1;
 			selectedSub = -1;
 			ColorScheme = Colors.Menu;
@@ -984,13 +985,14 @@ namespace Terminal.Gui {
 			}
 			handled = false;
 
-			if (me.Flags == MouseFlags.Button1Clicked ||
+			if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked ||
 				(me.Flags == MouseFlags.ReportMousePosition && selected > -1)) {
 				int pos = 1;
 				int cx = me.X;
 				for (int i = 0; i < Menus.Length; i++) {
 					if (cx > pos && me.X < pos + 1 + Menus [i].TitleLength) {
-						if (selected == i && me.Flags == MouseFlags.Button1Clicked && !isMenuClosed) {
+						if (selected == i && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) &&
+							!isMenuClosed) {
 							Application.UngrabMouse ();
 							if (Menus [i].IsTopLevel) {
 								var menu = new Menu (this, i, 0, Menus [i]);
@@ -998,7 +1000,8 @@ namespace Terminal.Gui {
 							} else {
 								CloseMenu ();
 							}
-						} else if (me.Flags == MouseFlags.Button1Clicked && isMenuClosed) {
+						} else if ((me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) &&
+							isMenuClosed) {
 							if (Menus [i].IsTopLevel) {
 								var menu = new Menu (this, i, 0, Menus [i]);
 								menu.Run (Menus [i].Action);
@@ -1033,7 +1036,8 @@ namespace Terminal.Gui {
 						Application.GrabMouse (me.View);
 						me.View.MouseEvent (me);
 					}
-				} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
+				} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags.HasFlag (MouseFlags.Button1Pressed) ||
+					me.Flags == MouseFlags.Button1DoubleClicked)) {
 					Application.UngrabMouse ();
 					CloseAllMenus ();
 					handled = false;
@@ -1042,22 +1046,23 @@ namespace Terminal.Gui {
 					handled = false;
 					return false;
 				}
-			} else if (isMenuClosed && me.Flags.HasFlag (MouseFlags.Button1Clicked)) {
+			} else if (isMenuClosed && (me.Flags.HasFlag (MouseFlags.Button1Pressed) || me.Flags == MouseFlags.Button1DoubleClicked)) {
 				Application.GrabMouse (current);
 			} else {
 				handled = false;
 				return false;
 			}
-			//if (me.View != this && me.Flags != MouseFlags.Button1Clicked)
+			//if (me.View != this && (me.Flags != MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked))
 			//	return true;
-			//else if (me.View != this && me.Flags == MouseFlags.Button1Clicked) {
+			//else if (me.View != this && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked)) {
 			//	Application.UngrabMouse ();
 			//	host.CloseAllMenus ();
 			//	return true;
 			//}
 
 
-			//if (!(me.View is MenuBar) && !(me.View is Menu) && me.Flags != MouseFlags.Button1Clicked)
+			//if (!(me.View is MenuBar) && !(me.View is Menu) && (me.Flags != MouseFlags.Button1Pressed ||
+			// me.Flags != MouseFlags.Button1DoubleClicked))
 			//	return false;
 
 			//if (Application.mouseGrabView != null) {
@@ -1066,11 +1071,12 @@ namespace Terminal.Gui {
 			//		me.Y -= me.OfY;
 			//		me.View.MouseEvent (me);
 			//		return true;
-			//	} else if (!(me.View is MenuBar || me.View is Menu) && me.Flags == MouseFlags.Button1Clicked) {
+			//	} else if (!(me.View is MenuBar || me.View is Menu) && (me.Flags == MouseFlags.Button1Pressed ||
+			//		me.Flags == MouseFlags.Button1DoubleClicked)) {
 			//		Application.UngrabMouse ();
 			//		CloseAllMenus ();
 			//	}
-			//} else if (!isMenuClosed && selected == -1 && me.Flags == MouseFlags.Button1Clicked) {
+			//} else if (!isMenuClosed && selected == -1 && (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked)) {
 			//	Application.GrabMouse (this);
 			//	return true;
 			//}
@@ -1082,7 +1088,7 @@ namespace Terminal.Gui {
 			//	} else if (me.View != current && me.View is MenuBar && me.View is Menu) {
 			//		Application.UngrabMouse ();
 			//		Application.GrabMouse (me.View);
-			//	} else if (me.Flags == MouseFlags.Button1Clicked) {
+			//	} else if (me.Flags == MouseFlags.Button1Pressed || me.Flags == MouseFlags.Button1DoubleClicked) {
 			//		Application.UngrabMouse ();
 			//		CloseMenu ();
 			//	}

+ 28 - 11
Terminal.Gui/Views/ScrollView.cs

@@ -114,21 +114,19 @@ namespace Terminal.Gui {
 					var by1 = position * bh / Size;
 					var by2 = (position + bh) * bh / Size;
 
-					
 					Move (col, 0);
 					Driver.AddRune ('^');
 					Move (col, Bounds.Height - 1);
 					Driver.AddRune ('v');
 					for (int y = 0; y < bh; y++) {
 						Move (col, y+1);
-
-						if (y < by1 || y > by2)
+						if (y < by1 - 1 || y > by2)
 							special = Driver.Stipple;
 						else {
-							if (by2 - by1 == 0)
+							if (by2 - by1 == 0 && by1 < bh - 1)
 								special = Driver.Diamond;
 							else {
-								if (y == by1)
+								if (y == by1 - 1)
 									special = Driver.TopTee;
 								else if (y == by2)
 									special = Driver.BottomTee;
@@ -192,7 +190,8 @@ namespace Terminal.Gui {
 
 		public override bool MouseEvent(MouseEvent me)
 		{
-			if (me.Flags != MouseFlags.Button1Clicked)
+			if (me.Flags != MouseFlags.Button1Pressed && me.Flags != MouseFlags.Button1Clicked &&
+				!me.Flags.HasFlag (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
 				return false;
 
 			int location = vertical ? me.Y : me.X;
@@ -208,11 +207,23 @@ namespace Terminal.Gui {
 				if (location == 0) {
 					if (pos > 0)
 						SetPosition (pos - 1);
-				} else if (location == barsize + 1){
+				} else if (location == barsize + 1) {
 					if (pos + 1 + barsize < Size)
 						SetPosition (pos + 1);
 				} else {
-					Console.WriteLine ("TODO at ScrollBarView");
+					var b1 = pos * barsize / Size;
+					var b2 = (pos + barsize) * barsize / Size;
+
+					if (b2 == 0 && location == 1 && pos == 0 ||
+						(b2 == barsize && location == barsize) ||
+						(location > b1 && location < b2)) {
+						return true;
+					} else if (location <= barsize) {
+						if (location > 1 && location >= b2)
+							SetPosition (Math.Min (pos + barsize, Size));
+						else if (location <= b2 && pos > 0 || pos > 0)
+							SetPosition (Math.Max (pos - barsize, 0));
+					}
 				}
 			}
 
@@ -309,7 +320,7 @@ namespace Terminal.Gui {
 			set {
 				if (value == showHorizontalScrollIndicator)
 					return;
-				
+
 				showHorizontalScrollIndicator = value;
 				SetNeedsDisplay ();
 				if (value)
@@ -338,7 +349,7 @@ namespace Terminal.Gui {
 			set {
 				if (value == showVerticalScrollIndicator)
 					return;
-				
+
 				showVerticalScrollIndicator = value;
 				SetNeedsDisplay ();
 				if (value)
@@ -431,7 +442,7 @@ namespace Terminal.Gui {
 			var nx = Math.Max (-contentSize.Width, contentOffset.X - cols);
 			if (nx == contentOffset.X)
 				return false;
-			
+
 			ContentOffset = new Point (nx, contentOffset.Y);
 			return true;
 		}
@@ -461,6 +472,12 @@ namespace Terminal.Gui {
 			case Key.CursorRight:
 				return ScrollRight (1);
 
+			case Key.Home:
+				return ScrollUp (contentSize.Height);
+
+			case Key.End:
+				return ScrollDown (contentSize.Height);
+
 			}
 			return false;
 		}

+ 52 - 3
Terminal.Gui/Views/StatusBar.cs

@@ -55,11 +55,43 @@ namespace Terminal.Gui {
 	/// So for each context must be a new instance of a statusbar.
 	/// </summary>
 	public class StatusBar : View {
+// After attempting to implement this, I noticed that there are hard dependencies
+// on StatusBar and MenuBars within core. They will need to be refactored for having the
+// StatusBar work at the top
+#if SNAP_TO_TOP
+		/// <summary>
+		/// The style supported by StatusBar
+		/// </summary>
+		public enum StatusBarStyle {
+			Default = 0,
+			/// <summary>
+			/// The StatusBar will snap at the the bottom line of the Parent view.
+			/// If the console window is made larger while the app is runing, the StatusBar
+			/// will continue to snap to the bottom line of the Parent, staying visible.
+			/// On consoles that support resizing of console apps (e.g. Windows Terminal and ConEmu),
+			/// if the console window is subsequently made shorter, the status bar will remain visible
+			/// as the Parent view resizes. If Parent is null, the StatusBar will snap to the bottom line
+			/// of the console window.
+			/// This is the default.
+			/// </summary>
+			SnapToBottom = Default,
+
+			/// <summary>
+			/// The StatusBar will act identically to MenuBar, snapping to the first line of the
+			/// console window.
+			/// </summary>
+			SnapToTop = 1,
+		}
+
+		public StatusBarStyle Style { get; set; } = StatusBarStyle.Default;
+#endif
+		public View Parent { get; set; }
+
 		public StatusItem [] Items { get; set; }
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="T:Terminal.Gui.StatusBar"/> class with the specified set of statusbar items.
-		/// It will be drawn in the lowest column of the terminal.
+		/// It will be drawn in the lowest line of the terminal.
 		/// </summary>
 		/// <param name="items">A list of statusbar items.</param>
 		public StatusBar (StatusItem [] items) : base ()
@@ -71,8 +103,25 @@ namespace Terminal.Gui {
 			ColorScheme = Colors.Menu;
 
 			Application.OnLoad += () => {
-				this.X = Pos.Left (Application.Top);
-				this.Y = Pos.Bottom (Application.Top);
+				X = 0;
+				Height = 1;
+#if SNAP_TO_TOP
+				switch (Style) {
+				case StatusBarStyle.SnapToTop:
+					X = 0;
+					Y = 0;
+					break;
+				case StatusBarStyle.SnapToBottom:
+#endif
+					if (Parent == null) {
+						Y = Application.Driver.Rows - 1; // TODO: using internals of Application
+					} else {
+						Y = Pos.Bottom (Parent);
+					}
+#if SNAP_TO_TOP
+					break;
+				}
+#endif
 			};
 		}
 

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

@@ -43,7 +43,7 @@ namespace Terminal.Gui {
 		/// <param name="text">Initial text contents.</param>
 		public TextField (string text) : this (ustring.Make (text))
 		{
-
+			Height = 1;
 		}
 
 		/// <summary>
@@ -85,6 +85,8 @@ namespace Terminal.Gui {
 		{
 			if (Application.mouseGrabView != null && Application.mouseGrabView == this)
 				Application.UngrabMouse ();
+			if (SelectedLength != 0 && !(Application.mouseGrabView is MenuBar))
+				ClearAllSelection ();
 		}
 
 		public override Rect Frame {
@@ -473,18 +475,17 @@ namespace Terminal.Gui {
 
 		public override bool MouseEvent (MouseEvent ev)
 		{
-			if (!ev.Flags.HasFlag (MouseFlags.Button1Clicked) && !ev.Flags.HasFlag (MouseFlags.Button1Pressed) &&
-				!ev.Flags.HasFlag (MouseFlags.ReportMousePosition))
+			if (!ev.Flags.HasFlag (MouseFlags.Button1Pressed) && !ev.Flags.HasFlag (MouseFlags.ReportMousePosition) &&
+				!ev.Flags.HasFlag (MouseFlags.Button1Released))
 				return false;
 
-			if (ev.Flags == MouseFlags.Button1Clicked) {
+			if (ev.Flags == MouseFlags.Button1Pressed) {
 				if (!HasFocus)
 					SuperView.SetFocus (this);
-				int x = PositionCursor (ev);
+				PositionCursor (ev);
 				if (isButtonReleased)
 					ClearAllSelection ();
 				isButtonReleased = true;
-				Application.UngrabMouse ();
 			} else if (ev.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition)) {
 				int x = PositionCursor (ev);
 				isButtonReleased = false;
@@ -496,6 +497,9 @@ namespace Terminal.Gui {
 				int x = PositionCursor (ev);
 				if (SelectedLength != 0)
 					ClearAllSelection ();
+			} else if (ev.Flags == MouseFlags.Button1Released) {
+				isButtonReleased = true;
+				Application.UngrabMouse ();
 			}
 
 			SetNeedsDisplay ();