diff --git a/BSIPA-ModList/BSIPA-ModList.csproj b/BSIPA-ModList/BSIPA-ModList.csproj index 2cffd2e6..8ba37248 100644 --- a/BSIPA-ModList/BSIPA-ModList.csproj +++ b/BSIPA-ModList/BSIPA-ModList.csproj @@ -78,6 +78,7 @@ + @@ -94,6 +95,9 @@ + + 0.15.1 + 1.2.0 @@ -112,12 +116,15 @@ - + + + + \ No newline at end of file diff --git a/BSIPA-ModList/Plugin.cs b/BSIPA-ModList/Plugin.cs index 51ee7c63..d9e7c24e 100644 --- a/BSIPA-ModList/Plugin.cs +++ b/BSIPA-ModList/Plugin.cs @@ -3,12 +3,15 @@ using UnityEngine.SceneManagement; using IPALogger = IPA.Logging.Logger; using BSIPA_ModList.UI; using UnityEngine; +using IPA.Logging; namespace BSIPA_ModList { internal static class Logger { internal static IPALogger log { get; set; } + + internal static IPALogger md => log.GetChildLogger("MarkDown"); } public class Plugin : IBeatSaberPlugin diff --git a/BSIPA-ModList/PostBuild.msbuild b/BSIPA-ModList/PostBuild.msbuild new file mode 100644 index 00000000..d2016a49 --- /dev/null +++ b/BSIPA-ModList/PostBuild.msbuild @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/BSIPA-ModList/UI/ViewControllers/MarkdownView.cs b/BSIPA-ModList/UI/ViewControllers/MarkdownView.cs new file mode 100644 index 00000000..8b27e520 --- /dev/null +++ b/BSIPA-ModList/UI/ViewControllers/MarkdownView.cs @@ -0,0 +1,204 @@ +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(); + + //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 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 + + // check if its embedded + if (HasEmbeddedImage != null && HasEmbeddedImage(arg)) + return "!::" + arg; + else + return "w::" + arg; + } + + return arg; + } + + protected void Awake() + { + content = new GameObject("Content Wrapper").AddComponent(); + content.SetParent(transform); + var contentLayout = content.gameObject.AddComponent(); + var contentFitter = content.gameObject.AddComponent(); + contentFitter.horizontalFit = ContentSizeFitter.FitMode.PreferredSize; + contentFitter.verticalFit = ContentSizeFitter.FitMode.Unconstrained; + contentLayout.preferredWidth = 100f; // to be adjusted + content.sizeDelta = new Vector2(100f,100f); + + /*view = GetComponent(); + 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 layout = new Stack(); + 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) + { + 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(); + vlayout.SetParent(layout.Peek()); + go.AddComponent().Tag = block.Tag; + layout.Push(vlayout); + + if (isVertical) + { + var vl = go.GetComponent(); + vl.childControlHeight = vl.childControlWidth = + vl.childForceExpandHeight = vl.childForceExpandWidth = false; + vl.spacing = spacing; + } + else + { + var hl = go.GetComponent(); + 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", 10f, 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().Tag; + currentText = BeatSaberUI.CreateText(layout.Peek(), "", Vector2.zero); + //var le = currentText.gameObject.AddComponent(); + + 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); + } + } +} diff --git a/BSIPA-ModList/UI/ViewControllers/ModInfoViewController.cs b/BSIPA-ModList/UI/ViewControllers/ModInfoViewController.cs index e7f764d4..1ba01716 100644 --- a/BSIPA-ModList/UI/ViewControllers/ModInfoViewController.cs +++ b/BSIPA-ModList/UI/ViewControllers/ModInfoViewController.cs @@ -1,4 +1,5 @@ -using CustomUI.BeatSaber; +using BSIPA_ModList.UI.ViewControllers; +using CustomUI.BeatSaber; using CustomUI.MenuButton; using IPA.Loader; using IPA.Updating.BeatMods; @@ -182,10 +183,17 @@ namespace BSIPA_ModList.UI titleText.fontSize = 6f; authorText = BeatSaberUI.CreateText(rectTransform, controller.Author, new Vector2(11f, 22f)); authorText.fontSize = 4.5f; + + /* descText = BeatSaberUI.CreateText(rectTransform, controller.Description, new Vector2(-4.5f, 12f)); descText.fontSize = 3.5f; descText.enableWordWrapping = true; - descText.overflowMode = TextOverflowModes.ScrollRect; + descText.overflowMode = TextOverflowModes.ScrollRect;*/ + + var mdv = new GameObject("MarkDown Desc").AddComponent(); + mdv.rectTransform.anchoredPosition = new Vector2(-4.5f, 12f); + mdv.rectTransform.SetParent(rectTransform); + mdv.Markdown = controller.Description; icon = new GameObject("Mod Info View Icon", typeof(RectTransform)).AddComponent(); icon.gameObject.SetActive(false); @@ -328,5 +336,5 @@ namespace BSIPA_ModList.UI #endif } #endif - } + } } diff --git a/BSIPA-ModList/manifest.json b/BSIPA-ModList/manifest.json index 689ae4c7..c1a1f3ef 100644 --- a/BSIPA-ModList/manifest.json +++ b/BSIPA-ModList/manifest.json @@ -1,7 +1,13 @@ { "$schema": "https://raw.githubusercontent.com/nike4613/ModSaber-MetadataFileSchema/master/Schema.json", "author": "DaNike", - "description": "An in-game interface client for BSIPA.", + "description": [ + "## An in-game interface client for BSIPA.", + "", + "***", + "", + "Look, ma! Markdown! Its **[CommonMark](w::https://commonmark.org/)**!" + ], "gameVersion": "0.13.2", "id": "BSIPA Mod List", "name": "BSIPA Mod List",