commonMain.androidx.compose.ui.layout.MultiContentMeasurePolicy.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of ui-desktop Show documentation
Show all versions of ui-desktop Show documentation
Compose UI primitives. This library contains the primitives that form the Compose UI Toolkit, such as drawing, measurement and layout.
/*
* Copyright 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.compose.ui.layout
import androidx.compose.runtime.Stable
import androidx.compose.ui.node.getChildrenOfVirtualChildren
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.util.fastMap
/**
* Defines the measure and layout behavior of a [Layout] overload which accepts a list of multiple
* composable content lambdas.
*
* This interface is identical to [MeasurePolicy], but provides you with a list of lists of
* [Measurable]s which allows to threat children put into different content lambdas differently.
* Such list has the same size as the list of contents passed into [Layout] and contains the list
* of [Measurable]s of the corresponding content lambda in the same order.
*
* Intrinsic measurement methods define the intrinsic size of the layout. These can be queried
* by the layout's parent in order to obtain, in specific cases, more information about
* the size of the layout in the absence of specific constraints:
* - [minIntrinsicWidth] defines the minimum width this layout can take, given
* a specific height, such that the content of the layout will be painted correctly
* - [minIntrinsicHeight] defines the minimum height this layout can take, given
* a specific width, such that the content of the layout will be painted correctly
* - [maxIntrinsicWidth] defines the minimum width such that increasing it further
* will not decrease the minimum intrinsic height
* - [maxIntrinsicHeight] defines the minimum height such that increasing it further
* will not decrease the minimum intrinsic width
* Most layout scenarios do not require querying intrinsic measurements. Therefore, when writing
* a custom layout, it is common to only define the actual measurement, as most of the times
* the intrinsic measurements of the layout will not be queried. Moreover, intrinsic measurement
* methods have default implementations that make a best effort attempt to calculate the intrinsic
* measurements by reusing the [measure] method. Note this will not be correct for all layouts,
* but can be a convenient approximation.
* Intrinsic measurements can be useful when the layout system enforcement of no more than one
* measurement per child is limiting. Layouts that use them are the `preferredWidth(IntrinsicSize)`
* and `preferredHeight(IntrinsicSize)` modifiers. See their samples for when they can be useful.
*
* @see Layout
* @see MeasurePolicy
*/
@Stable
fun interface MultiContentMeasurePolicy {
/**
* The function that defines the measurement and layout. Each [Measurable] in the [measurables]
* lists corresponds to a layout child of the layout, and children can be measured using the
* [Measurable.measure] method. This method takes the [Constraints] which the child should
* respect; different children can be measured with different constraints.
* Measuring a child returns a [Placeable], which reveals the size chosen by the child as a
* result of its own measurement. According to the children sizes, the parent defines the
* position of the children, by [placing][Placeable.PlacementScope.place] the [Placeable]s in
* the [MeasureResult.placeChildren] of the returned [MeasureResult]. Therefore the parent needs
* to measure its children with appropriate [Constraints], such that whatever valid sizes
* children choose, they can be laid out correctly according to the parent's layout algorithm.
* This is because there is no measurement negotiation between the parent and children:
* once a child chooses its size, the parent needs to handle it correctly.
*
* It is identical to [MeasurePolicy.measure], but provides you with a list of lists of
* [Measurable]s which allows to threat children put into different content lambdas
* differently. Such list has the same size as the list of contents passed into [Layout] and
* contains the list of [Measurable]s of the corresponding content lambda in the same order.
*
* Note that a child is allowed to choose a size that does not satisfy its constraints. However,
* when this happens, the placeable's [width][Placeable.width] and [height][Placeable.height]
* will not represent the real size of the child, but rather the size coerced in the
* child's constraints. Therefore, it is common for parents to assume in their layout
* algorithm that its children will always respect the constraints. When this
* does not happen in reality, the position assigned to the child will be
* automatically offset to be centered on the space assigned by the parent under
* the assumption that constraints were respected. Rarely, when a parent really needs to know
* the true size of the child, they can read this from the placeable's
* [Placeable.measuredWidth] and [Placeable.measuredHeight].
*
* [MeasureResult] objects are usually created using the [MeasureScope.layout]
* factory, which takes the calculated size of this layout, its alignment lines, and a block
* defining the positioning of the children layouts.
*/
fun MeasureScope.measure(
measurables: List>,
constraints: Constraints
): MeasureResult
/**
* The function used to calculate [IntrinsicMeasurable.minIntrinsicWidth]. It represents
* the minimum width this layout can take, given a specific height, such that the content
* of the layout can be painted correctly.
*
* It is identical to [MeasurePolicy.minIntrinsicWidth], but provides you with a list of
* lists of [Measurable]s which allows to threat children put into different content lambdas
* differently. Such list has the same size as the list of contents passed into [Layout] and
* contains the list of [Measurable]s of the corresponding content lambda in the same order.
*/
fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List>,
height: Int
): Int {
val mapped = measurables.fastMap { list ->
list.fastMap {
DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Width)
}
}
val constraints = Constraints(maxHeight = height)
val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
val layoutResult = layoutReceiver.measure(mapped, constraints)
return layoutResult.width
}
/**
* The function used to calculate [IntrinsicMeasurable.minIntrinsicHeight]. It represents
* the minimum height this layout can take, given a specific width, such that the content
* of the layout will be painted correctly.
*
* It is identical to [MeasurePolicy.minIntrinsicHeight], but provides you with a list of
* lists of [Measurable]s which allows to threat children put into different content lambdas
* differently. Such list has the same size as the list of contents passed into [Layout] and
* contains the list of [Measurable]s of the corresponding content lambda in the same order.
*/
fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List>,
width: Int
): Int {
val mapped = measurables.fastMap { list ->
list.fastMap {
DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Min, IntrinsicWidthHeight.Height)
}
}
val constraints = Constraints(maxWidth = width)
val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
val layoutResult = layoutReceiver.measure(mapped, constraints)
return layoutResult.height
}
/**
* The function used to calculate [IntrinsicMeasurable.maxIntrinsicWidth]. It represents the
* minimum width such that increasing it further will not decrease the minimum intrinsic height.
*
* It is identical to [MeasurePolicy.maxIntrinsicWidth], but provides you with a list of
* lists of [Measurable]s which allows to threat children put into different content lambdas
* differently. Such list has the same size as the list of contents passed into [Layout] and
* contains the list of [Measurable]s of the corresponding content lambda in the same order.
*/
fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurables: List>,
height: Int
): Int {
val mapped = measurables.fastMap { list ->
list.fastMap {
DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Width)
}
}
val constraints = Constraints(maxHeight = height)
val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
val layoutResult = layoutReceiver.measure(mapped, constraints)
return layoutResult.width
}
/**
* The function used to calculate [IntrinsicMeasurable.maxIntrinsicHeight]. It represents the
* minimum height such that increasing it further will not decrease the minimum intrinsic width.
*
* It is identical to [MeasurePolicy.maxIntrinsicHeight], but provides you with a list of
* lists of [Measurable]s which allows to threat children put into different content lambdas
* differently. Such list has the same size as the list of contents passed into [Layout] and
* contains the list of [Measurable]s of the corresponding content lambda in the same order.
*/
fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List>,
width: Int
): Int {
val mapped = measurables.fastMap { list ->
list.fastMap {
DefaultIntrinsicMeasurable(it, IntrinsicMinMax.Max, IntrinsicWidthHeight.Height)
}
}
val constraints = Constraints(maxWidth = width)
val layoutReceiver = IntrinsicsMeasureScope(this, layoutDirection)
val layoutResult = layoutReceiver.measure(mapped, constraints)
return layoutResult.height
}
}
@PublishedApi
internal fun createMeasurePolicy(
measurePolicy: MultiContentMeasurePolicy
): MeasurePolicy = MultiContentMeasurePolicyImpl(measurePolicy)
internal data class MultiContentMeasurePolicyImpl(
val measurePolicy: MultiContentMeasurePolicy
) : MeasurePolicy {
override fun MeasureScope.measure(
measurables: List,
constraints: Constraints
) = with(measurePolicy) {
measure(getChildrenOfVirtualChildren(this@measure), constraints)
}
override fun IntrinsicMeasureScope.minIntrinsicWidth(
measurables: List,
height: Int
) = with(measurePolicy) {
minIntrinsicWidth(getChildrenOfVirtualChildren(this@minIntrinsicWidth), height)
}
override fun IntrinsicMeasureScope.minIntrinsicHeight(
measurables: List,
width: Int
) = with(measurePolicy) {
minIntrinsicHeight(getChildrenOfVirtualChildren(this@minIntrinsicHeight), width)
}
override fun IntrinsicMeasureScope.maxIntrinsicWidth(
measurables: List,
height: Int
) = with(measurePolicy) {
maxIntrinsicWidth(getChildrenOfVirtualChildren(this@maxIntrinsicWidth), height)
}
override fun IntrinsicMeasureScope.maxIntrinsicHeight(
measurables: List,
width: Int
) = with(measurePolicy) {
maxIntrinsicHeight(getChildrenOfVirtualChildren(this@maxIntrinsicHeight), width)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy