Colors palette for Custom theme

* colors palette

* color button styling & scroll to top

* button's color animation

* Color change with hex. Also using default BrightnessSlider
This commit is contained in:
Cuong-Tran 2024-09-23 19:59:43 +07:00 committed by GitHub
parent b72d910cc9
commit 444f199225
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 262 additions and 123 deletions

View file

@ -1,100 +0,0 @@
package eu.kanade.presentation.more.settings.widget
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Canvas
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ImageBitmap
import androidx.compose.ui.graphics.ImageBitmapConfig
import androidx.compose.ui.graphics.LinearGradientShader
import androidx.compose.ui.graphics.Paint
import androidx.compose.ui.graphics.TileMode
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.github.skydoves.colorpicker.compose.BrightnessSlider
import com.github.skydoves.colorpicker.compose.ColorPickerController
@Composable
fun CustomBrightnessSlider(
initialColor: Color,
controller: ColorPickerController,
modifier: Modifier = Modifier,
) {
// Define your colors and sizes directly
val borderColor = Color.LightGray // Color for the slider border
val thumbRadius = 12.dp // Example thumb radius
val trackHeight = 4.dp // Example track height
val borderSize = 1.dp // Example border size for the slider
// Set up the paint for the thumb (wheel)
val wheelPaint = Paint().apply {
color = Color.White
alpha = 1.0f
}
// This function creates the ImageBitmap for the gradient background of the slider
@Composable
fun rememberSliderGradientBitmap(
width: Dp,
height: Dp,
startColor: Color,
endColor: Color,
): ImageBitmap {
val sizePx = with(LocalDensity.current) { IntSize(width.roundToPx(), height.roundToPx()) }
return remember(sizePx, startColor, endColor) {
ImageBitmap(sizePx.width, sizePx.height, ImageBitmapConfig.Argb8888).apply {
val canvas = Canvas(this)
val shader = LinearGradientShader(
colors = listOf(startColor, endColor),
from = Offset(0f, 0f),
to = Offset(sizePx.width.toFloat(), 0f),
tileMode = TileMode.Clamp,
)
val paint = Paint().apply {
this.shader = shader
}
canvas.drawRect(
0f,
0f,
sizePx.width.toFloat(),
sizePx.height.toFloat(),
paint,
)
}
}
}
// Obtain the Composable's size for the gradient background
val sliderWidth = 20.dp // Example width, adjust to your needs
val sliderHeight = thumbRadius * 2 // The height is double the thumb radius
val gradientBitmap = rememberSliderGradientBitmap(
width = sliderWidth, // Subtract the thumb radii from the total width
height = trackHeight,
startColor = Color.White,
endColor = Color.White,
)
BrightnessSlider(
modifier = modifier
.height(sliderHeight)
.fillMaxWidth()
.padding(horizontal = thumbRadius), // Padding equals thumb radius
controller = controller,
initialColor = initialColor,
borderRadius = thumbRadius, // Use thumbRadius for the rounded corners
borderSize = borderSize,
borderColor = borderColor, // Use borderColor for the slider border
wheelRadius = thumbRadius,
wheelColor = Color.White, // Thumb (wheel) color
wheelImageBitmap = gradientBitmap, // Use the generated gradient bitmap as the background
wheelAlpha = 1.0f, // Full opacity for the thumb
wheelPaint = wheelPaint, // Use the defined wheel paint
)
}

View file

