selection model bull shit
This commit is contained in:
parent
7f7a84d747
commit
636774d787
6 changed files with 169 additions and 152 deletions
|
@ -4,7 +4,6 @@ audrey_sources = [
|
||||||
'globalconf.vala',
|
'globalconf.vala',
|
||||||
'main.vala',
|
'main.vala',
|
||||||
'mpris.vala',
|
'mpris.vala',
|
||||||
'play_queue.vala',
|
|
||||||
'playbin.vala',
|
'playbin.vala',
|
||||||
'ui/play_queue.vala',
|
'ui/play_queue.vala',
|
||||||
'ui/setup.vala',
|
'ui/setup.vala',
|
||||||
|
|
|
@ -1,136 +0,0 @@
|
||||||
// this is a custom SelectionModel that lets us only signal the
|
|
||||||
// selection has changed on user interaction
|
|
||||||
internal class Audrey.PlayQueue : GLib.Object, GLib.ListModel, Gtk.SelectionModel {
|
|
||||||
public GLib.ListStore inner { get; private set; }
|
|
||||||
// if current_position == inner.get_n_items (), play queue is stopped
|
|
||||||
public uint current_position { get; private set; }
|
|
||||||
|
|
||||||
// emitted when a track is purposefully selected
|
|
||||||
public signal void user_selected (uint position);
|
|
||||||
|
|
||||||
public PlayQueue () {
|
|
||||||
this.inner = new GLib.ListStore (typeof (Song));
|
|
||||||
this.current_position = this.inner.get_n_items ();
|
|
||||||
|
|
||||||
this.inner.items_changed.connect (this.on_inner_items_changed);
|
|
||||||
}
|
|
||||||
|
|
||||||
// only called by playbin
|
|
||||||
// does not emit user_selected
|
|
||||||
internal void playbin_select (uint new_position) {
|
|
||||||
var previous_position = this.current_position;
|
|
||||||
this.current_position = new_position;
|
|
||||||
|
|
||||||
if (previous_position < this.inner.get_n_items ()) {
|
|
||||||
if (new_position < this.inner.get_n_items ()) {
|
|
||||||
if (previous_position < new_position) {
|
|
||||||
this.selection_changed (previous_position, new_position-previous_position+1);
|
|
||||||
} else {
|
|
||||||
this.selection_changed (new_position, previous_position-new_position+1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this.selection_changed (previous_position, 1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (new_position < this.inner.get_n_items ()) {
|
|
||||||
this.selection_changed (new_position, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// called by anything else that wishes to switch tracks
|
|
||||||
// emits user_selected
|
|
||||||
public void user_select (uint position)
|
|
||||||
requires (position < this.get_n_items ())
|
|
||||||
{
|
|
||||||
this.user_selected (position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void on_inner_items_changed (GLib.ListModel inner, uint position, uint removed, uint added) {
|
|
||||||
// FIXME: potentially try to see if the current item was reordered
|
|
||||||
// see what Gtk.SingleSelection does
|
|
||||||
|
|
||||||
bool emit_signal = false;
|
|
||||||
|
|
||||||
if (this.current_position >= position) {
|
|
||||||
if (this.current_position < position+removed && added == 0) {
|
|
||||||
emit_signal = true;
|
|
||||||
this.current_position = position;
|
|
||||||
} else {
|
|
||||||
this.current_position += added;
|
|
||||||
this.current_position -= removed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.items_changed (position, removed, added);
|
|
||||||
if (emit_signal) this.selection_changed (position, 1);
|
|
||||||
// user_select doesnt need to be signalled, since the playqueue
|
|
||||||
// handles this on its own
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gtk.SelectionModel methods
|
|
||||||
|
|
||||||
Gtk.Bitset get_selection_in_range (uint position, uint n_items)
|
|
||||||
requires (position+n_items <= this.inner.get_n_items ())
|
|
||||||
{
|
|
||||||
var bitset = new Gtk.Bitset.empty ();
|
|
||||||
bitset.add (this.current_position);
|
|
||||||
return bitset;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_selected (uint position)
|
|
||||||
requires (position < this.inner.get_n_items ())
|
|
||||||
{
|
|
||||||
return position == this.current_position;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool select_all () {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool select_item (uint position, bool unselect_rest)
|
|
||||||
requires (position < this.inner.get_n_items ())
|
|
||||||
{
|
|
||||||
if (!unselect_rest) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.user_select (position);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool select_range (uint position, uint n_items, bool unselect_rest) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_selection (Gtk.Bitset selected, Gtk.Bitset mask) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unselect_all () {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unselect_item (uint position) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool unselect_range (uint position, uint n_items) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// GLib.ListView methods
|
|
||||||
|
|
||||||
Object? get_item (uint position) {
|
|
||||||
return this.inner.get_item (position);
|
|
||||||
}
|
|
||||||
|
|
||||||
Type get_item_type () {
|
|
||||||
return this.inner.get_item_type ();
|
|
||||||
}
|
|
||||||
|
|
||||||
uint get_n_items () {
|
|
||||||
return this.inner.get_n_items ();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,7 +18,7 @@ template $UiPlayQueue: Adw.NavigationPage {
|
||||||
ColumnView view {
|
ColumnView view {
|
||||||
styles [ "data-table" ]
|
styles [ "data-table" ]
|
||||||
|
|
||||||
model: bind template.model;
|
model: bind template.selection;
|
||||||
|
|
||||||
//ColumnViewColumn {
|
//ColumnViewColumn {
|
||||||
// factory: SignalListItemFactory {
|
// factory: SignalListItemFactory {
|
||||||
|
|
|
@ -1,13 +1,167 @@
|
||||||
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
|
[GtkTemplate (ui = "/eu/callcc/audrey/ui/play_queue.ui")]
|
||||||
public class Ui.PlayQueue : Adw.NavigationPage {
|
public class Ui.PlayQueue : Adw.NavigationPage {
|
||||||
[GtkChild] private unowned Gtk.ColumnView view;
|
public PlayQueueSelection selection { get; default = new PlayQueueSelection (); }
|
||||||
public Gtk.SelectionModel model { get; set; }
|
|
||||||
|
|
||||||
public bool can_clear_all { get; private set; default = false; }
|
private GLib.ListStore _store;
|
||||||
|
public GLib.ListStore store {
|
||||||
|
get { return _store; }
|
||||||
|
set {
|
||||||
|
// can only be set once
|
||||||
|
assert (_store == null);
|
||||||
|
_store = value;
|
||||||
|
_store.items_changed.connect (this.on_store_items_changed);
|
||||||
|
this.can_clear_all = _store.get_n_items () > 0;
|
||||||
|
this.selection.model = _store;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public signal void clear ();
|
public bool can_clear_all { get; private set; }
|
||||||
|
|
||||||
[GtkCallback] private void on_clear () {
|
[GtkCallback] private void on_clear () {
|
||||||
this.clear ();
|
this.store.remove_all ();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_store_items_changed (GLib.ListModel store, uint position, uint removed, uint added) {
|
||||||
|
this.can_clear_all = store.get_n_items () > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// this is a custom SelectionModel that lets us only signal the
|
||||||
|
// selection has changed on user interaction
|
||||||
|
public class PlayQueueSelection : GLib.Object, GLib.ListModel, Gtk.SelectionModel {
|
||||||
|
private GLib.ListModel _model;
|
||||||
|
public GLib.ListModel model {
|
||||||
|
get { return _model; }
|
||||||
|
set {
|
||||||
|
// can only be set once
|
||||||
|
assert (_model == null);
|
||||||
|
_model = value;
|
||||||
|
_model.items_changed.connect (this.on_model_items_changed);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if current_position == model.get_n_items (), play queue is stopped
|
||||||
|
public uint current_position { get; private set; }
|
||||||
|
|
||||||
|
// emitted when a track is purposefully selected
|
||||||
|
public signal void user_selected (uint position);
|
||||||
|
|
||||||
|
// only called by playbin
|
||||||
|
// does not emit user_selected
|
||||||
|
internal void playbin_select (uint new_position) {
|
||||||
|
var previous_position = this.current_position;
|
||||||
|
this.current_position = new_position;
|
||||||
|
|
||||||
|
if (previous_position < this.get_n_items ()) {
|
||||||
|
if (new_position < this.get_n_items ()) {
|
||||||
|
if (previous_position < new_position) {
|
||||||
|
this.selection_changed (previous_position, new_position-previous_position+1);
|
||||||
|
} else {
|
||||||
|
this.selection_changed (new_position, previous_position-new_position+1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.selection_changed (previous_position, 1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (new_position < this.get_n_items ()) {
|
||||||
|
this.selection_changed (new_position, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// called by anything else that wishes to switch tracks
|
||||||
|
// emits user_selected
|
||||||
|
public void user_select (uint position)
|
||||||
|
requires (position < this.get_n_items ())
|
||||||
|
{
|
||||||
|
this.user_selected (position);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void on_model_items_changed (GLib.ListModel model, uint position, uint removed, uint added) {
|
||||||
|
// FIXME: potentially try to see if the current item was reordered
|
||||||
|
// see what Gtk.SingleSelection does
|
||||||
|
|
||||||
|
bool emit_signal = false;
|
||||||
|
|
||||||
|
if (this.current_position >= position) {
|
||||||
|
if (this.current_position < position+removed && added == 0) {
|
||||||
|
emit_signal = true;
|
||||||
|
this.current_position = position;
|
||||||
|
} else {
|
||||||
|
this.current_position += added;
|
||||||
|
this.current_position -= removed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.items_changed (position, removed, added);
|
||||||
|
if (emit_signal) this.selection_changed (position, 1);
|
||||||
|
// user_select doesnt need to be signalled, since the playqueue
|
||||||
|
// handles this on its own
|
||||||
|
}
|
||||||
|
|
||||||
|
// Gtk.SelectionModel methods
|
||||||
|
|
||||||
|
Gtk.Bitset get_selection_in_range (uint position, uint n_items)
|
||||||
|
requires (position+n_items <= this.get_n_items ())
|
||||||
|
{
|
||||||
|
var bitset = new Gtk.Bitset.empty ();
|
||||||
|
bitset.add (this.current_position);
|
||||||
|
return bitset;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_selected (uint position)
|
||||||
|
requires (position < this.get_n_items ())
|
||||||
|
{
|
||||||
|
return position == this.current_position;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_all () {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_item (uint position, bool unselect_rest)
|
||||||
|
requires (position < this.get_n_items ())
|
||||||
|
{
|
||||||
|
if (!unselect_rest) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.user_select (position);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool select_range (uint position, uint n_items, bool unselect_rest) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool set_selection (Gtk.Bitset selected, Gtk.Bitset mask) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unselect_all () {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unselect_item (uint position) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool unselect_range (uint position, uint n_items) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// GLib.ListModel methods
|
||||||
|
|
||||||
|
Object? get_item (uint position) {
|
||||||
|
return this.model.get_item (position);
|
||||||
|
}
|
||||||
|
|
||||||
|
Type get_item_type () {
|
||||||
|
return this.model.get_item_type ();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint get_n_items () {
|
||||||
|
return this.model.get_n_items ();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ template $UiWindow: Adw.ApplicationWindow {
|
||||||
title: _("Play queue");
|
title: _("Play queue");
|
||||||
|
|
||||||
child: $UiPlayQueue play_queue {
|
child: $UiPlayQueue play_queue {
|
||||||
model: bind template.play_queue_model;
|
store: bind template.play_queue_store;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
public Gdk.Paintable playing_cover_art { get; set; }
|
public Gdk.Paintable playing_cover_art { get; set; }
|
||||||
|
|
||||||
public Playbin playbin { get; private set; default = new Playbin (); }
|
public Playbin playbin { get; private set; default = new Playbin (); }
|
||||||
public Audrey.PlayQueue play_queue_model { get; private set; default = new Audrey.PlayQueue (); }
|
public ListStore play_queue_store { get; private set; default = new ListStore (typeof (Song)); }
|
||||||
|
|
||||||
public Window (Gtk.Application app) {
|
public Window (Gtk.Application app) {
|
||||||
Object (application: app);
|
Object (application: app);
|
||||||
|
@ -54,7 +54,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
() => {},
|
() => {},
|
||||||
() => { error ("could not acquire dbus name"); });
|
() => { error ("could not acquire dbus name"); });
|
||||||
|
|
||||||
this.playbin.play_queue = this.play_queue_model;
|
this.playbin.play_queue = this.play_queue_store;
|
||||||
|
|
||||||
this.setup = new Setup ();
|
this.setup = new Setup ();
|
||||||
|
|
||||||
|
@ -65,10 +65,10 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
this.song = song;
|
this.song = song;
|
||||||
this.duration = duration;
|
this.duration = duration;
|
||||||
api.scrobble.begin (song.id);
|
api.scrobble.begin (song.id);
|
||||||
this.play_queue_model.playbin_select (position);
|
this.play_queue.selection.playbin_select (position);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.play_queue_model.user_selected.connect ((position) => {
|
this.play_queue.selection.user_selected.connect ((position) => {
|
||||||
this.playbin.select_track (position);
|
this.playbin.select_track (position);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,9 +77,9 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
this.shuffle_all_tracks.sensitive = true;
|
this.shuffle_all_tracks.sensitive = true;
|
||||||
this.shuffle_all_tracks.activated.connect (() => {
|
this.shuffle_all_tracks.activated.connect (() => {
|
||||||
this.shuffle_all_tracks.sensitive = false;
|
this.shuffle_all_tracks.sensitive = false;
|
||||||
this.play_queue_model.inner.remove_all ();
|
this.play_queue_store.remove_all ();
|
||||||
api.get_random_songs.begin (null, (song) => {
|
api.get_random_songs.begin (null, (song) => {
|
||||||
this.play_queue_model.inner.append (song);
|
this.play_queue_store.append (song);
|
||||||
}, (obj, res) => {
|
}, (obj, res) => {
|
||||||
try {
|
try {
|
||||||
api.get_random_songs.end (res);
|
api.get_random_songs.end (res);
|
||||||
|
@ -88,7 +88,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
}
|
}
|
||||||
this.shuffle_all_tracks.sensitive = true;
|
this.shuffle_all_tracks.sensitive = true;
|
||||||
|
|
||||||
this.play_queue_model.select_item (0, true);
|
this.playbin.select_track (0);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -196,7 +196,7 @@ class Ui.Window : Adw.ApplicationWindow {
|
||||||
|
|
||||||
[GtkCallback] private void on_skip_backward_clicked () {
|
[GtkCallback] private void on_skip_backward_clicked () {
|
||||||
if (this.playbin.current_position > 0) {
|
if (this.playbin.current_position > 0) {
|
||||||
this.play_queue_model.user_select (this.playbin.current_position-1);
|
this.playbin.select_track (this.playbin.current_position-1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue