[go: nahoru, domu]

blob: b29eb7c2fd347b3fb17168affe88c3df01b9c3ba [file] [log] [blame]
Mihai Popa53db3272020-03-16 17:06:47 +00001/*
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
Louis Pullen-Freilich623e4052020-07-19 20:24:03 +010017package androidx.compose.foundation.layout
Mihai Popa53db3272020-03-16 17:06:47 +000018
Louis Pullen-Freilich1f10a592020-07-24 16:35:14 +010019import androidx.compose.runtime.Composable
20import androidx.compose.runtime.Immutable
21import androidx.compose.runtime.Stable
22import androidx.compose.runtime.remember
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010023import androidx.compose.ui.Alignment
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010024import androidx.compose.ui.Modifier
Matvei Malkov103f6052020-11-04 18:36:00 +000025import androidx.compose.ui.layout.Layout
Louis Pullen-Freilicha03fd6c2020-07-24 23:26:29 +010026import androidx.compose.ui.layout.Measured
Matvei Malkov103f6052020-11-04 18:36:00 +000027import androidx.compose.ui.layout.VerticalAlignmentLine
Jens Ole Lauridsen9945da72020-10-23 15:20:06 -070028import androidx.compose.ui.platform.debugInspectorInfo
Louis Pullen-Freilich1f2bcd82020-07-22 18:30:31 +010029import androidx.compose.ui.util.annotation.FloatRange
Nikolay Igotti63c6e122020-07-06 20:21:59 +030030
Mihai Popa53db3272020-03-16 17:06:47 +000031/**
32 * A layout composable that places its children in a vertical sequence. For a layout composable
Mihai Popaa842ce22020-11-18 14:41:59 +000033 * that places its children in a horizontal sequence, see [Row]. For a layout that places children
34 * in a vertical sequence and is also scrollable, see `ScrollableColumn`. For a vertically
35 * scrollable list that only composes and lays out the currently visible items see `LazyColumn`.
Mihai Popa53db3272020-03-16 17:06:47 +000036 *
Mihai Popaa842ce22020-11-18 14:41:59 +000037 * The [Column] layout is able to assign children heights according to their weights provided
Mihai Popa5b7a6bb2020-04-08 20:11:03 +010038 * using the [ColumnScope.weight] modifier. If a child is not provided a weight, it will be
Mihai Popa53db3272020-03-16 17:06:47 +000039 * asked for its preferred height before the sizes of the children with weights are calculated
40 * proportionally to their weight based on the remaining available space.
41 *
42 * When none of its children have weights, a [Column] will be as small as possible to fit its
Mihai Popaae5237e2020-05-19 19:15:42 +010043 * children one on top of the other. In order to change the height of the [Column], use the
44 * [Modifier.height] modifiers; e.g. to make it fill the available height [Modifier.fillMaxHeight]
45 * can be used. If at least one child of a [Column] has a [weight][ColumnScope.weight],
46 * the [Column] will fill the available height, so there is no need for [Modifier.fillMaxHeight].
47 * However, if [Column]'s size should be limited, the [Modifier.height] or [Modifier.size] layout
Mihai Popa53db3272020-03-16 17:06:47 +000048 * modifiers should be applied.
49 *
Mihai Popa09a9eb82020-04-08 23:16:46 +010050 * When the size of the [Column] is larger than the sum of its children sizes, a
51 * [verticalArrangement] can be specified to define the positioning of the children inside the
52 * [Column]. See [Arrangement] for available positioning behaviors; a custom arrangement can also
53 * be defined using the constructor of [Arrangement].
Mihai Popa53db3272020-03-16 17:06:47 +000054 *
55 * Example usage:
56 *
Louis Pullen-Freilich623e4052020-07-19 20:24:03 +010057 * @sample androidx.compose.foundation.layout.samples.SimpleColumn
Mihai Popa53db3272020-03-16 17:06:47 +000058 *
59 * @param modifier The modifier to be applied to the Column.
Mihai Popa09a9eb82020-04-08 23:16:46 +010060 * @param verticalArrangement The vertical arrangement of the layout's children.
Mihai Popad0e8f052020-09-02 15:49:54 +010061 * @param horizontalAlignment The horizontal alignment of the layout's children.
Mihai Popa53db3272020-03-16 17:06:47 +000062 *
Mihai Popaa842ce22020-11-18 14:41:59 +000063 * @see Row
64 * @see [androidx.compose.foundation.ScrollableColumn]
65 * @see [androidx.compose.foundation.lazy.LazyColumn]
Mihai Popa53db3272020-03-16 17:06:47 +000066 */
67@Composable
Chuck Jazdzewskie9338022020-07-22 11:31:22 -070068inline fun Column(
Adam Powellb6d8db22020-04-02 12:40:03 -070069 modifier: Modifier = Modifier,
Mihai Popa09a9eb82020-04-08 23:16:46 +010070 verticalArrangement: Arrangement.Vertical = Arrangement.Top,
Mihai Popad0e8f052020-09-02 15:49:54 +010071 horizontalAlignment: Alignment.Horizontal = Alignment.Start,
Louis Pullen-Freilichdc68dd502020-11-13 02:10:48 +000072 content: @Composable ColumnScope.() -> Unit
Mihai Popa53db3272020-03-16 17:06:47 +000073) {
Mihai Popad0e8f052020-09-02 15:49:54 +010074 val measureBlocks = columnMeasureBlocks(verticalArrangement, horizontalAlignment)
Chuck Jazdzewskief18c632020-06-30 08:29:29 -070075 Layout(
Louis Pullen-Freilichdc68dd502020-11-13 02:10:48 +000076 content = { ColumnScope.content() },
Chuck Jazdzewskief18c632020-06-30 08:29:29 -070077 measureBlocks = measureBlocks,
78 modifier = modifier
Mihai Popa53db3272020-03-16 17:06:47 +000079 )
80}
Adam Powell999a89b2020-03-11 09:08:07 -070081
Chuck Jazdzewskief18c632020-06-30 08:29:29 -070082@PublishedApi
83internal val DefaultColumnMeasureBlocks = rowColumnMeasureBlocks(
84 orientation = LayoutOrientation.Vertical,
Mihai Popabc179852020-08-06 16:07:18 +010085 arrangement = { totalSize, size, _, density, outPosition ->
86 Arrangement.Top.arrange(totalSize, size, density, outPosition)
Chuck Jazdzewskief18c632020-06-30 08:29:29 -070087 },
Mihai Popabc179852020-08-06 16:07:18 +010088 arrangementSpacing = Arrangement.Top.spacing,
Chuck Jazdzewskief18c632020-06-30 08:29:29 -070089 crossAxisAlignment = CrossAxisAlignment.horizontal(Alignment.Start),
90 crossAxisSize = SizeMode.Wrap
91)
92
93@PublishedApi
94@Composable
95internal fun columnMeasureBlocks(
96 verticalArrangement: Arrangement.Vertical,
Mihai Popad0e8f052020-09-02 15:49:54 +010097 horizontalAlignment: Alignment.Horizontal
98) = remember(verticalArrangement, horizontalAlignment) {
99 if (verticalArrangement == Arrangement.Top && horizontalAlignment == Alignment.Start) {
Chuck Jazdzewskief18c632020-06-30 08:29:29 -0700100 DefaultColumnMeasureBlocks
101 } else {
102 rowColumnMeasureBlocks(
103 orientation = LayoutOrientation.Vertical,
Mihai Popabc179852020-08-06 16:07:18 +0100104 arrangement = { totalSize, size, _, density, outPosition ->
105 verticalArrangement.arrange(totalSize, size, density, outPosition)
Chuck Jazdzewskief18c632020-06-30 08:29:29 -0700106 },
Mihai Popabc179852020-08-06 16:07:18 +0100107 arrangementSpacing = verticalArrangement.spacing,
Mihai Popad0e8f052020-09-02 15:49:54 +0100108 crossAxisAlignment = CrossAxisAlignment.horizontal(horizontalAlignment),
Chuck Jazdzewskief18c632020-06-30 08:29:29 -0700109 crossAxisSize = SizeMode.Wrap
110 )
111 }
112}
113
Mihai Popa53db3272020-03-16 17:06:47 +0000114/**
115 * Scope for the children of [Column].
116 */
117@LayoutScopeMarker
Leland Richardson47df77f2020-05-21 09:15:40 -0700118@Immutable
Mihai Popac8deeac2020-09-07 12:16:36 +0100119interface ColumnScope {
Mihai Popa53db3272020-03-16 17:06:47 +0000120 /**
Mihai Popad0e8f052020-09-02 15:49:54 +0100121 * Align the element horizontally within the [Column]. This alignment will have priority over
122 * the [Column]'s `horizontalAlignment` parameter.
Mihai Popa294b06a2020-04-20 20:39:58 +0100123 *
124 * Example usage:
Mihai Popad0e8f052020-09-02 15:49:54 +0100125 * @sample androidx.compose.foundation.layout.samples.SimpleAlignInColumn
Adam Powell999a89b2020-03-11 09:08:07 -0700126 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700127 @Stable
Jens Ole Lauridsen9945da72020-10-23 15:20:06 -0700128 fun Modifier.align(alignment: Alignment.Horizontal) = this.then(
129 HorizontalAlignModifier(
130 horizontal = alignment,
131 inspectorInfo = debugInspectorInfo {
132 name = "align"
133 value = alignment
134 }
135 )
136 )
Mihai Popad0e8f052020-09-02 15:49:54 +0100137
Mihai Popa53db3272020-03-16 17:06:47 +0000138 /**
Adam Powell999a89b2020-03-11 09:08:07 -0700139 * Position the element horizontally such that its [alignmentLine] aligns with sibling elements
Mihai Popa0f1851a82020-10-12 16:01:55 +0100140 * also configured to [alignBy]. [alignBy] is a form of [align],
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100141 * so both modifiers will not work together if specified for the same layout.
Mihai Popa0f1851a82020-10-12 16:01:55 +0100142 * Within a [Column], all components with [alignBy] will align horizontally using
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100143 * the specified [VerticalAlignmentLine]s or values provided using the other
Mihai Popa0f1851a82020-10-12 16:01:55 +0100144 * [alignBy] overload, forming a sibling group.
Mihai Popad0e8f052020-09-02 15:49:54 +0100145 * At least one element of the sibling group will be placed as it had [Alignment.Start] align
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100146 * in [Column], and the alignment of the other siblings will be then determined such that
147 * the alignment lines coincide. Note that if only one element in a [Column] has the
Mihai Popa0f1851a82020-10-12 16:01:55 +0100148 * [alignBy] modifier specified the element will be positioned
Mihai Popad0e8f052020-09-02 15:49:54 +0100149 * as if it had [Alignment.Start] align.
Adam Powell999a89b2020-03-11 09:08:07 -0700150 *
151 * Example usage:
Louis Pullen-Freilich623e4052020-07-19 20:24:03 +0100152 * @sample androidx.compose.foundation.layout.samples.SimpleRelativeToSiblingsInColumn
Adam Powell999a89b2020-03-11 09:08:07 -0700153 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700154 @Stable
Jens Ole Lauridsen9945da72020-10-23 15:20:06 -0700155 fun Modifier.alignBy(alignmentLine: VerticalAlignmentLine) = this.then(
156 SiblingsAlignedModifier.WithAlignmentLine(
157 line = alignmentLine,
158 inspectorInfo = debugInspectorInfo {
159 name = "alignBy"
160 value = alignmentLine
161 }
162 )
163 )
Adam Powell999a89b2020-03-11 09:08:07 -0700164
Mihai Popa0f1851a82020-10-12 16:01:55 +0100165 @Deprecated(
166 "alignWithSiblings was renamed to alignBy.",
167 ReplaceWith("alignBy(alignmentLine)")
168 )
169 fun Modifier.alignWithSiblings(alignmentLine: VerticalAlignmentLine) = alignBy(alignmentLine)
170
Adam Powell999a89b2020-03-11 09:08:07 -0700171 /**
Adam Powell999a89b2020-03-11 09:08:07 -0700172 * Size the element's height proportional to its [weight] relative to other weighted sibling
173 * elements in the [Column]. The parent will divide the vertical space remaining after measuring
174 * unweighted child elements and distribute it according to this weight.
Mihai Popac4391e82020-05-15 13:54:37 +0000175 * When [fill] is true, the element will be forced to occupy the whole height allocated to it.
176 * Otherwise, the element is allowed to be smaller - this will result in [Column] being smaller,
177 * as the unused allocated height will not be redistributed to other siblings.
Adam Powell999a89b2020-03-11 09:08:07 -0700178 *
Louis Pullen-Freilich623e4052020-07-19 20:24:03 +0100179 * @sample androidx.compose.foundation.layout.samples.SimpleColumn
Adam Powell999a89b2020-03-11 09:08:07 -0700180 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700181 @Stable
Adam Powell999a89b2020-03-11 09:08:07 -0700182 fun Modifier.weight(
Nikolay Igotti63c6e122020-07-06 20:21:59 +0300183 @FloatRange(from = 0.0, to = 3.4e38 /* POSITIVE_INFINITY */, fromInclusive = false)
184 weight: Float,
Adam Powell999a89b2020-03-11 09:08:07 -0700185 fill: Boolean = true
186 ): Modifier {
187 require(weight > 0.0) { "invalid weight $weight; must be greater than zero" }
Jens Ole Lauridsenb4dadfe2020-10-27 13:51:34 -0700188 return this.then(
189 LayoutWeightImpl(
190 weight = weight,
191 fill = fill,
192 inspectorInfo = debugInspectorInfo {
193 name = "weight"
194 value = weight
195 properties["weight"] = weight
196 properties["fill"] = fill
197 }
198 )
199 )
Adam Powell999a89b2020-03-11 09:08:07 -0700200 }
201
202 /**
Adam Powell999a89b2020-03-11 09:08:07 -0700203 * Position the element horizontally such that the alignment line for the content as
204 * determined by [alignmentLineBlock] aligns with sibling elements also configured to
Mihai Popa0f1851a82020-10-12 16:01:55 +0100205 * [alignBy]. [alignBy] is a form of [align], so both modifiers
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100206 * will not work together if specified for the same layout.
Mihai Popa0f1851a82020-10-12 16:01:55 +0100207 * Within a [Column], all components with [alignBy] will align horizontally using
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100208 * the specified [VerticalAlignmentLine]s or values obtained from [alignmentLineBlock],
209 * forming a sibling group.
Mihai Popad0e8f052020-09-02 15:49:54 +0100210 * At least one element of the sibling group will be placed as it had [Alignment.Start] align
Mihai Popa3f98a4b2020-04-20 16:39:49 +0100211 * in [Column], and the alignment of the other siblings will be then determined such that
212 * the alignment lines coincide. Note that if only one element in a [Column] has the
Mihai Popa0f1851a82020-10-12 16:01:55 +0100213 * [alignBy] modifier specified the element will be positioned
Mihai Popad0e8f052020-09-02 15:49:54 +0100214 * as if it had [Alignment.Start] align.
Adam Powell999a89b2020-03-11 09:08:07 -0700215 *
216 * Example usage:
Louis Pullen-Freilich623e4052020-07-19 20:24:03 +0100217 * @sample androidx.compose.foundation.layout.samples.SimpleRelativeToSiblings
Adam Powell999a89b2020-03-11 09:08:07 -0700218 */
Leland Richardson47df77f2020-05-21 09:15:40 -0700219 @Stable
Jens Ole Lauridsen9945da72020-10-23 15:20:06 -0700220 fun Modifier.alignBy(alignmentLineBlock: (Measured) -> Int) = this.then(
221 SiblingsAlignedModifier.WithAlignmentLineBlock(
222 block = alignmentLineBlock,
223 inspectorInfo = debugInspectorInfo {
224 name = "alignBy"
225 value = alignmentLineBlock
226 }
227 )
228 )
Mihai Popac8deeac2020-09-07 12:16:36 +0100229
Mihai Popa0f1851a82020-10-12 16:01:55 +0100230 @Deprecated(
231 "alignWithSiblings was renamed to alignBy.",
232 ReplaceWith("alignBy(alignmentLineBlock)")
233 )
234 fun Modifier.alignWithSiblings(alignmentLineBlock: (Measured) -> Int) =
235 alignBy(alignmentLineBlock)
236
Mihai Popac8deeac2020-09-07 12:16:36 +0100237 companion object : ColumnScope
Mihai Popa53db3272020-03-16 17:06:47 +0000238}