2021-02-09 17:37:26 +02:00
/* koto-playerbar.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 .
*/
2021-03-23 19:50:09 +02:00
# include <gstreamer-1.0/gst/gst.h>
2021-02-24 20:17:18 +02:00
# include <gtk-4.0/gtk/gtk.h>
2021-03-23 19:50:09 +02:00
# include "playback/engine.h"
2021-02-12 12:56:41 +02:00
# include "koto-button.h"
2021-02-09 17:37:26 +02:00
# include "koto-config.h"
# include "koto-playerbar.h"
2021-03-23 19:50:09 +02:00
extern KotoPlaybackEngine * playback_engine ;
2021-02-09 17:37:26 +02:00
struct _KotoPlayerBar {
2021-02-24 20:17:18 +02:00
GObject parent_instance ;
GtkWidget * main ;
2021-03-23 19:50:09 +02:00
GtkWidget * controls ;
2021-02-09 17:37:26 +02:00
/* Sections */
GtkWidget * playback_section ;
GtkWidget * primary_controls_section ;
GtkWidget * secondary_controls_section ;
/* Primary Buttons */
2021-02-12 12:56:41 +02:00
KotoButton * back_button ;
KotoButton * play_pause_button ;
KotoButton * forward_button ;
KotoButton * repeat_button ;
KotoButton * shuffle_button ;
KotoButton * playlist_button ;
KotoButton * eq_button ;
2021-02-09 17:37:26 +02:00
GtkWidget * volume_button ;
2021-03-23 19:50:09 +02:00
GtkWidget * progress_bar ;
2021-02-09 17:37:26 +02:00
/* Selected Playback Section */
GtkWidget * playback_details_section ;
GtkWidget * artwork ;
GtkWidget * playback_title ;
GtkWidget * playback_album ;
GtkWidget * playback_artist ;
} ;
struct _KotoPlayerBarClass {
2021-02-24 20:17:18 +02:00
GObjectClass parent_class ;
2021-02-09 17:37:26 +02:00
} ;
2021-02-24 20:17:18 +02:00
G_DEFINE_TYPE ( KotoPlayerBar , koto_playerbar , G_TYPE_OBJECT ) ;
2021-02-09 17:37:26 +02:00
static void koto_playerbar_constructed ( GObject * obj ) ;
static void koto_playerbar_class_init ( KotoPlayerBarClass * c ) {
GObjectClass * gobject_class ;
gobject_class = G_OBJECT_CLASS ( c ) ;
gobject_class - > constructed = koto_playerbar_constructed ;
}
static void koto_playerbar_constructed ( GObject * obj ) {
KotoPlayerBar * self = KOTO_PLAYERBAR ( obj ) ;
2021-03-23 19:50:09 +02:00
self - > main = gtk_box_new ( GTK_ORIENTATION_VERTICAL , 0 ) ;
self - > progress_bar = gtk_scale_new_with_range ( GTK_ORIENTATION_HORIZONTAL , 0 , 120 , 1 ) ; // Default to 120 as random max
gtk_scale_set_draw_value ( GTK_SCALE ( self - > progress_bar ) , FALSE ) ;
gtk_scale_set_digits ( GTK_SCALE ( self - > progress_bar ) , 0 ) ;
gtk_range_set_increments ( GTK_RANGE ( self - > progress_bar ) , 1 , 1 ) ;
self - > controls = gtk_center_box_new ( ) ;
gtk_center_box_set_baseline_position ( GTK_CENTER_BOX ( self - > controls ) , GTK_BASELINE_POSITION_CENTER ) ;
2021-02-24 20:17:18 +02:00
gtk_widget_add_css_class ( self - > main , " player-bar " ) ;
2021-02-09 17:37:26 +02:00
self - > playback_section = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL , 0 ) ;
self - > primary_controls_section = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL , 0 ) ;
self - > secondary_controls_section = gtk_box_new ( GTK_ORIENTATION_HORIZONTAL , 0 ) ;
2021-03-23 19:50:09 +02:00
gtk_center_box_set_start_widget ( GTK_CENTER_BOX ( self - > controls ) , GTK_WIDGET ( self - > primary_controls_section ) ) ;
gtk_center_box_set_center_widget ( GTK_CENTER_BOX ( self - > controls ) , GTK_WIDGET ( self - > playback_section ) ) ;
gtk_center_box_set_end_widget ( GTK_CENTER_BOX ( self - > controls ) , GTK_WIDGET ( self - > secondary_controls_section ) ) ;
2021-02-09 17:37:26 +02:00
koto_playerbar_create_playback_details ( self ) ;
koto_playerbar_create_primary_controls ( self ) ;
koto_playerbar_create_secondary_controls ( self ) ;
2021-03-23 19:50:09 +02:00
gtk_box_prepend ( GTK_BOX ( self - > main ) , self - > progress_bar ) ;
gtk_box_append ( GTK_BOX ( self - > main ) , self - > controls ) ;
2021-02-24 20:17:18 +02:00
gtk_widget_set_hexpand ( GTK_WIDGET ( self - > main ) , TRUE ) ;
2021-03-23 19:50:09 +02:00
// Set up the bindings
g_signal_connect ( playback_engine , " duration-changed " , G_CALLBACK ( koto_playerbar_handle_duration_change ) , self ) ;
g_signal_connect ( playback_engine , " play-state-changed " , G_CALLBACK ( koto_playerbar_handle_engine_state_change ) , self ) ;
g_signal_connect ( playback_engine , " progress-changed " , G_CALLBACK ( koto_playerbar_handle_progress_change ) , self ) ;
2021-02-09 17:37:26 +02:00
}
static void koto_playerbar_init ( KotoPlayerBar * self ) {
2021-02-24 20:17:18 +02:00
( void ) self ;
2021-02-09 17:37:26 +02:00
}
KotoPlayerBar * koto_playerbar_new ( void ) {
2021-02-24 20:17:18 +02:00
return g_object_new ( KOTO_TYPE_PLAYERBAR , NULL ) ;
2021-02-09 17:37:26 +02:00
}
void koto_playerbar_create_playback_details ( KotoPlayerBar * bar ) {
bar - > playback_details_section = gtk_box_new ( GTK_ORIENTATION_VERTICAL , 0 ) ;
2021-02-24 20:17:18 +02:00
GtkIconTheme * default_icon_theme = gtk_icon_theme_get_for_display ( gdk_display_get_default ( ) ) ; // Get the icon theme for this display
2021-02-09 17:37:26 +02:00
if ( default_icon_theme ! = NULL ) {
2021-02-27 17:26:24 +02:00
gint scale_factor = gtk_widget_get_scale_factor ( GTK_WIDGET ( bar - > main ) ) ;
2021-02-24 20:17:18 +02:00
GtkIconPaintable * audio_paintable = gtk_icon_theme_lookup_icon ( default_icon_theme , " audio-x-generic-symbolic " , NULL , 96 , scale_factor , GTK_TEXT_DIR_NONE , GTK_ICON_LOOKUP_PRELOAD ) ;
if ( GTK_IS_ICON_PAINTABLE ( audio_paintable ) ) {
if ( GTK_IS_IMAGE ( bar - > artwork ) ) {
gtk_image_set_from_paintable ( GTK_IMAGE ( bar - > artwork ) , GDK_PAINTABLE ( audio_paintable ) ) ;
} else { // Not an image
bar - > artwork = gtk_image_new_from_paintable ( GDK_PAINTABLE ( audio_paintable ) ) ;
gtk_widget_set_size_request ( bar - > artwork , 96 , 96 ) ;
gtk_box_append ( GTK_BOX ( bar - > playback_section ) , bar - > artwork ) ;
}
2021-02-09 17:37:26 +02:00
}
}
2021-02-12 12:56:41 +02:00
bar - > playback_title = gtk_label_new ( " Title " ) ;
bar - > playback_album = gtk_label_new ( " Album " ) ;
bar - > playback_artist = gtk_label_new ( " Artist " ) ;
2021-02-09 17:37:26 +02:00
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > playback_details_section ) , GTK_WIDGET ( bar - > playback_title ) ) ;
gtk_box_append ( GTK_BOX ( bar - > playback_details_section ) , GTK_WIDGET ( bar - > playback_album ) ) ;
gtk_box_append ( GTK_BOX ( bar - > playback_details_section ) , GTK_WIDGET ( bar - > playback_artist ) ) ;
2021-02-09 17:37:26 +02:00
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > playback_section ) , GTK_WIDGET ( bar - > playback_details_section ) ) ;
2021-02-09 17:37:26 +02:00
}
void koto_playerbar_create_primary_controls ( KotoPlayerBar * bar ) {
2021-02-25 18:15:36 +02:00
bar - > back_button = koto_button_new_with_icon ( " " , " media-skip-backward-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
2021-03-10 13:44:08 +02:00
bar - > play_pause_button = koto_button_new_with_icon ( " " , " media-playback-start-symbolic " , " media-playback-pause-symbolic " , KOTO_BUTTON_PIXBUF_SIZE_LARGE ) ; // TODO: Have this take in a state and switch to a different icon if necessary
2021-02-25 18:15:36 +02:00
bar - > forward_button = koto_button_new_with_icon ( " " , " media-skip-forward-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
2021-02-09 17:37:26 +02:00
if ( bar - > back_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > primary_controls_section ) , GTK_WIDGET ( bar - > back_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > play_pause_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > primary_controls_section ) , GTK_WIDGET ( bar - > play_pause_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > forward_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > primary_controls_section ) , GTK_WIDGET ( bar - > forward_button ) ) ;
2021-02-09 17:37:26 +02:00
}
2021-03-23 19:50:09 +02:00
GtkGesture * back_controller = gtk_gesture_click_new ( ) ; // Create a new GtkGestureClick
g_signal_connect ( back_controller , " pressed " , G_CALLBACK ( koto_playerbar_go_backwards ) , NULL ) ;
gtk_widget_add_controller ( GTK_WIDGET ( bar - > back_button ) , GTK_EVENT_CONTROLLER ( back_controller ) ) ;
GtkGesture * controller = gtk_gesture_click_new ( ) ; // Create a new GtkGestureClick
g_signal_connect ( controller , " pressed " , G_CALLBACK ( koto_playerbar_toggle_play_pause ) , NULL ) ;
gtk_widget_add_controller ( GTK_WIDGET ( bar - > play_pause_button ) , GTK_EVENT_CONTROLLER ( controller ) ) ;
GtkGesture * forwards_controller = gtk_gesture_click_new ( ) ; // Create a new GtkGestureClick
g_signal_connect ( forwards_controller , " pressed " , G_CALLBACK ( koto_playerbar_go_forwards ) , NULL ) ;
gtk_widget_add_controller ( GTK_WIDGET ( bar - > forward_button ) , GTK_EVENT_CONTROLLER ( forwards_controller ) ) ;
2021-02-09 17:37:26 +02:00
}
void koto_playerbar_create_secondary_controls ( KotoPlayerBar * bar ) {
2021-02-25 18:15:36 +02:00
bar - > repeat_button = koto_button_new_with_icon ( " " , " media-playlist-repeat-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
bar - > shuffle_button = koto_button_new_with_icon ( " " , " media-playlist-shuffle-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
bar - > playlist_button = koto_button_new_with_icon ( " " , " playlist-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
bar - > eq_button = koto_button_new_with_icon ( " " , " multimedia-equalizer-symbolic " , NULL , KOTO_BUTTON_PIXBUF_SIZE_NORMAL ) ;
2021-02-09 17:37:26 +02:00
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 ) ;
if ( bar - > repeat_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > secondary_controls_section ) , GTK_WIDGET ( bar - > repeat_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > shuffle_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > secondary_controls_section ) , GTK_WIDGET ( bar - > shuffle_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > playlist_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > secondary_controls_section ) , GTK_WIDGET ( bar - > playlist_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > eq_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > secondary_controls_section ) , GTK_WIDGET ( bar - > eq_button ) ) ;
2021-02-09 17:37:26 +02:00
}
if ( bar - > volume_button ! = NULL ) {
2021-02-24 20:17:18 +02:00
gtk_box_append ( GTK_BOX ( bar - > secondary_controls_section ) , bar - > volume_button ) ;
2021-02-09 17:37:26 +02:00
}
}
2021-02-24 20:17:18 +02:00
2021-03-23 19:50:09 +02:00
void koto_playerbar_go_backwards ( GtkGestureClick * gesture , int n_press , double x , double y , gpointer data ) {
( void ) gesture ; ( void ) n_press ; ( void ) x ; ( void ) y ; ( void ) data ;
koto_playback_engine_backwards ( playback_engine ) ;
}
void koto_playerbar_go_forwards ( GtkGestureClick * gesture , int n_press , double x , double y , gpointer data ) {
( void ) gesture ; ( void ) n_press ; ( void ) x ; ( void ) y ; ( void ) data ;
koto_playback_engine_forwards ( playback_engine ) ;
}
void koto_playerbar_handle_duration_change ( KotoPlaybackEngine * engine , gpointer user_data ) {
if ( ! KOTO_IS_PLAYBACK_ENGINE ( engine ) ) {
return ;
}
KotoPlayerBar * bar = user_data ;
koto_playerbar_set_progressbar_duration ( bar , koto_playback_engine_get_duration ( engine ) ) ;
}
void koto_playerbar_handle_engine_state_change ( KotoPlaybackEngine * engine , gpointer user_data ) {
if ( ! KOTO_IS_PLAYBACK_ENGINE ( engine ) ) {
return ;
}
KotoPlayerBar * bar = user_data ;
if ( ! KOTO_IS_PLAYERBAR ( bar ) ) {
return ;
}
GstState new_state = koto_playback_engine_get_state ( playback_engine ) ;
if ( new_state = = GST_STATE_PLAYING ) { // Now playing
koto_button_show_image ( bar - > play_pause_button , TRUE ) ; // Set to TRUE to show pause as the next action
} else if ( new_state = = GST_STATE_PAUSED ) { // Now paused
koto_button_show_image ( bar - > play_pause_button , FALSE ) ; // Set to FALSE to show play as the next action
}
}
void koto_playerbar_handle_progress_change ( KotoPlaybackEngine * engine , gpointer user_data ) {
if ( ! KOTO_IS_PLAYBACK_ENGINE ( engine ) ) {
return ;
}
KotoPlayerBar * bar = user_data ;
koto_playerbar_set_progressbar_value ( bar , koto_playback_engine_get_progress ( engine ) ) ;
}
void koto_playerbar_reset_progressbar ( KotoPlayerBar * bar ) {
gtk_range_set_range ( GTK_RANGE ( bar - > progress_bar ) , 0 , 0 ) ; // Reset range
gtk_range_set_value ( GTK_RANGE ( bar - > progress_bar ) , 0 ) ; // Set value to 0
}
void koto_playerbar_set_progressbar_duration ( KotoPlayerBar * bar , gint64 duration ) {
if ( duration < 0 ) {
return ;
}
gtk_range_set_range ( GTK_RANGE ( bar - > progress_bar ) , 0 , duration ) ;
}
void koto_playerbar_set_progressbar_value ( KotoPlayerBar * bar , gint64 progress ) {
if ( progress < 0 ) {
return ;
}
gtk_range_set_value ( GTK_RANGE ( bar - > progress_bar ) , progress ) ;
}
void koto_playerbar_toggle_play_pause ( GtkGestureClick * gesture , int n_press , double x , double y , gpointer data ) {
( void ) gesture ; ( void ) n_press ; ( void ) x ; ( void ) y ; ( void ) data ;
koto_playback_engine_toggle ( playback_engine ) ;
}
2021-02-24 20:17:18 +02:00
GtkWidget * koto_playerbar_get_main ( KotoPlayerBar * bar ) {
return bar - > main ;
}