Skip to content

Commit a6f73c3

Browse files
committed
library: ColorPicker: Add enableHapticEffect param
1 parent 50280e1 commit a6f73c3

File tree

1 file changed

+44
-14
lines changed

1 file changed

+44
-14
lines changed

miuix/src/commonMain/kotlin/top/yukonga/miuix/kmp/basic/ColorPicker.kt

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,11 @@ import androidx.compose.ui.graphics.Brush
2727
import androidx.compose.ui.graphics.Color
2828
import androidx.compose.ui.graphics.TileMode
2929
import androidx.compose.ui.graphics.drawscope.DrawScope
30+
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
3031
import androidx.compose.ui.input.pointer.pointerInput
3132
import androidx.compose.ui.layout.onGloballyPositioned
3233
import androidx.compose.ui.platform.LocalDensity
34+
import androidx.compose.ui.platform.LocalHapticFeedback
3335
import androidx.compose.ui.unit.Dp
3436
import androidx.compose.ui.unit.dp
3537
import top.yukonga.miuix.kmp.theme.MiuixTheme
@@ -42,13 +44,15 @@ import top.yukonga.miuix.kmp.utils.SmoothRoundedCornerShape
4244
* @param initialColor The initial color of the picker.
4345
* @param onColorChanged The callback to be called when the color changes.
4446
* @param showPreview Whether to show the color preview.
47+
* @param enableHapticEffect Whether to enable haptic feedback.
4548
* @param modifier The modifier to be applied to the color picker.
4649
*/
4750
@Composable
4851
fun ColorPicker(
4952
initialColor: Color = MiuixTheme.colorScheme.primary,
5053
onColorChanged: (Color) -> Unit = {},
5154
showPreview: Boolean = true,
55+
enableHapticEffect: Boolean = true,
5256
modifier: Modifier = Modifier
5357
) {
5458
var initialSetup by remember { mutableStateOf(true) }
@@ -102,7 +106,8 @@ fun ColorPicker(
102106
// Hue selection
103107
HueSlider(
104108
currentHue = currentHue,
105-
onHueChanged = { newHue -> currentHue = newHue * 360f }
109+
onHueChanged = { newHue -> currentHue = newHue * 360f },
110+
enableHapticEffect = enableHapticEffect
106111
)
107112

108113
Spacer(modifier = Modifier.height(12.dp))
@@ -111,7 +116,8 @@ fun ColorPicker(
111116
SaturationSlider(
112117
currentHue = currentHue,
113118
currentSaturation = currentSaturation,
114-
onSaturationChanged = { currentSaturation = it }
119+
onSaturationChanged = { currentSaturation = it },
120+
enableHapticEffect = enableHapticEffect
115121
)
116122

117123
Spacer(modifier = Modifier.height(12.dp))
@@ -121,7 +127,8 @@ fun ColorPicker(
121127
currentHue = currentHue,
122128
currentSaturation = currentSaturation,
123129
currentValue = currentValue,
124-
onValueChanged = { currentValue = it }
130+
onValueChanged = { currentValue = it },
131+
enableHapticEffect = enableHapticEffect
125132
)
126133

127134
Spacer(modifier = Modifier.height(12.dp))
@@ -132,7 +139,8 @@ fun ColorPicker(
132139
currentSaturation = currentSaturation,
133140
currentValue = currentValue,
134141
currentAlpha = currentAlpha,
135-
onAlphaChanged = { currentAlpha = it }
142+
onAlphaChanged = { currentAlpha = it },
143+
enableHapticEffect = enableHapticEffect
136144
)
137145
}
138146

@@ -141,18 +149,21 @@ fun ColorPicker(
141149
*
142150
* @param currentHue The current hue value.
143151
* @param onHueChanged The callback to be called when the hue changes.
152+
* @param enableHapticEffect Whether to enable haptic feedback.
144153
*/
145154
@Composable
146155
fun HueSlider(
147156
currentHue: Float,
148157
onHueChanged: (Float) -> Unit,
158+
enableHapticEffect: Boolean = true
149159
) {
150160
val hueColors = List(36) { i -> Color.hsv(i * 10f, 1f, 1f) }
151161
ColorSlider(
152162
value = currentHue / 360f,
153163
onValueChanged = onHueChanged,
154164
drawBrushColors = hueColors,
155-
modifier = Modifier.fillMaxWidth()
165+
modifier = Modifier.fillMaxWidth(),
166+
enableHapticEffect = enableHapticEffect
156167
)
157168
}
158169

@@ -162,18 +173,21 @@ fun HueSlider(
162173
* @param currentHue The current hue value.
163174
* @param currentSaturation The current saturation value.
164175
* @param onSaturationChanged The callback to be called when the saturation changes.
176+
* @param enableHapticEffect Whether to enable haptic feedback.
165177
*/
166178
@Composable
167179
fun SaturationSlider(
168180
currentHue: Float,
169181
currentSaturation: Float,
170182
onSaturationChanged: (Float) -> Unit,
183+
enableHapticEffect: Boolean = true
171184
) {
172185
ColorSlider(
173186
value = currentSaturation,
174187
onValueChanged = onSaturationChanged,
175188
drawBrushColors = listOf(Color.hsv(currentHue, 0f, 1f, 1f), Color.hsv(currentHue, 1f, 1f, 1f)),
176-
modifier = Modifier.fillMaxWidth()
189+
modifier = Modifier.fillMaxWidth(),
190+
enableHapticEffect = enableHapticEffect
177191
)
178192
}
179193

@@ -185,19 +199,22 @@ fun SaturationSlider(
185199
* @param currentSaturation The current saturation value.
186200
* @param currentValue The current value value.
187201
* @param onValueChanged The callback to be called when the value changes.
202+
* @param enableHapticEffect Whether to enable haptic feedback.
188203
*/
189204
@Composable
190205
fun ValueSlider(
191206
currentHue: Float,
192207
currentSaturation: Float,
193208
currentValue: Float,
194209
onValueChanged: (Float) -> Unit,
210+
enableHapticEffect: Boolean = true
195211
) {
196212
ColorSlider(
197213
value = currentValue,
198214
onValueChanged = onValueChanged,
199215
drawBrushColors = listOf(Color.Black, Color.hsv(currentHue, currentSaturation, 1f)),
200-
modifier = Modifier.fillMaxWidth()
216+
modifier = Modifier.fillMaxWidth(),
217+
enableHapticEffect = enableHapticEffect
201218
)
202219
}
203220

@@ -209,6 +226,7 @@ fun ValueSlider(
209226
* @param currentValue The current value value.
210227
* @param currentAlpha The current alpha value.
211228
* @param onAlphaChanged The callback to be called when the alpha changes.
229+
* @param enableHapticEffect Whether to enable haptic feedback.
212230
*/
213231
@Composable
214232
fun AlphaSlider(
@@ -217,6 +235,7 @@ fun AlphaSlider(
217235
currentValue: Float,
218236
currentAlpha: Float,
219237
onAlphaChanged: (Float) -> Unit,
238+
enableHapticEffect: Boolean = true
220239
) {
221240
val baseColor = Color.hsv(currentHue, currentSaturation, currentValue)
222241
val startColor = remember(baseColor) { baseColor.copy(alpha = 0f) }
@@ -258,6 +277,7 @@ fun AlphaSlider(
258277
onValueChanged = onAlphaChanged,
259278
drawBrushColors = listOf(startColor, endColor),
260279
modifier = Modifier.fillMaxWidth(),
280+
enableHapticEffect = enableHapticEffect,
261281
drawBackground = checkerBrush::draw
262282
)
263283
}
@@ -271,6 +291,7 @@ private fun ColorSlider(
271291
onValueChanged: (Float) -> Unit,
272292
drawBrushColors: List<Color>,
273293
modifier: Modifier = Modifier,
294+
enableHapticEffect: Boolean = true,
274295
drawBackground: (DrawScope.(width: Float, height: Float) -> Unit)? = null
275296
) {
276297
val density = LocalDensity.current
@@ -281,10 +302,19 @@ private fun ColorSlider(
281302
val borderShape = remember { SmoothRoundedCornerShape(50.dp) }
282303
val borderStroke = remember { 0.5.dp }
283304
val borderColor = remember { Color.Gray.copy(0.1f) }
305+
val hapticFeedback = LocalHapticFeedback.current
306+
var lastHapticStep by remember { mutableStateOf(0f) }
284307

285308
val dragHandler = remember(onValueChanged, sliderSizePx) {
286309
{ posX: Float, width: Float ->
287-
handleSliderInteraction(posX, width, sliderSizePx, onValueChanged)
310+
handleSliderInteraction(posX, width, sliderSizePx).coerceIn(0f, 1f)
311+
}
312+
}
313+
314+
fun performHapticFeedbackIfNeeded(valueChanged: Float) {
315+
if (enableHapticEffect && valueChanged != lastHapticStep) {
316+
hapticFeedback.performHapticFeedback(HapticFeedbackType.TextHandleMove)
317+
lastHapticStep = valueChanged
288318
}
289319
}
290320

@@ -303,7 +333,9 @@ private fun ColorSlider(
303333
.pointerInput(dragHandler) {
304334
detectHorizontalDragGestures { change, _ ->
305335
change.consume()
306-
dragHandler(change.position.x, size.width.toFloat())
336+
val currentValue = dragHandler(change.position.x, size.width.toFloat())
337+
onValueChanged(currentValue)
338+
performHapticFeedbackIfNeeded(currentValue)
307339
}
308340
}
309341
) {
@@ -329,7 +361,6 @@ private fun ColorSlider(
329361
}
330362
}
331363

332-
333364
@Composable
334365
private fun SliderIndicator(
335366
modifier: Modifier,
@@ -360,12 +391,11 @@ private fun SliderIndicator(
360391
private fun handleSliderInteraction(
361392
positionX: Float,
362393
totalWidth: Float,
363-
sliderSizePx: Float,
364-
onValueChanged: (Float) -> Unit
365-
) {
394+
sliderSizePx: Float
395+
): Float {
366396
val sliderHalfSizePx = sliderSizePx / 2
367397
val effectiveWidth = totalWidth - sliderSizePx
368398
val constrainedX = positionX.coerceIn(sliderHalfSizePx, totalWidth - sliderHalfSizePx)
369399
val newPosition = (constrainedX - sliderHalfSizePx) / effectiveWidth
370-
onValueChanged(newPosition.coerceIn(0f, 1f))
400+
return newPosition.coerceIn(0f, 1f)
371401
}

0 commit comments

Comments
 (0)