[orx-text-writer] Add orx-text-writer
This commit is contained in:
@@ -10,7 +10,7 @@ dependencies {
|
||||
demoImplementation(project(":orx-shader-phrases"))
|
||||
demoImplementation(project(":orx-camera"))
|
||||
demoImplementation(project(":orx-shapes"))
|
||||
demoImplementation(project(":orx-svg"))
|
||||
demoImplementation(libs.slf4j.simple)
|
||||
demoImplementation(libs.openrndr.ffmpeg)
|
||||
demoImplementation(libs.openrndr.svg)
|
||||
}
|
||||
@@ -15,6 +15,7 @@ tasks.test {
|
||||
|
||||
dependencies {
|
||||
implementation(project(":orx-expression-evaluator"))
|
||||
implementation(project(":orx-text-writer"))
|
||||
implementation(libs.openrndr.application)
|
||||
implementation(libs.openrndr.math)
|
||||
implementation(libs.kotlin.coroutines)
|
||||
|
||||
@@ -3,9 +3,10 @@ package org.openrndr.panel.elements
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.FontImageMap
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.draw.isolated
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.panel.style.*
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
@@ -53,7 +54,7 @@ class Button : Element(ElementType("button")) {
|
||||
val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 14.0
|
||||
val fontMap = FontImageMap.fromUrl(fontUrl, fontSize)
|
||||
|
||||
val writer = Writer(null)
|
||||
val writer = TextWriter(null)
|
||||
|
||||
writer.box = Rectangle(0.0,
|
||||
0.0,
|
||||
@@ -84,7 +85,7 @@ class Button : Element(ElementType("button")) {
|
||||
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
val textWidth = writer.textWidth(label)
|
||||
val textHeight = font.ascenderLength
|
||||
|
||||
@@ -4,8 +4,9 @@ import kotlinx.coroutines.yield
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.LineCap
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.launch
|
||||
import org.openrndr.panel.style.*
|
||||
|
||||
@@ -62,7 +63,7 @@ class ColorpickerButton : Element(ElementType("colorpicker-button")), Disposable
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
|
||||
val text = "$label"
|
||||
|
||||
@@ -5,12 +5,12 @@ import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.FontImageMap
|
||||
import org.openrndr.panel.style.*
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import kotlinx.coroutines.yield
|
||||
import org.openrndr.KEY_ARROW_DOWN
|
||||
import org.openrndr.KEY_ARROW_UP
|
||||
import org.openrndr.KEY_ENTER
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.launch
|
||||
import kotlin.math.max
|
||||
@@ -80,7 +80,7 @@ class DropdownButton : Element(ElementType("dropdown-button")), DisposableElemen
|
||||
val fontUrl = (root() as? Body)?.controlManager?.fontManager?.resolve(style.fontFamily) ?: "broken"
|
||||
val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 16.0
|
||||
val fontMap = FontImageMap.fromUrl(fontUrl, fontSize)
|
||||
val writer = Writer(null)
|
||||
val writer = TextWriter(null)
|
||||
|
||||
writer.box = Rectangle(0.0,
|
||||
0.0,
|
||||
@@ -116,7 +116,7 @@ class DropdownButton : Element(ElementType("dropdown-button")), DisposableElemen
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
|
||||
val text = (value?.label) ?: "<choose>"
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package org.openrndr.panel.elements
|
||||
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Cursor
|
||||
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.Writer
|
||||
import org.openrndr.extra.textwriter.Cursor
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.panel.style.*
|
||||
|
||||
@@ -53,7 +55,7 @@ class EnvelopeButton : Element(ElementType("envelope-button")) {
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
drawer.fill = (ColorRGBa.BLACK)
|
||||
writer.cursor = Cursor(0.0,layout.screenHeight - 4.0)
|
||||
|
||||
@@ -5,6 +5,8 @@ import org.openrndr.KeyModifier
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.*
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.Cursor
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.map
|
||||
import org.openrndr.panel.style.effectiveColor
|
||||
@@ -196,7 +198,7 @@ open class SequenceEditorBase(type: String = "sequence-editor-base") : Element(E
|
||||
drawer.fill = computedStyle.effectiveColor
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
drawer.fill = computedStyle.effectiveColor
|
||||
writer.cursor = Cursor(0.0, 4.0)
|
||||
|
||||
@@ -3,11 +3,13 @@ package org.openrndr.panel.elements
|
||||
import io.github.oshai.kotlinlogging.KotlinLogging
|
||||
import kotlinx.coroutines.*
|
||||
import org.openrndr.*
|
||||
import org.openrndr.draw.Cursor
|
||||
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.LineCap
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.Cursor
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.panel.style.Color
|
||||
import org.openrndr.panel.style.color
|
||||
@@ -267,7 +269,7 @@ class Slider : Element(ElementType("slider")), DisposableElement {
|
||||
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val font = it.font(computedStyle)
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (font)
|
||||
drawer.fill = computedStyle.effectiveColor
|
||||
writer.cursor = Cursor(0.0, 8.0)
|
||||
|
||||
@@ -4,7 +4,8 @@ import kotlinx.coroutines.yield
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.FontImageMap
|
||||
import org.openrndr.draw.Writer
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
|
||||
import org.openrndr.launch
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.panel.style.*
|
||||
@@ -20,7 +21,7 @@ class TextNode(var text: String) : Element(ElementType("text")) {
|
||||
drawer.fill = (fill)
|
||||
}
|
||||
val fontMap = (root() as Body).controlManager.fontManager.font(computedStyle)
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = (fontMap)
|
||||
|
||||
writer.box = Rectangle(Vector2(layout.screenX * 0.0, layout.screenY * 0.0), layout.screenWidth, layout.screenHeight)
|
||||
@@ -35,7 +36,7 @@ class TextNode(var text: String) : Element(ElementType("text")) {
|
||||
val fontSize = (style.fontSize as? LinearDimension.PX)?.value?: 14.0
|
||||
val fontMap = FontImageMap.fromUrl(fontUrl, fontSize)
|
||||
|
||||
val writer = Writer(null)
|
||||
val writer = TextWriter(null)
|
||||
|
||||
writer.box = Rectangle(layout.screenX,
|
||||
layout.screenY,
|
||||
|
||||
@@ -7,9 +7,9 @@ import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.LineCap
|
||||
import org.openrndr.panel.style.*
|
||||
import org.openrndr.KeyModifier
|
||||
import org.openrndr.draw.Cursor
|
||||
import org.openrndr.draw.writer
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.Cursor
|
||||
import org.openrndr.extra.textwriter.writer
|
||||
import org.openrndr.launch
|
||||
import org.openrndr.shape.Rectangle
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
@@ -7,8 +7,9 @@ import org.openrndr.draw.LineCap
|
||||
import org.openrndr.panel.style.*
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.launch
|
||||
import kotlin.reflect.KMutableProperty0
|
||||
|
||||
@@ -35,7 +36,7 @@ class Toggle : Element(ElementType("toggle")), DisposableElement {
|
||||
val fontSize = (style.fontSize as? LinearDimension.PX)?.value ?: 14.0
|
||||
val fontMap = FontImageMap.fromUrl(fontUrl, fontSize)
|
||||
|
||||
val writer = Writer(null)
|
||||
val writer = TextWriter(null)
|
||||
|
||||
writer.box = Rectangle(0.0,
|
||||
0.0,
|
||||
|
||||
@@ -4,8 +4,9 @@ import kotlinx.coroutines.yield
|
||||
import org.openrndr.*
|
||||
import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.Writer
|
||||
|
||||
import org.openrndr.events.Event
|
||||
import org.openrndr.extra.textwriter.TextWriter
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.math.clamp
|
||||
import org.openrndr.math.map
|
||||
@@ -213,7 +214,7 @@ class XYPad : Element(ElementType("xy-pad")) {
|
||||
val valueLabel = "${String.format("%.0${precision}f", value.x)}, ${String.format("%.0${precision}f", value.y)}"
|
||||
|
||||
(root() as? Body)?.controlManager?.fontManager?.let {
|
||||
val writer = Writer(drawer)
|
||||
val writer = TextWriter(drawer)
|
||||
drawer.fontMap = it.font(computedStyle)
|
||||
val textWidth = writer.textWidth(valueLabel)
|
||||
|
||||
|
||||
@@ -4,7 +4,8 @@ import org.openrndr.color.ColorRGBa
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.FontImageMap
|
||||
import org.openrndr.draw.isolated
|
||||
import org.openrndr.draw.writer
|
||||
import org.openrndr.extra.textwriter.writer
|
||||
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.panel.elements.Body
|
||||
import org.openrndr.panel.elements.Element
|
||||
|
||||
6
orx-text-writer/README.md
Normal file
6
orx-text-writer/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# orx-text-writer
|
||||
|
||||
Writing texts with layouts
|
||||
|
||||
Code in `orx-text-writer` was previously part of `openrndr-draw`.
|
||||
|
||||
22
orx-text-writer/build.gradle.kts
Normal file
22
orx-text-writer/build.gradle.kts
Normal file
@@ -0,0 +1,22 @@
|
||||
plugins {
|
||||
org.openrndr.extra.convention.`kotlin-multiplatform`
|
||||
alias(libs.plugins.kotest.multiplatform)
|
||||
}
|
||||
|
||||
kotlin {
|
||||
sourceSets {
|
||||
val commonMain by getting {
|
||||
dependencies {
|
||||
implementation(libs.openrndr.shape)
|
||||
implementation(libs.openrndr.draw)
|
||||
implementation(libs.openrndr.application)
|
||||
}
|
||||
}
|
||||
|
||||
val jvmDemo by getting {
|
||||
dependencies {
|
||||
implementation(project(":orx-text-writer"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
16
orx-text-writer/src/commonMain/kotlin/DrawerExtensions.kt
Normal file
16
orx-text-writer/src/commonMain/kotlin/DrawerExtensions.kt
Normal file
@@ -0,0 +1,16 @@
|
||||
package org.openrndr.extra.textwriter
|
||||
|
||||
import org.openrndr.draw.Drawer
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
import kotlin.jvm.JvmName
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
@JvmName("drawerWriter")
|
||||
fun <T> Drawer.writer(f: TextWriter.() -> T): T {
|
||||
contract {
|
||||
callsInPlace(f, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return writer(this, f)
|
||||
}
|
||||
14
orx-text-writer/src/commonMain/kotlin/ProgramExtensions.kt
Normal file
14
orx-text-writer/src/commonMain/kotlin/ProgramExtensions.kt
Normal file
@@ -0,0 +1,14 @@
|
||||
package org.openrndr.extra.textwriter
|
||||
|
||||
import org.openrndr.Program
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.InvocationKind
|
||||
import kotlin.contracts.contract
|
||||
|
||||
@OptIn(ExperimentalContracts::class)
|
||||
fun <T> Program.writer(f: TextWriter.() -> T): T {
|
||||
contract {
|
||||
callsInPlace(f, InvocationKind.EXACTLY_ONCE)
|
||||
}
|
||||
return writer(drawer, f)
|
||||
}
|
||||
211
orx-text-writer/src/commonMain/kotlin/TextWriter.kt
Normal file
211
orx-text-writer/src/commonMain/kotlin/TextWriter.kt
Normal file
@@ -0,0 +1,211 @@
|
||||
package org.openrndr.extra.textwriter
|
||||
|
||||
import org.openrndr.draw.DrawStyle
|
||||
import org.openrndr.draw.Drawer
|
||||
import org.openrndr.draw.FontImageMap
|
||||
import org.openrndr.math.Vector2
|
||||
import org.openrndr.shape.Rectangle
|
||||
|
||||
class Cursor(var x: Double = 0.0, var y: Double = 0.0) {
|
||||
constructor(cursor: Cursor) : this(cursor.x, cursor.y)
|
||||
}
|
||||
|
||||
@Suppress("unused")
|
||||
class TextToken(val token: String, val x: Double, val y: Double, val width: Double, val tracking: Double)
|
||||
|
||||
class WriteStyle {
|
||||
var leading = 0.0
|
||||
var tracking = 0.0
|
||||
var ellipsis: String? = "…"
|
||||
}
|
||||
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
class TextWriter(val drawerRef: Drawer?) {
|
||||
var cursor = Cursor()
|
||||
var box = Rectangle(
|
||||
Vector2.ZERO, drawerRef?.width?.toDouble() ?: Double.POSITIVE_INFINITY, drawerRef?.height?.toDouble()
|
||||
?: Double.POSITIVE_INFINITY
|
||||
)
|
||||
set(value) {
|
||||
field = value
|
||||
cursor.x = value.corner.x
|
||||
cursor.y = value.corner.y
|
||||
}
|
||||
|
||||
var style = WriteStyle()
|
||||
val styleStack = ArrayDeque<WriteStyle>()
|
||||
|
||||
var leading
|
||||
get() = style.leading
|
||||
set(value) {
|
||||
style.leading = value
|
||||
}
|
||||
|
||||
var tracking
|
||||
get() = style.tracking
|
||||
set(value) {
|
||||
style.tracking = value
|
||||
}
|
||||
|
||||
var ellipsis
|
||||
get() = style.ellipsis
|
||||
set(value) {
|
||||
style.ellipsis = value
|
||||
}
|
||||
|
||||
var drawStyle: DrawStyle = DrawStyle()
|
||||
get() {
|
||||
return drawerRef?.drawStyle ?: field
|
||||
}
|
||||
set(value) {
|
||||
field = drawStyle
|
||||
}
|
||||
|
||||
fun newLine() {
|
||||
cursor.x = box.corner.x
|
||||
cursor.y += (drawStyle.fontMap?.leading ?: 0.0) + style.leading
|
||||
}
|
||||
|
||||
fun gaplessNewLine() {
|
||||
cursor.x = box.corner.x
|
||||
cursor.y += drawStyle.fontMap?.height ?: 0.0
|
||||
}
|
||||
|
||||
fun move(x: Double, y: Double) {
|
||||
cursor.x += x
|
||||
cursor.y += y
|
||||
}
|
||||
|
||||
fun textWidth(text: String): Double =
|
||||
text.sumOf {
|
||||
((drawStyle.fontMap as FontImageMap).glyphMetrics[it]?.advanceWidth ?: 0.0) + style.tracking
|
||||
} - (text.count { it == ' ' } + 1) * style.tracking
|
||||
|
||||
/**
|
||||
* Draw text
|
||||
* @param text the text to write, may contain newlines
|
||||
* @param visible draw the text when set to true, when set to false only type setting is performed
|
||||
* @return a list of [TextToken] instances
|
||||
*/
|
||||
fun text(text: String, visible: Boolean = true): List<TextToken> {
|
||||
// Triggers loading the default font (if needed) by accessing .fontMap
|
||||
// otherwise makeRenderTokens() is not aware of the default font.
|
||||
drawerRef?.fontMap
|
||||
|
||||
val renderTokens = makeTextTokens(text, false)
|
||||
|
||||
if (visible) {
|
||||
drawTextTokens(renderTokens)
|
||||
}
|
||||
return renderTokens
|
||||
}
|
||||
|
||||
/**
|
||||
* Draw pre-set text tokens.
|
||||
* @param tokens a list of [TextToken] instances
|
||||
* @since 0.4.3
|
||||
*/
|
||||
fun drawTextTokens(tokens: List<TextToken>) {
|
||||
drawerRef?.let { d ->
|
||||
val renderer = d.fontImageMapDrawer
|
||||
val queue = renderer.getQueue(tokens.size)
|
||||
tokens.forEach {
|
||||
renderer.queueText(
|
||||
fontMap = d.drawStyle.fontMap!!,
|
||||
text = it.token,
|
||||
x = it.x,
|
||||
y = it.y,
|
||||
tracking = style.tracking,
|
||||
kerning = drawStyle.kerning,
|
||||
textSetting = drawStyle.textSetting,
|
||||
queue
|
||||
)
|
||||
}
|
||||
renderer.flush(d.context, d.drawStyle, queue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeTextTokens(text: String, mustFit: Boolean = false): List<TextToken> {
|
||||
drawStyle.fontMap?.let { font ->
|
||||
|
||||
var fits = true
|
||||
font as FontImageMap
|
||||
val lines = text.split("((?<=\n)|(?=\n))".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
|
||||
val tokens = mutableListOf<String>()
|
||||
lines.forEach { line ->
|
||||
val lineTokens = line.split(" ")
|
||||
tokens.addAll(lineTokens)
|
||||
}
|
||||
|
||||
val localCursor = Cursor(cursor)
|
||||
|
||||
val spaceWidth = (font.glyphMetrics[' ']?.advanceWidth ?: error("no metrics for space"))
|
||||
val verticalSpace = style.leading + font.leading
|
||||
|
||||
val textTokens = mutableListOf<TextToken>()
|
||||
|
||||
tokenLoop@ for (i in 0 until tokens.size) {
|
||||
val token = tokens[i]
|
||||
if (token == "\n") {
|
||||
localCursor.x = box.corner.x
|
||||
localCursor.y += verticalSpace
|
||||
} else {
|
||||
val tokenWidth = token.sumOf {
|
||||
(font.glyphMetrics[it]?.advanceWidth ?: 0.0)
|
||||
} + style.tracking * (token.length - 1).coerceAtLeast(0)
|
||||
if (localCursor.x + tokenWidth < box.x + box.width && localCursor.y <= box.y + box.height) run {
|
||||
val textToken = TextToken(token, localCursor.x, localCursor.y, tokenWidth, style.tracking)
|
||||
emitToken(localCursor, textTokens, textToken)
|
||||
} else {
|
||||
if (localCursor.y > box.corner.y + box.height) {
|
||||
fits = false
|
||||
}
|
||||
if (localCursor.y + verticalSpace <= box.y + box.height) {
|
||||
localCursor.y += verticalSpace
|
||||
localCursor.x = box.x
|
||||
|
||||
emitToken(
|
||||
localCursor,
|
||||
textTokens,
|
||||
TextToken(token, localCursor.x, localCursor.y, tokenWidth, style.tracking)
|
||||
)
|
||||
} else {
|
||||
if (!mustFit && style.ellipsis != null && cursor.y <= box.y + box.height) {
|
||||
emitToken(
|
||||
localCursor, textTokens, TextToken(
|
||||
style.ellipsis
|
||||
?: "", localCursor.x, localCursor.y, tokenWidth, style.tracking
|
||||
)
|
||||
)
|
||||
break@tokenLoop
|
||||
} else {
|
||||
fits = false
|
||||
}
|
||||
}
|
||||
}
|
||||
localCursor.x += tokenWidth
|
||||
|
||||
if (i != tokens.lastIndex) {
|
||||
localCursor.x += spaceWidth + tracking
|
||||
}
|
||||
}
|
||||
}
|
||||
if (fits || (!fits && !mustFit)) {
|
||||
cursor = Cursor(localCursor)
|
||||
} else {
|
||||
textTokens.clear()
|
||||
}
|
||||
return textTokens
|
||||
}
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
private fun emitToken(cursor: Cursor, textTokens: MutableList<TextToken>, textToken: TextToken) {
|
||||
textTokens.add(textToken)
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> writer(drawer: Drawer, f: TextWriter.() -> T): T {
|
||||
val textWriter = TextWriter(drawer)
|
||||
return textWriter.f()
|
||||
}
|
||||
@@ -91,6 +91,7 @@ include(
|
||||
"orx-depth-camera",
|
||||
"orx-jvm:orx-depth-camera-calibrator",
|
||||
"orx-view-box",
|
||||
"orx-text-writer",
|
||||
"orx-turtle"
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user