From 444f19922536dc404504ef825ec3d039a59df297 Mon Sep 17 00:00:00 2001 From: Cuong-Tran Date: Mon, 23 Sep 2024 19:59:43 +0700 Subject: [PATCH] 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 --- .../settings/widget/CustomBrightnessSlider.kt | 100 ------- .../settings/widget/ThemeColorPickerWidget.kt | 272 ++++++++++++++++-- .../moko-resources/base/strings.xml | 13 + 3 files changed, 262 insertions(+), 123 deletions(-) delete mode 100644 app/src/main/java/eu/kanade/presentation/more/settings/widget/CustomBrightnessSlider.kt diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/CustomBrightnessSlider.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/CustomBrightnessSlider.kt deleted file mode 100644 index a1da01d20..000000000 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/CustomBrightnessSlider.kt +++ /dev/null @@ -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 - ) -} diff --git a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ThemeColorPickerWidget.kt b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ThemeColorPickerWidget.kt index 9f628f540..378cec75d 100644 --- a/app/src/main/java/eu/kanade/presentation/more/settings/widget/ThemeColorPickerWidget.kt +++ b/app/src/main/java/eu/kanade/presentation/more/settings/widget/ThemeColorPickerWidget.kt @@ -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 = { _, _ -> }, + ) +} diff --git a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml index 065e6b8f7..09eca6532 100644 --- a/i18n-kmk/src/commonMain/moko-resources/base/strings.xml +++ b/i18n-kmk/src/commonMain/moko-resources/base/strings.xml @@ -47,6 +47,19 @@ Palette Personalizer Pick a seed color for the custom Theme Custom Theme + Casual + Sunset + Outrun + Raspberry + Pop + Cathode + Bubblegum + Night at the beach + Spectral + Patagonia + Springfield + LCD + PICO-8 Show updating progress banner