浏览代码

Focus works, hot-keys on buttons, cursor positioning, Window repaint

Miguel de Icaza 7 年之前
父节点
当前提交
27a8e1ca9d
共有 5 个文件被更改,包括 344 次插入289 次删除
  1. 56 17
      Core.cs
  2. 101 101
      Event.cs
  3. 161 161
      Views/Button.cs
  4. 1 1
      Views/TextField.cs
  5. 25 9
      demo.cs

+ 56 - 17
Core.cs

@@ -16,7 +16,7 @@ namespace Terminal {
 
 	public class Responder {
 		public virtual bool CanFocus { get; set; }
-		public bool HasFocus { get; internal set; }
+		public virtual bool HasFocus { get; internal set; }
 
 		// Key handling
 		/// <summary>
@@ -198,6 +198,14 @@ namespace Terminal {
 				CanFocus = true;
 		}
 
+		public void Add (params View [] views)
+		{
+			if (views == null)
+				return;
+			foreach (var view in views)
+				Add (view);
+		}
+
 		/// <summary>
 		///   Removes all the widgets from this container.
 		/// </summary>
@@ -332,6 +340,16 @@ namespace Terminal {
 				Move (frame.X, frame.Y);
 		}
 
+		public override bool HasFocus {
+			get {
+				return base.HasFocus;
+			}
+			internal set {
+				if (base.HasFocus != value)
+					SetNeedsDisplay ();
+				base.HasFocus = value;
+			}
+		}
 		/// <summary>
 		/// Returns the currently focused view inside this view, or null if nothing is focused.
 		/// </summary>
@@ -403,13 +421,12 @@ namespace Terminal {
 			if (c == null)
 				throw new ArgumentException ("the specified view is not part of the hierarchy of this view");
 
-			if (focused != null)
+			if (focused != null) 
 				focused.HasFocus = false;
+			
 			focused = view;
-			view.HasFocus = true;
-			if (view != null)
-				view.EnsureFocus ();
-			focused.PositionCursor ();
+			focused.HasFocus = true;
+			focused.EnsureFocus ();
 		}
 
 		public override bool ProcessKey (KeyEvent kb)
@@ -420,6 +437,25 @@ namespace Terminal {
 			return false;
 		}
 
+		public override bool ProcessHotKey (KeyEvent kb)
+		{
+			if (subviews == null || subviews.Count == 0)
+				return false;
+			foreach (var view in subviews)
+				if (view.ProcessHotKey (kb))
+					return true;
+			return false;
+		}
+
+		public override bool ProcessColdKey (KeyEvent kb)
+		{
+			if (subviews == null || subviews.Count == 0)
+				return false;
+			foreach (var view in subviews)
+				if (view.ProcessHotKey (kb))
+					return true;
+			return false;
+		}
 
 		/// <summary>
 		/// Finds the first view in the hierarchy that wants to get the focus if nothing is currently focused, otherwise, it does nothing.
@@ -677,19 +713,21 @@ namespace Terminal {
 
 		public override void Redraw (Rect bounds)
 		{
-			Driver.SetAttribute (Colors.Base.Normal);
-			DrawFrame ();
-			if (HasFocus)
+			if (NeedDisplay) {
+				Driver.SetAttribute (Colors.Base.Normal);
+				DrawFrame ();
+				if (HasFocus)
+					Driver.SetAttribute (Colors.Dialog.Normal);
+				var width = Frame.Width;
+				if (Title != null && width > 4) {
+					Move (1, 0);
+					Driver.AddCh (' ');
+					var str = Title.Length > width ? Title.Substring (0, width - 4) : Title;
+					Driver.AddStr (str);
+					Driver.AddCh (' ');
+				}
 				Driver.SetAttribute (Colors.Dialog.Normal);
-			var width = Frame.Width;
-			if (Title != null && width > 4) {
-				Move (1, 0);
-				Driver.AddCh (' ');
-				var str = Title.Length > width ? Title.Substring (0, width - 4) : Title;
-				Driver.AddStr (str);
-				Driver.AddCh (' ');
 			}
-			Driver.SetAttribute (Colors.Dialog.Normal);
 			contentView.Redraw (contentView.Bounds);
 		}
 	}
@@ -848,6 +886,7 @@ namespace Terminal {
 					return;
 				if (state.Toplevel.NeedDisplay || state.Toplevel.childNeedsDisplay) {
 					state.Toplevel.Redraw (state.Toplevel.Bounds);
+					state.Toplevel.PositionCursor ();
 					Driver.Refresh ();
 				}
 			}

+ 101 - 101
Event.cs

@@ -1,114 +1,114 @@
 namespace Terminal {
 
-    /// <summary>
-    /// The Key enumeration contains special encoding for some keys, but can also
-    /// encode all the unicode values that can be passed.   
-    /// </summary>
-    /// <remarks>
-    /// <para>
-    ///   If the SpecialMask is set, then the value is that of the special mask,
-    ///   otherwise, the value is the one of the lower bits (as extracted by CharMask)
-    /// </para>
-    /// <para>
-    ///   Control keys are the values between 1 and 26 corresponding to Control-A to Control-Z
-    /// </para>
-    /// </remarks>
-    public enum Key : uint {
-        CharMask = 0xfffff,
-        SpecialMask = 0xfff00000,
-        ControlA = 1,
-        ControlB,
-        ControlC,
-        ControlD,
-        ControlE,
-        ControlF,
-        ControlG,
-        ControlH,
-        ControlI,
-        Tab = ControlI,
-        ControlJ,
-        ControlK,
-        ControlL,
-        ControlM,
-        ControlN,
-        ControlO,
-        ControlP,
-        ControlQ,
-        ControlR,
-        ControlS,
-        ControlT,
-        ControlU,
-        ControlV,
-        ControlW,
-        ControlX,
-        ControlY,
-        ControlZ,
-        Esc = 27,
-        Space = 32,
-        Delete = 127,
+	/// <summary>
+	/// The Key enumeration contains special encoding for some keys, but can also
+	/// encode all the unicode values that can be passed.   
+	/// </summary>
+	/// <remarks>
+	/// <para>
+	///   If the SpecialMask is set, then the value is that of the special mask,
+	///   otherwise, the value is the one of the lower bits (as extracted by CharMask)
+	/// </para>
+	/// <para>
+	///   Control keys are the values between 1 and 26 corresponding to Control-A to Control-Z
+	/// </para>
+	/// </remarks>
+	public enum Key : uint {
+		CharMask = 0xfffff,
+		SpecialMask = 0xfff00000,
+		ControlA = 1,
+		ControlB,
+		ControlC,
+		ControlD,
+		ControlE,
+		ControlF,
+		ControlG,
+		ControlH,
+		ControlI,
+		Tab = ControlI,
+		ControlJ,
+		ControlK,
+		ControlL,
+		ControlM,
+		ControlN,
+		ControlO,
+		ControlP,
+		ControlQ,
+		ControlR,
+		ControlS,
+		ControlT,
+		ControlU,
+		ControlV,
+		ControlW,
+		ControlX,
+		ControlY,
+		ControlZ,
+		Esc = 27,
+		Space = 32,
+		Delete = 127,
 
-        AltMask = 0x80000000,
+		AltMask = 0x80000000,
 
-        Backspace = 0x100000,
-        CursorUp,
-        CursorDown,
-        CursorLeft,
-        CursorRight,
-        PageUp,
-        PageDown,
-        Home,
-        End,
-        DeleteChar,
-        InsertChar,
-        F1,
-        F2,
-        F3,
-        F4,
-        F5,
-        F6,
-        F7,
-        F8,
-        F9,
-        F10,
-        BackTab,
-        Unknown
-    }
+		Backspace = 0x100000,
+		CursorUp,
+		CursorDown,
+		CursorLeft,
+		CursorRight,
+		PageUp,
+		PageDown,
+		Home,
+		End,
+		DeleteChar,
+		InsertChar,
+		F1,
+		F2,
+		F3,
+		F4,
+		F5,
+		F6,
+		F7,
+		F8,
+		F9,
+		F10,
+		BackTab,
+		Unknown
+	}
 
-    public struct KeyEvent {
-        public Key Key;
-        public int KeyValue => (int)Key;
-        public bool IsAlt => (Key & Key.AltMask) != 0;
-        public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
+	public struct KeyEvent {
+		public Key Key;
+		public int KeyValue => (int)Key;
+		public bool IsAlt => (Key & Key.AltMask) != 0;
+		public bool IsCtrl => ((uint)Key >= 1) && ((uint)Key <= 26);
 
-        public KeyEvent (Key k)
-        {
-            Key = k;
-        }
-    }
+		public KeyEvent (Key k)
+		{
+			Key = k;
+		}
+	}
 
-    public class Event {
-        public class Key : Event {
-            public int Code { get; private set; }
-            public bool Alt { get; private set; }
-            public Key (int code)
-            {
-                Code = code;
-            }
-        }
+	public class Event {
+		public class Key : Event {
+			public int Code { get; private set; }
+			public bool Alt { get; private set; }
+			public Key (int code)
+			{
+				Code = code;
+			}
+		}
 
-        public class Mouse : Event {
-        }
+		public class Mouse : Event {
+		}
 
-        public static Event CreateMouseEvent ()
-        {
-            return new Mouse ();
-        }
+		public static Event CreateMouseEvent ()
+		{
+			return new Mouse ();
+		}
 
-        public static Event CreateKeyEvent (int code)
-        {
-            return new Key (code);
-        }
+		public static Event CreateKeyEvent (int code)
+		{
+			return new Key (code);
+		}
 
-    }
+	}
 
 }

+ 161 - 161
Views/Button.cs

@@ -3,166 +3,166 @@ using System.Collections.Generic;
 using System.Linq;
 
 namespace Terminal {
-    /// <summary>
-    ///   Button view
-    /// </summary>
-    /// <remarks>
-    ///   Provides a button that can be clicked, or pressed with
-    ///   the enter key and processes hotkeys (the first uppercase
-    ///   letter in the button becomes the hotkey).
-    /// </remarks>
-    public class Button : View {
-        string text;
-        string shown_text;
-        char hot_key;
-        int hot_pos = -1;
-        bool is_default;
-
-        /// <summary>
-        ///   Clicked event, raised when the button is clicked.
-        /// </summary>
-        /// <remarks>
-        ///   Client code can hook up to this event, it is
-        ///   raised when the button is activated either with
-        ///   the mouse or the keyboard.
-        /// </remarks>
-        public event EventHandler Clicked;
-
-        /// <summary>
-        ///   Public constructor, creates a button based on
-        ///   the given text at position 0,0
-        /// </summary>
-        /// <remarks>
-        ///   The size of the button is computed based on the
-        ///   text length.   This button is not a default button.
-        /// </remarks>
-        public Button (string s) : this (0, 0, s) { }
-
-        /// <summary>
-        ///   Public constructor, creates a button based on
-        ///   the given text.
-        /// </summary>
-        /// <remarks>
-        ///   If the value for is_default is true, a special
-        ///   decoration is used, and the enter key on a
-        ///   dialog would implicitly activate this button.
-        /// </remarks>
-        public Button (string s, bool is_default) : this (0, 0, s, is_default) { }
-
-        /// <summary>
-        ///   Public constructor, creates a button based on
-        ///   the given text at the given position.
-        /// </summary>
-        /// <remarks>
-        ///   The size of the button is computed based on the
-        ///   text length.   This button is not a default button.
-        /// </remarks>
-        public Button (int x, int y, string s) : this (x, y, s, false) { }
-
-        /// <summary>
-        ///   The text displayed by this widget.
-        /// </summary>
-        public string Text {
-            get {
-                return text;
-            }
-
-            set {
-                text = value;
-                if (is_default)
-                    shown_text = "[< " + value + " >]";
-                else
-                    shown_text = "[ " + value + " ]";
-
-                hot_pos = -1;
-                hot_key = (char)0;
-                int i = 0;
-                foreach (char c in shown_text) {
-                    if (Char.IsUpper (c)) {
-                        hot_key = c;
-                        hot_pos = i;
-                        break;
-                    }
-                    i++;
-                }
-            }
-        }
-
-        /// <summary>
-        ///   Public constructor, creates a button based on
-        ///   the given text at the given position.
-        /// </summary>
-        /// <remarks>
-        ///   If the value for is_default is true, a special
-        ///   decoration is used, and the enter key on a
-        ///   dialog would implicitly activate this button.
-        /// </remarks>
-        public Button (int x, int y, string s, bool is_default)
-            : base (new Rect (x, y, s.Length + 4 + (is_default ? 2 : 0), 1))
-        {
-            CanFocus = true;
-
-            this.is_default = is_default;
-            Text = s;
-        }
-
-        public override void Redraw (Rect region)
-        {
-            Driver.SetAttribute (HasFocus ? Colors.Base.Focus : Colors.Base.Normal);
-            Move (0, 0);
-            Driver.AddStr (shown_text);
-
-            if (hot_pos != -1) {
-                Move (hot_pos, 0);
-                Driver.SetAttribute (HasFocus ? Colors.Base.HotFocus: Colors.Base.HotNormal);
-                Driver.AddCh (hot_key);
-            }
-        }
-
-        public override void PositionCursor ()
-        {
-            Move (hot_pos, 0);
-        }
-
-        bool CheckKey (KeyEvent key)
-        {
-            if (Char.ToUpper ((char)key.KeyValue) == hot_key) {
-                this.SetFocus (this);
-                if (Clicked != null)
-                    Clicked (this, EventArgs.Empty);
-                return true;
-            }
-            return false;
-        }
-
-        public override bool ProcessHotKey (KeyEvent kb)
-        {
-            if (kb.IsAlt)
-                return CheckKey (kb);
-
-            return false;
-        }
-
-        public override bool ProcessColdKey (KeyEvent kb)
-        {
-            if (is_default && kb.KeyValue == '\n') {
-                if (Clicked != null)
-                    Clicked (this, EventArgs.Empty);
-                return true;
-            }
-            return CheckKey (kb);
-        }
-
-        public override bool ProcessKey (KeyEvent kb)
-        {
-            var c = kb.KeyValue;
-            if (c == '\n' || c == ' ' || Char.ToUpper ((char)c) == hot_key) {
-                if (Clicked != null)
-                    Clicked (this, EventArgs.Empty);
-                return true;
-            }
-            return false;
-        }
+	/// <summary>
+	///   Button view
+	/// </summary>
+	/// <remarks>
+	///   Provides a button that can be clicked, or pressed with
+	///   the enter key and processes hotkeys (the first uppercase
+	///   letter in the button becomes the hotkey).
+	/// </remarks>
+	public class Button : View {
+		string text;
+		string shown_text;
+		char hot_key;
+		int hot_pos = -1;
+		bool is_default;
+
+		/// <summary>
+		///   Clicked event, raised when the button is clicked.
+		/// </summary>
+		/// <remarks>
+		///   Client code can hook up to this event, it is
+		///   raised when the button is activated either with
+		///   the mouse or the keyboard.
+		/// </remarks>
+		public event EventHandler Clicked;
+
+		/// <summary>
+		///   Public constructor, creates a button based on
+		///   the given text at position 0,0
+		/// </summary>
+		/// <remarks>
+		///   The size of the button is computed based on the
+		///   text length.   This button is not a default button.
+		/// </remarks>
+		public Button (string s) : this (0, 0, s) { }
+
+		/// <summary>
+		///   Public constructor, creates a button based on
+		///   the given text.
+		/// </summary>
+		/// <remarks>
+		///   If the value for is_default is true, a special
+		///   decoration is used, and the enter key on a
+		///   dialog would implicitly activate this button.
+		/// </remarks>
+		public Button (string s, bool is_default) : this (0, 0, s, is_default) { }
+
+		/// <summary>
+		///   Public constructor, creates a button based on
+		///   the given text at the given position.
+		/// </summary>
+		/// <remarks>
+		///   The size of the button is computed based on the
+		///   text length.   This button is not a default button.
+		/// </remarks>
+		public Button (int x, int y, string s) : this (x, y, s, false) { }
+
+		/// <summary>
+		///   The text displayed by this widget.
+		/// </summary>
+		public string Text {
+			get {
+				return text;
+			}
+
+			set {
+				text = value;
+				if (is_default)
+					shown_text = "[< " + value + " >]";
+				else
+					shown_text = "[ " + value + " ]";
+
+				hot_pos = -1;
+				hot_key = (char)0;
+				int i = 0;
+				foreach (char c in shown_text) {
+					if (Char.IsUpper (c)) {
+						hot_key = c;
+						hot_pos = i;
+						break;
+					}
+					i++;
+				}
+			}
+		}
+
+		/// <summary>
+		///   Public constructor, creates a button based on
+		///   the given text at the given position.
+		/// </summary>
+		/// <remarks>
+		///   If the value for is_default is true, a special
+		///   decoration is used, and the enter key on a
+		///   dialog would implicitly activate this button.
+		/// </remarks>
+		public Button (int x, int y, string s, bool is_default)
+		    : base (new Rect (x, y, s.Length + 4 + (is_default ? 2 : 0), 1))
+		{
+			CanFocus = true;
+
+			this.is_default = is_default;
+			Text = s;
+		}
+
+		public override void Redraw (Rect region)
+		{
+			Driver.SetAttribute (HasFocus ? Colors.Base.Focus : Colors.Base.Normal);
+			Move (0, 0);
+			Driver.AddStr (shown_text);
+
+			if (hot_pos != -1) {
+				Move (hot_pos, 0);
+				Driver.SetAttribute (HasFocus ? Colors.Base.HotFocus : Colors.Base.HotNormal);
+				Driver.AddCh (hot_key);
+			}
+		}
+
+		public override void PositionCursor ()
+		{
+			Move (hot_pos, 0);
+		}
+
+		bool CheckKey (KeyEvent key)
+		{
+			if (Char.ToUpper ((char)key.KeyValue) == hot_key) {
+				this.SuperView.SetFocus (this);
+				if (Clicked != null)
+					Clicked (this, EventArgs.Empty);
+				return true;
+			}
+			return false;
+		}
+
+		public override bool ProcessHotKey (KeyEvent kb)
+		{
+			if (kb.IsAlt)
+				return CheckKey (kb);
+
+			return false;
+		}
+
+		public override bool ProcessColdKey (KeyEvent kb)
+		{
+			if (is_default && kb.KeyValue == '\n') {
+				if (Clicked != null)
+					Clicked (this, EventArgs.Empty);
+				return true;
+			}
+			return CheckKey (kb);
+		}
+
+		public override bool ProcessKey (KeyEvent kb)
+		{
+			var c = kb.KeyValue;
+			if (c == '\n' || c == ' ' || Char.ToUpper ((char)c) == hot_key) {
+				if (Clicked != null)
+					Clicked (this, EventArgs.Empty);
+				return true;
+			}
+			return false;
+		}
 
 #if false
         public override void ProcessMouse (Curses.MouseEvent ev)
@@ -175,5 +175,5 @@ namespace Terminal {
             }
         }
 #endif
-    }
+	}
 }

+ 1 - 1
Views/TextField.cs

@@ -105,7 +105,7 @@ namespace Terminal {
 				if (p < text.Length) {
 					Driver.AddCh (Secret ? '*' : text [p]);
 				} else
-					Driver.AddCh ('_');
+					Driver.AddCh (' ');
 			}
 			PositionCursor ();
 		}

+ 25 - 9
demo.cs

@@ -1,19 +1,35 @@
 using Terminal;
 
 class Demo {
+	static void ShowTextAlignments (View container)
+	{
+		container.Add (
+			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 });
+	}
+
+	static void ShowEntries (View container)
+	{
+		container.Add (
+			new Label (3, 2, "Login: "),
+			new TextField (14, 2, 40, ""),
+			new Label (3, 4, "Password: "),
+			new TextField (14, 4, 40, "") { Secret = true },
+			new Button (3, 6, "Ok"),
+			new Button (10, 6, "Cancel")
+		);
+	}
+
 	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, ""),
-		};
+		var win = new Window (new Rect (0, 0, 80, 24), "Hello");
+
+		ShowEntries (win);
+		// ShowTextAlignments (win);
 		top.Add (win);
 		Application.Run ();
 	}