audrey/src/ui/play_queue.vala

113 lines
3.7 KiB
Vala

[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
public class Ui.PlayQueue : Adw.NavigationPage {
public ListStore songs { get; private set; }
public uint selected_index { get; set; }
// this is the index of the song that will play on next on_stream_start
private uint next_stream_index;
public signal void play_next (Song? song);
public signal void play_now (Song song);
public signal void now_playing (Song song);
private bool ignore_selection = false;
[GtkChild] private unowned Gtk.SingleSelection selection;
public bool can_clear_all { get; private set; default = false; }
construct {
this.songs = new ListStore (typeof (Song));
this.next_stream_index = 0;
}
[GtkCallback] public void clear () {
this.songs.remove_all ();
this.can_clear_all = false;
}
public void queue (Song song) {
uint new_index = this.songs.get_n_items ();
this.songs.append (song);
if (new_index == next_stream_index) {
this.play_next (song);
}
this.can_clear_all = true;
}
[GtkCallback] private void on_song_selected () {
this.selected_index = this.selection.selected; // manual bidi binding
if (this.ignore_selection) return;
this.pick_song (this.selected_index);
}
private void pick_song (uint index) {
Song? song = (Song?) this.songs.get_item (index);
if (song != null) {
this.ignore_selection = true;
this.selected_index = index;
this.ignore_selection = false;
this.next_stream_index = index;
this.play_next (song);
this.play_now (song);
}
}
internal void on_stream_start (Playbin playbin, string uri) {
Song song = (Song) this.songs.get_item (this.next_stream_index);
if (public_api.stream_uri (song.id) != uri) {
// prerolled track wasnt actually next one!
// this can happen if it was deleted from the play queue after about-to-finish was signalled
// gapless playback is ruined anyway, go wild
this.pick_song (this.next_stream_index);
return;
}
this.now_playing (song);
this.ignore_selection = true;
this.selected_index = this.next_stream_index;
this.ignore_selection = false;
// prepare for next song ahead of time (gapless)
this.next_stream_index += 1;
Song? next_song = (Song?) this.songs.get_item (this.next_stream_index);
this.play_next (next_song);
}
internal void restart () {
this.pick_song (0);
}
public void skip_forward () {
this.pick_song (this.selected_index + 1);
}
public void skip_backward () {
if (this.selected_index >= 1) {
this.pick_song (this.selected_index - 1);
}
}
[GtkCallback] private void delete_cell_setup (Object object) {
Gtk.ColumnViewCell cell = (Gtk.ColumnViewCell) object;
Gtk.Button button = new Gtk.Button.from_icon_name ("edit-delete");
button.add_css_class ("flat");
cell.child = button;
button.clicked.connect (() => {
this.songs.remove (cell.position);
this.can_clear_all = this.songs.get_n_items() > 0;
if (cell.position == this.next_stream_index) {
// we just deleted the track that was to be prerolled next
// replace it
this.play_next ((Song?) this.songs.get_item (cell.position));
} else if (cell.position+1 == this.next_stream_index) {
// conversely, we just deleted the currently playing track
// redo
this.pick_song (cell.position);
}
});
}
}