Skip to content

Commit 7adbea4

Browse files
authored
Merge pull request #8 from KvColorPalette/feature/create-new-picker-with-hue-property
Feature/create new picker with hue property
2 parents 17b05d9 + becc4fd commit 7adbea4

File tree

8 files changed

+335
-7
lines changed

8 files changed

+335
-7
lines changed

gradle/version-catalog/libs.versions.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ appcompat = "1.7.0"
1818
material = "1.12.0"
1919
composeMaterial = "1.7.6"
2020
composeNavigation = "2.8.5"
21-
kvColorPalette = "1.2.1"
21+
kvColorPalette = "2.1.0"
2222

2323
[libraries]
2424
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }

kv-color-picker/gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
kvColorPaletteGroupId=com.github.KvColorPalette
22
kvColorPickerArtifactId=KvColorPicker-Android
3-
kvColorPickerVersion=1.0.0
3+
kvColorPickerVersion=2.0.0

kv-color-picker/src/main/kotlin/com/kavi/droid/color/picker/extension/Extension.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ package com.kavi.droid.color.picker.extension
55
*
66
* @return The integer representation of the color component.
77
*/
8-
internal fun Float.toColorInt(): Int = (this * 255 + 0.5f).toInt()
8+
internal fun Float.toColorRangeInt(): Int = (this * 255 + 0.5f).toInt()

kv-color-picker/src/main/kotlin/com/kavi/droid/color/picker/ui/KvColorPickerBottomSheet.kt

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ import com.kavi.droid.color.palette.util.ColorUtil
3737
import com.kavi.droid.color.picker.R
3838
import com.kavi.droid.color.picker.ui.common.SelectedColorDetail
3939
import com.kavi.droid.color.picker.ui.pickers.GridColorPicker
40+
import com.kavi.droid.color.picker.ui.pickers.HSLAColorPicker
4041
import com.kavi.droid.color.picker.ui.pickers.RGBAColorPicker
4142

