Text Wrapping
-
How to perform text auto wrapping with predefined width?
Calling
text.setTextBounds(Rectangle(0.0, 0.0, 300.0, 200.0))
doesn't seem to solve the issue.
Thanks -
I have the same question... how to wrap text?
-
I ended up doing this.
In KorGE version 2 we can pass custom TextRenderer when creating Text.
This is my "quick" implementation in case anyone want to use it.package view import com.soywiz.korge.view.Container import com.soywiz.korge.view.Text import com.soywiz.korge.view.ViewDslMarker import com.soywiz.korge.view.addTo 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.korim.font.getTextBounds import com.soywiz.korim.text.TextAlignment import com.soywiz.korim.text.TextRenderer import com.soywiz.korim.text.TextRendererActions import com.soywiz.korio.resources.Resourceable enum class Gravity { LEFT, CENTER, RIGHT } inline fun Container.wrappableText( text: String, wrapWidth: Double, textSize: Double = 16.0, color: RGBA = Colors.WHITE, font: Resourceable<out Font> = DefaultTtfFont, gravity: Gravity = Gravity.CENTER, callback: @ViewDslMarker WrappableText.() -> Unit = {} ) = WrappableText( text, textSize = textSize, color = color, font = font, wrapWidth = wrapWidth, renderer = WrappableTextRenderer(wrapWidth, gravity) ).addTo(this, callback) class WrappableText( text: String, textSize: Double = DEFAULT_TEXT_SIZE, color: RGBA = Colors.WHITE, font: Resourceable<out Font> = DefaultTtfFont, alignment: TextAlignment = TextAlignment.TOP_LEFT, wrapWidth: Double, renderer: TextRenderer<String>, autoScaling: Boolean = true ) : Text(text, textSize, color, font, alignment, renderer, autoScaling) { init { val f = this.font.getOrNull() if (f is Font) { val metrics = f.getTextBounds(textSize, text, renderer = renderer) metrics.bounds.x = 0.0 metrics.bounds.y = 0.0 metrics.bounds.width = wrapWidth setTextBounds(metrics.bounds) } } } class WrappableTextRenderer(val wrapWidth: Double, val gravity: Gravity) : TextRenderer<String> { override fun TextRendererActions.run(text: String, size: Double, defaultFont: Font) { reset() setFont(defaultFont, size) val lines = mutableListOf(Line()) var curX = 0.0 val spaceWidth = getGlyphMetrics(' '.toInt()).xadvance + getKerning(' '.toInt(), 'A'.toInt()) for (word in text.split(' ')) { var wordWidth = 0.0 var curWord = "" for (n in word.indices) { val c = word[n].toInt() val c1 = word.getOrElse(n + 1) { '\u0000' }.toInt() val g = getGlyphMetrics(c) val kerning = getKerning(c, c1) if (c == '\n'.toInt()) { curX = 0.0 lines.last().words.add(Word(curWord, wordWidth)) curWord = "" wordWidth = 0.0 lines.add(Line()) } else { wordWidth += g.xadvance + kerning curWord += c.toChar() } } curX += wordWidth + spaceWidth if (curX > wrapWidth) { lines.add(Line()) curX = wordWidth + spaceWidth } lines.last().words.add(Word(curWord, wordWidth)) } for (line in lines) { var start = when (gravity) { Gravity.LEFT -> 0.0 Gravity.CENTER -> (wrapWidth - line.calculateWidth(spaceWidth)) / 2 Gravity.RIGHT -> wrapWidth - line.calculateWidth(spaceWidth) } for (word in line.words) { x = start for (n in word.text.indices) { val c = word.text[n].toInt() val c1 = word.text.getOrElse(n + 1) { '\u0000' }.toInt() val g = getGlyphMetrics(c) transform.identity() val advance = g.xadvance + getKerning(c, c1) put(c) advance(advance) } start += word.width + spaceWidth } newLine(lineHeight) } put(0) } data class Line( val words: MutableList<Word> = mutableListOf() ) { fun calculateWidth(spaceWidth: Double): Double { return words.sumByDouble { it.width } + (words.size - 1) * spaceWidth } } data class Word(val text: String, val width: Double) }
-
@Saiful-Anwar Thank you for the contribution!
I only had to fix it a bitand add wrapping for e.g. Chinese texts,
and it works well for my Help screen.
https://github.com/andstatus/game2048/blob/master/src/commonMain/kotlin/org/andstatus/game2048/view/WrappableText.kt