feat: update colour picker sheet for material 3; shader checkerboard
Signed-off-by: Infi <infi@infi.sh>
This commit is contained in:
parent
5cb9df731b
commit
b37f7c1c9c
|
|
@ -1,5 +1,7 @@
|
||||||
package chat.revolt.sheets
|
package chat.revolt.sheets
|
||||||
|
|
||||||
|
import android.graphics.RuntimeShader
|
||||||
|
import android.os.Build
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.compose.animation.AnimatedContent
|
import androidx.compose.animation.AnimatedContent
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
|
|
@ -36,6 +38,7 @@ import androidx.compose.material3.SegmentedButton
|
||||||
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
import androidx.compose.material3.SingleChoiceSegmentedButtonRow
|
||||||
import androidx.compose.material3.Slider
|
import androidx.compose.material3.Slider
|
||||||
import androidx.compose.material3.SliderDefaults
|
import androidx.compose.material3.SliderDefaults
|
||||||
|
import androidx.compose.material3.SliderState
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
|
@ -47,15 +50,20 @@ import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.focus.onFocusChanged
|
import androidx.compose.ui.focus.onFocusChanged
|
||||||
|
import androidx.compose.ui.geometry.CornerRadius
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.RectangleShape
|
import androidx.compose.ui.graphics.RectangleShape
|
||||||
|
import androidx.compose.ui.graphics.ShaderBrush
|
||||||
|
import androidx.compose.ui.graphics.drawscope.DrawScope
|
||||||
import androidx.compose.ui.graphics.toArgb
|
import androidx.compose.ui.graphics.toArgb
|
||||||
import androidx.compose.ui.platform.LocalFocusManager
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import chat.revolt.R
|
import chat.revolt.R
|
||||||
import chat.revolt.internals.TailwindColourScheme
|
import chat.revolt.internals.TailwindColourScheme
|
||||||
|
import org.intellij.lang.annotations.Language
|
||||||
|
|
||||||
enum class ColourPickerMode {
|
enum class ColourPickerMode {
|
||||||
Sliders,
|
Sliders,
|
||||||
|
|
@ -83,6 +91,21 @@ private fun Color.asHsv(): Triple<Float, Float, Float> {
|
||||||
return Triple(hue, saturation, max)
|
return Triple(hue, saturation, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
|
private fun DrawScope.drawThumbMargin(
|
||||||
|
color: Color,
|
||||||
|
sliderState: SliderState
|
||||||
|
) {
|
||||||
|
// Area around the thumb to mimic material3 track
|
||||||
|
val thumbOutlineXBase =
|
||||||
|
(sliderState.value / sliderState.valueRange.endInclusive) * size.width
|
||||||
|
drawRect(
|
||||||
|
color,
|
||||||
|
topLeft = Offset(thumbOutlineXBase - 8.dp.toPx(), 0f),
|
||||||
|
size = androidx.compose.ui.geometry.Size(16.dp.toPx(), size.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun Color.asHexString(): String {
|
private fun Color.asHexString(): String {
|
||||||
val alpha = (alpha * 255).toInt()
|
val alpha = (alpha * 255).toInt()
|
||||||
val red = (red * 255).toInt()
|
val red = (red * 255).toInt()
|
||||||
|
|
@ -226,6 +249,21 @@ val palette = listOf(
|
||||||
TailwindColourScheme.rose[10],
|
TailwindColourScheme.rose[10],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Language("AGSL")
|
||||||
|
const val CheckerboardShader = """
|
||||||
|
layout(color) uniform vec4 color1;
|
||||||
|
layout(color) uniform vec4 color2;
|
||||||
|
uniform float squareSize;
|
||||||
|
|
||||||
|
vec4 main(vec2 fragCoord) {
|
||||||
|
// add 4 to make it less boring
|
||||||
|
float x = floor((fragCoord.x + 4) / squareSize);
|
||||||
|
float y = floor((fragCoord.y + 4) / squareSize);
|
||||||
|
|
||||||
|
return mod(x + y, 2.0) == 0.0 ? color1 : color2;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
@OptIn(ExperimentalMaterial3Api::class, ExperimentalLayoutApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ColumnScope.ColourPickerSheet(
|
fun ColumnScope.ColourPickerSheet(
|
||||||
|
|
@ -245,6 +283,20 @@ fun ColumnScope.ColourPickerSheet(
|
||||||
|
|
||||||
val colorHsv by remember(color) { derivedStateOf { color.asHsv() } }
|
val colorHsv by remember(color) { derivedStateOf { color.asHsv() } }
|
||||||
|
|
||||||
|
val checkerboardShader = with(MaterialTheme.colorScheme) {
|
||||||
|
remember {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
|
RuntimeShader(CheckerboardShader).apply {
|
||||||
|
setFloatUniform("squareSize", 8f)
|
||||||
|
setColorUniform("color1", surface.toArgb())
|
||||||
|
setColorUniform("color2", surfaceVariant.toArgb())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Column(
|
Column(
|
||||||
Modifier
|
Modifier
|
||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
|
|
@ -322,18 +374,22 @@ fun ColumnScope.ColourPickerSheet(
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
track = {
|
track = { state ->
|
||||||
Canvas(
|
with(MaterialTheme.colorScheme) {
|
||||||
Modifier
|
Canvas(
|
||||||
.fillMaxWidth()
|
Modifier
|
||||||
.height(4.dp)
|
.fillMaxWidth()
|
||||||
) {
|
.height(16.dp)
|
||||||
drawRect(
|
) {
|
||||||
Brush.horizontalGradient(
|
drawRoundRect(
|
||||||
hueTrackColours,
|
Brush.horizontalGradient(
|
||||||
endX = size.width
|
hueTrackColours,
|
||||||
|
endX = size.width
|
||||||
|
),
|
||||||
|
cornerRadius = CornerRadius(999f, 999f)
|
||||||
)
|
)
|
||||||
)
|
drawThumbMargin(surfaceContainerLow, state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -370,29 +426,33 @@ fun ColumnScope.ColourPickerSheet(
|
||||||
1f
|
1f
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
track = {
|
track = { state ->
|
||||||
Canvas(
|
with(MaterialTheme.colorScheme) {
|
||||||
Modifier
|
Canvas(
|
||||||
.fillMaxWidth()
|
Modifier
|
||||||
.height(4.dp)
|
.fillMaxWidth()
|
||||||
) {
|
.height(16.dp)
|
||||||
drawRect(
|
) {
|
||||||
Brush.horizontalGradient(
|
drawRoundRect(
|
||||||
listOf(
|
Brush.horizontalGradient(
|
||||||
Color.hsv(
|
listOf(
|
||||||
colorHsv.first,
|
Color.hsv(
|
||||||
0f,
|
colorHsv.first,
|
||||||
1f
|
0f,
|
||||||
|
1f
|
||||||
|
),
|
||||||
|
Color.hsv(
|
||||||
|
colorHsv.first,
|
||||||
|
1f,
|
||||||
|
1f
|
||||||
|
)
|
||||||
),
|
),
|
||||||
Color.hsv(
|
endX = size.width
|
||||||
colorHsv.first,
|
|
||||||
1f,
|
|
||||||
1f
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
endX = size.width
|
cornerRadius = CornerRadius(999f, 999f)
|
||||||
)
|
)
|
||||||
)
|
drawThumbMargin(surfaceContainerLow, state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -429,29 +489,33 @@ fun ColumnScope.ColourPickerSheet(
|
||||||
colorHsv.third
|
colorHsv.third
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
track = {
|
track = { state ->
|
||||||
Canvas(
|
with(MaterialTheme.colorScheme) {
|
||||||
Modifier
|
Canvas(
|
||||||
.fillMaxWidth()
|
Modifier
|
||||||
.height(4.dp)
|
.fillMaxWidth()
|
||||||
) {
|
.height(16.dp)
|
||||||
drawRect(
|
) {
|
||||||
Brush.horizontalGradient(
|
drawRoundRect(
|
||||||
listOf(
|
Brush.horizontalGradient(
|
||||||
Color.hsv(
|
listOf(
|
||||||
colorHsv.first,
|
Color.hsv(
|
||||||
colorHsv.second,
|
colorHsv.first,
|
||||||
0f
|
colorHsv.second,
|
||||||
|
0f
|
||||||
|
),
|
||||||
|
Color.hsv(
|
||||||
|
colorHsv.first,
|
||||||
|
colorHsv.second,
|
||||||
|
1f
|
||||||
|
)
|
||||||
),
|
),
|
||||||
Color.hsv(
|
endX = size.width
|
||||||
colorHsv.first,
|
|
||||||
colorHsv.second,
|
|
||||||
1f
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
endX = size.width
|
cornerRadius = CornerRadius(999f, 999f)
|
||||||
)
|
)
|
||||||
)
|
drawThumbMargin(surfaceContainerLow, state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
@ -481,21 +545,33 @@ fun ColumnScope.ColourPickerSheet(
|
||||||
colors = SliderDefaults.colors().copy(
|
colors = SliderDefaults.colors().copy(
|
||||||
thumbColor = color
|
thumbColor = color
|
||||||
),
|
),
|
||||||
track = {
|
track = { state ->
|
||||||
Canvas(
|
with(MaterialTheme.colorScheme) {
|
||||||
Modifier
|
Canvas(
|
||||||
.fillMaxWidth()
|
Modifier
|
||||||
.height(4.dp)
|
.fillMaxWidth()
|
||||||
) {
|
.height(16.dp)
|
||||||
drawRect(
|
) {
|
||||||
Brush.horizontalGradient(
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||||
listOf(
|
checkerboardShader?.let { shader ->
|
||||||
color.copy(alpha = 0f),
|
drawRoundRect(
|
||||||
color.copy(alpha = 1f)
|
ShaderBrush(shader),
|
||||||
|
cornerRadius = CornerRadius(999f, 999f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
drawRoundRect(
|
||||||
|
Brush.horizontalGradient(
|
||||||
|
listOf(
|
||||||
|
color.copy(alpha = 0f),
|
||||||
|
color.copy(alpha = 1f)
|
||||||
|
),
|
||||||
|
endX = size.width
|
||||||
),
|
),
|
||||||
endX = size.width
|
cornerRadius = CornerRadius(999f, 999f)
|
||||||
)
|
)
|
||||||
)
|
drawThumbMargin(surfaceContainerLow, state)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue