| /* |
| * Copyright 2020 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.foundation |
| |
| import androidx.compose.Composable |
| import androidx.compose.emptyContent |
| import androidx.ui.core.Alignment |
| import androidx.compose.ui.unit.LayoutDirection |
| import androidx.ui.core.Modifier |
| import androidx.compose.ui.graphics.Color |
| import androidx.compose.ui.graphics.RectangleShape |
| import androidx.compose.ui.graphics.Shape |
| import androidx.compose.foundation.layout.Arrangement |
| import androidx.compose.foundation.layout.Column |
| import androidx.compose.foundation.layout.padding |
| import androidx.compose.ui.unit.Dp |
| import androidx.compose.ui.unit.IntSize |
| import androidx.compose.ui.unit.dp |
| import androidx.compose.ui.util.fastForEach |
| |
| /** |
| * A convenience composable that combines common layout and draw logic. |
| * |
| * In order to define the size of the [Box], the [androidx.compose.foundation.layout.LayoutWidth], |
| * [androidx.compose.foundation.layout.LayoutHeight] and [androidx.compose.foundation.layout.LayoutSize] modifiers can be used. |
| * The [Box] will try to be only as small as its content. However, if it is constrained |
| * otherwise, [Box] will allow its content to be smaller and will position the content inside, |
| * according to [gravity]. |
| * |
| * The specified [padding] will be applied inside the [Box]. In order to apply padding outside |
| * the [Box], the [androidx.compose.foundation.layout.LayoutPadding] modifier should be used. |
| * |
| * @sample androidx.compose.foundation.samples.SimpleCircleBox |
| * |
| * @param modifier The modifier to be applied to the Box |
| * @param shape The shape of the box |
| * @param backgroundColor The [Color] for background with. If [Color.Transparent], there will be no |
| * background |
| * @param border [Border] object that specifies border appearance, such as size and color. If |
| * `null`, there will be no border |
| * @param padding The padding to be applied inside Box, along its edges. Unless otherwise |
| * specified, content will be padded by the [Border.size], if [border] is provided |
| * @param paddingStart sets the padding of the start edge. Setting this will override [padding] |
| * for the start edge |
| * @param paddingTop sets the padding of the top edge. Setting this will override [padding] for |
| * the top edge |
| * @param paddingEnd sets the padding of the end edge. Setting this will override [padding] for |
| * the end edge |
| * @param paddingBottom sets the padding of the bottom edge. Setting this will override [padding] |
| * for the bottom edge |
| * @param gravity The gravity of the content inside Box |
| */ |
| @Composable |
| fun Box( |
| modifier: Modifier = Modifier, |
| shape: Shape = RectangleShape, |
| backgroundColor: Color = Color.Transparent, |
| border: Border? = null, |
| padding: Dp = border?.size ?: 0.dp, |
| paddingStart: Dp = Dp.Unspecified, |
| paddingTop: Dp = Dp.Unspecified, |
| paddingEnd: Dp = Dp.Unspecified, |
| paddingBottom: Dp = Dp.Unspecified, |
| gravity: ContentGravity = ContentGravity.TopStart, |
| children: @Composable () -> Unit = emptyContent() |
| ) { |
| val borderModifier = |
| if (border != null) Modifier.drawBorder(border, shape) else Modifier |
| val backgroundModifier = |
| if (backgroundColor != Color.Transparent) { |
| Modifier.background(color = backgroundColor, shape = shape) |
| } else { |
| Modifier |
| } |
| val paddingModifier = |
| if (needsPadding(padding, paddingStart, paddingTop, paddingEnd, paddingBottom)) { |
| Modifier.padding( |
| if (paddingStart != Dp.Unspecified) paddingStart else padding, |
| if (paddingTop != Dp.Unspecified) paddingTop else padding, |
| if (paddingEnd != Dp.Unspecified) paddingEnd else padding, |
| if (paddingBottom != Dp.Unspecified) paddingBottom else padding |
| ) |
| } else { |
| Modifier |
| } |
| // TODO(malkov): support ContentColor prorogation (b/148129218) |
| |
| val columnArrangement = gravity.toColumnArrangement() |
| val columnGravity = gravity.toColumnGravity() |
| Column( |
| modifier = modifier.then(backgroundModifier).then(borderModifier).then(paddingModifier), |
| verticalArrangement = columnArrangement, |
| horizontalGravity = columnGravity |
| ) { |
| children() |
| } |
| } |
| |
| // TODO(popam/148014745): add a Gravity class consistent with cross axis alignment for Row/Column |
| typealias ContentGravity = Alignment |
| |
| private fun needsPadding( |
| padding: Dp, |
| paddingStart: Dp, |
| paddingTop: Dp, |
| paddingEnd: Dp, |
| paddingBottom: Dp |
| ) = (padding != Dp.Unspecified && padding != 0.dp) || |
| (paddingStart != Dp.Unspecified && paddingStart != 0.dp) || |
| (paddingTop != Dp.Unspecified && paddingTop != 0.dp) || |
| (paddingEnd != Dp.Unspecified && paddingEnd != 0.dp) || |
| (paddingBottom != Dp.Unspecified && paddingBottom != 0.dp) |
| |
| private fun Alignment.toColumnArrangement(): Arrangement.Vertical = object : Arrangement.Vertical { |
| override fun arrange( |
| totalSize: Int, |
| size: List<Int> |
| ): List<Int> { |
| val usedSize = size.fold(0) { sum, current -> sum + current } |
| var y = align(IntSize(0, totalSize - usedSize)).y |
| |
| val positions = mutableListOf<Int>() |
| size.fastForEach { childSize -> |
| positions += y |
| y += childSize |
| } |
| return positions |
| } |
| } |
| |
| private fun Alignment.toColumnGravity(): Alignment.Horizontal = object : Alignment.Horizontal { |
| override fun align(size: Int, layoutDirection: LayoutDirection): Int { |
| return align(IntSize(size, 0), layoutDirection).x |
| } |
| } |