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

net.peanuuutz.fork.ui.preset.ProgressIndicator.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.Stable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import net.peanuuutz.fork.ui.foundation.draw.paint
import net.peanuuutz.fork.ui.foundation.layout.Box
import net.peanuuutz.fork.ui.foundation.layout.fillMaxSize
import net.peanuuutz.fork.ui.ui.draw.ContentScale
import net.peanuuutz.fork.ui.ui.draw.DrawScope
import net.peanuuutz.fork.ui.ui.draw.Painter
import net.peanuuutz.fork.ui.ui.draw.shape.Rect
import net.peanuuutz.fork.ui.ui.draw.withClip
import net.peanuuutz.fork.ui.ui.layout.LayoutDirection
import net.peanuuutz.fork.ui.ui.layout.LayoutDirection.Down
import net.peanuuutz.fork.ui.ui.layout.LayoutDirection.Left
import net.peanuuutz.fork.ui.ui.layout.LayoutDirection.Right
import net.peanuuutz.fork.ui.ui.layout.LayoutDirection.Up
import net.peanuuutz.fork.ui.ui.modifier.Modifier
import net.peanuuutz.fork.ui.ui.unit.FloatSize

@Composable
fun ProgressIndicator(
    progress: Float,
    indicator: ProgressIndicator,
    modifier: Modifier = Modifier
) {
    val painter = rememberProgressIndicatorAsPainter(
        progress = progress,
        indicator = indicator
    )

    Box(
        modifier = modifier
            .fillMaxSize()
            .paint(
                painter = painter,
                contentScale = ContentScale.Fill
            )
    )
}

@Stable
fun interface ProgressIndicator {
    fun DrawScope.drawIndicator(progress: Float)

    companion object {
        @Stable
        fun linear(
            fill: Painter,
            mask: Painter? = null,
            direction: LayoutDirection = Right
        ): ProgressIndicator {
            return if (mask == null) {
                BarProgressIndicator(
                    fill = fill,
                    direction = direction
                )
            } else {
                MaskedProgressIndicator(
                    fill = fill,
                    mask = mask,
                    direction = direction
                )
            }
        }
    }
}

@Composable
fun rememberProgressIndicatorAsPainter(
    progress: Float,
    indicator: ProgressIndicator
): Painter {
    val coercedProgress = progress.coerceIn(0.0f, 1.0f)
    val progressRemembered by rememberUpdatedState(coercedProgress)
    val indicatorRemembered by rememberUpdatedState(indicator)
    return remember {
        object : Painter() {
            override val size: FloatSize
                get() = FloatSize.Unspecified

            override fun DrawScope.onDraw() {
                with(indicatorRemembered) {
                    drawIndicator(progressRemembered)
                }
            }

            override fun toString(): String {
                return "ProgressIndicatorPainter(indicator=$indicatorRemembered)"
            }
        }
    }
}

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

private abstract class LinearProgressIndicator : ProgressIndicator {
    abstract val direction: LayoutDirection

    abstract fun DrawScope.drawFiller()

    override fun DrawScope.drawIndicator(progress: Float) {
        when {
            progress <= 0.0f -> {}
            progress >= 1.0f -> {
                drawFiller()
            }
            else -> {
                withClip(
                    rect = calculateFillArea(size, progress)
                ) {
                    drawFiller()
                }
            }
        }
    }

    private fun calculateFillArea(
        size: FloatSize,
        progress: Float
    ): Rect {
        val (width, height) = size
        return when (direction) {
            Right -> {
                Rect(
                    leftX = 0.0f,
                    topY = 0.0f,
                    rightX = width * progress,
                    bottomY = height
                )
            }
            Up -> {
                Rect(
                    leftX = 0.0f,
                    topY = height * (1.0f - progress),
                    rightX = width,
                    bottomY = height
                )
            }
            Down -> {
                Rect(
                    leftX = 0.0f,
                    topY = 0.0f,
                    rightX = width,
                    bottomY = height * progress
                )
            }
            Left -> {
                Rect(
                    leftX = width * (1.0f - progress),
                    topY = 0.0f,
                    rightX = width,
                    bottomY = height
                )
            }
        }
    }
}

private data class BarProgressIndicator(
    val fill: Painter,
    override val direction: LayoutDirection
) : LinearProgressIndicator() {
    override fun DrawScope.drawFiller() {
        with(fill) {
            onDraw()
        }
    }
}

private data class MaskedProgressIndicator(
    val fill: Painter,
    val mask: Painter,
    override val direction: LayoutDirection
) : LinearProgressIndicator() {
    override fun DrawScope.drawFiller() {
        withClip(
            clip = {
                with(mask) {
                    onDraw()
                }
            }
        ) {
            with(fill) {
                onDraw()
            }
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy