diff --git a/components/generator/common/src/main/kotlin/io/github/composegears/valkyrie/generator/ext/CodeBlock.kt b/components/generator/common/src/main/kotlin/io/github/composegears/valkyrie/generator/ext/CodeBlock.kt index 79b08607..467b7988 100644 --- a/components/generator/common/src/main/kotlin/io/github/composegears/valkyrie/generator/ext/CodeBlock.kt +++ b/components/generator/common/src/main/kotlin/io/github/composegears/valkyrie/generator/ext/CodeBlock.kt @@ -2,8 +2,28 @@ package io.github.composegears.valkyrie.generator.ext import com.squareup.kotlinpoet.CodeBlock +fun CodeBlock.Builder.argumentBlock( + argumentFlow: String, + vararg args: Any?, + isNested: Boolean = false, + block: CodeBlock.Builder.() -> Unit, +) { + add("$argumentFlow\n", *args) + indention(block) + newLine() + add(")") + if (isNested) { + add(",") + newLine() + } +} + fun CodeBlock.Builder.indention(block: CodeBlock.Builder.() -> Unit) { indent() block() unindent() } + +fun CodeBlock.Builder.newLine() { + add("\n") +} diff --git a/components/generator/imagevector/src/main/kotlin/io/github/composegears/valkyrie/generator/imagevector/util/PathBuilder.kt b/components/generator/imagevector/src/main/kotlin/io/github/composegears/valkyrie/generator/imagevector/util/PathBuilder.kt index ce675eb9..7af1c641 100644 --- a/components/generator/imagevector/src/main/kotlin/io/github/composegears/valkyrie/generator/imagevector/util/PathBuilder.kt +++ b/components/generator/imagevector/src/main/kotlin/io/github/composegears/valkyrie/generator/imagevector/util/PathBuilder.kt @@ -9,8 +9,10 @@ import androidx.compose.material.icons.generator.vector.StrokeJoin import androidx.compose.material.icons.generator.vector.VectorNode import com.squareup.kotlinpoet.CodeBlock import com.squareup.kotlinpoet.buildCodeBlock +import io.github.composegears.valkyrie.generator.ext.argumentBlock import io.github.composegears.valkyrie.generator.ext.formatFloat import io.github.composegears.valkyrie.generator.ext.indention +import io.github.composegears.valkyrie.generator.ext.newLine import io.github.composegears.valkyrie.generator.ext.toColorHex import io.github.composegears.valkyrie.generator.imagevector.util.PathParams.FillAlphaParam import io.github.composegears.valkyrie.generator.imagevector.util.PathParams.FillParam @@ -38,7 +40,7 @@ internal fun CodeBlock.Builder.addPath( add( codeBlock = buildCodeBlock { add("%M(", MemberNames.Path) - fillPathArgs(pathParams.first()) + fillPathArgs(param = pathParams.first(), handleMultiline = true) beginControlFlow(")") pathBody() endControlFlow() @@ -69,10 +71,13 @@ internal fun CodeBlock.Builder.addPath( } } -private fun CodeBlock.Builder.fillPathArgs(param: PathParams) { +private fun CodeBlock.Builder.fillPathArgs( + param: PathParams, + handleMultiline: Boolean = false, +) { // TODO: arg "name" missing when (param) { - is FillParam -> fillArg(param) + is FillParam -> fillArg(param, handleMultiline) is FillAlphaParam -> fillAlphaArg(param) is FillTypeParam -> pathFillTypeArg(param) is StrokeAlphaParam -> strokeAlphaArg(param) @@ -84,65 +89,80 @@ private fun CodeBlock.Builder.fillPathArgs(param: PathParams) { } } -private fun CodeBlock.Builder.fillArg(path: FillParam) { +private fun CodeBlock.Builder.fillArg( + path: FillParam, + handleMultiline: Boolean, +) { when (val fill = path.fill) { is Fill.Color -> { add("fill = %M(%M(${fill.colorHex.toColorHex()}))", MemberNames.SolidColor, MemberNames.Color) } is Fill.LinearGradient -> { - add("\n") - indention { - add("fill = %T.linearGradient(\n", ClassNames.Brush) + if (handleMultiline) { + newLine() indention { - add("colorStops = arrayOf(\n") - indention { - add( - fill.colorStops.joinToString(separator = ",\n") { stop -> - "${stop.first.formatFloat()} to %M(${stop.second.toColorHex()})" - }, - *Array(fill.colorStops.size) { MemberNames.Color }, - ) - } - add("\n),\n") - add( - "start = %M(${fill.startX.formatFloat()}, ${fill.startY.formatFloat()}),\n", - MemberNames.Offset, - ) - add( - "end = %M(${fill.endX.formatFloat()}, ${fill.endY.formatFloat()})\n", - MemberNames.Offset, - ) + addLinearGradient(fill) } - add(")\n") + newLine() + } else { + addLinearGradient(fill) } } is Fill.RadialGradient -> { - add("\n") - indention { - add("fill = %T.radialGradient(\n", ClassNames.Brush) + if (handleMultiline) { + newLine() indention { - add("colorStops = arrayOf(\n") - indention { - add( - fill.colorStops.joinToString(separator = ",\n") { stop -> - "${stop.first.formatFloat()} to %M(${stop.second.toColorHex()})" - }, - *Array(fill.colorStops.size) { MemberNames.Color }, - ) - } - add("\n),\n") - add( - "center = %M(${fill.centerX.formatFloat()}, ${fill.centerY.formatFloat()}),\n", - MemberNames.Offset, - ) - add("radius = ${fill.gradientRadius.formatFloat()}\n") + addRadialGradient(fill) } - add(")\n") + newLine() + } else { + addRadialGradient(fill) } } } } +private fun CodeBlock.Builder.addLinearGradient(fill: Fill.LinearGradient) { + argumentBlock("fill = %T.linearGradient(", ClassNames.Brush) { + argumentBlock("colorStops = arrayOf(", isNested = true) { + add( + fill.colorStops.joinToString(separator = ",\n") { stop -> + "${stop.first.formatFloat()} to %M(${stop.second.toColorHex()})" + }, + *Array(fill.colorStops.size) { MemberNames.Color }, + ) + } + add( + "start = %M(${fill.startX.formatFloat()}, ${fill.startY.formatFloat()}),", + MemberNames.Offset, + ) + newLine() + add( + "end = %M(${fill.endX.formatFloat()}, ${fill.endY.formatFloat()})", + MemberNames.Offset, + ) + } +} + +private fun CodeBlock.Builder.addRadialGradient(fill: Fill.RadialGradient) { + argumentBlock("fill = %T.radialGradient(", ClassNames.Brush) { + argumentBlock("colorStops = arrayOf(", isNested = true) { + add( + fill.colorStops.joinToString(separator = ",\n") { stop -> + "${stop.first.formatFloat()} to %M(${stop.second.toColorHex()})" + }, + *Array(fill.colorStops.size) { MemberNames.Color }, + ) + } + add( + "center = %M(${fill.centerX.formatFloat()}, ${fill.centerY.formatFloat()}),", + MemberNames.Offset, + ) + newLine() + add("radius = ${fill.gradientRadius.formatFloat()}") + } +} + private fun CodeBlock.Builder.fillAlphaArg(param: FillAlphaParam) { add("fillAlpha = ${param.fillAlpha.formatFloat()}") } diff --git a/components/generator/imagevector/src/test/kotlin/io/github/composegears/valkyrie/generator/imagevector/SvgGradientParserTest.kt b/components/generator/imagevector/src/test/kotlin/io/github/composegears/valkyrie/generator/imagevector/SvgGradientParserTest.kt index 75b3f4de..3178a997 100644 --- a/components/generator/imagevector/src/test/kotlin/io/github/composegears/valkyrie/generator/imagevector/SvgGradientParserTest.kt +++ b/components/generator/imagevector/src/test/kotlin/io/github/composegears/valkyrie/generator/imagevector/SvgGradientParserTest.kt @@ -270,4 +270,114 @@ class SvgGradientParserTest { """.trimIndent() assertEquals(expectedOutput, output) } + + @Test + fun `svg linear gradient with stroke parsing`() { + val icon = loadIcon("ic_linear_gradient_with_stroke.svg") + val parserOutput = IconParser.toVector(icon) + val output = ImageVectorGenerator.convert( + vector = parserOutput.vector, + kotlinName = parserOutput.kotlinName, + config = ImageVectorGeneratorConfig( + packageName = "io.github.composegears.valkyrie.icons", + packName = "", + nestedPackName = "", + generatePreview = false, + ), + ).content + + val expectedOutput = """ + package io.github.composegears.valkyrie.icons + + import androidx.compose.ui.geometry.Offset + import androidx.compose.ui.graphics.Brush + import androidx.compose.ui.graphics.Color + import androidx.compose.ui.graphics.SolidColor + import androidx.compose.ui.graphics.vector.ImageVector + import androidx.compose.ui.graphics.vector.path + import androidx.compose.ui.unit.dp + + val LinearGradientWithStroke: ImageVector + get() { + if (_LinearGradientWithStroke != null) { + return _LinearGradientWithStroke!! + } + _LinearGradientWithStroke = ImageVector.Builder( + name = "LinearGradientWithStroke", + defaultWidth = 24.dp, + defaultHeight = 24.dp, + viewportWidth = 24f, + viewportHeight = 24f + ).apply { + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0.097f to Color(0xFF0095D5), + 0.301f to Color(0xFF238AD9), + 0.621f to Color(0xFF557BDE), + 0.864f to Color(0xFF7472E2), + 1f to Color(0xFF806EE3) + ), + start = Offset(6.384f, 29.605f), + end = Offset(17.723f, 18.267f) + ), + stroke = SolidColor(Color(0x00000000)) + ) { + moveTo(0f, 24f) + lineTo(12.039f, 11.961f) + lineTo(24f, 24f) + close() + moveTo(0f, 24f) + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0.118f to Color(0xFF0095D5), + 0.418f to Color(0xFF3C83DC), + 0.696f to Color(0xFF6D74E1), + 0.833f to Color(0xFF806EE3) + ), + start = Offset(1.684f, 4.824f), + end = Offset(8.269f, -1.762f) + ), + stroke = SolidColor(Color(0x00000000)) + ) { + moveTo(0f, 0f) + lineTo(12.039f, 0f) + lineTo(0f, 13f) + close() + moveTo(0f, 0f) + } + path( + fill = Brush.linearGradient( + colorStops = arrayOf( + 0.107f to Color(0xFFC757BC), + 0.214f to Color(0xFFD0609A), + 0.425f to Color(0xFFE1725C), + 0.605f to Color(0xFFEE7E2F), + 0.743f to Color(0xFFF58613), + 0.823f to Color(0xFFF88909) + ), + start = Offset(-4.041f, 22.066f), + end = Offset(18.293f, -0.268f) + ), + stroke = SolidColor(Color(0x00000000)) + ) { + moveTo(12.039f, 0f) + lineTo(0f, 12.68f) + lineTo(0f, 24f) + lineTo(24f, 0f) + close() + moveTo(12.039f, 0f) + } + }.build() + + return _LinearGradientWithStroke!! + } + + private var _LinearGradientWithStroke: ImageVector? = null + + """.trimIndent() + assertEquals(expectedOutput, output) + } } diff --git a/components/generator/imagevector/src/test/resources/ic_linear_gradient_with_stroke.svg b/components/generator/imagevector/src/test/resources/ic_linear_gradient_with_stroke.svg new file mode 100644 index 00000000..8ccd4cb0 --- /dev/null +++ b/components/generator/imagevector/src/test/resources/ic_linear_gradient_with_stroke.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +