mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-28 00:38:00 +01:00
Merge branch 'master' of tig:migueldeicaza/gui.cs
This commit is contained in:
@@ -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
|
||||
|
||||
13
.gitattributes
vendored
Normal file
13
.gitattributes
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Set the default behavior for all files.
|
||||
* text=auto
|
||||
|
||||
# Normalized and converts to
|
||||
# native line endings on checkout.
|
||||
*.cs text
|
||||
|
||||
# Convert to LF line endings on checkout.
|
||||
*.sh text eol=lf
|
||||
|
||||
# Binary files.
|
||||
*.png binary
|
||||
*.jpg binary
|
||||
5
.gitignore
vendored
5
.gitignore
vendored
@@ -1,8 +1,9 @@
|
||||
bin
|
||||
obj
|
||||
*~
|
||||
~$*
|
||||
*.userprefs
|
||||
*~
|
||||
packages
|
||||
.vs
|
||||
*.csproj.user
|
||||
# User-specific files
|
||||
*.user
|
||||
|
||||
115
Example/demo.cs
115
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);
|
||||
@@ -408,6 +413,44 @@ static class Demo {
|
||||
#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;
|
||||
@@ -418,7 +461,6 @@ static class Demo {
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
|
||||
|
||||
//Application.UseSystemConsole = true;
|
||||
Console.WindowHeight = 35;
|
||||
|
||||
Application.Init ();
|
||||
|
||||
@@ -426,11 +468,13 @@ static class Demo {
|
||||
|
||||
//Open ();
|
||||
#if true
|
||||
int margin = 3;
|
||||
var win = new Window ("Hello") {
|
||||
X = 1,
|
||||
Y = 1,
|
||||
Width = Dim.Fill (),
|
||||
Height = Dim.Fill ()-1
|
||||
|
||||
Width = Dim.Fill () - margin,
|
||||
Height = Dim.Fill () - margin
|
||||
};
|
||||
#else
|
||||
var tframe = top.Frame;
|
||||
@@ -451,7 +495,7 @@ static class Demo {
|
||||
|
||||
menu = new MenuBar (new MenuBarItem [] {
|
||||
new MenuBarItem ("_File", new MenuItem [] {
|
||||
new MenuItem ("Text Editor Demo", "", () => { Editor (top); }),
|
||||
new MenuItem ("Text _Editor Demo", "", () => { Editor (top); }),
|
||||
new MenuItem ("_New", "Creates new file", NewFile),
|
||||
new MenuItem ("_Open", "", Open),
|
||||
new MenuItem ("_Hex", "", () => ShowHex (top)),
|
||||
@@ -469,18 +513,19 @@ static class Demo {
|
||||
menuItems[3]
|
||||
}),
|
||||
new MenuBarItem ("_List Demos", new MenuItem [] {
|
||||
new MenuItem ("Select Multiple Items", "", () => ListSelectionDemo (true)),
|
||||
new MenuItem ("Select Single Item", "", () => ListSelectionDemo (false)),
|
||||
new MenuItem ("Select _Multiple Items", "", () => ListSelectionDemo (true)),
|
||||
new MenuItem ("Select _Single Item", "", () => ListSelectionDemo (false)),
|
||||
}),
|
||||
new MenuBarItem ("Assorted", new MenuItem [] {
|
||||
new MenuItem ("Show text alignments", "", () => ShowTextAlignments ())
|
||||
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 ("SubMenu1Item1",
|
||||
new MenuBarItem ("_Test Menu and SubMenus", new MenuItem [] {
|
||||
new MenuItem ("SubMenu1Item_1",
|
||||
new MenuBarItem (new MenuItem[] {
|
||||
new MenuItem ("SubMenu2Item1",
|
||||
new MenuItem ("SubMenu2Item_1",
|
||||
new MenuBarItem (new MenuItem [] {
|
||||
new MenuItem ("SubMenu3Item1",
|
||||
new MenuItem ("SubMenu3Item_1",
|
||||
new MenuBarItem (new MenuItem [] { menuItems [2] })
|
||||
)
|
||||
})
|
||||
@@ -488,6 +533,7 @@ static class Demo {
|
||||
})
|
||||
)
|
||||
}),
|
||||
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);
|
||||
@@ -498,7 +544,7 @@ static class Demo {
|
||||
ShowEntries (win);
|
||||
|
||||
int count = 0;
|
||||
ml = new Label (new Rect (3, 18, 47, 1), "Mouse: ");
|
||||
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++}";
|
||||
@@ -520,24 +566,31 @@ 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
|
||||
// This currently causes a stack overflow, because it is referencing a window that has not had its size allocated yet
|
||||
// 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!");
|
||||
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.OnResized = () => {
|
||||
bottom.X = Pos.Left (win);
|
||||
bottom.Y = Pos.Bottom (win);
|
||||
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, ml);
|
||||
top.Add (menu, statusBar);
|
||||
Application.Run ();
|
||||
}
|
||||
}
|
||||
|
||||
4
FSharpExample/.editorconfig
Normal file
4
FSharpExample/.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
[*.fs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
tab_width = 4
|
||||
@@ -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
FSharpExample/FSharpExample.sln
Normal file
31
FSharpExample/FSharpExample.sln
Normal file
@@ -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
|
||||
@@ -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>
|
||||
|
||||
let ustr (x:string) = ustring.Make(x)
|
||||
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
|
||||
()
|
||||
|
||||
let stop = Action Application.RequestStop
|
||||
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 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 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 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 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)
|
||||
|
||||
let buildMenu() =
|
||||
MenuBar ([|
|
||||
MenuBarItem (ustr ("File"),
|
||||
[| MenuItem(ustr("_New"), "Creates a new file", System.Action newFile);
|
||||
MenuItem(ustr("_Quit"), null, System.Action quit)
|
||||
|])|])
|
||||
Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300.0), timer) |> ignore
|
||||
|
||||
[<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
|
||||
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 ()
|
||||
|
||||
module Demo__run =
|
||||
[<EntryPoint>]
|
||||
let main argv =
|
||||
Main ()
|
||||
0
|
||||
@@ -121,6 +121,27 @@ namespace Terminal.Gui {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked when a key is pressed.
|
||||
/// </summary>
|
||||
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
|
||||
/// <returns>true if the event was handled</returns>
|
||||
public virtual bool KeyDown (KeyEvent keyEvent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked when a key is released.
|
||||
/// </summary>
|
||||
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
|
||||
/// <returns>true if the event was handled</returns>
|
||||
public virtual bool KeyUp (KeyEvent keyEvent)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Method invoked when a mouse event is generated
|
||||
/// </summary>
|
||||
@@ -1011,6 +1032,41 @@ namespace Terminal.Gui {
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a key is pressed
|
||||
/// </summary>
|
||||
public Action<KeyEvent> OnKeyDown;
|
||||
|
||||
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
|
||||
public override bool KeyDown (KeyEvent keyEvent)
|
||||
{
|
||||
OnKeyDown?.Invoke (keyEvent);
|
||||
if (subviews == null || subviews.Count == 0)
|
||||
return false;
|
||||
foreach (var view in subviews)
|
||||
if (view.KeyDown (keyEvent))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a key is released
|
||||
/// </summary>
|
||||
public Action<KeyEvent> OnKeyUp;
|
||||
|
||||
/// <param name="keyEvent">Contains the details about the key that produced the event.</param>
|
||||
public override bool KeyUp (KeyEvent keyEvent)
|
||||
{
|
||||
OnKeyUp?.Invoke (keyEvent);
|
||||
if (subviews == null || subviews.Count == 0)
|
||||
return false;
|
||||
foreach (var view in subviews)
|
||||
if (view.KeyUp (keyEvent))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
/// <summary>
|
||||
/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
|
||||
/// </summary>
|
||||
@@ -1192,12 +1248,12 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Topological_sorting
|
||||
static List<View> TopologicalSort (HashSet<View> nodes, HashSet<(View, View)> edges)
|
||||
List<View> TopologicalSort (HashSet<View> nodes, HashSet<(View From, View To)> edges)
|
||||
{
|
||||
var result = new List<View> ();
|
||||
|
||||
// Set of all nodes with no incoming edges
|
||||
var S = new HashSet<View> (nodes.Where (n => edges.All (e => e.Item2.Equals (n) == false)));
|
||||
var S = new HashSet<View> (nodes.Where (n => edges.All (e => e.To.Equals (n) == false)));
|
||||
|
||||
while (S.Any ()) {
|
||||
// remove a node n from S
|
||||
@@ -1205,17 +1261,18 @@ namespace Terminal.Gui {
|
||||
S.Remove (n);
|
||||
|
||||
// add n to tail of L
|
||||
result.Add (n);
|
||||
if (n != this?.SuperView)
|
||||
result.Add (n);
|
||||
|
||||
// for each node m with an edge e from n to m do
|
||||
foreach (var e in edges.Where (e => e.Item1.Equals (n)).ToList ()) {
|
||||
var m = e.Item2;
|
||||
foreach (var e in edges.Where (e => e.From.Equals (n)).ToArray ()) {
|
||||
var m = e.To;
|
||||
|
||||
// remove edge e from the graph
|
||||
edges.Remove (e);
|
||||
|
||||
// if m has no other incoming edges then
|
||||
if (edges.All (me => me.Item2.Equals (m) == false)) {
|
||||
if (edges.All (me => me.To.Equals (m) == false) && m != this?.SuperView) {
|
||||
// insert m into S
|
||||
S.Add (m);
|
||||
}
|
||||
@@ -1249,19 +1306,18 @@ namespace Terminal.Gui {
|
||||
foreach (var v in InternalSubviews) {
|
||||
nodes.Add (v);
|
||||
if (v.LayoutStyle == LayoutStyle.Computed) {
|
||||
if (v.X is Pos.PosView)
|
||||
edges.Add ((v, (v.X as Pos.PosView).Target));
|
||||
if (v.Y is Pos.PosView)
|
||||
edges.Add ((v, (v.Y as Pos.PosView).Target));
|
||||
if (v.Width is Dim.DimView)
|
||||
edges.Add ((v, (v.Width as Dim.DimView).Target));
|
||||
if (v.Height is Dim.DimView)
|
||||
edges.Add ((v, (v.Height as Dim.DimView).Target));
|
||||
if (v.X is Pos.PosView vX)
|
||||
edges.Add ((vX.Target, v));
|
||||
if (v.Y is Pos.PosView vY)
|
||||
edges.Add ((vY.Target, v));
|
||||
if (v.Width is Dim.DimView vWidth)
|
||||
edges.Add ((vWidth.Target, v));
|
||||
if (v.Height is Dim.DimView vHeight)
|
||||
edges.Add ((vHeight.Target, v));
|
||||
}
|
||||
}
|
||||
|
||||
var ordered = TopologicalSort (nodes, edges);
|
||||
ordered.Reverse ();
|
||||
if (ordered == null)
|
||||
throw new Exception ("There is a recursive cycle in the relative Pos/Dim in the views of " + this);
|
||||
|
||||
@@ -1269,11 +1325,15 @@ namespace Terminal.Gui {
|
||||
if (v.LayoutStyle == LayoutStyle.Computed)
|
||||
v.RelativeLayout (Frame);
|
||||
|
||||
if (this?.SuperView != v)
|
||||
v.LayoutSubviews ();
|
||||
v.LayoutSubviews ();
|
||||
v.layoutNeeded = false;
|
||||
|
||||
}
|
||||
|
||||
if (SuperView == Application.Top && layoutNeeded && ordered.Count == 0 && LayoutStyle == LayoutStyle.Computed) {
|
||||
RelativeLayout (Frame);
|
||||
}
|
||||
|
||||
layoutNeeded = false;
|
||||
}
|
||||
|
||||
@@ -1482,7 +1542,7 @@ namespace Terminal.Gui {
|
||||
{
|
||||
if (this != Application.Top) {
|
||||
EnsureVisibleBounds (this, Frame.X, Frame.Y, out int nx, out int ny);
|
||||
if (nx != Frame.X || ny != Frame.Y) {
|
||||
if ((nx != Frame.X || ny != Frame.Y) && LayoutStyle != LayoutStyle.Computed) {
|
||||
X = nx;
|
||||
Y = ny;
|
||||
}
|
||||
@@ -1490,7 +1550,7 @@ namespace Terminal.Gui {
|
||||
foreach (var top in Subviews) {
|
||||
if (top is Toplevel) {
|
||||
EnsureVisibleBounds ((Toplevel)top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
|
||||
if (nx != top.Frame.X || ny != top.Frame.Y) {
|
||||
if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle != LayoutStyle.Computed) {
|
||||
top.X = nx;
|
||||
top.Y = ny;
|
||||
}
|
||||
@@ -1513,7 +1573,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
foreach (var view in Subviews) {
|
||||
if (view.Frame.IntersectsWith (region)) {
|
||||
//view.SetNeedsLayout ();
|
||||
view.SetNeedsLayout ();
|
||||
view.SetNeedsDisplay (view.Bounds);
|
||||
}
|
||||
}
|
||||
@@ -1943,6 +2003,7 @@ namespace Terminal.Gui {
|
||||
|
||||
static void ProcessKeyEvent (KeyEvent ke)
|
||||
{
|
||||
|
||||
var chain = toplevels.ToList();
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.ProcessHotKey (ke))
|
||||
@@ -1967,6 +2028,29 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
static void ProcessKeyDownEvent (KeyEvent ke)
|
||||
{
|
||||
var chain = toplevels.ToList ();
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.KeyDown (ke))
|
||||
return;
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void ProcessKeyUpEvent (KeyEvent ke)
|
||||
{
|
||||
var chain = toplevels.ToList ();
|
||||
foreach (var topLevel in chain) {
|
||||
if (topLevel.KeyUp (ke))
|
||||
return;
|
||||
if (topLevel.Modal)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static View FindDeepestView (View start, int x, int y, out int resx, out int resy)
|
||||
{
|
||||
var startFrame = start.Frame;
|
||||
@@ -2062,6 +2146,11 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Action that is invoked once at beginning.
|
||||
/// </summary>
|
||||
static public Action OnLoad;
|
||||
|
||||
/// <summary>
|
||||
/// Building block API: Prepares the provided toplevel for execution.
|
||||
/// </summary>
|
||||
@@ -2092,10 +2181,11 @@ namespace Terminal.Gui {
|
||||
}
|
||||
toplevels.Push (toplevel);
|
||||
Current = toplevel;
|
||||
Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessMouseEvent);
|
||||
Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
|
||||
if (toplevel.LayoutStyle == LayoutStyle.Computed)
|
||||
toplevel.RelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
|
||||
toplevel.LayoutSubviews ();
|
||||
OnLoad?.Invoke ();
|
||||
toplevel.WillPresent ();
|
||||
Redraw (toplevel);
|
||||
toplevel.PositionCursor ();
|
||||
|
||||
@@ -469,7 +469,7 @@ namespace Terminal.Gui {
|
||||
/// <param name="mainLoop"></param>
|
||||
/// <param name="keyHandler"></param>
|
||||
/// <param name="mouseHandler"></param>
|
||||
public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler);
|
||||
public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler);
|
||||
|
||||
/// <summary>
|
||||
/// Updates the screen to reflect all the changes that have been done to the display buffer
|
||||
|
||||
@@ -203,8 +203,9 @@ namespace Terminal.Gui {
|
||||
keyHandler (new KeyEvent ((Key)wch));
|
||||
}
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
|
||||
Curses.timeout (-1);
|
||||
|
||||
(mainLoop.Driver as Mono.Terminal.UnixMainLoop).AddWatch (0, Mono.Terminal.UnixMainLoop.Condition.PollIn, x => {
|
||||
|
||||
@@ -320,8 +320,9 @@ namespace Terminal.Gui {
|
||||
return (Key)(0xffffffff);
|
||||
}
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
|
||||
(mainLoop.Driver as NetMainLoop).WindowsKeyPressed = delegate (ConsoleKeyInfo consoleKey) {
|
||||
var map = MapKey (consoleKey);
|
||||
if (map == (Key)0xffffffff)
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
// SOFTWARE.
|
||||
//
|
||||
using System;
|
||||
using System.CodeDom;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Threading;
|
||||
@@ -102,7 +103,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
if (ScreenBuffer != IntPtr.Zero)
|
||||
CloseHandle(ScreenBuffer);
|
||||
CloseHandle (ScreenBuffer);
|
||||
|
||||
ScreenBuffer = IntPtr.Zero;
|
||||
}
|
||||
@@ -358,8 +359,8 @@ namespace Terminal.Gui {
|
||||
[DllImport ("kernel32.dll", SetLastError = true)]
|
||||
static extern IntPtr GetStdHandle (int nStdHandle);
|
||||
|
||||
[DllImport("kernel32.dll", SetLastError = true)]
|
||||
static extern bool CloseHandle(IntPtr handle);
|
||||
[DllImport ("kernel32.dll", SetLastError = true)]
|
||||
static extern bool CloseHandle (IntPtr handle);
|
||||
|
||||
[DllImport ("kernel32.dll", EntryPoint = "ReadConsoleInputW", CharSet = CharSet.Unicode)]
|
||||
public static extern bool ReadConsoleInput (
|
||||
@@ -424,8 +425,8 @@ namespace Terminal.Gui {
|
||||
|
||||
internal class WindowsDriver : ConsoleDriver, Mono.Terminal.IMainLoopDriver {
|
||||
static bool sync;
|
||||
ManualResetEventSlim eventReady = new ManualResetEventSlim(false);
|
||||
ManualResetEventSlim waitForProbe = new ManualResetEventSlim(false);
|
||||
ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
||||
MainLoop mainLoop;
|
||||
WindowsConsole.CharInfo [] OutputBuffer;
|
||||
int cols, rows;
|
||||
@@ -456,17 +457,17 @@ namespace Terminal.Gui {
|
||||
Task.Run ((Action)WindowsInputHandler);
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
public struct ConsoleKeyInfoEx {
|
||||
public ConsoleKeyInfo consoleKeyInfo;
|
||||
public bool CapsLock;
|
||||
public bool NumLock;
|
||||
|
||||
public ConsoleKeyInfoEx(ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
|
||||
public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
|
||||
{
|
||||
this.consoleKeyInfo = consoleKeyInfo;
|
||||
CapsLock = capslock;
|
||||
NumLock = numlock;
|
||||
this.consoleKeyInfo = consoleKeyInfo;
|
||||
CapsLock = capslock;
|
||||
NumLock = numlock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -476,8 +477,8 @@ namespace Terminal.Gui {
|
||||
void WindowsInputHandler ()
|
||||
{
|
||||
while (true) {
|
||||
waitForProbe.Wait();
|
||||
waitForProbe.Reset();
|
||||
waitForProbe.Wait ();
|
||||
waitForProbe.Reset ();
|
||||
|
||||
uint numberEventsRead = 0;
|
||||
|
||||
@@ -487,7 +488,7 @@ namespace Terminal.Gui {
|
||||
else
|
||||
result = records;
|
||||
|
||||
eventReady.Set();
|
||||
eventReady.Set ();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -498,7 +499,9 @@ namespace Terminal.Gui {
|
||||
|
||||
void IMainLoopDriver.Wakeup ()
|
||||
{
|
||||
tokenSource.Cancel();
|
||||
//tokenSource.Cancel ();
|
||||
eventReady.Reset ();
|
||||
eventReady.Set ();
|
||||
}
|
||||
|
||||
bool IMainLoopDriver.EventsPending (bool wait)
|
||||
@@ -517,31 +520,35 @@ namespace Terminal.Gui {
|
||||
waitTimeout = 0;
|
||||
|
||||
result = null;
|
||||
waitForProbe.Set();
|
||||
waitForProbe.Set ();
|
||||
|
||||
try {
|
||||
if(!tokenSource.IsCancellationRequested)
|
||||
eventReady.Wait(waitTimeout, tokenSource.Token);
|
||||
if (!tokenSource.IsCancellationRequested)
|
||||
eventReady.Wait (waitTimeout, tokenSource.Token);
|
||||
} catch (OperationCanceledException) {
|
||||
return true;
|
||||
} finally {
|
||||
eventReady.Reset();
|
||||
eventReady.Reset ();
|
||||
}
|
||||
|
||||
if (!tokenSource.IsCancellationRequested)
|
||||
return result != null;
|
||||
|
||||
tokenSource.Dispose();
|
||||
tokenSource = new CancellationTokenSource();
|
||||
tokenSource.Dispose ();
|
||||
tokenSource = new CancellationTokenSource ();
|
||||
return true;
|
||||
}
|
||||
|
||||
Action<KeyEvent> keyHandler;
|
||||
Action<KeyEvent> keyDownHandler;
|
||||
Action<KeyEvent> keyUpHandler;
|
||||
Action<MouseEvent> mouseHandler;
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<MouseEvent> mouseHandler)
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
this.keyHandler = keyHandler;
|
||||
this.keyDownHandler = keyDownHandler;
|
||||
this.keyUpHandler = keyUpHandler;
|
||||
this.mouseHandler = mouseHandler;
|
||||
}
|
||||
|
||||
@@ -554,16 +561,43 @@ namespace Terminal.Gui {
|
||||
var inputEvent = result [0];
|
||||
switch (inputEvent.EventType) {
|
||||
case WindowsConsole.EventType.Key:
|
||||
if (inputEvent.KeyEvent.bKeyDown == false)
|
||||
return;
|
||||
var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
|
||||
if (inputEvent.KeyEvent.UnicodeChar == 0 && map == (Key)0xffffffff)
|
||||
return;
|
||||
keyHandler (new KeyEvent (map));
|
||||
if (map == (Key)0xffffffff) {
|
||||
KeyEvent key;
|
||||
// Shift = VK_SHIFT = 0x10
|
||||
// Ctrl = VK_CONTROL = 0x11
|
||||
// Alt = VK_MENU = 0x12
|
||||
switch (inputEvent.KeyEvent.wVirtualKeyCode) {
|
||||
case 0x11:
|
||||
key = new KeyEvent (Key.CtrlMask);
|
||||
break;
|
||||
case 0x12:
|
||||
key = new KeyEvent (Key.AltMask);
|
||||
break;
|
||||
default:
|
||||
key = new KeyEvent (Key.Unknown);
|
||||
break;
|
||||
}
|
||||
|
||||
if (inputEvent.KeyEvent.bKeyDown)
|
||||
keyDownHandler (key);
|
||||
else
|
||||
keyUpHandler (key);
|
||||
} else {
|
||||
if (inputEvent.KeyEvent.bKeyDown) {
|
||||
// Key Down - Fire KeyDown Event and KeyStroke (ProcessKey) Event
|
||||
keyDownHandler (new KeyEvent (map));
|
||||
keyHandler (new KeyEvent (map));
|
||||
} else {
|
||||
keyUpHandler (new KeyEvent (map));
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case WindowsConsole.EventType.Mouse:
|
||||
mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
||||
if (IsButtonReleased)
|
||||
mouseHandler (ToDriverMouse (inputEvent.MouseEvent));
|
||||
break;
|
||||
|
||||
case WindowsConsole.EventType.WindowBufferSize:
|
||||
@@ -571,15 +605,17 @@ namespace Terminal.Gui {
|
||||
rows = inputEvent.WindowBufferSizeEvent.size.Y;
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
TerminalResized?.Invoke();
|
||||
TerminalResized?.Invoke ();
|
||||
break;
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
|
||||
WindowsConsole.ButtonState? LastMouseButtonPressed = null;
|
||||
bool IsButtonPressed = false;
|
||||
bool IsButtonReleased = false;
|
||||
bool IsButtonDoubleClicked = false;
|
||||
Point point;
|
||||
|
||||
MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
|
||||
{
|
||||
@@ -587,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;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -599,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;
|
||||
@@ -619,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;
|
||||
@@ -637,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;
|
||||
@@ -734,8 +808,8 @@ namespace Terminal.Gui {
|
||||
bool capslock = (state & (WindowsConsole.ControlKeyState.CapslockOn)) != 0;
|
||||
bool numlock = (state & (WindowsConsole.ControlKeyState.NumlockOn)) != 0;
|
||||
|
||||
var ConsoleKeyInfo = new ConsoleKeyInfo(keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
|
||||
return new ConsoleKeyInfoEx(ConsoleKeyInfo, capslock, numlock);
|
||||
var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
|
||||
return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
|
||||
}
|
||||
|
||||
public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
|
||||
@@ -837,6 +911,7 @@ namespace Terminal.Gui {
|
||||
|
||||
return (Key)((int)Key.F1 + delta);
|
||||
}
|
||||
|
||||
return (Key)(0xffffffff);
|
||||
}
|
||||
|
||||
@@ -871,7 +946,7 @@ namespace Terminal.Gui {
|
||||
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
|
||||
Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Cyan);
|
||||
Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
|
||||
Colors.Menu.Disabled = MakeColor(ConsoleColor.DarkGray, ConsoleColor.Cyan);
|
||||
Colors.Menu.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Cyan);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Cyan);
|
||||
@@ -925,10 +1000,10 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
ccol++;
|
||||
var runeWidth = Rune.ColumnWidth(rune);
|
||||
var runeWidth = Rune.ColumnWidth (rune);
|
||||
if (runeWidth > 1) {
|
||||
for (int i = 1; i < runeWidth; i++) {
|
||||
AddStr(" ");
|
||||
AddStr (" ");
|
||||
}
|
||||
}
|
||||
if (ccol == Cols) {
|
||||
@@ -947,7 +1022,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
int currentAttribute;
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource();
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
||||
|
||||
public override void SetAttribute (Attribute c)
|
||||
{
|
||||
@@ -995,39 +1070,39 @@ namespace Terminal.Gui {
|
||||
if (damageRegion.Left == -1)
|
||||
return;
|
||||
|
||||
var bufferCoords = new WindowsConsole.Coord (){
|
||||
var bufferCoords = new WindowsConsole.Coord () {
|
||||
X = (short)Clip.Width,
|
||||
Y = (short)Clip.Height
|
||||
};
|
||||
|
||||
var window = new WindowsConsole.SmallRect (){
|
||||
var window = new WindowsConsole.SmallRect () {
|
||||
Top = 0,
|
||||
Left = 0,
|
||||
Right = (short)Clip.Right,
|
||||
Bottom = (short)Clip.Bottom
|
||||
};
|
||||
|
||||
UpdateCursor();
|
||||
UpdateCursor ();
|
||||
winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
|
||||
// System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
|
||||
// System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
|
||||
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
|
||||
}
|
||||
|
||||
public override void UpdateCursor()
|
||||
public override void UpdateCursor ()
|
||||
{
|
||||
var position = new WindowsConsole.Coord(){
|
||||
var position = new WindowsConsole.Coord () {
|
||||
X = (short)ccol,
|
||||
Y = (short)crow
|
||||
};
|
||||
winConsole.SetCursorPosition(position);
|
||||
winConsole.SetCursorPosition (position);
|
||||
}
|
||||
|
||||
public override void End ()
|
||||
{
|
||||
winConsole.Cleanup();
|
||||
winConsole.Cleanup ();
|
||||
}
|
||||
|
||||
#region Unused
|
||||
#region Unused
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
}
|
||||
@@ -1055,7 +1130,7 @@ namespace Terminal.Gui {
|
||||
public override void CookMouse ()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,840 +0,0 @@
|
||||
//
|
||||
// WindowsDriver.cs: Windows specific driver
|
||||
//
|
||||
// Authors:
|
||||
// Miguel de Icaza (miguel@gnome.org)
|
||||
// Nick Van Dyck (vandyck.nick@outlook.com)
|
||||
//
|
||||
// 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
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -178,6 +178,12 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
AltMask = 0x80000000,
|
||||
|
||||
/// <summary>
|
||||
/// When this value is set, the Key encodes the sequence Ctrl-KeyValue.
|
||||
/// And the actual value must be extracted by removing the CtrlMask.
|
||||
/// </summary>
|
||||
CtrlMask = 0x40000000,
|
||||
|
||||
/// <summary>
|
||||
/// Backspace key.
|
||||
/// </summary>
|
||||
@@ -297,7 +303,7 @@ namespace Terminal.Gui {
|
||||
public bool IsAlt => (Key & Key.AltMask) != 0;
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the value is a control key
|
||||
/// Determines whether the value is a control key (and NOT just the ctrl key)
|
||||
/// </summary>
|
||||
/// <value><c>true</c> if is ctrl; otherwise, <c>false</c>.</value>
|
||||
public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
@@ -6,27 +6,24 @@
|
||||
//
|
||||
// TODO:
|
||||
// Add mouse support
|
||||
// Uses internals of Application
|
||||
using System;
|
||||
using NStack;
|
||||
|
||||
namespace Terminal.Gui
|
||||
{
|
||||
namespace Terminal.Gui {
|
||||
/// <summary>
|
||||
/// A statusbar item has a title, a shortcut aka hotkey, and an action to execute on activation.
|
||||
/// Such an item is ment to be as part of the global hotkeys of the application, which are available in the current context of the screen.
|
||||
/// The colour of the text will be changed after each ~. Having an statusbar item with a text of `~F1~ Help` will draw *F1* as shortcut and
|
||||
/// The colour of the text will be changed after each ~. Having an statusbar item with a text of `~F1~ Help` will draw *F1* as shortcut and
|
||||
/// *Help* as standard text.
|
||||
/// </summary>
|
||||
public class StatusItem
|
||||
{
|
||||
public class StatusItem {
|
||||
/// <summary>
|
||||
/// Initializes a new <see cref="T:Terminal.Gui.StatusItem"/>.
|
||||
/// </summary>
|
||||
/// <param name="shortcut">Shortcut to activate the item.</param>
|
||||
/// <param name="title">Title for the statusbar item.</param>
|
||||
/// <param name="action">Action to invoke when the staturbar item is activated.</param>
|
||||
public StatusItem(Key shortcut, ustring title, Action action)
|
||||
public StatusItem (Key shortcut, ustring title, Action action)
|
||||
{
|
||||
Title = title ?? "";
|
||||
Shortcut = shortcut;
|
||||
@@ -52,39 +49,91 @@ namespace Terminal.Gui
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A statusbar for your application.
|
||||
/// A statusbar for your application.
|
||||
/// The statusbar should be context sensitive. This means, if the main menu and an open text editor are visible, the items probably shown will
|
||||
/// be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog to ask a file to load is executed, the remaining commands will probably be ~F1~ Help.
|
||||
/// So for each context must be a new instance of a statusbar.
|
||||
/// be ~F1~ Help ~F2~ Save ~F3~ Load. While a dialog to ask a file to load is executed, the remaining commands will probably be ~F1~ Help.
|
||||
/// So for each context must be a new instance of a statusbar.
|
||||
/// </summary>
|
||||
public class StatusBar : View
|
||||
{
|
||||
public 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()
|
||||
public StatusBar (StatusItem [] items) : base ()
|
||||
{
|
||||
X = 0;
|
||||
Y = Application.Driver.Rows - 1; // TODO: using internals of Application
|
||||
Width = Dim.Fill ();
|
||||
Height = 1;
|
||||
Items = items;
|
||||
CanFocus = false;
|
||||
ColorScheme = Colors.Menu;
|
||||
|
||||
Application.OnLoad += () => {
|
||||
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
|
||||
};
|
||||
}
|
||||
|
||||
Attribute ToggleScheme(Attribute scheme)
|
||||
Attribute ToggleScheme (Attribute scheme)
|
||||
{
|
||||
var result = scheme==ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
|
||||
Driver.SetAttribute(result);
|
||||
var result = scheme == ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
|
||||
Driver.SetAttribute (result);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override void Redraw(Rect region) {
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
if (Frame.Y != Driver.Rows - 1) {
|
||||
Frame = new Rect (Frame.X, Driver.Rows - 1, Frame.Width, Frame.Height);
|
||||
Y = Driver.Rows - 1;
|
||||
@@ -98,15 +147,15 @@ namespace Terminal.Gui
|
||||
|
||||
Move (1, 0);
|
||||
var scheme = ColorScheme.Normal;
|
||||
Driver.SetAttribute(scheme);
|
||||
for(int i=0; i<Items.Length; i++) {
|
||||
var title = Items[i].Title;
|
||||
for(int n=0; n<title.Length; n++) {
|
||||
if(title[n]=='~') {
|
||||
scheme = ToggleScheme(scheme);
|
||||
Driver.SetAttribute (scheme);
|
||||
for (int i = 0; i < Items.Length; i++) {
|
||||
var title = Items [i].Title;
|
||||
for (int n = 0; n < title.Length; n++) {
|
||||
if (title [n] == '~') {
|
||||
scheme = ToggleScheme (scheme);
|
||||
continue;
|
||||
}
|
||||
Driver.AddRune(title[n]);
|
||||
Driver.AddRune (title [n]);
|
||||
}
|
||||
Driver.AddRune (' ');
|
||||
}
|
||||
@@ -114,9 +163,9 @@ namespace Terminal.Gui
|
||||
|
||||
public override bool ProcessHotKey (KeyEvent kb)
|
||||
{
|
||||
foreach(var item in Items) {
|
||||
if(kb.Key==item.Shortcut) {
|
||||
if( item.Action!=null ) item.Action();
|
||||
foreach (var item in Items) {
|
||||
if (kb.Key == item.Shortcut) {
|
||||
if (item.Action != null) item.Action ();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 ();
|
||||
|
||||
118
Terminal.sln
118
Terminal.sln
@@ -1,59 +1,59 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{B0A602CD-E176-449D-8663-64238D54F857}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.Build.0 = Debug|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.ActiveCfg = Release|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.Build.0 = Release|x86
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.Build.0 = Debug|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
$0.TextStylePolicy = $1
|
||||
$1.FileWidth = 80
|
||||
$1.scope = text/x-csharp
|
||||
$1.TabWidth = 8
|
||||
$1.IndentWidth = 8
|
||||
$0.CSharpFormattingPolicy = $2
|
||||
$2.scope = text/x-csharp
|
||||
$2.IndentSwitchSection = False
|
||||
$2.NewLinesForBracesInTypes = False
|
||||
$2.NewLinesForBracesInProperties = False
|
||||
$2.NewLinesForBracesInAccessors = False
|
||||
$2.NewLinesForBracesInAnonymousMethods = False
|
||||
$2.NewLinesForBracesInControlBlocks = False
|
||||
$2.NewLinesForBracesInAnonymousTypes = False
|
||||
$2.NewLinesForBracesInObjectCollectionArrayInitializers = False
|
||||
$2.NewLinesForBracesInLambdaExpressionBody = False
|
||||
$2.NewLineForElse = False
|
||||
$2.NewLineForCatch = False
|
||||
$2.NewLineForFinally = False
|
||||
$2.NewLineForMembersInObjectInit = False
|
||||
$2.NewLineForMembersInAnonymousTypes = False
|
||||
$2.NewLineForClausesInQuery = False
|
||||
$2.SpacingAfterMethodDeclarationName = True
|
||||
$2.SpaceAfterMethodCallName = True
|
||||
$2.SpaceBeforeOpenSquareBracket = True
|
||||
$0.DotNetNamingPolicy = $3
|
||||
$0.StandardHeader = $4
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 2012
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Example", "Example\Example.csproj", "{B0A602CD-E176-449D-8663-64238D54F857}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Terminal.Gui", "Terminal.Gui\Terminal.Gui.csproj", "{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Designer", "Designer\Designer.csproj", "{1228D992-C801-49BB-839A-7BD28A3FFF0A}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Debug|x86.Build.0 = Debug|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.ActiveCfg = Release|x86
|
||||
{B0A602CD-E176-449D-8663-64238D54F857}.Release|x86.Build.0 = Release|x86
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{00F366F8-DEE4-482C-B9FD-6DB0200B79E5}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.ActiveCfg = Debug|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Debug|x86.Build.0 = Debug|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.ActiveCfg = Release|x86
|
||||
{1228D992-C801-49BB-839A-7BD28A3FFF0A}.Release|x86.Build.0 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(MonoDevelopProperties) = preSolution
|
||||
Policies = $0
|
||||
$0.TextStylePolicy = $1
|
||||
$1.FileWidth = 80
|
||||
$1.scope = text/x-csharp
|
||||
$1.TabWidth = 8
|
||||
$1.IndentWidth = 8
|
||||
$0.CSharpFormattingPolicy = $2
|
||||
$2.scope = text/x-csharp
|
||||
$2.IndentSwitchSection = False
|
||||
$2.NewLinesForBracesInTypes = False
|
||||
$2.NewLinesForBracesInProperties = False
|
||||
$2.NewLinesForBracesInAccessors = False
|
||||
$2.NewLinesForBracesInAnonymousMethods = False
|
||||
$2.NewLinesForBracesInControlBlocks = False
|
||||
$2.NewLinesForBracesInAnonymousTypes = False
|
||||
$2.NewLinesForBracesInObjectCollectionArrayInitializers = False
|
||||
$2.NewLinesForBracesInLambdaExpressionBody = False
|
||||
$2.NewLineForElse = False
|
||||
$2.NewLineForCatch = False
|
||||
$2.NewLineForFinally = False
|
||||
$2.NewLineForMembersInObjectInit = False
|
||||
$2.NewLineForMembersInAnonymousTypes = False
|
||||
$2.NewLineForClausesInQuery = False
|
||||
$2.SpacingAfterMethodDeclarationName = True
|
||||
$2.SpaceAfterMethodCallName = True
|
||||
$2.SpaceBeforeOpenSquareBracket = True
|
||||
$0.DotNetNamingPolicy = $3
|
||||
$0.StandardHeader = $4
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
||||
Reference in New Issue
Block a user