Browse Source

Fixes #92. Remove dependency on ustring. (#2620)

* Remove NStack and replace ustring to string.

* Add unit test and improving some code.

* Adjust code and fix all unit tests errors.

* Add XML Document and move the Rune folder into the Text folder.

* Improve unit tests with byte array on DecodeRune and DecodeLastRune.

* Fix unit test.

* 😂Code review

* Reduce unit tests code.

* Change StringExtensions.Make to StringExtensions.ToString and added some more unit tests.

* Fix merge errors.

* Remove GetTextWidth and calls replaced with StringExtensions.GetColumns.

* Hack to use UseSystemConsole passed in the command line arguments.

* Revert "Hack to use UseSystemConsole passed in the command line arguments."

This reverts commit b74d11c7864fa6e20d40ef5cbead89a42f81ee5e.

* Remove Application.UseSystemConsole from the config file.

* Fix errors related by removing UseSystemConsole from the config file.

* Fixes #2633. DecodeEscSeq throw an exception if cki is null.

* Fix an exception if SelectedItem is -1.

* Set SelectedItem to 0 and remove unnecessary ToString.

* Using a unique ToString method for Rune and other for byte.

* Fix a bug where a wider rune is added with only a width of 1.

* Force the SelectedGlyph is the one that was typed after jumpList is executed.

* Added more InlineData to RuneTests.

* Reducing significantly the code by using Theory attribute in the TextFormatterTests.

* Override PositionCursor to handle the CharMap cursor position.

* Fix merge errors.

* Minor tweaks to API docs

---------

Co-authored-by: Tig Kindel <[email protected]>
BDisp 2 years ago
parent
commit
713b2c4725
100 changed files with 1676 additions and 1266 deletions
  1. 7 7
      ReactiveExample/LoginView.cs
  2. 7 7
      ReactiveExample/LoginViewModel.cs
  3. 1 1
      ReactiveExample/README.md
  4. 3 4
      Terminal.Gui/Application.cs
  5. 4 4
      Terminal.Gui/Clipboard/Clipboard.cs
  6. 8 9
      Terminal.Gui/Configuration/RuneJsonConverter.cs
  7. 10 27
      Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs
  8. 10 10
      Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs
  9. 12 12
      Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs
  10. 14 13
      Terminal.Gui/ConsoleDrivers/NetDriver.cs
  11. 13 13
      Terminal.Gui/ConsoleDrivers/WindowsDriver.cs
  12. 124 124
      Terminal.Gui/Drawing/Glyphs.cs
  13. 1 1
      Terminal.Gui/Drawing/LineCanvas.cs
  14. 2 3
      Terminal.Gui/Drawing/Ruler.cs
  15. 11 12
      Terminal.Gui/Drawing/Thickness.cs
  16. 13 14
      Terminal.Gui/Input/ShortcutHelper.cs
  17. 0 1
      Terminal.Gui/Resources/config.json
  18. 0 32
      Terminal.Gui/StringExtensions.cs
  19. 0 1
      Terminal.Gui/Terminal.Gui.csproj
  20. 18 22
      Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs
  21. 1 1
      Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs
  22. 1 1
      Terminal.Gui/Text/Autocomplete/IAutocomplete.cs
  23. 1 1
      Terminal.Gui/Text/Autocomplete/ISuggestionGenerator.cs
  24. 2 2
      Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs
  25. 3 3
      Terminal.Gui/Text/Autocomplete/SingleWordSuggestionGenerator.cs
  26. 311 0
      Terminal.Gui/Text/RuneExtensions.cs
  27. 154 0
      Terminal.Gui/Text/StringExtensions.cs
  28. 143 145
      Terminal.Gui/Text/TextFormatter.cs
  29. 10 10
      Terminal.Gui/View/Frame.cs
  30. 4 4
      Terminal.Gui/View/TitleEventArgs.cs
  31. 10 10
      Terminal.Gui/View/View.cs
  32. 8 8
      Terminal.Gui/View/ViewDrawing.cs
  33. 2 2
      Terminal.Gui/View/ViewKeyboard.cs
  34. 3 3
      Terminal.Gui/View/ViewLayout.cs
  35. 1 1
      Terminal.Gui/View/ViewMouse.cs
  36. 1 1
      Terminal.Gui/View/ViewSubViews.cs
  37. 8 8
      Terminal.Gui/View/ViewText.cs
  38. 10 11
      Terminal.Gui/Views/AutocompleteFilepathContext.cs
  39. 10 11
      Terminal.Gui/Views/Button.cs
  40. 13 13
      Terminal.Gui/Views/CheckBox.cs
  41. 4 6
      Terminal.Gui/Views/ColorPicker.cs
  42. 14 12
      Terminal.Gui/Views/ComboBox.cs
  43. 26 27
      Terminal.Gui/Views/DateField.cs
  44. 1 1
      Terminal.Gui/Views/Dialog.cs
  45. 7 7
      Terminal.Gui/Views/FileDialog.cs
  46. 4 4
      Terminal.Gui/Views/FrameView.cs
  47. 1 1
      Terminal.Gui/Views/GraphView/Annotations.cs
  48. 2 1
      Terminal.Gui/Views/GraphView/Axis.cs
  49. 1 0
      Terminal.Gui/Views/GraphView/GraphCellToRender.cs
  50. 2 2
      Terminal.Gui/Views/GraphView/GraphView.cs
  51. 1 0
      Terminal.Gui/Views/GraphView/Series.cs
  52. 6 5
      Terminal.Gui/Views/HexView.cs
  53. 1 1
      Terminal.Gui/Views/HistoryTextItem.cs
  54. 5 5
      Terminal.Gui/Views/Label.cs
  55. 2 1
      Terminal.Gui/Views/LineView.cs
  56. 13 13
      Terminal.Gui/Views/ListView.cs
  57. 42 42
      Terminal.Gui/Views/Menu.cs
  58. 22 22
      Terminal.Gui/Views/MessageBox.cs
  59. 2 2
      Terminal.Gui/Views/OpenDialog.cs
  60. 6 6
      Terminal.Gui/Views/ProgressBar.cs
  61. 15 15
      Terminal.Gui/Views/RadioGroup.cs
  62. 3 3
      Terminal.Gui/Views/SaveDialog.cs
  63. 1 0
      Terminal.Gui/Views/ScrollBarView.cs
  64. 12 12
      Terminal.Gui/Views/StatusBar.cs
  65. 6 6
      Terminal.Gui/Views/TabView.cs
  66. 1 0
      Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs
  67. 3 6
      Terminal.Gui/Views/TableView/ListTableSource.cs
  68. 10 10
      Terminal.Gui/Views/TableView/TableView.cs
  69. 3 3
      Terminal.Gui/Views/TextChangedEventArgs.cs
  70. 3 3
      Terminal.Gui/Views/TextChangingEventArgs.cs
  71. 45 51
      Terminal.Gui/Views/TextField.cs
  72. 24 24
      Terminal.Gui/Views/TextValidateField.cs
  73. 102 103
      Terminal.Gui/Views/TextView.cs
  74. 4 4
      Terminal.Gui/Views/TileView.cs
  75. 11 11
      Terminal.Gui/Views/TimeField.cs
  76. 13 12
      Terminal.Gui/Views/TreeView/Branch.cs
  77. 1 0
      Terminal.Gui/Views/TreeView/TreeStyle.cs
  78. 2 2
      Terminal.Gui/Views/TreeView/TreeView.cs
  79. 1 1
      Terminal.Gui/Views/Window.cs
  80. 13 13
      Terminal.Gui/Views/Wizard/Wizard.cs
  81. 1 1
      UICatalog/Scenario.cs
  82. 17 17
      UICatalog/Scenarios/AllViewsTester.cs
  83. 1 1
      UICatalog/Scenarios/Animation.cs
  84. 11 11
      UICatalog/Scenarios/Buttons.cs
  85. 56 36
      UICatalog/Scenarios/CharacterMap.cs
  86. 1 3
      UICatalog/Scenarios/ComputedLayout.cs
  87. 2 2
      UICatalog/Scenarios/CsvEditor.cs
  88. 2 2
      UICatalog/Scenarios/Dialogs.cs
  89. 26 27
      UICatalog/Scenarios/DynamicMenuBar.cs
  90. 20 21
      UICatalog/Scenarios/DynamicStatusBar.cs
  91. 33 33
      UICatalog/Scenarios/Editor.cs
  92. 64 70
      UICatalog/Scenarios/FileDialogExamples.cs
  93. 5 6
      UICatalog/Scenarios/Frames.cs
  94. 7 6
      UICatalog/Scenarios/GraphViewExample.cs
  95. 1 1
      UICatalog/Scenarios/Keys.cs
  96. 26 26
      UICatalog/Scenarios/LabelsAsButtons.cs
  97. 3 3
      UICatalog/Scenarios/LineDrawing.cs
  98. 3 3
      UICatalog/Scenarios/LineViewExample.cs
  99. 5 5
      UICatalog/Scenarios/ListViewWithSelection.cs
  100. 3 3
      UICatalog/Scenarios/ListsAndCombos.cs

+ 7 - 7
ReactiveExample/LoginView.cs

@@ -1,6 +1,6 @@
 using System.Reactive.Disposables;
 using System.Reactive.Linq;
-using NStack;
+using System.Text;
 using ReactiveUI;
 using Terminal.Gui;
 using ReactiveMarbles.ObservableEvents;
@@ -64,7 +64,7 @@ namespace ReactiveExample {
 			};
 			ViewModel
 				.WhenAnyValue (x => x.UsernameLength)
-				.Select (length => ustring.Make ($"Username ({length} characters)"))
+				.Select (length => $"Username ({length} characters)")
 				.BindTo (usernameLengthLabel, x => x.Text)
 				.DisposeWith (_disposable);
 			Add (usernameLengthLabel);
@@ -100,7 +100,7 @@ namespace ReactiveExample {
 			};
 			ViewModel
 				.WhenAnyValue (x => x.PasswordLength)
-				.Select (length => ustring.Make ($"Password ({length} characters)"))
+				.Select (length => $"Password ({length} characters)")
 				.BindTo (passwordLengthLabel, x => x.Text)
 				.DisposeWith (_disposable);
 			Add (passwordLengthLabel);
@@ -108,8 +108,8 @@ namespace ReactiveExample {
 		}
 
 		Label ValidationLabel (View previous) {
-			var error = ustring.Make("Please, enter user name and password.");
-			var success = ustring.Make("The input is valid!");
+			var error = "Please, enter user name and password.";
+			var success = "The input is valid!";
 			var validationLabel = new Label(error) {
 				X = Pos.Left(previous),
 				Y = Pos.Top(previous) + 1,
@@ -130,8 +130,8 @@ namespace ReactiveExample {
 		}
 
 		Label LoginProgressLabel (View previous) {
-			var progress = ustring.Make ("Logging in...");
-			var idle = ustring.Make ("Press 'Login' to log in.");
+			var progress = "Logging in...";
+			var idle = "Press 'Login' to log in.";
 			var loginProgressLabel = new Label(idle) {
 				X = Pos.Left(previous),
 				Y = Pos.Top(previous) + 1,

+ 7 - 7
ReactiveExample/LoginViewModel.cs

@@ -3,7 +3,7 @@ using System.Reactive;
 using System.Reactive.Linq;
 using System.Runtime.Serialization;
 using System.Threading.Tasks;
-using NStack;
+using System.Text;
 using ReactiveUI;
 using ReactiveUI.Fody.Helpers;
 
@@ -30,8 +30,8 @@ namespace ReactiveExample {
 				x => x.Username, 
 				x => x.Password,
 				(username, password) =>
-					!ustring.IsNullOrEmpty (username) &&
-					!ustring.IsNullOrEmpty (password));
+					!string.IsNullOrEmpty (username) &&
+					!string.IsNullOrEmpty (password));
 			
 			_isValid = canLogin.ToProperty (this, x => x.IsValid);
 			Login = ReactiveCommand.CreateFromTask (
@@ -49,16 +49,16 @@ namespace ReactiveExample {
 			
 			Clear = ReactiveCommand.Create (() => { });
 			Clear.Subscribe (unit => {
-				Username = ustring.Empty;
-				Password = ustring.Empty;
+				Username = string.Empty;
+				Password = string.Empty;
 			});
 		}
 		
 		[Reactive, DataMember]
-		public ustring Username { get; set; } = ustring.Empty;
+		public string Username { get; set; } = string.Empty;
 		
 		[Reactive, DataMember]
-		public ustring Password { get; set; } = ustring.Empty;
+		public string Password { get; set; } = string.Empty;
 		
 		[IgnoreDataMember]
 		public int UsernameLength => _usernameLength.Value;

+ 1 - 1
ReactiveExample/README.md

@@ -38,7 +38,7 @@ usernameInput
 	.BindTo (ViewModel, x => x.Username);
 ```
 
-If you combine `OneWay` and `OneWayToSource` data bindings, you get `TwoWay` data binding. Also be sure to use the `ustring` type instead of the `string` type. Invoking commands should be as simple as this:
+If you combine `OneWay` and `OneWayToSource` data bindings, you get `TwoWay` data binding. Also be sure to use the `string` type instead of the `string` type. Invoking commands should be as simple as this:
 ```cs
 // 'clearButton' is 'Button'
 clearButton

+ 3 - 4
Terminal.Gui/Application.cs

@@ -51,7 +51,6 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// If <see langword="true"/>, forces the use of the System.Console-based (see <see cref="NetDriver"/>) driver. The default is <see langword="false"/>.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof (SettingsScope))]
 		public static bool UseSystemConsole { get; set; } = false;
 
 		// For Unit testing - ignores UseSystemConsole
@@ -442,10 +441,10 @@ namespace Terminal.Gui {
 				} else if (Top != null && Toplevel != Top && _toplevels.Contains (Top)) {
 					Top.OnLeave (Toplevel);
 				}
-				if (string.IsNullOrEmpty (Toplevel.Id.ToString ())) {
+				if (string.IsNullOrEmpty (Toplevel.Id)) {
 					var count = 1;
 					var id = (_toplevels.Count + count).ToString ();
-					while (_toplevels.Count > 0 && _toplevels.FirstOrDefault (x => x.Id.ToString () == id) != null) {
+					while (_toplevels.Count > 0 && _toplevels.FirstOrDefault (x => x.Id == id) != null) {
 						count++;
 						id = (_toplevels.Count + count).ToString ();
 					}
@@ -453,7 +452,7 @@ namespace Terminal.Gui {
 
 					_toplevels.Push (Toplevel);
 				} else {
-					var dup = _toplevels.FirstOrDefault (x => x.Id.ToString () == Toplevel.Id);
+					var dup = _toplevels.FirstOrDefault (x => x.Id == Toplevel.Id);
 					if (dup == null) {
 						_toplevels.Push (Toplevel);
 					}

+ 4 - 4
Terminal.Gui/Clipboard/Clipboard.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 
 namespace Terminal.Gui {
@@ -25,16 +25,16 @@ namespace Terminal.Gui {
 	/// </para>
 	/// </remarks>
 	public static class Clipboard {
-		static ustring contents;
+		static string contents;
 
 		/// <summary>
 		/// Gets (copies from) or sets (pastes to) the contents of the OS clipboard.
 		/// </summary>
-		public static ustring Contents {
+		public static string Contents {
 			get {
 				try {
 					if (IsSupported) {
-						return contents = ustring.Make (Application.Driver.Clipboard.GetClipboardData ());
+						return contents = Application.Driver.Clipboard.GetClipboardData ();
 					} else {
 						return contents;
 					}

+ 8 - 9
Terminal.Gui/Configuration/RuneJsonConverter.cs

@@ -2,11 +2,10 @@
 using System.Text;
 using System.Text.Json;
 using System.Text.Json.Serialization;
-//using Rune = System.Rune;
 
 namespace Terminal.Gui {
 	/// <summary>
-	/// Json converter for <see cref="System.Rune"/>. Supports
+	/// Json converter for <see cref="Rune"/>. Supports
 	/// A string as one of:
 	/// - unicode char (e.g. "☑")
 	/// - U+hex format (e.g. "U+2611")
@@ -14,29 +13,29 @@ namespace Terminal.Gui {
 	/// A number
 	/// - The unicode code in decimal
 	/// </summary>
-	internal class RuneJsonConverter : JsonConverter<System.Rune> {
-		public override System.Rune Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
+	internal class RuneJsonConverter : JsonConverter<Rune> {
+		public override Rune Read (ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
 		{
 			if (reader.TokenType == JsonTokenType.String) {
 				var value = reader.GetString ();
-				if (value.ToUpper ().StartsWith ("U+") || value.StartsWith ("\\u")) {
+				if (value.StartsWith ("U+", StringComparison.OrdinalIgnoreCase) || value.StartsWith ("\\u")) {
 					try {
 						uint result = uint.Parse (value [2..^0], System.Globalization.NumberStyles.HexNumber);
-						return new System.Rune (result);
+						return new Rune (result);
 					} catch (FormatException e) {
 						throw new JsonException ($"Invalid Rune format: {value}.", e);
 					}
 				} else {
-					return new System.Rune (value [0]);
+					return new Rune (value [0]);
 				}
 				throw new JsonException ($"Invalid Rune format: {value}.");
 			} else if (reader.TokenType == JsonTokenType.Number) {
-				return new System.Rune (reader.GetUInt32 ());
+				return new Rune (reader.GetUInt32 ());
 			}
 			throw new JsonException ($"Unexpected StartObject token when parsing Rune: {reader.TokenType}.");
 		}
 
-		public override void Write (Utf8JsonWriter writer, System.Rune value, JsonSerializerOptions options)
+		public override void Write (Utf8JsonWriter writer, Rune value, JsonSerializerOptions options)
 		{
 			// HACK: Writes a JSON comment in addition to the glyph to ease debugging.
 			// Technically, JSON comments are not valid, but we use relaxed decoding

+ 10 - 27
Terminal.Gui/ConsoleDrivers/ConsoleDriver.cs

@@ -1,7 +1,7 @@
 //
 // ConsoleDriver.cs: Base class for Terminal.Gui ConsoleDriver implementations.
 //
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -347,13 +347,13 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Creates a new instance.
 		/// </summary>
-		public ColorScheme() { }
+		public ColorScheme () { }
 
 		/// <summary>
 		/// Creates a new instance, initialized with the values from <paramref name="scheme"/>.
 		/// </summary>
 		/// <param name="scheme">The scheme to initlize the new instance with.</param>
-		public ColorScheme (ColorScheme scheme) : base()
+		public ColorScheme (ColorScheme scheme) : base ()
 		{
 			if (scheme != null) {
 				_normal = scheme.Normal;
@@ -617,8 +617,8 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Provides the defined <see cref="ColorScheme"/>s.
 		/// </summary>
-		[SerializableConfigurationProperty (Scope = typeof(ThemeScope), OmitClassName = true)]
-		[JsonConverter(typeof(DictionaryJsonConverter<ColorScheme>))]
+		[SerializableConfigurationProperty (Scope = typeof (ThemeScope), OmitClassName = true)]
+		[JsonConverter (typeof (DictionaryJsonConverter<ColorScheme>))]
 		public static Dictionary<string, ColorScheme> ColorSchemes { get; private set; }
 	}
 
@@ -679,7 +679,7 @@ namespace Terminal.Gui {
 		/// <remarks>Works under Xterm-like terminal otherwise this is equivalent to <see ref="Block"/></remarks>
 		BoxFix = 0x02020164,
 	}
-	
+
 	/// <summary>
 	/// ConsoleDriver is an abstract class that defines the requirements for a console driver.  
 	/// There are currently three implementations: <see cref="CursesDriver"/> (for Unix and Mac), <see cref="WindowsDriver"/>, and <see cref="NetDriver"/> that uses the .NET Console API.
@@ -756,24 +756,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="rune">Rune to add.</param>
 		public abstract void AddRune (Rune rune);
-
-		/// <summary>
-		/// Ensures a Rune is not a control character and can be displayed by translating characters below 0x20
-		/// to equivalent, printable, Unicode chars.
-		/// </summary>
-		/// <param name="c">Rune to translate</param>
-		/// <returns></returns>
-		public static Rune MakePrintable (Rune c)
-		{
-			if (c <= 0x1F || (c >= 0X7F && c <= 0x9F)) {
-				// ASCII (C0) control characters.
-				// C1 control characters (https://www.aivosto.com/articles/control-characters.html#c1)
-				return new Rune (c + 0x2400);
-			}
-
-			return c;
-		}
-
+		
 		/// <summary>
 		/// Ensures that the column and line are in a valid range from the size of the driver.
 		/// </summary>
@@ -788,7 +771,7 @@ namespace Terminal.Gui {
 		/// Adds the <paramref name="str"/> to the display at the cursor position.
 		/// </summary>
 		/// <param name="str">String.</param>
-		public abstract void AddStr (ustring str);
+		public abstract void AddStr (string str);
 
 		/// <summary>
 		/// Prepare the driver and set the key and mouse events handlers.
@@ -928,12 +911,12 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="rect"></param>
 		/// <param name="rune"></param>
-		public virtual void FillRect (Rect rect, System.Rune rune = default)
+		public virtual void FillRect (Rect rect, Rune rune = default)
 		{
 			for (var r = rect.Y; r < rect.Y + rect.Height; r++) {
 				for (var c = rect.X; c < rect.X + rect.Width; c++) {
 					Application.Driver.Move (c, r);
-					Application.Driver.AddRune (rune == default ? ' ' : rune);
+					Application.Driver.AddRune ((Rune)(rune == default ? ' ' : rune.Value));
 				}
 			}
 		}

+ 10 - 10
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -7,7 +7,7 @@ using System.Diagnostics;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading.Tasks;
-using NStack;
+using System.Text;
 using Unix.Terminal;
 
 namespace Terminal.Gui {
@@ -50,8 +50,8 @@ namespace Terminal.Gui {
 		static bool sync = false;
 		public override void AddRune (Rune rune)
 		{
-			rune = MakePrintable (rune);
-			var runeWidth = Rune.ColumnWidth (rune);
+			rune = rune.MakePrintable ();
+			var runeWidth = rune.GetColumns ();
 			var validClip = IsValidContent (ccol, crow, Clip);
 
 			if (validClip) {
@@ -61,7 +61,7 @@ namespace Terminal.Gui {
 				}
 				if (runeWidth == 0 && ccol > 0) {
 					var r = contents [crow, ccol - 1, 0];
-					var s = new string (new char [] { (char)r, (char)rune });
+					var s = new string (new char [] { (char)r, (char)rune.Value });
 					string sn;
 					if (!s.IsNormalized ()) {
 						sn = s.Normalize ();
@@ -76,7 +76,7 @@ namespace Terminal.Gui {
 
 				} else {
 					if (runeWidth < 2 && ccol > 0
-						&& Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
 
 						var curAtttib = CurrentAttribute;
 						Curses.attrset (contents [crow, ccol - 1, 1]);
@@ -86,7 +86,7 @@ namespace Terminal.Gui {
 						Curses.attrset (curAtttib);
 
 					} else if (runeWidth < 2 && ccol <= Clip.Right - 1
-						&& Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
 
 						var curAtttib = CurrentAttribute;
 						Curses.attrset (contents [crow, ccol + 1, 1]);
@@ -100,8 +100,8 @@ namespace Terminal.Gui {
 						Curses.addch ((int)(uint)' ');
 						contents [crow, ccol, 0] = (int)(uint)' ';
 					} else {
-						Curses.addch ((int)(uint)rune);
-						contents [crow, ccol, 0] = (int)(uint)rune;
+						Curses.addch ((int)(uint)rune.Value);
+						contents [crow, ccol, 0] = (int)(uint)rune.Value;
 					}
 					contents [crow, ccol, 1] = CurrentAttribute;
 					contents [crow, ccol, 2] = 1;
@@ -127,10 +127,10 @@ namespace Terminal.Gui {
 			}
 		}
 
-		public override void AddStr (ustring str)
+		public override void AddStr (string 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)
+			foreach (var rune in str.EnumerateRunes ())
 				AddRune (rune);
 		}
 

+ 12 - 12
Terminal.Gui/ConsoleDrivers/FakeDriver/FakeDriver.cs

@@ -7,7 +7,7 @@ using System.Diagnostics;
 using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
-using NStack;
+using System.Text;
 
 // Alias Console to MockConsole so we don't accidentally use Console
 using Console = Terminal.Gui.FakeConsole;
@@ -118,8 +118,8 @@ namespace Terminal.Gui {
 
 		public override void AddRune (Rune rune)
 		{
-			rune = MakePrintable (rune);
-			var runeWidth = Rune.ColumnWidth (rune);
+			rune = rune.MakePrintable ();
+			var runeWidth = rune.GetColumns ();
 			var validClip = IsValidContent (ccol, crow, Clip);
 
 			if (validClip) {
@@ -130,7 +130,7 @@ namespace Terminal.Gui {
 				}
 				if (runeWidth == 0 && ccol > 0) {
 					var r = contents [crow, ccol - 1, 0];
-					var s = new string (new char [] { (char)r, (char)rune });
+					var s = new string (new char [] { (char)r, (char)rune.Value });
 					string sn;
 					if (!s.IsNormalized ()) {
 						sn = s.Normalize ();
@@ -144,12 +144,12 @@ namespace Terminal.Gui {
 
 				} else {
 					if (runeWidth < 2 && ccol > 0
-					&& Rune.ColumnWidth ((Rune)contents [crow, ccol - 1, 0]) > 1) {
+					&& ((Rune)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
 
 						contents [crow, ccol - 1, 0] = (int)(uint)' ';
 
 					} else if (runeWidth < 2 && ccol <= Clip.Right - 1
-						&& Rune.ColumnWidth ((Rune)contents [crow, ccol, 0]) > 1) {
+						&& ((Rune)contents [crow, ccol, 0]).GetColumns () > 1) {
 
 						contents [crow, ccol + 1, 0] = (int)(uint)' ';
 						contents [crow, ccol + 1, 2] = 1;
@@ -158,7 +158,7 @@ namespace Terminal.Gui {
 					if (runeWidth > 1 && ccol == Clip.Right - 1) {
 						contents [crow, ccol, 0] = (int)(uint)' ';
 					} else {
-						contents [crow, ccol, 0] = (int)(uint)rune;
+						contents [crow, ccol, 0] = (int)(uint)rune.Value;
 					}
 					contents [crow, ccol, 1] = CurrentAttribute;
 					contents [crow, ccol, 2] = 1;
@@ -191,9 +191,9 @@ namespace Terminal.Gui {
 			}
 		}
 
-		public override void AddStr (ustring str)
+		public override void AddStr (string str)
 		{
-			foreach (var rune in str)
+			foreach (var rune in str.EnumerateRunes ())
 				AddRune (rune);
 		}
 
@@ -281,11 +281,11 @@ namespace Terminal.Gui {
 						if (color != redrawColor)
 							SetColor (color);
 
-						Rune rune = contents [row, col, 0];
-						if (Rune.DecodeSurrogatePair (rune, out char [] spair)) {
+						Rune rune = (Rune)contents [row, col, 0];
+						if (rune.DecodeSurrogatePair (out char [] spair)) {
 							FakeConsole.Write (spair);
 						} else {
-							FakeConsole.Write ((char)rune);
+							FakeConsole.Write ((char)rune.Value);
 						}
 						contents [row, col, 2] = 0;
 					}

+ 14 - 13
Terminal.Gui/ConsoleDrivers/NetDriver.cs

@@ -11,7 +11,7 @@ using System.Linq;
 using System.Runtime.InteropServices;
 using System.Threading;
 using System.Threading.Tasks;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	internal class NetWinVTConsole {
@@ -195,12 +195,13 @@ namespace Terminal.Gui {
 						isEscSeq = false;
 						break;
 					}
-				} else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq) {
+				} else if (consoleKeyInfo.KeyChar == (char)Key.Esc && isEscSeq && cki != null) {
 					DecodeEscSeq (ref newConsoleKeyInfo, ref key, cki, ref mod);
 					cki = null;
 					break;
 				} else {
 					GetConsoleInputType (consoleKeyInfo);
+					isEscSeq = false;
 					break;
 				}
 			}
@@ -668,14 +669,14 @@ namespace Terminal.Gui {
 			if (contents.Length != Rows * Cols * 3) {
 				return;
 			}
-			rune = MakePrintable (rune);
-			var runeWidth = Rune.ColumnWidth (rune);
+			rune = rune.MakePrintable ();
+			var runeWidth = rune.GetColumns ();
 			var validClip = IsValidContent (ccol, crow, Clip);
 
 			if (validClip) {
 				if (runeWidth == 0 && ccol > 0) {
 					var r = contents [crow, ccol - 1, 0];
-					var s = new string (new char [] { (char)r, (char)rune });
+					var s = new string (new char [] { (char)r, (char)rune.Value });
 					string sn;
 					if (!s.IsNormalized ()) {
 						sn = s.Normalize ();
@@ -689,12 +690,12 @@ namespace Terminal.Gui {
 
 				} else {
 					if (runeWidth < 2 && ccol > 0
-						&& Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
 
 						contents [crow, ccol - 1, 0] = (int)(uint)' ';
 
 					} else if (runeWidth < 2 && ccol <= Clip.Right - 1
-						&& Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
 
 						contents [crow, ccol + 1, 0] = (int)(uint)' ';
 						contents [crow, ccol + 1, 2] = 1;
@@ -703,7 +704,7 @@ namespace Terminal.Gui {
 					if (runeWidth > 1 && ccol == Clip.Right - 1) {
 						contents [crow, ccol, 0] = (int)(uint)' ';
 					} else {
-						contents [crow, ccol, 0] = (int)(uint)rune;
+						contents [crow, ccol, 0] = (int)(uint)rune.Value;
 					}
 					contents [crow, ccol, 1] = CurrentAttribute;
 					contents [crow, ccol, 2] = 1;
@@ -729,9 +730,9 @@ namespace Terminal.Gui {
 			}
 		}
 
-		public override void AddStr (ustring str)
+		public override void AddStr (string str)
 		{
-			foreach (var rune in str)
+			foreach (var rune in str.EnumerateRunes ())
 				AddRune (rune);
 		}
 
@@ -950,12 +951,12 @@ namespace Terminal.Gui {
 							output.Append (WriteAttributes (attr));
 						}
 						outputWidth++;
-						var rune = contents [row, col, 0];
+						var rune = (Rune)contents [row, col, 0];
 						char [] spair;
-						if (Rune.DecodeSurrogatePair ((uint)rune, out spair)) {
+						if (rune.DecodeSurrogatePair (out spair)) {
 							output.Append (spair);
 						} else {
-							output.Append ((char)rune);
+							output.Append ((char)rune.Value);
 						}
 						contents [row, col, 2] = 0;
 					}

+ 13 - 13
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -1,7 +1,7 @@
 //
 // WindowsDriver.cs: Windows specific driver
 //
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -742,7 +742,7 @@ namespace Terminal.Gui {
 
 			mLoop.ProcessInput = (e) => ProcessInput (e);
 
-			mLoop.WinChanged = (s,e) => {
+			mLoop.WinChanged = (s, e) => {
 				ChangeWin (e.Size);
 			};
 		}
@@ -1520,15 +1520,15 @@ namespace Terminal.Gui {
 
 		public override void AddRune (Rune rune)
 		{
-			rune = MakePrintable (rune);
-			var runeWidth = Rune.ColumnWidth (rune);
+			rune = rune.MakePrintable ();
+			var runeWidth = rune.GetColumns ();
 			var position = GetOutputBufferPosition ();
 			var validClip = IsValidContent (ccol, crow, Clip);
 
 			if (validClip) {
 				if (runeWidth == 0 && ccol > 0) {
 					var r = contents [crow, ccol - 1, 0];
-					var s = new string (new char [] { (char)r, (char)rune });
+					var s = new string (new char [] { (char)r, (char)rune.Value });
 					string sn;
 					if (!s.IsNormalized ()) {
 						sn = s.Normalize ();
@@ -1545,14 +1545,14 @@ namespace Terminal.Gui {
 					WindowsConsole.SmallRect.Update (ref damageRegion, (short)(ccol - 1), (short)crow);
 				} else {
 					if (runeWidth < 2 && ccol > 0
-						&& Rune.ColumnWidth ((char)contents [crow, ccol - 1, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol - 1, 0]).GetColumns () > 1) {
 
 						var prevPosition = crow * Cols + (ccol - 1);
 						OutputBuffer [prevPosition].Char.UnicodeChar = ' ';
 						contents [crow, ccol - 1, 0] = (int)(uint)' ';
 
 					} else if (runeWidth < 2 && ccol <= Clip.Right - 1
-						&& Rune.ColumnWidth ((char)contents [crow, ccol, 0]) > 1) {
+						&& ((Rune)(char)contents [crow, ccol, 0]).GetColumns () > 1) {
 
 						var prevPosition = GetOutputBufferPosition () + 1;
 						OutputBuffer [prevPosition].Char.UnicodeChar = (char)' ';
@@ -1563,8 +1563,8 @@ namespace Terminal.Gui {
 						OutputBuffer [position].Char.UnicodeChar = (char)' ';
 						contents [crow, ccol, 0] = (int)(uint)' ';
 					} else {
-						OutputBuffer [position].Char.UnicodeChar = (char)rune;
-						contents [crow, ccol, 0] = (int)(uint)rune;
+						OutputBuffer [position].Char.UnicodeChar = (char)rune.Value;
+						contents [crow, ccol, 0] = (int)(uint)rune.Value;
 					}
 					OutputBuffer [position].Attributes = (ushort)CurrentAttribute;
 					contents [crow, ccol, 1] = CurrentAttribute;
@@ -1594,9 +1594,9 @@ namespace Terminal.Gui {
 			}
 		}
 
-		public override void AddStr (ustring str)
+		public override void AddStr (string str)
 		{
-			foreach (var rune in str)
+			foreach (var rune in str.EnumerateRunes ())
 				AddRune (rune);
 		}
 
@@ -1868,7 +1868,7 @@ namespace Terminal.Gui {
 		/// Invoked when the window is changed.
 		/// </summary>
 		public EventHandler<SizeChangedEventArgs> WinChanged;
-		
+
 		public WindowsMainLoop (ConsoleDriver consoleDriver = null)
 		{
 			this.consoleDriver = consoleDriver ?? throw new ArgumentNullException ("Console driver instance must be provided.");
@@ -1990,7 +1990,7 @@ namespace Terminal.Gui {
 			}
 			if (winChanged) {
 				winChanged = false;
-				WinChanged?.Invoke (this, new SizeChangedEventArgs(windowSize));
+				WinChanged?.Invoke (this, new SizeChangedEventArgs (windowSize));
 			}
 		}
 	}

+ 124 - 124
Terminal.Gui/Drawing/Glyphs.cs

@@ -1,6 +1,6 @@
 using static Terminal.Gui.ConfigurationManager;
 using System.Text.Json.Serialization;
-using Rune = System.Rune;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -24,369 +24,369 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Checked indicator (e.g. for <see cref="ListView"/> and <see cref="CheckBox"/>).
 		/// </summary>
-		public Rune Checked { get; set; } = '☑';
+		public Rune Checked { get; set; } = (Rune)'☑';
 
 		/// <summary>
 		/// Not Checked indicator (e.g. for <see cref="ListView"/> and <see cref="CheckBox"/>).
 		/// </summary>
-		public Rune UnChecked { get; set; } = '☐';
+		public Rune UnChecked { get; set; } = (Rune)'☐';
 
 		/// <summary>
 		/// Null Checked indicator (e.g. for <see cref="ListView"/> and <see cref="CheckBox"/>).
 		/// </summary>
-		public Rune NullChecked { get; set; } = '☒';
+		public Rune NullChecked { get; set; } = (Rune)'☒';
 
 		/// <summary>
 		/// Selected indicator  (e.g. for <see cref="ListView"/> and <see cref="RadioGroup"/>).
 		/// </summary>
-		public Rune Selected { get; set; } = '◉';
+		public Rune Selected { get; set; } = (Rune)'◉';
 
 		/// <summary>
 		/// Not Selected indicator (e.g. for <see cref="ListView"/> and <see cref="RadioGroup"/>).
 		/// </summary>
-		public Rune UnSelected { get; set; } = '○';
+		public Rune UnSelected { get; set; } = (Rune)'○';
 
 		/// <summary>
 		/// Horizontal arrow.
 		/// </summary>
-		public Rune RightArrow { get; set; } = '►';
+		public Rune RightArrow { get; set; } = (Rune)'►';
 
 		/// <summary>
 		/// Left arrow.
 		/// </summary>
-		public Rune LeftArrow { get; set; } = '◄';
+		public Rune LeftArrow { get; set; } = (Rune)'◄';
 
 		/// <summary>
 		/// Down arrow.
 		/// </summary>
-		public Rune DownArrow { get; set; } = '▼';
+		public Rune DownArrow { get; set; } = (Rune)'▼';
 
 		/// <summary>
 		/// Vertical arrow.
 		/// </summary>
-		public Rune UpArrow { get; set; } = '▲';
+		public Rune UpArrow { get; set; } = (Rune)'▲';
 
 		/// <summary>
 		/// Left default indicator (e.g. for <see cref="Button"/>.
 		/// </summary>
-		public Rune LeftDefaultIndicator { get; set; } = '►';
+		public Rune LeftDefaultIndicator { get; set; } = (Rune)'►';
 
 		/// <summary>
 		/// Horizontal default indicator (e.g. for <see cref="Button"/>.
 		/// </summary>
-		public Rune RightDefaultIndicator { get; set; } = '◄';
+		public Rune RightDefaultIndicator { get; set; } = (Rune)'◄';
 
 		/// <summary>
 		/// Left Bracket (e.g. for <see cref="Button"/>. Default is (U+005B) - [.
 		/// </summary>
-		public Rune LeftBracket { get; set; } = '⟦';
+		public Rune LeftBracket { get; set; } = (Rune)'⟦';
 
 		/// <summary>
 		/// Horizontal Bracket (e.g. for <see cref="Button"/>. Default is (U+005D) - ].
 		/// </summary>
-		public Rune RightBracket { get; set; } = '⟧';
+		public Rune RightBracket { get; set; } = (Rune)'⟧';
 
 		/// <summary>
 		/// Half block meter segment (e.g. for <see cref="ProgressBar"/>).
 		/// </summary>
-		public Rune BlocksMeterSegment { get; set; } = '▌';
+		public Rune BlocksMeterSegment { get; set; } = (Rune)'▌';
 
 		/// <summary>
 		/// Continuous block meter segment (e.g. for <see cref="ProgressBar"/>).
 		/// </summary>
-		public Rune ContinuousMeterSegment { get; set; } = '█';
+		public Rune ContinuousMeterSegment { get; set; } = (Rune)'█';
 
 		/// <summary>
 		/// Stipple pattern (e.g. for <see cref="ScrollBarView"/>). Default is Light Shade (U+2591) - ░.
 		/// </summary>
-		public Rune Stipple { get; set; } = '░';
+		public Rune Stipple { get; set; } = (Rune)'░';
 
 		/// <summary>
 		/// Diamond (e.g. for <see cref="ScrollBarView"/>. Default is Lozenge (U+25CA) - ◊.
 		/// </summary>
-		public Rune Diamond { get; set; } = '◊';
+		public Rune Diamond { get; set; } = (Rune)'◊';
 
 		/// <summary>
 		/// Close. Default is Heavy Ballot X (U+2718) - ✘.
 		/// </summary>
-		public Rune Close { get; set; } = '✘';
+		public Rune Close { get; set; } = (Rune)'✘';
 
 		/// <summary>
 		/// Minimize. Default is Lower Horizontal Shadowed White Circle (U+274F) - ❏.
 		/// </summary>
-		public Rune Minimize { get; set; } = '❏';
+		public Rune Minimize { get; set; } = (Rune)'❏';
 
 		/// <summary>
 		/// Maximize. Default is Upper Horizontal Shadowed White Circle (U+273D) - ✽.
 		/// </summary>
-		public Rune Maximize { get; set; } = '✽';
+		public Rune Maximize { get; set; } = (Rune)'✽';
 
 		/// <summary>
 		/// Dot. Default is (U+2219) - ∙.
 		/// </summary>
-		public Rune Dot { get; set; } = '∙';
+		public Rune Dot { get; set; } = (Rune)'∙';
 
 		/// <summary>
 		/// Expand (e.g. for <see cref="TreeView"/>.
 		/// </summary>
-		public Rune Expand { get; set; } = '+';
+		public Rune Expand { get; set; } = (Rune)'+';
 
 		/// <summary>
 		/// Expand (e.g. for <see cref="TreeView"/>.
 		/// </summary>
-		public Rune Collapse { get; set; } = '-';
+		public Rune Collapse { get; set; } = (Rune)'-';
 
 		/// <summary>
 		/// Apple. Because snek.
 		/// </summary>
-		public Rune Apple { get; set; } = '❦' ; // BUGBUG: "🍎"[0] should work, but doesn't 
+		public Rune Apple { get; set; } = (Rune)'❦' ; // BUGBUG: "🍎"[0] should work, but doesn't 
 
 		#endregion
 
 		/// <summary>
 		/// Folder icon.  Defaults to ꤉ (Kayah Li Digit Nine)
 		/// </summary>
-		public Rune Folder { get; set; } = '꤉';
+		public Rune Folder { get; set; } = (Rune)'꤉';
 
 		/// <summary>
 		/// File icon.  Defaults to ☰ (Trigram For Heaven)
 		/// </summary>
-		public Rune File { get; set; } = '☰';
+		public Rune File { get; set; } = (Rune)'☰';
 
 		#region ----------------- Lines -----------------
 		/// <summary>
 		/// Box Drawings Horizontal Line - Light (U+2500) - ─
 		/// </summary>
-		public Rune HLine { get; set; } = '─';
+		public Rune HLine { get; set; } = (Rune)'─';
 
 		/// <summary>
 		/// Box Drawings Vertical Line - Light (U+2502) - │
 		/// </summary>
-		public Rune VLine { get; set; } = '│';
+		public Rune VLine { get; set; } = (Rune)'│';
 
 		/// <summary>
 		/// Box Drawings Double Horizontal (U+2550) - ═
 		/// </summary>
-		public Rune HLineDbl { get; set; } = '═';
+		public Rune HLineDbl { get; set; } = (Rune)'═';
 
 		/// <summary>
 		/// Box Drawings Double Vertical (U+2551) - ║
 		/// </summary>
-		public Rune VLineDbl { get; set; } = '║';
+		public Rune VLineDbl { get; set; } = (Rune)'║';
 
 		/// <summary>
 		/// Box Drawings Heavy Double Dash Horizontal (U+254D) - ╍
 		/// </summary>
-		public Rune HLineHvDa2 { get; set; } = '╍';
+		public Rune HLineHvDa2 { get; set; } = (Rune)'╍';
 
 		/// <summary>
 		/// Box Drawings Heavy Triple Dash Vertical (U+2507) - ┇
 		/// </summary>
-		public Rune VLineHvDa3 { get; set; } = '┇';
+		public Rune VLineHvDa3 { get; set; } = (Rune)'┇';
 
 		/// <summary>
 		/// Box Drawings Heavy Triple Dash Horizontal (U+2505) - ┅
 		/// </summary>
-		public Rune HLineHvDa3 { get; set; } = '┅';
+		public Rune HLineHvDa3 { get; set; } = (Rune)'┅';
 
 		/// <summary>
 		/// Box Drawings Heavy Quadruple Dash Horizontal (U+2509) - ┉
 		/// </summary>
-		public Rune HLineHvDa4 { get; set; } = '┉';
+		public Rune HLineHvDa4 { get; set; } = (Rune)'┉';
 
 		/// <summary>
 		/// Box Drawings Heavy Double Dash Vertical (U+254F) - ╏
 		/// </summary>
-		public Rune VLineHvDa2 { get; set; } = '╏';
+		public Rune VLineHvDa2 { get; set; } = (Rune)'╏';
 
 		/// <summary>
 		/// Box Drawings Heavy Quadruple Dash Vertical (U+250B) - ┋
 		/// </summary>
-		public Rune VLineHvDa4 { get; set; } = '┋';
+		public Rune VLineHvDa4 { get; set; } = (Rune)'┋';
 
 		/// <summary>
 		/// Box Drawings Light Double Dash Horizontal (U+254C) - ╌
 		/// </summary>
-		public Rune HLineDa2 { get; set; } = '╌';
+		public Rune HLineDa2 { get; set; } = (Rune)'╌';
 
 		/// <summary>
 		/// Box Drawings Light Triple Dash Vertical (U+2506) - ┆
 		/// </summary>
-		public Rune VLineDa3 { get; set; } = '┆';
+		public Rune VLineDa3 { get; set; } = (Rune)'┆';
 
 		/// <summary>
 		/// Box Drawings Light Triple Dash Horizontal (U+2504) - ┄
 		/// </summary>
-		public Rune HLineDa3 { get; set; } = '┄';
+		public Rune HLineDa3 { get; set; } = (Rune)'┄';
 
 		/// <summary>
 		/// Box Drawings Light Quadruple Dash Horizontal (U+2508) - ┈
 		/// </summary>
-		public Rune HLineDa4 { get; set; } = '┈';
+		public Rune HLineDa4 { get; set; } = (Rune)'┈';
 
 		/// <summary>
 		/// Box Drawings Light Double Dash Vertical (U+254E) - ╎
 		/// </summary>
-		public Rune VLineDa2 { get; set; } = '╎';
+		public Rune VLineDa2 { get; set; } = (Rune)'╎';
 
 		/// <summary>
 		/// Box Drawings Light Quadruple Dash Vertical (U+250A) - ┊
 		/// </summary>
-		public Rune VLineDa4 { get; set; } = '┊';
+		public Rune VLineDa4 { get; set; } = (Rune)'┊';
 
 		/// <summary>
 		/// Box Drawings Heavy Horizontal (U+2501) - ━
 		/// </summary>
-		public Rune HLineHv { get; set; } = '━';
+		public Rune HLineHv { get; set; } = (Rune)'━';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical (U+2503) - ┃
 		/// </summary>
-		public Rune VLineHv { get; set; } = '┃';
+		public Rune VLineHv { get; set; } = (Rune)'┃';
 
 		/// <summary>
 		/// Box Drawings Light Left (U+2574) - ╴
 		/// </summary>
-		public Rune HalfLeftLine { get; set; } = '╴';
+		public Rune HalfLeftLine { get; set; } = (Rune)'╴';
 
 		/// <summary>
 		/// Box Drawings Light Vertical (U+2575) - ╵
 		/// </summary>
-		public Rune HalfTopLine { get; set; } = '╵';
+		public Rune HalfTopLine { get; set; } = (Rune)'╵';
 
 		/// <summary>
 		/// Box Drawings Light Horizontal (U+2576) - ╶
 		/// </summary>
-		public Rune HalfRightLine { get; set; } = '╶';
+		public Rune HalfRightLine { get; set; } = (Rune)'╶';
 
 		/// <summary>
 		/// Box Drawings Light Down (U+2577) - ╷
 		/// </summary>
-		public Rune HalfBottomLine { get; set; } = '╷';
+		public Rune HalfBottomLine { get; set; } = (Rune)'╷';
 
 		/// <summary>
 		/// Box Drawings Heavy Left (U+2578) - ╸
 		/// </summary>
-		public Rune HalfLeftLineHv { get; set; } = '╸';
+		public Rune HalfLeftLineHv { get; set; } = (Rune)'╸';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical (U+2579) - ╹
 		/// </summary>
-		public Rune HalfTopLineHv { get; set; } = '╹';
+		public Rune HalfTopLineHv { get; set; } = (Rune)'╹';
 
 		/// <summary>
 		/// Box Drawings Heavy Horizontal (U+257A) - ╺
 		/// </summary>
-		public Rune HalfRightLineHv { get; set; } = '╺';
+		public Rune HalfRightLineHv { get; set; } = (Rune)'╺';
 
 		/// <summary>
 		/// Box Drawings Light Vertical and Horizontal (U+257B) - ╻
 		/// </summary>
-		public Rune HalfBottomLineLt { get; set; } = '╻';
+		public Rune HalfBottomLineLt { get; set; } = (Rune)'╻';
 
 		/// <summary>
 		/// Box Drawings Light Horizontal and Heavy Horizontal (U+257C) - ╼
 		/// </summary>
-		public Rune RightSideLineLtHv { get; set; } = '╼';
+		public Rune RightSideLineLtHv { get; set; } = (Rune)'╼';
 
 		/// <summary>
 		/// Box Drawings Light Vertical and Heavy Horizontal (U+257D) - ╽
 		/// </summary>
-		public Rune BottomSideLineLtHv { get; set; } = '╽';
+		public Rune BottomSideLineLtHv { get; set; } = (Rune)'╽';
 
 		/// <summary>
 		/// Box Drawings Heavy Left and Light Horizontal (U+257E) - ╾
 		/// </summary>
-		public Rune LeftSideLineHvLt { get; set; } = '╾';
+		public Rune LeftSideLineHvLt { get; set; } = (Rune)'╾';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical and Light Horizontal (U+257F) - ╿
 		/// </summary>
-		public Rune TopSideLineHvLt { get; set; } = '╿';
+		public Rune TopSideLineHvLt { get; set; } = (Rune)'╿';
 		#endregion
 
 		#region ----------------- Upper Left Corners -----------------
 		/// <summary>
 		/// Box Drawings Upper Left Corner - Light Vertical and Light Horizontal (U+250C) - ┌
 		/// </summary>
-		public Rune ULCorner { get; set; } = '┌';
+		public Rune ULCorner { get; set; } = (Rune)'┌';
 
 		/// <summary>
 		/// Box Drawings Upper Left Corner -  Double (U+2554) - ╔
 		/// </summary>
-		public Rune ULCornerDbl { get; set; } = '╔';
+		public Rune ULCornerDbl { get; set; } = (Rune)'╔';
 
 		/// <summary>
 		/// Box Drawings Upper Left Corner - Light Arc Down and Horizontal (U+256D) - ╭
 		/// </summary>
-		public Rune ULCornerR { get; set; } = '╭';
+		public Rune ULCornerR { get; set; } = (Rune)'╭';
 
 		/// <summary>
 		/// Box Drawings Heavy Down and Horizontal (U+250F) - ┏
 		/// </summary>
-		public Rune ULCornerHv { get; set; } = '┏';
+		public Rune ULCornerHv { get; set; } = (Rune)'┏';
 
 		/// <summary>
 		/// Box Drawings Down Heavy and Horizontal Light (U+251E) - ┎
 		/// </summary>
-		public Rune ULCornerHvLt { get; set; } = '┎';
+		public Rune ULCornerHvLt { get; set; } = (Rune)'┎';
 
 		/// <summary>
 		/// Box Drawings Down Light and Horizontal Heavy (U+250D) - ┎
 		/// </summary>
-		public Rune ULCornerLtHv { get; set; } = '┍';
+		public Rune ULCornerLtHv { get; set; } = (Rune)'┍';
 
 		/// <summary>
 		/// Box Drawings Double Down and Single Horizontal (U+2553) - ╓
 		/// </summary>
-		public Rune ULCornerDblSingle { get; set; } = '╓';
+		public Rune ULCornerDblSingle { get; set; } = (Rune)'╓';
 
 		/// <summary>
 		/// Box Drawings Single Down and Double Horizontal (U+2552) - ╒
 		/// </summary>
-		public Rune ULCornerSingleDbl { get; set; } = '╒';
+		public Rune ULCornerSingleDbl { get; set; } = (Rune)'╒';
 		#endregion
 
 		#region ----------------- Lower Left Corners -----------------
 		/// <summary>
 		/// Box Drawings Lower Left Corner - Light Vertical and Light Horizontal (U+2514) - └
 		/// </summary>
-		public Rune LLCorner { get; set; } = '└';
+		public Rune LLCorner { get; set; } = (Rune)'└';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical and Horizontal (U+2517) - ┗
 		/// </summary>
-		public Rune LLCornerHv { get; set; } = '┗';
+		public Rune LLCornerHv { get; set; } = (Rune)'┗';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical and Horizontal Light (U+2516) - ┖
 		/// </summary>
-		public Rune LLCornerHvLt { get; set; } = '┖';
+		public Rune LLCornerHvLt { get; set; } = (Rune)'┖';
 
 		/// <summary>
 		/// Box Drawings Vertical Light and Horizontal Heavy (U+2511) - ┕
 		/// </summary>
-		public Rune LLCornerLtHv { get; set; } = '┕';
+		public Rune LLCornerLtHv { get; set; } = (Rune)'┕';
 
 		/// <summary>
 		/// Box Drawings Double Vertical and Double Left (U+255A) - ╚
 		/// </summary>
-		public Rune LLCornerDbl { get; set; } = '╚';
+		public Rune LLCornerDbl { get; set; } = (Rune)'╚';
 
 		/// <summary>
 		/// Box Drawings Single Vertical and Double Left (U+2558) - ╘
 		/// </summary>
-		public Rune LLCornerSingleDbl { get; set; } = '╘';
+		public Rune LLCornerSingleDbl { get; set; } = (Rune)'╘';
 
 		/// <summary>
 		/// Box Drawings Double Down and Single Left (U+2559) - ╙
 		/// </summary>
-		public Rune LLCornerDblSingle { get; set; } = '╙';
+		public Rune LLCornerDblSingle { get; set; } = (Rune)'╙';
 
 		/// <summary>
 		/// Box Drawings Upper Left Corner - Light Arc Down and Left (U+2570) - ╰
 		/// </summary>
-		public Rune LLCornerR { get; set; } = '╰';
+		public Rune LLCornerR { get; set; } = (Rune)'╰';
 
 		#endregion
 
@@ -394,226 +394,226 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Box Drawings Upper Horizontal Corner - Light Vertical and Light Horizontal (U+2510) - ┐
 		/// </summary>
-		public Rune URCorner { get; set; } = '┐';
+		public Rune URCorner { get; set; } = (Rune)'┐';
 
 		/// <summary>
 		/// Box Drawings Upper Horizontal Corner - Double Vertical and Double Horizontal (U+2557) - ╗
 		/// </summary>
-		public Rune URCornerDbl { get; set; } = '╗';
+		public Rune URCornerDbl { get; set; } = (Rune)'╗';
 
 		/// <summary>
 		/// Box Drawings Upper Horizontal Corner - Light Arc Vertical and Horizontal (U+256E) - ╮
 		/// </summary>
-		public Rune URCornerR { get; set; } = '╮';
+		public Rune URCornerR { get; set; } = (Rune)'╮';
 
 		/// <summary>
 		/// Box Drawings Heavy Down and Left (U+2513) - ┓
 		/// </summary>
-		public Rune URCornerHv { get; set; } = '┓';
+		public Rune URCornerHv { get; set; } = (Rune)'┓';
 
 		/// <summary>
 		/// Box Drawings Heavy Vertical and Left Down Light (U+2511) - ┑
 		/// </summary>
-		public Rune URCornerHvLt { get; set; } = '┑';
+		public Rune URCornerHvLt { get; set; } = (Rune)'┑';
 
 		/// <summary>
 		/// Box Drawings Down Light and Horizontal Heavy (U+2514) - ┒
 		/// </summary>
-		public Rune URCornerLtHv { get; set; } = '┒';
+		public Rune URCornerLtHv { get; set; } = (Rune)'┒';
 
 		/// <summary>
 		/// Box Drawings Double Vertical and Single Left (U+2556) - ╖
 		/// </summary>
-		public Rune URCornerDblSingle { get; set; } = '╖';
+		public Rune URCornerDblSingle { get; set; } = (Rune)'╖';
 
 		/// <summary>
 		/// Box Drawings Single Vertical and Double Left (U+2555) - ╕
 		/// </summary>
-		public Rune URCornerSingleDbl { get; set; } = '╕';
+		public Rune URCornerSingleDbl { get; set; } = (Rune)'╕';
 		#endregion
 
 		#region ----------------- Lower Right Corners -----------------
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Light (U+2518) - ┘
 		/// </summary>
-		public Rune LRCorner { get; set; } = '┘';
+		public Rune LRCorner { get; set; } = (Rune)'┘';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Double (U+255D) - ╝
 		/// </summary>
-		public Rune LRCornerDbl { get; set; } = '╝';
+		public Rune LRCornerDbl { get; set; } = (Rune)'╝';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Rounded (U+256F) - ╯
 		/// </summary>
-		public Rune LRCornerR { get; set; } = '╯';
+		public Rune LRCornerR { get; set; } = (Rune)'╯';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Heavy (U+251B) - ┛
 		/// </summary>
-		public Rune LRCornerHv { get; set; } = '┛';
+		public Rune LRCornerHv { get; set; } = (Rune)'┛';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Double Vertical and Single Horizontal (U+255C) - ╜
 		/// </summary>
-		public Rune LRCornerDblSingle { get; set; } = '╜';
+		public Rune LRCornerDblSingle { get; set; } = (Rune)'╜';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Single Vertical and Double Horizontal (U+255B) - ╛
 		/// </summary>
-		public Rune LRCornerSingleDbl { get; set; } = '╛';
+		public Rune LRCornerSingleDbl { get; set; } = (Rune)'╛';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Light Vertical and Heavy Horizontal (U+2519) - ┙
 		/// </summary>
-		public Rune LRCornerLtHv { get; set; } = '┙';
+		public Rune LRCornerLtHv { get; set; } = (Rune)'┙';
 
 		/// <summary>
 		/// Box Drawings Lower Right Corner - Heavy Vertical and Light Horizontal (U+251A) - ┚
 		/// </summary>
-		public Rune LRCornerHvLt { get; set; } = '┚';
+		public Rune LRCornerHvLt { get; set; } = (Rune)'┚';
 		#endregion
 
 		#region ----------------- Tees -----------------
 		/// <summary>
 		/// Box Drawings Left Tee - Single Vertical and Single Horizontal (U+251C) - ├
 		/// </summary>
-		public Rune LeftTee { get; set; } = '├';
+		public Rune LeftTee { get; set; } = (Rune)'├';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Single Vertical and Double Horizontal (U+255E) - ╞
 		/// </summary>
-		public Rune LeftTeeDblH { get; set; } = '╞';
+		public Rune LeftTeeDblH { get; set; } = (Rune)'╞';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Double Vertical and Single Horizontal (U+255F) - ╟
 		/// </summary>
-		public Rune LeftTeeDblV { get; set; } = '╟';
+		public Rune LeftTeeDblV { get; set; } = (Rune)'╟';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Double Vertical and Double Horizontal (U+2560) - ╠
 		/// </summary>
-		public Rune LeftTeeDbl { get; set; } = '╠';
+		public Rune LeftTeeDbl { get; set; } = (Rune)'╠';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Heavy Horizontal and Light Vertical (U+2523) - ┝
 		/// </summary>
-		public Rune LeftTeeHvH { get; set; } = '┝';
+		public Rune LeftTeeHvH { get; set; } = (Rune)'┝';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Light Horizontal and Heavy Vertical (U+252B) - ┠
 		/// </summary>
-		public Rune LeftTeeHvV { get; set; } = '┠';
+		public Rune LeftTeeHvV { get; set; } = (Rune)'┠';
 
 		/// <summary>
 		/// Box Drawings Left Tee - Heavy Vertical and Heavy Horizontal (U+2527) - ┣
 		/// </summary>
-		public Rune LeftTeeHvDblH { get; set; } = '┣';
+		public Rune LeftTeeHvDblH { get; set; } = (Rune)'┣';
 
 		/// <summary>
 		/// Box Drawings Righ Tee - Single Vertical and Single Horizontal (U+2524) - ┤
 		/// </summary>
-		public Rune RightTee { get; set; } = '┤';
+		public Rune RightTee { get; set; } = (Rune)'┤';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Single Vertical and Double Horizontal (U+2561) - ╡
 		/// </summary>
-		public Rune RightTeeDblH { get; set; } = '╡';
+		public Rune RightTeeDblH { get; set; } = (Rune)'╡';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Double Vertical and Single Horizontal (U+2562) - ╢
 		/// </summary>
-		public Rune RightTeeDblV { get; set; } = '╢';
+		public Rune RightTeeDblV { get; set; } = (Rune)'╢';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Double Vertical and Double Horizontal (U+2563) - ╣
 		/// </summary>
-		public Rune RightTeeDbl { get; set; } = '╣';
+		public Rune RightTeeDbl { get; set; } = (Rune)'╣';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Heavy Horizontal and Light Vertical (U+2528) - ┥
 		/// </summary>
-		public Rune RightTeeHvH { get; set; } = '┥';
+		public Rune RightTeeHvH { get; set; } = (Rune)'┥';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Light Horizontal and Heavy Vertical (U+2530) - ┨
 		/// </summary>
-		public Rune RightTeeHvV { get; set; } = '┨';
+		public Rune RightTeeHvV { get; set; } = (Rune)'┨';
 
 		/// <summary>
 		/// Box Drawings Right Tee - Heavy Vertical and Heavy Horizontal (U+252C) - ┫
 		/// </summary>
-		public Rune RightTeeHvDblH { get; set; } = '┫';
+		public Rune RightTeeHvDblH { get; set; } = (Rune)'┫';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Single Vertical and Single Horizontal (U+252C) - ┬
 		/// </summary>
-		public Rune TopTee { get; set; } = '┬';
+		public Rune TopTee { get; set; } = (Rune)'┬';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Single Vertical and Double Horizontal (U+2564) - ╤
 		/// </summary>
-		public Rune TopTeeDblH { get; set; } = '╤';
+		public Rune TopTeeDblH { get; set; } = (Rune)'╤';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Double Vertical and Single Horizontal  (U+2565) - ╥
 		/// </summary>
-		public Rune TopTeeDblV { get; set; } = '╥';
+		public Rune TopTeeDblV { get; set; } = (Rune)'╥';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Double Vertical and Double Horizontal (U+2566) - ╦
 		/// </summary>
-		public Rune TopTeeDbl { get; set; } = '╦';
+		public Rune TopTeeDbl { get; set; } = (Rune)'╦';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Heavy Horizontal and Light Vertical (U+252F) - ┯
 		/// </summary>
-		public Rune TopTeeHvH { get; set; } = '┯';
+		public Rune TopTeeHvH { get; set; } = (Rune)'┯';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Light Horizontal and Heavy Vertical (U+2537) - ┰
 		/// </summary>
-		public Rune TopTeeHvV { get; set; } = '┰';
+		public Rune TopTeeHvV { get; set; } = (Rune)'┰';
 
 		/// <summary>
 		/// Box Drawings Top Tee - Heavy Vertical and Heavy Horizontal (U+2533) - ┳
 		/// </summary>
-		public Rune TopTeeHvDblH { get; set; } = '┳';
+		public Rune TopTeeHvDblH { get; set; } = (Rune)'┳';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Single Vertical and Single Horizontal (U+2534) - ┴
 		/// </summary>
-		public Rune BottomTee { get; set; } = '┴';
+		public Rune BottomTee { get; set; } = (Rune)'┴';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Single Vertical and Double Horizontal (U+2567) - ╧
 		/// </summary>
-		public Rune BottomTeeDblH { get; set; } = '╧';
+		public Rune BottomTeeDblH { get; set; } = (Rune)'╧';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Double Vertical and Single Horizontal (U+2568) - ╨
 		/// </summary>
-		public Rune BottomTeeDblV { get; set; } = '╨';
+		public Rune BottomTeeDblV { get; set; } = (Rune)'╨';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Double Vertical and Double Horizontal (U+2569) - ╩
 		/// </summary>
-		public Rune BottomTeeDbl { get; set; } = '╩';
+		public Rune BottomTeeDbl { get; set; } = (Rune)'╩';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Heavy Horizontal and Light Vertical (U+2535) - ┷
 		/// </summary>
-		public Rune BottomTeeHvH { get; set; } = '┷';
+		public Rune BottomTeeHvH { get; set; } = (Rune)'┷';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Light Horizontal and Heavy Vertical (U+253D) - ┸
 		/// </summary>
-		public Rune BottomTeeHvV { get; set; } = '┸';
+		public Rune BottomTeeHvV { get; set; } = (Rune)'┸';
 
 		/// <summary>
 		/// Box Drawings Bottom Tee - Heavy Vertical and Heavy Horizontal (U+2539) - ┻
 		/// </summary>
-		public Rune BottomTeeHvDblH { get; set; } = '┻';
+		public Rune BottomTeeHvDblH { get; set; } = (Rune)'┻';
 
 		#endregion
 
@@ -621,37 +621,37 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Box Drawings Cross - Single Vertical and Single Horizontal (U+253C) - ┼
 		/// </summary>
-		public Rune Cross { get; set; } = '┼';
+		public Rune Cross { get; set; } = (Rune)'┼';
 
 		/// <summary>
 		/// Box Drawings Cross - Single Vertical and Double Horizontal (U+256A) - ╪
 		/// </summary>
-		public Rune CrossDblH { get; set; } = '╪';
+		public Rune CrossDblH { get; set; } = (Rune)'╪';
 
 		/// <summary>
 		/// Box Drawings Cross - Double Vertical and Single Horizontal (U+256B) - ╫
 		/// </summary>
-		public Rune CrossDblV { get; set; } = '╫';
+		public Rune CrossDblV { get; set; } = (Rune)'╫';
 
 		/// <summary>
 		/// Box Drawings Cross - Double Vertical and Double Horizontal (U+256C) - ╬
 		/// </summary>
-		public Rune CrossDbl { get; set; } = '╬';
+		public Rune CrossDbl { get; set; } = (Rune)'╬';
 
 		/// <summary>
 		/// Box Drawings Cross - Heavy Horizontal and Light Vertical (U+253F) - ┿
 		/// </summary>
-		public Rune CrossHvH { get; set; } = '┿';
+		public Rune CrossHvH { get; set; } = (Rune)'┿';
 
 		/// <summary>
 		/// Box Drawings Cross - Light Horizontal and Heavy Vertical (U+2541) - ╂
 		/// </summary>
-		public Rune CrossHvV { get; set; } = '╂';
+		public Rune CrossHvV { get; set; } = (Rune)'╂';
 
 		/// <summary>
 		/// Box Drawings Cross - Heavy Vertical and Heavy Horizontal (U+254B) - ╋
 		/// </summary>
-		public Rune CrossHv { get; set; } = '╋';
+		public Rune CrossHv { get; set; } = (Rune)'╋';
 		#endregion
 	}
 }

+ 1 - 1
Terminal.Gui/Drawing/LineCanvas.cs

@@ -3,7 +3,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
-using Rune = System.Rune;
+
 
 namespace Terminal.Gui {
 

+ 2 - 3
Terminal.Gui/Drawing/Ruler.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Data;
 using System.Text;
@@ -58,7 +57,7 @@ namespace Terminal.Gui {
 				var vrule = _vTemplate.Repeat ((int)Math.Ceiling ((double)(Length + 2) / (double)_vTemplate.Length)) [start..(Length + start)];
 				for (var r = location.Y; r < location.Y + Length; r++) {
 					Application.Driver.Move (location.X, r);
-					Application.Driver.AddRune (vrule [r - location.Y]);
+					Application.Driver.AddRune ((Rune)vrule [r - location.Y]);
 				}
 			}
 		}

+ 11 - 12
Terminal.Gui/Drawing/Thickness.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Text;
 using System.Text.Json.Serialization;
@@ -143,19 +142,19 @@ namespace Terminal.Gui {
 				return Rect.Empty;
 			}
 
-			System.Rune clearChar = ' ';
-			System.Rune leftChar = clearChar;
-			System.Rune rightChar = clearChar;
-			System.Rune topChar = clearChar;
-			System.Rune bottomChar = clearChar;
+			Rune clearChar = (Rune)' ';
+			Rune leftChar = clearChar;
+			Rune rightChar = clearChar;
+			Rune topChar = clearChar;
+			Rune bottomChar = clearChar;
 
 			if ((ConsoleDriver.Diagnostics & ConsoleDriver.DiagnosticFlags.FramePadding) == ConsoleDriver.DiagnosticFlags.FramePadding) {
-				leftChar = 'L';
-				rightChar = 'R';
-				topChar = 'T';
-				bottomChar = 'B';
+				leftChar = (Rune)'L';
+				rightChar = (Rune)'R';
+				topChar = (Rune)'T';
+				bottomChar = (Rune)'B';
 				if (!string.IsNullOrEmpty (label)) {
-					leftChar = rightChar = bottomChar = topChar = label [0];
+					leftChar = rightChar = bottomChar = topChar = (Rune)label [0];
 				}
 			}
 

+ 13 - 14
Terminal.Gui/Input/ShortcutHelper.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
@@ -27,7 +26,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The keystroke combination used in the <see cref="Shortcut"/> as string.
 		/// </summary>
-		public virtual ustring ShortcutTag => GetShortcutTag (shortcut);
+		public virtual string ShortcutTag => GetShortcutTag (shortcut);
 
 		/// <summary>
 		/// The action to run if the <see cref="Shortcut"/> is defined.
@@ -61,7 +60,7 @@ namespace Terminal.Gui {
 		/// <param name="shortcut">The shortcut key.</param>
 		/// <param name="delimiter">The delimiter string.</param>
 		/// <returns></returns>
-		public static ustring GetShortcutTag (Key shortcut, ustring delimiter = null)
+		public static string GetShortcutTag (Key shortcut, string delimiter = null)
 		{
 			if (shortcut == Key.Null) {
 				return "";
@@ -71,7 +70,7 @@ namespace Terminal.Gui {
 			if (delimiter == null) {
 				delimiter = MenuBar.ShortcutDelimiter;
 			}
-			ustring tag = ustring.Empty;
+			string tag = string.Empty;
 			var sCut = GetKeyToString (k, out Key knm).ToString ();
 			if (knm == Key.Unknown) {
 				k &= ~Key.Unknown;
@@ -81,25 +80,25 @@ namespace Terminal.Gui {
 				tag = "Ctrl";
 			}
 			if ((k & Key.ShiftMask) != 0) {
-				if (!tag.IsEmpty) {
+				if (!string.IsNullOrEmpty(tag)) {
 					tag += delimiter;
 				}
 				tag += "Shift";
 			}
 			if ((k & Key.AltMask) != 0) {
-				if (!tag.IsEmpty) {
+				if (!string.IsNullOrEmpty(tag)) {
 					tag += delimiter;
 				}
 				tag += "Alt";
 			}
 
-			ustring [] keys = ustring.Make (sCut).Split (",");
+			string [] keys = sCut.Split (",");
 			for (int i = 0; i < keys.Length; i++) {
-				var key = keys [i].TrimSpace ();
+				var key = keys [i].Trim ();
 				if (key == Key.AltMask.ToString () || key == Key.ShiftMask.ToString () || key == Key.CtrlMask.ToString ()) {
 					continue;
 				}
-				if (!tag.IsEmpty) {
+				if (!string.IsNullOrEmpty(tag)) {
 					tag += delimiter;
 				}
 				if (!key.Contains ("F") && key.Length > 2 && keys.Length == 1) {
@@ -120,7 +119,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="key">The key to extract.</param>
 		/// <param name="knm">Correspond to the non modifier key.</param>
-		public static ustring GetKeyToString (Key key, out Key knm)
+		public static string GetKeyToString (Key key, out Key knm)
 		{
 			if (key == Key.Null) {
 				knm = Key.Null;
@@ -150,10 +149,10 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="tag">The key as string.</param>
 		/// <param name="delimiter">The delimiter string.</param>
-		public static Key GetShortcutFromTag (ustring tag, ustring delimiter = null)
+		public static Key GetShortcutFromTag (string tag, string delimiter = null)
 		{
 			var sCut = tag;
-			if (sCut.IsEmpty) {
+			if (string.IsNullOrEmpty(sCut)) {
 				return default;
 			}
 
@@ -163,7 +162,7 @@ namespace Terminal.Gui {
 				delimiter = MenuBar.ShortcutDelimiter;
 			}
 
-			ustring [] keys = sCut.Split (delimiter);
+			string [] keys = sCut.Split (delimiter);
 			for (int i = 0; i < keys.Length; i++) {
 				var k = keys [i];
 				if (k == "Ctrl") {

+ 0 - 1
Terminal.Gui/Resources/config.json

@@ -35,7 +35,6 @@
       "Ctrl"
     ]
   },
-  "Application.UseSystemConsole": false,
   "Application.IsMouseDisabled": false,
   "Theme": "Default",
   "Themes": [

+ 0 - 32
Terminal.Gui/StringExtensions.cs

@@ -1,32 +0,0 @@
-using System.Text;
-
-namespace Terminal.Gui {
-	/// <summary>
-	/// Extension helper of <see cref="System.String"/> to work with specific text manipulation./>
-	/// </summary>
-	public static class StringExtensions {
-		/// <summary>
-		/// Repeats the <paramref name="instr"/> <paramref name="n"/>  times.
-		/// </summary>
-		/// <param name="instr">The text to repeat.</param>
-		/// <param name="n">Number of times to repeat the text.</param>
-		/// <returns>
-		///  The text repeated if <paramref name="n"/> is greater than zero, 
-		///  otherwise <see langword="null"/>.
-		/// </returns>
-		public static string Repeat (this string instr, int n)
-		{
-			if (n <= 0) {
-				return null;
-			}
-
-			if (string.IsNullOrEmpty (instr) || n == 1) {
-				return instr;
-			}
-
-			return new StringBuilder (instr.Length * n)
-				.Insert (0, instr, n)
-				.ToString ();
-		}
-	}
-}

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

@@ -26,7 +26,6 @@
     <PackageReference Include="Microsoft.SourceLink.GitHub" Version="1.1.1" PrivateAssets="All" />
     <PackageReference Include="Microsoft.DotNet.PlatformAbstractions" Version="3.1.6" />
     <PackageReference Include="Microsoft.NETFramework.ReferenceAssemblies" Version="1.0.3" PrivateAssets="all" />
-    <PackageReference Include="NStack.Core" Version="1.0.7" />
     <PackageReference Include="System.IO.Abstractions" Version="19.2.4" />
     <PackageReference Include="System.Text.Json" Version="7.0.1" />
     <PackageReference Include="System.Management" Version="7.0.0" />

+ 18 - 22
Terminal.Gui/Text/Autocomplete/AppendAutocomplete.cs

@@ -1,6 +1,7 @@
 using System;
 using System.IO;
 using System.Linq;
+using System.Text;
 
 namespace Terminal.Gui {
 
@@ -30,12 +31,12 @@ namespace Terminal.Gui {
 			this.textField = textField;
 			SelectionKey = Key.Tab;
 
-			ColorScheme = new ColorScheme{
-				Normal = new Attribute(Color.DarkGray,0),
-				Focus = new Attribute(Color.DarkGray,0),
-				HotNormal = new Attribute(Color.DarkGray,0),
-				HotFocus = new Attribute(Color.DarkGray,0),
-				Disabled = new Attribute(Color.DarkGray,0),
+			ColorScheme = new ColorScheme {
+				Normal = new Attribute (Color.DarkGray, 0),
+				Focus = new Attribute (Color.DarkGray, 0),
+				HotNormal = new Attribute (Color.DarkGray, 0),
+				HotFocus = new Attribute (Color.DarkGray, 0),
+				Disabled = new Attribute (Color.DarkGray, 0),
 			};
 		}
 
@@ -64,16 +65,13 @@ namespace Terminal.Gui {
 			} else
 			if (key == Key.CursorDown) {
 				return this.CycleSuggestion (-1);
-			}
-			else if(key == CloseKey && Suggestions.Any())
-			{
-				ClearSuggestions();
+			} else if (key == CloseKey && Suggestions.Any ()) {
+				ClearSuggestions ();
 				_suspendSuggestions = true;
 				return true;
 			}
 
-			if(char.IsLetterOrDigit((char)kb.KeyValue))
-			{
+			if (char.IsLetterOrDigit ((char)kb.KeyValue)) {
 				_suspendSuggestions = false;
 			}
 
@@ -84,8 +82,7 @@ namespace Terminal.Gui {
 		/// <inheritdoc/>
 		public override void GenerateSuggestions (AutocompleteContext context)
 		{
-			if(_suspendSuggestions)
-			{
+			if (_suspendSuggestions) {
 				return;
 			}
 			base.GenerateSuggestions (context);
@@ -107,14 +104,13 @@ namespace Terminal.Gui {
 			var suggestion = this.Suggestions.ElementAt (this.SelectedIdx);
 			var fragment = suggestion.Replacement.Substring (suggestion.Remove);
 
-			int spaceAvailable = textField.Bounds.Width - textField.Text.ConsoleWidth;
-			int spaceRequired = fragment.Sum(c=>Rune.ColumnWidth(c));
+			int spaceAvailable = textField.Bounds.Width - textField.Text.GetColumns ();
+			int spaceRequired = fragment.EnumerateRunes ().Sum (c => c.GetColumns ());
 
-			if(spaceAvailable < spaceRequired)
-			{
-				fragment = new string(
-					fragment.TakeWhile(c=> (spaceAvailable -= Rune.ColumnWidth(c)) >= 0)
-					.ToArray()
+			if (spaceAvailable < spaceRequired) {
+				fragment = new string (
+					fragment.TakeWhile (c => (spaceAvailable -= ((Rune)c).GetColumns ()) >= 0)
+					.ToArray ()
 				);
 			}
 
@@ -137,7 +133,7 @@ namespace Terminal.Gui {
 				newText += insert.Replacement;
 				textField.Text = newText;
 
-				this.textField.MoveEnd();
+				this.textField.MoveEnd ();
 
 				this.ClearSuggestions ();
 				return true;

+ 1 - 1
Terminal.Gui/Text/Autocomplete/AutocompleteContext.cs

@@ -1,5 +1,5 @@
 using System.Collections.Generic;
-using Rune = System.Rune;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 1 - 1
Terminal.Gui/Text/Autocomplete/IAutocomplete.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
-using Rune = System.Rune;
+
 
 namespace Terminal.Gui {
 

+ 1 - 1
Terminal.Gui/Text/Autocomplete/ISuggestionGenerator.cs

@@ -1,5 +1,5 @@
 using System.Collections.Generic;
-using Rune = System.Rune;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 2 - 2
Terminal.Gui/Text/Autocomplete/PopupAutocomplete.cs

@@ -3,7 +3,7 @@ using System.Collections.Generic;
 using System.Collections.ObjectModel;
 using System.Linq;
 using System.Text;
-using Rune = System.Rune;
+
 
 namespace Terminal.Gui {
 
@@ -275,7 +275,7 @@ namespace Terminal.Gui {
 		/// <returns><c>true</c>if the key can be handled <c>false</c>otherwise.</returns>
 		public override bool ProcessKey (KeyEvent kb)
 		{
-			if (SuggestionGenerator.IsWordChar ((char)kb.Key)) {
+			if (SuggestionGenerator.IsWordChar ((Rune)(char)kb.Key)) {
 				Visible = true;
 				ManipulatePopup ();
 				closed = false;

+ 3 - 3
Terminal.Gui/Text/Autocomplete/SingleWordSuggestionGenerator.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
-using Rune = System.Rune;
+
 
 namespace Terminal.Gui {
 	
@@ -50,7 +50,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public virtual bool IsWordChar (Rune rune)
 		{
-			return Char.IsLetterOrDigit ((char)rune);
+			return Char.IsLetterOrDigit ((char)rune.Value);
 		}
 
 		/// <summary>
@@ -92,7 +92,7 @@ namespace Terminal.Gui {
 			// we are at the end of a word. Work out what has been typed so far
 			while (endIdx-- > 0) {
 				if (IsWordChar (line [endIdx])) {
-					sb.Insert (0, (char)line [endIdx]);
+					sb.Insert (0, (char)line [endIdx].Value);
 				} else {
 					break;
 				}

+ 311 - 0
Terminal.Gui/Text/RuneExtensions.cs

@@ -0,0 +1,311 @@
+using System.Globalization;
+using System.Text;
+
+namespace Terminal.Gui;
+
+/// <summary>
+/// Extends <see cref="System.Text.Rune"/> to support TUI text manipulation.
+/// </summary>
+public static class RuneExtensions {
+	/// <summary>
+	/// Maximum Unicode code point.
+	/// </summary>
+	public static int MaxUnicodeCodePoint = 0x10FFFF;
+
+	/// <summary>
+	/// Gets the number of columns the rune occupies in the terminal.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune">The rune to measure.</param>
+	/// <returns>
+	/// The number of columns required to fit the rune, 0 if the argument is the null character, or
+	/// -1 if the value is not printable, 
+	/// otherwise the number of columns that the rune occupies.
+	/// </returns>
+	public static int GetColumns (this Rune rune)
+	{
+		// TODO: I believe there is a way to do this without using our own tables, using Rune.
+		var codePoint = rune.Value;
+		switch (codePoint) {
+		case < 0x20:
+		case >= 0x7f and < 0xa0:
+			return -1;
+		case < 0x7f:
+			return 1;
+		}
+		/* binary search in table of non-spacing characters */
+		if (BiSearch (codePoint, _combining, _combining.GetLength (0) - 1) != 0) {
+			return 0;
+		}
+		/* if we arrive here, ucs is not a combining or C0/C1 control character */
+		return 1 + (BiSearch (codePoint, _combiningWideChars, _combiningWideChars.GetLength (0) - 1) != 0 ? 1 : 0);
+	}
+	
+	/// <summary>
+	/// Returns <see langword="true"/> if the rune is a combining character.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune"></param>
+	/// <returns></returns>
+	public static bool IsCombiningMark (this System.Text.Rune rune)
+	{
+		UnicodeCategory category = Rune.GetUnicodeCategory (rune);
+		return Rune.GetUnicodeCategory (rune) == UnicodeCategory.NonSpacingMark
+			|| category == UnicodeCategory.SpacingCombiningMark
+			|| category == UnicodeCategory.EnclosingMark;
+	}
+
+	/// <summary>
+	/// Ensures the rune is not a control character and can be displayed by translating characters below 0x20
+	/// to equivalent, printable, Unicode chars.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune"></param>
+	/// <returns></returns>
+	public static Rune MakePrintable (this System.Text.Rune rune) => Rune.IsControl (rune) ? new Rune (rune.Value + 0x2400) : rune;
+
+	/// <summary>
+	/// Get number of bytes required to encode the rune, based on the provided encoding.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune">The rune to probe.</param>
+	/// <param name="encoding">The encoding used; the default is UTF8.</param>
+	/// <returns>The number of bytes required.</returns>
+	public static int GetEncodingLength (this Rune rune, Encoding encoding = null)
+	{
+		encoding ??= Encoding.UTF8;
+		var bytes = encoding.GetBytes (rune.ToString ().ToCharArray ());
+		var offset = 0;
+		if (bytes [^1] == 0) {
+			offset++;
+		}
+		return bytes.Length - offset;
+	}
+
+	/// <summary>
+	/// Writes into the destination buffer starting at offset the UTF8 encoded version of the rune.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune">The rune to encode.</param>
+	/// <param name="dest">The destination buffer.</param>
+	/// <param name="start">Starting offset to look into.</param>
+	/// <param name="count">Number of bytes valid in the buffer, or -1 to make it the length of the buffer.</param>
+	/// <returns>he number of bytes written into the destination buffer.</returns>
+	public static int Encode (this Rune rune, byte [] dest, int start = 0, int count = -1)
+	{
+		var bytes = Encoding.UTF8.GetBytes (rune.ToString ());
+		var length = 0;
+		for (var i = 0; i < (count == -1 ? bytes.Length : count); i++) {
+			if (bytes [i] == 0) {
+				break;
+			}
+			dest [start + i] = bytes [i];
+			length++;
+		}
+		return length;
+	}
+
+	/// <summary>
+	/// Attempts to decode the rune as a surrogate pair to UTF-16.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune">The rune to decode.</param>
+	/// <param name="chars">The chars if the rune is a surrogate pair. Null otherwise.</param>
+	/// <returns><see langword="true"/> if the rune is a valid surrogate pair; <see langword="false"/> otherwise.</returns>
+	public static bool DecodeSurrogatePair (this Rune rune, out char [] chars)
+	{
+		if (rune.IsSurrogatePair ()) {
+			chars = rune.ToString ().ToCharArray ();
+			return true;
+		}
+		chars = null;
+		return false;
+	}
+
+	/// <summary>
+	/// Attempts to encode (as UTF-16) a surrogate pair.
+	/// </summary>
+	/// <param name="highSurrogate">The high surrogate code point.</param>
+	/// <param name="lowSurrogate">The low surrogate code point.</param>
+	/// <param name="result">The encoded rune.</param>
+	/// <returns><see langword="true"/> if the encoding succeeded; <see langword="false"/> otherwise.</returns>
+	public static bool EncodeSurrogatePair (char highSurrogate, char lowSurrogate, out Rune result)
+	{
+		result = default;
+		if (char.IsSurrogatePair (highSurrogate, lowSurrogate)) {
+			result = (Rune)char.ConvertToUtf32 (highSurrogate, lowSurrogate);
+			return true;
+		}
+		return false;
+	}
+
+	/// <summary>
+	/// Reports whether a rune is a surrogate code point.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="System.Text.Rune"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="rune">The rune to probe.</param>
+	/// <returns><see langword="true"/> if the rune is a surrogate code point; <see langword="false"/> otherwise.</returns>
+	public static bool IsSurrogatePair (this Rune rune)
+	{
+		return char.IsSurrogatePair (rune.ToString (), 0);
+	}
+
+	/// <summary>
+	/// Reports if the provided array of bytes can be encoded as UTF-8.
+	/// </summary>
+	/// <param name="buffer">The byte array to probe.</param>
+	/// <value><c>true</c> if is valid; otherwise, <c>false</c>.</value>
+	public static bool CanBeEncodedAsRune (byte [] buffer)
+	{
+		var str = Encoding.Unicode.GetString (buffer);
+		foreach (var rune in str.EnumerateRunes ()) {
+			if (rune == Rune.ReplacementChar) {
+				return false;
+			}
+		}
+		return true;
+	}
+
+	// ---------------- implementation details ------------------
+	// TODO: Can this be handled by the new .NET 8 Rune type?
+	static readonly int [,] _combining = new int [,] {
+		{ 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 },
+		{ 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 },
+		{ 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 },
+		{ 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 },
+		{ 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED },
+		{ 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A },
+		{ 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 },
+		{ 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D },
+		{ 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 },
+		{ 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD },
+		{ 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C },
+		{ 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D },
+		{ 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC },
+		{ 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD },
+		{ 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C },
+		{ 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D },
+		{ 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 },
+		{ 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 },
+		{ 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC },
+		{ 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD },
+		{ 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D },
+		{ 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 },
+		{ 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E },
+		{ 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC },
+		{ 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 },
+		{ 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E },
+		{ 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 },
+		{ 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 },
+		{ 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 },
+		{ 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F },
+		{ 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 },
+		{ 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD },
+		{ 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD },
+		{ 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 },
+		{ 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B },
+		{ 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 },
+		{ 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 },
+		{ 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF },
+		{ 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 },
+		{ 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x2E9A, 0x2E9A },
+		{ 0x2EF4, 0x2EFF }, { 0x2FD6, 0x2FEF }, { 0x2FFC, 0x2FFF },
+		{ 0x31E4, 0x31EF }, { 0x321F, 0x321F }, { 0xA48D, 0xA48F },
+		{ 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, { 0xA825, 0xA826 },
+		{ 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, { 0xFE1A, 0xFE1F },
+		{ 0xFE20, 0xFE23 }, { 0xFE53, 0xFE53 }, { 0xFE67, 0xFE67 },
+		{ 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB },
+		{ 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F },
+		{ 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 },
+		{ 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD },
+		{ 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F },
+		{ 0xE0100, 0xE01EF }
+	};
+
+	static readonly int [,] _combiningWideChars = new int [,] {
+		/* Hangul Jamo init. consonants - 0x1100, 0x11ff */
+		/* Miscellaneous Technical - 0x2300, 0x23ff */
+		/* Hangul Syllables - 0x11a8, 0x11c2 */
+		/* CJK Compatibility Ideographs - f900, fad9 */
+		/* Vertical forms - fe10, fe19 */
+		/* CJK Compatibility Forms - fe30, fe4f */
+		/* Fullwidth Forms - ff01, ffee */
+		/* Alphabetic Presentation Forms - 0xFB00, 0xFb4f */
+		/* Chess Symbols - 0x1FA00, 0x1FA0f */
+
+		{ 0x1100, 0x115f }, { 0x231a, 0x231b }, { 0x2329, 0x232a },
+		{ 0x23e9, 0x23ec }, { 0x23f0, 0x23f0 }, { 0x23f3, 0x23f3 },
+		{ 0x25fd, 0x25fe }, { 0x2614, 0x2615 }, { 0x2648, 0x2653 },
+		{ 0x267f, 0x267f }, { 0x2693, 0x2693 }, { 0x26a1, 0x26a1 },
+		{ 0x26aa, 0x26ab }, { 0x26bd, 0x26be }, { 0x26c4, 0x26c5 },
+		{ 0x26ce, 0x26ce }, { 0x26d4, 0x26d4 }, { 0x26ea, 0x26ea },
+		{ 0x26f2, 0x26f3 }, { 0x26f5, 0x26f5 }, { 0x26fa, 0x26fa },
+		{ 0x26fd, 0x26fd }, { 0x2705, 0x2705 }, { 0x270a, 0x270b },
+		{ 0x2728, 0x2728 }, { 0x274c, 0x274c }, { 0x274e, 0x274e },
+		{ 0x2753, 0x2755 }, { 0x2757, 0x2757 }, { 0x2795, 0x2797 },
+		{ 0x27b0, 0x27b0 }, { 0x27bf, 0x27bf }, { 0x2b1b, 0x2b1c },
+		{ 0x2b50, 0x2b50 }, { 0x2b55, 0x2b55 }, { 0x2e80, 0x303e },
+		{ 0x3041, 0x3096 }, { 0x3099, 0x30ff }, { 0x3105, 0x312f },
+		{ 0x3131, 0x318e }, { 0x3190, 0x3247 }, { 0x3250, 0x4dbf },
+		{ 0x4e00, 0xa4c6 }, { 0xa960, 0xa97c }, { 0xac00, 0xd7a3 },
+		{ 0xf900, 0xfaff }, { 0xfe10, 0xfe1f }, { 0xfe30, 0xfe6b },
+		{ 0xff01, 0xff60 }, { 0xffe0, 0xffe6 },
+		{ 0x16fe0, 0x16fe4 }, { 0x16ff0, 0x16ff1 }, { 0x17000, 0x187f7 },
+		{ 0x18800, 0x18cd5 }, { 0x18d00, 0x18d08 }, { 0x1aff0, 0x1affc },
+		{ 0x1b000, 0x1b122 }, { 0x1b150, 0x1b152 }, { 0x1b164, 0x1b167 }, { 0x1b170, 0x1b2fb }, { 0x1d538, 0x1d550 },
+		{ 0x1f004, 0x1f004 }, { 0x1f0cf, 0x1f0cf }, /*{ 0x1f100, 0x1f10a },*/
+		//{ 0x1f110, 0x1f12d }, { 0x1f130, 0x1f169 }, { 0x1f170, 0x1f1ac },
+		{ 0x1f18f, 0x1f199 },
+		{ 0x1f1e6, 0x1f1ff }, { 0x1f200, 0x1f202 }, { 0x1f210, 0x1f23b },
+		{ 0x1f240, 0x1f248 }, { 0x1f250, 0x1f251 }, { 0x1f260, 0x1f265 },
+		{ 0x1f300, 0x1f320 }, { 0x1f32d, 0x1f33e }, { 0x1f340, 0x1f37e },
+		{ 0x1f380, 0x1f393 }, { 0x1f3a0, 0x1f3ca }, { 0x1f3cf, 0x1f3d3 },
+		{ 0x1f3e0, 0x1f3f0 }, { 0x1f3f4, 0x1f3f4 }, { 0x1f3f8, 0x1f43e },
+		{ 0x1f440, 0x1f44e }, { 0x1f450, 0x1f4fc }, { 0x1f4ff, 0x1f53d },
+		{ 0x1f54b, 0x1f54e }, { 0x1f550, 0x1f567 }, { 0x1f57a, 0x1f57a },
+		{ 0x1f595, 0x1f596 }, { 0x1f5a4, 0x1f5a4 }, { 0x1f5fb, 0x1f606 },
+		{ 0x1f607, 0x1f64f }, { 0x1f680, 0x1f6c5 }, { 0x1f6cc, 0x1f6cc },
+		{ 0x1f6d0, 0x1f6d2 }, { 0x1f6d5, 0x1f6d7 }, { 0x1f6dd, 0x1f6df }, { 0x1f6eb, 0x1f6ec },
+		{ 0x1f6f4, 0x1f6fc }, { 0x1f7e0, 0x1f7eb }, { 0x1f7f0, 0x1f7f0 }, { 0x1f90c, 0x1f93a },
+		{ 0x1f93c, 0x1f945 }, { 0x1f947, 0x1f97f }, { 0x1f980, 0x1f9cc },
+		{ 0x1f9cd, 0x1f9ff }, { 0x1fa70, 0x1fa74 }, { 0x1fa78, 0x1fa7c }, { 0x1fa80, 0x1fa86 },
+		{ 0x1fa90, 0x1faac }, { 0x1fab0, 0x1faba }, { 0x1fac0, 0x1fac5 },
+		{ 0x1fad0, 0x1fad9 }, { 0x1fae0, 0x1fae7 }, { 0x1faf0, 0x1faf6 }, { 0x20000, 0x2fffd }, { 0x30000, 0x3fffd },
+		//{ 0xe0100, 0xe01ef }, { 0xf0000, 0xffffd }, { 0x100000, 0x10fffd }
+	};
+
+	static int BiSearch (int rune, int [,] table, int max)
+	{
+		var min = 0;
+
+		if (rune < table [0, 0] || rune > table [max, 1]) {
+			return 0;
+		}
+		while (max >= min) {
+			var mid = (min + max) / 2;
+			if (rune > table [mid, 1]) {
+				min = mid + 1;
+			} else if (rune < table [mid, 0]) {
+				max = mid - 1;
+			} else {
+				return 1;
+			}
+		}
+
+		return 0;
+	}
+}

+ 154 - 0
Terminal.Gui/Text/StringExtensions.cs

@@ -0,0 +1,154 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+
+namespace Terminal.Gui;
+/// <summary>
+/// Extensions to <see cref="string"/> to support TUI text manipulation.
+/// </summary>
+public static class StringExtensions {
+	/// <summary>
+	/// Repeats the string <paramref name="n"/> times.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The text to repeat.</param>
+	/// <param name="n">Number of times to repeat the text.</param>
+	/// <returns>
+	///  The text repeated if <paramref name="n"/> is greater than zero, 
+	///  otherwise <see langword="null"/>.
+	/// </returns>
+	public static string Repeat (this string str, int n)
+	{
+		if (n <= 0) {
+			return null;
+		}
+
+		if (string.IsNullOrEmpty (str) || n == 1) {
+			return str;
+		}
+
+		return new StringBuilder (str.Length * n)
+			.Insert (0, str, n)
+			.ToString ();
+	}
+
+	/// <summary>
+	/// Gets the number of columns the string occupies in the terminal.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to measure.</param>
+	/// <returns></returns>
+	public static int GetColumns (this string str)
+	{
+		return str == null ? 0 : str.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 0));
+	}
+
+	/// <summary>
+	/// Gets the number of runes in the string.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to count.</param>
+	/// <returns></returns>
+	public static int GetRuneCount (this string str) => str.EnumerateRunes ().Count ();
+
+	/// <summary>
+	/// Converts the string into a <see cref="Rune"/> array.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to convert.</param>
+	/// <returns></returns>
+	public static Rune [] ToRunes (this string str) => str.EnumerateRunes ().ToArray ();
+
+	/// <summary>
+	/// Converts the string into a <see cref="List{Rune}"/>.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to convert.</param>
+	/// <returns></returns>
+	public static List<Rune> ToRuneList (this string str) => str.EnumerateRunes ().ToList ();
+
+	/// <summary>
+	/// Unpacks the first UTF-8 encoding in the string and returns the rune and its width in bytes.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to decode.</param>
+	/// <param name="start">Starting offset.</param>
+	/// <param name="count">Number of bytes in the buffer, or -1 to make it the length of the buffer.</param>
+	/// <returns></returns>
+	public static (Rune Rune, int Size) DecodeRune (this string str, int start = 0, int count = -1)
+	{
+		var rune = str.EnumerateRunes ().ToArray () [start];
+		var bytes = Encoding.UTF8.GetBytes (rune.ToString ());
+		if (count == -1) {
+			count = bytes.Length;
+		}
+		var operationStatus = Rune.DecodeFromUtf8 (bytes, out rune, out int bytesConsumed);
+		if (operationStatus == System.Buffers.OperationStatus.Done && bytesConsumed >= count) {
+			return (rune, bytesConsumed);
+		}
+		return (Rune.ReplacementChar, 1);
+	}
+
+	/// <summary>
+	/// Unpacks the last UTF-8 encoding in the string.
+	/// </summary>
+	/// <remarks>
+	/// This is a Terminal.Gui extension method to <see cref="string"/> to support TUI text manipulation.
+	/// </remarks>
+	/// <param name="str">The string to decode.</param>
+	/// <param name="end">Index in string to stop at; if -1, use the buffer length.</param>
+	/// <returns></returns>
+	public static (Rune rune, int size) DecodeLastRune (this string str, int end = -1)
+	{
+		var rune = str.EnumerateRunes ().ToArray () [end == -1 ? ^1 : end];
+		var bytes = Encoding.UTF8.GetBytes (rune.ToString ());
+		var operationStatus = Rune.DecodeFromUtf8 (bytes, out rune, out int bytesConsumed);
+		if (operationStatus == System.Buffers.OperationStatus.Done) {
+			return (rune, bytesConsumed);
+		}
+		return (Rune.ReplacementChar, 1);
+	}
+
+	/// <summary>
+	/// Converts a <see cref="Rune"/> generic collection into a string.
+	/// </summary>
+	/// <param name="runes">The enumerable rune to convert.</param>
+	/// <returns></returns>
+	public static string ToString (IEnumerable<Rune> runes)
+	{
+		var str = string.Empty;
+
+		foreach (var rune in runes) {
+			str += rune.ToString ();
+		}
+
+		return str;
+	}
+
+	/// <summary>
+	/// Converts a byte generic collection into a string in the provided encoding (default is UTF8)
+	/// </summary>
+	/// <param name="bytes">The enumerable byte to convert.</param>
+	/// <param name="encoding">The encoding to be used.</param>
+	/// <returns></returns>
+	public static string ToString (IEnumerable<byte> bytes, Encoding encoding = null)
+	{
+		if (encoding == null) {
+			encoding = Encoding.UTF8;
+		}
+		return encoding.GetString (bytes.ToArray ());
+	}
+}

+ 143 - 145
Terminal.Gui/Text/TextFormatter.cs

@@ -3,8 +3,6 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Text;
-using NStack;
-using Rune = System.Rune;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -12,19 +10,20 @@ namespace Terminal.Gui {
 	/// </summary>
 	public enum TextAlignment {
 		/// <summary>
-		/// Aligns the text to the left of the frame.
+		/// The text will be left-aligned.
 		/// </summary>
 		Left,
 		/// <summary>
-		/// Aligns the text to the right side of the frame.
+		/// The text will be right-aligned.
 		/// </summary>
 		Right,
 		/// <summary>
-		/// Centers the text in the frame.
+		/// The text will be centered horizontally.
 		/// </summary>
 		Centered,
 		/// <summary>
-		/// Shows the text as justified text in the frame.
+		/// The text will be justified (spaces will be added to existing spaces such that
+		/// the text fills the container horizontally).
 		/// </summary>
 		Justified
 	}
@@ -34,19 +33,20 @@ namespace Terminal.Gui {
 	/// </summary>
 	public enum VerticalTextAlignment {
 		/// <summary>
-		/// Aligns the text to the top of the frame.
+		/// The text will be top-aligned.
 		/// </summary>
 		Top,
 		/// <summary>
-		/// Aligns the text to the bottom of the frame.
+		/// The text will be bottom-aligned.
 		/// </summary>
 		Bottom,
 		/// <summary>
-		/// Centers the text verticaly in the frame.
+		/// The text will centered vertically.
 		/// </summary>
 		Middle,
 		/// <summary>
-		/// Shows the text as justified text in the frame.
+		/// The text will be justified (spaces will be added to existing spaces such that
+		/// the text fills the container vertically).
 		/// </summary>
 		Justified
 	}
@@ -112,17 +112,17 @@ namespace Terminal.Gui {
 	}
 
 	/// <summary>
-	/// Provides text formatting capabilities for console apps. Supports, hotkeys, horizontal alignment, multiple lines, and word-based line wrap.
+	/// Provides text formatting. Supports <see cref="View.HotKey"/>s, horizontal alignment, vertical alignment, multiple lines, and word-based line wrap.
 	/// </summary>
 	public class TextFormatter {
 
 		#region Static Members
 
-		static ustring StripCRLF (ustring str, bool keepNewLine = false)
+		static string StripCRLF (string str, bool keepNewLine = false)
 		{
 			var runes = str.ToRuneList ();
 			for (int i = 0; i < runes.Count; i++) {
-				switch (runes [i]) {
+				switch ((char)runes [i].Value) {
 				case '\n':
 					if (!keepNewLine) {
 						runes.RemoveAt (i);
@@ -130,7 +130,7 @@ namespace Terminal.Gui {
 					break;
 
 				case '\r':
-					if ((i + 1) < runes.Count && runes [i + 1] == '\n') {
+					if ((i + 1) < runes.Count && runes [i + 1].Value == '\n') {
 						runes.RemoveAt (i);
 						if (!keepNewLine) {
 							runes.RemoveAt (i);
@@ -144,19 +144,19 @@ namespace Terminal.Gui {
 					break;
 				}
 			}
-			return ustring.Make (runes);
+			return StringExtensions.ToString (runes);
 		}
-		static ustring ReplaceCRLFWithSpace (ustring str)
+		static string ReplaceCRLFWithSpace (string str)
 		{
 			var runes = str.ToRuneList ();
 			for (int i = 0; i < runes.Count; i++) {
-				switch (runes [i]) {
+				switch (runes [i].Value) {
 				case '\n':
 					runes [i] = (Rune)' ';
 					break;
 
 				case '\r':
-					if ((i + 1) < runes.Count && runes [i + 1] == '\n') {
+					if ((i + 1) < runes.Count && runes [i + 1].Value == '\n') {
 						runes [i] = (Rune)' ';
 						runes.RemoveAt (i + 1);
 						i++;
@@ -166,7 +166,7 @@ namespace Terminal.Gui {
 					break;
 				}
 			}
-			return ustring.Make (runes);
+			return StringExtensions.ToString (runes);
 		}
 
 		/// <summary>
@@ -175,29 +175,29 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="text">The text.</param>
 		/// <returns>A list of text without the newline characters.</returns>
-		public static List<ustring> SplitNewLine (ustring text)
+		public static List<string> SplitNewLine (string text)
 		{
 			var runes = text.ToRuneList ();
-			var lines = new List<ustring> ();
+			var lines = new List<string> ();
 			var start = 0;
 			var end = 0;
 
 			for (int i = 0; i < runes.Count; i++) {
 				end = i;
-				switch (runes [i]) {
+				switch (runes [i].Value) {
 				case '\n':
-					lines.Add (ustring.Make (runes.GetRange (start, end - start)));
+					lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
 					i++;
 					start = i;
 					break;
 
 				case '\r':
-					if ((i + 1) < runes.Count && runes [i + 1] == '\n') {
-						lines.Add (ustring.Make (runes.GetRange (start, end - start)));
+					if ((i + 1) < runes.Count && runes [i + 1].Value == '\n') {
+						lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
 						i += 2;
 						start = i;
 					} else {
-						lines.Add (ustring.Make (runes.GetRange (start, end - start)));
+						lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
 						i++;
 						start = i;
 					}
@@ -205,11 +205,11 @@ namespace Terminal.Gui {
 				}
 			}
 			if (runes.Count > 0 && lines.Count == 0) {
-				lines.Add (ustring.Make (runes));
+				lines.Add (StringExtensions.ToString (runes));
 			} else if (runes.Count > 0 && start < runes.Count) {
-				lines.Add (ustring.Make (runes.GetRange (start, runes.Count - start)));
+				lines.Add (StringExtensions.ToString (runes.GetRange (start, runes.Count - start)));
 			} else {
-				lines.Add (ustring.Make (""));
+				lines.Add ("");
 			}
 			return lines;
 		}
@@ -228,16 +228,16 @@ namespace Terminal.Gui {
 				return text;
 
 			// if value is not wide enough
-			if (text.Sum (c => Rune.ColumnWidth (c)) < width) {
+			if (text.EnumerateRunes ().Sum (c => c.GetColumns ()) < width) {
 
 				// pad it out with spaces to the given alignment
-				int toPad = width - (text.Sum (c => Rune.ColumnWidth (c)));
+				int toPad = width - (text.EnumerateRunes ().Sum (c => c.GetColumns ()));
 
 				return text + new string (' ', toPad);
 			}
 
 			// value is too wide
-			return new string (text.TakeWhile (c => (width -= Rune.ColumnWidth (c)) >= 0).ToArray ());
+			return new string (text.TakeWhile (c => (width -= ((Rune)c).GetColumns ()) >= 0).ToArray ());
 		}
 
 		/// <summary>
@@ -261,7 +261,7 @@ namespace Terminal.Gui {
 		/// If <paramref name="preserveTrailingSpaces"/> is <see langword="false"/> at most one space will be preserved at the end of the last line.
 		/// </para>
 		/// </remarks>
-		public static List<ustring> WordWrapText (ustring text, int width, bool preserveTrailingSpaces = false, int tabWidth = 0,
+		public static List<string> WordWrapText (string text, int width, bool preserveTrailingSpaces = false, int tabWidth = 0,
 			TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			if (width < 0) {
@@ -269,9 +269,9 @@ namespace Terminal.Gui {
 			}
 
 			int start = 0, end;
-			var lines = new List<ustring> ();
+			var lines = new List<string> ();
 
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return lines;
 			}
 
@@ -280,13 +280,13 @@ namespace Terminal.Gui {
 				while ((end = start) < runes.Count) {
 					end = GetNextWhiteSpace (start, width, out bool incomplete);
 					if (end == 0 && incomplete) {
-						start = text.RuneCount;
+						start = text.GetRuneCount ();
 						break;
 					}
-					lines.Add (ustring.Make (runes.GetRange (start, end - start)));
+					lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
 					start = end;
 					if (incomplete) {
-						start = text.RuneCount;
+						start = text.GetRuneCount ();
 						break;
 					}
 				}
@@ -333,7 +333,7 @@ namespace Terminal.Gui {
 					//		//if (line [0] == ' ' && (lines.Count > 0 && lines [lines.Count - 1] [0] == ' ')) {
 					//		//} else {
 					//		//}
-					//		lines.Add (ustring.Make (line));
+					//		lines.Add (string.Make (line));
 
 					//		// move forward to next non-space
 					//		while (width > 1 && start < runes.Count && runes [start] == ' ') {
@@ -343,28 +343,34 @@ namespace Terminal.Gui {
 					//}
 
 					while ((end = start + Math.Max (GetLengthThatFits (runes.GetRange (start, runes.Count - start), width), 1)) < runes.Count) {
-						while (runes [end] != ' ' && end > start)
+						while (runes [end].Value != ' ' && end > start)
 							end--;
 						if (end == start)
 							end = start + GetLengthThatFits (runes.GetRange (end, runes.Count - end), width);
-						lines.Add (ustring.Make (runes.GetRange (start, end - start)));
-						start = end;
-						if (runes [end] == ' ') {
-							start++;
+						var str = StringExtensions.ToString (runes.GetRange (start, end - start));
+						if (end > start && str.GetColumns () <= width) {
+							lines.Add (str);
+							start = end;
+							if (runes [end].Value == ' ') {
+								start++;
+							}
+						} else {
+							end++;
+							start = end;
 						}
 					}
-					
+
 				} else {
 					while ((end = start + width) < runes.Count) {
-						while (runes [end] != ' ' && end > start) {
+						while (runes [end].Value != ' ' && end > start) {
 							end--;
 						}
 						if (end == start) {
 							end = start + width;
 						}
-						lines.Add (ustring.Make (runes.GetRange (start, end - start)));
+						lines.Add (StringExtensions.ToString (runes.GetRange (start, end - start)));
 						start = end;
-						if (runes [end] == ' ') {
+						if (runes [end].Value == ' ') {
 							start++;
 						}
 					}
@@ -381,7 +387,7 @@ namespace Terminal.Gui {
 				while (length < cWidth && to < runes.Count) {
 					var rune = runes [to];
 					if (IsHorizontalDirection (textDirection)) {
-						length += Rune.ColumnWidth (rune);
+						length += rune.GetColumns ();
 					} else {
 						length++;
 					}
@@ -391,7 +397,7 @@ namespace Terminal.Gui {
 						}
 						return to;
 					}
-					if (rune == ' ') {
+					if (rune.Value == ' ') {
 						if (length == cWidth) {
 							return to + 1;
 						} else if (length > cWidth) {
@@ -399,7 +405,7 @@ namespace Terminal.Gui {
 						} else {
 							return GetNextWhiteSpace (to + 1, cWidth, out incomplete, length);
 						}
-					} else if (rune == '\t') {
+					} else if (rune.Value == '\t') {
 						length += tabWidth + 1;
 						if (length == tabWidth && tabWidth > cWidth) {
 							return to + 1;
@@ -411,17 +417,20 @@ namespace Terminal.Gui {
 					}
 					to++;
 				}
-				if (cLength > 0 && to < runes.Count && runes [to] != ' ' && runes [to] != '\t') {
+				if (cLength > 0 && to < runes.Count && runes [to].Value != ' ' && runes [to].Value != '\t') {
 					return from;
-				} else if (cLength > 0 && to < runes.Count && (runes [to] == ' ' || runes [to] == '\t')) {
+				} else if (cLength > 0 && to < runes.Count && (runes [to].Value == ' ' || runes [to].Value == '\t')) {
 					return lastFrom;
 				} else {
 					return to;
 				}
 			}
 
-			if (start < text.RuneCount) {
-				lines.Add (ustring.Make (runes.GetRange (start, runes.Count - start)));
+			if (start < text.GetRuneCount ()) {
+				var str = StringExtensions.ToString (runes.GetRange (start, runes.Count - start));
+				if (IsVerticalDirection (textDirection) || preserveTrailingSpaces || (!preserveTrailingSpaces && str.GetColumns () <= width)) {
+					lines.Add (str);
+				}
 			}
 
 			return lines;
@@ -435,7 +444,7 @@ namespace Terminal.Gui {
 		/// <param name="talign">Alignment.</param>
 		/// <param name="textDirection">The text direction.</param>
 		/// <returns>Justified and clipped text.</returns>
-		public static ustring ClipAndJustify (ustring text, int width, TextAlignment talign, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+		public static string ClipAndJustify (string text, int width, TextAlignment talign, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			return ClipAndJustify (text, width, talign == TextAlignment.Justified, textDirection);
 		}
@@ -448,12 +457,12 @@ namespace Terminal.Gui {
 		/// <param name="justify">Justify.</param>
 		/// <param name="textDirection">The text direction.</param>
 		/// <returns>Justified and clipped text.</returns>
-		public static ustring ClipAndJustify (ustring text, int width, bool justify, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+		public static string ClipAndJustify (string text, int width, bool justify, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			if (width < 0) {
 				throw new ArgumentOutOfRangeException ("Width cannot be negative.");
 			}
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return text;
 			}
 
@@ -461,15 +470,15 @@ namespace Terminal.Gui {
 			int slen = runes.Count;
 			if (slen > width) {
 				if (IsHorizontalDirection (textDirection)) {
-					return ustring.Make (runes.GetRange (0, GetLengthThatFits (text, width)));
+					return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
 				} else {
-					return ustring.Make (runes.GetRange (0, width));
+					return StringExtensions.ToString (runes.GetRange (0, width));
 				}
 			} else {
 				if (justify) {
 					return Justify (text, width, ' ', textDirection);
-				} else if (IsHorizontalDirection (textDirection) && GetTextWidth (text) > width) {
-					return ustring.Make (runes.GetRange (0, GetLengthThatFits (text, width)));
+				} else if (IsHorizontalDirection (textDirection) && text.GetColumns () > width) {
+					return StringExtensions.ToString (runes.GetRange (0, GetLengthThatFits (text, width)));
 				}
 				return text;
 			}
@@ -484,21 +493,21 @@ namespace Terminal.Gui {
 		/// <param name="spaceChar">Character to replace whitespace and pad with. For debugging purposes.</param>
 		/// <param name="textDirection">The text direction.</param>
 		/// <returns>The justified text.</returns>
-		public static ustring Justify (ustring text, int width, char spaceChar = ' ', TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+		public static string Justify (string text, int width, char spaceChar = ' ', TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			if (width < 0) {
 				throw new ArgumentOutOfRangeException ("Width cannot be negative.");
 			}
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return text;
 			}
 
-			var words = text.Split (ustring.Make (' '));
+			var words = text.Split (' ');
 			int textCount;
 			if (IsHorizontalDirection (textDirection)) {
-				textCount = words.Sum (arg => GetTextWidth (arg));
+				textCount = words.Sum (arg => arg.GetColumns ());
 			} else {
-				textCount = words.Sum (arg => arg.RuneCount);
+				textCount = words.Sum (arg => arg.GetRuneCount ());
 			}
 			var spaces = words.Length > 1 ? (width - textCount) / (words.Length - 1) : 0;
 			var extras = words.Length > 1 ? (width - textCount) % (words.Length - 1) : 0;
@@ -520,7 +529,7 @@ namespace Terminal.Gui {
 						s.Append (spaceChar);
 				}
 			}
-			return ustring.Make (s.ToString ());
+			return s.ToString ();
 		}
 
 		static char [] whitespace = new char [] { ' ', '\t' };
@@ -549,7 +558,7 @@ namespace Terminal.Gui {
 		/// If <paramref name="width"/> is int.MaxValue, the text will be formatted to the maximum width possible. 
 		/// </para>
 		/// </remarks>
-		public static List<ustring> Format (ustring text, int width, TextAlignment talign, bool wordWrap, bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
+		public static List<string> Format (string text, int width, TextAlignment talign, bool wordWrap, bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			return Format (text, width, talign == TextAlignment.Justified, wordWrap, preserveTrailingSpaces, tabWidth, textDirection);
 		}
@@ -578,16 +587,16 @@ namespace Terminal.Gui {
 		/// If <paramref name="width"/> is int.MaxValue, the text will be formatted to the maximum width possible. 
 		/// </para>
 		/// </remarks>
-		public static List<ustring> Format (ustring text, int width, bool justify, bool wordWrap,
+		public static List<string> Format (string text, int width, bool justify, bool wordWrap,
 			bool preserveTrailingSpaces = false, int tabWidth = 0, TextDirection textDirection = TextDirection.LeftRight_TopBottom)
 		{
 			if (width < 0) {
 				throw new ArgumentOutOfRangeException ("width cannot be negative");
 			}
-			List<ustring> lineResult = new List<ustring> ();
+			List<string> lineResult = new List<string> ();
 
-			if (ustring.IsNullOrEmpty (text) || width == 0) {
-				lineResult.Add (ustring.Empty);
+			if (string.IsNullOrEmpty (text) || width == 0) {
+				lineResult.Add (string.Empty);
 				return lineResult;
 			}
 
@@ -602,18 +611,18 @@ namespace Terminal.Gui {
 			int lp = 0;
 			for (int i = 0; i < runeCount; i++) {
 				Rune c = runes [i];
-				if (c == '\n') {
-					var wrappedLines = WordWrapText (ustring.Make (runes.GetRange (lp, i - lp)), width, preserveTrailingSpaces, tabWidth, textDirection);
+				if (c.Value == '\n') {
+					var wrappedLines = WordWrapText (StringExtensions.ToString (runes.GetRange (lp, i - lp)), width, preserveTrailingSpaces, tabWidth, textDirection);
 					foreach (var line in wrappedLines) {
 						lineResult.Add (ClipAndJustify (line, width, justify, textDirection));
 					}
 					if (wrappedLines.Count == 0) {
-						lineResult.Add (ustring.Empty);
+						lineResult.Add (string.Empty);
 					}
 					lp = i + 1;
 				}
 			}
-			foreach (var line in WordWrapText (ustring.Make (runes.GetRange (lp, runeCount - lp)), width, preserveTrailingSpaces, tabWidth, textDirection)) {
+			foreach (var line in WordWrapText (StringExtensions.ToString (runes.GetRange (lp, runeCount - lp)), width, preserveTrailingSpaces, tabWidth, textDirection)) {
 				lineResult.Add (ClipAndJustify (line, width, justify, textDirection));
 			}
 
@@ -626,7 +635,7 @@ namespace Terminal.Gui {
 		/// <returns>Number of lines.</returns>
 		/// <param name="text">Text, may contain newlines.</param>
 		/// <param name="width">The minimum width for the text.</param>
-		public static int MaxLines (ustring text, int width)
+		public static int MaxLines (string text, int width)
 		{
 			var result = TextFormatter.Format (text, width, false, true);
 			return result.Count;
@@ -639,13 +648,13 @@ namespace Terminal.Gui {
 		/// <returns>Width of the longest line after formatting the text constrained by <paramref name="maxColumns"/>.</returns>
 		/// <param name="text">Text, may contain newlines.</param>
 		/// <param name="maxColumns">The number of columns to constrain the text to for formatting.</param>
-		public static int MaxWidth (ustring text, int maxColumns)
+		public static int MaxWidth (string text, int maxColumns)
 		{
 			var result = TextFormatter.Format (text: text, width: maxColumns, justify: false, wordWrap: true);
 			var max = 0;
 			result.ForEach (s => {
 				var m = 0;
-				s.ToRuneList ().ForEach (r => m += Math.Max (Rune.ColumnWidth (r), 1));
+				s.ToRuneList ().ForEach (r => m += Math.Max (r.GetColumns (), 1));
 				if (m > max) {
 					max = m;
 				}
@@ -654,25 +663,15 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Returns the width of the widest line in the text, accounting for wide-glyphs (uses <see cref="ustring.ConsoleWidth"/>).
+		/// Returns the width of the widest line in the text, accounting for wide-glyphs (uses <see cref="StringExtensions.GetColumns"/>).
 		/// <paramref name="text"/> if it contains newlines.
 		/// </summary>
 		/// <param name="text">Text, may contain newlines.</param>
 		/// <returns>The length of the longest line.</returns>
-		public static int MaxWidthLine (ustring text)
+		public static int MaxWidthLine (string text)
 		{
 			var result = TextFormatter.SplitNewLine (text);
-			return result.Max (x => x.ConsoleWidth);
-		}
-
-		/// <summary>
-		/// Gets the number of columns the passed text will use, ignoring newlines and accounting for wide-glyphs (uses <see cref="ustring.ConsoleWidth"/>).
-		/// </summary>
-		/// <param name="text"></param>
-		/// <returns>The text width.</returns>
-		public static int GetTextWidth (ustring text)
-		{
-			return text.ToRuneList ().Sum (r => Math.Max (Rune.ColumnWidth (r), 1));
+			return result.Max (x => x.GetColumns ());
 		}
 
 		/// <summary>
@@ -683,13 +682,13 @@ namespace Terminal.Gui {
 		/// <param name="startIndex">The start index.</param>
 		/// <param name="length">The length.</param>
 		/// <returns>The maximum characters width.</returns>
-		public static int GetSumMaxCharWidth (List<ustring> lines, int startIndex = -1, int length = -1)
+		public static int GetSumMaxCharWidth (List<string> lines, int startIndex = -1, int length = -1)
 		{
 			var max = 0;
 			for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? lines.Count : startIndex + length); i++) {
 				var runes = lines [i];
 				if (runes.Length > 0)
-					max += runes.Max (r => Math.Max (Rune.ColumnWidth (r), 1));
+					max += runes.EnumerateRunes ().Max (r => Math.Max (r.GetColumns (), 1));
 			}
 			return max;
 		}
@@ -702,23 +701,23 @@ namespace Terminal.Gui {
 		/// <param name="startIndex">The start index.</param>
 		/// <param name="length">The length.</param>
 		/// <returns>The maximum characters width.</returns>
-		public static int GetSumMaxCharWidth (ustring text, int startIndex = -1, int length = -1)
+		public static int GetSumMaxCharWidth (string text, int startIndex = -1, int length = -1)
 		{
 			var max = 0;
 			var runes = text.ToRunes ();
 			for (int i = (startIndex == -1 ? 0 : startIndex); i < (length == -1 ? runes.Length : startIndex + length); i++) {
-				max += Math.Max (Rune.ColumnWidth (runes [i]), 1);
+				max += Math.Max (runes [i].GetColumns (), 1);
 			}
 			return max;
 		}
 
 		/// <summary>
-		/// Gets the number of the Runes in a <see cref="ustring"/> that will fit in <paramref name="columns"/>.
+		/// Gets the number of the Runes in a <see cref="string"/> that will fit in <paramref name="columns"/>.
 		/// </summary>
 		/// <param name="text">The text.</param>
 		/// <param name="columns">The width.</param>
 		/// <returns>The index of the text that fit the width.</returns>
-		public static int GetLengthThatFits (ustring text, int columns) => GetLengthThatFits (text?.ToRuneList (), columns);
+		public static int GetLengthThatFits (string text, int columns) => GetLengthThatFits (text?.ToRuneList (), columns);
 
 		/// <summary>
 		/// Gets the number of the Runes in a list of Runes that will fit in <paramref name="columns"/>.
@@ -735,7 +734,7 @@ namespace Terminal.Gui {
 			var runesLength = 0;
 			var runeIdx = 0;
 			for (; runeIdx < runes.Count; runeIdx++) {
-				var runeWidth = Math.Max (Rune.ColumnWidth (runes [runeIdx]), 1);
+				var runeWidth = Math.Max (runes [runeIdx].GetColumns (), 1);
 				if (runesLength + runeWidth > columns) {
 					break;
 				}
@@ -750,14 +749,14 @@ namespace Terminal.Gui {
 		/// <param name="lines">The lines.</param>
 		/// <param name="width">The width.</param>
 		/// <returns>The index of the list that fit the width.</returns>
-		public static int GetMaxColsForWidth (List<ustring> lines, int width)
+		public static int GetMaxColsForWidth (List<string> lines, int width)
 		{
 			var runesLength = 0;
 			var lineIdx = 0;
 			for (; lineIdx < lines.Count; lineIdx++) {
 				var runes = lines [lineIdx].ToRuneList ();
 				var maxRruneWidth = runes.Count > 0
-					? runes.Max (r => Math.Max (Rune.ColumnWidth (r), 1)) : 1;
+					? runes.Max (r => Math.Max (r.GetColumns (), 1)) : 1;
 				if (runesLength + maxRruneWidth > width) {
 					break;
 				}
@@ -774,9 +773,9 @@ namespace Terminal.Gui {
 		/// <param name="text">The text to measure</param>
 		/// <param name="direction">The text direction.</param>
 		/// <returns></returns>
-		public static Rect CalcRect (int x, int y, ustring text, TextDirection direction = TextDirection.LeftRight_TopBottom)
+		public static Rect CalcRect (int x, int y, string text, TextDirection direction = TextDirection.LeftRight_TopBottom)
 		{
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return new Rect (new Point (x, y), Size.Empty);
 			}
 
@@ -787,16 +786,16 @@ namespace Terminal.Gui {
 				int ml = 1;
 
 				int cols = 0;
-				foreach (var rune in text) {
-					if (rune == '\n') {
+				foreach (var rune in text.EnumerateRunes ()) {
+					if (rune.Value == '\n') {
 						ml++;
 						if (cols > mw) {
 							mw = cols;
 						}
 						cols = 0;
-					} else if (rune != '\r') {
+					} else if (rune.Value != '\r') {
 						cols++;
-						var rw = Rune.ColumnWidth (rune);
+						var rw = ((Rune)rune).GetColumns ();
 						if (rw > 0) {
 							rw--;
 						}
@@ -813,17 +812,17 @@ namespace Terminal.Gui {
 				int vh = 0;
 
 				int rows = 0;
-				foreach (var rune in text) {
-					if (rune == '\n') {
+				foreach (var rune in text.EnumerateRunes ()) {
+					if (rune.Value == '\n') {
 						vw++;
 						if (rows > vh) {
 							vh = rows;
 						}
 						rows = 0;
 						cw = 1;
-					} else if (rune != '\r') {
+					} else if (rune.Value != '\r') {
 						rows++;
-						var rw = Rune.ColumnWidth (rune);
+						var rw = ((Rune)rune).GetColumns ();
 						if (cw < rw) {
 							cw = rw;
 							vw++;
@@ -850,9 +849,9 @@ namespace Terminal.Gui {
 		/// <param name="hotPos">Outputs the Rune index into <c>text</c>.</param>
 		/// <param name="hotKey">Outputs the hotKey.</param>
 		/// <returns><c>true</c> if a hotkey was found; <c>false</c> otherwise.</returns>
-		public static bool FindHotKey (ustring text, Rune hotKeySpecifier, bool firstUpperCase, out int hotPos, out Key hotKey)
+		public static bool FindHotKey (string text, Rune hotKeySpecifier, bool firstUpperCase, out int hotPos, out Key hotKey)
 		{
-			if (ustring.IsNullOrEmpty (text) || hotKeySpecifier == (Rune)0xFFFF) {
+			if (string.IsNullOrEmpty (text) || hotKeySpecifier == (Rune)0xFFFF) {
 				hotPos = -1;
 				hotKey = Key.Unknown;
 				return false;
@@ -865,8 +864,8 @@ namespace Terminal.Gui {
 			// TODO: Ignore hot_key of two are provided
 			// TODO: Do not support non-alphanumeric chars that can't be typed
 			int i = 0;
-			foreach (Rune c in text) {
-				if ((char)c != 0xFFFD) {
+			foreach (Rune c in text.EnumerateRunes ()) {
+				if ((char)c.Value != 0xFFFD) {
 					if (c == hotKeySpecifier) {
 						hot_pos = i;
 					} else if (hot_pos > -1) {
@@ -880,8 +879,8 @@ namespace Terminal.Gui {
 			// Legacy support - use first upper case char if the specifier was not found
 			if (hot_pos == -1 && firstUpperCase) {
 				i = 0;
-				foreach (Rune c in text) {
-					if ((char)c != 0xFFFD) {
+				foreach (Rune c in text.EnumerateRunes ()) {
+					if ((char)c.Value != 0xFFFD) {
 						if (Rune.IsUpper (c)) {
 							hot_key = c;
 							hot_pos = i;
@@ -895,8 +894,8 @@ namespace Terminal.Gui {
 			if (hot_key != (Rune)0 && hot_pos != -1) {
 				hotPos = hot_pos;
 
-				if (hot_key.IsValid && char.IsLetterOrDigit ((char)hot_key)) {
-					hotKey = (Key)char.ToUpperInvariant ((char)hot_key);
+				if (Rune.IsValid (hot_key.Value) && char.IsLetterOrDigit ((char)hot_key.Value)) {
+					hotKey = (Key)char.ToUpperInvariant ((char)hot_key.Value);
 					return true;
 				}
 			}
@@ -916,14 +915,14 @@ namespace Terminal.Gui {
 		/// <remarks>
 		/// The returned string will not render correctly without first un-doing the tag. To undo the tag, search for 
 		/// </remarks>
-		public ustring ReplaceHotKeyWithTag (ustring text, int hotPos)
+		public string ReplaceHotKeyWithTag (string text, int hotPos)
 		{
 			// Set the high bit
 			var runes = text.ToRuneList ();
-			if (Rune.IsLetterOrNumber (runes [hotPos])) {
-				runes [hotPos] = new Rune ((uint)runes [hotPos]);
+			if (Rune.IsLetterOrDigit (runes [hotPos])) {
+				runes [hotPos] = new Rune ((uint)runes [hotPos].Value);
 			}
-			return ustring.Make (runes);
+			return StringExtensions.ToString (runes);
 		}
 
 		/// <summary>
@@ -933,33 +932,32 @@ namespace Terminal.Gui {
 		/// <param name="hotKeySpecifier">The hot-key specifier (e.g. '_') to look for.</param>
 		/// <param name="hotPos">Returns the position of the hot-key in the text. -1 if not found.</param>
 		/// <returns>The input text with the hotkey specifier ('_') removed.</returns>
-		public static ustring RemoveHotKeySpecifier (ustring text, int hotPos, Rune hotKeySpecifier)
+		public static string RemoveHotKeySpecifier (string text, int hotPos, Rune hotKeySpecifier)
 		{
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return text;
 			}
 
 			// Scan 
-			ustring start = ustring.Empty;
+			string start = string.Empty;
 			int i = 0;
 			foreach (Rune c in text) {
 				if (c == hotKeySpecifier && i == hotPos) {
 					i++;
 					continue;
 				}
-				start += ustring.Make (c);
+				start += c;
 				i++;
 			}
 			return start;
 		}
 		#endregion // Static Members
 
-		List<ustring> _lines = new List<ustring> ();
-		ustring _text;
+		List<string> _lines = new List<string> ();
+		string _text;
 		TextAlignment _textAlignment;
 		VerticalTextAlignment _textVerticalAlignment;
 		TextDirection _textDirection;
-		Attribute _textColor = -1;
 		Key _hotKey;
 		int _hotKeyPos = -1;
 		Size _size;
@@ -970,14 +968,14 @@ namespace Terminal.Gui {
 		public event EventHandler<KeyChangedEventArgs> HotKeyChanged;
 
 		/// <summary>
-		///   The text to be displayed. This text is never modified.
+		///   The text to be displayed. This string is never modified.
 		/// </summary>
-		public virtual ustring Text {
+		public virtual string Text {
 			get => _text;
 			set {
 				_text = value;
 
-				if (_text != null && _text.RuneCount > 0 && (Size.Width == 0 || Size.Height == 0 || Size.Width != _text.ConsoleWidth)) {
+				if (_text != null && _text.GetRuneCount () > 0 && (Size.Width == 0 || Size.Height == 0 || Size.Width != _text.GetColumns ())) {
 					// Provide a default size (width = length of longest line, height = 1)
 					// TODO: It might makes more sense for the default to be width = length of first line?
 					Size = new Size (TextFormatter.MaxWidth (Text, int.MaxValue), 1);
@@ -1160,7 +1158,7 @@ namespace Terminal.Gui {
 		public Size GetFormattedSize ()
 		{
 			var lines = Lines;
-			var width = Lines.Max (line => TextFormatter.GetTextWidth (line));
+			var width = Lines.Max (line => line.GetColumns ());
 			var height = Lines.Count;
 			return new Size (width, height);
 		}
@@ -1171,15 +1169,15 @@ namespace Terminal.Gui {
 		/// <remarks>
 		/// <para>
 		/// Upon a 'get' of this property, if the text needs to be formatted (if <see cref="NeedsFormat"/> is <c>true</c>)
-		/// <see cref="Format(ustring, int, bool, bool, bool, int, TextDirection)"/> will be called internally. 
+		/// <see cref="Format(string, int, bool, bool, bool, int, TextDirection)"/> will be called internally. 
 		/// </para>
 		/// </remarks>
-		public List<ustring> Lines {
+		public List<string> Lines {
 			get {
 				// With this check, we protect against subclasses with overrides of Text
-				if (ustring.IsNullOrEmpty (Text) || Size.IsEmpty) {
-					_lines = new List<ustring> {
-						ustring.Empty
+				if (string.IsNullOrEmpty (Text) || Size.IsEmpty) {
+					_lines = new List<string> {
+						string.Empty
 					};
 					NeedsFormat = false;
 					return _lines;
@@ -1253,7 +1251,7 @@ namespace Terminal.Gui {
 		public void Draw (Rect bounds, Attribute normalColor, Attribute hotColor, Rect containerBounds = default, bool fillRemaining = true)
 		{
 			// With this check, we protect against subclasses with overrides of Text (like Button)
-			if (ustring.IsNullOrEmpty (_text)) {
+			if (string.IsNullOrEmpty (_text)) {
 				return;
 			}
 
@@ -1322,7 +1320,7 @@ namespace Terminal.Gui {
 						x = bounds.Right - runesWidth;
 						CursorPosition = bounds.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
 					} else {
-						var runesWidth = GetTextWidth (ustring.Make (runes));
+						var runesWidth = StringExtensions.ToString (runes).GetColumns ();
 						x = bounds.Right - runesWidth;
 						CursorPosition = bounds.Width - runesWidth + (_hotKeyPos > -1 ? _hotKeyPos : 0);
 					}
@@ -1340,7 +1338,7 @@ namespace Terminal.Gui {
 						x = bounds.Left + line + ((bounds.Width - runesWidth) / 2);
 						CursorPosition = (bounds.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
 					} else {
-						var runesWidth = GetTextWidth (ustring.Make (runes));
+						var runesWidth = StringExtensions.ToString (runes).GetColumns ();
 						x = bounds.Left + (bounds.Width - runesWidth) / 2;
 						CursorPosition = (bounds.Width - runesWidth) / 2 + (_hotKeyPos > -1 ? _hotKeyPos : 0);
 					}
@@ -1413,13 +1411,13 @@ namespace Terminal.Gui {
 					} else {
 						Application.Driver?.AddRune (rune);
 					}
-					var runeWidth = Math.Max (Rune.ColumnWidth (rune), 1);
+					var runeWidth = Math.Max (rune.GetColumns (), 1);
 					if (isVertical) {
 						current++;
 					} else {
 						current += runeWidth;
 					}
-					var nextRuneWidth = idx + 1 > -1 && idx + 1 < runes.Length ? Rune.ColumnWidth (runes [idx + 1]) : 0;
+					var nextRuneWidth = idx + 1 > -1 && idx + 1 < runes.Length ? runes [idx + 1].GetColumns () : 0;
 					if (!isVertical && idx + 1 < runes.Length && current + nextRuneWidth > start + size) {
 						break;
 					}

+ 10 - 10
Terminal.Gui/View/Frame.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -163,11 +163,11 @@ namespace Terminal.Gui {
 			var topTitleLineY = borderBounds.Y;
 			var titleY = borderBounds.Y;
 			var titleBarsLength = 0; // the little vertical thingies
-			var maxTitleWidth = Math.Min (Parent.Title.ConsoleWidth, Math.Min (screenBounds.Width - 4, borderBounds.Width - 4));
+			var maxTitleWidth = Math.Min (Parent.Title.GetColumns (), Math.Min (screenBounds.Width - 4, borderBounds.Width - 4));
 			var sideLineLength = borderBounds.Height;
 			var canDrawBorder = borderBounds.Width > 0 && borderBounds.Height > 0;
 
-			if (!ustring.IsNullOrEmpty (Parent?.Title)) {
+			if (!string.IsNullOrEmpty (Parent?.Title)) {
 				if (Thickness.Top == 2) {
 					topTitleLineY = borderBounds.Y - 1;
 					titleY = topTitleLineY + 1;
@@ -196,7 +196,7 @@ namespace Terminal.Gui {
 
 			}
 
-			if (Id == "Border" && canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
+			if (Id == "Border" && canDrawBorder && Thickness.Top > 0 && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
 				var prevAttr = Driver.GetAttribute ();
 				if (ColorScheme != null) {
 					Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
@@ -225,7 +225,7 @@ namespace Terminal.Gui {
 				if (drawTop) {
 					// ╔╡Title╞═════╗
 					// ╔╡╞═════╗
-					if (borderBounds.Width < 4 || ustring.IsNullOrEmpty (Parent?.Title)) {
+					if (borderBounds.Width < 4 || string.IsNullOrEmpty (Parent?.Title)) {
 						// ╔╡╞╗ should be ╔══╗
 						lc.AddLine (new Point (borderBounds.Location.X, titleY), borderBounds.Width, Orientation.Horizontal, BorderStyle, Driver.GetAttribute ());
 					} else {
@@ -275,14 +275,14 @@ namespace Terminal.Gui {
 					}
 
 					// Redraw title 
-					if (drawTop && Id == "Border" && maxTitleWidth > 0 && !ustring.IsNullOrEmpty (Parent?.Title)) {
+					if (drawTop && Id == "Border" && maxTitleWidth > 0 && !string.IsNullOrEmpty (Parent?.Title)) {
 						prevAttr = Driver.GetAttribute ();
 						if (ColorScheme != null) {
 							Driver.SetAttribute (HasFocus ? GetHotNormalColor () : GetNormalColor ());
 						} else {
 							Driver.SetAttribute (Parent.HasFocus ? Parent.GetHotNormalColor () : Parent.GetNormalColor ());
 						}
-						DrawTitle (new Rect (borderBounds.X, titleY, Parent.Title.ConsoleWidth, 1), Parent?.Title);
+						DrawTitle (new Rect (borderBounds.X, titleY, Parent.Title.GetColumns (), 1), Parent?.Title);
 						Driver.SetAttribute (prevAttr);
 					}
 
@@ -361,13 +361,13 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="region">Screen relative region where the title will be drawn.</param>
 		/// <param name="title">The title.</param>
-		public void DrawTitle (Rect region, ustring title)
+		public void DrawTitle (Rect region, string title)
 		{
 			var width = region.Width;
-			if (!ustring.IsNullOrEmpty (title)) {
+			if (!string.IsNullOrEmpty (title)) {
 				Driver.Move (region.X + 2, region.Y);
 				//Driver.AddRune (' ');
-				var str = title.Sum (r => Math.Max (Rune.ColumnWidth (r), 1)) >= width
+				var str = title.EnumerateRunes ().Sum (r => Math.Max (r.GetColumns (), 1)) >= width
 					? TextFormatter.Format (title, width, false, false) [0] : title;
 				Driver.AddStr (str);
 			}

+ 4 - 4
Terminal.Gui/View/TitleEventArgs.cs

@@ -1,5 +1,5 @@
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -9,12 +9,12 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The new Window Title.
 		/// </summary>
-		public ustring NewTitle { get; set; }
+		public string NewTitle { get; set; }
 
 		/// <summary>
 		/// The old Window Title.
 		/// </summary>
-		public ustring OldTitle { get; set; }
+		public string OldTitle { get; set; }
 
 		/// <summary>
 		/// Flag which allows canceling the Title change.
@@ -26,7 +26,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
 		/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
-		public TitleEventArgs (ustring oldTitle, ustring newTitle)
+		public TitleEventArgs (string oldTitle, string newTitle)
 		{
 			OldTitle = oldTitle;
 			NewTitle = newTitle;

+ 10 - 10
Terminal.Gui/View/View.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	#region API Docs
@@ -163,7 +163,7 @@ namespace Terminal.Gui {
 		/// <param name="x">column to locate the View.</param>
 		/// <param name="y">row to locate the View.</param>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
-		public View (int x, int y, ustring text) : this (TextFormatter.CalcRect (x, y, text), text) { }
+		public View (int x, int y, string text) : this (TextFormatter.CalcRect (x, y, text), text) { }
 
 		/// <summary>
 		///   Initializes a new instance of <see cref="View"/> using <see cref="Terminal.Gui.LayoutStyle.Absolute"/> layout.
@@ -180,7 +180,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="rect">Location.</param>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
-		public View (Rect rect, ustring text)
+		public View (Rect rect, string text)
 		{
 			SetInitialProperties (text, rect, LayoutStyle.Absolute, TextDirection.LeftRight_TopBottom);
 		}
@@ -200,7 +200,7 @@ namespace Terminal.Gui {
 		/// </remarks>
 		/// <param name="text">text to initialize the <see cref="Text"/> property with.</param>
 		/// <param name="direction">The text direction.</param>
-		public View (ustring text, TextDirection direction = TextDirection.LeftRight_TopBottom)
+		public View (string text, TextDirection direction = TextDirection.LeftRight_TopBottom)
 		{
 			SetInitialProperties (text, Rect.Empty, LayoutStyle.Computed, direction);
 		}
@@ -213,7 +213,7 @@ namespace Terminal.Gui {
 		/// <param name="rect"></param>
 		/// <param name="layoutStyle"></param>
 		/// <param name="direction"></param>
-		void SetInitialProperties (ustring text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed,
+		void SetInitialProperties (string text, Rect rect, LayoutStyle layoutStyle = LayoutStyle.Computed,
 		    TextDirection direction = TextDirection.LeftRight_TopBottom)
 		{
 			TextFormatter = new TextFormatter ();
@@ -226,7 +226,7 @@ namespace Terminal.Gui {
 			TabStop = false;
 			LayoutStyle = layoutStyle;
 
-			Text = text == null ? ustring.Empty : text;
+			Text = text == null ? string.Empty : text;
 			LayoutStyle = layoutStyle;
 			Frame = rect.IsEmpty ? TextFormatter.CalcRect (0, 0, text, direction) : rect;
 			OnResizeNeeded ();
@@ -334,13 +334,13 @@ namespace Terminal.Gui {
 		/// <remarks>The id should be unique across all Views that share a SuperView.</remarks>
 		public string Id { get; set; } = "";
 
-		ustring _title = ustring.Empty;
+		string _title = string.Empty;
 		/// <summary>
 		/// The title to be displayed for this <see cref="View"/>. The title will be displayed if <see cref="Border"/>.<see cref="Thickness.Top"/>
 		/// is greater than 0.
 		/// </summary>
 		/// <value>The title.</value>
-		public ustring Title {
+		public string Title {
 			get => _title;
 			set {
 				if (!OnTitleChanging (_title, value)) {
@@ -363,7 +363,7 @@ namespace Terminal.Gui {
 		/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
 		/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
 		/// <returns>`true` if an event handler canceled the Title change.</returns>
-		public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle)
+		public virtual bool OnTitleChanging (string oldTitle, string newTitle)
 		{
 			var args = new TitleEventArgs (oldTitle, newTitle);
 			TitleChanging?.Invoke (this, args);
@@ -381,7 +381,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="oldTitle">The <see cref="View.Title"/> that is/has been replaced.</param>
 		/// <param name="newTitle">The new <see cref="View.Title"/> to be replaced.</param>
-		public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle)
+		public virtual void OnTitleChanged (string oldTitle, string newTitle)
 		{
 			var args = new TitleEventArgs (oldTitle, newTitle);
 			TitleChanged?.Invoke (this, args);

+ 8 - 8
Terminal.Gui/View/ViewDrawing.cs

@@ -1,6 +1,6 @@
 using System;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	public partial class View {
@@ -155,7 +155,7 @@ namespace Terminal.Gui {
 			for (var line = 0; line < h; line++) {
 				Move (0, line);
 				for (var col = 0; col < w; col++)
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 			}
 		}
 
@@ -174,7 +174,7 @@ namespace Terminal.Gui {
 			for (var line = regionScreen.Y; line < regionScreen.Y + h; line++) {
 				Driver.Move (regionScreen.X, line);
 				for (var col = 0; col < w; col++)
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 			}
 		}
 
@@ -232,16 +232,16 @@ namespace Terminal.Gui {
 		/// <para>The hotkey is any character following the hotkey specifier, which is the underscore ('_') character by default.</para>
 		/// <para>The hotkey specifier can be changed via <see cref="HotKeySpecifier"/></para>
 		/// </remarks>
-		public void DrawHotString (ustring text, Attribute hotColor, Attribute normalColor)
+		public void DrawHotString (string text, Attribute hotColor, Attribute normalColor)
 		{
 			var hotkeySpec = HotKeySpecifier == (Rune)0xffff ? (Rune)'_' : HotKeySpecifier;
 			Application.Driver.SetAttribute (normalColor);
 			foreach (var rune in text) {
-				if (rune == hotkeySpec) {
+				if (rune == hotkeySpec.Value) {
 					Application.Driver.SetAttribute (hotColor);
 					continue;
 				}
-				Application.Driver.AddRune (rune);
+				Application.Driver.AddRune ((Rune)rune);
 				Application.Driver.SetAttribute (normalColor);
 			}
 		}
@@ -252,7 +252,7 @@ namespace Terminal.Gui {
 		/// <param name="text">String to display, the underscore before a letter flags the next letter as the hotkey.</param>
 		/// <param name="focused">If set to <see langword="true"/> 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 (ustring text, bool focused, ColorScheme scheme)
+		public void DrawHotString (string text, bool focused, ColorScheme scheme)
 		{
 			if (focused)
 				DrawHotString (text, scheme.HotFocus, scheme.Focus);
@@ -445,7 +445,7 @@ namespace Terminal.Gui {
 				Clear (ViewToScreen (Bounds));
 			}
 
-			if (!ustring.IsNullOrEmpty (TextFormatter.Text)) {
+			if (!string.IsNullOrEmpty (TextFormatter.Text)) {
 				if (TextFormatter != null) {
 					TextFormatter.NeedsFormat = true;
 				}

+ 2 - 2
Terminal.Gui/View/ViewKeyboard.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	public partial class View  {
@@ -69,7 +69,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The keystroke combination used in the <see cref="Shortcut"/> as string.
 		/// </summary>
-		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (_shortcutHelper.Shortcut);
+		public string ShortcutTag => ShortcutHelper.GetShortcutTag (_shortcutHelper.Shortcut);
 
 		/// <summary>
 		/// The action to run if the <see cref="Shortcut"/> is defined.

+ 3 - 3
Terminal.Gui/View/ViewLayout.cs

@@ -4,7 +4,7 @@ using System.ComponentModel;
 using System.Diagnostics;
 using System.Linq;
 using System.Reflection;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -385,10 +385,10 @@ namespace Terminal.Gui {
 			}
 			size = Bounds.Size;
 
-			if (!AutoSize && !ustring.IsNullOrEmpty (TextFormatter.Text)) {
+			if (!AutoSize && !string.IsNullOrEmpty (TextFormatter.Text)) {
 				switch (TextFormatter.IsVerticalDirection (TextDirection)) {
 				case true:
-					var colWidth = TextFormatter.GetSumMaxCharWidth (new List<ustring> { TextFormatter.Text }, 0, 1);
+					var colWidth = TextFormatter.GetSumMaxCharWidth (new List<string> { TextFormatter.Text }, 0, 1);
 					// TODO: v2 - This uses frame.Width; it should only use Bounds
 					if (_frame.Width < colWidth &&
 						(Width == null ||

+ 1 - 1
Terminal.Gui/View/ViewMouse.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	public partial class View  {

+ 1 - 1
Terminal.Gui/View/ViewSubViews.cs

@@ -2,7 +2,7 @@
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	public partial class View  {

+ 8 - 8
Terminal.Gui/View/ViewText.cs

@@ -1,10 +1,10 @@
-using NStack;
+using System.Text;
 using System;
 
 namespace Terminal.Gui {
 
 	public partial class View {
-		ustring _text;
+		string _text;
 
 		/// <summary>
 		///   The text displayed by the <see cref="View"/>.
@@ -26,7 +26,7 @@ namespace Terminal.Gui {
 		///  <c>(Rune)0xffff</c>.
 		/// </para>
 		/// </remarks>
-		public virtual ustring Text {
+		public virtual string Text {
 			get => _text;
 			set {
 				_text = value;
@@ -154,12 +154,12 @@ namespace Terminal.Gui {
 		{
 			if (isWidth) {
 				return TextFormatter.IsHorizontalDirection (TextDirection) &&
-				    TextFormatter.Text?.Contains (HotKeySpecifier) == true
-				    ? Math.Max (Rune.ColumnWidth (HotKeySpecifier), 0) : 0;
+				    TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+				    ? Math.Max (HotKeySpecifier.GetColumns (), 0) : 0;
 			} else {
 				return TextFormatter.IsVerticalDirection (TextDirection) &&
-				    TextFormatter.Text?.Contains (HotKeySpecifier) == true
-				    ? Math.Max (Rune.ColumnWidth (HotKeySpecifier), 0) : 0;
+				    TextFormatter.Text?.Contains ((char)HotKeySpecifier.Value) == true
+				    ? Math.Max (HotKeySpecifier.GetColumns(), 0) : 0;
 			}
 		}
 
@@ -179,7 +179,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public Size GetSizeNeededForTextAndHotKey ()
 		{
-			if (ustring.IsNullOrEmpty (TextFormatter.Text)) {
+			if (string.IsNullOrEmpty (TextFormatter.Text)) {
 
 				if (!IsInitialized) return Size.Empty;
 

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

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -10,8 +10,8 @@ namespace Terminal.Gui {
 	internal class AutocompleteFilepathContext : AutocompleteContext {
 		public FileDialogState State { get; set; }
 
-		public AutocompleteFilepathContext (ustring currentLine, int cursorPosition, FileDialogState state)
-			: base (currentLine.ToRuneList (), cursorPosition)
+		public AutocompleteFilepathContext (string currentLine, int cursorPosition, FileDialogState state)
+			: base (currentLine.EnumerateRunes ().ToList (), cursorPosition)
 		{
 			this.State = state;
 		}
@@ -30,18 +30,17 @@ namespace Terminal.Gui {
 				return Enumerable.Empty<Suggestion> ();
 			}
 
-			var path = ustring.Make (context.CurrentLine).ToString ();
+			var path = StringExtensions.ToString (context.CurrentLine);
 			var last = path.LastIndexOfAny (FileDialog.Separators);
-			
-			if(string.IsNullOrWhiteSpace(path) || !Path.IsPathRooted(path)) {
+
+			if (string.IsNullOrWhiteSpace (path) || !Path.IsPathRooted (path)) {
 				return Enumerable.Empty<Suggestion> ();
 			}
 
 			var term = path.Substring (last + 1);
-			
+
 			// If path is /tmp/ then don't just list everything in it
-			if(string.IsNullOrWhiteSpace(term))
-			{
+			if (string.IsNullOrWhiteSpace (term)) {
 				return Enumerable.Empty<Suggestion> ();
 			}
 
@@ -52,7 +51,7 @@ namespace Terminal.Gui {
 
 			bool isWindows = RuntimeInformation.IsOSPlatform (OSPlatform.Windows);
 
-			var suggestions = state.Children.Where(d=> !d.IsParent).Select (
+			var suggestions = state.Children.Where (d => !d.IsParent).Select (
 				e => e.FileSystemInfo is IDirectoryInfo d
 					? d.Name + System.IO.Path.DirectorySeparatorChar
 					: e.FileSystemInfo.Name)
@@ -76,7 +75,7 @@ namespace Terminal.Gui {
 
 		public bool IsWordChar (Rune rune)
 		{
-			if (rune == '\n') {
+			if (rune.Value == '\n') {
 				return false;
 			}
 

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

@@ -6,7 +6,7 @@
 //
 
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -58,7 +58,7 @@ namespace Terminal.Gui {
 		///   If <c>true</c>, a special decoration is used, and the user pressing the enter key 
 		///   in a <see cref="Dialog"/> will implicitly activate this button.
 		/// </param>
-		public Button (ustring text, bool is_default = false) : base (text)
+		public Button (string text, bool is_default = false) : base (text)
 		{
 			SetInitialProperties (text, is_default);
 		}
@@ -73,7 +73,7 @@ namespace Terminal.Gui {
 		/// <param name="x">X position where the button will be shown.</param>
 		/// <param name="y">Y position where the button will be shown.</param>
 		/// <param name="text">The button's text</param>
-		public Button (int x, int y, ustring text) : this (x, y, text, false) { }
+		public Button (int x, int y, string text) : this (x, y, text, false) { }
 
 		/// <summary>
 		///   Initializes a new instance of <see cref="Button"/> using <see cref="LayoutStyle.Absolute"/> layout, based on the given text.
@@ -89,8 +89,8 @@ namespace Terminal.Gui {
 		///   If <c>true</c>, a special decoration is used, and the user pressing the enter key 
 		///   in a <see cref="Dialog"/> will implicitly activate this button.
 		/// </param>
-		public Button (int x, int y, ustring text, bool is_default)
-		    : base (new Rect (x, y, text.RuneCount + 4 + (is_default ? 2 : 0), 1), text)
+		public Button (int x, int y, string text, bool is_default)
+		    : base (new Rect (x, y, text.GetRuneCount () + 4 + (is_default ? 2 : 0), 1), text)
 		{
 			SetInitialProperties (text, is_default);
 		}
@@ -100,7 +100,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="text"></param>
 		/// <param name="is_default"></param>
-		void SetInitialProperties (ustring text, bool is_default)
+		void SetInitialProperties (string text, bool is_default)
 		{
 			TextAlignment = TextAlignment.Centered;
 			VerticalTextAlignment = VerticalTextAlignment.Middle;
@@ -180,15 +180,14 @@ namespace Terminal.Gui {
 				TextFormatter.Text = Text;
 			} else
 			if (IsDefault)
-				TextFormatter.Text = ustring.Make (_leftBracket) + ustring.Make (_leftDefault) + " " + Text + " " + ustring.Make (_rightDefault) + ustring.Make (_rightBracket);
+				TextFormatter.Text = $"{_leftBracket}{_leftDefault} {Text} {_rightDefault}{_rightBracket}";
 			else {
 				if (NoPadding) {
-					TextFormatter.Text = ustring.Make (_leftBracket) + Text + ustring.Make (_rightBracket);
+					TextFormatter.Text = $"{_leftBracket}{Text}{_rightBracket}";
 				} else {
-					TextFormatter.Text = ustring.Make (_leftBracket) + " " + Text + " " + ustring.Make (_rightBracket);
+					TextFormatter.Text = $"{_leftBracket} {Text} {_rightBracket}";
 				}
 			}
-
 		}
 
 		///<inheritdoc/>
@@ -291,7 +290,7 @@ namespace Terminal.Gui {
 		public override void PositionCursor ()
 		{
 			if (HotKey == Key.Unknown && Text != "") {
-				for (int i = 0; i < TextFormatter.Text.RuneCount; i++) {
+				for (int i = 0; i < TextFormatter.Text.GetRuneCount (); i++) {
 					if (TextFormatter.Text [i] == Text [0]) {
 						Move (i, 0);
 						return;

+ 13 - 13
Terminal.Gui/Views/CheckBox.cs

@@ -5,7 +5,7 @@
 //   Miguel de Icaza ([email protected])
 //
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 
@@ -47,7 +47,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="s">S.</param>
 		/// <param name="is_checked">If set to <c>true</c> is checked.</param>
-		public CheckBox (ustring s, bool is_checked = false) : base ()
+		public CheckBox (string s, bool is_checked = false) : base ()
 		{
 			SetInitialProperties (s, is_checked);
 		}
@@ -59,7 +59,7 @@ namespace Terminal.Gui {
 		///   The size of <see cref="CheckBox"/> is computed based on the
 		///   text length. This <see cref="CheckBox"/> is not toggled.
 		/// </remarks>
-		public CheckBox (int x, int y, ustring s) : this (x, y, s, false)
+		public CheckBox (int x, int y, string s) : this (x, y, s, false)
 		{
 		}
 
@@ -70,7 +70,7 @@ namespace Terminal.Gui {
 		///   The size of <see cref="CheckBox"/> is computed based on the
 		///   text length. 
 		/// </remarks>
-		public CheckBox (int x, int y, ustring s, bool is_checked) : base (new Rect (x, y, s.Length, 1))
+		public CheckBox (int x, int y, string s, bool is_checked) : base (new Rect (x, y, s.Length, 1))
 		{
 			SetInitialProperties (s, is_checked);
 		}
@@ -81,17 +81,17 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="s"></param>
 		/// <param name="is_checked"></param>
-		void SetInitialProperties (ustring s, bool is_checked)
+		void SetInitialProperties (string s, bool is_checked)
 		{
 			charNullChecked = CM.Glyphs.NullChecked;
 			charChecked = CM.Glyphs.Checked;
 			charUnChecked = CM.Glyphs.UnChecked;
 			Checked = is_checked;
-			HotKeySpecifier = '_';
+			HotKeySpecifier = (Rune)'_';
 			CanFocus = true;
 			AutoSize = true;
 			Text = s;
-			
+
 			OnResizeNeeded ();
 
 			// Things this view knows how to do
@@ -109,10 +109,10 @@ namespace Terminal.Gui {
 			case TextAlignment.Left:
 			case TextAlignment.Centered:
 			case TextAlignment.Justified:
-				TextFormatter.Text = ustring.Make (GetCheckedState ()) + " " + GetFormatterText ();
+				TextFormatter.Text = $"{GetCheckedState ()} {GetFormatterText ()}";
 				break;
 			case TextAlignment.Right:
-				TextFormatter.Text = GetFormatterText () + " " + ustring.Make (GetCheckedState ());
+				TextFormatter.Text = $"{GetFormatterText ()} {GetCheckedState ()}";
 				break;
 			}
 		}
@@ -126,12 +126,12 @@ namespace Terminal.Gui {
 			}
 		}
 
-		ustring GetFormatterText ()
+		string GetFormatterText ()
 		{
-			if (AutoSize || ustring.IsNullOrEmpty (Text) || Frame.Width <= 2) {
+			if (AutoSize || string.IsNullOrEmpty (Text) || Frame.Width <= 2) {
 				return Text;
 			}
-			return Text.RuneSubstring (0, Math.Min (Frame.Width - 2, Text.RuneCount));
+			return Text [..Math.Min (Frame.Width - 2, Text.GetRuneCount ())];
 		}
 
 		/// <summary>
@@ -209,7 +209,7 @@ namespace Terminal.Gui {
 			} else {
 				Checked = !Checked;
 			}
-			
+
 			OnToggled (new ToggleEventArgs (previousChecked, Checked));
 			SetNeedsDisplay ();
 			return true;

+ 4 - 6
Terminal.Gui/Views/ColorPicker.cs

@@ -1,7 +1,5 @@
 using System;
-using System.Reflection;
-using NStack;
-using static Terminal.Gui.SpinnerStyle;
+using System.Text;
 
 namespace Terminal.Gui {
 
@@ -75,8 +73,8 @@ namespace Terminal.Gui {
 		// Cursor runes.
 		private static readonly Rune [] _cursorRunes = new Rune []
 		{
-			0x250C, 0x2500, 0x2500, 0x2510,
-			0x2514, 0x2500, 0x2500, 0x2518
+			(Rune)0x250C, (Rune) 0x2500, (Rune) 0x2500, (Rune) 0x2510,
+			(Rune) 0x2514, (Rune) 0x2500, (Rune) 0x2500, (Rune) 0x2518
 		};
 
 		/// <summary>
@@ -189,7 +187,7 @@ namespace Terminal.Gui {
 			for (var zoomedY = 0; zoomedY < BoxHeight; zoomedY++) {
 				for (var zoomedX = 0; zoomedX < BoxWidth; zoomedX++) {
 					Move (x * BoxWidth + zoomedX, y * BoxHeight + zoomedY);
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 					index++;
 				}
 			}

+ 14 - 12
Terminal.Gui/Views/ComboBox.cs

@@ -8,7 +8,7 @@
 using System;
 using System.Collections;
 using System.Collections.Generic;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -122,7 +122,7 @@ namespace Terminal.Gui {
 					Move (0, row);
 					if (Source == null || item >= Source.Count) {
 						for (int c = 0; c < f.Width; c++)
-							Driver.AddRune (' ');
+							Driver.AddRune ((Rune)' ');
 					} else {
 						var rowEventArgs = new ListViewRowEventArgs (item);
 						OnRowRender (rowEventArgs);
@@ -132,7 +132,7 @@ namespace Terminal.Gui {
 						}
 						if (AllowsMarking) {
 							Driver.AddRune (Source.IsMarked (item) ? (AllowsMultipleSelection ? CM.Glyphs.Checked : CM.Glyphs.Selected) : (AllowsMultipleSelection ? CM.Glyphs.UnChecked : CM.Glyphs.UnSelected));
-							Driver.AddRune (' ');
+							Driver.AddRune ((Rune)' ');
 						}
 						Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
 					}
@@ -232,7 +232,7 @@ namespace Terminal.Gui {
 		public event EventHandler<ListViewItemEventArgs> OpenSelectedItem;
 
 		readonly IList searchset = new List<object> ();
-		ustring text = "";
+		string text = "";
 		readonly TextField search;
 		readonly ComboListView listview;
 		bool autoHide = true;
@@ -249,7 +249,7 @@ namespace Terminal.Gui {
 		/// Public constructor
 		/// </summary>
 		/// <param name="text"></param>
-		public ComboBox (ustring text) : base ()
+		public ComboBox (string text) : base ()
 		{
 			search = new TextField ("");
 			listview = new ComboListView (this, HideDropdownListOnClick) { LayoutStyle = LayoutStyle.Computed, CanFocus = true, TabStop = false };
@@ -484,7 +484,7 @@ namespace Terminal.Gui {
 				search.SetFocus ();
 			}
 
-			search.CursorPosition = search.Text.RuneCount;
+			search.CursorPosition = search.Text.GetRuneCount ();
 
 			return base.OnEnter (view);
 		}
@@ -627,7 +627,7 @@ namespace Terminal.Gui {
 
 			if (listview.HasFocus && listview.SelectedItem == 0 && searchset?.Count > 0) // jump back to search
 			{
-				search.CursorPosition = search.Text.RuneCount;
+				search.CursorPosition = search.Text.GetRuneCount ();
 				search.SetFocus ();
 				return true;
 			}
@@ -640,7 +640,9 @@ namespace Terminal.Gui {
 				if (searchset?.Count > 0) {
 					listview.TabStop = true;
 					listview.SetFocus ();
-					SetValue (searchset [listview.SelectedItem]);
+					if (listview.SelectedItem > -1) {
+						SetValue (searchset [listview.SelectedItem]);
+					}
 				} else {
 					listview.TabStop = false;
 					SuperView?.FocusNext ();
@@ -716,7 +718,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The currently selected list item
 		/// </summary>
-		public new ustring Text {
+		public new string Text {
 			get {
 				return text;
 			}
@@ -750,7 +752,7 @@ namespace Terminal.Gui {
 			}
 
 			SetValue (listview.SelectedItem > -1 ? searchset [listview.SelectedItem] : text);
-			search.CursorPosition = search.Text.ConsoleWidth;
+			search.CursorPosition = search.Text.GetColumns ();
 			Search_Changed (this, new TextChangedEventArgs (search.Text));
 			OnOpenSelectedItem ();
 			Reset (keepSearchText: true);
@@ -758,7 +760,7 @@ namespace Terminal.Gui {
 			isShow = false;
 		}
 
-		private int GetSelectedItemFromSource (ustring value)
+		private int GetSelectedItemFromSource (string value)
 		{
 			if (source == null) {
 				return -1;
@@ -814,7 +816,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			if (ustring.IsNullOrEmpty (search.Text) && ustring.IsNullOrEmpty (e.OldValue)) {
+			if (string.IsNullOrEmpty (search.Text) && string.IsNullOrEmpty (e.OldValue)) {
 				ResetSearchSet ();
 			} else if (search.Text != e.OldValue) {
 				isShow = true;

+ 26 - 27
Terminal.Gui/Views/DateField.cs

@@ -8,7 +8,7 @@
 using System;
 using System.Globalization;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -123,13 +123,13 @@ namespace Terminal.Gui {
 
 		string GetLongFormat (string lf)
 		{
-			ustring [] frm = ustring.Make (lf).Split (ustring.Make (sepChar));
+			string [] frm = lf.Split (sepChar);
 			for (int i = 0; i < frm.Length; i++) {
-				if (frm [i].Contains ("M") && frm [i].RuneCount < 2)
+				if (frm [i].Contains ("M") && frm [i].GetRuneCount () < 2)
 					lf = lf.Replace ("M", "MM");
-				if (frm [i].Contains ("d") && frm [i].RuneCount < 2)
+				if (frm [i].Contains ("d") && frm [i].GetRuneCount () < 2)
 					lf = lf.Replace ("d", "dd");
-				if (frm [i].Contains ("y") && frm [i].RuneCount < 4)
+				if (frm [i].Contains ("y") && frm [i].GetRuneCount () < 4)
 					lf = lf.Replace ("yy", "yyyy");
 			}
 			return $" {lf}";
@@ -198,17 +198,17 @@ namespace Terminal.Gui {
 			newText.Add (key);
 			if (CursorPosition < fieldLen)
 				newText = newText.Concat (text.GetRange (CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList ();
-			return SetText (ustring.Make (newText));
+			return SetText (StringExtensions.ToString (newText));
 		}
 
-		bool SetText (ustring text)
+		bool SetText (string text)
 		{
-			if (text.IsEmpty) {
+			if (string.IsNullOrEmpty (text)) {
 				return false;
 			}
 
-			ustring [] vals = text.Split (ustring.Make (sepChar));
-			ustring [] frm = ustring.Make (format).Split (ustring.Make (sepChar));
+			string [] vals = text.Split (sepChar);
+			string [] frm = format.Split (sepChar);
 			bool isValidDate = true;
 			int idx = GetFormatIndex (frm, "y");
 			int year = Int32.Parse (vals [idx].ToString ());
@@ -245,7 +245,7 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		string GetDate (int month, int day, int year, ustring [] fm)
+		string GetDate (int month, int day, int year, string [] fm)
 		{
 			string date = " ";
 			for (int i = 0; i < fm.Length; i++) {
@@ -269,32 +269,31 @@ namespace Terminal.Gui {
 			return date;
 		}
 
-		ustring GetDate (ustring text)
+		string GetDate (string text)
 		{
-			ustring [] vals = text.Split (ustring.Make (sepChar));
-			ustring [] frm = ustring.Make (format).Split (ustring.Make (sepChar));
-			ustring [] date = { null, null, null };
+			string [] vals = text.Split (sepChar);
+			string [] frm = format.Split (sepChar);
+			string [] date = { null, null, null };
 
 			for (int i = 0; i < frm.Length; i++) {
 				if (frm [i].Contains ("M")) {
-					date [0] = vals [i].TrimSpace ();
+					date [0] = vals [i].Trim ();
 				} else if (frm [i].Contains ("d")) {
-					date [1] = vals [i].TrimSpace ();
+					date [1] = vals [i].Trim ();
 				} else {
-					var year = vals [i].TrimSpace ();
-					if (year.RuneCount == 2) {
+					var year = vals [i].Trim ();
+					if (year.GetRuneCount () == 2) {
 						var y = DateTime.Now.Year.ToString ();
 						date [2] = y.Substring (0, 2) + year.ToString ();
 					} else {
-						date [2] = vals [i].TrimSpace ();
+						date [2] = vals [i].Trim ();
 					}
 				}
 			}
-			return date [0] + ustring.Make (sepChar) + date [1] + ustring.Make (sepChar) + date [2];
-
+			return date [0] + sepChar + date [1] + sepChar + date [2];
 		}
 
-		int GetFormatIndex (ustring [] fm, string t)
+		int GetFormatIndex (string [] fm, string t)
 		{
 			int idx = -1;
 			for (int i = 0; i < fm.Length; i++) {
@@ -342,7 +341,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return true;
 
-			if (SetText (TextModel.ToRunes (ustring.Make ((uint)kb.Key)).First ()))
+			if (SetText (TextModel.ToRunes (((Rune)(uint)kb.Key).ToString ()).First ()))
 				IncCursorPosition ();
 
 			return true;
@@ -379,7 +378,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return;
 
-			SetText ('0');
+			SetText ((Rune)'0');
 			DecCursorPosition ();
 			return;
 		}
@@ -390,7 +389,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return;
 
-			SetText ('0');
+			SetText ((Rune)'0');
 			return;
 		}
 
@@ -418,7 +417,7 @@ namespace Terminal.Gui {
 		/// <param name="args">Event arguments</param>
 		public virtual void OnDateChanged (DateTimeEventArgs<DateTime> args)
 		{
-			DateChanged?.Invoke (this,args);
+			DateChanged?.Invoke (this, args);
 		}
 	}
 }

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

@@ -8,7 +8,7 @@ using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text.Json.Serialization;
-using NStack;
+using System.Text;
 using Terminal.Gui;
 using static Terminal.Gui.ConfigurationManager;
 

+ 7 - 7
Terminal.Gui/Views/FileDialog.cs

@@ -7,7 +7,7 @@ using System.Linq;
 using System.Text.RegularExpressions;
 using System.Threading;
 using System.Threading.Tasks;
-using NStack;
+using System.Text;
 using Terminal.Gui.Resources;
 using static Terminal.Gui.ConfigurationManager;
 
@@ -413,8 +413,8 @@ namespace Terminal.Gui {
 		private string GetToggleSplitterText (bool isExpanded)
 		{
 			return isExpanded ?
-				new string ((char)CM.Glyphs.LeftArrow, 2) :
-				new string ((char)CM.Glyphs.RightArrow, 2);
+				new string ((char)CM.Glyphs.LeftArrow.Value, 2) :
+				new string ((char)CM.Glyphs.RightArrow.Value, 2);
 		}
 
 		private void Delete ()
@@ -549,7 +549,7 @@ namespace Terminal.Gui {
 		/// is true.
 		/// </summary>
 		public string Path {
-			get => this.tbPath.Text.ToString ();
+			get => this.tbPath.Text;
 			set {
 				this.tbPath.Text = value;
 				this.tbPath.MoveEnd ();
@@ -600,7 +600,7 @@ namespace Terminal.Gui {
 			base.OnDrawContent (contentArea);
 
 			if (!string.IsNullOrWhiteSpace (feedback)) {
-				var feedbackWidth = feedback.Sum (c => Rune.ColumnWidth (c));
+				var feedbackWidth = feedback.EnumerateRunes ().Sum (c => c.GetColumns ());
 				var feedbackPadLeft = ((Bounds.Width - feedbackWidth) / 2) - 1;
 
 				feedbackPadLeft = Math.Min (Bounds.Width, feedbackPadLeft);
@@ -692,7 +692,7 @@ namespace Terminal.Gui {
 			this.tbPath.FocusFirst ();
 			this.tbPath.SelectAll ();
 
-			if (ustring.IsNullOrEmpty (Title)) {
+			if (string.IsNullOrEmpty (Title)) {
 				switch (OpenMode) {
 				case OpenMode.File:
 					this.Title = $"{Strings.fdOpen} {(MustExist ? Strings.fdExisting + " " : "")}{Strings.fdFile}";
@@ -802,7 +802,7 @@ namespace Terminal.Gui {
 				return;
 			}
 
-			if (!this.IsCompatibleWithOpenMode (this.tbPath.Text.ToString (), out string reason)) {
+			if (!this.IsCompatibleWithOpenMode (this.tbPath.Text, out string reason)) {
 				if (reason != null) {
 					feedback = reason;
 					SetNeedsDisplay ();

+ 4 - 4
Terminal.Gui/Views/FrameView.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Linq;
 using System.Text.Json.Serialization;
-using NStack;
+using System.Text;
 using static Terminal.Gui.ConfigurationManager;
 
 namespace Terminal.Gui {
@@ -16,7 +16,7 @@ namespace Terminal.Gui {
 		/// <param name="frame">Frame.</param>
 		/// <param name="title">Title.</param>
 		/// <param name="views">Views.</param>
-		public FrameView (Rect frame, ustring title = null, View [] views = null) : base (frame)
+		public FrameView (Rect frame, string title = null, View [] views = null) : base (frame)
 		{
 			SetInitialProperties (frame, title, views);
 		}
@@ -25,7 +25,7 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of the <see cref="Gui.FrameView"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
 		/// <param name="title">Title.</param>
-		public FrameView (ustring title)
+		public FrameView (string title)
 		{
 			SetInitialProperties (Rect.Empty, title, null);
 		}
@@ -46,7 +46,7 @@ namespace Terminal.Gui {
 		[SerializableConfigurationProperty (Scope = typeof (ThemeScope)), JsonConverter (typeof (JsonStringEnumConverter))]
 		public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 
-		void SetInitialProperties (Rect frame, ustring title, View [] views = null)
+		void SetInitialProperties (Rect frame, string title, View [] views = null)
 		{
 			this.Title = title;
 			Border.Thickness = new Thickness (1);

+ 1 - 1
Terminal.Gui/Views/GraphView/Annotations.cs

@@ -1,6 +1,6 @@
 using System;
 using System.Collections.Generic;
-using System.Linq;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 2 - 1
Terminal.Gui/Views/GraphView/Axis.cs

@@ -1,5 +1,6 @@
 using System;
 using System.Collections.Generic;
+using System.Text;
 
 namespace Terminal.Gui {
 
@@ -402,7 +403,7 @@ namespace Terminal.Gui {
 				for (int i = 0; i < toRender.Length; i++) {
 
 					graph.Move (0, startDrawingAtY + i);
-					Application.Driver.AddRune (toRender [i]);
+					Application.Driver.AddRune ((Rune)toRender [i]);
 				}
 
 			}

+ 1 - 0
Terminal.Gui/Views/GraphView/GraphCellToRender.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 2 - 2
Terminal.Gui/Views/GraphView/GraphView.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -156,7 +156,7 @@ namespace Terminal.Gui {
 
 			if (AxisX.Visible && AxisY.Visible) {
 				Move (axisIntersection.X, axisIntersection.Y);
-				AddRune (axisIntersection.X, axisIntersection.Y, '\u253C');
+				AddRune (axisIntersection.X, axisIntersection.Y, (Rune)'\u253C');
 			}
 
 			SetDriverColorToGraphColor ();

+ 1 - 0
Terminal.Gui/Views/GraphView/Series.cs

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

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

@@ -8,6 +8,7 @@
 using System;
 using System.Collections.Generic;
 using System.IO;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -238,7 +239,7 @@ namespace Terminal.Gui {
 
 						Driver.AddStr (offset >= n && !edited ? "  " : string.Format ("{0:x2}", value));
 						SetAttribute (GetNormalColor ());
-						Driver.AddRune (' ');
+						Driver.AddRune ((Rune)' ');
 					}
 					Driver.AddStr (block + 1 == nblocks ? " " : "| ");
 				}
@@ -248,14 +249,14 @@ namespace Terminal.Gui {
 					var b = GetData (data, offset, out bool edited);
 					Rune c;
 					if (offset >= n && !edited)
-						c = ' ';
+						c = (Rune)' ';
 					else {
 						if (b < 32)
-							c = '.';
+							c = (Rune)'.';
 						else if (b > 127)
-							c = '.';
+							c = (Rune)'.';
 						else
-							c = b;
+							Rune.DecodeFromUtf8 (new ReadOnlySpan<byte> (b), out c, out _);
 					}
 					if (offset + displayStart == position || edited)
 						SetAttribute (leftSide ? trackingColor : activeColor);

+ 1 - 1
Terminal.Gui/Views/HistoryTextItem.cs

@@ -1,7 +1,7 @@
 // TextView.cs: multi-line text editing
 using System;
 using System.Collections.Generic;
-using Rune = System.Rune;
+using System.Text;
 
 namespace Terminal.Gui {
 	partial class HistoryText {

+ 5 - 5
Terminal.Gui/Views/Label.cs

@@ -6,7 +6,7 @@
 //
 
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -30,25 +30,25 @@ namespace Terminal.Gui {
 		}
 
 		/// <inheritdoc/>
-		public Label (ustring text, bool autosize = true) : base (text)
+		public Label (string text, bool autosize = true) : base (text)
 		{
 			SetInitialProperties (autosize);
 		}
 
 		/// <inheritdoc/>
-		public Label (Rect rect, ustring text, bool autosize = false) : base (rect, text)
+		public Label (Rect rect, string text, bool autosize = false) : base (rect, text)
 		{
 			SetInitialProperties (autosize);
 		}
 
 		/// <inheritdoc/>
-		public Label (int x, int y, ustring text, bool autosize = true) : base (x, y, text)
+		public Label (int x, int y, string text, bool autosize = true) : base (x, y, text)
 		{
 			SetInitialProperties (autosize);
 		}
 
 		/// <inheritdoc/>
-		public Label (ustring text, TextDirection direction, bool autosize = true)
+		public Label (string text, TextDirection direction, bool autosize = true)
 			: base (text, direction)
 		{
 			SetInitialProperties (autosize);

+ 2 - 1
Terminal.Gui/Views/LineView.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Text;
 
 namespace Terminal.Gui {
 
@@ -73,7 +74,7 @@ namespace Terminal.Gui {
 			Move (0, 0);
 			Driver.SetAttribute (GetNormalColor ());
 
-			var hLineWidth = Math.Max (1, Rune.ColumnWidth (CM.Glyphs.HLine));
+			var hLineWidth = Math.Max (1, CM.Glyphs.HLine.GetColumns ());
 
 			var dEnd = Orientation == Orientation.Horizontal ?
 				Bounds.Width :

+ 13 - 13
Terminal.Gui/Views/ListView.cs

@@ -4,7 +4,7 @@ using System.Collections.Generic;
 using System.Linq;
 using System.Threading;
 using System.Threading.Tasks;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -76,8 +76,8 @@ namespace Terminal.Gui {
 	/// </para>
 	/// <para>
 	///   <see cref="ListView"/> can display any object that implements the <see cref="IList"/> interface.
-	///   <see cref="string"/> values are converted into <see cref="NStack.ustring"/> values before rendering, and other values are
-	///   converted into <see cref="string"/> by calling <see cref="object.ToString"/> and then converting to <see cref="NStack.ustring"/> .
+	///   <see cref="string"/> values are converted into <see cref="string"/> values before rendering, and other values are
+	///   converted into <see cref="string"/> by calling <see cref="object.ToString"/> and then converting to <see cref="string"/> .
 	/// </para>
 	/// <para>
 	///   To change the contents of the ListView, set the <see cref="Source"/> property (when 
@@ -376,7 +376,7 @@ namespace Terminal.Gui {
 				Move (0, row);
 				if (source == null || item >= source.Count) {
 					for (int c = 0; c < f.Width; c++)
-						Driver.AddRune (' ');
+						Driver.AddRune ((Rune)' ');
 				} else {
 					var rowEventArgs = new ListViewRowEventArgs (item);
 					OnRowRender (rowEventArgs);
@@ -387,7 +387,7 @@ namespace Terminal.Gui {
 					if (allowsMarking) {
 						Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? CM.Glyphs.Checked : CM.Glyphs.Selected) :
 							(AllowsMultipleSelection ? CM.Glyphs.UnChecked : CM.Glyphs.UnSelected));
-						Driver.AddRune (' ');
+						Driver.AddRune ((Rune)' ');
 					}
 					Source.Render (this, Driver, isSelected, item, col, row, f.Width - col, start);
 				}
@@ -853,8 +853,8 @@ namespace Terminal.Gui {
 			for (int i = 0; i < src.Count; i++) {
 				var t = src [i];
 				int l;
-				if (t is ustring u) {
-					l = TextFormatter.GetTextWidth (u);
+				if (t is string u) {
+					l = u.GetColumns ();
 				} else if (t is string s) {
 					l = s.Length;
 				} else {
@@ -869,13 +869,13 @@ namespace Terminal.Gui {
 			return maxLength;
 		}
 
-		void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
+		void RenderUstr (ConsoleDriver driver, string ustr, int col, int line, int width, int start = 0)
 		{
 			var u = TextFormatter.ClipAndJustify (ustr, width, TextAlignment.Left);
 			driver.AddStr (u);
-			width -= TextFormatter.GetTextWidth (u);
+			width -= u.GetColumns ();
 			while (width-- > 0) {
-				driver.AddRune (' ');
+				driver.AddRune ((Rune)' ');
 			}
 		}
 
@@ -885,9 +885,9 @@ namespace Terminal.Gui {
 			container.Move (col, line);
 			var t = src? [item];
 			if (t == null) {
-				RenderUstr (driver, ustring.Make (""), col, line, width);
+				RenderUstr (driver, "", col, line, width);
 			} else {
-				if (t is ustring u) {
+				if (t is string u) {
 					RenderUstr (driver, u, col, line, width, start);
 				} else if (t is string s) {
 					RenderUstr (driver, s, col, line, width, start);
@@ -927,7 +927,7 @@ namespace Terminal.Gui {
 
 			for (int i = 0; i < src.Count; i++) {
 				var t = src [i];
-				if (t is ustring u) {
+				if (t is string u) {
 					if (u.ToUpper ().StartsWith (search.ToUpperInvariant ())) {
 						return i;
 					}

+ 42 - 42
Terminal.Gui/Views/Menu.cs

@@ -1,5 +1,5 @@
 using System;
-using NStack;
+using System.Text;
 using System.Linq;
 using System.Collections.Generic;
 
@@ -31,7 +31,7 @@ namespace Terminal.Gui {
 	/// MenuItems can also have a checked indicator (see <see cref="Checked"/>).
 	/// </summary>
 	public class MenuItem {
-		ustring title;
+		string title;
 		ShortcutHelper shortcutHelper;
 		bool allowNullChecked;
 		MenuItemCheckStyle checkType;
@@ -58,7 +58,7 @@ namespace Terminal.Gui {
 		/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
 		/// <param name="parent">The <see cref="Parent"/> of this menu item.</param>
 		/// <param name="shortcut">The <see cref="Shortcut"/> keystroke combination.</param>
-		public MenuItem (ustring title, ustring help, Action action, Func<bool> canExecute = null, MenuItem parent = null, Key shortcut = Key.Null)
+		public MenuItem (string title, string help, Action action, Func<bool> canExecute = null, MenuItem parent = null, Key shortcut = Key.Null)
 		{
 			Title = title ?? "";
 			Help = help ?? "";
@@ -107,13 +107,13 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Gets the text describing the keystroke combination defined by <see cref="Shortcut"/>.
 		/// </summary>
-		public ustring ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
+		public string ShortcutTag => ShortcutHelper.GetShortcutTag (shortcutHelper.Shortcut);
 
 		/// <summary>
 		/// Gets or sets the title of the menu item .
 		/// </summary>
 		/// <value>The title.</value>
-		public ustring Title {
+		public string Title {
 			get { return title; }
 			set {
 				if (title != value) {
@@ -127,7 +127,7 @@ namespace Terminal.Gui {
 		/// Gets or sets the help text for the menu item. The help text is drawn to the right of the <see cref="Title"/>.
 		/// </summary>
 		/// <value>The help text.</value>
-		public ustring Help { get; set; }
+		public string Help { get; set; }
 
 		/// <summary>
 		/// Gets or sets the action to be invoked when the menu item is triggered.
@@ -162,8 +162,8 @@ namespace Terminal.Gui {
 			TitleLength +
 			2 + // space after Title - BUGBUG: This should be 1 
 			(Checked == true || CheckType.HasFlag (MenuItemCheckStyle.Checked) || CheckType.HasFlag (MenuItemCheckStyle.Radio) ? 2 : 0) + // check glyph + space 
-			(Help.ConsoleWidth > 0 ? 2 + Help.ConsoleWidth : 0) + // Two spaces before Help
-			(ShortcutTag.ConsoleWidth > 0 ? 2 + ShortcutTag.ConsoleWidth : 0); // Pad two spaces before shortcut tag (which are also aligned right)
+			(Help.GetColumns () > 0 ? 2 + Help.GetColumns () : 0) + // Two spaces before Help
+			(ShortcutTag.GetColumns () > 0 ? 2 + ShortcutTag.GetColumns () : 0); // Pad two spaces before shortcut tag (which are also aligned right)
 
 		/// <summary>
 		/// Sets or gets whether the <see cref="MenuItem"/> shows a check indicator or not. See <see cref="MenuItemCheckStyle"/>.
@@ -256,11 +256,11 @@ namespace Terminal.Gui {
 		{
 			bool nextIsHot = false;
 			foreach (var x in title) {
-				if (x == MenuBar.HotKeySpecifier) {
+				if (x == MenuBar.HotKeySpecifier.Value) {
 					nextIsHot = true;
 				} else {
 					if (nextIsHot) {
-						HotKey = Char.ToUpper ((char)x);
+						HotKey = (Rune)Char.ToUpper ((char)x);
 						break;
 					}
 					nextIsHot = false;
@@ -269,13 +269,13 @@ namespace Terminal.Gui {
 			}
 		}
 
-		int GetMenuBarItemLength (ustring title)
+		int GetMenuBarItemLength (string title)
 		{
 			int len = 0;
-			foreach (var ch in title) {
+			foreach (var ch in title.EnumerateRunes ()) {
 				if (ch == MenuBar.HotKeySpecifier)
 					continue;
-				len += Math.Max (Rune.ColumnWidth (ch), 1);
+				len += Math.Max (ch.GetColumns (), 1);
 			}
 
 			return len;
@@ -295,7 +295,7 @@ namespace Terminal.Gui {
 		/// <param name="action">Action to invoke when the menu item is activated.</param>
 		/// <param name="canExecute">Function to determine if the action can currently be executed.</param>
 		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
-		public MenuBarItem (ustring title, ustring help, Action action, Func<bool> canExecute = null, MenuItem parent = null) : base (title, help, action, canExecute, parent)
+		public MenuBarItem (string title, string help, Action action, Func<bool> canExecute = null, MenuItem parent = null) : base (title, help, action, canExecute, parent)
 		{
 			Initialize (title, null, null, true);
 		}
@@ -306,7 +306,7 @@ namespace Terminal.Gui {
 		/// <param name="title">Title for the menu item.</param>
 		/// <param name="children">The items in the current menu.</param>
 		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
-		public MenuBarItem (ustring title, MenuItem [] children, MenuItem parent = null)
+		public MenuBarItem (string title, MenuItem [] children, MenuItem parent = null)
 		{
 			Initialize (title, children, parent);
 		}
@@ -317,7 +317,7 @@ namespace Terminal.Gui {
 		/// <param name="title">Title for the menu item.</param>
 		/// <param name="children">The list of items in the current menu.</param>
 		/// <param name="parent">The parent <see cref="MenuItem"/> of this if exist, otherwise is null.</param>
-		public MenuBarItem (ustring title, List<MenuItem []> children, MenuItem parent = null)
+		public MenuBarItem (string title, List<MenuItem []> children, MenuItem parent = null)
 		{
 			Initialize (title, children, parent);
 		}
@@ -333,7 +333,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		public MenuBarItem () : this (children: new MenuItem [] { }) { }
 
-		void Initialize (ustring title, object children, MenuItem parent = null, bool isTopLevel = false)
+		void Initialize (string title, object children, MenuItem parent = null, bool isTopLevel = false)
 		{
 			if (!isTopLevel && children == null) {
 				throw new ArgumentNullException (nameof (children), "The parameter cannot be null. Use an empty array instead.");
@@ -414,10 +414,10 @@ namespace Terminal.Gui {
 			return -1;
 		}
 
-		void SetTitle (ustring title)
+		void SetTitle (string title)
 		{
 			if (title == null)
-				title = ustring.Empty;
+				title = string.Empty;
 			Title = title;
 		}
 
@@ -612,7 +612,7 @@ namespace Terminal.Gui {
 					else if (p == Frame.Width - 3 && barItems.SubMenu (barItems.Children [i]) != null)
 						Driver.AddRune (CM.Glyphs.RightArrow);
 					else
-						Driver.AddRune (' ');
+						Driver.AddRune ((Rune)' ');
 				}
 
 				if (item == null) {
@@ -623,7 +623,7 @@ namespace Terminal.Gui {
 					continue;
 				}
 
-				ustring textToDraw;
+				string textToDraw = null;
 				var nullCheckedChar = CM.Glyphs.NullChecked;
 				var checkChar = CM.Glyphs.Selected;
 				var uncheckedChar = CM.Glyphs.UnSelected;
@@ -635,11 +635,11 @@ namespace Terminal.Gui {
 
 				// Support Checked even though CheckType wasn't set
 				if (item.CheckType == MenuItemCheckStyle.Checked && item.Checked == null) {
-					textToDraw = ustring.Make (new Rune [] { nullCheckedChar, ' ' }) + item.Title;
+					textToDraw = $"{nullCheckedChar} {item.Title}";
 				} else if (item.Checked == true) {
-					textToDraw = ustring.Make (new Rune [] { checkChar, ' ' }) + item.Title;
+					textToDraw = $"{checkChar} {item.Title}";
 				} else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) || item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) {
-					textToDraw = ustring.Make (new Rune [] { uncheckedChar, ' ' }) + item.Title;
+					textToDraw = $"{uncheckedChar} {item.Title}";
 				} else {
 					textToDraw = item.Title;
 				}
@@ -667,7 +667,7 @@ namespace Terminal.Gui {
 					}
 
 					// The help string
-					var l = item.ShortcutTag.ConsoleWidth == 0 ? item.Help.ConsoleWidth : item.Help.ConsoleWidth + item.ShortcutTag.ConsoleWidth + 2;
+					var l = item.ShortcutTag.GetColumns () == 0 ? item.Help.GetColumns () : item.Help.GetColumns () + item.ShortcutTag.GetColumns () + 2;
 					var col = Frame.Width - l - 3;
 					ViewToScreen (col, i, out vtsCol, out vtsRow, false);
 					if (vtsCol < Driver.Cols) {
@@ -675,8 +675,8 @@ namespace Terminal.Gui {
 						Driver.AddStr (item.Help);
 
 						// The shortcut tag string
-						if (!item.ShortcutTag.IsEmpty) {
-							Driver.Move (vtsCol + l - item.ShortcutTag.ConsoleWidth, vtsRow);
+						if (!string.IsNullOrEmpty (item.ShortcutTag)) {
+							Driver.Move (vtsCol + l - item.ShortcutTag.GetColumns (), vtsRow);
 							Driver.AddStr (item.ShortcutTag);
 						}
 					}
@@ -760,7 +760,7 @@ namespace Terminal.Gui {
 				foreach (var item in barItems.Children) {
 					idx++;
 					if (item == null) continue;
-					if (item.IsEnabled () && item.HotKey == x) {
+					if (item.IsEnabled () && item.HotKey.Value == x) {
 						current = idx;
 						RunSelected ();
 						return true;
@@ -1050,15 +1050,15 @@ namespace Terminal.Gui {
 			}
 		}
 
-		static ustring shortcutDelimiter = "+";
+		static string shortcutDelimiter = "+";
 		/// <summary>
 		/// Sets or gets the shortcut delimiter separator. The default is "+".
 		/// </summary>
-		public static ustring ShortcutDelimiter {
+		public static string ShortcutDelimiter {
 			get => shortcutDelimiter;
 			set {
 				if (shortcutDelimiter != value) {
-					shortcutDelimiter = value == ustring.Empty ? " " : value;
+					shortcutDelimiter = value == string.Empty ? " " : value;
 				}
 			}
 		}
@@ -1066,7 +1066,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The specifier character for the hotkey to all menus.
 		/// </summary>
-		new public static Rune HotKeySpecifier => '_';
+		new public static Rune HotKeySpecifier => (Rune)'_';
 
 		private bool useSubMenusSingleFrame;
 
@@ -1238,7 +1238,7 @@ namespace Terminal.Gui {
 			Move (0, 0);
 			Driver.SetAttribute (GetNormalColor ());
 			for (int i = 0; i < Frame.Width; i++)
-				Driver.AddRune (' ');
+				Driver.AddRune ((Rune)' ');
 
 			Move (1, 0);
 			int pos = 0;
@@ -1255,8 +1255,8 @@ namespace Terminal.Gui {
 					normalColor = GetNormalColor ();
 				}
 				// Note Help on MenuBar is drawn with parens around it
-				DrawHotString (menu.Help.IsEmpty ? $" {menu.Title} " : $" {menu.Title} ({menu.Help}) ", hotColor, normalColor);
-				pos += leftPadding + menu.TitleLength + (menu.Help.ConsoleWidth > 0 ? leftPadding + menu.Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
+				DrawHotString (string.IsNullOrEmpty (menu.Help) ? $" {menu.Title} " : $" {menu.Title} ({menu.Help}) ", hotColor, normalColor);
+				pos += leftPadding + menu.TitleLength + (menu.Help.GetColumns () > 0 ? leftPadding + menu.Help.GetColumns () + parensAroundHelp : 0) + rightPadding;
 			}
 			PositionCursor ();
 		}
@@ -1274,7 +1274,7 @@ namespace Terminal.Gui {
 					Move (pos + 1, 0);
 					return;
 				} else {
-					pos += leftPadding + Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + parensAroundHelp : 0) + rightPadding;
+					pos += leftPadding + Menus [i].TitleLength + (Menus [i].Help.GetColumns () > 0 ? Menus [i].Help.GetColumns () + parensAroundHelp : 0) + rightPadding;
 				}
 			}
 		}
@@ -1429,7 +1429,7 @@ namespace Terminal.Gui {
 				// This positions the submenu horizontally aligned with the first character of the
 				// text belonging to the menu 
 				for (int i = 0; i < index; i++)
-					pos += Menus [i].TitleLength + (Menus [i].Help.ConsoleWidth > 0 ? Menus [i].Help.ConsoleWidth + 2 : 0) + leftPadding + rightPadding;
+					pos += Menus [i].TitleLength + (Menus [i].Help.GetColumns () > 0 ? Menus [i].Help.GetColumns () + 2 : 0) + leftPadding + rightPadding;
 
 				var locationOffset = Point.Empty;
 				// if SuperView is null then it's from a ContextMenu
@@ -1807,8 +1807,8 @@ namespace Terminal.Gui {
 			for (int i = 0; i < Menus.Length; i++) {
 				// TODO: this code is duplicated, hotkey should be part of the MenuBarItem
 				var mi = Menus [i];
-				int p = mi.Title.IndexOf (MenuBar.HotKeySpecifier);
-				if (p != -1 && p + 1 < mi.Title.RuneCount) {
+				int p = mi.Title.IndexOf (MenuBar.HotKeySpecifier.ToString ());
+				if (p != -1 && p + 1 < mi.Title.GetRuneCount ()) {
 					if (Char.ToUpperInvariant ((char)mi.Title [p + 1]) == c) {
 						ProcessMenu (i, mi);
 						return true;
@@ -1916,8 +1916,8 @@ namespace Terminal.Gui {
 				foreach (var mi in Menus [selected].Children) {
 					if (mi == null)
 						continue;
-					int p = mi.Title.IndexOf (MenuBar.HotKeySpecifier);
-					if (p != -1 && p + 1 < mi.Title.RuneCount) {
+					int p = mi.Title.IndexOf (MenuBar.HotKeySpecifier.ToString ());
+					if (p != -1 && p + 1 < mi.Title.GetRuneCount ()) {
 						if (mi.Title [p + 1] == c) {
 							Selected (mi);
 							return true;
@@ -1981,7 +1981,7 @@ namespace Terminal.Gui {
 				}
 				int cx = me.X - locationOffset.X;
 				for (int i = 0; i < Menus.Length; i++) {
-					if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.ConsoleWidth + rightPadding) {
+					if (cx >= pos && cx < pos + leftPadding + Menus [i].TitleLength + Menus [i].Help.GetColumns () + rightPadding) {
 						if (me.Flags == MouseFlags.Button1Clicked) {
 							if (Menus [i].IsTopLevel) {
 								ViewToScreen (i, 0, out int rx, out int ry);

+ 22 - 22
Terminal.Gui/Views/MessageBox.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using Terminal.Gui;
@@ -9,7 +9,7 @@ namespace Terminal.Gui {
 	/// MessageBox displays a modal message to the user, with a title, a message and a series of options that the user can choose from.
 	/// </summary>
 	/// <para>
-	///   The difference between the <see cref="Query(ustring, ustring, ustring[])"/> and <see cref="ErrorQuery(ustring, ustring, ustring[])"/> 
+	///   The difference between the <see cref="Query(string, string, string[])"/> and <see cref="ErrorQuery(string, string, string[])"/> 
 	///   method is the default set of colors used for the message box.
 	/// </para>
 	/// <para>
@@ -36,9 +36,9 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="Query(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int Query (int width, int height, ustring title, ustring message, params ustring [] buttons)
+		public static int Query (int width, int height, string title, string message, params string [] buttons)
 		{
 			return QueryFull (false, width, height, title, message, 0, true, buttons);
 		}
@@ -54,7 +54,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the message and buttons.
 		/// </remarks>
-		public static int Query (ustring title, ustring message, params ustring [] buttons)
+		public static int Query (string title, string message, params string [] buttons)
 		{
 			return QueryFull (false, 0, 0, title, message, 0, true, buttons);
 		}
@@ -69,9 +69,9 @@ namespace Terminal.Gui {
 		/// <param name="message">Message to display, might contain multiple lines.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="ErrorQuery(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int ErrorQuery (int width, int height, ustring title, ustring message, params ustring [] buttons)
+		public static int ErrorQuery (int width, int height, string title, string message, params string [] buttons)
 		{
 			return QueryFull (true, width, height, title, message, 0, true, buttons);
 		}
@@ -87,7 +87,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the title, message. and buttons.
 		/// </remarks>
-		public static int ErrorQuery (ustring title, ustring message, params ustring [] buttons)
+		public static int ErrorQuery (string title, string message, params string [] buttons)
 		{
 			return QueryFull (true, 0, 0, title, message, 0, true, buttons);
 		}
@@ -103,9 +103,9 @@ namespace Terminal.Gui {
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="Query(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int Query (int width, int height, ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
+		public static int Query (int width, int height, string title, string message, int defaultButton = 0, params string [] buttons)
 		{
 			return QueryFull (false, width, height, title, message, defaultButton, true, buttons);
 		}
@@ -122,7 +122,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the message and buttons.
 		/// </remarks>
-		public static int Query (ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
+		public static int Query (string title, string message, int defaultButton = 0, params string [] buttons)
 		{
 			return QueryFull (false, 0, 0, title, message, defaultButton, true, buttons);
 		}
@@ -139,9 +139,9 @@ namespace Terminal.Gui {
 		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="Query(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="Query(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int Query (int width, int height, ustring title, ustring message, int defaultButton = 0, bool wrapMessagge = true, params ustring [] buttons)
+		public static int Query (int width, int height, string title, string message, int defaultButton = 0, bool wrapMessagge = true, params string [] buttons)
 		{
 			return QueryFull (false, width, height, title, message, defaultButton, wrapMessagge, buttons);
 		}
@@ -159,7 +159,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the message and buttons.
 		/// </remarks>
-		public static int Query (ustring title, ustring message, int defaultButton = 0, bool wrapMessage = true, params ustring [] buttons)
+		public static int Query (string title, string message, int defaultButton = 0, bool wrapMessage = true, params string [] buttons)
 		{
 			return QueryFull (false, 0, 0, title, message, defaultButton, wrapMessage, buttons);
 		}
@@ -175,9 +175,9 @@ namespace Terminal.Gui {
 		/// <param name="defaultButton">Index of the default button.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="ErrorQuery(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int ErrorQuery (int width, int height, ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
+		public static int ErrorQuery (int width, int height, string title, string message, int defaultButton = 0, params string [] buttons)
 		{
 			return QueryFull (true, width, height, title, message, defaultButton, true, buttons);
 		}
@@ -194,7 +194,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the title, message. and buttons.
 		/// </remarks>
-		public static int ErrorQuery (ustring title, ustring message, int defaultButton = 0, params ustring [] buttons)
+		public static int ErrorQuery (string title, string message, int defaultButton = 0, params string [] buttons)
 		{
 			return QueryFull (true, 0, 0, title, message, defaultButton, true, buttons);
 		}
@@ -211,9 +211,9 @@ namespace Terminal.Gui {
 		/// <param name="wrapMessagge">If wrap the message or not.</param>
 		/// <param name="buttons">Array of buttons to add.</param>
 		/// <remarks>
-		/// Use <see cref="ErrorQuery(ustring, ustring, ustring[])"/> instead; it automatically sizes the MessageBox based on the contents.
+		/// Use <see cref="ErrorQuery(string, string, string[])"/> instead; it automatically sizes the MessageBox based on the contents.
 		/// </remarks>
-		public static int ErrorQuery (int width, int height, ustring title, ustring message, int defaultButton = 0, bool wrapMessagge = true, params ustring [] buttons)
+		public static int ErrorQuery (int width, int height, string title, string message, int defaultButton = 0, bool wrapMessagge = true, params string [] buttons)
 		{
 			return QueryFull (true, width, height, title, message, defaultButton, wrapMessagge, buttons);
 		}
@@ -231,7 +231,7 @@ namespace Terminal.Gui {
 		/// The message box will be vertically and horizontally centered in the container and the size will be automatically determined
 		/// from the size of the title, message. and buttons.
 		/// </remarks>
-		public static int ErrorQuery (ustring title, ustring message, int defaultButton = 0, bool wrapMessagge = true, params ustring [] buttons)
+		public static int ErrorQuery (string title, string message, int defaultButton = 0, bool wrapMessagge = true, params string [] buttons)
 		{
 			return QueryFull (true, 0, 0, title, message, defaultButton, wrapMessagge, buttons);
 		}
@@ -242,8 +242,8 @@ namespace Terminal.Gui {
 		[SerializableConfigurationProperty (Scope = typeof (ThemeScope))]
 		public static LineStyle DefaultBorderStyle { get; set; } = LineStyle.Single;
 
-		static int QueryFull (bool useErrorColors, int width, int height, ustring title, ustring message,
-			int defaultButton = 0, bool wrapMessage = true, params ustring [] buttons)
+		static int QueryFull (bool useErrorColors, int width, int height, string title, string message,
+			int defaultButton = 0, bool wrapMessage = true, params string [] buttons)
 		{
 			// Create button array for Dialog
 			int count = 0;

+ 2 - 2
Terminal.Gui/Views/OpenDialog.cs

@@ -11,7 +11,7 @@
 
 using System;
 using System.Collections.Generic;
-using NStack;
+using System.Text;
 using System.IO;
 using System.Linq;
 using Terminal.Gui.Resources;
@@ -68,7 +68,7 @@ namespace Terminal.Gui {
 		/// <param name="title">The title.</param>
 		/// <param name="allowedTypes">The allowed types.</param>
 		/// <param name="openMode">The open mode.</param>
-		public OpenDialog (ustring title, List<IAllowedType> allowedTypes = null, OpenMode openMode = OpenMode.File)
+		public OpenDialog (string title, List<IAllowedType> allowedTypes = null, OpenMode openMode = OpenMode.File)
 		{
 			this.OpenMode = openMode;
 			Title = title;

+ 6 - 6
Terminal.Gui/Views/ProgressBar.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 namespace Terminal.Gui {
 	/// <summary>
@@ -184,7 +184,7 @@ namespace Terminal.Gui {
 		}
 
 		///<inheritdoc/>
-		public override ustring Text {
+		public override string Text {
 			get => GetPercentageText ();
 			set {
 				base.Text = SetPercentageText (value);
@@ -206,7 +206,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		ustring GetPercentageText ()
+		string GetPercentageText ()
 		{
 			switch (progressBarStyle) {
 			case ProgressBarStyle.Blocks:
@@ -220,7 +220,7 @@ namespace Terminal.Gui {
 			return base.Text;
 		}
 
-		ustring SetPercentageText (ustring value)
+		string SetPercentageText (string value)
 		{
 			switch (progressBarStyle) {
 			case ProgressBarStyle.Blocks:
@@ -288,7 +288,7 @@ namespace Terminal.Gui {
 					if (Array.IndexOf (activityPos, i) != -1)
 						Driver.AddRune (SegmentCharacter);
 					else
-						Driver.AddRune (' ');
+						Driver.AddRune ((Rune)' ');
 			} else {
 				Move (padding, padding);
 				int mid = (int)(fraction * fWidth);
@@ -296,7 +296,7 @@ namespace Terminal.Gui {
 				for (i = 0; i < mid & i < fWidth; i++)
 					Driver.AddRune (SegmentCharacter);
 				for (; i < fWidth; i++)
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 			}
 
 			DrawText (fWidth);

+ 15 - 15
Terminal.Gui/Views/RadioGroup.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -17,14 +17,14 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
-		public RadioGroup () : this (radioLabels: new ustring [] { }) { }
+		public RadioGroup () : this (radioLabels: new string [] { }) { }
 
 		/// <summary>
 		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
 		/// <param name="radioLabels">The radio labels; an array of strings that can contain hotkeys using an underscore before the letter.</param>
 		/// <param name="selected">The index of the item to be selected, the value is clamped to the number of items.</param>
-		public RadioGroup (ustring [] radioLabels, int selected = 0) : base ()
+		public RadioGroup (string [] radioLabels, int selected = 0) : base ()
 		{
 			SetInitalProperties (Rect.Empty, radioLabels, selected);
 		}
@@ -35,7 +35,7 @@ namespace Terminal.Gui {
 		/// <param name="rect">Boundaries for the radio group.</param>
 		/// <param name="radioLabels">The radio labels; an array of strings that can contain hotkeys using an underscore before the letter.</param>
 		/// <param name="selected">The index of item to be selected, the value is clamped to the number of items.</param>
-		public RadioGroup (Rect rect, ustring [] radioLabels, int selected = 0) : base (rect)
+		public RadioGroup (Rect rect, string [] radioLabels, int selected = 0) : base (rect)
 		{
 			SetInitalProperties (rect, radioLabels, selected);
 		}
@@ -48,14 +48,14 @@ namespace Terminal.Gui {
 		/// <param name="y">The y coordinate.</param>
 		/// <param name="radioLabels">The radio labels; an array of strings that can contain hotkeys using an underscore before the letter.</param>
 		/// <param name="selected">The item to be selected, the value is clamped to the number of items.</param>
-		public RadioGroup (int x, int y, ustring [] radioLabels, int selected = 0) :
+		public RadioGroup (int x, int y, string [] radioLabels, int selected = 0) :
 			this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList () : null), radioLabels, selected)
 		{ }
 
-		void SetInitalProperties (Rect rect, ustring [] radioLabels, int selected)
+		void SetInitalProperties (Rect rect, string [] radioLabels, int selected)
 		{
 			if (radioLabels == null) {
-				this.radioLabels = new List<ustring> ();
+				this.radioLabels = new List<string> ();
 			} else {
 				this.radioLabels = radioLabels.ToList ();
 			}
@@ -116,7 +116,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		void SetWidthHeight (List<ustring> radioLabels)
+		void SetWidthHeight (List<string> radioLabels)
 		{
 			switch (displayMode) {
 			case DisplayModeLayout.Vertical:
@@ -141,7 +141,7 @@ namespace Terminal.Gui {
 			}
 		}
 
-		static Rect MakeRect (int x, int y, List<ustring> radioLabels)
+		static Rect MakeRect (int x, int y, List<string> radioLabels)
 		{
 			if (radioLabels == null) {
 				return new Rect (x, y, 0, 0);
@@ -150,18 +150,18 @@ namespace Terminal.Gui {
 			int width = 0;
 
 			foreach (var s in radioLabels) {
-				width = Math.Max (s.ConsoleWidth + 2, width);
+				width = Math.Max (s.GetColumns () + 2, width);
 			}
 			return new Rect (x, y, width, radioLabels.Count);
 		}
 
-		List<ustring> radioLabels = new List<ustring> ();
+		List<string> radioLabels = new List<string> ();
 
 		/// <summary>
 		/// The radio labels to display
 		/// </summary>
 		/// <value>The radio labels.</value>
-		public ustring [] RadioLabels {
+		public string [] RadioLabels {
 			get => radioLabels.ToArray ();
 			set {
 				var prevCount = radioLabels.Count;
@@ -183,7 +183,7 @@ namespace Terminal.Gui {
 				int length = 0;
 				for (int i = 0; i < radioLabels.Count; i++) {
 					start += length;
-					length = radioLabels [i].ConsoleWidth + 2 + (i < radioLabels.Count - 1 ? horizontalSpace : 0);
+					length = radioLabels [i].GetColumns () + 2 + (i < radioLabels.Count - 1 ? horizontalSpace : 0);
 					horizontal.Add ((start, length));
 				}
 			}
@@ -206,7 +206,7 @@ namespace Terminal.Gui {
 				}
 				var rl = radioLabels [i];
 				Driver.SetAttribute (GetNormalColor ());
-				Driver.AddStr (ustring.Make (new Rune [] { i == selected ? CM.Glyphs.Selected : CM.Glyphs.UnSelected, ' ' }));
+				Driver.AddStr ($"{(i == selected ? CM.Glyphs.Selected : CM.Glyphs.UnSelected)} ");
 				TextFormatter.FindHotKey (rl, HotKeySpecifier, true, out int hotPos, out Key hotKey);
 				if (hotPos != -1 && (hotKey != Key.Null || hotKey != Key.Unknown)) {
 					var rlRunes = rl.ToRunes ();
@@ -301,7 +301,7 @@ namespace Terminal.Gui {
 						if (c == HotKeySpecifier) {
 							nextIsHot = true;
 						} else {
-							if ((nextIsHot && Rune.ToUpper (c) == key) || (key == (uint)hotKey)) {
+							if ((nextIsHot && Rune.ToUpperInvariant (c).Value == key) || (key == (uint)hotKey)) {
 								SelectedItem = i;
 								cursor = i;
 								if (!HasFocus)

+ 3 - 3
Terminal.Gui/Views/SaveDialog.cs

@@ -11,7 +11,7 @@
 
 using System;
 using System.Collections.Generic;
-using NStack;
+using System.Text;
 using Terminal.Gui.Resources;
 
 namespace Terminal.Gui {
@@ -38,7 +38,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="title">The title.</param>
 		/// <param name="allowedTypes">The allowed types.</param>
-		public SaveDialog (ustring title, List<IAllowedType> allowedTypes = null)
+		public SaveDialog (string title, List<IAllowedType> allowedTypes = null)
 		{
 			//: base (title, prompt: Strings.fdSave, nameFieldLabel: $"{Strings.fdSaveAs}:", message: message, allowedTypes) { }
 			Title = title;
@@ -54,7 +54,7 @@ namespace Terminal.Gui {
 		/// if the user canceled the <see cref="SaveDialog"/>.
 		/// </summary>
 		/// <value>The name of the file.</value>
-		public ustring FileName {
+		public string FileName {
 			get {
 				if (Canceled)
 					return null;

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

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

+ 12 - 12
Terminal.Gui/Views/StatusBar.cs

@@ -8,7 +8,7 @@
 //   Add mouse support
 using System;
 using System.Collections.Generic;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -27,7 +27,7 @@ namespace Terminal.Gui {
 		/// <param name="shortcut">Shortcut to activate the <see cref="StatusItem"/>.</param>
 		/// <param name="title">Title for the <see cref="StatusItem"/>.</param>
 		/// <param name="action">Action to invoke when the <see cref="StatusItem"/> is activated.</param>
-		public StatusItem (Key shortcut, ustring title, Action action)
+		public StatusItem (Key shortcut, string title, Action action)
 		{
 			Title = title ?? "";
 			Shortcut = shortcut;
@@ -48,7 +48,7 @@ namespace Terminal.Gui {
 		/// A <see cref="StatusItem.Title"/> set to `~F1~ Help` will render as *F1* using <see cref="ColorScheme.HotNormal"/> and
 		/// *Help* as <see cref="ColorScheme.HotNormal"/>.
 		/// </remarks>
-		public ustring Title { get; set; }
+		public string Title { get; set; }
 
 		/// <summary>
 		/// Gets or sets the action to be invoked when the statusbar item is triggered
@@ -96,15 +96,15 @@ namespace Terminal.Gui {
 			Height = 1;
 		}
 
-		static ustring shortcutDelimiter = "-";
+		static string shortcutDelimiter = "-";
 		/// <summary>
 		/// Used for change the shortcut delimiter separator.
 		/// </summary>
-		public static ustring ShortcutDelimiter {
+		public static string ShortcutDelimiter {
 			get => shortcutDelimiter;
 			set {
 				if (shortcutDelimiter != value) {
-					shortcutDelimiter = value == ustring.Empty ? " " : value;
+					shortcutDelimiter = value == string.Empty ? " " : value;
 				}
 			}
 		}
@@ -122,24 +122,24 @@ namespace Terminal.Gui {
 			Move (0, 0);
 			Driver.SetAttribute (GetNormalColor ());
 			for (int i = 0; i < Frame.Width; i++)
-				Driver.AddRune (' ');
+				Driver.AddRune ((Rune)' ');
 
 			Move (1, 0);
 			var scheme = GetNormalColor ();
 			Driver.SetAttribute (scheme);
 			for (int i = 0; i < Items.Length; i++) {
 				var title = Items [i].Title.ToString ();
-				for (int n = 0; n < Items [i].Title.RuneCount; n++) {
+				for (int n = 0; n < Items [i].Title.GetRuneCount (); n++) {
 					if (title [n] == '~') {
 						scheme = ToggleScheme (scheme);
 						continue;
 					}
-					Driver.AddRune (title [n]);
+					Driver.AddRune ((Rune)title [n]);
 				}
 				if (i + 1 < Items.Length) {
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 					Driver.AddRune (CM.Glyphs.VLine);
-					Driver.AddRune (' ');
+					Driver.AddRune ((Rune)' ');
 				}
 			}
 		}
@@ -173,7 +173,7 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		int GetItemTitleLength (ustring title)
+		int GetItemTitleLength (string title)
 		{
 			int len = 0;
 			foreach (var ch in title) {

+ 6 - 6
Terminal.Gui/Views/TabView.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Data;
@@ -336,9 +336,9 @@ namespace Terminal.Gui {
 			foreach (var tab in Tabs.Skip (TabScrollOffset)) {
 
 				// while there is space for the tab
-				var tabTextWidth = tab.Text.Sum (c => Rune.ColumnWidth (c));
+				var tabTextWidth = tab.Text.EnumerateRunes ().Sum (c => c.GetColumns ());
 
-				string text = tab.Text.ToString ();
+				string text = tab.Text;
 
 				// The maximum number of characters to use for the tab name as specified
 				// by the user (MaxTabTextWidth).  But not more than the width of the view
@@ -352,7 +352,7 @@ namespace Terminal.Gui {
 				}
 
 				if (tabTextWidth > maxWidth) {
-					text = tab.Text.ToString ().Substring (0, (int)maxWidth);
+					text = tab.Text.Substring (0, (int)maxWidth);
 					tabTextWidth = (int)maxWidth;
 				}
 
@@ -750,13 +750,13 @@ namespace Terminal.Gui {
 		/// A single tab in a <see cref="TabView"/>
 		/// </summary>
 		public class Tab {
-			private ustring text;
+			private string text;
 
 			/// <summary>
 			/// The text to display in a <see cref="TabView"/>
 			/// </summary>
 			/// <value></value>
-			public ustring Text { get => text ?? "Unamed"; set => text = value; }
+			public string Text { get => text ?? "Unamed"; set => text = value; }
 
 			/// <summary>
 			/// The control to display when the tab is selected

+ 1 - 0
Terminal.Gui/Views/TableView/CheckBoxTableSourceWrapper.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Data;
 using System.Linq;
+using System.Text;
 
 namespace Terminal.Gui {
 

+ 3 - 6
Terminal.Gui/Views/TableView/ListTableSource.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Collections;
 using System.Data;
 using System.Linq;
@@ -136,10 +135,8 @@ namespace Terminal.Gui {
 			int maxLength = 0;
 			foreach (var t in List) {
 				int l;
-				if (t is ustring u) {
-					l = TextFormatter.GetTextWidth (u);
-				} else if (t is string s) {
-					l = s.Length;
+				if (t is string s) {
+					l = s.GetColumns ();
 				} else {
 					l = t.ToString ().Length;
 				}

+ 10 - 10
Terminal.Gui/Views/TableView/TableView.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -657,7 +657,7 @@ namespace Terminal.Gui {
 
 		/// <summary>
 		/// Override to provide custom multi colouring to cells.  Use <see cref="View.Driver"/> to
-		/// with <see cref="ConsoleDriver.AddStr(ustring)"/>.  The driver will already be
+		/// with <see cref="ConsoleDriver.AddStr(string)"/>.  The driver will already be
 		/// in the correct place when rendering and you must render the full <paramref name="render"/>
 		/// or the view will not look right.  For simpler provision of color use <see cref="ColumnStyle.ColorGetter"/>
 		/// For changing the content that is rendered use <see cref="ColumnStyle.RepresentationGetter"/>
@@ -675,7 +675,7 @@ namespace Terminal.Gui {
 				if (render.Length > 0) {
 					// invert the color of the current cell for the first character
 					Driver.SetAttribute (Driver.MakeAttribute (cellColor.Background, cellColor.Foreground));
-					Driver.AddRune (render [0]);
+					Driver.AddRune ((Rune)render [0]);
 
 					if (render.Length > 1) {
 						Driver.SetAttribute (cellColor);
@@ -695,7 +695,7 @@ namespace Terminal.Gui {
 
 			var renderLines = isHeader ? style.ShowVerticalHeaderLines : style.ShowVerticalCellLines;
 
-			Rune symbol = renderLines ? CM.Glyphs.VLine : SeparatorSymbol;
+			Rune symbol = renderLines ? CM.Glyphs.VLine : (Rune)SeparatorSymbol;
 			AddRune (col, row, symbol);
 		}
 
@@ -719,10 +719,10 @@ namespace Terminal.Gui {
 				return new string (' ', availableHorizontalSpace);
 
 			// if value is not wide enough
-			if (representation.Sum (c => Rune.ColumnWidth (c)) < availableHorizontalSpace) {
+			if (representation.EnumerateRunes ().Sum (c => c.GetColumns ()) < availableHorizontalSpace) {
 
 				// pad it out with spaces to the given alignment
-				int toPad = availableHorizontalSpace - (representation.Sum (c => Rune.ColumnWidth (c)) + 1 /*leave 1 space for cell boundary*/);
+				int toPad = availableHorizontalSpace - (representation.EnumerateRunes ().Sum (c => c.GetColumns ()) + 1 /*leave 1 space for cell boundary*/);
 
 				switch (colStyle?.GetAlignment (originalCellValue) ?? TextAlignment.Left) {
 
@@ -742,7 +742,7 @@ namespace Terminal.Gui {
 			}
 
 			// value is too wide
-			return new string (representation.TakeWhile (c => (availableHorizontalSpace -= Rune.ColumnWidth (c)) > 0).ToArray ());
+			return new string (representation.TakeWhile (c => (availableHorizontalSpace -= ((Rune)c).GetColumns ()) > 0).ToArray ());
 		}
 
 
@@ -1594,7 +1594,7 @@ namespace Terminal.Gui {
 		/// Invokes the <see cref="CellToggled"/> event
 		/// </summary>
 		/// <param name="args"></param>
-		protected virtual void OnCellToggled(CellToggledEventArgs args)
+		protected virtual void OnCellToggled (CellToggledEventArgs args)
 		{
 			CellToggled?.Invoke (this, args);
 		}
@@ -1716,7 +1716,7 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		private int CalculateMaxCellWidth (int col, int rowsToRender, ColumnStyle colStyle)
 		{
-			int spaceRequired = table.ColumnNames [col].Sum (c => Rune.ColumnWidth (c));
+			int spaceRequired = table.ColumnNames [col].EnumerateRunes ().Sum (c => c.GetColumns ());
 
 			// if table has no rows
 			if (RowOffset < 0)
@@ -1728,7 +1728,7 @@ namespace Terminal.Gui {
 				//expand required space if cell is bigger than the last biggest cell or header
 				spaceRequired = Math.Max (
 					spaceRequired,
-					GetRepresentation (Table [i, col], colStyle).Sum (c => Rune.ColumnWidth (c)));
+					GetRepresentation (Table [i, col], colStyle).EnumerateRunes ().Sum (c => c.GetColumns ()));
 			}
 
 			// Don't require more space than the style allows

+ 3 - 3
Terminal.Gui/Views/TextChangedEventArgs.cs

@@ -6,7 +6,7 @@
 //
 
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -18,7 +18,7 @@ namespace Terminal.Gui {
 		/// Creates a new instance of the <see cref="TextChangedEventArgs"/> class
 		/// </summary>
 		/// <param name="oldValue"></param>
-		public TextChangedEventArgs (ustring oldValue)
+		public TextChangedEventArgs (string oldValue)
 		{
 			OldValue = oldValue;
 		}
@@ -26,6 +26,6 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The old value before the text changed
 		/// </summary>
-		public ustring OldValue { get; }
+		public string OldValue { get; }
 	}
 }

+ 3 - 3
Terminal.Gui/Views/TextChangingEventArgs.cs

@@ -6,7 +6,7 @@
 //
 
 using System;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -16,7 +16,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The new text to be replaced.
 		/// </summary>
-		public ustring NewText { get; set; }
+		public string NewText { get; set; }
 		/// <summary>
 		/// Flag which allows to cancel the new text value.
 		/// </summary>
@@ -26,7 +26,7 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of <see cref="TextChangingEventArgs"/>
 		/// </summary>
 		/// <param name="newText">The new <see cref="TextField.Text"/> to be replaced.</param>
-		public TextChangingEventArgs (ustring newText)
+		public TextChangingEventArgs (string newText)
 		{
 			NewText = newText;
 		}

+ 45 - 51
Terminal.Gui/Views/TextField.cs

@@ -10,9 +10,9 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.Linq;
 using System.Threading;
-using NStack;
+using System.Text;
 using Terminal.Gui.Resources;
-using Rune = System.Rune;
+
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -25,7 +25,7 @@ namespace Terminal.Gui {
 		List<Rune> text;
 		int first, point;
 		int selectedStart = -1; // -1 represents there is no text selection.
-		ustring selectedText;
+		string selectedText;
 		HistoryText historyText = new HistoryText ();
 		CultureInfo currentCulture;
 
@@ -34,7 +34,7 @@ namespace Terminal.Gui {
 		/// been entered yet and the <see cref="View"/> does not yet have
 		/// input focus.
 		/// </summary>
-		public ustring Caption { get; set; }
+		public string Caption { get; set; }
 
 		/// <summary>
 		/// Gets or sets the foreground <see cref="Color"/> to use when 
@@ -64,16 +64,10 @@ namespace Terminal.Gui {
 		///   This event is raised when the <see cref="Text"/> changes. 
 		/// </remarks>
 		/// <remarks>
-		///   The passed <see cref="EventArgs"/> is a <see cref="NStack.ustring"/> containing the old value. 
+		///   The passed <see cref="EventArgs"/> is a <see cref="string"/> containing the old value. 
 		/// </remarks>
 		public event EventHandler<TextChangedEventArgs> TextChanged;
 
-		/// <summary>
-		/// Initializes a new instance of the <see cref="TextField"/> class using <see cref="LayoutStyle.Computed"/> positioning.
-		/// </summary>
-		/// <param name="text">Initial text contents.</param>
-		public TextField (string text) : this (ustring.Make (text)) { }
-
 		/// <summary>
 		/// Initializes a new instance of the <see cref="TextField"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// </summary>
@@ -83,9 +77,9 @@ namespace Terminal.Gui {
 		/// Initializes a new instance of the <see cref="TextField"/> class using <see cref="LayoutStyle.Computed"/> positioning.
 		/// </summary>
 		/// <param name="text">Initial text contents.</param>
-		public TextField (ustring text) : base (text)
+		public TextField (string text) : base (text)
 		{
-			Initialize (text, text.RuneCount + 1);
+			Initialize (text, text.GetRuneCount () + 1);
 		}
 
 		/// <summary>
@@ -95,12 +89,12 @@ namespace Terminal.Gui {
 		/// <param name="y">The y coordinate.</param>
 		/// <param name="w">The width.</param>
 		/// <param name="text">Initial text contents.</param>
-		public TextField (int x, int y, int w, ustring text) : base (new Rect (x, y, w, 1))
+		public TextField (int x, int y, int w, string text) : base (new Rect (x, y, w, 1))
 		{
 			Initialize (text, w);
 		}
 
-		void Initialize (ustring text, int w)
+		void Initialize (string text, int w)
 		{
 			Height = 1;
 
@@ -108,7 +102,7 @@ namespace Terminal.Gui {
 				text = "";
 
 			this.text = TextModel.ToRunes (text.Split ("\n") [0]);
-			point = text.RuneCount;
+			point = text.GetRuneCount ();
 			first = point > w + 1 ? point - w + 1 : 0;
 			CanFocus = true;
 			Used = true;
@@ -251,7 +245,7 @@ namespace Terminal.Gui {
 			if (obj == null)
 				return;
 
-			Text = ustring.Make (obj?.Lines [obj.CursorPosition.Y]);
+			Text = StringExtensions.ToString (obj?.Lines [obj.CursorPosition.Y]);
 			CursorPosition = obj.CursorPosition.X;
 			Adjust ();
 		}
@@ -294,7 +288,7 @@ namespace Terminal.Gui {
 			get => base.Frame;
 			set {
 				if (value.Height > 1) {
-					base.Frame = new Rect(value.X, value.Y, value.Width, 1);
+					base.Frame = new Rect (value.X, value.Y, value.Width, 1);
 					Height = 1;
 				} else {
 					base.Frame = value;
@@ -308,13 +302,13 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <remarks>
 		/// </remarks>
-		public new ustring Text {
+		public new string Text {
 			get {
-				return ustring.Make (text);
+				return StringExtensions.ToString (text);
 			}
 
 			set {
-				var oldText = ustring.Make (text);
+				var oldText = StringExtensions.ToString (text);
 
 				if (oldText == value)
 					return;
@@ -403,7 +397,7 @@ namespace Terminal.Gui {
 			for (int idx = first < 0 ? 0 : first; idx < text.Count; idx++) {
 				if (idx == point)
 					break;
-				var cols = Rune.ColumnWidth (text [idx]);
+				var cols = text [idx].GetColumns ();
 				TextModel.SetCol (ref col, Frame.Width - 1, cols);
 			}
 			var pos = point - first + Math.Min (Frame.X, 0);
@@ -456,7 +450,7 @@ namespace Terminal.Gui {
 			var roc = GetReadOnlyColor ();
 			for (int idx = p; idx < tcount; idx++) {
 				var rune = text [idx];
-				var cols = Rune.ColumnWidth (rune);
+				var cols = ((Rune)rune).GetColumns ();
 				if (idx == point && HasFocus && !Used && length == 0 && !ReadOnly) {
 					Driver.SetAttribute (selColor);
 				} else if (ReadOnly) {
@@ -474,14 +468,14 @@ namespace Terminal.Gui {
 				if (!TextModel.SetCol (ref col, width, cols)) {
 					break;
 				}
-				if (idx + 1 < tcount && col + Rune.ColumnWidth (text [idx + 1]) > width) {
+				if (idx + 1 < tcount && col + text [idx + 1].GetColumns () > width) {
 					break;
 				}
 			}
 
 			Driver.SetAttribute (ColorScheme.Focus);
 			for (int i = col; i < width; i++) {
-				Driver.AddRune (' ');
+				Driver.AddRune ((Rune)' ');
 			}
 
 			PositionCursor ();
@@ -514,8 +508,8 @@ namespace Terminal.Gui {
 			Move (0, 0);
 			var render = Caption;
 
-			if (render.ConsoleWidth > Bounds.Width) {
-				render = render.RuneSubstring (0, Bounds.Width);
+			if (render.GetColumns () > Bounds.Width) {
+				render = render [..Bounds.Width];
 			}
 
 			Driver.AddStr (render);
@@ -574,7 +568,7 @@ namespace Terminal.Gui {
 
 		void SetText (List<Rune> newText)
 		{
-			Text = ustring.Make (newText);
+			Text = StringExtensions.ToString (newText);
 		}
 
 		void SetText (IEnumerable<Rune> newText)
@@ -591,7 +585,7 @@ namespace Terminal.Gui {
 		void SetClipboard (IEnumerable<Rune> text)
 		{
 			if (!Secret)
-				Clipboard.Contents = ustring.Make (text.ToList ());
+				Clipboard.Contents = StringExtensions.ToString (text.ToList ());
 		}
 
 		int oldCursorPos;
@@ -655,7 +649,7 @@ namespace Terminal.Gui {
 			if (!useOldCursorPos) {
 				oldCursorPos = point;
 			}
-			var kbstr = TextModel.ToRunes (ustring.Make ((uint)kb.Key));
+			var kbstr = TextModel.ToRunes (((Rune)(uint)kb.Key).ToString ());
 			if (Used) {
 				point++;
 				if (point == newText.Count + 1) {
@@ -742,7 +736,7 @@ namespace Terminal.Gui {
 
 			historyText.Redo ();
 
-			//if (ustring.IsNullOrEmpty (Clipboard.Contents))
+			//if (string.IsNullOrEmpty (Clipboard.Contents))
 			//	return true;
 			//var clip = TextModel.ToRunes (Clipboard.Contents);
 			//if (clip == null)
@@ -912,7 +906,7 @@ namespace Terminal.Gui {
 				Adjust ();
 			} else {
 				var newText = DeleteSelectedText ();
-				Text = ustring.Make (newText);
+				Text = StringExtensions.ToString (newText);
 				Adjust ();
 			}
 		}
@@ -935,7 +929,7 @@ namespace Terminal.Gui {
 				Adjust ();
 			} else {
 				var newText = DeleteSelectedText ();
-				Text = ustring.Make (newText);
+				Text = StringExtensions.ToString (newText);
 				Adjust ();
 			}
 		}
@@ -1005,7 +999,7 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The selected text.
 		/// </summary>
-		public ustring SelectedText {
+		public string SelectedText {
 			get => Secret ? null : selectedText;
 			private set => selectedText = value;
 		}
@@ -1059,8 +1053,8 @@ namespace Terminal.Gui {
 				EnsureHasFocus ();
 				int x = PositionCursor (ev);
 				int sbw = x;
-				if (x == text.Count || (x > 0 && (char)text [x - 1] != ' ')
-					|| (x > 0 && (char)text [x] == ' ')) {
+				if (x == text.Count || (x > 0 && (char)text [x - 1].Value != ' ')
+					|| (x > 0 && (char)text [x].Value == ' ')) {
 
 					var newPosBw = GetModel ().WordBackward (x, 0);
 					if (newPosBw == null) return true;
@@ -1135,7 +1129,7 @@ namespace Terminal.Gui {
 				length = Math.Abs (x + direction <= text.Count ? x + direction - selectedStart : text.Count - selectedStart);
 				SetSelectedStartSelectedLength ();
 				if (start > -1 && length > 0) {
-					selectedText = length > 0 ? ustring.Make (text).ToString ().Substring (
+					selectedText = length > 0 ? StringExtensions.ToString (text).ToString ().Substring (
 						start < 0 ? 0 : start, length > text.Count ? text.Count : length) : "";
 					if (first > start) {
 						first = start;
@@ -1195,22 +1189,22 @@ namespace Terminal.Gui {
 
 			Clipboard.Contents = SelectedText;
 			var newText = DeleteSelectedText ();
-			Text = ustring.Make (newText);
+			Text = StringExtensions.ToString (newText);
 			Adjust ();
 		}
 
 		List<Rune> DeleteSelectedText ()
 		{
-			ustring actualText = Text;
+			string actualText = Text;
 			SetSelectedStartSelectedLength ();
 			int selStart = SelectedStart > -1 ? start : point;
 			(var _, var len) = TextModel.DisplaySize (text, 0, selStart, false);
 			(var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + length, false);
-			(var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.RuneCount, false);
-			var newText = actualText [0, len] +
-				actualText [len + len2, len + len2 + len3];
+			(var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.GetRuneCount (), false);
+			var newText = actualText [..len] +
+				actualText.Substring (len + len2, len3);
 			ClearAllSelection ();
-			point = selStart >= newText.RuneCount ? newText.RuneCount : selStart;
+			point = selStart >= newText.GetRuneCount () ? newText.GetRuneCount () : selStart;
 			return newText.ToRuneList ();
 		}
 
@@ -1219,21 +1213,21 @@ namespace Terminal.Gui {
 		/// </summary>
 		public virtual void Paste ()
 		{
-			if (ReadOnly || ustring.IsNullOrEmpty (Clipboard.Contents)) {
+			if (ReadOnly || string.IsNullOrEmpty (Clipboard.Contents)) {
 				return;
 			}
 
 			SetSelectedStartSelectedLength ();
 			int selStart = start == -1 ? CursorPosition : start;
-			ustring actualText = Text;
+			string actualText = Text;
 			(int _, int len) = TextModel.DisplaySize (text, 0, selStart, false);
 			(var _, var len2) = TextModel.DisplaySize (text, selStart, selStart + length, false);
-			(var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.RuneCount, false);
-			ustring cbTxt = Clipboard.Contents.Split ("\n") [0] ?? "";
-			Text = actualText [0, len] +
+			(var _, var len3) = TextModel.DisplaySize (text, selStart + length, actualText.GetRuneCount (), false);
+			string cbTxt = Clipboard.Contents.Split ("\n") [0] ?? "";
+			Text = actualText [..len] +
 				cbTxt +
-				actualText [len + len2, len + len2 + len3];
-			point = selStart + cbTxt.RuneCount;
+				actualText.Substring (len + len2, len3);
+			point = selStart + cbTxt.GetRuneCount ();
 			ClearAllSelection ();
 			SetNeedsDisplay ();
 			Adjust ();
@@ -1244,7 +1238,7 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// <param name="newText">The new text to be replaced.</param>
 		/// <returns>Returns the <see cref="TextChangingEventArgs"/></returns>
-		public virtual TextChangingEventArgs OnTextChanging (ustring newText)
+		public virtual TextChangingEventArgs OnTextChanging (string newText)
 		{
 			var ev = new TextChangingEventArgs (newText);
 			TextChanging?.Invoke (this, ev);

+ 24 - 24
Terminal.Gui/Views/TextValidateField.cs

@@ -5,7 +5,7 @@
 //	José Miguel Perricone ([email protected])
 //
 
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.ComponentModel;
@@ -83,12 +83,12 @@ namespace Terminal.Gui {
 			/// <summary>
 			/// Set the input text and get the current value.
 			/// </summary>
-			ustring Text { get; set; }
+			string Text { get; set; }
 
 			/// <summary>
 			/// Gets the formatted string for display.
 			/// </summary>
-			ustring DisplayText { get; }
+			string DisplayText { get; }
 		}
 
 		//////////////////////////////////////////////////////////////////////////////
@@ -117,13 +117,13 @@ namespace Terminal.Gui {
 			/// <summary>
 			/// Mask property
 			/// </summary>
-			public ustring Mask {
+			public string Mask {
 				get {
 					return provider?.Mask;
 				}
 				set {
 					var current = provider != null ? provider.ToString (false, false) : string.Empty;
-					provider = new MaskedTextProvider (value == ustring.Empty ? "&&&&&&" : value.ToString ());
+					provider = new MaskedTextProvider (value == string.Empty ? "&&&&&&" : value.ToString ());
 					if (string.IsNullOrEmpty (current) == false) {
 						provider.Set (current);
 					}
@@ -131,7 +131,7 @@ namespace Terminal.Gui {
 			}
 
 			///<inheritdoc/>
-			public ustring Text {
+			public string Text {
 				get {
 					return provider.ToString ();
 				}
@@ -147,7 +147,7 @@ namespace Terminal.Gui {
 			public bool Fixed => true;
 
 			///<inheritdoc/>
-			public ustring DisplayText => provider.ToDisplayString ();
+			public string DisplayText => provider.ToDisplayString ();
 
 			///<inheritdoc/>
 			public int Cursor (int pos)
@@ -230,9 +230,9 @@ namespace Terminal.Gui {
 			/// <summary>
 			/// Regex pattern property.
 			/// </summary>
-			public ustring Pattern {
+			public string Pattern {
 				get {
-					return ustring.Make (pattern);
+					return StringExtensions.ToString (pattern);
 				}
 				set {
 					pattern = value.ToRuneList ();
@@ -242,18 +242,18 @@ namespace Terminal.Gui {
 			}
 
 			///<inheritdoc/>
-			public ustring Text {
+			public string Text {
 				get {
-					return ustring.Make (text);
+					return StringExtensions.ToString (text);
 				}
 				set {
-					text = value != ustring.Empty ? value.ToRuneList () : null;
+					text = value != string.Empty ? value.ToRuneList () : null;
 					SetupText ();
 				}
 			}
 
 			///<inheritdoc/>
-			public ustring DisplayText => Text;
+			public string DisplayText => Text;
 
 			///<inheritdoc/>
 			public bool IsValid {
@@ -272,7 +272,7 @@ namespace Terminal.Gui {
 
 			bool Validate (List<Rune> text)
 			{
-				var match = regex.Match (ustring.Make (text).ToString ());
+				var match = regex.Match (StringExtensions.ToString (text));
 				return match.Success;
 			}
 
@@ -331,9 +331,9 @@ namespace Terminal.Gui {
 			public bool InsertAt (char ch, int pos)
 			{
 				var aux = text.ToList ();
-				aux.Insert (pos, ch);
+				aux.Insert (pos, (Rune)ch);
 				if (Validate (aux) || ValidateOnInput == false) {
-					text.Insert (pos, ch);
+					text.Insert (pos, (Rune)ch);
 					return true;
 				}
 				return false;
@@ -353,7 +353,7 @@ namespace Terminal.Gui {
 			/// </summary>
 			private void CompileMask ()
 			{
-				regex = new Regex (ustring.Make (pattern).ToString (), RegexOptions.Compiled);
+				regex = new Regex (StringExtensions.ToString (pattern), RegexOptions.Compiled);
 			}
 		}
 		#endregion
@@ -419,7 +419,7 @@ namespace Terminal.Gui {
 			set {
 				provider = value;
 				if (provider.Fixed == true) {
-					this.Width = provider.DisplayText == ustring.Empty ? 10 : Text.Length;
+					this.Width = provider.DisplayText == string.Empty ? 10 : Text.Length;
 				}
 				HomeKeyHandler ();
 				SetNeedsDisplay ();
@@ -446,10 +446,10 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// Text
 		/// </summary>
-		public new ustring Text {
+		public new string Text {
 			get {
 				if (provider == null) {
-					return ustring.Empty;
+					return string.Empty;
 				}
 
 				return provider.Text;
@@ -519,20 +519,20 @@ namespace Terminal.Gui {
 			// Left Margin
 			Driver.SetAttribute (textColor);
 			for (int i = 0; i < margin_left; i++) {
-				Driver.AddRune (' ');
+				Driver.AddRune ((Rune)' ');
 			}
 
 			// Content
 			Driver.SetAttribute (textColor);
 			// Content
 			for (int i = 0; i < provider.DisplayText.Length; i++) {
-				Driver.AddRune (provider.DisplayText [i]);
+				Driver.AddRune ((Rune)provider.DisplayText [i]);
 			}
 
 			// Right Margin
 			Driver.SetAttribute (textColor);
 			for (int i = 0; i < margin_right; i++) {
-				Driver.AddRune (' ');
+				Driver.AddRune ((Rune)' ');
 			}
 		}
 
@@ -627,7 +627,7 @@ namespace Terminal.Gui {
 
 			var key = new Rune ((uint)kb.KeyValue);
 
-			var inserted = provider.InsertAt ((char)key, cursorPosition);
+			var inserted = provider.InsertAt ((char)key.Value, cursorPosition);
 
 			if (inserted) {
 				CursorRight ();

+ 102 - 103
Terminal.Gui/Views/TextView.cs

@@ -7,9 +7,7 @@ using System.Linq;
 using System.Runtime.CompilerServices;
 using System.Text;
 using System.Threading;
-using NStack;
 using Terminal.Gui.Resources;
-using Rune = System.Rune;
 
 namespace Terminal.Gui {
 	class TextModel {
@@ -36,9 +34,9 @@ namespace Terminal.Gui {
 			return true;
 		}
 
-		// Turns the ustring into runes, this does not split the 
+		// Turns the string into runes, this does not split the 
 		// contents on a newline if it is present.
-		internal static List<Rune> ToRunes (ustring str)
+		internal static List<Rune> ToRunes (string str)
 		{
 			List<Rune> runes = new List<Rune> ();
 			foreach (var x in str.ToRunes ()) {
@@ -48,35 +46,36 @@ namespace Terminal.Gui {
 		}
 
 		// Splits a string into a List that contains a List<Rune> for each line
-		public static List<List<Rune>> StringToRunes (ustring content)
+		public static List<List<Rune>> StringToRunes (string content)
 		{
 			var lines = new List<List<Rune>> ();
 			int start = 0, i = 0;
 			var hasCR = false;
+			var runes = content.EnumerateRunes ().ToList ();
 			// ASCII code 13 = Carriage Return.
 			// ASCII code 10 = Line Feed.
-			for (; i < content.Length; i++) {
-				if (content [i] == 13) {
+			for (; i < runes.Count; i++) {
+				if (runes [i].Value == 13) {
 					hasCR = true;
 					continue;
 				}
-				if (content [i] == 10) {
+				if (runes [i].Value == 10) {
 					if (i - start > 0)
-						lines.Add (ToRunes (content [start, hasCR ? i - 1 : i]));
+						lines.Add (runes.GetRange (start, hasCR ? i - 1 - start : i - start));
 					else
-						lines.Add (ToRunes (ustring.Empty));
+						lines.Add (ToRunes (string.Empty));
 					start = i + 1;
 					hasCR = false;
 				}
 			}
 			if (i - start >= 0)
-				lines.Add (ToRunes (content [start, null]));
+				lines.Add (runes.GetRange (start, i - start));
 			return lines;
 		}
 
 		void Append (List<byte> line)
 		{
-			var str = ustring.Make (line.ToArray ());
+			var str = StringExtensions.ToString (line.ToArray ());
 			_lines.Add (ToRunes (str));
 		}
 
@@ -110,7 +109,7 @@ namespace Terminal.Gui {
 			OnLinesLoaded ();
 		}
 
-		public void LoadString (ustring content)
+		public void LoadString (string content)
 		{
 			_lines = StringToRunes (content);
 
@@ -126,7 +125,7 @@ namespace Terminal.Gui {
 		{
 			var sb = new StringBuilder ();
 			for (int i = 0; i < _lines.Count; i++) {
-				sb.Append (ustring.Make (_lines [i]));
+				sb.Append (StringExtensions.ToString (_lines [i]));
 				if ((i + 1) < _lines.Count) {
 					sb.AppendLine ();
 				}
@@ -205,7 +204,7 @@ namespace Terminal.Gui {
 			last = last < _lines.Count ? last : _lines.Count;
 			for (int i = first; i < last; i++) {
 				var line = GetLine (i);
-				var tabSum = line.Sum (r => r == '\t' ? Math.Max (tabWidth - 1, 0) : 0);
+				var tabSum = line.Sum (r => r.Value == '\t' ? Math.Max (tabWidth - 1, 0) : 0);
 				var l = line.Count + tabSum;
 				if (l > maxLength) {
 					maxLength = l;
@@ -234,8 +233,8 @@ namespace Terminal.Gui {
 			var pX = x + start;
 			for (int i = start; i < t.Count; i++) {
 				var r = t [i];
-				size += Rune.ColumnWidth (r);
-				if (r == '\t') {
+				size += r.GetColumns ();
+				if (r.Value == '\t') {
 					size += tabWidth + 1;
 				}
 				if (i == pX || (size > pX)) {
@@ -258,9 +257,9 @@ namespace Terminal.Gui {
 			int i = start == -1 ? 0 : start;
 			for (; i < tcount; i++) {
 				var rune = t [i];
-				size += Rune.ColumnWidth (rune);
-				len += Rune.RuneLen (rune);
-				if (rune == '\t') {
+				size += rune.GetColumns ();
+				len += rune.GetEncodingLength (Encoding.Unicode);
+				if (rune.Value == '\t') {
 					size += tabWidth + 1;
 					len += tabWidth - 1;
 				}
@@ -273,9 +272,9 @@ namespace Terminal.Gui {
 
 			bool IsWideRune (Rune r, int tWidth, out int s, out int l)
 			{
-				s = Rune.ColumnWidth (r);
-				l = Rune.RuneLen (r);
-				if (r == '\t') {
+				s = r.GetColumns ();
+				l = r.GetEncodingLength ();
+				if (r.Value == '\t') {
 					s += tWidth + 1;
 					l += tWidth - 1;
 				}
@@ -298,8 +297,8 @@ namespace Terminal.Gui {
 
 			for (int i = tcount; i >= 0; i--) {
 				var rune = t [i];
-				size += Rune.ColumnWidth (rune);
-				if (rune == '\t') {
+				size += rune.GetColumns ();
+				if (rune.Value == '\t') {
 					size += tabWidth + 1;
 				}
 				if (size > width) {
@@ -318,7 +317,7 @@ namespace Terminal.Gui {
 
 		(Point startPointToFind, Point currentPointToFind, bool found) _toFind;
 
-		internal (Point current, bool found) FindNextText (ustring text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
+		internal (Point current, bool found) FindNextText (string text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
 		{
 			if (text == null || _lines.Count == 0) {
 				gaveFullTurn = false;
@@ -337,7 +336,7 @@ namespace Terminal.Gui {
 			return foundPos;
 		}
 
-		internal (Point current, bool found) FindPreviousText (ustring text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
+		internal (Point current, bool found) FindPreviousText (string text, out bool gaveFullTurn, bool matchCase = false, bool matchWholeWord = false)
 		{
 			if (text == null || _lines.Count == 0) {
 				gaveFullTurn = false;
@@ -358,7 +357,7 @@ namespace Terminal.Gui {
 			return foundPos;
 		}
 
-		internal (Point current, bool found) ReplaceAllText (ustring text, bool matchCase = false, bool matchWholeWord = false, ustring textToReplace = null)
+		internal (Point current, bool found) ReplaceAllText (string text, bool matchCase = false, bool matchWholeWord = false, string textToReplace = null)
 		{
 			bool found = false;
 			Point pos = Point.Empty;
@@ -395,7 +394,7 @@ namespace Terminal.Gui {
 
 			string GetText (List<Rune> x)
 			{
-				var txt = ustring.Make (x).ToString ();
+				var txt = StringExtensions.ToString (x);
 				if (!matchCase) {
 					txt = txt.ToUpper ();
 				}
@@ -405,16 +404,16 @@ namespace Terminal.Gui {
 			return (pos, found);
 		}
 
-		ustring ReplaceText (List<Rune> source, ustring textToReplace, string matchText, int col)
+		string ReplaceText (List<Rune> source, string textToReplace, string matchText, int col)
 		{
-			var origTxt = ustring.Make (source);
+			var origTxt = StringExtensions.ToString (source);
 			(int _, int len) = TextModel.DisplaySize (source, 0, col, false);
-			(var _, var len2) = TextModel.DisplaySize (source, col, col + matchText.Length, false);
-			(var _, var len3) = TextModel.DisplaySize (source, col + matchText.Length, origTxt.RuneCount, false);
+			(int _, int len2) = TextModel.DisplaySize (source, col, col + matchText.Length, false);
+			(int _, int len3) = TextModel.DisplaySize (source, col + matchText.Length, origTxt.GetRuneCount (), false);
 
-			return origTxt [0, len] +
-				textToReplace.ToString () +
-				origTxt [len + len2, len + len2 + len3];
+			return origTxt [..len] +
+				textToReplace +
+				origTxt.Substring (len + len2, len3);
 		}
 
 		bool ApplyToFind ((Point current, bool found) foundPos)
@@ -434,11 +433,11 @@ namespace Terminal.Gui {
 			return gaveFullTurn;
 		}
 
-		(Point current, bool found) GetFoundNextTextPoint (ustring text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
+		(Point current, bool found) GetFoundNextTextPoint (string text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
 		{
 			for (int i = start.Y; i < linesCount; i++) {
 				var x = _lines [i];
-				var txt = ustring.Make (x).ToString ();
+				var txt = StringExtensions.ToString (x);
 				if (!matchCase) {
 					txt = txt.ToUpper ();
 				}
@@ -459,11 +458,11 @@ namespace Terminal.Gui {
 			return (Point.Empty, false);
 		}
 
-		(Point current, bool found) GetFoundPreviousTextPoint (ustring text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
+		(Point current, bool found) GetFoundPreviousTextPoint (string text, int linesCount, bool matchCase, bool matchWholeWord, Point start)
 		{
 			for (int i = linesCount; i >= 0; i--) {
 				var x = _lines [i];
-				var txt = ustring.Make (x).ToString ();
+				var txt = StringExtensions.ToString (x);
 				if (!matchCase) {
 					txt = txt.ToUpper ();
 				}
@@ -495,8 +494,8 @@ namespace Terminal.Gui {
 			var start = index > 0 ? index - 1 : 0;
 			var end = index + txt.Length;
 
-			if ((start == 0 || Rune.IsWhiteSpace (source [start]))
-				&& (end == source.Length || Rune.IsWhiteSpace (source [end]))) {
+			if ((start == 0 || Rune.IsWhiteSpace ((Rune)source [start]))
+				&& (end == source.Length || Rune.IsWhiteSpace ((Rune)source [end]))) {
 				return true;
 			}
 
@@ -519,7 +518,7 @@ namespace Terminal.Gui {
 			if (line.Count > 0) {
 				return line [col > line.Count - 1 ? line.Count - 1 : col];
 			} else {
-				return 0;
+				return default;
 			}
 		}
 
@@ -546,7 +545,7 @@ namespace Terminal.Gui {
 					return true;
 				}
 			}
-			rune = 0;
+			rune = default;
 			return false;
 		}
 
@@ -560,7 +559,7 @@ namespace Terminal.Gui {
 				return true;
 			}
 			if (row == 0) {
-				rune = 0;
+				rune = default;
 				return false;
 			}
 			while (row > 0) {
@@ -572,7 +571,7 @@ namespace Terminal.Gui {
 					return true;
 				}
 			}
-			rune = 0;
+			rune = default;
 			return false;
 		}
 
@@ -758,7 +757,7 @@ namespace Terminal.Gui {
 
 		List<HistoryTextItem> _historyTextItems = new List<HistoryTextItem> ();
 		int _idxHistoryText = -1;
-		ustring _originalText;
+		string _originalText;
 
 		public bool IsFromHistory { get; private set; }
 
@@ -919,7 +918,7 @@ namespace Terminal.Gui {
 			ChangeText?.Invoke (this, lines);
 		}
 
-		public void Clear (ustring text)
+		public void Clear (string text)
 		{
 			_historyTextItems.Clear ();
 			_idxHistoryText = -1;
@@ -927,7 +926,7 @@ namespace Terminal.Gui {
 			OnChangeText (null);
 		}
 
-		public bool IsDirty (ustring text)
+		public bool IsDirty (string text)
 		{
 			return _originalText != text;
 		}
@@ -974,7 +973,7 @@ namespace Terminal.Gui {
 			for (int i = 0; i < Model.Count; i++) {
 				var line = Model.GetLine (i);
 				var wrappedLines = ToListRune (
-					TextFormatter.Format (ustring.Make (line), width, TextAlignment.Left, true, preserveTrailingSpaces, tabWidth));
+					TextFormatter.Format (StringExtensions.ToString (line), width, TextAlignment.Left, true, preserveTrailingSpaces, tabWidth));
 				int sumColWidth = 0;
 				for (int j = 0; j < wrappedLines.Count; j++) {
 					var wrapLine = wrappedLines [j];
@@ -1031,7 +1030,7 @@ namespace Terminal.Gui {
 			return wrappedModel;
 		}
 
-		public List<List<Rune>> ToListRune (List<ustring> textList)
+		public List<List<Rune>> ToListRune (List<string> textList)
 		{
 			var runesList = new List<List<Rune>> ();
 
@@ -1598,7 +1597,7 @@ namespace Terminal.Gui {
 			//historyText.Clear (Text);
 
 			if (!_multiline && !IsInitialized) {
-				_currentColumn = Text.RuneCount;
+				_currentColumn = Text.GetRuneCount ();
 				_leftColumn = _currentColumn > Frame.Width + 1 ? _currentColumn - Frame.Width + 1 : 0;
 			}
 		}
@@ -1687,7 +1686,7 @@ namespace Terminal.Gui {
 		/// The <see cref="TextChanged"/> event is fired whenever this property is set. Note, however,
 		/// that Text is not set by <see cref="TextView"/> as the user types.
 		/// </remarks>
-		public override ustring Text {
+		public override string Text {
 			get {
 				if (_wordWrap) {
 					return _wrapManager.Model.ToString ();
@@ -1814,10 +1813,10 @@ namespace Terminal.Gui {
 		/// <summary>
 		/// The selected text.
 		/// </summary>
-		public ustring SelectedText {
+		public string SelectedText {
 			get {
 				if (!_selecting || (_model.Count == 1 && _model.GetLine (0).Count == 0)) {
-					return ustring.Empty;
+					return string.Empty;
 				}
 
 				return GetSelectedRegion ();
@@ -2002,7 +2001,7 @@ namespace Terminal.Gui {
 			set {
 				_historyText.Clear (Text);
 			}
-		} 
+		}
 
 		/// <summary>
 		/// Indicates whatever the text has history changes or not.
@@ -2121,8 +2120,8 @@ namespace Terminal.Gui {
 				for (int idx = _leftColumn; idx < line.Count; idx++) {
 					if (idx >= _currentColumn)
 						break;
-					var cols = Rune.ColumnWidth (line [idx]);
-					if (line [idx] == '\t') {
+					var cols = line [idx].GetColumns ();
+					if (line [idx].Value == '\t') {
 						cols += TabWidth + 1;
 					}
 					if (!TextModel.SetCol (ref col, Frame.Width, cols)) {
@@ -2147,7 +2146,7 @@ namespace Terminal.Gui {
 			for (int row = top; row < bottom; row++) {
 				Move (left, row);
 				for (int col = left; col < right; col++)
-					AddRune (col, row, ' ');
+					AddRune (col, row, (Rune)' ');
 			}
 		}
 
@@ -2298,15 +2297,15 @@ namespace Terminal.Gui {
 		}
 
 		//
-		// Returns a ustring with the text in the selected 
+		// Returns a string with the text in the selected 
 		// region.
 		//
-		ustring GetRegion (int? sRow = null, int? sCol = null, int? cRow = null, int? cCol = null, TextModel model = null)
+		string GetRegion (int? sRow = null, int? sCol = null, int? cRow = null, int? cCol = null, TextModel model = null)
 		{
 			long start, end;
 			GetEncodedRegionBounds (out start, out end, sRow, sCol, cRow, cCol);
 			if (start == end) {
-				return ustring.Empty;
+				return string.Empty;
 			}
 			int startRow = (int)(start >> 32);
 			var maxrow = ((int)(end >> 32));
@@ -2317,14 +2316,14 @@ namespace Terminal.Gui {
 			if (startRow == maxrow)
 				return StringFromRunes (line.GetRange (startCol, endCol - startCol));
 
-			ustring res = StringFromRunes (line.GetRange (startCol, line.Count - startCol));
+			string res = StringFromRunes (line.GetRange (startCol, line.Count - startCol));
 
 			for (int row = startRow + 1; row < maxrow; row++) {
-				res = res + ustring.Make (Environment.NewLine) + StringFromRunes (model == null
+				res = res + Environment.NewLine + StringFromRunes (model == null
 					? this._model.GetLine (row) : model.GetLine (row));
 			}
 			line = model == null ? this._model.GetLine (maxrow) : model.GetLine (maxrow);
-			res = res + ustring.Make (Environment.NewLine) + StringFromRunes (line.GetRange (0, endCol));
+			res = res + Environment.NewLine + StringFromRunes (line.GetRange (0, endCol));
 			return res;
 		}
 
@@ -2419,8 +2418,8 @@ namespace Terminal.Gui {
 		/// <param name="textToReplace">The text to replace.</param>
 		/// <param name="replace"><c>true</c>If is replacing.<c>false</c>otherwise.</param>
 		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
-		public bool FindNextText (ustring textToFind, out bool gaveFullTurn, bool matchCase = false,
-			bool matchWholeWord = false, ustring textToReplace = null, bool replace = false)
+		public bool FindNextText (string textToFind, out bool gaveFullTurn, bool matchCase = false,
+			bool matchWholeWord = false, string textToReplace = null, bool replace = false)
 		{
 			if (_model.Count == 0) {
 				gaveFullTurn = false;
@@ -2444,8 +2443,8 @@ namespace Terminal.Gui {
 		/// <param name="textToReplace">The text to replace.</param>
 		/// <param name="replace"><c>true</c>If the text was found.<c>false</c>otherwise.</param>
 		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
-		public bool FindPreviousText (ustring textToFind, out bool gaveFullTurn, bool matchCase = false,
-			bool matchWholeWord = false, ustring textToReplace = null, bool replace = false)
+		public bool FindPreviousText (string textToFind, out bool gaveFullTurn, bool matchCase = false,
+			bool matchWholeWord = false, string textToReplace = null, bool replace = false)
 		{
 			if (_model.Count == 0) {
 				gaveFullTurn = false;
@@ -2475,8 +2474,8 @@ namespace Terminal.Gui {
 		/// <param name="matchWholeWord">The match whole word setting.</param>
 		/// <param name="textToReplace">The text to replace.</param>
 		/// <returns><c>true</c>If the text was found.<c>false</c>otherwise.</returns>
-		public bool ReplaceAllText (ustring textToFind, bool matchCase = false, bool matchWholeWord = false,
-			ustring textToReplace = null)
+		public bool ReplaceAllText (string textToFind, bool matchCase = false, bool matchWholeWord = false,
+			string textToReplace = null)
 		{
 			if (_isReadOnly || _model.Count == 0) {
 				return false;
@@ -2489,25 +2488,25 @@ namespace Terminal.Gui {
 			return SetFoundText (textToFind, foundPos, textToReplace, false, true);
 		}
 
-		bool SetFoundText (ustring text, (Point current, bool found) foundPos,
-			ustring textToReplace = null, bool replace = false, bool replaceAll = false)
+		bool SetFoundText (string text, (Point current, bool found) foundPos,
+			string textToReplace = null, bool replace = false, bool replaceAll = false)
 		{
 			if (foundPos.found) {
 				StartSelecting ();
 				_selectionStartColumn = foundPos.current.X;
 				_selectionStartRow = foundPos.current.Y;
 				if (!replaceAll) {
-					_currentColumn = _selectionStartColumn + text.RuneCount;
+					_currentColumn = _selectionStartColumn + text.GetRuneCount ();
 				} else {
-					_currentColumn = _selectionStartColumn + textToReplace.RuneCount;
+					_currentColumn = _selectionStartColumn + textToReplace.GetRuneCount ();
 				}
 				_currentRow = foundPos.current.Y;
 				if (!_isReadOnly && replace) {
 					Adjust ();
 					ClearSelectedRegion ();
-					InsertText (textToReplace);
+					InsertAllText (textToReplace);
 					StartSelecting ();
-					_selectionStartColumn = _currentColumn - textToReplace.RuneCount;
+					_selectionStartColumn = _currentColumn - textToReplace.GetRuneCount ();
 				} else {
 					UpdateWrapModel ();
 					SetNeedsDisplay ();
@@ -2591,7 +2590,7 @@ namespace Terminal.Gui {
 			UnwrappedCursorPosition?.Invoke (this, new PointEventArgs (new Point ((int)col, (int)row)));
 		}
 
-		ustring GetSelectedRegion ()
+		string GetSelectedRegion ()
 		{
 			var cRow = _currentRow;
 			var cCol = _currentColumn;
@@ -2625,8 +2624,8 @@ namespace Terminal.Gui {
 
 				Move (0, row);
 				for (int idxCol = _leftColumn; idxCol < lineRuneCount; idxCol++) {
-					var rune = idxCol >= lineRuneCount ? ' ' : line [idxCol];
-					var cols = Rune.ColumnWidth (rune);
+					var rune = idxCol >= lineRuneCount ? (Rune)' ' : line [idxCol];
+					var cols = rune.GetColumns ();
 					if (idxCol < line.Count && _selecting && PointInSelection (idxCol, idxRow)) {
 						SetSelectionColor (line, idxCol);
 					} else if (idxCol == _currentColumn && idxRow == _currentRow && !_selecting && !Used
@@ -2638,23 +2637,23 @@ namespace Terminal.Gui {
 						SetNormalColor (line, idxCol);
 					}
 
-					if (rune == '\t') {
+					if (rune.Value == '\t') {
 						cols += TabWidth + 1;
 						if (col + cols > right) {
 							cols = right - col;
 						}
 						for (int i = 0; i < cols; i++) {
 							if (col + i < right) {
-								AddRune (col + i, row, ' ');
+								AddRune (col + i, row, (Rune)' ');
 							}
 						}
 					} else {
-						AddRune (col, row, rune);
+						AddRune (col, row, (Rune)rune);
 					}
 					if (!TextModel.SetCol (ref col, contentArea.Right, cols)) {
 						break;
 					}
-					if (idxCol + 1 < lineRuneCount && col + Rune.ColumnWidth (line [idxCol + 1]) > right) {
+					if (idxCol + 1 < lineRuneCount && col + line [idxCol + 1].GetColumns () > right) {
 						break;
 					}
 				}
@@ -2711,14 +2710,14 @@ namespace Terminal.Gui {
 			set { base.CanFocus = value; }
 		}
 
-		void SetClipboard (ustring text)
+		void SetClipboard (string text)
 		{
 			if (text != null) {
 				Clipboard.Contents = text;
 			}
 		}
 
-		void AppendClipboard (ustring text)
+		void AppendClipboard (string text)
 		{
 			Clipboard.Contents += text;
 		}
@@ -2770,20 +2769,20 @@ namespace Terminal.Gui {
 			}
 		}
 
-		ustring StringFromRunes (List<Rune> runes)
+		string StringFromRunes (List<Rune> runes)
 		{
 			if (runes == null)
 				throw new ArgumentNullException (nameof (runes));
 			int size = 0;
 			foreach (var rune in runes) {
-				size += Utf8.RuneLen (rune);
+				size += rune.GetEncodingLength ();
 			}
 			var encoded = new byte [size];
 			int offset = 0;
 			foreach (var rune in runes) {
-				offset += Utf8.EncodeRune (rune, encoded, offset);
+				offset += rune.Encode (encoded, offset);
 			}
-			return ustring.Make (encoded);
+			return StringExtensions.ToString (encoded);
 		}
 
 		/// <summary>
@@ -2794,9 +2793,9 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public List<Rune> GetCurrentLine () => _model.GetLine (_currentRow);
 
-		void InsertText (ustring text)
+		void InsertAllText (string text)
 		{
-			if (ustring.IsNullOrEmpty (text)) {
+			if (string.IsNullOrEmpty (text)) {
 				return;
 			}
 
@@ -3349,7 +3348,7 @@ namespace Terminal.Gui {
 				SetWrapModel ();
 
 				var currentLine = GetCurrentLine ();
-				if (currentLine.Count > 0 && currentLine [_currentColumn - 1] == '\t') {
+				if (currentLine.Count > 0 && currentLine [_currentColumn - 1].Value == '\t') {
 
 					_historyText.Add (new List<List<Rune>> () { new List<Rune> (currentLine) }, CursorPosition);
 
@@ -3587,7 +3586,7 @@ namespace Terminal.Gui {
 					_model.RemoveLine (_currentRow);
 
 					if (_model.Count > 0 || _lastWasKill) {
-						var val = ustring.Make (Environment.NewLine);
+						var val = Environment.NewLine;
 						if (_lastWasKill) {
 							AppendClipboard (val);
 						} else {
@@ -3613,7 +3612,7 @@ namespace Terminal.Gui {
 			} else {
 				var restCount = _currentColumn;
 				var rest = currentLine.GetRange (0, restCount);
-				var val = ustring.Empty;
+				var val = string.Empty;
 				val += StringFromRunes (rest);
 				if (_lastWasKill) {
 					AppendClipboard (val);
@@ -3669,7 +3668,7 @@ namespace Terminal.Gui {
 						HistoryText.LineStatus.Removed);
 				}
 				if (_model.Count > 0 || _lastWasKill) {
-					var val = ustring.Make (Environment.NewLine);
+					var val = Environment.NewLine;
 					if (_lastWasKill) {
 						AppendClipboard (val);
 					} else {
@@ -3683,7 +3682,7 @@ namespace Terminal.Gui {
 			} else {
 				var restCount = currentLine.Count - _currentColumn;
 				var rest = currentLine.GetRange (_currentColumn, restCount);
-				var val = ustring.Empty;
+				var val = string.Empty;
 				val += StringFromRunes (rest);
 				if (_lastWasKill) {
 					AppendClipboard (val);
@@ -3915,14 +3914,14 @@ namespace Terminal.Gui {
 				_currentColumn = 0;
 			} else {
 				if (Used) {
-					Insert ((uint)kb.Key);
+					Insert ((Rune)(uint)kb.Key);
 					_currentColumn++;
 					if (_currentColumn >= _leftColumn + Frame.Width) {
 						_leftColumn++;
 						SetNeedsDisplay ();
 					}
 				} else {
-					Insert ((uint)kb.Key);
+					Insert ((Rune)(uint)kb.Key);
 					_currentColumn++;
 				}
 			}
@@ -4122,7 +4121,7 @@ namespace Terminal.Gui {
 				_copyWithoutSelection = false;
 			} else {
 				var currentLine = GetCurrentLine ();
-				SetClipboard (ustring.Make (currentLine));
+				SetClipboard (StringExtensions.ToString (currentLine));
 				_copyWithoutSelection = true;
 			}
 			UpdateWrapModel ();
@@ -4182,7 +4181,7 @@ namespace Terminal.Gui {
 					ClearRegion ();
 				}
 				_copyWithoutSelection = false;
-				InsertText (contents);
+				InsertAllText (contents);
 
 				if (_selecting) {
 					_historyText.ReplaceLast (new List<List<Rune>> () { new List<Rune> (GetCurrentLine ()) }, CursorPosition,
@@ -4424,8 +4423,8 @@ namespace Terminal.Gui {
 				}
 				ProcessMouseClick (ev, out List<Rune> line);
 				(int col, int row)? newPos;
-				if (_currentColumn == line.Count || (_currentColumn > 0 && (line [_currentColumn - 1] != ' '
-					|| line [_currentColumn] == ' '))) {
+				if (_currentColumn == line.Count || (_currentColumn > 0 && (line [_currentColumn - 1].Value != ' '
+					|| line [_currentColumn].Value == ' '))) {
 
 					newPos = _model.WordBackward (_currentColumn, _currentRow);
 					if (newPos.HasValue) {

+ 4 - 4
Terminal.Gui/Views/TileView.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -66,7 +66,7 @@ namespace Terminal.Gui {
 			/// <param name="oldTitle">The <see cref="Title"/> that is/has been replaced.</param>
 			/// <param name="newTitle">The new <see cref="Title"/> to be replaced.</param>
 			/// <returns><c>true</c> if an event handler cancelled the Title change.</returns>
-			public virtual bool OnTitleChanging (ustring oldTitle, ustring newTitle)
+			public virtual bool OnTitleChanging (string oldTitle, string newTitle)
 			{
 				var args = new TitleEventArgs (oldTitle, newTitle);
 				TitleChanging?.Invoke (this, args);
@@ -84,7 +84,7 @@ namespace Terminal.Gui {
 			/// </summary>
 			/// <param name="oldTitle">The <see cref="Title"/> that is/has been replaced.</param>
 			/// <param name="newTitle">The new <see cref="Title"/> to be replaced.</param>
-			public virtual void OnTitleChanged (ustring oldTitle, ustring newTitle)
+			public virtual void OnTitleChanged (string oldTitle, string newTitle)
 			{
 				var args = new TitleEventArgs (oldTitle, newTitle);
 				TitleChanged?.Invoke (this, args);
@@ -467,7 +467,7 @@ namespace Terminal.Gui {
 				var title = titleToRender.GetTrimmedTitle ();
 
 				for (int i = 0; i < title.Length; i++) {
-					AddRune (renderAt.X + i, renderAt.Y, title [i]);
+					AddRune (renderAt.X + i, renderAt.Y, (Rune)title [i]);
 				}
 			}
 		}

+ 11 - 11
Terminal.Gui/Views/TimeField.cs

@@ -7,7 +7,7 @@
 using System;
 using System.Globalization;
 using System.Linq;
-using NStack;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>
@@ -174,19 +174,19 @@ namespace Terminal.Gui {
 			newText.Add (key);
 			if (CursorPosition < fieldLen)
 				newText = newText.Concat (text.GetRange (CursorPosition + 1, text.Count - (CursorPosition + 1))).ToList ();
-			return SetText (ustring.Make (newText));
+			return SetText (StringExtensions.ToString (newText));
 		}
 
-		bool SetText (ustring text)
+		bool SetText (string text)
 		{
-			if (text.IsEmpty) {
+			if (string.IsNullOrEmpty (text)) {
 				return false;
 			}
 
-			ustring [] vals = text.Split (ustring.Make (sepChar));
+			string [] vals = text.Split (sepChar);
 			bool isValidTime = true;
-			int hour = Int32.Parse (vals [0].ToString ());
-			int minute = Int32.Parse (vals [1].ToString ());
+			int hour = Int32.Parse (vals [0]);
+			int minute = Int32.Parse (vals [1]);
 			int second = isShort ? 0 : vals.Length > 2 ? Int32.Parse (vals [2].ToString ()) : 0;
 			if (hour < 0) {
 				isValidTime = false;
@@ -260,7 +260,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return true;
 
-			if (SetText (TextModel.ToRunes (ustring.Make ((uint)kb.Key)).First ()))
+			if (SetText (TextModel.ToRunes (((Rune)(uint)kb.Key).ToString ()).First ()))
 				IncCursorPosition ();
 
 			return true;
@@ -297,7 +297,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return;
 
-			SetText ('0');
+			SetText ((Rune)'0');
 			DecCursorPosition ();
 			return;
 		}
@@ -308,7 +308,7 @@ namespace Terminal.Gui {
 			if (ReadOnly)
 				return;
 
-			SetText ('0');
+			SetText ((Rune)'0');
 			return;
 		}
 
@@ -336,7 +336,7 @@ namespace Terminal.Gui {
 		/// <param name="args">The event arguments</param>
 		public virtual void OnTimeChanged (DateTimeEventArgs<TimeSpan> args)
 		{
-			TimeChanged?.Invoke (this,args);
+			TimeChanged?.Invoke (this, args);
 		}
 	}
 }

+ 13 - 12
Terminal.Gui/Views/TreeView/Branch.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 
 namespace Terminal.Gui {
 	class Branch<T> where T : class {
@@ -73,8 +74,8 @@ namespace Terminal.Gui {
 		public virtual int GetWidth (ConsoleDriver driver)
 		{
 			return
-				GetLinePrefix (driver).Sum (Rune.ColumnWidth) +
-				Rune.ColumnWidth (GetExpandableSymbol (driver)) +
+				GetLinePrefix (driver).Sum (r => r.GetColumns ()) +
+				GetExpandableSymbol (driver).GetColumns () +
 				(tree.AspectGetter (Model) ?? "").Length;
 		}
 
@@ -111,7 +112,7 @@ namespace Terminal.Gui {
 					toSkip--;
 				} else {
 					driver.AddRune (r);
-					availableWidth -= Rune.ColumnWidth (r);
+					availableWidth -= r.GetColumns ();
 				}
 			}
 
@@ -140,7 +141,7 @@ namespace Terminal.Gui {
 				toSkip--;
 			} else {
 				driver.AddRune (expansion);
-				availableWidth -= Rune.ColumnWidth (expansion);
+				availableWidth -= expansion.GetColumns ();
 			}
 
 			// horizontal scrolling has already skipped the prefix but now must also skip some of the line body
@@ -153,9 +154,9 @@ namespace Terminal.Gui {
 			}
 
 			// If body of line is too long
-			if (lineBody.Sum (l => Rune.ColumnWidth (l)) > availableWidth) {
+			if (lineBody.EnumerateRunes ().Sum (l => l.GetColumns ()) > availableWidth) {
 				// remaining space is zero and truncate the line
-				lineBody = new string (lineBody.TakeWhile (c => (availableWidth -= Rune.ColumnWidth (c)) >= 0).ToArray ());
+				lineBody = new string (lineBody.TakeWhile (c => (availableWidth -= ((Rune)c).GetColumns ()) >= 0).ToArray ());
 				availableWidth = 0;
 			} else {
 
@@ -246,17 +247,17 @@ namespace Terminal.Gui {
 		/// <returns></returns>
 		public Rune GetExpandableSymbol (ConsoleDriver driver)
 		{
-			var leafSymbol = tree.Style.ShowBranchLines ? CM.Glyphs.HLine : ' ';
+			var leafSymbol = tree.Style.ShowBranchLines ? CM.Glyphs.HLine : (Rune)' ';
 
 			if (IsExpanded) {
-				return tree.Style.CollapseableSymbol ?? leafSymbol;
+				return tree.Style.CollapseableSymbol ?? (Rune)leafSymbol;
 			}
 
 			if (CanExpand ()) {
-				return tree.Style.ExpandableSymbol ?? leafSymbol;
+				return tree.Style.ExpandableSymbol ?? (Rune)leafSymbol;
 			}
 
-			return leafSymbol;
+			return (Rune)leafSymbol;
 		}
 
 		/// <summary>
@@ -402,12 +403,12 @@ namespace Terminal.Gui {
 			}
 
 			// if we could theoretically expand
-			if (!IsExpanded && tree.Style.ExpandableSymbol != null) {
+			if (!IsExpanded && tree.Style.ExpandableSymbol != default) {
 				return x == GetLinePrefix (driver).Count ();
 			}
 
 			// if we could theoretically collapse
-			if (IsExpanded && tree.Style.CollapseableSymbol != null) {
+			if (IsExpanded && tree.Style.CollapseableSymbol != default) {
 				return x == GetLinePrefix (driver).Count ();
 			}
 

+ 1 - 0
Terminal.Gui/Views/TreeView/TreeStyle.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Text;
 
 namespace Terminal.Gui {
 	/// <summary>

+ 2 - 2
Terminal.Gui/Views/TreeView/TreeView.cs

@@ -2,7 +2,7 @@
 // by [email protected]). Phillip has explicitly granted permission for his design
 // and code to be used in this library under the MIT license.
 
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Collections.ObjectModel;
@@ -156,7 +156,7 @@ namespace Terminal.Gui {
 		/// Error message to display when the control is not properly initialized at draw time 
 		/// (nodes added but no tree builder set).
 		/// </summary>
-		public static ustring NoBuilderError = "ERROR: TreeBuilder Not Set";
+		public static string NoBuilderError = "ERROR: TreeBuilder Not Set";
 		private Key objectActivationKey = Key.Enter;
 
 		/// <summary>

+ 1 - 1
Terminal.Gui/Views/Window.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Collections;
 using System.Text.Json.Serialization;
-using NStack;
+using System.Text;
 using Terminal.Gui;
 using static Terminal.Gui.ConfigurationManager;
 

+ 13 - 13
Terminal.Gui/Views/Wizard/Wizard.cs

@@ -1,7 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using NStack;
+using System.Text;
 using Terminal.Gui.Resources;
 
 namespace Terminal.Gui {
@@ -21,7 +21,7 @@ namespace Terminal.Gui {
 	/// <example>
 	/// <code>
 	/// using Terminal.Gui;
-	/// using NStack;
+	/// using System.Text;
 	/// 
 	/// Application.Init();
 	/// 
@@ -77,7 +77,7 @@ namespace Terminal.Gui {
 			///// The title of the <see cref="WizardStep"/>. 
 			///// </summary>
 			///// <remarks>The Title is only displayed when the <see cref="Wizard"/> is used as a modal pop-up (see <see cref="Wizard.Modal"/>.</remarks>
-			//public new ustring Title {
+			//public new string Title {
 			//	// BUGBUG: v2 - No need for this as View now has Title w/ notifications.
 			//	get => title;
 			//	set {
@@ -91,7 +91,7 @@ namespace Terminal.Gui {
 			//	}
 			//}
 
-			//private ustring title = ustring.Empty;
+			//private string title = string.Empty;
 
 			// The contentView works like the ContentView in FrameView.
 			private View contentView = new View () { Data = "WizardContentView" };
@@ -101,7 +101,7 @@ namespace Terminal.Gui {
 			/// the help pane will not be visible and the content will fill the entire WizardStep.
 			/// </summary>
 			/// <remarks>The help text is displayed using a read-only <see cref="TextView"/>.</remarks>
-			public ustring HelpText {
+			public string HelpText {
 				get => helpTextView.Text;
 				set {
 					helpTextView.Text = value;
@@ -116,13 +116,13 @@ namespace Terminal.Gui {
 			/// steps after the first step.
 			/// </summary>
 			/// <remarks>The default text is "Back"</remarks>
-			public ustring BackButtonText { get; set; } = ustring.Empty;
+			public string BackButtonText { get; set; } = string.Empty;
 
 			/// <summary>
 			/// Sets or gets the text for the next/finish button.
 			/// </summary>
 			/// <remarks>The default text is "Next..." if the Pane is not the last pane. Otherwise it is "Finish"</remarks>
-			public ustring NextButtonText { get; set; } = ustring.Empty;
+			public string NextButtonText { get; set; } = string.Empty;
 
 			/// <summary>
 			/// Initializes a new instance of the <see cref="Wizard"/> class using <see cref="LayoutStyle.Computed"/> positioning.
@@ -308,7 +308,7 @@ namespace Terminal.Gui {
 
 		private void Wizard_TitleChanged (object sender, TitleEventArgs e)
 		{
-			if (ustring.IsNullOrEmpty (wizardTitle)) {
+			if (string.IsNullOrEmpty (wizardTitle)) {
 				wizardTitle = e.NewTitle;
 			}
 		}
@@ -531,7 +531,7 @@ namespace Terminal.Gui {
 		///// <remarks>
 		///// The Title is only displayed when the <see cref="Wizard"/> <see cref="Wizard.Modal"/> is set to <c>false</c>.
 		///// </remarks>
-		//public new ustring Title {
+		//public new string Title {
 		//	get {
 		//		// The base (Dialog) Title holds the full title ("Wizard Title - Step Title")
 		//		return base.Title;
@@ -541,7 +541,7 @@ namespace Terminal.Gui {
 		//		base.Title = $"{wizardTitle}{(steps.Count > 0 && currentStep != null ? " - " + currentStep.Title : string.Empty)}";
 		//	}
 		//}
-		private ustring wizardTitle = ustring.Empty;
+		private string wizardTitle = string.Empty;
 
 		/// <summary>
 		/// Raised when the Back button in the <see cref="Wizard"/> is clicked. The Back button is always
@@ -664,14 +664,14 @@ namespace Terminal.Gui {
 			Title = $"{wizardTitle}{(steps.Count > 0 ? " - " + CurrentStep.Title : string.Empty)}";
 
 			// Configure the Back button
-			backBtn.Text = CurrentStep.BackButtonText != ustring.Empty ? CurrentStep.BackButtonText : Strings.wzBack; // "_Back";
+			backBtn.Text = CurrentStep.BackButtonText != string.Empty ? CurrentStep.BackButtonText : Strings.wzBack; // "_Back";
 			backBtn.Visible = (CurrentStep != GetFirstStep ());
 
 			// Configure the Next/Finished button
 			if (CurrentStep == GetLastStep ()) {
-				nextfinishBtn.Text = CurrentStep.NextButtonText != ustring.Empty ? CurrentStep.NextButtonText : Strings.wzFinish; // "Fi_nish";
+				nextfinishBtn.Text = CurrentStep.NextButtonText != string.Empty ? CurrentStep.NextButtonText : Strings.wzFinish; // "Fi_nish";
 			} else {
-				nextfinishBtn.Text = CurrentStep.NextButtonText != ustring.Empty ? CurrentStep.NextButtonText : Strings.wzNext; // "_Next...";
+				nextfinishBtn.Text = CurrentStep.NextButtonText != string.Empty ? CurrentStep.NextButtonText : Strings.wzNext; // "_Next...";
 			}
 
 			SizeStep (CurrentStep);

+ 1 - 1
UICatalog/Scenario.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;

+ 17 - 17
UICatalog/Scenarios/AllViewsTester.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.Diagnostics;
@@ -85,6 +84,7 @@ namespace UICatalog.Scenarios {
 				Height = Dim.Fill (0),
 				AllowsMarking = false,
 				ColorScheme = Colors.TopLevel,
+				SelectedItem = 0
 			};
 			_classListView.OpenSelectedItem += (s, a) => {
 				_settingsPane.SetFocus ();
@@ -119,7 +119,7 @@ namespace UICatalog.Scenarios {
 			};
 			_settingsPane.Add (_computedCheckBox);
 
-			var radioItems = new ustring [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
+			var radioItems = new string [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
 			_locationFrame = new FrameView ("Location (Pos)") {
 				X = Pos.Left (_computedCheckBox),
 				Y = Pos.Bottom (_computedCheckBox),
@@ -138,7 +138,7 @@ namespace UICatalog.Scenarios {
 			_xText = new TextField ($"{_xVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
 			_xText.TextChanged += (s, args) => {
 				try {
-					_xVal = int.Parse (_xText.Text.ToString ());
+					_xVal = int.Parse (_xText.Text);
 					DimPosChanged (_curView);
 				} catch {
 
@@ -148,13 +148,13 @@ namespace UICatalog.Scenarios {
 
 			_locationFrame.Add (_xRadioGroup);
 
-			radioItems = new ustring [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
+			radioItems = new string [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
 			label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
 			_locationFrame.Add (label);
 			_yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
 			_yText.TextChanged += (s,args) => {
 				try {
-					_yVal = int.Parse (_yText.Text.ToString ());
+					_yVal = int.Parse (_yText.Text);
 					DimPosChanged (_curView);
 				} catch {
 
@@ -175,7 +175,7 @@ namespace UICatalog.Scenarios {
 				Width = 40,
 			};
 
-			radioItems = new ustring [] { "Percent(width)", "Fill(width)", "Sized(width)" };
+			radioItems = new string [] { "Percent(width)", "Fill(width)", "Sized(width)" };
 			label = new Label ("width:") { X = 0, Y = 0 };
 			_sizeFrame.Add (label);
 			_wRadioGroup = new RadioGroup (radioItems) {
@@ -188,11 +188,11 @@ namespace UICatalog.Scenarios {
 				try {
 					switch (_wRadioGroup.SelectedItem) {
 					case 0:
-						_wVal = Math.Min (int.Parse (_wText.Text.ToString ()), 100);
+						_wVal = Math.Min (int.Parse (_wText.Text), 100);
 						break;
 					case 1:
 					case 2:
-						_wVal = int.Parse (_wText.Text.ToString ());
+						_wVal = int.Parse (_wText.Text);
 						break;
 					}
 					DimPosChanged (_curView);
@@ -203,7 +203,7 @@ namespace UICatalog.Scenarios {
 			_sizeFrame.Add (_wText);
 			_sizeFrame.Add (_wRadioGroup);
 
-			radioItems = new ustring [] { "Percent(height)", "Fill(height)", "Sized(height)" };
+			radioItems = new string [] { "Percent(height)", "Fill(height)", "Sized(height)" };
 			label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
 			_sizeFrame.Add (label);
 			_hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
@@ -211,11 +211,11 @@ namespace UICatalog.Scenarios {
 				try {
 					switch (_hRadioGroup.SelectedItem) {
 					case 0:
-						_hVal = Math.Min (int.Parse (_hText.Text.ToString ()), 100);
+						_hVal = Math.Min (int.Parse (_hText.Text), 100);
 						break;
 					case 1:
 					case 2:
-						_hVal = int.Parse (_hText.Text.ToString ());
+						_hVal = int.Parse (_hText.Text);
 						break;
 					}
 					DimPosChanged (_curView);
@@ -341,7 +341,7 @@ namespace UICatalog.Scenarios {
 
 		void UpdateTitle (View view)
 		{
-			_hostPane.Title = $"{view.GetType ().Name} - {view.X.ToString ()}, {view.Y.ToString ()}, {view.Width.ToString ()}, {view.Height.ToString ()}";
+			_hostPane.Title = $"{view.GetType ().Name} - {view.X}, {view.Y}, {view.Width}, {view.Height}";
 		}
 
 		List<Type> GetAllViewClassesCollection ()
@@ -388,7 +388,7 @@ namespace UICatalog.Scenarios {
 			// If the view supports a Text property, set it so we have something to look at
 			if (view.GetType ().GetProperty ("Text") != null) {
 				try {
-					view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Text") });
+					view.GetType ().GetProperty ("Text")?.GetSetMethod ()?.Invoke (view, new [] { "Test Text" });
 				} catch (TargetInvocationException e) {
 					MessageBox.ErrorQuery ("Exception", e.InnerException.Message, "Ok");
 					view = null;
@@ -397,8 +397,8 @@ namespace UICatalog.Scenarios {
 
 			// If the view supports a Title property, set it so we have something to look at
 			if (view != null && view.GetType ().GetProperty ("Title") != null) {
-				if (view.GetType ().GetProperty ("Title").PropertyType == typeof (ustring)) {
-					view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { ustring.Make ("Test Title") });
+				if (view.GetType ().GetProperty ("Title").PropertyType == typeof (string)) {
+					view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
 				} else {
 					view?.GetType ().GetProperty ("Title")?.GetSetMethod ()?.Invoke (view, new [] { "Test Title" });
 				}
@@ -406,7 +406,7 @@ namespace UICatalog.Scenarios {
 
 			// If the view supports a Source property, set it so we have something to look at
 			if (view != null && view.GetType ().GetProperty ("Source") != null && view.GetType ().GetProperty ("Source").PropertyType == typeof (Terminal.Gui.IListDataSource)) {
-				var source = new ListWrapper (new List<ustring> () { ustring.Make ("Test Text #1"), ustring.Make ("Test Text #2"), ustring.Make ("Test Text #3") });
+				var source = new ListWrapper (new List<string> () { "Test Text #1", "Test Text #2", "Test Text #3" });
 				view?.GetType ().GetProperty ("Source")?.GetSetMethod ()?.Invoke (view, new [] { source });
 			}
 

+ 1 - 1
UICatalog/Scenarios/Animation.cs

@@ -196,7 +196,7 @@ namespace UICatalog.Scenarios {
 				for (int y = 0; y < lines.Length; y++) {
 					var line = lines [y];
 					for (int x = 0; x < line.Length; x++) {
-						AddRune (x, y, line [x]);
+						AddRune (x, y, (Rune)line [x]);
 					}
 				}
 			}

+ 11 - 11
UICatalog/Scenarios/Buttons.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -40,7 +40,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (swapButton);
 
-			static void DoMessage (Button button, ustring txt)
+			static void DoMessage (Button button, string txt)
 			{
 				button.Clicked += (s,e) => {
 					var btnText = button.Text.ToString ();
@@ -178,7 +178,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (label);
 
-			var radioGroup = new RadioGroup (new ustring [] { "Left", "Right", "Centered", "Justified" }) {
+			var radioGroup = new RadioGroup (new string [] { "Left", "Right", "Centered", "Justified" }) {
 				X = 4,
 				Y = Pos.Bottom (label) + 1,
 				SelectedItem = 2,
@@ -186,17 +186,17 @@ namespace UICatalog.Scenarios {
 			Win.Add (radioGroup);
 
 			// Demo changing hotkey
-			ustring MoveHotkey (ustring txt)
+			string MoveHotkey (string txt)
 			{
 				// Remove the '_'
 				var runes = txt.ToRuneList ();
 
-				var i = runes.IndexOf ('_');
-				ustring start = "";
+				var i = runes.IndexOf ((Rune)'_');
+				string start = "";
 				if (i > -1) {
-					start = ustring.Make (runes.GetRange (0, i));
+					start = StringExtensions.ToString (runes.GetRange (0, i));
 				}
-				txt = start + ustring.Make (runes.GetRange (i + 1, runes.Count - (i + 1)));
+				txt = start + StringExtensions.ToString (runes.GetRange (i + 1, runes.Count - (i + 1)));
 
 				runes = txt.ToRuneList ();
 
@@ -207,8 +207,8 @@ namespace UICatalog.Scenarios {
 				}
 
 				// Slip in the '_'
-				start = ustring.Make (runes.GetRange (0, i));
-				return start + ustring.Make ('_') + ustring.Make (runes.GetRange (i, runes.Count - i));
+				start = StringExtensions.ToString (runes.GetRange (0, i));
+				return start + '_' + StringExtensions.ToString (runes.GetRange (i, runes.Count - i));
 			}
 
 			var mhkb = "Click to Change th_is Button's Hotkey";
@@ -223,7 +223,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (moveHotKeyBtn);
 
-			var muhkb = ustring.Make (" ~  s  gui.cs   master ↑10 = Сохранить");
+			var muhkb = " ~  s  gui.cs   master ↑10 = Сохранить";
 			var moveUnicodeHotKeyBtn = new Button (muhkb) {
 				X = Pos.Left (absoluteFrame) + 1,
 				Y = Pos.Bottom (radioGroup) + 1,

+ 56 - 36
UICatalog/Scenarios/CharacterMap.cs

@@ -2,14 +2,12 @@
 //#define BASE_DRAW_CONTENT
 
 using Microsoft.VisualBasic;
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
-using System.Text;
 using Terminal.Gui;
 using Terminal.Gui.Resources;
-using Rune = System.Rune;
 
 namespace UICatalog.Scenarios {
 	/// <summary>
@@ -40,36 +38,16 @@ namespace UICatalog.Scenarios {
 			Win.Add (jumpEdit);
 			var errorLabel = new Label ("") { X = Pos.Right (jumpEdit) + 1, Y = Pos.Y (_charMap), ColorScheme = Colors.ColorSchemes ["error"] };
 			Win.Add (errorLabel);
-			jumpEdit.TextChanged += (s, e) => {
-				uint result = 0;
-				if (jumpEdit.Text.Length == 0) return;
-				try {
-					result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 10);
-				} catch (OverflowException) {
-					errorLabel.Text = $"Invalid (overflow)";
-					return;
-				} catch (FormatException) {
-					try {
-						result = Convert.ToUInt32 (jumpEdit.Text.ToString (), 16);
-					} catch (OverflowException) {
-						errorLabel.Text = $"Invalid (overflow)";
-						return;
-					} catch (FormatException) {
-						errorLabel.Text = $"Invalid (can't parse)";
-						return;
-					}
-				}
-				errorLabel.Text = $"U+{result:x4}";
-				_charMap.SelectedGlyph = result;
-			};
 
-			var radioItems = new (ustring radioLabel, uint start, uint end) [UnicodeRange.Ranges.Count];
+			var radioItems = new (string radioLabel, uint start, uint end) [UnicodeRange.Ranges.Count];
+
+			var ranges = UnicodeRange.Ranges.OrderBy (o => o.Start).ToList ();
 
-			for (var i = 0; i < UnicodeRange.Ranges.Count; i++) {
-				var range = UnicodeRange.Ranges [i];
+			for (var i = 0; i < ranges.Count; i++) {
+				var range = ranges [i];
 				radioItems [i] = CreateRadio (range.Category, range.Start, range.End);
 			}
-			(ustring radioLabel, uint start, uint end) CreateRadio (ustring title, uint start, uint end)
+			(string radioLabel, uint start, uint end) CreateRadio (string title, uint start, uint end)
 			{
 				return ($"{title} (U+{start:x5}-{end:x5})", start, end);
 			}
@@ -90,6 +68,34 @@ namespace UICatalog.Scenarios {
 
 			Win.Add (jumpList);
 
+			jumpEdit.TextChanged += (s, e) => {
+				uint result = 0;
+				if (jumpEdit.Text.Length == 0) return;
+				try {
+					result = Convert.ToUInt32 (jumpEdit.Text, 10);
+				} catch (OverflowException) {
+					errorLabel.Text = $"Invalid (overflow)";
+					return;
+				} catch (FormatException) {
+					try {
+						result = Convert.ToUInt32 (jumpEdit.Text, 16);
+					} catch (OverflowException) {
+						errorLabel.Text = $"Invalid (overflow)";
+						return;
+					} catch (FormatException) {
+						errorLabel.Text = $"Invalid (can't parse)";
+						return;
+					}
+				}
+				errorLabel.Text = $"U+{result:x4}";
+				var foundIndex = ranges.FindIndex (x => x.Start <= result && x.End >= result);
+				if (foundIndex > -1 && jumpList.SelectedItem != foundIndex) {
+					jumpList.SelectedItem = foundIndex;
+				}
+				// Ensure the typed glyph is elected after jumpList
+				_charMap.SelectedGlyph = result;
+			};
+
 			//jumpList.Refresh ();
 			_charMap.SetFocus ();
 
@@ -247,6 +253,8 @@ namespace UICatalog.Scenarios {
 			}
 		}
 
+		private Point _cursorPos;
+
 		public override void OnDrawContentComplete (Rect contentArea)
 		{
 			Rect viewport = new Rect (ContentOffset,
@@ -280,16 +288,16 @@ namespace UICatalog.Scenarios {
 					Driver.SetAttribute (GetNormalColor ());
 					for (int col = 0; col < 16; col++) {
 						uint glyph = (uint)((uint)val + col);
-						var rune = new Rune (glyph);
-						//if (rune >= 0x00D800 && rune <= 0x00DFFF) {
-						//	if (col == 0) {
-						//		Driver.AddStr ("Reserved for surrogate pairs.");
-						//	}
-						//	continue;
-						//}						
+						Rune rune;
+						if (char.IsSurrogate ((char)glyph)) {
+							rune = Rune.ReplacementChar;
+						} else {
+							rune = new Rune (glyph);
+						}
 						Move (firstColumnX + (col * COLUMN_WIDTH) + 1, y + 1);
 						if (glyph == SelectedGlyph) {
 							Driver.SetAttribute (HasFocus ? ColorScheme.HotFocus : ColorScheme.HotNormal);
+							_cursorPos = new Point (firstColumnX + (col * COLUMN_WIDTH) + 1, y + 1);
 						} else {
 							Driver.SetAttribute (GetNormalColor ());
 						}
@@ -304,6 +312,18 @@ namespace UICatalog.Scenarios {
 			Driver.Clip = oldClip;
 		}
 
+		public override void PositionCursor ()
+		{
+			if (_cursorPos.Y < Bounds.Height && SelectedGlyph >= -ContentOffset.Y + _cursorPos.Y - 1
+				&& SelectedGlyph <= (-ContentOffset.Y + _cursorPos.Y - (ShowHorizontalScrollIndicator ? 1 : 0)) * 16 - 1) {
+
+				Application.Driver.SetCursorVisibility (CursorVisibility.Default);
+				Move (_cursorPos.X, _cursorPos.Y);
+			} else {
+				Application.Driver.SetCursorVisibility (CursorVisibility.Invisible);
+			}
+		}
+
 		ContextMenu _contextMenu = new ContextMenu ();
 		void Handle_MouseClick (object sender, MouseEventEventArgs args)
 		{

+ 1 - 3
UICatalog/Scenarios/ComputedLayout.cs

@@ -1,9 +1,7 @@
-using NStack;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection;
-using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {

+ 2 - 2
UICatalog/Scenarios/CsvEditor.cs

@@ -4,7 +4,7 @@ using System.Linq;
 using System.Globalization;
 using System.IO;
 using System.Text.RegularExpressions;
-using NStack;
+using System.Text;
 using Terminal.Gui;
 using CsvHelper;
 using System.Collections.Generic;
@@ -336,7 +336,7 @@ namespace UICatalog.Scenarios {
 
 				var newColIdx = Math.Min (Math.Max (0, tableView.SelectedColumn + 1), tableView.Table.Columns);
 
-				int result = MessageBox.Query ("Column Type", "Pick a data type for the column", new ustring [] { "Date", "Integer", "Double", "Text", "Cancel" });
+				int result = MessageBox.Query ("Column Type", "Pick a data type for the column", new string [] { "Date", "Integer", "Double", "Text", "Cancel" });
 
 				if (result <= -1 || result >= 4)
 					return;

+ 2 - 2
UICatalog/Scenarios/Dialogs.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using Terminal.Gui;
@@ -108,7 +108,7 @@ namespace UICatalog.Scenarios {
 			};
 			frame.Add (label);
 
-			var styleRadioGroup = new RadioGroup (new ustring [] { "Center", "Justify", "Left", "Right" }) {
+			var styleRadioGroup = new RadioGroup (new string [] { "Center", "Justify", "Left", "Right" }) {
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 			};

+ 26 - 27
UICatalog/Scenarios/DynamicMenuBar.cs

@@ -1,11 +1,10 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Reflection;
 using System.Runtime.CompilerServices;
-using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
@@ -20,12 +19,12 @@ namespace UICatalog.Scenarios {
 		}
 
 		public class DynamicMenuItemList {
-			public ustring Title { get; set; }
+			public string Title { get; set; }
 			public MenuItem MenuItem { get; set; }
 
 			public DynamicMenuItemList () { }
 
-			public DynamicMenuItemList (ustring title, MenuItem menuItem)
+			public DynamicMenuItemList (string title, MenuItem menuItem)
 			{
 				Title = title;
 				MenuItem = menuItem;
@@ -35,24 +34,24 @@ namespace UICatalog.Scenarios {
 		}
 
 		public class DynamicMenuItem {
-			public ustring title = "_New";
-			public ustring help = "";
-			public ustring action = "";
+			public string title = "_New";
+			public string help = "";
+			public string action = "";
 			public bool isTopLevel;
 			public bool hasSubMenu;
 			public MenuItemCheckStyle checkStyle;
-			public ustring shortcut;
+			public string shortcut;
 			public bool allowNullChecked;
 
 			public DynamicMenuItem () { }
 
-			public DynamicMenuItem (ustring title, bool hasSubMenu = false)
+			public DynamicMenuItem (string title, bool hasSubMenu = false)
 			{
 				this.title = title;
 				this.hasSubMenu = hasSubMenu;
 			}
 
-			public DynamicMenuItem (ustring title, ustring help, ustring action, bool isTopLevel, bool hasSubMenu, MenuItemCheckStyle checkStyle = MenuItemCheckStyle.NoCheck, ustring shortcut = null, bool allowNullChecked = false)
+			public DynamicMenuItem (string title, string help, string action, bool isTopLevel, bool hasSubMenu, MenuItemCheckStyle checkStyle = MenuItemCheckStyle.NoCheck, string shortcut = null, bool allowNullChecked = false)
 			{
 				this.title = title;
 				this.help = help;
@@ -278,10 +277,10 @@ namespace UICatalog.Scenarios {
 						if (_currentMenuBarItem.Parent != null) {
 							DataContext.Parent = _currentMenuBarItem.Title;
 						} else {
-							DataContext.Parent = ustring.Empty;
+							DataContext.Parent = string.Empty;
 						}
 					} else {
-						DataContext.Parent = ustring.Empty;
+						DataContext.Parent = string.Empty;
 					}
 				};
 
@@ -305,7 +304,7 @@ namespace UICatalog.Scenarios {
 				};
 
 				_btnOk.Clicked += (s, e) => {
-					if (ustring.IsNullOrEmpty (_frmMenuDetails._txtTitle.Text) && _currentEditMenuBarItem != null) {
+					if (string.IsNullOrEmpty (_frmMenuDetails._txtTitle.Text) && _currentEditMenuBarItem != null) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else if (_currentEditMenuBarItem != null) {
 						var menuItem = new DynamicMenuItem (_frmMenuDetails._txtTitle.Text, _frmMenuDetails._txtHelp.Text,
@@ -483,7 +482,7 @@ namespace UICatalog.Scenarios {
 						DataContext.Menus = new List<DynamicMenuItemList> ();
 						_currentMenuBarItem = null;
 						_currentSelectedMenuBar = -1;
-						_lblMenuBar.Text = ustring.Empty;
+						_lblMenuBar.Text = string.Empty;
 					} else {
 						_lblMenuBar.Text = _menuBar.Menus [_currentSelectedMenuBar].Title;
 					}
@@ -530,7 +529,7 @@ namespace UICatalog.Scenarios {
 					_currentMenuBarItem = menuBarItem;
 					DataContext.Menus = new List<DynamicMenuItemList> ();
 					SetListViewSource (_currentMenuBarItem, true);
-					_lblParent.Text = ustring.Empty;
+					_lblParent.Text = string.Empty;
 				}
 
 				void SetListViewSource (MenuItem _currentMenuBarItem, bool fill = false)
@@ -644,7 +643,7 @@ namespace UICatalog.Scenarios {
 				this.hasParent = hasParent;
 			}
 
-			public DynamicMenuBarDetails (ustring title) : base (title)
+			public DynamicMenuBarDetails (string title) : base (title)
 			{
 				var _lblTitle = new Label ("Title:") {
 					Y = 1
@@ -704,7 +703,7 @@ namespace UICatalog.Scenarios {
 				};
 				Add (_ckbNullCheck);
 
-				var _rChkLabels = new ustring [] { "NoCheck", "Checked", "Radio" };
+				var _rChkLabels = new string [] { "NoCheck", "Checked", "Radio" };
 				_rbChkStyle = new RadioGroup (_rChkLabels) {
 					X = Pos.Left (_lblTitle),
 					Y = Pos.Bottom (_ckbSubMenu) + 1,
@@ -862,7 +861,7 @@ namespace UICatalog.Scenarios {
 					IsDefault = true,
 				};
 				_btnOk.Clicked += (s, e) => {
-					if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
+					if (string.IsNullOrEmpty (_txtTitle.Text)) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else {
 						valid = true;
@@ -871,7 +870,7 @@ namespace UICatalog.Scenarios {
 				};
 				var _btnCancel = new Button ("Cancel");
 				_btnCancel.Clicked += (s, e) => {
-					_txtTitle.Text = ustring.Empty;
+					_txtTitle.Text = string.Empty;
 					Application.RequestStop ();
 				};
 				var _dialog = new Dialog (_btnOk, _btnCancel) { Title = "Enter the menu details." };
@@ -909,7 +908,7 @@ namespace UICatalog.Scenarios {
 				_menuItem = menuItem;
 				_txtTitle.Text = menuItem?.Title ?? "";
 				_txtHelp.Text = menuItem?.Help ?? "";
-				_txtAction.Text = menuItem != null && menuItem.Action != null ? GetTargetAction (menuItem.Action) : ustring.Empty;
+				_txtAction.Text = menuItem != null && menuItem.Action != null ? GetTargetAction (menuItem.Action) : string.Empty;
 				_ckbIsTopLevel.Checked = IsTopLevel (menuItem);
 				_ckbSubMenu.Checked = HasSubMenus (menuItem);
 				_ckbNullCheck.Checked = menuItem.AllowNullChecked;
@@ -931,7 +930,7 @@ namespace UICatalog.Scenarios {
 				_txtShortcut.Text = "";
 			}
 
-			ustring GetTargetAction (Action action)
+			string GetTargetAction (Action action)
 			{
 				var me = action.Target;
 
@@ -944,7 +943,7 @@ namespace UICatalog.Scenarios {
 						v = field.GetValue (me);
 					}
 				}
-				return v == null || !(v is DynamicMenuItem item) ? ustring.Empty : item.action;
+				return v == null || !(v is DynamicMenuItem item) ? string.Empty : item.action;
 			}
 
 			bool IsTopLevel (MenuItem menuItem)
@@ -1013,11 +1012,11 @@ namespace UICatalog.Scenarios {
 		public class DynamicMenuItemModel : INotifyPropertyChanged {
 			public event PropertyChangedEventHandler PropertyChanged;
 
-			private ustring menuBar;
-			private ustring parent;
+			private string menuBar;
+			private string parent;
 			private List<DynamicMenuItemList> menus;
 
-			public ustring MenuBar {
+			public string MenuBar {
 				get => menuBar;
 				set {
 					if (value != menuBar) {
@@ -1027,7 +1026,7 @@ namespace UICatalog.Scenarios {
 				}
 			}
 
-			public ustring Parent {
+			public string Parent {
 				get => parent;
 				set {
 					if (value != parent) {
@@ -1123,7 +1122,7 @@ namespace UICatalog.Scenarios {
 			public object Convert (object value, object parameter = null)
 			{
 				var data = Encoding.ASCII.GetBytes (value.ToString ());
-				return ustring.Make (data);
+				return StringExtensions.ToString (data);
 			}
 		}
 	}

+ 20 - 21
UICatalog/Scenarios/DynamicStatusBar.cs

@@ -1,11 +1,10 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Reflection;
 using System.Runtime.CompilerServices;
-using System.Text;
 using Terminal.Gui;
 
 namespace UICatalog.Scenarios {
@@ -19,12 +18,12 @@ namespace UICatalog.Scenarios {
 		}
 
 		public class DynamicStatusItemList {
-			public ustring Title { get; set; }
+			public string Title { get; set; }
 			public StatusItem StatusItem { get; set; }
 
 			public DynamicStatusItemList () { }
 
-			public DynamicStatusItemList (ustring title, StatusItem statusItem)
+			public DynamicStatusItemList (string title, StatusItem statusItem)
 			{
 				Title = title;
 				StatusItem = statusItem;
@@ -34,18 +33,18 @@ namespace UICatalog.Scenarios {
 		}
 
 		public class DynamicStatusItem {
-			public ustring title = "New";
-			public ustring action = "";
-			public ustring shortcut;
+			public string title = "New";
+			public string action = "";
+			public string shortcut;
 
 			public DynamicStatusItem () { }
 
-			public DynamicStatusItem (ustring title)
+			public DynamicStatusItem (string title)
 			{
 				this.title = title;
 			}
 
-			public DynamicStatusItem (ustring title, ustring action, ustring shortcut = null)
+			public DynamicStatusItem (string title, string action, string shortcut = null)
 			{
 				this.title = title;
 				this.action = action;
@@ -193,7 +192,7 @@ namespace UICatalog.Scenarios {
 				};
 
 				_btnOk.Clicked += (s,e) => {
-					if (ustring.IsNullOrEmpty (_frmStatusBarDetails._txtTitle.Text) && _currentEditStatusItem != null) {
+					if (string.IsNullOrEmpty (_frmStatusBarDetails._txtTitle.Text) && _currentEditStatusItem != null) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else if (_currentEditStatusItem != null) {
 						_frmStatusBarDetails._txtTitle.Text = SetTitleText (
@@ -330,7 +329,7 @@ namespace UICatalog.Scenarios {
 				//_frmStatusBarDetails.Initialized += (s, e) => _frmStatusBarDetails.Enabled = false;
 			}
 
-			public static ustring SetTitleText (ustring title, ustring shortcut)
+			public static string SetTitleText (string title, string shortcut)
 			{
 				var txt = title;
 				var split = title.ToString ().Split ('~');
@@ -356,7 +355,7 @@ namespace UICatalog.Scenarios {
 				_statusItem = statusItem;
 			}
 
-			public DynamicStatusBarDetails (ustring title) : base (title)
+			public DynamicStatusBarDetails (string title) : base (title)
 			{
 				var _lblTitle = new Label ("Title:") {
 					Y = 1
@@ -474,10 +473,10 @@ namespace UICatalog.Scenarios {
 					IsDefault = true,
 				};
 				_btnOk.Clicked += (s,e) => {
-					if (ustring.IsNullOrEmpty (_txtTitle.Text)) {
+					if (string.IsNullOrEmpty (_txtTitle.Text)) {
 						MessageBox.ErrorQuery ("Invalid title", "Must enter a valid title!.", "Ok");
 					} else {
-						if (!ustring.IsNullOrEmpty (_txtShortcut.Text)) {
+						if (!string.IsNullOrEmpty (_txtShortcut.Text)) {
 							_txtTitle.Text = DynamicStatusBarSample.SetTitleText (
 								_txtTitle.Text, _txtShortcut.Text);
 						}
@@ -487,7 +486,7 @@ namespace UICatalog.Scenarios {
 				};
 				var _btnCancel = new Button ("Cancel");
 				_btnCancel.Clicked += (s,e) => {
-					_txtTitle.Text = ustring.Empty;
+					_txtTitle.Text = string.Empty;
 					Application.RequestStop ();
 				};
 				var _dialog = new Dialog (_btnOk, _btnCancel) { Title = "Enter the menu details." };
@@ -517,7 +516,7 @@ namespace UICatalog.Scenarios {
 				}
 				_statusItem = statusItem;
 				_txtTitle.Text = statusItem?.Title ?? "";
-				_txtAction.Text = statusItem != null && statusItem.Action != null ? GetTargetAction (statusItem.Action) : ustring.Empty;
+				_txtAction.Text = statusItem != null && statusItem.Action != null ? GetTargetAction (statusItem.Action) : string.Empty;
 				_txtShortcut.Text = ShortcutHelper.GetShortcutTag (statusItem.Shortcut, StatusBar.ShortcutDelimiter) ?? "";
 			}
 
@@ -528,7 +527,7 @@ namespace UICatalog.Scenarios {
 				_txtShortcut.Text = "";
 			}
 
-			ustring GetTargetAction (Action action)
+			string GetTargetAction (Action action)
 			{
 				var me = action.Target;
 
@@ -541,7 +540,7 @@ namespace UICatalog.Scenarios {
 						v = field.GetValue (me);
 					}
 				}
-				return v == null || !(v is DynamicStatusItem item) ? ustring.Empty : item.action;
+				return v == null || !(v is DynamicStatusItem item) ? string.Empty : item.action;
 			}
 
 			public Action CreateAction (DynamicStatusItem item)
@@ -553,10 +552,10 @@ namespace UICatalog.Scenarios {
 		public class DynamicStatusItemModel : INotifyPropertyChanged {
 			public event PropertyChangedEventHandler PropertyChanged;
 
-			private ustring statusBar;
+			private string statusBar;
 			private List<DynamicStatusItemList> items;
 
-			public ustring StatusBar {
+			public string StatusBar {
 				get => statusBar;
 				set {
 					if (value != statusBar) {
@@ -652,7 +651,7 @@ namespace UICatalog.Scenarios {
 			public object Convert (object value, object parameter = null)
 			{
 				var data = Encoding.ASCII.GetBytes (value.ToString ());
-				return ustring.Make (data);
+				return StringExtensions.ToString (data);
 			}
 		}
 	}

+ 33 - 33
UICatalog/Scenarios/Editor.cs

@@ -38,7 +38,7 @@ namespace UICatalog.Scenarios {
 			_cultureInfos = Application.SupportedCultures;
 			ConfigurationManager.Themes.Theme = Theme;
 			ConfigurationManager.Apply ();
-			
+
 			Win = new Window () {
 				Title = _fileName ?? "Untitled",
 				X = 0,
@@ -62,7 +62,7 @@ namespace UICatalog.Scenarios {
 
 			var siCursorPosition = new StatusItem (Key.Null, "", null);
 
-			_textView.UnwrappedCursorPosition += (s,e) => {
+			_textView.UnwrappedCursorPosition += (s, e) => {
 				siCursorPosition.Title = $"Ln {e.Point.Y + 1}, Col {e.Point.X + 1}";
 			};
 
@@ -131,7 +131,7 @@ namespace UICatalog.Scenarios {
 
 			_scrollBar = new ScrollBarView (_textView, true);
 
-			_scrollBar.ChangedPosition += (s,e) => {
+			_scrollBar.ChangedPosition += (s, e) => {
 				_textView.TopRow = _scrollBar.Position;
 				if (_textView.TopRow != _scrollBar.Position) {
 					_scrollBar.Position = _textView.TopRow;
@@ -139,7 +139,7 @@ namespace UICatalog.Scenarios {
 				_textView.SetNeedsDisplay ();
 			};
 
-			_scrollBar.OtherScrollBarView.ChangedPosition += (s,e) => {
+			_scrollBar.OtherScrollBarView.ChangedPosition += (s, e) => {
 				_textView.LeftColumn = _scrollBar.OtherScrollBarView.Position;
 				if (_textView.LeftColumn != _scrollBar.OtherScrollBarView.Position) {
 					_scrollBar.OtherScrollBarView.Position = _textView.LeftColumn;
@@ -147,7 +147,7 @@ namespace UICatalog.Scenarios {
 				_textView.SetNeedsDisplay ();
 			};
 
-			_scrollBar.VisibleChanged += (s,e) => {
+			_scrollBar.VisibleChanged += (s, e) => {
 				if (_scrollBar.Visible && _textView.RightOffset == 0) {
 					_textView.RightOffset = 1;
 				} else if (!_scrollBar.Visible && _textView.RightOffset == 1) {
@@ -155,7 +155,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			_scrollBar.OtherScrollBarView.VisibleChanged += (s,e) => {
+			_scrollBar.OtherScrollBarView.VisibleChanged += (s, e) => {
 				if (_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 0) {
 					_textView.BottomOffset = 1;
 				} else if (!_scrollBar.OtherScrollBarView.Visible && _textView.BottomOffset == 1) {
@@ -163,7 +163,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			_textView.DrawContent += (s,e) => {
+			_textView.DrawContent += (s, e) => {
 				_scrollBar.Size = _textView.Lines;
 				_scrollBar.Position = _textView.TopRow;
 				if (_scrollBar.OtherScrollBarView != null) {
@@ -199,7 +199,7 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			Application.Top.Closed += (s,e) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
+			Application.Top.Closed += (s, e) => Thread.CurrentThread.CurrentUICulture = new CultureInfo ("en-US");
 		}
 
 		private void DisposeWinDialog ()
@@ -222,7 +222,7 @@ namespace UICatalog.Scenarios {
 			Win.Title = "Untitled.txt";
 			_fileName = null;
 			_originalText = new System.IO.MemoryStream ().ToArray ();
-			_textView.Text = _originalText;
+			_textView.Text = Encoding.Unicode.GetString (_originalText);
 		}
 
 		private void LoadFile ()
@@ -231,7 +231,7 @@ namespace UICatalog.Scenarios {
 				// FIXED: BUGBUG: #452 TextView.LoadFile keeps file open and provides no way of closing it
 				_textView.LoadFile (_fileName);
 				//_textView.Text = System.IO.File.ReadAllText (_fileName);
-				_originalText = _textView.Text.ToByteArray ();
+				_originalText = Encoding.Unicode.GetBytes(_textView.Text);
 				Win.Title = _fileName;
 				_saved = true;
 			}
@@ -345,7 +345,7 @@ namespace UICatalog.Scenarios {
 
 		private bool CanCloseFile ()
 		{
-			if (_textView.Text == _originalText) {
+			if (_textView.Text == Encoding.Unicode.GetString (_originalText)) {
 				//System.Diagnostics.Debug.Assert (!_textView.IsDirty);
 				return true;
 			}
@@ -427,7 +427,7 @@ namespace UICatalog.Scenarios {
 				Win.Title = title;
 				_fileName = file;
 				System.IO.File.WriteAllText (_fileName, _textView.Text.ToString ());
-				_originalText = _textView.Text.ToByteArray ();
+				_originalText = Encoding.Unicode.GetBytes(_textView.Text);
 				_saved = true;
 				_textView.ClearHistoryChanges ();
 				MessageBox.Query ("Save File", "File was successfully saved.", "Ok");
@@ -768,8 +768,8 @@ namespace UICatalog.Scenarios {
 
 		private void SetFindText ()
 		{
-			_textToFind = !_textView.SelectedText.IsEmpty
-				? _textView.SelectedText.ToString ()
+			_textToFind = !string.IsNullOrEmpty(_textView.SelectedText)
+				? _textView.SelectedText
 				: string.IsNullOrEmpty (_textToFind) ? "" : _textToFind;
 
 			_textToReplace = string.IsNullOrEmpty (_textToReplace) ? "" : _textToReplace;
@@ -778,7 +778,7 @@ namespace UICatalog.Scenarios {
 		private View FindTab ()
 		{
 			var d = new View ();
-			d.DrawContent += (s,e) => {
+			d.DrawContent += (s, e) => {
 				foreach (var v in d.Subviews) {
 					v.SetNeedsDisplay ();
 				}
@@ -807,30 +807,30 @@ namespace UICatalog.Scenarios {
 				X = Pos.Right (txtToFind) + 1,
 				Y = Pos.Top (label),
 				Width = 20,
-				Enabled = !txtToFind.Text.IsEmpty,
+				Enabled = !string.IsNullOrEmpty(txtToFind.Text),
 				TextAlignment = TextAlignment.Centered,
 				IsDefault = true,
 				AutoSize = false
 			};
-			btnFindNext.Clicked += (s,e) => FindNext ();
+			btnFindNext.Clicked += (s, e) => FindNext ();
 			d.Add (btnFindNext);
 
 			var btnFindPrevious = new Button ("Find _Previous") {
 				X = Pos.Right (txtToFind) + 1,
 				Y = Pos.Top (btnFindNext) + 1,
 				Width = 20,
-				Enabled = !txtToFind.Text.IsEmpty,
+				Enabled = !string.IsNullOrEmpty(txtToFind.Text),
 				TextAlignment = TextAlignment.Centered,
 				AutoSize = false
 			};
-			btnFindPrevious.Clicked += (s,e) => FindPrevious ();
+			btnFindPrevious.Clicked += (s, e) => FindPrevious ();
 			d.Add (btnFindPrevious);
 
 			txtToFind.TextChanged += (s, e) => {
 				_textToFind = txtToFind.Text.ToString ();
 				_textView.FindTextChanged ();
-				btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
-				btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
+				btnFindNext.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
+				btnFindPrevious.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
 			};
 
 			var btnCancel = new Button ("Cancel") {
@@ -840,7 +840,7 @@ namespace UICatalog.Scenarios {
 				TextAlignment = TextAlignment.Centered,
 				AutoSize = false
 			};
-			btnCancel.Clicked += (s,e) => {
+			btnCancel.Clicked += (s, e) => {
 				DisposeWinDialog ();
 			};
 			d.Add (btnCancel);
@@ -870,7 +870,7 @@ namespace UICatalog.Scenarios {
 		private View ReplaceTab ()
 		{
 			var d = new View ();
-			d.DrawContent += (s,e) => {
+			d.DrawContent += (s, e) => {
 				foreach (var v in d.Subviews) {
 					v.SetNeedsDisplay ();
 				}
@@ -899,12 +899,12 @@ namespace UICatalog.Scenarios {
 				X = Pos.Right (txtToFind) + 1,
 				Y = Pos.Top (label),
 				Width = 20,
-				Enabled = !txtToFind.Text.IsEmpty,
+				Enabled = !string.IsNullOrEmpty(txtToFind.Text),
 				TextAlignment = TextAlignment.Centered,
 				IsDefault = true,
 				AutoSize = false
 			};
-			btnFindNext.Clicked += (s,e) => ReplaceNext ();
+			btnFindNext.Clicked += (s, e) => ReplaceNext ();
 			d.Add (btnFindNext);
 
 			label = new Label ("Replace:") {
@@ -928,30 +928,30 @@ namespace UICatalog.Scenarios {
 				X = Pos.Right (txtToFind) + 1,
 				Y = Pos.Top (btnFindNext) + 1,
 				Width = 20,
-				Enabled = !txtToFind.Text.IsEmpty,
+				Enabled = !string.IsNullOrEmpty(txtToFind.Text),
 				TextAlignment = TextAlignment.Centered,
 				AutoSize = false
 			};
-			btnFindPrevious.Clicked += (s,e) => ReplacePrevious ();
+			btnFindPrevious.Clicked += (s, e) => ReplacePrevious ();
 			d.Add (btnFindPrevious);
 
 			var btnReplaceAll = new Button ("Replace _All") {
 				X = Pos.Right (txtToFind) + 1,
 				Y = Pos.Top (btnFindPrevious) + 1,
 				Width = 20,
-				Enabled = !txtToFind.Text.IsEmpty,
+				Enabled = !string.IsNullOrEmpty(txtToFind.Text),
 				TextAlignment = TextAlignment.Centered,
 				AutoSize = false
 			};
-			btnReplaceAll.Clicked += (s,e) => ReplaceAll ();
+			btnReplaceAll.Clicked += (s, e) => ReplaceAll ();
 			d.Add (btnReplaceAll);
 
 			txtToFind.TextChanged += (s, e) => {
 				_textToFind = txtToFind.Text.ToString ();
 				_textView.FindTextChanged ();
-				btnFindNext.Enabled = !txtToFind.Text.IsEmpty;
-				btnFindPrevious.Enabled = !txtToFind.Text.IsEmpty;
-				btnReplaceAll.Enabled = !txtToFind.Text.IsEmpty;
+				btnFindNext.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
+				btnFindPrevious.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
+				btnReplaceAll.Enabled = !string.IsNullOrEmpty(txtToFind.Text);
 			};
 
 			var btnCancel = new Button ("Cancel") {
@@ -961,7 +961,7 @@ namespace UICatalog.Scenarios {
 				TextAlignment = TextAlignment.Centered,
 				AutoSize = false
 			};
-			btnCancel.Clicked += (s,e) => {
+			btnCancel.Clicked += (s, e) => {
 				DisposeWinDialog ();
 			};
 			d.Add (btnCancel);

+ 64 - 70
UICatalog/Scenarios/FileDialogExamples.cs

@@ -62,7 +62,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (new Label ("Caption") { X = x++, Y = y++ });
 
 			rgCaption = new RadioGroup { X = x, Y = y };
-			rgCaption.RadioLabels = new NStack.ustring [] { "Ok", "Open", "Save" };
+			rgCaption.RadioLabels = new string [] { "Ok", "Open", "Save" };
 			Win.Add (rgCaption);
 
 			y = 0;
@@ -76,7 +76,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (new Label ("OpenMode") { X = x++, Y = y++ });
 
 			rgOpenMode = new RadioGroup { X = x, Y = y };
-			rgOpenMode.RadioLabels = new NStack.ustring [] { "File", "Directory", "Mixed" };
+			rgOpenMode.RadioLabels = new string [] { "File", "Directory", "Mixed" };
 			Win.Add (rgOpenMode);
 
 			y = 0;
@@ -90,11 +90,11 @@ namespace UICatalog.Scenarios {
 			Win.Add (new Label ("Icons") { X = x++, Y = y++ });
 
 			rgIcons = new RadioGroup { X = x, Y = y };
-			rgIcons.RadioLabels = new NStack.ustring [] { "None", "Unicode", "Nerd*" };
+			rgIcons.RadioLabels = new string [] { "None", "Unicode", "Nerd*" };
 			Win.Add (rgIcons);
 
-			Win.Add(new Label("* Requires installing Nerd fonts"){Y = Pos.AnchorEnd(2)});
-			Win.Add(new Label("  (see: https://github.com/devblackops/Terminal-Icons)"){Y = Pos.AnchorEnd(1)});
+			Win.Add (new Label ("* Requires installing Nerd fonts") { Y = Pos.AnchorEnd (2) });
+			Win.Add (new Label ("  (see: https://github.com/devblackops/Terminal-Icons)") { Y = Pos.AnchorEnd (1) });
 
 			y = 5;
 			x = 24;
@@ -107,7 +107,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (new Label ("Allowed") { X = x++, Y = y++ });
 
 			rgAllowedTypes = new RadioGroup { X = x, Y = y };
-			rgAllowedTypes.RadioLabels = new NStack.ustring [] { "Any", "Csv (Recommended)", "Csv (Strict)" };
+			rgAllowedTypes.RadioLabels = new string [] { "Any", "Csv (Recommended)", "Csv (Strict)" };
 			Win.Add (rgAllowedTypes);
 
 			var btn = new Button ($"Run Dialog") {
@@ -121,14 +121,11 @@ namespace UICatalog.Scenarios {
 
 		private void SetupHandler (Button btn)
 		{
-			btn.Clicked += (s,e) => {
-				try
-				{
-					CreateDialog();
-				}	
-				catch(Exception ex)
-				{
-					MessageBox.ErrorQuery("Error",ex.ToString(),"Ok");
+			btn.Clicked += (s, e) => {
+				try {
+					CreateDialog ();
+				} catch (Exception ex) {
+					MessageBox.ErrorQuery ("Error", ex.ToString (), "Ok");
 
 				}
 			};
@@ -136,83 +133,80 @@ namespace UICatalog.Scenarios {
 
 		private void CreateDialog ()
 		{
-			
-				var fd = new FileDialog () {
-					OpenMode = Enum.Parse<OpenMode> (
-						rgOpenMode.RadioLabels [rgOpenMode.SelectedItem].ToString ()),
-					MustExist = cbMustExist.Checked ?? false,
-					AllowsMultipleSelection = cbAllowMultipleSelection.Checked ?? false,
-				};
 
-				fd.Style.OkButtonText = rgCaption.RadioLabels [rgCaption.SelectedItem].ToString ();
+			var fd = new FileDialog () {
+				OpenMode = Enum.Parse<OpenMode> (
+					rgOpenMode.RadioLabels [rgOpenMode.SelectedItem].ToString ()),
+				MustExist = cbMustExist.Checked ?? false,
+				AllowsMultipleSelection = cbAllowMultipleSelection.Checked ?? false,
+			};
 
-				// If Save style dialog then give them an overwrite prompt
-				if(rgCaption.SelectedItem == 2) {
-					fd.FilesSelected += ConfirmOverwrite;
-				}
+			fd.Style.OkButtonText = rgCaption.RadioLabels [rgCaption.SelectedItem].ToString ();
 
-				if(rgIcons.SelectedItem == 1)
-				{
-					fd.Style.UseUnicodeCharacters = true;
-				}
-				else if(rgIcons.SelectedItem == 2)
-				{
-					fd.Style.UseNerdForIcons();
-				}
+			// If Save style dialog then give them an overwrite prompt
+			if (rgCaption.SelectedItem == 2) {
+				fd.FilesSelected += ConfirmOverwrite;
+			}
 
-				if (cbCaseSensitive.Checked ?? false) {
+			if (rgIcons.SelectedItem == 1) {
+				fd.Style.UseUnicodeCharacters = true;
+			} else if (rgIcons.SelectedItem == 2) {
+				fd.Style.UseNerdForIcons ();
+			}
 
-					fd.SearchMatcher = new CaseSensitiveSearchMatcher ();
-				}
+			if (cbCaseSensitive.Checked ?? false) {
 
-				fd.Style.UseColors = cbUseColors.Checked ?? false;
+				fd.SearchMatcher = new CaseSensitiveSearchMatcher ();
+			}
 
-				fd.Style.TreeStyle.ShowBranchLines = cbShowTreeBranchLines.Checked ?? false;
-				fd.Style.TableStyle.AlwaysShowHeaders = cbAlwaysTableShowHeaders.Checked ?? false;
+			fd.Style.UseColors = cbUseColors.Checked ?? false;
 
-				var dirInfoFactory = new FileSystem().DirectoryInfo;
+			fd.Style.TreeStyle.ShowBranchLines = cbShowTreeBranchLines.Checked ?? false;
+			fd.Style.TableStyle.AlwaysShowHeaders = cbAlwaysTableShowHeaders.Checked ?? false;
 
-				if (cbDrivesOnlyInTree.Checked ?? false) {
-					fd.Style.TreeRootGetter = () => {
-						return System.Environment.GetLogicalDrives ()
-						.Select (d => new FileDialogRootTreeNode (d, dirInfoFactory.New(d)));
-					};
-				}
+			var dirInfoFactory = new FileSystem ().DirectoryInfo;
 
-				if (rgAllowedTypes.SelectedItem > 0) {
-					fd.AllowedTypes.Add (new AllowedType ("Data File", ".csv", ".tsv"));
+			if (cbDrivesOnlyInTree.Checked ?? false) {
+				fd.Style.TreeRootGetter = () => {
+					return System.Environment.GetLogicalDrives ()
+					.Select (d => new FileDialogRootTreeNode (d, dirInfoFactory.New (d)));
+				};
+			}
 
-					if (rgAllowedTypes.SelectedItem == 1) {
-						fd.AllowedTypes.Insert (1, new AllowedTypeAny ());
-					}
+			if (rgAllowedTypes.SelectedItem > 0) {
+				fd.AllowedTypes.Add (new AllowedType ("Data File", ".csv", ".tsv"));
 
+				if (rgAllowedTypes.SelectedItem == 1) {
+					fd.AllowedTypes.Insert (1, new AllowedTypeAny ());
 				}
 
-				Application.Run (fd);
+			}
 
-				if (fd.Canceled) {
-					MessageBox.Query (
-						"Canceled",
-						"You canceled navigation and did not pick anything",
+			Application.Run (fd);
+
+			if (fd.Canceled) {
+				MessageBox.Query (
+					"Canceled",
+					"You canceled navigation and did not pick anything",
+				"Ok");
+			} else if (cbAllowMultipleSelection.Checked ?? false) {
+				MessageBox.Query (
+					"Chosen!",
+					"You chose:" + Environment.NewLine +
+					string.Join (Environment.NewLine, fd.MultiSelected.Select (m => m)),
 					"Ok");
-				} else if (cbAllowMultipleSelection.Checked ?? false) {
-					MessageBox.Query (
-						"Chosen!",
-						"You chose:" + Environment.NewLine +
-						string.Join (Environment.NewLine, fd.MultiSelected.Select (m => m)),
-						"Ok");
-				} else {
-					MessageBox.Query (
-						"Chosen!",
-						"You chose:" + Environment.NewLine + fd.Path,
-						"Ok");
-				}
+			} else {
+				MessageBox.Query (
+					"Chosen!",
+					"You chose:" + Environment.NewLine + fd.Path,
+					"Ok");
+			}
 		}
 
 		private void ConfirmOverwrite (object sender, FilesSelectedEventArgs e)
 		{
 			if (!string.IsNullOrWhiteSpace (e.Dialog.Path)) {
-				if(File.Exists(e.Dialog.Path)) {
+				if (File.Exists (e.Dialog.Path)) {
 					int result = MessageBox.Query ("Overwrite?", "File already exists", "Yes", "No");
 					e.Cancel = result == 1;
 				}

+ 5 - 6
UICatalog/Scenarios/Frames.cs

@@ -1,5 +1,4 @@
-using NStack;
-using System;
+using System;
 using System.Linq;
 using Terminal.Gui;
 
@@ -100,7 +99,7 @@ namespace UICatalog.Scenarios {
 				};
 				copyTop.Clicked += (s, e) => {
 					Thickness = new Thickness (Thickness.Top);
-					if (_topEdit.Text.IsEmpty) {
+					if (string.IsNullOrEmpty (_topEdit.Text)) {
 						_topEdit.Text = "0";
 					}
 					_bottomEdit.Text = _leftEdit.Text = _rightEdit.Text = _topEdit.Text;
@@ -181,7 +180,7 @@ namespace UICatalog.Scenarios {
 						break;
 					}
 				} catch {
-					if (!e.NewText.IsEmpty) {
+					if (!string.IsNullOrEmpty(e.NewText)) {
 						e.Cancel = true;
 					}
 				}
@@ -194,7 +193,7 @@ namespace UICatalog.Scenarios {
 			private FrameEditor _borderEditor;
 			private FrameEditor _paddingEditor;
 
-			public FramesEditor (ustring title, View viewToEdit)
+			public FramesEditor (string title, View viewToEdit)
 			{
 				this._viewToEdit = viewToEdit;
 
@@ -227,7 +226,7 @@ namespace UICatalog.Scenarios {
 
 				var borderStyleEnum = Enum.GetValues (typeof (LineStyle)).Cast<LineStyle> ().ToList ();
 				var rbBorderStyle = new RadioGroup (borderStyleEnum.Select (
-					e => NStack.ustring.Make (e.ToString ())).ToArray ()) {
+					e => e.ToString ()).ToArray ()) {
 
 					X = Pos.Right (_borderEditor) - 1,
 					Y = Pos.Top (_borderEditor),

+ 7 - 6
UICatalog/Scenarios/GraphViewExample.cs

@@ -1,6 +1,7 @@
 using System;
 using System.Collections.Generic;
 using System.Linq;
+using System.Text;
 using Terminal.Gui;
 
 using Color = Terminal.Gui.Color;
@@ -181,7 +182,7 @@ namespace UICatalog.Scenarios {
 
 			var points2 = new ScatterSeries () {
 				Points = randomPoints,
-				Fill = new GraphCellToRender ('x', red)
+				Fill = new GraphCellToRender ((Rune)'x', red)
 			};
 
 			var line2 = new PathAnnotation () {
@@ -297,8 +298,8 @@ namespace UICatalog.Scenarios {
 
 			about.Text = "This graph shows the life expectancy at birth of a range of countries";
 
-			var softStiple = new GraphCellToRender ('\u2591');
-			var mediumStiple = new GraphCellToRender ('\u2592');
+			var softStiple = new GraphCellToRender ((Rune)'\u2591');
+			var mediumStiple = new GraphCellToRender ((Rune)'\u2592');
 
 			var barSeries = new BarSeries () {
 				Bars = new List<BarSeries.Bar> () {
@@ -492,8 +493,8 @@ namespace UICatalog.Scenarios {
 				}
 			};
 
-			var softStiple = new GraphCellToRender ('\u2591');
-			var mediumStiple = new GraphCellToRender ('\u2592');
+			var softStiple = new GraphCellToRender ((Rune)'\u2591');
+			var mediumStiple = new GraphCellToRender ((Rune)'\u2592');
 
 			for (int i = 0; i < malesSeries.Bars.Count; i++) {
 				malesSeries.Bars [i].Fill = i % 2 == 0 ? softStiple : mediumStiple;
@@ -559,7 +560,7 @@ namespace UICatalog.Scenarios {
 
 			graphView.GraphColor = Application.Driver.MakeAttribute (Color.White, Color.Black);
 
-			var stiple = new GraphCellToRender ('\u2593');
+			var stiple = new GraphCellToRender ((Rune)'\u2593');
 
 			Random r = new Random ();
 			var series = new DiscoBarSeries ();

+ 1 - 1
UICatalog/Scenarios/Keys.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System.Collections.Generic;
 using Terminal.Gui;
 

+ 26 - 26
UICatalog/Scenarios/LabelsAsButtons.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections.Generic;
 using System.Linq;
@@ -29,14 +29,14 @@ namespace UICatalog.Scenarios {
 				//TODO: Change to use Pos.AnchorEnd()
 				Y = Pos.Bottom (Win) - 3,
 				//IsDefault = true,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			defaultLabel.Clicked += (s,e) => Application.RequestStop ();
 			Win.Add (defaultLabel);
 
 			var swapLabel = new Label (50, 0, "S_wap Default (Absolute Layout)") {
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			swapLabel.Clicked += (s,e) => {
@@ -45,7 +45,7 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (swapLabel);
 
-			static void DoMessage (Label Label, ustring txt)
+			static void DoMessage (Label Label, string txt)
 			{
 				Label.Clicked += (s,e) => {
 					var btnText = Label.Text.ToString ();
@@ -66,7 +66,7 @@ namespace UICatalog.Scenarios {
 					ColorScheme = colorScheme.Value,
 					X = x,
 					Y = Pos.Y (colorLabelsLabel),
-					HotKeySpecifier = (System.Rune)'_',
+					HotKeySpecifier = (Rune)'_',
 					CanFocus = true,
 				};
 				DoMessage (colorLabel, colorLabel.Text);
@@ -79,7 +79,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (Label = new Label ("A super long _Label that will probably expose a bug in clipping or wrapping of text. Will it?") {
 				X = 2,
 				Y = Pos.Bottom (colorLabelsLabel) + 1,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			});
 			DoMessage (Label, Label.Text);
@@ -88,7 +88,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (Label = new Label ("a Newline\nin the Label") {
 				X = 2,
 				Y = Pos.Bottom (Label) + 1,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 				TextAlignment = TextAlignment.Centered,
 				VerticalTextAlignment = VerticalTextAlignment.Middle
@@ -98,7 +98,7 @@ namespace UICatalog.Scenarios {
 			var textChanger = new Label ("Te_xt Changer") {
 				X = 2,
 				Y = Pos.Bottom (Label) + 1,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			Win.Add (textChanger);
@@ -107,7 +107,7 @@ namespace UICatalog.Scenarios {
 			Win.Add (Label = new Label ("Lets see if this will move as \"Text Changer\" grows") {
 				X = Pos.Right (textChanger) + 2,
 				Y = Pos.Y (textChanger),
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			});
 
@@ -115,7 +115,7 @@ namespace UICatalog.Scenarios {
 				X = 2,
 				Y = Pos.Bottom (Label) + 1,
 				ColorScheme = Colors.Error,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			Win.Add (removeLabel);
@@ -142,7 +142,7 @@ namespace UICatalog.Scenarios {
 				Y = Pos.Center () - 1,
 				Width = 30,
 				ColorScheme = Colors.Error,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			moveBtn.Clicked += (s,e) => {
@@ -159,7 +159,7 @@ namespace UICatalog.Scenarios {
 				Y = Pos.Center () + 1,
 				Width = 30,
 				ColorScheme = Colors.Error,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 				AutoSize = false
 			};
@@ -180,7 +180,7 @@ namespace UICatalog.Scenarios {
 			// Demonstrates how changing the View.Frame property can move Views
 			var moveBtnA = new Label (0, 0, "Move This Label via Frame") {
 				ColorScheme = Colors.Error,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			moveBtnA.Clicked += (s,e) => {
@@ -191,7 +191,7 @@ namespace UICatalog.Scenarios {
 			// Demonstrates how changing the View.Frame property can SIZE Views (#583)
 			var sizeBtnA = new Label (0, 2, " ~  s  gui.cs   master ↑10 = Со_хранить") {
 				ColorScheme = Colors.Error,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 				AutoSize = false
 			};
@@ -203,12 +203,12 @@ namespace UICatalog.Scenarios {
 			var label = new Label ("Text Alignment (changes the four Labels above): ") {
 				X = 2,
 				Y = Pos.Bottom (computedFrame) + 1,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			Win.Add (label);
 
-			var radioGroup = new RadioGroup (new ustring [] { "Left", "Right", "Centered", "Justified" }) {
+			var radioGroup = new RadioGroup (new string [] { "Left", "Right", "Centered", "Justified" }) {
 				X = 4,
 				Y = Pos.Bottom (label) + 1,
 				SelectedItem = 2,
@@ -216,17 +216,17 @@ namespace UICatalog.Scenarios {
 			Win.Add (radioGroup);
 
 			// Demo changing hotkey
-			ustring MoveHotkey (ustring txt)
+			string MoveHotkey (string txt)
 			{
 				// Remove the '_'
 				var runes = txt.ToRuneList ();
 
-				var i = runes.IndexOf ('_');
-				ustring start = "";
+				var i = runes.IndexOf ((Rune)'_');
+				string start = "";
 				if (i > -1) {
-					start = ustring.Make (runes.GetRange (0, i));
+					start = StringExtensions.ToString (runes.GetRange (0, i));
 				}
-				txt = start + ustring.Make (runes.GetRange (i + 1, runes.Count - (i + 1)));
+				txt = start + StringExtensions.ToString (runes.GetRange (i + 1, runes.Count - (i + 1)));
 
 				runes = txt.ToRuneList ();
 
@@ -237,8 +237,8 @@ namespace UICatalog.Scenarios {
 				}
 
 				// Slip in the '_'
-				start = ustring.Make (runes.GetRange (0, i));
-				return start + ustring.Make ('_') + ustring.Make (runes.GetRange (i, runes.Count - i));
+				start = StringExtensions.ToString (runes.GetRange (0, i));
+				return start + '_' + StringExtensions.ToString (runes.GetRange (i, runes.Count - i));
 			}
 
 			var mhkb = "Click to Change th_is Label's Hotkey";
@@ -247,7 +247,7 @@ namespace UICatalog.Scenarios {
 				Y = Pos.Bottom (radioGroup) + 1,
 				Width = Dim.Width (computedFrame) - 2,
 				ColorScheme = Colors.TopLevel,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			moveHotKeyBtn.Clicked += (s,e) => {
@@ -255,13 +255,13 @@ namespace UICatalog.Scenarios {
 			};
 			Win.Add (moveHotKeyBtn);
 
-			ustring muhkb = " ~  s  gui.cs   master ↑10 = Сохранить";
+			string muhkb = " ~  s  gui.cs   master ↑10 = Сохранить";
 			var moveUnicodeHotKeyBtn = new Label (muhkb) {
 				X = Pos.Left (absoluteFrame) + 1,
 				Y = Pos.Bottom (radioGroup) + 1,
 				Width = Dim.Width (absoluteFrame) - 2,
 				ColorScheme = Colors.TopLevel,
-				HotKeySpecifier = (System.Rune)'_',
+				HotKeySpecifier = (Rune)'_',
 				CanFocus = true,
 			};
 			moveUnicodeHotKeyBtn.Clicked += (s,e) => {

+ 3 - 3
UICatalog/Scenarios/LineDrawing.cs

@@ -1,8 +1,8 @@
-using NStack;
-using System;
+using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Reflection.Metadata.Ecma335;
+using System.Text;
 using Terminal.Gui;
 using Attribute = Terminal.Gui.Attribute;
 
@@ -73,7 +73,7 @@ namespace UICatalog.Scenarios {
 
 				_colorPicker.ColorChanged += (s, a) => ColorChanged?.Invoke (a.Color);
 
-				_stylePicker = new RadioGroup (Enum.GetNames (typeof (LineStyle)).Select (s => ustring.Make (s)).ToArray ()) {
+				_stylePicker = new RadioGroup (Enum.GetNames (typeof (LineStyle)).ToArray ()) {
 					X = 0,
 					Y = Pos.Bottom (_colorPicker)
 				};

+ 3 - 3
UICatalog/Scenarios/LineViewExample.cs

@@ -40,7 +40,7 @@ namespace UICatalog.Scenarios {
 			// creates a horizontal line
 			var doubleLine = new LineView () {
 				Y = 3,
-				LineRune = '\u2550'
+				LineRune = (Rune)'\u2550'
 			};
 
 			Win.Add (doubleLine);
@@ -62,7 +62,7 @@ namespace UICatalog.Scenarios {
 				Y = 7,
 				Width = 10,
 				StartingAnchor = CM.Glyphs.LeftTee,
-				EndingAnchor = '>'
+				EndingAnchor = (Rune)'>'
 			};
 
 			Win.Add (arrowLine);
@@ -82,7 +82,7 @@ namespace UICatalog.Scenarios {
 			var verticalArrow = new LineView (Orientation.Vertical) {
 				X = 27,
 				StartingAnchor = CM.Glyphs.TopTee,
-				EndingAnchor = 'V'
+				EndingAnchor = (Rune)'V'
 			};
 
 			Win.Add (verticalArrow);

+ 5 - 5
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -1,4 +1,4 @@
-using NStack;
+using System.Text;
 using System;
 using System.Collections;
 using System.Collections.Generic;
@@ -202,13 +202,13 @@ namespace UICatalog.Scenarios {
 			}
 
 			// A slightly adapted method from: https://github.com/gui-cs/Terminal.Gui/blob/fc1faba7452ccbdf49028ac49f0c9f0f42bbae91/Terminal.Gui/Views/ListView.cs#L433-L461
-			private void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
+			private void RenderUstr (ConsoleDriver driver, string ustr, int col, int line, int width, int start = 0)
 			{
 				int used = 0;
 				int index = start;
 				while (index < ustr.Length) {
-					(var rune, var size) = Utf8.DecodeRune (ustr, index, index - ustr.Length);
-					var count = Rune.ColumnWidth (rune);
+					(var rune, var size) = ustr.DecodeRune (index, index - ustr.Length);
+					var count = rune.GetColumns ();
 					if (used + count >= width) break;
 					driver.AddRune (rune);
 					used += count;
@@ -216,7 +216,7 @@ namespace UICatalog.Scenarios {
 				}
 
 				while (used < width) {
-					driver.AddRune (' ');
+					driver.AddRune ((Rune)' ');
 					used++;
 				}
 			}

+ 3 - 3
UICatalog/Scenarios/ListsAndCombos.cs

@@ -3,7 +3,7 @@ using System.Linq;
 using System.IO;
 using System.Collections.Generic;
 using Terminal.Gui;
-using NStack;
+using System.Text;
 
 namespace UICatalog.Scenarios {
 	[ScenarioMetadata (Name: "ListView & ComboBox", Description: "Demonstrates a ListView populating a ComboBox that acts as a filter.")]
@@ -13,13 +13,13 @@ namespace UICatalog.Scenarios {
 		public override void Setup ()
 		{
 			//TODO: Duplicated code in Demo.cs Consider moving to shared assembly
-			var items = new List<ustring> ();
+			var items = new List<string> ();
 			foreach (var dir in new [] { "/etc", @$"{Environment.GetEnvironmentVariable ("SystemRoot")}\System32" }) {
 				if (Directory.Exists (dir)) {
 					items = Directory.GetFiles (dir).Union(Directory.GetDirectories(dir))
 						.Select (Path.GetFileName)
 						.Where (x => char.IsLetterOrDigit (x [0]))
-						.OrderBy (x => x).Select(x => ustring.Make(x)).ToList() ;
+						.OrderBy (x => x).Select(x => x).ToList() ;
 				}
 			}
 

Some files were not shown because too many files changed in this diff