Explorar o código

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 %!s(int64=7) %!d(string=hai) anos
pai
achega
2af83a14cc
Modificáronse 2 ficheiros con 50 adicións e 21 borrados
  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 {