Browse Source

Merge pull request #646 from tig/charmap_improvements

RadioGroup and ScrollView now use better glyphs. ConsoleDriver updates to support.
Charlie Kindel 5 years ago
parent
commit
fcb71c09e4

+ 1 - 1
Example/demo.cs

@@ -168,7 +168,7 @@ static class Demo {
 			passText,
 			passText,
 			new FrameView (new Rect (3, 10, 25, 6), "Options"){
 			new FrameView (new Rect (3, 10, 25, 6), "Options"){
 				new CheckBox (1, 0, "Remember me"),
 				new CheckBox (1, 0, "Remember me"),
-				new RadioGroup (1, 2, new [] { "_Personal", "_Company" }),
+				new RadioGroup (1, 2, new ustring [] { "_Personal", "_Company" }),
 			},
 			},
 			new ListView (new Rect (59, 6, 16, 4), new string [] {
 			new ListView (new Rect (59, 6, 16, 4), new string [] {
 				"First row",
 				"First row",

+ 14 - 0
Terminal.Gui/ConsoleDrivers/CursesDriver/CursesDriver.cs

@@ -531,6 +531,20 @@ namespace Terminal.Gui {
 			RightTee = Curses.ACS_RTEE;
 			RightTee = Curses.ACS_RTEE;
 			TopTee = Curses.ACS_TTEE;
 			TopTee = Curses.ACS_TTEE;
 			BottomTee = Curses.ACS_BTEE;
 			BottomTee = Curses.ACS_BTEE;
+			Checked = '\u221a';
+			UnChecked = ' ';
+			Selected = '\u25cf';
+			UnSelected = '\u25cc';
+			RightArrow = Curses.ACS_RARROW;
+			LeftArrow = Curses.ACS_LARROW;
+			UpArrow = Curses.ACS_UARROW;
+			DownArrow = Curses.ACS_DARROW;
+			LeftDefaultIndicator = '\u25e6';
+			RightDefaultIndicator = '\u25e6';
+			LeftBracket = '[';
+			RightBracket = ']';
+			OnMeterSegment = '\u258c';
+			OffMeterSegement = ' ';
 
 
 			Colors.TopLevel = new ColorScheme ();
 			Colors.TopLevel = new ColorScheme ();
 			Colors.Base = new ColorScheme ();
 			Colors.Base = new ColorScheme ();

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

@@ -164,6 +164,20 @@ namespace Terminal.Gui {
 			RightTee = '\u2524';
 			RightTee = '\u2524';
 			TopTee = '\u22a4';
 			TopTee = '\u22a4';
 			BottomTee = '\u22a5';
 			BottomTee = '\u22a5';
+			Checked = '\u221a';
+			UnChecked = ' ';
+			Selected = '\u25cf';
+			UnSelected = '\u25cc';
+			RightArrow = '\u25ba';
+			LeftArrow = '\u25c4';
+			UpArrow = '\u25b2';
+			DownArrow = '\u25bc';
+			LeftDefaultIndicator = '\u25e6';
+			RightDefaultIndicator = '\u25e6';
+			LeftBracket = '[';
+			RightBracket = ']';
+			OnMeterSegment = '\u258c';
+			OffMeterSegement = ' ';
 		}
 		}
 
 
 		public override Attribute MakeAttribute (Color fore, Color back)
 		public override Attribute MakeAttribute (Color fore, Color back)

+ 18 - 4
Terminal.Gui/ConsoleDrivers/WindowsDriver.cs

@@ -487,16 +487,30 @@ namespace Terminal.Gui {
 
 
 			HLine = '\u2500';
 			HLine = '\u2500';
 			VLine = '\u2502';
 			VLine = '\u2502';
-			Stipple = '\u2592';
-			Diamond = '\u25c6';
+			Stipple = '\u2591';
+			Diamond = '\u25ca';
 			ULCorner = '\u250C';
 			ULCorner = '\u250C';
 			LLCorner = '\u2514';
 			LLCorner = '\u2514';
 			URCorner = '\u2510';
 			URCorner = '\u2510';
 			LRCorner = '\u2518';
 			LRCorner = '\u2518';
 			LeftTee = '\u251c';
 			LeftTee = '\u251c';
 			RightTee = '\u2524';
 			RightTee = '\u2524';
-			TopTee = '\u22a4';
-			BottomTee = '\u22a5';
+			TopTee = '\u252c';
+			BottomTee = '\u2534';
+			Checked = '\u221a';
+			UnChecked = ' ';
+			Selected = '\u25cf';
+			UnSelected = '\u25cc';
+			RightArrow = '\u25ba';
+			LeftArrow = '\u25c4';
+			UpArrow = '\u25b2';
+			DownArrow = '\u25bc';
+			LeftDefaultIndicator = '\u25e6';
+			RightDefaultIndicator = '\u25e6';
+			LeftBracket = '[';
+			RightBracket = ']';
+			OnMeterSegment = '\u258c';
+			OffMeterSegement = ' ';
 		}
 		}
 
 
 		[StructLayout (LayoutKind.Sequential)]
 		[StructLayout (LayoutKind.Sequential)]

+ 70 - 0
Terminal.Gui/Core/ConsoleDriver.cs

@@ -948,6 +948,76 @@ namespace Terminal.Gui {
 		/// </summary>
 		/// </summary>
 		public Rune BottomTee;
 		public Rune BottomTee;
 
 
+		/// <summary>
+		/// Checkmark.
+		/// </summary>
+		public Rune Checked;
+
+		/// <summary>
+		/// Un-checked checkmark.
+		/// </summary>
+		public Rune UnChecked;
+
+		/// <summary>
+		/// Selected mark.
+		/// </summary>
+		public Rune Selected;
+
+		/// <summary>
+		/// Un-selected selected mark.
+		/// </summary>
+		public Rune UnSelected;
+
+		/// <summary>
+		/// Right Arrow.
+		/// </summary>
+		public Rune RightArrow;
+
+		/// <summary>
+		/// Left Arrow.
+		/// </summary>
+		public Rune LeftArrow;
+
+		/// <summary>
+		/// Down Arrow.
+		/// </summary>
+		public Rune DownArrow;
+
+		/// <summary>
+		/// Up Arrow.
+		/// </summary>
+		public Rune UpArrow;
+
+		/// <summary>
+		/// Left indicator for default action (e.g. for <see cref="Button"/>).
+		/// </summary>
+		public Rune LeftDefaultIndicator;
+
+		/// <summary>
+		/// Right indicator for default action (e.g. for <see cref="Button"/>).
+		/// </summary>
+		public Rune RightDefaultIndicator;
+
+		/// <summary>
+		/// Left frame/bracket (e.g. '[' for <see cref="Button"/>).
+		/// </summary>
+		public Rune LeftBracket;
+
+		/// <summary>
+		/// Right frame/bracket (e.g. ']' for <see cref="Button"/>).
+		/// </summary>
+		public Rune RightBracket;
+
+		/// <summary>
+		/// On Segment indicator for meter views (e.g. <see cref="ProgressBar"/>.
+		/// </summary>
+		public Rune OnMeterSegment;
+
+		/// <summary>
+		/// Off Segment indicator for meter views (e.g. <see cref="ProgressBar"/>.
+		/// </summary>
+		public Rune OffMeterSegement;
+
 		/// <summary>
 		/// <summary>
 		/// Make the attribute for the foreground and background colors.
 		/// Make the attribute for the foreground and background colors.
 		/// </summary>
 		/// </summary>

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

@@ -960,7 +960,7 @@ namespace Terminal.Gui {
 							Application.CurrentView = view;
 							Application.CurrentView = view;
 
 
 							// Clip the sub-view
 							// Clip the sub-view
-							var savedClip = ClipToBounds ();
+							var savedClip = view.ClipToBounds ();
 
 
 							// Draw the subview
 							// Draw the subview
 							// Use the view's bounds (view-relative; Location will always be (0,0) because
 							// Use the view's bounds (view-relative; Location will always be (0,0) because

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

@@ -93,6 +93,13 @@
       * Fixes #421 Now builds on Linux with "dotnet build". (Thanks @AArnott!)
       * Fixes #421 Now builds on Linux with "dotnet build". (Thanks @AArnott!)
       * MenuItem now supports checked/selected items. (Thanks @tig!)
       * MenuItem now supports checked/selected items. (Thanks @tig!)
       * Label no longer incorreclty displays formfeed char. (Thanks @tig!)
       * Label no longer incorreclty displays formfeed char. (Thanks @tig!)
+      * Fixes #645 - RadioGroup now supports unicode. (Thanks @tig!)
+      * Fixes #573 - RadioGroup supports Computed Layout. (Thanks @tig!)
+      * RadioGroup now uses a single, good looking, glyph. (Thanks @tig!)
+      * RadioGroup now supportrs the Action-based event pattern correctly. BREAKING CHANGE. (Thanks @tig!)
+      * ConsoleDriver and Drivers have new standard glyph definitions for things like right arrow. (Thanks @tig!)
+      * ScrollView updated to use pretty glyphs. (Thanks @tig!)
+      * Menubar now uses pretty arrow glyph for sub-menus. (Thanks @tig!)
 
 
       0.81:
       0.81:
       * Fix ncurses engine for macOS/Linux, it works again
       * Fix ncurses engine for macOS/Linux, it works again

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

@@ -329,7 +329,7 @@ namespace Terminal.Gui {
 					if (item == null)
 					if (item == null)
 						Driver.AddRune (Driver.HLine);
 						Driver.AddRune (Driver.HLine);
 					else if (p == Frame.Width - 3 && barItems.Children [i].SubMenu != null)
 					else if (p == Frame.Width - 3 && barItems.Children [i].SubMenu != null)
-						Driver.AddRune ('>');
+						Driver.AddRune (Driver.RightArrow);
 					else
 					else
 						Driver.AddRune (' ');
 						Driver.AddRune (' ');
 
 
@@ -340,20 +340,20 @@ namespace Terminal.Gui {
 				}
 				}
 
 
 				ustring textToDraw;
 				ustring textToDraw;
-				var checkChar = (char)0x25cf;
-				var uncheckedChar = (char)0x25cc;
+				var checkChar = Driver.Selected;
+				var uncheckedChar = Driver.UnSelected;
 
 
 				if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked)) {
 				if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked)) {
-					checkChar = (char)0x221a;
-					uncheckedChar = ' ';
+					checkChar = Driver.Checked; 
+					uncheckedChar = Driver.UnChecked;
 				}
 				}
 
 
 				// Support Checked even though CHeckType wasn't set
 				// Support Checked even though CHeckType wasn't set
 				if (item.Checked) {
 				if (item.Checked) {
-					textToDraw = checkChar + " " + item.Title;
+					textToDraw = ustring.Make(new Rune [] { checkChar, ' ' }) + item.Title;
 				} else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) ||
 				} else if (item.CheckType.HasFlag (MenuItemCheckStyle.Checked) ||
 					item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) {
 					item.CheckType.HasFlag (MenuItemCheckStyle.Radio)) {
-					textToDraw = uncheckedChar + " " + item.Title;
+					textToDraw = ustring.Make (new Rune [] { uncheckedChar, ' ' }) + item.Title;
 				} else {
 				} else {
 					textToDraw = item.Title;
 					textToDraw = item.Title;
 				}
 				}

