TrimOverlay
The trim window over the strip: a scrim on what the trim leaves out, and a handle at each end.
Controlled, so a drag reports the range it wants through onRangeChange and draws whatever range is next passed back in. A caller that drops the change draws a handle that does not move.
The range is a TimeRange, the same half-open type Clip.trim takes, so what the handles produce goes straight onto the edit.
var trimRange by remember(source) { mutableStateOf(TimeRange.from(Duration.ZERO)) }
Box(Modifier.height(FilmstripTimelineDefaults.StripHeight)) {
FilmstripStrip(frames = frames, grid = timeline.grid, state = timeline.listState)
TrimOverlay(
range = trimRange,
scale = timeline.scale,
onRangeChange = { trimRange = it },
// The same scroll the strip is at, so the handles sit over the tiles they name.
scrollPx = timeline::scrollPx,
constraint = TrimConstraint.MinDuration(1.seconds),
)
}
val composition = remember(source, trimRange) {
compositionOf { clip(source) { trim(trimRange) } }
}Parameters
Where the trim currently sits. An open end runs to the end of the source, and stays open while the start handle moves. Dragging the end handle closes it, since that gesture is the caller naming an end.
What turns composition time into content pixels, and where the clip's output length comes from.
Called with the range a gesture asks for, already constrained.
Modifier for the overlay, which fills whatever it is placed in.
How far the timeline has scrolled, in content pixels.
What the gesture is allowed to produce.
What the overlay paints with.
How wide a handle is drawn.
How wide a handle answers to a finger, which is wider than it is drawn.
The speed the clip plays at. The strip's axis is composition time and range is source time, so a handle is drawn at the output length of its source time and a drag writes the source length of where it lands.