From 9df5a80dc58d19d8ef607e3e9cc5849788f77f92 Mon Sep 17 00:00:00 2001 From: EpicDima Date: Sat, 2 Dec 2023 15:00:29 +0300 Subject: [PATCH] Changing logic beyond the positive boundaries of canvas --- .../kotlin/com/jakewharton/mosaic/canvas.kt | 35 +- .../jakewharton/mosaic/layout/OffsetTest.kt | 370 +++++++++++++++--- 2 files changed, 313 insertions(+), 92 deletions(-) diff --git a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/canvas.kt b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/canvas.kt index 93c8b601..93d797b9 100644 --- a/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/canvas.kt +++ b/mosaic-runtime/src/commonMain/kotlin/com/jakewharton/mosaic/canvas.kt @@ -20,51 +20,24 @@ internal interface TextCanvas { } internal class TextSurface( - initialWidth: Int, - initialHeight: Int, + override val width: Int, + override val height: Int, ) : TextCanvas { - private var realWidth: Int = initialWidth - private var realHeight: Int = initialHeight - - override val width: Int get() = realWidth - override val height: Int get() = realHeight override var translationX = 0 override var translationY = 0 - private val rows = MutableList(height) { createBlankRow(width) } + private val rows = Array(height) { Array(width) { newBlankPixel } } override operator fun get(row: Int, column: Int): TextPixel { val x = translationX + column val y = translationY + row - if (x < 0 || y < 0) { + if (x >= width || y >= height || x < 0 || y < 0) { return reusableDirtyPixel } - val widthDiff = x - width + 1 - if (widthDiff > 0) { - if (widthDiff == 1) { - rows.forEach { it.add(newBlankPixel) } - } else { - rows.forEach { it.addAll(createBlankRow(widthDiff)) } - } - realWidth += widthDiff - } - val heightDiff = y - height + 1 - if (heightDiff > 0) { - if (heightDiff == 1) { - rows.add(createBlankRow(width)) - } else { - rows.addAll(MutableList(heightDiff) { createBlankRow(width) }) - } - realHeight += heightDiff - } return rows[y][x] } - private inline fun createBlankRow(size: Int): MutableList { - return MutableList(size) { newBlankPixel } - } - fun appendRowTo(appendable: Appendable, row: Int) { // Reused heap allocation for building ANSI attributes inside the loop. val attributes = mutableListOf() diff --git a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/layout/OffsetTest.kt b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/layout/OffsetTest.kt index 03c45b5a..c15759d6 100644 --- a/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/layout/OffsetTest.kt +++ b/mosaic-runtime/src/commonTest/kotlin/com/jakewharton/mosaic/layout/OffsetTest.kt @@ -11,7 +11,6 @@ import com.jakewharton.mosaic.s import com.jakewharton.mosaic.ui.Box import com.jakewharton.mosaic.ui.unit.IntOffset import kotlin.test.Test -import kotlin.test.assertFails class OffsetTest { @Test fun offsetHorizontalFixed() { @@ -33,24 +32,64 @@ class OffsetTest { ) } + @Test fun offsetHorizontalFixedFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.requiredSize(size).offset(30, 0)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetHorizontalFixedNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(-30, 0)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetHorizontalFixedBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(30, 0)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.requiredSize(size).offset(4, 0)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $TestChar$TestChar + | $TestChar$TestChar + | $TestChar$TestChar + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetHorizontalFixedNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(-3, 0)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(-1, 0)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar $s + |$TestChar$TestChar $s + |$TestChar$TestChar $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetVerticalFixed() { @@ -72,24 +111,64 @@ class OffsetTest { ) } + @Test fun offsetVerticalFixedFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(0, 40)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetVerticalFixedNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(0, -40)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetVerticalFixedBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(0, 40)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(0, 4)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $s + | $s + | $s + | $s + |$TestChar$TestChar$TestChar $s + |$TestChar$TestChar$TestChar $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetVerticalFixedNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(0, -4)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(0, -1)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar$TestChar $s + |$TestChar$TestChar$TestChar $s + | $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetFixed() { @@ -111,24 +190,64 @@ class OffsetTest { ) } + @Test fun offsetFixedFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(30, 40)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetFixedNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(-30, -40)) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetFixedBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(30, 40)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(4, 5)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $s + | $s + | $s + | $s + | $s + | $TestChar$TestChar + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetFixedNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset(-3, -4)) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset(-1, -2)) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar $s + | $s + | $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetFixedDebug() { @@ -155,24 +274,64 @@ class OffsetTest { ) } + @Test fun offsetHorizontalModifiableFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(30, 0) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetHorizontalModifiableNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(-30, 0) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetHorizontalModifiableBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(30, 0) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(4, 0) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $TestChar$TestChar + | $TestChar$TestChar + | $TestChar$TestChar + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetHorizontalModifiableNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(-3, 0) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(-1, 0) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar $s + |$TestChar$TestChar $s + |$TestChar$TestChar $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetVerticalModifiable() { @@ -194,24 +353,64 @@ class OffsetTest { ) } + @Test fun offsetVerticalModifiableFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(0, 40) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetVerticalModifiableNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(0, -40) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetVerticalModifiableBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(0, 40) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(0, 4) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $s + | $s + | $s + | $s + |$TestChar$TestChar$TestChar $s + |$TestChar$TestChar$TestChar $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetVerticalModifiableNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(0, -4) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(0, -1) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar$TestChar $s + |$TestChar$TestChar$TestChar $s + | $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetModifiable() { @@ -233,24 +432,64 @@ class OffsetTest { ) } + @Test fun offsetModifiableFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(30, 40) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + + @Test fun offsetModifiableNegativeFarBeyondBorders() { + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(-30, -40) }) { + TestFiller(modifier = Modifier.size(1)) + } + } + assertThat(actual).isEqualTo(getBlankStringBlock(size)) + } + @Test fun offsetModifiableBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(30, 40) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(4, 5) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + | $s + | $s + | $s + | $s + | $s + | $TestChar$TestChar + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetModifiableNegativeBeyondBorders() { - assertFails { - renderMosaic { - Box(modifier = Modifier.size(6).offset { IntOffset(-3, -4) }) { - TestFiller(modifier = Modifier.size(1)) - } + val size = 6 + val actual = renderMosaic { + Box(modifier = Modifier.size(size).offset { IntOffset(-1, -2) }) { + TestFiller(modifier = Modifier.size(3)) } } + assertThat(actual).isEqualTo( + """ + |$TestChar$TestChar $s + | $s + | $s + | $s + | $s + | $s + | + """.trimMargin().replaceLineEndingsWithCRLF(), + ) } @Test fun offsetModifiableDebug() { @@ -258,4 +497,13 @@ class OffsetTest { val actual = Modifier.offset(offsetLambda).toString() assertThat(actual).isEqualTo("ChangeableOffset(offset=$offsetLambda)") } + + private fun getBlankStringBlock(size: Int): String { + val line = s.repeat(size) + return buildString { + repeat(size) { + appendLine(line) + } + }.replaceLineEndingsWithCRLF() + } }