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

net.peanuuutz.fork.ui.preset.Divider.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.preset

import androidx.compose.runtime.Composable
import androidx.compose.runtime.NonRestartableComposable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import net.peanuuutz.fork.render.screen.clip.ClipOp
import net.peanuuutz.fork.ui.foundation.draw.background
import net.peanuuutz.fork.ui.foundation.layout.Arrangement
import net.peanuuutz.fork.ui.foundation.layout.Spacer
import net.peanuuutz.fork.ui.foundation.layout.fillMaxWidth
import net.peanuuutz.fork.ui.foundation.layout.height
import net.peanuuutz.fork.ui.foundation.layout.list.Row
import net.peanuuutz.fork.ui.foundation.layout.padding
import net.peanuuutz.fork.ui.foundation.text.LocalTextStyle
import net.peanuuutz.fork.ui.inspection.InspectInfo
import net.peanuuutz.fork.ui.ui.draw.ContentDrawScope
import net.peanuuutz.fork.ui.ui.draw.DrawScope
import net.peanuuutz.fork.ui.ui.draw.canvas.DrawStyle
import net.peanuuutz.fork.ui.ui.draw.shading.Brush
import net.peanuuutz.fork.ui.ui.draw.text.Paragraph
import net.peanuuutz.fork.ui.ui.draw.text.TextAlign
import net.peanuuutz.fork.ui.ui.draw.text.TextStyle
import net.peanuuutz.fork.ui.ui.draw.withClip
import net.peanuuutz.fork.ui.ui.layout.Alignment
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.modifier.ModifierNodeElement
import net.peanuuutz.fork.ui.ui.node.DrawModifierNode
import net.peanuuutz.fork.ui.ui.node.ModifierNode
import net.peanuuutz.fork.ui.ui.unit.FloatOffset

@NonRestartableComposable
@Composable
fun Divider(
    modifier: Modifier = Modifier,
    brush: Brush = Brush.solid(LocalContentColor.current),
    thickness: Int = 1,
    padding: Int = 0
) {
    Spacer(
        modifier = modifier
            .fillMaxWidth()
            .height(thickness)
            .padding(horizontal = padding)
            .background(brush)
    )
}

@Composable
fun TextDivider(
    paragraph: Paragraph,
    modifier: Modifier = Modifier,
    textStyle: TextStyle = LocalTextStyle.current,
    lineBrush: Brush = Brush.solid(LocalContentColor.current),
    lineThickness: Int = 1,
    innerPadding: Int = 12,
    outerPadding: Int = 0
) {
    val textWidthState = remember { mutableStateOf(0.0f) }

    Row(
        modifier = modifier
            .fillMaxWidth()
            .padding(horizontal = outerPadding)
            .textDivider(
                textWidthProvider = textWidthState::value,
                lineBrush = lineBrush,
                lineThickness = lineThickness,
                innerPadding = innerPadding
            ),
        verticalAlignment = Alignment.CenterVertically,
        horizontalArrangement = Arrangement.Center
    ) {
        Text(
            paragraph = paragraph,
            textAlign = TextAlign.Center,
            textStyle = textStyle,
            onTextLayout = { result ->
                textWidthState.value = result.measuredParagraph.measuredSize.width
            }
        )
    }
}

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

private fun Modifier.textDivider(
    textWidthProvider: () -> Float,
    lineBrush: Brush,
    lineThickness: Int,
    innerPadding: Int
): Modifier {
    val element = TextDividerModifier(
        textWidthProvider = textWidthProvider,
        lineBrush = lineBrush,
        lineThickness = lineThickness,
        innerPadding = innerPadding
    )
    return this then element
}

private data class TextDividerModifier(
    val textWidthProvider: () -> Float,
    val lineBrush: Brush,
    val lineThickness: Int,
    val innerPadding: Int
) : ModifierNodeElement() {
    override fun create(): TextDividerModifierNode {
        return TextDividerModifierNode(
            textWidthProvider = textWidthProvider,
            lineBrush = lineBrush,
            lineThickness = lineThickness,
            innerPadding = innerPadding
        )
    }

    override fun update(node: TextDividerModifierNode) {
        node.lineBrush = lineBrush
        node.lineThickness = lineThickness
        node.innerPadding = innerPadding
    }

    override fun InspectInfo.inspect() {
        set("lineBrush", lineBrush)
        set("lineThickness", lineThickness)
        set("innerPadding", innerPadding)
    }
}

private class TextDividerModifierNode(
    val textWidthProvider: () -> Float,
    var lineBrush: Brush,
    var lineThickness: Int,
    var innerPadding: Int
) : ModifierNode(), DrawModifierNode {
    override fun ContentDrawScope.draw() {
        drawDivider()
        drawContent()
    }

    private fun DrawScope.drawDivider() {
        val (width, height) = size
        val centerX = width / 2
        val centerY = height / 2
        val halfClipBoxWidth = innerPadding + textWidthProvider() / 2
        withClip(
            leftX = centerX - halfClipBoxWidth,
            topY = 0.0f,
            rightX = centerX + halfClipBoxWidth,
            bottomY = height,
            op = ClipOp.Xor
        ) {
            drawLine(
                brush = lineBrush,
                start = FloatOffset(0.0f, centerY),
                end = FloatOffset(width, centerY),
                stroke = DrawStyle.Stroke(width = lineThickness.toFloat())
            )
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy