Skip to content

Commit c018efa

Browse files
Merge remote-tracking branch 'origin/master'
2 parents 44a4802 + 3e38ec9 commit c018efa

File tree

2 files changed

+296
-101
lines changed

2 files changed

+296
-101
lines changed

slider/src/main/java/com/smarttoolfactory/slider/ColorfulIconSlider.kt

Lines changed: 80 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -2,28 +2,42 @@ package com.smarttoolfactory.slider
22

33
import androidx.compose.foundation.BorderStroke
44
import androidx.compose.foundation.Canvas
5-
import androidx.compose.foundation.gestures.detectDragGestures
6-
import androidx.compose.foundation.gestures.detectTapGestures
7-
import androidx.compose.foundation.layout.*
5+
import androidx.compose.foundation.gestures.Orientation
6+
import androidx.compose.foundation.gestures.draggable
7+
import androidx.compose.foundation.interaction.MutableInteractionSource
8+
import androidx.compose.foundation.layout.Box
9+
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.heightIn
11+
import androidx.compose.foundation.layout.offset
12+
import androidx.compose.foundation.layout.requiredSizeIn
813
import androidx.compose.material.minimumInteractiveComponentSize
914
import androidx.compose.runtime.Composable
1015
import androidx.compose.runtime.mutableStateOf
1116
import androidx.compose.runtime.remember
17+
import androidx.compose.runtime.rememberCoroutineScope
1218
import androidx.compose.runtime.rememberUpdatedState
1319
import androidx.compose.ui.Alignment
1420
import androidx.compose.ui.Modifier
1521
import androidx.compose.ui.geometry.CornerRadius
1622
import androidx.compose.ui.geometry.Offset
1723
import androidx.compose.ui.geometry.Size
18-
import androidx.compose.ui.graphics.*
24+
import androidx.compose.ui.graphics.Brush
25+
import androidx.compose.ui.graphics.Canvas
26+
import androidx.compose.ui.graphics.Color
27+
import androidx.compose.ui.graphics.PointMode
28+
import androidx.compose.ui.graphics.StrokeCap
1929
import androidx.compose.ui.graphics.drawscope.Stroke
20-
import androidx.compose.ui.input.pointer.PointerInputChange
21-
import androidx.compose.ui.input.pointer.pointerInput
2230
import androidx.compose.ui.layout.Placeable
2331
import androidx.compose.ui.layout.SubcomposeLayout
2432
import androidx.compose.ui.platform.LocalDensity
2533
import androidx.compose.ui.platform.LocalLayoutDirection
26-
import androidx.compose.ui.unit.*
34+
import androidx.compose.ui.unit.Constraints
35+
import androidx.compose.ui.unit.Dp
36+
import androidx.compose.ui.unit.IntOffset
37+
import androidx.compose.ui.unit.IntSize
38+
import androidx.compose.ui.unit.LayoutDirection
39+
import androidx.compose.ui.unit.coerceAtLeast
40+
import kotlinx.coroutines.launch
2741

