Basic scrolling logic.

This commit is contained in:
Florian Nücke 2015-04-08 02:16:54 +02:00
parent ca8f1e3b94
commit bef070b9ff
2 changed files with 111 additions and 24 deletions

View File

@ -1,14 +1,29 @@
package li.cil.oc.client.gui package li.cil.oc.client.gui
import java.util
import li.cil.oc.client.Textures import li.cil.oc.client.Textures
import li.cil.oc.util.PseudoMarkdown import li.cil.oc.util.PseudoMarkdown
import net.minecraft.client.Minecraft import net.minecraft.client.Minecraft
import net.minecraft.client.gui.Gui import net.minecraft.client.gui.Gui
import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.GuiScreen
import net.minecraft.client.gui.ScaledResolution import net.minecraft.client.gui.ScaledResolution
import org.lwjgl.input.Mouse
class Manual extends GuiScreen { 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. |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) |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 mc = Minecraft.getMinecraft
val screenSize = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight) val screenSize = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight)
val guiSize = new ScaledResolution(mc, 256, 192) val guiSize = new ScaledResolution(mc, 256, 192)
val (midX, midY) = (screenSize.getScaledWidth / 2, screenSize.getScaledHeight / 2) 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) 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) 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
}
} }
} }

View File

@ -27,20 +27,50 @@ object PseudoMarkdown {
* Renders a list of segments and tooltips if a segment with a tooltip is hovered. * Renders a list of segments and tooltips if a segment with a tooltip is hovered.
* Returns a link address if a link 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 currentX = 0
var currentY = 0 var currentY = 0
for (segment <- document) { for (segment <- document) {
if (currentY >= offset) { segment.render(x, y + currentY - yOffset, currentX, maxWidth, maxHeight - (currentY - yOffset), renderer)
segment.render(x, y + currentY, currentX, maxWidth, renderer) currentY += segment.height(currentX, maxWidth, renderer) - lineHeight(renderer)
}
currentY += segment.height(currentX, maxWidth, renderer) - renderer.FONT_HEIGHT
currentX = segment.width(currentX, maxWidth, renderer) currentX = segment.width(currentX, maxWidth, renderer)
} }
GL11.glDepthFunc(GL11.GL_LEQUAL)
GL11.glPopMatrix()
None 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 { trait Segment {
@ -66,7 +96,7 @@ object PseudoMarkdown {
*/ */
def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = 0 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) chars = chars.drop(lineChars)
lineChars = maxChars(chars, maxWidth, renderer) lineChars = maxChars(chars, maxWidth, renderer)
} }
lines * renderer.FONT_HEIGHT lines * lineHeight(renderer)
} }
override def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = { override def width(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
@ -122,15 +152,15 @@ object PseudoMarkdown {
currentX + renderer.getStringWidth(fullFormat + chars) 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 currentX = x + indent
var currentY = y var currentY = y
var chars = text var chars = text
var numChars = maxChars(chars, maxWidth - indent, renderer) var numChars = maxChars(chars, maxWidth - indent, renderer)
while (chars.length > 0) { while (chars.length > 0 && (currentY - y) < maxHeight) {
renderer.drawString(fullFormat + chars.take(numChars), currentX, currentY, 0xFFFFFF) renderer.drawString(fullFormat + chars.take(numChars), currentX, currentY, 0xFAFAFA)
currentX = x currentX = x
currentY += renderer.FONT_HEIGHT currentY += lineHeight(renderer)
chars = chars.drop(numChars) chars = chars.drop(numChars)
numChars = maxChars(chars, maxWidth, renderer) numChars = maxChars(chars, maxWidth, renderer)
} }
@ -147,13 +177,13 @@ object PseudoMarkdown {
private def maxChars(s: String, maxWidth: Int, renderer: FontRenderer): Int = { private def maxChars(s: String, maxWidth: Int, renderer: FontRenderer): Int = {
val breaks = Set(' ', '-', '.', '+', '*', '_', '/') val breaks = Set(' ', '-', '.', '+', '*', '_', '/')
var pos = 1 var pos = 0
var lastBreak = -1 var lastBreak = -1
while (pos < s.length) { 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 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 pos
} }
@ -164,18 +194,18 @@ object PseudoMarkdown {
private class HeaderSegment(parent: Segment, text: String, val level: Int) extends TextSegment(parent, text) { private class HeaderSegment(parent: Segment, text: String, val level: Int) extends TextSegment(parent, text) {
private def scale = math.max(2, 5 - level) / 2f 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 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 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.glPushMatrix()
GL11.glTranslatef(x, y, 0) GL11.glTranslatef(x, y, 0)
GL11.glScalef(scale, scale, scale) GL11.glScalef(scale, scale, scale)
GL11.glTranslatef(-x, -y, 0) GL11.glTranslatef(-x, -y, 0)
super.render(x, y, indent, maxWidth, renderer) super.render(x, y, indent, maxWidth, maxHeight, renderer)
GL11.glPopMatrix() GL11.glPopMatrix()
} }
@ -211,7 +241,7 @@ object PseudoMarkdown {
private class NewLineSegment extends Segment { private class NewLineSegment extends Segment {
override protected def parent: Segment = null 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}" override def toString: String = s"{NewLineSegment}"
} }