commit 9266199620d9d85efa5e252deffd3e83c7cbe831
parent ef05a889587ef3989b1f43ad82707ea46db49165
Author: Jean Schurger <jean@schurger.org>
Date: Fri, 16 Oct 2026 15:11:09 -0400
Skip and loop over the same span no longer conflict
Adding a skip zone over a loop zone's span removes that loop (stopping it if
active); looping between marks removes a skip zone over the same span.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0141Zm4UQq8PATQAG98MdNCc
Diffstat:
1 file changed, 15 insertions(+), 3 deletions(-)
diff --git a/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/player/player_view_model.kt b/composeApp/src/commonMain/kotlin/ubi2/sonodrill/ui/player/player_view_model.kt
@@ -186,7 +186,7 @@ class PlayerViewModel(
if (next_start != null) {
val next_end = sorted.firstOrNull { it.position_ms > next_start.position_ms }
if (next_end != null) {
- loop.set_loop(next_start, next_end)
+ loop_between(next_start, next_end)
return
}
}
@@ -217,7 +217,7 @@ class PlayerViewModel(
val sorted = _marks.value.sortedBy { it.position_ms }
val prev = sorted.lastOrNull { it.position_ms <= position_ms }
val next = sorted.firstOrNull { it.position_ms > position_ms }
- if (prev != null && next != null) loop.set_loop(prev, next)
+ if (prev != null && next != null) loop_between(prev, next)
}
fun add_mark_and_loop() {
@@ -256,7 +256,15 @@ class PlayerViewModel(
val sorted = _marks.value.sortedBy { it.position_ms }
val a = sorted.lastOrNull { it.position_ms <= zone.start_ms } ?: MarkInfo(id = "_lz_a", file_id = "", position_ms = zone.start_ms)
val b = sorted.firstOrNull { it.position_ms >= zone.end_ms } ?: MarkInfo(id = "_lz_b", file_id = "", position_ms = zone.end_ms)
- loop.set_loop(a, b, seek_to_start = false)
+ loop_between(a, b, seek_to_start = false)
+ }
+
+ // Loops between marks; a skip zone over the same span would jump out of the loop, so it goes
+ private fun loop_between(a: MarkInfo, b: MarkInfo, seek_to_start: Boolean = true) {
+ val start = minOf(a.position_ms, b.position_ms)
+ val end = maxOf(a.position_ms, b.position_ms)
+ set_skip_zones(_skip_zones.value.filter { !(it.start_ms == start && it.end_ms == end) })
+ loop.set_loop(a, b, seek_to_start)
}
fun toggle_full_loop() {
@@ -296,6 +304,10 @@ class PlayerViewModel(
val prev = sorted.lastOrNull { it.position_ms <= pos } ?: return
val next = sorted.firstOrNull { it.position_ms > pos } ?: return
val zone = SkipZone(prev.position_ms, next.position_ms)
+ // Skipping a span replaces a loop over the same span (and stops it if it's playing)
+ val same_loop = LoopZone(zone.start_ms, zone.end_ms)
+ if (loop_state.value.is_zone(same_loop)) loop.clear()
+ set_loop_zones(_loop_zones.value.filter { it != same_loop })
if (_skip_zones.value.none { it == zone }) set_skip_zones((_skip_zones.value + zone).sortedBy { it.start_ms })
}