mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-26 15:57:56 +01:00
Some work on display flags
This commit is contained in:
578
Driver.cs
578
Driver.cs
@@ -5,328 +5,328 @@ using Unix.Terminal;
|
||||
|
||||
namespace Terminal {
|
||||
|
||||
/// <summary>
|
||||
/// Basic colors that can be used to set the foreground and background colors in console applications. These can only be
|
||||
/// </summary>
|
||||
public enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
Cyan,
|
||||
Red,
|
||||
Magenta,
|
||||
Brown,
|
||||
Gray,
|
||||
DarkGray,
|
||||
BrightBlue,
|
||||
BrightGreen,
|
||||
BrighCyan,
|
||||
BrightRed,
|
||||
BrightMagenta,
|
||||
BrightYellow,
|
||||
White
|
||||
}
|
||||
/// <summary>
|
||||
/// Basic colors that can be used to set the foreground and background colors in console applications. These can only be
|
||||
/// </summary>
|
||||
public enum Color {
|
||||
Black,
|
||||
Blue,
|
||||
Green,
|
||||
Cyan,
|
||||
Red,
|
||||
Magenta,
|
||||
Brown,
|
||||
Gray,
|
||||
DarkGray,
|
||||
BrightBlue,
|
||||
BrightGreen,
|
||||
BrighCyan,
|
||||
BrightRed,
|
||||
BrightMagenta,
|
||||
BrightYellow,
|
||||
White
|
||||
}
|
||||
|
||||
public struct Attribute {
|
||||
internal int value;
|
||||
public Attribute (int v)
|
||||
{
|
||||
value = v;
|
||||
}
|
||||
public struct Attribute {
|
||||
internal int value;
|
||||
public Attribute (int v)
|
||||
{
|
||||
value = v;
|
||||
}
|
||||
|
||||
public static implicit operator int (Attribute c) => c.value;
|
||||
public static implicit operator Attribute (int v) => new Attribute (v);
|
||||
}
|
||||
public static implicit operator int (Attribute c) => c.value;
|
||||
public static implicit operator Attribute (int v) => new Attribute (v);
|
||||
}
|
||||
|
||||
public class ColorScheme {
|
||||
public Attribute Normal;
|
||||
public Attribute Focus;
|
||||
public Attribute HotNormal;
|
||||
public Attribute HotFocus;
|
||||
public Attribute Marked => HotNormal;
|
||||
public Attribute MarkedSelected => HotFocus;
|
||||
}
|
||||
public class ColorScheme {
|
||||
public Attribute Normal;
|
||||
public Attribute Focus;
|
||||
public Attribute HotNormal;
|
||||
public Attribute HotFocus;
|
||||
public Attribute Marked => HotNormal;
|
||||
public Attribute MarkedSelected => HotFocus;
|
||||
}
|
||||
|
||||
public static class Colors {
|
||||
public static ColorScheme Base, Dialog, Menu, Error;
|
||||
public static class Colors {
|
||||
public static ColorScheme Base, Dialog, Menu, Error;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ConsoleDriver {
|
||||
public abstract int Cols { get; }
|
||||
public abstract int Rows { get; }
|
||||
public abstract void Init (Action terminalResized);
|
||||
public abstract void Move (int col, int row);
|
||||
public abstract void AddCh (int ch);
|
||||
public abstract void AddStr (string str);
|
||||
public abstract void PrepareToRun (MainLoop mainLoop, Responder target);
|
||||
public abstract void Refresh ();
|
||||
public abstract void End ();
|
||||
public abstract void RedrawTop ();
|
||||
public abstract void SetAttribute (Attribute c);
|
||||
public abstract class ConsoleDriver {
|
||||
public abstract int Cols { get; }
|
||||
public abstract int Rows { get; }
|
||||
public abstract void Init (Action terminalResized);
|
||||
public abstract void Move (int col, int row);
|
||||
public abstract void AddCh (int ch);
|
||||
public abstract void AddStr (string str);
|
||||
public abstract void PrepareToRun (MainLoop mainLoop, Responder target);
|
||||
public abstract void Refresh ();
|
||||
public abstract void End ();
|
||||
public abstract void RedrawTop ();
|
||||
public abstract void SetAttribute (Attribute c);
|
||||
|
||||
// Set Colors from limit sets of colors
|
||||
public abstract void SetColors (ConsoleColor foreground, ConsoleColor background);
|
||||
// Set Colors from limit sets of colors
|
||||
public abstract void SetColors (ConsoleColor foreground, ConsoleColor background);
|
||||
|
||||
// Advanced uses - set colors to any pre-set pairs, you would need to init_color
|
||||
// that independently with the R, G, B values.
|
||||
public abstract void SetColors (short foreColorId, short backgroundColorId);
|
||||
// Advanced uses - set colors to any pre-set pairs, you would need to init_color
|
||||
// that independently with the R, G, B values.
|
||||
public abstract void SetColors (short foreColorId, short backgroundColorId);
|
||||
|
||||
public abstract void DrawFrame (Rect region, bool fill);
|
||||
public abstract void DrawFrame (Rect region, bool fill);
|
||||
|
||||
Rect clip;
|
||||
public Rect Clip {
|
||||
get => clip;
|
||||
set => this.clip = value;
|
||||
}
|
||||
}
|
||||
Rect clip;
|
||||
public Rect Clip {
|
||||
get => clip;
|
||||
set => this.clip = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class CursesDriver : ConsoleDriver {
|
||||
Action terminalResized;
|
||||
public class CursesDriver : ConsoleDriver {
|
||||
Action terminalResized;
|
||||
|
||||
public override int Cols => Curses.Cols;
|
||||
public override int Rows => Curses.Lines;
|
||||
public override int Cols => Curses.Cols;
|
||||
public override int Rows => Curses.Lines;
|
||||
|
||||
// Current row, and current col, tracked by Move/AddCh only
|
||||
int ccol, crow;
|
||||
bool needMove;
|
||||
public override void Move (int col, int row)
|
||||
{
|
||||
ccol = col;
|
||||
crow = row;
|
||||
// Current row, and current col, tracked by Move/AddCh only
|
||||
int ccol, crow;
|
||||
bool needMove;
|
||||
public override void Move (int col, int row)
|
||||
{
|
||||
ccol = col;
|
||||
crow = row;
|
||||
|
||||
if (Clip.Contains (col, row)) {
|
||||
Curses.move (row, col);
|
||||
needMove = false;
|
||||
} else {
|
||||
Curses.move (Clip.Y, Clip.X);
|
||||
needMove = true;
|
||||
}
|
||||
}
|
||||
if (Clip.Contains (col, row)) {
|
||||
Curses.move (row, col);
|
||||
needMove = false;
|
||||
} else {
|
||||
Curses.move (Clip.Y, Clip.X);
|
||||
needMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
public override void AddCh (int ch)
|
||||
{
|
||||
if (Clip.Contains (ccol, crow)) {
|
||||
if (needMove) {
|
||||
Curses.move (crow, ccol);
|
||||
needMove = false;
|
||||
}
|
||||
Curses.addch (ch);
|
||||
} else
|
||||
needMove = true;
|
||||
ccol++;
|
||||
}
|
||||
public override void AddCh (int ch)
|
||||
{
|
||||
if (Clip.Contains (ccol, crow)) {
|
||||
if (needMove) {
|
||||
Curses.move (crow, ccol);
|
||||
needMove = false;
|
||||
}
|
||||
Curses.addch (ch);
|
||||
} else
|
||||
needMove = true;
|
||||
ccol++;
|
||||
}
|
||||
|
||||
public override void AddStr (string str)
|
||||
{
|
||||
// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
|
||||
foreach (var c in str)
|
||||
AddCh ((int)c);
|
||||
}
|
||||
public override void AddStr (string str)
|
||||
{
|
||||
// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
|
||||
foreach (var c in str)
|
||||
AddCh ((int)c);
|
||||
}
|
||||
|
||||
public override void Refresh () => Curses.refresh ();
|
||||
public override void End () => Curses.endwin ();
|
||||
public override void RedrawTop () => window.redrawwin ();
|
||||
public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
|
||||
public Curses.Window window;
|
||||
public override void Refresh () => Curses.refresh ();
|
||||
public override void End () => Curses.endwin ();
|
||||
public override void RedrawTop () => window.redrawwin ();
|
||||
public override void SetAttribute (Attribute c) => Curses.attrset (c.value);
|
||||
public Curses.Window window;
|
||||
|
||||
static short last_color_pair = 16;
|
||||
static Attribute MakeColor (short f, short b)
|
||||
{
|
||||
Curses.InitColorPair (++last_color_pair, f, b);
|
||||
return new Attribute () { value = Curses.ColorPair (last_color_pair) };
|
||||
}
|
||||
static short last_color_pair = 16;
|
||||
static Attribute MakeColor (short f, short b)
|
||||
{
|
||||
Curses.InitColorPair (++last_color_pair, f, b);
|
||||
return new Attribute () { value = Curses.ColorPair (last_color_pair) };
|
||||
}
|
||||
|
||||
int [,] colorPairs = new int [16, 16];
|
||||
int [,] colorPairs = new int [16, 16];
|
||||
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
int f = (short)foreground;
|
||||
int b = (short)background;
|
||||
var v = colorPairs [f, b];
|
||||
if ((v & 0x10000) == 0) {
|
||||
b = b & 0x7;
|
||||
bool bold = (f & 0x8) != 0;
|
||||
f = f & 0x7;
|
||||
public override void SetColors (ConsoleColor foreground, ConsoleColor background)
|
||||
{
|
||||
int f = (short)foreground;
|
||||
int b = (short)background;
|
||||
var v = colorPairs [f, b];
|
||||
if ((v & 0x10000) == 0) {
|
||||
b = b & 0x7;
|
||||
bool bold = (f & 0x8) != 0;
|
||||
f = f & 0x7;
|
||||
|
||||
v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0);
|
||||
colorPairs [(int)foreground, (int)background] = v | 0x1000;
|
||||
}
|
||||
SetAttribute (v & 0xffff);
|
||||
}
|
||||
v = MakeColor ((short)f, (short)b) | (bold ? Curses.A_BOLD : 0);
|
||||
colorPairs [(int)foreground, (int)background] = v | 0x1000;
|
||||
}
|
||||
SetAttribute (v & 0xffff);
|
||||
}
|
||||
|
||||
Dictionary<int, int> rawPairs = new Dictionary<int, int> ();
|
||||
public override void SetColors (short foreColorId, short backgroundColorId)
|
||||
{
|
||||
int key = (((ushort)foreColorId << 16)) | (ushort)backgroundColorId;
|
||||
if (!rawPairs.TryGetValue (key, out var v)) {
|
||||
v = MakeColor (foreColorId, backgroundColorId);
|
||||
rawPairs [key] = v;
|
||||
}
|
||||
SetAttribute (v);
|
||||
}
|
||||
Dictionary<int, int> rawPairs = new Dictionary<int, int> ();
|
||||
public override void SetColors (short foreColorId, short backgroundColorId)
|
||||
{
|
||||
int key = (((ushort)foreColorId << 16)) | (ushort)backgroundColorId;
|
||||
if (!rawPairs.TryGetValue (key, out var v)) {
|
||||
v = MakeColor (foreColorId, backgroundColorId);
|
||||
rawPairs [key] = v;
|
||||
}
|
||||
SetAttribute (v);
|
||||
}
|
||||
|
||||
static Key MapCursesKey (int cursesKey)
|
||||
{
|
||||
switch (cursesKey) {
|
||||
case Curses.KeyF1: return Key.F1;
|
||||
case Curses.KeyF2: return Key.F2;
|
||||
case Curses.KeyF3: return Key.F3;
|
||||
case Curses.KeyF4: return Key.F4;
|
||||
case Curses.KeyF5: return Key.F5;
|
||||
case Curses.KeyF6: return Key.F6;
|
||||
case Curses.KeyF7: return Key.F7;
|
||||
case Curses.KeyF8: return Key.F8;
|
||||
case Curses.KeyF9: return Key.F9;
|
||||
case Curses.KeyF10: return Key.F10;
|
||||
case Curses.KeyUp: return Key.CursorUp;
|
||||
case Curses.KeyDown: return Key.CursorDown;
|
||||
case Curses.KeyLeft: return Key.CursorLeft;
|
||||
case Curses.KeyRight: return Key.CursorRight;
|
||||
case Curses.KeyHome: return Key.Home;
|
||||
case Curses.KeyEnd: return Key.End;
|
||||
case Curses.KeyNPage: return Key.PageDown;
|
||||
case Curses.KeyPPage: return Key.PageUp;
|
||||
case Curses.KeyDeleteChar: return Key.DeleteChar;
|
||||
case Curses.KeyInsertChar: return Key.InsertChar;
|
||||
case Curses.KeyBackTab: return Key.BackTab;
|
||||
default: return Key.Unknown;
|
||||
}
|
||||
}
|
||||
static Key MapCursesKey (int cursesKey)
|
||||
{
|
||||
switch (cursesKey) {
|
||||
case Curses.KeyF1: return Key.F1;
|
||||
case Curses.KeyF2: return Key.F2;
|
||||
case Curses.KeyF3: return Key.F3;
|
||||
case Curses.KeyF4: return Key.F4;
|
||||
case Curses.KeyF5: return Key.F5;
|
||||
case Curses.KeyF6: return Key.F6;
|
||||
case Curses.KeyF7: return Key.F7;
|
||||
case Curses.KeyF8: return Key.F8;
|
||||
case Curses.KeyF9: return Key.F9;
|
||||
case Curses.KeyF10: return Key.F10;
|
||||
case Curses.KeyUp: return Key.CursorUp;
|
||||
case Curses.KeyDown: return Key.CursorDown;
|
||||
case Curses.KeyLeft: return Key.CursorLeft;
|
||||
case Curses.KeyRight: return Key.CursorRight;
|
||||
case Curses.KeyHome: return Key.Home;
|
||||
case Curses.KeyEnd: return Key.End;
|
||||
case Curses.KeyNPage: return Key.PageDown;
|
||||
case Curses.KeyPPage: return Key.PageUp;
|
||||
case Curses.KeyDeleteChar: return Key.DeleteChar;
|
||||
case Curses.KeyInsertChar: return Key.InsertChar;
|
||||
case Curses.KeyBackTab: return Key.BackTab;
|
||||
default: return Key.Unknown;
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessInput (Responder handler)
|
||||
{
|
||||
int wch;
|
||||
var code = Curses.get_wch (out wch);
|
||||
if (code == Curses.KEY_CODE_YES) {
|
||||
if (wch == Curses.KeyResize) {
|
||||
if (Curses.CheckWinChange ()) {
|
||||
terminalResized ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (code == Curses.KeyMouse) {
|
||||
// TODO
|
||||
// Curses.MouseEvent ev;
|
||||
// Curses.getmouse (out ev);
|
||||
// handler.HandleMouse ();
|
||||
return;
|
||||
}
|
||||
handler.ProcessKey (new KeyEvent (MapCursesKey (wch)));
|
||||
return;
|
||||
}
|
||||
void ProcessInput (Responder handler)
|
||||
{
|
||||
int wch;
|
||||
var code = Curses.get_wch (out wch);
|
||||
if (code == Curses.KEY_CODE_YES) {
|
||||
if (wch == Curses.KeyResize) {
|
||||
if (Curses.CheckWinChange ()) {
|
||||
terminalResized ();
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (code == Curses.KeyMouse) {
|
||||
// TODO
|
||||
// Curses.MouseEvent ev;
|
||||
// Curses.getmouse (out ev);
|
||||
// handler.HandleMouse ();
|
||||
return;
|
||||
}
|
||||
handler.ProcessKey (new KeyEvent (MapCursesKey (wch)));
|
||||
return;
|
||||
}
|
||||
|
||||
// Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter.
|
||||
if (wch == 27) {
|
||||
Curses.timeout (100);
|
||||
// Special handling for ESC, we want to try to catch ESC+letter to simulate alt-letter.
|
||||
if (wch == 27) {
|
||||
Curses.timeout (100);
|
||||
|
||||
code = Curses.get_wch (out wch);
|
||||
if (code == Curses.KEY_CODE_YES)
|
||||
handler.ProcessKey (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
|
||||
if (code == 0)
|
||||
handler.ProcessKey (new KeyEvent (Key.AltMask | (Key)wch));
|
||||
} else
|
||||
handler.ProcessKey (new KeyEvent ((Key)wch));
|
||||
}
|
||||
code = Curses.get_wch (out wch);
|
||||
if (code == Curses.KEY_CODE_YES)
|
||||
handler.ProcessKey (new KeyEvent (Key.AltMask | MapCursesKey (wch)));
|
||||
if (code == 0)
|
||||
handler.ProcessKey (new KeyEvent (Key.AltMask | (Key)wch));
|
||||
} else
|
||||
handler.ProcessKey (new KeyEvent ((Key)wch));
|
||||
}
|
||||
|
||||
public override void PrepareToRun (MainLoop mainLoop, Responder handler)
|
||||
{
|
||||
Curses.timeout (-1);
|
||||
public override void PrepareToRun (MainLoop mainLoop, Responder handler)
|
||||
{
|
||||
Curses.timeout (-1);
|
||||
|
||||
mainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
|
||||
ProcessInput (handler);
|
||||
return true;
|
||||
});
|
||||
mainLoop.AddWatch (0, Mono.Terminal.MainLoop.Condition.PollIn, x => {
|
||||
ProcessInput (handler);
|
||||
return true;
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public override void DrawFrame (Rect region, bool fill)
|
||||
{
|
||||
int width = region.Width;
|
||||
int height = region.Height;
|
||||
int b;
|
||||
public override void DrawFrame (Rect region, bool fill)
|
||||
{
|
||||
int width = region.Width;
|
||||
int height = region.Height;
|
||||
int b;
|
||||
|
||||
Move (region.X, region.Y);
|
||||
AddCh (Curses.ACS_ULCORNER);
|
||||
for (b = 0; b < width - 2; b++)
|
||||
AddCh (Curses.ACS_HLINE);
|
||||
AddCh (Curses.ACS_URCORNER);
|
||||
for (b = 1; b < height - 1; b++) {
|
||||
Move (region.X, region.Y + b);
|
||||
AddCh (Curses.ACS_VLINE);
|
||||
if (fill) {
|
||||
for (int x = 1; x < width - 1; x++)
|
||||
AddCh (' ');
|
||||
} else
|
||||
Move (region.X + width - 1, region.Y + b);
|
||||
AddCh (Curses.ACS_VLINE);
|
||||
}
|
||||
Move (region.X, region.Y + height - 1);
|
||||
AddCh (Curses.ACS_LLCORNER);
|
||||
for (b = 0; b < width - 2; b++)
|
||||
AddCh (Curses.ACS_HLINE);
|
||||
AddCh (Curses.ACS_LRCORNER);
|
||||
}
|
||||
Move (region.X, region.Y);
|
||||
AddCh (Curses.ACS_ULCORNER);
|
||||
for (b = 0; b < width - 2; b++)
|
||||
AddCh (Curses.ACS_HLINE);
|
||||
AddCh (Curses.ACS_URCORNER);
|
||||
for (b = 1; b < height - 1; b++) {
|
||||
Move (region.X, region.Y + b);
|
||||
AddCh (Curses.ACS_VLINE);
|
||||
if (fill) {
|
||||
for (int x = 1; x < width - 1; x++)
|
||||
AddCh (' ');
|
||||
} else
|
||||
Move (region.X + width - 1, region.Y + b);
|
||||
AddCh (Curses.ACS_VLINE);
|
||||
}
|
||||
Move (region.X, region.Y + height - 1);
|
||||
AddCh (Curses.ACS_LLCORNER);
|
||||
for (b = 0; b < width - 2; b++)
|
||||
AddCh (Curses.ACS_HLINE);
|
||||
AddCh (Curses.ACS_LRCORNER);
|
||||
}
|
||||
|
||||
public override void Init(Action terminalResized)
|
||||
{
|
||||
if (window != null)
|
||||
return;
|
||||
public override void Init (Action terminalResized)
|
||||
{
|
||||
if (window != null)
|
||||
return;
|
||||
|
||||
try {
|
||||
window = Curses.initscr ();
|
||||
} catch (Exception e){
|
||||
Console.WriteLine ("Curses failed to initialize, the exception is: " + e);
|
||||
}
|
||||
Curses.raw ();
|
||||
Curses.noecho ();
|
||||
Curses.Window.Standard.keypad (true);
|
||||
this.terminalResized = terminalResized;
|
||||
|
||||
Colors.Base = new ColorScheme ();
|
||||
Colors.Dialog = new ColorScheme ();
|
||||
Colors.Menu = new ColorScheme ();
|
||||
Colors.Error = new ColorScheme ();
|
||||
Clip = new Rect (0, 0, Cols, Rows);
|
||||
if (Curses.HasColors){
|
||||
Curses.StartColor ();
|
||||
Curses.UseDefaultColors ();
|
||||
try {
|
||||
window = Curses.initscr ();
|
||||
} catch (Exception e) {
|
||||
Console.WriteLine ("Curses failed to initialize, the exception is: " + e);
|
||||
}
|
||||
Curses.raw ();
|
||||
Curses.noecho ();
|
||||
Curses.Window.Standard.keypad (true);
|
||||
this.terminalResized = terminalResized;
|
||||
|
||||
Colors.Base.Normal = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLUE);
|
||||
Colors.Base.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
|
||||
Colors.Base.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLUE);
|
||||
Colors.Base.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
|
||||
Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
|
||||
Colors.Menu.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
|
||||
Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
|
||||
Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
|
||||
Colors.Dialog.HotNormal = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_WHITE);
|
||||
Colors.Dialog.HotFocus = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_CYAN);
|
||||
Colors.Base = new ColorScheme ();
|
||||
Colors.Dialog = new ColorScheme ();
|
||||
Colors.Menu = new ColorScheme ();
|
||||
Colors.Error = new ColorScheme ();
|
||||
Clip = new Rect (0, 0, Cols, Rows);
|
||||
if (Curses.HasColors) {
|
||||
Curses.StartColor ();
|
||||
Curses.UseDefaultColors ();
|
||||
|
||||
Colors.Error.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_RED);
|
||||
Colors.Error.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
|
||||
Colors.Error.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_RED);
|
||||
Colors.Error.HotFocus = Colors.Error.HotNormal;
|
||||
} else {
|
||||
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.Menu.Normal = Curses.A_REVERSE;
|
||||
Colors.Menu.Focus = Curses.A_NORMAL;
|
||||
Colors.Menu.HotNormal = Curses.A_BOLD;
|
||||
Colors.Menu.HotFocus = Curses.A_NORMAL;
|
||||
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.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.Base.Normal = MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLUE);
|
||||
Colors.Base.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
|
||||
Colors.Base.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLUE);
|
||||
Colors.Base.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_CYAN);
|
||||
Colors.Menu.Focus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_CYAN);
|
||||
Colors.Menu.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_BLACK);
|
||||
Colors.Menu.HotFocus = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_BLACK);
|
||||
Colors.Dialog.Normal = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
|
||||
Colors.Dialog.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_CYAN);
|
||||
Colors.Dialog.HotNormal = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_WHITE);
|
||||
Colors.Dialog.HotFocus = MakeColor (Curses.COLOR_BLUE, Curses.COLOR_CYAN);
|
||||
|
||||
Colors.Error.Normal = Curses.A_BOLD | MakeColor (Curses.COLOR_WHITE, Curses.COLOR_RED);
|
||||
Colors.Error.Focus = MakeColor (Curses.COLOR_BLACK, Curses.COLOR_WHITE);
|
||||
Colors.Error.HotNormal = Curses.A_BOLD | MakeColor (Curses.COLOR_YELLOW, Curses.COLOR_RED);
|
||||
Colors.Error.HotFocus = Colors.Error.HotNormal;
|
||||
} else {
|
||||
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.Menu.Normal = Curses.A_REVERSE;
|
||||
Colors.Menu.Focus = Curses.A_NORMAL;
|
||||
Colors.Menu.HotNormal = Curses.A_BOLD;
|
||||
Colors.Menu.HotFocus = Curses.A_NORMAL;
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Event.cs
2
Event.cs
@@ -76,7 +76,7 @@ namespace Terminal {
|
||||
|
||||
public struct KeyEvent {
|
||||
public Key Key;
|
||||
public int KeyValue => (int)KeyValue;
|
||||
public int KeyValue => (int)Key;
|
||||
public bool IsAlt => (Key & Key.AltMask) != 0;
|
||||
public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
|
||||
|
||||
|
||||
6
TODO.md
6
TODO.md
@@ -58,6 +58,10 @@ model, but needs revisiting in the new model.
|
||||
On the demo, press tab twice, instead of selecting Ok, the first tab
|
||||
does nothing, the second tab clears the screen.
|
||||
|
||||
=> Explanation: the Window gets a NeedsDisplay, so it displays
|
||||
tiself, but the contentView does not have NeedsDisplay
|
||||
set recursively, so it does not render any of the subviews
|
||||
|
||||
# Merge Responder into View
|
||||
|
||||
# Make HasFocus implicitly call SetNeedsDisplay
|
||||
# Make HasFocus implicitly call SetNeedsDisplay
|
||||
|
||||
314
Views/Label.cs
314
Views/Label.cs
@@ -4,177 +4,177 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Terminal {
|
||||
public enum TextAlignment {
|
||||
Left, Right, Centered, Justified
|
||||
}
|
||||
public enum TextAlignment {
|
||||
Left, Right, Centered, Justified
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Label widget, displays a string at a given position, can include multiple lines.
|
||||
/// </summary>
|
||||
public class Label : View {
|
||||
List<string> lines = new List<string> ();
|
||||
bool recalcPending = true;
|
||||
string text;
|
||||
TextAlignment textAlignment;
|
||||
/// <summary>
|
||||
/// Label widget, displays a string at a given position, can include multiple lines.
|
||||
/// </summary>
|
||||
public class Label : View {
|
||||
List<string> lines = new List<string> ();
|
||||
bool recalcPending = true;
|
||||
string text;
|
||||
TextAlignment textAlignment;
|
||||
|
||||
static Rect CalcRect (int x, int y, string s)
|
||||
{
|
||||
int mw = 0;
|
||||
int ml = 1;
|
||||
static Rect CalcRect (int x, int y, string s)
|
||||
{
|
||||
int mw = 0;
|
||||
int ml = 1;
|
||||
|
||||
int cols = 0;
|
||||
foreach (var c in s) {
|
||||
if (c == '\n'){
|
||||
ml++;
|
||||
if (cols > mw)
|
||||
mw = cols;
|
||||
cols = 0;
|
||||
} else
|
||||
cols++;
|
||||
}
|
||||
return new Rect (x, y, cols, ml);
|
||||
}
|
||||
int cols = 0;
|
||||
foreach (var c in s) {
|
||||
if (c == '\n') {
|
||||
ml++;
|
||||
if (cols > mw)
|
||||
mw = cols;
|
||||
cols = 0;
|
||||
} else
|
||||
cols++;
|
||||
}
|
||||
return new Rect (x, y, cols, ml);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor: creates a label at the given
|
||||
/// coordinate with the given string, computes the bounding box
|
||||
/// based on the size of the string, assumes that the string contains
|
||||
/// newlines for multiple lines, no special breaking rules are used.
|
||||
/// </summary>
|
||||
public Label (int x, int y, string text) : this (CalcRect (x, y, text), text)
|
||||
{
|
||||
}
|
||||
/// <summary>
|
||||
/// Public constructor: creates a label at the given
|
||||
/// coordinate with the given string, computes the bounding box
|
||||
/// based on the size of the string, assumes that the string contains
|
||||
/// newlines for multiple lines, no special breaking rules are used.
|
||||
/// </summary>
|
||||
public Label (int x, int y, string text) : this (CalcRect (x, y, text), text)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor: creates a label at the given
|
||||
/// coordinate with the given string and uses the specified
|
||||
/// frame for the string.
|
||||
/// </summary>
|
||||
public Label (Rect rect, string text) : base (rect)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
/// <summary>
|
||||
/// Public constructor: creates a label at the given
|
||||
/// coordinate with the given string and uses the specified
|
||||
/// frame for the string.
|
||||
/// </summary>
|
||||
public Label (Rect rect, string text) : base (rect)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
static char [] whitespace = new char [] { ' ', '\t' };
|
||||
static char [] whitespace = new char [] { ' ', '\t' };
|
||||
|
||||
string ClipAndJustify (string str)
|
||||
{
|
||||
int slen = str.Length;
|
||||
if (slen > Frame.Width)
|
||||
return str.Substring (0, Frame.Width);
|
||||
else {
|
||||
if (textAlignment == TextAlignment.Justified) {
|
||||
var words = str.Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
|
||||
int textCount = words.Sum ((arg) => arg.Length);
|
||||
string ClipAndJustify (string str)
|
||||
{
|
||||
int slen = str.Length;
|
||||
if (slen > Frame.Width)
|
||||
return str.Substring (0, Frame.Width);
|
||||
else {
|
||||
if (textAlignment == TextAlignment.Justified) {
|
||||
var words = str.Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
|
||||
int textCount = words.Sum ((arg) => arg.Length);
|
||||
|
||||
var spaces = (Frame.Width - textCount) / (words.Length - 1);
|
||||
var extras = (Frame.Width - textCount) % words.Length;
|
||||
var s = new System.Text.StringBuilder ();
|
||||
//s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
|
||||
for (int w = 0; w < words.Length; w++) {
|
||||
var x = words [w];
|
||||
s.Append (x);
|
||||
if (w + 1 < words.Length)
|
||||
for (int i = 0; i < spaces; i++)
|
||||
s.Append (' ');
|
||||
if (extras > 0) {
|
||||
s.Append ('_');
|
||||
extras--;
|
||||
}
|
||||
}
|
||||
return s.ToString ();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
var spaces = (Frame.Width - textCount) / (words.Length - 1);
|
||||
var extras = (Frame.Width - textCount) % words.Length;
|
||||
var s = new System.Text.StringBuilder ();
|
||||
//s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
|
||||
for (int w = 0; w < words.Length; w++) {
|
||||
var x = words [w];
|
||||
s.Append (x);
|
||||
if (w + 1 < words.Length)
|
||||
for (int i = 0; i < spaces; i++)
|
||||
s.Append (' ');
|
||||
if (extras > 0) {
|
||||
s.Append ('_');
|
||||
extras--;
|
||||
}
|
||||
}
|
||||
return s.ToString ();
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
||||
void Recalc ()
|
||||
{
|
||||
lines.Clear ();
|
||||
if (text.IndexOf ('\n') == -1) {
|
||||
lines.Add (ClipAndJustify (text));
|
||||
return;
|
||||
}
|
||||
int textLen = text.Length;
|
||||
int lp = 0;
|
||||
for (int i = 0; i < textLen; i++) {
|
||||
char c = text [i];
|
||||
void Recalc ()
|
||||
{
|
||||
recalcPending = false;
|
||||
lines.Clear ();
|
||||
if (text.IndexOf ('\n') == -1) {
|
||||
lines.Add (ClipAndJustify (text));
|
||||
return;
|
||||
}
|
||||
int textLen = text.Length;
|
||||
int lp = 0;
|
||||
for (int i = 0; i < textLen; i++) {
|
||||
char c = text [i];
|
||||
|
||||
if (c == '\n') {
|
||||
lines.Add (ClipAndJustify (text.Substring (lp, i - lp)));
|
||||
lp = i + 1;
|
||||
}
|
||||
}
|
||||
recalcPending = false;
|
||||
}
|
||||
if (c == '\n') {
|
||||
lines.Add (ClipAndJustify (text.Substring (lp, i - lp)));
|
||||
lp = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
if (recalcPending)
|
||||
Recalc ();
|
||||
|
||||
if (TextColor != -1)
|
||||
Driver.SetAttribute (TextColor);
|
||||
else
|
||||
Driver.SetAttribute(Colors.Base.Normal);
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
if (recalcPending)
|
||||
Recalc ();
|
||||
|
||||
Clear ();
|
||||
Move (Frame.X, Frame.Y);
|
||||
for (int line = 0; line < lines.Count; line++) {
|
||||
if (line < region.Top || line >= region.Bottom)
|
||||
continue;
|
||||
var str = lines [line];
|
||||
int x;
|
||||
switch (textAlignment) {
|
||||
case TextAlignment.Left:
|
||||
case TextAlignment.Justified:
|
||||
x = 0;
|
||||
break;
|
||||
case TextAlignment.Right:
|
||||
x = Frame.Right - str.Length;
|
||||
break;
|
||||
case TextAlignment.Centered:
|
||||
x = Frame.Left + (Frame.Width - str.Length) / 2;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException ();
|
||||
}
|
||||
Move (x, line);
|
||||
Driver.AddStr (str);
|
||||
}
|
||||
}
|
||||
if (TextColor != -1)
|
||||
Driver.SetAttribute (TextColor);
|
||||
else
|
||||
Driver.SetAttribute (Colors.Base.Normal);
|
||||
|
||||
/// <summary>
|
||||
/// The text displayed by this widget.
|
||||
/// </summary>
|
||||
public virtual string Text {
|
||||
get => text;
|
||||
set {
|
||||
text = value;
|
||||
recalcPending = true;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
Clear ();
|
||||
Move (Frame.X, Frame.Y);
|
||||
for (int line = 0; line < lines.Count; line++) {
|
||||
if (line < region.Top || line >= region.Bottom)
|
||||
continue;
|
||||
var str = lines [line];
|
||||
int x;
|
||||
switch (textAlignment) {
|
||||
case TextAlignment.Left:
|
||||
case TextAlignment.Justified:
|
||||
x = 0;
|
||||
break;
|
||||
case TextAlignment.Right:
|
||||
x = Frame.Right - str.Length;
|
||||
break;
|
||||
case TextAlignment.Centered:
|
||||
x = Frame.Left + (Frame.Width - str.Length) / 2;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException ();
|
||||
}
|
||||
Move (x, line);
|
||||
Driver.AddStr (str);
|
||||
}
|
||||
}
|
||||
|
||||
public TextAlignment TextAlignment {
|
||||
get => textAlignment;
|
||||
set {
|
||||
textAlignment = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The text displayed by this widget.
|
||||
/// </summary>
|
||||
public virtual string Text {
|
||||
get => text;
|
||||
set {
|
||||
text = value;
|
||||
recalcPending = true;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color used for the label
|
||||
/// </summary>
|
||||
Attribute textColor = -1;
|
||||
public Attribute TextColor {
|
||||
get => textColor;
|
||||
set {
|
||||
textColor = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
}
|
||||
public TextAlignment TextAlignment {
|
||||
get => textAlignment;
|
||||
set {
|
||||
textAlignment = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The color used for the label
|
||||
/// </summary>
|
||||
Attribute textColor = -1;
|
||||
public Attribute TextColor {
|
||||
get => textColor;
|
||||
set {
|
||||
textColor = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,299 +3,298 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Terminal {
|
||||
/// <summary>
|
||||
/// Text data entry widget
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Entry widget provides Emacs-like editing
|
||||
/// functionality, and mouse support.
|
||||
/// </remarks>
|
||||
public class TextField : View {
|
||||
string text, kill;
|
||||
int first, point;
|
||||
bool used;
|
||||
/// <summary>
|
||||
/// Text data entry widget
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The Entry widget provides Emacs-like editing
|
||||
/// functionality, and mouse support.
|
||||
/// </remarks>
|
||||
public class TextField : View {
|
||||
string text, kill;
|
||||
int first, point;
|
||||
bool used;
|
||||
|
||||
/// <summary>
|
||||
/// Changed event, raised when the text has clicked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Client code can hook up to this event, it is
|
||||
/// raised when the text in the entry changes.
|
||||
/// </remarks>
|
||||
public event EventHandler Changed;
|
||||
/// <summary>
|
||||
/// Changed event, raised when the text has clicked.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Client code can hook up to this event, it is
|
||||
/// raised when the text in the entry changes.
|
||||
/// </remarks>
|
||||
public event EventHandler Changed;
|
||||
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public TextField (int x, int y, int w, string s) : base (new Rect (x, y, w, 1))
|
||||
{
|
||||
if (s == null)
|
||||
s = "";
|
||||
/// <summary>
|
||||
/// Public constructor.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public TextField (int x, int y, int w, string s) : base (new Rect (x, y, w, 1))
|
||||
{
|
||||
if (s == null)
|
||||
s = "";
|
||||
|
||||
text = s;
|
||||
point = s.Length;
|
||||
first = point > w ? point - w : 0;
|
||||
CanFocus = true;
|
||||
Color = Colors.Dialog.Focus;
|
||||
}
|
||||
text = s;
|
||||
point = s.Length;
|
||||
first = point > w ? point - w : 0;
|
||||
CanFocus = true;
|
||||
Color = Colors.Dialog.Focus;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets or gets the text in the entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public string Text {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets or gets the text in the entry.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// </remarks>
|
||||
public string Text {
|
||||
get {
|
||||
return text;
|
||||
}
|
||||
|
||||
set {
|
||||
text = value;
|
||||
if (point > text.Length)
|
||||
point = text.Length;
|
||||
first = point > Frame.Width ? point - Frame.Width : 0;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
set {
|
||||
text = value;
|
||||
if (point > text.Length)
|
||||
point = text.Length;
|
||||
first = point > Frame.Width ? point - Frame.Width : 0;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the secret property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This makes the text entry suitable for entering passwords.
|
||||
/// </remarks>
|
||||
public bool Secret { get; set; }
|
||||
/// <summary>
|
||||
/// Sets the secret property.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This makes the text entry suitable for entering passwords.
|
||||
/// </remarks>
|
||||
public bool Secret { get; set; }
|
||||
|
||||
Attribute color;
|
||||
/// <summary>
|
||||
/// Sets the color attribute to use (includes foreground and background).
|
||||
/// </summary>
|
||||
/// <value>The color.</value>
|
||||
public Attribute Color {
|
||||
get => color;
|
||||
set {
|
||||
color = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
Attribute color;
|
||||
/// <summary>
|
||||
/// Sets the color attribute to use (includes foreground and background).
|
||||
/// </summary>
|
||||
/// <value>The color.</value>
|
||||
public Attribute Color {
|
||||
get => color;
|
||||
set {
|
||||
color = value;
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The current cursor position.
|
||||
/// </summary>
|
||||
public int CursorPosition { get { return point; } }
|
||||
/// <summary>
|
||||
/// The current cursor position.
|
||||
/// </summary>
|
||||
public int CursorPosition { get { return point; } }
|
||||
|
||||
/// <summary>
|
||||
/// Sets the cursor position.
|
||||
/// </summary>
|
||||
public override void PositionCursor ()
|
||||
{
|
||||
Move (point - first, 0);
|
||||
}
|
||||
/// <summary>
|
||||
/// Sets the cursor position.
|
||||
/// </summary>
|
||||
public override void PositionCursor ()
|
||||
{
|
||||
Move (point - first, 0);
|
||||
}
|
||||
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
Driver.SetAttribute (Color);
|
||||
Move (0, 0);
|
||||
public override void Redraw (Rect region)
|
||||
{
|
||||
Driver.SetAttribute (Color);
|
||||
Move (0, 0);
|
||||
|
||||
for (int i = 0; i < Frame.Width; i++) {
|
||||
int p = first + i;
|
||||
for (int i = 0; i < Frame.Width; i++) {
|
||||
int p = first + i;
|
||||
|
||||
if (p < text.Length) {
|
||||
Driver.AddCh (Secret ? '*' : text [p]);
|
||||
} else
|
||||
Driver.AddCh (' ');
|
||||
}
|
||||
PositionCursor ();
|
||||
}
|
||||
if (p < text.Length) {
|
||||
Driver.AddCh (Secret ? '*' : text [p]);
|
||||
} else
|
||||
Driver.AddCh ('_');
|
||||
}
|
||||
PositionCursor ();
|
||||
}
|
||||
|
||||
void Adjust ()
|
||||
{
|
||||
if (point < first)
|
||||
first = point;
|
||||
else if (first + point >= Frame.Width)
|
||||
first = point - (Frame.Width / 3);
|
||||
Redraw (Bounds);
|
||||
Driver.Refresh ();
|
||||
}
|
||||
void Adjust ()
|
||||
{
|
||||
if (point < first)
|
||||
first = point;
|
||||
else if (first + point >= Frame.Width)
|
||||
first = point - (Frame.Width / 3);
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
|
||||
void SetText (string new_text)
|
||||
{
|
||||
text = new_text;
|
||||
if (Changed != null)
|
||||
Changed (this, EventArgs.Empty);
|
||||
}
|
||||
void SetText (string new_text)
|
||||
{
|
||||
text = new_text;
|
||||
if (Changed != null)
|
||||
Changed (this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
public override bool CanFocus {
|
||||
get => true;
|
||||
set { base.CanFocus = value; }
|
||||
}
|
||||
public override bool CanFocus {
|
||||
get => true;
|
||||
set { base.CanFocus = value; }
|
||||
}
|
||||
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
switch (kb.Key) {
|
||||
case Key.Delete:
|
||||
case Key.Backspace:
|
||||
if (point == 0)
|
||||
return true;
|
||||
public override bool ProcessKey (KeyEvent kb)
|
||||
{
|
||||
switch (kb.Key) {
|
||||
case Key.Delete:
|
||||
case Key.Backspace:
|
||||
if (point == 0)
|
||||
return true;
|
||||
|
||||
SetText (text.Substring (0, point - 1) + text.Substring (point));
|
||||
point--;
|
||||
Adjust ();
|
||||
break;
|
||||
SetText (text.Substring (0, point - 1) + text.Substring (point));
|
||||
point--;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
// Home, C-A
|
||||
case Key.Home:
|
||||
case Key.ControlA:
|
||||
point = 0;
|
||||
Adjust ();
|
||||
break;
|
||||
// Home, C-A
|
||||
case Key.Home:
|
||||
case Key.ControlA:
|
||||
point = 0;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.CursorLeft:
|
||||
case Key.ControlB:
|
||||
if (point > 0) {
|
||||
point--;
|
||||
Adjust ();
|
||||
}
|
||||
break;
|
||||
case Key.CursorLeft:
|
||||
case Key.ControlB:
|
||||
if (point > 0) {
|
||||
point--;
|
||||
Adjust ();
|
||||
}
|
||||
break;
|
||||
|
||||
case Key.ControlD: // Delete
|
||||
if (point == text.Length)
|
||||
break;
|
||||
SetText (text.Substring (0, point) + text.Substring (point + 1));
|
||||
Adjust ();
|
||||
break;
|
||||
case Key.ControlD: // Delete
|
||||
if (point == text.Length)
|
||||
break;
|
||||
SetText (text.Substring (0, point) + text.Substring (point + 1));
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlE: // End
|
||||
point = text.Length;
|
||||
Adjust ();
|
||||
break;
|
||||
case Key.ControlE: // End
|
||||
point = text.Length;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.CursorRight:
|
||||
case Key.ControlF:
|
||||
if (point == text.Length)
|
||||
break;
|
||||
point++;
|
||||
Adjust ();
|
||||
break;
|
||||
case Key.CursorRight:
|
||||
case Key.ControlF:
|
||||
if (point == text.Length)
|
||||
break;
|
||||
point++;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlK: // kill-to-end
|
||||
kill = text.Substring (point);
|
||||
SetText (text.Substring (0, point));
|
||||
Adjust ();
|
||||
break;
|
||||
case Key.ControlK: // kill-to-end
|
||||
kill = text.Substring (point);
|
||||
SetText (text.Substring (0, point));
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case Key.ControlY: // Control-y, yank
|
||||
if (kill == null)
|
||||
return true;
|
||||
case Key.ControlY: // Control-y, yank
|
||||
if (kill == null)
|
||||
return true;
|
||||
|
||||
if (point == text.Length) {
|
||||
SetText (text + kill);
|
||||
point = text.Length;
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + kill + text.Substring (point));
|
||||
point += kill.Length;
|
||||
}
|
||||
Adjust ();
|
||||
break;
|
||||
if (point == text.Length) {
|
||||
SetText (text + kill);
|
||||
point = text.Length;
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + kill + text.Substring (point));
|
||||
point += kill.Length;
|
||||
}
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case (Key)((int)'b' + Key.AltMask):
|
||||
int bw = WordBackward (point);
|
||||
if (bw != -1)
|
||||
point = bw;
|
||||
Adjust ();
|
||||
break;
|
||||
case (Key)((int)'b' + Key.AltMask):
|
||||
int bw = WordBackward (point);
|
||||
if (bw != -1)
|
||||
point = bw;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
case (Key)((int)'f' + Key.AltMask):
|
||||
int fw = WordForward (point);
|
||||
if (fw != -1)
|
||||
point = fw;
|
||||
Adjust ();
|
||||
break;
|
||||
case (Key)((int)'f' + Key.AltMask):
|
||||
int fw = WordForward (point);
|
||||
if (fw != -1)
|
||||
point = fw;
|
||||
Adjust ();
|
||||
break;
|
||||
|
||||
default:
|
||||
// Ignore other control characters.
|
||||
if (kb.Key < Key.Space || kb.Key > Key.CharMask)
|
||||
return false;
|
||||
default:
|
||||
// Ignore other control characters.
|
||||
if (kb.Key < Key.Space || kb.Key > Key.CharMask)
|
||||
return false;
|
||||
|
||||
if (used) {
|
||||
if (point == text.Length) {
|
||||
SetText (text + (char)kb.Key);
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + (char)kb.Key + text.Substring (point));
|
||||
}
|
||||
point++;
|
||||
} else {
|
||||
SetText ("" + (char)kb.Key);
|
||||
first = 0;
|
||||
point = 1;
|
||||
}
|
||||
used = true;
|
||||
Adjust ();
|
||||
return true;
|
||||
}
|
||||
used = true;
|
||||
return true;
|
||||
}
|
||||
if (used) {
|
||||
if (point == text.Length) {
|
||||
SetText (text + (char)kb.Key);
|
||||
} else {
|
||||
SetText (text.Substring (0, point) + (char)kb.Key + text.Substring (point));
|
||||
}
|
||||
point++;
|
||||
} else {
|
||||
SetText ("" + (char)kb.Key);
|
||||
first = 0;
|
||||
point = 1;
|
||||
}
|
||||
used = true;
|
||||
Adjust ();
|
||||
return true;
|
||||
}
|
||||
used = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
int WordForward (int p)
|
||||
{
|
||||
if (p >= text.Length)
|
||||
return -1;
|
||||
int WordForward (int p)
|
||||
{
|
||||
if (p >= text.Length)
|
||||
return -1;
|
||||
|
||||
int i = p;
|
||||
if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text [p])) {
|
||||
for (; i < text.Length; i++) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != p)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
int i = p;
|
||||
if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text [p])) {
|
||||
for (; i < text.Length; i++) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i < text.Length; i++) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (i != p)
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WordBackward (int p)
|
||||
{
|
||||
if (p == 0)
|
||||
return -1;
|
||||
int WordBackward (int p)
|
||||
{
|
||||
if (p == 0)
|
||||
return -1;
|
||||
|
||||
int i = p - 1;
|
||||
if (i == 0)
|
||||
return 0;
|
||||
int i = p - 1;
|
||||
if (i == 0)
|
||||
return 0;
|
||||
|
||||
if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text [i])) {
|
||||
for (; i >= 0; i--) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text [i])) {
|
||||
for (; i >= 0; i--) {
|
||||
if (Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
for (; i >= 0; i--) {
|
||||
if (!Char.IsLetterOrDigit (text [i]))
|
||||
break;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
|
||||
if (i != p)
|
||||
return i;
|
||||
if (i != p)
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if false
|
||||
public override void ProcessMouse (Curses.MouseEvent ev)
|
||||
@@ -315,7 +314,7 @@ namespace Terminal {
|
||||
SetNeedsDisplay ();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
32
demo.cs
32
demo.cs
@@ -1,20 +1,20 @@
|
||||
using Terminal;
|
||||
|
||||
class Demo {
|
||||
static void Main ()
|
||||
{
|
||||
Application.Init ();
|
||||
var top = Application.Top;
|
||||
var win = new Window (new Rect (0, 0, 80, 24), "Hello") {
|
||||
new Label (new Rect (0, 0, 40, 3), "1-Hello world, how are you doing today") { TextAlignment = TextAlignment.Left },
|
||||
new Label (new Rect (0, 4, 40, 3), "2-Hello world, how are you doing today") { TextAlignment = TextAlignment.Right},
|
||||
new Label (new Rect (0, 8, 40, 3), "3-Hello world, how are you doing today") { TextAlignment = TextAlignment.Centered },
|
||||
new Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified},
|
||||
new Label (3, 14, "Login: "),
|
||||
new TextField (10, 14, 40, ""),
|
||||
new Button (3, 16, "Ok")
|
||||
};
|
||||
top.Add (win);
|
||||
Application.Run ();
|
||||
}
|
||||
static void Main ()
|
||||
{
|
||||
Application.Init ();
|
||||
var top = Application.Top;
|
||||
var win = new Window (new Rect (0, 0, 80, 24), "Hello") {
|
||||
new Label (new Rect (0, 0, 40, 3), "1-Hello world, how are you doing today") { TextAlignment = TextAlignment.Left },
|
||||
new Label (new Rect (0, 4, 40, 3), "2-Hello world, how are you doing today") { TextAlignment = TextAlignment.Right},
|
||||
new Label (new Rect (0, 8, 40, 3), "3-Hello world, how are you doing today") { TextAlignment = TextAlignment.Centered },
|
||||
new Label (new Rect (0, 12, 40, 3), "4-Hello world, how are you doing today") { TextAlignment = TextAlignment.Justified},
|
||||
//new Button (3, 16, "Ok"),
|
||||
new Label (3, 14, "Login: "),
|
||||
new TextField (10, 14, 40, ""),
|
||||
};
|
||||
top.Add (win);
|
||||
Application.Run ();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user