mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-29 09:18:01 +01:00
Merge pull request #1021 from BDisp/netdriver-unicode
Fixes #1020. NetDriver does not deal well with unicode characters yet.
This commit is contained in:
@@ -562,9 +562,11 @@ static class Demo {
|
||||
if (Debugger.IsAttached)
|
||||
CultureInfo.DefaultThreadCurrentUICulture = CultureInfo.GetCultureInfo ("en-US");
|
||||
|
||||
//Application.UseSystemConsole = true;
|
||||
Application.UseSystemConsole = true;
|
||||
|
||||
Application.Init();
|
||||
Application.HeightAsBuffer = true;
|
||||
//ConsoleDriver.Diagnostics = ConsoleDriver.DiagnosticFlags.FramePadding | ConsoleDriver.DiagnosticFlags.FrameRuler;
|
||||
|
||||
var top = Application.Top;
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Terminal.Gui {
|
||||
public override int Cols => Curses.Cols;
|
||||
public override int Rows => Curses.Lines;
|
||||
public override int Top => 0;
|
||||
public override bool HeightAsBuffer { get; set; }
|
||||
|
||||
// Current row, and current col, tracked by Move/AddRune only
|
||||
int ccol, crow;
|
||||
@@ -70,6 +71,7 @@ namespace Terminal.Gui {
|
||||
public override void Refresh () {
|
||||
Curses.refresh ();
|
||||
if (Curses.CheckWinChange ()) {
|
||||
Clip = new Rect (0, 0, Cols, Rows);
|
||||
TerminalResized?.Invoke ();
|
||||
}
|
||||
}
|
||||
@@ -645,16 +647,16 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
Action<MouseEvent> mouseHandler;
|
||||
MainLoop mainLoop;
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
// Note: Curses doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
|
||||
Curses.timeout (0);
|
||||
this.mouseHandler = mouseHandler;
|
||||
this.mainLoop = mainLoop;
|
||||
|
||||
(mainLoop.Driver as UnixMainLoop).AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
|
||||
var mLoop = mainLoop.Driver as UnixMainLoop;
|
||||
|
||||
mLoop.AddWatch (0, UnixMainLoop.Condition.PollIn, x => {
|
||||
ProcessInput (keyHandler, keyDownHandler, keyUpHandler, mouseHandler);
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ namespace Terminal.Gui {
|
||||
public override int Cols => cols;
|
||||
public override int Rows => rows;
|
||||
public override int Top => 0;
|
||||
public override bool HeightAsBuffer { get; set; }
|
||||
|
||||
// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
|
||||
int [,,] contents;
|
||||
|
||||
@@ -18,83 +18,59 @@ namespace Terminal.Gui {
|
||||
public override int Cols => cols;
|
||||
public override int Rows => rows;
|
||||
public override int Top => top;
|
||||
public override bool HeightAsBuffer { get; set; }
|
||||
|
||||
// The format is rows, columns and 3 values on the last column: Rune, Attribute and Dirty Flag
|
||||
int [,,] contents;
|
||||
bool [] dirtyLine;
|
||||
|
||||
public NetDriver ()
|
||||
{
|
||||
ResizeScreen ();
|
||||
UpdateOffscreen ();
|
||||
}
|
||||
|
||||
void UpdateOffscreen ()
|
||||
{
|
||||
int cols = Cols;
|
||||
int rows = Rows;
|
||||
|
||||
contents = new int [rows, cols, 3];
|
||||
dirtyLine = new bool [rows];
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
contents [row, c, 0] = ' ';
|
||||
contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
|
||||
contents [row, c, 2] = 0;
|
||||
dirtyLine [row] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static bool sync = false;
|
||||
|
||||
bool needMove;
|
||||
// Current row, and current col, tracked by Move/AddCh only
|
||||
int ccol, crow;
|
||||
public override void Move (int col, int row)
|
||||
{
|
||||
ccol = col;
|
||||
crow = row;
|
||||
|
||||
if (Clip.Contains (col, row)) {
|
||||
if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
|
||||
Console.SetCursorPosition (col, row);
|
||||
needMove = false;
|
||||
}
|
||||
} else {
|
||||
if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
|
||||
if (Console.WindowHeight > 0) {
|
||||
Console.SetCursorPosition (Clip.X, Clip.Y);
|
||||
}
|
||||
needMove = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddRune (Rune rune)
|
||||
{
|
||||
if (contents.Length != Rows * Cols * 3) {
|
||||
return;
|
||||
}
|
||||
rune = MakePrintable (rune);
|
||||
if (Clip.Contains (ccol, crow)) {
|
||||
if (needMove) {
|
||||
if (cols == Console.WindowWidth && rows == Console.WindowHeight) {
|
||||
Console.SetCursorPosition (ccol, crow);
|
||||
}
|
||||
needMove = false;
|
||||
}
|
||||
var runeWidth = Rune.ColumnWidth (rune);
|
||||
if (Clip.Contains (ccol, crow) && ccol + Math.Max (runeWidth, 1) <= Cols) {
|
||||
contents [crow, ccol, 0] = (int)(uint)rune;
|
||||
contents [crow, ccol, 1] = currentAttribute;
|
||||
contents [crow, ccol, 2] = 1;
|
||||
dirtyLine [crow] = true;
|
||||
} else
|
||||
needMove = true;
|
||||
ccol++;
|
||||
|
||||
ccol++;
|
||||
if (runeWidth > 1) {
|
||||
for (int i = 1; i < runeWidth; i++) {
|
||||
if (ccol < cols) {
|
||||
contents [crow, ccol, 2] = 0;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
ccol++;
|
||||
}
|
||||
}
|
||||
} else if (ccol < cols && crow < rows) {
|
||||
contents [crow, ccol, 2] = 1;
|
||||
dirtyLine [crow] = true;
|
||||
}
|
||||
|
||||
//if (ccol == Cols) {
|
||||
// ccol = 0;
|
||||
// if (crow + 1 < Rows)
|
||||
// crow++;
|
||||
//}
|
||||
if (sync)
|
||||
if (sync) {
|
||||
UpdateScreen ();
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddStr (ustring str)
|
||||
@@ -122,10 +98,24 @@ namespace Terminal.Gui {
|
||||
return new Attribute () { value = ((((int)f) & 0xffff) << 16) | (((int)b) & 0xffff) };
|
||||
}
|
||||
|
||||
bool isWinPlatform;
|
||||
|
||||
public override void Init (Action terminalResized)
|
||||
{
|
||||
TerminalResized = terminalResized;
|
||||
|
||||
Console.TreatControlCAsInput = true;
|
||||
var p = Environment.OSVersion.Platform;
|
||||
if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
|
||||
isWinPlatform = true;
|
||||
}
|
||||
|
||||
cols = Console.WindowWidth;
|
||||
rows = Console.WindowHeight;
|
||||
|
||||
Clear ();
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
|
||||
Colors.TopLevel = new ColorScheme ();
|
||||
Colors.Base = new ColorScheme ();
|
||||
@@ -163,15 +153,71 @@ namespace Terminal.Gui {
|
||||
Colors.Error.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Error.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.Red);
|
||||
Colors.Error.HotFocus = Colors.Error.HotNormal;
|
||||
Clear ();
|
||||
}
|
||||
|
||||
void ResizeScreen ()
|
||||
{
|
||||
cols = Console.WindowWidth;
|
||||
rows = Console.WindowHeight;
|
||||
if (!HeightAsBuffer) {
|
||||
if (Console.WindowHeight > 0) {
|
||||
// Can raise an exception while is still resizing.
|
||||
try {
|
||||
// Not supported on Unix.
|
||||
if (isWinPlatform) {
|
||||
#pragma warning disable CA1416
|
||||
Console.CursorTop = 0;
|
||||
Console.CursorLeft = 0;
|
||||
Console.WindowTop = 0;
|
||||
Console.WindowLeft = 0;
|
||||
Console.SetBufferSize (Cols, Rows);
|
||||
#pragma warning restore CA1416
|
||||
} else {
|
||||
//Console.Out.Write ($"\x1b[8;{Console.WindowHeight};{Console.WindowWidth}t");
|
||||
Console.Out.Write ($"\x1b[0;0" +
|
||||
$";{Rows};{Cols}w");
|
||||
}
|
||||
} catch (System.IO.IOException) {
|
||||
return;
|
||||
} catch (ArgumentOutOfRangeException) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (isWinPlatform && Console.WindowHeight > 0) {
|
||||
// Can raise an exception while is still resizing.
|
||||
try {
|
||||
#pragma warning disable CA1416
|
||||
Console.WindowTop = Math.Max (Math.Min (top, Rows - Console.WindowHeight), 0);
|
||||
#pragma warning restore CA1416
|
||||
} catch (Exception) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
Console.Out.Write ($"\x1b[{top};{Console.WindowLeft}" +
|
||||
$";{Rows};{Cols}w");
|
||||
}
|
||||
}
|
||||
|
||||
Clip = new Rect (0, 0, Cols, Rows);
|
||||
top = Console.WindowTop;
|
||||
|
||||
contents = new int [Rows, Cols, 3];
|
||||
dirtyLine = new bool [Rows];
|
||||
}
|
||||
|
||||
void UpdateOffScreen ()
|
||||
{
|
||||
// Can raise an exception while is still resizing.
|
||||
try {
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int c = 0; c < cols; c++) {
|
||||
contents [row, c, 0] = ' ';
|
||||
contents [row, c, 1] = (ushort)Colors.TopLevel.Normal;
|
||||
contents [row, c, 2] = 0;
|
||||
dirtyLine [row] = true;
|
||||
}
|
||||
}
|
||||
} catch (IndexOutOfRangeException) { }
|
||||
|
||||
winChanging = false;
|
||||
}
|
||||
|
||||
public override Attribute MakeAttribute (Color fore, Color back)
|
||||
@@ -196,29 +242,42 @@ namespace Terminal.Gui {
|
||||
|
||||
public override void UpdateScreen ()
|
||||
{
|
||||
if (Rows == 0) {
|
||||
if (winChanging || Console.WindowHeight == 0 || contents.Length != Rows * Cols * 3
|
||||
|| (!HeightAsBuffer && Rows != Console.WindowHeight)
|
||||
|| (HeightAsBuffer && Rows != Console.BufferHeight)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int rows = Rows;
|
||||
int top = Top;
|
||||
int rows = Math.Min (Console.WindowHeight + top, Rows);
|
||||
int cols = Cols;
|
||||
|
||||
for (int row = top; row < rows; row++) {
|
||||
if (!dirtyLine [row])
|
||||
if (!dirtyLine [row]) {
|
||||
continue;
|
||||
}
|
||||
dirtyLine [row] = false;
|
||||
for (int col = 0; col < cols; col++) {
|
||||
if (contents [row, col, 2] != 1)
|
||||
if (contents [row, col, 2] != 1) {
|
||||
continue;
|
||||
|
||||
}
|
||||
if (Console.WindowHeight > 0) {
|
||||
Console.SetCursorPosition (col, row);
|
||||
// Could happens that the windows is still resizing and the col is bigger than Console.WindowWidth.
|
||||
try {
|
||||
Console.SetCursorPosition (col, row);
|
||||
} catch (Exception) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
for (; col < cols && contents [row, col, 2] == 1; col++) {
|
||||
// Needed for the .Net Framework.
|
||||
if (row == rows - 1 && col == cols - 1) {
|
||||
break;
|
||||
}
|
||||
var color = contents [row, col, 1];
|
||||
if (color != redrawColor)
|
||||
if (color != redrawColor) {
|
||||
SetColor (color);
|
||||
|
||||
}
|
||||
Console.Write ((char)contents [row, col, 0]);
|
||||
contents [row, col, 2] = 0;
|
||||
}
|
||||
@@ -230,12 +289,6 @@ namespace Terminal.Gui {
|
||||
|
||||
public override void Refresh ()
|
||||
{
|
||||
if (Console.WindowWidth != Cols || Console.WindowHeight != Rows || Console.WindowTop != Top) {
|
||||
ResizeScreen ();
|
||||
UpdateOffscreen ();
|
||||
TerminalResized.Invoke ();
|
||||
}
|
||||
|
||||
UpdateScreen ();
|
||||
}
|
||||
|
||||
@@ -243,10 +296,12 @@ namespace Terminal.Gui {
|
||||
{
|
||||
// Prevents the exception of size changing during resizing.
|
||||
try {
|
||||
if (ccol > 0 && ccol < Console.WindowWidth && crow > 0 && crow < Console.WindowHeight) {
|
||||
if (ccol >= 0 && ccol <= cols && crow >= 0 && crow <= rows) {
|
||||
Console.SetCursorPosition (ccol, crow);
|
||||
}
|
||||
} catch (ArgumentOutOfRangeException) { }
|
||||
} catch (System.IO.IOException) {
|
||||
} catch (ArgumentOutOfRangeException) {
|
||||
}
|
||||
}
|
||||
|
||||
public override void StartReportingMouseMoves ()
|
||||
@@ -269,36 +324,39 @@ namespace Terminal.Gui {
|
||||
|
||||
Key MapKey (ConsoleKeyInfo keyInfo)
|
||||
{
|
||||
MapKeyModifiers (keyInfo);
|
||||
MapKeyModifiers (keyInfo, (Key)keyInfo.Key);
|
||||
switch (keyInfo.Key) {
|
||||
case ConsoleKey.Escape:
|
||||
return Key.Esc;
|
||||
return MapKeyModifiers (keyInfo, Key.Esc);
|
||||
case ConsoleKey.Tab:
|
||||
return keyInfo.Modifiers == ConsoleModifiers.Shift ? Key.BackTab : Key.Tab;
|
||||
case ConsoleKey.Home:
|
||||
return Key.Home;
|
||||
return MapKeyModifiers (keyInfo, Key.Home);
|
||||
case ConsoleKey.End:
|
||||
return Key.End;
|
||||
return MapKeyModifiers (keyInfo, Key.End);
|
||||
case ConsoleKey.LeftArrow:
|
||||
return Key.CursorLeft;
|
||||
return MapKeyModifiers (keyInfo, Key.CursorLeft);
|
||||
case ConsoleKey.RightArrow:
|
||||
return Key.CursorRight;
|
||||
return MapKeyModifiers (keyInfo, Key.CursorRight);
|
||||
case ConsoleKey.UpArrow:
|
||||
return Key.CursorUp;
|
||||
return MapKeyModifiers (keyInfo, Key.CursorUp);
|
||||
case ConsoleKey.DownArrow:
|
||||
return Key.CursorDown;
|
||||
return MapKeyModifiers (keyInfo, Key.CursorDown);
|
||||
case ConsoleKey.PageUp:
|
||||
return Key.PageUp;
|
||||
return MapKeyModifiers (keyInfo, Key.PageUp);
|
||||
case ConsoleKey.PageDown:
|
||||
return Key.PageDown;
|
||||
return MapKeyModifiers (keyInfo, Key.PageDown);
|
||||
case ConsoleKey.Enter:
|
||||
return Key.Enter;
|
||||
return MapKeyModifiers (keyInfo, Key.Enter);
|
||||
case ConsoleKey.Spacebar:
|
||||
return Key.Space;
|
||||
return MapKeyModifiers (keyInfo, Key.Space);
|
||||
case ConsoleKey.Backspace:
|
||||
return Key.Backspace;
|
||||
return MapKeyModifiers (keyInfo, Key.Backspace);
|
||||
case ConsoleKey.Delete:
|
||||
return Key.Delete;
|
||||
return MapKeyModifiers (keyInfo, Key.DeleteChar);
|
||||
case ConsoleKey.Insert:
|
||||
return MapKeyModifiers (keyInfo, Key.InsertChar);
|
||||
|
||||
|
||||
case ConsoleKey.Oem1:
|
||||
case ConsoleKey.Oem2:
|
||||
@@ -327,7 +385,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
||||
if (keyInfo.KeyChar == 0 || (keyInfo.KeyChar != 0 && keyInfo.KeyChar >= 1 && keyInfo.KeyChar <= 26)) {
|
||||
return (Key)((uint)Key.A + delta);
|
||||
return MapKeyModifiers (keyInfo, (Key)((uint)Key.A + delta));
|
||||
}
|
||||
}
|
||||
return (Key)((uint)keyInfo.KeyChar);
|
||||
@@ -342,7 +400,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
if ((keyInfo.Modifiers & (ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
||||
if (keyInfo.KeyChar == 0 || keyInfo.KeyChar == 30) {
|
||||
return (Key)((uint)Key.D0 + delta);
|
||||
return MapKeyModifiers (keyInfo, (Key)((uint)Key.D0 + delta));
|
||||
}
|
||||
}
|
||||
return (Key)((uint)keyInfo.KeyChar);
|
||||
@@ -350,7 +408,7 @@ namespace Terminal.Gui {
|
||||
if (key >= ConsoleKey.F1 && key <= ConsoleKey.F12) {
|
||||
var delta = key - ConsoleKey.F1;
|
||||
if ((keyInfo.Modifiers & (ConsoleModifiers.Shift | ConsoleModifiers.Alt | ConsoleModifiers.Control)) != 0) {
|
||||
return (Key)((uint)Key.F1 + delta);
|
||||
return MapKeyModifiers (keyInfo, (Key)((uint)Key.F1 + delta));
|
||||
}
|
||||
|
||||
return (Key)((uint)Key.F1 + delta);
|
||||
@@ -364,41 +422,82 @@ namespace Terminal.Gui {
|
||||
|
||||
KeyModifiers keyModifiers;
|
||||
|
||||
void MapKeyModifiers (ConsoleKeyInfo keyInfo)
|
||||
Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
|
||||
{
|
||||
if (keyModifiers == null)
|
||||
if (keyModifiers == null) {
|
||||
keyModifiers = new KeyModifiers ();
|
||||
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
|
||||
}
|
||||
Key keyMod = new Key ();
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0) {
|
||||
keyMod = Key.ShiftMask;
|
||||
keyModifiers.Shift = true;
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0)
|
||||
}
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Control) != 0) {
|
||||
keyMod |= Key.CtrlMask;
|
||||
keyModifiers.Ctrl = true;
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0)
|
||||
}
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Alt) != 0) {
|
||||
keyMod |= Key.AltMask;
|
||||
keyModifiers.Alt = true;
|
||||
}
|
||||
|
||||
return keyMod != Key.Null ? keyMod | key : key;
|
||||
}
|
||||
|
||||
bool winChanging;
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
var mLoop = mainLoop.Driver as NetMainLoop;
|
||||
|
||||
// Note: Net doesn't support keydown/up events and thus any passed keyDown/UpHandlers will never be called
|
||||
(mainLoop.Driver as NetMainLoop).KeyPressed = delegate (ConsoleKeyInfo consoleKey) {
|
||||
mLoop.KeyPressed = (consoleKey) => {
|
||||
var map = MapKey (consoleKey);
|
||||
if (map == (Key)0xffffffff) {
|
||||
return;
|
||||
}
|
||||
keyHandler (new KeyEvent (map, keyModifiers));
|
||||
keyUpHandler (new KeyEvent (map, keyModifiers));
|
||||
keyModifiers = null;
|
||||
|
||||
if (map == (Key.Space | Key.CtrlMask) || map == (Key.Space | Key.AltMask)) {
|
||||
map = Key.AltMask;
|
||||
keyModifiers.Alt = true;
|
||||
keyModifiers.Ctrl = false;
|
||||
keyDownHandler (new KeyEvent (map, keyModifiers));
|
||||
keyUpHandler (new KeyEvent (map, keyModifiers));
|
||||
} else {
|
||||
keyDownHandler (new KeyEvent (map, keyModifiers));
|
||||
keyHandler (new KeyEvent (map, keyModifiers));
|
||||
keyUpHandler (new KeyEvent (map, keyModifiers));
|
||||
}
|
||||
keyModifiers = new KeyModifiers ();
|
||||
};
|
||||
|
||||
mLoop.WinChanged = (e) => {
|
||||
winChanging = true;
|
||||
const int Min_WindowWidth = 14;
|
||||
Size size = new Size ();
|
||||
if (!HeightAsBuffer) {
|
||||
size = new Size (Math.Max (Min_WindowWidth, Console.WindowWidth),
|
||||
Console.WindowHeight);
|
||||
top = 0;
|
||||
} else {
|
||||
size = new Size (Console.BufferWidth, Console.BufferHeight);
|
||||
top = e;
|
||||
}
|
||||
cols = size.Width;
|
||||
rows = size.Height;
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
if (!winChanging) {
|
||||
TerminalResized.Invoke ();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public override void SetColors (short foregroundColorId, short backgroundColorId)
|
||||
{
|
||||
throw new NotImplementedException ();
|
||||
}
|
||||
|
||||
public override void CookMouse ()
|
||||
@@ -424,14 +523,15 @@ namespace Terminal.Gui {
|
||||
/// <remarks>
|
||||
/// This implementation is used for NetDriver.
|
||||
/// </remarks>
|
||||
public class NetMainLoop : IMainLoopDriver {
|
||||
internal class NetMainLoop : IMainLoopDriver {
|
||||
ManualResetEventSlim keyReady = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim winChange = new ManualResetEventSlim (false);
|
||||
ConsoleKeyInfo? keyResult = null;
|
||||
Queue<ConsoleKeyInfo?> keyResult = new Queue<ConsoleKeyInfo?> ();
|
||||
MainLoop mainLoop;
|
||||
ConsoleDriver consoleDriver;
|
||||
bool winChanged;
|
||||
int newTop;
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
||||
|
||||
/// <summary>
|
||||
@@ -439,6 +539,11 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
public Action<ConsoleKeyInfo> KeyPressed;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the window is changed.
|
||||
/// </summary>
|
||||
public Action<int> WinChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the class with the console driver.
|
||||
/// </summary>
|
||||
@@ -449,7 +554,7 @@ namespace Terminal.Gui {
|
||||
public NetMainLoop (ConsoleDriver consoleDriver = null)
|
||||
{
|
||||
if (consoleDriver == null) {
|
||||
throw new ArgumentNullException ("console driver instance must be provided.");
|
||||
throw new ArgumentNullException ("Console driver instance must be provided.");
|
||||
}
|
||||
this.consoleDriver = consoleDriver;
|
||||
}
|
||||
@@ -459,7 +564,9 @@ namespace Terminal.Gui {
|
||||
while (true) {
|
||||
waitForProbe.Wait ();
|
||||
waitForProbe.Reset ();
|
||||
keyResult = Console.ReadKey (true);
|
||||
if (keyResult.Count == 0) {
|
||||
keyResult.Enqueue (Console.ReadKey (true));
|
||||
}
|
||||
keyReady.Set ();
|
||||
}
|
||||
}
|
||||
@@ -475,12 +582,23 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
int lastWindowHeight;
|
||||
void WaitWinChange ()
|
||||
{
|
||||
while (true) {
|
||||
if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows
|
||||
|| Console.WindowTop != consoleDriver.Top) { // Top only working on Windows.
|
||||
return;
|
||||
if (!consoleDriver.HeightAsBuffer) {
|
||||
if (Console.WindowWidth != consoleDriver.Cols || Console.WindowHeight != consoleDriver.Rows) {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (Console.BufferWidth != consoleDriver.Cols || Console.BufferHeight != consoleDriver.Rows
|
||||
|| Console.WindowTop != consoleDriver.Top
|
||||
|| Console.WindowHeight != lastWindowHeight) {
|
||||
// Top only working on Windows.
|
||||
newTop = Console.WindowTop;
|
||||
lastWindowHeight = Console.WindowHeight;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -499,8 +617,6 @@ namespace Terminal.Gui {
|
||||
|
||||
bool IMainLoopDriver.EventsPending (bool wait)
|
||||
{
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
|
||||
waitForProbe.Set ();
|
||||
winChange.Set ();
|
||||
|
||||
@@ -519,7 +635,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
if (!tokenSource.IsCancellationRequested) {
|
||||
return keyResult.HasValue || CheckTimers (wait, out _) || winChanged;
|
||||
return keyResult.Count > 0 || CheckTimers (wait, out _) || winChanged;
|
||||
}
|
||||
|
||||
tokenSource.Dispose ();
|
||||
@@ -552,14 +668,12 @@ namespace Terminal.Gui {
|
||||
|
||||
void IMainLoopDriver.MainIteration ()
|
||||
{
|
||||
if (keyResult.HasValue) {
|
||||
var kr = keyResult;
|
||||
keyResult = null;
|
||||
KeyPressed?.Invoke (kr.Value);
|
||||
if (keyResult.Count > 0) {
|
||||
KeyPressed?.Invoke (keyResult.Dequeue ().Value);
|
||||
}
|
||||
if (winChanged) {
|
||||
winChanged = false;
|
||||
consoleDriver.Refresh ();
|
||||
WinChanged?.Invoke (newTop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,32 @@ namespace Terminal.Gui {
|
||||
return WriteConsoleOutput (ScreenBuffer, charInfoBuffer, coords, new Coord () { X = window.Left, Y = window.Top }, ref window);
|
||||
}
|
||||
|
||||
public void ReadFromConsoleOutput (Size size, Coord coords)
|
||||
{
|
||||
ScreenBuffer = CreateConsoleScreenBuffer (
|
||||
DesiredAccess.GenericRead | DesiredAccess.GenericWrite,
|
||||
ShareMode.FileShareRead | ShareMode.FileShareWrite,
|
||||
IntPtr.Zero,
|
||||
1,
|
||||
IntPtr.Zero
|
||||
);
|
||||
if (ScreenBuffer == INVALID_HANDLE_VALUE) {
|
||||
var err = Marshal.GetLastWin32Error ();
|
||||
|
||||
if (err != 0)
|
||||
throw new System.ComponentModel.Win32Exception (err);
|
||||
}
|
||||
|
||||
if (!SetConsoleActiveScreenBuffer (ScreenBuffer)) {
|
||||
var err = Marshal.GetLastWin32Error ();
|
||||
throw new System.ComponentModel.Win32Exception (err);
|
||||
}
|
||||
|
||||
OriginalStdOutChars = new CharInfo [size.Height * size.Width];
|
||||
SmallRect window = new SmallRect ();
|
||||
ReadConsoleOutput (OutputHandle, OriginalStdOutChars, coords, new Coord () { X = 0, Y = 0 }, ref window);
|
||||
}
|
||||
|
||||
public bool SetCursorPosition (Coord position)
|
||||
{
|
||||
return SetConsoleCursorPosition (ScreenBuffer, position);
|
||||
@@ -207,7 +233,7 @@ namespace Terminal.Gui {
|
||||
public override string ToString () => $"({X},{Y})";
|
||||
};
|
||||
|
||||
internal struct WindowBufferSizeRecord {
|
||||
public struct WindowBufferSizeRecord {
|
||||
public Coordinate size;
|
||||
|
||||
public WindowBufferSizeRecord (short x, short y)
|
||||
@@ -355,6 +381,20 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
public struct ConsoleKeyInfoEx {
|
||||
public ConsoleKeyInfo consoleKeyInfo;
|
||||
public bool CapsLock;
|
||||
public bool NumLock;
|
||||
|
||||
public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
|
||||
{
|
||||
this.consoleKeyInfo = consoleKeyInfo;
|
||||
CapsLock = capslock;
|
||||
NumLock = numlock;
|
||||
}
|
||||
}
|
||||
|
||||
[DllImport ("kernel32.dll", SetLastError = true)]
|
||||
static extern IntPtr GetStdHandle (int nStdHandle);
|
||||
|
||||
@@ -431,231 +471,106 @@ namespace Terminal.Gui {
|
||||
|
||||
return numberEventsRead == 0
|
||||
? null
|
||||
: new [] {Marshal.PtrToStructure<InputRecord> (pRecord)};
|
||||
: new [] { Marshal.PtrToStructure<InputRecord> (pRecord) };
|
||||
} catch (Exception) {
|
||||
return null;
|
||||
} finally {
|
||||
Marshal.FreeHGlobal (pRecord);
|
||||
}
|
||||
}
|
||||
#if false // Not needed on the constructor. Perhaps could be used on resizing. To study.
|
||||
|
||||
// Not needed on the constructor. Perhaps could be used on resizing. To study.
|
||||
[DllImport ("kernel32.dll", ExactSpelling = true)]
|
||||
private static extern IntPtr GetConsoleWindow ();
|
||||
static extern IntPtr GetConsoleWindow ();
|
||||
|
||||
[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||
private static extern bool ShowWindow (IntPtr hWnd, int nCmdShow);
|
||||
[DllImport ("user32.dll")]
|
||||
[return: MarshalAs (UnmanagedType.Bool)]
|
||||
static extern bool GetWindowPlacement (IntPtr hWnd, ref WindowPlacement lpwndpl);
|
||||
|
||||
[DllImport ("user32.dll", SetLastError = true)]
|
||||
[return: MarshalAs (UnmanagedType.Bool)]
|
||||
static extern bool SetWindowPlacement (IntPtr hWnd, [In] ref WindowPlacement lpwndpl);
|
||||
|
||||
internal struct WindowPlacement {
|
||||
public int length;
|
||||
public int flags;
|
||||
public int showCmd;
|
||||
public System.Drawing.Point ptMinPosition;
|
||||
public System.Drawing.Point ptMaxPosition;
|
||||
public System.Drawing.Rectangle rcNormalPosition;
|
||||
public System.Drawing.Rectangle rcDevice;
|
||||
}
|
||||
|
||||
// flags
|
||||
public const int WPF_SET_MIN_POSITION = 1;
|
||||
public const int WPF_RESTORE_TO_MAXIMIZED = 2;
|
||||
public const int WPF_ASYNC_WINDOWPLACEMENT = 4;
|
||||
|
||||
// showCmd
|
||||
public const int HIDE = 0;
|
||||
public const int MAXIMIZE = 3;
|
||||
public const int NORMAL = 1;
|
||||
public const int SHOW_MINIMIZED = 2;
|
||||
public const int SHOW_MAXIMIZED = 3;
|
||||
public const int SHOW_NOACTIVATE = 4;
|
||||
public const int SHOW = 5;
|
||||
public const int MINIMIZE = 6;
|
||||
public const int SHOW_MIN_NOACTIVE = 7;
|
||||
public const int SHOW_NA = 8;
|
||||
public const int RESTORE = 9;
|
||||
public const int SHOW_DEFAULT = 10;
|
||||
public const int FORCE_MINIMIZE = 11;
|
||||
|
||||
internal void ShowWindow (int state)
|
||||
internal WindowPlacement GetWindow ()
|
||||
{
|
||||
IntPtr thisConsole = GetConsoleWindow ();
|
||||
ShowWindow (thisConsole, state);
|
||||
}
|
||||
#endif
|
||||
#if false // See: https://github.com/migueldeicaza/gui.cs/issues/357
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
public struct SMALL_RECT {
|
||||
public short Left;
|
||||
public short Top;
|
||||
public short Right;
|
||||
public short Bottom;
|
||||
WindowPlacement placement = new WindowPlacement {
|
||||
length = Marshal.SizeOf (typeof (WindowPlacement))
|
||||
};
|
||||
GetWindowPlacement (thisConsole, ref placement);
|
||||
|
||||
public SMALL_RECT (short Left, short Top, short Right, short Bottom)
|
||||
{
|
||||
this.Left = Left;
|
||||
this.Top = Top;
|
||||
this.Right = Right;
|
||||
this.Bottom = Bottom;
|
||||
}
|
||||
return placement;
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
public struct CONSOLE_SCREEN_BUFFER_INFO {
|
||||
public int dwSize;
|
||||
public int dwCursorPosition;
|
||||
public short wAttributes;
|
||||
public SMALL_RECT srWindow;
|
||||
public int dwMaximumWindowSize;
|
||||
}
|
||||
|
||||
[DllImport ("kernel32.dll", SetLastError = true)]
|
||||
static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out CONSOLE_SCREEN_BUFFER_INFO ConsoleScreenBufferInfo);
|
||||
|
||||
// Theoretically GetConsoleScreenBuffer height should give the console Windoww size
|
||||
// It does not work, however, and always returns the size the window was initially created at
|
||||
internal Size GetWindowSize ()
|
||||
internal void SetWindow (int showCmd)
|
||||
{
|
||||
var consoleScreenBufferInfo = new CONSOLE_SCREEN_BUFFER_INFO ();
|
||||
//consoleScreenBufferInfo.dwSize = Marshal.SizeOf (typeof (CONSOLE_SCREEN_BUFFER_INFO));
|
||||
GetConsoleScreenBufferInfo (OutputHandle, out consoleScreenBufferInfo);
|
||||
return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left,
|
||||
consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top);
|
||||
IntPtr thisConsole = GetConsoleWindow ();
|
||||
WindowPlacement placement = new WindowPlacement {
|
||||
length = Marshal.SizeOf (typeof (WindowPlacement)),
|
||||
showCmd = showCmd
|
||||
};
|
||||
SetWindowPlacement (thisConsole, ref placement);
|
||||
}
|
||||
|
||||
#if false
|
||||
[DllImport ("kernel32.dll", SetLastError = true)]
|
||||
static extern bool GetConsoleScreenBufferInfo (IntPtr hConsoleOutput, out ConsoleScreenBufferInfo ConsoleScreenBufferInfo);
|
||||
|
||||
// Theoretically GetConsoleScreenBuffer height should give the console Window size, but the Top is always 0.
|
||||
// It does not work, however, and always returns the size the window was initially created at
|
||||
internal Size GetWindowSize (IntPtr handle)
|
||||
{
|
||||
GetConsoleScreenBufferInfo (handle, out ConsoleScreenBufferInfo consoleScreenBufferInfo);
|
||||
return new Size (consoleScreenBufferInfo.srWindow.Right - consoleScreenBufferInfo.srWindow.Left + 1,
|
||||
consoleScreenBufferInfo.srWindow.Bottom - consoleScreenBufferInfo.srWindow.Top + 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
internal class WindowsDriver : ConsoleDriver, IMainLoopDriver {
|
||||
internal class WindowsDriver : ConsoleDriver {
|
||||
static bool sync = false;
|
||||
ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
||||
MainLoop mainLoop;
|
||||
WindowsConsole.CharInfo [] OutputBuffer;
|
||||
int cols, rows;
|
||||
int cols, rows, top;
|
||||
WindowsConsole winConsole;
|
||||
WindowsConsole.SmallRect damageRegion;
|
||||
|
||||
public override int Cols => cols;
|
||||
public override int Rows => rows;
|
||||
public override int Top => 0;
|
||||
public override int Top => top;
|
||||
public override bool HeightAsBuffer { get; set; }
|
||||
|
||||
public WindowsDriver ()
|
||||
{
|
||||
winConsole = new WindowsConsole ();
|
||||
|
||||
SetupColorsAndBorders ();
|
||||
|
||||
cols = Console.WindowWidth;
|
||||
rows = Console.WindowHeight;
|
||||
#if false
|
||||
winConsole.ShowWindow (WindowsConsole.RESTORE);
|
||||
#endif
|
||||
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
|
||||
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
|
||||
Task.Run ((Action)WindowsInputHandler);
|
||||
}
|
||||
|
||||
private void SetupColorsAndBorders ()
|
||||
{
|
||||
Colors.TopLevel = new ColorScheme ();
|
||||
Colors.Base = new ColorScheme ();
|
||||
Colors.Dialog = new ColorScheme ();
|
||||
Colors.Menu = new ColorScheme ();
|
||||
Colors.Error = new ColorScheme ();
|
||||
|
||||
Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
|
||||
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.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.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
|
||||
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
|
||||
Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
|
||||
Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
|
||||
Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
|
||||
Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
|
||||
Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
[StructLayout (LayoutKind.Sequential)]
|
||||
public struct ConsoleKeyInfoEx {
|
||||
public ConsoleKeyInfo consoleKeyInfo;
|
||||
public bool CapsLock;
|
||||
public bool NumLock;
|
||||
|
||||
public ConsoleKeyInfoEx (ConsoleKeyInfo consoleKeyInfo, bool capslock, bool numlock)
|
||||
{
|
||||
this.consoleKeyInfo = consoleKeyInfo;
|
||||
CapsLock = capslock;
|
||||
NumLock = numlock;
|
||||
}
|
||||
}
|
||||
|
||||
// The records that we keep fetching
|
||||
WindowsConsole.InputRecord [] result, records = new WindowsConsole.InputRecord [1];
|
||||
|
||||
void WindowsInputHandler ()
|
||||
{
|
||||
while (true) {
|
||||
waitForProbe.Wait ();
|
||||
waitForProbe.Reset ();
|
||||
|
||||
result = winConsole.ReadConsoleInput ();
|
||||
|
||||
eventReady.Set ();
|
||||
}
|
||||
}
|
||||
|
||||
void IMainLoopDriver.Setup (MainLoop mainLoop)
|
||||
{
|
||||
this.mainLoop = mainLoop;
|
||||
}
|
||||
|
||||
void IMainLoopDriver.Wakeup ()
|
||||
{
|
||||
//tokenSource.Cancel ();
|
||||
eventReady.Reset ();
|
||||
eventReady.Set ();
|
||||
}
|
||||
|
||||
bool IMainLoopDriver.EventsPending (bool wait)
|
||||
{
|
||||
if (CheckTimers (wait, out var waitTimeout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
result = null;
|
||||
waitForProbe.Set ();
|
||||
|
||||
try {
|
||||
if (!tokenSource.IsCancellationRequested) {
|
||||
eventReady.Wait (waitTimeout, tokenSource.Token);
|
||||
}
|
||||
} catch (OperationCanceledException) {
|
||||
return true;
|
||||
} finally {
|
||||
eventReady.Reset ();
|
||||
}
|
||||
|
||||
if (!tokenSource.IsCancellationRequested) {
|
||||
return result != null || CheckTimers (wait, out waitTimeout);
|
||||
}
|
||||
|
||||
tokenSource.Dispose ();
|
||||
tokenSource = new CancellationTokenSource ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckTimers (bool wait, out int waitTimeout)
|
||||
{
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
|
||||
if (mainLoop.timeouts.Count > 0) {
|
||||
waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
|
||||
if (waitTimeout < 0)
|
||||
return true;
|
||||
} else {
|
||||
waitTimeout = -1;
|
||||
}
|
||||
|
||||
if (!wait)
|
||||
waitTimeout = 0;
|
||||
|
||||
int ic;
|
||||
lock (mainLoop.idleHandlers) {
|
||||
ic = mainLoop.idleHandlers.Count;
|
||||
}
|
||||
|
||||
return ic > 0;
|
||||
public WindowsConsole WinConsole {
|
||||
get => winConsole;
|
||||
private set => winConsole = value;
|
||||
}
|
||||
|
||||
Action<KeyEvent> keyHandler;
|
||||
@@ -663,20 +578,51 @@ namespace Terminal.Gui {
|
||||
Action<KeyEvent> keyUpHandler;
|
||||
Action<MouseEvent> mouseHandler;
|
||||
|
||||
public WindowsDriver ()
|
||||
{
|
||||
winConsole = new WindowsConsole ();
|
||||
}
|
||||
|
||||
bool winChanging;
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> keyHandler, Action<KeyEvent> keyDownHandler, Action<KeyEvent> keyUpHandler, Action<MouseEvent> mouseHandler)
|
||||
{
|
||||
this.keyHandler = keyHandler;
|
||||
this.keyDownHandler = keyDownHandler;
|
||||
this.keyUpHandler = keyUpHandler;
|
||||
this.mouseHandler = mouseHandler;
|
||||
|
||||
var mLoop = mainLoop.Driver as WindowsMainLoop;
|
||||
|
||||
mLoop.ProcessInput = (e) => ProcessInput (e);
|
||||
|
||||
mLoop.WinChanged = (e) => ChangeWin (e);
|
||||
}
|
||||
|
||||
void IMainLoopDriver.MainIteration ()
|
||||
void ChangeWin (Size size)
|
||||
{
|
||||
if (result == null)
|
||||
return;
|
||||
if (!HeightAsBuffer) {
|
||||
winChanging = true;
|
||||
top = 0;
|
||||
cols = size.Width;
|
||||
rows = size.Height;
|
||||
var bufferCoords = new WindowsConsole.Coord () {
|
||||
X = (short)cols,
|
||||
Y = (short)rows
|
||||
};
|
||||
winConsole.ReadFromConsoleOutput (size, bufferCoords);
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
if (!winChanging) {
|
||||
TerminalResized.Invoke ();
|
||||
} else {
|
||||
System.Diagnostics.Debugger.Break ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var inputEvent = result [0];
|
||||
void ProcessInput (WindowsConsole.InputRecord inputEvent)
|
||||
{
|
||||
switch (inputEvent.EventType) {
|
||||
case WindowsConsole.EventType.Key:
|
||||
var map = MapKey (ToConsoleKeyInfoEx (inputEvent.KeyEvent));
|
||||
@@ -766,20 +712,18 @@ namespace Terminal.Gui {
|
||||
break;
|
||||
|
||||
case WindowsConsole.EventType.WindowBufferSize:
|
||||
cols = inputEvent.WindowBufferSizeEvent.size.X;
|
||||
rows = inputEvent.WindowBufferSizeEvent.size.Y;
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
TerminalResized?.Invoke ();
|
||||
if (HeightAsBuffer) {
|
||||
cols = inputEvent.WindowBufferSizeEvent.size.X;
|
||||
rows = inputEvent.WindowBufferSizeEvent.size.Y;
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
TerminalResized?.Invoke ();
|
||||
}
|
||||
break;
|
||||
|
||||
case WindowsConsole.EventType.Focus:
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
|
||||
WindowsConsole.ButtonState? LastMouseButtonPressed = null;
|
||||
@@ -871,23 +815,23 @@ namespace Terminal.Gui {
|
||||
Y = mouseEvent.MousePosition.Y
|
||||
};
|
||||
//if (p == point) {
|
||||
switch (LastMouseButtonPressed) {
|
||||
case WindowsConsole.ButtonState.Button1Pressed:
|
||||
mouseFlag = MouseFlags.Button1Clicked;
|
||||
break;
|
||||
switch (LastMouseButtonPressed) {
|
||||
case WindowsConsole.ButtonState.Button1Pressed:
|
||||
mouseFlag = MouseFlags.Button1Clicked;
|
||||
break;
|
||||
|
||||
case WindowsConsole.ButtonState.Button2Pressed:
|
||||
mouseFlag = MouseFlags.Button2Clicked;
|
||||
break;
|
||||
case WindowsConsole.ButtonState.Button2Pressed:
|
||||
mouseFlag = MouseFlags.Button2Clicked;
|
||||
break;
|
||||
|
||||
case WindowsConsole.ButtonState.RightmostButtonPressed:
|
||||
mouseFlag = MouseFlags.Button3Clicked;
|
||||
break;
|
||||
}
|
||||
point = new Point () {
|
||||
X = mouseEvent.MousePosition.X,
|
||||
Y = mouseEvent.MousePosition.Y
|
||||
};
|
||||
case WindowsConsole.ButtonState.RightmostButtonPressed:
|
||||
mouseFlag = MouseFlags.Button3Clicked;
|
||||
break;
|
||||
}
|
||||
point = new Point () {
|
||||
X = mouseEvent.MousePosition.X,
|
||||
Y = mouseEvent.MousePosition.Y
|
||||
};
|
||||
//} else {
|
||||
// mouseFlag = 0;
|
||||
//}
|
||||
@@ -1009,7 +953,7 @@ namespace Terminal.Gui {
|
||||
|
||||
KeyModifiers keyModifiers;
|
||||
|
||||
public ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
|
||||
public WindowsConsole.ConsoleKeyInfoEx ToConsoleKeyInfoEx (WindowsConsole.KeyEventRecord keyEvent)
|
||||
{
|
||||
var state = keyEvent.dwControlKeyState;
|
||||
|
||||
@@ -1036,10 +980,10 @@ namespace Terminal.Gui {
|
||||
keyModifiers.Scrolllock = scrolllock;
|
||||
|
||||
var ConsoleKeyInfo = new ConsoleKeyInfo (keyEvent.UnicodeChar, (ConsoleKey)keyEvent.wVirtualKeyCode, shift, alt, control);
|
||||
return new ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
|
||||
return new WindowsConsole.ConsoleKeyInfoEx (ConsoleKeyInfo, capslock, numlock);
|
||||
}
|
||||
|
||||
public Key MapKey (ConsoleKeyInfoEx keyInfoEx)
|
||||
public Key MapKey (WindowsConsole.ConsoleKeyInfoEx keyInfoEx)
|
||||
{
|
||||
var keyInfo = keyInfoEx.consoleKeyInfo;
|
||||
switch (keyInfo.Key) {
|
||||
@@ -1163,7 +1107,7 @@ namespace Terminal.Gui {
|
||||
return (Key)(0xffffffff);
|
||||
}
|
||||
|
||||
private Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
|
||||
Key MapKeyModifiers (ConsoleKeyInfo keyInfo, Key key)
|
||||
{
|
||||
Key keyMod = new Key ();
|
||||
if ((keyInfo.Modifiers & ConsoleModifiers.Shift) != 0)
|
||||
@@ -1179,7 +1123,48 @@ namespace Terminal.Gui {
|
||||
public override void Init (Action terminalResized)
|
||||
{
|
||||
TerminalResized = terminalResized;
|
||||
SetupColorsAndBorders ();
|
||||
|
||||
cols = Console.WindowWidth;
|
||||
rows = Console.WindowHeight;
|
||||
#if false
|
||||
winConsole.ShowWindow (WindowsConsole.RESTORE);
|
||||
#endif
|
||||
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
|
||||
|
||||
ResizeScreen ();
|
||||
UpdateOffScreen ();
|
||||
|
||||
Colors.TopLevel = new ColorScheme ();
|
||||
Colors.Base = new ColorScheme ();
|
||||
Colors.Dialog = new ColorScheme ();
|
||||
Colors.Menu = new ColorScheme ();
|
||||
Colors.Error = new ColorScheme ();
|
||||
|
||||
Colors.TopLevel.Normal = MakeColor (ConsoleColor.Green, ConsoleColor.Black);
|
||||
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.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.Menu.Normal = MakeColor (ConsoleColor.White, ConsoleColor.DarkGray);
|
||||
Colors.Menu.Focus = MakeColor (ConsoleColor.White, ConsoleColor.Black);
|
||||
Colors.Menu.HotNormal = MakeColor (ConsoleColor.Yellow, ConsoleColor.DarkGray);
|
||||
Colors.Menu.HotFocus = MakeColor (ConsoleColor.Yellow, ConsoleColor.Black);
|
||||
Colors.Menu.Disabled = MakeColor (ConsoleColor.Gray, ConsoleColor.DarkGray);
|
||||
|
||||
Colors.Dialog.Normal = MakeColor (ConsoleColor.Black, ConsoleColor.Gray);
|
||||
Colors.Dialog.Focus = MakeColor (ConsoleColor.Black, ConsoleColor.DarkGray);
|
||||
Colors.Dialog.HotNormal = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.Gray);
|
||||
Colors.Dialog.HotFocus = MakeColor (ConsoleColor.DarkBlue, ConsoleColor.DarkGray);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
void ResizeScreen ()
|
||||
@@ -1196,12 +1181,15 @@ namespace Terminal.Gui {
|
||||
|
||||
void UpdateOffScreen ()
|
||||
{
|
||||
for (int row = 0; row < rows; row++)
|
||||
for (int row = 0; row < rows; row++) {
|
||||
for (int col = 0; col < cols; col++) {
|
||||
int position = row * cols + col;
|
||||
OutputBuffer [position].Attributes = (ushort)Colors.TopLevel.Normal;
|
||||
OutputBuffer [position].Char.UnicodeChar = ' ';
|
||||
}
|
||||
}
|
||||
|
||||
winChanging = false;
|
||||
}
|
||||
|
||||
int ccol, crow;
|
||||
@@ -1245,7 +1233,6 @@ namespace Terminal.Gui {
|
||||
}
|
||||
|
||||
int currentAttribute;
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
||||
|
||||
public override void SetAttribute (Attribute c)
|
||||
{
|
||||
@@ -1298,16 +1285,16 @@ namespace Terminal.Gui {
|
||||
Y = (short)Clip.Height
|
||||
};
|
||||
|
||||
var window = new WindowsConsole.SmallRect () {
|
||||
Top = 0,
|
||||
Left = 0,
|
||||
Right = (short)Clip.Right,
|
||||
Bottom = (short)Clip.Bottom
|
||||
};
|
||||
//var window = new WindowsConsole.SmallRect () {
|
||||
// Top = 0,
|
||||
// Left = 0,
|
||||
// Right = (short)Clip.Right,
|
||||
// Bottom = (short)Clip.Bottom
|
||||
//};
|
||||
|
||||
UpdateCursor ();
|
||||
winConsole.WriteToConsole (OutputBuffer, bufferCoords, damageRegion);
|
||||
// System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
|
||||
//System.Diagnostics.Debugger.Log(0, "debug", $"Region={damageRegion.Right - damageRegion.Left},{damageRegion.Bottom - damageRegion.Top}\n");
|
||||
WindowsConsole.SmallRect.MakeEmpty (ref damageRegion);
|
||||
}
|
||||
|
||||
@@ -1325,7 +1312,7 @@ namespace Terminal.Gui {
|
||||
winConsole.Cleanup ();
|
||||
}
|
||||
|
||||
#region Unused
|
||||
#region Unused
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
}
|
||||
@@ -1353,8 +1340,172 @@ namespace Terminal.Gui {
|
||||
public override void CookMouse ()
|
||||
{
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mainloop intended to be used with the <see cref="WindowsDriver"/>, and can
|
||||
/// only be used on Windows.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This implementation is used for WindowsDriver.
|
||||
/// </remarks>
|
||||
internal class WindowsMainLoop : IMainLoopDriver {
|
||||
ManualResetEventSlim eventReady = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim waitForProbe = new ManualResetEventSlim (false);
|
||||
ManualResetEventSlim winChange = new ManualResetEventSlim (false);
|
||||
MainLoop mainLoop;
|
||||
ConsoleDriver consoleDriver;
|
||||
WindowsConsole winConsole;
|
||||
bool winChanged;
|
||||
WindowsConsole.WindowPlacement windowPlacement;
|
||||
Size windowSize;
|
||||
CancellationTokenSource tokenSource = new CancellationTokenSource ();
|
||||
|
||||
// The records that we keep fetching
|
||||
WindowsConsole.InputRecord [] result = new WindowsConsole.InputRecord [1];
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when a Key is pressed or released.
|
||||
/// </summary>
|
||||
public Action<WindowsConsole.InputRecord> ProcessInput;
|
||||
|
||||
/// <summary>
|
||||
/// Invoked when the window is changed.
|
||||
/// </summary>
|
||||
public Action<Size> WinChanged;
|
||||
|
||||
public WindowsMainLoop (ConsoleDriver consoleDriver = null)
|
||||
{
|
||||
if (consoleDriver == null) {
|
||||
throw new ArgumentNullException ("Console driver instance must be provided.");
|
||||
}
|
||||
this.consoleDriver = consoleDriver;
|
||||
winConsole = ((WindowsDriver)consoleDriver).WinConsole;
|
||||
}
|
||||
|
||||
void IMainLoopDriver.Setup (MainLoop mainLoop)
|
||||
{
|
||||
this.mainLoop = mainLoop;
|
||||
Task.Run (WindowsInputHandler);
|
||||
Task.Run (CheckWinChange);
|
||||
}
|
||||
|
||||
void WindowsInputHandler ()
|
||||
{
|
||||
while (true) {
|
||||
waitForProbe.Wait ();
|
||||
waitForProbe.Reset ();
|
||||
|
||||
result = winConsole.ReadConsoleInput ();
|
||||
|
||||
eventReady.Set ();
|
||||
}
|
||||
}
|
||||
|
||||
void CheckWinChange ()
|
||||
{
|
||||
while (true) {
|
||||
winChange.Wait ();
|
||||
winChange.Reset ();
|
||||
WaitWinChange ();
|
||||
winChanged = true;
|
||||
eventReady.Set ();
|
||||
}
|
||||
}
|
||||
|
||||
void WaitWinChange ()
|
||||
{
|
||||
while (true) {
|
||||
if (!consoleDriver.HeightAsBuffer) {
|
||||
windowPlacement = winConsole.GetWindow ();
|
||||
if (windowPlacement.rcNormalPosition.Size.Height > -1) {
|
||||
windowSize = new Size (Math.Max (((windowPlacement.rcNormalPosition.Size.Width -
|
||||
windowPlacement.rcNormalPosition.X) / 8) - 2, 0),
|
||||
Math.Max (((windowPlacement.rcNormalPosition.Size.Height -
|
||||
windowPlacement.rcNormalPosition.Y) / 16) - 7, 0));
|
||||
if (windowPlacement.showCmd != WindowsConsole.SHOW_MAXIMIZED
|
||||
&& (windowSize.Width != consoleDriver.Cols || windowSize.Height != consoleDriver.Rows)) {
|
||||
return;
|
||||
} else if (windowPlacement.showCmd == WindowsConsole.SHOW_MAXIMIZED
|
||||
&& (Console.LargestWindowWidth != consoleDriver.Cols || Console.LargestWindowHeight != consoleDriver.Rows)) {
|
||||
windowSize = new Size (Console.LargestWindowWidth, Console.LargestWindowHeight);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IMainLoopDriver.Wakeup ()
|
||||
{
|
||||
//tokenSource.Cancel ();
|
||||
eventReady.Set ();
|
||||
}
|
||||
|
||||
bool IMainLoopDriver.EventsPending (bool wait)
|
||||
{
|
||||
if (CheckTimers (wait, out var waitTimeout)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
//result = null;
|
||||
waitForProbe.Set ();
|
||||
winChange.Set ();
|
||||
|
||||
try {
|
||||
if (!tokenSource.IsCancellationRequested) {
|
||||
eventReady.Wait (waitTimeout, tokenSource.Token);
|
||||
}
|
||||
} catch (OperationCanceledException) {
|
||||
return true;
|
||||
} finally {
|
||||
eventReady.Reset ();
|
||||
}
|
||||
|
||||
if (!tokenSource.IsCancellationRequested) {
|
||||
return result != null || CheckTimers (wait, out waitTimeout) || winChanged;
|
||||
}
|
||||
|
||||
tokenSource.Dispose ();
|
||||
tokenSource = new CancellationTokenSource ();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool CheckTimers (bool wait, out int waitTimeout)
|
||||
{
|
||||
long now = DateTime.UtcNow.Ticks;
|
||||
|
||||
if (mainLoop.timeouts.Count > 0) {
|
||||
waitTimeout = (int)((mainLoop.timeouts.Keys [0] - now) / TimeSpan.TicksPerMillisecond);
|
||||
if (waitTimeout < 0)
|
||||
return true;
|
||||
} else {
|
||||
waitTimeout = -1;
|
||||
}
|
||||
|
||||
if (!wait)
|
||||
waitTimeout = 0;
|
||||
|
||||
int ic;
|
||||
lock (mainLoop.idleHandlers) {
|
||||
ic = mainLoop.idleHandlers.Count;
|
||||
}
|
||||
|
||||
return ic > 0;
|
||||
}
|
||||
|
||||
void IMainLoopDriver.MainIteration ()
|
||||
{
|
||||
if (result != null) {
|
||||
var inputEvent = result [0];
|
||||
result = null;
|
||||
ProcessInput?.Invoke (inputEvent);
|
||||
}
|
||||
if (winChanged) {
|
||||
winChanged = false;
|
||||
WinChanged?.Invoke (windowSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Terminal.Gui {
|
||||
/// The current <see cref="ConsoleDriver"/> in use.
|
||||
/// </summary>
|
||||
public static ConsoleDriver Driver;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="Toplevel"/> object used for the application on startup (<seealso cref="Application.Top"/>)
|
||||
/// </summary>
|
||||
@@ -73,11 +73,32 @@ namespace Terminal.Gui {
|
||||
public static Toplevel Current { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TThe current <see cref="View"/> object being redrawn.
|
||||
/// The current <see cref="View"/> object being redrawn.
|
||||
/// </summary>
|
||||
/// /// <value>The current.</value>
|
||||
public static View CurrentView { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The current <see cref="ConsoleDriver.HeightAsBuffer"/> used in the terminal.
|
||||
/// </summary>
|
||||
public static bool HeightAsBuffer {
|
||||
get {
|
||||
if (Driver == null) {
|
||||
throw new ArgumentNullException ("The driver must be initialized first.");
|
||||
}
|
||||
return Driver.HeightAsBuffer;
|
||||
}
|
||||
set {
|
||||
if (Driver == null) {
|
||||
throw new ArgumentNullException ("The driver must be initialized first.");
|
||||
}
|
||||
if (Driver.HeightAsBuffer != value) {
|
||||
Driver.HeightAsBuffer = value;
|
||||
Driver.Refresh ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The <see cref="MainLoop"/> driver for the application
|
||||
/// </summary>
|
||||
@@ -167,7 +188,7 @@ namespace Terminal.Gui {
|
||||
static void Init (Func<Toplevel> topLevelFactory, ConsoleDriver driver = null, IMainLoopDriver mainLoopDriver = null)
|
||||
{
|
||||
if (_initialized && driver == null) return;
|
||||
|
||||
|
||||
// Used only for start debugging on Unix.
|
||||
//#if DEBUG
|
||||
// while (!System.Diagnostics.Debugger.IsAttached) {
|
||||
@@ -193,9 +214,8 @@ namespace Terminal.Gui {
|
||||
Driver = new NetDriver ();
|
||||
mainLoopDriver = new NetMainLoop (Driver);
|
||||
} else if (p == PlatformID.Win32NT || p == PlatformID.Win32S || p == PlatformID.Win32Windows) {
|
||||
var windowsDriver = new WindowsDriver ();
|
||||
mainLoopDriver = windowsDriver;
|
||||
Driver = windowsDriver;
|
||||
Driver = new WindowsDriver ();
|
||||
mainLoopDriver = new WindowsMainLoop (Driver);
|
||||
} else {
|
||||
mainLoopDriver = new UnixMainLoop ();
|
||||
Driver = new CursesDriver ();
|
||||
@@ -588,11 +608,11 @@ namespace Terminal.Gui {
|
||||
} else if (!wait) {
|
||||
return;
|
||||
}
|
||||
if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay)) {
|
||||
if (state.Toplevel != Top && (!Top.NeedDisplay.IsEmpty || Top.ChildNeedsDisplay || Top.LayoutNeeded)) {
|
||||
Top.Redraw (Top.Bounds);
|
||||
state.Toplevel.SetNeedsDisplay (state.Toplevel.Bounds);
|
||||
}
|
||||
if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay) {
|
||||
if (!state.Toplevel.NeedDisplay.IsEmpty || state.Toplevel.ChildNeedsDisplay || state.Toplevel.LayoutNeeded) {
|
||||
state.Toplevel.Redraw (state.Toplevel.Bounds);
|
||||
if (DebugDrawBounds) {
|
||||
DrawBounds (state.Toplevel);
|
||||
|
||||
@@ -546,6 +546,12 @@ namespace Terminal.Gui {
|
||||
/// </summary>
|
||||
public abstract int Top { get; }
|
||||
|
||||
/// <summary>
|
||||
/// If false height is measured by the window height and thus no scrolling.
|
||||
/// If true then height is measured by the buffer height, enabling scrolling.
|
||||
/// </summary>
|
||||
public abstract bool HeightAsBuffer { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the driver
|
||||
/// </summary>
|
||||
@@ -570,12 +576,10 @@ namespace Terminal.Gui {
|
||||
/// <returns></returns>
|
||||
public static Rune MakePrintable (Rune c)
|
||||
{
|
||||
if (c <= 0x1F) {
|
||||
// ASCII (C0) control characters.
|
||||
return new Rune (c + 0x2400);
|
||||
} else if (c >= 0x80 && c <= 0x9F) {
|
||||
if (c <= 0x1F || (c >= 0x80 && c <= 0x9F)) {
|
||||
// ASCII (C0) control characters.
|
||||
// C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1)
|
||||
return new Rune (0x25a1); // U+25A1, WHITE SQUARE, □:
|
||||
return new Rune (c + 0x2400);
|
||||
} else {
|
||||
return c;
|
||||
}
|
||||
|
||||
@@ -401,10 +401,10 @@ namespace Terminal.Gui {
|
||||
{
|
||||
EnsureVisibleBounds (top, top.Frame.X, top.Frame.Y, out int nx, out int ny);
|
||||
if ((nx != top.Frame.X || ny != top.Frame.Y) && top.LayoutStyle == LayoutStyle.Computed) {
|
||||
if (top.X is Pos.PosAbsolute && top.Bounds.X != nx) {
|
||||
if ((top.X == null || top.X is Pos.PosAbsolute) && top.Bounds.X != nx) {
|
||||
top.X = nx;
|
||||
}
|
||||
if (top.Y is Pos.PosAbsolute && top.Bounds.Y != ny) {
|
||||
if ((top.Y == null || top.Y is Pos.PosAbsolute) && top.Bounds.Y != ny) {
|
||||
top.Y = ny;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1314,10 +1314,13 @@ namespace Terminal.Gui {
|
||||
return;
|
||||
}
|
||||
|
||||
Application.CurrentView = this;
|
||||
|
||||
var clipRect = new Rect (Point.Empty, frame.Size);
|
||||
|
||||
if (ColorScheme != null)
|
||||
if (ColorScheme != null) {
|
||||
Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
|
||||
}
|
||||
|
||||
if (!ustring.IsNullOrEmpty (Text)) {
|
||||
Clear ();
|
||||
@@ -1333,14 +1336,14 @@ namespace Terminal.Gui {
|
||||
|
||||
if (subviews != null) {
|
||||
foreach (var view in subviews) {
|
||||
if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay) {
|
||||
if (!view.NeedDisplay.IsEmpty || view.ChildNeedsDisplay || view.LayoutNeeded) {
|
||||
if (view.Frame.IntersectsWith (clipRect) && (view.Frame.IntersectsWith (bounds) || bounds.X < 0 || bounds.Y < 0)) {
|
||||
if (view.LayoutNeeded)
|
||||
view.LayoutSubviews ();
|
||||
Application.CurrentView = view;
|
||||
|
||||
// Draw the subview
|
||||
// Use the view's bounds (view-relative; Location will always be (0,0) because
|
||||
// Use the view's bounds (view-relative; Location will always be (0,0)
|
||||
if (view.Visible) {
|
||||
view.Redraw (view.Bounds);
|
||||
}
|
||||
@@ -1350,6 +1353,7 @@ namespace Terminal.Gui {
|
||||
}
|
||||
}
|
||||
}
|
||||
ClearLayoutNeeded ();
|
||||
ClearNeedsDisplay ();
|
||||
}
|
||||
|
||||
|
||||
@@ -1415,7 +1415,7 @@ namespace Terminal.Gui {
|
||||
CloseMenu ();
|
||||
if (openedByAltKey) {
|
||||
openedByAltKey = false;
|
||||
LastFocused.SetFocus ();
|
||||
LastFocused?.SetFocus ();
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
using NStack;
|
||||
#define DRAW_CONTENT
|
||||
//#define BASE_DRAW_CONTENT
|
||||
|
||||
using NStack;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
@@ -58,15 +61,20 @@ namespace UICatalog {
|
||||
CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal),
|
||||
};
|
||||
|
||||
var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ());
|
||||
jumpList.X = Pos.X (label);
|
||||
jumpList.Y = Pos.Bottom (label);
|
||||
jumpList.Width = Dim.Fill ();
|
||||
var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ()) {
|
||||
X = Pos.X (label),
|
||||
Y = Pos.Bottom (label),
|
||||
Width = Dim.Fill (),
|
||||
SelectedItem = 8
|
||||
};
|
||||
jumpList.SelectedItemChanged += (args) => {
|
||||
_charMap.Start = radioItems[args.SelectedItem].start;
|
||||
_charMap.Start = radioItems [args.SelectedItem].start;
|
||||
};
|
||||
|
||||
Win.Add (jumpList);
|
||||
|
||||
jumpList.Refresh ();
|
||||
jumpList.SetFocus ();
|
||||
}
|
||||
|
||||
public override void Run ()
|
||||
@@ -91,11 +99,14 @@ namespace UICatalog {
|
||||
}
|
||||
int _start = 0x2500;
|
||||
|
||||
public const int H_SPACE = 2;
|
||||
public const int V_SPACE = 2;
|
||||
|
||||
public static int MaxCodePointVal => 0xE0FFF;
|
||||
|
||||
// Row Header + space + (space + char + space)
|
||||
public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
|
||||
public static int RowWidth => RowHeaderWidth + (" c".Length * 16);
|
||||
public static int RowWidth => RowHeaderWidth + (H_SPACE * 16);
|
||||
|
||||
public CharMap ()
|
||||
{
|
||||
@@ -109,10 +120,12 @@ namespace UICatalog {
|
||||
ShowHorizontalScrollIndicator = false;
|
||||
}
|
||||
};
|
||||
#if DRAW_CONTENT
|
||||
|
||||
DrawContent += CharMap_DrawContent;
|
||||
#endif
|
||||
}
|
||||
#if true
|
||||
|
||||
private void CharMap_DrawContent (Rect viewport)
|
||||
{
|
||||
//Rune ReplaceNonPrintables (Rune c)
|
||||
@@ -125,10 +138,10 @@ namespace UICatalog {
|
||||
//}
|
||||
|
||||
for (int header = 0; header < 16; header++) {
|
||||
Move (viewport.X + RowHeaderWidth + (header * 2), 0);
|
||||
Move (viewport.X + RowHeaderWidth + (header * H_SPACE), 0);
|
||||
Driver.AddStr ($" {header:x} ");
|
||||
}
|
||||
for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) {
|
||||
for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y+= V_SPACE) {
|
||||
int val = (-viewport.Y + row) * 16;
|
||||
if (val < MaxCodePointVal) {
|
||||
var rowLabel = $"U+{val / 16:x4}x";
|
||||
@@ -137,17 +150,17 @@ namespace UICatalog {
|
||||
var prevColWasWide = false;
|
||||
for (int col = 0; col < 16; col++) {
|
||||
var rune = new Rune ((uint)((uint)(-viewport.Y + row) * 16 + col));
|
||||
Move (viewport.X + RowHeaderWidth + (col * 2) + (prevColWasWide ? 0 : 1), 0 + y + 1);
|
||||
Move (viewport.X + RowHeaderWidth + (col * H_SPACE) + (prevColWasWide ? 0 : 1), y + 1);
|
||||
Driver.AddRune (rune);
|
||||
//prevColWasWide = Rune.ColumnWidth(rune) > 1;
|
||||
//prevColWasWide = Rune.ColumnWidth (rune) > 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
#if BASE_DRAW_CONTENT
|
||||
public override void OnDrawContent (Rect viewport)
|
||||
{
|
||||
CharMap_DrawContent(this, viewport);
|
||||
CharMap_DrawContent (viewport);
|
||||
base.OnDrawContent (viewport);
|
||||
}
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user