1 /*
<lambda>null2 * 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.foundation.layout
18
19 import androidx.compose.runtime.Composable
20 import androidx.compose.runtime.Stable
21 import androidx.compose.ui.Alignment
22 import androidx.compose.ui.Modifier
23 import androidx.compose.ui.UiComposable
24 import androidx.compose.ui.layout.SubcomposeLayout
25 import androidx.compose.ui.unit.Constraints
26 import androidx.compose.ui.unit.Density
27 import androidx.compose.ui.unit.Dp
28 import androidx.compose.ui.unit.LayoutDirection
29
30 /**
31 * A composable that defines its own content according to the available space, based on the incoming
32 * constraints or the current [LayoutDirection]. Example usage:
33 *
34 * @sample androidx.compose.foundation.layout.samples.BoxWithConstraintsSample
35 *
36 * The composable will compose the given children, and will position the resulting layout elements
37 * in a parent layout which behaves similar to a [Box]. The layout will size itself to fit the
38 * content, subject to the incoming constraints. When children are smaller than the parent, by
39 * default they will be positioned inside the layout according to the [contentAlignment]. For
40 * individually specifying the alignments of the children layouts, use the [BoxScope.align]
41 * modifier. By default, the content will be measured without the [Box]'s incoming min constraints,
42 * unless [propagateMinConstraints] is `true`. As an example, setting [propagateMinConstraints] to
43 * `true` can be useful when the [BoxWithConstraints] has content on which modifiers cannot be
44 * specified directly and setting a min size on the content of the [BoxWithConstraints] is needed.
45 * If [propagateMinConstraints] is set to `true`, the min size set on the [BoxWithConstraints] will
46 * also be applied to the content, whereas otherwise the min size will only apply to the
47 * [BoxWithConstraints]. When the content has more than one layout child the layout children will be
48 * stacked one on top of the other (positioned as explained above) in the composition order.
49 *
50 * @param modifier Modifier to be applied to the layout.
51 * @param contentAlignment The default alignment inside the [BoxWithConstraints].
52 * @param propagateMinConstraints Whether the incoming min constraints should be passed to content.
53 * @param content The content of the [BoxWithConstraints].
54 */
55 @Composable
56 @UiComposable
57 fun BoxWithConstraints(
58 modifier: Modifier = Modifier,
59 contentAlignment: Alignment = Alignment.TopStart,
60 propagateMinConstraints: Boolean = false,
61 content: @Composable @UiComposable BoxWithConstraintsScope.() -> Unit
62 ) {
63 val measurePolicy = maybeCachedBoxMeasurePolicy(contentAlignment, propagateMinConstraints)
64 SubcomposeLayout(modifier) { constraints ->
65 val scope = BoxWithConstraintsScopeImpl(this, constraints)
66 val measurables = subcompose(Unit) { scope.content() }
67 with(measurePolicy) { measure(measurables, constraints) }
68 }
69 }
70
71 /** Receiver scope being used by the children parameter of [BoxWithConstraints] */
72 @Stable
73 interface BoxWithConstraintsScope : BoxScope {
74 /**
75 * The constraints given by the parent layout in pixels.
76 *
77 * Use [minWidth], [maxWidth], [minHeight] or [maxHeight] if you need value in [Dp].
78 */
79 val constraints: Constraints
80 /**
81 * The minimum width in [Dp].
82 *
83 * @see constraints for the values in pixels.
84 */
85 val minWidth: Dp
86 /**
87 * The maximum width in [Dp].
88 *
89 * @see constraints for the values in pixels.
90 */
91 val maxWidth: Dp
92 /**
93 * The minimum height in [Dp].
94 *
95 * @see constraints for the values in pixels.
96 */
97 val minHeight: Dp
98 /**
99 * The maximum height in [Dp].
100 *
101 * @see constraints for the values in pixels.
102 */
103 val maxHeight: Dp
104 }
105
106 private data class BoxWithConstraintsScopeImpl(
107 private val density: Density,
108 override val constraints: Constraints
<lambda>null109 ) : BoxWithConstraintsScope, BoxScope by BoxScopeInstance {
110 override val minWidth: Dp
111 get() = with(density) { constraints.minWidth.toDp() }
112
113 override val maxWidth: Dp
114 get() =
115 with(density) {
116 if (constraints.hasBoundedWidth) constraints.maxWidth.toDp() else Dp.Infinity
117 }
118
119 override val minHeight: Dp
120 get() = with(density) { constraints.minHeight.toDp() }
121
122 override val maxHeight: Dp
123 get() =
124 with(density) {
125 if (constraints.hasBoundedHeight) constraints.maxHeight.toDp() else Dp.Infinity
126 }
127 }
128