feat(IconOpts): allow entering hex RGB colors
This commit is contained in:
parent
41de5c8b56
commit
c22567f337
4 changed files with 114 additions and 12 deletions
|
|
@ -7,8 +7,8 @@ import androidx.compose.foundation.rememberScrollState
|
|||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.HorizontalDivider
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
|
|
@ -18,10 +18,11 @@ import androidx.compose.ui.unit.dp
|
|||
import cafe.adriel.voyager.core.screen.Screen
|
||||
import com.aliucord.manager.R
|
||||
import com.aliucord.manager.ui.components.InteractiveSlider
|
||||
import com.aliucord.manager.ui.screens.iconopts.components.IconOptionsAppBar
|
||||
import com.aliucord.manager.ui.screens.iconopts.components.RadioSelectorItem
|
||||
import com.aliucord.manager.ui.screens.iconopts.components.*
|
||||
import com.aliucord.manager.ui.screens.patchopts.components.options.PatchOption
|
||||
import com.aliucord.manager.ui.util.ColorSaver
|
||||
import dev.zt64.compose.pipette.CircularColorPicker
|
||||
import dev.zt64.compose.pipette.util.*
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
|
|
@ -109,15 +110,19 @@ fun IconOptionsScreenContent(
|
|||
|
||||
@Composable
|
||||
private fun CustomColorOptions(color: HSVColorState) {
|
||||
// This color is separated from the live color and intentionally lags behind while the RGBTextField is being edited.
|
||||
// When this changes, then the text inside the RGBTextField is reset to the fully formatted color.
|
||||
// As such, this only happens when the color is changed via the other color pickers.
|
||||
var initialRGBFieldColor by rememberSaveable(stateSaver = ColorSaver) { mutableStateOf(color.toARGB()) }
|
||||
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(30.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
modifier = Modifier.padding(horizontal = 15.dp)
|
||||
) {
|
||||
PatchOption(
|
||||
name = "Hue & Saturation",
|
||||
description = "The main color components",
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
name = stringResource(R.string.iconopts_colorpicker_title),
|
||||
description = stringResource(R.string.iconopts_colorpicker_desc),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
) {
|
||||
CircularColorPicker(
|
||||
hue = color.hue,
|
||||
|
|
@ -126,21 +131,25 @@ private fun CustomColorOptions(color: HSVColorState) {
|
|||
onColorChange = { hue, saturation ->
|
||||
color.hue = hue
|
||||
color.saturation = saturation
|
||||
initialRGBFieldColor = color.toARGB()
|
||||
},
|
||||
modifier = Modifier
|
||||
.padding(top = 20.dp)
|
||||
.padding(top = 12.dp)
|
||||
.size(260.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
)
|
||||
}
|
||||
|
||||
PatchOption(
|
||||
name = "Lightness",
|
||||
description = "The brightness component of the color",
|
||||
name = stringResource(R.string.iconopts_lightness_title),
|
||||
description = stringResource(R.string.iconopts_lightness_desc),
|
||||
) {
|
||||
InteractiveSlider(
|
||||
value = color.value,
|
||||
onValueChange = { color.value = it },
|
||||
onValueChange = {
|
||||
color.value = it
|
||||
initialRGBFieldColor = color.toARGB()
|
||||
},
|
||||
valueRange = 0f..1f,
|
||||
brush = Brush.horizontalGradient(
|
||||
listOf(
|
||||
|
|
@ -151,5 +160,20 @@ private fun CustomColorOptions(color: HSVColorState) {
|
|||
thumbColor = color.toARGB(),
|
||||
)
|
||||
}
|
||||
|
||||
PatchOption(
|
||||
name = stringResource(R.string.iconopts_hex_title),
|
||||
description = stringResource(R.string.iconopts_hex_desc),
|
||||
) {
|
||||
RGBTextField(
|
||||
initialColor = initialRGBFieldColor,
|
||||
setColor = {
|
||||
color.hue = it.hue
|
||||
color.saturation = it.saturation
|
||||
color.value = it.hsvValue
|
||||
},
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,62 @@
|
|||
package com.aliucord.manager.ui.screens.iconopts.components
|
||||
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextField
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalFocusManager
|
||||
import androidx.compose.ui.unit.dp
|
||||
import java.util.Locale
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
private fun formatRGBComponent(value: Float): String {
|
||||
return (value * 255).roundToInt()
|
||||
.toString(16)
|
||||
.padStart(2, '0')
|
||||
}
|
||||
|
||||
private fun formatRGBColor(color: Color): String {
|
||||
return (formatRGBComponent(color.red) + formatRGBComponent(color.green) + formatRGBComponent(color.blue))
|
||||
.uppercase(Locale.ENGLISH)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun RGBTextField(
|
||||
initialColor: Color,
|
||||
setColor: (Color) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
var isError by rememberSaveable { mutableStateOf(false) }
|
||||
var text by rememberSaveable(initialColor) { mutableStateOf(formatRGBColor(initialColor)) }
|
||||
|
||||
// Clear focus from TextField if the starting color has changed
|
||||
val focusManager = LocalFocusManager.current
|
||||
DisposableEffect(initialColor) {
|
||||
focusManager.clearFocus()
|
||||
|
||||
onDispose {}
|
||||
}
|
||||
|
||||
TextField(
|
||||
value = text,
|
||||
onValueChange = { newText ->
|
||||
val cleaned = newText.removePrefix("#").uppercase()
|
||||
val newColor = cleaned.toLongOrNull(16)?.let(::Color)?.copy(alpha = 1f)
|
||||
|
||||
text = cleaned
|
||||
isError = newColor == null
|
||||
newColor?.let(setColor)
|
||||
},
|
||||
isError = isError,
|
||||
singleLine = true,
|
||||
keyboardOptions = KeyboardOptions(autoCorrectEnabled = false),
|
||||
label = { Text("Hex Value") },
|
||||
prefix = { Text("# ") },
|
||||
modifier = modifier
|
||||
.widthIn(min = 200.dp, max = 300.dp),
|
||||
)
|
||||
}
|
||||
|
|
@ -5,8 +5,10 @@ package com.aliucord.manager.ui.util
|
|||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.scale
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.*
|
||||
import kotlinx.collections.immutable.ImmutableList
|
||||
|
|
@ -64,3 +66,11 @@ inline fun annotatingStringResource(
|
|||
@Stable
|
||||
inline fun Modifier.mirrorVertically(): Modifier =
|
||||
scale(scaleX = -1f, scaleY = 1f)
|
||||
|
||||
/**
|
||||
* Allow using compose [Color] in [androidx.compose.runtime.saveable.rememberSaveable]
|
||||
*/
|
||||
val ColorSaver = Saver<Color, Long>(
|
||||
save = { it.value.toLong() },
|
||||
restore = { Color(it.toULong()) },
|
||||
)
|
||||
|
|
|
|||
|
|
@ -169,6 +169,12 @@
|
|||
<string name="iconopts_variant_desc_discord">Does not alter the original icon, instead only adding the missing monochrome icon to the APK.</string>
|
||||
<string name="iconopts_variant_desc_aliucord">Changes the original icon color to Aliucord\'s branding.</string>
|
||||
<string name="iconopts_variant_desc_color">Select a custom icon background color.</string>
|
||||
<string name="iconopts_colorpicker_title">Hue & Saturation</string>
|
||||
<string name="iconopts_colorpicker_desc">The main HSV color components.</string>
|
||||
<string name="iconopts_lightness_title">Lightness</string>
|
||||
<string name="iconopts_lightness_desc">The brightness component of the color.</string>
|
||||
<string name="iconopts_hex_title">Hex Color</string>
|
||||
<string name="iconopts_hex_desc">Manually enter the RGB hex value of the color. Transparency is ignored!</string>
|
||||
|
||||
<string name="notif_group_install_title">Active installation</string>
|
||||
<string name="notif_group_install_desc">Progress notifications sent during a minimized installation</string>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue