Skip to content

Commit

Permalink
feat(Slider): add basic SliderLayout to position slider elements
Browse files Browse the repository at this point in the history
  • Loading branch information
levinzonr committed Feb 23, 2024
1 parent ec76916 commit a96014d
Show file tree
Hide file tree
Showing 7 changed files with 208 additions and 34 deletions.
2 changes: 1 addition & 1 deletion demo/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ kotlin {
}

dependencies {

implementation(project(":slider"))
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
Expand Down
9 changes: 3 additions & 6 deletions demo/src/main/java/io/monstarlab/mosaic/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import io.monstarlab.mosaic.features.SliderDemo
import io.monstarlab.mosaic.ui.theme.MosaicTheme

class MainActivity : ComponentActivity() {
Expand All @@ -19,12 +21,7 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
setContent {
MosaicTheme {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding),
)
}
SliderDemo()
}
}
}
Expand Down
32 changes: 32 additions & 0 deletions demo/src/main/java/io/monstarlab/mosaic/features/SliderDemo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package io.monstarlab.mosaic.features

import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import io.monstarlab.mosaic.slider.Slider
import io.monstarlab.mosaic.slider.SliderColors

@Composable
fun SliderDemo() = Scaffold(modifier = Modifier) {
Column(modifier = Modifier.padding(it).padding(16.dp)) {
Slider(
value = 0.5f,
onValueChange = {},
colors = SliderColors(Color.Red),
modifier = Modifier.clip(RoundedCornerShape(2.dp))
)
}
}

@Preview
@Composable
private fun PreviewSliderDemo() {
SliderDemo()
}
62 changes: 62 additions & 0 deletions slider/src/main/java/io/monstarlab/mosaic/slider/Slider.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package io.monstarlab.mosaic.slider

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.BoxWithConstraints
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.tooling.preview.Preview

@Composable
public fun Slider(
value: Float,
onValueChange: (Float) -> Unit,
colors: SliderColors,
modifier: Modifier = Modifier,
thumb: @Composable () -> Unit = { DefaultSliderThumb(colors = colors) }
) {

SliderLayout(
progress = value,
thumb = thumb,
track = {
SliderTrack(
modifier = modifier,
progress = value,
colors = colors,
)
}
)
}

@Composable
internal fun DefaultSliderThumb(colors: SliderColors) {
Box(
modifier = Modifier
.size(SliderDefaults.ThumbSize)
.background(
color = colors.active,
shape = CircleShape
)
)
}

@Preview
@Composable
private fun PreviewSlider() {
Slider(
value = 0.5f,
onValueChange = {},
colors = SliderColors(Color.Yellow)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ import androidx.compose.ui.unit.dp

internal object SliderDefaults {
val TrackHeight = 4.dp
val ThumbSize = 16.dp
}
79 changes: 79 additions & 0 deletions slider/src/main/java/io/monstarlab/mosaic/slider/SliderLayout.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package io.monstarlab.mosaic.slider

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import kotlin.math.max
import kotlin.math.roundToInt

@Composable
internal fun SliderLayout(
progress: Float,
track: @Composable () -> Unit,
thumb: @Composable () -> Unit
) {
Layout(
content = {
Box(modifier = Modifier.layoutId(SliderLayoutElements.Track)) {
track()
}
Box(modifier = Modifier.layoutId(SliderLayoutElements.Thumb)) {
thumb()
}
}) { mesuarables, constraints ->

val trackPlaceable = mesuarables
.first { it.layoutId == SliderLayoutElements.Track }
.measure(constraints)

val thumbPlaceable = mesuarables
.first { it.layoutId == SliderLayoutElements.Thumb }
.measure(constraints)

val sliderHeight = max(trackPlaceable.height, thumbPlaceable.height)
val sliderWidth = trackPlaceable.width
layout(sliderWidth, sliderHeight) {
val trackY = sliderHeight / 2 - trackPlaceable.height / 2
val thumbY = sliderHeight / 2 - thumbPlaceable.height / 2
val thumbX = (sliderWidth * progress).roundToInt() - thumbPlaceable.width / 2
trackPlaceable.place(0, trackY)
thumbPlaceable.place(thumbX, thumbY)
}
}
}


internal enum class SliderLayoutElements {
Track, Thumb
}


@Preview
@Composable
private fun PreviewSliderLayout() {
SliderLayout(
progress = 0.5f,
track = {
Box(
modifier = Modifier
.background(Color.Yellow)
.fillMaxWidth()
.height(20.dp)
)
},
thumb = {
Box(modifier = Modifier
.background(Color.Blue, shape = CircleShape)
.size(50.dp))
})
}
57 changes: 30 additions & 27 deletions slider/src/main/java/io/monstarlab/mosaic/slider/SliderTrack.kt
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package io.monstarlab.mosaic.slider

import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Color
Expand All @@ -19,34 +21,35 @@ internal fun SliderTrack(
) {

check(progress in 0f..1f) { "Invalid progress value should be between 0 and 1" }
Canvas(
Box(
modifier = modifier
.fillMaxWidth()
.height(SliderDefaults.TrackHeight)
) {
val activeRectWidth = size.width * progress
drawRect(
color = colors.active,
topLeft = Offset.Zero,
size = Size(activeRectWidth, size.height)
)
.heightIn(min = SliderDefaults.TrackHeight)
.drawBehind {
val activeRectWidth = size.width * progress
drawRect(
color = colors.active,
topLeft = Offset.Zero,
size = Size(activeRectWidth, size.height)
)

drawRect(
color = colors.inactive,
topLeft = Offset(activeRectWidth, 0f),
size = Size(size.width - activeRectWidth, size.height)
)
drawRect(
color = colors.inactive,
topLeft = Offset(activeRectWidth, 0f),
size = Size(size.width - activeRectWidth, size.height)
)

if (!disabledRange.isEmpty()) {
val disabledStart = size.width * disabledRange.start
val disabledEnd = size.width * disabledRange.endInclusive
drawRect(
color = colors.disabled,
topLeft = Offset(size.width * disabledRange.start, 0f),
size = Size(disabledEnd - disabledStart, size.height)
)
}
}
if (!disabledRange.isEmpty()) {
val disabledStart = size.width * disabledRange.start
val disabledEnd = size.width * disabledRange.endInclusive
drawRect(
color = colors.disabled,
topLeft = Offset(size.width * disabledRange.start, 0f),
size = Size(disabledEnd - disabledStart, size.height)
)
}
}
)
}


Expand All @@ -55,7 +58,7 @@ internal fun SliderTrack(
private fun PreviewSliderTrack() {
SliderTrack(
progress = 0.5f,
colors = SliderColors(Color.Red),
disabledRange = 0.8f..1f
colors = SliderColors(Color.Yellow),
disabledRange = 0.8f..1f,
)
}

0 comments on commit a96014d

Please sign in to comment.