From 56dd6b45b4e969dcf452a4169bd75eb1d3ef1ac6 Mon Sep 17 00:00:00 2001 From: Joshua Strobl Date: Thu, 25 Feb 2021 18:15:36 +0200 Subject: [PATCH] Convert from CSS to SCSS. Make a multitude of refinements to styling along the way. Remove unused glade UI file. Added CSS classes to various components. Fix some alignment issues. Renamed our albums_strip to favorites_list. Implement recursive file parsing in KotoIndexedAlbum with the intent of using it for "discs" / CD, useful for albums like Foo Fighters: In Your Honor that have 2 or more CDs. Still need to work on refining this further. Add stub function in our album view for a planned separation of the track listing so we can do it based on discs and other depth-of-3 sub-folders. --- .gitignore | 3 +- data/style.css | 37 ---------- meson.build | 1 + src/indexer/album.c | 127 +++++++++++++++++++++++----------- src/indexer/file.c | 30 +++++++- src/indexer/file.h | 3 +- src/koto-headerbar.ui | 2 +- src/koto-playerbar.c | 14 ++-- src/koto-track-item.c | 1 + src/koto-utils.c | 1 + src/koto-window.c | 36 +++++++++- src/koto-window.h | 1 + src/koto.gresource.xml | 3 +- src/main.c | 1 + src/meson.build | 3 +- src/pages/music/album-view.c | 10 +++ src/pages/music/album-view.h | 1 + src/pages/music/artist-view.c | 24 ++++--- theme/_button.scss | 5 ++ theme/_expander.scss | 17 +++++ theme/_player-bar.scss | 17 +++++ theme/_primary-nav.scss | 21 ++++++ theme/_track-item.scss | 3 + theme/_vars.scss | 6 ++ theme/main.scss | 17 +++++ theme/meson.build | 21 ++++++ theme/pages/_music-local.scss | 58 ++++++++++++++++ 27 files changed, 359 insertions(+), 104 deletions(-) delete mode 100644 data/style.css create mode 100644 theme/_button.scss create mode 100644 theme/_expander.scss create mode 100644 theme/_player-bar.scss create mode 100644 theme/_primary-nav.scss create mode 100644 theme/_track-item.scss create mode 100644 theme/_vars.scss create mode 100644 theme/main.scss create mode 100644 theme/meson.build create mode 100644 theme/pages/_music-local.scss diff --git a/.gitignore b/.gitignore index 032748e..91dd162 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ builddir -.buildconfig \ No newline at end of file +.buildconfig +src/theme/style.css diff --git a/data/style.css b/data/style.css deleted file mode 100644 index 24b0cba..0000000 --- a/data/style.css +++ /dev/null @@ -1,37 +0,0 @@ -window { - background-color: #2e2e2e; -} - -headerbar, .player-bar { - background-color: #1d1d1d; -} - -.primary-nav > .frame > box >.koto-button > box { - padding: 10px 0; -} - -.primary-nav .expander .expander-header { - padding-bottom: 10px; -} - -.expander > revealer > box > .koto-button, -.expander > revealer > box > .koto-button > box { - min-height: 40px; -} - -.koto-button > box > image { - margin-right: 10px; -} - -.primary-nav label, -.expander-header label { - font-size: large; -} - -.page-music-local .artist-list { - background-color: #1d1d1d; -} - -.page-music-local row { - padding: 10px; -} diff --git a/meson.build b/meson.build index ca1ee90..332afbc 100644 --- a/meson.build +++ b/meson.build @@ -21,6 +21,7 @@ add_project_arguments([ '-I' + meson.current_build_dir(), ], language: 'c') +subdir('theme') subdir('data') subdir('src') subdir('po') diff --git a/src/indexer/album.c b/src/indexer/album.c index ec4104a..4fd97db 100644 --- a/src/indexer/album.c +++ b/src/indexer/album.c @@ -151,6 +151,90 @@ void koto_indexed_album_set_album_art(KotoIndexedAlbum *self, const gchar *album self->has_album_art = TRUE; } +void koto_indexed_album_read_path(KotoIndexedAlbum *self, magic_t cookie, const gchar* path) { + DIR *dir = opendir(path); // Attempt to open our directory + + if (dir == NULL) { + return; + } + + struct dirent *entry; + + while ((entry = readdir(dir))) { + if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item + continue; // Skip + } + + gchar *full_path = g_strdup_printf("%s%s%s", path, G_DIR_SEPARATOR_S, entry->d_name); + + if (entry->d_type == DT_DIR) { // If this is a directory + koto_indexed_album_read_path(self, cookie, full_path); // Read this directory as well + continue; + } + + if (entry->d_type != DT_REG) { // If this is not a regular file + continue; // Skip + } + + const char *mime_type = magic_file(cookie, full_path); + + if (mime_type == NULL) { // Failed to get the mimetype + g_free(full_path); + continue; // Skip + } + + if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet + gchar *album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension + gchar *lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase + + if ( + (g_strrstr(lower_art, "Small") == NULL) && // Not Small + (g_strrstr(lower_art, "back") == NULL) // Not back + ) { + koto_indexed_album_set_album_art(self, full_path); + } + + g_free(album_art_no_ext); + g_free(lower_art); + } else if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special + gchar *appended_slash_to_path = g_strdup_printf("%s%s", g_strdup(self->path), G_DIR_SEPARATOR_S); + gchar **possible_cd_split = g_strsplit(full_path, appended_slash_to_path, -1); // Split based on the album path + guint *cd = 0; + + gchar *file_with_cd_sep = g_strdup(possible_cd_split[1]); // Duplicate + gchar **split_on_cd = g_strsplit(file_with_cd_sep, G_DIR_SEPARATOR_S, -1); // Split based on separator (e.g. / ) + + if (g_strv_length(split_on_cd) > 1) { + gchar *cdd = g_strdup(split_on_cd[0]); + gchar **cd_sep = g_strsplit(g_utf8_strdown(cdd, -1), "cd", -1); + + if (g_strv_length(cd_sep) > 1) { + gchar *pos_str = g_strdup(cd_sep[1]); + cd = (guint*) g_ascii_strtoull(pos_str, NULL, 10); // Attempt to convert + g_free(pos_str); + } + + g_strfreev(cd_sep); + g_free(cdd); + } + + g_strfreev(split_on_cd); + g_free(file_with_cd_sep); + + g_strfreev(possible_cd_split); + g_free(appended_slash_to_path); + + KotoIndexedFile *file = koto_indexed_file_new(full_path, cd); + + if (file != NULL) { // Is a file + koto_indexed_album_add_file(self, file); // Add our file + } + } + + g_free(full_path); + } +} + void koto_indexed_album_remove_file(KotoIndexedAlbum *self, KotoIndexedFile *file) { if (file == NULL) { // Not a file return; @@ -235,48 +319,7 @@ void koto_indexed_album_update_path(KotoIndexedAlbum *self, const gchar* new_pat return; } - struct dirent *entry; - - while ((entry = readdir(dir))) { - if (g_str_has_prefix(entry->d_name, ".")) { // Reference to parent dir, self, or a hidden item - continue; // Skip - } - - if (entry->d_type != DT_REG) { // If this is not a regular file - continue; // Skip - } - - gchar *full_path = g_strdup_printf("%s%s%s", self->path, G_DIR_SEPARATOR_S, entry->d_name); - const char *mime_type = magic_file(magic_cookie, full_path); - - if (mime_type == NULL) { // Failed to get the mimetype - g_free(full_path); - continue; // Skip - } - - if (g_str_has_prefix(mime_type, "image/") && !self->has_album_art) { // Is an image file and doesn't have album art yet - gchar *album_art_no_ext = g_strdup(koto_utils_get_filename_without_extension(entry->d_name)); // Get the name of the file without the extension - gchar *lower_art = g_strdup(g_utf8_strdown(album_art_no_ext, -1)); // Lowercase - - if ( - (g_strrstr(lower_art, "Small") == NULL) && // Not Small - (g_strrstr(lower_art, "back") == NULL) // Not back - ) { - koto_indexed_album_set_album_art(self, full_path); - } - - g_free(album_art_no_ext); - g_free(lower_art); - } else if (g_str_has_prefix(mime_type, "audio/") || g_str_has_prefix(mime_type, "video/ogg")) { // Is an audio file or ogg because it is special - KotoIndexedFile *file = koto_indexed_file_new(full_path); - - if (file != NULL) { // Is a file - koto_indexed_album_add_file(self, file); // Add our file - } - } - - g_free(full_path); - } + koto_indexed_album_read_path(self, magic_cookie, self->path); magic_close(magic_cookie); } diff --git a/src/indexer/file.c b/src/indexer/file.c index 043dd7a..6963a37 100644 --- a/src/indexer/file.c +++ b/src/indexer/file.c @@ -28,6 +28,7 @@ struct _KotoIndexedFile { gchar *parsed_name; gchar *artist; gchar *album; + guint *cd; guint *position; gboolean acquired_metadata_from_id3; }; @@ -41,6 +42,7 @@ enum { PROP_ALBUM, PROP_FILE_NAME, PROP_PARSED_NAME, + PROP_CD, PROP_POSITION, N_PROPERTIES }; @@ -96,6 +98,16 @@ static void koto_indexed_file_class_init(KotoIndexedFileClass *c) { G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE ); + props[PROP_CD] = g_param_spec_uint( + "cd", + "CD the Track belongs to", + "CD the Track belongs to", + 0, + G_MAXUINT16, + 1, + G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE + ); + props[PROP_POSITION] = g_param_spec_uint( "position", "Position in Audiobook, Album, etc.", @@ -132,6 +144,9 @@ static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue * case PROP_PARSED_NAME: g_value_set_string(val, self->parsed_name); break; + case PROP_CD: + g_value_set_uint(val, GPOINTER_TO_UINT(self->cd)); + break; case PROP_POSITION: g_value_set_uint(val, GPOINTER_TO_UINT(self->position)); break; @@ -160,6 +175,9 @@ static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GV case PROP_PARSED_NAME: koto_indexed_file_set_parsed_name(self, g_strdup(g_value_get_string(val))); break; + case PROP_CD: + koto_indexed_file_set_cd(self, g_value_get_uint(val)); + break; case PROP_POSITION: koto_indexed_file_set_position(self, g_value_get_uint(val)); break; @@ -249,6 +267,15 @@ void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name } } +void koto_indexed_file_set_cd(KotoIndexedFile *self, guint cd) { + if (cd <= 1) { // No change really + return; + } + + self->cd = GUINT_TO_POINTER(cd); + g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CD]); +} + void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name) { if (new_parsed_name == NULL) { return; @@ -307,9 +334,10 @@ void koto_indexed_file_update_path(KotoIndexedFile *self, const gchar *new_path) g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PATH]); } -KotoIndexedFile* koto_indexed_file_new(const gchar *path) { +KotoIndexedFile* koto_indexed_file_new(const gchar *path, guint *cd) { return g_object_new(KOTO_TYPE_INDEXED_FILE, "path", path, + "cd", cd, NULL ); } diff --git a/src/indexer/file.h b/src/indexer/file.h index 4913a68..bb28202 100644 --- a/src/indexer/file.h +++ b/src/indexer/file.h @@ -23,10 +23,11 @@ G_BEGIN_DECLS #define KOTO_TYPE_INDEXED_FILE koto_indexed_file_get_type() G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GObject); -KotoIndexedFile* koto_indexed_file_new(const gchar *path); +KotoIndexedFile* koto_indexed_file_new(const gchar *path, guint *cd); void koto_indexed_file_parse_name(KotoIndexedFile *self); void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name); +void koto_indexed_file_set_cd(KotoIndexedFile *self, guint cd); void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name); void koto_indexed_file_set_position(KotoIndexedFile *self, guint pos); void koto_indexed_file_update_metadata(KotoIndexedFile *self); diff --git a/src/koto-headerbar.ui b/src/koto-headerbar.ui index 43244f9..6bad467 100644 --- a/src/koto-headerbar.ui +++ b/src/koto-headerbar.ui @@ -1,4 +1,4 @@ - +?xml version="1.0" encoding="UTF-8"?> diff --git a/src/koto-playerbar.c b/src/koto-playerbar.c index 384cd21..1719366 100644 --- a/src/koto-playerbar.c +++ b/src/koto-playerbar.c @@ -124,9 +124,9 @@ void koto_playerbar_create_playback_details(KotoPlayerBar* bar) { } void koto_playerbar_create_primary_controls(KotoPlayerBar* bar) { - bar->back_button = koto_button_new_with_icon("", "media-skip-backward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); - bar->play_pause_button = koto_button_new_with_icon("", "media-playback-start-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); // TODO: Have this take in a state and switch to a different icon if necessary - bar->forward_button = koto_button_new_with_icon("", "media-skip-forward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); + bar->back_button = koto_button_new_with_icon("", "media-skip-backward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); + bar->play_pause_button = koto_button_new_with_icon("", "media-playback-start-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_LARGE); // TODO: Have this take in a state and switch to a different icon if necessary + bar->forward_button = koto_button_new_with_icon("", "media-skip-forward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); if (bar->back_button != NULL) { gtk_box_append(GTK_BOX(bar->primary_controls_section), GTK_WIDGET(bar->back_button)); @@ -142,10 +142,10 @@ void koto_playerbar_create_primary_controls(KotoPlayerBar* bar) { } void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar) { - bar->repeat_button = koto_button_new_with_icon("", "media-playlist-repeat-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); - bar->shuffle_button = koto_button_new_with_icon("", "media-playlist-shuffle-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); - bar->playlist_button = koto_button_new_with_icon("", "playlist-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); - bar->eq_button = koto_button_new_with_icon("", "multimedia-equalizer-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL); + bar->repeat_button = koto_button_new_with_icon("", "media-playlist-repeat-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); + bar->shuffle_button = koto_button_new_with_icon("", "media-playlist-shuffle-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); + bar->playlist_button = koto_button_new_with_icon("", "playlist-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); + bar->eq_button = koto_button_new_with_icon("", "multimedia-equalizer-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); bar->volume_button = gtk_volume_button_new(); // Have this take in a state and switch to a different icon if necessary gtk_scale_button_set_value(GTK_SCALE_BUTTON(bar->volume_button), 0.5); diff --git a/src/koto-track-item.c b/src/koto-track-item.c index c784669..ff861d3 100644 --- a/src/koto-track-item.c +++ b/src/koto-track-item.c @@ -93,6 +93,7 @@ static void koto_track_item_init(KotoTrackItem *self) { self->add_to_playlist_button = koto_button_new_with_icon(NULL, "playlist-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_TINY); + gtk_widget_add_css_class(GTK_WIDGET(self), "track-item"); gtk_widget_set_hexpand(GTK_WIDGET(self), TRUE); gtk_widget_set_hexpand(GTK_WIDGET(self->track_label), TRUE); diff --git a/src/koto-utils.c b/src/koto-utils.c index 13ec4df..214abc5 100644 --- a/src/koto-utils.c +++ b/src/koto-utils.c @@ -32,6 +32,7 @@ GtkWidget* koto_utils_create_image_from_filepath(gchar *filepath, gchar *fallbac } gtk_image_set_icon_size(GTK_IMAGE(image), GTK_ICON_SIZE_INHERIT); + gtk_image_set_pixel_size(GTK_IMAGE(image), width); gtk_widget_set_size_request(image, width, height); return image; diff --git a/src/koto-window.c b/src/koto-window.c index 9d86d65..54fba8c 100644 --- a/src/koto-window.c +++ b/src/koto-window.c @@ -15,6 +15,7 @@ * limitations under the License. */ +#include #include "indexer/file-indexer.h" #include "pages/music/music-local.h" #include "koto-config.h" @@ -47,17 +48,17 @@ static void koto_window_class_init (KotoWindowClass *klass) { static void koto_window_init (KotoWindow *self) { GtkCssProvider* provider = gtk_css_provider_new(); gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css"); - // TODO: Change 900 back to GTK_STYLE_PROVIDER_PRIORITY_APPLICATION so users are allowed to override the style. - // We want them to be able to override it. It's their system, style it how they want. We just need this to test our own currently. - gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), 900); + gtk_style_context_add_provider_for_display(gdk_display_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); create_new_headerbar(self); // Create our headerbar self->primary_layout = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); + gtk_widget_add_css_class(self->primary_layout, "primary-layout"); gtk_widget_set_hexpand(self->primary_layout, TRUE); gtk_widget_set_vexpand(self->primary_layout, TRUE); self->content_layout = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_add_css_class(self->content_layout, "content-layout"); gtk_widget_set_hexpand(self->content_layout, TRUE); gtk_widget_set_vexpand(self->content_layout, TRUE); @@ -84,7 +85,11 @@ static void koto_window_init (KotoWindow *self) { } gtk_window_set_child(GTK_WINDOW(self), self->primary_layout); +#ifdef GDK_WINDOWING_X11 + set_optimal_default_window_size(self); +#else gtk_widget_set_size_request(GTK_WIDGET(self), 1200, 675); +#endif gtk_window_set_title(GTK_WINDOW(self), "Koto"); gtk_window_set_icon_name(GTK_WINDOW(self), "audio-headphones"); gtk_window_set_startup_id(GTK_WINDOW(self), "com.github.joshstrobl.koto"); @@ -94,6 +99,7 @@ static void koto_window_init (KotoWindow *self) { void create_new_headerbar(KotoWindow *self) { self->header_bar = gtk_header_bar_new(); + gtk_widget_add_css_class(self->header_bar, "hdr"); g_return_if_fail(GTK_IS_HEADER_BAR(self->header_bar)); self->menu_button = gtk_button_new_from_icon_name("audio-headphones"); @@ -126,3 +132,27 @@ void load_library(KotoWindow *self) { } } } + +void set_optimal_default_window_size(KotoWindow *self) { + GdkDisplay *default_display = gdk_display_get_default(); + g_return_if_fail(GDK_IS_X11_DISPLAY(default_display)); + + GdkMonitor *default_monitor = gdk_x11_display_get_primary_monitor(GDK_X11_DISPLAY(default_display)); // Get primary monitor for the X11 + g_return_if_fail(default_monitor); + + + GdkRectangle workarea = {0}; + gdk_monitor_get_geometry(default_monitor, &workarea); + + if (workarea.width <= 1280) { // Honestly how do you even get anything done? + gtk_widget_set_size_request(GTK_WIDGET(self), 1200, 675); + } else if ((workarea.width > 1280) && (workarea.width <= 1600)) { // Plebian monitor resolution + gtk_widget_set_size_request(GTK_WIDGET(self), 1400, 787); + } else if ((workarea.width > 1600) && (workarea.width <= 1920)) { // Something slightly normal + gtk_widget_set_size_request(GTK_WIDGET(self), 1600, 900); + } else if ((workarea.width > 1920) && (workarea.width <= 2560)) { // Well aren't you hot stuff? + gtk_widget_set_size_request(GTK_WIDGET(self), 1920, 1080); + } else { // Now you're just flexing + gtk_widget_set_size_request(GTK_WIDGET(self), 2560, 1400); + } +} diff --git a/src/koto-window.h b/src/koto-window.h index 07de180..59f9077 100644 --- a/src/koto-window.h +++ b/src/koto-window.h @@ -29,3 +29,4 @@ G_END_DECLS void create_new_headerbar(KotoWindow *self); void load_library(KotoWindow *self); +void set_optimal_default_window_size(KotoWindow *self); diff --git a/src/koto.gresource.xml b/src/koto.gresource.xml index 250bec4..0edd239 100644 --- a/src/koto.gresource.xml +++ b/src/koto.gresource.xml @@ -1,7 +1,6 @@ - koto-headerbar.ui - ../data/style.css + ../theme/style.css diff --git a/src/main.c b/src/main.c index 4bf6957..417f06d 100644 --- a/src/main.c +++ b/src/main.c @@ -42,6 +42,7 @@ int main (int argc, char *argv[]) { bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); textdomain (GETTEXT_PACKAGE); + gtk_init(); app = gtk_application_new ("com.github.joshstrobl.koto", G_APPLICATION_FLAGS_NONE); g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL); ret = g_application_run (G_APPLICATION (app), argc, argv); diff --git a/src/meson.build b/src/meson.build index 18d9c94..d95e31e 100644 --- a/src/meson.build +++ b/src/meson.build @@ -30,7 +30,8 @@ gnome = import('gnome') koto_sources += gnome.compile_resources('koto-resources', 'koto.gresource.xml', - c_name: 'koto' + dependencies: [ theme ], + c_name: 'koto', ) executable('com.github.joshstrobl.koto', koto_sources, diff --git a/src/pages/music/album-view.c b/src/pages/music/album-view.c index d44481e..0d7c1bb 100644 --- a/src/pages/music/album-view.c +++ b/src/pages/music/album-view.c @@ -32,6 +32,7 @@ struct _KotoAlbumView { GtkWidget *tracks; GtkWidget *album_label; + GHashTable *cd_to_track_listbox; }; G_DEFINE_TYPE(KotoAlbumView, koto_album_view, G_TYPE_OBJECT); @@ -64,12 +65,15 @@ static void koto_album_view_class_init(KotoAlbumViewClass *c) { } static void koto_album_view_init(KotoAlbumView *self) { + self->cd_to_track_listbox = g_hash_table_new(g_str_hash, g_str_equal); self->main = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0); + gtk_widget_add_css_class(self->main, "album-view"); gtk_widget_set_can_focus(self->main, FALSE); self->album_tracks_box = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0); self->tracks = gtk_list_box_new(); // Create our list of our tracks gtk_list_box_set_sort_func(GTK_LIST_BOX(self->tracks), koto_album_view_sort_tracks, NULL, NULL); // Ensure we can sort our tracks + gtk_widget_add_css_class(self->tracks, "track-list"); gtk_widget_set_size_request(self->tracks, 600, -1); gtk_box_append(GTK_BOX(self->main), self->album_tracks_box); // Add the tracks box to the art info combo box @@ -106,6 +110,10 @@ static void koto_album_view_set_property(GObject *obj, guint prop_id, const GVal } } +void koto_album_view_add_track_to_listbox(KotoIndexedAlbum *self, KotoIndexedFile *file) { + (void) self; (void) file; +} + void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) { if (album == NULL) { return; @@ -115,6 +123,7 @@ void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) { gchar *album_art = koto_indexed_album_get_album_art(self->album); // Get the art for the album GtkWidget *art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220); + gtk_widget_set_valign(art_image, GTK_ALIGN_START); // Align to top of list for album gtk_box_prepend(GTK_BOX(self->main), art_image); // Prepend the image to the art info box @@ -122,6 +131,7 @@ void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album) { g_object_get(album, "name", &album_name, NULL); // Get the album name self->album_label = gtk_label_new(album_name); + gtk_widget_set_halign(self->album_label, GTK_ALIGN_START); gtk_box_prepend(GTK_BOX(self->album_tracks_box), self->album_label); // Prepend our new label to the album + tracks box GList *t; diff --git a/src/pages/music/album-view.h b/src/pages/music/album-view.h index ddaacdd..705ad59 100644 --- a/src/pages/music/album-view.h +++ b/src/pages/music/album-view.h @@ -30,6 +30,7 @@ G_DECLARE_FINAL_TYPE(KotoAlbumView, koto_album_view, KOTO, ALBUM_VIEW, GObject) KotoAlbumView* koto_album_view_new(KotoIndexedAlbum *album); GtkWidget* koto_album_view_get_main(KotoAlbumView *self); +void koto_album_view_add_track_to_listbox(KotoIndexedAlbum *self, KotoIndexedFile *file); void koto_album_view_set_album(KotoAlbumView *self, KotoIndexedAlbum *album); int koto_album_view_sort_tracks(GtkListBoxRow *track1, GtkListBoxRow *track2, gpointer user_data); diff --git a/src/pages/music/artist-view.c b/src/pages/music/artist-view.c index 1e1fe7f..1da0342 100644 --- a/src/pages/music/artist-view.c +++ b/src/pages/music/artist-view.c @@ -29,7 +29,7 @@ struct _KotoArtistView { KotoIndexedArtist *artist; GtkWidget *scrolled_window; GtkWidget *content; - GtkWidget *albums_strip; + GtkWidget *favorites_list; GtkWidget *album_list; GHashTable *albums_to_component; @@ -108,19 +108,26 @@ static void koto_artist_view_constructed(GObject *obj) { gtk_scrolled_window_set_propagate_natural_height(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE); gtk_scrolled_window_set_propagate_natural_width(GTK_SCROLLED_WINDOW(self->scrolled_window), TRUE); gtk_scrolled_window_set_child(GTK_SCROLLED_WINDOW(self->scrolled_window), self->content); // Add the content as the widget for the scrolled window + gtk_widget_add_css_class(GTK_WIDGET(self->scrolled_window), "artist-view"); + gtk_widget_add_css_class(GTK_WIDGET(self->content), "artist-view-content"); - self->albums_strip = gtk_flow_box_new(); // Create our album strip - gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->albums_strip), TRUE); - gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->albums_strip), GTK_SELECTION_NONE); + self->favorites_list = gtk_flow_box_new(); // Create our favorites list + gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->favorites_list), TRUE); + gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->favorites_list), GTK_SELECTION_NONE); + gtk_flow_box_set_min_children_per_line(GTK_FLOW_BOX(self->favorites_list), 6); + gtk_flow_box_set_max_children_per_line(GTK_FLOW_BOX(self->favorites_list), 6); + gtk_widget_add_css_class(GTK_WIDGET(self->favorites_list), "album-strip"); + gtk_widget_set_halign(self->favorites_list, GTK_ALIGN_START); - self->album_list = gtk_flow_box_new(); // Create our list of albums as a flow box + self->album_list = gtk_flow_box_new(); // Create our list of our albums gtk_flow_box_set_activate_on_single_click(GTK_FLOW_BOX(self->album_list), FALSE); gtk_flow_box_set_selection_mode(GTK_FLOW_BOX(self->album_list), GTK_SELECTION_NONE); + gtk_widget_add_css_class(self->album_list, "album-list"); - gtk_box_prepend(GTK_BOX(self->content), self->albums_strip); // Add the strip + gtk_box_prepend(GTK_BOX(self->content), self->favorites_list); // Add the strip gtk_box_append(GTK_BOX(self->content), self->album_list); // Add the list - gtk_widget_set_hexpand(GTK_WIDGET(self->albums_strip), TRUE); + gtk_widget_set_hexpand(GTK_WIDGET(self->favorites_list), TRUE); gtk_widget_set_hexpand(GTK_WIDGET(self->album_list), TRUE); G_OBJECT_CLASS (koto_artist_view_parent_class)->constructed (obj); @@ -131,7 +138,8 @@ void koto_artist_view_add_album(KotoArtistView *self, KotoIndexedAlbum *album) { gchar *album_art = koto_indexed_album_get_album_art(album); // Get the art for the album GtkWidget *art_image = koto_utils_create_image_from_filepath(album_art, "audio-x-generic-symbolic", 220, 220); - gtk_flow_box_insert(GTK_FLOW_BOX(self->albums_strip), art_image, -1); // Append the album art + gtk_widget_set_halign(art_image, GTK_ALIGN_START); // Align to start + gtk_flow_box_insert(GTK_FLOW_BOX(self->favorites_list), art_image, -1); // Append the album art KotoAlbumView* album_view = koto_album_view_new(album); // Create our new album view GtkWidget* album_view_main = koto_album_view_get_main(album_view); diff --git a/theme/_button.scss b/theme/_button.scss new file mode 100644 index 0000000..b46ad88 --- /dev/null +++ b/theme/_button.scss @@ -0,0 +1,5 @@ +.koto-button { + & > image { + margin-right: 10px; + } +} diff --git a/theme/_expander.scss b/theme/_expander.scss new file mode 100644 index 0000000..c07efe7 --- /dev/null +++ b/theme/_expander.scss @@ -0,0 +1,17 @@ +@import 'vars'; + +.expander { + & > .expander-header { + & > label { + font-size: large; + } + } + + & > revealer > box { + & > .koto-button { + &, & > box { + min-height: 40px; + } + } + } +} diff --git a/theme/_player-bar.scss b/theme/_player-bar.scss new file mode 100644 index 0000000..8cf13e8 --- /dev/null +++ b/theme/_player-bar.scss @@ -0,0 +1,17 @@ +@import 'vars'; + +.player-bar { + background-color: $midnight; + background-image: none; + padding: $halvedpadding; + + .koto-button { + &:not(.toggled) { + color: $darkgrey; + } + + &.toggled { + color: white; + } + } +} diff --git a/theme/_primary-nav.scss b/theme/_primary-nav.scss new file mode 100644 index 0000000..e1a2f21 --- /dev/null +++ b/theme/_primary-nav.scss @@ -0,0 +1,21 @@ +.primary-nav { + padding: 10px; + + & > viewport > box { + & > .koto-botton { + & > box { + padding: 10px 0; + + & > label { + font-size: large; + } + } + } + + & > .expander { + & > .expander-header { + margin-bottom: 10px; + } + } + } +} diff --git a/theme/_track-item.scss b/theme/_track-item.scss new file mode 100644 index 0000000..7afbbb0 --- /dev/null +++ b/theme/_track-item.scss @@ -0,0 +1,3 @@ +.track-item { + padding: 10px; +} diff --git a/theme/_vars.scss b/theme/_vars.scss new file mode 100644 index 0000000..37e72f4 --- /dev/null +++ b/theme/_vars.scss @@ -0,0 +1,6 @@ +$grey: #2e2e2e; +$midnight: #1d1d1d; +$darkgrey: #666666; + +$itempadding: 40px; +$halvedpadding: $itempadding / 2; diff --git a/theme/main.scss b/theme/main.scss new file mode 100644 index 0000000..13b4ae0 --- /dev/null +++ b/theme/main.scss @@ -0,0 +1,17 @@ +@import 'pages/music-local.scss'; + +@import 'button'; +@import 'vars'; +@import 'expander'; +@import 'player-bar'; +@import 'primary-nav'; +@import 'track-item'; + +window { + background-color: $grey; + + & > headerbar, & > headerbar:active { + background-color: $midnight; + background-image: none; + } +} diff --git a/theme/meson.build b/theme/meson.build new file mode 100644 index 0000000..f53e8ab --- /dev/null +++ b/theme/meson.build @@ -0,0 +1,21 @@ +sassc = find_program('sassc', required: true) + +theme = custom_target('Theme generation', + input: 'main.scss', + output: 'style.css', + command: [ + sassc, + [ '-a', '-M', '-t', 'compact' ], + '@INPUT@', '@OUTPUT@', + ], + depend_files: files([ + 'pages/_music-local.scss', + '_button.scss', + '_vars.scss', + '_expander.scss', + '_player-bar.scss', + '_primary-nav.scss', + '_track-item.scss', + ]), + build_by_default: true, +) diff --git a/theme/pages/_music-local.scss b/theme/pages/_music-local.scss new file mode 100644 index 0000000..00d042b --- /dev/null +++ b/theme/pages/_music-local.scss @@ -0,0 +1,58 @@ +@import '../vars'; + +.page-music-local { + & > .artist-list { + &, & > viewport, & > viewport > list { + background-color: $midnight; + } + + & > viewport > list { + & > row { + padding: $halvedpadding; + } + } + } + + & > stack { + & > .artist-view { + & > viewport > .artist-view-content { + padding: $itempadding; + + & > .album-strip { + margin-bottom: $itempadding; + & > flowboxchild { + margin-right: $itempadding; + } + } + + & > .album-list { + & > flowboxchild > .album-view { + & > image { + margin-right: $itempadding; + } + + & > box { + & > label { + font-size: xx-large; + font-weight: 900; + padding: $halvedpadding 0; + } + + & > .track-list { + & > row { + &:nth-child(odd):not(:hover) { + background-color: $midnight; + } + + &:nth-child(even), &:hover { + background-color: $grey; + } + } + } + } + } + } + } + } + } +}