Deprecate KotoFlipperButton, integrate flip functionality with an alt image into KotoButton.

We are now leveraging KotoButton in the KotoPlayerBar and KotoExpander. Dropped Glade UI templating from KotoWindow, set its default size using gtk_widget_set_size_request to force a minimum window size, set window title, WMClass, and icon name.

Start implementing KotoIndexedFile class to handle our provided audio files, setting its file name and attempting to parse the file name when we are not using ID3 data. The parsing is as follows:

- All `_` are replaced with whitespace
- We will attempt to remove nested references to the artist and album name (we need to implemented the relevant classes to leverage that).
- Remove any references to ` - ` followed by any stragglers `-` (without the whitespace around it).
- Split based on `.` and remove the trailing file extension, preserving the rest (so something like `Ch. 01.ogg` would be considered just `Ch. 01`).
This commit is contained in:
Joshua Strobl 2021-02-12 12:56:41 +02:00
parent 625c1be645
commit 9b6fa4593a
19 changed files with 472 additions and 333 deletions

View file

@ -1,4 +1,4 @@
.primary-nav > .frame > box >.koto-button { .primary-nav > .frame > box >.koto-button > box {
padding: 10px 0; padding: 10px 0;
} }
@ -6,11 +6,12 @@
padding-bottom: 10px; padding-bottom: 10px;
} }
.expander > revealer > box > .koto-button { .expander > revealer > box > .koto-button,
.expander > revealer > box > .koto-button > box {
min-height: 40px; min-height: 40px;
} }
.koto-button > image { .koto-button > box > image {
margin-right: 10px; margin-right: 10px;
} }

View file

@ -18,14 +18,9 @@
#include <dirent.h> #include <dirent.h>
#include <magic.h> #include <magic.h>
#include <sys/stat.h> #include <sys/stat.h>
#include "file.h"
#include "file-indexer.h" #include "file-indexer.h"
struct KotoIndexedFile {
GObject parent_instance;
gchar *file_name;
gchar *path;
};
struct KotoIndexedAlbum { struct KotoIndexedAlbum {
GObject parent_instance; GObject parent_instance;
gboolean has_album_art; gboolean has_album_art;
@ -41,7 +36,7 @@ struct _KotoIndexedArtist {
gchar *artist_name; gchar *artist_name;
GHashTable *albums; GHashTable *albums;
gchar *path; gchar *path;
} };
struct _KotoIndexedLibrary { struct _KotoIndexedLibrary {
GObject parent_instance; GObject parent_instance;
@ -74,12 +69,16 @@ static void koto_indexed_library_class_init(KotoIndexedLibraryClass *c) {
"Path", "Path",
"Path to Music", "Path to Music",
NULL, NULL,
G_PARAM_CONSTRUCT_ONLY|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
} }
static void koto_indexed_library_init(KotoIndexedLibrary *self) {
self->music_artists = g_hash_table_new(g_str_hash, g_str_equal);
}
static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_indexed_library_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj); KotoIndexedLibrary *self = KOTO_INDEXED_LIBRARY(obj);
@ -175,18 +174,29 @@ void index_file(KotoIndexedLibrary *self, gchar *path) {
g_str_has_prefix(mime_info[0], "audio/") || // Is audio g_str_has_prefix(mime_info[0], "audio/") || // Is audio
g_str_has_prefix(mime_info[0], "image/") // Is image g_str_has_prefix(mime_info[0], "image/") // Is image
) { ) {
g_message("File Name: %s", path); //g_message("File Name: %s", path);
}
if (g_str_has_prefix(mime_info[0], "audio/")) { // Is an audio file
KotoIndexedFile *file = koto_indexed_file_new(path);
gchar *filepath;
gchar *parsed_name;
g_object_get(file,
"path", &filepath,
"parsed-name", &parsed_name,
NULL);
g_free(filepath);
g_free(parsed_name);
g_object_unref(file);
} }
g_strfreev(mime_info); // Free our mimeinfo g_strfreev(mime_info); // Free our mimeinfo
} }
static void koto_indexed_library_init(KotoIndexedLibrary *self) {
self->files = g_hash_table_new(g_str_hash, g_str_equal);
}
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) { KotoIndexedLibrary* koto_indexed_library_new(const gchar *path) {
return g_object_new(KOTO_INDEXED_TYPE_LIBRARY, return g_object_new(KOTO_TYPE_INDEXED_LIBRARY,
"path", path, "path", path,
NULL NULL
); );

View file

@ -20,14 +20,10 @@
G_BEGIN_DECLS G_BEGIN_DECLS
#define KOTO_INDEXED_TYPE_FILE koto_indexed_file_get_type() #define KOTO_TYPE_INDEXED_LIBRARY koto_indexed_library_get_type()
G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GObject);
#define KOTO_INDEXED_TYPE_LIBRARY koto_indexed_library_get_type()
G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject); G_DECLARE_FINAL_TYPE(KotoIndexedLibrary, koto_indexed_library, KOTO, INDEXED_LIBRARY, GObject);
KotoIndexedLibrary* koto_indexed_library_new(const gchar *path); KotoIndexedLibrary* koto_indexed_library_new(const gchar *path);
KotoIndexedFile* koto_indexed_file_new(gchar *path);
void start_indexing(KotoIndexedLibrary *self); void start_indexing(KotoIndexedLibrary *self);
void index_folder(KotoIndexedLibrary *self, gchar *path); void index_folder(KotoIndexedLibrary *self, gchar *path);

274
src/indexer/file.c Normal file
View file

