浏览代码

Some work on display flags

Miguel de Icaza 7 年之前
父节点
当前提交
59f73e587a
共有 7 个文件被更改,包括 1417 次插入1360 次删除
  1. 606 552
      Core.cs
  2. 324 324
      Driver.cs
  3. 1 1
      Event.cs
  4. 5 1
      TODO.md
  5. 172 172
      Views/Label.cs
  6. 293 294
      Views/TextField.cs
  7. 16 16
      demo.cs

文件差异内容过多而无法显示
+ 606 - 552
Core.cs


+ 324 - 324
Driver.cs

@@ -5,328 +5,328 @@ using Unix.Terminal;
 
 
 namespace 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
-    }
-
-    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 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 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);
-
-        // 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);
-
-        Rect clip;
-        public Rect Clip {
-            get => clip;
-            set => this.clip = value;
-        }
-    }
-
-    public class CursesDriver : ConsoleDriver {
-        Action terminalResized;
-
-        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;
-
-            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 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;
-
-        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];
-
-        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);
-        }
-
-        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;
-            }
-        }
-
-        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);
-
-                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);
-
-            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;
-
-            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;
-
-            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 ();
-
-                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;
-            }
-        }
-    }
+	/// <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 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 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);
+
+		// 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);
+
+		public abstract void DrawFrame (Rect region, bool fill);
+
+		Rect clip;
+		public Rect Clip {
+			get => clip;
+			set => this.clip = value;
+		}
+	}
+
+	public class CursesDriver : ConsoleDriver {
+		Action terminalResized;
+
+		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;
+
+			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 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;
+
+		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];
+
+		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);
+		}
+
+		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;
+			}
+		}
+
+		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);
+
+				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);
+
+			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;
+
+			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;
+
+			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 ();
+
+				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;
+			}
+		}
+	}
 }
 }

+ 1 - 1
Event.cs

@@ -76,7 +76,7 @@ namespace Terminal {
 
 
     public struct KeyEvent {
     public struct KeyEvent {
         public Key Key;
         public Key Key;
-        public int KeyValue => (int)KeyValue;
+        public int KeyValue => (int)Key;
         public bool IsAlt => (Key & Key.AltMask) != 0;
         public bool IsAlt => (Key & Key.AltMask) != 0;
         public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
         public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
 
 

+ 5 - 1
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
 On the demo, press tab twice, instead of selecting Ok, the first tab
 does nothing, the second tab clears the screen.
 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
 # Merge Responder into View
 
 
-# Make HasFocus implicitly call SetNeedsDisplay
+# Make HasFocus implicitly call SetNeedsDisplay

+ 172 - 172
Views/Label.cs

@@ -4,177 +4,177 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 
 
 namespace Terminal {
 namespace Terminal {
-    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;
-
-        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);
-        }
-
-        /// <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;
-        }
-
-        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);
-
-                    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];
-
-                if (c == '\n') {
-                    lines.Add (ClipAndJustify (text.Substring (lp, i - lp)));
-                    lp = i + 1;
-                }
-            }
-            recalcPending = false;
-        }
-
-        public override void Redraw (Rect region)
-        {
-            if (recalcPending)
-                Recalc ();
-            
-            if (TextColor != -1)
-                Driver.SetAttribute (TextColor);
-            else
-                Driver.SetAttribute(Colors.Base.Normal);
-
-            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);
-            }
-        }
-
-        /// <summary>
-        ///   The text displayed by this widget.
-        /// </summary>
-        public virtual string Text {
-            get => text;
-            set {
-                text = value;
-                recalcPending = true;
-                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 ();
-            }
-        }
-    }
+	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;
+
+		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);
+		}
+
+		/// <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;
+		}
+
+		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);
+
+					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 ()
+		{
+			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;
+				}
+			}
+		}
+
+		public override void Redraw (Rect region)
+		{
+			if (recalcPending)
+				Recalc ();
+
+			if (TextColor != -1)
+				Driver.SetAttribute (TextColor);
+			else
+				Driver.SetAttribute (Colors.Base.Normal);
+
+			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);
+			}
+		}
+
+		/// <summary>
+		///   The text displayed by this widget.
+		/// </summary>
+		public virtual string Text {
+			get => text;
+			set {
+				text = value;
+				recalcPending = true;
+				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 ();
+			}
+		}
+	}
 
 
 }
 }

+ 293 - 294
Views/TextField.cs

@@ -3,299 +3,298 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Linq;
 
 
 namespace Terminal {
 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>
-        ///   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 = "";
-
-            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;
-            }
-
-            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; }
-
-        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>
-        ///   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);
-
-            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 ();
-        }
-
-        void Adjust ()
-        {
-            if (point < first)
-                first = point;
-            else if (first + point >= Frame.Width)
-                first = point - (Frame.Width / 3);
-            Redraw (Bounds);
-            Driver.Refresh ();
-        }
-
-        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 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;
-
-                // 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.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.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.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;
-
-            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;
-
-            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;
-        }
-
-        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 WordBackward (int p)
-        {
-            if (p == 0)
-                return -1;
-
-            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 (i != p)
-                return i;
-
-            return -1;
-        }
+	/// <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>
+		///   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;
+		}
+
+		/// <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 ();
+			}
+		}
+
+		/// <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 ();
+			}
+		}
+
+		/// <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);
+		}
+
+		public override void Redraw (Rect region)
+		{
+			Driver.SetAttribute (Color);
+			Move (0, 0);
+
+			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 ();
+		}
+
+		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);
+		}
+
+		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;
+
+				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;
+
+			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.ControlE: // End
+				point = text.Length;
+				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.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;
+
+			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;
+
+			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;
+		}
+
+		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 WordBackward (int p)
+		{
+			if (p == 0)
+				return -1;
+
+			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 (i != p)
+				return i;
+
+			return -1;
+		}
 
 
 #if false
 #if false
         public override void ProcessMouse (Curses.MouseEvent ev)
         public override void ProcessMouse (Curses.MouseEvent ev)
@@ -315,7 +314,7 @@ namespace Terminal {
             SetNeedsDisplay ();
             SetNeedsDisplay ();
         }
         }
 #endif
 #endif
-    }
+	}
 
 
 
 
 }
 }

+ 16 - 16
demo.cs

@@ -1,20 +1,20 @@
 using Terminal;
 using Terminal;
 
 
 class Demo {
 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 ();
+	}
 }
 }

部分文件因为文件数量过多而无法显示