Merge pull request #1918 from vitorpamplona/claude/draggable-video-progress-CQODi
feat: add drag-to-seek on video progress indicator
This commit is contained in:
+28
-3
@@ -23,6 +23,7 @@ package com.vitorpamplona.amethyst.service.playback.composable.controls
|
|||||||
import androidx.annotation.OptIn
|
import androidx.annotation.OptIn
|
||||||
import androidx.compose.foundation.Canvas
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.gestures.detectDragGestures
|
||||||
import androidx.compose.foundation.gestures.detectTapGestures
|
import androidx.compose.foundation.gestures.detectTapGestures
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
@@ -30,7 +31,9 @@ import androidx.compose.foundation.layout.height
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.runtime.mutableFloatStateOf
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
@@ -103,21 +106,43 @@ private fun HorizontalLinearProgressIndicator(
|
|||||||
scrubberColor: Color = playedColor,
|
scrubberColor: Color = playedColor,
|
||||||
rectHeightDp: Dp = 4.dp,
|
rectHeightDp: Dp = 4.dp,
|
||||||
) {
|
) {
|
||||||
|
var isDragging by remember { mutableStateOf(false) }
|
||||||
|
var dragProgress by remember { mutableFloatStateOf(0f) }
|
||||||
|
|
||||||
Canvas(
|
Canvas(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(horizontal = rectHeightDp * 2.5f)
|
.padding(horizontal = rectHeightDp * 2.5f)
|
||||||
.pointerInput(Unit) {
|
.pointerInput(Unit) {
|
||||||
detectTapGestures { offset ->
|
detectTapGestures { offset ->
|
||||||
// Capture the exact position
|
|
||||||
onSeek(offset.x / this.size.width.toFloat())
|
onSeek(offset.x / this.size.width.toFloat())
|
||||||
}
|
}
|
||||||
|
}.pointerInput(Unit) {
|
||||||
|
detectDragGestures(
|
||||||
|
onDragStart = { offset ->
|
||||||
|
isDragging = true
|
||||||
|
dragProgress = (offset.x / this.size.width.toFloat()).coerceIn(0f, 1f)
|
||||||
|
},
|
||||||
|
onDrag = { change, _ ->
|
||||||
|
change.consume()
|
||||||
|
dragProgress = (change.position.x / this.size.width.toFloat()).coerceIn(0f, 1f)
|
||||||
|
},
|
||||||
|
onDragEnd = {
|
||||||
|
onSeek(dragProgress)
|
||||||
|
isDragging = false
|
||||||
|
},
|
||||||
|
onDragCancel = {
|
||||||
|
isDragging = false
|
||||||
|
},
|
||||||
|
)
|
||||||
}.padding(vertical = rectHeightDp * 2)
|
}.padding(vertical = rectHeightDp * 2)
|
||||||
.height(rectHeightDp)
|
.height(rectHeightDp)
|
||||||
.onSizeChanged { (w, _) -> onLayoutWidthChanged(w) },
|
.onSizeChanged { (w, _) -> onLayoutWidthChanged(w) },
|
||||||
) {
|
) {
|
||||||
val positionX = (currentPositionProgress() * size.width).coerceAtLeast(0f)
|
val displayProgress = if (isDragging) dragProgress else currentPositionProgress()
|
||||||
|
val positionX = (displayProgress * size.width).coerceAtLeast(0f)
|
||||||
val bufferX = (bufferedPositionProgress() * size.width).coerceAtLeast(0f)
|
val bufferX = (bufferedPositionProgress() * size.width).coerceAtLeast(0f)
|
||||||
|
val scrubberRadius = if (isDragging) size.height * 3f else size.height * 2f
|
||||||
|
|
||||||
drawRect(unplayedColor, size = Size(size.width, size.height))
|
drawRect(unplayedColor, size = Size(size.width, size.height))
|
||||||
drawRect(bufferedColor, size = Size(bufferX, size.height))
|
drawRect(bufferedColor, size = Size(bufferX, size.height))
|
||||||
@@ -125,7 +150,7 @@ private fun HorizontalLinearProgressIndicator(
|
|||||||
|
|
||||||
drawCircle(
|
drawCircle(
|
||||||
color = scrubberColor,
|
color = scrubberColor,
|
||||||
radius = size.height * 2f,
|
radius = scrubberRadius,
|
||||||
center = Offset(x = positionX, y = size.height / 2.0f),
|
center = Offset(x = positionX, y = size.height / 2.0f),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user