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:
parent
588a68b2cc
commit
56dd6b45b4
27 changed files with 359 additions and 104 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
|||
builddir
|
||||
.buildconfig
|
||||
.buildconfig
|
||||
src/theme/style.css
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -21,6 +21,7 @@ add_project_arguments([
|
|||
'-I' + meson.current_build_dir(),
|
||||
], language: 'c')
|
||||
|
||||
subdir('theme')
|
||||
subdir('data')
|
||||
subdir('src')
|
||||
subdir('po')
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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"/>
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
5
theme/_button.scss
Normal file
|
@ -0,0 +1,5 @@
|
|||
.koto-button {
|
||||
& > image {
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
17
theme/_expander.scss
Normal file
17
theme/_expander.scss
Normal 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
17
theme/_player-bar.scss
Normal 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
21
theme/_primary-nav.scss
Normal 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
3
theme/_track-item.scss
Normal file
|
@ -0,0 +1,3 @@
|
|||
.track-item {
|
||||
padding: 10px;
|
||||
}
|
6
theme/_vars.scss
Normal file
6
theme/_vars.scss
Normal file
|
@ -0,0 +1,6 @@
|
|||
$grey: #2e2e2e;
|
||||
$midnight: #1d1d1d;
|
||||
$darkgrey: #666666;
|
||||
|
||||
$itempadding: 40px;
|
||||
$halvedpadding: $itempadding / 2;
|
17
theme/main.scss
Normal file
17
theme/main.scss
Normal 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
21
theme/meson.build
Normal 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,
|
||||
)
|
58
theme/pages/_music-local.scss
Normal file
58
theme/pages/_music-local.scss
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue