xref: /aosp_15_r20/external/accompanist/adaptive/src/main/java/com/google/accompanist/adaptive/RowColumnImpl.kt (revision fa44fe6ae8e729aa3cfe5c03eedbbf98fb44e2c6)
1 /*
<lambda>null2  * Copyright 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      https://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.google.accompanist.adaptive
18 
19 import androidx.compose.runtime.Immutable
20 import androidx.compose.runtime.Stable
21 import androidx.compose.ui.Alignment
22 import androidx.compose.ui.layout.AlignmentLine
23 import androidx.compose.ui.layout.IntrinsicMeasurable
24 import androidx.compose.ui.layout.Measured
25 import androidx.compose.ui.layout.ParentDataModifier
26 import androidx.compose.ui.layout.Placeable
27 import androidx.compose.ui.platform.InspectorInfo
28 import androidx.compose.ui.platform.InspectorValueInfo
29 import androidx.compose.ui.unit.Constraints
30 import androidx.compose.ui.unit.Density
31 import androidx.compose.ui.unit.LayoutDirection
32 import androidx.compose.ui.util.fastForEach
33 import com.google.accompanist.adaptive.LayoutOrientation.Horizontal
34 import com.google.accompanist.adaptive.LayoutOrientation.Vertical
35 import kotlin.math.max
36 import kotlin.math.min
37 import kotlin.math.roundToInt
38 
39 /**
40  * Copied from:
41  * RowColumnImpl.kt
42  * https://android-review.googlesource.com/c/platform/frameworks/support/+/2260390/27/compose/foundation/foundation-layout/src/commonMain/kotlin/androidx/compose/foundation/layout/RowColumnImpl.kt
43  *
44  * The only changes were updating access modifiers and removing unused code
45  */
46 
47 /**
48  * [Row] will be [Horizontal], [Column] is [Vertical].
49  */
50 internal enum class LayoutOrientation {
51     Horizontal,
52     Vertical
53 }
54 
55 /**
56  * Used to specify the alignment of a layout's children, in cross axis direction.
57  */
58 @Immutable
59 internal sealed class CrossAxisAlignment {
60     /**
61      * Aligns to [size]. If this is a vertical alignment, [layoutDirection] should be
62      * [LayoutDirection.Ltr].
63      *
64      * @param size The remaining space (total size - content size) in the container.
65      * @param layoutDirection The layout direction of the content if horizontal or
66      * [LayoutDirection.Ltr] if vertical.
67      * @param placeable The item being aligned.
68      * @param beforeCrossAxisAlignmentLine The space before the cross-axis alignment line if
69      * an alignment line is being used or 0 if no alignment line is being used.
70      */
alignnull71     internal abstract fun align(
72         size: Int,
73         layoutDirection: LayoutDirection,
74         placeable: Placeable,
75         beforeCrossAxisAlignmentLine: Int
76     ): Int
77 
78     /**
79      * Returns `true` if this is [Relative].
80      */
81     internal open val isRelative: Boolean
82         get() = false
83 
84     /**
85      * Returns the alignment line position relative to the left/top of the space or `null` if
86      * this alignment doesn't rely on alignment lines.
87      */
88     internal open fun calculateAlignmentLinePosition(placeable: Placeable): Int? = null
89 
90     companion object {
91         /**
92          * Place children such that their center is in the middle of the cross axis.
93          */
94         @Stable
95         val Center: CrossAxisAlignment = CenterCrossAxisAlignment
96 
97         /**
98          * Place children such that their start edge is aligned to the start edge of the cross
99          * axis. TODO(popam): Consider rtl directionality.
100          */
101         @Stable
102         val Start: CrossAxisAlignment = StartCrossAxisAlignment
103 
104         /**
105          * Place children such that their end edge is aligned to the end edge of the cross
106          * axis. TODO(popam): Consider rtl directionality.
107          */
108         @Stable
109         val End: CrossAxisAlignment = EndCrossAxisAlignment
110 
111         /**
112          * Align children by their baseline.
113          */
114         fun AlignmentLine(alignmentLine: AlignmentLine): CrossAxisAlignment =
115             AlignmentLineCrossAxisAlignment(AlignmentLineProvider.Value(alignmentLine))
116 
117         /**
118          * Align children relative to their siblings using the alignment line provided as a
119          * parameter using [AlignmentLineProvider].
120          */
121         internal fun Relative(alignmentLineProvider: AlignmentLineProvider): CrossAxisAlignment =
122             AlignmentLineCrossAxisAlignment(alignmentLineProvider)
123 
124         /**
125          * Align children with vertical alignment.
126          */
127         internal fun vertical(vertical: Alignment.Vertical): CrossAxisAlignment =
128             VerticalCrossAxisAlignment(vertical)
129 
130         /**
131          * Align children with horizontal alignment.
132          */
133         internal fun horizontal(horizontal: Alignment.Horizontal): CrossAxisAlignment =
134             HorizontalCrossAxisAlignment(horizontal)
135     }
136 
137     private object CenterCrossAxisAlignment : CrossAxisAlignment() {
alignnull138         override fun align(
139             size: Int,
140             layoutDirection: LayoutDirection,
141             placeable: Placeable,
142             beforeCrossAxisAlignmentLine: Int
143         ): Int {
144             return size / 2
145         }
146     }
147 
148     private object StartCrossAxisAlignment : CrossAxisAlignment() {
alignnull149         override fun align(
150             size: Int,
151             layoutDirection: LayoutDirection,
152             placeable: Placeable,
153             beforeCrossAxisAlignmentLine: Int
154         ): Int {
155             return if (layoutDirection == LayoutDirection.Ltr) 0 else size
156         }
157     }
158 
159     private object EndCrossAxisAlignment : CrossAxisAlignment() {
alignnull160         override fun align(
161             size: Int,
162             layoutDirection: LayoutDirection,
163             placeable: Placeable,
164             beforeCrossAxisAlignmentLine: Int
165         ): Int {
166             return if (layoutDirection == LayoutDirection.Ltr) size else 0
167         }
168     }
169 
170     private class AlignmentLineCrossAxisAlignment(
171         val alignmentLineProvider: AlignmentLineProvider
172     ) : CrossAxisAlignment() {
173         override val isRelative: Boolean
174             get() = true
175 
calculateAlignmentLinePositionnull176         override fun calculateAlignmentLinePosition(placeable: Placeable): Int {
177             return alignmentLineProvider.calculateAlignmentLinePosition(placeable)
178         }
179 
alignnull180         override fun align(
181             size: Int,
182             layoutDirection: LayoutDirection,
183             placeable: Placeable,
184             beforeCrossAxisAlignmentLine: Int
185         ): Int {
186             val alignmentLinePosition =
187                 alignmentLineProvider.calculateAlignmentLinePosition(placeable)
188             return if (alignmentLinePosition != AlignmentLine.Unspecified) {
189                 val line = beforeCrossAxisAlignmentLine - alignmentLinePosition
190                 if (layoutDirection == LayoutDirection.Rtl) {
191                     size - line
192                 } else {
193                     line
194                 }
195             } else {
196                 0
197             }
198         }
199     }
200 
201     private class VerticalCrossAxisAlignment(
202         val vertical: Alignment.Vertical
203     ) : CrossAxisAlignment() {
alignnull204         override fun align(
205             size: Int,
206             layoutDirection: LayoutDirection,
207             placeable: Placeable,
208             beforeCrossAxisAlignmentLine: Int
209         ): Int {
210             return vertical.align(0, size)
211         }
212     }
213 
214     private class HorizontalCrossAxisAlignment(
215         val horizontal: Alignment.Horizontal
216     ) : CrossAxisAlignment() {
alignnull217         override fun align(
218             size: Int,
219             layoutDirection: LayoutDirection,
220             placeable: Placeable,
221             beforeCrossAxisAlignmentLine: Int
222         ): Int {
223             return horizontal.align(0, size, layoutDirection)
224         }
225     }
226 }
227 
228 /**
229  * Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.
230  */
231 internal data class OrientationIndependentConstraints(
232     val mainAxisMin: Int,
233     val mainAxisMax: Int,
234     val crossAxisMin: Int,
235     val crossAxisMax: Int
236 ) {
237     constructor(c: Constraints, orientation: LayoutOrientation) : this(
238         if (orientation === Horizontal) c.minWidth else c.minHeight,
239         if (orientation === Horizontal) c.maxWidth else c.maxHeight,
240         if (orientation === Horizontal) c.minHeight else c.minWidth,
241         if (orientation === Horizontal) c.maxHeight else c.maxWidth
242     )
243 
244     // Creates a new instance with the same main axis constraints and maximum tight cross axis.
stretchCrossAxisnull245     fun stretchCrossAxis() = OrientationIndependentConstraints(
246         mainAxisMin,
247         mainAxisMax,
248         if (crossAxisMax != Constraints.Infinity) crossAxisMax else crossAxisMin,
249         crossAxisMax
250     )
251 
252     // Given an orientation, resolves the current instance to traditional constraints.
253     fun toBoxConstraints(orientation: LayoutOrientation) =
254         if (orientation === Horizontal) {
255             Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
256         } else {
257             Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)
258         }
259 
260     // Given an orientation, resolves the max width constraint this instance represents.
maxWidthnull261     fun maxWidth(orientation: LayoutOrientation) =
262         if (orientation === Horizontal) {
263             mainAxisMax
264         } else {
265             crossAxisMax
266         }
267 
268     // Given an orientation, resolves the max height constraint this instance represents.
maxHeightnull269     fun maxHeight(orientation: LayoutOrientation) =
270         if (orientation === Horizontal) {
271             crossAxisMax
272         } else {
273             mainAxisMax
274         }
275 }
276 
277 internal val IntrinsicMeasurable.rowColumnParentData: RowColumnParentData?
278     get() = parentData as? RowColumnParentData
279 
280 internal val RowColumnParentData?.weight: Float
281     get() = this?.weight ?: 0f
282 
283 internal val RowColumnParentData?.fill: Boolean
284     get() = this?.fill ?: true
285 
286 internal val RowColumnParentData?.crossAxisAlignment: CrossAxisAlignment?
287     get() = this?.crossAxisAlignment
288 
289 internal val RowColumnParentData?.isRelative: Boolean
290     get() = this.crossAxisAlignment?.isRelative ?: false
291 
MinIntrinsicWidthMeasureBlocknull292 internal fun MinIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =
293     if (orientation == Horizontal) {
294         IntrinsicMeasureBlocks.HorizontalMinWidth
295     } else {
296         IntrinsicMeasureBlocks.VerticalMinWidth
297     }
298 
MinIntrinsicHeightMeasureBlocknull299 internal fun MinIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =
300     if (orientation == Horizontal) {
301         IntrinsicMeasureBlocks.HorizontalMinHeight
302     } else {
303         IntrinsicMeasureBlocks.VerticalMinHeight
304     }
305 
MaxIntrinsicWidthMeasureBlocknull306 internal fun MaxIntrinsicWidthMeasureBlock(orientation: LayoutOrientation) =
307     if (orientation == Horizontal) {
308         IntrinsicMeasureBlocks.HorizontalMaxWidth
309     } else {
310         IntrinsicMeasureBlocks.VerticalMaxWidth
311     }
312 
MaxIntrinsicHeightMeasureBlocknull313 internal fun MaxIntrinsicHeightMeasureBlock(orientation: LayoutOrientation) =
314     if (orientation == Horizontal) {
315         IntrinsicMeasureBlocks.HorizontalMaxHeight
316     } else {
317         IntrinsicMeasureBlocks.VerticalMaxHeight
318     }
319 
320 internal object IntrinsicMeasureBlocks {
321     val HorizontalMinWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull322         { measurables, availableHeight, mainAxisSpacing ->
323             intrinsicSize(
324                 measurables,
325                 { h -> minIntrinsicWidth(h) },
326                 { w -> maxIntrinsicHeight(w) },
327                 availableHeight,
328                 mainAxisSpacing,
329                 Horizontal,
330                 Horizontal
331             )
332         }
333     val VerticalMinWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull334         { measurables, availableHeight, mainAxisSpacing ->
335             intrinsicSize(
336                 measurables,
337                 { h -> minIntrinsicWidth(h) },
338                 { w -> maxIntrinsicHeight(w) },
339                 availableHeight,
340                 mainAxisSpacing,
341                 Vertical,
342                 Horizontal
343             )
344         }
345     val HorizontalMinHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull346         { measurables, availableWidth, mainAxisSpacing ->
347             intrinsicSize(
348                 measurables,
349                 { w -> minIntrinsicHeight(w) },
350                 { h -> maxIntrinsicWidth(h) },
351                 availableWidth,
352                 mainAxisSpacing,
353                 Horizontal,
354                 Vertical
355             )
356         }
357     val VerticalMinHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull358         { measurables, availableWidth, mainAxisSpacing ->
359             intrinsicSize(
360                 measurables,
361                 { w -> minIntrinsicHeight(w) },
362                 { h -> maxIntrinsicWidth(h) },
363                 availableWidth,
364                 mainAxisSpacing,
365                 Vertical,
366                 Vertical
367             )
368         }
369     val HorizontalMaxWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull370         { measurables, availableHeight, mainAxisSpacing ->
371             intrinsicSize(
372                 measurables,
373                 { h -> maxIntrinsicWidth(h) },
374                 { w -> maxIntrinsicHeight(w) },
375                 availableHeight,
376                 mainAxisSpacing,
377                 Horizontal,
378                 Horizontal
379             )
380         }
381     val VerticalMaxWidth: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull382         { measurables, availableHeight, mainAxisSpacing ->
383             intrinsicSize(
384                 measurables,
385                 { h -> maxIntrinsicWidth(h) },
386                 { w -> maxIntrinsicHeight(w) },
387                 availableHeight,
388                 mainAxisSpacing,
389                 Vertical,
390                 Horizontal
391             )
392         }
393     val HorizontalMaxHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull394         { measurables, availableWidth, mainAxisSpacing ->
395             intrinsicSize(
396                 measurables,
397                 { w -> maxIntrinsicHeight(w) },
398                 { h -> maxIntrinsicWidth(h) },
399                 availableWidth,
400                 mainAxisSpacing,
401                 Horizontal,
402                 Vertical
403             )
404         }
405     val VerticalMaxHeight: (List<IntrinsicMeasurable>, Int, Int) -> Int =
mainAxisSpacingnull406         { measurables, availableWidth, mainAxisSpacing ->
407             intrinsicSize(
408                 measurables,
409                 { w -> maxIntrinsicHeight(w) },
410                 { h -> maxIntrinsicWidth(h) },
411                 availableWidth,
412                 mainAxisSpacing,
413                 Vertical,
414                 Vertical
415             )
416         }
417 }
418 
intrinsicSizenull419 private fun intrinsicSize(
420     children: List<IntrinsicMeasurable>,
421     intrinsicMainSize: IntrinsicMeasurable.(Int) -> Int,
422     intrinsicCrossSize: IntrinsicMeasurable.(Int) -> Int,
423     crossAxisAvailable: Int,
424     mainAxisSpacing: Int,
425     layoutOrientation: LayoutOrientation,
426     intrinsicOrientation: LayoutOrientation
427 ) = if (layoutOrientation == intrinsicOrientation) {
428     intrinsicMainAxisSize(children, intrinsicMainSize, crossAxisAvailable, mainAxisSpacing)
429 } else {
430     intrinsicCrossAxisSize(
431         children,
432         intrinsicCrossSize,
433         intrinsicMainSize,
434         crossAxisAvailable,
435         mainAxisSpacing
436     )
437 }
438 
intrinsicMainAxisSizenull439 private fun intrinsicMainAxisSize(
440     children: List<IntrinsicMeasurable>,
441     mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
442     crossAxisAvailable: Int,
443     mainAxisSpacing: Int
444 ): Int {
445     var weightUnitSpace = 0
446     var fixedSpace = 0
447     var totalWeight = 0f
448     children.fastForEach { child ->
449         val weight = child.rowColumnParentData.weight
450         val size = child.mainAxisSize(crossAxisAvailable)
451         if (weight == 0f) {
452             fixedSpace += size
453         } else if (weight > 0f) {
454             totalWeight += weight
455             weightUnitSpace = max(weightUnitSpace, (size / weight).roundToInt())
456         }
457     }
458     return (weightUnitSpace * totalWeight).roundToInt() + fixedSpace +
459         (children.size - 1) * mainAxisSpacing
460 }
461 
intrinsicCrossAxisSizenull462 private fun intrinsicCrossAxisSize(
463     children: List<IntrinsicMeasurable>,
464     mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
465     crossAxisSize: IntrinsicMeasurable.(Int) -> Int,
466     mainAxisAvailable: Int,
467     mainAxisSpacing: Int
468 ): Int {
469     var fixedSpace = min((children.size - 1) * mainAxisSpacing, mainAxisAvailable)
470     var crossAxisMax = 0
471     var totalWeight = 0f
472     children.fastForEach { child ->
473         val weight = child.rowColumnParentData.weight
474         if (weight == 0f) {
475             // Ask the child how much main axis space it wants to occupy. This cannot be more
476             // than the remaining available space.
477             val mainAxisSpace = min(
478                 child.mainAxisSize(Constraints.Infinity),
479                 mainAxisAvailable - fixedSpace
480             )
481             fixedSpace += mainAxisSpace
482             // Now that the assigned main axis space is known, ask about the cross axis space.
483             crossAxisMax = max(crossAxisMax, child.crossAxisSize(mainAxisSpace))
484         } else if (weight > 0f) {
485             totalWeight += weight
486         }
487     }
488 
489     // For weighted children, calculate how much main axis space weight=1 would represent.
490     val weightUnitSpace = if (totalWeight == 0f) {
491         0
492     } else if (mainAxisAvailable == Constraints.Infinity) {
493         Constraints.Infinity
494     } else {
495         (max(mainAxisAvailable - fixedSpace, 0) / totalWeight).roundToInt()
496     }
497 
498     children.fastForEach { child ->
499         val weight = child.rowColumnParentData.weight
500         // Now the main axis for weighted children is known, so ask about the cross axis space.
501         if (weight > 0f) {
502             crossAxisMax = max(
503                 crossAxisMax,
504                 child.crossAxisSize(
505                     if (weightUnitSpace != Constraints.Infinity) {
506                         (weightUnitSpace * weight).roundToInt()
507                     } else {
508                         Constraints.Infinity
509                     }
510                 )
511             )
512         }
513     }
514     return crossAxisMax
515 }
516 
517 internal class LayoutWeightImpl(
518     val weight: Float,
519     val fill: Boolean,
520     inspectorInfo: InspectorInfo.() -> Unit
521 ) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
modifyParentDatanull522     override fun Density.modifyParentData(parentData: Any?) =
523         ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
524             it.weight = weight
525             it.fill = fill
526         }
527 
equalsnull528     override fun equals(other: Any?): Boolean {
529         if (this === other) return true
530         val otherModifier = other as? LayoutWeightImpl ?: return false
531         return weight == otherModifier.weight &&
532             fill == otherModifier.fill
533     }
534 
hashCodenull535     override fun hashCode(): Int {
536         var result = weight.hashCode()
537         result = 31 * result + fill.hashCode()
538         return result
539     }
540 
toStringnull541     override fun toString(): String =
542         "LayoutWeightImpl(weight=$weight, fill=$fill)"
543 }
544 
545 internal sealed class SiblingsAlignedModifier(
546     inspectorInfo: InspectorInfo.() -> Unit
547 ) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
548     abstract override fun Density.modifyParentData(parentData: Any?): Any?
549 
550     internal class WithAlignmentLineBlock(
551         val block: (Measured) -> Int,
552         inspectorInfo: InspectorInfo.() -> Unit
553     ) : SiblingsAlignedModifier(inspectorInfo) {
554         override fun Density.modifyParentData(parentData: Any?): Any {
555             return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
556                 it.crossAxisAlignment =
557                     CrossAxisAlignment.Relative(AlignmentLineProvider.Block(block))
558             }
559         }
560 
561         override fun equals(other: Any?): Boolean {
562             if (this === other) return true
563             val otherModifier = other as? WithAlignmentLineBlock ?: return false
564             return block == otherModifier.block
565         }
566 
567         override fun hashCode(): Int = block.hashCode()
568 
569         override fun toString(): String = "WithAlignmentLineBlock(block=$block)"
570     }
571 
572     internal class WithAlignmentLine(
573         val alignmentLine: AlignmentLine,
574         inspectorInfo: InspectorInfo.() -> Unit
575     ) : SiblingsAlignedModifier(inspectorInfo) {
576         override fun Density.modifyParentData(parentData: Any?): Any {
577             return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
578                 it.crossAxisAlignment =
579                     CrossAxisAlignment.Relative(AlignmentLineProvider.Value(alignmentLine))
580             }
581         }
582 
583         override fun equals(other: Any?): Boolean {
584             if (this === other) return true
585             val otherModifier = other as? WithAlignmentLine ?: return false
586             return alignmentLine == otherModifier.alignmentLine
587         }
588 
589         override fun hashCode(): Int = alignmentLine.hashCode()
590 
591         override fun toString(): String = "WithAlignmentLine(line=$alignmentLine)"
592     }
593 }
594 
595 internal class HorizontalAlignModifier(
596     val horizontal: Alignment.Horizontal,
597     inspectorInfo: InspectorInfo.() -> Unit
598 ) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
modifyParentDatanull599     override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
600         return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
601             it.crossAxisAlignment = CrossAxisAlignment.horizontal(horizontal)
602         }
603     }
604 
equalsnull605     override fun equals(other: Any?): Boolean {
606         if (this === other) return true
607         val otherModifier = other as? HorizontalAlignModifier ?: return false
608         return horizontal == otherModifier.horizontal
609     }
610 
hashCodenull611     override fun hashCode(): Int = horizontal.hashCode()
612 
613     override fun toString(): String =
614         "HorizontalAlignModifier(horizontal=$horizontal)"
615 }
616 
617 internal class VerticalAlignModifier(
618     val vertical: Alignment.Vertical,
619     inspectorInfo: InspectorInfo.() -> Unit
620 ) : ParentDataModifier, InspectorValueInfo(inspectorInfo) {
621     override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
622         return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
623             it.crossAxisAlignment = CrossAxisAlignment.vertical(vertical)
624         }
625     }
626 
627     override fun equals(other: Any?): Boolean {
628         if (this === other) return true
629         val otherModifier = other as? VerticalAlignModifier ?: return false
630         return vertical == otherModifier.vertical
631     }
632 
633     override fun hashCode(): Int = vertical.hashCode()
634 
635     override fun toString(): String =
636         "VerticalAlignModifier(vertical=$vertical)"
637 }
638 
639 /**
640  * Provides the alignment line.
641  */
642 internal sealed class AlignmentLineProvider {
calculateAlignmentLinePositionnull643     abstract fun calculateAlignmentLinePosition(placeable: Placeable): Int
644     data class Block(val lineProviderBlock: (Measured) -> Int) : AlignmentLineProvider() {
645         override fun calculateAlignmentLinePosition(
646             placeable: Placeable
647         ): Int {
648             return lineProviderBlock(placeable)
649         }
650     }
651 
652     data class Value(val alignmentLine: AlignmentLine) : AlignmentLineProvider() {
calculateAlignmentLinePositionnull653         override fun calculateAlignmentLinePosition(placeable: Placeable): Int {
654             return placeable[alignmentLine]
655         }
656     }
657 }
658 
659 /**
660  * Used to specify how a layout chooses its own size when multiple behaviors are possible.
661  */
662 // TODO(popam): remove this when Flow is reworked
663 internal enum class SizeMode {
664     /**
665      * Minimize the amount of free space by wrapping the children,
666      * subject to the incoming layout constraints.
667      */
668     Wrap,
669 
670     /**
671      * Maximize the amount of free space by expanding to fill the available space,
672      * subject to the incoming layout constraints.
673      */
674     Expand
675 }
676