@@ -138,6 +138,15 @@ internal data class AnimSpec(val curve: String, val durationMs: Int)
138138
139139internal val LocalAnimationSpec = compositionLocalOf<AnimSpec ?> { null }
140140
141+ // Inherited style environment (SwiftUI's `.font`/`.foregroundColor`/`.disabled`
142+ // set values for a whole subtree). A node folds its own such modifiers into
143+ // these before rendering its children; leaves consume them as defaults that
144+ // their own modifiers still override.
145+ internal val LocalInheritedFontSize = compositionLocalOf { TextUnit .Unspecified }
146+ internal val LocalInheritedFontWeight = compositionLocalOf<FontWeight ?> { null }
147+ internal val LocalInheritedColor = compositionLocalOf { Color .Unspecified }
148+ internal val LocalInheritedDisabled = compositionLocalOf { false }
149+
141150// / Interprets a Swift-evaluated node tree into Material 3 composables.
142151// /
143152// / One `when` per node type; unknown types render a diagnostic so schema
@@ -152,7 +161,36 @@ fun Render(node: ViewNode) {
152161 val spec = node.string(" animationCurve" )?.let {
153162 AnimSpec (it, (node.double(" animationDurationMs" ) ? : 350.0 ).toInt())
154163 } ? : LocalAnimationSpec .current
155- CompositionLocalProvider (LocalAnimationSpec provides spec) { RenderResolved (node) }
164+
165+ // Fold this node's own style modifiers into the inherited environment so
166+ // its subtree sees them.
167+ var fontSize = LocalInheritedFontSize .current
168+ var fontWeight = LocalInheritedFontWeight .current
169+ var color = LocalInheritedColor .current
170+ var disabled = LocalInheritedDisabled .current
171+ for (m in node.modifiers) {
172+ when (m.kind) {
173+ " font" -> {
174+ m.args.string(" style" )?.let {
175+ fontSize = fontSizeForStyle(it).sp
176+ fontWeight = defaultWeightForStyle(it)
177+ }
178+ m.args.double(" size" )?.let { fontSize = it.sp }
179+ m.args.string(" weight" )?.let { fontWeight = fontWeightFor(it) }
180+ }
181+ " fontWeight" -> m.args.string(" weight" )?.let { fontWeight = fontWeightFor(it) }
182+ " foregroundColor" -> m.args.long(" color" )?.let { color = Color (it.toInt()) }
183+ " disabled" -> if ((m.args[" value" ] as ? kotlinx.serialization.json.JsonPrimitive )?.content == " true" ) disabled = true
184+ }
185+ }
186+
187+ CompositionLocalProvider (
188+ LocalAnimationSpec provides spec,
189+ LocalInheritedFontSize provides fontSize,
190+ LocalInheritedFontWeight provides fontWeight,
191+ LocalInheritedColor provides color,
192+ LocalInheritedDisabled provides disabled,
193+ ) { RenderResolved (node) }
156194}
157195
158196@Composable
@@ -165,8 +203,9 @@ private fun RenderResolved(node: ViewNode) {
165203 " Button" -> {
166204 val onTap = node.long(" onTap" )
167205 Button (
206+ // own disabled and any inherited `.disabled` both apply
168207 onClick = { onTap?.let { SwiftBridge .sink.invokeVoid(it) } },
169- enabled = ! node.isDisabled(),
208+ enabled = ! node.isDisabled() && ! LocalInheritedDisabled .current ,
170209 modifier = node.composeModifiers(),
171210 ) {
172211 RenderChildren (node)
@@ -744,9 +783,10 @@ private fun materialIcon(name: String): ImageVector? = when (name) {
744783// attributes. Layout modifiers in the same chain still apply via composeModifiers().
745784@Composable
746785private fun RenderText (node : ViewNode ) {
747- var color = Color .Unspecified
748- var fontSize: TextUnit = TextUnit .Unspecified
749- var weight: FontWeight ? = null
786+ // Start from the inherited environment; the node's own modifiers override.
787+ var color = LocalInheritedColor .current
788+ var fontSize: TextUnit = LocalInheritedFontSize .current
789+ var weight: FontWeight ? = LocalInheritedFontWeight .current
750790 var fontStyle: FontStyle ? = null
751791 var maxLines = Int .MAX_VALUE
752792 var textAlign: TextAlign ? = null
0 commit comments