@ -0,0 +1,274 @@
/* file.c
*
* Copyright 2021 Joshua Strobl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <glib-2.0/glib.h>
#include "file.h"
struct _KotoIndexedFile {
GObject parent_instance;
gchar *path;
gchar *file_name;
gchar *parsed_name;
gchar *artist;
gchar *album;
guint position;
gboolean acquired_metadata_from_id3;
};
G_DEFINE_TYPE(KotoIndexedFile, koto_indexed_file, G_TYPE_OBJECT);
enum {
PROP_0,
PROP_PATH,
PROP_ARTIST,
PROP_ALBUM,
PROP_FILE_NAME,
PROP_PARSED_NAME,
PROP_POSITION,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL };
static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_indexed_file_class_init(KotoIndexedFileClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_indexed_file_set_property;
gobject_class->get_property = koto_indexed_file_get_property;
props[PROP_PATH] = g_param_spec_string(
"path",
"Path",
"Path to File",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_ARTIST] = g_param_spec_string(
"artist",
"Name of Artist",
"Name of Artist",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_ALBUM] = g_param_spec_string(
"album",
"Name of Album",
"Name of Album",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_FILE_NAME] = g_param_spec_string(
"file-name",
"Name of File",
"Name of File",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_PARSED_NAME] = g_param_spec_string(
"parsed-name",
"Parsed Name of File",
"Parsed Name of File",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
props[PROP_POSITION] = g_param_spec_uint(
"position",
"Position in Audiobook, Album, etc.",
"Position in Audiobook, Album, etc.",
0,
G_MAXUINT16,
0,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_indexed_file_init(KotoIndexedFile *self) {
self->acquired_metadata_from_id3 = FALSE;
}
static void koto_indexed_file_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
switch (prop_id) {
case PROP_PATH:
g_value_set_string(val, self->path);
break;
case PROP_ARTIST:
g_value_set_string(val, self->artist);
break;
case PROP_ALBUM:
g_value_set_string(val, self->album);
break;
case PROP_FILE_NAME:
g_value_set_string(val, self->file_name);
break;
case PROP_PARSED_NAME:
g_value_set_string(val, self->parsed_name);
break;
case PROP_POSITION:
g_value_set_uint(val, self->position);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
static void koto_indexed_file_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
KotoIndexedFile *self = KOTO_INDEXED_FILE(obj);
switch (prop_id) {
case PROP_PATH:
self->path = g_strdup(g_value_get_string(val)); // Update our path
koto_indexed_file_set_file_name(self, g_path_get_basename(self->path)); // Update our file name
break;
case PROP_ARTIST:
self->artist = g_strdup(g_value_get_string(val));
break;
case PROP_ALBUM:
self->album = g_strdup(g_value_get_string(val));
break;
case PROP_FILE_NAME:
koto_indexed_file_set_file_name(self, g_strdup(g_value_get_string(val))); // Update the file name
break;
case PROP_PARSED_NAME:
koto_indexed_file_set_parsed_name(self, g_strdup(g_value_get_string(val)));
break;
case PROP_POSITION:
self->position = g_value_get_uint(val);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
}
}
void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name) {
if (new_file_name == NULL) {
return;
}
if ((self->file_name != NULL) && (strcmp(self->file_name, new_file_name) == 0)) { // Not null and the same
return; // Don't do anything
}
if (self->file_name != NULL) { // If it is defined
g_free(self->file_name);
}
self->file_name = g_strdup(new_file_name);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_FILE_NAME]);
if (!self->acquired_metadata_from_id3) { // Haven't acquired our information from ID3
koto_indexed_file_parse_name(self); // Update our parsed name
}
}
void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name) {
if (new_parsed_name == NULL) {
return;
}
if ((self->parsed_name != NULL) && (strcmp(self->parsed_name, new_parsed_name) == 0)) { // Not null and the same
return; // Don't do anything
}
if (self->parsed_name != NULL) {
g_free(self->parsed_name);
}
self->parsed_name = g_strdup(new_parsed_name);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PARSED_NAME]);
}
void koto_indexed_file_parse_name(KotoIndexedFile *self) {
gchar *copied_file_name = g_strdelimit(g_strdup(self->file_name), "_", ' '); // Replace _ with whitespace for starters
if (self->artist != NULL && strcmp(self->artist, "") != 0) { // If we have artist set
gchar **split = g_strsplit(copied_file_name, self->artist, -1); // Split whenever we encounter the artist
copied_file_name = g_strjoinv("", split); // Remove the artist
g_strfreev(split);
}
if (self->artist != NULL && strcmp(self->album, "") != 0) { // If we have album set
gchar **split = g_strsplit(copied_file_name, self->album, -1); // Split whenever we encounter the album
copied_file_name = g_strjoinv("", split); // Remove the album
g_strfreev(split);
split = g_strsplit(copied_file_name, g_utf8_strdown(self->album, g_utf8_strlen(self->album, -1)), -1); // Split whenever we encounter the album lowercased
copied_file_name = g_strjoin("", split, NULL); // Remove the lowercased album
g_strfreev(split);
}
if (g_str_match_string(copied_file_name, "-", FALSE)) { // Contains - like a heathen
gchar **split = g_strsplit(copied_file_name, " - ", -1); // Split whenever we encounter " - "
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
if (g_str_match_string(copied_file_name, "-", FALSE)) { // If we have any stragglers
split = g_strsplit(copied_file_name, "-", -1); // Split whenever we encounter -
copied_file_name = g_strjoin("", split, NULL); // Remove entirely
g_strfreev(split);
}
}
gchar **split = g_strsplit(copied_file_name, ".", -1); // Split every time we see .
g_free(copied_file_name);
guint len_of_extension_split = g_strv_length(split);
if (len_of_extension_split == 2) { // Only have two elements
copied_file_name = g_strdup(split[0]); // Get the first element
} else {
gchar *new_parsed_name = "";
for (guint i = 0; i < len_of_extension_split - 1; i++) { // Iterate over everything except the last item
if (g_strcmp0(new_parsed_name, "") == 0) { // Currently empty
new_parsed_name = g_strdup(split[i]); // Just duplicate this string
} else {
gchar *tmp_copy = g_strdup(new_parsed_name);
g_free(new_parsed_name); // Free the old
new_parsed_name = g_strdup(g_strjoin(".", tmp_copy, split[i], NULL)); // Join the two strings with a . again and duplicate it, setting it to our new_parsed_name
g_free(tmp_copy); // Free our temporary copy
}
}
copied_file_name = g_strdup(new_parsed_name);
g_free(new_parsed_name);
}
g_strfreev(split);
koto_indexed_file_set_parsed_name(self, copied_file_name);
g_free(copied_file_name);
}
KotoIndexedFile* koto_indexed_file_new(const gchar *path) {
return g_object_new(KOTO_TYPE_INDEXED_FILE,
"path", path,
NULL
);
}

View file

@ -1,4 +1,4 @@
/* koto-flipper-button.h /* file.h
* *
* Copyright 2021 Joshua Strobl * Copyright 2021 Joshua Strobl
* *
@ -16,17 +16,17 @@
*/ */
#pragma once #pragma once
#include <glib-2.0/glib-object.h>
#include <gtk-3.0/gtk/gtk.h>
G_BEGIN_DECLS G_BEGIN_DECLS
#define KOTO_TYPE_FLIPPER_BUTTON (koto_flipper_button_get_type()) #define KOTO_TYPE_INDEXED_FILE koto_indexed_file_get_type()
G_DECLARE_FINAL_TYPE(KotoIndexedFile, koto_indexed_file, KOTO, INDEXED_FILE, GObject);
G_DECLARE_FINAL_TYPE (KotoFlipperButton, koto_flipper_button, KOTO, FLIPPER_BUTTON, GtkButton) KotoIndexedFile* koto_indexed_file_new(const gchar *path);
KotoFlipperButton* koto_flipper_button_new(gchar *initial_icon, gchar *flipped_icon, GtkIconSize size); void koto_indexed_file_set_file_name(KotoIndexedFile *self, gchar *new_file_name);
void koto_flipper_button_flip(KotoFlipperButton *self); void koto_indexed_file_set_parsed_name(KotoIndexedFile *self, gchar *new_parsed_name);
void koto_flipper_button_set_icon(KotoFlipperButton *self, gchar *name); void koto_indexed_file_parse_name(KotoIndexedFile *self);
G_END_DECLS G_END_DECLS

View file

@ -1,4 +1,5 @@
indexer_sources = [ indexer_sources = [
'file.c',
'file-indexer.c', 'file-indexer.c',
] ]

View file

