diff --git a/src/main/resources/assets/opencomputers/doc/img/button_power.png b/src/main/resources/assets/opencomputers/doc/img/button_power.png new file mode 100644 index 000000000..ed1d38056 Binary files /dev/null and b/src/main/resources/assets/opencomputers/doc/img/button_power.png differ diff --git a/src/main/resources/assets/opencomputers/doc/index.md b/src/main/resources/assets/opencomputers/doc/index.md new file mode 100644 index 000000000..8e3d7e823 --- /dev/null +++ b/src/main/resources/assets/opencomputers/doc/index.md @@ -0,0 +1,22 @@ +# Headline with more lines [with link](huehue) and *some* more + +This is some test text for the subset of Markdown supported by the planned ingame documentation system for OpenComputers. +![opencomputers:transistor](../../textures/gui/button_power.png) +*This* is *italic* text, ~~strikethrough~~ maybe abc-ter **some** text **in bold**. Is _this underlined_? Oh, no, _it's also italic!_ Well, this \*isn't bold*. + +## Smaller headline [also with *link* but this __one__ longer](huehue) + +This is *italic +over two* lines. But *this ... no *this is* **_bold italic_** *text*. + +### even smaller + +*not italic *because ** why would it be*eh + +isn't*. + + # not a header + +asdasd ![Hello There!](button_power.png) qweqwe + +And finally, [this is a link!](https://avatars1.githubusercontent.com/u/514903). diff --git a/src/main/scala/li/cil/oc/client/gui/Manual.scala b/src/main/scala/li/cil/oc/client/gui/Manual.scala index 87d8c6a62..1ed9cbcee 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -2,15 +2,19 @@ package li.cil.oc.client.gui import java.util +import com.google.common.base.Charsets +import li.cil.oc.Settings import li.cil.oc.client.Textures import li.cil.oc.util.PseudoMarkdown import net.minecraft.client.Minecraft import net.minecraft.client.gui.Gui import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.ScaledResolution +import net.minecraft.util.ResourceLocation import org.lwjgl.input.Mouse import scala.collection.convert.WrapAsJava._ +import scala.io.Source class Manual extends GuiScreen { var guiLeft = 0 @@ -18,6 +22,7 @@ class Manual extends GuiScreen { var xSize = 0 var ySize = 0 var offset = 0 + var document = Iterable.empty[PseudoMarkdown.Segment] var documentHeight = 0 final val documentMaxWidth = 230 final val documentMaxHeight = 176 @@ -25,33 +30,16 @@ class Manual extends GuiScreen { final val scrollPosY = 6 final val scrollHeight = 180 - val document = PseudoMarkdown.parse( """# Headline with more lines [with link](huehue) and *some* more - | - |This is some test text for the subset of Markdown supported by the planned ingame documentation system for OpenComputers. - | - |*This* is *italic* text, ~~strikethrough~~ maybe abc-ter **some** text **in bold**. Is _this underlined_? Oh, no, _it's also italic!_ Well, this \*isn't bold*. - | - |## Smaller headline [also with *link* but this __one__ longer](huehue) - | - |This is *italic - |over two* lines. But *this ... no *this is* **_bold italic_** *text*. - | - |### even smaller - | - |*not italic *because ** why would it be*eh - | - |isn't*. - | - | # not a header - | - |![](https://avatars1.githubusercontent.com/u/514903) - | - |And finally, [this is a link!](https://avatars1.githubusercontent.com/u/514903).""".stripMargin) - def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) protected var scrollButton: ImageButton = _ + def loadPage(location: ResourceLocation): Iterator[String] = { + val resource = Minecraft.getMinecraft.getResourceManager.getResource(location) + val is = resource.getInputStream + Source.fromInputStream(is)(Charsets.UTF_8).getLines() + } + override def doesGuiPauseGame = false override def initGui(): Unit = { @@ -66,6 +54,7 @@ class Manual extends GuiScreen { xSize = guiSize.getScaledWidth ySize = guiSize.getScaledHeight offset = 0 + document = PseudoMarkdown.parse(loadPage(new ResourceLocation(Settings.resourceDomain, "doc/index.md"))) documentHeight = PseudoMarkdown.height(document, documentMaxWidth, fontRendererObj) scrollButton = new ImageButton(1, guiLeft + scrollPosX, guiTop + scrollPosY, 6, 13, Textures.guiButtonScroll) @@ -80,7 +69,7 @@ class Manual extends GuiScreen { PseudoMarkdown.render(document, guiLeft + 8, guiTop + 8, documentMaxWidth, documentMaxHeight, offset, fontRendererObj, mouseX, mouseY) match { case Some(segment) => segment.tooltip match { - case Some(text) => drawHoveringText(seqAsJavaList(text.lines.toSeq), mouseX, mouseY, fontRendererObj) + case Some(text) if text.nonEmpty => drawHoveringText(seqAsJavaList(text.lines.toSeq), mouseX, mouseY, fontRendererObj) case _ => } case _ => diff --git a/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala b/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala index 05c226d19..24aa4a260 100644 --- a/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala +++ b/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala @@ -1,7 +1,16 @@ package li.cil.oc.util +import java.io.InputStream +import javax.imageio.ImageIO + +import li.cil.oc.Settings +import net.minecraft.client.Minecraft import net.minecraft.client.gui.FontRenderer +import net.minecraft.client.renderer.texture.AbstractTexture +import net.minecraft.client.renderer.texture.TextureUtil +import net.minecraft.client.resources.IResourceManager import net.minecraft.util.EnumChatFormatting +import net.minecraft.util.ResourceLocation import org.lwjgl.opengl.GL11 import scala.annotation.tailrec @@ -16,8 +25,8 @@ object PseudoMarkdown { /** * Parses a plain text document into a list of segments. */ - def parse(document: String): Iterable[Segment] = { - var segments = document.lines.flatMap(line => Iterable(new TextSegment(null, line), new NewLineSegment())).toArray + def parse(document: Iterator[String]): Iterable[Segment] = { + var segments = document.flatMap(line => Iterable(new TextSegment(null, line), new NewLineSegment())).toArray for ((pattern, factory) <- segmentTypes) { segments = segments.flatMap(_.refine(pattern, factory)) } @@ -118,7 +127,7 @@ object PseudoMarkdown { def link: Option[String] = None - private[PseudoMarkdown] def notifyHover(): Unit + private[PseudoMarkdown] def notifyHover(): Unit = {} private[PseudoMarkdown] def checkHovered(mouseX: Int, mouseY: Int, x: Int, y: Int, w: Int, h: Int): Option[InteractiveSegment] = if (mouseX >= x && mouseY >= y && mouseX <= x + w && mouseY <= y + h) Some(this) else None } @@ -306,7 +315,45 @@ object PseudoMarkdown { override def toString: String = s"{StrikethroughSegment: text = $text}" } - private class ImageSegment(val parent: Segment, val tooltip: String, val url: String) extends Segment { + private class ImageSegment(val parent: Segment, val title: String, val url: String) extends InteractiveSegment { + val path = if (url.startsWith("/")) url else "doc/img/" + url + val location = new ResourceLocation(Settings.resourceDomain, path) + val texture = { + val manager = Minecraft.getMinecraft.getTextureManager + manager.getTexture(location) match { + case image: ImageTexture => image + case _ => + val image = new ImageTexture(location) + manager.loadTexture(location, image) + image + } + } + + override def tooltip: Option[String] = Option(title) + + override def height(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = math.max(lineHeight(renderer), texture.height + 10 - lineHeight(renderer)) + + override def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = maxWidth + + override def render(x: Int, y: Int, indent: Int, maxWidth: Int, maxHeight: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = { + Minecraft.getMinecraft.getTextureManager.bindTexture(location) + val xOffset = (maxWidth - texture.width) / 2 + val yOffset = 4 + (if (indent > 0) lineHeight(renderer) else 0) + GL11.glColor4f(1, 1, 1, 1) + GL11.glBegin(GL11.GL_QUADS) + GL11.glTexCoord2f(0, 0) + GL11.glVertex2f(x + xOffset, y + yOffset) + GL11.glTexCoord2f(0, 1) + GL11.glVertex2f(x + xOffset, y + yOffset + texture.height) + GL11.glTexCoord2f(1, 1) + GL11.glVertex2f(x + xOffset + texture.width, y + yOffset + texture.height) + GL11.glTexCoord2f(1, 0) + GL11.glVertex2f(x + xOffset + texture.width, y + yOffset) + GL11.glEnd() + + checkHovered(mouseX, mouseY, x + xOffset, y + yOffset, texture.width, texture.height) + } + override def toString: String = s"{ImageSegment: tooltip = $tooltip, url = $url}" } @@ -321,6 +368,28 @@ object PseudoMarkdown { override def toString: String = s"{NewLineSegment}" } + private class ImageTexture(val location: ResourceLocation) extends AbstractTexture { + var width = 0 + var height = 0 + + override def loadTexture(manager: IResourceManager): Unit = { + deleteGlTexture() + + var is: InputStream = null + try { + val resource = manager.getResource(location) + is = resource.getInputStream + val bi = ImageIO.read(is) + TextureUtil.uploadTextureImageAllocate(getGlTextureId, bi, false, false) + width = bi.getWidth + height = bi.getHeight + } + finally { + Option(is).foreach(_.close()) + } + } + } + // ----------------------------------------------------------------------- // private def HeaderSegment(s: Segment, m: Regex.Match) = new HeaderSegment(s, m.group(2), m.group(1).length)