// song widget+drag behavior taken from gnome music [GtkTemplate (ui = "/eu/callcc/audrey/play_queue_song.ui")] class Ui.PlayQueueSong : Gtk.Box { public bool draggable { get; set; default = false; } public bool show_position { get; set; default = false; } public bool show_artist { get; set; default = false; } public bool show_cover { get; set; default = false; } private bool _current = false; public bool current { get { return _current; } set { this._current = value; if (value) { this.add_css_class ("playing"); } else { this.remove_css_class ("playing"); } } } public uint displayed_position { get; set; } public PlaybinSong song { get; set; } private weak Playbin playbin; public PlayQueueSong (Playbin playbin) { this.playbin = playbin; var action_group = new SimpleActionGroup (); var remove = new SimpleAction ("remove", null); remove.activate.connect (() => { this.playbin.remove_track (this.displayed_position-1); }); action_group.add_action (remove); this.insert_action_group ("song", action_group); } private ulong connection; public void bind (uint position, PlaybinSong song) { this.displayed_position = position+1; this.song = song; this.current = this.playbin.play_queue_position == position; this.connection = this.playbin.notify["play-queue-position"].connect (() => { this.current = this.playbin.play_queue_position == position; }); song.need_cover_art (); } public void unbind () { this.playbin.disconnect (this.connection); } [GtkCallback] private string format_duration (int duration) { return "%02d:%02d".printf(duration/60, duration%60); } [GtkCallback] private string star_button_icon_name (DateTime? starred) { return starred == null ? "non-starred" : "starred"; } private double drag_x; private double drag_y; [GtkCallback] private Gdk.ContentProvider? on_drag_prepare (double x, double y) { if (this.draggable) { this.drag_x = x; this.drag_y = y; return new Gdk.ContentProvider.for_value (this); } else return null; } private Gtk.ListBox? drag_widget; [GtkCallback] private void on_drag_begin (Gtk.DragSource source, Gdk.Drag drag) { this.drag_widget = new Gtk.ListBox (); var drag_row = new PlayQueueSong (this.playbin); drag_row.draggable = false; drag_row.show_position = this.show_position; drag_row.show_artist = this.show_artist; drag_row.show_cover = this.show_cover; drag_row.current = false; drag_row.displayed_position = this.displayed_position; drag_row.song = this.song; drag_row.set_size_request (this.get_width (), this.get_height ()); var drag_row_real = new Gtk.ListBoxRow (); drag_row_real.child = drag_row; this.drag_widget.append (drag_row_real); this.drag_widget.drag_highlight_row (drag_row_real); var drag_icon = Gtk.DragIcon.get_for_drag (drag); drag_icon.set("child", this.drag_widget); drag.set_hotspot ((int) this.drag_x, (int) this.drag_y); } [GtkCallback] private bool on_drop (Value value, double x, double y) { this.drag_widget = null; this.drag_x = 0.0; this.drag_y = 0.0; var source = value as PlayQueueSong; debug ("dropped %u on %u", source.displayed_position, this.displayed_position); this.playbin.move_track (source.displayed_position-1, this.displayed_position-1); return false; } } [GtkTemplate (ui = "/eu/callcc/audrey/play_queue.ui")] public class Ui.PlayQueue : Adw.Bin { private weak Playbin _playbin; public Playbin playbin { get { return _playbin; } set { assert (_playbin == null); // only set once _playbin = value; _playbin.play_queue.items_changed.connect (this.on_store_items_changed); this.can_clear_all = _playbin.play_queue.get_n_items () > 0; } } public bool can_clear_all { get; private set; } /*[GtkCallback] private void on_clear () { this.playbin.clear (); }*/ private void on_store_items_changed (GLib.ListModel store, uint position, uint removed, uint added) { this.can_clear_all = store.get_n_items () > 0; } [GtkCallback] private void on_song_list_setup (Gtk.SignalListItemFactory factory, Object object) { var item = object as Gtk.ListItem; var child = new PlayQueueSong (this.playbin); child.draggable = true; child.show_position = true; child.show_artist = true; child.show_cover = true; item.child = child; } [GtkCallback] private void on_song_list_bind (Gtk.SignalListItemFactory factory, Object object) { var item = object as Gtk.ListItem; var child = item.child as PlayQueueSong; child.bind (item.position, item.item as PlaybinSong); } [GtkCallback] private void on_song_list_unbind (Gtk.SignalListItemFactory factory, Object object) { var item = object as Gtk.ListItem; var child = item.child as PlayQueueSong; child.unbind (); } [GtkCallback] private void on_row_activated (uint position) { playbin.select_track (position); } [GtkCallback] private string visible_child_name (uint n_items) { return n_items > 0 ? "not-empty" : "empty"; } ~PlayQueue () { debug ("destroying play queue widget"); } }