Auto commit Fri Mar 20 08:01:02 PM CDT 2026

This commit is contained in:
Tretzi
2026-03-20 20:01:02 -05:00
parent a359a5e191
commit e2cd1ff4df

View File

@@ -7,7 +7,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.calculateCentroid
import androidx.compose.foundation.gestures.calculatePan
import androidx.compose.foundation.gestures.calculateZoom
import androidx.compose.foundation.gestures.detectDragGestures
import androidx.compose.foundation.gestures.awaitFirstDown
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -258,60 +258,36 @@ internal fun TimelineGraph(
.fillMaxWidth()
.height(190.dp)
.background(Color(0xFF0A0E18), RoundedCornerShape(10.dp))
.pointerInput(activeCurve, viewport, curveLeft, curveRight) {
.pointerInput(viewport, selection, curveLeft, curveRight, activeCurve) {
awaitPointerEventScope {
while (true) {
val event = awaitPointerEvent(pass = PointerEventPass.Initial)
val pressedCount = event.changes.count { it.pressed }
if (pressedCount < 2) continue
val zoom = event.calculateZoom()
val pan = event.calculatePan()
val centroid = event.calculateCentroid(useCurrent = true)
if (zoom != 1f || pan.x != 0f || pan.y != 0f) {
val newViewport = viewport.applyZoomPan(
zoomChange = zoom,
panPx = pan.x,
widthPx = size.width.toFloat(),
centroidPx = centroid.x,
)
onViewportChanged(newViewport)
}
// Reserve all 2+ finger gestures for transform handling so they don't
// compete with one-finger point/handle drags.
event.changes.forEach { it.consume() }
draggingPointIndex = null
draggingCurveId = null
dragMode = HandleDrag.NONE
}
}
}
.pointerInput(viewport, selection, curveLeft, curveRight, activeCurve) {
detectDragGestures(
onDragStart = { start ->
val down = awaitFirstDown(requireUnconsumed = false)
val w = size.width.toFloat()
val h = size.height.toFloat()
val startX = viewport.timeToX(selection.startSec, w)
val endX = viewport.timeToX(selection.endSec, w)
val hitPx = 32f
var activePointerId = down.id
var lastPos = down.position
var isTransform = false
var moved = false
val hits = buildList {
curveLeft.hitTestPoint(start, viewport, w, h)?.let { idx ->
curveLeft.hitTestPoint(down.position, viewport, w, h)?.let { idx ->
val point = curveLeft.points[idx]
val x = viewport.timeToX(point.tSec, w)
val y = valueToY(point.value01, h)
val dx = start.x - x
val dy = start.y - y
val dx = down.position.x - x
val dy = down.position.y - y
add(Triple(leftId, idx, dx * dx + dy * dy))
}
curveRight.hitTestPoint(start, viewport, w, h)?.let { idx ->
curveRight.hitTestPoint(down.position, viewport, w, h)?.let { idx ->
val point = curveRight.points[idx]
val x = viewport.timeToX(point.tSec, w)
val y = valueToY(point.value01, h)
val dx = start.x - x
val dy = start.y - y
val dx = down.position.x - x
val dy = down.position.y - y
add(Triple(rightId, idx, dx * dx + dy * dy))
}
}
@@ -323,65 +299,99 @@ internal fun TimelineGraph(
dragMode = HandleDrag.NONE
onTapSideSelect(if (nearestHit.first == leftId) Side.LEFT else Side.RIGHT)
} else {
dragMode = when {
abs(start.x - startX) <= hitPx -> HandleDrag.START
abs(start.x - endX) <= hitPx -> HandleDrag.END
else -> HandleDrag.NONE
}
draggingPointIndex = null
draggingCurveId = null
}
},
onDragEnd = {
draggingPointIndex = null
draggingCurveId = null
dragMode = HandleDrag.NONE
},
onDragCancel = {
draggingPointIndex = null
draggingCurveId = null
dragMode = HandleDrag.NONE
},
onDrag = { change, dragAmount ->
change.consume()
val w = size.width.toFloat()
val h = size.height.toFloat()
val pointIndex = draggingPointIndex
val curveId = draggingCurveId
if (pointIndex != null && curveId != null) {
val targetCurve = if (curveId == leftId) curveLeft else curveRight
val updated = targetCurve.movePointVertical(pointIndex, change.position.y, h)
onUpdateCurve(curveId, updated)
onCurveEditedHaptic()
return@detectDragGestures
}
val t = viewport.xToTimeSec(change.position.x, w).toInt().coerceAtLeast(0)
when (dragMode) {
HandleDrag.START -> onSelectionChanged(TimelineSelection(startSec = min(t, selection.endSec - 60), endSec = selection.endSec))
HandleDrag.END -> onSelectionChanged(TimelineSelection(startSec = selection.startSec, endSec = max(t, selection.startSec + 60)))
HandleDrag.NONE -> {
val newViewport = viewport.applyZoomPan(
zoomChange = 1f,
panPx = dragAmount.x,
widthPx = w,
centroidPx = w / 2f,
)
onViewportChanged(newViewport)
dragMode = when {
abs(down.position.x - startX) <= hitPx -> HandleDrag.START
abs(down.position.x - endX) <= hitPx -> HandleDrag.END
else -> HandleDrag.NONE
}
}
while (true) {
val event = awaitPointerEvent(pass = PointerEventPass.Initial)
val pressed = event.changes.filter { it.pressed }
if (pressed.isEmpty()) break
if (pressed.size >= 2) {
isTransform = true
draggingPointIndex = null
draggingCurveId = null
dragMode = HandleDrag.NONE
}
if (isTransform) {
val zoom = event.calculateZoom()
val pan = event.calculatePan()
val centroid = event.calculateCentroid(useCurrent = true)
if (zoom != 1f || pan.x != 0f || pan.y != 0f) {
moved = true
val newViewport = viewport.applyZoomPan(
zoomChange = zoom,
panPx = pan.x,
widthPx = size.width.toFloat(),
centroidPx = centroid.x,
)
onViewportChanged(newViewport)
}
event.changes.forEach { it.consume() }
continue
}
val primary = pressed.firstOrNull { it.id == activePointerId } ?: pressed.first()
activePointerId = primary.id
val delta = primary.position - lastPos
lastPos = primary.position
if (delta.getDistanceSquared() > 0.5f) moved = true
val pointIndex = draggingPointIndex
val curveId = draggingCurveId
if (pointIndex != null && curveId != null) {
val targetCurve = if (curveId == leftId) curveLeft else curveRight
val updated = targetCurve.movePointVertical(pointIndex, primary.position.y, h)
onUpdateCurve(curveId, updated)
onCurveEditedHaptic()
primary.consume()
continue
}
val t = viewport.xToTimeSec(primary.position.x, w).toInt().coerceAtLeast(0)
when (dragMode) {
HandleDrag.START -> {
onSelectionChanged(TimelineSelection(startSec = min(t, selection.endSec - 60), endSec = selection.endSec))
primary.consume()
}
HandleDrag.END -> {
onSelectionChanged(TimelineSelection(startSec = selection.startSec, endSec = max(t, selection.startSec + 60)))
primary.consume()
}
HandleDrag.NONE -> {
if (delta.x != 0f) {
val newViewport = viewport.applyZoomPan(
zoomChange = 1f,
panPx = delta.x,
widthPx = w,
centroidPx = w / 2f,
)
onViewportChanged(newViewport)
primary.consume()
}
}
}
}
if (!moved && !isTransform) {
val side = if (down.position.x < size.width / 2f) Side.LEFT else Side.RIGHT
onTapSideSelect(side)
}
draggingPointIndex = null
draggingCurveId = null
dragMode = HandleDrag.NONE
}
)
}
}
.pointerInput(activeCurve) {
detectTapGestures(
onTap = { offset ->
val side = if (offset.x < size.width / 2f) Side.LEFT else Side.RIGHT
onTapSideSelect(side)
}
)
}
) {
) {
Canvas(modifier = Modifier.fillMaxSize()) {
drawGrid()