| /* |
| * 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.compose.Composable |
| import androidx.ui.core.Alignment |
| import androidx.ui.core.Constraints |
| import androidx.ui.core.Layout |
| import androidx.ui.core.LayoutModifier |
| import androidx.ui.unit.DensityScope |
| import androidx.ui.unit.IntPxPosition |
| import androidx.ui.unit.IntPxSize |
| import androidx.ui.unit.ipx |
| import androidx.ui.unit.isFinite |
| |
| /** |
| * A layout that takes a child and aligns it within itself, according to the alignment parameter. |
| * The layout will be as large as possible for finite incoming constraints, |
| * or wrap content otherwise. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.SimpleAlign |
| * |
| * For a composable that just does center alignment, see [Center]. |
| * For a composable that does alignment and tries to be the same size as its child, see [Wrap]. |
| * @see Center |
| * @see Wrap |
| */ |
| @Composable |
| fun Align(alignment: Alignment, children: @Composable() () -> Unit) { |
| Layout(children) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| // The child cannot be larger than our max constraints, but we ignore min constraints. |
| val placeable = measurable?.measure(constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx)) |
| |
| // The layout is as large as possible for bounded constraints, |
| // or wrap content otherwise. |
| val layoutWidth = if (constraints.maxWidth.isFinite()) { |
| constraints.maxWidth |
| } else { |
| placeable?.width ?: constraints.minWidth |
| } |
| val layoutHeight = if (constraints.maxHeight.isFinite()) { |
| constraints.maxHeight |
| } else { |
| placeable?.height ?: constraints.minHeight |
| } |
| |
| layout(layoutWidth, layoutHeight) { |
| if (placeable != null) { |
| val position = alignment.align( |
| IntPxSize(layoutWidth - placeable.width, layoutHeight - placeable.height) |
| ) |
| placeable.place(position.x, position.y) |
| } |
| } |
| } |
| } |
| |
| /** |
| * A layout that takes a child and centers it within itself. |
| * The layout will be as large as possible for finite incoming |
| * constraints, or wrap content otherwise. |
| * |
| * Example usage: |
| * @sample androidx.ui.layout.samples.SimpleCenter |
| * |
| * For a composable that supports other alignments than just center, see [Align]. |
| * For a composable that does alignment and tries to be the same size as its child, see [Wrap]. |
| * @see Align |
| * @see Wrap |
| */ |
| @Composable |
| fun Center(children: @Composable() () -> Unit) { |
| Align(alignment = Alignment.Center, children = children) |
| } |
| |
| /** |
| * Provides scope-dependent alignment options for children layouts where the alignment is handled |
| * by the parent layout rather than the child itself. Different layout models allow different |
| * [LayoutGravity] options. For example, [Row] provides Top and Bottom, while [Column] provides |
| * Start and End. |
| * Unlike [LayoutAlign], layout children with [LayoutGravity] are aligned only after the size |
| * of the parent is known, therefore not affecting the size of the parent in order to achieve |
| * their own alignment. |
| * |
| * Example usage: |
| * |
| * @sample androidx.ui.layout.samples.SimpleGravityInRow |
| * |
| * @sample androidx.ui.layout.samples.SimpleGravityInColumn |
| */ |
| object LayoutGravity |
| |
| /** |
| * Provides alignment options for a target layout where the alignment is handled by the modifier |
| * itself (rather than by the layout's parent). To achieve this, the modifier tries to fill the |
| * available space and align the target layout within itself. If the incoming constraints are |
| * infinite, the modifier will wrap the child instead and the alignment will not be achieved. |
| * |
| * Example usage: |
| * |
| * @sample androidx.ui.layout.samples.SimpleAlignedModifier |
| * @sample androidx.ui.layout.samples.SimpleVerticallyAlignedModifier |
| */ |
| object LayoutAlign { |
| /** |
| * A layout modifier that positions the target component inside its parent to the top in |
| * vertical direction and wraps the component in horizontal direction. |
| */ |
| val Top: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopLeft, direction = Direction.Vertical) |
| |
| /** |
| * A layout modifier that positions the target component in the center of the parent in |
| * vertical direction and wraps the component in horizontal direction. |
| */ |
| val CenterVertically: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.CenterLeft, direction = Direction.Vertical) |
| |
| /** |
| * A layout modifier that positions the target component inside its parent to the bottom in |
| * vertical direction and wraps the component in horizontal direction. |
| */ |
| val Bottom: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.BottomLeft, direction = Direction.Vertical) |
| |
| /** |
| * A layout modifier that positions the target component inside its parent to the start edge |
| * in horizontal direction and wraps the component in vertical direction. |
| */ |
| val Start: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopLeft, direction = Direction.Horizontal) |
| |
| /** |
| * A layout modifier that positions the target component in the center of the parent in |
| * horizontal direction and wraps the component in vertical direction. |
| */ |
| val CenterHorizontally: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopCenter, direction = Direction.Horizontal) |
| |
| /** |
| * A layout modifier that positions the target component inside its parent to the end edge |
| * in horizontal direction and wraps the component in vertical direction. |
| */ |
| val End: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopRight, direction = Direction.Horizontal) |
| |
| /** |
| * A layout modifier that positions the target component top-left inside its parent. |
| */ |
| val TopLeft: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopLeft, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component top-center inside its parent. |
| */ |
| val TopCenter: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopCenter, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component top-right inside its parent. |
| */ |
| val TopRight: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.TopRight, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component center-left inside its parent. |
| */ |
| val CenterLeft: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.CenterLeft, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component in the center of its parent. |
| */ |
| val Center: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.Center, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component center-right inside its parent. |
| */ |
| val CenterRight: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.CenterRight, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component bottom-left inside its parent. |
| */ |
| val BottomLeft: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.BottomLeft, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component bottom-center inside its parent. |
| */ |
| val BottomCenter: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.BottomCenter, direction = Direction.Both) |
| |
| /** |
| * A layout modifier that positions the target component bottom-right inside its parent. |
| */ |
| val BottomRight: LayoutModifier = |
| AlignmentModifier(alignment = Alignment.BottomRight, direction = Direction.Both) |
| } |
| |
| private enum class Direction { |
| Vertical, Horizontal, Both |
| } |
| |
| private data class AlignmentModifier( |
| private val alignment: Alignment, |
| private val direction: Direction |
| ) : LayoutModifier { |
| override fun DensityScope.modifyConstraints(constraints: Constraints) = when (direction) { |
| Direction.Both -> constraints.copy(minWidth = 0.ipx, minHeight = 0.ipx) |
| Direction.Horizontal -> constraints.copy(minWidth = 0.ipx) |
| Direction.Vertical -> constraints.copy(minHeight = 0.ipx) |
| } |
| |
| override fun DensityScope.modifySize( |
| constraints: Constraints, |
| childSize: IntPxSize |
| ): IntPxSize { |
| val width = if ( |
| direction != Direction.Vertical && constraints.maxWidth.isFinite() |
| ) { |
| constraints.maxWidth |
| } else { |
| childSize.width |
| } |
| val height = if ( |
| direction != Direction.Horizontal && constraints.maxHeight.isFinite() |
| ) { |
| constraints.maxHeight |
| } else { |
| childSize.height |
| } |
| return IntPxSize(width, height) |
| } |
| |
| override fun DensityScope.modifyPosition( |
| childSize: IntPxSize, |
| containerSize: IntPxSize |
| ): IntPxPosition { |
| return alignment.align( |
| IntPxSize( |
| containerSize.width - childSize.width, |
| containerSize.height - childSize.height |
| ) |
| ) |
| } |
| } |