浏览代码

Fixes #995. Improving TextField to work properly with Unicode characters.

BDisp 4 年之前
父节点
当前提交
0f1a0e2821
共有 1 个文件被更改,包括 69 次插入46 次删除
  1. 69 46
      Terminal.Gui/Views/TextField.cs

+ 69 - 46
Terminal.Gui/Views/TextField.cs

@@ -147,11 +147,10 @@ namespace Terminal.Gui {
 				}
 				TextChanged?.Invoke (oldText);
 
-				if (point > text.Count)
-					point = Math.Max (DisplaySize (text, 0) - 1, 0);
+				if (point > text.Count) {
+					point = Math.Max (DisplaySize (text, 0).Item1 - 1, 0);
+				}
 
-				// FIXME: this needs to be updated to use Rune.ColumnWidth
-				//first = point > Frame.Width ? point - Frame.Width : 0;
 				Adjust ();
 				SetNeedsDisplay ();
 			}
@@ -206,11 +205,8 @@ namespace Terminal.Gui {
 			int width = Frame.Width;
 			var tcount = text.Count;
 			var roc = new Attribute (Color.DarkGray, Color.Gray);
-			for (int idx = 0; idx < tcount; idx++) {
+			for (int idx = p; idx < tcount; idx++) {
 				var rune = text [idx];
-				if (idx < p) {
-					continue;
-				}
 				var cols = Rune.ColumnWidth (rune);
 				if (idx == point && HasFocus && !Used && SelectedLength == 0 && !ReadOnly) {
 					Driver.SetAttribute (Colors.Menu.HotFocus);
@@ -223,10 +219,13 @@ namespace Terminal.Gui {
 					Driver.AddRune ((Rune)(Secret ? '*' : rune));
 				}
 				col = SetCol (col, width, cols);
+				if (idx + 1 < tcount && col + Rune.ColumnWidth (text [idx + 1]) > width) {
+					break;
+				}
 			}
 
 			Driver.SetAttribute (ColorScheme.Focus);
-			for (int i = col; i < Frame.Width; i++) {
+			for (int i = col; i < width; i++) {
 				Driver.AddRune (' ');
 			}
 
@@ -242,20 +241,26 @@ namespace Terminal.Gui {
 			return col;
 		}
 
-		// Returns the size in a range of the string.
-		int DisplaySize (List<Rune> t, int start = -1, int end = -1)
+		// Returns the size and length in a range of the string.
+		(int, int) DisplaySize (List<Rune> t, int start = -1, int end = -1, bool checkNextRune = true)
 		{
 			if (t == null || t.Count == 0) {
-				return 0;
+				return (0, 0);
 			}
 			int size = 0;
+			int len = 0;
 			int tcount = end == -1 ? t.Count : end > t.Count ? t.Count : end;
 			int i = start == -1 ? 0 : start;
 			for (; i < tcount; i++) {
 				var rune = t [i];
 				size += Rune.ColumnWidth (rune);
+				len += Rune.RuneLen (rune);
+				if (checkNextRune && i == tcount - 1 && t.Count > tcount && Rune.ColumnWidth (t [i + 1]) > 1) {
+					size += Rune.ColumnWidth (t [i + 1]);
+					len += Rune.RuneLen (t [i + 1]);
+				}
 			}
-			return size;
+			return (size, len);
 		}
 
 		void Adjust ()
@@ -264,10 +269,9 @@ namespace Terminal.Gui {
 			if (point < first) {
 				first = point;
 			} else if (first + point - (Frame.Width + offB) == 0 ||
-				  DisplaySize (text, first, point) >= Frame.Width + offB) {
+				  DisplaySize (text, first, point).Item1 >= Frame.Width + offB) {
 				first = Math.Max (CalculateFirst (text, first, point, Frame.Width - 1 + offB), 0);
 			}
-
 			SetNeedsDisplay ();
 		}
 
@@ -283,22 +287,29 @@ namespace Terminal.Gui {
 
 		int CalculateFirst (List<Rune> t, int start, int end, int width)
 		{
-			if (start + end - width >= width) {
-				return end - width;
+			if (t == null) {
+				return 0;
+			}
+			(var dSize, _) = DisplaySize (t, start, end);
+			if (dSize < width) {
+				return start;
 			}
 			int size = 0;
-			int tcount = end > width || end > t.Count - 1 ? t.Count - 1 : end;
+			int tcount = end > t.Count - 1 ? t.Count - 1 : end;
 			int col = 0;
-			for (int i = tcount; i > -1; i--) {
+			for (int i = tcount; i > start; i--) {
 				var rune = t [i];
 				var s = Rune.ColumnWidth (rune);
 				size += s;
-				if (size > width) {
-					col += size - width;
+				if (size >= dSize - width) {
+					col = tcount - i + start;
+					if (col == start || (point == t.Count && (point - col > width))) {
+						col++;
+					}
 					break;
 				}
 			}
-			return col + start;
+			return col;
 		}
 
 		void SetText (List<Rune> newText)
@@ -396,7 +407,7 @@ namespace Terminal.Gui {
 			case Key.End | Key.ShiftMask:
 			case Key.End | Key.ShiftMask | Key.CtrlMask:
 			case Key.E | Key.ShiftMask | Key.CtrlMask:
-				if (point < text.Count) {
+				if (point <= text.Count) {
 					int x = point;
 					point = text.Count;
 					PrepareSelection (x, point - x);
@@ -429,19 +440,21 @@ namespace Terminal.Gui {
 			case Key.CursorLeft | Key.ShiftMask | Key.CtrlMask:
 			case Key.CursorUp | Key.ShiftMask | Key.CtrlMask:
 				if (point > 0) {
-					int x = start > -1 ? start : point;
-					int sbw = WordBackward (point);
-					if (sbw != -1)
-						point = sbw;
-					PrepareSelection (x, sbw - x);
+					int x = start > -1 && start > point ? start : point;
+					if (x > 0) {
+						int sbw = WordBackward (x);
+						if (sbw != -1)
+							point = sbw;
+						PrepareSelection (x, sbw - x);
+					}
 				}
 				break;
 
 			case Key.CursorRight | Key.ShiftMask | Key.CtrlMask:
 			case Key.CursorDown | Key.ShiftMask | Key.CtrlMask:
 				if (point < text.Count) {
-					int x = start > -1 ? start : point;
-					int sfw = WordForward (point);
+					int x = start > -1 && start > point ? start : point;
+					int sfw = WordForward (x);
 					if (sfw != -1)
 						point = sfw;
 					PrepareSelection (x, sfw - x);
@@ -807,12 +820,15 @@ namespace Terminal.Gui {
 			if (SelectedStart > -1) {
 				SelectedLength = x + direction <= text.Count ? x + direction - SelectedStart : text.Count - SelectedStart;
 				SetSelectedStartSelectedLength ();
-				SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
-					start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
-				if (first > start) {
-					first = start;
+				if (start > -1) {
+					SelectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
+						start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
+					if (first > start) {
+						first = start;
+					}
+				} else {
+					ClearAllSelection ();
 				}
-				point = start + length;
 			}
 			Adjust ();
 		}
@@ -828,6 +844,7 @@ namespace Terminal.Gui {
 			SelectedLength = 0;
 			SelectedText = "";
 			start = 0;
+			length = 0;
 			SetNeedsDisplay ();
 		}
 
@@ -871,11 +888,14 @@ namespace Terminal.Gui {
 			ustring actualText = Text;
 			int selStart = SelectedLength < 0 ? SelectedLength + SelectedStart : SelectedStart;
 			int selLength = Math.Abs (SelectedLength);
-			Text = actualText[0, selStart] +
-				actualText[selStart + selLength, actualText.RuneCount - selLength];
+			(var _, var len) = DisplaySize (text, 0, selStart, false);
+			(var _, var len2) = DisplaySize (text, selStart, selStart + selLength, false);
+			(var _, var len3) = DisplaySize (text, selStart + selLength, actualText.RuneCount, false);
+			Text = actualText[0, len] +
+				actualText[len + len2, len + len2 + len3];
 			ClearAllSelection ();
-			CursorPosition = selStart >= Text.RuneCount ? Text.RuneCount : selStart;
-			SetNeedsDisplay ();
+			point = selStart >= Text.RuneCount ? Text.RuneCount : selStart;
+			Adjust ();
 		}
 
 		/// <summary>
@@ -883,20 +903,23 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void Paste ()
 		{
-			if (ReadOnly)
+			if (ReadOnly) {
 				return;
+			}
 
+			SetSelectedStartSelectedLength ();
+			int selStart = start == -1 ? CursorPosition : start;
 			ustring actualText = Text;
-			int start = SelectedStart == -1 ? CursorPosition : SelectedStart;
+			(int _, int len) = DisplaySize (text, 0, selStart, false);
+			(var _, var len2) = DisplaySize (text, selStart, selStart + length, false);
+			(var _, var len3) = DisplaySize (text, selStart + length, actualText.RuneCount, false);
 			ustring cbTxt = Clipboard.Contents ?? "";
-			Text = actualText[0, start] +
+			Text = actualText [0, len] +
 				cbTxt +
-				actualText[start + SelectedLength, actualText.RuneCount - SelectedLength];
-			point = start + cbTxt.RuneCount;
-			SelectedLength = 0;
+				actualText [len + len2, len + len2 + len3];
+			point = selStart + cbTxt.RuneCount;
 			ClearAllSelection ();
 			SetNeedsDisplay ();
 		}
-
 	}
 }