public static void CreateFromTemplateAndStyle(VisualElement container, string templateName) { VisualTreeAsset template = AssetDatabase.LoadAssetAtPath <VisualTreeAsset>(templatePath + templateName + ".uxml"); template.CloneTree(container); container.styleSheets.Add(AssetDatabase.LoadAssetAtPath <StyleSheet>(templatePath + templateName + ".uss")); container.ClearClassList(); container.AddToClassList(templateName); }
public GroupNode() { m_ContentItem = new GroupNodeDropArea(); m_ContentItem.ClearClassList(); m_ContentItem.AddToClassList("content"); var visualTree = EditorGUIUtility.Load("UXML/GraphView/GroupNode.uxml") as VisualTreeAsset; m_MainContainer = visualTree.CloneTree(null); m_MainContainer.AddToClassList("mainContainer"); m_HeaderItem = m_MainContainer.Q(name: "header"); m_HeaderItem.AddToClassList("header"); m_TitleItem = m_MainContainer.Q <Label>(name: "titleLabel"); m_TitleItem.AddToClassList("label"); m_TitleEditor = m_MainContainer.Q(name: "titleField") as TextField; m_TitleEditor.AddToClassList("textfield"); m_TitleEditor.visible = false; m_TitleEditor.RegisterCallback <FocusOutEvent>(e => { OnEditTitleFinished(); }); m_TitleEditor.RegisterCallback <KeyDownEvent>(OnKeyPressed); VisualElement contentPlaceholder = m_MainContainer.Q(name: "contentPlaceholder"); contentPlaceholder.Add(m_ContentItem); Add(m_MainContainer); ClearClassList(); AddToClassList("groupNode"); clippingOptions = ClippingOptions.ClipAndCacheContents; capabilities |= Capabilities.Selectable | Capabilities.Movable | Capabilities.Deletable; m_HeaderItem.RegisterCallback <PostLayoutEvent>(OnHeaderSizeChanged); RegisterCallback <PostLayoutEvent>(e => { MoveElements(); }); RegisterCallback <MouseDownEvent>(OnMouseUpEvent); this.schedule.Execute(e => { if (visible && (m_Initialized == false)) { m_Initialized = true; UpdateGeometryFromContent(); } }); }
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; }
public DocumentationContent(VisualElement root, T window, string stateEditorPrefsKey = null) { this.window = window; this.stateEditorPrefsKey = stateEditorPrefsKey; searchEditorPrefsKey = $"{stateEditorPrefsKey}_Search"; if (EditorPrefs.HasKey(searchEditorPrefsKey)) { searchString = EditorPrefs.GetString(searchEditorPrefsKey); } searchFieldName = $"SearchField_{window.GetType().FullName}"; #if !UIELEMENT_BROWSER_BAR IMGUIContainer browserBar = new IMGUIContainer(BrowserBar) { style = { height = 18 } }; root.Add(browserBar); #else BrowserBar(root); #endif /* The content root contains the styles. * It also contains the scroll view and search root, with the scroll view containing the user-generated content */ VisualElement content = new VisualElement { style = { flexGrow = 1 } }; root.Add(content); StyleSheet docsStyleSheet = LoadAssetOfType <StyleSheet>("nDocumentationStyles", SearchFilter.Packages); content.styleSheets.Add(docsStyleSheet); StyleSheet codeStyleSheet = LoadAssetOfType <StyleSheet>("CsharpHighlightingStyles", SearchFilter.Packages); content.styleSheets.Add(codeStyleSheet); ScrollView scrollView = new ScrollView { name = "Scroll View", showHorizontal = false, showVertical = true }; scrollView.AddToClassList("scroll-view"); scrollView.contentContainer.AddToClassList("scroll-view-content"); contentRoot = scrollView.contentContainer; content.Add(scrollView); searchRoot = new VisualElement(); searchRoot.ClearClassList(); searchRoot.AddToClassList("search-container"); content.Add(searchRoot); searchRoot.StretchToParentSize(); searchRoot.visible = false; SetCurrentDefaultRoot(contentRoot); }
public static List <VisualElement> AddRichText(string text, IButtonRegistry buttonRegistry, VisualElement root, bool isInsideCodeBlock) { List <VisualElement> results = new List <VisualElement>(); IEnumerable <RichText> richTexts = ParseRichText(text, isInsideCodeBlock); //Parse rich texts to create paragraphs. List <List <RichText> > paragraphs = new List <List <RichText> > { new List <RichText>() }; foreach (RichText richText in richTexts) { if (richText.richTextTag.tag == RichTextTag.Tag.button || richText.richTextTag.tag == RichTextTag.Tag.code) { paragraphs[paragraphs.Count - 1].Add(richText); continue; } string[] strings = richText.associatedText.Split('\n'); for (int i = 0; i < strings.Length; i++) { if (i != 0) { paragraphs.Add(new List <RichText>()); } //Split paragraph content (already split by tag) into individual words string[] wordSplit = Regex.Split(strings[i], @"(?<=[ -])"); //Split but keep delimiters attached. foreach (var word in wordSplit) { if (!string.IsNullOrEmpty(word)) { paragraphs[paragraphs.Count - 1].Add(new RichText(richText.richTextTag, word)); } } } } foreach (List <RichText> paragraph in paragraphs) { //Add all the paragraphs VisualElement rootTemp = root; root = AddParagraphContainer(root); for (int i = 0; i < paragraph.Count; i++) { RichText word = paragraph[i]; if (i < paragraph.Count - 1) { //If there are more words RichText nextWord = paragraph[i + 1]; string nextText = nextWord.associatedText; if (Regex.IsMatch(nextText, "^[^a-zA-Z] ?")) { VisualElement inlineGroup = new VisualElement(); root.Add(inlineGroup); inlineGroup.AddToClassList("inline-text-group"); AddRichTextInternal(word, inlineGroup); AddRichTextInternal(nextWord, inlineGroup); ++i; continue; } } AddRichTextInternal(word, root); //Add all the words and style them. void AddRichTextInternal(RichText richText, VisualElement rootToAddTo) { RichTextTag tag = richText.richTextTag; TextElement inlineText = null; switch (tag.tag) { case RichTextTag.Tag.none: inlineText = AddInlineText(richText.associatedText, rootToAddTo); break; case RichTextTag.Tag.button: if (buttonRegistry == null) { Debug.LogWarning("There was no ButtonRegistry provided to AddRichText. Button tags will not function."); inlineText = AddInlineButton(() => Debug.LogWarning("There was no ButtonRegistry provided to AddRichText. Button tags will not function."), richText.associatedText, rootToAddTo); break; } if (!buttonRegistry.GetRegisteredButtonAction(tag.stringVariables, out Action action)) { return; } inlineText = AddInlineButton(action, richText.associatedText, rootToAddTo); break; case RichTextTag.Tag.code: //Scroll ScrollView codeScroll = new ScrollView(ScrollViewMode.Horizontal); VisualElement contentContainer = codeScroll.contentContainer; codeScroll.contentViewport.style.flexDirection = FlexDirection.Column; codeScroll.contentViewport.style.alignItems = Align.Stretch; codeScroll.AddToClassList("code-scroll"); root.Add(codeScroll); contentContainer.ClearClassList(); contentContainer.AddToClassList("code-container"); VisualElement codeContainer = contentContainer; CSharpHighlighter highlighter = new CSharpHighlighter { AddStyleDefinition = false }; // To add code, we first use the CSharpHighlighter to construct rich text for us. string highlit = highlighter.Highlight(richText.associatedText); // After constructing new rich text we pass the text back recursively through this function with the new parent. AddRichText(highlit, buttonRegistry, codeContainer, true); // only parse spans because this is all the CSharpHighlighter parses. //Finalise content container foreach (VisualElement child in codeContainer.Children()) { if (child.ClassListContains(paragraphContainerClass)) { child.AddToClassList("code"); if (child.childCount == 1) { AddInlineText("", child); //This seems to be required to get layout to function properly. } } } //Begin Hack FieldInfo m_inheritedStyle = typeof(VisualElement).GetField("inheritedStyle", BindingFlags.NonPublic | BindingFlags.Instance); if (m_inheritedStyle == null) { m_inheritedStyle = typeof(VisualElement).GetField("m_InheritedStylesData", BindingFlags.NonPublic | BindingFlags.Instance); } Type inheritedStylesData = Type.GetType("UnityEngine.UIElements.StyleSheets.InheritedStylesData,UnityEngine"); FieldInfo font = inheritedStylesData.GetField("font", BindingFlags.Public | BindingFlags.Instance); FieldInfo fontSize = inheritedStylesData.GetField("fontSize", BindingFlags.Public | BindingFlags.Instance); Font consola = (Font)EditorGUIUtility.Load("consola"); contentContainer.Query <Label>().ForEach(l => { l.AddToClassList("code"); //Hack to regenerate the font size as Rich Text tags are removed from the original calculation. object value = m_inheritedStyle.GetValue(l); StyleFont fontVar = (StyleFont)font.GetValue(value); fontVar.value = consola; font.SetValue(value, fontVar); StyleLength fontSizeVar = 12; // = (StyleLength) fontSize.GetValue(value); //This doesn't seem to work properly, hard coded for now. fontSize.SetValue(value, fontSizeVar); m_inheritedStyle.SetValue(l, value); Vector2 measuredTextSize = l.MeasureTextSize(l.text.Replace('>', ' '), 0, VisualElement.MeasureMode.Undefined, 0, VisualElement.MeasureMode.Undefined); l.style.width = measuredTextSize.x; l.style.height = measuredTextSize.y; }); //Button Button codeCopyButtonButtonContainer = new Button(() => { EditorGUIUtility.systemCopyBuffer = richText.associatedText; Debug.Log("Copied Code to Clipboard"); }); codeCopyButtonButtonContainer.ClearClassList(); codeCopyButtonButtonContainer.AddToClassList("code-button"); codeCopyButtonButtonContainer.StretchToParentSize(); codeContainer.Add(codeCopyButtonButtonContainer); break; case RichTextTag.Tag.span: Label spanLabel = new Label { text = richText.associatedText }; spanLabel.AddToClassList(tag.stringVariables); rootToAddTo.Add(spanLabel); break; case RichTextTag.Tag.image: throw new NotImplementedException(); default: throw new ArgumentOutOfRangeException(); } if (inlineText != null) { inlineText.style.unityFontStyleAndWeight = tag.fontStyle; if (tag.size > 0) { inlineText.style.fontSize = tag.size; } if (tag.color != Color.clear) { inlineText.style.color = tag.color; } results.Add(inlineText); } } } root = rootTemp; } return(results); /*void RichTextDebug(string richText) => Debug.Log(GetRichTextCapableText(richText)); * string GetRichTextCapableText(string richText) => text.Replace("<", "<<b></b>");*/ }