Skip to content

Commit

Permalink
Merge pull request #101 from Konyaco/separate_slider
Browse files Browse the repository at this point in the history
Seperate slider to `Slider` and `BasicSlider`
  • Loading branch information
Sanlorng authored Feb 10, 2025
2 parents d6335ae + 5186f6d commit 64e550c
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ fun ColorPicker(
) {}
}
val (hug, saturation, _) = spectrumColor.value.hsv()
Slider(
BasicSlider(
modifier = Modifier.padding(top = 21.dp).width(312.dp).height(32.dp),
value = value,
onValueChange = {
Expand Down Expand Up @@ -151,7 +151,7 @@ fun ColorPicker(
}
)
if (alphaEnabled) {
Slider(
BasicSlider(
modifier = Modifier.width(312.dp),
value = alpha,
onValueChange = {
Expand Down
97 changes: 68 additions & 29 deletions fluent/src/commonMain/kotlin/com/konyaco/fluent/component/Slider.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,40 +63,85 @@ fun Slider(
enabled: Boolean = true,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
steps: Int = 0,
snap: Boolean = steps != 0,
showTickMark: Boolean = steps != 0,
onValueChangeFinished: ((Float) -> Unit)? = null,
tooltipContent: @Composable (SliderState) -> Unit = { SliderDefaults.Tooltip(it, snap = snap) },
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
Slider(
BasicSlider(
value = value,
onValueChange = onValueChange,
modifier = modifier,
enabled = enabled,
valueRange = valueRange,
steps = steps,
snap = snap,
onValueChangeFinished = onValueChangeFinished,
interactionSource = interactionSource,
rail = { state -> SliderDefaults.Rail(state, enabled = enabled) },
track = { state -> SliderDefaults.Track(state, enabled = enabled) },
thumb = { state -> SliderDefaults.Thumb(state, enabled = enabled) }
rail = { state ->
SliderDefaults.Rail(
state = state,
enabled = enabled,
showTick = showTickMark
)
},
track = { state ->
SliderDefaults.Track(state, enabled = enabled)
},
thumb = { state ->
SliderDefaults.Thumb(state, enabled = enabled, label = tooltipContent)
}
)
}

@Composable
fun Slider(
state: SliderState,
modifier: Modifier = Modifier,
enabled: Boolean = true,
showTickMark: Boolean = state.steps != 0,
tooltipContent: @Composable (SliderState) -> Unit = { SliderDefaults.Tooltip(it, snap = state.snap) },
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
) {
BasicSlider(
state = state,
modifier = modifier,
enabled = enabled,
interactionSource = interactionSource,
rail = { state ->
SliderDefaults.Rail(
state = state,
enabled = enabled,
showTick = showTickMark
)
},
track = { state ->
SliderDefaults.Track(state, enabled = enabled)
},
thumb = { state ->
SliderDefaults.Thumb(state, enabled = enabled, label = tooltipContent)
}
)
}

@Composable
fun BasicSlider(
value: Float,
onValueChange: (Float) -> Unit,
modifier: Modifier = Modifier,
enabled: Boolean = true,
valueRange: ClosedFloatingPointRange<Float> = 0f..1f,
steps: Int = 0,
snap: Boolean = steps != 0,
onValueChangeFinished: ((Float) -> Unit)? = null,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
rail: @Composable (SliderState) -> Unit,
track: @Composable (SliderState) -> Unit,
thumb: @Composable (SliderState) -> Unit,
) {
val state =
remember(steps, valueRange) { SliderState(value, steps, onValueChangeFinished, valueRange) }
remember(steps, valueRange) { SliderState(value, steps, snap, onValueChangeFinished, valueRange) }
state.value = value
state.onValueChangeFinished = onValueChangeFinished
state.onValueChange = onValueChange
Expand All @@ -113,25 +158,7 @@ fun Slider(
}

@Composable
fun Slider(
state: SliderState,
modifier: Modifier = Modifier,
enabled: Boolean = true,
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
) {
SliderImpl(
modifier = modifier,
state = state,
enabled = enabled,
interactionSource = interactionSource,
rail = { state -> SliderDefaults.Rail(state, enabled = enabled) },
track = { state -> SliderDefaults.Track(state, enabled = enabled) },
thumb = { state -> SliderDefaults.Thumb(state, enabled = enabled) }
)
}

@Composable
fun Slider(
fun BasicSlider(
state: SliderState,
modifier: Modifier = Modifier,
enabled: Boolean = true,
Expand Down Expand Up @@ -233,6 +260,7 @@ private fun SliderImpl(
class SliderState(
value: Float = 0f,
val steps: Int = 0,
val snap: Boolean = steps != 0,
var onValueChangeFinished: ((Float) -> Unit)? = null,
val valueRange: ClosedFloatingPointRange<Float>
) {
Expand Down Expand Up @@ -301,10 +329,15 @@ class SliderState(
// Snap
// TODO: Add snap animation, maybe we should use anchoredDraggable?
val currentValue = this.value
val nearestValue = snapToNearestTickValue(currentValue)
val fraction = valueToFraction(nearestValue, this.valueRange)
this.value = nearestValue
setRawFraction(fraction, width, density)
if (this.snap) {
val nearestValue = snapToNearestTickValue(currentValue)
val fraction = valueToFraction(nearestValue, this.valueRange)
this.value = nearestValue
setRawFraction(fraction, width, density)
} else {
val fraction = valueToFraction(currentValue, this.valueRange)
setRawFraction(fraction, width, density)
}
}

this.onValueChangeFinished?.invoke(this.value)
Expand All @@ -316,6 +349,12 @@ class SliderState(
.map { lerp(this.valueRange.start, this.valueRange.endInclusive, it) }
.minBy { abs(it - value) }
}

fun nearestValue(): Float {
return this.stepFractions
.map { lerp(this.valueRange.start, this.valueRange.endInclusive, it) }
.minBy { abs(it - value) }
}
}

private fun getStepFractions(steps: Int): FloatArray {
Expand Down Expand Up @@ -561,7 +600,7 @@ object SliderDefaults {
}

@Composable
fun Tooltip(state: SliderState, snap: Boolean = state.steps != 0) {
fun Tooltip(state: SliderState, snap: Boolean = state.snap) {
Text(
if (snap) state.snapToNearestTickValue(state.value).toString()
else state.value.toString()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
package com.konyaco.fluent.gallery.screen.basicinput

import androidx.compose.foundation.layout.width
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import com.konyaco.fluent.component.Slider
import com.konyaco.fluent.component.SliderState
import com.konyaco.fluent.component.Text
import com.konyaco.fluent.component.*
import com.konyaco.fluent.gallery.annotation.Component
import com.konyaco.fluent.gallery.annotation.Sample
import com.konyaco.fluent.gallery.component.ComponentPagePath
import com.konyaco.fluent.gallery.component.GalleryPage
import com.konyaco.fluent.gallery.component.TodoComponent
import com.konyaco.fluent.source.generated.FluentSourceFile
import kotlin.math.roundToInt

@Component(
index = 12,
Expand All @@ -38,21 +42,39 @@ fun SliderScreen() {
}
)

Section(
title = "A Slider with tick marks.",
sourceCode = sourceCodeOfSliderTickMarkSample,
content = { SliderTickMarkSample() }
)

val (confirmValue, setConfirmValue) = remember { mutableStateOf(0f) }
val stepsSliderState = remember { SliderState(0f, 4, setConfirmValue, 0f..100f) }
val stepsSliderState = remember {
SliderState(
value = 0f,
steps = 4,
snap = true,
onValueChangeFinished = setConfirmValue,
valueRange = 0f..100f
)
}

Section(
title = "A Slider with range, steps and tick marks.",
title = "A Slider with custom tooltip content and snaps to tickmark.",
sourceCode = sourceCodeOfSliderStepsSample,
content = { SliderStepsSample(stepsSliderState) },
output = {
Text("value: ${stepsSliderState.value}")
Text("confirmValue: $confirmValue")
}
)
/*Section("A Slider with tick marks.", "") {
TodoComponent()
}*/

Section(
title = "Customize slider by using BasicSlider.",
sourceCode = sourceCodeOfBasicSliderSample,
content = { BasicSliderSample(remember { SliderState(valueRange = 0f..100f) }) },
)

Section("A vertical slider with range and tick marks specified.", "") {
TodoComponent()
}
Expand All @@ -69,11 +91,57 @@ private fun SliderSample(value: Float, onValueChanged: (Float) -> Unit) {
)
}

@Sample
@Composable
private fun SliderTickMarkSample() {
val (value, setValue) = remember { mutableStateOf(0f) }
Slider(
modifier = Modifier.width(200.dp),
valueRange = 0f..100f,
value = value,
steps = 5,
showTickMark = true,
snap = false,
onValueChange = setValue,
)
}

@Sample
@Composable
private fun SliderStepsSample(state: SliderState) {
Slider(
modifier = Modifier.width(200.dp),
state = state
state = state,
tooltipContent = {
Text(it.nearestValue().roundToInt().toString())
},
)
}

@Sample
@Composable
private fun BasicSliderSample(state: SliderState) {
BasicSlider(
modifier = Modifier.width(200.dp),
state = state,
rail = {
Box(
modifier = Modifier
.fillMaxWidth()
.requiredHeight(12.dp)
.clip(CircleShape)
.background(
Brush.horizontalGradient(
0f to Color.White.copy(0f),
1f to Color.Red.copy(1f)
)
)
)
},
track = {
},
thumb = {
SliderDefaults.Thumb(state)
}
)
}

0 comments on commit 64e550c

Please sign in to comment.