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