ソースを参照

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;
 			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
 		// 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>
 		/// </summary>
 		public override void PositionCursor ()
 		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)
 		public override void Redraw (Rect region)
@@ -118,14 +127,21 @@ namespace Terminal.Gui {
 			Driver.SetAttribute (ColorScheme.Focus);
 			Driver.SetAttribute (ColorScheme.Focus);
 			Move (0, 0);
 			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 ();
 			PositionCursor ();
 		}
 		}
 
 
@@ -156,6 +172,19 @@ namespace Terminal.Gui {
 				Clipboard.Contents = text;
 				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)
 		public override bool ProcessKey (KeyEvent kb)
 		{
 		{
 			switch (kb.Key) {
 			switch (kb.Key) {
@@ -163,7 +192,7 @@ namespace Terminal.Gui {
 				if (text.Length == 0 || text.Length == point)
 				if (text.Length == 0 || text.Length == point)
 					return true;
 					return true;
 
 
-				SetText (text [0, point] + text [point + 1, null]);
+				SetText (text [0, RuneIndexToByteIndex (point)] + text [RuneIndexToByteIndex (point + 1), null]);
 				Adjust ();
 				Adjust ();
 				break;
 				break;
 
 
@@ -172,7 +201,7 @@ namespace Terminal.Gui {
 				if (point == 0)
 				if (point == 0)
 					return true;
 					return true;
 
 
-				SetText (text [0, point - 1] + text [point, null]);
+				SetText (text [0, RuneIndexToByteIndex (point - 1)] + text [RuneIndexToByteIndex (point), null]);
 				point--;
 				point--;
 				Adjust ();
 				Adjust ();
 				break;
 				break;
@@ -195,27 +224,27 @@ namespace Terminal.Gui {
 			case Key.ControlD: // Delete
 			case Key.ControlD: // Delete
 				if (point == text.Length)
 				if (point == text.Length)
 					break;
 					break;
-				SetText (text [0, point] + text [point + 1, null]);
+				SetText (text [0, RuneIndexToByteIndex (point)] + text [RuneIndexToByteIndex (point + 1), null]);
 				Adjust ();
 				Adjust ();
 				break;
 				break;
 
 
 			case Key.End:
 			case Key.End:
 			case Key.ControlE: // End
 			case Key.ControlE: // End
-				point = text.Length;
+				point = text.RuneCount;
 				Adjust ();
 				Adjust ();
 				break;
 				break;
 
 
 			case Key.CursorRight:
 			case Key.CursorRight:
 			case Key.ControlF:
 			case Key.ControlF:
-				if (point == text.Length)
+				if (point == text.RuneCount)
 					break;
 					break;
 				point++;
 				point++;
 				Adjust ();
 				Adjust ();
 				break;
 				break;
 
 
 			case Key.ControlK: // kill-to-end
 			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 ();
 				Adjust ();
 				break;
 				break;
 
 
@@ -224,11 +253,11 @@ namespace Terminal.Gui {
 				if (clip== null)
 				if (clip== null)
 					return true;
 					return true;
 
 
-				if (point == text.Length) {
+				if (point == text.RuneCount) {
 					SetText (text + clip);
 					SetText (text + clip);
-					point = text.Length;
+					point = text.RuneCount;
 				} else {
 				} else {
-					SetText (text [0, point] + clip + text.Substring (point));
+					SetText (text [0, RuneIndexToByteIndex (point)] + clip + text.Substring (RuneIndexToByteIndex (point)));
 					point += clip.RuneCount;
 					point += clip.RuneCount;
 				}
 				}
 				Adjust ();
 				Adjust ();
@@ -260,10 +289,10 @@ namespace Terminal.Gui {
 
 
 				var kbstr = ustring.Make ((uint)kb.Key);
 				var kbstr = ustring.Make ((uint)kb.Key);
 				if (used) {
 				if (used) {
-					if (point == text.Length) {
+					if (point == text.RuneCount) {
 						SetText (text + kbstr);
 						SetText (text + kbstr);
 					} else {
 					} else {
-						SetText (text [0, point] + kbstr + text [point, null]);
+						SetText (text [0, RuneIndexToByteIndex (point)] + kbstr + text [RuneIndexToByteIndex (point), null]);
 					}
 					}
 					point++;
 					point++;
 				} else {
 				} else {