package uk.co.nickthecoder.gamescupboard.client
import com.soywiz.korge.annotations.KorgeExperimental
import com.soywiz.korge.input.cursor
import com.soywiz.korge.input.onClick
import com.soywiz.korge.ui.UIScrollable
import com.soywiz.korge.view.Text
import com.soywiz.korge.view.position
import com.soywiz.korgw.GameWindow
import com.soywiz.korim.color.Colors
import com.soywiz.korim.color.RGBA
import com.soywiz.korim.font.DefaultTtfFont
import com.soywiz.korim.font.Font
import com.soywiz.korio.lang.substr
import com.soywiz.korio.resources.Resourceable
/**
* A [UIScrollable] that can be printed to (i.e. text can be added to it using [print] and [println]).
*/
@OptIn(KorgeExperimental::class)
class PrintContainer(
private val myWidth: Double,
myHeight: Double,
val paddingLeft: Double = 0.0,
val paddingRight: Double = paddingLeft,
val paddingTop: Double = paddingRight,
val font: Resourceable<out Font> = DefaultTtfFont,
val textSize: Double = Text.DEFAULT_TEXT_SIZE,
val defaultColor: RGBA = textColor
) : UIScrollable(myWidth, myHeight) {
/**
* Needed for [println].
*/
private var textHeight = createText("X").height
/**
* The location where the next text will be printed.
* At the end of a line, [px] is set to 0.0, and [py] is incremented by [textHeight].
*/
private var px = paddingLeft
private var py = paddingTop
/**
* The text color
*/
private var color = defaultColor
fun print(str: String, color: RGBA = defaultColor, clickAction: (() -> Unit)? = null) {
//normalPrintln("print $str @ $px,$py")
this.color = color
tryToFit(str, "", clickAction)
scrollTopRatio = 1.0
scrollLeftRatio = 0.0
}
fun println() {
px = paddingLeft
py += textHeight
scrollTopRatio = 1.0
scrollLeftRatio = 0.0
}
fun println(str: String, color: RGBA = defaultColor) {
//normalPrintln("println $str @ $px, $py")
this.color = color
if (str.isNotBlank()) {
tryToFit(str, "")
scrollTopRatio = 1.0
scrollLeftRatio = 0.0
}
px = paddingLeft
py += textHeight
}
private fun createText(str: String) = Text(str, font = font, textSize = textSize, color = color)
/**
* The first time this is called, [remainder] will be blank, and [firstPart] is the text to be printed.
* If [firstPart] cannot be fit without being split, then this will be called recursively,
* with parts of [firstPart] removed from the end, and added to the front of [remainder].
*/
private fun tryToFit(firstPart: String, remainder: String, clickAction: (() -> Unit)? = null) {
with(container) {
val text = createText(firstPart)
if (clickAction != null) {
text.onClick { clickAction() }
this.cursor = GameWindow.Cursor.HAND
}
if (text.width + px > myWidth - paddingRight) {
// We need to break the text over multiple lines.
val space = firstPart.lastIndexOf(" ")
if (space < 0) {
// There is no space
if (px == paddingLeft) {
// Oh well, there will be a horizontal scroll bar :-(
text.position(px, py)
addChild(text)
px = paddingLeft
py += text.height
// We HAVE added `firstPart` (badly)
//normalPrintln("Line break (badly)")
} else {
// We couldn't fit this word on the current line, so try again on a new line.
px = paddingLeft
py += text.height
//normalPrintln("Line break long word")
tryToFit(firstPart.trimStart() + " " + remainder.trimStart(), "", clickAction)
return
}
} else {
// There IS a space, so chop off the last word, and add it to remainder,
// Try again with the shorter first part and longer remainder.
val newRemainder = (firstPart.substring(space) + " " + remainder.trimStart())
tryToFit(firstPart.substr(0, space), newRemainder, clickAction)
return
}
} else {
// The whole of first part fits onto the current line.
text.position(px, py)
px += text.width
if (text.text.isNotBlank()) {
addChild(text)
}
// We HAVE added `firstPart` (without overlapping the edge)
}
if (remainder.isNotBlank()) {
// If we get here, then `firstPart` must have been added.
// So now we do the same for `remainder` on the next line.
px = paddingLeft
py += text.height
//normalPrintln("Line break for the remainder")
tryToFit(remainder.trimStart(), "", clickAction)
}
}
}
}
private fun normalPrintln(str: String) = println(str)