From bef070b9ffcdb462b54294e8bfd6c9aa261ade08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Florian=20N=C3=BCcke?= Date: Wed, 8 Apr 2015 02:16:54 +0200 Subject: [PATCH] Basic scrolling logic. --- .../scala/li/cil/oc/client/gui/Manual.scala | 67 ++++++++++++++++-- .../scala/li/cil/oc/util/PseudoMarkdown.scala | 68 +++++++++++++------ 2 files changed, 111 insertions(+), 24 deletions(-) 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 d14faea12..4f44ae4d1 100644 --- a/src/main/scala/li/cil/oc/client/gui/Manual.scala +++ b/src/main/scala/li/cil/oc/client/gui/Manual.scala @@ -1,14 +1,29 @@ package li.cil.oc.client.gui +import java.util + 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 org.lwjgl.input.Mouse class Manual extends GuiScreen { - val document = PseudoMarkdown.parse( """# Headline + var guiLeft = 0 + var guiTop = 0 + var xSize = 0 + var ySize = 0 + var offset = 0 + var documentHeight = 0 + final val documentMaxWidth = 230 + final val documentMaxHeight = 176 + final val scrollPosX = 244 + final val scrollPosY = 6 + final val scrollHeight = 180 + + val document = PseudoMarkdown.parse( """# Headline with more lines | |The Adapter block is the core of most of OpenComputers' mod integration. | @@ -31,18 +46,60 @@ class Manual extends GuiScreen { | |And finally, [this is a link!](https://avatars1.githubusercontent.com/u/514903).""".stripMargin) - override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { + def add[T](list: util.List[T], value: Any) = list.add(value.asInstanceOf[T]) + + protected var scrollButton: ImageButton = _ + + override def doesGuiPauseGame = false + + override def initGui(): Unit = { + super.initGui() + val mc = Minecraft.getMinecraft val screenSize = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight) val guiSize = new ScaledResolution(mc, 256, 192) val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) - val (left, top) = (midX - guiSize.getScaledWidth / 2, midY - guiSize.getScaledHeight / 2) + guiLeft = midX - guiSize.getScaledWidth / 2 + guiTop = midY - guiSize.getScaledHeight / 2 + xSize = guiSize.getScaledWidth + ySize = guiSize.getScaledHeight + offset = 0 + documentHeight = PseudoMarkdown.height(document, documentMaxWidth, fontRendererObj) + scrollButton = new ImageButton(1, guiLeft + scrollPosX, guiTop + scrollPosY, 6, 13, Textures.guiButtonScroll) + add(buttonList, scrollButton) + } + + override def drawScreen(mouseX: Int, mouseY: Int, dt: Float): Unit = { mc.renderEngine.bindTexture(Textures.guiManual) - Gui.func_146110_a(left, top, 0, 0, guiSize.getScaledWidth, guiSize.getScaledHeight, 256, 192) + Gui.func_146110_a(guiLeft, guiTop, 0, 0, xSize, ySize, 256, 192) super.drawScreen(mouseX, mouseY, dt) - PseudoMarkdown.render(document, left + 8, top + 8, 220, 176, 0, fontRendererObj) + PseudoMarkdown.render(document, guiLeft + 8, guiTop + 8, documentMaxWidth, documentMaxHeight, offset, fontRendererObj) + } + + override def handleMouseInput(): Unit = { + super.handleMouseInput() + if (Mouse.hasWheel && Mouse.getEventDWheel != 0) { + if (math.signum(Mouse.getEventDWheel) < 0) scrollDown() + else scrollUp() + } + } + + private def scrollUp() = scrollTo(offset - PseudoMarkdown.lineHeight(fontRendererObj) * 3) + + private def scrollDown() = scrollTo(offset + PseudoMarkdown.lineHeight(fontRendererObj) * 3) + + private def scrollTo(row: Int): Unit = { + val maxOffset = documentHeight - documentMaxHeight + offset = math.max(0, math.min(maxOffset, row)) + val yMin = guiTop + scrollPosY + if (maxOffset > 0) { + scrollButton.yPosition = yMin + (scrollHeight - 13) * offset / maxOffset + } + else { + scrollButton.yPosition = yMin + } } } diff --git a/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala b/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala index d42b46096..41d7acbd1 100644 --- a/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala +++ b/src/main/scala/li/cil/oc/util/PseudoMarkdown.scala @@ -27,20 +27,50 @@ object PseudoMarkdown { * Renders a list of segments and tooltips if a segment with a tooltip is hovered. * Returns a link address if a link is hovered. */ - def render(document: Iterable[Segment], x: Int, y: Int, maxWidth: Int, height: Int, offset: Int, renderer: FontRenderer): Option[String] = { + def render(document: Iterable[Segment], x: Int, y: Int, maxWidth: Int, maxHeight: Int, yOffset: Int, renderer: FontRenderer): Option[String] = { + GL11.glPushMatrix() + GL11.glTranslatef(0, 0, 1) + GL11.glDepthMask(true) + GL11.glColor4f(0.01f, 0.01f, 0.01f, 1) + GL11.glBegin(GL11.GL_QUADS) + GL11.glVertex2f(x - 1, y - 1) + GL11.glVertex2f(x - 1, y + 1 + maxHeight) + GL11.glVertex2f(x + 1 + maxWidth, y + 1 + maxHeight) + GL11.glVertex2f(x + 1 + maxWidth, y - 1) + GL11.glEnd() + + GL11.glDepthMask(false) + GL11.glDepthFunc(GL11.GL_EQUAL) + var currentX = 0 var currentY = 0 for (segment <- document) { - if (currentY >= offset) { - segment.render(x, y + currentY, currentX, maxWidth, renderer) - } - currentY += segment.height(currentX, maxWidth, renderer) - renderer.FONT_HEIGHT + segment.render(x, y + currentY - yOffset, currentX, maxWidth, maxHeight - (currentY - yOffset), renderer) + currentY += segment.height(currentX, maxWidth, renderer) - lineHeight(renderer) currentX = segment.width(currentX, maxWidth, renderer) } + GL11.glDepthFunc(GL11.GL_LEQUAL) + GL11.glPopMatrix() + None } + /** + * Compute the overall height of a document, for computation of scroll offsets. + */ + def height(document: Iterable[Segment], maxWidth: Int, renderer: FontRenderer): Int = { + var currentX = 0 + var currentY = 0 + for (segment <- document) { + currentY += segment.height(currentX, maxWidth, renderer) - lineHeight(renderer) + currentX = segment.width(currentX, maxWidth, renderer) + } + currentY + } + + def lineHeight(renderer: FontRenderer): Int = renderer.FONT_HEIGHT + 1 + // ----------------------------------------------------------------------- // trait Segment { @@ -66,7 +96,7 @@ object PseudoMarkdown { */ def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = 0 - def render(x: Int, y: Int, indent: Int, width: Int, renderer: FontRenderer): Unit = {} + def render(x: Int, y: Int, indent: Int, maxWidth: Int, maxHeight: Int, renderer: FontRenderer): Unit = {} } // ----------------------------------------------------------------------- // @@ -107,7 +137,7 @@ object PseudoMarkdown { chars = chars.drop(lineChars) lineChars = maxChars(chars, maxWidth, renderer) } - lines * renderer.FONT_HEIGHT + lines * lineHeight(renderer) } override def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = { @@ -122,15 +152,15 @@ object PseudoMarkdown { currentX + renderer.getStringWidth(fullFormat + chars) } - override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer): Unit = { + override def render(x: Int, y: Int, indent: Int, maxWidth: Int, maxHeight: Int, renderer: FontRenderer): Unit = { var currentX = x + indent var currentY = y var chars = text var numChars = maxChars(chars, maxWidth - indent, renderer) - while (chars.length > 0) { - renderer.drawString(fullFormat + chars.take(numChars), currentX, currentY, 0xFFFFFF) + while (chars.length > 0 && (currentY - y) < maxHeight) { + renderer.drawString(fullFormat + chars.take(numChars), currentX, currentY, 0xFAFAFA) currentX = x - currentY += renderer.FONT_HEIGHT + currentY += lineHeight(renderer) chars = chars.drop(numChars) numChars = maxChars(chars, maxWidth, renderer) } @@ -147,13 +177,13 @@ object PseudoMarkdown { private def maxChars(s: String, maxWidth: Int, renderer: FontRenderer): Int = { val breaks = Set(' ', '-', '.', '+', '*', '_', '/') - var pos = 1 + var pos = 0 var lastBreak = -1 while (pos < s.length) { - val width = stringWidth(fullFormat + s.take(pos), renderer) - if (breaks.contains(s.charAt(pos))) lastBreak = pos - if (width > maxWidth) return lastBreak + 1 pos += 1 + val width = stringWidth(fullFormat + s.take(pos), renderer) + if (width >= maxWidth) return lastBreak + 1 + if (pos < s.length && breaks.contains(s.charAt(pos))) lastBreak = pos } pos } @@ -164,18 +194,18 @@ object PseudoMarkdown { private class HeaderSegment(parent: Segment, text: String, val level: Int) extends TextSegment(parent, text) { private def scale = math.max(2, 5 - level) / 2f - override protected def format = EnumChatFormatting.BOLD.toString + override protected def format = EnumChatFormatting.UNDERLINE.toString override protected def stringWidth(s: String, renderer: FontRenderer): Int = (super.stringWidth(s, renderer) * scale).toInt override def height(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = (super.height(indent, maxWidth, renderer) * scale).toInt - override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer): Unit = { + override def render(x: Int, y: Int, indent: Int, maxWidth: Int, maxHeight: Int, renderer: FontRenderer): Unit = { GL11.glPushMatrix() GL11.glTranslatef(x, y, 0) GL11.glScalef(scale, scale, scale) GL11.glTranslatef(-x, -y, 0) - super.render(x, y, indent, maxWidth, renderer) + super.render(x, y, indent, maxWidth, maxHeight, renderer) GL11.glPopMatrix() } @@ -211,7 +241,7 @@ object PseudoMarkdown { private class NewLineSegment extends Segment { override protected def parent: Segment = null - override def height(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = renderer.FONT_HEIGHT * 2 + override def height(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = lineHeight(renderer) * 2 override def toString: String = s"{NewLineSegment}" }