1 /*
<lambda>null2 * Copyright 2019 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 * http://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 androidx.compose.foundation.layout
18
19 import androidx.compose.foundation.layout.LayoutOrientation.Horizontal
20 import androidx.compose.foundation.layout.LayoutOrientation.Vertical
21 import androidx.compose.runtime.Immutable
22 import androidx.compose.runtime.Stable
23 import androidx.compose.ui.Alignment
24 import androidx.compose.ui.Modifier
25 import androidx.compose.ui.layout.AlignmentLine
26 import androidx.compose.ui.layout.IntrinsicMeasurable
27 import androidx.compose.ui.layout.Measured
28 import androidx.compose.ui.layout.Placeable
29 import androidx.compose.ui.node.ModifierNodeElement
30 import androidx.compose.ui.node.ParentDataModifierNode
31 import androidx.compose.ui.platform.InspectorInfo
32 import androidx.compose.ui.unit.Constraints
33 import androidx.compose.ui.unit.Density
34 import androidx.compose.ui.unit.LayoutDirection
35 import androidx.compose.ui.util.fastForEach
36 import androidx.compose.ui.util.fastRoundToInt
37 import kotlin.jvm.JvmInline
38 import kotlin.math.max
39 import kotlin.math.min
40
41 /** [Row] will be [Horizontal], [Column] is [Vertical]. */
42 internal enum class LayoutOrientation {
43 Horizontal,
44 Vertical
45 }
46
47 /** Used to specify the alignment of a layout's children, in cross axis direction. */
48 @Immutable
49 internal sealed class CrossAxisAlignment {
50 /**
51 * Aligns to [size]. If this is a vertical alignment, [layoutDirection] should be
52 * [LayoutDirection.Ltr].
53 *
54 * @param size The remaining space (total size - content size) in the container.
55 * @param layoutDirection The layout direction of the content if horizontal or
56 * [LayoutDirection.Ltr] if vertical.
57 * @param placeable The item being aligned.
58 * @param beforeCrossAxisAlignmentLine The space before the cross-axis alignment line if an
59 * alignment line is being used or 0 if no alignment line is being used.
60 */
alignnull61 internal abstract fun align(
62 size: Int,
63 layoutDirection: LayoutDirection,
64 placeable: Placeable,
65 beforeCrossAxisAlignmentLine: Int
66 ): Int
67
68 /** Returns `true` if this is [Relative]. */
69 internal open val isRelative: Boolean
70 get() = false
71
72 /**
73 * Returns the alignment line position relative to the left/top of the space or `null` if this
74 * alignment doesn't rely on alignment lines.
75 */
76 internal open fun calculateAlignmentLinePosition(placeable: Placeable): Int? = null
77
78 companion object {
79 /** Place children such that their center is in the middle of the cross axis. */
80 @Stable val Center: CrossAxisAlignment = CenterCrossAxisAlignment
81
82 /**
83 * Place children such that their start edge is aligned to the start edge of the cross
84 * axis. TODO(popam): Consider rtl directionality.
85 */
86 @Stable val Start: CrossAxisAlignment = StartCrossAxisAlignment
87
88 /**
89 * Place children such that their end edge is aligned to the end edge of the cross
90 * axis. TODO(popam): Consider rtl directionality.
91 */
92 @Stable val End: CrossAxisAlignment = EndCrossAxisAlignment
93
94 /** Align children by their baseline. */
95 fun AlignmentLine(alignmentLine: AlignmentLine): CrossAxisAlignment =
96 AlignmentLineCrossAxisAlignment(AlignmentLineProvider.Value(alignmentLine))
97
98 /**
99 * Align children relative to their siblings using the alignment line provided as a
100 * parameter using [AlignmentLineProvider].
101 */
102 internal fun Relative(alignmentLineProvider: AlignmentLineProvider): CrossAxisAlignment =
103 AlignmentLineCrossAxisAlignment(alignmentLineProvider)
104
105 /** Align children with vertical alignment. */
106 internal fun vertical(vertical: Alignment.Vertical): CrossAxisAlignment =
107 VerticalCrossAxisAlignment(vertical)
108
109 /** Align children with horizontal alignment. */
110 internal fun horizontal(horizontal: Alignment.Horizontal): CrossAxisAlignment =
111 HorizontalCrossAxisAlignment(horizontal)
112 }
113
114 private object CenterCrossAxisAlignment : CrossAxisAlignment() {
alignnull115 override fun align(
116 size: Int,
117 layoutDirection: LayoutDirection,
118 placeable: Placeable,
119 beforeCrossAxisAlignmentLine: Int
120 ): Int {
121 return size / 2
122 }
123 }
124
125 private object StartCrossAxisAlignment : CrossAxisAlignment() {
alignnull126 override fun align(
127 size: Int,
128 layoutDirection: LayoutDirection,
129 placeable: Placeable,
130 beforeCrossAxisAlignmentLine: Int
131 ): Int {
132 return if (layoutDirection == LayoutDirection.Ltr) 0 else size
133 }
134 }
135
136 private object EndCrossAxisAlignment : CrossAxisAlignment() {
alignnull137 override fun align(
138 size: Int,
139 layoutDirection: LayoutDirection,
140 placeable: Placeable,
141 beforeCrossAxisAlignmentLine: Int
142 ): Int {
143 return if (layoutDirection == LayoutDirection.Ltr) size else 0
144 }
145 }
146
147 private class AlignmentLineCrossAxisAlignment(
148 val alignmentLineProvider: AlignmentLineProvider
149 ) : CrossAxisAlignment() {
150 override val isRelative: Boolean
151 get() = true
152
calculateAlignmentLinePositionnull153 override fun calculateAlignmentLinePosition(placeable: Placeable): Int {
154 return alignmentLineProvider.calculateAlignmentLinePosition(placeable)
155 }
156
alignnull157 override fun align(
158 size: Int,
159 layoutDirection: LayoutDirection,
160 placeable: Placeable,
161 beforeCrossAxisAlignmentLine: Int
162 ): Int {
163 val alignmentLinePosition =
164 alignmentLineProvider.calculateAlignmentLinePosition(placeable)
165 return if (alignmentLinePosition != AlignmentLine.Unspecified) {
166 val line = beforeCrossAxisAlignmentLine - alignmentLinePosition
167 if (layoutDirection == LayoutDirection.Rtl) {
168 size - line
169 } else {
170 line
171 }
172 } else {
173 0
174 }
175 }
176 }
177
178 private data class VerticalCrossAxisAlignment(val vertical: Alignment.Vertical) :
179 CrossAxisAlignment() {
alignnull180 override fun align(
181 size: Int,
182 layoutDirection: LayoutDirection,
183 placeable: Placeable,
184 beforeCrossAxisAlignmentLine: Int
185 ): Int {
186 return vertical.align(0, size)
187 }
188 }
189
190 private data class HorizontalCrossAxisAlignment(val horizontal: Alignment.Horizontal) :
191 CrossAxisAlignment() {
alignnull192 override fun align(
193 size: Int,
194 layoutDirection: LayoutDirection,
195 placeable: Placeable,
196 beforeCrossAxisAlignmentLine: Int
197 ): Int {
198 return horizontal.align(0, size, layoutDirection)
199 }
200 }
201 }
202
203 /**
204 * Box [Constraints], but which abstract away width and height in favor of main axis and cross axis.
205 */
206 @JvmInline
207 internal value class OrientationIndependentConstraints
208 private constructor(private val value: Constraints) {
209 inline val mainAxisMin: Int
210 get() = value.minWidth
211
212 inline val mainAxisMax: Int
213 get() = value.maxWidth
214
215 inline val crossAxisMin: Int
216 get() = value.minHeight
217
218 inline val crossAxisMax: Int
219 get() = value.maxHeight
220
221 constructor(
222 mainAxisMin: Int,
223 mainAxisMax: Int,
224 crossAxisMin: Int,
225 crossAxisMax: Int
226 ) : this(
227 Constraints(
228 minWidth = mainAxisMin,
229 maxWidth = mainAxisMax,
230 minHeight = crossAxisMin,
231 maxHeight = crossAxisMax
232 )
233 )
234
235 constructor(
236 c: Constraints,
237 orientation: LayoutOrientation
238 ) : this(
239 if (orientation === Horizontal) c.minWidth else c.minHeight,
240 if (orientation === Horizontal) c.maxWidth else c.maxHeight,
241 if (orientation === Horizontal) c.minHeight else c.minWidth,
242 if (orientation === Horizontal) c.maxHeight else c.maxWidth
243 )
244
245 // Creates a new instance with the same main axis constraints and maximum tight cross axis.
stretchCrossAxisnull246 fun stretchCrossAxis() =
247 OrientationIndependentConstraints(
248 mainAxisMin,
249 mainAxisMax,
250 if (crossAxisMax != Constraints.Infinity) crossAxisMax else crossAxisMin,
251 crossAxisMax
252 )
253
254 // Given an orientation, resolves the current instance to traditional constraints.
255 fun toBoxConstraints(orientation: LayoutOrientation) =
256 if (orientation === Horizontal) {
257 Constraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
258 } else {
259 Constraints(crossAxisMin, crossAxisMax, mainAxisMin, mainAxisMax)
260 }
261
262 // Given an orientation, resolves the max width constraint this instance represents.
maxWidthnull263 fun maxWidth(orientation: LayoutOrientation) =
264 if (orientation === Horizontal) {
265 mainAxisMax
266 } else {
267 crossAxisMax
268 }
269
270 // Given an orientation, resolves the max height constraint this instance represents.
maxHeightnull271 fun maxHeight(orientation: LayoutOrientation) =
272 if (orientation === Horizontal) {
273 crossAxisMax
274 } else {
275 mainAxisMax
276 }
277
copynull278 fun copy(
279 mainAxisMin: Int = this.mainAxisMin,
280 mainAxisMax: Int = this.mainAxisMax,
281 crossAxisMin: Int = this.crossAxisMin,
282 crossAxisMax: Int = this.crossAxisMax
283 ): OrientationIndependentConstraints =
284 OrientationIndependentConstraints(mainAxisMin, mainAxisMax, crossAxisMin, crossAxisMax)
285 }
286
287 internal val IntrinsicMeasurable.rowColumnParentData: RowColumnParentData?
288 get() = parentData as? RowColumnParentData
289
290 internal val Placeable.rowColumnParentData: RowColumnParentData?
291 get() = parentData as? RowColumnParentData
292
293 internal val RowColumnParentData?.weight: Float
294 get() = this?.weight ?: 0f
295
296 internal val RowColumnParentData?.fill: Boolean
297 get() = this?.fill ?: true
298
299 internal val RowColumnParentData?.crossAxisAlignment: CrossAxisAlignment?
300 get() = this?.crossAxisAlignment
301
302 internal val RowColumnParentData?.isRelative: Boolean
303 get() = this.crossAxisAlignment?.isRelative ?: false
304
305 internal object IntrinsicMeasureBlocks {
306 fun HorizontalMinWidth(
307 measurables: List<IntrinsicMeasurable>,
308 availableHeight: Int,
309 mainAxisSpacing: Int
310 ): Int {
311 return intrinsicMainAxisSize(
312 measurables,
313 { h -> minIntrinsicWidth(h) },
314 availableHeight,
315 mainAxisSpacing,
316 )
317 }
318
319 fun VerticalMinWidth(
320 measurables: List<IntrinsicMeasurable>,
321 availableHeight: Int,
322 mainAxisSpacing: Int
323 ): Int {
324 return intrinsicCrossAxisSize(
325 measurables,
326 { w -> maxIntrinsicHeight(w) },
327 { h -> minIntrinsicWidth(h) },
328 availableHeight,
329 mainAxisSpacing,
330 )
331 }
332
333 fun HorizontalMinHeight(
334 measurables: List<IntrinsicMeasurable>,
335 availableWidth: Int,
336 mainAxisSpacing: Int
337 ): Int {
338 return intrinsicCrossAxisSize(
339 measurables,
340 { h -> maxIntrinsicWidth(h) },
341 { w -> minIntrinsicHeight(w) },
342 availableWidth,
343 mainAxisSpacing,
344 )
345 }
346
347 fun VerticalMinHeight(
348 measurables: List<IntrinsicMeasurable>,
349 availableWidth: Int,
350 mainAxisSpacing: Int
351 ): Int {
352 return intrinsicMainAxisSize(
353 measurables,
354 { w -> minIntrinsicHeight(w) },
355 availableWidth,
356 mainAxisSpacing,
357 )
358 }
359
360 fun HorizontalMaxWidth(
361 measurables: List<IntrinsicMeasurable>,
362 availableHeight: Int,
363 mainAxisSpacing: Int
364 ): Int {
365 return intrinsicMainAxisSize(
366 measurables,
367 { h -> maxIntrinsicWidth(h) },
368 availableHeight,
369 mainAxisSpacing,
370 )
371 }
372
373 fun VerticalMaxWidth(
374 measurables: List<IntrinsicMeasurable>,
375 availableHeight: Int,
376 mainAxisSpacing: Int
377 ): Int {
378 return intrinsicCrossAxisSize(
379 measurables,
380 { w -> maxIntrinsicHeight(w) },
381 { h -> maxIntrinsicWidth(h) },
382 availableHeight,
383 mainAxisSpacing,
384 )
385 }
386
387 fun HorizontalMaxHeight(
388 measurables: List<IntrinsicMeasurable>,
389 availableWidth: Int,
390 mainAxisSpacing: Int
391 ): Int {
392 return intrinsicCrossAxisSize(
393 measurables,
394 { h -> maxIntrinsicWidth(h) },
395 { w -> maxIntrinsicHeight(w) },
396 availableWidth,
397 mainAxisSpacing,
398 )
399 }
400
401 fun VerticalMaxHeight(
402 measurables: List<IntrinsicMeasurable>,
403 availableWidth: Int,
404 mainAxisSpacing: Int
405 ): Int {
406 return intrinsicMainAxisSize(
407 measurables,
408 { w -> maxIntrinsicHeight(w) },
409 availableWidth,
410 mainAxisSpacing,
411 )
412 }
413 }
414
intrinsicMainAxisSizenull415 private inline fun intrinsicMainAxisSize(
416 children: List<IntrinsicMeasurable>,
417 mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
418 crossAxisAvailable: Int,
419 mainAxisSpacing: Int
420 ): Int {
421 if (children.isEmpty()) return 0
422 var weightUnitSpace = 0
423 var fixedSpace = 0
424 var totalWeight = 0f
425 children.fastForEach { child ->
426 val weight = child.rowColumnParentData.weight
427 val size = child.mainAxisSize(crossAxisAvailable)
428 if (weight == 0f) {
429 fixedSpace += size
430 } else if (weight > 0f) {
431 totalWeight += weight
432 weightUnitSpace = max(weightUnitSpace, (size / weight).fastRoundToInt())
433 }
434 }
435 return (weightUnitSpace * totalWeight).fastRoundToInt() +
436 fixedSpace +
437 (children.size - 1) * mainAxisSpacing
438 }
439
intrinsicCrossAxisSizenull440 private inline fun intrinsicCrossAxisSize(
441 children: List<IntrinsicMeasurable>,
442 mainAxisSize: IntrinsicMeasurable.(Int) -> Int,
443 crossAxisSize: IntrinsicMeasurable.(Int) -> Int,
444 mainAxisAvailable: Int,
445 mainAxisSpacing: Int
446 ): Int {
447 if (children.isEmpty()) return 0
448 var fixedSpace = min((children.size - 1) * mainAxisSpacing, mainAxisAvailable)
449 var crossAxisMax = 0
450 var totalWeight = 0f
451 children.fastForEach { child ->
452 val weight = child.rowColumnParentData.weight
453 if (weight == 0f) {
454 // Ask the child how much main axis space it wants to occupy. This cannot be more
455 // than the remaining available space.
456 val remaining =
457 if (mainAxisAvailable == Constraints.Infinity) Constraints.Infinity
458 else mainAxisAvailable - fixedSpace
459 val mainAxisSpace = min(child.mainAxisSize(Constraints.Infinity), remaining)
460 fixedSpace += mainAxisSpace
461 // Now that the assigned main axis space is known, ask about the cross axis space.
462 crossAxisMax = max(crossAxisMax, child.crossAxisSize(mainAxisSpace))
463 } else if (weight > 0f) {
464 totalWeight += weight
465 }
466 }
467
468 // For weighted children, calculate how much main axis space weight=1 would represent.
469 val weightUnitSpace =
470 if (totalWeight == 0f) {
471 0
472 } else if (mainAxisAvailable == Constraints.Infinity) {
473 Constraints.Infinity
474 } else {
475 (max(mainAxisAvailable - fixedSpace, 0) / totalWeight).fastRoundToInt()
476 }
477
478 children.fastForEach { child ->
479 val weight = child.rowColumnParentData.weight
480 // Now the main axis for weighted children is known, so ask about the cross axis space.
481 if (weight > 0f) {
482 crossAxisMax =
483 max(
484 crossAxisMax,
485 child.crossAxisSize(
486 if (weightUnitSpace != Constraints.Infinity) {
487 (weightUnitSpace * weight).fastRoundToInt()
488 } else {
489 Constraints.Infinity
490 }
491 )
492 )
493 }
494 }
495 return crossAxisMax
496 }
497
498 internal class LayoutWeightElement(
499 val weight: Float,
500 val fill: Boolean,
501 ) : ModifierNodeElement<LayoutWeightNode>() {
createnull502 override fun create(): LayoutWeightNode {
503 return LayoutWeightNode(weight, fill)
504 }
505
updatenull506 override fun update(node: LayoutWeightNode) {
507 node.weight = weight
508 node.fill = fill
509 }
510
inspectablePropertiesnull511 override fun InspectorInfo.inspectableProperties() {
512 name = "weight"
513 value = weight
514 properties["weight"] = weight
515 properties["fill"] = fill
516 }
517
hashCodenull518 override fun hashCode(): Int {
519 var result = weight.hashCode()
520 result = 31 * result + fill.hashCode()
521 return result
522 }
523
equalsnull524 override fun equals(other: Any?): Boolean {
525 if (this === other) return true
526 val otherModifier = other as? LayoutWeightElement ?: return false
527 return weight == otherModifier.weight && fill == otherModifier.fill
528 }
529 }
530
531 internal class LayoutWeightNode(
532 var weight: Float,
533 var fill: Boolean,
534 ) : ParentDataModifierNode, Modifier.Node() {
modifyParentDatanull535 override fun Density.modifyParentData(parentData: Any?) =
536 ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
537 it.weight = weight
538 it.fill = fill
539 }
540 }
541
542 internal class WithAlignmentLineBlockElement(val block: (Measured) -> Int) :
543 ModifierNodeElement<SiblingsAlignedNode.WithAlignmentLineBlockNode>() {
createnull544 override fun create(): SiblingsAlignedNode.WithAlignmentLineBlockNode {
545 return SiblingsAlignedNode.WithAlignmentLineBlockNode(block)
546 }
547
updatenull548 override fun update(node: SiblingsAlignedNode.WithAlignmentLineBlockNode) {
549 node.block = block
550 }
551
equalsnull552 override fun equals(other: Any?): Boolean {
553 if (this === other) return true
554 val otherModifier = other as? WithAlignmentLineBlockElement ?: return false
555 return block === otherModifier.block
556 }
557
hashCodenull558 override fun hashCode(): Int = block.hashCode()
559
560 override fun InspectorInfo.inspectableProperties() {
561 name = "alignBy"
562 value = block
563 }
564 }
565
566 internal class WithAlignmentLineElement(val alignmentLine: AlignmentLine) :
567 ModifierNodeElement<SiblingsAlignedNode.WithAlignmentLineNode>() {
createnull568 override fun create(): SiblingsAlignedNode.WithAlignmentLineNode {
569 return SiblingsAlignedNode.WithAlignmentLineNode(alignmentLine)
570 }
571
updatenull572 override fun update(node: SiblingsAlignedNode.WithAlignmentLineNode) {
573 node.alignmentLine = alignmentLine
574 }
575
inspectablePropertiesnull576 override fun InspectorInfo.inspectableProperties() {
577 name = "alignBy"
578 value = alignmentLine
579 }
580
hashCodenull581 override fun hashCode(): Int = alignmentLine.hashCode()
582
583 override fun equals(other: Any?): Boolean {
584 if (this === other) return true
585 val otherModifier = other as? WithAlignmentLineElement ?: return false
586 return alignmentLine == otherModifier.alignmentLine
587 }
588 }
589
590 internal sealed class SiblingsAlignedNode : ParentDataModifierNode, Modifier.Node() {
modifyParentDatanull591 abstract override fun Density.modifyParentData(parentData: Any?): Any?
592
593 internal class WithAlignmentLineBlockNode(
594 var block: (Measured) -> Int,
595 ) : SiblingsAlignedNode() {
596 override fun Density.modifyParentData(parentData: Any?): Any {
597 return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
598 it.crossAxisAlignment =
599 CrossAxisAlignment.Relative(AlignmentLineProvider.Block(block))
600 }
601 }
602 }
603
604 internal class WithAlignmentLineNode(
605 var alignmentLine: AlignmentLine,
606 ) : SiblingsAlignedNode() {
modifyParentDatanull607 override fun Density.modifyParentData(parentData: Any?): Any {
608 return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
609 it.crossAxisAlignment =
610 CrossAxisAlignment.Relative(AlignmentLineProvider.Value(alignmentLine))
611 }
612 }
613 }
614 }
615
616 internal class HorizontalAlignElement(val horizontal: Alignment.Horizontal) :
617 ModifierNodeElement<HorizontalAlignNode>() {
createnull618 override fun create(): HorizontalAlignNode {
619 return HorizontalAlignNode(horizontal)
620 }
621
updatenull622 override fun update(node: HorizontalAlignNode) {
623 node.horizontal = horizontal
624 }
625
inspectablePropertiesnull626 override fun InspectorInfo.inspectableProperties() {
627 name = "align"
628 value = horizontal
629 }
630
hashCodenull631 override fun hashCode(): Int = horizontal.hashCode()
632
633 override fun equals(other: Any?): Boolean {
634 if (this === other) return true
635 val otherModifier = other as? HorizontalAlignElement ?: return false
636 return horizontal == otherModifier.horizontal
637 }
638 }
639
640 internal class HorizontalAlignNode(var horizontal: Alignment.Horizontal) :
641 ParentDataModifierNode, Modifier.Node() {
modifyParentDatanull642 override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
643 return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
644 it.crossAxisAlignment = CrossAxisAlignment.horizontal(horizontal)
645 }
646 }
647 }
648
649 internal class VerticalAlignElement(
650 val alignment: Alignment.Vertical,
651 ) : ModifierNodeElement<VerticalAlignNode>() {
createnull652 override fun create(): VerticalAlignNode {
653 return VerticalAlignNode(alignment)
654 }
655
updatenull656 override fun update(node: VerticalAlignNode) {
657 node.vertical = alignment
658 }
659
inspectablePropertiesnull660 override fun InspectorInfo.inspectableProperties() {
661 name = "align"
662 value = alignment
663 }
664
hashCodenull665 override fun hashCode(): Int = alignment.hashCode()
666
667 override fun equals(other: Any?): Boolean {
668 if (this === other) return true
669 val otherModifier = other as? VerticalAlignElement ?: return false
670 return alignment == otherModifier.alignment
671 }
672 }
673
674 internal class VerticalAlignNode(var vertical: Alignment.Vertical) :
675 ParentDataModifierNode, Modifier.Node() {
modifyParentDatanull676 override fun Density.modifyParentData(parentData: Any?): RowColumnParentData {
677 return ((parentData as? RowColumnParentData) ?: RowColumnParentData()).also {
678 it.crossAxisAlignment = CrossAxisAlignment.vertical(vertical)
679 }
680 }
681 }
682
683 /** Parent data associated with children. */
684 internal data class RowColumnParentData(
685 var weight: Float = 0f,
686 var fill: Boolean = true,
687 var crossAxisAlignment: CrossAxisAlignment? = null,
688 var flowLayoutData: FlowLayoutData? = null,
689 )
690
691 /** Provides the alignment line. */
692 internal sealed class AlignmentLineProvider {
calculateAlignmentLinePositionnull693 abstract fun calculateAlignmentLinePosition(placeable: Placeable): Int
694
695 data class Block(val lineProviderBlock: (Measured) -> Int) : AlignmentLineProvider() {
696 override fun calculateAlignmentLinePosition(placeable: Placeable): Int {
697 return lineProviderBlock(placeable)
698 }
699 }
700
701 data class Value(val alignmentLine: AlignmentLine) : AlignmentLineProvider() {
calculateAlignmentLinePositionnull702 override fun calculateAlignmentLinePosition(placeable: Placeable): Int {
703 return placeable[alignmentLine]
704 }
705 }
706 }
707