feat: support linear gradients in WebCompat.parseColour

rename it to ColourCompat already

Signed-off-by: Infi <wingit@geist.ga>
This commit is contained in:
Infi 2023-06-18 17:04:22 +02:00
parent f8ae7607a5
commit c2c72ecfe1
1 changed files with 158 additions and 16 deletions

View File

@ -5,6 +5,7 @@ import androidx.compose.material3.LocalContentColor
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Brush.Companion.linearGradient
import androidx.compose.ui.graphics.Color
// color is spelled american because Color from compose is spelled american
@ -17,28 +18,169 @@ fun Brush.Companion.solidColor(colour: Color) = linearGradient(
object WebCompat {
@Composable
fun parseColour(colour: String): Brush {
if (colour.startsWith("var(")) {
private fun parseLinearGradient(gradient: String): Brush {
val stops = mutableListOf<Pair<Float, Color>>()
val parts = mutableListOf<String>()
var startIndex = 0
var openParenthesesCount = 0
for (i in gradient.indices) {
when (gradient[i]) {
'(' -> openParenthesesCount++
')' -> openParenthesesCount--
',' -> {
if (openParenthesesCount == 0) {
val part = gradient.substring(startIndex, i).trim()
parts.add(part)
startIndex = i + 1
}
}
}
}
val lastPart = gradient.substring(startIndex).trim()
if (lastPart.isNotEmpty()) {
parts.add(lastPart)
}
parts.forEachIndexed { index, part ->
if (part.startsWith("to") || part.endsWith("deg")) {
// we don't support any other direction / blocked on compose supporting them
// TODO could probably emulate this by swapping the values around
} else {
val splitPart = part.split(" ")
val colourPart = splitPart[0]
val colour = when {
colourPart.startsWith("var(") -> {
parseVarToColour(
colourPart.substringAfter("var(").substringBeforeLast(")")
)
}
else -> parseFunctionColour(colourPart) ?: try {
Color(android.graphics.Color.parseColor(colourPart))
} catch (e: IllegalArgumentException) {
Log.d(
"WebCompat",
"Failed to parse colour $colourPart in $gradient, falling back to LocalContentColor.current"
)
LocalContentColor.current
}
}
val stop = if (splitPart.size == 2) {
splitPart[1].removeSuffix("%").toFloat() / 100f
} else {
index.toFloat() / (parts.size - 1)
}
stops.add(stop to colour)
}
}
return linearGradient(
colorStops = stops.toTypedArray()
)
}
@Composable
private fun parseFunctionColour(colourString: String): Color? {
val cleanedString = colourString.trim()
return try {
if (cleanedString.startsWith("rgb(")) {
parseRGBColour(cleanedString)
} else if (cleanedString.startsWith("rgba(")) {
parseRGBAColour(cleanedString)
} else {
throw IllegalArgumentException("Invalid colour format: $colourString")
}
} catch (e: Exception) {
Log.d(
"WebCompat",
"Parsing colour $colour. ${colour.substringAfter("var(").substringBefore(")")}"
"Failed to parse colour $colourString, falling back to LocalContentColor.current"
)
return when (colour.substringAfter("var(").substringBefore(")")) {
"--accent" -> Brush.solidColor(MaterialTheme.colorScheme.primary)
"--foreground" -> Brush.solidColor(MaterialTheme.colorScheme.onBackground)
"--background" -> Brush.solidColor(MaterialTheme.colorScheme.background)
"--error" -> Brush.solidColor(MaterialTheme.colorScheme.error)
else -> Brush.solidColor(LocalContentColor.current)
}
} else {
try {
return Brush.solidColor(Color(android.graphics.Color.parseColor(colour)))
} catch (e: IllegalArgumentException) {
null
}
}
private fun parseRGBColour(rgbString: String): Color {
val colourParts = rgbString.removePrefix("rgb(")
.removeSuffix(")")
.split(",")
.map { it.trim().toInt() }
val red = colourParts[0] / 255.0f
val green = colourParts[1] / 255.0f
val blue = colourParts[2] / 255.0f
return Color(red, green, blue)
}
private fun parseRGBAColour(rgbaString: String): Color {
val colourParts = rgbaString.removePrefix("rgba(")
.removeSuffix(")")
.split(",")
.map { it.trim() }
val red = colourParts[0].toInt() / 255.0f
val green = colourParts[1].toInt() / 255.0f
val blue = colourParts[2].toInt() / 255.0f
val alpha = colourParts[3].removeSuffix("%").toFloat() / 100.0f
return Color(red, green, blue, alpha)
}
@Composable
private fun parseVarToColour(varName: String): Color {
return when (varName) {
"--accent" -> MaterialTheme.colorScheme.primary
"--foreground" -> MaterialTheme.colorScheme.onBackground
"--background" -> MaterialTheme.colorScheme.background
"--error" -> MaterialTheme.colorScheme.error
else -> LocalContentColor.current
}
}
@Composable
private fun parseVar(varName: String): Brush {
return Brush.solidColor(parseVarToColour(varName))
}
@Composable
fun parseColour(colour: String): Brush {
when {
colour.startsWith("var(") -> {
Log.d(
"WebCompat",
"Failed to parse colour $colour, falling back to LocalContentColor.current"
"Parsing variable $colour"
)
return Brush.solidColor(LocalContentColor.current)
return parseVar(
colour.substringAfter("var(").substringBeforeLast(")")
)
}
colour.startsWith("linear-gradient(") || colour.startsWith("repeating-linear-gradient(") -> {
return parseLinearGradient(
colour
.substringAfter("repeating-")
.substringAfter("linear-gradient(")
.substringBeforeLast(")")
)
}
else -> {
return try {
Brush.solidColor(Color(android.graphics.Color.parseColor(colour)))
} catch (e: IllegalArgumentException) {
Log.d(
"WebCompat",
"Failed to parse colour $colour, falling back to LocalContentColor.current"
)
Brush.solidColor(LocalContentColor.current)
}
}
}
}