@ -50,19 +50,21 @@ guint koto_get_pixbuf_size(KotoButtonPixbufSize s) {
} }
enum { enum {
PROP_0, PROP_BTN_0,
PROP_PIX_SIZE, PROP_PIX_SIZE,
PROP_TEXT, PROP_TEXT,
PROP_BADGE_TEXT, PROP_BADGE_TEXT,
PROP_PIX, PROP_PIX,
PROP_ICON_NAME, PROP_ICON_NAME,
N_PROPERTIES PROP_ALT_ICON_NAME,
N_BTN_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec *btn_props[N_BTN_PROPERTIES] = { NULL, };
struct _KotoButton { struct _KotoButton {
GtkBox parent_instance; GtkEventBox parent_instance;
GtkBox *content;
guint pix_size; guint pix_size;
GtkWidget *button_image; GtkWidget *button_image;
@ -71,16 +73,18 @@ struct _KotoButton {
gchar *badge_text; gchar *badge_text;
gchar *icon_name; gchar *icon_name;
gchar *alt_icon_name;
gchar *text; gchar *text;
GdkPixbuf *pix; GdkPixbuf *pix;
gboolean currently_showing_alt;
}; };
struct _KotoButtonClass { struct _KotoButtonClass {
GtkBoxClass parent_class; GtkBoxClass parent_class;
}; };
G_DEFINE_TYPE(KotoButton, koto_button, GTK_TYPE_BOX); G_DEFINE_TYPE(KotoButton, koto_button, GTK_TYPE_EVENT_BOX);
static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec); static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec);
static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec); static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
@ -91,7 +95,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
gobject_class->set_property = koto_button_set_property; gobject_class->set_property = koto_button_set_property;
gobject_class->get_property = koto_button_get_property; gobject_class->get_property = koto_button_get_property;
props[PROP_PIX_SIZE] = g_param_spec_uint( btn_props[PROP_PIX_SIZE] = g_param_spec_uint(
"pixbuf-size", "pixbuf-size",
"Pixbuf Size", "Pixbuf Size",
"Size of the pixbuf", "Size of the pixbuf",
@ -101,7 +105,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_TEXT] = g_param_spec_string( btn_props[PROP_TEXT] = g_param_spec_string(
"button-text", "button-text",
"Button Text", "Button Text",
"Text of Button", "Text of Button",
@ -109,7 +113,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_BADGE_TEXT] = g_param_spec_string( btn_props[PROP_BADGE_TEXT] = g_param_spec_string(
"badge-text", "badge-text",
"Badge Text", "Badge Text",
"Text of Badge", "Text of Badge",
@ -117,7 +121,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_PIX] = g_param_spec_object( btn_props[PROP_PIX] = g_param_spec_object(
"pixbuf", "pixbuf",
"Pixbuf", "Pixbuf",
"Pixbuf", "Pixbuf",
@ -125,7 +129,7 @@ static void koto_button_class_init(KotoButtonClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_ICON_NAME] = g_param_spec_string( btn_props[PROP_ICON_NAME] = g_param_spec_string(
"icon-name", "icon-name",
"Icon Name", "Icon Name",
"Name of Icon", "Name of Icon",
@ -133,12 +137,23 @@ static void koto_button_class_init(KotoButtonClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); btn_props[PROP_ALT_ICON_NAME] = g_param_spec_string(
"alt-icon-name",
"Name of an Alternate Icon",
"Name of an Alternate Icon",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_BTN_PROPERTIES, btn_props);
} }
static void koto_button_init(KotoButton *self) { static void koto_button_init(KotoButton *self) {
GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(self)); GtkStyleContext *style = gtk_widget_get_style_context(GTK_WIDGET(self));
gtk_style_context_add_class(style, "koto-button"); gtk_style_context_add_class(style, "koto-button");
self->currently_showing_alt = FALSE;
self->content = GTK_BOX(gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0));
gtk_container_add(GTK_CONTAINER(self), GTK_WIDGET(self->content));
} }
static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
@ -157,6 +172,9 @@ static void koto_button_get_property(GObject *obj, guint prop_id, GValue *val, G
case PROP_ICON_NAME: case PROP_ICON_NAME:
g_value_set_string(val, self->icon_name); g_value_set_string(val, self->icon_name);
break; break;
case PROP_ALT_ICON_NAME:
g_value_set_string(val, self->alt_icon_name);
break;
case PROP_PIX: case PROP_PIX:
g_value_set_object(val, self->pix); g_value_set_object(val, self->pix);
break; break;
@ -183,7 +201,16 @@ static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *
koto_button_set_pixbuf(self, (GdkPixbuf*) g_value_get_object(val)); koto_button_set_pixbuf(self, (GdkPixbuf*) g_value_get_object(val));
break; break;
case PROP_ICON_NAME: case PROP_ICON_NAME:
koto_button_set_icon_name(self, g_strdup(g_value_get_string(val))); koto_button_set_icon_name(self, g_strdup(g_value_get_string(val)), FALSE);
if (!self->currently_showing_alt) { // Not showing alt
koto_button_show_image(self, FALSE);
}
break;
case PROP_ALT_ICON_NAME:
koto_button_set_icon_name(self, g_strdup(g_value_get_string(val)), TRUE);
if (self->currently_showing_alt) { // Currently showing the alt image
koto_button_show_image(self, TRUE);
}
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
@ -191,6 +218,10 @@ static void koto_button_set_property(GObject *obj, guint prop_id, const GValue *
} }
} }
void koto_button_flip(KotoButton *self) {
koto_button_show_image(self, !self->currently_showing_alt);
}
void koto_button_set_badge_text(KotoButton *self, gchar *text) { void koto_button_set_badge_text(KotoButton *self, gchar *text) {
if ((text == NULL) || (strcmp(text, "") == 0)) { // If the text is empty if ((text == NULL) || (strcmp(text, "") == 0)) { // If the text is empty
self->badge_text = g_strdup(""); self->badge_text = g_strdup("");
@ -203,7 +234,7 @@ void koto_button_set_badge_text(KotoButton *self, gchar *text) {
gtk_label_set_text(GTK_LABEL(self->badge_label), self->badge_text); gtk_label_set_text(GTK_LABEL(self->badge_label), self->badge_text);
} else { } else {
self->badge_label = gtk_label_new(self->badge_text); // Create our label self->badge_label = gtk_label_new(self->badge_text); // Create our label
gtk_box_pack_end(GTK_BOX(self), self->badge_label, FALSE, FALSE, 0); // Add to the end of the box gtk_box_pack_end(self->content, self->badge_label, FALSE, FALSE, 0); // Add to the end of the box
} }
if (strcmp(self->badge_text, "") != 0) { // Empty badge if (strcmp(self->badge_text, "") != 0) { // Empty badge
@ -212,17 +243,34 @@ void koto_button_set_badge_text(KotoButton *self, gchar *text) {
gtk_widget_show(self->badge_label); // Show our badge gtk_widget_show(self->badge_label); // Show our badge
} }
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_BADGE_TEXT]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_BADGE_TEXT]);
} }
void koto_button_set_icon_name(KotoButton *self, gchar *icon_name) { void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_alt) {
gchar *copied_icon_name = g_strdup(icon_name); gchar *copied_icon_name = g_strdup(icon_name);
g_message("icon name set in koto button: %s", icon_name);
g_free(self->icon_name);
self->icon_name = copied_icon_name;
if ((self->icon_name == NULL) || (strcmp(self->icon_name,"") == 0)) { // Have no icon name now if (for_alt) { // Is for the alternate icon
g_message("Have no icon name now?"); if ((self->alt_icon_name != NULL) && strcmp(icon_name, self->alt_icon_name) != 0) { // If the icons are different
g_free(self->alt_icon_name);
}
self->alt_icon_name = copied_icon_name;
} else {
if ((self->icon_name != NULL) && strcmp(icon_name, self->icon_name) != 0) {
g_free(self->icon_name);
}
self->icon_name = copied_icon_name;
}
gboolean hide_image = FALSE;
if (for_alt && self->currently_showing_alt && ((self->alt_icon_name == NULL) || strcmp(self->alt_icon_name, "") == 0)) { // For alt, alt is currently showing, and no longer have alt
hide_image = TRUE;
} else if (!for_alt && ((self->icon_name == NULL) || (strcmp(self->icon_name,"") == 0))) { // Not for alt, no icon
hide_image = TRUE;
}
if (hide_image) { // Should hide the image
if (GTK_IS_IMAGE(self->button_image)) { // If we already have a button image if (GTK_IS_IMAGE(self->button_image)) { // If we already have a button image
gtk_widget_hide(self->button_image); // Hide gtk_widget_hide(self->button_image); // Hide
} }
@ -230,12 +278,7 @@ void koto_button_set_icon_name(KotoButton *self, gchar *icon_name) {
return; return;
} }
GtkIconTheme *theme = gtk_icon_theme_get_default(); // Get the default icon theme g_object_notify_by_pspec(G_OBJECT(self), for_alt ? btn_props[PROP_ALT_ICON_NAME] : btn_props[PROP_ICON_NAME]);
GdkPixbuf* icon_pix = gtk_icon_theme_load_icon_for_scale(theme, self->icon_name, (gint) self->pix_size, 1, GTK_ICON_LOOKUP_USE_BUILTIN & GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
g_return_if_fail(GDK_IS_PIXBUF(icon_pix)); // Return if not a pixbuf
koto_button_set_pixbuf(self, icon_pix);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_ICON_NAME]);
} }
void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix) { void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix) {
@ -260,11 +303,11 @@ void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix) {
GtkWidget *new_image = gtk_image_new_from_pixbuf(use_pix); // Create our new image GtkWidget *new_image = gtk_image_new_from_pixbuf(use_pix); // Create our new image
g_return_if_fail(GTK_IS_IMAGE(new_image)); // Return if we failed to create our image g_return_if_fail(GTK_IS_IMAGE(new_image)); // Return if we failed to create our image
self->button_image = new_image; self->button_image = new_image;
gtk_box_pack_start(GTK_BOX(self), self->button_image, FALSE, FALSE, 0); // Prepend the image gtk_box_pack_start(self->content, self->button_image, FALSE, FALSE, 0); // Prepend the image
gtk_box_reorder_child(GTK_BOX(self), self->button_image, 0); gtk_box_reorder_child(self->content, self->button_image, 0);
} }
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PIX]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_PIX]);
} }
void koto_button_set_pixbuf_size(KotoButton *self, guint size) { void koto_button_set_pixbuf_size(KotoButton *self, guint size) {
@ -276,7 +319,7 @@ void koto_button_set_pixbuf_size(KotoButton *self, guint size) {
koto_button_set_pixbuf(self, self->pix); koto_button_set_pixbuf(self, self->pix);
} }
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_PIX_SIZE]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_PIX_SIZE]);
} }
void koto_button_set_text(KotoButton *self, gchar *text) { void koto_button_set_text(KotoButton *self, gchar *text) {
@ -300,30 +343,43 @@ void koto_button_set_text(KotoButton *self, gchar *text) {
if (strcmp(self->text, "") != 0) { // If we have text if (strcmp(self->text, "") != 0) { // If we have text
self->button_label = gtk_label_new(self->text); // Create our label self->button_label = gtk_label_new(self->text); // Create our label
gtk_label_set_xalign(GTK_LABEL(self->button_label), 0); gtk_label_set_xalign(GTK_LABEL(self->button_label), 0);
gtk_box_pack_start(GTK_BOX(self), self->button_label, FALSE, FALSE, 0); // Add to the beginning of the box gtk_box_pack_start(self->content, self->button_label, FALSE, FALSE, 0); // Add to the beginning of the box
if (GTK_IS_IMAGE(self->button_image)) { // If we have an image if (GTK_IS_IMAGE(self->button_image)) { // If we have an image
gtk_box_reorder_child(GTK_BOX(self), self->button_image, 0); // Move to the beginning gtk_box_reorder_child(self->content, self->button_image, 0); // Move to the beginning
} }
} }
} }
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_TEXT]); g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_TEXT]);
}
void koto_button_show_image(KotoButton *self, gboolean use_alt) {
if (use_alt && ((self->alt_icon_name == NULL) || (strcmp(self->alt_icon_name, "") == 0))) { // Don't have an alt icon set
return;
} else if (!use_alt && ((self->icon_name == NULL) || (strcmp(self->icon_name, "") == 0))) { // Don't have icon set
return;
}
GtkIconTheme *theme = gtk_icon_theme_get_default(); // Get the default icon theme
GdkPixbuf* icon_pix = gtk_icon_theme_load_icon_for_scale(theme, use_alt ? self->alt_icon_name : self->icon_name, (gint) self->pix_size, 1, GTK_ICON_LOOKUP_USE_BUILTIN & GTK_ICON_LOOKUP_GENERIC_FALLBACK, NULL);
g_return_if_fail(GDK_IS_PIXBUF(icon_pix)); // Return if not a pixbuf
koto_button_set_pixbuf(self, icon_pix);
self->currently_showing_alt = use_alt;
} }
KotoButton* koto_button_new_plain(gchar *label) { KotoButton* koto_button_new_plain(gchar *label) {
return g_object_new(KOTO_TYPE_BUTTON, return g_object_new(KOTO_TYPE_BUTTON,
"button-text", label, "button-text", label,
"orientation", GTK_ORIENTATION_HORIZONTAL,
NULL NULL
); );
} }
KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, KotoButtonPixbufSize size) { KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, gchar *alt_icon_name, KotoButtonPixbufSize size) {
return g_object_new(KOTO_TYPE_BUTTON, return g_object_new(KOTO_TYPE_BUTTON,
"button-text", label, "button-text", label,
"icon-name", icon_name, "icon-name", icon_name,
"orientation", GTK_ORIENTATION_HORIZONTAL, "alt-icon-name", alt_icon_name,
"pixbuf-size", koto_get_pixbuf_size(size), "pixbuf-size", koto_get_pixbuf_size(size),
NULL NULL
); );
@ -333,7 +389,6 @@ KotoButton* koto_button_new_with_pixbuf(gchar *label, GdkPixbuf *pix, KotoButton
return g_object_new(KOTO_TYPE_BUTTON, return g_object_new(KOTO_TYPE_BUTTON,
"button-text", label, "button-text", label,
"pixbuf", pix, "pixbuf", pix,
"orientation", GTK_ORIENTATION_HORIZONTAL,
"pixbuf-size", koto_get_pixbuf_size(size), "pixbuf-size", koto_get_pixbuf_size(size),
NULL NULL
); );

View file

@ -16,9 +16,6 @@
*/ */
#pragma once #pragma once
#ifndef __GTK_ENUMS_H__
#define __GTK_ENUMS_H__
#endif
#include <glib-object.h> #include <glib-object.h>
#include <gdk-pixbuf-2.0/gdk-pixbuf/gdk-pixbuf.h> #include <gdk-pixbuf-2.0/gdk-pixbuf/gdk-pixbuf.h>
@ -40,18 +37,20 @@ typedef enum {
#define KOTO_TYPE_BUTTON (koto_button_get_type()) #define KOTO_TYPE_BUTTON (koto_button_get_type())
G_DECLARE_FINAL_TYPE (KotoButton, koto_button, KOTO, BUTTON, GtkBox) G_DECLARE_FINAL_TYPE (KotoButton, koto_button, KOTO, BUTTON, GtkEventBox)
guint koto_get_pixbuf_size(KotoButtonPixbufSize size); guint koto_get_pixbuf_size(KotoButtonPixbufSize size);
KotoButton* koto_button_new_plain(gchar *label); KotoButton* koto_button_new_plain(gchar *label);
KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, KotoButtonPixbufSize size); KotoButton* koto_button_new_with_icon(gchar *label, gchar *icon_name, gchar *alt_icon_name, KotoButtonPixbufSize size);
KotoButton* koto_button_new_with_pixbuf(gchar *label, GdkPixbuf *pix, KotoButtonPixbufSize size); KotoButton* koto_button_new_with_pixbuf(gchar *label, GdkPixbuf *pix, KotoButtonPixbufSize size);
void koto_button_flip(KotoButton *self);
void koto_button_set_badge_text(KotoButton *self, gchar *text); void koto_button_set_badge_text(KotoButton *self, gchar *text);
void koto_button_set_icon_name(KotoButton *self, gchar *icon_name); void koto_button_set_icon_name(KotoButton *self, gchar *icon_name, gboolean for_alt);
void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix); void koto_button_set_pixbuf(KotoButton *self, GdkPixbuf *pix);
void koto_button_set_pixbuf_size(KotoButton *self, guint size); void koto_button_set_pixbuf_size(KotoButton *self, guint size);
void koto_button_set_text(KotoButton *self, gchar *text); void koto_button_set_text(KotoButton *self, gchar *text);
void koto_button_show_image(KotoButton *self, gboolean use_alt);
G_END_DECLS G_END_DECLS

View file

@ -19,20 +19,18 @@
#include <gtk-3.0/gtk/gtk.h> #include <gtk-3.0/gtk/gtk.h>
#include "koto-config.h" #include "koto-config.h"
#include "koto-button.h" #include "koto-button.h"
#include "koto-flipper-button.h"
#include "koto-expander.h" #include "koto-expander.h"
#include "koto-utils.h"
enum { enum {
PROP_0, PROP_EXP_0,
PROP_HEADER_ICON_NAME, PROP_HEADER_ICON_NAME,
PROP_HEADER_LABEL, PROP_HEADER_LABEL,
PROP_HEADER_SECONDARY_BUTTON, PROP_HEADER_SECONDARY_BUTTON,
PROP_CONTENT, PROP_CONTENT,
N_PROPERTIES N_EXP_PROPERTIES
}; };
static GParamSpec *props[N_PROPERTIES] = { NULL, }; static GParamSpec *expander_props[N_EXP_PROPERTIES] = { NULL, };
struct _KotoExpander { struct _KotoExpander {
GtkBox parent_instance; GtkBox parent_instance;
@ -43,8 +41,8 @@ struct _KotoExpander {
gchar *icon_name; gchar *icon_name;
gchar *label; gchar *label;
GtkWidget *header_secondary_button; KotoButton *header_secondary_button;
KotoFlipperButton *header_expand_button; KotoButton *header_expand_button;
GtkWidget *revealer; GtkWidget *revealer;
GtkWidget *content; GtkWidget *content;
@ -65,7 +63,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
gobject_class->set_property = koto_expander_set_property; gobject_class->set_property = koto_expander_set_property;
gobject_class->get_property = koto_expander_get_property; gobject_class->get_property = koto_expander_get_property;
props[PROP_HEADER_ICON_NAME] = g_param_spec_string( expander_props[PROP_HEADER_ICON_NAME] = g_param_spec_string(
"icon-name", "icon-name",
"Icon Name", "Icon Name",
"Name of the icon to use in the Expander", "Name of the icon to use in the Expander",
@ -73,7 +71,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_HEADER_LABEL] = g_param_spec_string( expander_props[PROP_HEADER_LABEL] = g_param_spec_string(
"label", "label",
"Label", "Label",
"Label for the Expander", "Label for the Expander",
@ -81,15 +79,15 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_HEADER_SECONDARY_BUTTON] = g_param_spec_object( expander_props[PROP_HEADER_SECONDARY_BUTTON] = g_param_spec_object(
"secondary-button", "secondary-button",
"Secondary Button", "Secondary Button",
"Secondary Button to be placed next to Expander button", "Secondary Button to be placed next to Expander button",
GTK_TYPE_WIDGET, KOTO_TYPE_BUTTON,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
props[PROP_CONTENT] = g_param_spec_object( expander_props[PROP_CONTENT] = g_param_spec_object(
"content", "content",
"Content", "Content",
"Content inside the Expander", "Content inside the Expander",
@ -97,7 +95,7 @@ static void koto_expander_class_init(KotoExpanderClass *c) {
G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE G_PARAM_EXPLICIT_NOTIFY|G_PARAM_READWRITE
); );
g_object_class_install_properties(gobject_class, N_PROPERTIES, props); g_object_class_install_properties(gobject_class, N_EXP_PROPERTIES, expander_props);
} }
static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) { static void koto_expander_get_property(GObject *obj, guint prop_id, GValue *val, GParamSpec *spec) {
@ -126,7 +124,7 @@ static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue
KotoExpander *self = KOTO_EXPANDER(obj); KotoExpander *self = KOTO_EXPANDER(obj);
if (!GTK_IS_WIDGET(self->header_button)) { // Header Button is not a widget if (!GTK_IS_WIDGET(self->header_button)) { // Header Button is not a widget
KotoButton *new_button = koto_button_new_with_icon("Temporary Text", "emblem-favorite-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL); KotoButton *new_button = koto_button_new_with_icon("Temporary Text", "emblem-favorite-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
if (GTK_IS_WIDGET(new_button)) { // Created our widget successfully if (GTK_IS_WIDGET(new_button)) { // Created our widget successfully
self->header_button = new_button; self->header_button = new_button;
@ -137,14 +135,14 @@ static void koto_expander_set_property(GObject *obj, guint prop_id, const GValue
switch (prop_id) { switch (prop_id) {
case PROP_HEADER_ICON_NAME: case PROP_HEADER_ICON_NAME:
g_return_if_fail(GTK_IS_WIDGET(self->header_button)); g_return_if_fail(GTK_IS_WIDGET(self->header_button));
koto_button_set_icon_name(self->header_button, g_strdup(g_value_get_string(val))); koto_button_set_icon_name(self->header_button, g_strdup(g_value_get_string(val)), FALSE);
break; break;
case PROP_HEADER_LABEL: case PROP_HEADER_LABEL:
g_return_if_fail(GTK_IS_WIDGET(self->header_button)); g_return_if_fail(GTK_IS_WIDGET(self->header_button));
koto_button_set_text(self->header_button, g_strdup(g_value_get_string(val))); koto_button_set_text(self->header_button, g_strdup(g_value_get_string(val)));
break; break;
case PROP_HEADER_SECONDARY_BUTTON: case PROP_HEADER_SECONDARY_BUTTON:
koto_expander_set_secondary_button(self, (GtkWidget*) g_value_get_object(val)); koto_expander_set_secondary_button(self, (KotoButton*) g_value_get_object(val));
break; break;
case PROP_CONTENT: case PROP_CONTENT:
koto_expander_set_content(self, (GtkWidget*) g_value_get_object(val)); koto_expander_set_content(self, (GtkWidget*) g_value_get_object(val));
@ -171,7 +169,7 @@ static void koto_expander_init(KotoExpander *self) {
gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE); // Set to be revealed by default gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE); // Set to be revealed by default
gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN); gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_SLIDE_DOWN);
self->header_expand_button = koto_flipper_button_new("pan-down-symbolic", "pan-up-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR); self->header_expand_button = koto_button_new_with_icon("", "pan-down-symbolic", "pan-up-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL);
gtk_box_pack_end(GTK_BOX(self->header), GTK_WIDGET(self->header_expand_button), FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(self->header), GTK_WIDGET(self->header_expand_button), FALSE, FALSE, 0);
gtk_box_pack_start(GTK_BOX(self), self->header, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(self), self->header, FALSE, FALSE, 0);
@ -179,27 +177,26 @@ static void koto_expander_init(KotoExpander *self) {
self->constructed = TRUE; self->constructed = TRUE;
g_signal_connect(self->header_expand_button, "clicked", G_CALLBACK(koto_expander_toggle_content), self); g_signal_connect(self->header_expand_button, "button-release-event", G_CALLBACK(koto_expander_toggle_content), self);
} }
void koto_expander_set_secondary_button(KotoExpander *self, GtkWidget *new_button) { void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_button) {
if (!self->constructed) { if (!self->constructed) {
return; return;
} }
if (!GTK_IS_BUTTON(new_button)) { if (!GTK_IS_WIDGET(new_button)) {
return; return;
} }
if (GTK_IS_BUTTON(self->header_secondary_button)) { // Already have a button if (GTK_IS_WIDGET(self->header_secondary_button)) { // Already have a button
gtk_container_remove(GTK_CONTAINER(self->header), self->header_secondary_button); gtk_container_remove(GTK_CONTAINER(self->header), GTK_WIDGET(self->header_secondary_button));
//g_free(self->header_secondary_button);
} }
self->header_secondary_button = new_button; self->header_secondary_button = new_button;
gtk_box_pack_end(GTK_BOX(self->header), self->header_secondary_button, FALSE, FALSE, 0); gtk_box_pack_end(GTK_BOX(self->header), GTK_WIDGET(self->header_secondary_button), FALSE, FALSE, 0);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_HEADER_SECONDARY_BUTTON]); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_HEADER_SECONDARY_BUTTON]);
} }
void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) { void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) {
@ -215,12 +212,12 @@ void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content) {
self->content = new_content; self->content = new_content;
gtk_container_add(GTK_CONTAINER(self->revealer), self->content); gtk_container_add(GTK_CONTAINER(self->revealer), self->content);
g_object_notify_by_pspec(G_OBJECT(self), props[PROP_CONTENT]); g_object_notify_by_pspec(G_OBJECT(self), expander_props[PROP_CONTENT]);
} }
void koto_expander_toggle_content(GtkWidget *button, gpointer data) { void koto_expander_toggle_content(GtkWidget *button, GdkEvent *event, gpointer data) {
KotoExpander* self = data; KotoExpander* self = data;
koto_flipper_button_flip(KOTO_FLIPPER_BUTTON(button)); koto_button_flip(KOTO_BUTTON(button));
GtkRevealer* rev = GTK_REVEALER(self->revealer); GtkRevealer* rev = GTK_REVEALER(self->revealer);
gtk_revealer_set_reveal_child(rev, !gtk_revealer_get_reveal_child(rev)); // Invert our values gtk_revealer_set_reveal_child(rev, !gtk_revealer_get_reveal_child(rev)); // Invert our values
} }
@ -234,7 +231,7 @@ KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_t
); );
} }
KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, GtkWidget *secondary_button) { KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, KotoButton *secondary_button) {
return g_object_new(KOTO_TYPE_EXPANDER, return g_object_new(KOTO_TYPE_EXPANDER,
"orientation", GTK_ORIENTATION_VERTICAL, "orientation", GTK_ORIENTATION_VERTICAL,
"icon-name", primary_icon_name, "icon-name", primary_icon_name,

View file

@ -18,6 +18,7 @@
#pragma once #pragma once
#include <gtk-3.0/gtk/gtk.h> #include <gtk-3.0/gtk/gtk.h>
#include "koto-button.h"
G_BEGIN_DECLS G_BEGIN_DECLS
@ -26,11 +27,11 @@ G_BEGIN_DECLS
G_DECLARE_FINAL_TYPE (KotoExpander, koto_expander, KOTO, EXPANDER, GtkBox) G_DECLARE_FINAL_TYPE (KotoExpander, koto_expander, KOTO, EXPANDER, GtkBox)
KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_text); KotoExpander* koto_expander_new(gchar *primary_icon_name, gchar *primary_label_text);
KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, GtkWidget *secondary_button); KotoExpander* koto_expander_new_with_button(gchar *primary_icon_name, gchar *primary_label_text, KotoButton *secondary_button);
void koto_expander_set_icon_name(KotoExpander *self, const gchar *in); void koto_expander_set_icon_name(KotoExpander *self, const gchar *in);
void koto_expander_set_label(KotoExpander *self, const gchar *label); void koto_expander_set_label(KotoExpander *self, const gchar *label);
void koto_expander_set_secondary_button(KotoExpander *self, GtkWidget *new_button); void koto_expander_set_secondary_button(KotoExpander *self, KotoButton *new_button);
void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content); void koto_expander_set_content(KotoExpander *self, GtkWidget *new_content);
void koto_expander_toggle_content(GtkWidget *button, gpointer data); void koto_expander_toggle_content(GtkWidget *button, GdkEvent *event, gpointer data);
G_END_DECLS G_END_DECLS

