Skip to content

Commit 5a82782

Browse files
remove box wrapping ColorfulIconSlider's SubcomposeLayout
1 parent efe1441 commit 5a82782

File tree

1 file changed

+101
-100
lines changed

1 file changed

+101
-100
lines changed

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

Lines changed: 101 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ package com.smarttoolfactory.slider
22

33
import androidx.compose.foundation.BorderStroke
44
import androidx.compose.foundation.Canvas
5-
import androidx.compose.foundation.layout.*
5+
import androidx.compose.foundation.layout.Box
6+
import androidx.compose.foundation.layout.fillMaxSize
7+
import androidx.compose.foundation.layout.offset
8+
import androidx.compose.foundation.layout.requiredSizeIn
69
import androidx.compose.runtime.Composable
710
import androidx.compose.runtime.mutableStateOf
811
import androidx.compose.runtime.remember
@@ -20,10 +23,7 @@ import androidx.compose.ui.layout.Placeable
2023
import androidx.compose.ui.layout.SubcomposeLayout
2124
import androidx.compose.ui.platform.LocalDensity
2225
import androidx.compose.ui.platform.LocalLayoutDirection
23-
import androidx.compose.ui.unit.Dp
24-
import androidx.compose.ui.unit.IntOffset
25-
import androidx.compose.ui.unit.IntSize
26-
import androidx.compose.ui.unit.LayoutDirection
26+
import androidx.compose.ui.unit.*
2727
import com.smarttoolfactory.slider.gesture.pointerMotionEvents
2828

