All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.peanuuutz.fork.ui.foundation.layout.Padding.kt Maven / Gradle / Ivy

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.Stable
import net.peanuuutz.fork.ui.inspection.InspectInfo
import net.peanuuutz.fork.ui.ui.layout.Constraints
import net.peanuuutz.fork.ui.ui.layout.Measurable
import net.peanuuutz.fork.ui.ui.layout.MeasureResult
import net.peanuuutz.fork.ui.ui.layout.constrainHeight
import net.peanuuutz.fork.ui.ui.layout.constrainWidth
import net.peanuuutz.fork.ui.ui.layout.offset
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.modifier.ModifierNodeElement
import net.peanuuutz.fork.ui.ui.node.LayoutModifierNode
import net.peanuuutz.fork.ui.ui.node.ModifierNode

@Stable
fun Modifier.padding(all: Int): Modifier {
    return padding(all, all, all, all)
}

@Stable
fun Modifier.padding(
    horizontal: Int = 0,
    vertical: Int = 0
): Modifier {
    return padding(
        left = horizontal,
        top = vertical,
        right = horizontal,
        bottom = vertical
    )
}

@Stable
fun Modifier.padding(
    left: Int = 0,
    top: Int = 0,
    right: Int = 0,
    bottom: Int = 0
): Modifier {
    val element = PaddingModifier(
        left = left,
        top = top,
        right = right,
        bottom = bottom
    )
    return this then element
}

@Stable
interface PaddingValues {
    fun calculateLeftPadding(): Int

    fun calculateTopPadding(): Int

    fun calculateRightPadding(): Int

    fun calculateBottomPadding(): Int
}

@Stable
val NoPadding: PaddingValues = PaddingValues(0)

@Stable
fun PaddingValues(all: Int): PaddingValues {
    return PaddingValues(all, all, all, all)
}

@Stable
fun PaddingValues(
    horizontal: Int = 0,
    vertical: Int = 0
): PaddingValues {
    return PaddingValues(
        left = horizontal,
        top = vertical,
        right = horizontal,
        bottom = vertical
    )
}

@Stable
fun PaddingValues(
    left: Int = 0,
    top: Int = 0,
    right: Int = 0,
    bottom: Int = 0
): PaddingValues {
    return FixedPaddingValues(
        left = left,
        top = top,
        right = right,
        bottom = bottom
    )
}

@Stable
fun Modifier.padding(paddingValues: PaddingValues): Modifier {
    return this then PaddingValuesModifier(paddingValues)
}

// ======== Internal ========

private data class FixedPaddingValues(
    val left: Int,
    val top: Int,
    val right: Int,
    val bottom: Int
) : PaddingValues {
    init {
        require(left >= 0 && top >= 0 && right >= 0 && bottom >= 0) {
            "Cannot have negative padding, but found $this"
        }
    }

    override fun calculateLeftPadding(): Int {
        return left
    }

    override fun calculateTopPadding(): Int {
        return top
    }

    override fun calculateRightPadding(): Int {
        return right
    }

    override fun calculateBottomPadding(): Int {
        return bottom
    }
}

private data class PaddingModifier(
    val left: Int,
    val top: Int,
    val right: Int,
    val bottom: Int
) : ModifierNodeElement() {
    init {
        require(left >= 0 && top >= 0 && right >= 0 && bottom >= 0) {
            "Cannot have negative padding, but found ($left, $top, $right, $bottom)"
        }
    }

    override fun create(): PaddingModifierNode {
        return PaddingModifierNode(
            left = left,
            top = top,
            right = right,
            bottom = bottom
        )
    }

    override fun update(node: PaddingModifierNode) {
        node.left = left
        node.top = top
        node.right = right
        node.bottom = bottom
    }

    override fun InspectInfo.inspect() {
        set("left", left)
        set("top", top)
        set("right", right)
        set("bottom", bottom)
    }
}

private class PaddingModifierNode(
    var left: Int,
    var top: Int,
    var right: Int,
    var bottom: Int
) : ModifierNode(), LayoutModifierNode {
    override fun measure(measurable: Measurable, constraints: Constraints): MeasureResult {
        val horizontal = left + right
        val vertical = top + bottom
        val contentConstraints = constraints.offset(-horizontal, -vertical)
        val placeable = measurable.measure(contentConstraints)
        val width = constraints.constrainWidth(placeable.width + horizontal)
        val height = constraints.constrainHeight(placeable.height + vertical)
        return MeasureResult(width, height) {
            placeable.place(left, top)
        }
    }
}

private data class PaddingValuesModifier(
    val paddingValues: PaddingValues
) : ModifierNodeElement() {
    override fun create(): PaddingValuesModifierNode {
        return PaddingValuesModifierNode(paddingValues)
    }

    override fun update(node: PaddingValuesModifierNode) {
        node.paddingValues = paddingValues
    }

    override fun InspectInfo.inspect() {
        set("paddingValues", paddingValues)
    }
}

private class PaddingValuesModifierNode(
    var paddingValues: PaddingValues
) : ModifierNode(), LayoutModifierNode {
    override fun measure(measurable: Measurable, constraints: Constraints): MeasureResult {
        val paddingValues = paddingValues
        val left = paddingValues.calculateLeftPadding()
        val top = paddingValues.calculateTopPadding()
        val right = paddingValues.calculateRightPadding()
        val bottom = paddingValues.calculateBottomPadding()
        require(left >= 0 && top >= 0 && right >= 0 && bottom >= 0) {
            "Cannot have negative padding, but found $paddingValues"
        }
        val horizontal = left + right
        val vertical = top + bottom
        val contentConstraints = constraints.offset(-horizontal, -vertical)
        val placeable = measurable.measure(contentConstraints)
        return MeasureResult(
            width = placeable.width + horizontal,
            height = placeable.height + vertical
        ) {
            placeable.place(left, top)
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy