Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for Color #18

Merged
merged 3 commits into from
May 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ Apart from that if you want to controll range / legnth / size of the values bein
- Only classes already annotated with `@Dowel` can be annotated with `@DowelList`
- All of the properties listed in the primary constructor of class annotated with `@Dowel` can only be of the following types:
- Primitives (`Int`, `Long`, `Float`, `Double`, `Char`, `Boolean`, `String`)
- `androidx.compose.runtime.State`
- `androidx.compose.runtime.State`, `androidx.compose.ui.graphics.Color`
- `kotlinx.coroutines.flow.Flow`
- Functional types (high-order functions, lambdas)
- `@Dowel` classes (`@Dowel` classes can be nested. A `@Dowel` annotated class can have properties of the type of classes which are again annotated with `@Dowel`)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ fun HomeScreen(

items(
items = people,
key = { person -> person.name },
key = { person -> person.id },
) { person ->

ItemPerson(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ internal fun ItemPerson(
.padding(8.dp)
.size(40.dp)
.clip(CircleShape)
.background(color = Color.Gray.copy(alpha = 0.8f)),
.background(color = person.color),
painter = painterResource(id = person.avatar),
contentDescription = "Person avatar",
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.jayasuryat.dowel.sample.ui.home.model
import androidx.annotation.DrawableRes
import androidx.annotation.Size
import androidx.compose.runtime.State
import androidx.compose.ui.graphics.Color
import com.jayasuryat.dowel.annotation.Dowel
import com.jayasuryat.dowel.annotation.DowelList
import com.jayasuryat.dowel.sample.R
Expand All @@ -31,6 +32,7 @@ import kotlinx.coroutines.flow.Flow
@DowelList(count = 5)
@Dowel(count = 30)
data class Person(
val id: Long,
@Size(value = 5) val name: String,
@Size(value = 300) val bio: String?,
@DrawableRes val avatar: Int = R.drawable.ic_launcher_foreground,
Expand All @@ -54,4 +56,5 @@ data class Person(
val title: Char,
@Size(value = 1) val interests: List<Float>,
val onClick: suspend (a: Person, b: Int) -> Unit,
val color: Color = Color(0xFFe5efab),
)
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,6 @@ import com.squareup.kotlinpoet.ksp.toClassName

internal object Names {

val previewParamProvider: ClassName = ClassName(
packageName = "androidx.compose.ui.tooling.preview",
"PreviewParameterProvider"
)

val intRangeName: ClassName = ClassName(
packageName = "androidx.annotation",
"IntRange"
Expand All @@ -42,11 +37,6 @@ internal object Names {
"Size"
)

val stateName: ClassName = ClassName(
packageName = "androidx.compose.runtime",
"State"
)

val flowName: ClassName = ClassName(
packageName = "kotlinx.coroutines.flow",
"Flow"
Expand All @@ -56,6 +46,21 @@ internal object Names {

val sequenceName: ClassName = Sequence::class.asTypeName()

val previewParamProvider: ClassName = ClassName(
packageName = "androidx.compose.ui.tooling.preview",
"PreviewParameterProvider"
)

val stateName: ClassName = ClassName(
packageName = "androidx.compose.runtime",
"State"
)

val colorName: ClassName = ClassName(
packageName = "androidx.compose.ui.graphics",
"Color"
)

const val dowelClassNameSuffix: String = "PreviewParamProvider"
const val dowelListClassNameSuffix: String = "ListPreviewParamProvider"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -255,9 +255,6 @@ internal class DowelGenerator(
is ClassRepresentation.ParameterSpec.UnsupportedNullableSpec,
-> emptyList()

is ClassRepresentation.ParameterSpec.StateSpec ->
spec.elementSpec.getAllSupportingProvidersRecursively()

is ClassRepresentation.ParameterSpec.ListSpec ->
spec.elementSpec.getAllSupportingProvidersRecursively()

Expand All @@ -284,6 +281,11 @@ internal class DowelGenerator(
is DowelSpec -> listOf(spec)

is PreDefinedProviderSpec -> listOf(spec)

is ClassRepresentation.ParameterSpec.StateSpec ->
spec.elementSpec.getAllSupportingProvidersRecursively()

is ClassRepresentation.ParameterSpec.ColorSpec -> emptyList()
}

return specs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package com.jayasuryat.dowel.processor.generator

import com.jayasuryat.dowel.processor.Names
import com.jayasuryat.dowel.processor.StringSource
import com.jayasuryat.dowel.processor.model.ClassRepresentation
import com.jayasuryat.dowel.processor.model.ClassRepresentation.ParameterSpec.*
Expand Down Expand Up @@ -83,7 +84,6 @@ internal class ObjectConstructor {
is BooleanSpec -> spec.getBoolAssigner()
is StringSpec -> spec.getStringAssigner()

is StateSpec -> spec.getStateAssigner() // This would be a recursive call
is ListSpec -> spec.getListAssigner() // This would be a recursive call
is SetSpec -> spec.getSetAssigner() // This would be a recursive call
is MapSpec -> spec.getMapAssigner() // This would be a recursive call
Expand All @@ -100,6 +100,10 @@ internal class ObjectConstructor {

is NoArgsConstructorSpec -> spec.getNoArgsConstructorAssigner()

// Compose types
is StateSpec -> spec.getStateAssigner() // This would be a recursive call
is ColorSpec -> spec.getColorAssigner()

is UnsupportedNullableSpec -> spec.getUnsupportedNullableAssigner()
}

Expand Down Expand Up @@ -178,16 +182,6 @@ internal class ObjectConstructor {
return buildCodeBlock { add("%S", value) }
}

private fun StateSpec.getStateAssigner(): CodeBlock {

val spec = this

return buildCodeBlock {
val mutableStateOf = MemberName("androidx.compose.runtime", "mutableStateOf")
add("%M(%L)", mutableStateOf, spec.elementSpec.getAssigner())
}
}

private fun ListSpec.getListAssigner(): CodeBlock {

val spec = this
Expand Down Expand Up @@ -365,6 +359,27 @@ internal class ObjectConstructor {
}
}

private fun StateSpec.getStateAssigner(): CodeBlock {

val spec = this

return buildCodeBlock {
val mutableStateOf = MemberName("androidx.compose.runtime", "mutableStateOf")
add("%M(%L)", mutableStateOf, spec.elementSpec.getAssigner())
}
}

@Suppress("unused")
private fun ColorSpec.getColorAssigner(): CodeBlock {

return buildCodeBlock {
val member = MemberName(Names.colorName.packageName, Names.colorName.simpleName)
val colorValue = Random.nextInt(0xffffff + 1)
val hexColor = String.format("0xFF%06x", colorValue)
add("%M($hexColor)", member)
}
}

@Suppress("unused")
private fun UnsupportedNullableSpec.getUnsupportedNullableAssigner(): CodeBlock {
return buildCodeBlock { add("null") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,6 @@ internal data class ClassRepresentation(
) : ParameterSpec
// endregion

data class StateSpec(
val elementSpec: ParameterSpec,
) : ParameterSpec

data class ListSpec(
val size: Size,
val elementSpec: ParameterSpec,
Expand Down Expand Up @@ -162,6 +158,13 @@ internal data class ClassRepresentation(
val classDeclarations: KSClassDeclaration,
) : ParameterSpec

// Compose types
data class StateSpec(
val elementSpec: ParameterSpec,
) : ParameterSpec

object ColorSpec : ParameterSpec

/**
* Types which are not directly supported by Dowel for code generation, but are nullable
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ internal class ClassRepresentationMapper(
val ksName = resolver.getKSNameFromString(Map::class.qualifiedName!!)
resolver.getClassDeclarationByName(ksName)!!.asStarProjectedType()
}
private val stateDeclaration: KSType by unsafeLazy {
val ksName = resolver.getKSNameFromString(Names.stateName.canonicalName)
resolver.getClassDeclarationByName(ksName)?.asStarProjectedType() ?: builtIns.nothingType
}
private val flowDeclaration: KSType by unsafeLazy {
val ksName = resolver.getKSNameFromString(Names.flowName.canonicalName)
resolver.getClassDeclarationByName(ksName)?.asStarProjectedType() ?: builtIns.nothingType
Expand All @@ -96,6 +92,14 @@ internal class ClassRepresentationMapper(
val ksName = resolver.getKSNameFromString(Pair::class.qualifiedName!!)
resolver.getClassDeclarationByName(ksName)!!.asStarProjectedType()
}
private val stateDeclaration: KSType by unsafeLazy {
val ksName = resolver.getKSNameFromString(Names.stateName.canonicalName)
resolver.getClassDeclarationByName(ksName)?.asStarProjectedType() ?: builtIns.nothingType
}
private val colorDeclaration: KSType by unsafeLazy {
val ksName = resolver.getKSNameFromString(Names.colorName.canonicalName)
resolver.getClassDeclarationByName(ksName)?.asStarProjectedType() ?: builtIns.nothingType
}
// endregion

fun map(
Expand Down Expand Up @@ -173,9 +177,6 @@ internal class ClassRepresentationMapper(
propType.isAssignableFrom(builtIns.booleanType) -> getBooleanSpec().right()
propType.isAssignableFrom(builtIns.stringType) -> getStringSpec(annotations).right()

// State
stateDeclaration.isAssignableFrom(propType) -> propType.getStateSpec()

// List
listDeclaration.isAssignableFrom(propType) -> propType.getListSpec(annotations)

Expand Down Expand Up @@ -217,6 +218,13 @@ internal class ClassRepresentationMapper(
propTypeDeclaration.hasNoArgsConstructor() ->
propTypeDeclaration.getNoArgsConstructorSpec().right()

// Compose types
// State
stateDeclaration.isAssignableFrom(propType) -> propType.getStateSpec()

// Color
colorDeclaration.isAssignableFrom(propType) -> ColorSpec.right()

// Unsupported types which are nullable
propType.isMarkedNullable -> getUnsupportedNullableSpec().right()

Expand Down Expand Up @@ -332,23 +340,6 @@ internal class ClassRepresentationMapper(
)
}

private fun KSType.getStateSpec(): MaybeSpec<StateSpec> {

require(this.arguments.size == 1) { "State must have have exactly one type argument. Current size = ${this.arguments.size}" }
val arg = this.arguments.first()

val resolvedType = arg.type!!.resolve()
val spec = resolvedType.getSpec(
annotations = arg.annotations.toList(),
)

return either {
StateSpec(
elementSpec = spec.bind(),
)
}
}

private fun KSType.getListSpec(
annotations: List<KSAnnotation>,
): MaybeSpec<ListSpec> {
Expand Down Expand Up @@ -574,6 +565,23 @@ internal class ClassRepresentationMapper(
)
}

private fun KSType.getStateSpec(): MaybeSpec<StateSpec> {

require(this.arguments.size == 1) { "State must have have exactly one type argument. Current size = ${this.arguments.size}" }
val arg = this.arguments.first()

val resolvedType = arg.type!!.resolve()
val spec = resolvedType.getSpec(
annotations = arg.annotations.toList(),
)

return either {
StateSpec(
elementSpec = spec.bind(),
)
}
}

private fun getUnsupportedNullableSpec(): UnsupportedNullableSpec = UnsupportedNullableSpec

private fun KSDeclaration.isDowelClass(): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,20 @@ internal class DowelWholeEnchiladaProcessingTest {
SourceFile.kotlin(name = "SnapshotState.kt", contents = source)
}

private val ColorStub: SourceFile by lazy {
val source = """
package androidx.compose.ui.graphics

class Color(val value: ULong)

@Suppress("TestFunctionName")
fun Color(color: Long): Color {
return Color(value = (color.toULong() and 0xffffffffUL) shl 32)
}
""".trimIndent()
SourceFile.kotlin(name = "Color.kt", contents = source)
}

private val VehicleSource: SourceFile by lazy {
val source = """
package dowel.vehicle
Expand Down Expand Up @@ -191,6 +205,7 @@ internal class DowelWholeEnchiladaProcessingTest {
import dowel.vehicle.Vehicle
import dowel.static.info.SomeStaticInfo
import dowel.location.Location
import androidx.compose.ui.graphics.Color

@Dowel(count = 30)
internal data class Person(
Expand All @@ -207,6 +222,7 @@ internal class DowelWholeEnchiladaProcessingTest {
val liveLocation: Flow<Location?>,
val latLon: Pair<Long, Long>,
val info: SomeStaticInfo,
val bannerColor : Color,
@Size(value = 2) val locations: List<Location>,
@Size(value = 3) val uniqueLocations: Set<Location>,
val isExpanded: State<Boolean>,
Expand Down Expand Up @@ -241,6 +257,7 @@ internal class DowelWholeEnchiladaProcessingTest {
StatusSource,
StaticInfoSource,
LocationSource,
ColorStub,
/*AmbiguityLocationSource*/
)

Expand Down