+ 137 - 71
Terminal.Gui/Views/RadioGroup.cs

@@ -1,118 +1,147 @@
-using System;
+using NStack;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
 namespace Terminal.Gui {
 namespace Terminal.Gui {
 	/// <summary>
 	/// <summary>
 	/// <see cref="RadioGroup"/> shows a group of radio labels, only one of those can be selected at a given time
 	/// <see cref="RadioGroup"/> shows a group of radio labels, only one of those can be selected at a given time
 	/// </summary>
 	/// </summary>
 	public class RadioGroup : View {
 	public class RadioGroup : View {
-		int selected, cursor;
+		int selected = -1;
+		int cursor;
+
+		void Init(Rect rect, ustring [] radioLabels, int selected)
+		{
+			if (radioLabels == null) {
+				this.radioLabels = new List<ustring>();
+			} else {
+				this.radioLabels = radioLabels.ToList ();
+			}
+			
+			this.selected = selected;
+			SetWidthHeight (this.radioLabels);
+			CanFocus = true;
+		}
+
 
 
 		/// <summary>
 		/// <summary>
 		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Computed"/> layout.
 		/// </summary>
 		/// </summary>
+		public RadioGroup () : this (radioLabels: new ustring [] { }) { }
+
+		/// <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 ()
+		{
+			Init (Rect.Empty, radioLabels, selected);
+		}
+
+		/// <summary>
+		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Absolute"/> layout.
+		/// </summary>
 		/// <param name="rect">Boundaries for the radio group.</param>
 		/// <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="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>
 		/// <param name="selected">The index of item to be selected, the value is clamped to the number of items.</param>
-		public RadioGroup (Rect rect, string [] radioLabels, int selected = 0) : base (rect)
+		public RadioGroup (Rect rect, ustring [] radioLabels, int selected = 0) : base (rect)
 		{
 		{
-			this.selected = selected;
-			this.radioLabels = radioLabels;
-			CanFocus = true;
+			Init (rect, radioLabels, selected);
 		}
 		}
 
 
+		/// <summary>
+		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Absolute"/> layout.
+		/// The <see cref="View"/> frame is computed from the provided radio labels.
+		/// </summary>
+		/// <param name="x">The x coordinate.</param>
+		/// <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) : 
+			this (MakeRect (x, y, radioLabels != null ? radioLabels.ToList() : null), radioLabels, selected) { }
+
 		/// <summary>
 		/// <summary>
 		/// The location of the cursor in the <see cref="RadioGroup"/>
 		/// The location of the cursor in the <see cref="RadioGroup"/>
 		/// </summary>
 		/// </summary>
 		public int Cursor {
 		public int Cursor {
 			get => cursor;
 			get => cursor;
 			set {
 			set {
-				if (cursor < 0 || cursor >= radioLabels.Length)
+				if (cursor < 0 || cursor >= radioLabels.Count)
 					return;
 					return;
 				cursor = value;
 				cursor = value;
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 			}
 			}
 		}
 		}
 
 
-		/// <summary>
-		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Computed"/> layout.
-		/// </summary>
-		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 (string [] radioLabels, int selected = 0) : base ()
-		{
-			SetWidthHeight(radioLabels);
-
-			this.selected = selected;
-			this.radioLabels = radioLabels;
-			CanFocus = true;
-		}
-
-		void SetWidthHeight(string[] radioLabels)
+		void SetWidthHeight (List<ustring> radioLabels)
 		{
 		{
 			var r = MakeRect(0, 0, radioLabels);
 			var r = MakeRect(0, 0, radioLabels);
-			Width = r.Width;
-			Height = radioLabels.Length;
+			if (LayoutStyle == LayoutStyle.Computed) {
+				Width = r.Width;
+				Height = radioLabels.Count;
+			} else {
+				Frame = new Rect (Frame.Location, new Size (r.Width, radioLabels.Count));
+			}
 		}
 		}
 
 
-		static Rect MakeRect (int x, int y, string [] radioLabels)
+		static Rect MakeRect (int x, int y, List<ustring> radioLabels)
 		{
 		{
 			int width = 0;
 			int width = 0;
 
 
+			if (radioLabels == null) {
+				return new Rect (x, y, width, 0);
+			}
+
 			foreach (var s in radioLabels)
 			foreach (var s in radioLabels)
-				width = Math.Max (s.Length + 4, width);
-			return new Rect (x, y, width, radioLabels.Length);
+				width = Math.Max (s.Length + 3, width);
+			return new Rect (x, y, width, radioLabels.Count);
 		}
 		}
 
 
-		/// <summary>
-		/// Initializes a new instance of the <see cref="RadioGroup"/> class using <see cref="LayoutStyle.Absolute"/> layout.
-		/// The <see cref="View"/> frame is computed from the provided radio labels.
-		/// </summary>
-		/// <param name="x">The x coordinate.</param>
-		/// <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, string [] radioLabels, int selected = 0) : this (MakeRect (x, y, radioLabels), radioLabels, selected) { }
 
 
-		string [] radioLabels;
+		List<ustring> radioLabels = new List<ustring> ();
 
 
 		/// <summary>
 		/// <summary>
 		/// The radio labels to display
 		/// The radio labels to display
 		/// </summary>
 		/// </summary>
 		/// <value>The radio labels.</value>
 		/// <value>The radio labels.</value>
-		public string [] RadioLabels { 
-			get => radioLabels;
+		public ustring [] RadioLabels { 
+			get => radioLabels.ToArray();
 			set {
 			set {
-				Update(value);
-				radioLabels = value;
-				selected = 0;
+				var prevCount = radioLabels.Count;
+				radioLabels = value.ToList ();
+				if (prevCount != radioLabels.Count) {
+					SetWidthHeight (radioLabels);
+				}
+				SelectedItem = 0;
 				cursor = 0;
 				cursor = 0;
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 			}
 			}
 		}
 		}
 
 
-		void Update(string [] newRadioLabels)
-		{
-			for (int i = 0; i < radioLabels.Length; i++) {
-				Move(0, i);
-				Driver.SetAttribute(ColorScheme.Normal);
-				Driver.AddStr(new string(' ', radioLabels[i].Length + 4));
-			}
-			if (newRadioLabels.Length != radioLabels.Length) {
-				SetWidthHeight(newRadioLabels);
-			}
-		}
+		//// Redraws the RadioGroup 
+		//void Update(List<ustring> newRadioLabels)
+		//{
+		//	for (int i = 0; i < radioLabels.Count; i++) {
+		//		Move(0, i);
+		//		Driver.SetAttribute(ColorScheme.Normal);
+		//		Driver.AddStr(ustring.Make(new string (' ', radioLabels[i].Length + 4)));
+		//	}
+		//	if (newRadioLabels.Count != radioLabels.Count) {
+		//		SetWidthHeight(newRadioLabels);
+		//	}
+		//}
 
 
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override void Redraw (Rect bounds)
 		public override void Redraw (Rect bounds)
 		{
 		{
-			for (int i = 0; i < radioLabels.Length; i++) {
+			Driver.SetAttribute (ColorScheme.Normal);
+			Clear ();
+			for (int i = 0; i < radioLabels.Count; i++) {
 				Move (0, i);
 				Move (0, i);
 				Driver.SetAttribute (ColorScheme.Normal);
 				Driver.SetAttribute (ColorScheme.Normal);
-				Driver.AddStr (i == selected ? "(o) " : "( ) ");
+				Driver.AddStr (ustring.Make(new Rune[] { (i == selected ? Driver.Selected : Driver.UnSelected), ' '}));
 				DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme);
 				DrawHotString (radioLabels [i], HasFocus && i == cursor, ColorScheme);
 			}
 			}
 			base.Redraw (bounds);
 			base.Redraw (bounds);
@@ -121,27 +150,64 @@ namespace Terminal.Gui {
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override void PositionCursor ()
 		public override void PositionCursor ()
 		{
 		{
-			Move (1, cursor);
+			Move (0, cursor);
+		}
+
+		// TODO: Make this a global class
+		/// <summary>
+		/// Event arguments for the SelectedItemChagned event.
+		/// </summary>
+		public class SelectedItemChangedArgs : EventArgs {
+			/// <summary>
+			/// Gets the index of the item that was previously selected. -1 if there was no previous selection.
+			/// </summary>
+			public int PreviousSelectedItem { get;  }
+
+			/// <summary>
+			/// Gets the index of the item that is now selected. -1 if there is no selection.
+			/// </summary>
+			public int SelectedItem { get; }
+
+			/// <summary>
+			/// Initializes a new <see cref="SelectedItemChangedArgs"/> class.
+			/// </summary>
+			/// <param name="selectedItem"></param>
+			/// <param name="previousSelectedItem"></param>
+			public SelectedItemChangedArgs(int selectedItem, int previousSelectedItem)
+			{
+				PreviousSelectedItem = previousSelectedItem;
+				SelectedItem = selectedItem;
+			}
 		}
 		}
 
 
 		/// <summary>
 		/// <summary>
-		/// Invoked when the selected radio label has changed
+		/// Invoked when the selected radio label has changed.
 		/// </summary>
 		/// </summary>
-		public Action<int> SelectedItemChanged;
+		public Action<SelectedItemChangedArgs> SelectedItemChanged;
 
 
 		/// <summary>
 		/// <summary>
 		/// The currently selected item from the list of radio labels
 		/// The currently selected item from the list of radio labels
 		/// </summary>
 		/// </summary>
 		/// <value>The selected.</value>
 		/// <value>The selected.</value>
-		public int Selected {
+		public int SelectedItem {
 			get => selected;
 			get => selected;
 			set {
 			set {
-				selected = value;
-				SelectedItemChanged?.Invoke (selected);
+				OnSelectedItemChanged (value, SelectedItem);
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 			}
 			}
 		}
 		}
 
 
+		/// <summary>
+		/// Called whenever the current selected item changes. Invokes the <see cref="SelectedItemChanged"/> event.
+		/// </summary>
+		/// <param name="selectedItem"></param>
+		/// <param name="previousSelectedItem"></param>
+		public virtual void OnSelectedItemChanged (int selectedItem, int previousSelectedItem)
+		{
+			selected = selectedItem;
+			SelectedItemChanged?.Invoke (new SelectedItemChangedArgs (selectedItem, previousSelectedItem));
+		}
+
 		///<inheritdoc/>
 		///<inheritdoc/>
 		public override bool ProcessColdKey (KeyEvent kb)
 		public override bool ProcessColdKey (KeyEvent kb)
 		{
 		{
@@ -156,7 +222,7 @@ namespace Terminal.Gui {
 							nextIsHot = true;
 							nextIsHot = true;
 						else {
 						else {
 							if (nextIsHot && c == key) {
 							if (nextIsHot && c == key) {
-								Selected = i;
+								SelectedItem = i;
 								cursor = i;
 								cursor = i;
 								if (!HasFocus)
 								if (!HasFocus)
 									SuperView.SetFocus (this);
 									SuperView.SetFocus (this);
@@ -183,14 +249,14 @@ namespace Terminal.Gui {
 				}
 				}
 				break;
 				break;
 			case Key.CursorDown:
 			case Key.CursorDown:
-				if (cursor + 1 < radioLabels.Length) {
+				if (cursor + 1 < radioLabels.Count) {
 					cursor++;
 					cursor++;
 					SetNeedsDisplay ();
 					SetNeedsDisplay ();
 					return true;
 					return true;
 				}
 				}
 				break;
 				break;
 			case Key.Space:
 			case Key.Space:
-				Selected = cursor;
+				SelectedItem = cursor;
 				return true;
 				return true;
 			}
 			}
 			return base.ProcessKey (kb);
 			return base.ProcessKey (kb);
@@ -204,8 +270,8 @@ namespace Terminal.Gui {
 
 
 			SuperView.SetFocus (this);
 			SuperView.SetFocus (this);
 
 
-			if (me.Y < radioLabels.Length) {
-				cursor = Selected = me.Y;
+			if (me.Y < radioLabels.Count) {
+				cursor = SelectedItem = me.Y;
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 			}
 			}
 			return true;
 			return true;

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

@@ -156,9 +156,9 @@ namespace Terminal.Gui {
 					var by2 = (position + bh) * bh / Size;
 					var by2 = (position + bh) * bh / Size;
 
 
 					Move (col, 0);
 					Move (col, 0);
-					Driver.AddRune ('^');
+					Driver.AddRune (Driver.UpArrow);
 					Move (col, Bounds.Height - 1);
 					Move (col, Bounds.Height - 1);
-					Driver.AddRune ('v');
+					Driver.AddRune (Driver.DownArrow);
 					for (int y = 0; y < bh; y++) {
 					for (int y = 0; y < bh; y++) {
 						Move (col, y + 1);
 						Move (col, y + 1);
 						if (y < by1 - 1 || y > by2)
 						if (y < by1 - 1 || y > by2)
@@ -204,7 +204,7 @@ namespace Terminal.Gui {
 					var bx2 = (position + bw) * bw / Size;
 					var bx2 = (position + bw) * bw / Size;
 
 
 					Move (0, row);
 					Move (0, row);
-					Driver.AddRune ('<');
+					Driver.AddRune (Driver.LeftArrow);
 
 
 					for (int x = 0; x < bw; x++) {
 					for (int x = 0; x < bw; x++) {
 
 
@@ -224,7 +224,7 @@ namespace Terminal.Gui {
 						}
 						}
 						Driver.AddRune (special);
 						Driver.AddRune (special);
 					}
 					}
-					Driver.AddRune ('>');
+					Driver.AddRune (Driver.RightArrow);
 				}
 				}
 			}
 			}
 		}
 		}

+ 12 - 12
UICatalog/Scenarios/AllViewsTester.cs

@@ -117,7 +117,7 @@ namespace UICatalog {
 			};
 			};
 			_settingsPane.Add (_computedCheckBox);
 			_settingsPane.Add (_computedCheckBox);
 
 
-			var radioItems = new [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
+			var radioItems = new ustring [] { "Percent(x)", "AnchorEnd(x)", "Center", "At(x)" };
 			_locationFrame = new FrameView ("Location (Pos)") {
 			_locationFrame = new FrameView ("Location (Pos)") {
 				X = Pos.Left (_computedCheckBox),
 				X = Pos.Left (_computedCheckBox),
 				Y = Pos.Bottom (_computedCheckBox),
 				Y = Pos.Bottom (_computedCheckBox),
@@ -146,7 +146,7 @@ namespace UICatalog {
 
 
 			_locationFrame.Add (_xRadioGroup);
 			_locationFrame.Add (_xRadioGroup);
 
 
-			radioItems = new [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
+			radioItems = new ustring [] { "Percent(y)", "AnchorEnd(y)", "Center", "At(y)" };
 			label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
 			label = new Label ("y:") { X = Pos.Right (_xRadioGroup) + 1, Y = 0 };
 			_locationFrame.Add (label);
 			_locationFrame.Add (label);
 			_yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
 			_yText = new TextField ($"{_yVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
@@ -173,7 +173,7 @@ namespace UICatalog {
 				Width = 40,
 				Width = 40,
 			};
 			};
 
 
-			radioItems = new [] { "Percent(width)", "Fill(width)", "Sized(width)" };
+			radioItems = new ustring [] { "Percent(width)", "Fill(width)", "Sized(width)" };
 			label = new Label ("width:") { X = 0, Y = 0 };
 			label = new Label ("width:") { X = 0, Y = 0 };
 			_sizeFrame.Add (label);
 			_sizeFrame.Add (label);
 			_wRadioGroup = new RadioGroup (radioItems) {
 			_wRadioGroup = new RadioGroup (radioItems) {
@@ -193,7 +193,7 @@ namespace UICatalog {
 			_sizeFrame.Add (_wText);
 			_sizeFrame.Add (_wText);
 			_sizeFrame.Add (_wRadioGroup);
 			_sizeFrame.Add (_wRadioGroup);
 
 
-			radioItems = new [] { "Percent(height)", "Fill(height)", "Sized(height)" };
+			radioItems = new ustring [] { "Percent(height)", "Fill(height)", "Sized(height)" };
 			label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
 			label = new Label ("height:") { X = Pos.Right (_wRadioGroup) + 1, Y = 0 };
 			_sizeFrame.Add (label);
 			_sizeFrame.Add (label);
 			_hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
 			_hText = new TextField ($"{_hVal}") { X = Pos.Right (label) + 1, Y = 0, Width = 4 };
@@ -235,7 +235,7 @@ namespace UICatalog {
 				return;
 				return;
 			}
 			}
 			try {
 			try {
-				switch (_xRadioGroup.Selected) {
+				switch (_xRadioGroup.SelectedItem) {
 				case 0:
 				case 0:
 					view.X = Pos.Percent (_xVal);
 					view.X = Pos.Percent (_xVal);
 					break;
 					break;
@@ -250,7 +250,7 @@ namespace UICatalog {
 					break;
 					break;
 				}
 				}
 
 
-				switch (_yRadioGroup.Selected) {
+				switch (_yRadioGroup.SelectedItem) {
 				case 0:
 				case 0:
 					view.Y = Pos.Percent (_yVal);
 					view.Y = Pos.Percent (_yVal);
 					break;
 					break;
@@ -265,7 +265,7 @@ namespace UICatalog {
 					break;
 					break;
 				}
 				}
 
 
-				switch (_wRadioGroup.Selected) {
+				switch (_wRadioGroup.SelectedItem) {
 				case 0:
 				case 0:
 					view.Width = Dim.Percent (_wVal);
 					view.Width = Dim.Percent (_wVal);
 					break;
 					break;
@@ -277,7 +277,7 @@ namespace UICatalog {
 					break;
 					break;
 				}
 				}
 
 
-				switch (_hRadioGroup.Selected) {
+				switch (_hRadioGroup.SelectedItem) {
 				case 0:
 				case 0:
 					view.Height = Dim.Percent (_hVal);
 					view.Height = Dim.Percent (_hVal);
 					break;
 					break;
@@ -301,15 +301,15 @@ namespace UICatalog {
 		{
 		{
 			var x = view.X.ToString ();
 			var x = view.X.ToString ();
 			var y = view.Y.ToString ();
 			var y = view.Y.ToString ();
-			_xRadioGroup.Selected = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ());
-			_yRadioGroup.Selected = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ());
+			_xRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => x.Contains (s)).First ());
+			_yRadioGroup.SelectedItem = posNames.IndexOf (posNames.Where (s => y.Contains (s)).First ());
 			_xText.Text = $"{view.Frame.X}";
 			_xText.Text = $"{view.Frame.X}";
 			_yText.Text = $"{view.Frame.Y}";
 			_yText.Text = $"{view.Frame.Y}";
 
 
 			var w = view.Width.ToString ();
 			var w = view.Width.ToString ();
 			var h = view.Height.ToString ();
 			var h = view.Height.ToString ();
-			_wRadioGroup.Selected = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ());
-			_hRadioGroup.Selected = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ());
+			_wRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => w.Contains (s)).First ());
+			_hRadioGroup.SelectedItem = dimNames.IndexOf (dimNames.Where (s => h.Contains (s)).First ());
 			_wText.Text = $"{view.Frame.Width}";
 			_wText.Text = $"{view.Frame.Width}";
 			_hText.Text = $"{view.Frame.Height}";
 			_hText.Text = $"{view.Frame.Height}";
 		}
 		}

+ 6 - 6
UICatalog/Scenarios/Buttons.cs

@@ -111,7 +111,7 @@ namespace UICatalog {
 			Win.Add (computedFrame);
 			Win.Add (computedFrame);
 
 
 			// Demonstrates how changing the View.Frame property can move Views
 			// Demonstrates how changing the View.Frame property can move Views
-			var moveBtn = new Button ("Move This Button via Pos") {
+			var moveBtn = new Button ("Move This \u263b Button _via Pos") {
 				X = 0,
 				X = 0,
 				Y = Pos.Center() - 1,
 				Y = Pos.Center() - 1,
 				Width = 30,
 				Width = 30,
@@ -124,7 +124,7 @@ namespace UICatalog {
 			computedFrame.Add (moveBtn);
 			computedFrame.Add (moveBtn);
 
 
 			// Demonstrates how changing the View.Frame property can SIZE Views (#583)
 			// Demonstrates how changing the View.Frame property can SIZE Views (#583)
-			var sizeBtn = new Button ("Size This Button via Pos") {
+			var sizeBtn = new Button ("Size This \u263a Button _via Pos") {
 				X = 0,
 				X = 0,
 				Y = Pos.Center () + 1,
 				Y = Pos.Center () + 1,
 				Width = 30,
 				Width = 30,
@@ -168,12 +168,12 @@ namespace UICatalog {
 			};
 			};
 			Win.Add (label);
 			Win.Add (label);
 
 
-			var radioGroup = new RadioGroup (new [] { "Left", "Right", "Centered", "Justified" }) {
+			var radioGroup = new RadioGroup (new ustring [] { "Left", "Right", "Centered", "Justified" }) {
 				X = 4,
 				X = 4,
 				Y = Pos.Bottom (label) + 1,
 				Y = Pos.Bottom (label) + 1,
-				Selected = 2,
-				SelectedItemChanged = (selected) => {
-					switch (selected) {
+				SelectedItem = 2,
+				SelectedItemChanged = (args) => {
+					switch (args.SelectedItem) {
 					case 0:
 					case 0:
 						moveBtn.TextAlignment = TextAlignment.Left;
 						moveBtn.TextAlignment = TextAlignment.Left;
 						sizeBtn.TextAlignment = TextAlignment.Left;
 						sizeBtn.TextAlignment = TextAlignment.Left;

+ 45 - 35
UICatalog/Scenarios/CharacterMap.cs

@@ -1,5 +1,6 @@
 using NStack;
 using NStack;
 using System.Collections.Generic;
 using System.Collections.Generic;
+using System.Linq;
 using System.Text;
 using System.Text;
 using Terminal.Gui;
 using Terminal.Gui;
 
 
@@ -16,40 +17,50 @@ namespace UICatalog {
 	class CharacterMap : Scenario {
 	class CharacterMap : Scenario {
 		public override void Setup ()
 		public override void Setup ()
 		{
 		{
-			var charMap = new CharMap () { X = 0, Y = 0, Width = CharMap.RowWidth + 2, Height = Dim.Fill(), Start = 0x2500, 
-				ColorScheme = Colors.Dialog};
+			var charMap = new CharMap () {
+				X = 0,
+				Y = 0,
+				Width = CharMap.RowWidth + 2,
+				Height = Dim.Fill (),
+				Start = 0x2500,
+				ColorScheme = Colors.Dialog
+			};
 
 
 			Win.Add (charMap);
 			Win.Add (charMap);
+			var label = new Label ("Jump To Unicode Block:") { X = Pos.Right (charMap) + 1, Y = Pos.Y (charMap) };
+			Win.Add (label);
 
 
-			Button CreateBlock(Window win, ustring title, int start, int end, View align)
+			(ustring radioLabel, int start, int end) CreateRadio (ustring title, int start, int end)
 			{
 			{
-				var button = new Button ($"{title} (U+{start:x5}-{end:x5})") {
-					X = Pos.X (align),
-					Y = Pos.Bottom (align),
-					Clicked = () => {
-						charMap.Start = start;
-					},
-				};
-				win.Add (button);
-				return button;
+				return ($"{title} (U+{start:x5}-{end:x5})", start, end);
+			}
+
+			var radioItems = new (ustring radioLabel, int start, int end) [] {
+				CreateRadio("Currency Symbols", 0x20A0, 0x20CF),
+				CreateRadio("Letterlike Symbols", 0x2100, 0x214F),
+				CreateRadio("Arrows", 0x2190, 0x21ff),
+				CreateRadio("Mathematical symbols", 0x2200, 0x22ff),
+				CreateRadio("Miscellaneous Technical", 0x2300, 0x23ff),
+				CreateRadio("Box Drawing & Geometric Shapes", 0x2500, 0x25ff),
+				CreateRadio("Miscellaneous Symbols", 0x2600, 0x26ff),
+				CreateRadio("Dingbats", 0x2700, 0x27ff),
+				CreateRadio("Braille", 0x2800, 0x28ff),
+				CreateRadio("Miscellaneous Symbols and Arrows", 0x2b00, 0x2bff),
+				CreateRadio("Alphabetic Presentation Forms", 0xFB00, 0xFb4f),
+				CreateRadio("Cuneiform Numbers and Punctuation", 0x12400, 0x1240f),
+				CreateRadio("Chess Symbols", 0x1FA00, 0x1FA0f),
+				CreateRadio("End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal),
 			};
 			};
 
 
-			var label = new Label ("Unicode Blocks:") { X = Pos.Right (charMap) + 2, Y = Pos.Y (charMap) };
-			Win.Add (label);
-			var button = CreateBlock (Win, "Currency Symbols", 0x20A0, 0x20CF, label);
-			button = CreateBlock (Win, "Letterlike Symbols", 0x2100, 0x214F, button);
-			button = CreateBlock (Win, "Arrows", 0x2190, 0x21ff, button);
-			button = CreateBlock (Win, "Mathematical symbols", 0x2200, 0x22ff, button);
-			button = CreateBlock (Win, "Miscellaneous Technical", 0x2300, 0x23ff, button);
-			button = CreateBlock (Win, "Box Drawing & Geometric Shapes", 0x2500, 0x25ff, button);
-			button = CreateBlock (Win, "Miscellaneous Symbols", 0x2600, 0x26ff, button);
-			button = CreateBlock (Win, "Dingbats", 0x2700, 0x27ff, button);
-			button = CreateBlock (Win, "Braille", 0x2800, 0x28ff, button);
-			button = CreateBlock (Win, "Miscellaneous Symbols and Arrows", 0x2b00, 0x2bff, button);
-			button = CreateBlock (Win, "Alphabetic Presentation Forms", 0xFB00, 0xFb4f, button);
-			button = CreateBlock (Win, "Cuneiform Numbers and Punctuation[1", 0x12400, 0x1240f, button);
-			button = CreateBlock (Win, "Chess Symbols", 0x1FA00, 0x1FA0f, button);
-			button = CreateBlock (Win, "End", CharMap.MaxCodePointVal - 16, CharMap.MaxCodePointVal, button);
+			var jumpList = new RadioGroup (radioItems.Select (t => t.radioLabel).ToArray ());
+			jumpList.X = Pos.X (label);
+			jumpList.Y = Pos.Bottom (label);
+			jumpList.Width = Dim.Fill ();
+			jumpList.SelectedItemChanged = (args) => {
+				charMap.Start = radioItems[args.SelectedItem].start;
+			};
+
+			Win.Add (jumpList);
 		}
 		}
 	}
 	}
 
 
@@ -64,7 +75,6 @@ namespace UICatalog {
 			set {
 			set {
 				_start = value;
 				_start = value;
 				ContentOffset = new Point (0, _start / 16);
 				ContentOffset = new Point (0, _start / 16);
-
 				SetNeedsDisplay ();
 				SetNeedsDisplay ();
 			}
 			}
 		}
 		}
@@ -74,7 +84,7 @@ namespace UICatalog {
 
 
 		// Row Header + space + (space + char + space)
 		// Row Header + space + (space + char + space)
 		public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
 		public static int RowHeaderWidth => $"U+{MaxCodePointVal:x5}".Length;
-		public static int RowWidth => RowHeaderWidth + 1 + (" c ".Length * 16);
+		public static int RowWidth => RowHeaderWidth + (" c".Length * 16);
 
 
 		public CharMap ()
 		public CharMap ()
 		{
 		{
@@ -96,18 +106,18 @@ namespace UICatalog {
 		private void CharMap_DrawContent (Rect viewport)
 		private void CharMap_DrawContent (Rect viewport)
 		{
 		{
 			for (int header = 0; header < 16; header++) {
 			for (int header = 0; header < 16; header++) {
-				Move (viewport.X + RowHeaderWidth + 1 + (header * 3), 0);
+				Move (viewport.X + RowHeaderWidth + (header * 2), 0);
 				Driver.AddStr ($" {header:x} ");
 				Driver.AddStr ($" {header:x} ");
 			}
 			}
-			for (int row = 0; row < viewport.Height - 1; row++) {
+			for (int row = 0, y = 0; row < viewport.Height / 2 - 1; row++, y += 2) {
 				int val = (-viewport.Y + row) * 16;
 				int val = (-viewport.Y + row) * 16;
 				if (val < MaxCodePointVal) {
 				if (val < MaxCodePointVal) {
 					var rowLabel = $"U+{val / 16:x4}x";
 					var rowLabel = $"U+{val / 16:x4}x";
-					Move (0, row + 1);
+					Move (0, y + 1);
 					Driver.AddStr (rowLabel);
 					Driver.AddStr (rowLabel);
 					for (int col = 0; col < 16; col++) {
 					for (int col = 0; col < 16; col++) {
-						Move (viewport.X + RowHeaderWidth + 1 + (col * 3), 0 + row + 1);
-						Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)} ");
+						Move (viewport.X + RowHeaderWidth + (col * 2), 0 + y + 1);
+						Driver.AddStr ($" {(char)((-viewport.Y + row) * 16 + col)}");
 					}
 					}
 				}
 				}
 			}
 			}

+ 2 - 2
UICatalog/Scenarios/MessageBoxes.cs

@@ -118,7 +118,7 @@ namespace UICatalog {
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 				TextAlignment = Terminal.Gui.TextAlignment.Right,
 			};
 			};
 			frame.Add (label);
 			frame.Add (label);
-			var styleRadioGroup = new RadioGroup (new [] { "_Query", "_Error" } ) {
+			var styleRadioGroup = new RadioGroup (new ustring [] { "_Query", "_Error" } ) {
 				X = Pos.Right (label) + 1,
 				X = Pos.Right (label) + 1,
 				Y = Pos.Top (label),
 				Y = Pos.Top (label),
 			};
 			};
@@ -158,7 +158,7 @@ namespace UICatalog {
 						for (int i = 0; i < numButtons; i++) {
 						for (int i = 0; i < numButtons; i++) {
 							btns.Add(btnText[i % 10]);
 							btns.Add(btnText[i % 10]);
 						}
 						}
-						if (styleRadioGroup.Selected == 0) {
+						if (styleRadioGroup.SelectedItem == 0) {
 							buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";
 							buttonPressedLabel.Text = $"{MessageBox.Query (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";
 						} else {
 						} else {
 							buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";
 							buttonPressedLabel.Text = $"{MessageBox.ErrorQuery (width, height, titleEdit.Text.ToString (), messageEdit.Text.ToString (), btns.ToArray ())}";

+ 1 - 1
UICatalog/Scenarios/Unicode.cs

@@ -69,7 +69,7 @@ namespace UICatalog {
 
 
 			label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 };
 			label = new Label ("RadioGroup:") { X = Pos.X (label), Y = Pos.Bottom (listView) + 1 };
 			Win.Add (label);
 			Win.Add (label);
-			var radioGroup = new RadioGroup (new [] { "item #1", " ~  s  gui.cs   master ↑10", "Со_хранить" }, selected: 0) {
+			var radioGroup = new RadioGroup (new ustring [] { "item #1", " ~  s  gui.cs   master ↑10", "Со_хранить" }, selected: 0) {
 				X = 15,
 				X = 15,
 				Y = Pos.Y (label),
 				Y = Pos.Y (label),
 				Width = Dim.Percent (60),
 				Width = Dim.Percent (60),

+ 1 - 0
UICatalog/UICatalog.csproj

@@ -5,6 +5,7 @@
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <TargetFramework>netcoreapp3.1</TargetFramework>
     <StartupObject>UICatalog.UICatalogApp</StartupObject>
     <StartupObject>UICatalog.UICatalogApp</StartupObject>
     <AssemblyVersion>1.0.0.1</AssemblyVersion>
     <AssemblyVersion>1.0.0.1</AssemblyVersion>
+    <LangVersion>8.0</LangVersion>
   </PropertyGroup>
   </PropertyGroup>
 
 
   <ItemGroup>
   <ItemGroup>