View file

@ -1,129 +0,0 @@
/* koto-flipper-button.c
*
* Copyright 2021 Joshua Strobl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtk-3.0/gtk/gtk.h>
#include "koto-config.h"
#include "koto-flipper-button.h"
enum {
PROP_0,
PROP_SIZE,
PROP_INITIAL_IMAGE,
PROP_FLIPPED_IMAGE,
N_PROPERTIES
};
static GParamSpec *props[N_PROPERTIES] = { NULL, };
struct _KotoFlipperButton {
GtkButton parent_instance;
GtkIconSize size;
gboolean flipped;
gchar *initial_image;
gchar *flipped_image;
};
struct _KotoFlipperButtonClass {
GtkButtonClass parent_class;
};
G_DEFINE_TYPE(KotoFlipperButton, koto_flipper_button, GTK_TYPE_BUTTON);
static void koto_flipper_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec);
static void koto_flipper_button_class_init(KotoFlipperButtonClass *c) {
GObjectClass *gobject_class;
gobject_class = G_OBJECT_CLASS(c);
gobject_class->set_property = koto_flipper_button_set_property;
props[PROP_SIZE] = g_param_spec_enum(
"size",
"Icon Size",
"Size of the icon",
GTK_TYPE_ICON_SIZE,
GTK_ICON_SIZE_MENU,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE
);
props[PROP_INITIAL_IMAGE] = g_param_spec_string(
"initial-image",
"Initial Image",
"Image to use initially for the button",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE
);
props[PROP_FLIPPED_IMAGE] = g_param_spec_string(
"flipped-image",
"Flipped Image",
"Image to use when the button is flipped",
NULL,
G_PARAM_CONSTRUCT|G_PARAM_EXPLICIT_NOTIFY|G_PARAM_WRITABLE
);
g_object_class_install_properties(gobject_class, N_PROPERTIES, props);
}
static void koto_flipper_button_set_property(GObject *obj, guint prop_id, const GValue *val, GParamSpec *spec) {
KotoFlipperButton *self = KOTO_FLIPPER_BUTTON(obj);
if (prop_id == PROP_INITIAL_IMAGE) {
g_free(self->initial_image);
self->initial_image = g_strdup(g_value_get_string(val));
koto_flipper_button_set_icon(self, self->initial_image);
} else if (prop_id == PROP_FLIPPED_IMAGE) {
g_free(self->flipped_image);
self->flipped_image = g_strdup(g_value_get_string(val));
} else if (prop_id == PROP_SIZE) {
self->size = (GtkIconSize) g_value_get_enum(val);
if (self->initial_image != NULL && !self->flipped) {
koto_flipper_button_set_icon(self, self->flipped ? self->initial_image : self->flipped_image);
}
} else {
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
return;
}
g_object_notify_by_pspec(obj, props[prop_id]);
}
static void koto_flipper_button_init(KotoFlipperButton *self) {
self->flipped = FALSE;
GtkStyleContext *button_style = gtk_widget_get_style_context(GTK_WIDGET(self)); // Get the styling for the flipper button
gtk_style_context_add_class(button_style, "flat"); // Set it to flat
}
void koto_flipper_button_flip(KotoFlipperButton *self) {
koto_flipper_button_set_icon(self, self->flipped ? self->initial_image : self->flipped_image);
self->flipped = !self->flipped;
}
void koto_flipper_button_set_icon(KotoFlipperButton *self, gchar *name) {
GtkWidget *new_image = gtk_image_new_from_icon_name(name, self->size);
gtk_button_set_image(GTK_BUTTON(self), new_image);
}
KotoFlipperButton* koto_flipper_button_new(gchar *initial_icon, gchar *flipped_icon, GtkIconSize size) {
return g_object_new(KOTO_TYPE_FLIPPER_BUTTON,
"initial-image", initial_icon,
"flipped-image", flipped_icon,
"size", size,
NULL
);
}

View file

@ -20,7 +20,6 @@
#include "koto-button.h" #include "koto-button.h"
#include "koto-expander.h" #include "koto-expander.h"
#include "koto-nav.h" #include "koto-nav.h"
#include "koto-utils.h"
struct _KotoNav { struct _KotoNav {
GtkScrolledWindow parent_instance; GtkScrolledWindow parent_instance;
@ -68,7 +67,7 @@ static void koto_nav_init(KotoNav *self) {
gtk_container_add(GTK_CONTAINER(self), self->content); gtk_container_add(GTK_CONTAINER(self), self->content);
KotoButton *h_button = koto_button_new_with_icon("Home", "user-home-symbolic", KOTO_BUTTON_PIXBUF_SIZE_SMALL); KotoButton *h_button = koto_button_new_with_icon("Home", "user-home-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
if (h_button != NULL) { if (h_button != NULL) {
self->home_button = h_button; self->home_button = h_button;
@ -80,8 +79,7 @@ static void koto_nav_init(KotoNav *self) {
koto_nav_create_podcasts_section(self); koto_nav_create_podcasts_section(self);
KotoButton *playlist_add_button = koto_button_new_with_icon("", "list-add-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_SMALL);
GtkWidget *playlist_add_button = koto_create_flat_icon_button("list-add-symbolic", GTK_ICON_SIZE_SMALL_TOOLBAR);
KotoExpander *pl_expander = koto_expander_new_with_button("playlist-symbolic", "Playlists", playlist_add_button); KotoExpander *pl_expander = koto_expander_new_with_button("playlist-symbolic", "Playlists", playlist_add_button);
if (pl_expander != NULL) { if (pl_expander != NULL) {

View file

@ -15,10 +15,9 @@
* limitations under the License. * limitations under the License.
*/ */
#include <glib/gi18n.h> #include "koto-button.h"
#include "koto-config.h" #include "koto-config.h"
#include "koto-playerbar.h" #include "koto-playerbar.h"
#include "koto-utils.h"
struct _KotoPlayerBar { struct _KotoPlayerBar {
GtkBox parent_instance; GtkBox parent_instance;
@ -29,13 +28,13 @@ struct _KotoPlayerBar {
GtkWidget *secondary_controls_section; GtkWidget *secondary_controls_section;
/* Primary Buttons */ /* Primary Buttons */
GtkWidget *back_button; KotoButton *back_button;
GtkWidget *play_pause_button; KotoButton *play_pause_button;
GtkWidget *forward_button; KotoButton *forward_button;
GtkWidget *repeat_button; KotoButton *repeat_button;
GtkWidget *shuffle_button; KotoButton *shuffle_button;
GtkWidget *playlist_button; KotoButton *playlist_button;
GtkWidget *eq_button; KotoButton *eq_button;
GtkWidget *volume_button; GtkWidget *volume_button;
/* Selected Playback Section */ /* Selected Playback Section */
@ -113,9 +112,9 @@ void koto_playerbar_create_playback_details(KotoPlayerBar* bar) {
gtk_box_pack_start(GTK_BOX(bar->playback_section), bar->artwork, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->playback_section), bar->artwork, FALSE, FALSE, 0);
} }
bar->playback_title = gtk_label_new(_("Title")); bar->playback_title = gtk_label_new("Title");
bar->playback_album = gtk_label_new(_("Album")); bar->playback_album = gtk_label_new("Album");
bar->playback_artist = gtk_label_new(_("Artist")); bar->playback_artist = gtk_label_new("Artist");
gtk_box_pack_start(GTK_BOX(bar->playback_details_section), GTK_WIDGET(bar->playback_title), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(bar->playback_details_section), GTK_WIDGET(bar->playback_title), TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(bar->playback_details_section), GTK_WIDGET(bar->playback_album), TRUE, TRUE, 0); gtk_box_pack_start(GTK_BOX(bar->playback_details_section), GTK_WIDGET(bar->playback_album), TRUE, TRUE, 0);
@ -125,45 +124,45 @@ void koto_playerbar_create_playback_details(KotoPlayerBar* bar) {
} }
void koto_playerbar_create_primary_controls(KotoPlayerBar* bar) { void koto_playerbar_create_primary_controls(KotoPlayerBar* bar) {
bar->back_button = koto_create_flat_icon_button("media-skip-backward-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); bar->back_button = koto_button_new_with_icon("", "media-skip-backward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
bar->play_pause_button = koto_create_flat_icon_button("media-playback-start-symbolic", GTK_ICON_SIZE_DND); // TODO: Have this take in a state and switch to a different icon if necessary 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_create_flat_icon_button("media-skip-forward-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); bar->forward_button = koto_button_new_with_icon("", "media-skip-forward-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
if (bar->back_button != NULL) { if (bar->back_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), bar->back_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), GTK_WIDGET(bar->back_button), FALSE, FALSE, 0);
} }
if (bar->play_pause_button != NULL) { if (bar->play_pause_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), bar->play_pause_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), GTK_WIDGET(bar->play_pause_button), FALSE, FALSE, 0);
} }
if (bar->forward_button != NULL) { if (bar->forward_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), bar->forward_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->primary_controls_section), GTK_WIDGET(bar->forward_button), FALSE, FALSE, 0);
} }
} }
void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar) { void koto_playerbar_create_secondary_controls(KotoPlayerBar* bar) {
bar->repeat_button = koto_create_flat_icon_button("media-playlist-repeat-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); bar->repeat_button = koto_button_new_with_icon("", "media-playlist-repeat-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
bar->shuffle_button = koto_create_flat_icon_button("media-playlist-shuffle-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); bar->shuffle_button = koto_button_new_with_icon("", "media-playlist-shuffle-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
bar->playlist_button = koto_create_flat_icon_button("playlist-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); bar->playlist_button = koto_button_new_with_icon("", "playlist-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
bar->eq_button = koto_create_flat_icon_button("multimedia-equalizer-symbolic", GTK_ICON_SIZE_LARGE_TOOLBAR); 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 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); gtk_scale_button_set_value(GTK_SCALE_BUTTON(bar->volume_button), 0.5);
if (bar->repeat_button != NULL) { if (bar->repeat_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), bar->repeat_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), GTK_WIDGET(bar->repeat_button), FALSE, FALSE, 0);
} }
if (bar->shuffle_button != NULL) { if (bar->shuffle_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), bar->shuffle_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), GTK_WIDGET(bar->shuffle_button), FALSE, FALSE, 0);
} }
if (bar->playlist_button != NULL) { if (bar->playlist_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), bar->playlist_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), GTK_WIDGET(bar->playlist_button), FALSE, FALSE, 0);
} }
if (bar->eq_button != NULL) { if (bar->eq_button != NULL) {
gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), bar->eq_button, FALSE, FALSE, 0); gtk_box_pack_start(GTK_BOX(bar->secondary_controls_section), GTK_WIDGET(bar->eq_button), FALSE, FALSE, 0);
} }
if (bar->volume_button != NULL) { if (bar->volume_button != NULL) {

View file

@ -1,28 +0,0 @@
/* koto-utils.c
*
* Copyright 2021 Joshua Strobl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "koto-utils.h"
GtkWidget* koto_create_flat_icon_button(gchar *icon_name, GtkIconSize size) {
GtkWidget* button;
button = gtk_button_new_from_icon_name(icon_name, size);
GtkStyleContext *button_style = gtk_widget_get_style_context(button);
gtk_style_context_add_class(button_style, "flat");
return button;
}

View file

@ -1,24 +0,0 @@
/* koto-utils.h
*
* Copyright 2021 Joshua Strobl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtk-3.0/gtk/gtk.h>
G_BEGIN_DECLS
GtkWidget* koto_create_flat_icon_button(gchar *icon_name, GtkIconSize size);
G_END_DECLS

View file

@ -37,12 +37,9 @@ G_DEFINE_TYPE (KotoWindow, koto_window, GTK_TYPE_APPLICATION_WINDOW)
static void koto_window_class_init (KotoWindowClass *klass) { static void koto_window_class_init (KotoWindowClass *klass) {
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
gtk_widget_class_set_template_from_resource (widget_class, "/com/github/joshstrobl/koto/koto-window.ui");
} }
static void koto_window_init (KotoWindow *self) { static void koto_window_init (KotoWindow *self) {
gtk_widget_init_template (GTK_WIDGET (self));
GtkCssProvider* provider = gtk_css_provider_new(); GtkCssProvider* provider = gtk_css_provider_new();
gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css"); gtk_css_provider_load_from_resource(provider, "/com/github/joshstrobl/koto/style.css");
gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); gtk_style_context_add_provider_for_screen(gdk_screen_get_default(), GTK_STYLE_PROVIDER(provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
@ -75,8 +72,12 @@ static void koto_window_init (KotoWindow *self) {
} }
gtk_container_add(GTK_CONTAINER(self), self->primary_layout); gtk_container_add(GTK_CONTAINER(self), self->primary_layout);
gtk_widget_show_all(GTK_WIDGET(self)); gtk_widget_set_size_request(GTK_WIDGET(self), 1200, 675);
gtk_window_set_title(GTK_WINDOW(self), "Koto");
gtk_window_set_wmclass(GTK_WINDOW(self), "com.github.joshstrobl.koto", "com.github.joshstrobl.koto");
gtk_window_set_icon_name(GTK_WINDOW(self), "audio-headphones");
gtk_widget_show_all(GTK_WIDGET(self));
g_thread_new("load-library", load_library, self); g_thread_new("load-library", load_library, self);
} }

View file

@ -1,10 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.38.2 -->
<interface>
<requires lib="gtk+" version="3.24"/>
<template class="KotoWindow" parent="GtkApplicationWindow">
<property name="can-focus">False</property>
<property name="default-width">1200</property>
<property name="default-height">675</property>
</template>
</interface>

View file

@ -2,7 +2,6 @@
<gresources> <gresources>
<gresource prefix="/com/github/joshstrobl/koto"> <gresource prefix="/com/github/joshstrobl/koto">
<file>koto-headerbar.ui</file> <file>koto-headerbar.ui</file>
<file>koto-window.ui</file>
<file alias="style.css">../data/style.css</file> <file alias="style.css">../data/style.css</file>
</gresource> </gresource>
</gresources> </gresources>

View file

@ -1,13 +1,12 @@
koto_sources = [ koto_sources = [
'indexer/file.c',
'indexer/file-indexer.c', 'indexer/file-indexer.c',
'main.c', 'main.c',
'koto-button.c', 'koto-button.c',
'koto-expander.c', 'koto-expander.c',
'koto-flipper-button.c',
'koto-headerbar.c', 'koto-headerbar.c',
'koto-nav.c', 'koto-nav.c',
'koto-playerbar.c', 'koto-playerbar.c',
'koto-utils.c',
'koto-window.c', 'koto-window.c',
] ]