Implement Playlist functionality. My god...
Too many changes to summarize. - Fixes #2. - Fixes #3. - Fixes #5. - Fixes #7. Start work on uncrustify config.
This commit is contained in:
parent
ddf1987b50
commit
0e2244ba90
62 changed files with 6280 additions and 374 deletions
355
src/components/koto-action-bar.c
Normal file
355
src/components/koto-action-bar.c
Normal file
|
@ -0,0 +1,355 @@
|
|||
/* koto-action-bar.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 <gtk-4.0/gtk/gtk.h>
|
||||
#include "koto-action-bar.h"
|
||||
#include "../db/cartographer.h"
|
||||
#include "../pages/music/music-local.h"
|
||||
#include "../playlist/add-remove-track-popover.h"
|
||||
#include "../playback/engine.h"
|
||||
#include "../koto-button.h"
|
||||
#include "../koto-window.h"
|
||||
|
||||
extern KotoAddRemoveTrackPopover *koto_add_remove_track_popup;
|
||||
extern KotoCartographer *koto_maps;
|
||||
extern KotoPageMusicLocal *music_local_page;
|
||||
extern KotoPlaybackEngine *playback_engine;
|
||||
extern KotoWindow *main_window;
|
||||
|
||||
enum {
|
||||
SIGNAL_CLOSED,
|
||||
N_SIGNALS
|
||||
};
|
||||
|
||||
static guint actionbar_signals[N_SIGNALS] = { 0 };
|
||||
|
||||
struct _KotoActionBar {
|
||||
GObject parent_instance;
|
||||
|
||||
GtkActionBar *main;
|
||||
|
||||
GtkWidget *center_box_content;
|
||||
GtkWidget *start_box_content;
|
||||
GtkWidget *stop_box_content;
|
||||
|
||||
KotoButton *close_button;
|
||||
GtkWidget *go_to_artist;
|
||||
GtkWidget *playlists;
|
||||
GtkWidget *play_track;
|
||||
GtkWidget *remove_from_playlist;
|
||||
|
||||
GList *current_list;
|
||||
gchar *current_album_uuid;
|
||||
gchar *current_playlist_uuid;
|
||||
KotoActionBarRelative relative;
|
||||
};
|
||||
|
||||
struct _KotoActionBarClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (* closed) (KotoActionBar *self);
|
||||
};
|
||||
|
||||
G_DEFINE_TYPE(KotoActionBar, koto_action_bar, G_TYPE_OBJECT);
|
||||
|
||||
KotoActionBar* action_bar;
|
||||
|
||||
static void koto_action_bar_class_init(KotoActionBarClass *c) {
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS(c);
|
||||
|
||||
actionbar_signals[SIGNAL_CLOSED] = g_signal_new(
|
||||
"closed",
|
||||
G_TYPE_FROM_CLASS(gobject_class),
|
||||
G_SIGNAL_RUN_FIRST | G_SIGNAL_ACTION,
|
||||
G_STRUCT_OFFSET(KotoActionBarClass, closed),
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
G_TYPE_NONE,
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
static void koto_action_bar_init(KotoActionBar *self) {
|
||||
self->main = GTK_ACTION_BAR(gtk_action_bar_new()); // Create a new action bar
|
||||
self->current_list = NULL;
|
||||
|
||||
self->close_button = koto_button_new_with_icon(NULL, "window-close-symbolic", NULL, KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
|
||||
self->go_to_artist = gtk_button_new_with_label("Go to Artist");
|
||||
self->playlists = gtk_button_new_with_label("Playlists");
|
||||
self->play_track = gtk_button_new_with_label("Play");
|
||||
self->remove_from_playlist = gtk_button_new_with_label("Remove from Playlist");
|
||||
|
||||
gtk_widget_add_css_class(self->playlists, "suggested-action");
|
||||
gtk_widget_add_css_class(self->play_track, "suggested-action");
|
||||
gtk_widget_add_css_class(self->remove_from_playlist, "destructive-action");
|
||||
|
||||
gtk_widget_set_size_request(self->play_track, 160, -1);
|
||||
|
||||
self->center_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
self->start_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
self->stop_box_content = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 20);
|
||||
|
||||
gtk_box_prepend(GTK_BOX(self->start_box_content), GTK_WIDGET(self->go_to_artist));
|
||||
|
||||
gtk_box_prepend(GTK_BOX(self->center_box_content), GTK_WIDGET(self->play_track));
|
||||
|
||||
gtk_box_prepend(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->playlists));
|
||||
gtk_box_append(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->remove_from_playlist));
|
||||
gtk_box_append(GTK_BOX(self->stop_box_content), GTK_WIDGET(self->close_button));
|
||||
|
||||
gtk_action_bar_pack_start(self->main, self->start_box_content);
|
||||
gtk_action_bar_pack_end(self->main, self->stop_box_content);
|
||||
gtk_action_bar_set_center_widget(self->main, self->center_box_content);
|
||||
|
||||
// Hide all the widgets by default
|
||||
gtk_widget_hide(GTK_WIDGET(self->go_to_artist));
|
||||
gtk_widget_hide(GTK_WIDGET(self->playlists));
|
||||
gtk_widget_hide(GTK_WIDGET(self->play_track));
|
||||
gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist));
|
||||
|
||||
// Set up bindings
|
||||
|
||||
koto_button_add_click_handler(self->close_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_action_bar_handle_close_button_clicked), self);
|
||||
g_signal_connect(self->go_to_artist, "clicked", G_CALLBACK(koto_action_bar_handle_go_to_artist_button_clicked), self);
|
||||
g_signal_connect(self->playlists, "clicked", G_CALLBACK(koto_action_bar_handle_playlists_button_clicked), self);
|
||||
g_signal_connect(self->play_track, "clicked", G_CALLBACK(koto_action_bar_handle_play_track_button_clicked), self);
|
||||
g_signal_connect(self->remove_from_playlist, "clicked", G_CALLBACK(koto_action_bar_handle_remove_from_playlist_button_clicked), self);
|
||||
|
||||
koto_action_bar_toggle_reveal(self, FALSE); // Hide by default
|
||||
}
|
||||
|
||||
void koto_action_bar_close(KotoActionBar *self) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_widget_hide(GTK_WIDGET(koto_add_remove_track_popup)); // Hide the Add / Remove Track Playlists popover
|
||||
koto_action_bar_toggle_reveal(self, FALSE); // Hide the action bar
|
||||
|
||||
g_signal_emit(
|
||||
self,
|
||||
actionbar_signals[SIGNAL_CLOSED],
|
||||
0,
|
||||
NULL
|
||||
);
|
||||
}
|
||||
|
||||
GtkActionBar* koto_action_bar_get_main(KotoActionBar *self) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return self->main;
|
||||
}
|
||||
|
||||
void koto_action_bar_handle_close_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data) {
|
||||
(void) gesture; (void) n_press; (void) x; (void) y;
|
||||
koto_action_bar_close(data);
|
||||
}
|
||||
|
||||
void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpointer data) {
|
||||
(void) button;
|
||||
KotoActionBar *self = data;
|
||||
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_list == NULL || (g_list_length(self->current_list) != 1)) { // Not a list or not exactly one item
|
||||
return;
|
||||
}
|
||||
|
||||
KotoIndexedTrack *selected_track = g_list_nth_data(self->current_list, 0); // Get the first item
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(selected_track)) { // Not a track
|
||||
return;
|
||||
}
|
||||
|
||||
gchar *artist_uuid = NULL;
|
||||
g_object_get(
|
||||
selected_track,
|
||||
"artist-uuid",
|
||||
&artist_uuid,
|
||||
NULL
|
||||
);
|
||||
|
||||
koto_page_music_local_go_to_artist_by_uuid(music_local_page, artist_uuid); // Go to the artist
|
||||
koto_window_go_to_page(main_window, "music.local"); // Navigate to the local music stack so we can see the substack page
|
||||
koto_action_bar_close(self); // Close the action bar
|
||||
}
|
||||
void koto_action_bar_handle_playlists_button_clicked(GtkButton *button, gpointer data) {
|
||||
(void) button;
|
||||
KotoActionBar *self = data;
|
||||
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_list == NULL || (g_list_length(self->current_list) == 0)) { // Not a list or no content
|
||||
return;
|
||||
}
|
||||
|
||||
koto_add_remove_track_popover_set_tracks(koto_add_remove_track_popup, self->current_list); // Set the popover tracks
|
||||
koto_add_remove_track_popover_set_pointing_to_widget(koto_add_remove_track_popup, GTK_WIDGET(self->playlists), GTK_POS_TOP); // Show the popover above the button
|
||||
gtk_widget_show(GTK_WIDGET(koto_add_remove_track_popup));
|
||||
}
|
||||
|
||||
void koto_action_bar_handle_play_track_button_clicked(GtkButton *button, gpointer data) {
|
||||
(void) button;
|
||||
KotoActionBar *self = data;
|
||||
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_list == NULL || (g_list_length(self->current_list) != 1)) { // Not a list or not exactly 1 item selected
|
||||
goto doclose;
|
||||
}
|
||||
|
||||
KotoIndexedTrack *track = g_list_nth_data(self->current_list, 0); // Get the first track
|
||||
|
||||
if (!KOTO_IS_INDEXED_TRACK(track)) { // Not a track
|
||||
goto doclose;
|
||||
}
|
||||
|
||||
koto_playback_engine_set_track_by_uuid(playback_engine, koto_indexed_track_get_uuid(track)); // Set the track to play
|
||||
|
||||
doclose:
|
||||
koto_action_bar_close(self);
|
||||
}
|
||||
|
||||
void koto_action_bar_handle_remove_from_playlist_button_clicked(GtkButton *button, gpointer data) {
|
||||
(void) button;
|
||||
KotoActionBar *self = data;
|
||||
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_list == NULL || (g_list_length(self->current_list) == 0)) { // Not a list or no content
|
||||
goto doclose;
|
||||
}
|
||||
|
||||
if (self->current_playlist_uuid == NULL || (g_strcmp0(self->current_playlist_uuid, "") == 0)) { // Not valid UUID
|
||||
goto doclose;
|
||||
}
|
||||
|
||||
KotoPlaylist *playlist = koto_cartographer_get_playlist_by_uuid(koto_maps, self->current_playlist_uuid);
|
||||
|
||||
if (!KOTO_IS_PLAYLIST(playlist)) { // Not a playlist
|
||||
goto doclose;
|
||||
}
|
||||
|
||||
GList *cur_list;
|
||||
for (cur_list = self->current_list; cur_list != NULL; cur_list = cur_list->next) { // For each KotoIndexedTrack
|
||||
KotoIndexedTrack *track = cur_list->data;
|
||||
koto_playlist_remove_track_by_uuid(playlist, koto_indexed_track_get_uuid(track)); // Remove this track
|
||||
}
|
||||
|
||||
doclose:
|
||||
koto_action_bar_close(self);
|
||||
}
|
||||
|
||||
void koto_action_bar_set_tracks_in_album_selection(KotoActionBar *self, gchar *album_uuid, GList *tracks) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_album_uuid != NULL && (g_strcmp0(self->current_album_uuid, "") != 0)) { // Album UUID currently set
|
||||
g_free(self->current_album_uuid);
|
||||
}
|
||||
|
||||
if (self->current_playlist_uuid != NULL && (g_strcmp0(self->current_playlist_uuid, "") != 0)) { // Playlist UUID currently set
|
||||
g_free(self->current_playlist_uuid);
|
||||
}
|
||||
|
||||
self->current_playlist_uuid = NULL;
|
||||
self->current_album_uuid = g_strdup(album_uuid);
|
||||
self->relative = KOTO_ACTION_BAR_IS_ALBUM_RELATIVE;
|
||||
|
||||
g_list_free(self->current_list);
|
||||
self->current_list = g_list_copy(tracks);
|
||||
|
||||
koto_add_remove_track_popover_clear_tracks(koto_add_remove_track_popup); // Clear the current popover contents
|
||||
koto_add_remove_track_popover_set_tracks(koto_add_remove_track_popup, self->current_list); // Set the associated tracks to remove
|
||||
|
||||
koto_action_bar_toggle_go_to_artist_visibility(self, FALSE);
|
||||
koto_action_bar_toggle_play_button_visibility(self, g_list_length(self->current_list) == 1);
|
||||
|
||||
gtk_widget_show(GTK_WIDGET(self->playlists));
|
||||
gtk_widget_hide(GTK_WIDGET(self->remove_from_playlist));
|
||||
}
|
||||
|
||||
void koto_action_bar_set_tracks_in_playlist_selection(KotoActionBar *self, gchar *playlist_uuid, GList *tracks) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (self->current_album_uuid != NULL && (g_strcmp0(self->current_album_uuid, "") != 0)) { // Album UUID currently set
|
||||
g_free(self->current_album_uuid);
|
||||
}
|
||||
|
||||
if (self->current_playlist_uuid != NULL && (g_strcmp0(self->current_playlist_uuid, "") != 0)) { // Playlist UUID currently set
|
||||
g_free(self->current_playlist_uuid);
|
||||
}
|
||||
|
||||
self->current_album_uuid = NULL;
|
||||
self->current_playlist_uuid = g_strdup(playlist_uuid);
|
||||
self->relative = KOTO_ACTION_BAR_IS_PLAYLIST_RELATIVE;
|
||||
|
||||
g_list_free(self->current_list);
|
||||
self->current_list = g_list_copy(tracks);
|
||||
|
||||
gboolean single_selected = g_list_length(tracks) == 1;
|
||||
koto_action_bar_toggle_go_to_artist_visibility(self, single_selected);
|
||||
koto_action_bar_toggle_play_button_visibility(self, single_selected);
|
||||
gtk_widget_hide(GTK_WIDGET(self->playlists));
|
||||
gtk_widget_show(GTK_WIDGET(self->remove_from_playlist));
|
||||
}
|
||||
|
||||
void koto_action_bar_toggle_go_to_artist_visibility(KotoActionBar *self, gboolean visible) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(visible) ? gtk_widget_show(GTK_WIDGET(self->go_to_artist)) : gtk_widget_hide(GTK_WIDGET(self->go_to_artist));
|
||||
}
|
||||
|
||||
void koto_action_bar_toggle_play_button_visibility(KotoActionBar *self, gboolean visible) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
(visible) ? gtk_widget_show(GTK_WIDGET(self->play_track)) : gtk_widget_hide(GTK_WIDGET(self->play_track));
|
||||
}
|
||||
|
||||
void koto_action_bar_toggle_reveal(KotoActionBar *self, gboolean state) {
|
||||
if (!KOTO_IS_ACTION_BAR(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_action_bar_set_revealed(self->main, state);
|
||||
}
|
||||
|
||||
KotoActionBar* koto_action_bar_new() {
|
||||
return g_object_new(
|
||||
KOTO_TYPE_ACTION_BAR,
|
||||
NULL
|
||||
);
|
||||
}
|
58
src/components/koto-action-bar.h
Normal file
58
src/components/koto-action-bar.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/* koto-action-bar.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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <glib-2.0/glib.h>
|
||||
#include <gtk-4.0/gtk/gtk.h>
|
||||
#include "../indexer/structs.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
typedef enum {
|
||||
KOTO_ACTION_BAR_IS_ALBUM_RELATIVE = 1,
|
||||
KOTO_ACTION_BAR_IS_PLAYLIST_RELATIVE = 2
|
||||
} KotoActionBarRelative;
|
||||
|
||||
#define KOTO_TYPE_ACTION_BAR (koto_action_bar_get_type())
|
||||
#define KOTO_IS_ACTION_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_ACTION_BAR))
|
||||
|
||||
typedef struct _KotoActionBar KotoActionBar;
|
||||
typedef struct _KotoActionBarClass KotoActionBarClass;
|
||||
|
||||
GLIB_AVAILABLE_IN_ALL
|
||||
GType koto_action_bar_get_type(void) G_GNUC_CONST;
|
||||
|
||||
/**
|
||||
* Action Bar Functions
|
||||
**/
|
||||
|
||||
KotoActionBar* koto_action_bar_new(void);
|
||||
void koto_action_bar_close(KotoActionBar *self);
|
||||
GtkActionBar* koto_action_bar_get_main(KotoActionBar *self);
|
||||
void koto_action_bar_handle_close_button_clicked(GtkGestureClick *gesture, int n_press, double x, double y, gpointer data);
|
||||
void koto_action_bar_handle_go_to_artist_button_clicked(GtkButton *button, gpointer data);
|
||||
void koto_action_bar_handle_playlists_button_clicked(GtkButton *button, gpointer data);
|
||||
void koto_action_bar_handle_play_track_button_clicked(GtkButton *button, gpointer data);
|
||||
void koto_action_bar_handle_remove_from_playlist_button_clicked(GtkButton *button, gpointer data);
|
||||
void koto_action_bar_set_tracks_in_album_selection(KotoActionBar *self, gchar *album_uuid, GList *tracks);
|
||||
void koto_action_bar_set_tracks_in_playlist_selection(KotoActionBar *self, gchar *playlist_uuid, GList *tracks);
|
||||
void koto_action_bar_toggle_go_to_artist_visibility(KotoActionBar *self, gboolean visible);
|
||||
void koto_action_bar_toggle_play_button_visibility(KotoActionBar *self, gboolean visible);
|
||||
void koto_action_bar_toggle_reveal(KotoActionBar *self, gboolean state);
|
||||
|
||||
G_END_DECLS
|
217
src/components/koto-cover-art-button.c
Normal file
217
src/components/koto-cover-art-button.c
Normal file
|
@ -0,0 +1,217 @@
|
|||
/* koto-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 <glib-2.0/glib.h>
|
||||
#include <gtk-4.0/gtk/gtk.h>
|
||||
#include "koto-cover-art-button.h"
|
||||
#include "../koto-button.h"
|
||||
#include "../koto-utils.h"
|
||||
|
||||
struct _KotoCoverArtButton {
|
||||
GObject parent_instance;
|
||||
|
||||
GtkWidget *art;
|
||||
GtkWidget *main;
|
||||
GtkWidget *revealer;
|
||||
KotoButton *play_pause_button;
|
||||
|
||||
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_pause_button = koto_button_new_with_icon("", "media-playback-start-symbolic", "media-playback-pause-symbolic", KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
|
||||
gtk_center_box_set_center_widget(GTK_CENTER_BOX(controls), GTK_WIDGET(self->play_pause_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);
|
||||
}
|
||||
|
||||
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, 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_pause_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, const gchar *art_path) {
|
||||
if (!KOTO_IS_COVER_ART_BUTTON(self)) {
|
||||
return;
|
||||
}
|
||||
|
||||
gboolean defined_artwork = (art_path != NULL || (g_strcmp0(art_path, "") != 0));
|
||||
|
||||
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
|
||||
gtk_image_set_from_file(GTK_IMAGE(self->art), g_strdup(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, const gchar *art_path) {
|
||||
return g_object_new(KOTO_TYPE_COVER_ART_BUTTON,
|
||||
"desired-height",
|
||||
height,
|
||||
"desired-width",
|
||||
width,
|
||||
"art-path",
|
||||
art_path,
|
||||
NULL
|
||||
);
|
||||
}
|
40
src/components/koto-cover-art-button.h
Normal file
40
src/components/koto-cover-art-button.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/* koto-cover-art-button.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 <glib-2.0/glib.h>
|
||||
#include <gtk-4.0/gtk/gtk.h>
|
||||
#include "../koto-button.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define KOTO_TYPE_COVER_ART_BUTTON (koto_cover_art_button_get_type())
|
||||
G_DECLARE_FINAL_TYPE(KotoCoverArtButton, koto_cover_art_button, KOTO, COVER_ART_BUTTON, GObject);
|
||||
#define KOTO_IS_COVER_ART_BUTTON(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), KOTO_TYPE_COVER_ART_BUTTON))
|
||||
|
||||
/**
|
||||
* Cover Art Functions
|
||||
**/
|
||||
|
||||
KotoCoverArtButton* koto_cover_art_button_new(guint height, guint width, const gchar *art_path);
|
||||
KotoButton* koto_cover_art_button_get_button(KotoCoverArtButton *self);
|
||||
GtkWidget* koto_cover_art_button_get_main(KotoCoverArtButton *self);
|
||||
void koto_cover_art_button_hide_overlay_controls(GtkEventControllerFocus *controller, gpointer data);
|
||||
void koto_cover_art_button_set_art_path(KotoCoverArtButton *self, const gchar *art_path);
|
||||
void koto_cover_art_button_set_dimensions(KotoCoverArtButton *self, guint height, guint width);
|
||||
void koto_cover_art_button_show_overlay_controls(GtkEventControllerFocus *controller, gpointer data);
|
||||
|
||||
G_END_DECLS
|
Loading…
Add table
Add a link
Reference in a new issue