1 /*
<lambda>null2  * Copyright 2022 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.node.getChildrenOfVirtualChildren
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] overload which accepts a list of multiple
26  * composable content lambdas.
27  *
28  * This interface is identical to [MeasurePolicy], but provides you with a list of lists of
29  * [Measurable]s which allows to threat children put into different content lambdas differently.
30  * Such list has the same size as the list of contents passed into [Layout] and contains the list of
31  * [Measurable]s of the corresponding content lambda in the same order.
32  *
33  * Intrinsic measurement methods define the intrinsic size of the layout. These can be queried by
34  * the layout's parent in order to obtain, in specific cases, more information about the size of the
35  * layout in the absence of specific constraints:
36  * - [minIntrinsicWidth] defines the minimum width this layout can take, given a specific height,
37  *   such that the content of the layout will be painted correctly
38  * - [minIntrinsicHeight] defines the minimum height this layout can take, given a specific width,
39  *   such that the content of the layout will be painted correctly
40  * - [maxIntrinsicWidth] defines the minimum width such that increasing it further will not decrease
41  *   the minimum intrinsic height
42  * - [maxIntrinsicHeight] defines the minimum height such that increasing it further will not
43  *   decrease the minimum intrinsic width Most layout scenarios do not require querying intrinsic
44  *   measurements. Therefore, when writing a custom layout, it is common to only define the actual
45  *   measurement, as most of the times the intrinsic measurements of the layout will not be queried.
46  *   Moreover, intrinsic measurement methods have default implementations that make a best effort
47  *   attempt to calculate the intrinsic measurements by reusing the [measure] method. Note this will
48  *   not be correct for all layouts, but can be a convenient approximation. Intrinsic measurements
49  *   can be useful when the layout system enforcement of no more than one measurement per child is
50  *   limiting. Layouts that use them are the `preferredWidth(IntrinsicSize)` and
51  *   `preferredHeight(IntrinsicSize)` modifiers. See their samples for when they can be useful.
52  *
53  * @see Layout
54  * @see MeasurePolicy
55  */
56 @Stable
57 fun interface MultiContentMeasurePolicy {
58     /**
59      * The function that defines the measurement and layout. Each [Measurable] in the [measurables]
60      * lists corresponds to a layout child of the layout, and children can be measured using the
61      * [Measurable.measure] method. This method takes the [Constraints] which the child should
62      * respect; different children can be measured with different constraints. Measuring a child
63      * returns a [Placeable], which reveals the size chosen by the child as a result of its own
64      * measurement. According to the children sizes, the parent defines the position of the
65      * children, by [placing][Placeable.PlacementScope.place] the [Placeable]s in the
66      * [MeasureResult.placeChildren] of the returned [MeasureResult]. Therefore the parent needs to
67      * measure its children with appropriate [Constraints], such that whatever valid sizes children
68      * choose, they can be laid out correctly according to the parent's layout algorithm. This is
69      * because there is no measurement negotiation between the parent and children: once a child
70      * chooses its size, the parent needs to handle it correctly.
71      *
72      * It is identical to [MeasurePolicy.measure], but provides you with a list of lists of
73      * [Measurable]s which allows to threat children put into different content lambdas differently.
74      * Such list has the same size as the list of contents passed into [Layout] and contains the
75      * list of [Measurable]s of the corresponding content lambda in the same order.
76      *
77      * Note that a child is allowed to choose a size that does not satisfy its constraints. However,
78      * when this happens, the placeable's [width][Placeable.width] and [height][Placeable.height]
79      * will not represent the real size of the child, but rather the size coerced in the child's
80      * constraints. Therefore, it is common for parents to assume in their layout algorithm that its
81      * children will always respect the constraints. When this does not happen in reality, the
82      * position assigned to the child will be automatically offset to be centered on the space
83      * assigned by the parent under the assumption that constraints were respected. Rarely, when a
84      * parent really needs to know the true size of the child, they can read this from the
85      * placeable's [Placeable.measuredWidth] and [Placeable.measuredHeight].
86      *
87      * [MeasureResult] objects are usually created using the [MeasureScope.layout] factory, which
88      * takes the calculated size of this layout, its alignment lines, and a block defining the
89      * positioning of the children layouts.
90      */
91     fun MeasureScope.measure(
92         measurables: List<List<Measurable>>,
93         constraints: Constraints
94     ): MeasureResult
95 
96     /**
97      * The function used to calculate [IntrinsicMeasurable.minIntrinsicWidth]. It represents the
98      * minimum width this layout can take, given a specific height, such that the content of the
99      * layout can be painted correctly.
100      *
101      * It is identical to [MeasurePolicy.minIntrinsicWidth], but provides you with a list of lists
102      * of [Measurable]s which allows to threat children put into different content lambdas
103      * differently. Such list has the same size as the list of contents passed into [Layout] and
104      * contains the list of [Measurable]s of the corresponding content lambda in the same order.
105      */
106     fun IntrinsicMeasureScope.minIntrinsicWidth(
107         measurables: List<List<IntrinsicMeasurable>>,
108         height: Int
109     ): Int {
110         val mapped =
111             measurables.fastMap { list ->
112                 list.fastMap {
113                     DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Width)
114                 }
115             }
116         val constraints = Constraints(maxHeight = height)
117         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
118         val layoutResult = layoutReceiver.measure(mapped, constraints)
119         return layoutResult.width
120     }
121 
122     /**
123      * The function used to calculate [IntrinsicMeasurable.minIntrinsicHeight]. It represents the
124      * minimum height this layout can take, given a specific width, such that the content of the
125      * layout will be painted correctly.
126      *
127      * It is identical to [MeasurePolicy.minIntrinsicHeight], but provides you with a list of lists
128      * of [Measurable]s which allows to threat children put into different content lambdas
129      * differently. Such list has the same size as the list of contents passed into [Layout] and
130      * contains the list of [Measurable]s of the corresponding content lambda in the same order.
131      */
132     fun IntrinsicMeasureScope.minIntrinsicHeight(
133         measurables: List<List<IntrinsicMeasurable>>,
134         width: Int
135     ): Int {
136         val mapped =
137             measurables.fastMap { list ->
138                 list.fastMap {
139                     DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Height)
140                 }
141             }
142         val constraints = Constraints(maxWidth = width)
143         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
144         val layoutResult = layoutReceiver.measure(mapped, constraints)
145         return layoutResult.height
146     }
147 
148     /**
149      * The function used to calculate [IntrinsicMeasurable.maxIntrinsicWidth]. It represents the
150      * minimum width such that increasing it further will not decrease the minimum intrinsic height.
151      *
152      * It is identical to [MeasurePolicy.maxIntrinsicWidth], but provides you with a list of lists
153      * of [Measurable]s which allows to threat children put into different content lambdas
154      * differently. Such list has the same size as the list of contents passed into [Layout] and
155      * contains the list of [Measurable]s of the corresponding content lambda in the same order.
156      */
157     fun IntrinsicMeasureScope.maxIntrinsicWidth(
158         measurables: List<List<IntrinsicMeasurable>>,
159         height: Int
160     ): Int {
161         val mapped =
162             measurables.fastMap { list ->
163                 list.fastMap {
164                     DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Width)
165                 }
166             }
167         val constraints = Constraints(maxHeight = height)
168         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
169         val layoutResult = layoutReceiver.measure(mapped, constraints)
170         return layoutResult.width
171     }
172 
173     /**
174      * The function used to calculate [IntrinsicMeasurable.maxIntrinsicHeight]. It represents the
175      * minimum height such that increasing it further will not decrease the minimum intrinsic width.
176      *
177      * It is identical to [MeasurePolicy.maxIntrinsicHeight], but provides you with a list of lists
178      * of [Measurable]s which allows to threat children put into different content lambdas
179      * differently. Such list has the same size as the list of contents passed into [Layout] and
180      * contains the list of [Measurable]s of the corresponding content lambda in the same order.
181      */
182     fun IntrinsicMeasureScope.maxIntrinsicHeight(
183         measurables: List<List<IntrinsicMeasurable>>,
184         width: Int
185     ): Int {
186         val mapped =
187             measurables.fastMap { list ->
188                 list.fastMap {
189                     DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Height)
190                 }
191             }
192         val constraints = Constraints(maxWidth = width)
193         val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
194         val layoutResult = layoutReceiver.measure(mapped, constraints)
195         return layoutResult.height
196     }
197 }
198 
199 @PublishedApi
createMeasurePolicynull200 internal fun createMeasurePolicy(measurePolicy: MultiContentMeasurePolicy): MeasurePolicy =
201     MultiContentMeasurePolicyImpl(measurePolicy)
202 
203 internal data class MultiContentMeasurePolicyImpl(val measurePolicy: MultiContentMeasurePolicy) :
204     MeasurePolicy {
205     override fun MeasureScope.measure(measurables: List<Measurable>, constraints: Constraints) =
206         with(measurePolicy) { measure(getChildrenOfVirtualChildren(this@measure), constraints) }
207 
208     override fun IntrinsicMeasureScope.minIntrinsicWidth(
209         measurables: List<IntrinsicMeasurable>,
210         height: Int
211     ) =
212         with(measurePolicy) {
213             minIntrinsicWidth(getChildrenOfVirtualChildren(this@minIntrinsicWidth), height)
214         }
215 
216     override fun IntrinsicMeasureScope.minIntrinsicHeight(
217         measurables: List<IntrinsicMeasurable>,
218         width: Int
219     ) =
220         with(measurePolicy) {
221             minIntrinsicHeight(getChildrenOfVirtualChildren(this@minIntrinsicHeight), width)
222         }
223 
224     override fun IntrinsicMeasureScope.maxIntrinsicWidth(
225         measurables: List<IntrinsicMeasurable>,
226         height: Int
227     ) =
228         with(measurePolicy) {
229             maxIntrinsicWidth(getChildrenOfVirtualChildren(this@maxIntrinsicWidth), height)
230         }
231 
232     override fun IntrinsicMeasureScope.maxIntrinsicHeight(
233         measurables: List<IntrinsicMeasurable>,
234         width: Int
235     ) =
236         with(measurePolicy) {
237             maxIntrinsicHeight(getChildrenOfVirtualChildren(this@maxIntrinsicHeight), width)
238         }
239 }
240