KorGE

    • Register
    • Login
    • Search
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    Text Wrapping

    General
    3
    4
    137
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • Saiful Anwar
      Saiful Anwar last edited by

      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

      1 Reply Last reply Reply Quote 1
      • Joel Lauener
        Joel Lauener last edited by

        I have the same question... how to wrap text?

        1 Reply Last reply Reply Quote 0
        • Saiful Anwar
          Saiful Anwar last edited by Saiful Anwar

          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)
          }
          
          1 Reply Last reply Reply Quote 1
          • Yuri Volkov
            Yuri Volkov last edited by

            @Saiful-Anwar Thank you for the contribution!
            I only had to fix it a bit 🙂 and 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
            help_zh.png

            1 Reply Last reply Reply Quote 1
            • First post
              Last post