| /* |
| * 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.compose.foundation.layout |
| |
| import androidx.compose.runtime.Composable |
| import androidx.compose.runtime.Stable |
| import androidx.compose.ui.unit.Constraints |
| import androidx.ui.core.IntrinsicMeasurable |
| import androidx.ui.core.IntrinsicMeasureScope |
| import androidx.ui.core.Layout |
| import androidx.compose.ui.unit.LayoutDirection |
| import androidx.ui.core.LayoutModifier |
| import androidx.ui.core.Measurable |
| import androidx.ui.core.MeasureScope |
| import androidx.ui.core.Modifier |
| import androidx.compose.ui.unit.enforce |
| import androidx.compose.ui.unit.IntOffset |
| /** |
| * Declare the preferred width of the content to be the same as the min or max intrinsic width of |
| * the content. The incoming measurement [Constraints] may override this value, forcing the content |
| * to be either smaller or larger. |
| * |
| * See [preferredHeight] for options of sizing to intrinsic height. |
| * Also see [preferredWidth] and [preferredWidthIn] for other options to set the preferred width. |
| * |
| * Example usage for min intrinsic: |
| * @sample androidx.compose.foundation.layout.samples.SameWidthBoxes |
| * |
| * Example usage for max intrinsic: |
| * @sample androidx.compose.foundation.layout.samples.SameWidthTextBoxes |
| */ |
| @ExperimentalLayout |
| @Stable |
| fun Modifier.preferredWidth(intrinsicSize: IntrinsicSize) = when (intrinsicSize) { |
| IntrinsicSize.Min -> this.then(PreferredMinIntrinsicWidthModifier) |
| IntrinsicSize.Max -> this.then(PreferredMaxIntrinsicWidthModifier) |
| } |
| |
| /** |
| * Declare the preferred height of the content to be the same as the min or max intrinsic height of |
| * the content. The incoming measurement [Constraints] may override this value, forcing the content |
| * to be either smaller or larger. |
| * |
| * See [preferredWidth] for other options of sizing to intrinsic width. |
| * Also see [preferredHeight] and [preferredHeightIn] for other options to set the preferred height. |
| * |
| * Example usage for min intrinsic: |
| * @sample androidx.compose.foundation.layout.samples.MatchParentDividerForText |
| * |
| * Example usage for max intrinsic: |
| * @sample androidx.compose.foundation.layout.samples.MatchParentDividerForAspectRatio |
| */ |
| @ExperimentalLayout |
| @Stable |
| fun Modifier.preferredHeight(intrinsicSize: IntrinsicSize) = when (intrinsicSize) { |
| IntrinsicSize.Min -> this.then(PreferredMinIntrinsicHeightModifier) |
| IntrinsicSize.Max -> this.then(PreferredMaxIntrinsicHeightModifier) |
| } |
| |
| /** |
| * Intrinsic size used in [preferredWidth] or [preferredHeight] which can refer to width or height. |
| */ |
| enum class IntrinsicSize { Min, Max } |
| |
| private object PreferredMinIntrinsicWidthModifier : PreferredIntrinsicSizeModifier { |
| override fun MeasureScope.calculateContentConstraints( |
| measurable: Measurable, |
| constraints: Constraints |
| ): Constraints { |
| val width = measurable.minIntrinsicWidth(constraints.maxHeight) |
| return Constraints.fixedWidth(width) |
| } |
| |
| override fun IntrinsicMeasureScope.maxIntrinsicWidth( |
| measurable: IntrinsicMeasurable, |
| height: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.minIntrinsicWidth(height) |
| } |
| |
| private object PreferredMinIntrinsicHeightModifier : PreferredIntrinsicSizeModifier { |
| override fun MeasureScope.calculateContentConstraints( |
| measurable: Measurable, |
| constraints: Constraints |
| ): Constraints { |
| val height = measurable.minIntrinsicHeight(constraints.maxWidth) |
| return Constraints.fixedHeight(height) |
| } |
| |
| override fun IntrinsicMeasureScope.maxIntrinsicHeight( |
| measurable: IntrinsicMeasurable, |
| width: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.minIntrinsicHeight(width) |
| } |
| |
| private object PreferredMaxIntrinsicWidthModifier : PreferredIntrinsicSizeModifier { |
| override fun MeasureScope.calculateContentConstraints( |
| measurable: Measurable, |
| constraints: Constraints |
| ): Constraints { |
| val width = measurable.maxIntrinsicWidth(constraints.maxHeight) |
| return Constraints.fixedWidth(width) |
| } |
| |
| override fun IntrinsicMeasureScope.minIntrinsicWidth( |
| measurable: IntrinsicMeasurable, |
| height: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.maxIntrinsicWidth(height, layoutDirection) |
| } |
| |
| private object PreferredMaxIntrinsicHeightModifier : PreferredIntrinsicSizeModifier { |
| override fun MeasureScope.calculateContentConstraints( |
| measurable: Measurable, |
| constraints: Constraints |
| ): Constraints { |
| val height = measurable.maxIntrinsicHeight(constraints.maxWidth) |
| return Constraints.fixedHeight(height) |
| } |
| |
| override fun IntrinsicMeasureScope.minIntrinsicHeight( |
| measurable: IntrinsicMeasurable, |
| width: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.maxIntrinsicHeight(width) |
| } |
| |
| private interface PreferredIntrinsicSizeModifier : LayoutModifier { |
| fun MeasureScope.calculateContentConstraints( |
| measurable: Measurable, |
| constraints: Constraints |
| ): Constraints |
| |
| override fun MeasureScope.measure( |
| measurable: Measurable, |
| constraints: Constraints, |
| layoutDirection: LayoutDirection |
| ): MeasureScope.MeasureResult { |
| val placeable = measurable.measure( |
| calculateContentConstraints( |
| measurable, |
| constraints |
| ).enforce(constraints) |
| ) |
| return layout(placeable.width, placeable.height) { |
| placeable.place(IntOffset.Origin) |
| } |
| } |
| |
| override fun IntrinsicMeasureScope.minIntrinsicWidth( |
| measurable: IntrinsicMeasurable, |
| height: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.minIntrinsicWidth(height, layoutDirection) |
| |
| override fun IntrinsicMeasureScope.minIntrinsicHeight( |
| measurable: IntrinsicMeasurable, |
| width: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.minIntrinsicHeight(width, layoutDirection) |
| |
| override fun IntrinsicMeasureScope.maxIntrinsicWidth( |
| measurable: IntrinsicMeasurable, |
| height: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.maxIntrinsicWidth(height, layoutDirection) |
| |
| override fun IntrinsicMeasureScope.maxIntrinsicHeight( |
| measurable: IntrinsicMeasurable, |
| width: Int, |
| layoutDirection: LayoutDirection |
| ) = measurable.maxIntrinsicHeight(width, layoutDirection) |
| } |
| |
| /** |
| * Layout composable that forces its child to be as wide as its min intrinsic width. |
| * If incoming constraints do not allow this, the closest possible width will be used. |
| */ |
| @Deprecated("This component is deprecated. " + |
| "Please use the preferredWidth(IntrinsicSize.Min) modifier instead.") |
| @Composable |
| fun MinIntrinsicWidth(children: @Composable () -> Unit) { |
| Layout( |
| children, |
| minIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0 |
| }, |
| minIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0 |
| }, |
| maxIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0 |
| }, |
| maxIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0 |
| } |
| ) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| val width = measurable?.minIntrinsicWidth(constraints.maxHeight, layoutDirection) ?: 0 |
| val placeable = measurable?.measure( |
| Constraints.fixedWidth(width).enforce(constraints) |
| ) |
| layout(placeable?.width ?: 0, placeable?.height ?: 0) { |
| placeable?.placeAbsolute(0, 0) |
| } |
| } |
| } |
| |
| /** |
| * Layout composable that forces its child to be as tall as its min intrinsic height. |
| * If incoming constraints do not allow this, the closest possible height will be used. |
| */ |
| @Deprecated("This component is deprecated. " + |
| "Please use the preferredHeight(IntrinsicSize.Min) modifier instead.") |
| @Composable |
| fun MinIntrinsicHeight(children: @Composable () -> Unit) { |
| Layout( |
| children, |
| minIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0 |
| }, |
| minIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0 |
| }, |
| maxIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0 |
| }, |
| maxIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0 |
| } |
| ) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| val height = measurable?.minIntrinsicHeight(constraints.maxWidth) ?: 0 |
| val placeable = measurable?.measure( |
| Constraints.fixedHeight(height).enforce(constraints) |
| ) |
| layout(placeable?.width ?: 0, placeable?.height ?: 0) { |
| placeable?.placeAbsolute(0, 0) |
| } |
| } |
| } |
| |
| /** |
| * Layout composable that forces its child to be as wide as its max intrinsic width. |
| * If incoming constraints do not allow this, the closest possible width will be used. |
| */ |
| @Deprecated("This component is deprecated. " + |
| "Please use the preferredWidth(IntrinsicSize.Max) modifier instead.") |
| @Composable |
| fun MaxIntrinsicWidth(children: @Composable () -> Unit) { |
| Layout( |
| children, |
| minIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0 |
| }, |
| minIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.minIntrinsicHeight(w) ?: 0 |
| }, |
| maxIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0 |
| }, |
| maxIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0 |
| } |
| ) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| val width = measurable?.maxIntrinsicWidth(constraints.maxHeight) ?: 0 |
| val placeable = measurable?.measure( |
| Constraints.fixedWidth(width).enforce(constraints) |
| ) |
| layout(placeable?.width ?: 0, placeable?.height ?: 0) { |
| placeable?.placeAbsolute(0, 0) |
| } |
| } |
| } |
| |
| /** |
| * Layout composable that forces its child to be as tall as its max intrinsic height. |
| * If incoming constraints do not allow this, the closest possible height will be used. |
| */ |
| @Deprecated("This component is deprecated. " + |
| "Please use the preferredHeight(IntrinsicSize.Max) modifier instead.") |
| @Composable |
| fun MaxIntrinsicHeight(children: @Composable () -> Unit) { |
| Layout( |
| children, |
| minIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.minIntrinsicWidth(h) ?: 0 |
| }, |
| minIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0 |
| }, |
| maxIntrinsicWidthMeasureBlock = { measurables, h -> |
| measurables.firstOrNull()?.maxIntrinsicWidth(h) ?: 0 |
| }, |
| maxIntrinsicHeightMeasureBlock = { measurables, w -> |
| measurables.firstOrNull()?.maxIntrinsicHeight(w) ?: 0 |
| } |
| ) { measurables, constraints -> |
| val measurable = measurables.firstOrNull() |
| val height = measurable?.maxIntrinsicHeight(constraints.maxWidth) ?: 0 |
| val placeable = measurable?.measure( |
| Constraints.fixedHeight(height).enforce(constraints) |
| ) |
| layout(placeable?.width ?: 0, placeable?.height ?: 0) { |
| placeable?.placeAbsolute(0, 0) |
| } |
| } |
| } |