2929
/**
@@ -137,113 +137,110 @@ fun ColorfulIconSlider(
137137
thumb: @Composable () -> Unit
138138
) {
139139

140-
SliderComposeLayout(thumb = { thumb() }) { thumbSize ->
140+
SliderComposeLayout(
141+
modifier = modifier
142+
.minimumTouchTargetSize()
143+
.requiredSizeIn(
144+
minWidth = ThumbRadius * 2,
145+
minHeight = ThumbRadius * 2,
146+
),
147+
thumb = { thumb() }) { thumbSize: IntSize, constraints: Constraints ->
141148

142149
require(steps >= 0) { "steps should be >= 0" }
143150
val onValueChangeState = rememberUpdatedState(onValueChange)
144151
val tickFractions = remember(steps) {
145152
stepsToTickFractions(steps)
146153
}
147154

148-
BoxWithConstraints(
149-
modifier = modifier
150-
.minimumTouchTargetSize()
151-
.requiredSizeIn(
152-
minWidth = ThumbRadius * 2,
153-
minHeight = ThumbRadius * 2,
154-
),
155-
contentAlignment = Alignment.CenterStart
156-
) {
155+
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
157156

158-
val isRtl = LocalLayoutDirection.current == LayoutDirection.Rtl
157+
val width = constraints.maxWidth.toFloat()
158+
val thumbRadiusInPx = (thumbSize.width / 2).toFloat()
159159

160-
val width = constraints.maxWidth.toFloat()
161-
val thumbRadiusInPx = (thumbSize.width / 2).toFloat()
160+
// Start of the track used for measuring progress,
161+
// it's line + radius of cap which is half of height of track
162+
// to draw this on canvas starting point of line
163+
// should be at trackStart + trackHeightInPx / 2 while drawing
164+
val trackStart: Float
165+
// End of the track that is used for measuring progress
166+
val trackEnd: Float
167+
val strokeRadius: Float
168+
with(LocalDensity.current) {
162169

163-
// Start of the track used for measuring progress,
164-
// it's line + radius of cap which is half of height of track
165-
// to draw this on canvas starting point of line
166-
// should be at trackStart + trackHeightInPx / 2 while drawing
167-
val trackStart: Float
168-
// End of the track that is used for measuring progress
169-
val trackEnd: Float
170-
val strokeRadius: Float
171-
with(LocalDensity.current) {
170+
strokeRadius = trackHeight.toPx() / 2
171+
trackStart = thumbRadiusInPx.coerceAtLeast(strokeRadius)
172+
trackEnd = width - trackStart
173+
}
172174

173-
strokeRadius = trackHeight.toPx() / 2
174-
trackStart = thumbRadiusInPx.coerceAtLeast(strokeRadius)
175-
trackEnd = width - trackStart
176-
}
175+
// Sales and interpolates from offset from dragging to user value in valueRange
176+
fun scaleToUserValue(offset: Float) =
177+
scale(trackStart, trackEnd, offset, valueRange.start, valueRange.endInclusive)
177178

178-
// Sales and interpolates from offset from dragging to user value in valueRange
179-
fun scaleToUserValue(offset: Float) =
180-
scale(trackStart, trackEnd, offset, valueRange.start, valueRange.endInclusive)
179+
// Scales user value using valueRange to position on x axis on screen
180+
fun scaleToOffset(userValue: Float) =
181+
scale(valueRange.start, valueRange.endInclusive, userValue, trackStart, trackEnd)
181182

182-
// Scales user value using valueRange to position on x axis on screen
183-
fun scaleToOffset(userValue: Float) =
184-
scale(valueRange.start, valueRange.endInclusive, userValue, trackStart, trackEnd)
183+
val rawOffset = remember { mutableStateOf(scaleToOffset(value)) }
185184

186-
val rawOffset = remember { mutableStateOf(scaleToOffset(value)) }
185+
CorrectValueSideEffect(
186+
::scaleToOffset,
187+
valueRange,
188+
trackStart..trackEnd,
189+
rawOffset,
190+
value
191+
)
187192

188-
CorrectValueSideEffect(
189-
::scaleToOffset,
190-
valueRange,
191-
trackStart..trackEnd,
192-
rawOffset,
193-
value
194-
)
193+
val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)
194+
val fraction = calculateFraction(valueRange.start, valueRange.endInclusive, coerced)
195+
196+
val dragModifier = Modifier.pointerMotionEvents(
197+
onDown = {
198+
if (enabled) {
199+
rawOffset.value = if (!isRtl) it.position.x else trackEnd - it.position.x
200+
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
201+
onValueChangeState.value.invoke(
202+
scaleToUserValue(offsetInTrack),
203+
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
204+
)
205+
it.consumeDownChange()
206+
}
207+
},
208+
onMove = {
209+
if (enabled) {
210+
rawOffset.value = if (!isRtl) it.position.x else trackEnd - it.position.x
211+
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
212+
onValueChangeState.value.invoke(
213+
scaleToUserValue(offsetInTrack),
214+
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
215+
)
216+
it.consumePositionChange()
217+
}
195218

196-
val coerced = value.coerceIn(valueRange.start, valueRange.endInclusive)
197-
val fraction = calculateFraction(valueRange.start, valueRange.endInclusive, coerced)
198-
199-
val dragModifier = Modifier.pointerMotionEvents(
200-
onDown = {
201-
if (enabled) {
202-
rawOffset.value = if (!isRtl) it.position.x else trackEnd - it.position.x
203-
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
204-
onValueChangeState.value.invoke(
205-
scaleToUserValue(offsetInTrack),
206-
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
207-
)
208-
it.consumeDownChange()
209-
}
210-
},
211-
onMove = {
212-
if (enabled) {
213-
rawOffset.value = if (!isRtl) it.position.x else trackEnd - it.position.x
214-
val offsetInTrack = rawOffset.value.coerceIn(trackStart, trackEnd)
215-
onValueChangeState.value.invoke(
216-
scaleToUserValue(offsetInTrack),
217-
Offset(rawOffset.value.coerceIn(trackStart, trackEnd), strokeRadius)
218-
)
219-
it.consumePositionChange()
220-
}
221-
222-
},
223-
onUp = {
224-
if (enabled) {
225-
onValueChangeFinished?.invoke()
226-
it.consumeDownChange()
227-
}
219+
},
220+
onUp = {
221+
if (enabled) {
222+
onValueChangeFinished?.invoke()
223+
it.consumeDownChange()
228224
}
229-
)
225+
}
226+
)
227+
228+
IconSliderImpl(
229+
enabled = enabled,
230+
fraction = fraction,
231+
trackStart = trackStart,
232+
trackEnd = trackEnd,
233+
tickFractions = tickFractions,
234+
colors = colors,
235+
trackHeight = trackHeight,
236+
thumbRadius = thumbRadiusInPx,
237+
thumb = thumb,
238+
coerceThumbInTrack = coerceThumbInTrack,
239+
drawInactiveTrack = drawInactiveTrack,
240+
borderStroke = borderStroke,
241+
modifier = dragModifier
242+
)
230243

231-
IconSliderImpl(
232-
enabled = enabled,
233-
fraction = fraction,
234-
trackStart = trackStart,
235-
trackEnd = trackEnd,
236-
tickFractions = tickFractions,
237-
colors = colors,
238-
trackHeight = trackHeight,
239-
thumbRadius = thumbRadiusInPx,
240-
thumb = thumb,
241-
coerceThumbInTrack = coerceThumbInTrack,
242-
drawInactiveTrack = drawInactiveTrack,
243-
borderStroke = borderStroke,
244-
modifier = dragModifier
245-
)
246-
}
247244
}
248245
}
249246

@@ -437,17 +434,21 @@ private fun Track(
437434
}
438435

439436
enum class SlotsEnum {
440-
Track, Thumb
437+
Slider, Thumb
441438
}
442439

440+
/**
441+
* [SubcomposeLayout] that measure [thumb] size to set Slider's track start and track width.
442+
* @param thumb thumb Composable
443+
* @param slider Slider composable that contains **thumb** and **track** of this Slider.
444+
*/
443445
@Composable
444446
private fun SliderComposeLayout(
445447
modifier: Modifier = Modifier,
446448
thumb: @Composable () -> Unit,
447-
track: @Composable (IntSize) -> Unit
449+
slider: @Composable (IntSize, Constraints) -> Unit
448450
) {
449-
450-
SubcomposeLayout(modifier = modifier) { constraints ->
451+
SubcomposeLayout(modifier = modifier) { constraints: Constraints ->
451452

452453
// Subcompose(compose only a section) main content and get Placeable
453454
val thumbPlaceable: Placeable = subcompose(SlotsEnum.Thumb, thumb).map {
@@ -458,8 +459,8 @@ private fun SliderComposeLayout(
458459
val thumbSize = IntSize(thumbPlaceable.width, thumbPlaceable.height)
459460

460461
// Whole Slider Composable
461-
val sliderPlaceable: Placeable = subcompose(SlotsEnum.Track) {
462-
track(thumbSize)
462+
val sliderPlaceable: Placeable = subcompose(SlotsEnum.Slider) {
463+
slider(thumbSize, constraints)
463464
}.map {
464465
it.measure(constraints)
465466
}.first()

0 commit comments

Comments
 (0)