Browse Source

Renamed classes; fixed rendering bug in ListView

Charlie Kindel 2 years ago
parent
commit
66398eb9ef

+ 7 - 7
Terminal.Gui/Core/SearchCollectionNavigator.cs → Terminal.Gui/Core/CollectionNavigator.cs

@@ -15,17 +15,17 @@ namespace Terminal.Gui {
 	/// If the user pauses keystrokes for a short time (250ms), the search string is cleared.
 	/// </para>
 	/// </summary>
-	public class SearchCollectionNavigator {
+	public class CollectionNavigator {
 		/// <summary>
-		/// Constructs a new SearchCollectionNavigator.
+		/// Constructs a new CollectionNavigator.
 		/// </summary>
-		public SearchCollectionNavigator () { }
+		public CollectionNavigator () { }
 
 		/// <summary>
-		/// Constructs a new SearchCollectionNavigator for the given collection.
+		/// Constructs a new CollectionNavigator for the given collection.
 		/// </summary>
 		/// <param name="collection"></param>
-		public SearchCollectionNavigator (IEnumerable<object> collection) => Collection = collection;
+		public CollectionNavigator (IEnumerable<object> collection) => Collection = collection;
 
 		DateTime lastKeystroke = DateTime.Now;
 		internal int TypingDelay { get; set; } = 250;
@@ -41,7 +41,7 @@ namespace Terminal.Gui {
 		public IEnumerable<object> Collection { get; set; }
 
 		/// <summary>
-		/// Event arguments for the <see cref="SearchCollectionNavigator.SearchStringChanged"/> event.
+		/// Event arguments for the <see cref="CollectionNavigator.SearchStringChanged"/> event.
 		/// </summary>
 		public class KeystrokeNavigatorEventArgs {
 			/// <summary>
@@ -162,7 +162,7 @@ namespace Terminal.Gui {
 		/// <param name="minimizeMovement">Set to <see langword="true"/> to stop the search on the first match
 		/// if there are multiple matches for <paramref name="search"/>.
 		/// e.g. "ca" + 'r' should stay on "car" and not jump to "cart". If <see langword="false"/> (the default), 
-		/// the next matching item will be returned, even if it is above in the collection.</param>
+		/// the next matching item will be returned, even if it is above in the collection.
 		/// </param>
 		/// <returns>The index of the next matching item or <see langword="-1"/> if no match was found.</returns>
 		internal int GetNextMatchingItem (int currentIndex, string search, bool minimizeMovement = false)

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

@@ -23,7 +23,7 @@
   <!-- Uncomment the RestoreSources element to have dotnet restore pull NStack from a local dir for testing -->
   <PropertyGroup>
     <!-- See https://stackoverflow.com/a/44463578/297526 -->
-    <!--<RestoreSources>$(RestoreSources);../../local-packages;https://api.nuget.org/v3/index.json</RestoreSources>-->
+    <RestoreSources>$(RestoreSources);..\..\NStack\NStack\bin\Debug;https://api.nuget.org/v3/index.json</RestoreSources>
   </PropertyGroup>
   <!-- API Documentation -->
   <ItemGroup>

+ 16 - 23
Terminal.Gui/Views/ListView.cs

@@ -110,7 +110,7 @@ namespace Terminal.Gui {
 			get => source;
 			set {
 				source = value;
-				Navigator.Collection = source?.ToList ()?.Cast<object> ();
+				KeystrokeNavigator.Collection = source?.ToList ()?.Cast<object> ();
 				top = 0;
 				selected = 0;
 				lastSelectedItem = -1;
@@ -383,7 +383,7 @@ namespace Terminal.Gui {
 						Driver.SetAttribute (current);
 					}
 					if (allowsMarking) {
-						Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? Driver.Checked : Driver.Selected) : 
+						Driver.AddRune (source.IsMarked (item) ? (AllowsMultipleSelection ? Driver.Checked : Driver.Selected) :
 							(AllowsMultipleSelection ? Driver.UnChecked : Driver.UnSelected));
 						Driver.AddRune (' ');
 					}
@@ -408,9 +408,10 @@ namespace Terminal.Gui {
 		public event Action<ListViewRowEventArgs> RowRender;
 
 		/// <summary>
-		/// Gets the <see cref="SearchCollectionNavigator"/> that is used to navigate the <see cref="ListView"/> when searching.
+		/// Gets the <see cref="CollectionNavigator"/> that searches the <see cref="ListView.Source"/> collection as
+		/// the user types.
 		/// </summary>
-		public SearchCollectionNavigator Navigator { get; private set; } = new SearchCollectionNavigator ();
+		public CollectionNavigator KeystrokeNavigator { get; private set; } = new CollectionNavigator ();
 
 		///<inheritdoc/>
 		public override bool ProcessKey (KeyEvent kb)
@@ -423,10 +424,10 @@ namespace Terminal.Gui {
 			if (result != null) {
 				return (bool)result;
 			}
-			
+
 			// Enable user to find & select an item by typing text
-			if (SearchCollectionNavigator.IsCompatibleKey(kb)) {
-				var newItem = Navigator?.GetNextMatchingItem (SelectedItem, (char)kb.KeyValue);
+			if (CollectionNavigator.IsCompatibleKey (kb)) {
+				var newItem = KeystrokeNavigator?.GetNextMatchingItem (SelectedItem, (char)kb.KeyValue);
 				if (newItem is int && newItem != -1) {
 					SelectedItem = (int)newItem;
 					EnsuresVisibilitySelectedItem ();
@@ -840,13 +841,13 @@ namespace Terminal.Gui {
 			if (src == null || src?.Count == 0) {
 				return 0;
 			}
-
+			
 			int maxLength = 0;
 			for (int i = 0; i < src.Count; i++) {
 				var t = src [i];
 				int l;
 				if (t is ustring u) {
-					l = u.RuneCount;
+					l = TextFormatter.GetTextWidth (u);
 				} else if (t is string s) {
 					l = s.Length;
 				} else {
@@ -863,18 +864,10 @@ namespace Terminal.Gui {
 
 		void RenderUstr (ConsoleDriver driver, ustring ustr, int col, int line, int width, int start = 0)
 		{
-			int byteLen = ustr.Length;
-			int used = 0;
-			for (int i = start; i < byteLen;) {
-				(var rune, var size) = Utf8.DecodeRune (ustr, i, i - byteLen);
-				var count = Rune.ColumnWidth (rune);
-				if (used + count > width)
-					break;
-				driver.AddRune (rune);
-				used += count;
-				i += size;
-			}
-			for (; used < width; used++) {
+			var u = TextFormatter.ClipAndJustify (ustr, width, TextAlignment.Left);
+			driver.AddStr (u);
+			width -= TextFormatter.GetTextWidth (u);
+			while (width-- > 0) {
 				driver.AddRune (' ');
 			}
 		}
@@ -924,7 +917,7 @@ namespace Terminal.Gui {
 			if (src == null || src?.Count == 0) {
 				return -1;
 			}
-			
+
 			for (int i = 0; i < src.Count; i++) {
 				var t = src [i];
 				if (t is ustring u) {
@@ -932,7 +925,7 @@ namespace Terminal.Gui {
 						return i;
 					}
 				} else if (t is string s) {
-					if (s.ToUpperInvariant().StartsWith (search.ToUpperInvariant())) {
+					if (s.ToUpperInvariant ().StartsWith (search.ToUpperInvariant ())) {
 						return i;
 					}
 				}

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

@@ -547,7 +547,7 @@ namespace Terminal.Gui {
 			cachedLineMap = new ReadOnlyCollection<Branch<T>> (toReturn);
 			
 			// Update the collection used for search-typing
-			Navigator.Collection = cachedLineMap.Select (b => AspectGetter (b.Model)).ToArray ();
+			KeystrokeNavigator.Collection = cachedLineMap.Select (b => AspectGetter (b.Model)).ToArray ();
 			return cachedLineMap;
 		}
 
@@ -565,10 +565,10 @@ namespace Terminal.Gui {
 		}
 
 		/// <summary>
-		/// Gets the <see cref="SearchCollectionNavigator"/> that is used to navigate the <see cref="TreeView"/> 
-		/// when searching with the keyboard.
+		/// Gets the <see cref="CollectionNavigator"/> that searches the <see cref="Objects"/> collection as
+		/// the user types.
 		/// </summary>
-		public SearchCollectionNavigator Navigator { get; private set; } = new SearchCollectionNavigator ();
+		public CollectionNavigator KeystrokeNavigator { get; private set; } = new CollectionNavigator ();
 
 		/// <inheritdoc/>
 		public override bool ProcessKey (KeyEvent keyEvent)
@@ -585,7 +585,7 @@ namespace Terminal.Gui {
 				}
 
 				// If not a keybinding, is the key a searchable key press?
-				if (SearchCollectionNavigator.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) {
+				if (CollectionNavigator.IsCompatibleKey (keyEvent) && AllowLetterBasedNavigation) {
 					IReadOnlyCollection<Branch<T>> map;
 
 					// If there has been a call to InvalidateMap since the last time
@@ -594,7 +594,7 @@ namespace Terminal.Gui {
 
 					// Find the current selected object within the tree
 					var current = map.IndexOf (b => b.Model == SelectedObject);
-					var newIndex = Navigator?.GetNextMatchingItem (current, (char)keyEvent.KeyValue);
+					var newIndex = KeystrokeNavigator?.GetNextMatchingItem (current, (char)keyEvent.KeyValue);
 
 					if (newIndex is int && newIndex != -1) {
 						SelectedObject = map.ElementAt ((int)newIndex).Model;

+ 1 - 1
UICatalog/Properties/launchSettings.json

@@ -45,7 +45,7 @@
       "commandName": "WSL2",
       "distributionName": ""
     },
-    "SearchCollectionNavigatorTester": {
+    "CollectionNavigatorTester": {
       "commandName": "Project",
       "commandLineArgs": "\"Search Collection Nav\""
     }

+ 2 - 2
UICatalog/Scenario.cs

@@ -233,7 +233,7 @@ namespace UICatalog {
 		}
 
 		/// <summary>
-		/// Returns an instance of each <see cref="Scenario"/> defined in the project. 
+		/// Returns a list of all <see cref="Scenario"/> instanaces defined in the project, sorted by <see cref="ScenarioMetadata.Name"/>.
 		/// https://stackoverflow.com/questions/5411694/get-all-inherited-classes-of-an-abstract-class
 		/// </summary>
 		public static List<Scenario> GetScenarios ()
@@ -245,7 +245,7 @@ namespace UICatalog {
 				objects.Add (scenario);
 				_maxScenarioNameLen = Math.Max (_maxScenarioNameLen, scenario.GetName ().Length + 1);
 			}
-			return objects;
+			return objects.OrderBy (s => s.GetName ()).ToList ();
 		}
 
 		protected virtual void Dispose (bool disposing)

+ 12 - 9
UICatalog/Scenarios/SearchCollectionNavigatorTester.cs → UICatalog/Scenarios/CollectionNavigatorTester.cs

@@ -6,13 +6,13 @@ using Terminal.Gui.Trees;
 
 namespace UICatalog.Scenarios {
 
-	[ScenarioMetadata (Name: "Search Collection Nav", Description: "Demonstrates & tests SearchCollectionNavigator.")]
-	[ScenarioCategory ("Controls"), 
-		ScenarioCategory ("ListView"), 
-		ScenarioCategory ("TreeView"), 
+	[ScenarioMetadata (Name: "Collection Navigator", Description: "Demonstrates keyboard navigation in ListView & TreeView (CollectionNavigator).")]
+	[ScenarioCategory ("Controls"),
+		ScenarioCategory ("ListView"),
+		ScenarioCategory ("TreeView"),
 		ScenarioCategory ("Text and Formatting"),
 		ScenarioCategory ("Mouse and Keyboard")]
-	public class SearchCollectionNavigatorTester : Scenario {
+	public class CollectionNavigatorTester : Scenario {
 
 		// Don't create a Window, just return the top-level view
 		public override void Init (Toplevel top, ColorScheme colorScheme)
@@ -70,6 +70,9 @@ namespace UICatalog.Scenarios {
 				"egg",
 				"candle",
 				" <- space",
+				"\t<- tab",
+				"\n<- newline",
+				"\r<- formfeed",
 				"q",
 				"quit",
 				"quitter"
@@ -141,7 +144,7 @@ namespace UICatalog.Scenarios {
 
 			_listView.SetSource (_items);
 
-			_listView.Navigator.SearchStringChanged += (state) => {
+			_listView.KeystrokeNavigator.SearchStringChanged += (state) => {
 				label.Text = $"ListView: {state.SearchString}";
 			};
 		}
@@ -169,16 +172,16 @@ namespace UICatalog.Scenarios {
 			};
 			Top.Add (_treeView);
 
-			var root = new TreeNode ("Alpha examples");
+			var root = new TreeNode ("IsLetterOrDigit examples");
 			root.Children = _items.Where (i => char.IsLetterOrDigit (i [0])).Select (i => new TreeNode (i)).Cast<ITreeNode> ().ToList ();
 			_treeView.AddObject (root);
-			root = new TreeNode ("Non-Alpha examples");
+			root = new TreeNode ("Non-IsLetterOrDigit examples");
 			root.Children = _items.Where (i => !char.IsLetterOrDigit (i [0])).Select (i => new TreeNode (i)).Cast<ITreeNode> ().ToList ();
 			_treeView.AddObject (root);
 			_treeView.ExpandAll ();
 			_treeView.GoToFirst ();
 
-			_treeView.Navigator.SearchStringChanged += (state) => {
+			_treeView.KeystrokeNavigator.SearchStringChanged += (state) => {
 				label.Text = $"TreeView: {state.SearchString}";
 			};
 		}

+ 1 - 1
UICatalog/Scenarios/ListViewWithSelection.cs

@@ -21,7 +21,7 @@ namespace UICatalog.Scenarios {
 
 		public override void Setup ()
 		{
-			_scenarios = Scenario.GetScenarios ().OrderBy (s => s.GetName ()).ToList ();
+			_scenarios = Scenario.GetScenarios ();
 
 			_customRenderCB = new CheckBox ("Use custom rendering") {
 				X = 0,

+ 13 - 13
UnitTests/SearchCollectionNavigatorTests.cs → UnitTests/CollectionNavigatorTests.cs

@@ -2,7 +2,7 @@
 using Xunit;
 
 namespace Terminal.Gui.Core {
-	public class SearchCollectionNavigatorTests {
+	public class CollectionNavigatorTests {
 		static string [] simpleStrings = new string []{
 		    "appricot", // 0
 		    "arm",      // 1
@@ -14,7 +14,7 @@ namespace Terminal.Gui.Core {
 		[Fact]
 		public void ShouldAcceptNegativeOne ()
 		{
-			var n = new SearchCollectionNavigator (simpleStrings);
+			var n = new CollectionNavigator (simpleStrings);
 
 			// Expect that index of -1 (i.e. no selection) should work correctly
 			// and select the first entry of the letter 'b'
@@ -23,7 +23,7 @@ namespace Terminal.Gui.Core {
 		[Fact]
 		public void OutOfBoundsShouldBeIgnored ()
 		{
-			var n = new SearchCollectionNavigator (simpleStrings);
+			var n = new CollectionNavigator (simpleStrings);
 
 			// Expect saying that index 500 is the current selection should not cause
 			// error and just be ignored (treated as no selection)
@@ -33,7 +33,7 @@ namespace Terminal.Gui.Core {
 		[Fact]
 		public void Cycling ()
 		{
-			var n = new SearchCollectionNavigator (simpleStrings);
+			var n = new CollectionNavigator (simpleStrings);
 			Assert.Equal (2, n.GetNextMatchingItem (0, 'b'));
 			Assert.Equal (3, n.GetNextMatchingItem (2, 'b'));
 
@@ -55,7 +55,7 @@ namespace Terminal.Gui.Core {
 			  };
 
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (2, current = n.GetNextMatchingItem (current, 'b')); // match bat
 			Assert.Equal (4, current = n.GetNextMatchingItem (current, 'b')); // match bbfish
 
@@ -77,7 +77,7 @@ namespace Terminal.Gui.Core {
 			    "candle"
 			  };
 
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (2, n.GetNextMatchingItem (0, 't'));
 
 			// should match "te" in "text"
@@ -104,7 +104,7 @@ namespace Terminal.Gui.Core {
 			    "candle"
 			  };
 
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (3, n.GetNextMatchingItem (0, '丗'));
 
 			// 丗丙业丞 is as good a match as 丗丙丛
@@ -135,7 +135,7 @@ namespace Terminal.Gui.Core {
 			    "candle"
 			  };
 
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (3, n.GetNextMatchingItem (0, '@'));
 			Assert.Equal (3, n.GetNextMatchingItem (3, 'b'));
 			Assert.Equal (4, n.GetNextMatchingItem (3, 'b'));
@@ -153,7 +153,7 @@ namespace Terminal.Gui.Core {
 			    "candle"
 			  };
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 'b')); // match bat
 			Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 'a')); // match bat
 			Assert.Equal (strings.IndexOf ("bat"), current = n.GetNextMatchingItem (current, 't')); // match bat
@@ -178,7 +178,7 @@ namespace Terminal.Gui.Core {
 			    "appricot"
 			  };
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
 			Assert.Equal ("a", n.SearchString);
 
@@ -221,7 +221,7 @@ namespace Terminal.Gui.Core {
 			    "appricot"
 			  };
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 
 			// No delay
 			Assert.Equal (strings.IndexOf ("appricot"), current = n.GetNextMatchingItem (current, 'a'));
@@ -271,7 +271,7 @@ namespace Terminal.Gui.Core {
 				"cart",
 			};
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", false));
 			Assert.Equal (strings.IndexOf ("$100.00"), current = n.GetNextMatchingItem (current, "$", false));
 			Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", false)); // back to top
@@ -317,7 +317,7 @@ namespace Terminal.Gui.Core {
 				"cart",
 			};
 			int current = 0;
-			var n = new SearchCollectionNavigator (strings);
+			var n = new CollectionNavigator (strings);
 			Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", true));
 			Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$", true));
 			Assert.Equal (strings.IndexOf ("$$"), current = n.GetNextMatchingItem (current, "$$", true)); // back to top