Kaynağa Gözat

Merge branch 'master' into unit_test_update

Charlie Kindel 5 yıl önce
ebeveyn
işleme
6a2504ec44

+ 172 - 1
Terminal.Gui/Core/View.cs

@@ -17,6 +17,27 @@ using System.Linq;
 using NStack;
 
 namespace Terminal.Gui {
+	/// <summary>
+	/// Text alignment enumeration, controls how text is displayed.
+	/// </summary>
+	public enum TextAlignment {
+		/// <summary>
+		/// Aligns the text to the left of the frame.
+		/// </summary>
+		Left,
+		/// <summary>
+		/// Aligns the text to the right side of the frame.
+		/// </summary>
+		Right,
+		/// <summary>
+		/// Centers the text in the frame.
+		/// </summary>
+		Centered,
+		/// <summary>
+		/// Shows the text as justified text in the frame.
+		/// </summary>
+		Justified
+	}
 
 	/// <summary>
 	/// Determines the LayoutStyle for a view, if Absolute, during LayoutSubviews, the
@@ -299,6 +320,7 @@ namespace Terminal.Gui {
 			set {
 				x = value;
 				SetNeedsLayout ();
+				SetNeedsDisplay (frame);
 			}
 		}
 
@@ -314,6 +336,7 @@ namespace Terminal.Gui {
 			set {
 				y = value;
 				SetNeedsLayout ();
+				SetNeedsDisplay (frame);
 			}
 		}
 
@@ -331,6 +354,7 @@ namespace Terminal.Gui {
 			set {
 				width = value;
 				SetNeedsLayout ();
+				SetNeedsDisplay (frame);
 			}
 		}
 
@@ -344,6 +368,7 @@ namespace Terminal.Gui {
 			set {
 				height = value;
 				SetNeedsLayout ();
+				SetNeedsDisplay (frame);
 			}
 		}
 
@@ -1450,6 +1475,153 @@ namespace Terminal.Gui {
 			OnLayoutComplete (new LayoutEventArgs () { OldBounds = oldBounds });
 		}
 
+		/// <summary>
+		/// A generic virtual method at the level of View to manipulate any hot-keys.
+		/// </summary>
+		/// <param name="text">The text to manipulate.</param>
+		/// <param name="hotKey">The hot-key to look for.</param>
+		/// <param name="hotPos">The returning hot-key position.</param>
+		/// <param name="showHotKey">The character immediately to the right relative to the hot-key position</param>
+		/// <returns>It aims to facilitate the preparation for <see cref="TextAlignment"/> procedures.</returns>
+		public virtual ustring GetTextFromHotKey(ustring text, Rune hotKey, out int hotPos, out Rune showHotKey)
+		{
+			Rune hot_key = (Rune)0;
+			int hot_pos = -1;
+			ustring shown_text = text;
+
+			// Use first hot_key char passed into 'hotKey'.
+			int i = 0;
+			foreach (Rune c in shown_text) {
+				if ((char)c != 0xFFFD) {
+					if (c == hotKey) {
+						hot_pos = i;
+					} else if (hot_pos > -1) {
+						hot_key = c;
+						break;
+					}
+				}
+				i++;
+			}
+
+			if (hot_pos == -1) {
+				// Use first upper-case char if there are no hot-key in the text.
+				i = 0;
+				foreach (Rune c in shown_text) {
+					if ((char)c != 0xFFFD) {
+						if (Rune.IsUpper (c)) {
+							hot_key = c;
+							hot_pos = i;
+							break;
+						}
+					}
+					i++;
+				}
+			} else {
+				// Use char after 'hotKey'
+				ustring start = "";
+				i = 0;
+				foreach (Rune c in shown_text) {
+					start += ustring.Make (c);
+					i++;
+					if (i == hot_pos)
+						break;
+				}
+				var st = shown_text;
+				shown_text = start;
+				i = 0;
+				foreach (Rune c in st) {
+					i++;
+					if (i > hot_pos + 1) {
+						shown_text += ustring.Make (c);
+					}
+				}
+			}
+			hotPos = hot_pos;
+			showHotKey = hot_key;
+			return shown_text;
+		}
+
+		/// <summary>
+		/// A generic virtual method at the level of View to manipulate any hot-keys with <see cref="TextAlignment"/> process.
+		/// </summary>
+		/// <param name="shown_text">The text to manipulate to align.</param>
+		/// <param name="hot_pos">The passed in hot-key position.</param>
+		/// <param name="c_hot_pos">The returning hot-key position.</param>
+		/// <param name="textAlignment">The <see cref="TextAlignment"/> to align to.</param>
+		/// <returns>It performs the <see cref="TextAlignment"/> process to the caller.</returns>
+		public virtual ustring GetTextAlignment (ustring shown_text, int hot_pos, out int c_hot_pos, TextAlignment textAlignment)
+		{
+			int start;
+			var caption = shown_text;
+			c_hot_pos = hot_pos;
+
+			if (Frame.Width > shown_text.Length + 1) {
+				switch (textAlignment) {
+				case TextAlignment.Left:
+					caption += new string (' ', Frame.Width - caption.RuneCount);
+					break;
+				case TextAlignment.Right:
+					start = Frame.Width - caption.RuneCount;
+					caption = $"{new string (' ', Frame.Width - caption.RuneCount)}{caption}";
+					if (c_hot_pos > -1) {
+						c_hot_pos += start;
+					}
+					break;
+				case TextAlignment.Centered:
+					start = Frame.Width / 2 - caption.RuneCount / 2;
+					caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.RuneCount - start)}";
+					if (c_hot_pos > -1) {
+						c_hot_pos += start;
+					}
+					break;
+				case TextAlignment.Justified:
+					var words = caption.Split (" ");
+					var wLen = GetWordsLength (words, c_hot_pos, out int runeCount, out int w_hot_pos);
+					var space = (Frame.Width - runeCount) / (caption.Length - wLen);
+					caption = "";
+					for (int i = 0; i < words.Length; i++) {
+						if (i == words.Length - 1) {
+							caption += new string (' ', Frame.Width - caption.RuneCount - 1);
+							caption += words [i];
+						} else {
+							caption += words [i];
+						}
+						if (i < words.Length - 1) {
+							caption += new string (' ', space);
+						}
+					}
+					if (c_hot_pos > -1) {
+						if (wLen - runeCount == 0) {
+							c_hot_pos += (wLen - runeCount == 0 ? w_hot_pos * (space) - space - w_hot_pos + 1 : space + wLen - runeCount);
+						} else {
+							c_hot_pos += space + wLen - runeCount;
+						}
+					}
+					break;
+				}
+			}
+
+			return caption;
+		}
+
+		int GetWordsLength (ustring [] words, int hotPos, out int runeCount, out int wordHotPos)
+		{
+			int length = 0;
+			int rCount = 0;
+			int wHotPos = -1;
+			for (int i = 0; i < words.Length; i++) {
+				if (wHotPos == -1 && rCount + words [i].RuneCount >= hotPos)
+					wHotPos = i;
+				length += words [i].Length;
+				rCount += words [i].RuneCount;
+			}
+			if (wHotPos == -1 && hotPos > -1)
+				wHotPos = words.Length;
+			runeCount = rCount;
+			wordHotPos = wHotPos;
+			return length;
+		}
+
 		/// <summary>
 		/// Pretty prints the View
 		/// </summary>
