浏览代码

Coordinates in the TextField are now Rune indexes, not byte indexes into the ustring.

The ustring stores strings in Utf8 format which is variable encoding,
and I was mixing code that assumed that indexes into Utf8 were valid
rune indexes.

This normalizes point to be based on the rune index, not the offset
into the storage, so we need to do some processing to get this
information right.  It might make sense perhaps to "Explode" the string
into a List<Rune> rather than try to manipualte the ustring, but this
should work for now.

Also, fixed the positioning code, and the drawing code for this, should
fix the reported issue that came up in a pull request:

https://github.com/migueldeicaza/gui.cs/pull/90
miguel 7 年之前
父节点
当前提交
2af83a14cc
共有 2 个文件被更改,包括 50 次插入21 次删除
  1. 1 1
      Example/demo.cs
  2. 49 20
      Terminal.Gui/Views/TextField.cs

+ 1 - 1
Example/demo.cs

@@ -90,7 +90,7 @@ static class Demo {
 			return true;
 		}
 
-		Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+		//Application.MainLoop.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
 
 
 		// A little convoluted, this is because I am using this to test the

+ 49 - 20
Terminal.Gui/Views/TextField.cs

@@ -110,7 +110,16 @@ namespace Terminal.Gui {
 		/// </summary>
 		public override void PositionCursor ()
 		{
-			Move (point - first, 0);
+			var x = first;
+			var col = 0;
+			foreach ((var idx, var rune) in text [first, null].Range ()) {
+				if (x == point)
+					break;
+				var cols = Rune.ColumnWidth (rune);
+				col += cols;
+				x++;
+			}
+			Move (col, 0);
 		}
 
 		public override void Redraw (Rect region)
@@ -118,14 +127,21 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (ColorScheme.Focus);
 			Move (0, 0);
 
-			for (int i = 0; i < Frame.Width; i++) {
-				int p = first + i;
-
-				if (p < text.Length) {
-					Driver.AddRune (Secret ? (Rune)'*' : text [p]);
-				} else
-					Driver.AddRune (' ');
+			int p = first;
+			int col = 0;
+			int width = Frame.Width;
+			foreach ((var idx, var rune) in text.Range ()) {
+				if (idx < first)
+					continue;
+				var cols = Rune.ColumnWidth (rune);
+				if (col + cols < width)
+					Driver.AddRune ((Rune)(Secret ? '*' : rune));
+				col += cols;
 			}
+
+			for (int i = col; i < Frame.Width; i++) 
+				Driver.AddRune (' ');
+			
 			PositionCursor ();
 		}
 
@@ -156,6 +172,19 @@ namespace Terminal.Gui {
 				Clipboard.Contents = text;
 		}
 
+		// Maps a rune index to the byte index inside the utf8 string
+		int RuneIndexToByteIndex (int index)
+		{
+			var blen = text.Length;
+			for (int byteIndex = 0, runeIndex = 0; byteIndex < blen; runeIndex++) {
+				if (index == runeIndex)
+					return byteIndex;
+				(var rune, var size) = Utf8.DecodeRune (text, byteIndex, byteIndex - blen);
+				byteIndex += size;
+			}
+			throw new InvalidOperationException ();
+		}
+
 		public override bool ProcessKey (KeyEvent kb)
 		{
 			switch (kb.Key) {
@@ -163,7 +192,7 @@ namespace Terminal.Gui {
 				if (text.Length == 0 || text.Length == point)
 					return true;
 
-				SetText (text [0, point] + text [point + 1, null]);
+				SetText (text [0, RuneIndexToByteIndex (point)] + text [RuneIndexToByteIndex (point + 1), null]);
 				Adjust ();
 				break;
 
@@ -172,7 +201,7 @@ namespace Terminal.Gui {
 				if (point == 0)
 					return true;
 
-				SetText (text [0, point - 1] + text [point, null]);
+				SetText (text [0, RuneIndexToByteIndex (point - 1)] + text [RuneIndexToByteIndex (point), null]);
 				point--;
 				Adjust ();
 				break;
@@ -195,27 +224,27 @@ namespace Terminal.Gui {
 			case Key.ControlD: // Delete
 				if (point == text.Length)
 					break;
-				SetText (text [0, point] + text [point + 1, null]);
+				SetText (text [0, RuneIndexToByteIndex (point)] + text [RuneIndexToByteIndex (point + 1), null]);
 				Adjust ();
 				break;
 
 			case Key.End:
 			case Key.ControlE: // End
-				point = text.Length;
+				point = text.RuneCount;
 				Adjust ();
 				break;
 
 			case Key.CursorRight:
 			case Key.ControlF:
-				if (point == text.Length)
+				if (point == text.RuneCount)
 					break;
 				point++;
 				Adjust ();
 				break;
 
 			case Key.ControlK: // kill-to-end
-				SetClipboard (text.Substring (point));
-				SetText (text [0, point]);
+				SetClipboard (text.Substring (RuneIndexToByteIndex (point)));
+				SetText (text [0, RuneIndexToByteIndex (point)]);
 				Adjust ();
 				break;
 
@@ -224,11 +253,11 @@ namespace Terminal.Gui {
 				if (clip== null)
 					return true;
 
-				if (point == text.Length) {
+				if (point == text.RuneCount) {
 					SetText (text + clip);
-					point = text.Length;
+					point = text.RuneCount;
 				} else {
-					SetText (text [0, point] + clip + text.Substring (point));
+					SetText (text [0, RuneIndexToByteIndex (point)] + clip + text.Substring (RuneIndexToByteIndex (point)));
 					point += clip.RuneCount;
 				}
 				Adjust ();
@@ -260,10 +289,10 @@ namespace Terminal.Gui {
 
 				var kbstr = ustring.Make ((uint)kb.Key);
 				if (used) {
-					if (point == text.Length) {
+					if (point == text.RuneCount) {
 						SetText (text + kbstr);
 					} else {
-						SetText (text [0, point] + kbstr + text [point, null]);
+						SetText (text [0, RuneIndexToByteIndex (point)] + kbstr + text [RuneIndexToByteIndex (point), null]);
 					}
 					point++;
 				} else {