4243
/**
@@ -68,7 +69,8 @@ fun KvColorPickerBottomSheet(showSheet: MutableState<Boolean>, sheetState: Sheet
6869
var tabIndex by remember { mutableIntStateOf(0) }
6970
val tabs = listOf(
7071
stringResource(R.string.label_rgba),
71-
stringResource(R.string.label_grid)
72+
stringResource(R.string.label_grid),
73+
stringResource(R.string.label_hsla)
7274
)
7375

7476
Text(
@@ -124,6 +126,13 @@ fun KvColorPickerBottomSheet(showSheet: MutableState<Boolean>, sheetState: Sheet
124126
colorHex.value = TextFieldValue(ColorUtil.getHex(it))
125127
}
126128
)
129+
2 -> HSLAColorPicker(
130+
modifier = Modifier.padding(16.dp),
131+
onColorSelected = {
132+
selectedColor = it
133+
colorHex.value = TextFieldValue(ColorUtil.getHex(it))
134+
}
135+
)
127136
}
128137

129138
Column (

kv-color-picker/src/main/kotlin/com/kavi/droid/color/picker/ui/common/CommonUI.kt

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@ import androidx.compose.ui.res.stringResource
3535
import androidx.compose.ui.text.input.TextFieldValue
3636
import androidx.compose.ui.text.style.TextAlign
3737
import androidx.compose.ui.unit.dp
38+
import androidx.compose.ui.unit.sp
3839
import com.kavi.droid.color.palette.KvColorPalette
3940
import com.kavi.droid.color.palette.model.KvColor
4041
import com.kavi.droid.color.palette.util.ColorUtil
4142
import com.kavi.droid.color.picker.R
42-
import com.kavi.droid.color.picker.extension.toColorInt
43+
import com.kavi.droid.color.picker.extension.toColorRangeInt
4344

4445
/**
4546
* A composable function that creates a slider for adjusting a float value associated with a color.
@@ -63,6 +64,7 @@ internal fun ColorSlider(colorLabel: String, colorValueState: MutableState<Float
6364
Text(
6465
text = colorLabel,
6566
color = Color.Black,
67+
fontSize = 14.sp,
6668
modifier = Modifier.weight(.2f)
6769
)
6870
Slider(
@@ -75,7 +77,7 @@ internal fun ColorSlider(colorLabel: String, colorValueState: MutableState<Float
7577
modifier = Modifier.weight(.8f)
7678
)
7779
Text(
78-
text = colorValueState.value.toColorInt().toString(),
80+
text = colorValueState.value.toColorRangeInt().toString(),
7981
modifier = Modifier
8082
.width(25.dp)
8183
.weight(.1f),
@@ -105,8 +107,9 @@ internal fun AlphaSlider(alphaValueState: MutableState<Float>, color: Color) {
105107
horizontalArrangement = Arrangement.spacedBy(6.dp)
106108
) {
107109
Text(
108-
text = "ALPHA",
110+
text = "Alpha",
109111
color = Color.Black,
112+
fontSize = 12.sp,
110113
modifier = Modifier.weight(.2f)
111114
)
112115
Slider(
@@ -131,6 +134,53 @@ internal fun AlphaSlider(alphaValueState: MutableState<Float>, color: Color) {
131134
}
132135
}
133136

137+
/**
138+
* A composable function that creates a slider for adjusting a float value associated with a color alpha valuw.
139+
*
140+
* @param label: String: The label to display alongside the slider.
141+
* @param valueState: MutableState<Float>: The mutable state holding the current value of the slider.
142+
* @param color: Color: The color used for the active track of the slider.
143+
*
144+
* @return @Composable: A slider UI for alpha selection.
145+
*/
146+
@Composable
147+
internal fun ColorSaturationAndLightnessSlider(label: String, valueState: MutableState<Float>, color: Color) {
148+
/**
149+
* Displays a slider for adjusting the given [valueState] associated with the provided [label].
150+
* The slider's active track color is set to [color].
151+
*/
152+
Row(
153+
verticalAlignment = Alignment.CenterVertically,
154+
horizontalArrangement = Arrangement.spacedBy(6.dp)
155+
) {
156+
Text(
157+
text = label,
158+
color = Color.Black,
159+
fontSize = 12.sp,
160+
modifier = Modifier.weight(.2f)
161+
)
162+
Slider(
163+
value = valueState.value,
164+
onValueChange = valueState.component2(),
165+
colors = SliderDefaults.colors(
166+
thumbColor = MaterialTheme.colorScheme.primary,
167+
activeTrackColor = color
168+
),
169+
valueRange = 0f..1f,
170+
modifier = Modifier.weight(.8f),
171+
)
172+
Text(
173+
text = DecimalFormat("#.##").format(valueState.value).toString(),
174+
modifier = Modifier
175+
.width(25.dp)
176+
.weight(.1f),
177+
textAlign = TextAlign.End,
178+
style = MaterialTheme.typography.bodySmall,
179+
color = Color.Black
180+
)
181+
}
182+
}
183+
134184
/**
135185
* A composable function that displays the selected color and its details.
136186
*
Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
package com.kavi.droid.color.picker.ui.common
2+
3+
import android.graphics.Bitmap
4+
import android.graphics.Paint
5+
import android.graphics.RectF
6+
import androidx.compose.foundation.Canvas
7+
import androidx.compose.foundation.background
8+
import androidx.compose.foundation.border
9+
import androidx.compose.foundation.layout.Box
10+
import androidx.compose.foundation.layout.fillMaxWidth
11+
import androidx.compose.foundation.layout.height
12+
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.width
14+
import androidx.compose.foundation.shape.RoundedCornerShape
15+
import androidx.compose.material3.ExperimentalMaterial3Api
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.Slider
18+
import androidx.compose.material3.SliderDefaults
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.LaunchedEffect
21+
import androidx.compose.runtime.MutableState
22+
import androidx.compose.runtime.mutableFloatStateOf
23+
import androidx.compose.runtime.mutableStateOf
24+
import androidx.compose.runtime.remember
25+
import androidx.compose.runtime.saveable.rememberSaveable
26+
import androidx.compose.ui.Alignment
27+
import androidx.compose.ui.Modifier
28+
import androidx.compose.ui.draw.clip
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.graphics.drawscope.DrawScope
31+
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
32+
import androidx.compose.ui.graphics.nativeCanvas
33+
import androidx.compose.ui.graphics.toArgb
34+
import androidx.compose.ui.tooling.preview.Preview
35+
import androidx.compose.ui.unit.dp
36+
import androidx.core.graphics.toRect
37+
import android.graphics.Color as AndroidColor
38+
39+
@OptIn(ExperimentalMaterial3Api::class)
40+
@Composable
41+
internal fun SliderHue(modifier: Modifier, onColorSelect: (color: Color) -> Unit) {
42+
val hueValueState = rememberSaveable { mutableFloatStateOf(0f) }
43+
val huePanel = rememberSaveable { mutableStateOf(RectF()) }
44+
val hueColors = rememberSaveable { mutableStateOf(IntArray(0)) }
45+
46+
val hsv = remember {
47+
val hsv = floatArrayOf(0f, 0f, 0f)
48+
AndroidColor.colorToHSV(Color.Blue.toArgb(), hsv)
49+
50+
mutableStateOf(
51+
Triple(hsv[0], hsv[1], hsv[2])
52+
)
53+
}
54+
55+
// Launch an effect to invoke the provided callback with the selected color
56+
LaunchedEffect(hueValueState.floatValue) {
57+
val selectedHue = pointToHue(hueValueState.floatValue, huePanel.value)
58+
hsv.value = Triple(selectedHue, hsv.value.second, hsv.value.third)
59+
60+
val generatedColor = Color.hsv(hsv.value.first, hsv.value.second, hsv.value.third)
61+
onColorSelect.invoke(generatedColor)
62+
}
63+
64+
Box (modifier = modifier
65+
.fillMaxWidth()
66+
) {
67+
HuePanel(hueColors, huePanel)
68+
69+
Slider(
70+
modifier = Modifier
71+
.fillMaxWidth()
72+
.align(Alignment.Center),
73+
valueRange = 0f..(hueColors.value.size).toFloat(),
74+
value = hueValueState.floatValue,
75+
onValueChange = hueValueState.component2(),
76+
colors = SliderDefaults.colors(
77+
thumbColor = MaterialTheme.colorScheme.primary,
78+
activeTrackColor = Color.Transparent,
79+
inactiveTrackColor = Color.Transparent
80+
),
81+
thumb = {
82+
Box(
83+
modifier = Modifier
84+
.height(45.dp)
85+
.width(10.dp)
86+
.background(Color.Transparent, shape = MaterialTheme.shapes.large)
87+
.border(
88+
2.dp,
89+
Color.Gray,
90+
RoundedCornerShape(12.dp)
91+
)
92+
)
93+
}
94+
)
95+
}
96+
}
97+
98+
@Composable
99+
private fun HuePanel(hueColors: MutableState<IntArray>, huePanel: MutableState<RectF>) {
100+
Canvas(
101+
modifier = Modifier
102+
.padding(top = 1.dp)
103+
.height(45.dp)
104+
.fillMaxWidth()
105+
.clip(RoundedCornerShape(12))
106+
) {
107+
val bitmap = Bitmap.createBitmap(size.width.toInt(), size.height.toInt(), Bitmap.Config.ARGB_8888)
108+
val hueCanvas = android.graphics.Canvas(bitmap)
109+
huePanel.value = RectF(0f, 0f, bitmap.width.toFloat(), bitmap.height.toFloat())
110+
hueColors.value = IntArray((huePanel.value.width()).toInt())
111+
var hue = 0f
112+
for (i in hueColors.value.indices) {
113+
hueColors.value[i] = AndroidColor.HSVToColor(floatArrayOf(hue, 1f, 1f))
114+
hue += 360f / hueColors.value.size
115+
}
116+
val linePaint = Paint()
117+
linePaint.strokeWidth = 0f
118+
for(t in hueColors.value.indices) {
119+
linePaint.color = hueColors.value[t]
120+
hueCanvas.drawLine(t.toFloat(), 0f, t.toFloat(), huePanel.value.bottom, linePaint)
121+
}
122+
123+
drawBitmap(bitmap = bitmap, panel = huePanel.value)
124+
}
125+
}
126+
127+
private fun pointToHue(pointX: Float, huePanel: RectF): Float {
128+
val width = huePanel.width()
129+
val x = when {
130+
pointX < huePanel.left -> 0f
131+
pointX > huePanel.right -> width
132+
else -> pointX - huePanel.left
133+
}
134+
return x * 360 / width
135+
}
136+
137+
private fun DrawScope.drawBitmap(
138+
bitmap: Bitmap,
139+
panel: RectF
140+
) {
141+
drawIntoCanvas {
142+
it.nativeCanvas.drawBitmap(
143+
bitmap,
144+
null,
145+
panel.toRect(),
146+
null
147+
)
148+
}
149+
}
150+
151+
@Preview
152+
@Composable
153+
fun HuePanel_Preview() {
154+
SliderHue(Modifier, {})
155+
}

0 commit comments

Comments
 (0)