mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-13 01:10:19 -04:00
More cleanup, pulled common functionality from text and code segments into trait.
This commit is contained in:
parent
c6bfcb464e
commit
fe66cec9d6
@ -0,0 +1,68 @@
|
||||
package li.cil.oc.client.renderer.markdown.segment
|
||||
|
||||
import li.cil.oc.client.renderer.markdown.Document
|
||||
import net.minecraft.client.gui.FontRenderer
|
||||
|
||||
trait BasicTextSegment extends Segment {
|
||||
protected final val breaks = Set(' ', '.', ',', ':', ';', '!', '?', '_', '=', '-', '+', '*', '/', '\\')
|
||||
protected final val lists = Set("- ", "* ")
|
||||
protected lazy val rootPrefix = root.asInstanceOf[TextSegment].text.take(2)
|
||||
|
||||
override def nextX(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
if (isLast) return 0
|
||||
var currentX = indent
|
||||
var chars = text
|
||||
if (ignoreLeadingWhitespace && indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
while (chars.length > numChars) {
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
currentX = wrapIndent
|
||||
}
|
||||
currentX + stringWidth(chars, renderer)
|
||||
}
|
||||
|
||||
override def nextY(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
var lines = 0
|
||||
var chars = text
|
||||
if (ignoreLeadingWhitespace && indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
while (chars.length > numChars) {
|
||||
lines += 1
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
}
|
||||
if (isLast) lines += 1
|
||||
lines * lineHeight(renderer)
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
protected def text: String
|
||||
|
||||
protected def ignoreLeadingWhitespace: Boolean = true
|
||||
|
||||
protected def lineHeight(renderer: FontRenderer): Int = Document.lineHeight(renderer)
|
||||
|
||||
protected def stringWidth(s: String, renderer: FontRenderer): Int
|
||||
|
||||
protected def maxChars(s: String, maxWidth: Int, maxLineWidth: Int, renderer: FontRenderer): Int = {
|
||||
var pos = -1
|
||||
var lastBreak = -1
|
||||
val fullWidth = stringWidth(s, renderer)
|
||||
while (pos < s.length) {
|
||||
pos += 1
|
||||
val width = stringWidth(s.take(pos), renderer)
|
||||
if (width >= maxWidth) {
|
||||
if (lastBreak > 0 || fullWidth <= maxLineWidth || s.exists(breaks.contains)) return lastBreak + 1
|
||||
else return pos - 1
|
||||
}
|
||||
if (pos < s.length && breaks.contains(s.charAt(pos))) lastBreak = pos
|
||||
}
|
||||
pos
|
||||
}
|
||||
|
||||
protected def computeWrapIndent(renderer: FontRenderer) = if (lists.contains(rootPrefix)) renderer.getStringWidth(rootPrefix) else 0
|
||||
}
|
@ -1,43 +1,10 @@
|
||||
package li.cil.oc.client.renderer.markdown.segment
|
||||
|
||||
import li.cil.oc.client.renderer.TextBufferRenderCache
|
||||
import li.cil.oc.client.renderer.markdown.Document
|
||||
import net.minecraft.client.gui.FontRenderer
|
||||
import org.lwjgl.opengl.GL11
|
||||
|
||||
private[markdown] class CodeSegment(val parent: Segment, val text: String) extends Segment {
|
||||
private final val breaks = Set(' ', '.', ',', ':', ';', '!', '?', '_', '=', '-', '+', '*', '/', '\\')
|
||||
private final val lists = Set("- ", "* ")
|
||||
private lazy val rootPrefix = root.asInstanceOf[TextSegment].text.take(2)
|
||||
|
||||
override def nextX(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
if (isLast) return 0
|
||||
var currentX = indent
|
||||
var chars = text
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent)
|
||||
while (chars.length > numChars) {
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent)
|
||||
currentX = wrapIndent + 1
|
||||
}
|
||||
currentX + stringWidth(chars)
|
||||
}
|
||||
|
||||
override def nextY(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
var lines = 0
|
||||
var chars = text
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent)
|
||||
while (chars.length > numChars) {
|
||||
lines += 1
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent)
|
||||
}
|
||||
if (isLast) lines += 1
|
||||
lines * Document.lineHeight(renderer)
|
||||
}
|
||||
|
||||
private[markdown] class CodeSegment(val parent: Segment, val text: String) extends BasicTextSegment {
|
||||
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
||||
TextBufferRenderCache.renderer.generateChars(text.toCharArray)
|
||||
|
||||
@ -45,38 +12,23 @@ private[markdown] class CodeSegment(val parent: Segment, val text: String) exten
|
||||
var currentY = y
|
||||
var chars = text
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
while (chars.length > 0) {
|
||||
val part = chars.take(numChars)
|
||||
GL11.glColor4f(0.75f, 0.8f, 1, 1)
|
||||
TextBufferRenderCache.renderer.drawString(part, currentX, currentY)
|
||||
currentX = x + wrapIndent
|
||||
currentY += Document.lineHeight(renderer)
|
||||
currentY += lineHeight(renderer)
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
private def stringWidth(s: String): Int = s.length * TextBufferRenderCache.renderer.charRenderWidth
|
||||
override protected def ignoreLeadingWhitespace: Boolean = false
|
||||
|
||||
private def maxChars(s: String, maxWidth: Int, maxLineWidth: Int): Int = {
|
||||
var pos = 0
|
||||
var lastBreak = -1
|
||||
while (pos < s.length) {
|
||||
pos += 1
|
||||
val width = stringWidth(s.take(pos))
|
||||
if (width >= maxWidth) {
|
||||
if (lastBreak > 0 || stringWidth(s) <= maxLineWidth || s.exists(breaks.contains)) return lastBreak + 1
|
||||
else return pos - 1
|
||||
}
|
||||
if (pos < s.length && breaks.contains(s.charAt(pos))) lastBreak = pos
|
||||
}
|
||||
pos
|
||||
}
|
||||
|
||||
private def computeWrapIndent(renderer: FontRenderer) = if (lists.contains(rootPrefix)) renderer.getStringWidth(rootPrefix) else 0
|
||||
override protected def stringWidth(s: String, renderer: FontRenderer): Int = s.length * TextBufferRenderCache.renderer.charRenderWidth
|
||||
|
||||
override def toString: String = s"{CodeSegment: text = $text}"
|
||||
}
|
||||
|
@ -40,8 +40,14 @@ trait Segment {
|
||||
*/
|
||||
def nextY(indent: Int, maxWidth: Int, renderer: FontRenderer): Int
|
||||
|
||||
/**
|
||||
* Render the segment at the specified coordinates with the specified
|
||||
* properties.
|
||||
*/
|
||||
def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = None
|
||||
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
// Used during construction, checks a segment for inner segments.
|
||||
private[markdown] def refine(pattern: Regex, factory: (Segment, Regex.Match) => Segment): Iterable[Segment] = Iterable(this)
|
||||
|
||||
|
@ -8,10 +8,32 @@ import scala.annotation.tailrec
|
||||
import scala.collection.mutable
|
||||
import scala.util.matching.Regex
|
||||
|
||||
private[markdown] class TextSegment(val parent: Segment, val text: String) extends Segment {
|
||||
private final val breaks = Set(' ', '.', ',', ':', ';', '!', '?', '_', '=', '-', '+', '*', '/', '\\')
|
||||
private final val lists = Set("- ", "* ")
|
||||
private lazy val rootPrefix = root.asInstanceOf[TextSegment].text.take(2)
|
||||
private[markdown] class TextSegment(val parent: Segment, val text: String) extends BasicTextSegment {
|
||||
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
||||
var currentX = x + indent
|
||||
var currentY = y
|
||||
var chars = text
|
||||
if (indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
var hovered: Option[InteractiveSegment] = None
|
||||
while (chars.length > 0) {
|
||||
val part = chars.take(numChars)
|
||||
hovered = hovered.orElse(resolvedInteractive.fold(None: Option[InteractiveSegment])(_.checkHovered(mouseX, mouseY, currentX, currentY, stringWidth(part, renderer), (Document.lineHeight(renderer) * resolvedScale).toInt)))
|
||||
GL11.glPushMatrix()
|
||||
GL11.glTranslatef(currentX, currentY, 0)
|
||||
GL11.glScalef(resolvedScale, resolvedScale, resolvedScale)
|
||||
GL11.glTranslatef(-currentX, -currentY, 0)
|
||||
renderer.drawString(resolvedFormat + part, currentX, currentY, resolvedColor)
|
||||
GL11.glPopMatrix()
|
||||
currentX = x + wrapIndent
|
||||
currentY += lineHeight(renderer)
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
}
|
||||
|
||||
hovered
|
||||
}
|
||||
|
||||
override def refine(pattern: Regex, factory: (Segment, Regex.Match) => Segment): Iterable[Segment] = {
|
||||
val result = mutable.Buffer.empty[Segment]
|
||||
@ -39,63 +61,13 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten
|
||||
result
|
||||
}
|
||||
|
||||
override def nextX(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
if (isLast) return 0
|
||||
var currentX = indent
|
||||
var chars = text
|
||||
if (indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
while (chars.length > numChars) {
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
currentX = wrapIndent
|
||||
}
|
||||
currentX + (stringWidth(chars, renderer) * resolvedScale).toInt
|
||||
}
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
override def nextY(indent: Int, maxWidth: Int, renderer: FontRenderer): Int = {
|
||||
var lines = 0
|
||||
var chars = text
|
||||
if (indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
while (chars.length > numChars) {
|
||||
lines += 1
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
}
|
||||
if (isLast) lines += 1
|
||||
(lines * Document.lineHeight(renderer) * resolvedScale).toInt
|
||||
}
|
||||
override protected def lineHeight(renderer: FontRenderer): Int = (super.lineHeight(renderer) * resolvedScale).toInt
|
||||
|
||||
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
||||
val fontScale = resolvedScale
|
||||
var currentX = x + indent
|
||||
var currentY = y
|
||||
var chars = text
|
||||
if (indent == 0) chars = chars.dropWhile(_.isWhitespace)
|
||||
val wrapIndent = computeWrapIndent(renderer)
|
||||
var numChars = maxChars(chars, maxWidth - indent, maxWidth - wrapIndent, renderer)
|
||||
val interactive = findInteractive()
|
||||
var hovered: Option[InteractiveSegment] = None
|
||||
while (chars.length > 0) {
|
||||
val part = chars.take(numChars)
|
||||
hovered = hovered.orElse(interactive.fold(None: Option[InteractiveSegment])(_.checkHovered(mouseX, mouseY, currentX, currentY, (stringWidth(part, renderer) * fontScale).toInt, (Document.lineHeight(renderer) * fontScale).toInt)))
|
||||
GL11.glPushMatrix()
|
||||
GL11.glTranslatef(currentX, currentY, 0)
|
||||
GL11.glScalef(fontScale, fontScale, fontScale)
|
||||
GL11.glTranslatef(-currentX, -currentY, 0)
|
||||
renderer.drawString(resolvedFormat + part, currentX, currentY, resolvedColor)
|
||||
GL11.glPopMatrix()
|
||||
currentX = x + wrapIndent
|
||||
currentY += (Document.lineHeight(renderer) * fontScale).toInt
|
||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||
}
|
||||
override protected def stringWidth(s: String, renderer: FontRenderer): Int = (renderer.getStringWidth(resolvedFormat + s) * resolvedScale).toInt
|
||||
|
||||
hovered
|
||||
}
|
||||
// ----------------------------------------------------------------------- //
|
||||
|
||||
protected def color = None: Option[Int]
|
||||
|
||||
@ -103,48 +75,28 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten
|
||||
|
||||
protected def format = ""
|
||||
|
||||
protected def stringWidth(s: String, renderer: FontRenderer): Int = renderer.getStringWidth(resolvedFormat + s)
|
||||
private def resolvedColor: Int = color.getOrElse(parent match {
|
||||
case segment: TextSegment => segment.resolvedColor
|
||||
case _ => 0xDDDDDD
|
||||
})
|
||||
|
||||
def resolvedColor: Int = parent match {
|
||||
case segment: TextSegment => color.getOrElse(segment.resolvedColor)
|
||||
case _ => color.getOrElse(0xDDDDDD)
|
||||
private def resolvedScale: Float = parent match {
|
||||
case segment: TextSegment => scale.getOrElse(1f) * segment.resolvedScale
|
||||
case _ => 1f
|
||||
}
|
||||
|
||||
def resolvedScale: Float = parent match {
|
||||
case segment: TextSegment => scale.getOrElse(segment.resolvedScale)
|
||||
case _ => scale.getOrElse(1f)
|
||||
}
|
||||
|
||||
def resolvedFormat: String = parent match {
|
||||
private def resolvedFormat: String = parent match {
|
||||
case segment: TextSegment => segment.resolvedFormat + format
|
||||
case _ => format
|
||||
}
|
||||
|
||||
@tailrec private def findInteractive(): Option[InteractiveSegment] = this match {
|
||||
private lazy val resolvedInteractive: Option[InteractiveSegment] = this match {
|
||||
case segment: InteractiveSegment => Some(segment)
|
||||
case _ => parent match {
|
||||
case segment: TextSegment => segment.findInteractive()
|
||||
case segment: TextSegment => segment.resolvedInteractive
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
|
||||
private def maxChars(s: String, maxWidth: Int, maxLineWidth: Int, renderer: FontRenderer): Int = {
|
||||
val fontScale = resolvedScale
|
||||
var pos = -1
|
||||
var lastBreak = -1
|
||||
while (pos < s.length) {
|
||||
pos += 1
|
||||
val width = (stringWidth(s.take(pos), renderer) * fontScale).toInt
|
||||
if (width >= maxWidth) {
|
||||
if (lastBreak > 0 || stringWidth(s, renderer) <= maxLineWidth || s.exists(breaks.contains)) return lastBreak + 1
|
||||
else return pos - 1
|
||||
}
|
||||
if (pos < s.length && breaks.contains(s.charAt(pos))) lastBreak = pos
|
||||
}
|
||||
pos
|
||||
}
|
||||
|
||||
private def computeWrapIndent(renderer: FontRenderer) = if (lists.contains(rootPrefix)) renderer.getStringWidth(rootPrefix) else 0
|
||||
|
||||
override def toString: String = s"{TextSegment: text = $text}"
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user