Skip to content

Commit

Permalink
Create ImageVectorPreviewEditorProvider to preview ImageVector inside…
Browse files Browse the repository at this point in the history
… kt file
  • Loading branch information
egorikftp committed Aug 21, 2024
1 parent 34790aa commit 1f9a669
Show file tree
Hide file tree
Showing 31 changed files with 1,824 additions and 10 deletions.
5 changes: 5 additions & 0 deletions components/extensions/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
plugins {
alias(libs.plugins.kotlin.jvm)
}

dependencies {
testImplementation(libs.bundles.test)
testRuntimeOnly(libs.junit.launcher)
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
package io.github.composegears.valkyrie.extensions

inline fun <reified T : Any> Any?.castOrNull(): T? = this as? T
inline fun <reified T : Any> Any?.safeAs(): T? = this as? T

inline fun <reified T : Any> Any?.cast(): T = this as T
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package io.github.composegears.valkyrie.extensions

fun String.toColorInt() = this
.trimStart('#')
.padStart(8, 'F')
.toLong(16)
.toInt()
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package io.github.composegears.valkyrie.extensions

import assertk.assertThat
import assertk.assertions.isEqualTo
import org.junit.jupiter.api.Test

class ColorTest {

@Test
fun `string to color int`() {
assertThat("#FF000000".toColorInt()).isEqualTo(-16777216)
assertThat("FF000000".toColorInt()).isEqualTo(-16777216)
assertThat("000000".toColorInt()).isEqualTo(-16777216)
}
}
3 changes: 3 additions & 0 deletions components/ir/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
plugins {
alias(libs.plugins.kotlin.jvm)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package io.github.composegears.valkyrie.ir

data class IrImageVector(
val name: String = "",
val autoMirror: Boolean = false,
val defaultWidth: Float,
val defaultHeight: Float,
val viewportWidth: Float,
val viewportHeight: Float,
val vectorNodes: List<IrVectorNode>,
)

sealed interface IrVectorNode {
data class IrGroup(val nodes: List<IrPath>) : IrVectorNode

data class IrPath(
val name: String = "",
val fill: IrFill? = null,
val fillAlpha: Float = 1f,
val stroke: IrStroke? = null,
val strokeAlpha: Float = 1f,
val strokeLineWidth: Float = 0f,
val strokeLineCap: IrStrokeLineCap = IrStrokeLineCap.Butt,
val strokeLineJoin: IrStrokeLineJoin = IrStrokeLineJoin.Miter,
val strokeLineMiter: Float = 4f,
val pathFillType: IrPathFillType = IrPathFillType.NonZero,
val nodes: List<IrPathNode>,
) : IrVectorNode
}

enum class IrPathFillType {
EvenOdd,
NonZero,
}

enum class IrStrokeLineCap {
Butt,
Round,
Square,
}

enum class IrStrokeLineJoin {
Miter,
Round,
Bevel,
}

sealed interface IrFill {
data class Color(val colorHex: String) : IrFill

data class LinearGradient(
val startY: Float,
val startX: Float,
val endY: Float,
val endX: Float,
val colorStops: List<ColorStop> = listOf(),
) : IrFill

data class RadialGradient(
val radius: Float,
val centerX: Float,
val centerY: Float,
val colorStops: List<ColorStop> = listOf(),
) : IrFill

data class ColorStop(
val offset: Float,
val color: String,
)
}

sealed interface IrStroke {
data class Color(val colorHex: String) : IrStroke
}

sealed interface IrPathNode {

data object Close : IrPathNode
data class RelativeMoveTo(
val x: Float,
val y: Float,
) : IrPathNode

data class MoveTo(
val x: Float,
val y: Float,
) : IrPathNode

data class RelativeLineTo(
val x: Float,
val y: Float,
) : IrPathNode

data class LineTo(
val x: Float,
val y: Float,
) : IrPathNode

data class RelativeHorizontalTo(val x: Float) : IrPathNode
data class HorizontalTo(val x: Float) : IrPathNode
data class RelativeVerticalTo(val y: Float) : IrPathNode
data class VerticalTo(val y: Float) : IrPathNode
data class RelativeCurveTo(
val dx1: Float,
val dy1: Float,
val dx2: Float,
val dy2: Float,
val dx3: Float,
val dy3: Float,
) : IrPathNode

data class CurveTo(
val x1: Float,
val y1: Float,
val x2: Float,
val y2: Float,
val x3: Float,
val y3: Float,
) : IrPathNode

data class RelativeReflectiveCurveTo(
val x1: Float,
val y1: Float,
val x2: Float,
val y2: Float,
) : IrPathNode

data class ReflectiveCurveTo(
val x1: Float,
val y1: Float,
val x2: Float,
val y2: Float,
) : IrPathNode

data class RelativeQuadTo(
val x1: Float,
val y1: Float,
val x2: Float,
val y2: Float,
) : IrPathNode

data class QuadTo(
val x1: Float,
val y1: Float,
val x2: Float,
val y2: Float,
) : IrPathNode

data class RelativeReflectiveQuadTo(
val x: Float,
val y: Float,
) : IrPathNode

data class ReflectiveQuadTo(
val x: Float,
val y: Float,
) : IrPathNode

data class RelativeArcTo(
val horizontalEllipseRadius: Float,
val verticalEllipseRadius: Float,
val theta: Float,
val isMoreThanHalf: Boolean,
val isPositiveArc: Boolean,
val arcStartDx: Float,
val arcStartDy: Float,
) : IrPathNode

data class ArcTo(
val horizontalEllipseRadius: Float,
val verticalEllipseRadius: Float,
val theta: Float,
val isMoreThanHalf: Boolean,
val isPositiveArc: Boolean,
val arcStartX: Float,
val arcStartY: Float,
) : IrPathNode
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.github.composegears.valkyrie.psi.iconpack
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiManager
import com.intellij.testFramework.LightVirtualFile
import io.github.composegears.valkyrie.extensions.castOrNull
import io.github.composegears.valkyrie.extensions.safeAs
import java.nio.file.Path
import kotlin.io.path.name
import kotlin.io.path.readText
Expand All @@ -23,7 +23,7 @@ object IconPackPsiParser {
fun extractIconPack(path: Path, project: Project): IconPackInfo? {
val ktFile = PsiManager.getInstance(project)
.findFile(LightVirtualFile(path.name, KotlinFileType.INSTANCE, path.readText()))
.castOrNull<KtFile>() ?: return null
.safeAs<KtFile>() ?: return null

var iconPackName: String? = null
val nestedPacks = mutableListOf<String>()
Expand Down
26 changes: 26 additions & 0 deletions components/psi/imagevector/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import org.jetbrains.intellij.platform.gradle.TestFrameworkType

plugins {
alias(libs.plugins.kotlin.jvm)
alias(libs.plugins.jetbrains.intellij.module)
alias(libs.plugins.jetbrains.compose)
alias(libs.plugins.kotlin.compose)
}

dependencies {
implementation(projects.components.extensions)
implementation(projects.components.ir)

implementation(compose.ui)

testImplementation(compose.material3)
testImplementation(libs.bundles.test)
testRuntimeOnly(libs.junit.launcher)
// https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-faq.html#junit5-test-framework-refers-to-junit4
testRuntimeOnly(libs.junit4)

intellijPlatform {
testFramework(TestFrameworkType.Platform)
testFramework(TestFrameworkType.JUnit5)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package io.github.composegears.valkyrie.psi.extension

import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiTreeUtil

inline fun <reified T : PsiElement> PsiElement.childrenOfType(): Collection<T> =
PsiTreeUtil.findChildrenOfType(this, T::class.java)

inline fun <reified T : PsiElement> PsiElement.childOfType(): T? =
PsiTreeUtil.findChildOfType(this, T::class.java)
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.github.composegears.valkyrie.psi.imagevector

import androidx.compose.ui.graphics.vector.ImageVector
import io.github.composegears.valkyrie.psi.imagevector.parser.MaterialImageVectorPsiParser
import io.github.composegears.valkyrie.psi.imagevector.parser.RegularImageVectorPsiParser
import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.psi.KtImportList

object ImageVectorPsiParser {

fun parseToImageVector(ktFile: KtFile): ImageVector? {
val isMaterial = ktFile.importList?.isMaterial() ?: return null

return when {
isMaterial -> MaterialImageVectorPsiParser.parse(ktFile)
else -> RegularImageVectorPsiParser.parse(ktFile)
}
}

private fun KtImportList.isMaterial(): Boolean {
return imports.any {
val fqName = it.importedFqName

fqName?.asString() == "androidx.compose.material.icons.materialPath" ||
fqName?.asString() == "androidx.compose.material.icons.materialFilled"
}
}
}
Loading

0 comments on commit 1f9a669

Please sign in to comment.