diff --git a/po/POTFILES b/po/POTFILES index 79a80daea..72cd9ae15 100644 --- a/po/POTFILES +++ b/po/POTFILES @@ -2,6 +2,7 @@ src/Application.vala src/MainWindow.vala src/PlaybackManager.vala src/Views/NowPlayingView.vala +src/Views/QueueView.vala src/Widgets/AlbumImage.vala src/Widgets/SearchBar.vala src/Widgets/SeekBar.vala diff --git a/src/MainWindow.vala b/src/MainWindow.vala index da40c5cd9..f48ad96ec 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -4,136 +4,13 @@ */ public class Music.MainWindow : Gtk.ApplicationWindow { - private const string ACTION_PREFIX = "win."; - private const string ACTION_OPEN = "action-open"; + public const string ACTION_PREFIX = "win."; + public 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.SingleSelection selection_model; - private Gtk.Stack queue_stack; - private Settings settings; + private QueueView queue_view; construct { - var playback_manager = PlaybackManager.get_default (); - - var start_window_controls = new Gtk.WindowControls (Gtk.PackType.START); - - shuffle_button = new Gtk.Button.from_icon_name ("media-playlist-shuffle-symbolic") { - action_name = Application.ACTION_PREFIX + Application.ACTION_SHUFFLE, - tooltip_text = _("Shuffle") - }; - - repeat_button = new Gtk.Button (); - - search_bar = new SearchBar (playback_manager.queue_liststore); - - search_revealer = new Gtk.Revealer () { - child = search_bar - }; - - 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 = search_revealer - }; - queue_header.add_css_class (Granite.STYLE_CLASS_DEFAULT_DECORATION); - queue_header.pack_start (start_window_controls); - queue_header.pack_end (shuffle_button); - queue_header.pack_end (repeat_button); - - queue_placeholder = new Granite.Placeholder (_("Queue is Empty")) { - description = _("Audio files opened from Files will appear here"), - icon = new ThemedIcon ("playlist-queue") - }; - - search_placeholder = new Granite.Placeholder ("") { - description = _("Try changing search terms"), - icon = new ThemedIcon ("edit-find-symbolic") - }; - - selection_model = new Gtk.SingleSelection (search_bar.filter_model); - - var factory = new Gtk.SignalListItemFactory (); - - queue_listview = new Gtk.ListView (selection_model, factory) { - single_click_activate = true, - hexpand = true, - vexpand = true - }; - - scrolled = new Gtk.ScrolledWindow () { - child = queue_listview - }; - - 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); - - var add_button_label = new Gtk.Label (_("Open Files…")); - - var add_button_box = new Gtk.Box (HORIZONTAL, 0); - add_button_box.append (new Gtk.Image.from_icon_name ("document-open-symbolic")); - add_button_box.append (add_button_label); - - var open_action = new SimpleAction (ACTION_OPEN, null); - open_action.activate.connect (open_files); - - var add_button = new Gtk.Button () { - child = add_button_box, - action_name = ACTION_PREFIX + ACTION_OPEN - }; - add_button.add_css_class (Granite.STYLE_CLASS_FLAT); - - add_button_label.mnemonic_widget = add_button; - - var clear_button_label = new Gtk.Label (_("Clear Queue")); - - var clear_button_box = new Gtk.Box (HORIZONTAL, 0); - clear_button_box.append (new Gtk.Image.from_icon_name ("edit-clear-all-symbolic")); - clear_button_box.append (clear_button_label); - - var clear_button = new Gtk.Button () { - child = clear_button_box, - action_name = Application.ACTION_PREFIX + Application.ACTION_CLEAR_QUEUE - }; - clear_button.add_css_class (Granite.STYLE_CLASS_FLAT); - - clear_button_label.mnemonic_widget = clear_button; - - var queue_action_bar = new Gtk.ActionBar (); - queue_action_bar.pack_start (add_button); - queue_action_bar.pack_end (clear_button); - - var queue = new Adw.ToolbarView () { - bottom_bar_style = RAISED, - content = queue_stack - }; - queue.add_controller (drop_target); - queue.add_css_class (Granite.STYLE_CLASS_VIEW); - queue.add_top_bar (queue_header); - queue.add_bottom_bar (queue_action_bar); - - var error_toast = new Granite.Toast (""); - - var queue_overlay = new Gtk.Overlay () { - child = queue - }; - queue_overlay.add_overlay (error_toast); - - var queue_handle = new Gtk.WindowHandle () { - child = queue_overlay - }; + queue_view = new QueueView (); var end_window_controls = new Gtk.WindowControls (Gtk.PackType.END); @@ -162,7 +39,7 @@ public class Music.MainWindow : Gtk.ApplicationWindow { }; var paned = new Gtk.Paned (Gtk.Orientation.HORIZONTAL) { - start_child = queue_handle, + start_child = queue_view, end_child = now_playing_handle, resize_end_child = false, shrink_end_child = false, @@ -177,94 +54,19 @@ public class Music.MainWindow : Gtk.ApplicationWindow { }; set_titlebar (null_title); - settings = new Settings ("io.elementary.music"); + var settings = new Settings ("io.elementary.music"); settings.bind ("pane-position", paned, "position", SettingsBindFlags.DEFAULT); - settings.changed["repeat-mode"].connect (update_repeat_button); - update_repeat_button (); + var open_action = new SimpleAction (ACTION_OPEN, null); + open_action.activate.connect (open_files); + add_action (open_action); unowned var app = ((Gtk.Application) GLib.Application.get_default ()); app.set_accels_for_action (ACTION_PREFIX + ACTION_OPEN, {"O"}); - - add_action (open_action); - - drop_target.drop.connect ((target, value, x, y) => { - if (value.type () == typeof (Gdk.FileList)) { - var list = (Gdk.FileList)value; - - File[] file_array = {}; - foreach (unowned var file in list.get_files ()) { - file_array += file; - } - - var files_to_play = Application.loop_through_files (file_array); - PlaybackManager.get_default ().queue_files (files_to_play); - - return true; - } - - 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", - "%d invalid files were not added to the queue", - count).printf (count); - error_toast.send_notification (); - }); - - repeat_button.clicked.connect (() => { - var enum_step = settings.get_enum ("repeat-mode"); - if (enum_step < 2) { - settings.set_enum ("repeat-mode", enum_step + 1); - } else { - settings.set_enum ("repeat-mode", 0); - } - }); - - factory.setup.connect ((obj) => { - var list_item = (Gtk.ListItem) obj; - list_item.child = new TrackRow (); - }); - - factory.bind.connect ((obj) => { - var list_item = (Gtk.ListItem) obj; - ((TrackRow) list_item.child).audio_object = (AudioObject) list_item.item; - }); - - queue_listview.activate.connect ((index) => { - playback_manager.current_audio = (AudioObject) selection_model.get_item (index); - }); - - selection_model.items_changed.connect (on_items_changed); - - search_bar.activated.connect (() => { - var selected = selection_model.get_selected (); - if (selected != -1) { - var selected_audio = (AudioObject) selection_model.get_item (selected); - playback_manager.current_audio = selected_audio; - } - }); - - search_bar.search_entry.search_changed.connect (() => { - if (selection_model.n_items <= 0 && search_bar.search_entry.text != "") { - search_placeholder.title = _("No Results for “%s”").printf (search_bar.search_entry.text); - } - }); } public void start_search () { - if (search_revealer.child_revealed) { - search_bar.start_search (); - } + queue_view.start_search (); } private void open_files () { @@ -320,34 +122,4 @@ public class Music.MainWindow : Gtk.ApplicationWindow { } }); } - - private void update_repeat_button () { - switch (settings.get_string ("repeat-mode")) { - case "disabled": - repeat_button.icon_name = "media-playlist-no-repeat-symbolic"; - repeat_button.tooltip_text = _("Repeat None"); - break; - case "all": - repeat_button.icon_name = "media-playlist-repeat-symbolic"; - repeat_button.tooltip_text = _("Repeat All"); - break; - case "one": - repeat_button.icon_name = "media-playlist-repeat-song-symbolic"; - repeat_button.tooltip_text = _("Repeat One"); - break; - } - } - - private void on_items_changed () { - if (selection_model.n_items > 0) { - queue_stack.visible_child = scrolled; - return; - } - - if (search_bar.search_entry.text != "") { - queue_stack.visible_child = search_placeholder; - } else { - queue_stack.visible_child = queue_placeholder; - } - } } diff --git a/src/Views/QueueView.vala b/src/Views/QueueView.vala new file mode 100644 index 000000000..04dc0138f --- /dev/null +++ b/src/Views/QueueView.vala @@ -0,0 +1,247 @@ +/* + * SPDX-License-Identifier: LGPL-3.0-or-later + * SPDX-FileCopyrightText: 2025 elementary, Inc. (https://elementary.io) + */ + +public class Music.QueueView : Granite.Bin { + 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.SingleSelection selection_model; + private Gtk.Stack queue_stack; + private Settings settings; + + construct { + var playback_manager = PlaybackManager.get_default (); + + var start_window_controls = new Gtk.WindowControls (Gtk.PackType.START); + + shuffle_button = new Gtk.Button.from_icon_name ("media-playlist-shuffle-symbolic") { + action_name = Application.ACTION_PREFIX + Application.ACTION_SHUFFLE, + tooltip_text = _("Shuffle") + }; + + repeat_button = new Gtk.Button (); + + search_bar = new SearchBar (playback_manager.queue_liststore); + + search_revealer = new Gtk.Revealer () { + child = search_bar + }; + + 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 = search_revealer + }; + queue_header.add_css_class (Granite.STYLE_CLASS_DEFAULT_DECORATION); + queue_header.pack_start (start_window_controls); + queue_header.pack_end (shuffle_button); + queue_header.pack_end (repeat_button); + + queue_placeholder = new Granite.Placeholder (_("Queue is Empty")) { + description = _("Audio files opened from Files will appear here"), + icon = new ThemedIcon ("playlist-queue") + }; + + search_placeholder = new Granite.Placeholder ("") { + description = _("Try changing search terms"), + icon = new ThemedIcon ("edit-find-symbolic") + }; + + selection_model = new Gtk.SingleSelection (search_bar.filter_model); + + var factory = new Gtk.SignalListItemFactory (); + + queue_listview = new Gtk.ListView (selection_model, factory) { + single_click_activate = true, + hexpand = true, + vexpand = true + }; + + scrolled = new Gtk.ScrolledWindow () { + child = queue_listview + }; + + 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); + + var add_button_label = new Gtk.Label (_("Open Files…")); + + var add_button_box = new Gtk.Box (HORIZONTAL, 0); + add_button_box.append (new Gtk.Image.from_icon_name ("document-open-symbolic")); + add_button_box.append (add_button_label); + + var add_button = new Gtk.Button () { + child = add_button_box, + action_name = MainWindow.ACTION_PREFIX + MainWindow.ACTION_OPEN + }; + add_button.add_css_class (Granite.STYLE_CLASS_FLAT); + + add_button_label.mnemonic_widget = add_button; + + var clear_button_label = new Gtk.Label (_("Clear Queue")); + + var clear_button_box = new Gtk.Box (HORIZONTAL, 0); + clear_button_box.append (new Gtk.Image.from_icon_name ("edit-clear-all-symbolic")); + clear_button_box.append (clear_button_label); + + var clear_button = new Gtk.Button () { + child = clear_button_box, + action_name = Application.ACTION_PREFIX + Application.ACTION_CLEAR_QUEUE + }; + clear_button.add_css_class (Granite.STYLE_CLASS_FLAT); + + clear_button_label.mnemonic_widget = clear_button; + + var queue_action_bar = new Gtk.ActionBar (); + queue_action_bar.pack_start (add_button); + queue_action_bar.pack_end (clear_button); + + var queue = new Adw.ToolbarView () { + bottom_bar_style = RAISED, + content = queue_stack + }; + queue.add_controller (drop_target); + queue.add_css_class (Granite.STYLE_CLASS_VIEW); + queue.add_top_bar (queue_header); + queue.add_bottom_bar (queue_action_bar); + + var error_toast = new Granite.Toast (""); + + var queue_overlay = new Gtk.Overlay () { + child = queue + }; + queue_overlay.add_overlay (error_toast); + + var queue_handle = new Gtk.WindowHandle () { + child = queue_overlay + }; + + child = queue_handle; + + settings = new Settings ("io.elementary.music"); + settings.changed["repeat-mode"].connect (update_repeat_button); + + update_repeat_button (); + + drop_target.drop.connect ((target, value, x, y) => { + if (value.type () == typeof (Gdk.FileList)) { + var list = (Gdk.FileList)value; + + File[] file_array = {}; + foreach (unowned var file in list.get_files ()) { + file_array += file; + } + + var files_to_play = Application.loop_through_files (file_array); + PlaybackManager.get_default ().queue_files (files_to_play); + + return true; + } + + 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", + "%d invalid files were not added to the queue", + count).printf (count); + error_toast.send_notification (); + }); + + repeat_button.clicked.connect (() => { + var enum_step = settings.get_enum ("repeat-mode"); + if (enum_step < 2) { + settings.set_enum ("repeat-mode", enum_step + 1); + } else { + settings.set_enum ("repeat-mode", 0); + } + }); + + factory.setup.connect ((obj) => { + var list_item = (Gtk.ListItem) obj; + list_item.child = new TrackRow (); + }); + + factory.bind.connect ((obj) => { + var list_item = (Gtk.ListItem) obj; + ((TrackRow) list_item.child).audio_object = (AudioObject) list_item.item; + }); + + queue_listview.activate.connect ((index) => { + playback_manager.current_audio = (AudioObject) selection_model.get_item (index); + }); + + selection_model.items_changed.connect (on_items_changed); + + search_bar.activated.connect (() => { + var selected = selection_model.get_selected (); + if (selected != -1) { + var selected_audio = (AudioObject) selection_model.get_item (selected); + playback_manager.current_audio = selected_audio; + } + }); + + search_bar.search_entry.search_changed.connect (() => { + if (selection_model.n_items <= 0 && search_bar.search_entry.text != "") { + search_placeholder.title = _("No Results for “%s”").printf (search_bar.search_entry.text); + } + }); + } + + public void start_search () { + if (search_revealer.child_revealed) { + search_bar.start_search (); + } + } + + private void update_repeat_button () { + switch (settings.get_string ("repeat-mode")) { + case "disabled": + repeat_button.icon_name = "media-playlist-no-repeat-symbolic"; + repeat_button.tooltip_text = _("Repeat None"); + break; + case "all": + repeat_button.icon_name = "media-playlist-repeat-symbolic"; + repeat_button.tooltip_text = _("Repeat All"); + break; + case "one": + repeat_button.icon_name = "media-playlist-repeat-song-symbolic"; + repeat_button.tooltip_text = _("Repeat One"); + break; + } + } + + private void on_items_changed () { + if (selection_model.n_items > 0) { + queue_stack.visible_child = scrolled; + return; + } + + if (search_bar.search_entry.text != "") { + queue_stack.visible_child = search_placeholder; + } else { + queue_stack.visible_child = queue_placeholder; + } + } +} diff --git a/src/meson.build b/src/meson.build index a2eef2584..04d907412 100644 --- a/src/meson.build +++ b/src/meson.build @@ -6,6 +6,7 @@ sources = [ 'DBus/MprisPlayer.vala', 'DBus/MprisRoot.vala', 'Views/NowPlayingView.vala', + 'Views/QueueView.vala', 'Widgets/AlbumImage.vala', 'Widgets/SearchBar.vala', 'Widgets/SeekBar.vala',