@@ -13,10 +13,14 @@ import androidx.compose.foundation.layout.ColumnScope
1313import androidx.compose.foundation.layout.Row
1414import androidx.compose.foundation.layout.RowScope
1515import androidx.compose.foundation.layout.Spacer
16+ import androidx.compose.foundation.layout.fillMaxHeight
1617import androidx.compose.foundation.layout.height
18+ import androidx.compose.foundation.layout.heightIn
1719import androidx.compose.foundation.layout.padding
1820import androidx.compose.foundation.layout.size
1921import androidx.compose.foundation.layout.width
22+ import androidx.compose.foundation.layout.widthIn
23+ import androidx.compose.foundation.layout.wrapContentSize
2024import androidx.compose.animation.AnimatedContent
2125import androidx.compose.animation.animateColorAsState
2226import androidx.compose.animation.core.AnimationSpec
@@ -845,16 +849,7 @@ internal fun ViewNode.composeModifiers(): Modifier {
845849 }
846850 }
847851
848- " frame" -> {
849- val width = entry.args.double(" width" )
850- val height = entry.args.double(" height" )
851- when {
852- width != null && height != null -> modifier.size(animatedDp(width.dp, spec), animatedDp(height.dp, spec))
853- width != null -> modifier.width(animatedDp(width.dp, spec))
854- height != null -> modifier.height(animatedDp(height.dp, spec))
855- else -> modifier
856- }
857- }
852+ " frame" -> foldFrame(modifier, entry, spec)
858853
859854 " background" -> {
860855 val argb = entry.args.long(" color" ) ? : 0
@@ -913,6 +908,56 @@ internal fun ViewNode.composeModifiers(): Modifier {
913908 return modifier
914909}
915910
911+ // Folds a frame entry: fixed size, fill (maxWidth/Height .infinity), bounded
912+ // (widthIn/heightIn), and content alignment within the resulting box.
913+ @Composable
914+ private fun foldFrame (base : Modifier , entry : ModifierNode , spec : AnimSpec ? ): Modifier {
915+ var m = base
916+ val fixedW = entry.args.double(" width" )
917+ val fixedH = entry.args.double(" height" )
918+ val minW = entry.args.double(" minWidth" )
919+ val maxW = entry.args.double(" maxWidth" )
920+ val minH = entry.args.double(" minHeight" )
921+ val maxH = entry.args.double(" maxHeight" )
922+ val fillW = entry.args.bool(" fillWidth" ) == true
923+ val fillH = entry.args.bool(" fillHeight" ) == true
924+
925+ when {
926+ fixedW != null -> m = m.width(animatedDp(fixedW.dp, spec))
927+ fillW -> m = m.fillMaxWidth()
928+ minW != null || maxW != null -> m = m.widthIn(
929+ min = minW?.dp ? : Dp .Unspecified ,
930+ max = maxW?.dp ? : Dp .Unspecified ,
931+ )
932+ }
933+ when {
934+ fixedH != null -> m = m.height(animatedDp(fixedH.dp, spec))
935+ fillH -> m = m.fillMaxHeight()
936+ minH != null || maxH != null -> m = m.heightIn(
937+ min = minH?.dp ? : Dp .Unspecified ,
938+ max = maxH?.dp ? : Dp .Unspecified ,
939+ )
940+ }
941+ val h = entry.args.string(" horizontal" )
942+ val v = entry.args.string(" vertical" )
943+ if (h != null || v != null ) {
944+ m = m.wrapContentSize(frameAlignment(h ? : " center" , v ? : " center" ))
945+ }
946+ return m
947+ }
948+
949+ private fun frameAlignment (horizontal : String , vertical : String ): Alignment = when (vertical to horizontal) {
950+ " top" to " leading" -> Alignment .TopStart
951+ " top" to " center" -> Alignment .TopCenter
952+ " top" to " trailing" -> Alignment .TopEnd
953+ " center" to " leading" -> Alignment .CenterStart
954+ " center" to " trailing" -> Alignment .CenterEnd
955+ " bottom" to " leading" -> Alignment .BottomStart
956+ " bottom" to " center" -> Alignment .BottomCenter
957+ " bottom" to " trailing" -> Alignment .BottomEnd
958+ else -> Alignment .Center
959+ }
960+
916961// Always-called animated wrappers: the underlying Animatable persists across
917962// recompositions, so a later tween eases from wherever the value currently is.
918963
0 commit comments