@ -1,48 +1,68 @@
package eu.kanade.presentation.more.settings.widget
import android.graphics.Bitmap
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.expandVertically
import androidx.compose.animation.fadeIn
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowForward
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.github.skydoves.colorpicker.compose.AlphaTile
import com.github.skydoves.colorpicker.compose.BrightnessSlider
import com.github.skydoves.colorpicker.compose.ColorEnvelope
import com.github.skydoves.colorpicker.compose.ColorPickerController
import com.github.skydoves.colorpicker.compose.HsvColorPicker
import eu.kanade.domain.ui.model.AppTheme
import kotlinx.coroutines.launch
import tachiyomi.i18n.kmk.KMR
import tachiyomi.presentation.core.components.material.Button
import tachiyomi.presentation.core.components.material.ButtonDefaults.buttonColors
import tachiyomi.presentation.core.components.material.padding
import tachiyomi.presentation.core.i18n.stringResource
import kotlin.math.roundToInt
@OptIn(ExperimentalStdlibApi::class)
@Composable
internal fun ThemeColorPickerWidget(
initialColor: Color,
controller: ColorPickerController,
onItemClick: (Color, AppTheme) -> Unit,
) {
val interactionSource = remember { MutableInteractionSource() }
var selectedColor by remember { mutableStateOf(initialColor) }
var showConfirmButton by remember { mutableStateOf(false) }
var hexCode by remember { mutableStateOf(initialColor.toArgb().toHexString()) }
val wheelSize = with(LocalDensity.current) { 20.dp.toPx().roundToInt() }
val wheelStrokeWidth = with(LocalDensity.current) { 2.dp.toPx() }
@ -70,16 +90,18 @@ internal fun ThemeColorPickerWidget(
BasePreferenceWidget(
subcomponent = {
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
Column(
modifier = Modifier
.padding(horizontal = MaterialTheme.padding.large)
.verticalScroll(rememberScrollState()),
.verticalScroll(scrollState),
horizontalAlignment = Alignment.CenterHorizontally,
) {
Box(
modifier = Modifier
.padding(
vertical = MaterialTheme.padding.medium,
vertical = MaterialTheme.padding.small,
),
) {
HsvColorPicker(
@ -89,36 +111,240 @@ internal fun ThemeColorPickerWidget(
wheelImageBitmap = wheelBitmap,
initialColor = initialColor,
onColorChanged = { colorEnvelope: ColorEnvelope ->
hexCode = colorEnvelope.hexCode
selectedColor = colorEnvelope.color
showConfirmButton = true
},
)
}
CustomBrightnessSlider(
BrightnessSlider(
modifier = Modifier
.fillMaxWidth(),
.fillMaxWidth()
.padding(MaterialTheme.padding.small)
.height(24.dp),
controller = controller,
borderRadius = 12.dp,
wheelImageBitmap = wheelBitmap,
initialColor = initialColor,
)
AnimatedVisibility(
visible = showConfirmButton,
enter = fadeIn() + expandVertically(),
Row(
modifier = Modifier
.padding(top = MaterialTheme.padding.large),
.fillMaxWidth()
.padding(MaterialTheme.padding.small),
horizontalArrangement = Arrangement.Center,
) {
Button(
onClick = {
onItemClick(selectedColor, AppTheme.CUSTOM)
},
Column {
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = "#${initialColor.toArgb().toHexString()}",
style = MaterialTheme.typography.labelMedium.copy(
color = initialColor,
),
)
AlphaTile(
modifier = Modifier
.size(64.dp)
.clip(MaterialTheme.shapes.small),
selectedColor = initialColor,
)
}
Icon(
imageVector = Icons.AutoMirrored.Outlined.ArrowForward,
tint = Color.Gray,
modifier = Modifier
.size(64.dp)
.align(Alignment.CenterVertically)
.padding(MaterialTheme.padding.small)
.padding(top = MaterialTheme.padding.small),
contentDescription = null,
)
Column {
Text(
modifier = Modifier.align(Alignment.CenterHorizontally),
text = "#$hexCode",
style = MaterialTheme.typography.labelMedium.copy(
color = selectedColor,
),
)
AlphaTile(
modifier = Modifier
.size(64.dp)
.clip(MaterialTheme.shapes.small),
controller = controller,
)
}
}
Button(
onClick = {
onItemClick(selectedColor, AppTheme.CUSTOM)
},
colors = buttonColors(
containerColor = animateColorAsState(
label = "animateColorAsState",
targetValue = MaterialTheme.colorScheme.primary,
animationSpec = tween(durationMillis = 500),
).value,
),
interactionSource = interactionSource,
modifier = Modifier
.padding(vertical = MaterialTheme.padding.medium)
.fillMaxWidth()
.height(48.dp),
content = {
Text(text = stringResource(KMR.strings.action_confirm_color))
},
)
val colorsPalette = mapOf(
KMR.strings.custom_theme_palette_sunset to listOf(
Color(0xFF3e9cbf),
Color(0xFFa7ecf2),
Color(0xFFf2c43d),
Color(0xFFf17c37),
Color(0xFFf26d50),
),
KMR.strings.custom_theme_palette_outrun to listOf(
Color(0xFFfffc40),
Color(0xFFfaba61),
Color(0xFFff8172),
Color(0xFFff2fa9),
Color(0xFF3a579a),
),
KMR.strings.custom_theme_palette_raspberry to listOf(
Color(0xFF730517),
Color(0xFFf44560),
Color(0xFF44d1df),
Color(0xFF32a4a7),
Color(0xFF1e7069),
),
KMR.strings.custom_theme_palette_cathode to listOf(
Color(0xFFa8216b),
Color(0xFFf1184c),
Color(0xFFf36943),
Color(0xFFf7dc66),
Color(0xFF2e9599),
),
KMR.strings.custom_theme_palette_bubblegum to listOf(
Color(0xFFf8cd82),
Color(0xFFf65b74),
Color(0xFFf72078),
Color(0xFF23b0bd),
Color(0xFF0df7db),
),
KMR.strings.custom_theme_palette_springfield to listOf(
Color(0xFF89d1dc),
Color(0xFFf89cfa),
Color(0xFFc386f1),
Color(0xFFf0d689),
Color(0xFFaff28b),
),
KMR.strings.custom_theme_palette_spectral to listOf(
Color(0xFF471337),
Color(0xFFb13254),
Color(0xFFff5349),
Color(0xFFff7249),
Color(0xFFff9248),
),
KMR.strings.custom_theme_palette_night_at_the_beach to listOf(
Color(0xFF262335),
Color(0xFF503c52),
Color(0xFF9f6c66),
Color(0xFFd4896a),
Color(0xFFffbb6c),
),
KMR.strings.custom_theme_palette_casual to listOf(
Color(0xFF4f6a8f),
Color(0xFF88a2bc),
Color(0xFFf0dbb0),
Color(0xFFefb680),
Color(0xFFd99477),
),
KMR.strings.custom_theme_palette_patagonia to listOf(
Color(0xFF202e32),
Color(0xFF85937a),
Color(0xFF586c5c),
Color(0xFFa9af90),
Color(0xFFdfdcb9),
),
KMR.strings.custom_theme_palette_lcd to listOf(
Color(0xFF0f370e),
Color(0xFF30622f),
Color(0xFF8bad0d),
Color(0xFF9bbc0e),
),
KMR.strings.custom_theme_palette_pop to listOf(
Color(0xFF00ff3f),
Color(0xFF35b5ff),
Color(0xFFff479c),
Color(0xFFfffb38),
),
KMR.strings.custom_theme_palette_pico to listOf(
Color(0xFF1d2b53),
Color(0xFF7e2453),
Color(0xFF008751),
Color(0xFFab5236),
Color(0xFF5f574e),
Color(0xFFc2c3c7),
Color(0xFFff004d),
Color(0xFFffa300),
Color(0xFFffec27),
Color(0xFF00e536),
Color(0xFF29aeff),
Color(0xFF83769c),
Color(0xFFff77a8),
Color(0xFFffcdaa),
),
)
colorsPalette.forEach { (title, colors) ->
Text(
text = stringResource(title),
style = MaterialTheme.typography.bodyLarge,
modifier = Modifier
.padding(top = MaterialTheme.padding.medium),
)
FlowRow(
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
content = {
Text(text = stringResource(KMR.strings.action_confirm_color))
},
)
.padding(top = MaterialTheme.padding.small),
horizontalArrangement = Arrangement.SpaceEvenly,
) {
colors.forEach { color ->
Box(
modifier = Modifier
.background(
color = color,
shape = MaterialTheme.shapes.small,
)
.height(48.dp)
.aspectRatio(1f)
.clickable(
role = Role.Button,
onClick = {
controller.selectByColor(color, true)
scope.launch {
scrollState.animateScrollTo(0)
}
},
),
content = { },
)
}
}
}
}
},
)
}
@Preview
@Composable
fun ThemeColorPickerWidgetPreview() {
ThemeColorPickerWidget(
initialColor = Color(0xFFDF0090),
controller = ColorPickerController(),
onItemClick = { _, _ -> },
)
}

View file

@ -47,6 +47,19 @@
<string name="pref_custom_color">Palette Personalizer</string>
<string name="custom_color_description">Pick a seed color for the custom Theme</string>
<string name="theme_custom">Custom Theme</string>
<string name="custom_theme_palette_casual">Casual</string>
<string name="custom_theme_palette_sunset">Sunset</string>
<string name="custom_theme_palette_outrun">Outrun</string>
<string name="custom_theme_palette_raspberry">Raspberry</string>
<string name="custom_theme_palette_pop">Pop</string>
<string name="custom_theme_palette_cathode">Cathode</string>
<string name="custom_theme_palette_bubblegum">Bubblegum</string>
<string name="custom_theme_palette_night_at_the_beach">Night at the beach</string>
<string name="custom_theme_palette_spectral">Spectral</string>
<string name="custom_theme_palette_patagonia">Patagonia</string>
<string name="custom_theme_palette_springfield">Springfield</string>
<string name="custom_theme_palette_lcd">LCD</string>
<string name="custom_theme_palette_pico">PICO-8</string>
<!-- Library section -->
<string name="pref_show_updating_progress_banner">Show updating progress banner</string>