diff --git a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
index 93a4db09d..aae666622 100644
--- a/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
@@ -143,6 +143,11 @@ namespace Terminal.Gui {
background: MapCursesColor (background));
}
+ static Attribute MakeColor (Color fore, Color back)
+ {
+ return MakeColor ((short)MapColor (fore), (short)MapColor (back));
+ }
+
int [,] colorPairs = new int [16, 16];
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
@@ -253,6 +258,7 @@ namespace Terminal.Gui {
bool cancelButtonClicked;
bool isReportMousePosition;
Point point;
+ int buttonPressedCount;
MouseEvent ToDriverMouse (Curses.MouseEvent cev)
{
@@ -263,10 +269,59 @@ namespace Terminal.Gui {
isButtonPressed = false;
}
+ if (cev.ButtonState == Curses.Event.Button1Pressed
+ || cev.ButtonState == Curses.Event.Button2Pressed
+ || cev.ButtonState == Curses.Event.Button3Pressed) {
- if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked ||
- cev.ButtonState == Curses.Event.Button3Clicked) &&
- lastMouseButtonPressed == null) {
+ isButtonPressed = true;
+ buttonPressedCount++;
+ } else {
+ buttonPressedCount = 0;
+ }
+ //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}");
+
+ if (buttonPressedCount == 2
+ && (cev.ButtonState == Curses.Event.Button1Pressed
+ || cev.ButtonState == Curses.Event.Button2Pressed
+ || cev.ButtonState == Curses.Event.Button3Pressed)) {
+
+ switch (cev.ButtonState) {
+ case Curses.Event.Button1Pressed:
+ mouseFlag = MouseFlags.Button1DoubleClicked;
+ break;
+
+ case Curses.Event.Button2Pressed:
+ mouseFlag = MouseFlags.Button2DoubleClicked;
+ break;
+
+ case Curses.Event.Button3Pressed:
+ mouseFlag = MouseFlags.Button3DoubleClicked;
+ break;
+ }
+
+ } else if (buttonPressedCount == 3
+ && (cev.ButtonState == Curses.Event.Button1Pressed
+ || cev.ButtonState == Curses.Event.Button2Pressed
+ || cev.ButtonState == Curses.Event.Button3Pressed)) {
+
+ switch (cev.ButtonState) {
+ case Curses.Event.Button1Pressed:
+ mouseFlag = MouseFlags.Button1TripleClicked;
+ break;
+
+ case Curses.Event.Button2Pressed:
+ mouseFlag = MouseFlags.Button2TripleClicked;
+ break;
+
+ case Curses.Event.Button3Pressed:
+ mouseFlag = MouseFlags.Button3TripleClicked;
+ break;
+ }
+ buttonPressedCount = 0;
+
+ } else if ((cev.ButtonState == Curses.Event.Button1Clicked || cev.ButtonState == Curses.Event.Button2Clicked ||
+ cev.ButtonState == Curses.Event.Button3Clicked) &&
+ lastMouseButtonPressed == null) {
isButtonPressed = false;
mouseFlag = ProcessButtonClickedEvent (cev);
@@ -797,57 +852,66 @@ namespace Terminal.Gui {
Curses.StartColor ();
Curses.UseDefaultColors ();
- Colors.TopLevel.Normal = MakeColor (Curses.COLOR_GREEN, Curses.COLOR_BLACK);
- Colors.TopLevel.Focus = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
- Colors.TopLevel.HotNormal = MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
- Colors.TopLevel.HotFocus = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_CYAN);
+ Colors.TopLevel.Normal = MakeColor (Color.Green, Color.Black);
+ Colors.TopLevel.Focus = MakeColor (Color.White, Color.Cyan);
+ Colors.TopLevel.HotNormal = MakeColor (Color.Brown, Color.Black);
+ Colors.TopLevel.HotFocus = MakeColor (Color.Blue, Color.Cyan);
+ Colors.TopLevel.Disabled = MakeColor (Color.DarkGray, Color.Black);
- Colors.Base.Normal = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLUE);
- Colors.Base.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
- Colors.Base.HotNormal = MakeColor (Curses.COLOR_CYAN, Curses.COLOR_BLUE);
- Colors.Base.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_BLUE, Curses.COLOR_GRAY);
+ Colors.Base.Normal = MakeColor (Color.White, Color.Blue);
+ Colors.Base.Focus = MakeColor (Color.Black, Color.Gray);
+ Colors.Base.HotNormal = MakeColor (Color.Cyan, Color.Blue);
+ Colors.Base.HotFocus = MakeColor (Color.Blue, Color.Gray);
+ Colors.Base.Disabled = MakeColor (Color.DarkGray, Color.Blue);
// Focused,
// Selected, Hot: Yellow on Black
// Selected, text: white on black
// Unselected, hot: yellow on cyan
// unselected, text: same as unfocused
- Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_GRAY);
- Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
- Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_GRAY);
- Colors.Menu.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
- Colors.Menu.Disabled = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_GRAY);
+ Colors.Menu.Normal = MakeColor (Color.White, Color.DarkGray);
+ Colors.Menu.Focus = MakeColor (Color.White, Color.Black);
+ Colors.Menu.HotNormal = MakeColor (Color.BrightYellow, Color.DarkGray);
+ Colors.Menu.HotFocus = MakeColor (Color.BrightYellow, Color.Black);
+ Colors.Menu.Disabled = MakeColor (Color.Gray, Color.DarkGray);
- Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
- Colors.Dialog.Focus = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_GRAY);
- Colors.Dialog.HotNormal = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_WHITE);
- Colors.Dialog.HotFocus = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_GRAY);
+ Colors.Dialog.Normal = MakeColor (Color.Black, Color.Gray);
+ Colors.Dialog.Focus = MakeColor (Color.White, Color.DarkGray);
+ Colors.Dialog.HotNormal = MakeColor (Color.Blue, Color.Gray);
+ Colors.Dialog.HotFocus = MakeColor (Color.Blue, Color.DarkGray);
+ Colors.Dialog.Disabled = MakeColor (Color.DarkGray, Color.Gray);
- Colors.Error.Normal = MakeColor (Curses.COLOR_RED, Curses.COLOR_WHITE);
- Colors.Error.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_RED);
- Colors.Error.HotNormal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
- Colors.Error.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_BLACK, Curses.COLOR_RED);
+ Colors.Error.Normal = MakeColor (Color.Red, Color.White);
+ Colors.Error.Focus = MakeColor (Color.White, Color.Red);
+ Colors.Error.HotNormal = MakeColor (Color.Black, Color.White);
+ Colors.Error.HotFocus = MakeColor (Color.Black, Color.Red);
+ Colors.Error.Disabled = MakeColor (Color.DarkGray, Color.White);
} else {
Colors.TopLevel.Normal = Curses.COLOR_GREEN;
Colors.TopLevel.Focus = Curses.COLOR_WHITE;
Colors.TopLevel.HotNormal = Curses.COLOR_YELLOW;
Colors.TopLevel.HotFocus = Curses.COLOR_YELLOW;
+ Colors.TopLevel.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
Colors.Base.Normal = Curses.A_NORMAL;
Colors.Base.Focus = Curses.A_REVERSE;
Colors.Base.HotNormal = Curses.A_BOLD;
Colors.Base.HotFocus = Curses.A_BOLD | Curses.A_REVERSE;
+ Colors.Base.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
Colors.Menu.Normal = Curses.A_REVERSE;
Colors.Menu.Focus = Curses.A_NORMAL;
Colors.Menu.HotNormal = Curses.A_BOLD;
Colors.Menu.HotFocus = Curses.A_NORMAL;
+ Colors.Menu.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
Colors.Dialog.Normal = Curses.A_REVERSE;
Colors.Dialog.Focus = Curses.A_NORMAL;
Colors.Dialog.HotNormal = Curses.A_BOLD;
Colors.Dialog.HotFocus = Curses.A_NORMAL;
+ Colors.Dialog.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
Colors.Error.Normal = Curses.A_BOLD;
Colors.Error.Focus = Curses.A_BOLD | Curses.A_REVERSE;
Colors.Error.HotNormal = Curses.A_BOLD | Curses.A_REVERSE;
Colors.Error.HotFocus = Curses.A_REVERSE;
+ Colors.Error.Disabled = Curses.A_BOLD | Curses.COLOR_GRAY;
}
}
diff --git a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
index 0aa48539f..ba68cf3ef 100644
--- a/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
@@ -145,7 +145,9 @@ namespace Terminal.Gui {
cols = FakeConsole.WindowWidth = FakeConsole.BufferWidth = FakeConsole.WIDTH;
rows = FakeConsole.WindowHeight = FakeConsole.BufferHeight = FakeConsole.HEIGHT;
- UpdateOffscreen ();
+ FakeConsole.Clear ();
+ ResizeScreen ();
+ UpdateOffScreen ();
Colors.TopLevel = new ColorScheme ();
Colors.Base = new ColorScheme ();
@@ -158,11 +160,13 @@ namespace Terminal.Gui {
Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+ Colors.TopLevel.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Black);
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.Base.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.DarkBlue);
// Focused,
// Selected, Hot: Yellow on Black
@@ -179,11 +183,13 @@ namespace Terminal.Gui {
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.Dialog.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Gray);
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;
+ Colors.Error.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.White);
//MockConsole.Clear ();
}
@@ -442,7 +448,11 @@ namespace Terminal.Gui {
///
public override bool GetCursorVisibility (out CursorVisibility visibility)
{
- visibility = CursorVisibility.Default;
+ if (FakeConsole.CursorVisible) {
+ visibility = CursorVisibility.Default;
+ } else {
+ visibility = CursorVisibility.Invisible;
+ }
return false;
}
@@ -450,6 +460,12 @@ namespace Terminal.Gui {
///
public override bool SetCursorVisibility (CursorVisibility visibility)
{
+ if (visibility == CursorVisibility.Invisible) {
+ FakeConsole.CursorVisible = false;
+ } else {
+ FakeConsole.CursorVisible = true;
+ }
+
return false;
}
diff --git a/Terminal.Gui/ConsoleDrivers/NetDriver.cs b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
index df97fe92c..7aceb48bd 100644
--- a/Terminal.Gui/ConsoleDrivers/NetDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/NetDriver.cs
@@ -506,7 +506,7 @@ namespace Terminal.Gui {
bool isButtonDoubleClicked;
bool isButtonTripleClicked;
bool isProcContBtnPressedRuning;
- Point point = new Point ();
+ int buttonPressedCount;
//bool isButtonReleased;
void GetMouseEvent (ConsoleKeyInfo [] cki)
@@ -701,16 +701,52 @@ namespace Terminal.Gui {
if ((buttonState & MouseButtonState.Button1Pressed) != 0
|| (buttonState & MouseButtonState.Button2Pressed) != 0
|| (buttonState & MouseButtonState.Button3Pressed) != 0) {
+
+ if ((buttonState & MouseButtonState.ReportMousePosition) == 0) {
+ buttonPressedCount++;
+ } else {
+ buttonPressedCount = 0;
+ }
+ //System.Diagnostics.Debug.WriteLine ($"buttonPressedCount: {buttonPressedCount}");
isButtonPressed = true;
} else {
isButtonPressed = false;
+ buttonPressedCount = 0;
}
+ if (buttonPressedCount == 2 && !isButtonDoubleClicked
+ && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed
+ || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed
+ || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) {
+
+ isButtonDoubleClicked = true;
+ ProcessButtonDoubleClicked (mouseEvent);
+ inputReady.Set ();
+ return;
+ } else if (buttonPressedCount == 3 && isButtonDoubleClicked
+ && (lastMouseEvent.ButtonState == MouseButtonState.Button1Pressed
+ || lastMouseEvent.ButtonState == MouseButtonState.Button2Pressed
+ || lastMouseEvent.ButtonState == MouseButtonState.Button3Pressed)) {
+
+ isButtonDoubleClicked = false;
+ isButtonTripleClicked = true;
+ buttonPressedCount = 0;
+ ProcessButtonTripleClicked (mouseEvent);
+ lastMouseEvent = mouseEvent;
+ inputReady.Set ();
+ return;
+ }
+
+ //System.Diagnostics.Debug.WriteLine ($"isButtonClicked: {isButtonClicked} isButtonDoubleClicked: {isButtonDoubleClicked} isButtonTripleClicked: {isButtonTripleClicked}");
if ((isButtonClicked || isButtonDoubleClicked || isButtonTripleClicked)
&& ((buttonState & MouseButtonState.Button1Released) != 0
|| (buttonState & MouseButtonState.Button2Released) != 0
|| (buttonState & MouseButtonState.Button3Released) != 0)) {
+
+ //isButtonClicked = false;
+ //isButtonDoubleClicked = false;
isButtonTripleClicked = false;
+ buttonPressedCount = 0;
return;
}
@@ -721,11 +757,13 @@ namespace Terminal.Gui {
|| (buttonState & MouseButtonState.Button1Released) != 0
|| (buttonState & MouseButtonState.Button2Released) != 0
|| (buttonState & MouseButtonState.Button3Released) != 0)) {
+
+ isButtonClicked = false;
isButtonDoubleClicked = true;
ProcessButtonDoubleClicked (mouseEvent);
Application.MainLoop.AddIdle (() => {
Task.Run (async () => {
- await Task.Delay (300);
+ await Task.Delay (600);
isButtonDoubleClicked = false;
});
return false;
@@ -740,6 +778,8 @@ namespace Terminal.Gui {
|| (buttonState & MouseButtonState.Button1Released) != 0
|| (buttonState & MouseButtonState.Button2Released) != 0
|| (buttonState & MouseButtonState.Button3Released) != 0)) {
+
+ isButtonDoubleClicked = false;
isButtonTripleClicked = true;
ProcessButtonTripleClicked (mouseEvent);
inputReady.Set ();
@@ -792,7 +832,7 @@ namespace Terminal.Gui {
}
if ((buttonState & MouseButtonState.ReportMousePosition) == 0) {
Application.MainLoop.AddIdle (() => {
- Task.Run (async () => await ProcessContinuousButtonPressedAsync (mouseEvent));
+ Task.Run (async () => await ProcessContinuousButtonPressedAsync ());
return false;
});
}
@@ -873,24 +913,20 @@ namespace Terminal.Gui {
});
}
- async Task ProcessContinuousButtonPressedAsync (MouseEvent mouseEvent)
+ async Task ProcessContinuousButtonPressedAsync ()
{
isProcContBtnPressedRuning = true;
await Task.Delay (200);
while (isButtonPressed) {
await Task.Delay (100);
- var me = new MouseEvent () {
- Position = new Point (mouseEvent.Position.X, mouseEvent.Position.Y),
- ButtonState=mouseEvent.ButtonState
- };
var view = Application.wantContinuousButtonPressedView;
if (view == null) {
break;
}
- if (isButtonPressed && (mouseEvent.ButtonState & MouseButtonState.ReportMousePosition) == 0) {
+ if (isButtonPressed && (lastMouseEvent.ButtonState & MouseButtonState.ReportMousePosition) == 0) {
inputResultQueue.Enqueue (new InputResult () {
EventType = EventType.Mouse,
- MouseEvent = me
+ MouseEvent = lastMouseEvent
});
inputReady.Set ();
}
@@ -1244,11 +1280,13 @@ namespace Terminal.Gui {
Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+ Colors.TopLevel.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Black);
Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+ Colors.Base.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.DarkBlue);
// Focused,
// Selected, Hot: Yellow on Black
@@ -1265,11 +1303,13 @@ namespace Terminal.Gui {
Colors.Dialog.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+ Colors.Dialog.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Gray);
Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
+ Colors.Error.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.White);
}
void ResizeScreen ()
diff --git a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
index c77bd7c0e..340deba13 100644
--- a/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
+++ b/Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
@@ -791,11 +791,15 @@ namespace Terminal.Gui {
bool isButtonReleased = false;
bool isButtonDoubleClicked = false;
Point point;
+ int buttonPressedCount;
+ bool isOneFingerDoubleClicked = false;
MouseEvent ToDriverMouse (WindowsConsole.MouseEventRecord mouseEvent)
{
MouseFlags mouseFlag = MouseFlags.AllEvents;
+ //System.Diagnostics.Debug.WriteLine ($"ButtonState: {mouseEvent.ButtonState};EventFlags: {mouseEvent.EventFlags}");
+
if (isButtonDoubleClicked) {
Application.MainLoop.AddIdle (() => {
Task.Run (async () => await ProcessButtonDoubleClickedAsync ());
@@ -810,7 +814,7 @@ namespace Terminal.Gui {
// map to the correct clicked event.
if ((lastMouseButtonPressed != null || isButtonReleased) && mouseEvent.ButtonState != 0) {
lastMouseButtonPressed = null;
- isButtonPressed = false;
+ //isButtonPressed = false;
isButtonReleased = false;
}
@@ -819,9 +823,70 @@ namespace Terminal.Gui {
Y = mouseEvent.MousePosition.Y
};
- if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) ||
- (mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved &&
- mouseEvent.ButtonState != 0 && !isButtonReleased && !isButtonDoubleClicked)) {
+ if (!isButtonPressed && buttonPressedCount < 2
+ && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved
+ && (mouseEvent.ButtonState == WindowsConsole.ButtonState.Button1Pressed
+ || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button2Pressed
+ || mouseEvent.ButtonState == WindowsConsole.ButtonState.Button3Pressed)) {
+
+ lastMouseButtonPressed = mouseEvent.ButtonState;
+ buttonPressedCount++;
+ } else if (!isButtonPressed && buttonPressedCount > 0 && mouseEvent.ButtonState == 0
+ && mouseEvent.EventFlags == 0) {
+
+ buttonPressedCount++;
+ } else {
+ buttonPressedCount = 0;
+ isOneFingerDoubleClicked = false;
+ }
+ //System.Diagnostics.Debug.WriteLine ($"isButtonPressed: {isButtonPressed};buttonPressedCount: {buttonPressedCount};lastMouseButtonPressed: {lastMouseButtonPressed}");
+
+ if (buttonPressedCount == 3 && lastMouseButtonPressed != null
+ && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed
+ || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed
+ || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) {
+
+ switch (lastMouseButtonPressed) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1DoubleClicked;
+ break;
+
+ case WindowsConsole.ButtonState.Button2Pressed:
+ mouseFlag = MouseFlags.Button2DoubleClicked;
+ break;
+
+ case WindowsConsole.ButtonState.Button3Pressed:
+ mouseFlag = MouseFlags.Button3DoubleClicked;
+ break;
+ }
+ isOneFingerDoubleClicked = true;
+
+ } else if (buttonPressedCount == 5 && lastMouseButtonPressed != null && isOneFingerDoubleClicked
+ && lastMouseButtonPressed == WindowsConsole.ButtonState.Button1Pressed
+ || lastMouseButtonPressed == WindowsConsole.ButtonState.Button2Pressed
+ || lastMouseButtonPressed == WindowsConsole.ButtonState.Button3Pressed) {
+
+ switch (lastMouseButtonPressed) {
+ case WindowsConsole.ButtonState.Button1Pressed:
+ mouseFlag = MouseFlags.Button1TripleClicked;
+ break;
+
+ case WindowsConsole.ButtonState.Button2Pressed:
+ mouseFlag = MouseFlags.Button2TripleClicked;
+ break;
+
+ case WindowsConsole.ButtonState.Button3Pressed:
+ mouseFlag = MouseFlags.Button3TripleClicked;
+ break;
+ }
+ buttonPressedCount = 0;
+ lastMouseButtonPressed = null;
+ isOneFingerDoubleClicked = false;
+ isButtonReleased = false;
+
+ } else if ((mouseEvent.ButtonState != 0 && mouseEvent.EventFlags == 0 && lastMouseButtonPressed == null && !isButtonDoubleClicked) ||
+ (lastMouseButtonPressed == null && mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved &&
+ mouseEvent.ButtonState != 0 && !isButtonReleased && !isButtonDoubleClicked)) {
switch (mouseEvent.ButtonState) {
case WindowsConsole.ButtonState.Button1Pressed:
mouseFlag = MouseFlags.Button1Pressed;
@@ -856,7 +921,6 @@ namespace Terminal.Gui {
});
}
-
} else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
lastMouseButtonPressed != null && !isButtonReleased && !isButtonDoubleClicked) {
switch (lastMouseButtonPressed) {
@@ -874,8 +938,8 @@ namespace Terminal.Gui {
}
isButtonPressed = false;
isButtonReleased = true;
- } else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved) &&
- isButtonReleased && p == point) {
+ } else if ((mouseEvent.EventFlags == 0 || mouseEvent.EventFlags == WindowsConsole.EventFlags.MouseMoved)
+ && !isOneFingerDoubleClicked && isButtonReleased && p == point) {
switch (lastMouseButtonPressed) {
case WindowsConsole.ButtonState.Button1Pressed:
mouseFlag = MouseFlags.Button1Clicked;
@@ -1214,11 +1278,13 @@ namespace Terminal.Gui {
Colors.TopLevel.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkCyan);
Colors.TopLevel.HotNormal = MakeColor (ConsoleColor.DarkYellow, ConsoleColor.Black);
Colors.TopLevel.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkCyan);
+ Colors.TopLevel.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Black);
Colors.Base.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkBlue);
Colors.Base.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
Colors.Base.HotNormal = MakeColor (ConsoleColor.DarkCyan, ConsoleColor.DarkBlue);
Colors.Base.HotFocus = MakeColor (ConsoleColor.Blue, ConsoleColor.Gray);
+ Colors.Base.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.DarkBlue);
Colors.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
@@ -1230,11 +1296,13 @@ namespace Terminal.Gui {
Colors.Dialog.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
+ Colors.Dialog.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.Gray);
Colors.Error.Normal = MakeColor (ConsoleColor.DarkRed, ConsoleColor.White);
Colors.Error.Focus = MakeColor (ConsoleColor.White, ConsoleColor.DarkRed);
Colors.Error.HotNormal = MakeColor (ConsoleColor.Black, ConsoleColor.White);
Colors.Error.HotFocus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkRed);
+ Colors.Error.Disabled = MakeColor (ConsoleColor.DarkGray, ConsoleColor.White);
}
void ResizeScreen ()
diff --git a/Terminal.Gui/Core/Application.cs b/Terminal.Gui/Core/Application.cs
index a880e6f2c..c12af219a 100644
--- a/Terminal.Gui/Core/Application.cs
+++ b/Terminal.Gui/Core/Application.cs
@@ -728,8 +728,8 @@ namespace Terminal.Gui {
Driver.PrepareToRun (MainLoop, ProcessKeyEvent, ProcessKeyDownEvent, ProcessKeyUpEvent, ProcessMouseEvent);
if (toplevel.LayoutStyle == LayoutStyle.Computed)
toplevel.SetRelativeLayout (new Rect (0, 0, Driver.Cols, Driver.Rows));
- toplevel.PositionToplevels ();
toplevel.LayoutSubviews ();
+ toplevel.PositionToplevels ();
toplevel.WillPresent ();
if (refreshDriver) {
if (MdiTop != null) {
@@ -794,6 +794,7 @@ namespace Terminal.Gui {
RootMouseEvent = null;
Resized = null;
_initialized = false;
+ mouseGrabView = null;
// Reset synchronization context to allow the user to run async/await,
// as the main loop has been ended, the synchronization context from
@@ -1141,8 +1142,8 @@ namespace Terminal.Gui {
Driver.Clip = full;
foreach (var t in toplevels) {
t.SetRelativeLayout (full);
- t.PositionToplevels ();
t.LayoutSubviews ();
+ t.PositionToplevels ();
}
Refresh ();
}
@@ -1167,6 +1168,9 @@ namespace Terminal.Gui {
static bool SetCurrentAsTop ()
{
if (MdiTop == null && Current != Top && Current?.SuperView == null && Current?.Modal == false) {
+ if (Current.Frame != new Rect (0, 0, Driver.Cols, Driver.Rows)) {
+ Current.Frame = new Rect (0, 0, Driver.Cols, Driver.Rows);
+ }
Top = Current;
return true;
}
diff --git a/Terminal.Gui/Core/Responder.cs b/Terminal.Gui/Core/Responder.cs
index 1fe1e1231..37de82145 100644
--- a/Terminal.Gui/Core/Responder.cs
+++ b/Terminal.Gui/Core/Responder.cs
@@ -58,6 +58,16 @@ namespace Terminal.Gui {
/// true if has focus; otherwise, false.
public virtual bool HasFocus { get; }
+ ///
+ /// Gets or sets a value indicating whether this can respond to user interaction.
+ ///
+ public virtual bool Enabled { get; set; } = true;
+
+ ///
+ /// Gets or sets a value indicating whether this and all its child controls are displayed.
+ ///
+ public virtual bool Visible { get; set; } = true;
+
// Key handling
///
/// This method can be overwritten by view that
@@ -211,6 +221,21 @@ namespace Terminal.Gui {
return false;
}
+ ///
+ /// Method invoked when the property from a view is changed.
+ ///
+ public virtual void OnCanFocusChanged () { }
+
+ ///
+ /// Method invoked when the property from a view is changed.
+ ///
+ public virtual void OnEnabledChanged () { }
+
+ ///
+ /// Method invoked when the property from a view is changed.
+ ///
+ public virtual void OnVisibleChanged () { }
+
///
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
///
diff --git a/Terminal.Gui/Core/ShortcutHelper.cs b/Terminal.Gui/Core/ShortcutHelper.cs
index 9314afdd0..437a6e4a4 100644
--- a/Terminal.Gui/Core/ShortcutHelper.cs
+++ b/Terminal.Gui/Core/ShortcutHelper.cs
@@ -59,15 +59,18 @@ namespace Terminal.Gui {
/// Get the key as string.
///
/// The shortcut key.
+ /// The delimiter string.
///
- public static ustring GetShortcutTag (Key shortcut)
+ public static ustring GetShortcutTag (Key shortcut, ustring delimiter = null)
{
if (shortcut == Key.Null) {
return "";
}
var k = shortcut;
- var delimiter = MenuBar.ShortcutDelimiter;
+ if (delimiter == null) {
+ delimiter = MenuBar.ShortcutDelimiter;
+ }
ustring tag = ustring.Empty;
var sCut = GetKeyToString (k, out Key knm).ToString ();
if (knm == Key.Unknown) {
@@ -146,7 +149,8 @@ namespace Terminal.Gui {
/// Allows to retrieve a from a
///
/// The key as string.
- public static Key GetShortcutFromTag (ustring tag)
+ /// The delimiter string.
+ public static Key GetShortcutFromTag (ustring tag, ustring delimiter = null)
{
var sCut = tag;
if (sCut.IsEmpty) {
@@ -155,7 +159,9 @@ namespace Terminal.Gui {
Key key = Key.Null;
//var hasCtrl = false;
- var delimiter = MenuBar.ShortcutDelimiter;
+ if (delimiter == null) {
+ delimiter = MenuBar.ShortcutDelimiter;
+ }
ustring [] keys = sCut.Split (delimiter);
for (int i = 0; i < keys.Length; i++) {
diff --git a/Terminal.Gui/Core/Toplevel.cs b/Terminal.Gui/Core/Toplevel.cs
index 95453b709..c8e433036 100644
--- a/Terminal.Gui/Core/Toplevel.cs
+++ b/Terminal.Gui/Core/Toplevel.cs
@@ -478,15 +478,19 @@ namespace Terminal.Gui {
}
}
- internal void EnsureVisibleBounds (Toplevel top, int x, int y, out int nx, out int ny)
+ internal View EnsureVisibleBounds (Toplevel top, int x, int y,
+ out int nx, out int ny, out View mb, out View sb)
{
- nx = Math.Max (x, 0);
int l;
- if (SuperView == null || SuperView is Toplevel) {
+ View superView;
+ if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
l = Driver.Cols;
+ superView = Application.Top;
} else {
- l = SuperView.Frame.Width;
+ l = top.SuperView.Frame.Width;
+ superView = top.SuperView;
}
+ nx = Math.Max (x, 0);
nx = nx + top.Frame.Width > l ? Math.Max (l - top.Frame.Width, 0) : nx;
SetWidth (top.Frame.Width, out int rWidth);
if (rWidth < 0 && nx >= top.Frame.X) {
@@ -494,34 +498,48 @@ namespace Terminal.Gui {
}
//System.Diagnostics.Debug.WriteLine ($"nx:{nx}, rWidth:{rWidth}");
bool m, s;
- if (SuperView == null || SuperView.GetType () != typeof (Toplevel)) {
- m = Application.Top.MenuBar != null;
+ if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+ m = Application.Top.MenuBar?.Visible == true;
+ mb = Application.Top.MenuBar;
} else {
- m = ((Toplevel)SuperView).MenuBar != null;
+ var t = top.SuperView;
+ while (!(t is Toplevel)) {
+ t = t.SuperView;
+ }
+ m = ((Toplevel)t).MenuBar?.Visible == true;
+ mb = ((Toplevel)t).MenuBar;
}
- if (SuperView == null || SuperView is Toplevel) {
+ if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
l = m ? 1 : 0;
} else {
l = 0;
}
ny = Math.Max (y, l);
- if (SuperView == null || SuperView.GetType () != typeof (Toplevel)) {
- s = Application.Top.StatusBar != null && Application.Top.StatusBar.Visible;
+ if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
+ s = Application.Top.StatusBar?.Visible == true;
+ sb = Application.Top.StatusBar;
} else {
- s = ((Toplevel)SuperView).StatusBar != null && ((Toplevel)SuperView).StatusBar.Visible;
+ var t = top.SuperView;
+ while (!(t is Toplevel)) {
+ t = t.SuperView;
+ }
+ s = ((Toplevel)t).StatusBar?.Visible == true;
+ sb = ((Toplevel)t).StatusBar;
}
- if (SuperView == null || SuperView is Toplevel) {
+ if (top?.SuperView == null || top == Application.Top || top?.SuperView == Application.Top) {
l = s ? Driver.Rows - 1 : Driver.Rows;
} else {
- l = s ? SuperView.Frame.Height - 1 : SuperView.Frame.Height;
+ l = s ? top.SuperView.Frame.Height - 1 : top.SuperView.Frame.Height;
}
ny = Math.Min (ny, l);
- ny = ny + top.Frame.Height > l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
+ ny = ny + top.Frame.Height >= l ? Math.Max (l - top.Frame.Height, m ? 1 : 0) : ny;
SetHeight (top.Frame.Height, out int rHeight);
if (rHeight < 0 && ny >= top.Frame.Y) {
ny = Math.Max (top.Frame.Bottom - 2, 0);
}
//System.Diagnostics.Debug.WriteLine ($"ny:{ny}, rHeight:{rHeight}");
+
+ return superView;
}
internal void PositionToplevels ()
@@ -540,45 +558,32 @@ namespace Terminal.Gui {
/// The toplevel.
public virtual void PositionToplevel (Toplevel top)
{
- EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
- if ((top?.SuperView != null || top != Application.Top)
+ var superView = EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y,
+ out int nx, out int ny, out _, out View sb);
+ bool layoutSubviews = false;
+ if ((top?.SuperView != null || (top != Application.Top && top.Modal)
+ || (top?.SuperView == null && top.IsMdiChild))
&& (nx > top.Frame.X || ny > top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
+
if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
top.X = nx;
+ layoutSubviews = true;
}
if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Bounds.Y != ny) {
top.Y = ny;
+ layoutSubviews = true;
}
}
- View superView = null;
- StatusBar statusBar = null;
+ if (sb != null && ny + top.Frame.Height != superView.Frame.Height - (sb.Visible ? 1 : 0)
+ && top.Height is Dim.DimFill) {
- if (top != Application.Top && Application.Top.StatusBar != null) {
- superView = Application.Top;
- statusBar = Application.Top.StatusBar;
- } else if (top?.SuperView != null && top.SuperView is Toplevel toplevel) {
- superView = top.SuperView;
- statusBar = toplevel.StatusBar;
+ top.Height = Dim.Fill (sb.Visible ? 1 : 0);
+ layoutSubviews = true;
}
- if (statusBar != null) {
- if (ny + top.Frame.Height >= superView.Frame.Height - (statusBar.Visible ? 1 : 0)) {
- if (top.Height is Dim.DimFill) {
- top.Height = Dim.Fill (statusBar.Visible ? 1 : 0);
- }
- }
- if (superView == Application.Top) {
- top.SetRelativeLayout (superView.Frame);
- } else {
- superView.LayoutSubviews ();
- }
- }
- if (top.StatusBar != null) {
- if (top.StatusBar.Frame.Y != top.Frame.Height - (top.StatusBar.Visible ? 1 : 0)) {
- top.StatusBar.Y = top.Frame.Height - (top.StatusBar.Visible ? 1 : 0);
- top.LayoutSubviews ();
- }
- top.BringSubviewToFront (top.StatusBar);
+
+ if (layoutSubviews) {
+ superView.LayoutSubviews ();
}
}
@@ -590,18 +595,15 @@ namespace Terminal.Gui {
}
if (!NeedDisplay.IsEmpty || ChildNeedsDisplay || LayoutNeeded) {
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
// This is the Application.Top. Clear just the region we're being asked to redraw
// (the bounds passed to us).
- // Must be the screen-relative region to clear, not the bounds.
- Clear (Frame);
- Driver.SetAttribute (Colors.Base.Normal);
+ Clear ();
+ Driver.SetAttribute (Enabled ? Colors.Base.Normal : Colors.Base.Disabled);
- if (LayoutStyle == LayoutStyle.Computed)
- SetRelativeLayout (Bounds);
- PositionToplevels ();
LayoutSubviews ();
+ PositionToplevels ();
if (this == Application.MdiTop) {
foreach (var top in Application.MdiChildes.AsEnumerable ().Reverse ()) {
@@ -680,7 +682,8 @@ namespace Terminal.Gui {
SuperView.SetNeedsDisplay ();
}
EnsureVisibleBounds (this, mouseEvent.X + (SuperView == null ? mouseEvent.OfX - start.X : Frame.X - start.X),
- mouseEvent.Y + (SuperView == null ? mouseEvent.OfY : Frame.Y), out nx, out ny);
+ mouseEvent.Y + (SuperView == null ? mouseEvent.OfY : Frame.Y),
+ out nx, out ny, out _, out _);
dragPosition = new Point (nx, ny);
LayoutSubviews ();
diff --git a/Terminal.Gui/Core/View.cs b/Terminal.Gui/Core/View.cs
index 21a714082..9944559cc 100644
--- a/Terminal.Gui/Core/View.cs
+++ b/Terminal.Gui/Core/View.cs
@@ -163,6 +163,21 @@ namespace Terminal.Gui {
///
public event Action MouseClick;
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event Action CanFocusChanged;
+
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event Action EnabledChanged;
+
+ ///
+ /// Event fired when the value is being changed.
+ ///
+ public event Action VisibleChanged;
+
///
/// Gets or sets the HotKey defined for this view. A user pressing HotKey on the keyboard while this view has focus will cause the Clicked event to fire.
///
@@ -329,6 +344,11 @@ namespace Terminal.Gui {
TabIndex = SuperView != null ? SuperView.tabIndexes.IndexOf (this) : -1;
}
TabStop = value;
+ if (!value && HasFocus) {
+ SetHasFocus (false, this);
+ }
+ OnCanFocusChanged ();
+ SetNeedsDisplay ();
}
if (subviews != null && IsInitialized) {
foreach (var view in subviews) {
@@ -1114,7 +1134,7 @@ namespace Terminal.Gui {
if (focused)
DrawHotString (text, scheme.HotFocus, scheme.Focus);
else
- DrawHotString (text, scheme.HotNormal, scheme.Normal);
+ DrawHotString (text, Enabled ? scheme.HotNormal : scheme.Disabled, Enabled ? scheme.Normal : scheme.Disabled);
}
///
@@ -1143,11 +1163,11 @@ namespace Terminal.Gui {
/// in a visually sensible place.
public virtual void PositionCursor ()
{
- if (!CanBeVisible (this)) {
+ if (!CanBeVisible (this) || !Enabled) {
return;
}
- if (focused?.Frame.Width > 0 && focused.Frame.Height > 0) {
+ if (focused?.Visible == true && focused?.Enabled == true && focused?.Frame.Width > 0 && focused.Frame.Height > 0) {
focused.PositionCursor ();
} else {
if (CanFocus && HasFocus && Visible && Frame.Width > 0 && Frame.Height > 0) {
@@ -1271,6 +1291,8 @@ namespace Terminal.Gui {
}
}
+ ColorScheme colorScheme;
+
///
/// The color scheme for this view, if it is not defined, it returns the 's
/// color scheme.
@@ -1289,8 +1311,6 @@ namespace Terminal.Gui {
}
}
- ColorScheme colorScheme;
-
///
/// Displays the specified character in the specified column and row of the View.
///
@@ -1351,7 +1371,8 @@ namespace Terminal.Gui {
if (textFormatter != null) {
textFormatter.NeedsFormat = true;
}
- textFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : ColorScheme.Normal, HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
+ textFormatter?.Draw (ViewToScreen (Bounds), HasFocus ? ColorScheme.Focus : GetNormalColor (),
+ HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled);
}
// Invoke DrawContentEvent
@@ -1414,7 +1435,7 @@ namespace Terminal.Gui {
if (view == null)
return;
//Console.WriteLine ($"Request to focus {view}");
- if (!view.CanFocus || !view.Visible)
+ if (!view.CanFocus || !view.Visible || !view.Enabled)
return;
if (focused?.hasFocus == true && focused == view)
return;
@@ -1444,7 +1465,10 @@ namespace Terminal.Gui {
///
public void SetFocus ()
{
- if (!CanBeVisible (this)) {
+ if (!CanBeVisible (this) || !Enabled) {
+ if (HasFocus) {
+ SetHasFocus (false, this);
+ }
return;
}
@@ -1479,14 +1503,20 @@ namespace Terminal.Gui {
///
public override bool ProcessKey (KeyEvent keyEvent)
{
+ if (!Enabled) {
+ return false;
+ }
+
KeyEventEventArgs args = new KeyEventEventArgs (keyEvent);
KeyPress?.Invoke (args);
if (args.Handled)
return true;
- Focused?.KeyPress?.Invoke (args);
- if (args.Handled)
- return true;
- if (Focused?.ProcessKey (keyEvent) == true)
+ if (Focused?.Enabled == true) {
+ Focused?.KeyPress?.Invoke (args);
+ if (args.Handled)
+ return true;
+ }
+ if (Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true)
return true;
return false;
@@ -1495,19 +1525,25 @@ namespace Terminal.Gui {
///
public override bool ProcessHotKey (KeyEvent keyEvent)
{
+ if (!Enabled) {
+ return false;
+ }
+
KeyEventEventArgs args = new KeyEventEventArgs (keyEvent);
KeyPress?.Invoke (args);
if (args.Handled)
return true;
- Focused?.KeyPress?.Invoke (args);
- if (args.Handled)
- return true;
- if (Focused?.ProcessKey (keyEvent) == true)
+ if (Focused?.Enabled == true) {
+ Focused?.KeyPress?.Invoke (args);
+ if (args.Handled)
+ return true;
+ }
+ if (Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true)
return true;
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
- if (view.ProcessHotKey (keyEvent))
+ if (view.Enabled && view.ProcessHotKey (keyEvent))
return true;
return false;
}
@@ -1515,19 +1551,25 @@ namespace Terminal.Gui {
///
public override bool ProcessColdKey (KeyEvent keyEvent)
{
+ if (!Enabled) {
+ return false;
+ }
+
KeyEventEventArgs args = new KeyEventEventArgs (keyEvent);
KeyPress?.Invoke (args);
if (args.Handled)
return true;
- Focused?.KeyPress?.Invoke (args);
- if (args.Handled)
- return true;
- if (Focused?.ProcessKey (keyEvent) == true)
+ if (Focused?.Enabled == true) {
+ Focused?.KeyPress?.Invoke (args);
+ if (args.Handled)
+ return true;
+ }
+ if (Focused?.Enabled == true && Focused?.ProcessKey (keyEvent) == true)
return true;
if (subviews == null || subviews.Count == 0)
return false;
foreach (var view in subviews)
- if (view.ProcessColdKey (keyEvent))
+ if (view.Enabled && view.ProcessColdKey (keyEvent))
return true;
return false;
}
@@ -1540,12 +1582,16 @@ namespace Terminal.Gui {
/// Contains the details about the key that produced the event.
public override bool OnKeyDown (KeyEvent keyEvent)
{
+ if (!Enabled) {
+ return false;
+ }
+
KeyEventEventArgs args = new KeyEventEventArgs (keyEvent);
KeyDown?.Invoke (args);
if (args.Handled) {
return true;
}
- if (Focused?.OnKeyDown (keyEvent) == true) {
+ if (Focused?.Enabled == true && Focused?.OnKeyDown (keyEvent) == true) {
return true;
}
@@ -1560,12 +1606,16 @@ namespace Terminal.Gui {
/// Contains the details about the key that produced the event.
public override bool OnKeyUp (KeyEvent keyEvent)
{
+ if (!Enabled) {
+ return false;
+ }
+
KeyEventEventArgs args = new KeyEventEventArgs (keyEvent);
KeyUp?.Invoke (args);
if (args.Handled) {
return true;
}
- if (Focused?.OnKeyUp (keyEvent) == true) {
+ if (Focused?.Enabled == true && Focused?.OnKeyUp (keyEvent) == true) {
return true;
}
@@ -1601,7 +1651,7 @@ namespace Terminal.Gui {
}
foreach (var view in tabIndexes) {
- if (view.CanFocus && view.tabStop && view.Visible) {
+ if (view.CanFocus && view.tabStop && view.Visible && view.Enabled) {
SetFocus (view);
return;
}
@@ -1626,7 +1676,7 @@ namespace Terminal.Gui {
i--;
View v = tabIndexes [i];
- if (v.CanFocus && v.tabStop && v.Visible) {
+ if (v.CanFocus && v.tabStop && v.Visible && v.Enabled) {
SetFocus (v);
return;
}
@@ -1662,10 +1712,10 @@ namespace Terminal.Gui {
focused_idx = i;
continue;
}
- if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible) {
+ if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible && w.Enabled) {
focused.SetHasFocus (false, w);
- if (w != null && w.CanFocus && w.tabStop && w.Visible)
+ if (w != null && w.CanFocus && w.tabStop && w.Visible && w.Enabled)
w.FocusLast ();
SetFocus (w);
@@ -1708,10 +1758,10 @@ namespace Terminal.Gui {
focused_idx = i;
continue;
}
- if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible) {
+ if (w.CanFocus && focused_idx != -1 && w.tabStop && w.Visible && w.Enabled) {
focused.SetHasFocus (false, w);
- if (w != null && w.CanFocus && w.tabStop && w.Visible)
+ if (w != null && w.CanFocus && w.tabStop && w.Visible && w.Enabled)
w.FocusFirst ();
SetFocus (w);
@@ -2072,7 +2122,50 @@ namespace Terminal.Gui {
/// Get or sets if the was already initialized.
/// This derived from to allow notify all the views that are being initialized.
///
- public bool IsInitialized { get; set; }
+ public virtual bool IsInitialized { get; set; }
+
+ bool oldEnabled;
+
+ ///
+ public override bool Enabled {
+ get => base.Enabled;
+ set {
+ if (base.Enabled != value) {
+ base.Enabled = value;
+ if (!value && HasFocus) {
+ SetHasFocus (false, this);
+ }
+ OnEnabledChanged ();
+ SetNeedsDisplay ();
+ }
+ if (subviews != null) {
+ foreach (var view in subviews) {
+ if (!value) {
+ view.oldEnabled = view.Enabled;
+ view.Enabled = value;
+ } else {
+ view.Enabled = view.oldEnabled;
+ view.addingView = false;
+ }
+ }
+ }
+ }
+ }
+
+ /// >
+ public override bool Visible {
+ get => base.Visible;
+ set {
+ if (base.Visible != value) {
+ base.Visible = value;
+ if (!value && HasFocus) {
+ SetHasFocus (false, this);
+ }
+ OnVisibleChanged ();
+ SetNeedsDisplay ();
+ }
+ }
+ }
///
/// Pretty prints the View
@@ -2148,6 +2241,10 @@ namespace Terminal.Gui {
///
public override bool OnMouseEnter (MouseEvent mouseEvent)
{
+ if (!Enabled) {
+ return true;
+ }
+
if (!CanBeVisible (this)) {
return false;
}
@@ -2165,6 +2262,10 @@ namespace Terminal.Gui {
///
public override bool OnMouseLeave (MouseEvent mouseEvent)
{
+ if (!Enabled) {
+ return true;
+ }
+
if (!CanBeVisible (this)) {
return false;
}
@@ -2186,13 +2287,16 @@ namespace Terminal.Gui {
/// true, if the event was handled, false otherwise.
public virtual bool OnMouseEvent (MouseEvent mouseEvent)
{
+ if (!Enabled) {
+ return true;
+ }
+
if (!CanBeVisible (this)) {
return false;
}
MouseEventArgs args = new MouseEventArgs (mouseEvent);
- OnMouseClick (args);
- if (args.Handled)
+ if (OnMouseClick (args))
return true;
if (MouseEvent (mouseEvent))
return true;
@@ -2211,7 +2315,24 @@ namespace Terminal.Gui {
///
/// Invokes the MouseClick event.
///
- protected void OnMouseClick (MouseEventArgs args) => MouseClick?.Invoke (args);
+ protected bool OnMouseClick (MouseEventArgs args)
+ {
+ if (!Enabled) {
+ return true;
+ }
+
+ MouseClick?.Invoke (args);
+ return args.Handled;
+ }
+
+ ///
+ public override void OnCanFocusChanged () => CanFocusChanged?.Invoke ();
+
+ ///
+ public override void OnEnabledChanged () => EnabledChanged?.Invoke ();
+
+ ///
+ public override void OnVisibleChanged () => VisibleChanged?.Invoke ();
///
protected override void Dispose (bool disposing)
@@ -2258,11 +2379,6 @@ namespace Terminal.Gui {
Initialized?.Invoke (this, EventArgs.Empty);
}
- ///
- /// Gets or sets the view visibility.
- ///
- public bool Visible { get; set; } = true;
-
bool CanBeVisible (View view)
{
if (!view.Visible) {
@@ -2372,5 +2488,15 @@ namespace Terminal.Gui {
return CanSetHeight (0, out _);
}
+
+ ///
+ /// Determines the current based on the value.
+ ///
+ /// if is
+ /// or if is
+ protected Attribute GetNormalColor ()
+ {
+ return Enabled ? ColorScheme.Normal : ColorScheme.Disabled;
+ }
}
}
diff --git a/Terminal.Gui/Core/Window.cs b/Terminal.Gui/Core/Window.cs
index 99734a657..26a389275 100644
--- a/Terminal.Gui/Core/Window.cs
+++ b/Terminal.Gui/Core/Window.cs
@@ -181,7 +181,7 @@ namespace Terminal.Gui {
// BUGBUG: Why do we draw the frame twice? This call is here to clear the content area, I think. Why not just clear that area?
if (!NeedDisplay.IsEmpty) {
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
}
@@ -194,13 +194,13 @@ namespace Terminal.Gui {
ClearLayoutNeeded ();
ClearNeedsDisplay ();
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
// Checks if there are any SuperView view which intersect with this window.
if (SuperView != null) {
diff --git a/Terminal.Gui/Views/Button.cs b/Terminal.Gui/Views/Button.cs
index 094d297b2..4cd600f3f 100644
--- a/Terminal.Gui/Views/Button.cs
+++ b/Terminal.Gui/Views/Button.cs
@@ -184,6 +184,10 @@ namespace Terminal.Gui {
///
public override bool ProcessHotKey (KeyEvent kb)
{
+ if (!Enabled) {
+ return false;
+ }
+
if (kb.IsAlt)
return CheckKey (kb);
@@ -193,6 +197,10 @@ namespace Terminal.Gui {
///
public override bool ProcessColdKey (KeyEvent kb)
{
+ if (!Enabled) {
+ return false;
+ }
+
if (IsDefault && kb.KeyValue == '\n') {
Clicked?.Invoke ();
return true;
@@ -203,6 +211,10 @@ namespace Terminal.Gui {
///
public override bool ProcessKey (KeyEvent kb)
{
+ if (!Enabled) {
+ return false;
+ }
+
var c = kb.KeyValue;
if (c == '\n' || c == ' ' || kb.Key == HotKey) {
Clicked?.Invoke ();
@@ -228,7 +240,7 @@ namespace Terminal.Gui {
{
if (me.Flags == MouseFlags.Button1Clicked || me.Flags == MouseFlags.Button1DoubleClicked ||
me.Flags == MouseFlags.Button1TripleClicked) {
- if (CanFocus) {
+ if (CanFocus && Enabled) {
if (!HasFocus) {
SetFocus ();
SetNeedsDisplay ();
diff --git a/Terminal.Gui/Views/Checkbox.cs b/Terminal.Gui/Views/Checkbox.cs
index 72fc8304d..97bf09e7d 100644
--- a/Terminal.Gui/Views/Checkbox.cs
+++ b/Terminal.Gui/Views/Checkbox.cs
@@ -116,7 +116,7 @@ namespace Terminal.Gui {
///
public override void Redraw (Rect bounds)
{
- Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
+ Driver.SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ());
Move (0, 0);
Driver.AddRune (Checked ? Driver.Checked : Driver.UnChecked);
Driver.AddRune (' ');
@@ -124,7 +124,7 @@ namespace Terminal.Gui {
Driver.AddStr (Text);
if (hot_pos != -1) {
Move (2 + hot_pos, 0);
- Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
+ Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled);
Driver.AddRune (hot_key);
}
}
diff --git a/Terminal.Gui/Views/ComboBox.cs b/Terminal.Gui/Views/ComboBox.cs
index 122eeaa62..3eea30eb9 100644
--- a/Terminal.Gui/Views/ComboBox.cs
+++ b/Terminal.Gui/Views/ComboBox.cs
@@ -30,7 +30,7 @@ namespace Terminal.Gui {
source = value;
// Only need to refresh list if its been added to a container view
- if(SuperView != null && SuperView.Subviews.Contains(this)) {
+ if (SuperView != null && SuperView.Subviews.Contains (this)) {
Search_Changed ("");
SetNeedsDisplay ();
}
@@ -90,7 +90,7 @@ namespace Terminal.Gui {
{
search = new TextField ("");
listview = new ListView () { LayoutStyle = LayoutStyle.Computed, CanFocus = true, TabStop = false };
-
+
Initialize ();
Text = text;
}
@@ -124,8 +124,8 @@ namespace Terminal.Gui {
// On resize
LayoutComplete += (LayoutEventArgs a) => {
- if (!autoHide && search.Frame.Width != Bounds.Width ||
- autoHide && search.Frame.Width != Bounds.Width - 1) {
+ if ((!autoHide && Bounds.Width > 0 && search.Frame.Width != Bounds.Width) ||
+ (autoHide && Bounds.Width > 0 && search.Frame.Width != Bounds.Width - 1)) {
search.Width = listview.Width = autoHide ? Bounds.Width - 1 : Bounds.Width;
listview.Height = CalculatetHeight ();
search.SetRelativeLayout (Bounds);
@@ -144,7 +144,7 @@ namespace Terminal.Gui {
// Determine if this view is hosted inside a dialog and is the only control
for (View view = this.SuperView; view != null; view = view.SuperView) {
- if (view is Dialog && SuperView != null && SuperView.Subviews.Count == 1 && SuperView.Subviews[0] == this) {
+ if (view is Dialog && SuperView != null && SuperView.Subviews.Count == 1 && SuperView.Subviews [0] == this) {
autoHide = false;
break;
}
@@ -176,6 +176,21 @@ namespace Terminal.Gui {
}
}
+ ///
+ ///If set to true its not allow any changes in the text.
+ ///
+ public bool ReadOnly {
+ get => search.ReadOnly;
+ set {
+ search.ReadOnly = value;
+ if (search.ReadOnly) {
+ if (search.ColorScheme != null) {
+ search.ColorScheme.Normal = search.ColorScheme.Focus;
+ }
+ }
+ }
+ }
+
///
public override bool MouseEvent (MouseEvent me)
{
@@ -247,7 +262,7 @@ namespace Terminal.Gui {
{
// Note: Cannot rely on "listview.SelectedItem != lastSelectedItem" because the list is dynamic.
// So we cannot optimize. Ie: Don't call if not changed
- SelectedItemChanged?.Invoke (new ListViewItemEventArgs(SelectedItem, search.Text));
+ SelectedItemChanged?.Invoke (new ListViewItemEventArgs (SelectedItem, search.Text));
return true;
}
@@ -321,7 +336,7 @@ namespace Terminal.Gui {
return true;
}
- if(e.Key == Key.PageDown) {
+ if (e.Key == Key.PageDown) {
if (listview.SelectedItem != -1) {
listview.MovePageDown ();
}
@@ -342,8 +357,8 @@ namespace Terminal.Gui {
return true;
}
- if(e.Key == Key.End) {
- if(listview.SelectedItem != -1) {
+ if (e.Key == Key.End) {
+ if (listview.SelectedItem != -1) {
listview.MoveEnd ();
}
return true;
@@ -380,7 +395,7 @@ namespace Terminal.Gui {
private void SetValue (object text)
{
search.TextChanged -= Search_Changed;
- this.text = search.Text = text.ToString();
+ this.text = search.Text = text.ToString ();
search.CursorPosition = 0;
search.TextChanged += Search_Changed;
SelectedItem = GetSelectedItemFromSource (this.text);
@@ -472,7 +487,7 @@ namespace Terminal.Gui {
ResetSearchSet (noCopy: true);
foreach (var item in source.ToList ()) { // Iterate to preserver object type and force deep copy
- if (item.ToString().StartsWith (search.Text.ToString(), StringComparison.CurrentCultureIgnoreCase)) {
+ if (item.ToString ().StartsWith (search.Text.ToString (), StringComparison.CurrentCultureIgnoreCase)) {
searchset.Add (item);
}
}
@@ -501,9 +516,11 @@ namespace Terminal.Gui {
/// Consider making public
private void HideList ()
{
+ var rect = listview.ViewToScreen (listview.Bounds);
Reset (SelectedItem > -1);
- listview.Clear ();
+ listview.Clear (rect);
listview.TabStop = false;
+ SuperView?.SetNeedsDisplay (rect);
}
///
@@ -515,7 +532,7 @@ namespace Terminal.Gui {
if (Bounds.Height == 0)
return 0;
- return Math.Min (Math.Max(Bounds.Height - 1, minimumHeight - 1), searchset?.Count > 0 ? searchset.Count : isShow ? Math.Max (Bounds.Height - 1, minimumHeight - 1) : 0);
+ return Math.Min (Math.Max (Bounds.Height - 1, minimumHeight - 1), searchset?.Count > 0 ? searchset.Count : isShow ? Math.Max (Bounds.Height - 1, minimumHeight - 1) : 0);
}
}
}
diff --git a/Terminal.Gui/Views/FrameView.cs b/Terminal.Gui/Views/FrameView.cs
index 8f880d613..b19411f25 100644
--- a/Terminal.Gui/Views/FrameView.cs
+++ b/Terminal.Gui/Views/FrameView.cs
@@ -60,7 +60,7 @@ namespace Terminal.Gui {
///
/// Frame.
/// Title.
- /// /// Views.
+ /// Views.
public FrameView (Rect frame, ustring title, View [] views) : this (frame, title)
{
Initialize (title, frame, views);
@@ -157,7 +157,7 @@ namespace Terminal.Gui {
var scrRect = ViewToScreen (new Rect (0, 0, Frame.Width, Frame.Height));
if (!NeedDisplay.IsEmpty) {
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: true);
}
@@ -166,13 +166,13 @@ namespace Terminal.Gui {
Driver.Clip = savedClip;
ClearNeedsDisplay ();
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.DrawWindowFrame (scrRect, padding + 1, padding + 1, padding + 1, padding + 1, border: true, fill: false);
if (HasFocus)
Driver.SetAttribute (ColorScheme.HotNormal);
Driver.DrawWindowTitle (scrRect, Title, padding, padding, padding, padding);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
}
///
diff --git a/Terminal.Gui/Views/GraphView.cs b/Terminal.Gui/Views/GraphView.cs
index f012f7cd3..8a38c0e5f 100644
--- a/Terminal.Gui/Views/GraphView.cs
+++ b/Terminal.Gui/Views/GraphView.cs
@@ -99,7 +99,7 @@ namespace Terminal.Gui {
throw new Exception ($"{nameof(CellSize)} cannot be 0");
}
- SetDriverColorToGraphColor ();
+ SetDriverColorToGraphColor ();
Move (0, 0);
@@ -164,7 +164,7 @@ namespace Terminal.Gui {
///
public void SetDriverColorToGraphColor ()
{
- Driver.SetAttribute (GraphColor ?? ColorScheme.Normal);
+ Driver.SetAttribute (GraphColor ?? (GetNormalColor ()));
}
///
diff --git a/Terminal.Gui/Views/HexView.cs b/Terminal.Gui/Views/HexView.cs
index 85ea8d848..631fe10cd 100644
--- a/Terminal.Gui/Views/HexView.cs
+++ b/Terminal.Gui/Views/HexView.cs
@@ -162,7 +162,7 @@ namespace Terminal.Gui {
Driver.AddStr (string.Format ("{0:x8} ", displayStart + line * nblocks * 4));
currentAttribute = ColorScheme.HotNormal;
- SetAttribute (ColorScheme.Normal);
+ SetAttribute (GetNormalColor ());
for (int block = 0; block < nblocks; block++) {
for (int b = 0; b < 4; b++) {
@@ -172,10 +172,10 @@ namespace Terminal.Gui {
if (offset + displayStart == position || edited)
SetAttribute (leftSide ? activeColor : trackingColor);
else
- SetAttribute (ColorScheme.Normal);
+ SetAttribute (GetNormalColor ());
Driver.AddStr (offset >= n ? " " : string.Format ("{0:x2}", value));
- SetAttribute (ColorScheme.Normal);
+ SetAttribute (GetNormalColor ());
Driver.AddRune (' ');
}
Driver.AddStr (block + 1 == nblocks ? " " : "| ");
@@ -201,7 +201,7 @@ namespace Terminal.Gui {
if (offset + displayStart == position || edited)
SetAttribute (leftSide ? trackingColor : activeColor);
else
- SetAttribute (ColorScheme.Normal);
+ SetAttribute (GetNormalColor ());
Driver.AddRune (c);
}
@@ -401,12 +401,11 @@ namespace Terminal.Gui {
///
/// Get / Set the wished cursor when the field is focused
///
- public CursorVisibility DesiredCursorVisibility
- {
- get => desiredCursorVisibility;
+ public CursorVisibility DesiredCursorVisibility {
+ get => desiredCursorVisibility;
set {
if (desiredCursorVisibility != value && HasFocus) {
- Application.Driver.SetCursorVisibility (value);
+ Application.Driver.SetCursorVisibility (value);
}
desiredCursorVisibility = value;
diff --git a/Terminal.Gui/Views/Label.cs b/Terminal.Gui/Views/Label.cs
index 2c438bddd..56aaeca7d 100644
--- a/Terminal.Gui/Views/Label.cs
+++ b/Terminal.Gui/Views/Label.cs
@@ -94,8 +94,7 @@ namespace Terminal.Gui {
public override bool OnMouseEvent (MouseEvent mouseEvent)
{
MouseEventArgs args = new MouseEventArgs (mouseEvent);
- OnMouseClick (args);
- if (args.Handled)
+ if (OnMouseClick (args))
return true;
if (MouseEvent (mouseEvent))
return true;
diff --git a/Terminal.Gui/Views/LineView.cs b/Terminal.Gui/Views/LineView.cs
index a0290281b..e65b9f91c 100644
--- a/Terminal.Gui/Views/LineView.cs
+++ b/Terminal.Gui/Views/LineView.cs
@@ -73,7 +73,7 @@ namespace Terminal.Gui.Views {
base.Redraw (bounds);
Move (0, 0);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
var hLineWidth = Math.Max (1, Rune.ColumnWidth (Driver.HLine));
diff --git a/Terminal.Gui/Views/ListView.cs b/Terminal.Gui/Views/ListView.cs
index 85274cd31..fad92f4e2 100644
--- a/Terminal.Gui/Views/ListView.cs
+++ b/Terminal.Gui/Views/ListView.cs
@@ -334,7 +334,9 @@ namespace Terminal.Gui {
for (int row = 0; row < f.Height; row++, item++) {
bool isSelected = item == selected;
- var newcolor = focused ? (isSelected ? ColorScheme.Focus : ColorScheme.Normal) : (isSelected ? ColorScheme.HotNormal : ColorScheme.Normal);
+ var newcolor = focused ? (isSelected ? ColorScheme.Focus : GetNormalColor ())
+ : (isSelected ? ColorScheme.HotNormal : GetNormalColor ());
+
if (newcolor != current) {
Driver.SetAttribute (newcolor);
current = newcolor;
diff --git a/Terminal.Gui/Views/Menu.cs b/Terminal.Gui/Views/Menu.cs
index 3a4d5099e..2c77abcfb 100644
--- a/Terminal.Gui/Views/Menu.cs
+++ b/Terminal.Gui/Views/Menu.cs
@@ -426,17 +426,18 @@ namespace Terminal.Gui {
if (index == current) return ColorScheme.Focus;
if (!item.IsEnabled ()) return ColorScheme.Disabled;
}
- return ColorScheme.Normal;
+ return GetNormalColor ();
}
public override void Redraw (Rect bounds)
{
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
DrawFrame (bounds, padding: 0, fill: true);
for (int i = 0; i < barItems.Children.Length; i++) {
var item = barItems.Children [i];
- Driver.SetAttribute (item == null ? ColorScheme.Normal : i == current ? ColorScheme.Focus : ColorScheme.Normal);
+ Driver.SetAttribute (item == null ? GetNormalColor ()
+ : i == current ? ColorScheme.Focus : GetNormalColor ());
if (item == null) {
Move (0, i + 1);
Driver.AddRune (Driver.LeftTee);
@@ -482,7 +483,7 @@ namespace Terminal.Gui {
else
DrawHotString (textToDraw,
i == current ? ColorScheme.HotFocus : ColorScheme.HotNormal,
- i == current ? ColorScheme.Focus : ColorScheme.Normal);
+ i == current ? ColorScheme.Focus : GetNormalColor ());
// The help string
var l = item.ShortcutTag.RuneCount == 0 ? item.Help.RuneCount : item.Help.RuneCount + item.ShortcutTag.RuneCount + 2;
@@ -905,7 +906,7 @@ namespace Terminal.Gui {
public override void Redraw (Rect bounds)
{
Move (0, 0);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
for (int i = 0; i < Frame.Width; i++)
Driver.AddRune (' ');
@@ -918,13 +919,14 @@ namespace Terminal.Gui {
Attribute hotColor, normalColor;
if (i == selected && IsMenuOpen) {
hotColor = i == selected ? ColorScheme.HotFocus : ColorScheme.HotNormal;
- normalColor = i == selected ? ColorScheme.Focus : ColorScheme.Normal;
+ normalColor = i == selected ? ColorScheme.Focus :
+ GetNormalColor ();
} else if (openedByAltKey) {
hotColor = ColorScheme.HotNormal;
- normalColor = ColorScheme.Normal;
+ normalColor = GetNormalColor ();
} else {
- hotColor = ColorScheme.Normal;
- normalColor = ColorScheme.Normal;
+ hotColor = GetNormalColor ();
+ normalColor = GetNormalColor ();
}
DrawHotString (menu.Help.IsEmpty ? $" {menu.Title} " : $" {menu.Title} {menu.Help} ", hotColor, normalColor);
pos += 1 + menu.TitleLength + (menu.Help.Length > 0 ? menu.Help.Length + 2 : 0) + 2;
@@ -989,7 +991,7 @@ namespace Terminal.Gui {
/// Virtual method that will invoke the event if it's defined.
///
/// The current menu to be replaced.
- /// /// Returns the
+ /// Returns the
public virtual MenuOpeningEventArgs OnMenuOpening (MenuBarItem currentMenu)
{
var ev = new MenuOpeningEventArgs (currentMenu);
@@ -1175,7 +1177,7 @@ namespace Terminal.Gui {
}
LastFocused = lastFocused;
lastFocused = null;
- if (LastFocused != null) {
+ if (LastFocused != null && LastFocused.CanFocus) {
if (!reopen) {
selected = -1;
}
diff --git a/Terminal.Gui/Views/ProgressBar.cs b/Terminal.Gui/Views/ProgressBar.cs
index 7a1abb4d4..c1df8900e 100644
--- a/Terminal.Gui/Views/ProgressBar.cs
+++ b/Terminal.Gui/Views/ProgressBar.cs
@@ -279,7 +279,7 @@ namespace Terminal.Gui {
{
DrawFrame ();
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
int fWidth = GetFrameWidth ();
if (isActivity) {
diff --git a/Terminal.Gui/Views/RadioGroup.cs b/Terminal.Gui/Views/RadioGroup.cs
index a9c6cb05a..2e4a2fe66 100644
--- a/Terminal.Gui/Views/RadioGroup.cs
+++ b/Terminal.Gui/Views/RadioGroup.cs
@@ -17,7 +17,7 @@ namespace Terminal.Gui {
void Init (Rect rect, ustring [] radioLabels, int selected)
{
if (radioLabels == null) {
- this.radioLabels = new List();
+ this.radioLabels = new List ();
} else {
this.radioLabels = radioLabels.ToList ();
}
@@ -63,7 +63,8 @@ namespace Terminal.Gui {
/// The radio labels; an array of strings that can contain hotkeys using an underscore before the letter.
/// The item to be selected, the value is clamped to the number of items.
public RadioGroup (int x, int y, ustring [] radioLabels, int selected = 0) :
- this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList() : null), radioLabels, selected) { }
+ this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList () : null), radioLabels, selected)
+ { }
///
/// Gets or sets the for this .
@@ -141,7 +142,7 @@ namespace Terminal.Gui {
///
/// The radio labels.
public ustring [] RadioLabels {
- get => radioLabels.ToArray();
+ get => radioLabels.ToArray ();
set {
var prevCount = radioLabels.Count;
radioLabels = value.ToList ();
@@ -184,7 +185,7 @@ namespace Terminal.Gui {
///
public override void Redraw (Rect bounds)
{
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Clear ();
for (int i = 0; i < radioLabels.Count; i++) {
switch (DisplayMode) {
@@ -195,8 +196,8 @@ namespace Terminal.Gui {
Move (horizontal [i].pos, 0);
break;
}
- Driver.SetAttribute (ColorScheme.Normal);
- Driver.AddStr (ustring.Make(new Rune[] { (i == selected ? Driver.Selected : Driver.UnSelected), ' '}));
+ Driver.SetAttribute (GetNormalColor ());
+ Driver.AddStr (ustring.Make (new Rune [] { (i == selected ? Driver.Selected : Driver.UnSelected), ' ' }));
DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme);
}
}
@@ -222,7 +223,7 @@ namespace Terminal.Gui {
///
/// Gets the index of the item that was previously selected. -1 if there was no previous selection.
///
- public int PreviousSelectedItem { get; }
+ public int PreviousSelectedItem { get; }
///
/// Gets the index of the item that is now selected. -1 if there is no selection.
@@ -234,7 +235,7 @@ namespace Terminal.Gui {
///
///
///
- public SelectedItemChangedArgs(int selectedItem, int previousSelectedItem)
+ public SelectedItemChangedArgs (int selectedItem, int previousSelectedItem)
{
PreviousSelectedItem = previousSelectedItem;
SelectedItem = selectedItem;
diff --git a/Terminal.Gui/Views/ScrollBarView.cs b/Terminal.Gui/Views/ScrollBarView.cs
index ed08c607a..7760245eb 100644
--- a/Terminal.Gui/Views/ScrollBarView.cs
+++ b/Terminal.Gui/Views/ScrollBarView.cs
@@ -85,13 +85,22 @@ namespace Terminal.Gui {
X = isVertical ? Pos.Right (host) - 1 : Pos.Left (host);
Y = isVertical ? Pos.Top (host) : Pos.Bottom (host) - 1;
Host = host;
+ CanFocus = host.CanFocus;
+ Enabled = host.Enabled;
+ Visible = host.Visible;
+ Host.CanFocusChanged += Host_CanFocusChanged;
+ Host.EnabledChanged += Host_EnabledChanged;
+ Host.VisibleChanged += Host_VisibleChanged;
Host.SuperView.Add (this);
AutoHideScrollBars = true;
if (showBothScrollIndicator) {
OtherScrollBarView = new ScrollBarView (0, 0, !isVertical) {
ColorScheme = host.ColorScheme,
Host = host,
- OtherScrollBarView = this,
+ CanFocus = host.CanFocus,
+ Enabled = host.Enabled,
+ Visible = host.Visible,
+ OtherScrollBarView = this
};
OtherScrollBarView.hosted = true;
OtherScrollBarView.X = OtherScrollBarView.IsVertical ? Pos.Right (host) - 1 : Pos.Left (host);
@@ -100,7 +109,7 @@ namespace Terminal.Gui {
OtherScrollBarView.showScrollIndicator = true;
}
ShowScrollIndicator = true;
- contentBottomRightCorner = new View (" ");
+ contentBottomRightCorner = new View (" ") { Visible = host.Visible };
Host.SuperView.Add (contentBottomRightCorner);
contentBottomRightCorner.X = Pos.Right (host) - 1;
contentBottomRightCorner.Y = Pos.Bottom (host) - 1;
@@ -109,6 +118,36 @@ namespace Terminal.Gui {
contentBottomRightCorner.MouseClick += ContentBottomRightCorner_MouseClick;
}
+ private void Host_VisibleChanged ()
+ {
+ if (!Host.Visible) {
+ Visible = Host.Visible;
+ if (otherScrollBarView != null) {
+ otherScrollBarView.Visible = Visible;
+ }
+ contentBottomRightCorner.Visible = Visible;
+ } else {
+ ShowHideScrollBars ();
+ }
+ }
+
+ private void Host_EnabledChanged ()
+ {
+ Enabled = Host.Enabled;
+ if (otherScrollBarView != null) {
+ otherScrollBarView.Enabled = Enabled;
+ }
+ contentBottomRightCorner.Enabled = Enabled;
+ }
+
+ private void Host_CanFocusChanged ()
+ {
+ CanFocus = Host.CanFocus;
+ if (otherScrollBarView != null) {
+ otherScrollBarView.CanFocus = CanFocus;
+ }
+ }
+
void ContentBottomRightCorner_MouseClick (MouseEventArgs me)
{
if (me.MouseEvent.Flags == MouseFlags.WheeledDown || me.MouseEvent.Flags == MouseFlags.WheeledUp
@@ -322,6 +361,13 @@ namespace Terminal.Gui {
} else {
contentBottomRightCorner.Visible = false;
}
+ if (Host?.Visible == true && showScrollIndicator && !Visible) {
+ Visible = true;
+ }
+ if (Host?.Visible == true && otherScrollBarView != null && otherScrollBarView.showScrollIndicator
+ && !otherScrollBarView.Visible) {
+ otherScrollBarView.Visible = true;
+ }
if (showScrollIndicator) {
Redraw (Bounds);
}
@@ -390,7 +436,7 @@ namespace Terminal.Gui {
return;
}
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
if ((vertical && Bounds.Height == 0) || (!vertical && Bounds.Width == 0)) {
return;
@@ -549,7 +595,10 @@ namespace Terminal.Gui {
return false;
}
- if (Host != null && !Host.HasFocus) {
+ if (!Host.CanFocus) {
+ return true;
+ }
+ if (Host?.HasFocus == false) {
Host.SetFocus ();
}
diff --git a/Terminal.Gui/Views/ScrollView.cs b/Terminal.Gui/Views/ScrollView.cs
index 1842de883..943d2dbcb 100644
--- a/Terminal.Gui/Views/ScrollView.cs
+++ b/Terminal.Gui/Views/ScrollView.cs
@@ -250,7 +250,7 @@ namespace Terminal.Gui {
}
///
- /// /// Gets or sets the visibility for the vertical scroll indicator.
+ /// Gets or sets the visibility for the vertical scroll indicator.
///
/// true if show vertical scroll indicator; otherwise, false.
public bool ShowVerticalScrollIndicator {
@@ -281,7 +281,7 @@ namespace Terminal.Gui {
///
public override void Redraw (Rect region)
{
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
SetViewsNeedsDisplay ();
Clear ();
@@ -308,7 +308,7 @@ namespace Terminal.Gui {
if (ShowVerticalScrollIndicator && ShowHorizontalScrollIndicator) {
AddRune (Bounds.Width - 1, Bounds.Height - 1, ' ');
}
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
}
void ShowHideScrollBars ()
diff --git a/Terminal.Gui/Views/StatusBar.cs b/Terminal.Gui/Views/StatusBar.cs
index 0e3c3b60d..f8db8920c 100644
--- a/Terminal.Gui/Views/StatusBar.cs
+++ b/Terminal.Gui/Views/StatusBar.cs
@@ -7,6 +7,7 @@
// TODO:
// Add mouse support
using System;
+using System.Collections.Generic;
using NStack;
namespace Terminal.Gui {
@@ -82,8 +83,6 @@ namespace Terminal.Gui {
/// A list of statusbar items.
public StatusBar (StatusItem [] items) : base ()
{
- Width = Dim.Fill ();
- Height = 1;
Items = items;
CanFocus = false;
ColorScheme = Colors.Menu;
@@ -97,7 +96,17 @@ namespace Terminal.Gui {
private void StatusBar_Initialized (object sender, EventArgs e)
{
- Y = SuperView.Frame.Height - 1;
+ if (SuperView.Frame == Rect.Empty) {
+ ((Toplevel)SuperView).Loaded += StatusBar_Loaded;
+ } else {
+ Y = Math.Max (SuperView.Frame.Height - (Visible ? 1 : 0), 0);
+ }
+ }
+
+ private void StatusBar_Loaded ()
+ {
+ Y = Math.Max (SuperView.Frame.Height - (Visible ? 1 : 0), 0);
+ ((Toplevel)SuperView).Loaded -= StatusBar_Loaded;
}
private Action Application_Resized ()
@@ -106,13 +115,26 @@ namespace Terminal.Gui {
X = 0;
Height = 1;
if (SuperView != null || SuperView is Toplevel) {
- Y = SuperView.Frame.Height - (Visible ? 1 : 0);
- } else {
- //Y = Pos.Bottom (SuperView);
+ if (Frame.Y != SuperView.Frame.Height - (Visible ? 1 : 0)) {
+ Y = SuperView.Frame.Height - (Visible ? 1 : 0);
+ }
}
};
}
+ static ustring shortcutDelimiter = "-";
+ ///
+ /// Used for change the shortcut delimiter separator.
+ ///
+ public static ustring ShortcutDelimiter {
+ get => shortcutDelimiter;
+ set {
+ if (shortcutDelimiter != value) {
+ shortcutDelimiter = value == ustring.Empty ? " " : value;
+ }
+ }
+ }
+
Attribute ToggleScheme (Attribute scheme)
{
var result = scheme == ColorScheme.Normal ? ColorScheme.HotNormal : ColorScheme.Normal;
@@ -130,12 +152,12 @@ namespace Terminal.Gui {
//}
Move (0, 0);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
for (int i = 0; i < Frame.Width; i++)
Driver.AddRune (' ');
Move (1, 0);
- var scheme = ColorScheme.Normal;
+ var scheme = GetNormalColor ();
Driver.SetAttribute (scheme);
for (int i = 0; i < Items.Length; i++) {
var title = Items [i].Title.ToString ();
@@ -159,7 +181,7 @@ namespace Terminal.Gui {
{
foreach (var item in Items) {
if (kb.Key == item.Shortcut) {
- item.Action?.Invoke ();
+ Run (item.Action);
return true;
}
}
@@ -176,6 +198,7 @@ namespace Terminal.Gui {
for (int i = 0; i < Items.Length; i++) {
if (me.X >= pos && me.X < pos + GetItemTitleLength (Items [i].Title)) {
Run (Items [i].Action);
+ break;
}
pos += GetItemTitleLength (Items [i].Title) + 3;
}
@@ -223,5 +246,34 @@ namespace Terminal.Gui {
return base.OnEnter (view);
}
+
+ ///
+ /// Inserts a in the specified index of .
+ ///
+ /// The zero-based index at which item should be inserted.
+ /// The item to insert.
+ public void AddItemAt (int index, StatusItem item)
+ {
+ var itemsList = new List (Items);
+ itemsList.Insert (index, item);
+ Items = itemsList.ToArray ();
+ SetNeedsDisplay ();
+ }
+
+ ///
+ /// Removes a at specified index of .
+ ///
+ /// The zero-based index of the item to remove.
+ /// The removed.
+ public StatusItem RemoveItem (int index)
+ {
+ var itemsList = new List (Items);
+ var item = itemsList [index];
+ itemsList.RemoveAt (index);
+ Items = itemsList.ToArray ();
+ SetNeedsDisplay ();
+
+ return item;
+ }
}
}
\ No newline at end of file
diff --git a/Terminal.Gui/Views/TabView.cs b/Terminal.Gui/Views/TabView.cs
index 77cd20411..193751e4c 100644
--- a/Terminal.Gui/Views/TabView.cs
+++ b/Terminal.Gui/Views/TabView.cs
@@ -165,7 +165,7 @@ namespace Terminal.Gui {
public override void Redraw (Rect bounds)
{
Move (0, 0);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
if (Style.ShowBorder) {
@@ -486,7 +486,7 @@ namespace Terminal.Gui {
var tabLocations = host.CalculateViewport (bounds).ToArray ();
var width = bounds.Width;
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
if (host.Style.ShowTopLine) {
RenderOverline (tabLocations, width);
@@ -495,7 +495,7 @@ namespace Terminal.Gui {
RenderTabLine (tabLocations, width);
RenderUnderline (tabLocations, width);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
}
@@ -589,7 +589,7 @@ namespace Terminal.Gui {
Driver.AddStr (toRender.TextToRender);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
if (toRender.IsSelected) {
Driver.AddRune (Driver.VLine);
diff --git a/Terminal.Gui/Views/TableView.cs b/Terminal.Gui/Views/TableView.cs
index 4f8deab56..0a11ff10f 100644
--- a/Terminal.Gui/Views/TableView.cs
+++ b/Terminal.Gui/Views/TableView.cs
@@ -199,7 +199,7 @@ namespace Terminal.Gui {
// What columns to render at what X offset in viewport
var columnsToRender = CalculateViewport (bounds).ToArray ();
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
//invalidate current row (prevents scrolling around leaving old characters in the frame
Driver.AddStr (new string (' ', bounds.Width));
@@ -253,7 +253,7 @@ namespace Terminal.Gui {
private void ClearLine (int row, int width)
{
Move (0, row);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.AddStr (new string (' ', width));
}
@@ -391,7 +391,8 @@ namespace Terminal.Gui {
//start by clearing the entire line
Move (0, row);
- Driver.SetAttribute (FullRowSelect && IsSelected (0, rowToRender) ? rowScheme.HotFocus : rowScheme.Normal);
+ Driver.SetAttribute (FullRowSelect && IsSelected (0, rowToRender) ? rowScheme.HotFocus
+ : Enabled ? rowScheme.Normal : rowScheme.Disabled);
Driver.AddStr (new string (' ', Bounds.Width));
// Render cells for each visible header for the current row
@@ -431,7 +432,7 @@ namespace Terminal.Gui {
scheme = rowScheme;
}
- var cellColor = isSelectedCell ? scheme.HotFocus : scheme.Normal;
+ var cellColor = isSelectedCell ? scheme.HotFocus : Enabled ? scheme.Normal : scheme.Disabled;
var render = TruncateOrPad (val, representation, current.Width, colStyle);
@@ -442,12 +443,13 @@ namespace Terminal.Gui {
// Reset color scheme to normal for drawing separators if we drew text with custom scheme
if (scheme != rowScheme) {
- Driver.SetAttribute (isSelectedCell ? rowScheme.HotFocus : rowScheme.Normal);
+ Driver.SetAttribute (isSelectedCell ? rowScheme.HotFocus
+ : Enabled ? rowScheme.Normal : rowScheme.Disabled);
}
// If not in full row select mode always, reset color scheme to normal and render the vertical line (or space) at the end of the cell
if (!FullRowSelect)
- Driver.SetAttribute (rowScheme.Normal);
+ Driver.SetAttribute (Enabled ? rowScheme.Normal : rowScheme.Disabled);
RenderSeparator (current.X - 1, row, false);
diff --git a/Terminal.Gui/Views/TextField.cs b/Terminal.Gui/Views/TextField.cs
index fb6d5a7ab..61c2ab8b4 100644
--- a/Terminal.Gui/Views/TextField.cs
+++ b/Terminal.Gui/Views/TextField.cs
@@ -83,7 +83,7 @@ namespace Terminal.Gui {
void Initialize (ustring text, int w)
{
- Initialize ();
+ Height = 1;
if (text == null)
text = "";
@@ -96,11 +96,6 @@ namespace Terminal.Gui {
WantMousePositionReports = true;
}
- void Initialize ()
- {
- Height = 1;
- }
-
///
public override bool OnLeave (View view)
{
@@ -221,7 +216,7 @@ namespace Terminal.Gui {
int col = 0;
int width = Frame.Width + OffSetBackground ();
var tcount = text.Count;
- var roc = Colors.Menu.Disabled;
+ var roc = GetReadOnlyColor ();
for (int idx = p; idx < tcount; idx++) {
var rune = text [idx];
var cols = Rune.ColumnWidth (rune);
@@ -229,8 +224,10 @@ namespace Terminal.Gui {
Driver.SetAttribute (selColor);
} else if (ReadOnly) {
Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? selColor : roc);
- } else if (!HasFocus) {
+ } else if (!HasFocus && Enabled) {
Driver.SetAttribute (ColorScheme.Focus);
+ } else if (!Enabled) {
+ Driver.SetAttribute (roc);
} else {
Driver.SetAttribute (idx >= start && length > 0 && idx < start + length ? selColor : ColorScheme.Focus);
}
@@ -253,6 +250,14 @@ namespace Terminal.Gui {
PositionCursor ();
}
+ Attribute GetReadOnlyColor ()
+ {
+ if (ColorScheme.Disabled.Foreground == ColorScheme.Focus.Background) {
+ return new Attribute (ColorScheme.Focus.Foreground, ColorScheme.Focus.Background);
+ }
+ return new Attribute (ColorScheme.Disabled.Foreground, ColorScheme.Focus.Background);
+ }
+
void Adjust ()
{
int offB = OffSetBackground ();
diff --git a/Terminal.Gui/Views/TextView.cs b/Terminal.Gui/Views/TextView.cs
index b6084e4b9..f4da8c33b 100644
--- a/Terminal.Gui/Views/TextView.cs
+++ b/Terminal.Gui/Views/TextView.cs
@@ -303,10 +303,7 @@ namespace Terminal.Gui {
if (rune == '\t') {
size += tabWidth + 1;
}
- if (size > width) {
- if (end == t.Count) {
- col++;
- }
+ if (size >= width) {
break;
} else if (end < t.Count && col > 0 && start < end && col == start) {
break;
@@ -898,6 +895,7 @@ namespace Terminal.Gui {
bool wordWrap;
WordWrapManager wrapManager;
bool continuousFind;
+ int bottomOffset, rightOffset;
int tabWidth = 4;
bool allowsTab = true;
bool allowsReturn = true;
@@ -1128,13 +1126,31 @@ namespace Terminal.Gui {
/// The bottom offset needed to use a horizontal scrollbar or for another reason.
/// This is only needed with the keyboard navigation.
///
- public int BottomOffset { get; set; }
+ public int BottomOffset {
+ get => bottomOffset;
+ set {
+ if (currentRow == Lines - 1 && bottomOffset > 0 && value == 0) {
+ topRow = Math.Max (topRow - bottomOffset, 0);
+ }
+ bottomOffset = value;
+ Adjust ();
+ }
+ }
///
/// The right offset needed to use a vertical scrollbar or for another reason.
/// This is only needed with the keyboard navigation.
///
- public int RightOffset { get; set; }
+ public int RightOffset {
+ get => rightOffset;
+ set {
+ if (currentColumn == GetCurrentLine ().Count && rightOffset > 0 && value == 0) {
+ leftColumn = Math.Max (leftColumn - rightOffset, 0);
+ }
+ rightOffset = value;
+ Adjust ();
+ }
+ }
///
/// Gets or sets a value indicating whether pressing ENTER in a
@@ -1236,22 +1252,27 @@ namespace Terminal.Gui {
return SelectedText.Length;
}
- CursorVisibility savedCursorVisibility = CursorVisibility.Default;
+ CursorVisibility savedCursorVisibility;
void SaveCursorVisibility ()
{
if (desiredCursorVisibility != CursorVisibility.Invisible) {
- savedCursorVisibility = desiredCursorVisibility;
+ if (savedCursorVisibility == 0) {
+ savedCursorVisibility = desiredCursorVisibility;
+ }
DesiredCursorVisibility = CursorVisibility.Invisible;
}
}
void ResetCursorVisibility ()
{
- if (savedCursorVisibility != desiredCursorVisibility) {
+ if (savedCursorVisibility == 0) {
+ savedCursorVisibility = desiredCursorVisibility;
+ }
+ if (savedCursorVisibility != desiredCursorVisibility && !HasFocus) {
DesiredCursorVisibility = savedCursorVisibility;
savedCursorVisibility = CursorVisibility.Default;
- } else {
+ } else if (desiredCursorVisibility != CursorVisibility.Underline) {
DesiredCursorVisibility = CursorVisibility.Underline;
}
}
@@ -1309,6 +1330,10 @@ namespace Terminal.Gui {
///
public override void PositionCursor ()
{
+ if (!CanFocus || !Enabled) {
+ return;
+ }
+
if (selecting) {
var minRow = Math.Min (Math.Max (Math.Min (selectionStartRow, currentRow) - topRow, 0), Frame.Height);
var maxRow = Math.Min (Math.Max (Math.Max (selectionStartRow, currentRow) - topRow, 0), Frame.Height);
@@ -1325,10 +1350,13 @@ namespace Terminal.Gui {
if (line [idx] == '\t') {
cols += TabWidth + 1;
}
- TextModel.SetCol (ref col, Frame.Width, cols);
+ if (!TextModel.SetCol (ref col, Frame.Width, cols)) {
+ col = currentColumn;
+ break;
+ }
}
}
- if ((col >= leftColumn || col < Frame.Width)
+ if (col >= leftColumn && currentColumn - leftColumn + RightOffset < Frame.Width
&& topRow <= currentRow && currentRow - topRow + BottomOffset < Frame.Height) {
ResetCursorVisibility ();
Move (col, currentRow - topRow);
@@ -1351,7 +1379,7 @@ namespace Terminal.Gui {
///
protected virtual void ColorNormal ()
{
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
}
///
@@ -1363,7 +1391,7 @@ namespace Terminal.Gui {
///
protected virtual void ColorNormal (List line, int idx)
{
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
}
///
@@ -1400,6 +1428,7 @@ namespace Terminal.Gui {
get => isReadOnly;
set {
isReadOnly = value;
+ SetNeedsDisplay ();
}
}
@@ -1416,6 +1445,7 @@ namespace Terminal.Gui {
}
desiredCursorVisibility = value;
+ SetNeedsDisplay ();
}
}
@@ -1970,6 +2000,10 @@ namespace Terminal.Gui {
///
public override bool ProcessKey (KeyEvent kb)
{
+ if (!CanFocus) {
+ return true;
+ }
+
int restCount;
List rest;
diff --git a/Terminal.Gui/Views/TreeView.cs b/Terminal.Gui/Views/TreeView.cs
index ecf67c478..139178da6 100644
--- a/Terminal.Gui/Views/TreeView.cs
+++ b/Terminal.Gui/Views/TreeView.cs
@@ -377,7 +377,7 @@ namespace Terminal.Gui {
// Else clear the line to prevent stale symbols due to scrolling etc
Move (0, line);
- Driver.SetAttribute (ColorScheme.Normal);
+ Driver.SetAttribute (GetNormalColor ());
Driver.AddStr (new string (' ', bounds.Width));
}
diff --git a/Terminal.Gui/Windows/FileDialog.cs b/Terminal.Gui/Windows/FileDialog.cs
index 941f6e94f..35bb56937 100644
--- a/Terminal.Gui/Windows/FileDialog.cs
+++ b/Terminal.Gui/Windows/FileDialog.cs
@@ -19,6 +19,7 @@ namespace Terminal.Gui {
internal class DirListView : View {
int top, selected;
DirectoryInfo dirInfo;
+ FileSystemWatcher watcher;
List<(string, bool, bool)> infos;
internal bool canChooseFiles = true;
internal bool canChooseDirectories = false;
@@ -39,7 +40,7 @@ namespace Terminal.Gui {
if (allowedFileTypes == null)
return true;
foreach (var ft in allowedFileTypes)
- if (fsi.Name.EndsWith (ft))
+ if (fsi.Name.EndsWith (ft) || ft == ".*")
return true;
return false;
}
@@ -49,6 +50,21 @@ namespace Terminal.Gui {
bool valid = false;
try {
dirInfo = new DirectoryInfo (value == null ? directory.ToString () : value.ToString ());
+ watcher = new FileSystemWatcher (dirInfo.FullName);
+ watcher.NotifyFilter = NotifyFilters.Attributes
+ | NotifyFilters.CreationTime
+ | NotifyFilters.DirectoryName
+ | NotifyFilters.FileName
+ | NotifyFilters.LastAccess
+ | NotifyFilters.LastWrite
+ | NotifyFilters.Security
+ | NotifyFilters.Size;
+ watcher.Changed += Watcher_Changed;
+ watcher.Created += Watcher_Changed;
+ watcher.Deleted += Watcher_Changed;
+ watcher.Renamed += Watcher_Changed;
+ watcher.Error += Watcher_Error;
+ watcher.EnableRaisingEvents = true;
infos = (from x in dirInfo.GetFileSystemInfos ()
where IsAllowed (x) && (!canChooseFiles ? x.Attributes.HasFlag (FileAttributes.Directory) : true)
orderby (!x.Attributes.HasFlag (FileAttributes.Directory)) + x.Name
@@ -62,6 +78,7 @@ namespace Terminal.Gui {
case DirectoryNotFoundException _:
case ArgumentException _:
dirInfo = null;
+ watcher = null;
infos.Clear ();
valid = true;
break;
@@ -77,6 +94,16 @@ namespace Terminal.Gui {
return valid;
}
+ void Watcher_Error (object sender, ErrorEventArgs e)
+ {
+ Application.MainLoop.Invoke (() => Reload ());
+ }
+
+ void Watcher_Changed (object sender, FileSystemEventArgs e)
+ {
+ Application.MainLoop.Invoke (() => Reload ());
+ }
+
ustring directory;
public ustring Directory {
get => directory;
@@ -168,13 +195,11 @@ namespace Terminal.Gui {
return true;
}
- private void UnMarkAll ()
+ void UnMarkAll ()
{
- if (allowsMultipleSelection && infos.Count > 0) {
- for (int i = 0; i < infos.Count; i++) {
- if (infos [i].Item3) {
- infos [i] = (infos [i].Item1, infos [i].Item2, false);
- }
+ for (int i = 0; i < infos.Count; i++) {
+ if (infos [i].Item3) {
+ infos [i] = (infos [i].Item1, infos [i].Item2, false);
}
}
}
@@ -221,7 +246,8 @@ namespace Terminal.Gui {
for (int row = 0; row < f.Height; row++, item++) {
bool isSelected = item == selected;
Move (0, row);
- var newcolor = focused ? (isSelected ? ColorScheme.HotNormal : ColorScheme.Focus) : ColorScheme.Focus;
+ var newcolor = focused ? (isSelected ? ColorScheme.HotNormal : ColorScheme.Focus)
+ : Enabled ? ColorScheme.Focus : ColorScheme.Disabled;
if (newcolor != current) {
Driver.SetAttribute (newcolor);
current = newcolor;
@@ -250,11 +276,13 @@ namespace Terminal.Gui {
public Action DirectoryChanged { get; set; }
public Action FileChanged { get; set; }
+ string splitString = ",";
+
void OnSelectionChanged ()
{
if (allowsMultipleSelection) {
if (FilePaths.Count > 0) {
- FileChanged?.Invoke (string.Join (", ", GetFilesName (FilePaths)));
+ FileChanged?.Invoke (string.Join (splitString, GetFilesName (FilePaths)));
} else {
FileChanged?.Invoke (infos [selected].Item2 && !canChooseDirectories ? "" : Path.GetFileName (infos [selected].Item1));
}
@@ -275,6 +303,46 @@ namespace Terminal.Gui {
return filesName;
}
+ public bool GetValidFilesName (string files, out string result)
+ {
+ result = string.Empty;
+ if (infos?.Count == 0) {
+ return false;
+ }
+
+ var valid = true;
+ IReadOnlyList filesList = new List (files.Split (splitString.ToArray (), StringSplitOptions.None));
+ var filesName = new List ();
+ UnMarkAll ();
+
+ foreach (var file in filesList) {
+ if (!allowsMultipleSelection && filesName.Count > 0) {
+ break;
+ }
+ var idx = infos.IndexOf (x => x.Item1.IndexOf (file, StringComparison.OrdinalIgnoreCase) >= 0);
+ if (idx > -1 && string.Equals (infos [idx].Item1, file, StringComparison.OrdinalIgnoreCase)) {
+ if (canChooseDirectories && !canChooseFiles && !infos [idx].Item2) {
+ valid = false;
+ }
+ if (allowsMultipleSelection && !infos [idx].Item3) {
+ infos [idx] = (infos [idx].Item1, infos [idx].Item2, true);
+ }
+ if (!allowsMultipleSelection) {
+ selected = idx;
+ }
+ filesName.Add (Path.GetFileName (infos [idx].Item1));
+ } else if (idx > -1) {
+ valid = false;
+ filesName.Add (Path.GetFileName (file));
+ }
+ }
+ result = string.Join (splitString, filesName);
+ if (string.IsNullOrEmpty (result)) {
+ valid = false;
+ }
+ return valid;
+ }
+
public override bool ProcessKey (KeyEvent keyEvent)
{
switch (keyEvent.Key) {
@@ -378,7 +446,7 @@ namespace Terminal.Gui {
}
}
- internal bool ExecuteSelection ()
+ internal bool ExecuteSelection (bool navigateFolder = true)
{
if (infos.Count == 0) {
return false;
@@ -388,8 +456,11 @@ namespace Terminal.Gui {
if (isDir) {
Directory = Path.GetFullPath (Path.Combine (Path.GetFullPath (Directory.ToString ()), infos [selected].Item1));
DirectoryChanged?.Invoke (Directory);
+ if (canChooseDirectories && !navigateFolder) {
+ return true;
+ }
} else {
- FileChanged?.Invoke (infos [selected].Item1);
+ OnSelectionChanged ();
if (canChooseFiles) {
// Ensures that at least one file is selected.
if (FilePaths.Count == 0)
@@ -483,11 +554,14 @@ namespace Terminal.Gui {
Label nameFieldLabel, message, nameDirLabel;
TextField dirEntry, nameEntry;
internal DirListView dirListView;
+ ComboBox cmbAllowedTypes;
///
/// Initializes a new .
///
- public FileDialog () : this (title: string.Empty, prompt: string.Empty, nameFieldLabel: string.Empty, message: string.Empty) { }
+ public FileDialog () : this (title: string.Empty, prompt: string.Empty,
+ nameFieldLabel: string.Empty, message: string.Empty)
+ { }
///
/// Initializes a new instance of
@@ -496,8 +570,9 @@ namespace Terminal.Gui {
/// The prompt.
/// The name of the file field label..
/// The message.
- public FileDialog (ustring title, ustring prompt, ustring nameFieldLabel, ustring message)
- : this (title, prompt, ustring.Empty, nameFieldLabel, message) { }
+ /// The allowed types.
+ public FileDialog (ustring title, ustring prompt, ustring nameFieldLabel, ustring message, List allowedTypes = null)
+ : this (title, prompt, ustring.Empty, nameFieldLabel, message, allowedTypes) { }
///
/// Initializes a new instance of
@@ -505,8 +580,9 @@ namespace Terminal.Gui {
/// The title.
/// The prompt.
/// The message.
-
- public FileDialog (ustring title, ustring prompt, ustring message) : this (title, prompt, ustring.Empty, message) { }
+ /// The allowed types.
+ public FileDialog (ustring title, ustring prompt, ustring message, List allowedTypes)
+ : this (title, prompt, ustring.Empty, message, allowedTypes) { }
///
/// Initializes a new instance of
@@ -516,7 +592,9 @@ namespace Terminal.Gui {
/// The name of the directory field label.
/// The name of the file field label..
/// The message.
- public FileDialog (ustring title, ustring prompt, ustring nameDirLabel, ustring nameFieldLabel, ustring message) : base (title)//, Driver.Cols - 20, Driver.Rows - 5, null)
+ /// The allowed types.
+ public FileDialog (ustring title, ustring prompt, ustring nameDirLabel, ustring nameFieldLabel, ustring message,
+ List allowedTypes = null) : base (title)//, Driver.Cols - 20, Driver.Rows - 5, null)
{
this.message = new Label (message) {
X = 1,
@@ -550,10 +628,22 @@ namespace Terminal.Gui {
nameEntry = new TextField ("") {
X = Pos.Left (dirEntry),
Y = 3 + msgLines,
- Width = Dim.Fill () - 1
+ Width = Dim.Percent (70, true)
};
Add (this.nameFieldLabel, nameEntry);
+ cmbAllowedTypes = new ComboBox () {
+ X = Pos.Right (nameEntry) + 2,
+ Y = Pos.Top (nameEntry),
+ Width = Dim.Fill (1),
+ Height = allowedTypes != null ? allowedTypes.Count + 1 : 1,
+ Text = allowedTypes?.Count > 0 ? allowedTypes [0] : string.Empty,
+ ReadOnly = true
+ };
+ cmbAllowedTypes.SetSource (allowedTypes ?? new List ());
+ cmbAllowedTypes.OpenSelectedItem += (e) => AllowedFileTypes = cmbAllowedTypes.Text.ToString ().Split (';');
+ Add (cmbAllowedTypes);
+
dirListView = new DirListView (this) {
X = 1,
Y = 3 + msgLines + 2,
@@ -562,21 +652,40 @@ namespace Terminal.Gui {
};
DirectoryPath = Path.GetFullPath (Environment.CurrentDirectory);
Add (dirListView);
+
+ AllowedFileTypes = cmbAllowedTypes.Text.ToString ().Split (';');
dirListView.DirectoryChanged = (dir) => { nameEntry.Text = ustring.Empty; dirEntry.Text = dir; };
dirListView.FileChanged = (file) => nameEntry.Text = file == ".." ? "" : file;
dirListView.SelectedChanged = (file) => nameEntry.Text = file.Item1 == ".." ? "" : file.Item1;
this.cancel = new Button ("Cancel");
this.cancel.Clicked += () => {
- canceled = true;
- Application.RequestStop ();
+ Cancel ();
};
AddButton (cancel);
this.prompt = new Button (prompt.IsEmpty ? "Ok" : prompt) {
IsDefault = true,
- CanFocus = nameEntry.Text.IsEmpty ? false : true
+ Enabled = nameEntry.Text.IsEmpty ? false : true
};
this.prompt.Clicked += () => {
+ if (this is OpenDialog) {
+ if (!dirListView.GetValidFilesName (nameEntry.Text.ToString (), out string res)) {
+ nameEntry.Text = res;
+ dirListView.SetNeedsDisplay ();
+ return;
+ }
+ if (!dirListView.canChooseDirectories && !dirListView.ExecuteSelection (false)) {
+ return;
+ }
+ } else if (this is SaveDialog) {
+ var name = nameEntry.Text.ToString ();
+ if (FilePath.IsEmpty || name.Split (',').Length > 1) {
+ return;
+ }
+ var ext = name.EndsWith (cmbAllowedTypes.Text.ToString ())
+ ? "" : cmbAllowedTypes.Text.ToString ();
+ FilePath = Path.Combine (FilePath.ToString (), $"{name}{ext}");
+ }
canceled = false;
Application.RequestStop ();
};
@@ -584,9 +693,9 @@ namespace Terminal.Gui {
nameEntry.TextChanged += (e) => {
if (nameEntry.Text.IsEmpty) {
- this.prompt.CanFocus = false;
+ this.prompt.Enabled = false;
} else {
- this.prompt.CanFocus = true;
+ this.prompt.Enabled = true;
}
};
@@ -595,6 +704,18 @@ namespace Terminal.Gui {
// On success, we will set this to false.
canceled = true;
+
+ KeyPress += (e) => {
+ if (e.KeyEvent.Key == Key.Esc) {
+ Cancel ();
+ e.Handled = true;
+ }
+ };
+ void Cancel ()
+ {
+ canceled = true;
+ Application.RequestStop ();
+ }
}
internal bool canceled;
@@ -603,7 +724,7 @@ namespace Terminal.Gui {
public override void WillPresent ()
{
base.WillPresent ();
- //SetFocus (nameEntry);
+ dirListView.SetFocus ();
}
//protected override void Dispose (bool disposing)
@@ -689,7 +810,6 @@ namespace Terminal.Gui {
set => dirListView.AllowedFileTypes = value;
}
-
///
/// Gets or sets a value indicating whether this allows the file to be saved with a different extension
///
@@ -736,7 +856,9 @@ namespace Terminal.Gui {
///
/// The title.
/// The message.
- public SaveDialog (ustring title, ustring message) : base (title, prompt: "Save", nameFieldLabel: "Save as:", message: message) { }
+ /// The allowed types.
+ public SaveDialog (ustring title, ustring message, List allowedTypes = null)
+ : base (title, prompt: "Save", nameFieldLabel: "Save as:", message: message, allowedTypes) { }
///
/// Gets the name of the file the user selected for saving, or null
@@ -764,13 +886,33 @@ namespace Terminal.Gui {
///
/// To use, create an instance of , and pass it to
/// . This will run the dialog modally,
- /// and when this returns, the list of filds will be available on the property.
+ /// and when this returns, the list of files will be available on the property.
///
///
/// To select more than one file, users can use the spacebar, or control-t.
///
///
public class OpenDialog : FileDialog {
+ OpenMode openMode;
+
+ ///
+ /// Determine which type to open.
+ ///
+ public enum OpenMode {
+ ///
+ /// Opens only file or files.
+ ///
+ File,
+ ///
+ /// Opens only directory or directories.
+ ///
+ Directory,
+ ///
+ /// Opens files and directories.
+ ///
+ Mixed
+ }
+
///
/// Initializes a new .
///
@@ -779,10 +921,30 @@ namespace Terminal.Gui {
///
/// Initializes a new .
///
- ///
- ///
- public OpenDialog (ustring title, ustring message) : base (title, prompt: "Open", nameFieldLabel: "Open", message: message)
+ /// The title.
+ /// The message.
+ /// The allowed types.
+ /// The open mode.
+ public OpenDialog (ustring title, ustring message, List allowedTypes = null, OpenMode openMode = OpenMode.File) : base (title,
+ prompt: openMode == OpenMode.File ? "Open" : openMode == OpenMode.Directory ? "Select folder" : "Select Mixed",
+ nameFieldLabel: "Open", message: message, allowedTypes)
{
+ this.openMode = openMode;
+ switch (openMode) {
+ case OpenMode.File:
+ CanChooseFiles = true;
+ CanChooseDirectories = false;
+ break;
+ case OpenMode.Directory:
+ CanChooseFiles = false;
+ CanChooseDirectories = true;
+ break;
+ case OpenMode.Mixed:
+ CanChooseFiles = true;
+ CanChooseDirectories = true;
+ AllowsMultipleSelection = true;
+ break;
+ }
}
///
@@ -816,6 +978,9 @@ namespace Terminal.Gui {
public bool AllowsMultipleSelection {
get => dirListView.allowsMultipleSelection;
set {
+ if (!value && openMode == OpenMode.Mixed) {
+ return;
+ }
dirListView.allowsMultipleSelection = value;
dirListView.Reload ();
}
diff --git a/UICatalog/Scenarios/DynamicMenuBar.cs b/UICatalog/Scenarios/DynamicMenuBar.cs
index 54f86f96e..13918064d 100644
--- a/UICatalog/Scenarios/DynamicMenuBar.cs
+++ b/UICatalog/Scenarios/DynamicMenuBar.cs
@@ -515,10 +515,10 @@ namespace UICatalog {
_currentEditMenuBarItem = menuItem;
_frmMenuDetails.EditMenuBarItem (menuItem);
- var f = _btnOk.CanFocus == _frmMenuDetails.CanFocus;
+ var f = _btnOk.Enabled == _frmMenuDetails.Enabled;
if (!f) {
- _btnOk.CanFocus = _frmMenuDetails.CanFocus;
- _btnCancel.CanFocus = _frmMenuDetails.CanFocus;
+ _btnOk.Enabled = _frmMenuDetails.Enabled;
+ _btnCancel.Enabled = _frmMenuDetails.Enabled;
}
}
@@ -624,7 +624,7 @@ namespace UICatalog {
}
- _frmMenuDetails.Initialized += (s, e) => _frmMenuDetails.CanFocus = false;
+ //_frmMenuDetails.Initialized += (s, e) => _frmMenuDetails.Enabled = false;
}
}
@@ -790,19 +790,19 @@ namespace UICatalog {
if (_ckbIsTopLevel.Checked) {
_ckbSubMenu.Checked = false;
_ckbSubMenu.SetNeedsDisplay ();
- _txtHelp.CanFocus = true;
- _txtAction.CanFocus = true;
- _txtShortcut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
+ _txtHelp.Enabled = true;
+ _txtAction.Enabled = true;
+ _txtShortcut.Enabled = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
} else {
if (_menuItem == null && !hasParent || _menuItem.Parent == null) {
_ckbSubMenu.Checked = true;
_ckbSubMenu.SetNeedsDisplay ();
- _txtShortcut.CanFocus = false;
+ _txtShortcut.Enabled = false;
}
_txtHelp.Text = "";
- _txtHelp.CanFocus = false;
+ _txtHelp.Enabled = false;
_txtAction.Text = "";
- _txtAction.CanFocus = false;
+ _txtAction.Enabled = false;
}
};
_ckbSubMenu.Toggled += (e) => {
@@ -810,20 +810,20 @@ namespace UICatalog {
_ckbIsTopLevel.Checked = false;
_ckbIsTopLevel.SetNeedsDisplay ();
_txtHelp.Text = "";
- _txtHelp.CanFocus = false;
+ _txtHelp.Enabled = false;
_txtAction.Text = "";
- _txtAction.CanFocus = false;
+ _txtAction.Enabled = false;
_txtShortcut.Text = "";
- _txtShortcut.CanFocus = false;
+ _txtShortcut.Enabled = false;
} else {
if (!hasParent) {
_ckbIsTopLevel.Checked = true;
_ckbIsTopLevel.SetNeedsDisplay ();
- _txtShortcut.CanFocus = false;
+ _txtShortcut.Enabled = false;
}
- _txtHelp.CanFocus = true;
- _txtAction.CanFocus = true;
- _txtShortcut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
+ _txtHelp.Enabled = true;
+ _txtAction.Enabled = true;
+ _txtShortcut.Enabled = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
}
};
@@ -843,9 +843,9 @@ namespace UICatalog {
_txtAction.Text = m.action;
_ckbIsTopLevel.Checked = false;
_ckbSubMenu.Checked = !hasParent;
- _txtHelp.CanFocus = hasParent;
- _txtAction.CanFocus = hasParent;
- _txtShortcut.CanFocus = hasParent;
+ _txtHelp.Enabled = hasParent;
+ _txtAction.Enabled = hasParent;
+ _txtShortcut.Enabled = hasParent;
} else {
EditMenuBarItem (_menuItem);
}
@@ -891,12 +891,12 @@ namespace UICatalog {
{
if (menuItem == null) {
hasParent = false;
- CanFocus = false;
+ Enabled = false;
CleanEditMenuBarItem ();
return;
} else {
hasParent = menuItem.Parent != null;
- CanFocus = true;
+ Enabled = true;
}
_menuItem = menuItem;
_txtTitle.Text = menuItem?.Title ?? "";
@@ -904,11 +904,11 @@ namespace UICatalog {
_txtAction.Text = menuItem != null && menuItem.Action != null ? GetTargetAction (menuItem.Action) : ustring.Empty;
_ckbIsTopLevel.Checked = IsTopLevel (menuItem);
_ckbSubMenu.Checked = HasSubMenus (menuItem);
- _txtHelp.CanFocus = !_ckbSubMenu.Checked;
- _txtAction.CanFocus = !_ckbSubMenu.Checked;
+ _txtHelp.Enabled = !_ckbSubMenu.Checked;
+ _txtAction.Enabled = !_ckbSubMenu.Checked;
_rbChkStyle.SelectedItem = (int)(menuItem?.CheckType ?? MenuItemCheckStyle.NoCheck);
_txtShortcut.Text = menuItem?.ShortcutTag ?? "";
- _txtShortcut.CanFocus = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
+ _txtShortcut.Enabled = !_ckbIsTopLevel.Checked && !_ckbSubMenu.Checked;
}
void CleanEditMenuBarItem ()
diff --git a/UICatalog/Scenarios/DynamicStatusBar.cs b/UICatalog/Scenarios/DynamicStatusBar.cs
new file mode 100644
index 000000000..be4f960af
--- /dev/null
+++ b/UICatalog/Scenarios/DynamicStatusBar.cs
@@ -0,0 +1,594 @@
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Runtime.CompilerServices;
+using Terminal.Gui;
+
+namespace UICatalog {
+ [ScenarioMetadata (Name: "Dynamic StatusBar", Description: "Demonstrates how to add and remove a StatusBar and change items dynamically.")]
+ [ScenarioCategory ("Dynamic")]
+ class DynamicStatusBar : Scenario {
+ public override void Run ()
+ {
+ Top.Add (new DynamicStatusBarSample (Win.Title));
+ base.Run ();
+ }
+ }
+
+ public class DynamicStatusItemList {
+ public ustring Title { get; set; }
+ public StatusItem StatusItem { get; set; }
+
+ public DynamicStatusItemList () { }
+
+ public DynamicStatusItemList (ustring title, StatusItem statusItem)
+ {
+ Title = title;
+ StatusItem = statusItem;
+ }
+
+ public override string ToString () => $"{Title}, {StatusItem}";
+ }
+
+ public class DynamicStatusItem {
+ public ustring title = "New";
+ public ustring action = "";
+ public ustring shortcut;
+
+ public DynamicStatusItem () { }
+
+ public DynamicStatusItem (ustring title)
+ {
+ this.title = title;
+ }
+
+ public DynamicStatusItem (ustring title, ustring action, ustring shortcut = null)
+ {
+ this.title = title;
+ this.action = action;
+ this.shortcut = shortcut;
+ }
+ }
+
+ public class DynamicStatusBarSample : Window {
+ StatusBar _statusBar;
+ StatusItem _currentStatusItem;
+ int _currentSelectedStatusBar = -1;
+ StatusItem _currentEditStatusItem;
+ ListView _lstItems;
+
+ public DynamicStatusItemModel DataContext { get; set; }
+
+ public DynamicStatusBarSample (ustring title) : base (title)
+ {
+ DataContext = new DynamicStatusItemModel ();
+
+ var _frmDelimiter = new FrameView ("Shortcut Delimiter:") {
+ X = Pos.Center (),
+ Y = 0,
+ Width = 25,
+ Height = 4
+ };
+
+ var _txtDelimiter = new TextField (StatusBar.ShortcutDelimiter.ToString ()) {
+ X = Pos.Center (),
+ Width = 2,
+ };
+ _txtDelimiter.TextChanged += (_) => StatusBar.ShortcutDelimiter = _txtDelimiter.Text;
+ _frmDelimiter.Add (_txtDelimiter);
+
+ Add (_frmDelimiter);
+
+ var _frmStatusBar = new FrameView ("Items:") {
+ Y = 5,
+ Width = Dim.Percent (50),
+ Height = Dim.Fill (2)
+ };
+
+ var _btnAddStatusBar = new Button ("Add a StatusBar") {
+ Y = 1,
+ };
+ _frmStatusBar.Add (_btnAddStatusBar);
+
+ var _btnRemoveStatusBar = new Button ("Remove a StatusBar") {
+ Y = 1
+ };
+ _btnRemoveStatusBar.X = Pos.AnchorEnd () - (Pos.Right (_btnRemoveStatusBar) - Pos.Left (_btnRemoveStatusBar));
+ _frmStatusBar.Add (_btnRemoveStatusBar);
+
+ var _btnAdd = new Button (" Add ") {
+ Y = Pos.Top (_btnRemoveStatusBar) + 2,
+ };
+ _btnAdd.X = Pos.AnchorEnd () - (Pos.Right (_btnAdd) - Pos.Left (_btnAdd));
+ _frmStatusBar.Add (_btnAdd);
+
+ _lstItems = new ListView (new List ()) {
+ ColorScheme = Colors.Dialog,
+ Y = Pos.Top (_btnAddStatusBar) + 2,
+ Width = Dim.Fill () - Dim.Width (_btnAdd) - 1,
+ Height = Dim.Fill (),
+ };
+ _frmStatusBar.Add (_lstItems);
+
+ var _btnRemove = new Button ("Remove") {
+ X = Pos.Left (_btnAdd),
+ Y = Pos.Top (_btnAdd) + 1
+ };
+ _frmStatusBar.Add (_btnRemove);
+
+ var _btnUp = new Button ("^") {
+ X = Pos.Right (_lstItems) + 2,
+ Y = Pos.Top (_btnRemove) + 2
+ };
+ _frmStatusBar.Add (_btnUp);
+
+ var _btnDown = new Button ("v") {
+ X = Pos.Right (_lstItems) + 2,
+ Y = Pos.Top (_btnUp) + 1
+ };
+ _frmStatusBar.Add (_btnDown);
+
+ Add (_frmStatusBar);
+
+
+ var _frmStatusBarDetails = new DynamicStatusBarDetails ("StatusBar Item Details:") {
+ X = Pos.Right (_frmStatusBar),
+ Y = Pos.Top (_frmStatusBar),
+ Width = Dim.Fill (),
+ Height = Dim.Fill (4)
+ };
+ Add (_frmStatusBarDetails);
+
+ _btnUp.Clicked += () => {
+ var i = _lstItems.SelectedItem;
+ var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
+ if (statusItem != null) {
+ var items = _statusBar.Items;
+ if (i > 0) {
+ items [i] = items [i - 1];
+ items [i - 1] = statusItem;
+ DataContext.Items [i] = DataContext.Items [i - 1];
+ DataContext.Items [i - 1] = new DynamicStatusItemList (statusItem.Title, statusItem);
+ _lstItems.SelectedItem = i - 1;
+ _statusBar.SetNeedsDisplay ();
+ }
+ }
+ };
+
+ _btnDown.Clicked += () => {
+ var i = _lstItems.SelectedItem;
+ var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [i].StatusItem : null;
+ if (statusItem != null) {
+ var items = _statusBar.Items;
+ if (i < items.Length - 1) {
+ items [i] = items [i + 1];
+ items [i + 1] = statusItem;
+ DataContext.Items [i] = DataContext.Items [i + 1];
+ DataContext.Items [i + 1] = new DynamicStatusItemList (statusItem.Title, statusItem);
+ _lstItems.SelectedItem = i + 1;
+ _statusBar.SetNeedsDisplay ();
+ }
+ }
+ };
+
+ var _btnOk = new Button ("Ok") {
+ X = Pos.Right (_frmStatusBar) + 20,
+ Y = Pos.Bottom (_frmStatusBarDetails),
+ };
+ Add (_btnOk);
+
+ var _btnCancel = new Button ("Cancel") {
+ X = Pos.Right (_btnOk) + 3,
+ Y = Pos.Top (_btnOk),
+ };
+ _btnCancel.Clicked += () => {
+ SetFrameDetails (_currentEditStatusItem);
+ };
+ Add (_btnCancel);
+
+ _lstItems.SelectedItemChanged += (e) => {
+ SetFrameDetails ();
+ };
+
+ _btnOk.Clicked += () => {
+ if (ustring.IsNullOrEmpty (_frmStatusBarDetails._txtTitle.Text) && _currentEditStatusItem != null) {
+ MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
+ } else if (_currentEditStatusItem != null) {
+ _frmStatusBarDetails._txtTitle.Text = SetTitleText (
+ _frmStatusBarDetails._txtTitle.Text, _frmStatusBarDetails._txtShortcut.Text);
+ var statusItem = new DynamicStatusItem (_frmStatusBarDetails._txtTitle.Text,
+ _frmStatusBarDetails._txtAction.Text,
+ _frmStatusBarDetails._txtShortcut.Text);
+ UpdateStatusItem (_currentEditStatusItem, statusItem, _lstItems.SelectedItem);
+ }
+ };
+
+ _btnAdd.Clicked += () => {
+ if (StatusBar == null) {
+ MessageBox.ErrorQuery ("StatusBar Bar Error", "Must add a StatusBar first!", "Ok");
+ _btnAddStatusBar.SetFocus ();
+ return;
+ }
+
+ var frameDetails = new DynamicStatusBarDetails ();
+ var item = frameDetails.EnterStatusItem ();
+ if (item == null) {
+ return;
+ }
+
+ StatusItem newStatusItem = CreateNewStatusBar (item);
+ _currentSelectedStatusBar++;
+ _statusBar.AddItemAt (_currentSelectedStatusBar, newStatusItem);
+ DataContext.Items.Add (new DynamicStatusItemList (newStatusItem.Title, newStatusItem));
+ _lstItems.MoveDown ();
+ };
+
+ _btnRemove.Clicked += () => {
+ var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
+ if (statusItem != null) {
+ _statusBar.RemoveItem (_currentSelectedStatusBar);
+ DataContext.Items.RemoveAt (_lstItems.SelectedItem);
+ if (_lstItems.Source.Count > 0 && _lstItems.SelectedItem > _lstItems.Source.Count - 1) {
+ _lstItems.SelectedItem = _lstItems.Source.Count - 1;
+ }
+ _lstItems.SetNeedsDisplay ();
+ SetFrameDetails ();
+ }
+ };
+
+ _lstItems.Enter += (_) => {
+ var statusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
+ SetFrameDetails (statusItem);
+ };
+
+ _btnAddStatusBar.Clicked += () => {
+ if (_statusBar != null) {
+ return;
+ }
+
+ _statusBar = new StatusBar ();
+ Add (_statusBar);
+ };
+
+ _btnRemoveStatusBar.Clicked += () => {
+ if (_statusBar == null) {
+ return;
+ }
+
+ Remove (_statusBar);
+ _statusBar = null;
+ DataContext.Items = new List ();
+ _currentStatusItem = null;
+ _currentSelectedStatusBar = -1;
+ SetListViewSource (_currentStatusItem, true);
+ SetFrameDetails (null);
+ };
+
+
+ SetFrameDetails ();
+
+
+ var ustringConverter = new UStringValueConverter ();
+ var listWrapperConverter = new ListWrapperConverter ();
+
+ var lstItems = new Binding (this, "Items", _lstItems, "Source", listWrapperConverter);
+
+
+ void SetFrameDetails (StatusItem statusItem = null)
+ {
+ StatusItem newStatusItem;
+
+ if (statusItem == null) {
+ newStatusItem = DataContext.Items.Count > 0 ? DataContext.Items [_lstItems.SelectedItem].StatusItem : null;
+ } else {
+ newStatusItem = statusItem;
+ }
+
+ _currentEditStatusItem = newStatusItem;
+ _frmStatusBarDetails.EditStatusItem (newStatusItem);
+ var f = _btnOk.Enabled == _frmStatusBarDetails.Enabled;
+ if (!f) {
+ _btnOk.Enabled = _frmStatusBarDetails.Enabled;
+ _btnCancel.Enabled = _frmStatusBarDetails.Enabled;
+ }
+ }
+
+ void SetListViewSource (StatusItem _currentStatusItem, bool fill = false)
+ {
+ DataContext.Items = new List ();
+ var statusItem = _currentStatusItem;
+ if (!fill) {
+ return;
+ }
+ if (statusItem != null) {
+ foreach (var si in _statusBar.Items) {
+ DataContext.Items.Add (new DynamicStatusItemList (si.Title, si));
+ }
+ }
+ }
+
+ StatusItem CreateNewStatusBar (DynamicStatusItem item)
+ {
+ StatusItem newStatusItem;
+ newStatusItem = new StatusItem (ShortcutHelper.GetShortcutFromTag (
+ item.shortcut, StatusBar.ShortcutDelimiter),
+ item.title, _frmStatusBarDetails.CreateAction (item));
+
+ return newStatusItem;
+ }
+
+ void UpdateStatusItem (StatusItem _currentEditStatusItem, DynamicStatusItem statusItem, int index)
+ {
+ _currentEditStatusItem = CreateNewStatusBar (statusItem);
+ _statusBar.Items [index] = _currentEditStatusItem;
+ if (DataContext.Items.Count == 0) {
+ DataContext.Items.Add (new DynamicStatusItemList (_currentEditStatusItem.Title, _currentEditStatusItem));
+ }
+ DataContext.Items [index] = new DynamicStatusItemList (_currentEditStatusItem.Title, _currentEditStatusItem);
+ SetFrameDetails (_currentEditStatusItem);
+ }
+
+
+ //_frmStatusBarDetails.Initialized += (s, e) => _frmStatusBarDetails.Enabled = false;
+ }
+
+ public static ustring SetTitleText (ustring title, ustring shortcut)
+ {
+ var txt = title;
+ var split = title.ToString ().Split ('~');
+ if (split.Length > 1) {
+ txt = split [2].Trim (); ;
+ }
+ if (string.IsNullOrEmpty (shortcut.ToString ())) {
+ return txt;
+ }
+
+ return $"~{shortcut}~ {txt}";
+ }
+ }
+
+ public class DynamicStatusBarDetails : FrameView {
+ public StatusItem _statusItem;
+ public TextField _txtTitle;
+ public TextView _txtAction;
+ public TextField _txtShortcut;
+
+ public DynamicStatusBarDetails (StatusItem statusItem = null) : this (statusItem == null ? "Adding New StatusBar Item." : "Editing StatusBar Item.")
+ {
+ _statusItem = statusItem;
+ }
+
+ public DynamicStatusBarDetails (ustring title) : base (title)
+ {
+ var _lblTitle = new Label ("Title:") {
+ Y = 1
+ };
+ Add (_lblTitle);
+
+ _txtTitle = new TextField () {
+ X = Pos.Right (_lblTitle) + 4,
+ Y = Pos.Top (_lblTitle),
+ Width = Dim.Fill ()
+ };
+ Add (_txtTitle);
+
+ var _lblAction = new Label ("Action:") {
+ X = Pos.Left (_lblTitle),
+ Y = Pos.Bottom (_lblTitle) + 1
+ };
+ Add (_lblAction);
+
+ _txtAction = new TextView () {
+ ColorScheme = Colors.Dialog,
+ X = Pos.Left (_txtTitle),
+ Y = Pos.Top (_lblAction),
+ Width = Dim.Fill (),
+ Height = 5
+ };
+ Add (_txtAction);
+
+ var _lblShortcut = new Label ("Shortcut:") {
+ X = Pos.Left (_lblTitle),
+ Y = Pos.Bottom (_txtAction) + 1
+ };
+ Add (_lblShortcut);
+
+ _txtShortcut = new TextField () {
+ X = Pos.X (_txtAction),
+ Y = Pos.Y (_lblShortcut),
+ Width = Dim.Fill (),
+ ReadOnly = true
+ };
+ _txtShortcut.KeyDown += (e) => {
+ if (!ProcessKey (e.KeyEvent)) {
+ return;
+ }
+
+ var k = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+ if (CheckShortcut (k, true)) {
+ e.Handled = true;
+ }
+ };
+
+ bool ProcessKey (KeyEvent ev)
+ {
+ switch (ev.Key) {
+ case Key.CursorUp:
+ case Key.CursorDown:
+ case Key.Tab:
+ case Key.BackTab:
+ return false;
+ }
+
+ return true;
+ }
+
+ bool CheckShortcut (Key k, bool pre)
+ {
+ var m = _statusItem != null ? _statusItem : new StatusItem (k, "", null);
+ if (pre && !ShortcutHelper.PreShortcutValidation (k)) {
+ _txtShortcut.Text = "";
+ return false;
+ }
+ if (!pre) {
+ if (!ShortcutHelper.PostShortcutValidation (ShortcutHelper.GetShortcutFromTag (
+ _txtShortcut.Text, StatusBar.ShortcutDelimiter))) {
+ _txtShortcut.Text = "";
+ return false;
+ }
+ return true;
+ }
+ _txtShortcut.Text = ShortcutHelper.GetShortcutTag (k, StatusBar.ShortcutDelimiter);
+
+ return true;
+ }
+
+ _txtShortcut.KeyUp += (e) => {
+ var k = ShortcutHelper.GetModifiersKey (e.KeyEvent);
+ if (CheckShortcut (k, false)) {
+ e.Handled = true;
+ }
+ };
+ Add (_txtShortcut);
+
+ var _btnShortcut = new Button ("Clear Shortcut") {
+ X = Pos.X (_lblShortcut),
+ Y = Pos.Bottom (_txtShortcut) + 1
+ };
+ _btnShortcut.Clicked += () => {
+ _txtShortcut.Text = "";
+ };
+ Add (_btnShortcut);
+ }
+
+
+ public DynamicStatusItem EnterStatusItem ()
+ {
+ var valid = false;
+
+ if (_statusItem == null) {
+ var m = new DynamicStatusItem ();
+ _txtTitle.Text = m.title;
+ _txtAction.Text = m.action;
+ } else {
+ EditStatusItem (_statusItem);
+ }
+
+ var _btnOk = new Button ("Ok") {
+ IsDefault = true,
+ };
+ _btnOk.Clicked += () => {
+ if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
+ MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
+ } else {
+ if (!ustring.IsNullOrEmpty (_txtShortcut.Text)) {
+ _txtTitle.Text = DynamicStatusBarSample.SetTitleText (
+ _txtTitle.Text, _txtShortcut.Text);
+ }
+ valid = true;
+ Application.RequestStop ();
+ }
+ };
+ var _btnCancel = new Button ("Cancel");
+ _btnCancel.Clicked += () => {
+ _txtTitle.Text = ustring.Empty;
+ Application.RequestStop ();
+ };
+ var _dialog = new Dialog ("Please enter the item details.", _btnOk, _btnCancel);
+
+ Width = Dim.Fill ();
+ Height = Dim.Fill () - 1;
+ _dialog.Add (this);
+ _txtTitle.SetFocus ();
+ _txtTitle.CursorPosition = _txtTitle.Text.Length;
+ Application.Run (_dialog);
+
+ if (valid) {
+ return new DynamicStatusItem (_txtTitle.Text, _txtAction.Text, _txtShortcut.Text);
+ } else {
+ return null;
+ }
+ }
+
+ public void EditStatusItem (StatusItem statusItem)
+ {
+ if (statusItem == null) {
+ Enabled = false;
+ CleanEditStatusItem ();
+ return;
+ } else {
+ Enabled = true;
+ }
+ _statusItem = statusItem;
+ _txtTitle.Text = statusItem?.Title ?? "";
+ _txtAction.Text = statusItem != null && statusItem.Action != null ? GetTargetAction (statusItem.Action) : ustring.Empty;
+ _txtShortcut.Text = ShortcutHelper.GetShortcutTag (statusItem.Shortcut, StatusBar.ShortcutDelimiter) ?? "";
+ }
+
+ void CleanEditStatusItem ()
+ {
+ _txtTitle.Text = "";
+ _txtAction.Text = "";
+ _txtShortcut.Text = "";
+ }
+
+ ustring GetTargetAction (Action action)
+ {
+ var me = action.Target;
+
+ if (me == null) {
+ throw new ArgumentException ();
+ }
+ object v = new object ();
+ foreach (var field in me.GetType ().GetFields ()) {
+ if (field.Name == "item") {
+ v = field.GetValue (me);
+ }
+ }
+ return v == null || !(v is DynamicStatusItem item) ? ustring.Empty : item.action;
+ }
+
+ public Action CreateAction (DynamicStatusItem item)
+ {
+ return new Action (() => MessageBox.ErrorQuery (item.title, item.action, "Ok"));
+ }
+ }
+
+ public class DynamicStatusItemModel : INotifyPropertyChanged {
+ public event PropertyChangedEventHandler PropertyChanged;
+
+ private ustring statusBar;
+ private List items;
+
+ public ustring StatusBar {
+ get => statusBar;
+ set {
+ if (value != statusBar) {
+ statusBar = value;
+ PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (GetPropertyName ()));
+ }
+ }
+ }
+
+ public List Items {
+ get => items;
+ set {
+ if (value != items) {
+ items = value;
+ PropertyChanged?.Invoke (this, new PropertyChangedEventArgs (GetPropertyName ()));
+ }
+ }
+ }
+
+ public DynamicStatusItemModel ()
+ {
+ Items = new List ();
+ }
+
+ public string GetPropertyName ([CallerMemberName] string propertyName = null)
+ {
+ return propertyName;
+ }
+ }
+}
diff --git a/UICatalog/Scenarios/Editor.cs b/UICatalog/Scenarios/Editor.cs
index 045a478bc..3cb550c51 100644
--- a/UICatalog/Scenarios/Editor.cs
+++ b/UICatalog/Scenarios/Editor.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections.Generic;
using System.Text;
using Terminal.Gui;
@@ -19,7 +20,7 @@ namespace UICatalog {
private string _textToReplace;
private bool _matchCase;
private bool _matchWholeWord;
- Window winDialog;
+ private Window winDialog;
public override void Init (Toplevel top, ColorScheme colorScheme)
{
@@ -29,6 +30,30 @@ namespace UICatalog {
Top = Application.Top;
}
+ Win = new Window (_fileName ?? "Untitled") {
+ X = 0,
+ Y = 1,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ ColorScheme = colorScheme,
+ };
+ Top.Add (Win);
+
+ _textView = new TextView () {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill (),
+ BottomOffset = 1,
+ RightOffset = 1
+ };
+
+ CreateDemoFile (_fileName);
+
+ LoadFile ();
+
+ Win.Add (_textView);
+
var menu = new MenuBar (new MenuBarItem [] {
new MenuBarItem ("_File", new MenuItem [] {
new MenuItem ("_New", "", () => New()),
@@ -71,42 +96,24 @@ namespace UICatalog {
new MenuBarItem ("Forma_t", new MenuItem [] {
CreateWrapChecked (),
CreateAllowsTabChecked ()
- })
+ }),
+ new MenuBarItem ("_Responder", new MenuItem [] {
+ CreateCanFocusChecked (),
+ CreateEnabledChecked (),
+ CreateVisibleChecked ()
+ }),
});
Top.Add (menu);
var statusBar = new StatusBar (new StatusItem [] {
new StatusItem(Key.F2, "~F2~ Open", () => Open()),
new StatusItem(Key.F3, "~F3~ Save", () => Save()),
+ new StatusItem(Key.F4, "~F4~ Save As", () => SaveAs()),
new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Quit()),
new StatusItem(Key.Null, $"OS Clipboard IsSupported : {Clipboard.IsSupported}", null)
});
Top.Add (statusBar);
- CreateDemoFile (_fileName);
-
- Win = new Window (_fileName ?? "Untitled") {
- X = 0,
- Y = 1,
- Width = Dim.Fill (),
- Height = Dim.Fill (),
- ColorScheme = colorScheme,
- };
- Top.Add (Win);
-
- _textView = new TextView () {
- X = 0,
- Y = 0,
- Width = Dim.Fill (),
- Height = Dim.Fill (),
- BottomOffset = 1,
- RightOffset = 1
- };
-
- LoadFile ();
-
- Win.Add (_textView);
-
_scrollBar = new ScrollBarView (_textView, true);
_scrollBar.ChangedPosition += () => {
@@ -125,6 +132,22 @@ namespace UICatalog {
_textView.SetNeedsDisplay ();
};
+ _scrollBar.VisibleChanged += () => {
+ if (_scrollBar.Visible && _textView.RightOffset == 0) {
+ _textView.RightOffset = 1;
+ } else if (!_scrollBar.Visible && _textView.RightOffset == 1) {
+ _textView.RightOffset = 0;
+ }
+ };
+
+ _scrollBar.OtherScrollBarView.VisibleChanged += () => {
+ if (_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 0) {
+ _textView.BottomOffset = 1;
+ } else if (!_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 1) {
+ _textView.BottomOffset = 0;
+ }
+ };
+
_textView.DrawContent += (e) => {
_scrollBar.Size = _textView.Lines;
_scrollBar.Position = _textView.TopRow;
@@ -315,8 +338,8 @@ namespace UICatalog {
if (!CanCloseFile ()) {
return;
}
-
- var d = new OpenDialog ("Open", "Open a file") { AllowsMultipleSelection = false };
+ var aTypes = new List () { ".txt;.bin;.xml;.json", ".txt", ".bin", ".xml", ".*" };
+ var d = new OpenDialog ("Open", "Choose the path where to open the file.", aTypes) { AllowsMultipleSelection = false };
Application.Run (d);
if (!d.Canceled && d.FilePaths.Count > 0) {
@@ -338,7 +361,8 @@ namespace UICatalog {
private bool SaveAs ()
{
- var sd = new SaveDialog ("Save file", "Choose the path where to save the file.");
+ var aTypes = new List () { ".txt", ".bin", ".xml", ".*" };
+ var sd = new SaveDialog ("Save file", "Choose the path where to save the file.", aTypes);
sd.FilePath = System.IO.Path.Combine (sd.FilePath.ToString (), Win.Title.ToString ());
Application.Run (sd);
@@ -433,7 +457,7 @@ namespace UICatalog {
Title = "Word Wrap"
};
item.CheckType |= MenuItemCheckStyle.Checked;
- item.Checked = false;
+ item.Checked = _textView.WordWrap;
item.Action += () => {
_textView.WordWrap = item.Checked = !item.Checked;
if (_textView.WordWrap) {
@@ -455,7 +479,7 @@ namespace UICatalog {
Title = "Allows Tab"
};
item.CheckType |= MenuItemCheckStyle.Checked;
- item.Checked = true;
+ item.Checked = _textView.AllowsTab;
item.Action += () => {
_textView.AllowsTab = item.Checked = !item.Checked;
};
@@ -463,6 +487,57 @@ namespace UICatalog {
return item;
}
+ private MenuItem CreateCanFocusChecked ()
+ {
+ var item = new MenuItem {
+ Title = "CanFocus"
+ };
+ item.CheckType |= MenuItemCheckStyle.Checked;
+ item.Checked = _textView.CanFocus;
+ item.Action += () => {
+ _textView.CanFocus = item.Checked = !item.Checked;
+ if (_textView.CanFocus) {
+ _textView.SetFocus ();
+ }
+ };
+
+ return item;
+ }
+
+ private MenuItem CreateEnabledChecked ()
+ {
+ var item = new MenuItem {
+ Title = "Enabled"
+ };
+ item.CheckType |= MenuItemCheckStyle.Checked;
+ item.Checked = _textView.Enabled;
+ item.Action += () => {
+ _textView.Enabled = item.Checked = !item.Checked;
+ if (_textView.Enabled) {
+ _textView.SetFocus ();
+ }
+ };
+
+ return item;
+ }
+
+ private MenuItem CreateVisibleChecked ()
+ {
+ var item = new MenuItem {
+ Title = "Visible"
+ };
+ item.CheckType |= MenuItemCheckStyle.Checked;
+ item.Checked = _textView.Visible;
+ item.Action += () => {
+ _textView.Visible = item.Checked = !item.Checked;
+ if (_textView.Visible) {
+ _textView.SetFocus ();
+ }
+ };
+
+ return item;
+ }
+
private void CreateFindReplace (bool isFind = true)
{
winDialog = new Window (isFind ? "Find" : "Replace") {
@@ -538,7 +613,7 @@ namespace UICatalog {
X = Pos.Right (txtToFind) + 1,
Y = Pos.Top (label),
Width = 20,
- CanFocus = !txtToFind.Text.IsEmpty,
+ Enabled = !txtToFind.Text.IsEmpty,
TextAlignment = TextAlignment.Centered,
IsDefault = true
};
@@ -549,7 +624,7 @@ namespace UICatalog {
X = Pos.Right (txtToFind) + 1,
Y = Pos.Top (btnFindNext) + 1,
Width = 20,
- CanFocus = !txtToFind.Text.IsEmpty,
+ Enabled = !txtToFind.Text.IsEmpty,
TextAlignment = TextAlignment.Centered
};
btnFindPrevious.Clicked += () => FindPrevious ();
@@ -558,8 +633,8 @@ namespace UICatalog {
txtToFind.TextChanged += (e) => {
_textToFind = txtToFind.Text.ToString ();
_textView.FindTextChanged ();
- btnFindNext.CanFocus = !txtToFind.Text.IsEmpty;
- btnFindPrevious.CanFocus = !txtToFind.Text.IsEmpty;
+ btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
+ btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
};
var btnCancel = new Button ("Cancel") {
@@ -631,7 +706,7 @@ namespace UICatalog {
X = Pos.Right (txtToFind) + 1,
Y = Pos.Top (label),
Width = 20,
- CanFocus = !txtToFind.Text.IsEmpty,
+ Enabled = !txtToFind.Text.IsEmpty,
TextAlignment = TextAlignment.Centered,
IsDefault = true
};
@@ -659,7 +734,7 @@ namespace UICatalog {
X = Pos.Right (txtToFind) + 1,
Y = Pos.Top (btnFindNext) + 1,
Width = 20,
- CanFocus = !txtToFind.Text.IsEmpty,
+ Enabled = !txtToFind.Text.IsEmpty,
TextAlignment = TextAlignment.Centered
};
btnFindPrevious.Clicked += () => ReplacePrevious ();
@@ -669,7 +744,7 @@ namespace UICatalog {
X = Pos.Right (txtToFind) + 1,
Y = Pos.Top (btnFindPrevious) + 1,
Width = 20,
- CanFocus = !txtToFind.Text.IsEmpty,
+ Enabled = !txtToFind.Text.IsEmpty,
TextAlignment = TextAlignment.Centered
};
btnReplaceAll.Clicked += () => ReplaceAll ();
@@ -678,9 +753,9 @@ namespace UICatalog {
txtToFind.TextChanged += (e) => {
_textToFind = txtToFind.Text.ToString ();
_textView.FindTextChanged ();
- btnFindNext.CanFocus = !txtToFind.Text.IsEmpty;
- btnFindPrevious.CanFocus = !txtToFind.Text.IsEmpty;
- btnReplaceAll.CanFocus = !txtToFind.Text.IsEmpty;
+ btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
+ btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
+ btnReplaceAll.Enabled = !txtToFind.Text.IsEmpty;
};
var btnCancel = new Button ("Cancel") {
diff --git a/UICatalog/Scenarios/ProgressBarStyles.cs b/UICatalog/Scenarios/ProgressBarStyles.cs
index 4eaf5aa82..91da481ac 100644
--- a/UICatalog/Scenarios/ProgressBarStyles.cs
+++ b/UICatalog/Scenarios/ProgressBarStyles.cs
@@ -64,7 +64,7 @@ namespace UICatalog {
};
button.Clicked += () => {
if (_fractionTimer == null) {
- button.CanFocus = false;
+ button.Enabled = false;
blocksPB.Fraction = 0;
continuousPB.Fraction = 0;
float fractionSum = 0;
@@ -75,7 +75,7 @@ namespace UICatalog {
if (fractionSum > 1) {
_fractionTimer.Dispose ();
_fractionTimer = null;
- button.CanFocus = true;
+ button.Enabled = true;
}
Application.MainLoop.Driver.Wakeup ();
}, null, 0, _timerTick);
diff --git a/UICatalog/Scenarios/SingleBackgroundWorker.cs b/UICatalog/Scenarios/SingleBackgroundWorker.cs
index 2cf11e6ef..0cb7c262b 100644
--- a/UICatalog/Scenarios/SingleBackgroundWorker.cs
+++ b/UICatalog/Scenarios/SingleBackgroundWorker.cs
@@ -60,11 +60,6 @@ namespace UICatalog {
Add (top);
}
- public void Load ()
- {
- Application.Run (this);
- }
-
private void RunWorker ()
{
worker = new BackgroundWorker () { WorkerSupportsCancellation = true };
@@ -87,6 +82,10 @@ namespace UICatalog {
listLog.SetNeedsDisplay ();
var md = new Dialog ($"Running Worker started at {startStaging}.{startStaging:fff}", cancel);
+ md.Add (new Label ("Wait for worker to finish...") {
+ X = Pos.Center (),
+ Y = Pos.Center ()
+ });
worker.DoWork += (s, e) => {
var stageResult = new List ();
@@ -163,9 +162,6 @@ namespace UICatalog {
top.Add (statusBar);
Title = $"Worker started at {start}.{start:fff}";
- Y = 1;
- Height = Dim.Fill (1);
-
ColorScheme = Colors.Base;
Add (new ListView (list) {
diff --git a/UnitTests/LineViewTests.cs b/UnitTests/LineViewTests.cs
index 555d2ee5e..99b581be6 100644
--- a/UnitTests/LineViewTests.cs
+++ b/UnitTests/LineViewTests.cs
@@ -1,9 +1,7 @@
-using Terminal.Gui;
-using Terminal.Gui.Graphs;
-using Terminal.Gui.Views;
+using Terminal.Gui.Graphs;
using Xunit;
-namespace UnitTests {
+namespace Terminal.Gui.Views {
public class LineViewTests {
[Fact]
diff --git a/UnitTests/ResponderTests.cs b/UnitTests/ResponderTests.cs
index 3f5ed7e5f..7f2d927f8 100644
--- a/UnitTests/ResponderTests.cs
+++ b/UnitTests/ResponderTests.cs
@@ -4,7 +4,7 @@ using System.Linq;
using Terminal.Gui;
using Xunit;
-// Alais Console to MockConsole so we don't accidentally use Console
+// Alias Console to MockConsole so we don't accidentally use Console
using Console = Terminal.Gui.FakeConsole;
namespace Terminal.Gui.Core {
@@ -17,6 +17,8 @@ namespace Terminal.Gui.Core {
Assert.Equal ("Terminal.Gui.Responder", r.ToString ());
Assert.False (r.CanFocus);
Assert.False (r.HasFocus);
+ Assert.True (r.Enabled);
+ Assert.True (r.Visible);
}
[Fact]
diff --git a/UnitTests/StatusBarTests.cs b/UnitTests/StatusBarTests.cs
new file mode 100644
index 000000000..c9168e23a
--- /dev/null
+++ b/UnitTests/StatusBarTests.cs
@@ -0,0 +1,160 @@
+using System;
+using Xunit;
+using Xunit.Abstractions;
+
+namespace Terminal.Gui.Views {
+ public class StatusBarTests {
+ readonly ITestOutputHelper output;
+
+ public StatusBarTests (ITestOutputHelper output)
+ {
+ this.output = output;
+ }
+
+ [Fact]
+ public void StatusItem_Constructor ()
+ {
+ var si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null);
+ Assert.Equal (Key.CtrlMask | Key.Q, si.Shortcut);
+ Assert.Equal ("~^Q~ Quit", si.Title);
+ Assert.Null (si.Action);
+ si = new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => { });
+ Assert.NotNull (si.Action);
+ }
+
+ [Fact]
+ public void StatusBar_Contructor_Default ()
+ {
+ var sb = new StatusBar ();
+
+ Assert.Empty (sb.Items);
+ Assert.False (sb.CanFocus);
+ Assert.Equal (Colors.Menu, sb.ColorScheme);
+ Assert.Equal (0, sb.X);
+ Assert.Equal (Dim.Fill (), sb.Width);
+ Assert.Equal (1, sb.Height);
+
+ Assert.Equal (0, sb.Y);
+
+ var driver = new FakeDriver ();
+ Application.Init (driver, new FakeMainLoop (() => FakeConsole.ReadKey (true)));
+
+ sb = new StatusBar ();
+
+ driver.SetCursorVisibility (CursorVisibility.Default);
+ driver.GetCursorVisibility (out CursorVisibility cv);
+ Assert.Equal (CursorVisibility.Default, cv);
+ Assert.True (FakeConsole.CursorVisible);
+
+ Application.Iteration += () => {
+ Assert.Equal (24, sb.Y);
+
+ driver.SetWindowSize (driver.Cols, 15);
+
+ Assert.Equal (14, sb.Y);
+
+ sb.OnEnter (null);
+ driver.GetCursorVisibility (out cv);
+ Assert.Equal (CursorVisibility.Invisible, cv);
+ Assert.False (FakeConsole.CursorVisible);
+
+ Application.RequestStop ();
+ };
+
+ Application.Top.Add (sb);
+
+ Application.Run ();
+
+ Application.Shutdown ();
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Run_Action_With_Key_And_Mouse ()
+ {
+ var msg = "";
+ var sb = new StatusBar (new StatusItem [] { new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", () => msg = "Quiting...") });
+ Application.Top.Add (sb);
+
+ var iteration = 0;
+
+ Application.Iteration += () => {
+ if (iteration == 0) {
+ Assert.Equal ("", msg);
+ sb.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.Q, null));
+ } else if (iteration == 1) {
+ Assert.Equal ("Quiting...", msg);
+ msg = "";
+ sb.MouseEvent (new MouseEvent () { X = 1, Y = 24, Flags = MouseFlags.Button1Clicked });
+ } else {
+ Assert.Equal ("Quiting...", msg);
+
+ Application.RequestStop ();
+ }
+ iteration++;
+ };
+
+ Application.Run ();
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Redraw_Output ()
+ {
+ var sb = new StatusBar (new StatusItem [] {
+ new StatusItem (Key.CtrlMask | Key.Q, "~^O~ Open", null),
+ new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null)
+ });
+ Application.Top.Add (sb);
+
+ sb.Redraw (sb.Bounds);
+
+ string expected = @$"
+^O Open {Application.Driver.VLine} ^Q Quit
+";
+
+ GraphViewTests.AssertDriverContentsAre (expected, output);
+
+ sb = new StatusBar (new StatusItem [] {
+ new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-O~ Open", null),
+ new StatusItem (Key.CtrlMask | Key.Q, "~CTRL-Q~ Quit", null)
+ });
+ sb.Redraw (sb.Bounds);
+
+ expected = @$"
+CTRL-O Open {Application.Driver.VLine} CTRL-Q Quit
+";
+
+ GraphViewTests.AssertDriverContentsAre (expected, output);
+ }
+
+ [Fact]
+ public void AddItemAt_RemoveItem_Replacing ()
+ {
+ var sb = new StatusBar (new StatusItem [] {
+ new StatusItem (Key.CtrlMask | Key.Q, "~^O~ Open", null),
+ new StatusItem (Key.CtrlMask | Key.Q, "~^S~ Save", null),
+ new StatusItem (Key.CtrlMask | Key.Q, "~^Q~ Quit", null)
+ });
+
+ sb.AddItemAt (2, new StatusItem (Key.CtrlMask | Key.Q, "~^C~ Close", null));
+
+ Assert.Equal ("~^O~ Open", sb.Items [0].Title);
+ Assert.Equal ("~^S~ Save", sb.Items [1].Title);
+ Assert.Equal ("~^C~ Close", sb.Items [2].Title);
+ Assert.Equal ("~^Q~ Quit", sb.Items [^1].Title);
+
+ Assert.Equal ("~^S~ Save", sb.RemoveItem (1).Title);
+
+ Assert.Equal ("~^O~ Open", sb.Items [0].Title);
+ Assert.Equal ("~^C~ Close", sb.Items [1].Title);
+ Assert.Equal ("~^Q~ Quit", sb.Items [^1].Title);
+
+ sb.Items [1] = new StatusItem (Key.CtrlMask | Key.A, "~^A~ Save As", null);
+
+ Assert.Equal ("~^O~ Open", sb.Items [0].Title);
+ Assert.Equal ("~^A~ Save As", sb.Items [1].Title);
+ Assert.Equal ("~^Q~ Quit", sb.Items [^1].Title);
+ }
+ }
+}
diff --git a/UnitTests/TextViewTests.cs b/UnitTests/TextViewTests.cs
index 359338791..ab06df48a 100644
--- a/UnitTests/TextViewTests.cs
+++ b/UnitTests/TextViewTests.cs
@@ -1801,10 +1801,7 @@ namespace Terminal.Gui.Views {
if (r == '\t') {
sumLength += tabWidth + 1;
}
- if (sumLength > width) {
- if (cCol == line.Length) {
- col++;
- }
+ if (sumLength >= width) {
break;
} else if (cCol < line.Length && col > 0 && start < cCol && col == start) {
break;
@@ -1968,6 +1965,7 @@ line.
int col = 0;
Assert.True (TextModel.SetCol (ref col, 80, 79));
Assert.False (TextModel.SetCol (ref col, 80, 80));
+ Assert.Equal (79, col);
var start = 0;
var x = 8;
@@ -1981,9 +1979,9 @@ line.
Assert.Equal ((15, 15), TextModel.DisplaySize (txtRunes));
Assert.Equal ((6, 6), TextModel.DisplaySize (txtRunes, 1, 7));
- Assert.Equal (0, TextModel.CalculateLeftColumn (txtRunes, 0, 7, 8));
- Assert.Equal (1, TextModel.CalculateLeftColumn (txtRunes, 0, 8, 8));
- Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8));
+ Assert.Equal (1, TextModel.CalculateLeftColumn (txtRunes, 0, 7, 8));
+ Assert.Equal (2, TextModel.CalculateLeftColumn (txtRunes, 0, 8, 8));
+ Assert.Equal (3, TextModel.CalculateLeftColumn (txtRunes, 0, 9, 8));
var tm = new TextModel ();
tm.AddLine (0, TextModel.ToRunes ("This is first line."));
@@ -2020,5 +2018,65 @@ line.
Assert.Equal (TextModel.ToRunes ("This really first line."), tm.GetLine (0));
Assert.Equal (TextModel.ToRunes ("This really last line."), tm.GetLine (1));
}
+
+ [Fact]
+ [InitShutdown]
+ public void BottomOffset_Sets_To_Zero_Adjust_TopRow ()
+ {
+ string text = "";
+
+ for (int i = 0; i < 12; i++) {
+ text += $"This is the line {i}\n";
+ }
+ var tv = new TextView () { Width = 10, Height = 10, BottomOffset = 1 };
+ tv.Text = text;
+
+ tv.ProcessKey (new KeyEvent (Key.CtrlMask | Key.End, null));
+
+ Assert.Equal (4, tv.TopRow);
+ Assert.Equal (1, tv.BottomOffset);
+
+ tv.BottomOffset = 0;
+ Assert.Equal (3, tv.TopRow);
+ Assert.Equal (0, tv.BottomOffset);
+
+ tv.BottomOffset = 2;
+ Assert.Equal (5, tv.TopRow);
+ Assert.Equal (2, tv.BottomOffset);
+
+ tv.BottomOffset = 0;
+ Assert.Equal (3, tv.TopRow);
+ Assert.Equal (0, tv.BottomOffset);
+ }
+
+ [Fact]
+ [InitShutdown]
+ public void RightOffset_Sets_To_Zero_Adjust_leftColumn ()
+ {
+ string text = "";
+
+ for (int i = 0; i < 12; i++) {
+ text += $"{i.ToString () [^1]}";
+ }
+ var tv = new TextView () { Width = 10, Height = 10, RightOffset = 1 };
+ tv.Text = text;
+
+ tv.ProcessKey (new KeyEvent (Key.End, null));
+
+ Assert.Equal (4, tv.LeftColumn);
+ Assert.Equal (1, tv.RightOffset);
+
+ tv.RightOffset = 0;
+ Assert.Equal (3, tv.LeftColumn);
+ Assert.Equal (0, tv.RightOffset);
+
+ tv.RightOffset = 2;
+ Assert.Equal (5, tv.LeftColumn);
+ Assert.Equal (2, tv.RightOffset);
+
+ tv.RightOffset = 0;
+ Assert.Equal (3, tv.LeftColumn);
+ Assert.Equal (0, tv.RightOffset);
+ }
}
}
diff --git a/UnitTests/ToplevelTests.cs b/UnitTests/ToplevelTests.cs
new file mode 100644
index 000000000..d8eaf0ba8
--- /dev/null
+++ b/UnitTests/ToplevelTests.cs
@@ -0,0 +1,315 @@
+using System;
+using Xunit;
+
+namespace Terminal.Gui.Core {
+ public class ToplevelTests {
+ [Fact]
+ [AutoInitShutdown]
+ public void Constructor_Default ()
+ {
+ var top = new Toplevel ();
+
+ Assert.Equal (Colors.TopLevel, top.ColorScheme);
+ Assert.Equal ("Dim.Fill(margin=0)", top.Width.ToString ());
+ Assert.Equal ("Dim.Fill(margin=0)", top.Height.ToString ());
+ Assert.False (top.Running);
+ Assert.False (top.Modal);
+ Assert.Null (top.MenuBar);
+ Assert.Null (top.StatusBar);
+ Assert.False (top.IsMdiContainer);
+ Assert.False (top.IsMdiChild);
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Create_Toplevel ()
+ {
+ var top = Toplevel.Create ();
+ Assert.Equal (new Rect (0, 0, Application.Driver.Cols, Application.Driver.Rows), top.Bounds);
+ }
+
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Application_Top_EnsureVisibleBounds_To_Driver_Rows_And_Cols ()
+ {
+ var iterations = 0;
+
+ Application.Iteration += () => {
+ if (iterations == 0) {
+ Assert.Equal ("Top1", Application.Top.Text);
+ Assert.Equal (0, Application.Top.Frame.X);
+ Assert.Equal (0, Application.Top.Frame.Y);
+ Assert.Equal (Application.Driver.Cols, Application.Top.Frame.Width);
+ Assert.Equal (Application.Driver.Rows, Application.Top.Frame.Height);
+
+ Application.Top.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.R, new KeyModifiers ()));
+ } else if (iterations == 1) {
+ Assert.Equal ("Top2", Application.Top.Text);
+ Assert.Equal (0, Application.Top.Frame.X);
+ Assert.Equal (0, Application.Top.Frame.Y);
+ Assert.Equal (Application.Driver.Cols, Application.Top.Frame.Width);
+ Assert.Equal (Application.Driver.Rows, Application.Top.Frame.Height);
+
+ Application.Top.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.C, new KeyModifiers ()));
+ } else if (iterations == 3) {
+ Assert.Equal ("Top1", Application.Top.Text);
+ Assert.Equal (0, Application.Top.Frame.X);
+ Assert.Equal (0, Application.Top.Frame.Y);
+ Assert.Equal (Application.Driver.Cols, Application.Top.Frame.Width);
+ Assert.Equal (Application.Driver.Rows, Application.Top.Frame.Height);
+
+ Application.Top.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.R, new KeyModifiers ()));
+ } else if (iterations == 4) {
+ Assert.Equal ("Top2", Application.Top.Text);
+ Assert.Equal (0, Application.Top.Frame.X);
+ Assert.Equal (0, Application.Top.Frame.Y);
+ Assert.Equal (Application.Driver.Cols, Application.Top.Frame.Width);
+ Assert.Equal (Application.Driver.Rows, Application.Top.Frame.Height);
+
+ Application.Top.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.C, new KeyModifiers ()));
+ } else if (iterations == 6) {
+ Assert.Equal ("Top1", Application.Top.Text);
+ Assert.Equal (0, Application.Top.Frame.X);
+ Assert.Equal (0, Application.Top.Frame.Y);
+ Assert.Equal (Application.Driver.Cols, Application.Top.Frame.Width);
+ Assert.Equal (Application.Driver.Rows, Application.Top.Frame.Height);
+
+ Application.Top.ProcessHotKey (new KeyEvent (Key.CtrlMask | Key.Q, new KeyModifiers ()));
+ }
+ iterations++;
+ };
+
+ Application.Run (Top1 ());
+
+ Toplevel Top1 ()
+ {
+ var top = Application.Top;
+ top.Text = "Top1";
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_Options", new MenuItem [] {
+ new MenuItem ("_Run Top2", "", () => Application.Run (Top2 ()), null, null, Key.CtrlMask | Key.R),
+ new MenuItem ("_Quit", "", () => Application.RequestStop(), null, null, Key.CtrlMask | Key.Q)
+ })
+ });
+ top.Add (menu);
+
+ var statusBar = new StatusBar (new [] {
+ new StatusItem(Key.CtrlMask | Key.R, "~^R~ Run Top2", () => Application.Run (Top2 ())),
+ new StatusItem(Key.CtrlMask | Key.Q, "~^Q~ Quit", () => Application.RequestStop())
+ });
+ top.Add (statusBar);
+
+ var t1 = new Toplevel ();
+ top.Add (t1);
+
+ return top;
+ }
+
+ Toplevel Top2 ()
+ {
+ var top = new Toplevel (Application.Top.Frame);
+ top.Text = "Top2";
+ var win = new Window () { Width = Dim.Fill (), Height = Dim.Fill () };
+ var menu = new MenuBar (new MenuBarItem [] {
+ new MenuBarItem ("_Stage", new MenuItem [] {
+ new MenuItem ("_Close", "", () => Application.RequestStop(), null, null, Key.CtrlMask | Key.C)
+ })
+ });
+ top.Add (menu);
+
+ var statusBar = new StatusBar (new [] {
+ new StatusItem(Key.CtrlMask | Key.C, "~^C~ Close", () => Application.RequestStop()),
+ });
+ top.Add (statusBar);
+
+ win.Add (new ListView () {
+ X = 0,
+ Y = 0,
+ Width = Dim.Fill (),
+ Height = Dim.Fill ()
+ });
+ top.Add (win);
+
+ return top;
+ }
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Internal_Tests ()
+ {
+ var top = new Toplevel ();
+ var eventInvoked = "";
+
+ top.ChildUnloaded += (e) => eventInvoked = "ChildUnloaded";
+ top.OnChildUnloaded (top);
+ Assert.Equal ("ChildUnloaded", eventInvoked);
+ top.ChildLoaded += (e) => eventInvoked = "ChildLoaded";
+ top.OnChildLoaded (top);
+ Assert.Equal ("ChildLoaded", eventInvoked);
+ top.Closed += (e) => eventInvoked = "Closed";
+ top.OnClosed (top);
+ Assert.Equal ("Closed", eventInvoked);
+ top.Closing += (e) => eventInvoked = "Closing";
+ top.OnClosing (new ToplevelClosingEventArgs (top));
+ Assert.Equal ("Closing", eventInvoked);
+ top.AllChildClosed += () => eventInvoked = "AllChildClosed";
+ top.OnAllChildClosed ();
+ Assert.Equal ("AllChildClosed", eventInvoked);
+ top.ChildClosed += (e) => eventInvoked = "ChildClosed";
+ top.OnChildClosed (top);
+ Assert.Equal ("ChildClosed", eventInvoked);
+ top.Deactivate += (e) => eventInvoked = "Deactivate";
+ top.OnDeactivate (top);
+ Assert.Equal ("Deactivate", eventInvoked);
+ top.Activate += (e) => eventInvoked = "Activate";
+ top.OnActivate (top);
+ Assert.Equal ("Activate", eventInvoked);
+ top.Loaded += () => eventInvoked = "Loaded";
+ top.OnLoaded ();
+ Assert.Equal ("Loaded", eventInvoked);
+ top.Ready += () => eventInvoked = "Ready";
+ top.OnReady ();
+ Assert.Equal ("Ready", eventInvoked);
+ top.Unloaded += () => eventInvoked = "Unloaded";
+ top.OnUnloaded ();
+ Assert.Equal ("Unloaded", eventInvoked);
+
+ top.AddMenuStatusBar (new MenuBar ());
+ Assert.NotNull (top.MenuBar);
+ top.AddMenuStatusBar (new StatusBar ());
+ Assert.NotNull (top.StatusBar);
+ top.RemoveMenuStatusBar (top.MenuBar);
+ Assert.Null (top.MenuBar);
+ top.RemoveMenuStatusBar (top.StatusBar);
+ Assert.Null (top.StatusBar);
+
+ Application.Begin (top);
+ Assert.Equal (top, Application.Top);
+
+ // top is Application.Top without menu and status bar.
+ var supView = top.EnsureVisibleBounds (top, 2, 2, out int nx, out int ny, out View mb, out View sb);
+ Assert.Equal (Application.Top, supView);
+ Assert.Equal (0, nx);
+ Assert.Equal (0, ny);
+ Assert.Null (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new MenuBar ());
+ Assert.NotNull (top.MenuBar);
+
+ // top is Application.Top with a menu and without status bar.
+ top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (1, ny);
+ Assert.NotNull (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new StatusBar ());
+ Assert.NotNull (top.StatusBar);
+
+ // top is Application.Top with a menu and status bar.
+ top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (1, ny);
+ Assert.NotNull (mb);
+ Assert.NotNull (sb);
+
+ top.RemoveMenuStatusBar (top.MenuBar);
+ Assert.Null (top.MenuBar);
+
+ // top is Application.Top without a menu and with a status bar.
+ top.EnsureVisibleBounds (top, 2, 2, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (0, ny);
+ Assert.Null (mb);
+ Assert.NotNull (sb);
+
+ top.RemoveMenuStatusBar (top.StatusBar);
+ Assert.Null (top.StatusBar);
+ Assert.Null (top.MenuBar);
+
+ var win = new Window () { Width = Dim.Fill (), Height = Dim.Fill () };
+ top.Add (win);
+ top.LayoutSubviews ();
+
+ // The SuperView is always the same regardless of the caller.
+ supView = top.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
+ Assert.Equal (Application.Top, supView);
+ supView = win.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
+ Assert.Equal (Application.Top, supView);
+
+ // top is Application.Top without menu and status bar.
+ top.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (0, ny);
+ Assert.Null (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new MenuBar ());
+ Assert.NotNull (top.MenuBar);
+
+ // top is Application.Top with a menu and without status bar.
+ top.EnsureVisibleBounds (win, 2, 2, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (1, ny);
+ Assert.NotNull (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new StatusBar ());
+ Assert.NotNull (top.StatusBar);
+
+ // top is Application.Top with a menu and status bar.
+ top.EnsureVisibleBounds (win, 30, 20, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (1, ny);
+ Assert.NotNull (mb);
+ Assert.NotNull (sb);
+
+ top.RemoveMenuStatusBar (top.MenuBar);
+ top.RemoveMenuStatusBar (top.StatusBar);
+ Assert.Null (top.StatusBar);
+ Assert.Null (top.MenuBar);
+
+ top.Remove (win);
+
+ win = new Window () { Width = 60, Height = 15 };
+ top.Add (win);
+
+ // top is Application.Top without menu and status bar.
+ top.EnsureVisibleBounds (win, 0, 0, out nx, out ny, out mb, out sb);
+ Assert.Equal (0, nx);
+ Assert.Equal (0, ny);
+ Assert.Null (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new MenuBar ());
+ Assert.NotNull (top.MenuBar);
+
+ // top is Application.Top with a menu and without status bar.
+ top.EnsureVisibleBounds (win, 2, 2, out nx, out ny, out mb, out sb);
+ Assert.Equal (2, nx);
+ Assert.Equal (2, ny);
+ Assert.NotNull (mb);
+ Assert.Null (sb);
+
+ top.AddMenuStatusBar (new StatusBar ());
+ Assert.NotNull (top.StatusBar);
+
+ // top is Application.Top with a menu and status bar.
+ top.EnsureVisibleBounds (win, 30, 20, out nx, out ny, out mb, out sb);
+ Assert.Equal (20, nx); // 20+60=80
+ Assert.Equal (9, ny); // 9+15+1(mb)=25
+ Assert.NotNull (mb);
+ Assert.NotNull (sb);
+
+ top.PositionToplevels ();
+ Assert.Equal (new Rect (0, 1, 60, 15), win.Frame);
+
+ Assert.Null (Toplevel.dragPosition);
+ win.MouseEvent (new MouseEvent () { X = 6, Y = 0, Flags = MouseFlags.Button1Pressed });
+ Assert.Equal (new Point (6, 0), Toplevel.dragPosition);
+ }
+ }
+}
diff --git a/UnitTests/ViewTests.cs b/UnitTests/ViewTests.cs
index 7b73705f8..fb34db691 100644
--- a/UnitTests/ViewTests.cs
+++ b/UnitTests/ViewTests.cs
@@ -1412,5 +1412,161 @@ namespace Terminal.Gui.Views {
view.OnLayoutComplete (null);
Assert.False (layoutStarted);
}
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Enabled_False_Sets_HasFocus_To_False ()
+ {
+ var wasClicked = false;
+ var view = new Button ("Click Me");
+ view.Clicked += () => wasClicked = !wasClicked;
+ Application.Top.Add (view);
+
+ view.ProcessKey (new KeyEvent (Key.Enter, null));
+ Assert.True (wasClicked);
+ view.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Clicked });
+ Assert.False (wasClicked);
+ Assert.True (view.Enabled);
+ Assert.True (view.CanFocus);
+ Assert.True (view.HasFocus);
+
+ view.Enabled = false;
+ view.ProcessKey (new KeyEvent (Key.Enter, null));
+ Assert.False (wasClicked);
+ view.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Clicked });
+ Assert.False (wasClicked);
+ Assert.False (view.Enabled);
+ Assert.True (view.CanFocus);
+ Assert.False (view.HasFocus);
+ view.SetFocus ();
+ Assert.False (view.HasFocus);
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Enabled_Sets_Also_Sets_Subviews ()
+ {
+ var wasClicked = false;
+ var button = new Button ("Click Me");
+ button.Clicked += () => wasClicked = !wasClicked;
+ var win = new Window () { Width = Dim.Fill (), Height = Dim.Fill () };
+ win.Add (button);
+ Application.Top.Add (win);
+
+ var iterations = 0;
+
+ Application.Iteration += () => {
+ iterations++;
+
+ button.ProcessKey (new KeyEvent (Key.Enter, null));
+ Assert.True (wasClicked);
+ button.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Clicked });
+ Assert.False (wasClicked);
+ Assert.True (button.Enabled);
+ Assert.True (button.CanFocus);
+ Assert.True (button.HasFocus);
+ Assert.True (win.Enabled);
+ Assert.True (win.CanFocus);
+ Assert.True (win.HasFocus);
+
+ win.Enabled = false;
+ button.ProcessKey (new KeyEvent (Key.Enter, null));
+ Assert.False (wasClicked);
+ button.MouseEvent (new MouseEvent () { Flags = MouseFlags.Button1Clicked });
+ Assert.False (wasClicked);
+ Assert.False (button.Enabled);
+ Assert.True (button.CanFocus);
+ Assert.False (button.HasFocus);
+ Assert.False (win.Enabled);
+ Assert.True (win.CanFocus);
+ Assert.False (win.HasFocus);
+ button.SetFocus ();
+ Assert.False (button.HasFocus);
+ Assert.False (win.HasFocus);
+ win.SetFocus ();
+ Assert.False (button.HasFocus);
+ Assert.False (win.HasFocus);
+
+ win.Enabled = true;
+ win.FocusFirst ();
+ Assert.True (button.HasFocus);
+ Assert.True (win.HasFocus);
+
+ Application.RequestStop ();
+ };
+
+ Application.Run ();
+
+ Assert.Equal (1, iterations);
+ }
+
+ [Fact]
+ [AutoInitShutdown]
+ public void Visible_Sets_Also_Sets_Subviews ()
+ {
+ var button = new Button ("Click Me");
+ var win = new Window () { Width = Dim.Fill (), Height = Dim.Fill () };
+ win.Add (button);
+ var top = Application.Top;
+ top.Add (win);
+
+ var iterations = 0;
+
+ Application.Iteration += () => {
+ iterations++;
+
+ Assert.True (button.Visible);
+ Assert.True (button.CanFocus);
+ Assert.True (button.HasFocus);
+ Assert.True (win.Visible);
+ Assert.True (win.CanFocus);
+ Assert.True (win.HasFocus);
+ Assert.True (RunesCount () > 0);
+
+ win.Visible = false;
+ Assert.True (button.Visible);
+ Assert.True (button.CanFocus);
+ Assert.False (button.HasFocus);
+ Assert.False (win.Visible);
+ Assert.True (win.CanFocus);
+ Assert.False (win.HasFocus);
+ button.SetFocus ();
+ Assert.False (button.HasFocus);
+ Assert.False (win.HasFocus);
+ win.SetFocus ();
+ Assert.False (button.HasFocus);
+ Assert.False (win.HasFocus);
+ top.Redraw (top.Bounds);
+ Assert.True (RunesCount () == 0);
+
+ win.Visible = true;
+ win.FocusFirst ();
+ Assert.True (button.HasFocus);
+ Assert.True (win.HasFocus);
+ top.Redraw (top.Bounds);
+ Assert.True (RunesCount () > 0);
+
+ Application.RequestStop ();
+ };
+
+ Application.Run ();
+
+ Assert.Equal (1, iterations);
+
+ int RunesCount ()
+ {
+ var contents = ((FakeDriver)Application.Driver).Contents;
+ var runesCount = 0;
+
+ for (int i = 0; i < Application.Driver.Rows; i++) {
+ for (int j = 0; j < Application.Driver.Cols; j++) {
+ if (contents [i, j, 0] != ' ') {
+ runesCount++;
+ }
+ }
+ }
+ return runesCount;
+ }
+ }
}
}