@@ -1505,7 +1677,6 @@ namespace Terminal.Gui {
 			return false;
 		}
 
-
 		/// <summary>
 		/// Method invoked when a mouse event is generated
 		/// </summary>

+ 10 - 85
Terminal.Gui/Views/Button.cs

@@ -137,9 +137,7 @@ namespace Terminal.Gui {
 			}
 
 			set {
-				if (text?.Length != value?.Length) {
-					SetWidthHeight (value, is_default);
-				}
+				SetWidthHeight (value, is_default);
 				text = value;
 				Update ();
 			}
@@ -152,14 +150,14 @@ namespace Terminal.Gui {
 			get => textAlignment;
 			set {
 				textAlignment = value;
-				SetNeedsDisplay ();
+				Update ();
 			}
 		}
 
-		Rune _leftBracket = new Rune ('[');
-		Rune _rightBracket = new Rune (']');
-		Rune _leftDefault = new Rune ('<');
-		Rune _rightDefault = new Rune ('>');
+		Rune _leftBracket = new Rune (Driver.LeftBracket);
+		Rune _rightBracket = new Rune (Driver.RightBracket);
+		Rune _leftDefault = new Rune (Driver.LeftDefaultIndicator);
+		Rune _rightDefault = new Rune (Driver.RightDefaultIndicator);
 
 		internal void Update ()
 		{
@@ -168,26 +166,7 @@ namespace Terminal.Gui {
 			else
 				shown_text = ustring.Make (_leftBracket) + " " + text + " " + ustring.Make (_rightBracket);
 
-			hot_key = (Rune)0;
-			hot_pos = shown_text.IndexOf ('_');
-
-			if (hot_pos == -1) {
-				// Use first upper-case char
-				int i = 0;
-				foreach (Rune c in shown_text) {
-					if (Rune.IsUpper (c)) {
-						hot_key = c;
-						hot_pos = i;
-						break;
-					}
-					i++;
-				}
-			} else {
-				// Use char after '_'
-				var start = shown_text [0, hot_pos];
-				shown_text = start + shown_text [hot_pos + 1, shown_text.Length];
-				hot_key = Char.ToUpper((char)shown_text [hot_pos]);
-			}
+			shown_text = GetTextFromHotKey (shown_text, '_', out hot_pos, out hot_key);
 
 			SetNeedsDisplay ();
 		}
@@ -200,51 +179,8 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (HasFocus ? ColorScheme.Focus : ColorScheme.Normal);
 			Move (0, 0);
 
-			var caption = shown_text;
-			c_hot_pos = hot_pos;
-			int start;
-
-			if (Frame.Width > shown_text.Length + 1) {
-				switch (TextAlignment) {
-				case TextAlignment.Left:
-					caption += new string (' ', Frame.Width - caption.Length);
-					break;
-				case TextAlignment.Right:
-					start = Frame.Width - caption.Length;
-					caption = $"{new string (' ', Frame.Width - caption.Length)}{caption}";
-					if (c_hot_pos > -1) {
-						c_hot_pos += start;
-					}
-					break;
-				case TextAlignment.Centered:
-					start = Frame.Width / 2 - caption.Length / 2;
-					caption = $"{new string (' ', start)}{caption}{new string (' ', Frame.Width - caption.Length - start)}";
-					if (c_hot_pos > -1) {
-						c_hot_pos += start;
-					}
-					break;
-				case TextAlignment.Justified:
-					var words = caption.ToString ().Split (new string [] { " " }, StringSplitOptions.RemoveEmptyEntries);
-					var wLen = GetWordsLength (words);
-					var space = (Frame.Width - wLen) / (caption.Length - wLen);
-					caption = "";
-					for (int i = 0; i < words.Length; i++) {
-						if (i == words.Length - 1) {
-							caption += new string (' ', Frame.Width - caption.Length - 1);
-							caption += words [i];
-						} else {
-							caption += words [i];
-						}
-						if (i < words.Length - 1) {
-							caption += new string (' ', space);
-						}
-					}
-					if (c_hot_pos > -1) {
-						c_hot_pos += space - 1;
-					}
-					break;
-				}
-			}
+			var caption = GetTextAlignment (shown_text, hot_pos, out int s_hot_pos, TextAlignment);
+			c_hot_pos = s_hot_pos;
 
 			Driver.AddStr (caption);
 
@@ -255,17 +191,6 @@ namespace Terminal.Gui {
 			}
 		}
 
