| /* |
| * Copyright 2019 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.ui.layout |
| |
| import androidx.ui.core.Dp |
| import androidx.ui.core.dp |
| import androidx.ui.core.min |
| import androidx.ui.core.offset |
| import androidx.compose.Composable |
| import androidx.ui.core.AlignmentLine |
| import androidx.ui.core.Constraints |
| import androidx.ui.core.DensityScope |
| import androidx.ui.core.IntPx |
| import androidx.ui.core.IntPxPosition |
| import androidx.ui.core.IntPxSize |
| import androidx.ui.core.Layout |
| import androidx.ui.core.LayoutModifier |
| import androidx.ui.core.Measurable |
| import androidx.ui.core.coerceAtLeast |
| import androidx.ui.core.coerceIn |
| import androidx.ui.core.ipx |
| |
| /** |
| * Padding on all sides. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.AbsolutePaddingModifier |
| */ |
| fun absolutePadding( |
| left: Dp = 0.dp, |
| top: Dp = 0.dp, |
| right: Dp = 0.dp, |
| bottom: Dp = 0.dp |
| ) = AbsolutePadding(left = left, top = top, right = right, bottom = bottom) |
| |
| /** |
| * Padding with [all] dp on each side. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.PaddingModifier |
| */ |
| fun padding(all: Dp = 0.dp) = AbsolutePadding(left = all, top = all, right = all, bottom = all) |
| |
| /** |
| * Padding with [horizontal] dp of padding on the left and right and [vertical] dp of padding |
| * on the top and bottom. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.SymmetricalPaddingModifier |
| */ |
| fun padding( |
| horizontal: Dp = 0.dp, |
| vertical: Dp = 0.dp |
| ) = AbsolutePadding(left = horizontal, top = vertical, right = horizontal, bottom = vertical) |
| |
| /** |
| * A [LayoutModifier] that adds [left], [top], [right] and [bottom] padding |
| * to the wrapped UI element. |
| */ |
| data class AbsolutePadding( |
| val left: Dp = 0.dp, |
| val top: Dp = 0.dp, |
| val right: Dp = 0.dp, |
| val bottom: Dp = 0.dp |
| ) : LayoutModifier { |
| override fun DensityScope.minIntrinsicWidthOf(measurable: Measurable, height: IntPx): IntPx = |
| measurable.minIntrinsicWidth((height - (top + bottom).toIntPx()).coerceAtLeast(0.ipx)) + |
| (left + right).toIntPx() |
| |
| override fun DensityScope.maxIntrinsicWidthOf(measurable: Measurable, height: IntPx): IntPx = |
| measurable.maxIntrinsicWidth((height - (top + bottom).toIntPx()).coerceAtLeast(0.ipx)) + |
| (left + right).toIntPx() |
| |
| override fun DensityScope.minIntrinsicHeightOf(measurable: Measurable, width: IntPx): IntPx = |
| measurable.minIntrinsicHeight((width - (left + right).toIntPx()).coerceAtLeast(0.ipx)) + |
| (top + bottom).toIntPx() |
| |
| override fun DensityScope.maxIntrinsicHeightOf(measurable: Measurable, width: IntPx): IntPx = |
| measurable.maxIntrinsicHeight((width - (left + right).toIntPx()).coerceAtLeast(0.ipx)) + |
| (top + bottom).toIntPx() |
| |
| override fun DensityScope.modifyConstraints( |
| constraints: Constraints |
| ) = constraints.offset( |
| horizontal = -left.toIntPx() - right.toIntPx(), |
| vertical = -top.toIntPx() - bottom.toIntPx() |
| ) |
| |
| override fun DensityScope.modifySize( |
| constraints: Constraints, |
| childSize: IntPxSize |
| ) = IntPxSize( |
| (left.toIntPx() + childSize.width + right.toIntPx()) |
| .coerceIn(constraints.minWidth, constraints.maxWidth), |
| (top.toIntPx() + childSize.height + bottom.toIntPx()) |
| .coerceIn(constraints.minHeight, constraints.maxHeight) |
| ) |
| |
| override fun DensityScope.modifyPosition( |
| childPosition: IntPxPosition, |
| childSize: IntPxSize, |
| containerSize: IntPxSize |
| ) = IntPxPosition(left.toIntPx() + childPosition.x, top.toIntPx() + childPosition.y) |
| |
| override fun DensityScope.modifyAlignmentLine(line: AlignmentLine, value: IntPx?): IntPx? { |
| if (value == null) return null |
| |
| return if (line.horizontal) value + left.toIntPx() else value + top.toIntPx() |
| } |
| } |
| |
| /** |
| * Describes a set of offsets from each of the four sides of a box. For example, |
| * it is used to describe padding for the [Padding] widget. |
| */ |
| data class EdgeInsets( |
| val left: Dp = 0.dp, |
| val top: Dp = 0.dp, |
| val right: Dp = 0.dp, |
| val bottom: Dp = 0.dp |
| ) { |
| constructor(all: Dp) : this(all, all, all, all) |
| } |
| |
| /** |
| * Layout widget that takes a child composable and applies whitespace padding around it. |
| * When passing layout constraints to its child, [Padding] shrinks the constraints by the |
| * requested padding, causing the child to layout at a smaller size. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.PaddingWidgetEdgeInsets |
| */ |
| @Composable |
| fun Padding( |
| padding: EdgeInsets, |
| children: @Composable() () -> Unit |
| ) { |
| Layout(children) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| if (measurable == null) { |
| layout(constraints.minWidth, constraints.minHeight) { } |
| } else { |
| val paddingLeft = padding.left.toIntPx() |
| val paddingTop = padding.top.toIntPx() |
| val paddingRight = padding.right.toIntPx() |
| val paddingBottom = padding.bottom.toIntPx() |
| val horizontalPadding = (paddingLeft + paddingRight) |
| val verticalPadding = (paddingTop + paddingBottom) |
| |
| val newConstraints = constraints.offset(-horizontalPadding, -verticalPadding) |
| val placeable = measurable.measure(newConstraints) |
| val width = |
| min(placeable.width + horizontalPadding, constraints.maxWidth) |
| val height = |
| min(placeable.height + verticalPadding, constraints.maxHeight) |
| |
| layout(width, height) { |
| placeable.place(paddingLeft, paddingTop) |
| } |
| } |
| } |
| } |
| |
| /** |
| * Layout widget that takes a child composable and applies whitespace padding around it. |
| * |
| * When passing layout constraints to its child, [Padding] shrinks the constraints by the |
| * requested padding, causing the child to layout at a smaller size. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.PaddingWidget |
| */ |
| @Composable |
| fun Padding( |
| left: Dp = 0.dp, |
| top: Dp = 0.dp, |
| right: Dp = 0.dp, |
| bottom: Dp = 0.dp, |
| children: @Composable() () -> Unit |
| ) { |
| Padding( |
| padding = EdgeInsets(left = left, top = top, right = right, bottom = bottom), |
| children = children |
| ) |
| } |
| |
| /** |
| * Layout widget that takes a child composable and applies |
| * the same amount of whitespace padding around it. |
| * |
| * When passing layout constraints to its child, [Padding] shrinks the constraints by the |
| * requested padding, causing the child to layout at a smaller size. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.PaddingWidgetSameInset |
| */ |
| @Composable |
| fun Padding( |
| padding: Dp, |
| children: @Composable() () -> Unit |
| ) { |
| Padding(padding = EdgeInsets(padding), children = children) |
| } |