Add queue Move/RemoveAt + dormant-Enqueue coherence and shared QueueList (Phase 17.1)
This commit is contained in:
@@ -87,12 +87,45 @@ public interface IQueueService
|
||||
/// </summary>
|
||||
Task Start();
|
||||
|
||||
/// <summary>Appends a track to the end of the queue without changing what is currently playing.</summary>
|
||||
/// <summary>
|
||||
/// Appends a track to the end of the queue without changing what is currently playing.
|
||||
/// Into a dormant queue (<see cref="CurrentIndex"/> == -1) the append leaves a coherent
|
||||
/// <see cref="CurrentIndex"/> (the first appended track) so a subsequent play/skip is correct —
|
||||
/// but it does NOT begin playback (add is not play). Interop-free; safe during prerender.
|
||||
/// </summary>
|
||||
void Enqueue(TrackDto track);
|
||||
|
||||
/// <summary>Appends tracks to the end of the queue without changing what is currently playing.</summary>
|
||||
/// <summary>
|
||||
/// Appends tracks to the end of the queue without changing what is currently playing.
|
||||
/// Into a dormant queue (<see cref="CurrentIndex"/> == -1) the append leaves a coherent
|
||||
/// <see cref="CurrentIndex"/> (the first appended track) so a subsequent play/skip is correct —
|
||||
/// but it does NOT begin playback (add is not play). Interop-free; safe during prerender.
|
||||
/// </summary>
|
||||
void EnqueueRange(IEnumerable<TrackDto> tracks);
|
||||
|
||||
/// <summary>
|
||||
/// Reorders the queue, moving the track at <paramref name="fromIndex"/> to
|
||||
/// <paramref name="toIndex"/>, and re-emits <see cref="QueueChanged"/>. Adjusts
|
||||
/// <see cref="CurrentIndex"/> so the <em>same track</em> stays current across the move — it does
|
||||
/// not restart, re-stream, or interrupt the currently-playing track. Interop-free; safe during
|
||||
/// prerender. No-op (no throw, no <see cref="QueueChanged"/>) when either index is out of range
|
||||
/// or the indices are equal.
|
||||
/// </summary>
|
||||
void Move(int fromIndex, int toIndex);
|
||||
|
||||
/// <summary>
|
||||
/// Removes the track at <paramref name="index"/> and re-emits <see cref="QueueChanged"/>. Does
|
||||
/// not touch playback (the player stays a single-track device): removing the current track does
|
||||
/// not stop it — the playing track runs to its natural end while <see cref="CurrentIndex"/>
|
||||
/// resolves to the new occupant of that slot (the next track) so the next auto-advance/skip is
|
||||
/// coherent. Removing a track before the current decrements <see cref="CurrentIndex"/> (the same
|
||||
/// track stays current); removing after the current leaves it unchanged. Removing the last
|
||||
/// remaining track empties the queue (<see cref="CurrentIndex"/> == -1, dormant). Interop-free;
|
||||
/// safe during prerender. No-op (no throw, no <see cref="QueueChanged"/>) when
|
||||
/// <paramref name="index"/> is out of range.
|
||||
/// </summary>
|
||||
void RemoveAt(int index);
|
||||
|
||||
/// <summary>
|
||||
/// Advances to the next track and streams it. No-op when <see cref="HasNext"/> is false.
|
||||
/// </summary>
|
||||
|
||||
@@ -95,6 +95,11 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
public void Enqueue(TrackDto track)
|
||||
{
|
||||
_items.Add(track);
|
||||
// OQ8: appending into a dormant (empty) queue leaves a coherent CurrentIndex so the next
|
||||
// play/skip is correct — but does NOT auto-play (add is not play). PlayCurrent is never
|
||||
// called here, so this stays interop-free and prerender-safe.
|
||||
if (CurrentIndex == -1)
|
||||
CurrentIndex = 0;
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
@@ -102,8 +107,71 @@ public sealed class QueueService : IQueueService, IDisposable
|
||||
{
|
||||
var before = _items.Count;
|
||||
_items.AddRange(tracks);
|
||||
if (_items.Count != before)
|
||||
QueueChanged?.Invoke();
|
||||
if (_items.Count == before) return;
|
||||
// OQ8: see Enqueue — first append into a dormant queue stages a coherent CurrentIndex
|
||||
// without playing. The first newly-appended track becomes current.
|
||||
if (CurrentIndex == -1)
|
||||
CurrentIndex = 0;
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void Move(int fromIndex, int toIndex)
|
||||
{
|
||||
if (fromIndex == toIndex) return;
|
||||
if (fromIndex < 0 || fromIndex >= _items.Count) return;
|
||||
if (toIndex < 0 || toIndex >= _items.Count) return;
|
||||
|
||||
var moved = _items[fromIndex];
|
||||
_items.RemoveAt(fromIndex);
|
||||
_items.Insert(toIndex, moved);
|
||||
|
||||
// Keep the same track current across the reorder. No playback is touched (C2): we only
|
||||
// recompute which index the current track now sits at.
|
||||
if (CurrentIndex == fromIndex)
|
||||
{
|
||||
CurrentIndex = toIndex;
|
||||
}
|
||||
else if (fromIndex < CurrentIndex && toIndex >= CurrentIndex)
|
||||
{
|
||||
// The current track shifted one slot toward the front to fill the vacated lower slot.
|
||||
CurrentIndex--;
|
||||
}
|
||||
else if (fromIndex > CurrentIndex && toIndex <= CurrentIndex)
|
||||
{
|
||||
// An item inserted at/above the current slot pushed the current track one slot back.
|
||||
CurrentIndex++;
|
||||
}
|
||||
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
public void RemoveAt(int index)
|
||||
{
|
||||
if (index < 0 || index >= _items.Count) return;
|
||||
|
||||
_items.RemoveAt(index);
|
||||
|
||||
if (_items.Count == 0)
|
||||
{
|
||||
// Last remaining track removed → empty + dormant. Does not stop the player (C2).
|
||||
CurrentIndex = -1;
|
||||
}
|
||||
else if (index < CurrentIndex)
|
||||
{
|
||||
// A track before the current was removed: the same track stays current at a lower index.
|
||||
CurrentIndex--;
|
||||
}
|
||||
else if (index == CurrentIndex && CurrentIndex >= _items.Count)
|
||||
{
|
||||
// The current track was removed and it was the last slot: there is no "next" occupant to
|
||||
// resolve to, so the queue goes dormant (CurrentIndex == -1). Playback is NOT stopped
|
||||
// (C2) — the just-removed track keeps playing to its natural end; auto-advance simply has
|
||||
// nothing further. Removing current when it is NOT the last leaves CurrentIndex pointing
|
||||
// at the new occupant of that slot (the next track), so no adjustment is needed there.
|
||||
CurrentIndex = -1;
|
||||
}
|
||||
|
||||
QueueChanged?.Invoke();
|
||||
}
|
||||
|
||||
public async Task Next()
|
||||
|
||||
Reference in New Issue
Block a user