瀏覽代碼

Merge pull request #2477 from tznind/caption

Adds Caption to TextField
Tig 2 年之前
父節點
當前提交
ae612c1555
共有 3 個文件被更改,包括 139 次插入2 次删除
  1. 36 1
      Terminal.Gui/Views/TextField.cs
  2. 1 1
      UICatalog/Scenarios/CharacterMap.cs
  3. 102 0
      UnitTests/Views/TextFieldTests.cs

+ 36 - 1
Terminal.Gui/Views/TextField.cs

@@ -1,4 +1,4 @@
-//
+//
 // TextField.cs: single-line text editor with Emacs keybindings
 //
 // Authors:
@@ -29,6 +29,19 @@ namespace Terminal.Gui {
 		HistoryText historyText = new HistoryText ();
 		CultureInfo currentCulture;
 
+		/// <summary>
+		/// Gets or sets the text to render in control when no value has 
+		/// been entered yet and the <see cref="View"/> does not yet have
+		/// input focus.
+		/// </summary>
+		public ustring Caption {get;set;}
+
+		/// <summary>
+		/// Gets or sets the foreground <see cref="Color"/> to use when 
+		/// rendering <see cref="Caption"/>.
+		/// </summary>
+		public Color CaptionColor {get;set;} = Color.DarkGray;
+
 		/// <summary>
 		/// Tracks whether the text field should be considered "used", that is, that the user has moved in the entry, so new input should be appended at the cursor position, rather than clearing the entry
 		/// </summary>
@@ -468,6 +481,8 @@ namespace Terminal.Gui {
 
 			PositionCursor ();
 
+			RenderCaption();
+			
 			if (SelectedLength > 0)
 				return;
 
@@ -481,6 +496,26 @@ namespace Terminal.Gui {
 			Autocomplete.RenderOverlay (renderAt);
 		}
 
+		private void RenderCaption ()
+		{
+			
+			if (HasFocus || Caption == null || Caption.Length == 0
+				|| Text?.Length > 0) {
+				return;
+			}
+
+			var color = new Attribute (CaptionColor, GetNormalColor ().Background);
+			Driver.SetAttribute (color);
+
+			Move (0, 0);
+			var render = Caption;
+
+			if (render.ConsoleWidth > Bounds.Width) {
+				render = render.RuneSubstring (0, Bounds.Width);
+			}
+
+			Driver.AddStr (render);
+		}
 		private void GenerateSuggestions ()
 		{
 			var currentLine = Text.ToRuneList ();

+ 1 - 1
UICatalog/Scenarios/CharacterMap.cs

@@ -36,7 +36,7 @@ namespace UICatalog.Scenarios {
 
 			var jumpLabel = new Label ("Jump To Glyph:") { X = Pos.Right (_charMap) + 1, Y = Pos.Y (_charMap) };
 			Win.Add (jumpLabel);
-			var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, };
+			var jumpEdit = new TextField () { X = Pos.Right (jumpLabel) + 1, Y = Pos.Y (_charMap), Width = 10, Caption = "e.g. 01BE3"};
 			Win.Add (jumpEdit);
 			var errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] };
 			Win.Add (errorLabel);

+ 102 - 0
UnitTests/Views/TextFieldTests.cs

@@ -1,6 +1,7 @@
 using NStack;
 using System;
 using System.Globalization;
+using System.Linq;
 using System.Reflection;
 using Xunit;
 using Xunit.Abstractions;
@@ -1440,6 +1441,107 @@ Les Miśerables", output);
 			TestHelpers.AssertDriverContentsWithFrameAre (@"
 ắ", output);
 		}
+	
+		[Fact, AutoInitShutdown]
+		public void CaptionedTextField_RendersCaption_WhenNotFocused ()
+		{
+			var tf = GetTextFieldsInView();
+
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("",output);
+
+			// Caption has no effect when focused
+			tf.Caption  = "Enter txt";
+			Assert.True(tf.HasFocus);
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("",output);
+
+			Application.Driver.SendKeys('\t',ConsoleKey.Tab,false,false,false);
+
+			Assert.False(tf.HasFocus);
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("Enter txt",output);		
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData("blah")]
+		[InlineData(" ")]
+		public void CaptionedTextField_DoNotRenderCaption_WhenTextPresent (string content)
+		{
+			var tf = GetTextFieldsInView();
+
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("",output);
+
+			tf.Caption  = "Enter txt";
+			Application.Driver.SendKeys('\t',ConsoleKey.Tab,false,false,false);
+
+			// Caption should appear when not focused and no text
+			Assert.False(tf.HasFocus);
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("Enter txt",output);
+
+			// but disapear when text is added
+			tf.Text = content;
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre(content,output);
+		}
+
+
+		[Fact, AutoInitShutdown]
+		public void CaptionedTextField_DoesNotOverspillBounds_Unicode ()
+		{
+			var caption = "Mise" + Char.ConvertFromUtf32 (Int32.Parse ("0301", NumberStyles.HexNumber)) + "rables";
+
+			Assert.Equal(11,caption.Length);
+			Assert.Equal(10,caption.Sum(c => Rune.ColumnWidth(c)));
+
+			var tf = GetTextFieldsInView();
+			
+			tf.Caption  = caption;
+			Application.Driver.SendKeys('\t',ConsoleKey.Tab,false,false,false);
+			Assert.False(tf.HasFocus);
+
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre("Misérables",output);
+		}
+
+		[Theory, AutoInitShutdown]
+		[InlineData("0123456789","0123456789")]
+		[InlineData("01234567890","0123456789")]
+		public void CaptionedTextField_DoesNotOverspillBounds (string caption, string expectedRender)
+		{
+			var tf = GetTextFieldsInView();
+			// Caption has no effect when focused
+			tf.Caption  = caption;
+			Application.Driver.SendKeys('\t',ConsoleKey.Tab,false,false,false);
+			Assert.False(tf.HasFocus);
+
+			tf.Redraw(tf.Bounds);
+			TestHelpers.AssertDriverContentsAre(expectedRender,output);
+		}
+
+
+		private TextField GetTextFieldsInView ()
+		{
+            var tf = new TextField{
+				Width = 10
+			};
+            var tf2 = new TextField{
+				Y = 1,
+				Width = 10
+			};
+
+			var top = Application.Top;
+			top.Add (tf);
+			top.Add (tf2);
+
+			Application.Begin (top);
+			
+			Assert.Same(tf,top.Focused);
+
+			return tf;
+    	}
 
 		[Fact]
 		public void OnEnter_Does_Not_Throw_If_Not_IsInitialized_SetCursorVisibility ()