From 7bb16753e00f09d78a341c98a4308567fd45f3db Mon Sep 17 00:00:00 2001 From: MM20 <15646950+MM2-0@users.noreply.github.com> Date: Mon, 17 Apr 2023 15:44:20 +0200 Subject: [PATCH] Rewrite color picker --- app/ui/build.gradle.kts | 2 - .../ui/component/colorpicker/ColorPicker.kt | 230 ++++++++++++++++++ .../component/preferences/ColorPreference.kt | 34 +-- .../launcher2/licenses/OpenSourceLicenses.kt | 8 - settings.gradle.kts | 3 - 5 files changed, 234 insertions(+), 43 deletions(-) create mode 100644 app/ui/src/main/java/de/mm20/launcher2/ui/component/colorpicker/ColorPicker.kt diff --git a/app/ui/build.gradle.kts b/app/ui/build.gradle.kts index e1fe15a6..c88c7893 100644 --- a/app/ui/build.gradle.kts +++ b/app/ui/build.gradle.kts @@ -81,8 +81,6 @@ dependencies { implementation(libs.androidx.navigation.compose) - implementation(libs.composecolorpicker) - implementation(libs.jsoup) // Legacy dependencies diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/colorpicker/ColorPicker.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/colorpicker/ColorPicker.kt new file mode 100644 index 00000000..09bcf9fa --- /dev/null +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/colorpicker/ColorPicker.kt @@ -0,0 +1,230 @@ +package de.mm20.launcher2.ui.component.colorpicker + +import androidx.compose.foundation.Canvas +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectDragGestures +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxWithConstraints +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.aspectRatio +import androidx.compose.foundation.layout.fillMaxSize +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.shape.CircleShape +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.Slider +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.setValue +import androidx.compose.ui.AbsoluteAlignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.rotate +import androidx.compose.ui.draw.shadow +import androidx.compose.ui.geometry.CornerRadius +import androidx.compose.ui.graphics.Brush +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.drawscope.Fill +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.unit.dp +import de.mm20.launcher2.ui.ktx.toHexString +import kotlin.math.atan2 +import android.graphics.Color as AndroidColor + + +@Composable +fun ColorPicker( + value: Color, + onValueChanged: (Color) -> Unit, + modifier: Modifier = Modifier, +) { + val (hue, sat, vl) = remember(value) { + val hsv = FloatArray(3) + val r = value.red * 255f + val g = value.green * 255f + val b = value.blue * 255f + AndroidColor.RGBToHSV(r.toInt(), g.toInt(), b.toInt(), hsv) + hsv + } + Column(modifier = modifier) { + BoxWithConstraints( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 32.dp) + .aspectRatio(1f) + ) { + val width = this.maxWidth + val height = this.maxHeight + Canvas( + modifier = Modifier + .fillMaxSize() + .pointerInput(Unit) { + detectDragGestures( + onDrag = { change, it -> + val (x, y) = change.position + val angle = atan2( + y.toDouble() - height.toPx() / 2, + x.toDouble() - width.toPx() / 2, + ) + val h = (Math.toDegrees(angle) + 360f) % 360f + onValueChanged(Color.hsv(h.toFloat(), sat, vl)) + } + ) + } + .pointerInput(Unit) { + detectTapGestures { + val (x, y) = it + val angle = atan2( + y.toDouble() - height.toPx() / 2, + x.toDouble() - width.toPx() / 2, + ) + val h = (Math.toDegrees(angle) + 360f) % 360f + onValueChanged(Color.hsv(h.toFloat(), sat, vl)) + + } + } + .padding(8.dp) + ) { + drawCircle( + brush = Brush.sweepGradient( + colors = listOf( + Color.Red, + Color.Yellow, + Color.Green, + Color.Cyan, + Color.Blue, + Color.Magenta, + Color.Red + ) + ), + style = Stroke(20.dp.toPx()) + ) + drawCircle( + color = value, + style = Fill, + center = center, + radius = size.minDimension / 2 - 18.dp.toPx() + ) + } + Box( + modifier = Modifier + .fillMaxSize() + .rotate(hue), + ) { + Box( + modifier = Modifier + .size(16.dp) + .shadow(1.dp, CircleShape) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surface) + .align(AbsoluteAlignment.CenterRight) + ) + } + } + Slider( + modifier = Modifier.padding(top = 16.dp), + value = sat, + onValueChange = { + onValueChanged(Color.hsv(hue, it, vl)) + }, + track = { + Canvas( + modifier = Modifier + .fillMaxWidth() + .height(20.dp) + ) { + drawRoundRect( + brush = Brush.horizontalGradient( + colors = listOf( + Color.hsv(hue, 0f, 1f), + Color.hsv(hue, 1f, 1f) + ) + ), + style = Fill, + cornerRadius = CornerRadius(10.dp.toPx(), 10.dp.toPx()) + ) + } + }, + thumb = { + Box( + modifier = Modifier + .padding(vertical = 2.dp, horizontal = 8.dp) + .size(16.dp) + .shadow(1.dp, CircleShape) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surface) + ) + } + ) + Slider( + modifier = Modifier, + value = vl, + onValueChange = { + onValueChanged(Color.hsv(hue, sat, it)) + }, + track = { + Canvas( + modifier = Modifier + .fillMaxWidth() + .height(20.dp) + ) { + drawRoundRect( + brush = Brush.horizontalGradient( + colors = listOf( + Color.hsv(hue, sat, 0f), + Color.hsv(hue, sat, 1f) + ) + ), + style = Fill, + cornerRadius = CornerRadius(10.dp.toPx(), 10.dp.toPx()) + ) + } + }, + thumb = { + Box( + modifier = Modifier + .padding(vertical = 2.dp, horizontal = 8.dp) + .size(16.dp) + .shadow(1.dp, CircleShape) + .clip(CircleShape) + .background(MaterialTheme.colorScheme.surface) + ) + } + ) + + var hexValue by remember(value) { + mutableStateOf( + value.toHexString().substring(1) + ) + } + + OutlinedTextField( + modifier = Modifier.padding(top = 16.dp).padding(horizontal = 16.dp), + value = hexValue, + onValueChange = { + if (Regex("[0-9a-fA-F]{0,6}").matches(it)) { + hexValue = it + if (it.length == 6) { + val hex = it.toIntOrNull(16) ?: return@OutlinedTextField + val color = Color(hex).copy(alpha = 1f) + onValueChanged(color) + } + } + }, + prefix = { + Text( + text = "#", + ) + } + ) + } + +} \ No newline at end of file diff --git a/app/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ColorPreference.kt b/app/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ColorPreference.kt index d20db0de..a0d3b9ab 100644 --- a/app/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ColorPreference.kt +++ b/app/ui/src/main/java/de/mm20/launcher2/ui/component/preferences/ColorPreference.kt @@ -8,7 +8,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp -import com.godaddy.android.colorpicker.ClassicColorPicker +import de.mm20.launcher2.ui.component.colorpicker.ColorPicker import de.mm20.launcher2.ui.ktx.toHexString @Composable @@ -51,35 +51,9 @@ fun ColorPreference( Column( modifier = Modifier.fillMaxWidth() ) { - - ClassicColorPicker( - color = value ?: Color.Black, - onColorChanged = { - color = it.toColor() - }, - showAlphaBar = false, - modifier = Modifier - .fillMaxWidth() - .height(200.dp) - ) - - var hexValue by remember(color) { - mutableStateOf( - color?.toHexString() ?: "#000000" - ) - } - - TextField( - modifier = Modifier.padding(top = 16.dp), - value = hexValue, - onValueChange = { - hexValue = it - if (Regex("#[0-9a-fA-F]{6}").matches(it)) { - val hex = it.substring(1).toIntOrNull(16) ?: return@TextField - color = Color(hex).copy(alpha = 1f) - } - } - ) + ColorPicker(value = color ?: Color.Black, onValueChanged = { + color = it + }) } }, confirmButton = { diff --git a/core/base/src/main/java/de/mm20/launcher2/licenses/OpenSourceLicenses.kt b/core/base/src/main/java/de/mm20/launcher2/licenses/OpenSourceLicenses.kt index 3da80038..ab77c2f5 100644 --- a/core/base/src/main/java/de/mm20/launcher2/licenses/OpenSourceLicenses.kt +++ b/core/base/src/main/java/de/mm20/launcher2/licenses/OpenSourceLicenses.kt @@ -169,14 +169,6 @@ val OpenSourceLicenses = arrayOf( licenseText = R.raw.license_apache_2, url = "https://coil-kt.github.io/coil/" ), - OpenSourceLibrary( - name = "compose-color-picker", - description = "A component that provides an HSV color picker, written in Jetpack compose.", - copyrightNote = "Copyright (c) 2021GoDaddy Operating Company, LLC.", - licenseName = R.string.mit_license_name, - licenseText = R.raw.license_mit, - url = "https://github.com/godaddy/compose-color-picker" - ), OpenSourceLibrary( name = "Apache Commons Text", description = "Apache Commons Text is a library focused on algorithms working on strings. ", diff --git a/settings.gradle.kts b/settings.gradle.kts index f3e67b5e..97e7324a 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -186,9 +186,6 @@ dependencyResolutionManagement { library("coil.compose", "io.coil-kt", "coil-compose") .versionRef("coil") - library("composecolorpicker", "com.godaddy.android.colorpicker", "compose-color-picker") - .version("0.7.0") - library("leakcanary", "com.squareup.leakcanary", "leakcanary-android") .version("2.10")