1 /* 2 * Copyright 2020 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.ui.layout 18 19 import androidx.compose.ui.internal.JvmDefaultWithCompatibility 20 import androidx.compose.ui.node.LookaheadCapablePlaceable 21 import androidx.compose.ui.node.checkMeasuredSize 22 import androidx.compose.ui.unit.Density 23 import androidx.compose.ui.unit.LayoutDirection 24 25 @DslMarker annotation class MeasureScopeMarker 26 27 /** 28 * The receiver scope of a layout's measure lambda. The return value of the measure lambda is 29 * [MeasureResult], which should be returned by [layout] 30 */ 31 @MeasureScopeMarker 32 @JvmDefaultWithCompatibility 33 interface MeasureScope : IntrinsicMeasureScope { 34 /** 35 * Sets the size and alignment lines of the measured layout, as well as the positioning block 36 * that defines the children positioning logic. The [placementBlock] is a lambda used for 37 * positioning children. [Placeable.placeAt] should be called on children inside placementBlock. 38 * The [alignmentLines] can be used by the parent layouts to decide layout, and can be queried 39 * using the [Placeable.get] operator. Note that alignment lines will be inherited by parent 40 * layouts, such that indirect parents will be able to query them as well. 41 * 42 * @param width the measured width of the layout 43 * @param height the measured height of the layout 44 * @param alignmentLines the alignment lines defined by the layout 45 * @param placementBlock block defining the children positioning of the current layout 46 */ layoutnull47 fun layout( 48 width: Int, 49 height: Int, 50 alignmentLines: Map<AlignmentLine, Int> = emptyMap(), 51 placementBlock: Placeable.PlacementScope.() -> Unit 52 ) = layout(width, height, alignmentLines, null, placementBlock) 53 54 /** 55 * Sets the size and alignment lines of the measured layout, as well as the positioning block 56 * that defines the children positioning logic. The [placementBlock] is a lambda used for 57 * positioning children. [Placeable.placeAt] should be called on children inside placementBlock. 58 * The [alignmentLines] can be used by the parent layouts to decide layout, and can be queried 59 * using the [Placeable.get] operator. Note that alignment lines will be inherited by parent 60 * layouts, such that indirect parents will be able to query them as well. 61 * 62 * @param width the measured width of the layout 63 * @param height the measured height of the layout 64 * @param alignmentLines the alignment lines defined by the layout 65 * @param rulers a method to set Ruler values used by all placed children 66 * @param placementBlock block defining the children positioning of the current layout 67 */ 68 @Suppress("PrimitiveInCollection") 69 fun layout( 70 width: Int, 71 height: Int, 72 alignmentLines: Map<AlignmentLine, Int> = emptyMap(), 73 rulers: (RulerScope.() -> Unit)? = null, 74 placementBlock: Placeable.PlacementScope.() -> Unit 75 ): MeasureResult { 76 checkMeasuredSize(width, height) 77 return object : MeasureResult { 78 override val width = width 79 override val height = height 80 override val alignmentLines = alignmentLines 81 override val rulers = rulers 82 83 override fun placeChildren() { 84 // This isn't called from anywhere inside the compose framework. This might 85 // be called by tests or external frameworks. 86 if (this@MeasureScope is LookaheadCapablePlaceable) { 87 placementScope.placementBlock() 88 } else { 89 SimplePlacementScope(width, layoutDirection).placementBlock() 90 } 91 } 92 } 93 } 94 } 95 96 /** 97 * This is used by the default implementation of [MeasureScope.layout] and will never be called by 98 * any implementation of [MeasureScope] in the compose framework. 99 */ 100 private class SimplePlacementScope( 101 override val parentWidth: Int, 102 override val parentLayoutDirection: LayoutDirection, 103 ) : Placeable.PlacementScope() 104 105 /** 106 * A scope used in [MeasureScope.layout] for the `rulers` parameter to allow a layout to define 107 * [Ruler] values for children. 108 * 109 * @sample androidx.compose.ui.samples.RulerProducerUsage 110 */ 111 @MeasureScopeMarker 112 interface RulerScope : Density { 113 /** 114 * [LayoutCoordinates] of the position in the hierarchy that the [Ruler] will be 115 * [provided][Ruler.provides]. 116 */ 117 val coordinates: LayoutCoordinates 118 119 /** Provides a constant value for a [Ruler]. */ providesnull120 infix fun Ruler.provides(value: Float) 121 122 /** 123 * Provides a [VerticalRuler] value that is relative to the left side in an LTR layout or right 124 * side on an RTL layout. 125 */ 126 infix fun VerticalRuler.providesRelative(value: Float) 127 } 128