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.
This commit is contained in:
Joshua Strobl 2021-02-25 18:15:36 +02:00
parent 588a68b2cc
commit 56dd6b45b4
27 changed files with 359 additions and 104 deletions

3
.gitignore vendored
View file

@ -1,2 +1,3 @@
builddir
.buildconfig
.buildconfig
src/theme/style.css

View file

@ -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;
}

View file

@ -21,6 +21,7 @@ add_project_arguments([
'-I' + meson.current_build_dir(),
], language: 'c')
subdir('theme')
subdir('data')
subdir('src')
subdir('po')

View file

@ -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);
}

View file

@ -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
);
}

View file

@ -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);

View file

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>

View file

@ -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);

View file

@ -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);

View file

@ -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;

View file

@ -15,6 +15,7 @@
* limitations under the License.
*/
#include <gtk-4.0/gdk/x11/gdkx.h>
#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);
}
}

View file

@ -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);

View file

@ -1,7 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<gresources>
<gresource prefix="/com/github/joshstrobl/koto">
<file>koto-headerbar.ui</file>
<file alias="style.css">../data/style.css</file>
<file alias="style.css">../theme/style.css</file>
</gresource>
</gresources>

View file

@ -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);

View file

@ -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,

View file

@ -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;

View file

@ -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);

View file

@ -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);

5
theme/_button.scss Normal file
View file

@ -0,0 +1,5 @@
.koto-button {
& > image {
margin-right: 10px;
}
}

17
theme/_expander.scss Normal file
View file

@ -0,0 +1,17 @@
@import 'vars';
.expander {
& > .expander-header {
& > label {
font-size: large;
}
}
& > revealer > box {
& > .koto-button {
&, & > box {
min-height: 40px;
}
}
}
}

17
theme/_player-bar.scss Normal file
View file

@ -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;
}
}
}

21
theme/_primary-nav.scss Normal file
View file

@ -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;
}
}
}
}

3
theme/_track-item.scss Normal file
View file

@ -0,0 +1,3 @@
.track-item {
padding: 10px;
}

6
theme/_vars.scss Normal file
View file

@ -0,0 +1,6 @@
$grey: #2e2e2e;
$midnight: #1d1d1d;
$darkgrey: #666666;
$itempadding: 40px;
$halvedpadding: $itempadding / 2;

17
theme/main.scss Normal file
View file

@ -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;
}
}

21
theme/meson.build Normal file
View file

@ -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,
)

View file

@ -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;
}
}
}
}
}
}
}
}
}
}