diff --git a/mosaic-runtime/api/mosaic-runtime.api b/mosaic-runtime/api/mosaic-runtime.api index 27000adb2..a7a9a023f 100644 --- a/mosaic-runtime/api/mosaic-runtime.api +++ b/mosaic-runtime/api/mosaic-runtime.api @@ -1,4 +1,16 @@ +public abstract interface class com/jakewharton/mosaic/Mosaic { + public abstract fun awaitComplete (Lkotlin/coroutines/Continuation;)Ljava/lang/Object; + public abstract fun cancel ()V + public abstract fun dump ()Ljava/lang/String; + public abstract fun getTerminalState ()Landroidx/compose/runtime/MutableState; + public abstract fun paint ()Lcom/jakewharton/mosaic/TextCanvas; + public abstract fun paintStaticsTo (Landroidx/collection/MutableObjectList;)V + public abstract fun sendKeyEvent (Lcom/jakewharton/mosaic/layout/KeyEvent;)V + public abstract fun setContent (Lkotlin/jvm/functions/Function2;)V +} + public final class com/jakewharton/mosaic/MosaicKt { + public static final fun Mosaic (Lkotlin/coroutines/CoroutineContext;Lkotlin/jvm/functions/Function1;)Lcom/jakewharton/mosaic/Mosaic; public static final fun renderMosaic (Lkotlin/jvm/functions/Function2;)Ljava/lang/String; public static final fun runMosaic (Lkotlin/jvm/functions/Function2;Lkotlin/coroutines/Continuation;)Ljava/lang/Object; public static final fun runMosaicBlocking (Lkotlin/jvm/functions/Function2;)V @@ -17,6 +29,13 @@ public final class com/jakewharton/mosaic/TerminalKt { public static final fun getLocalTerminal ()Landroidx/compose/runtime/ProvidableCompositionLocal; } +public abstract interface class com/jakewharton/mosaic/TextCanvas { + public abstract fun appendRowTo (Ljava/lang/Appendable;ILcom/jakewharton/mosaic/ui/AnsiLevel;)V + public abstract fun getHeight ()I + public abstract fun getWidth ()I + public abstract fun render (Lcom/jakewharton/mosaic/ui/AnsiLevel;)Ljava/lang/String; +} + public final class com/jakewharton/mosaic/layout/AspectRatioKt { public static final fun aspectRatio (Lcom/jakewharton/mosaic/modifier/Modifier;FZ)Lcom/jakewharton/mosaic/modifier/Modifier; public static synthetic fun aspectRatio$default (Lcom/jakewharton/mosaic/modifier/Modifier;FZILjava/lang/Object;)Lcom/jakewharton/mosaic/modifier/Modifier; @@ -368,6 +387,16 @@ public abstract interface class com/jakewharton/mosaic/ui/Alignment$Vertical { public abstract fun align (II)I } +public final class com/jakewharton/mosaic/ui/AnsiLevel : java/lang/Enum { + public static final field ANSI16 Lcom/jakewharton/mosaic/ui/AnsiLevel; + public static final field ANSI256 Lcom/jakewharton/mosaic/ui/AnsiLevel; + public static final field NONE Lcom/jakewharton/mosaic/ui/AnsiLevel; + public static final field TRUECOLOR Lcom/jakewharton/mosaic/ui/AnsiLevel; + public static fun getEntries ()Lkotlin/enums/EnumEntries; + public static fun valueOf (Ljava/lang/String;)Lcom/jakewharton/mosaic/ui/AnsiLevel; + public static fun values ()[Lcom/jakewharton/mosaic/ui/AnsiLevel; +} + public final class com/jakewharton/mosaic/ui/Arrangement { public static final field $stable I public static final field INSTANCE Lcom/jakewharton/mosaic/ui/Arrangement; diff --git a/mosaic-runtime/api/mosaic-runtime.klib.api b/mosaic-runtime/api/mosaic-runtime.klib.api index c77d7d781..bdc3ca8ca 100644 --- a/mosaic-runtime/api/mosaic-runtime.klib.api +++ b/mosaic-runtime/api/mosaic-runtime.klib.api @@ -25,6 +25,19 @@ final enum class com.jakewharton.mosaic.layout/IntrinsicSize : kotlin/Enum // com.jakewharton.mosaic.layout/IntrinsicSize.values|values#static(){}[0] } +final enum class com.jakewharton.mosaic.ui/AnsiLevel : kotlin/Enum { // com.jakewharton.mosaic.ui/AnsiLevel|null[0] + enum entry ANSI16 // com.jakewharton.mosaic.ui/AnsiLevel.ANSI16|null[0] + enum entry ANSI256 // com.jakewharton.mosaic.ui/AnsiLevel.ANSI256|null[0] + enum entry NONE // com.jakewharton.mosaic.ui/AnsiLevel.NONE|null[0] + enum entry TRUECOLOR // com.jakewharton.mosaic.ui/AnsiLevel.TRUECOLOR|null[0] + + final val entries // com.jakewharton.mosaic.ui/AnsiLevel.entries|#static{}entries[0] + final fun (): kotlin.enums/EnumEntries // com.jakewharton.mosaic.ui/AnsiLevel.entries.|#static(){}[0] + + final fun valueOf(kotlin/String): com.jakewharton.mosaic.ui/AnsiLevel // com.jakewharton.mosaic.ui/AnsiLevel.valueOf|valueOf#static(kotlin.String){}[0] + final fun values(): kotlin/Array // com.jakewharton.mosaic.ui/AnsiLevel.values|values#static(){}[0] +} + abstract fun interface com.jakewharton.mosaic.layout/MeasurePolicy { // com.jakewharton.mosaic.layout/MeasurePolicy|null[0] abstract fun (com.jakewharton.mosaic.layout/MeasureScope).measure(kotlin.collections/List, com.jakewharton.mosaic.ui.unit/Constraints): com.jakewharton.mosaic.layout/MeasureResult // com.jakewharton.mosaic.layout/MeasurePolicy.measure|measure@com.jakewharton.mosaic.layout.MeasureScope(kotlin.collections.List;com.jakewharton.mosaic.ui.unit.Constraints){}[0] open fun maxIntrinsicHeight(kotlin.collections/List, kotlin/Int): kotlin/Int // com.jakewharton.mosaic.layout/MeasurePolicy.maxIntrinsicHeight|maxIntrinsicHeight(kotlin.collections.List;kotlin.Int){}[0] @@ -183,6 +196,29 @@ abstract interface com.jakewharton.mosaic.ui/RowScope { // com.jakewharton.mosai abstract fun (com.jakewharton.mosaic.modifier/Modifier).weight(kotlin/Float, kotlin/Boolean = ...): com.jakewharton.mosaic.modifier/Modifier // com.jakewharton.mosaic.ui/RowScope.weight|weight@com.jakewharton.mosaic.modifier.Modifier(kotlin.Float;kotlin.Boolean){}[0] } +abstract interface com.jakewharton.mosaic/Mosaic { // com.jakewharton.mosaic/Mosaic|null[0] + abstract val terminalState // com.jakewharton.mosaic/Mosaic.terminalState|{}terminalState[0] + abstract fun (): androidx.compose.runtime/MutableState // com.jakewharton.mosaic/Mosaic.terminalState.|(){}[0] + + abstract fun cancel() // com.jakewharton.mosaic/Mosaic.cancel|cancel(){}[0] + abstract fun dump(): kotlin/String // com.jakewharton.mosaic/Mosaic.dump|dump(){}[0] + abstract fun paint(): com.jakewharton.mosaic/TextCanvas // com.jakewharton.mosaic/Mosaic.paint|paint(){}[0] + abstract fun paintStaticsTo(androidx.collection/MutableObjectList) // com.jakewharton.mosaic/Mosaic.paintStaticsTo|paintStaticsTo(androidx.collection.MutableObjectList){}[0] + abstract fun sendKeyEvent(com.jakewharton.mosaic.layout/KeyEvent) // com.jakewharton.mosaic/Mosaic.sendKeyEvent|sendKeyEvent(com.jakewharton.mosaic.layout.KeyEvent){}[0] + abstract fun setContent(kotlin/Function2) // com.jakewharton.mosaic/Mosaic.setContent|setContent(kotlin.Function2){}[0] + abstract suspend fun awaitComplete() // com.jakewharton.mosaic/Mosaic.awaitComplete|awaitComplete(){}[0] +} + +abstract interface com.jakewharton.mosaic/TextCanvas { // com.jakewharton.mosaic/TextCanvas|null[0] + abstract val height // com.jakewharton.mosaic/TextCanvas.height|{}height[0] + abstract fun (): kotlin/Int // com.jakewharton.mosaic/TextCanvas.height.|(){}[0] + abstract val width // com.jakewharton.mosaic/TextCanvas.width|{}width[0] + abstract fun (): kotlin/Int // com.jakewharton.mosaic/TextCanvas.width.|(){}[0] + + abstract fun appendRowTo(kotlin.text/Appendable, kotlin/Int, com.jakewharton.mosaic.ui/AnsiLevel) // com.jakewharton.mosaic/TextCanvas.appendRowTo|appendRowTo(kotlin.text.Appendable;kotlin.Int;com.jakewharton.mosaic.ui.AnsiLevel){}[0] + abstract fun render(com.jakewharton.mosaic.ui/AnsiLevel): kotlin/String // com.jakewharton.mosaic/TextCanvas.render|render(com.jakewharton.mosaic.ui.AnsiLevel){}[0] +} + sealed interface com.jakewharton.mosaic.layout/DrawStyle { // com.jakewharton.mosaic.layout/DrawStyle|null[0] final class Stroke : com.jakewharton.mosaic.layout/DrawStyle { // com.jakewharton.mosaic.layout/DrawStyle.Stroke|null[0] constructor (kotlin/Int = ...) // com.jakewharton.mosaic.layout/DrawStyle.Stroke.|(kotlin.Int){}[0] @@ -752,6 +788,7 @@ final fun com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_RowColumnMeasureme final fun com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_RowColumnParentData$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_RowColumnParentData$stableprop_getter|com_jakewharton_mosaic_ui_RowColumnParentData$stableprop_getter(){}[0] final fun com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_RowScopeInstance$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_RowScopeInstance$stableprop_getter|com_jakewharton_mosaic_ui_RowScopeInstance$stableprop_getter(){}[0] final fun com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_VerticalAlignModifier$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic.ui/com_jakewharton_mosaic_ui_VerticalAlignModifier$stableprop_getter|com_jakewharton_mosaic_ui_VerticalAlignModifier$stableprop_getter(){}[0] +final fun com.jakewharton.mosaic/Mosaic(kotlin.coroutines/CoroutineContext, kotlin/Function1): com.jakewharton.mosaic/Mosaic // com.jakewharton.mosaic/Mosaic|Mosaic(kotlin.coroutines.CoroutineContext;kotlin.Function1){}[0] final fun com.jakewharton.mosaic/com_jakewharton_mosaic_AnsiRendering$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic/com_jakewharton_mosaic_AnsiRendering$stableprop_getter|com_jakewharton_mosaic_AnsiRendering$stableprop_getter(){}[0] final fun com.jakewharton.mosaic/com_jakewharton_mosaic_DebugRendering$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic/com_jakewharton_mosaic_DebugRendering$stableprop_getter|com_jakewharton_mosaic_DebugRendering$stableprop_getter(){}[0] final fun com.jakewharton.mosaic/com_jakewharton_mosaic_GlobalSnapshotManager$stableprop_getter(): kotlin/Int // com.jakewharton.mosaic/com_jakewharton_mosaic_GlobalSnapshotManager$stableprop_getter|com_jakewharton_mosaic_GlobalSnapshotManager$stableprop_getter(){}[0] diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/layout/Node.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/layout/Node.kt index a81fe43e0..e9ac23ba5 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/layout/Node.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/layout/Node.kt @@ -1,6 +1,7 @@ package com.jakewharton.mosaic.layout import androidx.collection.MutableObjectList +import com.jakewharton.mosaic.TextCanvas import com.jakewharton.mosaic.TextSurface import com.jakewharton.mosaic.layout.Placeable.PlacementScope import com.jakewharton.mosaic.modifier.Modifier @@ -139,7 +140,7 @@ internal class MosaicNode( * Draw this node to a [TextSurface]. * A call to [measureAndPlace] must precede calls to this function. */ - fun paint(): TextSurface { + fun paint(): TextCanvas { val surface = TextSurface(width, height) topLayer.drawTo(surface) return surface @@ -149,7 +150,7 @@ internal class MosaicNode( * Append any static [TextSurfaces][TextSurface] to [statics]. * A call to [measureAndPlace] must precede calls to this function. */ - fun paintStaticsTo(statics: MutableObjectList) { + fun paintStaticsTo(statics: MutableObjectList) { for (index in children.indices) { val child = children[index] if (isStatic) { diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/mosaic.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/mosaic.kt index fd915461e..e0d419bae 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/mosaic.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/mosaic.kt @@ -8,6 +8,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.Composition import androidx.compose.runtime.CompositionLocalProvider import androidx.compose.runtime.MonotonicFrameClock +import androidx.compose.runtime.MutableState import androidx.compose.runtime.Recomposer import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.snapshots.ObserverHandle @@ -124,7 +125,7 @@ private fun createRendering(ansiLevel: AnsiLevel = AnsiLevel.TRUECOLOR): Renderi } } -private fun CoroutineScope.updateTerminalInfo(terminal: MordantTerminal, mosaic: MosaicComposition) { +private fun CoroutineScope.updateTerminalInfo(terminal: MordantTerminal, mosaic: Mosaic) { launch(start = UNDISPATCHED) { while (true) { val currentTerminalInfo = mosaic.terminalState.value @@ -139,7 +140,7 @@ private fun CoroutineScope.updateTerminalInfo(terminal: MordantTerminal, mosaic: } } -private fun CoroutineScope.readRawModeKeys(rawMode: RawModeScope, mosaic: MosaicComposition) { +private fun CoroutineScope.readRawModeKeys(rawMode: RawModeScope, mosaic: Mosaic) { launch(Dispatchers.IO) { while (isActive) { val keyboardEvent = rawMode.readKeyOrNull(10.milliseconds) ?: continue @@ -154,14 +155,27 @@ private fun CoroutineScope.readRawModeKeys(rawMode: RawModeScope, mosaic: Mosaic } } -internal interface Mosaic { - fun paint(): TextSurface - fun paintStaticsTo(list: MutableObjectList) - fun dump(): String +public interface Mosaic { + public fun setContent(content: @Composable () -> Unit) + + public fun sendKeyEvent(keyEvent: KeyEvent) + public val terminalState: MutableState + + public fun paint(): TextCanvas + public fun paintStaticsTo(list: MutableObjectList) + public fun dump(): String + + public suspend fun awaitComplete() + public fun cancel() } -// https://en.wikipedia.org/wiki/VT52 -private val DefaultTestTerminalSize = IntSize(width = 80, height = 24) +// TODO This function signature is all kinds of broken for structured concurrency! +public fun Mosaic( + coroutineContext: CoroutineContext, + onDraw: (Mosaic) -> Unit, +): Mosaic { + return MosaicComposition(coroutineContext, onDraw) +} internal class MosaicComposition( coroutineContext: CoroutineContext, @@ -177,7 +191,7 @@ internal class MosaicComposition( val scope = CoroutineScope(composeContext) private val keyEvents = Channel(UNLIMITED) - val terminalState = mutableStateOf(Terminal(DefaultTestTerminalSize)) + override val terminalState = mutableStateOf(Terminal(DefaultTestTerminalSize)) private val applier = MosaicNodeApplier { needLayout = true } val rootNode = applier.root @@ -218,13 +232,13 @@ internal class MosaicComposition( onDraw(this) } - override fun paint(): TextSurface { + override fun paint(): TextCanvas { return Snapshot.observe(readObserver = drawBlockStateReadObserver) { rootNode.paint() } } - override fun paintStaticsTo(list: MutableObjectList) { + override fun paintStaticsTo(list: MutableObjectList) { rootNode.paintStaticsTo(list) } @@ -287,7 +301,7 @@ internal class MosaicComposition( } } - fun setContent(content: @Composable () -> Unit) { + override fun setContent(content: @Composable () -> Unit) { composition.setContent { CompositionLocalProvider( LocalTerminal provides terminalState.value, @@ -297,11 +311,11 @@ internal class MosaicComposition( performLayout() } - fun sendKeyEvent(keyEvent: KeyEvent) { + override fun sendKeyEvent(keyEvent: KeyEvent) { keyEvents.trySend(keyEvent) } - suspend fun awaitComplete() { + override suspend fun awaitComplete() { try { val effectJob = checkNotNull(recomposer.effectCoroutineContext[Job]) { "No Job in effectCoroutineContext of recomposer" @@ -322,7 +336,7 @@ internal class MosaicComposition( } } - fun cancel() { + override fun cancel() { applyObserverHandle.dispose() recomposer.cancel() job.cancel() @@ -333,6 +347,9 @@ internal class MosaicComposition( } } +// https://en.wikipedia.org/wiki/VT52 +private val DefaultTestTerminalSize = IntSize(width = 80, height = 24) + internal class MosaicNodeApplier( private val onChanges: () -> Unit = {}, ) : AbstractApplier( diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/rendering.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/rendering.kt index e3b2b0698..3024c0287 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/rendering.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/rendering.kt @@ -35,7 +35,7 @@ internal class DebugRendering( appendLine(mosaic.dump()) appendLine() - val statics = mutableObjectListOf() + val statics = mutableObjectListOf() try { mosaic.paintStaticsTo(statics) if (statics.isNotEmpty()) { @@ -69,7 +69,7 @@ internal class AnsiRendering( private val ansiLevel: AnsiLevel = AnsiLevel.TRUECOLOR, ) : Rendering { private val stringBuilder = StringBuilder(100) - private val staticSurfaces = mutableObjectListOf() + private val staticSurfaces = mutableObjectListOf() private var lastHeight = 0 override fun render(mosaic: Mosaic): CharSequence { @@ -83,7 +83,7 @@ internal class AnsiRendering( append(cursorUp) } - fun appendSurface(canvas: TextSurface) { + fun appendSurface(canvas: TextCanvas) { for (row in 0 until canvas.height) { canvas.appendRowTo(this, row, ansiLevel) if (staleLines-- > 0) { diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/surface.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/surface.kt index 61f5c0f6d..c5f8529c5 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/surface.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/surface.kt @@ -18,10 +18,18 @@ import de.cketti.codepoints.appendCodePoint private val blankPixel = TextPixel(' ') +public interface TextCanvas { + public val height: Int + public val width: Int + + public fun render(ansiLevel: AnsiLevel): String + public fun appendRowTo(appendable: Appendable, row: Int, ansiLevel: AnsiLevel) +} + internal class TextSurface( - val width: Int, - val height: Int, -) { + override val width: Int, + override val height: Int, +) : TextCanvas { var translationX = 0 var translationY = 0 @@ -35,7 +43,7 @@ internal class TextSurface( return cells[y * width + x] } - fun appendRowTo(appendable: Appendable, row: Int, ansiLevel: AnsiLevel) { + override fun appendRowTo(appendable: Appendable, row: Int, ansiLevel: AnsiLevel) { // Reused heap allocation for building ANSI attributes inside the loop. val attributes = mutableIntListOf() @@ -145,7 +153,7 @@ internal class TextSurface( } } - fun render(ansiLevel: AnsiLevel): String = buildString { + override fun render(ansiLevel: AnsiLevel): String = buildString { if (height > 0) { for (rowIndex in 0 until height) { appendRowTo(this, rowIndex, ansiLevel) diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/ui/AnsiLevel.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/ui/AnsiLevel.kt index e2ac0d058..31a94fa58 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/ui/AnsiLevel.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/ui/AnsiLevel.kt @@ -1,6 +1,6 @@ package com.jakewharton.mosaic.ui -internal enum class AnsiLevel { +public enum class AnsiLevel { NONE, ANSI16, ANSI256, diff --git a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/CounterTest.kt b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/CounterTest.kt index 45c915716..3bc85657e 100644 --- a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/CounterTest.kt +++ b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/CounterTest.kt @@ -13,6 +13,7 @@ import com.jakewharton.mosaic.ui.Alignment import com.jakewharton.mosaic.ui.Box import com.jakewharton.mosaic.ui.Column import com.jakewharton.mosaic.ui.Text +import com.jakewharton.mosaic.ui.unit.IntSize import kotlin.test.Test import kotlinx.coroutines.delay import kotlinx.coroutines.test.runTest @@ -29,13 +30,13 @@ class CounterTest { @Test fun counterInTerminalCenter() = runTest { runMosaicTest { - setTerminalSize(width = 30, height = 1) + terminalState.value = Terminal(size = IntSize(width = 30, height = 1)) setCounterInTerminalCenter() for (count in 0..9) { assertThat(awaitSnapshot()).isEqualTo(" The count is: $count ") } - setTerminalSize(width = 20, height = 1) + terminalState.value = Terminal(size = IntSize(width = 20, height = 1)) // After changing the terminal size, we wait for the counter to increase before getting a // new snapshot, otherwise there will be the previous value (9) and a different output size. diff --git a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/TestMosaic.kt b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/TestMosaic.kt index bc6cfc899..f461308f0 100644 --- a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/TestMosaic.kt +++ b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/TestMosaic.kt @@ -1,11 +1,11 @@ package com.jakewharton.mosaic +import androidx.collection.MutableObjectList import androidx.compose.runtime.BroadcastFrameClock import androidx.compose.runtime.Composable import com.jakewharton.mosaic.layout.KeyEvent import com.jakewharton.mosaic.layout.MosaicNode import com.jakewharton.mosaic.ui.AnsiLevel -import com.jakewharton.mosaic.ui.unit.IntSize import kotlin.coroutines.CoroutineContext import kotlin.time.Duration import kotlin.time.Duration.Companion.seconds @@ -26,23 +26,17 @@ internal suspend fun runMosaicTest( block: suspend TestMosaic.() -> R, ): R { return coroutineScope { - val testMosaicComposition = RealTestMosaic( + val tester = RealTestMosaic( coroutineContext = coroutineContext, snapshotStrategy = snapshotStrategy, ) - val result = block.invoke(testMosaicComposition) - testMosaicComposition.mosaicComposition.cancel() + val result = block.invoke(tester) + tester.cancel() result } } -internal interface TestMosaic { - fun setContent(content: @Composable () -> Unit) - - fun setTerminalSize(width: Int, height: Int) - - fun sendKeyEvent(keyEvent: KeyEvent) - +internal interface TestMosaic : Mosaic { suspend fun awaitSnapshot(duration: Duration = 1.seconds): T } @@ -57,22 +51,14 @@ private class RealTestMosaic( private var hasChanges = false private val clock = BroadcastFrameClock() - val mosaicComposition = MosaicComposition( + val mosaic = Mosaic( coroutineContext = coroutineContext + clock, onDraw = { hasChanges = true }, ) override fun setContent(content: @Composable () -> Unit) { contentSet = true - mosaicComposition.setContent(content) - } - - override fun setTerminalSize(width: Int, height: Int) { - mosaicComposition.terminalState.value = Terminal(size = IntSize(width, height)) - } - - override fun sendKeyEvent(keyEvent: KeyEvent) { - mosaicComposition.sendKeyEvent(keyEvent) + mosaic.setContent(content) } override suspend fun awaitSnapshot(duration: Duration): T { @@ -90,7 +76,29 @@ private class RealTestMosaic( } hasChanges = false - return snapshotStrategy.create(mosaicComposition) + return snapshotStrategy.create(mosaic) + } + + override fun sendKeyEvent(keyEvent: KeyEvent) { + mosaic.sendKeyEvent(keyEvent) + } + + override val terminalState get() = mosaic.terminalState + + override fun paint() = mosaic.paint() + + override fun paintStaticsTo(list: MutableObjectList) { + mosaic.paintStaticsTo(list) + } + + override fun dump() = mosaic.dump() + + override suspend fun awaitComplete() { + mosaic.awaitComplete() + } + + override fun cancel() { + mosaic.cancel() } }