diff --git a/src/Application.vala b/src/Application.vala index ce0db16b8..aecbba360 100644 --- a/src/Application.vala +++ b/src/Application.vala @@ -9,12 +9,14 @@ public class Music.Application : Gtk.Application { public const string ACTION_PLAY_PAUSE = "action-play-pause"; public const string ACTION_PREVIOUS = "action-previous"; public const string ACTION_SHUFFLE = "action-shuffle"; + public const string ACTION_FIND = "action-find"; private const ActionEntry[] ACTION_ENTRIES = { { ACTION_PLAY_PAUSE, action_play_pause, null, "false" }, { ACTION_NEXT, action_next }, { ACTION_PREVIOUS, action_previous }, - { ACTION_SHUFFLE, action_shuffle } + { ACTION_SHUFFLE, action_shuffle }, + { ACTION_FIND, action_find } }; private PlaybackManager? playback_manager = null; @@ -40,6 +42,8 @@ public class Music.Application : Gtk.Application { add_action_entries (ACTION_ENTRIES, this); + set_accels_for_action (ACTION_PREFIX + ACTION_FIND, {"F"}); + ((SimpleAction) lookup_action (ACTION_PLAY_PAUSE)).set_enabled (false); ((SimpleAction) lookup_action (ACTION_PLAY_PAUSE)).set_state (false); ((SimpleAction) lookup_action (ACTION_NEXT)).set_enabled (false); @@ -179,6 +183,10 @@ public class Music.Application : Gtk.Application { playback_manager.shuffle (); } + private void action_find () { + ((MainWindow)active_window).start_search (); + } + private void on_bus_acquired (DBusConnection connection, string name) { try { connection.register_object ("/org/mpris/MediaPlayer2", new MprisRoot ()); diff --git a/src/MainWindow.vala b/src/MainWindow.vala index e542cdf63..4fcac567e 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -7,6 +7,8 @@ public class Music.MainWindow : Gtk.ApplicationWindow { private Gtk.Button repeat_button; private Gtk.Button shuffle_button; private Settings settings; + private Gtk.SearchEntry search_entry; + private Gtk.Revealer search_revealer; construct { var playback_manager = PlaybackManager.get_default (); @@ -20,9 +22,20 @@ public class Music.MainWindow : Gtk.ApplicationWindow { repeat_button = new Gtk.Button (); + search_entry = new Gtk.SearchEntry () { + placeholder_text = _("Search titles in playlist") + }; + + search_revealer = new Gtk.Revealer () { + child = search_entry + }; + + playback_manager.bind_property ( + "has-items", search_revealer, "reveal-child", DEFAULT | SYNC_CREATE + ); var queue_header = new Gtk.HeaderBar () { show_title_buttons = false, - title_widget = new Gtk.Label ("") + title_widget = search_revealer }; queue_header.add_css_class (Granite.STYLE_CLASS_DEFAULT_DECORATION); queue_header.pack_start (start_window_controls); @@ -171,6 +184,30 @@ public class Music.MainWindow : Gtk.ApplicationWindow { queue_listbox.row_activated.connect ((row) => { playback_manager.current_audio = ((TrackRow) row).audio_object; }); + + search_entry.search_changed.connect (() => { + int pos = playback_manager.find_title (search_entry.text); + if (pos >= 0) { + queue_listbox.select_row (queue_listbox.get_row_at_index (pos)); + var adj = scrolled.vadjustment; + // Search entry is hidden if n_items is zero so no need to check + var ratio = (double)pos / (double)playback_manager.n_items; + adj.@value = adj.upper * ratio; + } + }); + + search_entry.activate.connect (() => { + var selected = queue_listbox.get_selected_row (); + if (selected != null) { + selected.activate (); + } + }); + } + + public void start_search () { + if (search_revealer.child_revealed) { + search_entry.grab_focus (); + } } private void action_open () { diff --git a/src/PlaybackManager.vala b/src/PlaybackManager.vala index abdc6bdfc..7147863b1 100644 --- a/src/PlaybackManager.vala +++ b/src/PlaybackManager.vala @@ -6,6 +6,12 @@ public class Music.PlaybackManager : Object { public AudioObject? current_audio { get; set; default = null; } public ListStore queue_liststore { get; private set; } + public bool has_items { get; private set; } + public uint n_items { + get { + return queue_liststore != null ? queue_liststore.get_n_items () : 0; + } + } public int64 playback_position { get; private set; } public signal void invalids_found (int count); @@ -50,8 +56,8 @@ public class Music.PlaybackManager : Object { queue_liststore.items_changed.connect (() => { var shuffle_action_action = (SimpleAction) GLib.Application.get_default ().lookup_action (Application.ACTION_SHUFFLE); + has_items = queue_liststore.get_n_items () > 0; shuffle_action_action.set_enabled (queue_liststore.get_n_items () > 1); - update_next_previous_sensitivity (); }); @@ -348,6 +354,28 @@ public class Music.PlaybackManager : Object { } } + public int find_title (string term) { + var search_object = new AudioObject ("") { + title = term + }; + + int found_at = -1; + uint position; + if (queue_liststore.find_with_equal_func ( + search_object, + (a, b) => { + var term_a = ((AudioObject)a).title.down (); + var term_b = ((AudioObject)b).title.down (); + return term_a.contains (term_b); + }, + out position + )) { + found_at = (int)position; + } + + return found_at; + } + private void update_next_previous_sensitivity () { var next_sensitive = false; var previous_sensitive = false;