Browse Source

Reformulates Checkbox with alignment features.

BDisp 3 years ago
parent
commit
31795d7cdb
2 changed files with 372 additions and 51 deletions
  1. 101 41
      Terminal.Gui/Views/Checkbox.cs
  2. 271 10
      UnitTests/CheckboxTests.cs

+ 101 - 41
Terminal.Gui/Views/Checkbox.cs

@@ -14,8 +14,11 @@ namespace Terminal.Gui {
 	/// </summary>
 	/// </summary>
 	public class CheckBox : View {
 	public class CheckBox : View {
 		ustring text;
 		ustring text;
-		int hot_pos = -1;
-		Rune hot_key;
+		Key hotKey = Key.Null;
+		Rune hotKeySpecifier;
+		Rune charChecked;
+		Rune charUnChecked;
+		bool @checked;
 
 
 		/// <summary>
 		/// <summary>
 		///   Toggled event, raised when the <see cref="CheckBox"/>  is toggled.
 		///   Toggled event, raised when the <see cref="CheckBox"/>  is toggled.
@@ -75,10 +78,13 @@ namespace Terminal.Gui {
 
 
 		void Initialize (ustring s, bool is_checked)
 		void Initialize (ustring s, bool is_checked)
 		{
 		{
+			charChecked = new Rune (Driver != null ? Driver.Checked : '√');
+			charUnChecked = new Rune (Driver != null ? Driver.UnChecked : '╴');
 			Checked = is_checked;
 			Checked = is_checked;
-			Text = s;
+			HotKeySpecifier = new Rune ('_');
 			CanFocus = true;
 			CanFocus = true;
 			AutoSize = true;
 			AutoSize = true;
+			Text = s;
 			Update ();
 			Update ();
 
 
 			// Things this view knows how to do
 			// Things this view knows how to do
@@ -89,21 +95,95 @@ namespace Terminal.Gui {
 			AddKeyBinding (Key.Space, Command.ToggleChecked);
 			AddKeyBinding (Key.Space, Command.ToggleChecked);
 		}
 		}
 
 
-		private void Update ()
+		void Update ()
 		{
 		{
-			TextFormatter.Text = text;
-			var h = 1;
-			var w = text.ConsoleWidth + 4;
-			TextFormatter.Size = new Size (w, h);
-			Height = h;
-			TextFormatter.Size = new Size (w, h);
-			Width = w;
+			switch (TextAlignment) {
+			case TextAlignment.Left:
+			case TextAlignment.Centered:
+			case TextAlignment.Justified:
+				if (Checked)
+					TextFormatter.Text = ustring.Make (charChecked) + " " + GetFormatterText ();
+				else
+					TextFormatter.Text = ustring.Make (charUnChecked) + " " + GetFormatterText ();
+				break;
+			case TextAlignment.Right:
+				if (Checked)
+					TextFormatter.Text = GetFormatterText () + " " + ustring.Make (charChecked);
+				else
+					TextFormatter.Text = GetFormatterText () + " " + ustring.Make (charUnChecked);
+				break;
+			}
+
+			int w = TextFormatter.Size.Width - (TextFormatter.Text.Contains (HotKeySpecifier) ? 1 : 0);
+			GetCurrentWidth (out int cWidth);
+			var canSetWidth = SetWidth (w, out int rWidth);
+			if (canSetWidth && (cWidth < rWidth || AutoSize)) {
+				Width = rWidth;
+				w = rWidth;
+			} else if (!canSetWidth || !AutoSize) {
+				w = cWidth;
+			}
+			var layout = LayoutStyle;
+			bool layoutChanged = false;
+			if (!(Height is Dim.DimAbsolute)) {
+				// The height is always equal to 1 and must be Dim.DimAbsolute.
+				layoutChanged = true;
+				LayoutStyle = LayoutStyle.Absolute;
+			}
+			Height = 1;
+			if (layoutChanged) {
+				LayoutStyle = layout;
+			}
+			Frame = new Rect (Frame.Location, new Size (w, 1));
+			SetNeedsDisplay ();
+		}
+
+		ustring GetFormatterText ()
+		{
+			if (AutoSize || ustring.IsNullOrEmpty (text)) {
+				return text;
+			}
+			return text.RuneSubstring (0, Math.Min (Frame.Width - 2, text.RuneCount));
+		}
+
+		/// <inheritdoc/>
+		public override Key HotKey {
+			get => hotKey;
+			set {
+				if (hotKey != value) {
+					var v = value == Key.Unknown ? Key.Null : value;
+					hotKey = v;
+				}
+			}
+		}
+
+		/// <inheritdoc/>
+		public override Rune HotKeySpecifier {
+			get => hotKeySpecifier;
+			set {
+				hotKeySpecifier = TextFormatter.HotKeySpecifier = value;
+			}
+		}
+
+		/// <inheritdoc/>
+		public override bool AutoSize {
+			get => base.AutoSize;
+			set {
+				base.AutoSize = value;
+				Update ();
+			}
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
 		///    The state of the <see cref="CheckBox"/>
 		///    The state of the <see cref="CheckBox"/>
 		/// </summary>
 		/// </summary>
-		public bool Checked { get; set; }
+		public bool Checked {
+			get => @checked;
+			set {
+				@checked = value;
+				Update ();
+			}
+		}
 
 
 		/// <summary>
 		/// <summary>
 		///   The text displayed by this <see cref="CheckBox"/>
 		///   The text displayed by this <see cref="CheckBox"/>
@@ -115,40 +195,20 @@ namespace Terminal.Gui {
 
 
 			set {
 			set {
 				text = value;
 				text = value;
-
-				int i = 0;
-				hot_pos = -1;
-				hot_key = (char)0;
-				foreach (Rune c in text) {
-					//if (Rune.IsUpper (c)) {
-					if (c == '_') {
-						hot_key = text [i + 1];
-						HotKey = (Key)(char)hot_key.ToString ().ToUpper () [0];
-						text = text.ToString ().Replace ("_", "");
-						hot_pos = i;
-						break;
-					}
-					i++;
-				}
-				if (AutoSize) {
-					Update ();
+				TextFormatter.FindHotKey (text, HotKeySpecifier, true, out _, out Key hk);
+				if (hotKey != hk) {
+					HotKey = hk;
 				}
 				}
+				Update ();
 			}
 			}
 		}
 		}
 
 
 		///<inheritdoc/>
 		///<inheritdoc/>
-		public override void Redraw (Rect bounds)
-		{
-			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : GetNormalColor ());
-			Move (0, 0);
-			Driver.AddRune (Checked ? Driver.Checked : Driver.UnChecked);
-			Driver.AddRune (' ');
-			Move (2, 0);
-			Driver.AddStr (Text);
-			if (hot_pos != -1) {
-				Move (2 + hot_pos, 0);
-				Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : Enabled ? ColorScheme.HotNormal : ColorScheme.Disabled);
-				Driver.AddRune (hot_key);
+		public override TextAlignment TextAlignment {
+			get => base.TextAlignment;
+			set {
+				base.TextAlignment = value;
+				Update ();
 			}
 			}
 		}
 		}
 
 

+ 271 - 10
UnitTests/CheckboxTests.cs

@@ -22,25 +22,25 @@ namespace Terminal.Gui.Views {
 			Assert.False (ckb.Checked);
 			Assert.False (ckb.Checked);
 			Assert.Equal (string.Empty, ckb.Text);
 			Assert.Equal (string.Empty, ckb.Text);
 			Assert.True (ckb.CanFocus);
 			Assert.True (ckb.CanFocus);
-			Assert.Equal (new Rect (0, 0, 4, 1), ckb.Frame);
+			Assert.Equal (new Rect (0, 0, 2, 1), ckb.Frame);
 
 
 			ckb = new CheckBox ("Test", true);
 			ckb = new CheckBox ("Test", true);
 			Assert.True (ckb.Checked);
 			Assert.True (ckb.Checked);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.True (ckb.CanFocus);
 			Assert.True (ckb.CanFocus);
-			Assert.Equal (new Rect (0, 0, 8, 1), ckb.Frame);
+			Assert.Equal (new Rect (0, 0, 6, 1), ckb.Frame);
 
 
 			ckb = new CheckBox (1, 2, "Test");
 			ckb = new CheckBox (1, 2, "Test");
 			Assert.False (ckb.Checked);
 			Assert.False (ckb.Checked);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.True (ckb.CanFocus);
 			Assert.True (ckb.CanFocus);
-			Assert.Equal (new Rect (1, 2, 8, 1), ckb.Frame);
+			Assert.Equal (new Rect (1, 2, 6, 1), ckb.Frame);
 
 
 			ckb = new CheckBox (3, 4, "Test", true);
 			ckb = new CheckBox (3, 4, "Test", true);
 			Assert.True (ckb.Checked);
 			Assert.True (ckb.Checked);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.Equal ("Test", ckb.Text);
 			Assert.True (ckb.CanFocus);
 			Assert.True (ckb.CanFocus);
-			Assert.Equal (new Rect (3, 4, 8, 1), ckb.Frame);
+			Assert.Equal (new Rect (3, 4, 6, 1), ckb.Frame);
 		}
 		}
 
 
 		[Fact]
 		[Fact]
@@ -48,17 +48,19 @@ namespace Terminal.Gui.Views {
 		public void KeyBindings_Command ()
 		public void KeyBindings_Command ()
 		{
 		{
 			var isChecked = false;
 			var isChecked = false;
-			CheckBox ckb = new CheckBox ("Test");
+			CheckBox ckb = new CheckBox ();
 			ckb.Toggled += (e) => isChecked = true;
 			ckb.Toggled += (e) => isChecked = true;
 			Application.Top.Add (ckb);
 			Application.Top.Add (ckb);
 			Application.Begin (Application.Top);
 			Application.Begin (Application.Top);
 
 
 			Assert.Equal (Key.Null, ckb.HotKey);
 			Assert.Equal (Key.Null, ckb.HotKey);
+			ckb.Text = "Test";
+			Assert.Equal (Key.T, ckb.HotKey);
 			Assert.False (ckb.ProcessHotKey (new KeyEvent (Key.T, new KeyModifiers ())));
 			Assert.False (ckb.ProcessHotKey (new KeyEvent (Key.T, new KeyModifiers ())));
 			Assert.False (isChecked);
 			Assert.False (isChecked);
-			ckb.Text = "_Test";
-			Assert.Equal (Key.T, ckb.HotKey);
-			Assert.True (ckb.ProcessHotKey (new KeyEvent (Key.T | Key.AltMask, new KeyModifiers () { Alt = true })));
+			ckb.Text = "T_est";
+			Assert.Equal (Key.E, ckb.HotKey);
+			Assert.True (ckb.ProcessHotKey (new KeyEvent (Key.E | Key.AltMask, new KeyModifiers () { Alt = true })));
 			Assert.True (isChecked);
 			Assert.True (isChecked);
 			isChecked = false;
 			isChecked = false;
 			Assert.True (ckb.ProcessKey (new KeyEvent ((Key)' ', new KeyModifiers ())));
 			Assert.True (ckb.ProcessKey (new KeyEvent ((Key)' ', new KeyModifiers ())));
@@ -66,8 +68,17 @@ namespace Terminal.Gui.Views {
 			isChecked = false;
 			isChecked = false;
 			Assert.True (ckb.ProcessKey (new KeyEvent (Key.Space, new KeyModifiers ())));
 			Assert.True (ckb.ProcessKey (new KeyEvent (Key.Space, new KeyModifiers ())));
 			Assert.True (isChecked);
 			Assert.True (isChecked);
-		}
+			Assert.True (ckb.AutoSize);
+
+			Application.Refresh ();
+
+			var expected = @"
+√ Test
+";
 
 
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 6, 1), pos);
+		}
 
 
 		[Fact, AutoInitShutdown]
 		[Fact, AutoInitShutdown]
 		public void AutoSize_StaysVisible ()
 		public void AutoSize_StaysVisible ()
@@ -91,7 +102,10 @@ namespace Terminal.Gui.Views {
 			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
 			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
 
 
 			Assert.True (checkBox.IsInitialized);
 			Assert.True (checkBox.IsInitialized);
+			Assert.Equal (new Rect (1, 1, 19, 1), checkBox.Frame);
 			Assert.Equal ("Check this out 你", checkBox.Text);
 			Assert.Equal ("Check this out 你", checkBox.Text);
+			Assert.Equal ("╴ Check this out 你", checkBox.TextFormatter.Text);
+			Assert.True (checkBox.AutoSize);
 
 
 			var expected = @"
 			var expected = @"
 ┌ Test Demo 你 ──────────────┐
 ┌ Test Demo 你 ──────────────┐
@@ -105,13 +119,260 @@ namespace Terminal.Gui.Views {
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 
 
-			// Negative test
+			// Also Positive test
 			checkBox.AutoSize = true;
 			checkBox.AutoSize = true;
 			bool first = false;
 			bool first = false;
 			Application.RunMainLoopIteration (ref runstate, true, ref first);
 			Application.RunMainLoopIteration (ref runstate, true, ref first);
 
 
 			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.Checked = true;
+			Application.RunMainLoopIteration (ref runstate, true, ref first);
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ √ Check this out 你        │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.AutoSize = false;
+			checkBox.Text = "Check this out 你 changed";
+			Application.RunMainLoopIteration (ref runstate, true, ref first);
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ √ Check this out 你        │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.AutoSize = true;
+			Application.RunMainLoopIteration (ref runstate, true, ref first);
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ √ Check this out 你 changed│
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TextAlignment_Left ()
+		{
+			var checkBox = new CheckBox () {
+				X = 1,
+				Y = Pos.Center (),
+				Text = "Check this out 你",
+				Width = 25
+			};
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (checkBox);
+			Application.Top.Add (win);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+
+			Assert.Equal (TextAlignment.Left, checkBox.TextAlignment);
+			Assert.Equal (new Rect (1, 1, 25, 1), checkBox.Frame);
+			Assert.Equal ("Check this out 你", checkBox.Text);
+			Assert.Equal ("╴ Check this out 你", checkBox.TextFormatter.Text);
+			Assert.False (checkBox.AutoSize);
+
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ ╴ Check this out 你        │
+│                            │
+└────────────────────────────┘
+";
+
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.Checked = true;
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ √ Check this out 你        │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TextAlignment_Centered ()
+		{
+			var checkBox = new CheckBox () {
+				X = 1,
+				Y = Pos.Center (),
+				Text = "Check this out 你",
+				Width = 25,
+				TextAlignment = TextAlignment.Centered
+			};
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (checkBox);
+			Application.Top.Add (win);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+
+			Assert.Equal (TextAlignment.Centered, checkBox.TextAlignment);
+			Assert.Equal (new Rect (1, 1, 25, 1), checkBox.Frame);
+			Assert.Equal ("Check this out 你", checkBox.Text);
+			Assert.Equal ("╴ Check this out 你", checkBox.TextFormatter.Text);
+			Assert.False (checkBox.AutoSize);
+
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│    ╴ Check this out 你     │
+│                            │
+└────────────────────────────┘
+";
+
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.Checked = true;
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│    √ Check this out 你     │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TextAlignment_Justified ()
+		{
+			var checkBox = new CheckBox () {
+				X = 1,
+				Y = Pos.Center (),
+				Text = "Check this out 你",
+				Width = 25,
+				TextAlignment = TextAlignment.Justified
+			};
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (checkBox);
+			Application.Top.Add (win);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+
+			Assert.Equal (TextAlignment.Justified, checkBox.TextAlignment);
+			Assert.Equal (new Rect (1, 1, 25, 1), checkBox.Frame);
+			Assert.Equal ("Check this out 你", checkBox.Text);
+			Assert.Equal ("╴ Check this out 你", checkBox.TextFormatter.Text);
+			Assert.False (checkBox.AutoSize);
+
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ ╴  Check  this  out  你    │
+│                            │
+└────────────────────────────┘
+";
+
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.Checked = true;
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│ √  Check  this  out  你    │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+		}
+
+		[Fact, AutoInitShutdown]
+		public void TextAlignment_Right ()
+		{
+			var checkBox = new CheckBox () {
+				X = 1,
+				Y = Pos.Center (),
+				Text = "Check this out 你",
+				Width = 25,
+				TextAlignment = TextAlignment.Right
+			};
+			var win = new Window () {
+				Width = Dim.Fill (),
+				Height = Dim.Fill (),
+				Title = "Test Demo 你"
+			};
+			win.Add (checkBox);
+			Application.Top.Add (win);
+
+			Application.Begin (Application.Top);
+			((FakeDriver)Application.Driver).SetBufferSize (30, 5);
+
+			Assert.Equal (TextAlignment.Right, checkBox.TextAlignment);
+			Assert.Equal (new Rect (1, 1, 25, 1), checkBox.Frame);
+			Assert.Equal ("Check this out 你", checkBox.Text);
+			Assert.Equal ("Check this out 你 ╴", checkBox.TextFormatter.Text);
+			Assert.False (checkBox.AutoSize);
+
+			var expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│       Check this out 你 ╴  │
+│                            │
+└────────────────────────────┘
+";
+
+			var pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
+
+			checkBox.Checked = true;
+			Application.Refresh ();
+			expected = @"
+┌ Test Demo 你 ──────────────┐
+│                            │
+│       Check this out 你 √  │
+│                            │
+└────────────────────────────┘
+";
+
+			pos = GraphViewTests.AssertDriverContentsWithFrameAre (expected, output);
+			Assert.Equal (new Rect (0, 0, 30, 5), pos);
 		}
 		}
 	}
 	}
 }
 }