@@ -21,9 +21,13 @@ import androidx.compose.runtime.setValue
2121import androidx.compose.ui.Alignment
2222import androidx.compose.ui.Modifier
2323import androidx.compose.ui.draw.clip
24+ import androidx.compose.ui.geometry.Offset
25+ import androidx.compose.ui.geometry.Size
2426import androidx.compose.ui.graphics.Brush
2527import androidx.compose.ui.graphics.Color
2628import androidx.compose.ui.graphics.drawscope.DrawScope
29+ import androidx.compose.ui.graphics.drawscope.scale
30+ import androidx.compose.ui.graphics.drawscope.translate
2731import androidx.compose.ui.input.pointer.pointerInput
2832import androidx.compose.ui.layout.onGloballyPositioned
2933import androidx.compose.ui.platform.LocalDensity
@@ -52,7 +56,10 @@ fun ColorPicker(
5256 var currentValue by remember { mutableStateOf(0f ) }
5357 var currentAlpha by remember { mutableStateOf(1f ) }
5458
55- // Set initial HSV values only once
59+ val selectedColor = remember(currentHue, currentSaturation, currentValue, currentAlpha) {
60+ Color .hsv(currentHue, currentSaturation, currentValue, currentAlpha)
61+ }
62+
5663 LaunchedEffect (initialColor, initialSetup) {
5764 if (initialSetup) {
5865 val hsv = FloatArray (3 )
@@ -70,14 +77,9 @@ fun ColorPicker(
7077 }
7178 }
7279
73- // Current selected color
74- val selectedColor = Color .hsv(currentHue, currentSaturation, currentValue, currentAlpha)
75-
76- // Track previous color to prevent recomposition loops
7780 var previousColor by remember { mutableStateOf(selectedColor) }
7881
79- // Only trigger callback when colors actually change from user interaction
80- LaunchedEffect (currentHue, currentSaturation, currentValue, currentAlpha) {
82+ LaunchedEffect (selectedColor) {
8183 if (! initialSetup && selectedColor != previousColor) {
8284 previousColor = selectedColor
8385 onColorChanged(selectedColor)
@@ -89,7 +91,7 @@ fun ColorPicker(
8991 modifier = Modifier
9092 .fillMaxWidth()
9193 .height(26 .dp)
92- .clip(SmoothRoundedCornerShape (13 .dp))
94+ .clip(SmoothRoundedCornerShape (50 .dp))
9395 .background(selectedColor)
9496 )
9597
@@ -132,7 +134,6 @@ fun ColorPicker(
132134 )
133135}
134136
135-
136137/* *
137138 * A [HueSlider] component for selecting the hue of a color.
138139 *
@@ -148,11 +149,10 @@ fun HueSlider(
148149 value = currentHue / 360f ,
149150 onValueChanged = onHueChanged,
150151 drawBrush = {
151- val hueColors = List (36 ) { i ->
152- Color .hsv(i * 10f , 1f , 1f )
153- }
154152 drawRect(brush = Brush .horizontalGradient(hueColors))
155153 },
154+ startEdgeColor = Color .hsv(0f , 1f , 1f , 1f ),
155+ endEdgeColor = Color .hsv(359f , 1f , 1f , 1f ),
156156 modifier = Modifier .fillMaxWidth()
157157 )
158158}
@@ -182,6 +182,8 @@ fun SaturationSlider(
182182 )
183183 drawRect(brush = brush)
184184 },
185+ startEdgeColor = Color .hsv(currentHue, 0f , 1f , 1f ),
186+ endEdgeColor = Color .hsv(currentHue, 1f , 1f , 1f ),
185187 modifier = Modifier .fillMaxWidth()
186188 )
187189}
@@ -214,6 +216,8 @@ fun ValueSlider(
214216 )
215217 drawRect(brush = brush)
216218 },
219+ startEdgeColor = Color .Black ,
220+ endEdgeColor = Color .hsv(currentHue, currentSaturation, 1f , 1f ),
217221 modifier = Modifier .fillMaxWidth()
218222 )
219223}
@@ -235,19 +239,54 @@ fun AlphaSlider(
235239 currentAlpha : Float ,
236240 onAlphaChanged : (Float ) -> Unit ,
237241) {
242+ val baseColor = Color .hsv(currentHue, currentSaturation, currentValue)
243+ val startColor = remember(baseColor) { baseColor.copy(alpha = 0f ) }
244+ val endColor = remember(baseColor) { baseColor.copy(alpha = 1f ) }
245+
246+ val checkerBrush = remember {
247+ object {
248+ // Pre-computed colors
249+ val light = Color (0xFFCCCCCC )
250+ val dark = Color (0xFFAAAAAA )
251+ val checkerSize = 3 .dp
252+
253+ fun draw (drawScope : DrawScope , width : Float , height : Float ) {
254+ with (drawScope) {
255+ val pixelSize = checkerSize.toPx()
256+ val horizontalCount = (width / pixelSize).toInt() + 1
257+ val verticalCount = (height / pixelSize).toInt() + 1
258+
259+ drawRect(light)
260+
261+ for (y in 0 until verticalCount) {
262+ val isEvenRow = y % 2 == 0
263+ val startX = if (isEvenRow) 0 else 1
264+
265+ for (x in startX until horizontalCount step 2 ) {
266+ drawRect(
267+ color = dark,
268+ topLeft = Offset (x * pixelSize, y * pixelSize),
269+ size = Size (pixelSize, pixelSize)
270+ )
271+ }
272+ }
273+ }
274+ }
275+ }
276+ }
277+
238278 ColorSlider (
239279 value = currentAlpha,
240280 onValueChanged = onAlphaChanged,
241281 drawBrush = {
242- val brush = Brush .horizontalGradient(
243- colors = listOf (
244- Color .hsv(currentHue, currentSaturation, currentValue, 0f ),
245- Color .hsv(currentHue, currentSaturation, currentValue, 1f )
246- )
282+ drawRect(
283+ brush = Brush .horizontalGradient(listOf (startColor, endColor))
247284 )
248- drawRect(brush = brush)
249285 },
250- modifier = Modifier .fillMaxWidth()
286+ startEdgeColor = startColor,
287+ endEdgeColor = endColor,
288+ modifier = Modifier .fillMaxWidth(),
289+ drawBackground = checkerBrush::draw
251290 )
252291}
253292
@@ -259,36 +298,76 @@ private fun ColorSlider(
259298 value : Float ,
260299 onValueChanged : (Float ) -> Unit ,
261300 drawBrush : DrawScope .() -> Unit ,
262- modifier : Modifier = Modifier
301+ startEdgeColor : Color ,
302+ endEdgeColor : Color ,
303+ modifier : Modifier = Modifier ,
304+ drawBackground : (DrawScope .(width: Float , height: Float ) -> Unit )? = null
263305) {
264306 val density = LocalDensity .current
265307 var sliderWidth by remember { mutableStateOf(0 .dp) }
266308 val indicatorSizeDp = 20 .dp
267- val sliderSizePx = with (density) { 26 .dp.toPx() }
309+ val sliderSizePx = with (density) { remember { 26 .dp.toPx() } }
310+ val halfSliderSizePx = remember(sliderSizePx) { sliderSizePx / 2f }
311+ val borderShape = remember { SmoothRoundedCornerShape (50 .dp) }
312+ val borderStroke = remember { 0.5 .dp }
313+ val borderColor = remember { Color .Gray .copy(0.1f ) }
314+
315+ val dragHandler = remember(onValueChanged, sliderSizePx) {
316+ { posX: Float , width: Float ->
317+ handleSliderInteraction(posX, width, sliderSizePx, onValueChanged)
318+ }
319+ }
268320
269321 Box (
270322 modifier = modifier
271323 .height(26 .dp)
272- .clip(SmoothRoundedCornerShape (13 .dp))
324+ .clip(borderShape)
325+ .border(borderStroke, borderColor, borderShape)
273326 ) {
274- // Draw gradient
275327 Canvas (
276328 modifier = Modifier
277329 .fillMaxSize()
278330 .onGloballyPositioned { coordinates ->
279331 sliderWidth = with (density) { coordinates.size.width.toDp() }
280332 }
281- .pointerInput(Unit ) {
333+ .pointerInput(dragHandler ) {
282334 detectHorizontalDragGestures { change, _ ->
283335 change.consume()
284- handleSliderInteraction (change.position.x, size.width.toFloat(), sliderSizePx, onValueChanged )
336+ dragHandler (change.position.x, size.width.toFloat())
285337 }
286338 }
287339 ) {
288- drawBrush()
340+ val canvasWidth = size.width
341+ val effectiveWidth = canvasWidth - sliderSizePx
342+
343+ // Background drawing
344+ drawBackground?.invoke(this , canvasWidth, size.height)
345+
346+ // Gradient drawing with transformations
347+ translate(left = halfSliderSizePx, top = 0f ) {
348+ scale(
349+ scaleX = effectiveWidth / canvasWidth,
350+ scaleY = 1f ,
351+ pivot = Offset .Zero
352+ ) {
353+ drawBrush()
354+ }
355+ }
356+
357+ // Edge color fills
358+ drawRect(
359+ color = startEdgeColor,
360+ topLeft = Offset (0f , 0f ),
361+ size = Size (halfSliderSizePx, size.height)
362+ )
363+
364+ drawRect(
365+ color = endEdgeColor,
366+ topLeft = Offset (canvasWidth - halfSliderSizePx, 0f ),
367+ size = Size (halfSliderSizePx, size.height)
368+ )
289369 }
290370
291- // Current value indicator
292371 SliderIndicator (
293372 modifier = Modifier .align(Alignment .CenterStart ),
294373 value = value,
@@ -299,6 +378,7 @@ private fun ColorSlider(
299378 }
300379}
301380
381+
302382@Composable
303383private fun SliderIndicator (
304384 modifier : Modifier ,
@@ -337,4 +417,6 @@ private fun handleSliderInteraction(
337417 val constrainedX = positionX.coerceIn(sliderHalfSizePx, totalWidth - sliderHalfSizePx)
338418 val newPosition = (constrainedX - sliderHalfSizePx) / effectiveWidth
339419 onValueChanged(newPosition.coerceIn(0f , 1f ))
340- }
420+ }
421+
422+ private val hueColors = List (36 ) { i -> Color .hsv(i * 10f , 1f , 1f ) }
0 commit comments