Skip to content

Instantly share code, notes, and snippets.

@Kaaveh
Created February 12, 2025 06:11
Show Gist options
  • Save Kaaveh/e5fae0aaa1843946df1b3f90d49ab8c5 to your computer and use it in GitHub Desktop.
Save Kaaveh/e5fae0aaa1843946df1b3f90d49ab8c5 to your computer and use it in GitHub Desktop.
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.DefaultAlpha
import androidx.compose.ui.graphics.FilterQuality
import androidx.compose.ui.graphics.drawscope.DrawScope.Companion.DefaultFilterQuality
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.TextUnit
import coil3.compose.AsyncImage
import coil3.compose.AsyncImagePainter.Companion.DefaultTransform
import coil3.compose.AsyncImagePainter.State
data class Element(
val type: ElementType,
val value: ElementValue,
val property: Property,
val children: List<Element>,
)
data class Property(
val borderRadius: String?,
val padding: String?,
val alignment: String?,
val backgroundColor: String?,
)
interface ElementValue
data class TextValue(
val text: String,
val modifier: Modifier = Modifier,
val color: Color = Color.Unspecified,
val fontSize: TextUnit = TextUnit.Unspecified,
val fontStyle: FontStyle? = null,
val fontWeight: FontWeight? = null,
val fontFamily: FontFamily? = null,
val letterSpacing: TextUnit = TextUnit.Unspecified,
val textDecoration: TextDecoration? = null,
val textAlign: TextAlign? = null,
val lineHeight: TextUnit = TextUnit.Unspecified,
val overflow: TextOverflow = TextOverflow.Clip,
val softWrap: Boolean = true,
val maxLines: Int = Int.MAX_VALUE,
val minLines: Int = 1,
val onTextLayout: ((TextLayoutResult) -> Unit)? = null,
val style: TextStyle,
) : ElementValue
data class ImageValue(
val imageUrl: String?,
val contentDescription: String?,
val modifier: Modifier = Modifier,
val transform: (State) -> State = DefaultTransform,
val onState: ((State) -> Unit)? = null,
val alignment: Alignment = Alignment.Center,
val contentScale: ContentScale = ContentScale.Fit,
val alpha: Float = DefaultAlpha,
val colorFilter: ColorFilter? = null,
val filterQuality: FilterQuality = DefaultFilterQuality,
val clipToBounds: Boolean = true,
) : ElementValue
data class TextInputValue(
val placeholder: String?,
val type: String?,
) : ElementValue {
enum class InputType {
TEXT,
NUMBER,
EMAIL,
PASSWORD,
}
}
data class ColumnValue(
val modifier: Modifier = Modifier,
val verticalArrangement: Arrangement.Vertical = Arrangement.Top,
val horizontalAlignment: Alignment.Horizontal = Alignment.Start,
) : ElementValue
data class RowValue(
val modifier: Modifier = Modifier,
val horizontalArrangement: Arrangement.Horizontal = Arrangement.Start,
val verticalAlignment: Alignment.Vertical = Alignment.Top,
) : ElementValue
enum class ElementType(val label: String) {
Column("Column"),
Row("Row"),
Text("Text"),
Image("Image"),
TextInput("TextInput"),
Dropdown("Dropdown"),
}
object ElementRendererFactory {
fun getRenderer(type: ElementType): ElementRenderer {
return when (type) {
ElementType.Column -> ColumnRenderer()
ElementType.Row -> RowRenderer()
ElementType.Text -> TextRenderer()
ElementType.Image -> ImageRenderer()
ElementType.TextInput -> TODO()
ElementType.Dropdown -> TODO()
}
}
}
@Composable
fun ElementComposer(
element: Element,
) {
ElementRendererFactory.getRenderer(type = element.type).Render(element)
}
interface ElementRenderer {
@Composable
fun Render(element: Element)
}
class TextRenderer : ElementRenderer {
@Composable
override fun Render(element: Element) {
(element.value as TextValue).apply {
Text(
text = text,
color = color,
fontSize = fontSize,
fontStyle = fontStyle,
fontWeight = fontWeight,
fontFamily = fontFamily,
letterSpacing = letterSpacing,
textDecoration = textDecoration,
textAlign = textAlign,
lineHeight = lineHeight,
overflow = overflow,
softWrap = softWrap,
maxLines = maxLines,
minLines = minLines,
onTextLayout = onTextLayout,
style = style,
)
}
}
}
class ImageRenderer : ElementRenderer {
@Composable
override fun Render(element: Element) {
(element.value as ImageValue).apply {
AsyncImage(
model = imageUrl,
contentDescription = contentDescription,
modifier = modifier,
transform = transform,
onState = onState,
alignment = alignment,
contentScale = contentScale,
alpha = alpha,
colorFilter = colorFilter,
filterQuality = filterQuality,
clipToBounds = clipToBounds,
)
}
}
}
class ColumnRenderer : ElementRenderer {
@Composable
override fun Render(element: Element) {
(element.value as ColumnValue).apply {
Column(
modifier = modifier,
verticalArrangement = verticalArrangement,
horizontalAlignment = horizontalAlignment,
) {
element.children.forEach { element ->
ElementComposer(element = element)
}
}
}
}
}
class RowRenderer : ElementRenderer {
@Composable
override fun Render(element: Element) {
(element.value as RowValue).apply {
Row(
modifier = modifier,
horizontalArrangement = horizontalArrangement,
verticalAlignment = verticalAlignment,
) {
element.children.forEach { element ->
ElementComposer(element = element)
}
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment