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