2842
/**
2943
* Material Slider allows to choose height for track and thumb radius and selection between
@@ -133,6 +147,7 @@ fun ColorfulIconSlider(
133147
borderStroke: BorderStroke? = null,
134148
drawInactiveTrack: Boolean = true,
135149
coerceThumbInTrack: Boolean = false,
150+
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
136151
thumb: @Composable () -> Unit
137152
) {
138153

@@ -181,8 +196,21 @@ fun ColorfulIconSlider(
181196
fun scaleToOffset(userValue: Float) =
182197
scale(valueRange.start, valueRange.endInclusive, userValue, trackStart, trackEnd)
183198

199+
val scope = rememberCoroutineScope()
184200
val rawOffset = remember { mutableStateOf(scaleToOffset(value)) }
185-
201+
val pressOffset = remember { mutableStateOf(0f) }
202+
203+
val draggableState = remember(trackStart, trackEnd, valueRange) {
204+
SliderDraggableState {
205+
rawOffset.value = (rawOffset.value + it + pressOffset.value)
206+
pressOffset.value = 0f
207+
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
208+
onValueChangeState.value.invoke(
209+
scaleToUserValue(offsetInTrack),
210+
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
211+
)
212+
}
213+
}
186214
CorrectValueSideEffect(
187215
::scaleToOffset,
188216
valueRange,
@@ -194,43 +222,42 @@ fun ColorfulIconSlider(
194222
val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)
195223
val fraction = calculateFraction(valueRange.start, valueRange.endInclusive, coerced)
196224

197-
val dragModifier = Modifier
198-
.pointerInput(Unit) {
199-
detectDragGestures(
200-
onDrag = { change: PointerInputChange, _: Offset ->
201-
if (enabled) {
202-
rawOffset.value =
203-
if (!isRtl) change.position.x else trackEnd - change.position.x
204-
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
205-
onValueChangeState.value.invoke(
206-
scaleToUserValue(offsetInTrack),
207-
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
208-
)
209-
}
210-
211-
},
212-
onDragEnd = {
213-
if (enabled) {
214-
onValueChangeFinished?.invoke()
215-
}
216-
}
217-
)
218-
}
219-
.pointerInput(Unit) {
220-
detectTapGestures { position: Offset ->
221-
if (enabled) {
222-
rawOffset.value =
223-
if (!isRtl) position.x else trackEnd - position.x
224-
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
225-
onValueChangeState.value.invoke(
226-
scaleToUserValue(offsetInTrack),
227-
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
228-
)
229-
onValueChangeFinished?.invoke()
230-
}
225+
val gestureEndAction = rememberUpdatedState<(Float) -> Unit> { velocity: Float ->
226+
val current = rawOffset.value
227+
val target = snapValueToTick(current, tickFractions, trackStart, trackEnd)
228+
if (current != target) {
229+
scope.launch {
230+
animateToTarget(draggableState, current, target, velocity)
231+
onValueChangeFinished?.invoke()
231232
}
233+
} else if (!draggableState.isDragging) {
234+
// check ifDragging in case the change is still in progress (touch -> drag case)
235+
onValueChangeFinished?.invoke()
232236
}
233-
237+
}
238+
val press = Modifier.sliderTapModifier(
239+
draggableState = draggableState,
240+
interactionSource = interactionSource,
241+
maxPx = constraints.maxWidth.toFloat(),
242+
isRtl = isRtl,
243+
rawOffset = rawOffset,
244+
gestureEndAction = gestureEndAction,
245+
pressOffset = pressOffset,
246+
enabled = enabled
247+
)
248+
val drag = Modifier.draggable(
249+
orientation = Orientation.Horizontal,
250+
reverseDirection = isRtl,
251+
enabled = enabled,
252+
interactionSource = interactionSource,
253+
onDragStopped = { _ ->
254+
if (enabled) {
255+
onValueChangeFinished?.invoke()
256+
}
257+
},
258+
startDragImmediately = draggableState.isDragging,
259+
state = draggableState
260+
)
234261
IconSliderImpl(
235262
enabled = enabled,
236263
fraction = fraction,
@@ -245,9 +272,8 @@ fun ColorfulIconSlider(
245272
coerceThumbInTrack = coerceThumbInTrack,
246273
drawInactiveTrack = drawInactiveTrack,
247274
borderStroke = borderStroke,
248-
modifier = dragModifier
275+
modifier = press.then(drag)
249276
)
250-
251277
}
252278
}
253279

@@ -389,13 +415,15 @@ private fun Track(
389415
)
390416

391417
// InActive Track
392-
drawLine(
393-
brush = inactiveTrackColor,
394-
start = sliderStart,
395-
end = sliderEnd,
396-
strokeWidth = trackHeight,
397-
cap = StrokeCap.Round
398-
)
418+
if (drawInactiveTrack) {
419+
drawLine(
420+
brush = inactiveTrackColor,
421+
start = sliderStart,
422+
end = sliderEnd,
423+
strokeWidth = trackHeight,
424+
cap = StrokeCap.Round
425+
)
426+
}
399427

400428
// Active Track
401429
drawLine(

0 commit comments

Comments
 (0)