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