/// <summary> /// Adds VisualElements corresponding to the provided rich text to a root. /// </summary> /// <param name="text">The rich text to parse</param> /// <param name="root">Visual Element to append the rich text UI to, current default if left null</param> /// <returns>A list of all immediate children added to the root.</returns> public List <VisualElement> AddRichText(string text, VisualElement root = null) => RichTextUtility.AddRichText(text, content, content.GetRoot(root));
void DoSearch() { EditorPrefs.SetString(searchEditorPrefsKey, searchString); if (string.IsNullOrEmpty(searchString)) { searchRoot.Clear(); searchRoot.visible = false; return; } string searchStringLower = searchString.ToLower(); //Cache current page string currentPageStateNameCached = currentPageStateName; //Clear the previous search searchRoot.Clear(); searchRoot.visible = true; //searchRootTemp is an un-parented root to append to so we can search content. VisualElement searchRootTemp = new VisualElement(); Dictionary <IDocumentationPage <T>, List <string> > searchResults = new Dictionary <IDocumentationPage <T>, List <string> >(); StringBuilder stringBuilder = new StringBuilder(); foreach (IDocumentationPage <T> page in pages.Values) { if (!searchStringsCache.TryGetValue(page, out var searchStrings)) { searchStrings = new List <string>(); searchStringsCache.Add(page, searchStrings); //Load the page under searchRootTemp LoadPage(page, searchRootTemp); //Get all the paragraph element's text (combined together) searchRootTemp.Query(null, "paragraph-container").ForEach(paragraph => { stringBuilder.Clear(); paragraph.Query <TextElement>().Build().ForEach(tE => stringBuilder.Append(tE.text)); searchStrings.Add(stringBuilder.ToString()); paragraph.Clear(); }); //Get all the text from the Text elements under searchRootTemp searchRootTemp.Query <TextElement>().ForEach(element => searchStrings.Add(element.text)); } foreach (string searchStringLocal in searchStrings) { string searchStringLocalLower = searchStringLocal.ToLower(); if (searchStringLocalLower.Contains(searchStringLower)) { if (!searchResults.TryGetValue(page, out var resultStrings)) { searchResults.Add(page, resultStrings = new List <string>()); } resultStrings.Add(searchStringLocal); } } } List <string> matches = new List <string>(maxMatchCount); foreach (var result in searchResults) { //Result Container VisualElement resultContainer = new VisualElement(); resultContainer.ClearClassList(); resultContainer.AddToClassList("result-container"); resultContainer.userData = result.Key; searchRoot.Add(resultContainer); //Result Button Button searchResultButton = window.AddFullWidthButton(result.Key.GetType(), resultContainer); searchResultButton.AddToClassList("result"); Label resultLabel = RichTextUtility.AddInlineText(searchResultButton.text, searchResultButton); resultLabel.style.color = searchResultButton.style.color; searchResultButton.text = null; Label resultCountLabel = RichTextUtility.AddInlineText($" ({result.Value.Count} Results)", searchResultButton); resultCountLabel.style.color = new Color(1, 1, 1, 0.35f); searchResultButton.RegisterCallback <MouseUpEvent>(evt => searchRoot.visible = false); //Results (the string matches) VisualElement resultMatchesContainer = new VisualElement(); resultMatchesContainer.ClearClassList(); resultMatchesContainer.AddToClassList("result-matches-container"); resultContainer.Add(resultMatchesContainer); //Create a hashset of all the matched words. matches.Clear(); foreach (string m in result.Value) { string match = Regex.Match(m, $@"\w*{searchStringLower}\w*", RegexOptions.IgnoreCase).Value; if (matches.Contains(match)) { continue; } matches.Add(match); if (matches.Count > maxMatchCount) { break; } } //Add a max of maxMatchCount inline text to the resultMatchesContainer int l = Mathf.Min(maxMatchCount, matches.Count); for (int i = 0; i < l; i++) { //Create group for matched coloured text. VisualElement inlineTextGroup = new VisualElement(); inlineTextGroup.ClearClassList(); inlineTextGroup.AddToClassList("inline-text-group"); inlineTextGroup.AddToClassList("result"); resultMatchesContainer.Add(inlineTextGroup); int indexOf = matches[i].IndexOf(searchStringLower, StringComparison.OrdinalIgnoreCase); Label matchContent; if (matches[i].Length == searchStringLower.Length) { matchContent = RichTextUtility.AddInlineText(matches[i], inlineTextGroup); } else if (indexOf == 0) { matchContent = RichTextUtility.AddInlineText(matches[i].Substring(indexOf, searchStringLower.Length), inlineTextGroup); RichTextUtility.AddInlineText(matches[i].Substring(searchStringLower.Length), inlineTextGroup); } else if (indexOf == matches[i].Length - searchStringLower.Length) { RichTextUtility.AddInlineText(matches[i].Substring(0, indexOf), inlineTextGroup); matchContent = RichTextUtility.AddInlineText(matches[i].Substring(indexOf), inlineTextGroup); } else { RichTextUtility.AddInlineText(matches[i].Substring(0, indexOf), inlineTextGroup); matchContent = RichTextUtility.AddInlineText(matches[i].Substring(indexOf, searchStringLower.Length), inlineTextGroup); RichTextUtility.AddInlineText(matches[i].Substring(indexOf + searchStringLower.Length), inlineTextGroup); } matchContent.style.color = new Color(1, 0.5f, 0); } if (l != matches.Count) { RichTextUtility.AddInlineText("...", resultMatchesContainer); } } searchRoot.Sort((a, b) => searchResults[(IDocumentationPage <T>)b.userData].Count.CompareTo(searchResults[(IDocumentationPage <T>)a.userData].Count)); //Reset page currentPageStateName = currentPageStateNameCached; }