| /* |
| * 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.layout |
| |
| import androidx.compose.foundation.layout.internal.JvmDefaultWithCompatibility |
| import androidx.compose.runtime.Immutable |
| import androidx.compose.runtime.Stable |
| import androidx.compose.ui.Alignment |
| import androidx.compose.ui.unit.Density |
| import androidx.compose.ui.unit.Dp |
| import androidx.compose.ui.unit.LayoutDirection |
| import androidx.compose.ui.unit.dp |
| import androidx.compose.ui.util.fastRoundToInt |
| import kotlin.math.min |
| |
| /** |
| * Used to specify the arrangement of the layout's children in layouts like [Row] or [Column] in the |
| * main axis direction (horizontal and vertical, respectively). |
| * |
| * Below is an illustration of different horizontal arrangements in [Row]s:  |
| * |
| * Different vertical arrangements in [Column]s:  |
| */ |
| @Immutable |
| object Arrangement { |
| /** |
| * Used to specify the horizontal arrangement of the layout's children in layouts like [Row]. |
| */ |
| @Stable |
| @JvmDefaultWithCompatibility |
| interface Horizontal { |
| /** Spacing that should be added between any two adjacent layout children. */ |
| val spacing |
| get() = 0.dp |
| |
| /** |
| * Horizontally places the layout children. |
| * |
| * @param totalSize Available space that can be occupied by the children, in pixels. |
| * @param sizes An array of sizes of all children, in pixels. |
| * @param layoutDirection A layout direction, left-to-right or right-to-left, of the parent |
| * layout that should be taken into account when determining positions of the children. |
| * @param outPositions An array of the size of [sizes] that returns the calculated positions |
| * relative to the left, in pixels. |
| */ |
| fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) |
| } |
| |
| /** |
| * Used to specify the vertical arrangement of the layout's children in layouts like [Column]. |
| */ |
| @Stable |
| @JvmDefaultWithCompatibility |
| interface Vertical { |
| /** Spacing that should be added between any two adjacent layout children. */ |
| val spacing |
| get() = 0.dp |
| |
| /** |
| * Vertically places the layout children. |
| * |
| * @param totalSize Available space that can be occupied by the children, in pixels. |
| * @param sizes An array of sizes of all children, in pixels. |
| * @param outPositions An array of the size of [sizes] that returns the calculated positions |
| * relative to the top, in pixels. |
| */ |
| fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) |
| } |
| |
| /** |
| * Used to specify the horizontal arrangement of the layout's children in horizontal layouts |
| * like [Row], or the vertical arrangement of the layout's children in vertical layouts like |
| * [Column]. |
| */ |
| @Stable |
| @JvmDefaultWithCompatibility |
| interface HorizontalOrVertical : Horizontal, Vertical { |
| /** Spacing that should be added between any two adjacent layout children. */ |
| override val spacing: Dp |
| get() = 0.dp |
| } |
| |
| /** |
| * Place children horizontally such that they are as close as possible to the beginning of the |
| * horizontal axis (left if the layout direction is LTR, right otherwise). Visually: 123#### for |
| * LTR and ####321. |
| */ |
| @Stable |
| val Start = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeLeftOrTop(sizes, outPositions, reverseInput = false) |
| } else { |
| placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun toString() = "Arrangement#Start" |
| } |
| |
| /** |
| * Place children horizontally such that they are as close as possible to the end of the main |
| * axis. Visually: ####123 for LTR and 321#### for RTL. |
| */ |
| @Stable |
| val End = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false) |
| } else { |
| placeLeftOrTop(sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun toString() = "Arrangement#End" |
| } |
| |
| /** |
| * Place children vertically such that they are as close as possible to the top of the main |
| * axis. Visually: (top) 123#### (bottom) |
| */ |
| @Stable |
| val Top = |
| object : Vertical { |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeLeftOrTop(sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#Top" |
| } |
| |
| /** |
| * Place children vertically such that they are as close as possible to the bottom of the main |
| * axis. Visually: (top) ####123 (bottom) |
| */ |
| @Stable |
| val Bottom = |
| object : Vertical { |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#Bottom" |
| } |
| |
| /** |
| * Place children such that they are as close as possible to the middle of the main axis. |
| * Visually: ##123## for LTR and ##321## for RTL. |
| */ |
| @Stable |
| val Center = |
| object : HorizontalOrVertical { |
| override val spacing = 0.dp |
| |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeCenter(totalSize, sizes, outPositions, reverseInput = false) |
| } else { |
| placeCenter(totalSize, sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeCenter(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#Center" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly across the main axis, including free space |
| * before the first child and after the last child. Visually: #1#2#3# for LTR and #3#2#1# for |
| * RTL. |
| */ |
| @Stable |
| val SpaceEvenly = |
| object : HorizontalOrVertical { |
| override val spacing = 0.dp |
| |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false) |
| } else { |
| placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#SpaceEvenly" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly across the main axis, without free space |
| * before the first child or after the last child. Visually: 1##2##3 for LTR or 3##2##1 for RTL. |
| */ |
| @Stable |
| val SpaceBetween = |
| object : HorizontalOrVertical { |
| override val spacing = 0.dp |
| |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false) |
| } else { |
| placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#SpaceBetween" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly across the main axis, including free space |
| * before the first child and after the last child, but half the amount of space existing |
| * otherwise between two consecutive children. Visually: #1##2##3# for LTR and #3##2##1# for RTL |
| */ |
| @Stable |
| val SpaceAround = |
| object : HorizontalOrVertical { |
| override val spacing = 0.dp |
| |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = |
| if (layoutDirection == LayoutDirection.Ltr) { |
| placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false) |
| } else { |
| placeSpaceAround(totalSize, sizes, outPositions, reverseInput = true) |
| } |
| |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "Arrangement#SpaceAround" |
| } |
| |
| /** |
| * Place children such that each two adjacent ones are spaced by a fixed [space] distance across |
| * the main axis. The spacing will be subtracted from the available space that the children can |
| * occupy. The [space] can be negative, in which case children will overlap. |
| * |
| * To change alignment of the spaced children horizontally or vertically, use [spacedBy] |
| * overloads with `alignment` parameter. |
| * |
| * @param space The space between adjacent children. |
| */ |
| @Stable |
| fun spacedBy(space: Dp): HorizontalOrVertical = |
| SpacedAligned(space, true) { size, layoutDirection -> |
| Alignment.Start.align(0, size, layoutDirection) |
| } |
| |
| /** |
| * Place children horizontally such that each two adjacent ones are spaced by a fixed [space] |
| * distance. The spacing will be subtracted from the available width that the children can |
| * occupy. An [alignment] can be specified to align the spaced children horizontally inside the |
| * parent, in case there is empty width remaining. The [space] can be negative, in which case |
| * children will overlap. |
| * |
| * @param space The space between adjacent children. |
| * @param alignment The alignment of the spaced children inside the parent. |
| */ |
| @Stable |
| fun spacedBy(space: Dp, alignment: Alignment.Horizontal): Horizontal = |
| SpacedAligned(space, true) { size, layoutDirection -> |
| alignment.align(0, size, layoutDirection) |
| } |
| |
| /** |
| * Place children vertically such that each two adjacent ones are spaced by a fixed [space] |
| * distance. The spacing will be subtracted from the available height that the children can |
| * occupy. An [alignment] can be specified to align the spaced children vertically inside the |
| * parent, in case there is empty height remaining. The [space] can be negative, in which case |
| * children will overlap. |
| * |
| * @param space The space between adjacent children. |
| * @param alignment The alignment of the spaced children inside the parent. |
| */ |
| @Stable |
| fun spacedBy(space: Dp, alignment: Alignment.Vertical): Vertical = |
| SpacedAligned(space, false) { size, _ -> alignment.align(0, size) } |
| |
| /** |
| * Place children horizontally one next to the other and align the obtained group according to |
| * an [alignment]. |
| * |
| * @param alignment The alignment of the children inside the parent. |
| */ |
| @Stable |
| fun aligned(alignment: Alignment.Horizontal): Horizontal = |
| SpacedAligned(0.dp, true) { size, layoutDirection -> |
| alignment.align(0, size, layoutDirection) |
| } |
| |
| /** |
| * Place children vertically one next to the other and align the obtained group according to an |
| * [alignment]. |
| * |
| * @param alignment The alignment of the children inside the parent. |
| */ |
| @Stable |
| fun aligned(alignment: Alignment.Vertical): Vertical = |
| SpacedAligned(0.dp, false) { size, _ -> alignment.align(0, size) } |
| |
| @Immutable |
| object Absolute { |
| /** |
| * Place children horizontally such that they are as close as possible to the left edge of |
| * the [Row]. |
| * |
| * Unlike [Arrangement.Start], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * Visually: 123#### |
| */ |
| @Stable |
| val Left = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeLeftOrTop(sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#Left" |
| } |
| |
| /** |
| * Place children such that they are as close as possible to the middle of the [Row]. |
| * |
| * Unlike [Arrangement.Center], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * Visually: ##123## |
| */ |
| @Stable |
| val Center = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeCenter(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#Center" |
| } |
| |
| /** |
| * Place children horizontally such that they are as close as possible to the right edge of |
| * the [Row]. |
| * |
| * Unlike [Arrangement.End], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * Visually: ####123 |
| */ |
| @Stable |
| val Right = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeRightOrBottom(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#Right" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly across the main axis, without free space |
| * before the first child or after the last child. |
| * |
| * Unlike [Arrangement.SpaceBetween], when the layout direction is RTL, the children will |
| * not be mirrored and as such children will appear in the order they are composed inside |
| * the [Row]. |
| * |
| * Visually: 1##2##3 |
| */ |
| @Stable |
| val SpaceBetween = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeSpaceBetween(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#SpaceBetween" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly across the main axis, including free |
| * space before the first child and after the last child. |
| * |
| * Unlike [Arrangement.SpaceEvenly], when the layout direction is RTL, the children will not |
| * be mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * Visually: #1#2#3# |
| */ |
| @Stable |
| val SpaceEvenly = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeSpaceEvenly(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#SpaceEvenly" |
| } |
| |
| /** |
| * Place children such that they are spaced evenly horizontally, including free space before |
| * the first child and after the last child, but half the amount of space existing otherwise |
| * between two consecutive children. |
| * |
| * Unlike [Arrangement.SpaceAround], when the layout direction is RTL, the children will not |
| * be mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * Visually: #1##2##3##4# |
| */ |
| @Stable |
| val SpaceAround = |
| object : Horizontal { |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) = placeSpaceAround(totalSize, sizes, outPositions, reverseInput = false) |
| |
| override fun toString() = "AbsoluteArrangement#SpaceAround" |
| } |
| |
| /** |
| * Place children such that each two adjacent ones are spaced by a fixed [space] distance |
| * across the main axis. The spacing will be subtracted from the available space that the |
| * children can occupy. |
| * |
| * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * @param space The space between adjacent children. |
| */ |
| @Stable fun spacedBy(space: Dp): HorizontalOrVertical = SpacedAligned(space, false, null) |
| |
| /** |
| * Place children horizontally such that each two adjacent ones are spaced by a fixed |
| * [space] distance. The spacing will be subtracted from the available width that the |
| * children can occupy. An [alignment] can be specified to align the spaced children |
| * horizontally inside the parent, in case there is empty width remaining. |
| * |
| * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * @param space The space between adjacent children. |
| * @param alignment The alignment of the spaced children inside the parent. |
| */ |
| @Stable |
| fun spacedBy(space: Dp, alignment: Alignment.Horizontal): Horizontal = |
| SpacedAligned(space, false) { size, layoutDirection -> |
| alignment.align(0, size, layoutDirection) |
| } |
| |
| /** |
| * Place children vertically such that each two adjacent ones are spaced by a fixed [space] |
| * distance. The spacing will be subtracted from the available height that the children can |
| * occupy. An [alignment] can be specified to align the spaced children vertically inside |
| * the parent, in case there is empty height remaining. |
| * |
| * Unlike [Arrangement.spacedBy], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * @param space The space between adjacent children. |
| * @param alignment The alignment of the spaced children inside the parent. |
| */ |
| @Stable |
| fun spacedBy(space: Dp, alignment: Alignment.Vertical): Vertical = |
| SpacedAligned(space, false) { size, _ -> alignment.align(0, size) } |
| |
| /** |
| * Place children horizontally one next to the other and align the obtained group according |
| * to an [alignment]. |
| * |
| * Unlike [Arrangement.aligned], when the layout direction is RTL, the children will not be |
| * mirrored and as such children will appear in the order they are composed inside the |
| * [Row]. |
| * |
| * @param alignment The alignment of the children inside the parent. |
| */ |
| @Stable |
| fun aligned(alignment: Alignment.Horizontal): Horizontal = |
| SpacedAligned(0.dp, false) { size, layoutDirection -> |
| alignment.align(0, size, layoutDirection) |
| } |
| } |
| |
| /** Calculator for SpacedAligned alignment to avoid primitive boxing. */ |
| internal fun interface SpacingAlignmentCalculator { |
| fun align(size: Int, layoutDirection: LayoutDirection): Int |
| } |
| |
| /** |
| * Arrangement with spacing between adjacent children and alignment for the spaced group. Should |
| * not be instantiated directly, use [spacedBy] instead. |
| */ |
| @Immutable |
| internal data class SpacedAligned( |
| val space: Dp, |
| val rtlMirror: Boolean, |
| val alignment: SpacingAlignmentCalculator?, |
| ) : HorizontalOrVertical { |
| |
| override val spacing = space |
| |
| override fun Density.arrange( |
| totalSize: Int, |
| sizes: IntArray, |
| layoutDirection: LayoutDirection, |
| outPositions: IntArray, |
| ) { |
| if (sizes.isEmpty()) return |
| val spacePx = space.roundToPx() |
| val isRtl = rtlMirror && layoutDirection == LayoutDirection.Rtl |
| var lastSpace = 0 |
| var freeSpace = totalSize |
| if (isRtl) { |
| sizes.forEachIndexed { index, it -> |
| outPositions[index] = maxOf(0, freeSpace - it) |
| lastSpace = min(spacePx, outPositions[index]) |
| freeSpace = outPositions[index] - lastSpace |
| } |
| freeSpace += lastSpace |
| } else { |
| var occupied = 0 |
| sizes.forEachIndexed { index, it -> |
| outPositions[index] = min(occupied, totalSize - it) |
| lastSpace = min(spacePx, totalSize - outPositions[index] - it) |
| occupied = outPositions[index] + it + lastSpace |
| } |
| occupied -= lastSpace |
| freeSpace = totalSize - occupied |
| } |
| |
| if (alignment != null && freeSpace > 0) { |
| val groupPosition = alignment.align(freeSpace, layoutDirection) |
| |
| // LTR items are packed at the start (0), so shifting by groupPosition is correct. |
| // RTL items are packed at the end (remainingSpace), so shift relative to that. |
| val offset = if (isRtl) groupPosition - freeSpace else groupPosition |
| |
| if (offset != 0) { |
| for (index in outPositions.indices) { |
| outPositions[index] += offset |
| } |
| } |
| } |
| } |
| |
| override fun Density.arrange(totalSize: Int, sizes: IntArray, outPositions: IntArray) = |
| arrange(totalSize, sizes, LayoutDirection.Ltr, outPositions) |
| |
| override fun toString() = |
| "${if (rtlMirror) "" else "Absolute"}Arrangement#spacedAligned($space, $alignment)" |
| } |
| |
| internal fun placeRightOrBottom( |
| totalSize: Int, |
| size: IntArray, |
| outPosition: IntArray, |
| reverseInput: Boolean, |
| ) { |
| val consumedSize = size.fold(0) { a, b -> a + b } |
| var current = totalSize - consumedSize |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current |
| current += it |
| } |
| } |
| |
| internal fun placeLeftOrTop(size: IntArray, outPosition: IntArray, reverseInput: Boolean) { |
| var current = 0 |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current |
| current += it |
| } |
| } |
| |
| internal fun placeCenter( |
| totalSize: Int, |
| size: IntArray, |
| outPosition: IntArray, |
| reverseInput: Boolean, |
| ) { |
| val consumedSize = size.fold(0) { a, b -> a + b } |
| var current = (totalSize - consumedSize).toFloat() / 2 |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current.fastRoundToInt() |
| current += it.toFloat() |
| } |
| } |
| |
| internal fun placeSpaceEvenly( |
| totalSize: Int, |
| size: IntArray, |
| outPosition: IntArray, |
| reverseInput: Boolean, |
| ) { |
| val consumedSize = size.fold(0) { a, b -> a + b } |
| val gapSize = (totalSize - consumedSize).toFloat() / (size.size + 1) |
| var current = gapSize |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current.fastRoundToInt() |
| current += it.toFloat() + gapSize |
| } |
| } |
| |
| internal fun placeSpaceBetween( |
| totalSize: Int, |
| size: IntArray, |
| outPosition: IntArray, |
| reverseInput: Boolean, |
| ) { |
| if (size.isEmpty()) return |
| |
| val consumedSize = size.fold(0) { a, b -> a + b } |
| val noOfGaps = maxOf(size.lastIndex, 1) |
| val gapSize = (totalSize - consumedSize).toFloat() / noOfGaps |
| |
| var current = 0f |
| if (reverseInput && size.size == 1) { |
| // If the layout direction is right-to-left and there is only one gap, |
| // we start current with the gap size. That forces the single item to be right-aligned. |
| current = gapSize |
| } |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current.fastRoundToInt() |
| current += it.toFloat() + gapSize |
| } |
| } |
| |
| internal fun placeSpaceAround( |
| totalSize: Int, |
| size: IntArray, |
| outPosition: IntArray, |
| reverseInput: Boolean, |
| ) { |
| val consumedSize = size.fold(0) { a, b -> a + b } |
| val gapSize = |
| if (size.isNotEmpty()) { |
| (totalSize - consumedSize).toFloat() / size.size |
| } else { |
| 0f |
| } |
| var current = gapSize / 2 |
| size.forEachIndexed(reverseInput) { index, it -> |
| outPosition[index] = current.fastRoundToInt() |
| current += it.toFloat() + gapSize |
| } |
| } |
| |
| private inline fun IntArray.forEachIndexed(reversed: Boolean, action: (Int, Int) -> Unit) { |
| if (!reversed) { |
| forEachIndexed(action) |
| } else { |
| for (i in (size - 1) downTo 0) { |
| action(i, get(i)) |
| } |
| } |
| } |
| } |