/* cover-art-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 #include #include "../koto-utils.h" #include "button.h" #include "cover-art-button.h" struct _KotoCoverArtButton { GObject parent_instance; GtkWidget * art; GtkWidget * main; GtkWidget * revealer; KotoButton * play_button; gchar * art_path; guint height; guint width; }; G_DEFINE_TYPE(KotoCoverArtButton, koto_cover_art_button, G_TYPE_OBJECT); enum { PROP_0, PROP_DESIRED_HEIGHT, PROP_DESIRED_WIDTH, PROP_ART_PATH, N_PROPERTIES }; static GParamSpec * props[N_PROPERTIES] = { NULL, }; static void koto_cover_art_button_get_property( GObject * obj, guint prop_id, GValue * val, GParamSpec * spec ); static void koto_cover_art_button_set_property( GObject * obj, guint prop_id, const GValue * val, GParamSpec * spec ); static void koto_cover_art_button_class_init(KotoCoverArtButtonClass * c) { GObjectClass * gobject_class; gobject_class = G_OBJECT_CLASS(c); gobject_class->get_property = koto_cover_art_button_get_property; gobject_class->set_property = koto_cover_art_button_set_property; props[PROP_DESIRED_HEIGHT] = g_param_spec_uint( "desired-height", "Desired height", "Desired height", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE ); props[PROP_DESIRED_WIDTH] = g_param_spec_uint( "desired-width", "Desired width", "Desired width", 0, G_MAXUINT, 0, G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE ); props[PROP_ART_PATH] = g_param_spec_string( "art-path", "Path to art", "Path to art", NULL, G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_WRITABLE ); g_object_class_install_properties(gobject_class, N_PROPERTIES, props); } static void koto_cover_art_button_init(KotoCoverArtButton * self) { self->main = gtk_overlay_new(); // Create our overlay container gtk_widget_add_css_class(self->main, "cover-art-button"); self->revealer = gtk_revealer_new(); // Create a new revealer gtk_revealer_set_transition_type(GTK_REVEALER(self->revealer), GTK_REVEALER_TRANSITION_TYPE_CROSSFADE); gtk_revealer_set_transition_duration(GTK_REVEALER(self->revealer), 400); GtkWidget * controls = gtk_center_box_new(); // Create a center box for the controls self->play_button = koto_button_new_with_icon("", "media-playback-start-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL); gtk_center_box_set_center_widget(GTK_CENTER_BOX(controls), GTK_WIDGET(self->play_button)); gtk_revealer_set_child(GTK_REVEALER(self->revealer), controls); koto_cover_art_button_hide_overlay_controls(NULL, self); // Hide by default gtk_overlay_add_overlay(GTK_OVERLAY(self->main), self->revealer); // Add our revealer as the overlay GtkEventController * motion_controller = gtk_event_controller_motion_new(); // Create our new motion event controller to track mouse leave and enter g_signal_connect(motion_controller, "enter", G_CALLBACK(koto_cover_art_button_show_overlay_controls), self); g_signal_connect(motion_controller, "leave", G_CALLBACK(koto_cover_art_button_hide_overlay_controls), self); gtk_widget_add_controller(self->main, motion_controller); self->art_path = NULL; } static void koto_cover_art_button_get_property( GObject * obj, guint prop_id, GValue * val, GParamSpec * spec ) { (void) val; switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); break; } } static void koto_cover_art_button_set_property( GObject * obj, guint prop_id, const GValue * val, GParamSpec * spec ) { KotoCoverArtButton * self = KOTO_COVER_ART_BUTTON(obj); switch (prop_id) { case PROP_ART_PATH: koto_cover_art_button_set_art_path(self, (gchar*) g_value_get_string(val)); // Get the value and call our set_art_path with it break; case PROP_DESIRED_HEIGHT: koto_cover_art_button_set_dimensions(self, g_value_get_uint(val), 0); break; case PROP_DESIRED_WIDTH: koto_cover_art_button_set_dimensions(self, 0, g_value_get_uint(val)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec); break; } } void koto_cover_art_button_hide_overlay_controls( GtkEventControllerFocus * controller, gpointer data ) { (void) controller; KotoCoverArtButton * self = data; gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), FALSE); } KotoButton * koto_cover_art_button_get_button(KotoCoverArtButton * self) { if (!KOTO_IS_COVER_ART_BUTTON(self)) { return NULL; } return self->play_button; } GtkWidget * koto_cover_art_button_get_main(KotoCoverArtButton * self) { if (!KOTO_IS_COVER_ART_BUTTON(self)) { return NULL; } return self->main; } void koto_cover_art_button_set_art_path( KotoCoverArtButton * self, gchar * art_path ) { if (!KOTO_IS_COVER_ART_BUTTON(self)) { return; } gboolean defined_artwork = koto_utils_string_is_valid(art_path); if (GTK_IS_IMAGE(self->art)) { // Already have an image if (!defined_artwork) { // No art path or empty string gtk_image_set_from_icon_name(GTK_IMAGE(self->art), "audio-x-generic-symbolic"); } else { // Have an art path if (g_strcmp0(self->art_path, art_path) != 0) { self->art_path = g_strdup(art_path); // Set our art path gtk_image_set_from_file(GTK_IMAGE(self->art), self->art_path); // Set from the file } } } else { // If we don't have an image self->art = koto_utils_create_image_from_filepath(defined_artwork ? g_strdup(art_path) : NULL, "audio-x-generic-symbolic", self->width, self->height); gtk_overlay_set_child(GTK_OVERLAY(self->main), self->art); // Set the child } } void koto_cover_art_button_set_dimensions( KotoCoverArtButton * self, guint height, guint width ) { if (!KOTO_IS_COVER_ART_BUTTON(self)) { return; } if (height != 0) { self->height = height; } if (width != 0) { self->width = width; } if ((self->height != 0) && (self->width != 0)) { // Both height and width set gtk_widget_set_size_request(self->main, self->width, self->height); // Update our widget if (GTK_IS_IMAGE(self->art)) { // Art is defined gtk_widget_set_size_request(self->art, self->width, self->height); // Update our image as well } } } void koto_cover_art_button_show_overlay_controls( GtkEventControllerFocus * controller, gpointer data ) { (void) controller; KotoCoverArtButton * self = data; gtk_revealer_set_reveal_child(GTK_REVEALER(self->revealer), TRUE); } KotoCoverArtButton * koto_cover_art_button_new( guint height, guint width, gchar * art_path ) { return g_object_new( KOTO_TYPE_COVER_ART_BUTTON, "desired-height", height, "desired-width", width, "art-path", art_path, NULL ); }