浏览代码

Use Runes and ustrings for text processing, gives us free unicode support

Miguel de Icaza 7 年之前
父节点
当前提交
170e3b0358

+ 17 - 16
Terminal.Gui/Core.cs

@@ -17,6 +17,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Threading;
 using System.Linq;
+using NStack;
 
 namespace Terminal.Gui {
 
@@ -177,7 +178,7 @@ namespace Terminal.Gui {
 		/// Gets or sets an identifier for the view;
 		/// </summary>
 		/// <value>The identifier.</value>
-		public string Id { get; set; } = "";
+		public ustring Id { get; set; } = "";
 
 		/// <summary>
 		/// Gets or sets a value indicating whether this <see cref="T:Terminal.View"/> want mouse position reports.
@@ -375,7 +376,7 @@ namespace Terminal.Gui {
 			for (int line = 0; line < h; line++) {
 				Move (0, line);
 				for (int col = 0; col < w; col++)
-					Driver.AddCh (' ');
+					Driver.AddRune (' ');
 			}
 		}
 
@@ -461,15 +462,15 @@ namespace Terminal.Gui {
 		/// <param name="text">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="hotColor">Hot color.</param>
 		/// <param name="normalColor">Normal color.</param>
-		public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
+		public void DrawHotString (ustring text, Attribute hotColor, Attribute normalColor)
 		{
 			Driver.SetAttribute (normalColor);
-			foreach (var c in text) {
-				if (c == '_') {
+			foreach (var rune in text) {
+				if (rune == '_') {
 					Driver.SetAttribute (hotColor);
 					continue;
 				}
-				Driver.AddCh (c);
+				Driver.AddRune (rune);
 				Driver.SetAttribute (normalColor);
 			}
 		}
@@ -480,7 +481,7 @@ namespace Terminal.Gui {
 		/// <param name="text">String to display, the underscoore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="focused">If set to <c>true</c> this uses the focused colors from the color scheme, otherwise the regular ones.</param>
 		/// <param name="scheme">The color scheme to use.</param>
-		public void DrawHotString (string text, bool focused, ColorScheme scheme)
+		public void DrawHotString (ustring text, bool focused, ColorScheme scheme)
 		{
 			if (focused)
 				DrawHotString (text, scheme.HotFocus, scheme.Focus);
@@ -569,14 +570,14 @@ namespace Terminal.Gui {
 		/// <param name="col">Col.</param>
 		/// <param name="row">Row.</param>
 		/// <param name="ch">Ch.</param>
-		public void AddCh (int col, int row, int ch)
+		public void AddRune (int col, int row, Rune ch)
 		{
 			if (row < 0 || col < 0)
 				return;
 			if (row > frame.Height - 1 || col > frame.Width - 1)
 				return;
 			Move (col, row);
-			Driver.AddCh (ch);
+			Driver.AddRune (ch);
 		}
 
 		/// <summary>
@@ -911,13 +912,13 @@ namespace Terminal.Gui {
 	/// </summary>
 	public class Window : Toplevel, IEnumerable {
 		View contentView;
-		string title;
+		ustring title;
 
 		/// <summary>
 		/// The title to be displayed for this window.
 		/// </summary>
 		/// <value>The title.</value>
-		public string Title {
+		public ustring Title {
 			get => title;
 			set {
 				title = value;
@@ -934,7 +935,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
-		public Window (Rect frame, string title = null) : this (frame, title, padding: 0)
+		public Window (Rect frame, ustring title = null) : this (frame, title, padding: 0)
 		{
 		}
 
@@ -947,7 +948,7 @@ namespace Terminal.Gui {
 		/// <param name="frame">Frame.</param>
 		/// <param name="padding">Number of characters to use for padding of the drawn frame.</param>
 		/// <param name="title">Title.</param>
-		public Window (Rect frame, string title = null, int padding = 0) : base (frame)
+		public Window (Rect frame, ustring title = null, int padding = 0) : base (frame)
 		{
 			this.Title = title;
 			int wb = 2 * (1 + padding);
@@ -990,10 +991,10 @@ namespace Terminal.Gui {
 				var width = Frame.Width;
 				if (Title != null && width > 4) {
 					Move (1+padding, padding);
-					Driver.AddCh (' ');
-					var str = Title.Length > width ? Title.Substring (0, width - 4) : Title;
+					Driver.AddRune (' ');
+					var str = Title.Length > width ? Title [0, width-4] : Title;
 					Driver.AddStr (str);
-					Driver.AddCh (' ');
+					Driver.AddRune (' ');
 				}
 				Driver.SetAttribute (ColorScheme.Normal);
 			}

+ 42 - 41
Terminal.Gui/Driver.cs

@@ -8,6 +8,7 @@ using System;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using Mono.Terminal;
+using NStack;
 using Unix.Terminal;
 
 namespace Terminal.Gui {
@@ -189,12 +190,12 @@ namespace Terminal.Gui {
 		/// Adds the specified rune to the display at the current cursor position
 		/// </summary>
 		/// <param name="rune">Rune to add.</param>
-		public abstract void AddCh (int rune);
+		public abstract void AddRune (Rune rune);
 		/// <summary>
 		/// Adds the specified 
 		/// </summary>
 		/// <param name="str">String.</param>
-		public abstract void AddStr (string str);
+		public abstract void AddStr (ustring str);
 		public abstract void PrepareToRun (MainLoop mainLoop, Action<KeyEvent> target, Action<MouseEvent> mouse);
 
 		/// <summary>
@@ -237,7 +238,7 @@ namespace Terminal.Gui {
 		Rect clip;
 
 		/// <summary>
-		/// Controls the current clipping region that AddCh/AddStr is subject to.
+		/// Controls the current clipping region that AddRune/AddStr is subject to.
 		/// </summary>
 		/// <value>The clip.</value>
 		public Rect Clip {
@@ -258,7 +259,7 @@ namespace Terminal.Gui {
 		public override int Cols => Curses.Cols;
 		public override int Rows => Curses.Lines;
 
-		// Current row, and current col, tracked by Move/AddCh only
+		// Current row, and current col, tracked by Move/AddRune only
 		int ccol, crow;
 		bool needMove;
 		public override void Move (int col, int row)
@@ -275,15 +276,15 @@ namespace Terminal.Gui {
 			}
 		}
 
-		static bool sync = true;
-		public override void AddCh (int rune)
+		static bool sync = false;
+		public override void AddRune (Rune rune)
 		{
 			if (Clip.Contains (ccol, crow)) {
 				if (needMove) {
 					Curses.move (crow, ccol);
 					needMove = false;
 				}
-				Curses.addch (rune);
+				Curses.addch ((int)(uint)rune);
 			} else
 				needMove = true;
 			if (sync)
@@ -295,16 +296,16 @@ namespace Terminal.Gui {
 		{
 			switch (ch) {
 			case SpecialChar.HLine:
-				AddCh (Curses.ACS_HLINE);
+				AddRune (Curses.ACS_HLINE);
 				break;
 			}
 		}
 
-		public override void AddStr (string str)
+		public override void AddStr (ustring str)
 		{
 			// TODO; optimize this to determine if the str fits in the clip region, and if so, use Curses.addstr directly
 			foreach (var rune in str)
-				AddCh ((int)rune);
+				AddRune (rune);
 		}
 
 		public override void Refresh () => Curses.refresh ();
@@ -456,46 +457,46 @@ namespace Terminal.Gui {
 			if (padding > 0) {
 				for (int l = 0; l < padding; l++)
 					for (b = 0; b < width; b++)
-						AddCh (' ');
+						AddRune (' ');
 			}
 			Move (region.X, region.Y + padding);
 			for (int c = 0; c < padding; c++)
-				AddCh (' ');
-			AddCh (Curses.ACS_ULCORNER);
+				AddRune (' ');
+			AddRune (Curses.ACS_ULCORNER);
 			for (b = 0; b < fwidth - 2; b++)
-				AddCh (Curses.ACS_HLINE);
-			AddCh (Curses.ACS_URCORNER);
+				AddRune (Curses.ACS_HLINE);
+			AddRune (Curses.ACS_URCORNER);
 			for (int c = 0; c < padding; c++)
-				AddCh (' ');
+				AddRune (' ');
 				
 			for (b = 1+padding; b < fheight; b++) {
 				Move (region.X, region.Y + b);
 				for (int c = 0; c < padding; c++)
-					AddCh (' ');
-				AddCh (Curses.ACS_VLINE);
+					AddRune (' ');
+				AddRune (Curses.ACS_VLINE);
 				if (fill) {	
 					for (int x = 1; x < fwidth - 1; x++)
-						AddCh (' ');
+						AddRune (' ');
 				} else
 					Move (region.X + fwidth - 1, region.Y + b);
-				AddCh (Curses.ACS_VLINE);
+				AddRune (Curses.ACS_VLINE);
 				for (int c = 0; c < padding; c++)
-					AddCh (' ');
+					AddRune (' ');
 			}
 			Move (region.X, region.Y + fheight);
 			for (int c = 0; c < padding; c++)
-				AddCh (' ');
-			AddCh (Curses.ACS_LLCORNER);
+				AddRune (' ');
+			AddRune (Curses.ACS_LLCORNER);
 			for (b = 0; b < fwidth - 2; b++)
-				AddCh (Curses.ACS_HLINE);
-			AddCh (Curses.ACS_LRCORNER);
+				AddRune (Curses.ACS_HLINE);
+			AddRune (Curses.ACS_LRCORNER);
 			for (int c = 0; c < padding; c++)
-				AddCh (' ');
+				AddRune (' ');
 			if (padding > 0) {
 				Move (region.X, region.Y + height - padding);
 				for (int l = 0; l < padding; l++)
 					for (b = 0; b < width; b++)
-						AddCh (' ');
+						AddRune (' ');
 			}
 		}
 
@@ -696,10 +697,10 @@ namespace Terminal.Gui {
 			crow = row;
 		}
 
-		public override void AddCh (int rune)
+		public override void AddRune (Rune rune)
 		{
 			if (Clip.Contains (ccol, crow)) {
-				contents [crow, ccol, 0] = rune;
+				contents [crow, ccol, 0] = (int) (uint) rune;
 				contents [crow, ccol, 2] = 1;
 			}
 			ccol++;
@@ -712,13 +713,13 @@ namespace Terminal.Gui {
 
 		public override void AddSpecial (SpecialChar ch)
 		{
-			AddCh ('*');
+			AddRune ('*');
 		}
 
-		public override void AddStr (string str)
+		public override void AddStr (ustring str)
 		{
 			foreach (var rune in str)
-				AddCh ((int)rune);
+				AddRune (rune);
 		}
 
 		public override void DrawFrame(Rect region, int padding, bool fill)
@@ -728,25 +729,25 @@ namespace Terminal.Gui {
 			int b;
 
 			Move (region.X, region.Y);
-			AddCh ('+');
+			AddRune ('+');
 			for (b = 0; b < width - 2; b++)
-				AddCh ('-');
-			AddCh ('+');
+				AddRune ('-');
+			AddRune ('+');
 			for (b = 1; b < height - 1; b++) {
 				Move (region.X, region.Y + b);
-				AddCh ('|');
+				AddRune ('|');
 				if (fill) {
 					for (int x = 1; x < width - 1; x++)
-						AddCh (' ');
+						AddRune (' ');
 				} else
 					Move (region.X + width - 1, region.Y + b);
-				AddCh ('|');
+				AddRune ('|');
 			}
 			Move (region.X, region.Y + height - 1);
-			AddCh ('+');
+			AddRune ('+');
 			for (b = 0; b < width - 2; b++)
-				AddCh ('-');
-			AddCh ('+');
+				AddRune ('-');
+			AddRune ('+');
 		}
 
 		public override void End()

+ 6 - 0
Terminal.Gui/Terminal.Gui.csproj

@@ -31,6 +31,9 @@
   <ItemGroup>
     <Reference Include="System" />
     <Reference Include="Mono.Posix" />
+    <Reference Include="NStack">
+      <HintPath>..\packages\NStack.Core.0.7.0\lib\netstandard1.5\NStack.dll</HintPath>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Folder Include="Properties\" />
@@ -59,5 +62,8 @@
     <Compile Include="MonoCurses\handles.cs" />
     <Compile Include="MonoCurses\mainloop.cs" />
   </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>

+ 5 - 6
Terminal.Gui/Views/Button.cs

@@ -6,8 +6,7 @@
 //
 
 using System;
-using System.Collections.Generic;
-using System.Linq;
+using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -26,8 +25,8 @@ namespace Terminal.Gui {
 	/// </para>
 	/// </remarks>
 	public class Button : View {
-		string text;
-		string shown_text;
+		ustring text;
+		ustring shown_text;
 		char hot_key;
 		int hot_pos = -1;
 		bool is_default;
@@ -88,7 +87,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		///   The text displayed by this widget.
 		/// </summary>
-		public string Text {
+		public ustring Text {
 			get {
 				return text;
 			}
@@ -147,7 +146,7 @@ namespace Terminal.Gui {
 			if (hot_pos != -1) {
 				Move (hot_pos, 0);
 				Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
-				Driver.AddCh (hot_key);
+				Driver.AddRune (hot_key);
 			}
 		}
 

+ 6 - 5
Terminal.Gui/Views/Checkbox.cs

@@ -5,6 +5,7 @@
 //   Miguel de Icaza ([email protected])
 //
 using System;
+using NStack;
 
 namespace Terminal.Gui {
 
@@ -12,7 +13,7 @@ namespace Terminal.Gui {
 	/// The Checkbox View shows an on/off toggle that the user can set
 	/// </summary>
 	public class CheckBox : View {
-		string text;
+		ustring text;
 		int hot_pos = -1;
 		char hot_key;
 
@@ -34,7 +35,7 @@ namespace Terminal.Gui {
 		///   The size of CheckButton is computed based on the
 		///   text length. This CheckButton is not toggled.
 		/// </remarks>
-		public CheckBox (int x, int y, string s) : this (x, y, s, false)
+		public CheckBox (int x, int y, ustring s) : this (x, y, s, false)
 		{
 		}
 
@@ -46,7 +47,7 @@ namespace Terminal.Gui {
 		///   The size of CheckButton is computed based on the
 		///   text length. 
 		/// </remarks>
-		public CheckBox (int x, int y, string s, bool is_checked) : base (new Rect (x, y, s.Length + 4, 1))
+		public CheckBox (int x, int y, ustring s, bool is_checked) : base (new Rect (x, y, s.Length + 4, 1))
 		{
 			Checked = is_checked;
 			Text = s;
@@ -62,7 +63,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		///   The text displayed by this widget.
 		/// </summary>
-		public string Text {
+		public ustring Text {
 			get {
 				return text;
 			}
@@ -94,7 +95,7 @@ namespace Terminal.Gui {
 			if (hot_pos != -1) {
 				Move (4 + hot_pos, 0);
 				Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
-				Driver.AddCh (hot_key);
+				Driver.AddRune (hot_key);
 			}
 		}
 

+ 1 - 0
Terminal.Gui/Views/Dialog.cs

@@ -7,6 +7,7 @@
 
 using System;
 using System.Collections.Generic;
+using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 20 - 17
Terminal.Gui/Views/Label.cs

@@ -8,6 +8,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -36,19 +37,19 @@ namespace Terminal.Gui {
 	/// Label view, displays a string at a given position, can include multiple lines.
 	/// </summary>
 	public class Label : View {
-		List<string> lines = new List<string> ();
+		List<ustring> lines = new List<ustring> ();
 		bool recalcPending = true;
-		string text;
+		ustring text;
 		TextAlignment textAlignment;
 
-		static Rect CalcRect (int x, int y, string s)
+		static Rect CalcRect (int x, int y, ustring s)
 		{
 			int mw = 0;
 			int ml = 1;
 
 			int cols = 0;
-			foreach (var c in s) {
-				if (c == '\n') {
+			foreach (var rune in s) {
+				if (rune == '\n') {
 					ml++;
 					if (cols > mw)
 						mw = cols;
@@ -65,7 +66,7 @@ namespace Terminal.Gui {
 		///   based on the size of the string, assumes that the string contains
 		///   newlines for multiple lines, no special breaking rules are used.
 		/// </summary>
-		public Label (int x, int y, string text) : this (CalcRect (x, y, text), text)
+		public Label (int x, int y, ustring text) : this (CalcRect (x, y, text), text)
 		{
 		}
 
@@ -74,25 +75,27 @@ namespace Terminal.Gui {
 		///   coordinate with the given string and uses the specified
 		///   frame for the string.
 		/// </summary>
-		public Label (Rect rect, string text) : base (rect)
+		public Label (Rect rect, ustring text) : base (rect)
 		{
 			this.text = text;
 		}
 
 		static char [] whitespace = new char [] { ' ', '\t' };
 
-		static string ClipAndJustify (string str, int width, TextAlignment talign)
+		static ustring ClipAndJustify (ustring str, int width, TextAlignment talign)
 		{
 			int slen = str.Length;
 			if (slen > width)
-				return str.Substring (0, width);
+				return str [0, width];
 			else {
 				if (talign == TextAlignment.Justified) {
-					var words = str.Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
+					// TODO: ustring needs this
+			               	var words = str.ToString ().Split (whitespace, StringSplitOptions.RemoveEmptyEntries);
 					int textCount = words.Sum ((arg) => arg.Length);
 
 					var spaces = (width- textCount) / (words.Length - 1);
 					var extras = (width - textCount) % words.Length;
+
 					var s = new System.Text.StringBuilder ();
 					//s.Append ($"tc={textCount} sp={spaces},x={extras} - ");
 					for (int w = 0; w < words.Length; w++) {
@@ -106,7 +109,7 @@ namespace Terminal.Gui {
 							extras--;
 						}
 					}
-					return s.ToString ();
+					return ustring.Make (s.ToString ());
 				}
 				return str;
 			}
@@ -118,7 +121,7 @@ namespace Terminal.Gui {
 			Recalc (text, lines, Frame.Width, textAlignment);
 		}
 
-		static void Recalc (string textStr, List<string> lineResult, int width, TextAlignment talign)
+		static void Recalc (ustring textStr, List<ustring> lineResult, int width, TextAlignment talign)
 		{
 			lineResult.Clear ();
 			if (textStr.IndexOf ('\n') == -1) {
@@ -128,10 +131,10 @@ namespace Terminal.Gui {
 			int textLen = textStr.Length;
 			int lp = 0;
 			for (int i = 0; i < textLen; i++) {
-				char c = textStr [i];
+				Rune c = textStr [i];
 
 				if (c == '\n') {
-					lineResult.Add (ClipAndJustify (textStr.Substring (lp, i - lp), width, talign));
+					lineResult.Add (ClipAndJustify (textStr [lp, i], width, talign));
 					lp = i + 1;
 				}
 			}
@@ -179,9 +182,9 @@ namespace Terminal.Gui {
 		/// <returns>Number of lines.</returns>
 		/// <param name="text">Text, may contain newlines.</param>
 		/// <param name="width">The width for the text.</param>
-		public static int MeasureLines (string text, int width)
+		public static int MeasureLines (ustring text, int width)
 		{
-			var result = new List<string> ();
+			var result = new List<ustring> ();
 			Recalc (text, result, width, TextAlignment.Left);
 			return result.Count ();
 		}
@@ -189,7 +192,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		///   The text displayed by this widget.
 		/// </summary>
-		public virtual string Text {
+		public virtual ustring Text {
 			get => text;
 			set {
 				text = value;

+ 11 - 10
Terminal.Gui/Views/Menu.cs

@@ -6,10 +6,11 @@
 //
 // TODO:
 //   Add accelerator support, but should also support chords (ShortCut in MenuItem)
-//   Add mouse support
 //   Allow menus inside menus
 
 using System;
+using NStack;
+
 namespace Terminal.Gui {
 
 	/// <summary>
@@ -23,7 +24,7 @@ namespace Terminal.Gui {
 		/// <param name="title">Title for the menu item.</param>
 		/// <param name="help">Help text to display.</param>
 		/// <param name="action">Action to invoke when the menu item is activated.</param>
-		public MenuItem (string title, string help, Action action)
+		public MenuItem (ustring title, string help, Action action)
 		{
 			Title = title ?? "";
 			Help = help ?? "";
@@ -50,7 +51,7 @@ namespace Terminal.Gui {
 		/// For example HotKey would be "N" when the File Menu is open (assuming there is a "_New" entry
 		/// if the ShortCut is set to "Control-N", this would be a global hotkey that would trigger as well
 		/// </summary>
-		public char HotKey;
+		public Rune HotKey;
 
 		/// <summary>
 		/// This is the global setting that can be used as a global shortcut to invoke the action on the menu.
@@ -61,13 +62,13 @@ namespace Terminal.Gui {
 		/// Gets or sets the title.
 		/// </summary>
 		/// <value>The title.</value>
-		public string Title { get; set; }
+		public ustring Title { get; set; }
 
 		/// <summary>
 		/// Gets or sets the help text for the menu item.
 		/// </summary>
 		/// <value>The help text.</value>
-		public string Help { get; set; }
+		public ustring Help { get; set; }
 
 		/// <summary>
 		/// Gets or sets the action to be invoked when the menu is triggered
@@ -81,13 +82,13 @@ namespace Terminal.Gui {
 	/// A menu bar item contains other menu items.
 	/// </summary>
 	public class MenuBarItem {
-		public MenuBarItem (string title, MenuItem [] children)
+		public MenuBarItem (ustring title, MenuItem [] children)
 		{
 			SetTitle (title ?? "");
 			Children = children;
 		}
 
-		void SetTitle (string title)
+		void SetTitle (ustring title)
 		{
 			if (title == null)
 				title = "";
@@ -105,7 +106,7 @@ namespace Terminal.Gui {
 		/// Gets or sets the title to display.
 		/// </summary>
 		/// <value>The title.</value>
-		public string Title { get; set; }
+		public ustring Title { get; set; }
 
 		/// <summary>
 		/// Gets or sets the children for this MenuBarItem
@@ -153,7 +154,7 @@ namespace Terminal.Gui {
 					if (item == null)
 						Driver.AddSpecial (SpecialChar.HLine);
 					else
-						Driver.AddCh (' ');
+						Driver.AddRune (' ');
 
 				if (item == null)
 					continue;
@@ -287,7 +288,7 @@ namespace Terminal.Gui {
 			Move (0, 0);
 			Driver.SetAttribute (Colors.Base.Focus);
 			for (int i = 0; i < Frame.Width; i++)
-				Driver.AddCh (' ');
+				Driver.AddRune (' ');
 
 			Move (1, 0);
 			int pos = 1;

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

@@ -8,6 +8,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using NStack;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -18,7 +19,7 @@ namespace Terminal.Gui {
 	///   functionality,  and mouse support.
 	/// </remarks>
 	public class TextField : View {
-		string text, kill;
+		ustring text, kill;
 		int first, point;
 		bool used;
 
@@ -52,7 +53,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <remarks>
 		/// </remarks>
-		public string Text {
+		public ustring Text {
 			get {
 				return text;
 			}
@@ -96,9 +97,9 @@ namespace Terminal.Gui {
 				int p = first + i;
 
 				if (p < text.Length) {
-					Driver.AddCh (Secret ? '*' : text [p]);
+					Driver.AddRune (Secret ? (Rune)'*' : text [p]);
 				} else
-					Driver.AddCh (' ');
+					Driver.AddRune (' ');
 			}
 			PositionCursor ();
 		}
@@ -112,7 +113,7 @@ namespace Terminal.Gui {
 			SetNeedsDisplay ();
 		}
 
-		void SetText (string new_text)
+		void SetText (ustring new_text)
 		{
 			text = new_text;
 			if (Changed != null)
@@ -132,7 +133,7 @@ namespace Terminal.Gui {
 				if (point == 0)
 					return true;
 
-				SetText (text.Substring (0, point - 1) + text.Substring (point));
+				SetText (text [0, point - 1] + text [point, null]);
 				point--;
 				Adjust ();
 				break;
@@ -155,7 +156,7 @@ namespace Terminal.Gui {
 			case Key.ControlD: // Delete
 				if (point == text.Length)
 					break;
-				SetText (text.Substring (0, point) + text.Substring (point + 1));
+				SetText (text [0, point] + text [point + 1, null]);
 				Adjust ();
 				break;
 
@@ -174,7 +175,7 @@ namespace Terminal.Gui {
 
 			case Key.ControlK: // kill-to-end
 				kill = text.Substring (point);
-				SetText (text.Substring (0, point));
+				SetText (text [0, point]);
 				Adjust ();
 				break;
 
@@ -186,7 +187,7 @@ namespace Terminal.Gui {
 					SetText (text + kill);
 					point = text.Length;
 				} else {
-					SetText (text.Substring (0, point) + kill + text.Substring (point));
+					SetText (text [0, point] + kill + text.Substring (point));
 					point += kill.Length;
 				}
 				Adjust ();
@@ -211,15 +212,16 @@ namespace Terminal.Gui {
 				if (kb.Key < Key.Space || kb.Key > Key.CharMask)
 					return false;
 
+				var kbstr = ustring.Make ((uint)kb.Key);
 				if (used) {
 					if (point == text.Length) {
-						SetText (text + (char)kb.Key);
+						SetText (text + kbstr);
 					} else {
-						SetText (text.Substring (0, point) + (char)kb.Key + text.Substring (point));
+						SetText (text [0, point] + kbstr + text [point, null]);
 					}
 					point++;
 				} else {
-					SetText ("" + (char)kb.Key);
+					SetText (kbstr);
 					first = 0;
 					point = 1;
 				}
@@ -237,18 +239,21 @@ namespace Terminal.Gui {
 				return -1;
 
 			int i = p;
-			if (Char.IsPunctuation (text [p]) || Char.IsWhiteSpace (text [p])) {
+			if (Rune.IsPunctuation (text [p]) || Rune.IsWhiteSpace(text [p])) {
 				for (; i < text.Length; i++) {
-					if (Char.IsLetterOrDigit (text [i]))
+					var r = text [i];
+					if (Rune.IsLetterOrDigit(r))
 						break;
 				}
 				for (; i < text.Length; i++) {
-					if (!Char.IsLetterOrDigit (text [i]))
+					var r = text [i];
+					if (!Rune.IsLetterOrDigit (r))
 						break;
 				}
 			} else {
 				for (; i < text.Length; i++) {
-					if (!Char.IsLetterOrDigit (text [i]))
+					var r = text [i];
+					if (!Rune.IsLetterOrDigit (r))
 						break;
 				}
 			}
@@ -266,18 +271,19 @@ namespace Terminal.Gui {
 			if (i == 0)
 				return 0;
 
-			if (Char.IsPunctuation (text [i]) || Char.IsSymbol (text [i]) || Char.IsWhiteSpace (text [i])) {
+			var ti = text [i];
+			if (Rune.IsPunctuation (ti) || Rune.IsSymbol(ti) || Rune.IsWhiteSpace(ti)) {
 				for (; i >= 0; i--) {
-					if (Char.IsLetterOrDigit (text [i]))
+					if (Rune.IsLetterOrDigit (text [i]))
 						break;
 				}
 				for (; i >= 0; i--) {
-					if (!Char.IsLetterOrDigit (text [i]))
+					if (!Rune.IsLetterOrDigit (text [i]))
 						break;
 				}
 			} else {
 				for (; i >= 0; i--) {
-					if (!Char.IsLetterOrDigit (text [i]))
+					if (!Rune.IsLetterOrDigit (text [i]))
 						break;
 				}
 			}

+ 4 - 0
Terminal.Gui/packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NStack.Core" version="0.7.0" targetFramework="net461" />
+</packages>

+ 7 - 0
Terminal.csproj

@@ -31,6 +31,10 @@
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
+    <Reference Include="NStack">
+      <HintPath>packages\NStack.Core.0.7.0\lib\netstandard1.5\NStack.dll</HintPath>
+      <Private>False</Private>
+    </Reference>
   </ItemGroup>
   <ItemGroup>
     <Compile Include="demo.cs" />
@@ -41,5 +45,8 @@
       <Name>Terminal.Gui</Name>
     </ProjectReference>
   </ItemGroup>
+  <ItemGroup>
+    <None Include="packages.config" />
+  </ItemGroup>
   <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
 </Project>

+ 4 - 0
packages.config

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+  <package id="NStack.Core" version="0.7.0" targetFramework="net461" />
+</packages>