|
|
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Threading.Tasks;
- using UnityEngine;
- using CommonMark;
- using CommonMark.Syntax;
- using UnityEngine.UI;
- using TMPro;
- using CustomUI.BeatSaber;
-
- namespace BSIPA_ModList.UI.ViewControllers
- {
- [RequireComponent(/*typeof(ScrollRect),*/ typeof(RectTransform))]
- public class MarkdownView : MonoBehaviour
- {
- private class TagTypeComponent : MonoBehaviour
- {
- internal BlockTag Tag;
- }
-
- private string markdown = "";
- public string Markdown
- {
- get => markdown;
- set
- {
- markdown = value;
- UpdateMd();
- }
- }
-
- public RectTransform rectTransform => GetComponent<RectTransform>();
-
- //private ScrollRect view;
- private RectTransform content;
-
- private CommonMarkSettings settings;
- public MarkdownView()
- {
- settings = CommonMarkSettings.Default.Clone();
- settings.AdditionalFeatures = CommonMarkAdditionalFeatures.All;
- settings.RenderSoftLineBreaksAsLineBreaks = false;
- settings.UriResolver = ResolveUri;
- }
-
- public Func<string, bool> HasEmbeddedImage;
-
- private string ResolveUri(string arg)
- {
- var name = arg.Substring(3);
- if (!arg.StartsWith("!::") && !arg.StartsWith("w::"))
- { // !:: means embedded, w:: means web
- // this block is for when neither is specified
-
- Logger.md.Debug($"Resolving nonspecific URI {arg}");
- // check if its embedded
- if (HasEmbeddedImage != null && HasEmbeddedImage(arg))
- return "!::" + arg;
- else
- return "w::" + arg;
- }
-
- Logger.md.Debug($"Resolved specific URI {arg}");
- return arg;
- }
-
- protected void Awake()
- {
- content = new GameObject("Content Wrapper").AddComponent<RectTransform>();
- content.SetParent(transform);
- content.localPosition = Vector2.zero;
- content.anchorMin = Vector2.zero;
- content.anchorMax = Vector2.one;
- var contentLayout = content.gameObject.AddComponent<LayoutElement>();
- var contentFitter = content.gameObject.AddComponent<ContentSizeFitter>();
- contentFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize;
- contentFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained;
- contentLayout.preferredWidth = 100f; // to be adjusted
- content.sizeDelta = new Vector2(100f,100f);
- content.gameObject.AddComponent<TagTypeComponent>();
-
- /*view = GetComponent<ScrollRect>();
- view.content = content;
- view.vertical = true;
- view.horizontal = false;
- view.verticalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHide;
- view.horizontalScrollbarVisibility = ScrollRect.ScrollbarVisibility.AutoHide;*/
- }
-
- private void UpdateMd()
- {
- Clear();
-
- var doc = CommonMarkConverter.Parse(markdown, settings);
-
- Stack<RectTransform> layout = new Stack<RectTransform>();
- layout.Push(content);
- TextMeshProUGUI currentText = null;
- foreach (var node in doc.AsEnumerable())
- {
- Logger.md.Debug($"node {node}");
-
- if (node.Block != null)
- {
- var block = node.Block;
-
- void BlockNode(string name, float spacing, bool isVertical, bool isDoc = false)
- {
- var type = isVertical ? typeof(VerticalLayoutGroup) : typeof(HorizontalLayoutGroup);
- if (node.IsOpening)
- {
- Logger.md.Debug($"Creating block container {name}");
-
- currentText = null;
- var go = new GameObject(name, typeof(RectTransform), type);
- var vlayout = go.GetComponent<RectTransform>();
- vlayout.SetParent(layout.Peek());
- //if (isDoc)
- vlayout.anchoredPosition = Vector2.zero;
- go.AddComponent<TagTypeComponent>().Tag = block.Tag;
- layout.Push(vlayout);
-
- if (isVertical)
- {
- var vl = go.GetComponent<VerticalLayoutGroup>();
- vl.childControlHeight = vl.childControlWidth =
- vl.childForceExpandHeight = vl.childForceExpandWidth = false;
- vl.spacing = spacing;
- }
- else
- {
- var hl = go.GetComponent<HorizontalLayoutGroup>();
- hl.childControlHeight = hl.childControlWidth =
- hl.childForceExpandHeight = hl.childForceExpandWidth = false;
- hl.spacing = spacing;
- }
- }
- else if (node.IsClosing)
- {
- currentText = null;
- layout.Pop();
- }
- }
-
- switch (block.Tag)
- {
- case BlockTag.Document:
- BlockNode("DocumentRoot", .2f, true, true);
- break;
- case BlockTag.SetextHeading:
- BlockNode("Heading1", .1f, false);
- break;
- case BlockTag.AtxHeading:
- BlockNode("Heading2", .1f, false);
- break;
- case BlockTag.Paragraph:
- BlockNode("Paragraph", .1f, false);
- break;
- // TODO: add the rest of the tag types
- }
- }
- else if (node.Inline != null)
- { // inline element
- var inl = node.Inline;
-
- switch (inl.Tag)
- {
- case InlineTag.String:
- if (currentText == null)
- {
- Logger.md.Debug($"Adding new text element");
-
- var btt = layout.Peek().gameObject.GetComponent<TagTypeComponent>().Tag;
- currentText = BeatSaberUI.CreateText(layout.Peek(), "", Vector2.zero);
- //var le = currentText.gameObject.AddComponent<LayoutElement>();
-
- switch (btt)
- {
- case BlockTag.List:
- case BlockTag.ListItem:
- case BlockTag.Paragraph:
- currentText.fontSize = 3.5f;
- currentText.enableWordWrapping = true;
- break;
- case BlockTag.AtxHeading:
- currentText.fontSize = 4f;
- currentText.enableWordWrapping = true;
- break;
- case BlockTag.SetextHeading:
- currentText.fontSize = 4.5f;
- currentText.enableWordWrapping = true;
- break;
- // TODO: add other relevant types
- }
- }
- Logger.md.Debug($"Appending '{inl.LiteralContent}' to current element");
- currentText.text += inl.LiteralContent;
- break;
- }
- }
- }
- }
-
- private void Clear()
- {
- foreach (Transform child in content)
- Destroy(child.gameObject);
- }
- }
- }
|