1 /*
2  * Copyright 2021 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.runtime.Stable
20 import androidx.compose.ui.internal.JvmDefaultWithCompatibility
21 import androidx.compose.ui.unit.Constraints
22 import androidx.compose.ui.util.fastMap
23 
24 /**
25  * Defines the measure and layout behavior of a [Layout]. [Layout] and [MeasurePolicy] are the way
26  * Compose layouts (such as `Box`, `Column`, etc.) are built, and they can also be used to achieve
27  * custom layouts.
28  *
29  * See [Layout] samples for examples of how to use [MeasurePolicy].
30  *
31  * Intrinsic measurement methods define the intrinsic size of the layout. These can be queried by
32  * the layout's parent in order to obtain, in specific cases, more information about the size of the
33  * layout in the absence of specific constraints:
34  * - [minIntrinsicWidth] defines the minimum width this layout can take, given a specific height,
35  *   such that the content of the layout will be painted correctly
36  * - [minIntrinsicHeight] defines the minimum height this layout can take, given a specific width,
37  *   such that the content of the layout will be painted correctly
38  * - [maxIntrinsicWidth] defines the minimum width such that increasing it further will not decrease
39  *   the minimum intrinsic height
40  * - [maxIntrinsicHeight] defines the minimum height such that increasing it further will not
41  *   decrease the minimum intrinsic width
42  *
43  * Most layout scenarios do not require querying intrinsic measurements. Therefore, when writing a
44  * custom layout, it is common to only define the actual measurement, as most of the times the
45  * intrinsic measurements of the layout will not be queried. Moreover, intrinsic measurement methods
46  * have default implementations that make a best effort attempt to calculate the intrinsic
47  * measurements by reusing the [measure] method. Note this will not be correct for all layouts, but
48  * can be a convenient approximation.
49  *
50  * Intrinsic measurements can be useful when the layout system enforcement of no more than one
51  * measurement per child is limiting. Layouts that use them are the `preferredWidth(IntrinsicSize)`
52  * and `preferredHeight(IntrinsicSize)` modifiers. See their samples for when they can be useful.
53  *
54  * @see Layout
55  */
56 @Stable
57 @JvmDefaultWithCompatibility
interfacenull58 fun interface MeasurePolicy {
59     /**
60      * The function that defines the measurement and layout. Each [Measurable] in the [measurables]
61      * list corresponds to a layout child of the layout, and children can be measured using the
62      * [Measurable.measure] method. This method takes the [Constraints] which the child should
63      * respect; different children can be measured with different constraints.
64      *
65      * Measuring a child returns a [Placeable], which reveals the size chosen by the child as a
66      * result of its own measurement. According to the children sizes, the parent defines the
67      * position of the children, by [placing][Placeable.PlacementScope.place] the [Placeable]s in
68      * the [MeasureResult.placeChildren] of the returned [MeasureResult]. Therefore the parent needs
69      * to measure its children with appropriate [Constraints], such that whatever valid sizes
70      * children choose, they can be laid out correctly according to the parent's layout algorithm.
71      * This is because there is no measurement negotiation between the parent and children: once a
72      * child chooses its size, the parent needs to handle it correctly.
73      *
74      * Note that a child is allowed to choose a size that does not satisfy its constraints. However,
75      * when this happens, the placeable's [width][Placeable.width] and [height][Placeable.height]
76      * will not represent the real size of the child, but rather the size coerced in the child's
77      * constraints. Therefore, it is common for parents to assume in their layout algorithm that its
78      * children will always respect the constraints. When this does not happen in reality, the
79      * position assigned to the child will be automatically offset to be centered on the space
80      * assigned by the parent under the assumption that constraints were respected. Rarely, when a
81      * parent really needs to know the true size of the child, they can read this from the
82      * placeable's [Placeable.measuredWidth] and [Placeable.measuredHeight].
83      *
84      * [MeasureResult] objects are usually created using the [MeasureScope.layout] factory, which
85      * takes the calculated size of this layout, its alignment lines, and a block defining the
86      * positioning of the children layouts.
87      */
88     fun MeasureScope.measure(measurables: List<Measurable>, constraints: Constraints): MeasureResult
89 
90     /**
91      * The function used to calculate [IntrinsicMeasurable.minIntrinsicWidth]. It represents the
92      * minimum width this layout can take, given a specific height, such that the content of the
93      * layout can be painted correctly. There should be no side-effect from implementers of
94      * [minIntrinsicWidth].
95      */
96     fun IntrinsicMeasureScope.minIntrinsicWidth(
97         measurables: List<IntrinsicMeasurable>,
98         height: Int
99     ): Int {
100         val mapped =
101             measurables.fastMap {
102                 DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Width)
103             }
104         val constraints = Constraints(maxHeight = height)
105         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
106         val layoutResult = layoutReceiver.measure(mapped, constraints)
107         return layoutResult.width
108     }
109 
110     /**
111      * The function used to calculate [IntrinsicMeasurable.minIntrinsicHeight]. It represents the
112      * minimum height this layout can take, given a specific width, such that the content of the
113      * layout will be painted correctly. There should be no side-effect from implementers of
114      * [minIntrinsicHeight].
115      */
116     fun IntrinsicMeasureScope.minIntrinsicHeight(
117         measurables: List<IntrinsicMeasurable>,
118         width: Int
119     ): Int {
120         val mapped =
121             measurables.fastMap {
122                 DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Height)
123             }
124         val constraints = Constraints(maxWidth = width)
125         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
126         val layoutResult = layoutReceiver.measure(mapped, constraints)
127         return layoutResult.height
128     }
129 
130     /**
131      * The function used to calculate [IntrinsicMeasurable.maxIntrinsicWidth]. It represents the
132      * minimum width such that increasing it further will not decrease the minimum intrinsic height.
133      * There should be no side-effects from implementers of [maxIntrinsicWidth].
134      */
135     fun IntrinsicMeasureScope.maxIntrinsicWidth(
136         measurables: List<IntrinsicMeasurable>,
137         height: Int
138     ): Int {
139         val mapped =
140             measurables.fastMap {
141                 DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Width)
142             }
143         val constraints = Constraints(maxHeight = height)
144         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
145         val layoutResult = layoutReceiver.measure(mapped, constraints)
146         return layoutResult.width
147     }
148 
149     /**
150      * The function used to calculate [IntrinsicMeasurable.maxIntrinsicHeight]. It represents the
151      * minimum height such that increasing it further will not decrease the minimum intrinsic width.
152      * There should be no side-effects from implementers of [maxIntrinsicHeight].
153      */
154     fun IntrinsicMeasureScope.maxIntrinsicHeight(
155         measurables: List<IntrinsicMeasurable>,
156         width: Int
157     ): Int {
158         val mapped =
159             measurables.fastMap {
160                 DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Height)
161             }
162         val constraints = Constraints(maxWidth = width)
163         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
164         val layoutResult = layoutReceiver.measure(mapped, constraints)
165         return layoutResult.height
166     }
167 }
168