mirror of
https://github.com/MightyPirates/OpenComputers.git
synced 2025-09-13 17:28:52 -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
|
package li.cil.oc.client.renderer.markdown.segment
|
||||||
|
|
||||||
import li.cil.oc.client.renderer.TextBufferRenderCache
|
import li.cil.oc.client.renderer.TextBufferRenderCache
|
||||||
import li.cil.oc.client.renderer.markdown.Document
|
|
||||||
import net.minecraft.client.gui.FontRenderer
|
import net.minecraft.client.gui.FontRenderer
|
||||||
import org.lwjgl.opengl.GL11
|
import org.lwjgl.opengl.GL11
|
||||||
|
|
||||||
private[markdown] class CodeSegment(val parent: Segment, val text: String) extends Segment {
|
private[markdown] class CodeSegment(val parent: Segment, val text: String) extends BasicTextSegment {
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
||||||
TextBufferRenderCache.renderer.generateChars(text.toCharArray)
|
TextBufferRenderCache.renderer.generateChars(text.toCharArray)
|
||||||
|
|
||||||
@ -45,38 +12,23 @@ private[markdown] class CodeSegment(val parent: Segment, val text: String) exten
|
|||||||
var currentY = y
|
var currentY = y
|
||||||
var chars = text
|
var chars = text
|
||||||
val wrapIndent = computeWrapIndent(renderer)
|
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) {
|
while (chars.length > 0) {
|
||||||
val part = chars.take(numChars)
|
val part = chars.take(numChars)
|
||||||
GL11.glColor4f(0.75f, 0.8f, 1, 1)
|
GL11.glColor4f(0.75f, 0.8f, 1, 1)
|
||||||
TextBufferRenderCache.renderer.drawString(part, currentX, currentY)
|
TextBufferRenderCache.renderer.drawString(part, currentX, currentY)
|
||||||
currentX = x + wrapIndent
|
currentX = x + wrapIndent
|
||||||
currentY += Document.lineHeight(renderer)
|
currentY += lineHeight(renderer)
|
||||||
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
chars = chars.drop(numChars).dropWhile(_.isWhitespace)
|
||||||
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent)
|
numChars = maxChars(chars, maxWidth - wrapIndent, maxWidth - wrapIndent, renderer)
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
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 = {
|
override protected def stringWidth(s: String, renderer: FontRenderer): Int = s.length * TextBufferRenderCache.renderer.charRenderWidth
|
||||||
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 def toString: String = s"{CodeSegment: text = $text}"
|
override def toString: String = s"{CodeSegment: text = $text}"
|
||||||
}
|
}
|
||||||
|
@ -40,8 +40,14 @@ trait Segment {
|
|||||||
*/
|
*/
|
||||||
def nextY(indent: Int, maxWidth: Int, renderer: FontRenderer): Int
|
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
|
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.
|
// Used during construction, checks a segment for inner segments.
|
||||||
private[markdown] def refine(pattern: Regex, factory: (Segment, Regex.Match) => Segment): Iterable[Segment] = Iterable(this)
|
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.collection.mutable
|
||||||
import scala.util.matching.Regex
|
import scala.util.matching.Regex
|
||||||
|
|
||||||
private[markdown] class TextSegment(val parent: Segment, val text: String) extends Segment {
|
private[markdown] class TextSegment(val parent: Segment, val text: String) extends BasicTextSegment {
|
||||||
private final val breaks = Set(' ', '.', ',', ':', ';', '!', '?', '_', '=', '-', '+', '*', '/', '\\')
|
override def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
||||||
private final val lists = Set("- ", "* ")
|
var currentX = x + indent
|
||||||
private lazy val rootPrefix = root.asInstanceOf[TextSegment].text.take(2)
|
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] = {
|
override def refine(pattern: Regex, factory: (Segment, Regex.Match) => Segment): Iterable[Segment] = {
|
||||||
val result = mutable.Buffer.empty[Segment]
|
val result = mutable.Buffer.empty[Segment]
|
||||||
@ -39,63 +61,13 @@ private[markdown] class TextSegment(val parent: Segment, val text: String) exten
|
|||||||
result
|
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 = {
|
override protected def lineHeight(renderer: FontRenderer): Int = (super.lineHeight(renderer) * resolvedScale).toInt
|
||||||
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 def render(x: Int, y: Int, indent: Int, maxWidth: Int, renderer: FontRenderer, mouseX: Int, mouseY: Int): Option[InteractiveSegment] = {
|
override protected def stringWidth(s: String, renderer: FontRenderer): Int = (renderer.getStringWidth(resolvedFormat + s) * resolvedScale).toInt
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
hovered
|
// ----------------------------------------------------------------------- //
|
||||||
}
|
|
||||||
|
|
||||||
protected def color = None: Option[Int]
|
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 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 {
|
private def resolvedScale: Float = parent match {
|
||||||
case segment: TextSegment => color.getOrElse(segment.resolvedColor)
|
case segment: TextSegment => scale.getOrElse(1f) * segment.resolvedScale
|
||||||
case _ => color.getOrElse(0xDDDDDD)
|
case _ => 1f
|
||||||
}
|
}
|
||||||
|
|
||||||
def resolvedScale: Float = parent match {
|
private def resolvedFormat: String = parent match {
|
||||||
case segment: TextSegment => scale.getOrElse(segment.resolvedScale)
|
|
||||||
case _ => scale.getOrElse(1f)
|
|
||||||
}
|
|
||||||
|
|
||||||
def resolvedFormat: String = parent match {
|
|
||||||
case segment: TextSegment => segment.resolvedFormat + format
|
case segment: TextSegment => segment.resolvedFormat + format
|
||||||
case _ => 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 segment: InteractiveSegment => Some(segment)
|
||||||
case _ => parent match {
|
case _ => parent match {
|
||||||
case segment: TextSegment => segment.findInteractive()
|
case segment: TextSegment => segment.resolvedInteractive
|
||||||
case _ => None
|
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}"
|
override def toString: String = s"{TextSegment: text = $text}"
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user