Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions data/music.metainfo.xml.in
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
<issue url="https://github.com/elementary/music/issues/762">Search mechanism in the app</issue>
<issue url="https://github.com/elementary/music/issues/777">Ctrl+Q to Quit app</issue>
<issue url="https://github.com/elementary/music/issues/787">Ability to remove a track</issue>
<issue url="https://github.com/elementary/music/issues/794">Search function only shows first occurrence of search term</issue>
<issue url="https://github.com/elementary/music/issues/798">Gap below album cover when setting text size greater than 1</issue>
<issue url="https://github.com/elementary/music/issues/803">NowPlaying area resizes when playing song with long artist name</issue>
</issues>
Expand Down
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ config_file = configure_file(
)

adw_dep = dependency('libadwaita-1', version: '>=1.4.0')
granite_dep = dependency('granite-7', version: '>=7.6.0')
granite_dep = dependency('granite-7', version: '>=7.6.0') # Granite.Bin
gstreamer_dep = dependency('gstreamer-1.0')
gstreamer_pbutils_dep = dependency('gstreamer-pbutils-1.0')
gstreamer_tag_dep = dependency('gstreamer-tag-1.0')
Expand Down
42 changes: 27 additions & 15 deletions src/MainWindow.vala
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ public class Music.MainWindow : Gtk.ApplicationWindow {
private const string ACTION_OPEN = "action-open";

private Granite.Placeholder queue_placeholder;
private Granite.Placeholder search_placeholder;
private Gtk.Button repeat_button;
private Gtk.Button shuffle_button;
private SearchBar search_bar;
private Gtk.ListView queue_listview;
private Gtk.Revealer search_revealer;
private Gtk.ScrolledWindow scrolled;
private Gtk.SearchEntry search_entry;
private Gtk.SingleSelection selection_model;
private Gtk.Stack queue_stack;
private Settings settings;
Expand All @@ -30,12 +31,11 @@ public class Music.MainWindow : Gtk.ApplicationWindow {

repeat_button = new Gtk.Button ();

search_entry = new Gtk.SearchEntry () {
placeholder_text = _("Search titles in playlist")
};
ListModel list_model;
search_bar = new SearchBar (playback_manager.queue_liststore, out list_model);

search_revealer = new Gtk.Revealer () {
child = search_entry
child = search_bar
};

playback_manager.bind_property (
Expand All @@ -55,7 +55,12 @@ public class Music.MainWindow : Gtk.ApplicationWindow {
icon = new ThemedIcon ("playlist-queue")
};

selection_model = new Gtk.SingleSelection (playback_manager.queue_liststore);
search_placeholder = new Granite.Placeholder ("") {
description = _("Try changing search terms"),
icon = new ThemedIcon ("edit-find-symbolic")
};

selection_model = new Gtk.SingleSelection (list_model);

var factory = new Gtk.SignalListItemFactory ();

Expand All @@ -71,6 +76,7 @@ public class Music.MainWindow : Gtk.ApplicationWindow {

queue_stack = new Gtk.Stack ();
queue_stack.add_child (queue_placeholder);
queue_stack.add_child (search_placeholder);
queue_stack.add_child (scrolled);

var drop_target = new Gtk.DropTarget (typeof (Gdk.FileList), Gdk.DragAction.COPY);
Expand Down Expand Up @@ -201,6 +207,13 @@ public class Music.MainWindow : Gtk.ApplicationWindow {
return false;
});

playback_manager.queue_liststore.items_changed.connect (() => {
if (playback_manager.n_items == 0) {
queue_stack.visible_child = queue_placeholder;
search_bar.search_entry.text = "";
}
});

playback_manager.invalids_found.connect ((count) => {
error_toast.title = ngettext (
"%d invalid file was not added to the queue",
Expand Down Expand Up @@ -234,14 +247,7 @@ public class Music.MainWindow : Gtk.ApplicationWindow {

selection_model.items_changed.connect (on_items_changed);

search_entry.search_changed.connect (() => {
int pos = playback_manager.find_title (search_entry.text);
if (pos >= 0) {
queue_listview.scroll_to (pos, SELECT, null);
}
});

search_entry.activate.connect (() => {
search_bar.activated.connect (() => {
var selected = selection_model.get_selected ();
if (selected != -1) {
var selected_audio = (AudioObject) selection_model.get_item (selected);
Expand All @@ -252,7 +258,7 @@ public class Music.MainWindow : Gtk.ApplicationWindow {

public void start_search () {
if (search_revealer.child_revealed) {
search_entry.grab_focus ();
search_bar.start_search ();
}
}

Expand Down Expand Up @@ -330,6 +336,12 @@ public class Music.MainWindow : Gtk.ApplicationWindow {
private void on_items_changed () {
if (selection_model.n_items > 0) {
queue_stack.visible_child = scrolled;
return;
}

if (search_bar.search_entry.text != "") {
search_placeholder.title = _("No Results for “%s”").printf (search_bar.search_entry.text);
queue_stack.visible_child = search_placeholder;
} else {
queue_stack.visible_child = queue_placeholder;
}
Expand Down
22 changes: 0 additions & 22 deletions src/PlaybackManager.vala
Original file line number Diff line number Diff line change
Expand Up @@ -412,28 +412,6 @@ 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;
Expand Down
53 changes: 53 additions & 0 deletions src/Widgets/SearchBar.vala
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io)
*/

public class Music.SearchBar : Granite.Bin {
public signal void activated ();

public ListModel list_model { get; construct; }

public Gtk.SearchEntry search_entry { get; private set; }

private Gtk.StringFilter filter;
private Gtk.FilterListModel filter_model;

/**
* @param new_model the new model with the search applied. Make sure to use this one in further UI
* instead of the old given model.
*/
public SearchBar (ListModel list_model, out ListModel new_model) {
Object (list_model: list_model);

new_model = filter_model;
}

construct {
var expression = new Gtk.PropertyExpression (typeof (AudioObject), null, "title");

filter = new Gtk.StringFilter (expression) {
ignore_case = true,
match_mode = SUBSTRING
};

filter_model = new Gtk.FilterListModel (list_model, filter);

search_entry = new Gtk.SearchEntry () {
placeholder_text = _("Search titles in playlist")
};

child = search_entry;

search_entry.search_changed.connect (on_search_changed);
search_entry.activate.connect (() => activated ());
}

private void on_search_changed () {
filter.search = search_entry.text;
}

public void start_search () {
search_entry.grab_focus ();
}
}
1 change: 1 addition & 0 deletions src/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ sources = [
'DBus/MprisRoot.vala',
'Views/NowPlayingView.vala',
'Widgets/AlbumImage.vala',
'Widgets/SearchBar.vala',
'Widgets/SeekBar.vala',
'Widgets/TrackRow.vala',
]
Expand Down