diff --git a/data/com.github.alainm23.byte.appdata.xml.in b/data/com.github.alainm23.byte.appdata.xml.in index 9bc98ee..6cd2fdf 100644 --- a/data/com.github.alainm23.byte.appdata.xml.in +++ b/data/com.github.alainm23.byte.appdata.xml.in @@ -23,6 +23,14 @@ com.github.alainm23.byte + + +

Fix some error and performance improvement

+
    +
  • Now you can update and delete your playlist
  • +
+
+

Fix some error and performance improvement

diff --git a/data/com.github.alainm23.byte.gschema.xml b/data/com.github.alainm23.byte.gschema.xml index 86e3555..650a568 100644 --- a/data/com.github.alainm23.byte.gschema.xml +++ b/data/com.github.alainm23.byte.gschema.xml @@ -3,7 +3,7 @@ - + diff --git a/data/resources/byte-album-symbolic.svg b/data/resources/byte-album-symbolic.svg index bfe3b96..adf5df1 100644 --- a/data/resources/byte-album-symbolic.svg +++ b/data/resources/byte-album-symbolic.svg @@ -10,7 +10,7 @@ viewBox="0 0 16 16" version="1.1" id="svg6" - sodipodi:docname="planner-album-symbolic.svg" + sodipodi:docname="byte-album-symbolic.svg" width="16" height="16" inkscape:version="0.92.3 (2405546, 2018-03-11)"> @@ -41,9 +41,9 @@ inkscape:window-height="705" id="namedview8" showgrid="false" - inkscape:zoom="23.069359" - inkscape:cx="3.8726963" - inkscape:cy="6.4002787" + inkscape:zoom="11.53468" + inkscape:cx="-14.020049" + inkscape:cy="3.7464256" inkscape:window-x="0" inkscape:window-y="30" inkscape:window-maximized="1" @@ -51,12 +51,12 @@ diff --git a/data/stylesheet.css b/data/stylesheet.css index 2ef50bf..b166e07 100644 --- a/data/stylesheet.css +++ b/data/stylesheet.css @@ -169,6 +169,7 @@ selection { .quick-find-cancel { color: @colorAccent; font-weight: bold; + padding-right: 0px; } .queue { diff --git a/meson.build b/meson.build index 763c691..143c47c 100644 --- a/meson.build +++ b/meson.build @@ -1,6 +1,6 @@ project('com.github.alainm23.byte', 'vala', 'c', - version: '0.1.2') + version: '0.1.3') gnome = import('gnome') i18n = import('i18n') diff --git a/src/Dialogs/Settings.vala b/src/Dialogs/Settings.vala index b5c69a6..5a862d5 100644 --- a/src/Dialogs/Settings.vala +++ b/src/Dialogs/Settings.vala @@ -127,6 +127,7 @@ public class Dialogs.Settings : Gtk.Dialog { var settings_04_label = new Gtk.Label (_("Music folder location")); var library_filechooser = new Gtk.FileChooserButton (_("Select Music Folder…"), Gtk.FileChooserAction.SELECT_FOLDER); + library_filechooser.valign = Gtk.Align.CENTER; File library_path = File.new_for_uri (Byte.settings.get_string ("library-location")); library_filechooser.set_current_folder (library_path.get_path ()); diff --git a/src/MainWindow.vala b/src/MainWindow.vala index 612bc56..48d8ecc 100644 --- a/src/MainWindow.vala +++ b/src/MainWindow.vala @@ -11,6 +11,7 @@ public class MainWindow : Gtk.Window { private Views.Playlists playlists_view; private Views.Favorites favorites_view; private Views.Playlist playlist_view; + private Views.Artist artist_view; private Views.Album album_view; @@ -53,6 +54,7 @@ public class MainWindow : Gtk.Window { playlists_view = new Views.Playlists (); favorites_view = new Views.Favorites (); playlist_view = new Views.Playlist (); + artist_view = new Views.Artist (); library_stack.add_named (home_view, "home_view"); library_stack.add_named (albums_view, "albums_view"); @@ -63,6 +65,7 @@ public class MainWindow : Gtk.Window { library_stack.add_named (playlists_view, "playlists_view"); library_stack.add_named (favorites_view, "favorites_view"); library_stack.add_named (playlist_view, "playlist_view"); + library_stack.add_named (artist_view, "artist_view"); var library_view = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); library_view.pack_start (media_control, false, false, 0); @@ -176,7 +179,6 @@ public class MainWindow : Gtk.Window { home_view.go_artists_view.connect (() => { library_stack.visible_child_name = "artists_view"; - artists_view.get_all_artists (); }); home_view.go_radios_view.connect (() => { @@ -195,7 +197,14 @@ public class MainWindow : Gtk.Window { quick_find.reveal = !quick_find.reveal_child; }); + artists_view.go_artist.connect ((artist) => { + library_stack.visible_child_name = "artist_view"; + artist_view.artist = artist; + }); + artist_view.go_back.connect (() => { + library_stack.visible_child_name = "artists_view"; + }); Byte.database.reset_library.connect (() => { main_stack.visible_child_name = "welcome_view"; diff --git a/src/Objects/Radio.vala b/src/Objects/Radio.vala index e29d508..a8cc980 100644 --- a/src/Objects/Radio.vala +++ b/src/Objects/Radio.vala @@ -7,6 +7,7 @@ public class Objects.Radio : GLib.Object { public string favicon { get; set; default = ""; } public string country { get; set; default = ""; } public string state { get; set; default = ""; } + public string votes { get; set; default = ""; } public Radio () { } diff --git a/src/Services/Database.vala b/src/Services/Database.vala index 424169c..7cd4015 100644 --- a/src/Services/Database.vala +++ b/src/Services/Database.vala @@ -9,10 +9,14 @@ public class Services.Database : GLib.Object { public signal void adden_new_playlist (Objects.Playlist playlist); public signal void removed_track (int id); + public signal void removed_playlist (int id); public signal void updated_album_cover (int album_id); public signal void updated_track_cover (int track_id); + public signal void updated_playlist_cover (int playlist_id); + public signal void updated_track_favorite (Objects.Track track, int favorite); + public signal void updated_playlist (Objects.Playlist playlist); public signal void reset_library (); @@ -506,6 +510,40 @@ public class Services.Database : GLib.Object { return track; } + public Gee.ArrayList get_all_albums_by_artist (int id) { + Sqlite.Statement stmt; + string sql; + int res; + + sql = """ + SELECT albums.id, albums.artist_id, albums.year, albums.title, albums.genre, artists.name from albums + INNER JOIN artists ON artists.id = albums.artist_id WHERE artists.id = ? ORDER BY albums.year DESC; + """; + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + res = stmt.bind_int (1, id); + assert (res == Sqlite.OK); + + var all = new Gee.ArrayList (); + + while ((res = stmt.step()) == Sqlite.ROW) { + var album = new Objects.Album (); + + album.id = stmt.column_int (0); + album.artist_id = stmt.column_int (1); + album.year = stmt.column_int (2); + album.title = stmt.column_text (3); + album.genre = stmt.column_text (4); + album.artist_name = stmt.column_text (5); + + all.add (album); + } + + return all; + } + public Gee.ArrayList get_all_albums () { Sqlite.Statement stmt; string sql; @@ -661,6 +699,51 @@ public class Services.Database : GLib.Object { return all; } + public Gee.ArrayList get_all_tracks_by_artist (int id) { + Sqlite.Statement stmt; + string sql; + int res; + + sql = """ + SELECT tracks.id, tracks.path, tracks.title, tracks.duration, tracks.is_favorite, tracks.date_added, + tracks.album_id, albums.title, artists.id, artists.name, tracks.favorite_added, tracks.last_played, tracks.play_count FROM tracks + INNER JOIN albums ON tracks.album_id = albums.id + INNER JOIN artists ON albums.artist_id = artists.id WHERE artists.id = ? ORDER BY tracks.play_count DESC; + """; + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + res = stmt.bind_int (1, id); + assert (res == Sqlite.OK); + + var all = new Gee.ArrayList (); + int index = 0; + + while ((res = stmt.step()) == Sqlite.ROW) { + var track = new Objects.Track (); + + track.track_order = index; + track.id = stmt.column_int (0); + track.path = stmt.column_text (1); + track.title = stmt.column_text (2); + track.duration = stmt.column_int64 (3); + track.is_favorite = stmt.column_int (4); + track.date_added = stmt.column_text (5); + track.album_id = stmt.column_int (6); + track.album_title = stmt.column_text (7); + track.artist_name = stmt.column_text (9); + track.favorite_added = stmt.column_text (10); + track.last_played = stmt.column_text (11); + track.play_count = stmt.column_int (12); + + all.add (track); + index = index + 1; + } + + return all; + } + public Gee.ArrayList get_all_tracks_order_by (int item, bool is_reverse) { Sqlite.Statement stmt; string sql; @@ -989,6 +1072,32 @@ public class Services.Database : GLib.Object { return all; } + public Gee.ArrayList get_all_artists_search (string search_text) { + Sqlite.Statement stmt; + string sql; + int res; + + sql = """ + SELECT artists.id, artists.name FROM artists WHERE artists.name LIKE '%s'; + """.printf ("%" + search_text + "%"); + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + var all = new Gee.ArrayList (); + + while ((res = stmt.step()) == Sqlite.ROW) { + var artist = new Objects.Artist (); + + artist.id = stmt.column_int (0); + artist.name = stmt.column_text (1); + + all.add (artist); + } + + return all; + } + public int get_tracks_number () { /* Sqlite.Statement stmt; @@ -1059,12 +1168,10 @@ public class Services.Database : GLib.Object { if (stmt.step () == Sqlite.ROW) { radio.id = stmt.column_int (0); - //stdout.printf ("Radio ID: %d - %s\n", radio.id, radio.name); - - //Byte.cover_import.import (track); - Byte.utils.download_image ("radio", radio.id, radio.favicon); adden_new_radio (radio); + + Byte.utils.download_image ("radio", radio.id, radio.favicon); } else { warning ("Error: %d: %s", db.errcode (), db.errmsg ()); } @@ -1208,6 +1315,8 @@ public class Services.Database : GLib.Object { if (stmt.step () == Sqlite.DONE) { print ("Track: %i agregado \n".printf (track_id)); + playlist.date_updated = new GLib.DateTime.now_local ().to_string (); + Byte.database.update_playlist (playlist); } } @@ -1375,6 +1484,46 @@ public class Services.Database : GLib.Object { return false; } + public bool remove_playlist_from_library (Objects.Playlist playlist) { + Sqlite.Statement stmt; + string sql; + int res; + + sql = """ + DELETE FROM playlists WHERE id = ?; + """; + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + res = stmt.bind_int (1, playlist.id); + assert (res == Sqlite.OK); + + if (stmt.step () == Sqlite.DONE) { + stmt.reset (); + + sql = """ + DELETE FROM playlist_tracks WHERE playlist_id = ?; + """; + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + res = stmt.bind_int (1, playlist.id); + assert (res == Sqlite.OK); + + if (stmt.step () == Sqlite.DONE) { + removed_playlist (playlist.id); + return true; + } else { + warning ("Error: %d: %s", db.errcode (), db.errmsg ()); + return false; + } + } + + return false; + } + public bool remove_from_playlist (Objects.Track track) { Sqlite.Statement stmt; string sql; @@ -1427,4 +1576,61 @@ public class Services.Database : GLib.Object { directory.dispose (); } + + public void update_playlist (Objects.Playlist playlist) { + Sqlite.Statement stmt; + string sql; + int res; + + sql = """ + UPDATE playlists SET title = ?, note = ?, date_updated = ? WHERE id = ?; + """; + + res = db.prepare_v2 (sql, -1, out stmt); + assert (res == Sqlite.OK); + + res = stmt.bind_text (1, playlist.title); + assert (res == Sqlite.OK); + + res = stmt.bind_text (2, playlist.note); + assert (res == Sqlite.OK); + + res = stmt.bind_text (3, playlist.date_updated); + assert (res == Sqlite.OK); + + res = stmt.bind_int (4, playlist.id); + assert (res == Sqlite.OK); + + res = stmt.step (); + + if (res == Sqlite.DONE) { + updated_playlist (playlist); + } + } + + public Objects.Playlist? get_playlist_by_title (string title) { + foreach (var playlist in Byte.database.get_all_playlists ()) { + if (playlist.title == title) { + return playlist; + } + } + return null; + } + + public Objects.Playlist create_new_playlist () { + string new_title = _ ("New Playlist"); + string next_title = new_title; + + var playlist = get_playlist_by_title (next_title); + for (int i = 1; playlist != null; i++) { + next_title = "%s (%d)".printf (new_title, i); + playlist = get_playlist_by_title (next_title); + } + + playlist = new Objects.Playlist (); + playlist.title = next_title; + playlist.id = insert_playlist (playlist); + + return playlist; + } } diff --git a/src/Services/RadioBrowser.vala b/src/Services/RadioBrowser.vala index 8c0b014..aef395d 100644 --- a/src/Services/RadioBrowser.vala +++ b/src/Services/RadioBrowser.vala @@ -39,7 +39,7 @@ public class Services.RadioBrowser : GLib.Object { cancel (); cancellable = new Cancellable (); - string url = API_URL + name + "?limit=100"; + string url = API_URL + name + "?order=votes&reverse=true&limit=100"; var message = new Soup.Message ("GET", url); try { var stream = yield session.send_async (message, cancellable); diff --git a/src/Utils.vala b/src/Utils.vala index d6c183a..0f7a55d 100644 --- a/src/Utils.vala +++ b/src/Utils.vala @@ -7,6 +7,8 @@ public class Utils : GLib.Object { public signal void add_next_track (Gee.ArrayList items); public signal void add_last_track (Gee.ArrayList items); + public signal void radio_image_downloaded (int id); + public string MAIN_FOLDER; public string COVER_FOLDER; public Utils () { @@ -105,18 +107,22 @@ public class Utils : GLib.Object { public Objects.Track? get_next_track (Objects.Track current_track) { int index = get_track_index_by_id (current_track.id, queue_playlist) + 1; - - if (index >= queue_playlist.size) { - var repeat_mode = Byte.settings.get_enum ("repeat-mode"); + Objects.Track? returned = null; + var repeat_mode = Byte.settings.get_enum ("repeat-mode"); + if (index >= queue_playlist.size) { if (repeat_mode == 0) { - return null; + returned = null; } else if (repeat_mode == 1) { - index = 0; + returned = queue_playlist [0]; + } else { + returned = null; } + } else { + returned = queue_playlist [index]; } - return queue_playlist [index]; + return returned; } public Objects.Track get_prev_track (Objects.Track current_track) { @@ -184,6 +190,9 @@ public class Utils : GLib.Object { try { if (file_from_uri.copy_async.end (res)) { print ("Image Downloaded\n"); + if (type == "radio") { + radio_image_downloaded (id); + } } } catch (Error e) { download_image (type, id, url); diff --git a/src/Views/Album.vala b/src/Views/Album.vala index a5a57e4..ba34607 100644 --- a/src/Views/Album.vala +++ b/src/Views/Album.vala @@ -64,7 +64,9 @@ public class Views.Album : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); diff --git a/src/Views/Albums.vala b/src/Views/Albums.vala index e170f83..9566cce 100644 --- a/src/Views/Albums.vala +++ b/src/Views/Albums.vala @@ -24,7 +24,9 @@ public class Views.Albums : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -60,7 +62,7 @@ public class Views.Albums : Gtk.EventBox { search_revealer.reveal_child = false; var sort_button = new Gtk.ToggleButton (); - sort_button.margin = 6; + sort_button.margin = 3; sort_button.can_focus = false; sort_button.add (new Gtk.Image.from_icon_name ("byte-sort-symbolic", Gtk.IconSize.MENU)); sort_button.tooltip_text = _("Sort"); diff --git a/src/Views/Artist.vala b/src/Views/Artist.vala new file mode 100644 index 0000000..5a70942 --- /dev/null +++ b/src/Views/Artist.vala @@ -0,0 +1,166 @@ +public class Views.Artist : Gtk.EventBox { + private Gtk.Label name_label; + private Widgets.Cover image_cover; + private Gtk.ListBox listbox; + private Gtk.FlowBox flowbox; + + public signal void go_back (string page); + public string back_page { set; get; } + + private Gee.ArrayList all_tracks; + private Gee.ArrayList all_albums; + + public Objects.Artist? _artist; + public Objects.Artist artist { + set { + _artist = value; + name_label.label = _artist.name; + + int item_max = 5; + + listbox.foreach ((widget) => { + widget.destroy (); + }); + + flowbox.foreach ((widget) => { + widget.destroy (); + }); + + if (Byte.scan_service.is_sync == false) { + all_tracks = new Gee.ArrayList (); + all_tracks = Byte.database.get_all_tracks_by_artist (_artist.id); + + if (item_max > all_tracks.size) { + item_max = all_tracks.size; + } + + for (int i = 0; i < item_max; i++) { + var row = new Widgets.TrackRow (all_tracks [i]); + + listbox.add (row); + listbox.show_all (); + } + + all_albums = new Gee.ArrayList (); + all_albums = Byte.database.get_all_albums_by_artist (_artist.id); + + foreach (var item in all_albums) { + var row = new Widgets.AlbumArtistChild (item); + flowbox.add (row); + flowbox.show_all (); + } + } + + try { + var cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("artist-%i.jpg").printf (_artist.id)); + var pixbuf = new Gdk.Pixbuf.from_file_at_size (cover_path, 64, 64); + image_cover.pixbuf = pixbuf; + } catch (Error e) { + image_cover.set_with_default_icon (64, "artist"); + } + } + + get { + return _artist; + } + } + + public Artist () {} + + construct { + get_style_context ().add_class (Gtk.STYLE_CLASS_VIEW); + get_style_context ().add_class ("w-round"); + + var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); + back_button.can_focus = false; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; + back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + back_button.get_style_context ().add_class ("label-color-primary"); + + var center_label = new Gtk.Label (_("Artist")); + center_label.use_markup = true; + center_label.valign = Gtk.Align.CENTER; + center_label.get_style_context ().add_class ("h3"); + center_label.get_style_context ().add_class ("label-color-primary"); + + var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + header_box.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); + header_box.pack_start (back_button, false, false, 0); + header_box.set_center_widget (center_label); + + image_cover = new Widgets.Cover.with_default_icon (128, "artist"); + image_cover.halign = Gtk.Align.CENTER; + image_cover.valign = Gtk.Align.CENTER; + + name_label = new Gtk.Label (null); + name_label.halign = Gtk.Align.CENTER; + name_label.valign = Gtk.Align.CENTER; + name_label.wrap = true; + name_label.wrap_mode = Pango.WrapMode.CHAR; + name_label.justify = Gtk.Justification.FILL; + name_label.get_style_context ().add_class ("font-bold"); + name_label.get_style_context ().add_class ("h3"); + + var most_played_label = new Gtk.Label ("%s".printf (_("Most Played"))); + most_played_label.get_style_context ().add_class ("label-color-primary"); + most_played_label.get_style_context ().add_class ("h3"); + most_played_label.margin_start = 6; + most_played_label.halign = Gtk.Align.START; + most_played_label.use_markup = true; + + listbox = new Gtk.ListBox (); + //listbox.hexpand = true; + + var albums_label = new Gtk.Label ("%s".printf (_("Albums"))); + albums_label.get_style_context ().add_class ("label-color-primary"); + albums_label.get_style_context ().add_class ("h3"); + albums_label.margin_start = 7; + albums_label.halign = Gtk.Align.START; + albums_label.use_markup = true; + + flowbox = new Gtk.FlowBox (); + flowbox.min_children_per_line = 2; + flowbox.max_children_per_line = 2; + + var detail_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + detail_box.get_style_context ().add_class (Granite.STYLE_CLASS_WELCOME); + detail_box.pack_start (image_cover, false, false, 6); + detail_box.pack_start (name_label, false, false, 6); + detail_box.pack_start (most_played_label, false, false, 3); + detail_box.pack_start (new Gtk.Separator (Gtk.Orientation.HORIZONTAL), false, false, 0); + detail_box.pack_start (listbox, false, false, 0); + detail_box.pack_start (albums_label, false, false, 3); + //detail_box.pack_start (new Gtk.Separator (Gtk.Orientation.HORIZONTAL), false, false, 0); + detail_box.pack_start (flowbox, false, false, 0); + + var main_scrolled = new Gtk.ScrolledWindow (null, null); + main_scrolled.margin_bottom = 48; + main_scrolled.hscrollbar_policy = Gtk.PolicyType.NEVER; + main_scrolled.expand = true; + main_scrolled.add (detail_box); + + var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + main_box.expand = true; + main_box.pack_start (header_box, false, false, 0); + main_box.pack_start (new Gtk.Separator (Gtk.Orientation.HORIZONTAL), false, false, 0); + main_box.pack_start (main_scrolled, true, true, 0); + + add (main_box); + + back_button.clicked.connect (() => { + go_back (back_page); + }); + + listbox.row_activated.connect ((row) => { + var item = row as Widgets.TrackRow; + + Byte.utils.set_items ( + all_tracks, + Byte.settings.get_boolean ("shuffle-mode"), + item.track + ); + }); + } +} \ No newline at end of file diff --git a/src/Views/Artists.vala b/src/Views/Artists.vala index ec9d6dc..9204bda 100644 --- a/src/Views/Artists.vala +++ b/src/Views/Artists.vala @@ -1,20 +1,32 @@ public class Views.Artists : Gtk.EventBox { private Gtk.ListBox listbox; - public signal void go_back (); + private Gtk.SearchEntry search_entry; + public signal void go_back (); + public signal void go_artist (Objects.Artist artist); + + private int item_index; + private int item_max; - private bool is_initialized = false; + private Gee.ArrayList all_items; public Artists () { } - construct { + construct { + item_index = 0; + item_max = 25; + + all_items = Byte.database.get_all_artists (); + get_style_context ().add_class (Gtk.STYLE_CLASS_VIEW); get_style_context ().add_class ("w-round"); - + var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -23,29 +35,34 @@ public class Views.Artists : Gtk.EventBox { title_label.valign = Gtk.Align.CENTER; title_label.get_style_context ().add_class ("h3"); - var search_entry = new Gtk.SearchEntry (); + search_entry = new Gtk.SearchEntry (); + search_entry.margin = 6; search_entry.valign = Gtk.Align.CENTER; - search_entry.width_request = 250; + search_entry.hexpand = true; search_entry.get_style_context ().add_class ("search-entry"); - search_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); - search_entry.placeholder_text = _("Your library"); + search_entry.tooltip_text = _("Search by title, artist and album"); + search_entry.placeholder_text = _("Search by title, artist and album"); - var center_stack = new Gtk.Stack (); - center_stack.hexpand = true; - center_stack.transition_type = Gtk.StackTransitionType.CROSSFADE; + var search_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + search_box.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); + search_box.add (search_entry); + search_box.add (new Gtk.Separator (Gtk.Orientation.HORIZONTAL)); - center_stack.add_named (title_label, "title_label"); - center_stack.add_named (search_entry, "search_entry"); - - center_stack.visible_child_name = "title_label"; + var search_revealer = new Gtk.Revealer (); + search_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_UP; + search_revealer.add (search_box); + search_revealer.reveal_child = false; var search_button = new Gtk.Button.from_icon_name ("edit-find-symbolic", Gtk.IconSize.MENU); - search_button.margin = 6; + search_button.margin = 3; + search_button.can_focus = false; + search_button.get_style_context ().add_class ("label-color-primary"); search_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + header_box.get_style_context ().add_class (Gtk.STYLE_CLASS_BACKGROUND); header_box.pack_start (back_button, false, false, 0); - header_box.set_center_widget (center_stack); + header_box.set_center_widget (title_label); header_box.pack_end (search_button, false, false, 0); listbox = new Gtk.ListBox (); @@ -60,17 +77,59 @@ public class Views.Artists : Gtk.EventBox { main_box.expand = true; main_box.pack_start (header_box, false, false, 0); main_box.pack_start (new Gtk.Separator (Gtk.Orientation.HORIZONTAL), false, false, 0); + main_box.pack_start (search_revealer, false, false, 0); main_box.pack_start (scrolled, true, true, 0); add (main_box); + add_all_items (); back_button.clicked.connect (() => { go_back (); }); - Byte.database.added_new_artist.connect ((track) => { + scrolled.edge_reached.connect((pos)=> { + if (pos == Gtk.PositionType.BOTTOM) { + + item_index = item_max; + item_max = item_max + 100; + + if (item_max > all_items.size) { + item_max = all_items.size; + } + + add_all_items (); + } + }); + + search_button.clicked.connect (() => { + if (search_revealer.reveal_child) { + search_revealer.reveal_child = false; + } else { + search_revealer.reveal_child = true; + search_entry.grab_focus (); + } + }); + + search_entry.key_release_event.connect ((key) => { + if (key.keyval == 65307) { + search_revealer.reveal_child = false; + search_entry.text = ""; + } + + return false; + }); + + search_entry.activate.connect (start_search); + search_entry.search_changed.connect (start_search); + + listbox.row_activated.connect ((row) => { + var item = row as Widgets.ArtistRow; + go_artist (item.artist); + }); + + Byte.database.added_new_artist.connect ((artist) => { Idle.add (() => { - add_artist (track); + add_artist (artist); return false; }); @@ -83,6 +142,33 @@ public class Views.Artists : Gtk.EventBox { }); } + private void start_search () { + if (search_entry.text != "") { + item_index = 0; + item_max = 100; + + listbox.foreach ((widget) => { + widget.destroy (); + }); + + all_items = Byte.database.get_all_artists_search ( + search_entry.text.down () + ); + + add_all_items (); + } else { + item_index = 0; + item_max = 100; + + listbox.foreach ((widget) => { + widget.destroy (); + }); + + all_items = Byte.database.get_all_artists (); + add_all_items (); + } + } + private void add_artist (Objects.Artist artist) { if (artist.id != 0) { var row = new Widgets.ArtistRow (artist); @@ -92,27 +178,16 @@ public class Views.Artists : Gtk.EventBox { } } - public void get_all_artists () { - if (is_initialized == false) { - Timeout.add (120, () => { - new Thread ("get_all_artists", () => { - var all_artists = new Gee.ArrayList (); - all_artists = Byte.database.get_all_artists (); - - foreach (var item in all_artists) { - Idle.add (() => { - add_artist (item); - - return false; - }); - } - - is_initialized = true; - return null; - }); - - return false; - }); + public void add_all_items () { + if (item_max > all_items.size) { + item_max = all_items.size; } + + for (int i = item_index; i < item_max; i++) { + var row = new Widgets.ArtistRow (all_items [i]); + + listbox.add (row); + listbox.show_all (); + } } } \ No newline at end of file diff --git a/src/Views/Favorites.vala b/src/Views/Favorites.vala index 8c53c7f..8901833 100644 --- a/src/Views/Favorites.vala +++ b/src/Views/Favorites.vala @@ -18,7 +18,9 @@ public class Views.Favorites : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); diff --git a/src/Views/Home.vala b/src/Views/Home.vala index d558794..c0c32ba 100644 --- a/src/Views/Home.vala +++ b/src/Views/Home.vala @@ -38,7 +38,6 @@ public class Views.Home : Gtk.EventBox { var songs_button = new Widgets.HomeButton (_("Songs"), "folder-music-symbolic"); var artists_button = new Widgets.HomeButton (_("Artists"), "byte-artist-symbolic"); - artists_button.sensitive = false; var radios_button = new Widgets.HomeButton (_("Radios"), "byte-radio-symbolic"); var favorites_button = new Widgets.HomeButton (_("Favorites"), "byte-favorite-symbolic"); diff --git a/src/Views/Playlist.vala b/src/Views/Playlist.vala index 99187e5..07c4c8f 100644 --- a/src/Views/Playlist.vala +++ b/src/Views/Playlist.vala @@ -1,4 +1,9 @@ public class Views.Playlist : Gtk.EventBox { + public Gtk.Entry title_entry; + private Gtk.TextView note_text; + private Gtk.Label note_placeholder; + private Gtk.Stack right_stack; + private Gtk.Label title_label; private Gtk.Label note_label; private Gtk.Label time_label; @@ -14,50 +19,61 @@ public class Views.Playlist : Gtk.EventBox { private Gee.ArrayList all_tracks; - public Objects.Playlist? _playlist; + public Objects.Playlist _playlist { get; set; } public Objects.Playlist playlist { set { - _playlist = value; + if (value != null) { + _playlist = value; - print ("Title: %s\n".printf (_playlist.title)); + title_label.label = _playlist.title; + title_entry.text = _playlist.title; - title_label.label = _playlist.title; - note_label.label = _playlist.note; - //time_label.label = "25 songs - 3h, 2 min"; - update_relative_label.label = Byte.utils.get_relative_datetime (_playlist.date_updated); + note_label.label = _playlist.note; + note_text.buffer.text = _playlist.note; - try { - cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("playlist-%i.jpg").printf (_playlist.id)); - var pixbuf = new Gdk.Pixbuf.from_file_at_size (cover_path, 128, 128); - image_cover.pixbuf = pixbuf; - } catch (Error e) { - var pixbuf = new Gdk.Pixbuf.from_file_at_size ("/usr/share/com.github.alainm23.byte/album-default-cover.svg", 128, 128); - image_cover.pixbuf = pixbuf; - } + if (_playlist.note != "") { + note_placeholder.visible = false; + } - listbox.foreach ((widget) => { - widget.destroy (); - }); + update_relative_label.label = Byte.utils.get_relative_datetime (_playlist.date_updated); - if (Byte.scan_service.is_sync == false) { - all_tracks = new Gee.ArrayList (); - all_tracks = Byte.database.get_all_tracks_by_playlist ( - _playlist.id, - Byte.settings.get_enum ("playlist-sort"), - Byte.settings.get_boolean ("playlist-order-reverse") - ); - - foreach (var item in all_tracks) { - var row = new Widgets.TrackRow (item); - listbox.add (row); + if (_playlist.note == "") { + note_label.visible = false; + } + + try { + cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("playlist-%i.jpg").printf (_playlist.id)); + var pixbuf = new Gdk.Pixbuf.from_file_at_size (cover_path, 128, 128); + image_cover.pixbuf = pixbuf; + } catch (Error e) { + var pixbuf = new Gdk.Pixbuf.from_file_at_size ("/usr/share/com.github.alainm23.byte/album-default-cover.svg", 128, 128); + image_cover.pixbuf = pixbuf; + } + + listbox.foreach ((widget) => { + widget.destroy (); + }); + + if (Byte.scan_service.is_sync == false) { + all_tracks = new Gee.ArrayList (); + all_tracks = Byte.database.get_all_tracks_by_playlist ( + _playlist.id, + Byte.settings.get_enum ("playlist-sort"), + Byte.settings.get_boolean ("playlist-order-reverse") + ); + + foreach (var item in all_tracks) { + print ("Track: %s\n".printf (item.title)); + var row = new Widgets.TrackRow (item); + listbox.add (row); + } + + listbox.show_all (); + + time_label.label = "%i songs".printf (all_tracks.size); } - - listbox.show_all (); } } - get { - return _playlist; - } } public Playlist () {} @@ -68,7 +84,9 @@ public class Views.Playlist : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -79,7 +97,7 @@ public class Views.Playlist : Gtk.EventBox { center_label.get_style_context ().add_class ("label-color-primary"); var sort_button = new Gtk.ToggleButton (); - sort_button.margin = 6; + sort_button.margin = 3; sort_button.can_focus = false; sort_button.add (new Gtk.Image.from_icon_name ("byte-sort-symbolic", Gtk.IconSize.MENU)); sort_button.tooltip_text = _("Sort"); @@ -101,8 +119,7 @@ public class Views.Playlist : Gtk.EventBox { sort_popover.radio_04_label = _("Date Added"); sort_popover.radio_05_label = _("Play Count"); - //cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("album-%i.jpg").printf (album.id)); - image_cover = new Widgets.Cover.with_default_icon (64, "playlist"); + image_cover = new Widgets.Cover.with_default_icon (128, "playlist"); image_cover.halign = Gtk.Align.START; image_cover.valign = Gtk.Align.START; @@ -116,30 +133,66 @@ public class Views.Playlist : Gtk.EventBox { note_label = new Gtk.Label (null); note_label.wrap = true; - note_label.margin = 6; - note_label.margin_start = 16; - note_label.wrap_mode = Pango.WrapMode.CHAR; + note_label.margin_bottom = 6; + note_label.margin_start = 12; + note_label.margin_end = 12; + note_label.wrap_mode = Pango.WrapMode.WORD; note_label.justify = Gtk.Justification.FILL; - note_label.get_style_context ().add_class ("dim-label"); note_label.halign = Gtk.Align.START; - note_label.selectable = true; time_label = new Gtk.Label (null); - time_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + time_label.get_style_context ().add_class ("h3"); time_label.wrap = true; time_label.justify = Gtk.Justification.FILL; time_label.wrap_mode = Pango.WrapMode.CHAR; time_label.halign = Gtk.Align.START; + var menu_icon = new Gtk.Image (); + menu_icon.gicon = new ThemedIcon ("view-more-symbolic"); + menu_icon.pixel_size = 14; + + var menu_button = new Gtk.MenuButton (); + menu_button.can_focus = false; + menu_button.valign = Gtk.Align.CENTER; + menu_button.tooltip_text = _("Edit Name and Appearance"); + menu_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + menu_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + menu_button.get_style_context ().add_class ("label-color-primary"); + menu_button.image = menu_icon; + + /* Items */ + var edit_menuitem = new Widgets.ModelButton (_("Edit Details"), "edit-symbolic", _("Edit Details")); + var cover_menuitem = new Widgets.ModelButton (_("Set new Cover"), "image-x-generic-symbolic", _("Set new Cover")); + var delete_menuitem = new Widgets.ModelButton (_("Delete"), "edit-delete-symbolic", _("Delete")); + + var menu_grid = new Gtk.Grid (); + menu_grid.margin_top = 6; + menu_grid.margin_bottom = 6; + menu_grid.orientation = Gtk.Orientation.VERTICAL; + menu_grid.width_request = 165; + + menu_grid.add (cover_menuitem); + menu_grid.add (edit_menuitem); + menu_grid.add (delete_menuitem); + + menu_grid.show_all (); + + var menu_popover = new Gtk.Popover (null); + menu_popover.add (menu_grid); + menu_button.popover = menu_popover; + + var h_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); + h_box.hexpand = true; + h_box.pack_start (time_label, false, false, 0); + h_box.pack_end (menu_button, false, false, 0); + var update_label = new Gtk.Label (_("Updated")); - update_label.margin_top = 6; update_label.halign = Gtk.Align.START; update_label.ellipsize = Pango.EllipsizeMode.END; - update_label.get_style_context ().add_class (Gtk.STYLE_CLASS_DIM_LABEL); + update_label.get_style_context ().add_class ("font-bold"); update_relative_label = new Gtk.Label (null); update_relative_label.halign = Gtk.Align.START; - update_relative_label.get_style_context ().add_class ("font-bold"); update_relative_label.ellipsize = Pango.EllipsizeMode.END; var play_button = new Gtk.Button.from_icon_name ("media-playback-start-symbolic", Gtk.IconSize.MENU); @@ -163,17 +216,54 @@ public class Views.Playlist : Gtk.EventBox { action_grid.add (shuffle_button); var detail_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + detail_box.margin_bottom = 3; detail_box.get_style_context ().add_class (Granite.STYLE_CLASS_WELCOME); - detail_box.pack_start (title_label, false, false, 0); - //detail_box.pack_start (time_label, false, false, 0); + detail_box.pack_start (title_label, false, false, 6); detail_box.pack_start (update_label, false, false, 0); detail_box.pack_start (update_relative_label, false, false, 0); - - var album_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); - album_box.hexpand = true; - album_box.margin = 6; - album_box.pack_start (image_cover, false, false, 0); - album_box.pack_start (detail_box, false, false, 0); + detail_box.pack_end (h_box, false, false, 6); + + // Edit view + title_entry = new Gtk.Entry (); + title_entry.margin_top = 6; + title_entry.placeholder_text = _("New Playlist"); + title_entry.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + title_entry.valign = Gtk.Align.START; + + note_text = new Gtk.TextView (); + note_text.height_request = 35; + note_text.wrap_mode = Gtk.WrapMode.WORD_CHAR; + note_text.hexpand = true; + + var note_scrolled = new Gtk.ScrolledWindow (null, null); + note_scrolled.margin_start = 3; + note_scrolled.add (note_text); + + note_placeholder = new Gtk.Label (_("Add Description")); + note_placeholder.opacity = 0.6; + note_text.add (note_placeholder); + + var update_button = new Gtk.Button.with_label (_("Save")); + update_button.halign = Gtk.Align.END; + update_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); + update_button.get_style_context ().add_class ("quick-find-cancel"); + + var edit_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); + edit_box.pack_start (title_entry, false, false, 0); + edit_box.pack_start (note_scrolled, true, true, 6); + edit_box.pack_end (update_button, false, false, 0); + + right_stack = new Gtk.Stack (); + right_stack.transition_type = Gtk.StackTransitionType.CROSSFADE; + + right_stack.add_named (detail_box, "detail_box"); + right_stack.add_named (edit_box, "edit_box"); + + var album_grid = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6); + album_grid.hexpand = true; + album_grid.margin = 6; + album_grid.add (image_cover); + album_grid.add (right_stack); listbox = new Gtk.ListBox (); listbox.expand = true; @@ -188,8 +278,8 @@ public class Views.Playlist : Gtk.EventBox { var scrolled_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0); scrolled_box.expand = true; - scrolled_box.pack_start (album_box, false, false, 0); - //scrolled_box.pack_start (note_label, false, false, 0); + scrolled_box.pack_start (album_grid, false, false, 0); + scrolled_box.pack_start (note_label, false, false, 0); scrolled_box.pack_start (separator, false, false, 0); scrolled_box.pack_start (action_grid, false, false, 0); scrolled_box.pack_start (separator_2, false, false, 0); @@ -292,28 +382,99 @@ public class Views.Playlist : Gtk.EventBox { listbox.show_all (); }); + + delete_menuitem.clicked.connect (() => { + menu_popover.popdown (); + + var message_dialog = new Granite.MessageDialog.with_image_from_icon_name ( + "Delete from library?", + "Are you sure you want to delete %s from your library?".printf (_playlist.title), + "dialog-warning", + Gtk.ButtonsType.CANCEL + ); - /* - Byte.database.adden_new_track.connect ((track) => { - if (_album != null && track.album_id == _album.id) { - var row = new Widgets.TrackAlbumRow (track); - listbox.add (row); - listbox.show_all (); + var set_button = new Gtk.Button.with_label (_("Delete")); + set_button.get_style_context ().add_class (Gtk.STYLE_CLASS_DESTRUCTIVE_ACTION); + message_dialog.add_action_widget (set_button, Gtk.ResponseType.ACCEPT); + + message_dialog.show_all (); + + if (message_dialog.run () == Gtk.ResponseType.ACCEPT) { + //Byte.database.remove_from_library (track); + if (Byte.database.remove_playlist_from_library (_playlist)) { + go_back (back_page); + } + } + + message_dialog.destroy (); + }); + + edit_menuitem.clicked.connect (() => { + menu_popover.popdown (); + + if (right_stack.visible_child_name == "detail_box") { + right_stack.visible_child_name = "edit_box"; + note_label.visible = false; + } else { + right_stack.visible_child_name = "detail_box"; + note_label.visible = true; } }); - Byte.database.updated_album_cover.connect ((album_id) => { - if (_album != null && album_id == _album.id) { + cover_menuitem.clicked.connect (() => { + menu_popover.popdown (); + + var new_cover = Byte.utils.choose_new_cover (); + if (new_cover != null) { + cover_path = new_cover; try { - image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ( - GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("album-%i.jpg").printf (album_id)), - 128, - 128); - } catch (Error e) { - stderr.printf ("Error setting default avatar icon: %s ", e.message); + var pixbuf = Byte.utils.align_and_scale_pixbuf ( + new Gdk.Pixbuf.from_file (cover_path), + 128 + ); + + image_cover.pixbuf = pixbuf; + string playlist_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("playlist-%i.jpg").printf (_playlist.id)); + + if (pixbuf.save (playlist_path, "jpeg", "quality", "100")) { + Byte.database.updated_playlist_cover (_playlist.id); + } + } catch (Error err) { + warning (err.message); } } }); - */ + + note_text.focus_in_event.connect (() => { + note_placeholder.visible = false; + return false; + }); + + note_text.focus_out_event.connect (() => { + if (note_text.buffer.text == "") { + note_placeholder.visible = true; + } + return false; + }); + + title_entry.activate.connect (update); + update_button.clicked.connect (update); + } + + private void update () { + if (title_entry.text != "") { + _playlist.title = title_entry.text; + _playlist.note = note_text.buffer.text; + _playlist.date_updated = new GLib.DateTime.now_local ().to_string (); + + title_label.label = _playlist.title; + note_label.label = _playlist.note; + update_relative_label.label = Byte.utils.get_relative_datetime (_playlist.date_updated); + + right_stack.visible_child_name = "detail_box"; + note_label.visible = true; + + Byte.database.update_playlist (_playlist); + } } } \ No newline at end of file diff --git a/src/Views/Playlists.vala b/src/Views/Playlists.vala index 8655168..3e5909b 100644 --- a/src/Views/Playlists.vala +++ b/src/Views/Playlists.vala @@ -21,7 +21,9 @@ public class Views.Playlists : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -35,7 +37,7 @@ public class Views.Playlists : Gtk.EventBox { add_button.can_focus = false; add_button.valign = Gtk.Align.CENTER; add_button.halign = Gtk.Align.CENTER; - add_button.margin = 6; + add_button.margin = 3; add_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); add_button.get_style_context ().add_class ("label-color-primary"); add_button.tooltip_text = _("Add New Playlist"); diff --git a/src/Views/Radios.vala b/src/Views/Radios.vala index c8c6597..ac59a46 100644 --- a/src/Views/Radios.vala +++ b/src/Views/Radios.vala @@ -19,7 +19,9 @@ public class Views.Radios : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -31,7 +33,7 @@ public class Views.Radios : Gtk.EventBox { var internet_radio_button = new Gtk.Button.from_icon_name ("internet-radio-symbolic", Gtk.IconSize.MENU); internet_radio_button.can_focus = false; internet_radio_button.tooltip_text = _("Search internet radios"); - internet_radio_button.margin = 6; + internet_radio_button.margin = 3; internet_radio_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); internet_radio_button.get_style_context ().add_class ("label-color-primary"); diff --git a/src/Views/Tracks.vala b/src/Views/Tracks.vala index 0838ab1..a5cf145 100644 --- a/src/Views/Tracks.vala +++ b/src/Views/Tracks.vala @@ -35,7 +35,9 @@ public class Views.Tracks : Gtk.EventBox { var back_button = new Gtk.Button.from_icon_name ("byte-arrow-back-symbolic", Gtk.IconSize.MENU); back_button.can_focus = false; - back_button.margin = 6; + back_button.margin = 3; + back_button.margin_bottom = 6; + back_button.margin_top = 6; back_button.get_style_context ().add_class (Gtk.STYLE_CLASS_FLAT); back_button.get_style_context ().add_class ("label-color-primary"); @@ -70,7 +72,7 @@ public class Views.Tracks : Gtk.EventBox { search_revealer.reveal_child = false; var sort_button = new Gtk.ToggleButton (); - sort_button.margin = 6; + sort_button.margin = 3; sort_button.can_focus = false; sort_button.add (new Gtk.Image.from_icon_name ("byte-sort-symbolic", Gtk.IconSize.MENU)); sort_button.tooltip_text = _("Sort"); @@ -178,7 +180,6 @@ public class Views.Tracks : Gtk.EventBox { }); search_entry.activate.connect (start_search); - search_entry.search_changed.connect (start_search); sort_button.toggled.connect (() => { diff --git a/src/Widgets/AlbumArtistChild.vala b/src/Widgets/AlbumArtistChild.vala new file mode 100644 index 0000000..20b3f36 --- /dev/null +++ b/src/Widgets/AlbumArtistChild.vala @@ -0,0 +1,62 @@ +public class Widgets.AlbumArtistChild : Gtk.FlowBoxChild { + public Objects.Album album { get; construct; } + + private Gtk.Label primary_label; + private Gtk.Label secondary_label; + private Widgets.Cover image_cover; + + public AlbumArtistChild (Objects.Album album) { + Object ( + album: album + ); + } + + construct { + tooltip_text = album.title; + + primary_label = new Gtk.Label (album.title); + primary_label.get_style_context ().add_class ("font-bold"); + primary_label.ellipsize = Pango.EllipsizeMode.END; + primary_label.halign = Gtk.Align.START; + primary_label.max_width_chars = 45; + primary_label.valign = Gtk.Align.END; + + secondary_label = new Gtk.Label ("%i".printf (album.year)); + secondary_label.halign = Gtk.Align.START; + secondary_label.valign = Gtk.Align.START; + secondary_label.ellipsize = Pango.EllipsizeMode.END; + + image_cover = new Widgets.Cover.from_file ( + GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("album-%i.jpg").printf (album.id)), 32, "track"); + image_cover.halign = Gtk.Align.START; + image_cover.valign = Gtk.Align.START; + + var main_grid = new Gtk.Grid (); + main_grid.margin_top = 1; + main_grid.margin_start = 3; + main_grid.margin_end = 9; + main_grid.column_spacing = 3; + main_grid.attach (image_cover, 0, 0, 1, 2); + main_grid.attach (primary_label, 1, 0, 1, 1); + main_grid.attach (secondary_label, 1, 1, 1, 1); + + add (main_grid); + + Byte.database.updated_album_cover.connect ((album_id) => { + Idle.add (() => { + if (album_id == album.id) { + try { + image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ( + GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("album-%i.jpg").printf (album_id)), + 64, + 64); + } catch (Error e) { + stderr.printf ("Error setting default avatar icon: %s ", e.message); + } + } + + return false; + }); + }); + } +} \ No newline at end of file diff --git a/src/Widgets/ArtistRow.vala b/src/Widgets/ArtistRow.vala index bcb0249..0ecec94 100644 --- a/src/Widgets/ArtistRow.vala +++ b/src/Widgets/ArtistRow.vala @@ -12,6 +12,8 @@ public class Widgets.ArtistRow : Gtk.ListBoxRow { } construct { + get_style_context ().add_class ("album-row"); + name_label = new Gtk.Label (artist.name); name_label.valign = Gtk.Align.CENTER; name_label.get_style_context ().add_class ("h3"); diff --git a/src/Widgets/HeaderBar.vala b/src/Widgets/HeaderBar.vala index 314d4c1..20ca6fa 100644 --- a/src/Widgets/HeaderBar.vala +++ b/src/Widgets/HeaderBar.vala @@ -192,15 +192,15 @@ public class Widgets.HeaderBar : Gtk.HeaderBar { Byte.player.mode_changed.connect ((mode) => { if (mode == "radio") { - shuffle_button.visible = false; - repeat_button.visible = false; - next_button.visible = false; - previous_button.visible = false; + shuffle_button.sensitive = false; + repeat_button.sensitive = false; + next_button.sensitive = false; + previous_button.sensitive = false; } else { - shuffle_button.visible = true; - repeat_button.visible = true; - next_button.visible = true; - previous_button.visible = true; + shuffle_button.sensitive = true; + repeat_button.sensitive = true; + next_button.sensitive = true; + previous_button.sensitive = true; } }); diff --git a/src/Widgets/MediaControl.vala b/src/Widgets/MediaControl.vala index baaf9da..3132292 100644 --- a/src/Widgets/MediaControl.vala +++ b/src/Widgets/MediaControl.vala @@ -75,7 +75,7 @@ public class Widgets.MediaControl : Gtk.Revealer { var header_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0); header_box.margin = 3; header_box.margin_start = 4; - header_box.margin_end = 6; + header_box.margin_end = 3; header_box.pack_start (image_cover, false, false, 0); header_box.set_center_widget (metainfo_box); header_box.pack_end (favorite_revealer, false, false, 0); @@ -125,7 +125,7 @@ public class Widgets.MediaControl : Gtk.Revealer { Byte.player.current_track_changed.connect ((track) => { title_label.label = track.title; - subtitle_label.label = "%s - %s".printf (track.artist_name, track.album_title); + subtitle_label.label = "%s — %s".printf (track.artist_name, track.album_title); string cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("track-%i.jpg").printf (track.id)); image_cover.set_from_file (cover_path, 32, "track"); diff --git a/src/Widgets/PlaylistRow.vala b/src/Widgets/PlaylistRow.vala index 383f171..e5b849b 100644 --- a/src/Widgets/PlaylistRow.vala +++ b/src/Widgets/PlaylistRow.vala @@ -21,18 +21,10 @@ public class Widgets.PlaylistRow : Gtk.ListBoxRow { title_label.halign = Gtk.Align.START; title_label.valign = Gtk.Align.END; - /* - var tracks_label = new Gtk.Label ("Updated %s".printf( - Granite.DateTime.get_relative_datetime ( - new GLib.DateTime.from_iso8601 (playlist.date_updated, new GLib.TimeZone.local ()) - )) - ); - */ - var tracks_label = new Gtk.Label (null); - tracks_label.get_style_context ().add_class ("h3"); - tracks_label.halign = Gtk.Align.START; + var tracks_label = new Gtk.Label ("Updated %s".printf(Byte.utils.get_relative_datetime (playlist.date_updated))); tracks_label.valign = Gtk.Align.START; - + tracks_label.halign = Gtk.Align.START; + cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("playlist-%i.jpg").printf (playlist.id)); image_cover = new Widgets.Cover.from_file (cover_path, 64, "playlist"); @@ -44,5 +36,31 @@ public class Widgets.PlaylistRow : Gtk.ListBoxRow { main_grid.attach (tracks_label, 1, 1, 1, 1); add (main_grid); + + Byte.database.removed_playlist.connect ((id) => { + if (playlist.id == id) { + destroy (); + } + }); + + Byte.database.updated_playlist.connect ((p) => { + if (playlist.id == p.id) { + title_label.label = p.title; + tracks_label.label = "Updated %s".printf(Byte.utils.get_relative_datetime (p.date_updated)); + } + }); + + Byte.database.updated_playlist_cover.connect ((id) => { + if (playlist.id == id) { + try { + image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ( + GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("playlist-%i.jpg").printf (playlist.id)), + 64, + 64); + } catch (Error e) { + stderr.printf ("Error setting default avatar icon: %s ", e.message); + } + } + }); } } \ No newline at end of file diff --git a/src/Widgets/Queue.vala b/src/Widgets/Queue.vala index f385f65..d064ac7 100644 --- a/src/Widgets/Queue.vala +++ b/src/Widgets/Queue.vala @@ -209,7 +209,7 @@ public class Widgets.Queue : Gtk.Revealer { Byte.player.set_track (items [0]); } else { Byte.player.set_track (_track); - + int current_index = Byte.utils.get_track_index_by_id (_track.id, items); listbox.set_filter_func ((row) => { @@ -233,8 +233,8 @@ public class Widgets.Queue : Gtk.Revealer { var index = row.get_index (); return index >= current_index; }); - - var next_track = Byte.utils.get_next_track (track); + + Objects.Track? next_track = Byte.utils.get_next_track (track); if (next_track != null) { reveal_child = true; @@ -249,9 +249,21 @@ public class Widgets.Queue : Gtk.Revealer { image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ("/usr/share/com.github.alainm23.byte/track-default-cover.svg", 27, 27); stderr.printf ("Error setting default avatar icon: %s ", e.message); } + } else { + reveal_child = false; } }); + Byte.player.state_changed.connect ((state) => { + if (state == Gst.State.PLAYING) { + if (Byte.player.current_track != null) { + reveal_child = true; + } + } else if (state == Gst.State.NULL) { + reveal_child = false; + } + }); + Byte.utils.update_next_track.connect (() => { var next_track = Byte.utils.get_next_track (Byte.player.current_track); @@ -297,7 +309,7 @@ public class Widgets.Queue : Gtk.Revealer { Byte.utils.update_next_track (); }); - + Byte.utils.add_last_track.connect ((_items) => { listbox.foreach ((widget) => { widget.destroy (); diff --git a/src/Widgets/RadioRow.vala b/src/Widgets/RadioRow.vala index de9a3f2..bfe0058 100644 --- a/src/Widgets/RadioRow.vala +++ b/src/Widgets/RadioRow.vala @@ -3,7 +3,6 @@ public class Widgets.RadioRow : Gtk.ListBoxRow { private Gtk.Label name_label; private Gtk.Label country_state_label; - private Widgets.Cover image_cover; public signal void send_notification_error (); @@ -36,14 +35,14 @@ public class Widgets.RadioRow : Gtk.ListBoxRow { name_label.halign = Gtk.Align.START; name_label.valign = Gtk.Align.END; - country_state_label = new Gtk.Label ("%s - %s".printf (radio.country, radio.state)); + country_state_label = new Gtk.Label (radio.country); country_state_label.halign = Gtk.Align.START; country_state_label.valign = Gtk.Align.START; country_state_label.max_width_chars = 45; country_state_label.ellipsize = Pango.EllipsizeMode.END; var cover_path = GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("radio-%i.jpg").printf (radio.id)); - image_cover = new Widgets.Cover.from_file (cover_path, 48, "radio"); + var image_cover = new Widgets.Cover.from_file (cover_path, 48, "radio"); image_cover.halign = Gtk.Align.START; image_cover.valign = Gtk.Align.START; @@ -65,7 +64,7 @@ public class Widgets.RadioRow : Gtk.ListBoxRow { var remove_revealer = new Gtk.Revealer (); remove_revealer.halign = Gtk.Align.END; remove_revealer.hexpand = true; - remove_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_LEFT ; + remove_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_LEFT; remove_revealer.add (remove_button); remove_revealer.reveal_child = false; @@ -128,5 +127,18 @@ public class Widgets.RadioRow : Gtk.ListBoxRow { message_dialog.destroy (); }); + + Byte.utils.radio_image_downloaded.connect ((id) => { + if (radio.id == id) { + try { + image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ( + GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("radio-%i.jpg").printf (id)), + 48, + 48); + } catch (Error e) { + stderr.printf ("Error setting default avatar icon: %s ", e.message); + } + } + }); } } \ No newline at end of file diff --git a/src/Widgets/RadioSearchRow.vala b/src/Widgets/RadioSearchRow.vala index 7ad2cb1..b500c28 100644 --- a/src/Widgets/RadioSearchRow.vala +++ b/src/Widgets/RadioSearchRow.vala @@ -2,7 +2,7 @@ public class Widgets.RadioSearchRow : Gtk.ListBoxRow { public Objects.Radio radio { get; construct; } private Gtk.Label name_label; - private Gtk.Label country_state_label; + private Gtk.Label secondary_label; private Widgets.Cover image_cover; public signal void send_notification_error (); @@ -24,30 +24,42 @@ public class Widgets.RadioSearchRow : Gtk.ListBoxRow { name_label.halign = Gtk.Align.START; name_label.valign = Gtk.Align.END; - country_state_label = new Gtk.Label ("%s - %s".printf (radio.country, radio.state)); - country_state_label.halign = Gtk.Align.START; - country_state_label.valign = Gtk.Align.START; - country_state_label.max_width_chars = 45; - country_state_label.ellipsize = Pango.EllipsizeMode.END; + secondary_label = new Gtk.Label ("👍️ %s - %s".printf (radio.votes, radio.country)); + secondary_label.halign = Gtk.Align.START; + secondary_label.valign = Gtk.Align.START; + secondary_label.max_width_chars = 45; + secondary_label.ellipsize = Pango.EllipsizeMode.END; image_cover = new Widgets.Cover.from_url_async (radio.favicon, 32, true, "radio"); var add_button = new Gtk.Button.from_icon_name ("list-add-symbolic", Gtk.IconSize.MENU); add_button.get_style_context ().add_class ("quick-find-add-radio"); + add_button.get_style_context ().add_class ("remove-button"); add_button.hexpand = true; add_button.halign = Gtk.Align.END; add_button.valign = Gtk.Align.CENTER; + var add_revealer = new Gtk.Revealer (); + add_revealer.halign = Gtk.Align.END; + add_revealer.valign = Gtk.Align.CENTER; + add_revealer.transition_type = Gtk.RevealerTransitionType.SLIDE_LEFT; + add_revealer.add (add_button); + add_revealer.reveal_child = false; + var main_grid = new Gtk.Grid (); main_grid.margin = 3; - main_grid.margin_end = 6; + main_grid.margin_end = 12; main_grid.column_spacing = 6; main_grid.attach (image_cover, 0, 0, 1, 2); main_grid.attach (name_label, 1, 0, 1, 1); - main_grid.attach (country_state_label, 1, 1, 1, 1); - main_grid.attach (add_button, 2, 0, 2, 2); + main_grid.attach (secondary_label, 1, 1, 1, 1); + main_grid.attach (add_revealer, 2, 0, 2, 2); + + var eventbox = new Gtk.EventBox (); + eventbox.add_events (Gdk.EventMask.ENTER_NOTIFY_MASK | Gdk.EventMask.LEAVE_NOTIFY_MASK); + eventbox.add (main_grid); - add (main_grid); + add (eventbox); add_button.clicked.connect (() => { if (Byte.database.radio_exists (radio.url)) { @@ -56,5 +68,21 @@ public class Widgets.RadioSearchRow : Gtk.ListBoxRow { Byte.database.insert_radio (radio); } }); + + eventbox.enter_notify_event.connect ((event) => { + add_revealer.reveal_child = true; + add_button.get_style_context ().add_class ("closed"); + return false; + }); + + eventbox.leave_notify_event.connect ((event) => { + if (event.detail == Gdk.NotifyType.INFERIOR) { + return false; + } + + add_revealer.reveal_child = false; + add_button.get_style_context ().remove_class ("closed"); + return false; + }); } } \ No newline at end of file diff --git a/src/Widgets/TrackAlbumRow.vala b/src/Widgets/TrackAlbumRow.vala index f74b345..22ff091 100644 --- a/src/Widgets/TrackAlbumRow.vala +++ b/src/Widgets/TrackAlbumRow.vala @@ -156,10 +156,10 @@ public class Widgets.TrackAlbumRow : Gtk.ListBoxRow { item.get_style_context ().add_class ("track-options"); item.get_style_context ().add_class ("css-item"); item.activate.connect (() => { - //var new_playlist = library_manager.create_new_playlist (); - //library_manager.add_track_into_playlist (new_playlist, track.ID); + var new_playlist = Byte.database.create_new_playlist (); + Byte.database.insert_track_into_playlist (new_playlist, track.id); }); - //playlists.add (item); + playlists.add (item); foreach (var playlist in all_items) { item = new Gtk.MenuItem.with_label (playlist.title); diff --git a/src/Widgets/TrackQueueRow.vala b/src/Widgets/TrackQueueRow.vala index 1ebba73..b579ff9 100644 --- a/src/Widgets/TrackQueueRow.vala +++ b/src/Widgets/TrackQueueRow.vala @@ -139,5 +139,22 @@ public class Widgets.TrackQueueRow : Gtk.ListBoxRow { destroy (); } }); + + Byte.database.updated_track_cover.connect ((track_id) => { + Idle.add (() => { + if (track_id == track.id) { + try { + image_cover.pixbuf = new Gdk.Pixbuf.from_file_at_size ( + GLib.Path.build_filename (Byte.utils.COVER_FOLDER, ("track-%i.jpg").printf (track_id)), + 32, + 32); + } catch (Error e) { + stderr.printf ("Error setting default avatar icon: %s ", e.message); + } + } + + return false; + }); + }); } } diff --git a/src/Widgets/TrackRow.vala b/src/Widgets/TrackRow.vala index 831a0c8..d5f3327 100644 --- a/src/Widgets/TrackRow.vala +++ b/src/Widgets/TrackRow.vala @@ -190,7 +190,6 @@ public class Widgets.TrackRow : Gtk.ListBoxRow { }); eventbox.enter_notify_event.connect ((event) => { - //root_window.cursor = hand_cursor; options_stack.visible_child_name = "options_button"; return false; }); @@ -199,7 +198,7 @@ public class Widgets.TrackRow : Gtk.ListBoxRow { if (event.detail == Gdk.NotifyType.INFERIOR) { return false; } - //root_window.cursor = arrow_cursor; + options_stack.visible_child_name = "duration_label"; return false; }); @@ -246,10 +245,10 @@ public class Widgets.TrackRow : Gtk.ListBoxRow { item.get_style_context ().add_class ("track-options"); item.get_style_context ().add_class ("css-item"); item.activate.connect (() => { - //var new_playlist = library_manager.create_new_playlist (); - //library_manager.add_track_into_playlist (new_playlist, track.ID); + var new_playlist = Byte.database.create_new_playlist (); + Byte.database.insert_track_into_playlist (new_playlist, track.id); }); - //playlists.add (item); + playlists.add (item); foreach (var playlist in all_items) { item = new Gtk.MenuItem.with_label (playlist.title); diff --git a/src/meson.build b/src/meson.build index 1414a33..5e472f8 100644 --- a/src/meson.build +++ b/src/meson.build @@ -33,6 +33,7 @@ sources = files( 'Widgets/ModelButton.vala', 'Widgets/MenuItem.vala', 'Widgets/AlertView.vala', + 'Widgets/AlbumArtistChild.vala', 'Widgets/Popovers/Sort.vala', 'Widgets/Popovers/NewPlaylist.vala', @@ -46,7 +47,8 @@ sources = files( 'Views/Playlists.vala', 'Views/Favorites.vala', 'Views/Playlist.vala', - + 'Views/Artist.vala', + 'Objects/Track.vala', 'Objects/Album.vala', 'Objects/Artist.vala',