net.peanuuutz.fork.ui.foundation.layout.Arrangement.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of fork-ui Show documentation
Show all versions of fork-ui Show documentation
Comprehensive API designed for Minecraft modders
The newest version!
/*
* Copyright 2020 The Android Open Source Project
* Modifications Copyright 2022 Peanuuutz
*
* 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 net.peanuuutz.fork.ui.foundation.layout
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.Stable
import net.peanuuutz.fork.ui.ui.layout.Alignment
import net.peanuuutz.fork.ui.ui.layout.Constraints.Companion.Unlimited
import net.peanuuutz.fork.util.common.fastForEachIndexed
import net.peanuuutz.fork.util.common.fastSum
import kotlin.math.roundToInt
object Arrangement {
@Stable
fun interface Horizontal {
val spacing: Int
get() = Unlimited
fun arrangeHorizontally(
contentSizes: IntArray,
availableSpace: Int
): IntArray
}
@Stable
fun interface Vertical {
val spacing: Int
get() = Unlimited
fun arrangeVertically(
contentSizes: IntArray,
availableSpace: Int
): IntArray
}
@Stable
fun interface HorizontalOrVertical : Horizontal, Vertical {
fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray
override val spacing: Int
get() = Unlimited
override fun arrangeHorizontally(contentSizes: IntArray, availableSpace: Int): IntArray {
return arrange(contentSizes, availableSpace)
}
override fun arrangeVertically(contentSizes: IntArray, availableSpace: Int): IntArray {
return arrange(contentSizes, availableSpace)
}
}
@Stable
val Left: Horizontal = object : Horizontal {
override fun arrangeHorizontally(contentSizes: IntArray, availableSpace: Int): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
return arrange(contentSizes, 0.0f) { size -> size }
}
override fun toString(): String {
return "Arrangement.Left"
}
}
@Stable
val Right: Horizontal = object : Horizontal {
override fun arrangeHorizontally(contentSizes: IntArray, availableSpace: Int): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Left.arrangeHorizontally(contentSizes, availableSpace)
}
val initialPosition = (availableSpace - contentSizes.fastSum()).toFloat()
return arrange(contentSizes, initialPosition) { size -> size }
}
override fun toString(): String {
return "Arrangement.Right"
}
}
@Stable
val Top: Vertical = object : Vertical {
override fun arrangeVertically(contentSizes: IntArray, availableSpace: Int): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
return arrange(contentSizes, 0.0f) { size -> size }
}
override fun toString(): String {
return "Arrangement.Top"
}
}
@Stable
val Bottom: Vertical = object : Vertical {
override fun arrangeVertically(contentSizes: IntArray, availableSpace: Int): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val initialPosition = (availableSpace - contentSizes.fastSum()).toFloat()
return arrange(contentSizes, initialPosition) { size -> size }
}
override fun toString(): String {
return "Arrangement.Bottom"
}
}
@Stable
val Start: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(contentSizes: IntArray, availableSpace: Int): IntArray {
return Top.arrangeVertically(contentSizes, availableSpace)
}
override fun toString(): String {
return "Arrangement.Start"
}
}
@Stable
val End: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(contentSizes: IntArray, availableSpace: Int): IntArray {
return Bottom.arrangeVertically(contentSizes, availableSpace)
}
override fun toString(): String {
return "Arrangement.End"
}
}
@Stable
val Center: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val initialPosition = (availableSpace - contentSizes.fastSum()).toFloat() / 2
return arrange(contentSizes, initialPosition) { size -> size }
}
override fun toString(): String {
return "Arrangement.Center"
}
}
@Stable
val SpaceEvenly: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val gap = (availableSpace - contentSizes.fastSum()).toFloat() / (contentSizes.size + 1)
return arrange(contentSizes, gap) { size -> size + gap }
}
override fun toString(): String {
return "Arrangement.SpaceEvenly"
}
}
@Stable
val SpaceBetween: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val gap = (availableSpace - contentSizes.fastSum()).toFloat() / (contentSizes.size - 1)
return arrange(contentSizes, 0.0f) { size -> size + gap }
}
override fun toString(): String {
return "Arrangement.SpaceBetween"
}
}
@Stable
val SpaceAround: HorizontalOrVertical = object : HorizontalOrVertical {
override fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val singleGap = (availableSpace - contentSizes.fastSum()).toFloat() / (contentSizes.size * 2)
return arrange(contentSizes, singleGap) { size -> size + singleGap * 2 }
}
override fun toString(): String {
return "Arrangement.SpaceAround"
}
}
@Stable
fun spacedBy(
space: Int
): HorizontalOrVertical {
return SpacedAligned(space, Alignment.Top::alignVertically)
}
@Stable
fun alignedBy(
alignment: Alignment.Horizontal
): Horizontal {
return SpacedAligned(0, alignment::alignHorizontally)
}
@Stable
fun alignedBy(
alignment: Alignment.Vertical
): Vertical {
return SpacedAligned(0, alignment::alignVertically)
}
@Stable
fun spacedAlignedBy(
spacing: Int,
alignment: Alignment.Horizontal
): Horizontal {
return SpacedAligned(spacing, alignment::alignHorizontally)
}
@Stable
fun spacedAlignedBy(
spacing: Int,
alignment: Alignment.Vertical
): Vertical {
return SpacedAligned(spacing, alignment::alignVertically)
}
// ======== Internal ========
@Immutable
private data class SpacedAligned(
override val spacing: Int,
val alignment: (Int, Int) -> Int
) : HorizontalOrVertical {
init {
require(spacing < Unlimited) { "Cannot have infinite space" }
}
override fun arrange(
contentSizes: IntArray,
availableSpace: Int
): IntArray {
if (contentSizes.isEmpty()) {
return contentSizes
}
if (availableSpace >= Unlimited) {
return Top.arrangeVertically(contentSizes, availableSpace)
}
val output = arrange(contentSizes, 0.0f) { size -> size + spacing }
val totalSize = contentSizes.fastSum() + spacing * (contentSizes.size - 1)
val offset = alignment(totalSize, availableSpace)
for (i in output.indices) {
output[i] += offset
}
return output
}
}
}
@Stable
val Arrangement.Horizontal.resolvedHorizontalSpacing: Int
get() {
val spacing = spacing
return if (spacing >= Unlimited) 0 else spacing
}
@Stable
val Arrangement.Vertical.resolvedVerticalSpacing: Int
get() {
val spacing = spacing
return if (spacing >= Unlimited) 0 else spacing
}
// ======== Internal ========
private inline fun arrange(
contentSizes: IntArray,
initialPosition: Float,
positionDeltaProvider: (size: Float) -> Float
): IntArray {
var currentPosition = initialPosition
val result = IntArray(contentSizes.size)
contentSizes.fastForEachIndexed { index, size ->
val previousPosition = currentPosition
currentPosition += positionDeltaProvider(size.toFloat())
result[index] = previousPosition.roundToInt()
}
return result
}