@@ -64,7 +64,12 @@ import androidx.compose.ui.draw.clip
6464import androidx.compose.ui.draw.rotate
6565import androidx.compose.ui.draw.scale
6666import androidx.compose.ui.graphics.Color
67+ import androidx.compose.ui.text.font.FontStyle
68+ import androidx.compose.ui.text.font.FontWeight
69+ import androidx.compose.ui.text.style.TextAlign
6770import androidx.compose.ui.text.input.TextFieldValue
71+ import androidx.compose.ui.unit.TextUnit
72+ import androidx.compose.ui.unit.sp
6873import androidx.compose.ui.unit.IntOffset
6974import androidx.compose.ui.unit.dp
7075import androidx.compose.foundation.layout.fillMaxWidth
@@ -79,10 +84,7 @@ import androidx.compose.foundation.shape.RoundedCornerShape
7984fun Render (node : ViewNode ) {
8085 key(node.id) {
8186 when (node.type) {
82- " Text" -> Text (
83- text = node.string(" text" ) ? : " " ,
84- modifier = node.composeModifiers(),
85- )
87+ " Text" -> RenderText (node)
8688
8789 " Button" -> {
8890 val onTap = node.long(" onTap" )
@@ -332,6 +334,84 @@ private fun zStackAlignment(node: ViewNode): Alignment {
332334 }
333335}
334336
337+ // Text-styling modifiers describe attributes of the Text composable itself,
338+ // not the layout Modifier chain, so they are read off the node here and passed
339+ // as `Text(...)` parameters. Later (innermost) entries win for scalar
340+ // attributes. Layout modifiers in the same chain still apply via composeModifiers().
341+ @Composable
342+ private fun RenderText (node : ViewNode ) {
343+ var color = Color .Unspecified
344+ var fontSize: TextUnit = TextUnit .Unspecified
345+ var weight: FontWeight ? = null
346+ var fontStyle: FontStyle ? = null
347+ var maxLines = Int .MAX_VALUE
348+ var textAlign: TextAlign ? = null
349+ for (m in node.modifiers) {
350+ when (m.kind) {
351+ " font" -> {
352+ m.args.string(" style" )?.let { style ->
353+ fontSize = fontSizeForStyle(style).sp
354+ if (weight == null ) weight = defaultWeightForStyle(style)
355+ }
356+ m.args.double(" size" )?.let { fontSize = it.sp }
357+ m.args.string(" weight" )?.let { weight = fontWeightFor(it) }
358+ }
359+ " fontWeight" -> m.args.string(" weight" )?.let { weight = fontWeightFor(it) }
360+ " italic" -> fontStyle = FontStyle .Italic
361+ " foregroundColor" -> m.args.long(" color" )?.let { color = Color (it.toInt()) }
362+ " lineLimit" -> maxLines = m.args.long(" count" )?.toInt() ? : Int .MAX_VALUE
363+ " multilineTextAlignment" -> textAlign = when (m.args.string(" value" )) {
364+ " leading" -> TextAlign .Start
365+ " trailing" -> TextAlign .End
366+ else -> TextAlign .Center
367+ }
368+ }
369+ }
370+ Text (
371+ text = node.string(" text" ) ? : " " ,
372+ color = color,
373+ fontSize = fontSize,
374+ fontWeight = weight,
375+ fontStyle = fontStyle,
376+ maxLines = maxLines,
377+ textAlign = textAlign,
378+ modifier = node.composeModifiers(),
379+ )
380+ }
381+
382+ // SwiftUI named text styles → point sizes (Compose has no built-in analog).
383+ private fun fontSizeForStyle (style : String ): Double = when (style) {
384+ " largeTitle" -> 34.0
385+ " title" -> 28.0
386+ " title2" -> 22.0
387+ " title3" -> 20.0
388+ " headline" -> 17.0
389+ " body" -> 17.0
390+ " callout" -> 16.0
391+ " subheadline" -> 15.0
392+ " footnote" -> 13.0
393+ " caption" -> 12.0
394+ " caption2" -> 11.0
395+ else -> 17.0
396+ }
397+
398+ // Only headline carries a non-regular default weight in SwiftUI.
399+ private fun defaultWeightForStyle (style : String ): FontWeight ? =
400+ if (style == " headline" ) FontWeight .SemiBold else null
401+
402+ private fun fontWeightFor (name : String ): FontWeight = when (name) {
403+ " ultraLight" -> FontWeight .ExtraLight
404+ " thin" -> FontWeight .Thin
405+ " light" -> FontWeight .Light
406+ " regular" -> FontWeight .Normal
407+ " medium" -> FontWeight .Medium
408+ " semibold" -> FontWeight .SemiBold
409+ " bold" -> FontWeight .Bold
410+ " heavy" -> FontWeight .ExtraBold
411+ " black" -> FontWeight .Black
412+ else -> FontWeight .Normal
413+ }
414+
335415// / Folds a node's ordered modifier chain into a Compose `Modifier`.
336416// / The chain arrives outermost-first, which is exactly Compose's order.
337417internal fun ViewNode.composeModifiers (): Modifier {
0 commit comments