-		int GetWordsLength (string[] words)
-		{
-			int length = 0;
-
-			for (int i = 0; i < words.Length; i++) {
-				length += words [i].Length;
-			}
-
-			return length;
-		}
-
 		///<inheritdoc/>
 		public override void PositionCursor ()
 		{
@@ -274,7 +199,7 @@ namespace Terminal.Gui {
 
 		bool CheckKey (KeyEvent key)
 		{
-			if (Char.ToUpper ((char)key.KeyValue) == hot_key) {
+			if ((char)key.KeyValue == hot_key) {
 				this.SuperView.SetFocus (this);
 				Clicked?.Invoke ();
 				return true;

+ 0 - 22
Terminal.Gui/Views/Label.cs

@@ -11,28 +11,6 @@ using System.Linq;
 using NStack;
 
 namespace Terminal.Gui {
-	/// <summary>
-	/// Text alignment enumeration, controls how text is displayed.
-	/// </summary>
-	public enum TextAlignment {
-		/// <summary>
-		/// Aligns the text to the left of the frame.
-		/// </summary>
-		Left, 
-		/// <summary>
-		/// Aligns the text to the right side of the frame.
-		/// </summary>
-		Right, 
-		/// <summary>
-		/// Centers the text in the frame.
-		/// </summary>
-		Centered, 
-		/// <summary>
-		/// Shows the text as justified text in the frame.
-		/// </summary>
-		Justified
-	}
-
 	/// <summary>
 	/// The Label <see cref="View"/> displays a string at a given position and supports multiple lines separted by newline characters.
 	/// </summary>

+ 63 - 38
UICatalog/Scenarios/Buttons.cs

@@ -89,7 +89,7 @@ namespace UICatalog {
 			textChanger.Clicked = () => textChanger.Text += "!";
 
 			Win.Add (button = new Button ("Lets see if this will move as \"Text Changer\" grows") {
-				X = Pos.Right(textChanger) + 2,
+				X = Pos.Right (textChanger) + 2,
 				Y = Pos.Y (textChanger),
 			});
 
@@ -105,7 +105,7 @@ namespace UICatalog {
 			var computedFrame = new FrameView ("Computed Layout") {
 				X = 0,
 				Y = Pos.Bottom (removeButton) + 1,
-				Width = Dim.Percent(50),
+				Width = Dim.Percent (50),
 				Height = 5
 			};
 			Win.Add (computedFrame);
@@ -113,7 +113,7 @@ namespace UICatalog {
 			// Demonstrates how changing the View.Frame property can move Views
 			var moveBtn = new Button ("Move This \u263b Button _via Pos") {
 				X = 0,
-				Y = Pos.Center() - 1,
+				Y = Pos.Center () - 1,
 				Width = 30,
 				ColorScheme = Colors.Error,
 			};
@@ -132,14 +132,14 @@ namespace UICatalog {
 			};
 			sizeBtn.Clicked = () => {
 				sizeBtn.Width = sizeBtn.Frame.Width + 5;
-				computedFrame.LayoutSubviews (); // BUGBUG: This call should not be needed. View.X is not causing relayout correctly
+				//computedFrame.LayoutSubviews (); // FIXED: This call should not be needed. View.X is not causing relayout correctly
 			};
 			computedFrame.Add (sizeBtn);
 
 			var absoluteFrame = new FrameView ("Absolute Layout") {
-				X = Pos.Right(computedFrame),
+				X = Pos.Right (computedFrame),
 				Y = Pos.Bottom (removeButton) + 1,
-				Width = Dim.Fill(),
+				Width = Dim.Fill (),
 				Height = 5
 			};
 			Win.Add (absoluteFrame);
@@ -154,7 +154,7 @@ namespace UICatalog {
 			absoluteFrame.Add (moveBtnA);
 
 			// Demonstrates how changing the View.Frame property can SIZE Views (#583)
-			var sizeBtnA = new Button (0, 2, "Size This Button via Frame") {
+			var sizeBtnA = new Button (0, 2, " ~  s  gui.cs   master ↑10 = Со_хранить") {
 				ColorScheme = Colors.Error,
 			};
 			sizeBtnA.Clicked = () => {
@@ -172,34 +172,6 @@ namespace UICatalog {
 				X = 4,
 				Y = Pos.Bottom (label) + 1,
 				SelectedItem = 2,
-				SelectedItemChanged = (args) => {
-					switch (args.SelectedItem) {
-					case 0:
-						moveBtn.TextAlignment = TextAlignment.Left;
-						sizeBtn.TextAlignment = TextAlignment.Left;
-						moveBtnA.TextAlignment = TextAlignment.Left;
-						sizeBtnA.TextAlignment = TextAlignment.Left;
-						break;
-					case 1:
-						moveBtn.TextAlignment = TextAlignment.Right;
-						sizeBtn.TextAlignment = TextAlignment.Right;
-						moveBtnA.TextAlignment = TextAlignment.Right;
-						sizeBtnA.TextAlignment = TextAlignment.Right;
-						break;
-					case 2:
-						moveBtn.TextAlignment = TextAlignment.Centered;
-						sizeBtn.TextAlignment = TextAlignment.Centered;
-						moveBtnA.TextAlignment = TextAlignment.Centered;
-						sizeBtnA.TextAlignment = TextAlignment.Centered;
-						break;
-					case 3:
-						moveBtn.TextAlignment = TextAlignment.Justified;
-						sizeBtn.TextAlignment = TextAlignment.Justified;
-						moveBtnA.TextAlignment = TextAlignment.Justified;
-						sizeBtnA.TextAlignment = TextAlignment.Justified;
-						break;
-					}
-				}
 			};
 			Win.Add (radioGroup);
 
@@ -208,7 +180,9 @@ namespace UICatalog {
 			{
 				// Remove the '_'
 				var i = txt.IndexOf ('_');
-				var start = txt [0, i];
+				ustring start = "";
+				if (i > -1)
+					start = txt [0, i];
 				txt = start + txt [i + 1, txt.Length];
 
 				// Move over one or go to start
@@ -224,15 +198,66 @@ namespace UICatalog {
 				return txt;
 			}
 
-			var moveHotKeyBtn = new Button ("Click to Change th_is Button's Hotkey") {
+			var mhkb = "Click to Change th_is Button's Hotkey";
+			var moveHotKeyBtn = new Button (mhkb) {
 				X = 2,
 				Y = Pos.Bottom (radioGroup) + 1,
+				Width = mhkb.Length + 10,
 				ColorScheme = Colors.TopLevel,
 			};
 			moveHotKeyBtn.Clicked = () => {
 				moveHotKeyBtn.Text = MoveHotkey (moveHotKeyBtn.Text);
 			};
 			Win.Add (moveHotKeyBtn);
+
+			var muhkb = " ~  s  gui.cs   master ↑10 = Сохранить";
+			var moveUnicodeHotKeyBtn = new Button (muhkb) {
+				X = Pos.Right (moveHotKeyBtn) + 6,
+				Y = Pos.Bottom (radioGroup) + 1,
+				Width = muhkb.Length + 30,
+				ColorScheme = Colors.TopLevel,
+			};
+			moveUnicodeHotKeyBtn.Clicked = () => {
+				moveUnicodeHotKeyBtn.Text = MoveHotkey (moveUnicodeHotKeyBtn.Text);
+			};
+			Win.Add (moveUnicodeHotKeyBtn);
+
+			radioGroup.SelectedItemChanged += (args) => {
+				switch (args.SelectedItem) {
+				case 0:
+					moveBtn.TextAlignment = TextAlignment.Left;
+					sizeBtn.TextAlignment = TextAlignment.Left;
+					moveBtnA.TextAlignment = TextAlignment.Left;
+					sizeBtnA.TextAlignment = TextAlignment.Left;
+					moveHotKeyBtn.TextAlignment = TextAlignment.Left;
+					moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Left;
+					break;
+				case 1:
+					moveBtn.TextAlignment = TextAlignment.Right;
+					sizeBtn.TextAlignment = TextAlignment.Right;
+					moveBtnA.TextAlignment = TextAlignment.Right;
+					sizeBtnA.TextAlignment = TextAlignment.Right;
+					moveHotKeyBtn.TextAlignment = TextAlignment.Right;
+					moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Right;
+					break;
+				case 2:
+					moveBtn.TextAlignment = TextAlignment.Centered;
+					sizeBtn.TextAlignment = TextAlignment.Centered;
+					moveBtnA.TextAlignment = TextAlignment.Centered;
+					sizeBtnA.TextAlignment = TextAlignment.Centered;
+					moveHotKeyBtn.TextAlignment = TextAlignment.Centered;
+					moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Centered;
+					break;
+				case 3:
+					moveBtn.TextAlignment = TextAlignment.Justified;
+					sizeBtn.TextAlignment = TextAlignment.Justified;
+					moveBtnA.TextAlignment = TextAlignment.Justified;
+					sizeBtnA.TextAlignment = TextAlignment.Justified;
+					moveHotKeyBtn.TextAlignment = TextAlignment.Justified;
+					moveUnicodeHotKeyBtn.TextAlignment = TextAlignment.Justified;
+					break;
+				}
+			};
 		}
 	}
-}
+}