Implement playback speed and jump forwards / backwards functionality.

Ref #14.

Additionally fixed some bugs around the KotoButton showing the badge text label when it shouldn't, styling related to margins, and various legacy null and string valid checks.
This commit is contained in:
Joshua Strobl 2021-08-17 19:27:33 +03:00
parent 22d5bcc777
commit 62f2883849
17 changed files with 387 additions and 24 deletions

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -11.25)">
<g transform="translate(-312.3 -265.79)" opacity=".996">
<path d="m312.49 277.23-0.18709 2.4322 2.4322-0.1871-0.74834-0.74833a2.1167 2.1167 0 0 1 2.9934-1e-5 2.1167 2.1167 0 0 1 0 2.9934 2.1167 2.1167 0 0 1-2.9934 2e-5l-0.74836 0.74835a3.175 3.175 0 0 0 4.4901-3e-5 3.175 3.175 0 0 0 2e-5 -4.4901 3.175 3.175 0 0 0-4.4902-2e-5z" fill="#666" stroke-width=".26458"/>
<path d="m315.46 279.29v1.1199s0.43968 0.4382 0.54774 0.54626" fill="none" stroke="#666" stroke-linecap="round" stroke-width=".79375"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 695 B

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="24" height="24" version="1.1" viewBox="0 0 6.35 6.35" xmlns="http://www.w3.org/2000/svg">
<g transform="translate(0 -11.25)">
<g transform="matrix(.99913 0 0 1 -320.37 -265.83)" opacity=".996">
<path d="m326.82 277.26 0.18709 2.4322-2.4322-0.18709 0.74834-0.74833a2.1167 2.1167 0 0 0-2.9934-1e-5 2.1167 2.1167 0 0 0 0 2.9934 2.1167 2.1167 0 0 0 2.9934 1e-5l0.74836 0.74835a3.175 3.175 0 0 1-4.4901-3e-5 3.175 3.175 0 0 1-2e-5 -4.4901 3.175 3.175 0 0 1 4.4902-1e-5z" fill="#666" opacity=".998" stroke-width=".2647"/>
<path d="m323.85 279.32v1.12s-0.43968 0.4382-0.54774 0.54625" fill="none" opacity=".998" stroke="#666" stroke-linecap="round" stroke-width=".7941"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 733 B

View file

@ -62,6 +62,7 @@ enum {
PROP_IMAGE_FILE_PATH,
PROP_ICON_NAME,
PROP_ALT_ICON_NAME,
PROP_RESOURCE_PATH,
N_BTN_PROPERTIES
};
@ -86,6 +87,7 @@ struct _KotoButton {
gchar * badge_text;
gchar * icon_name;
gchar * alt_icon_name;
gchar * resource_path;
gchar * text;
KotoButtonImagePosition image_position;
@ -181,6 +183,14 @@ static void koto_button_class_init(KotoButtonClass * c) {
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
);
btn_props[PROP_RESOURCE_PATH] = g_param_spec_string(
"resource-path",
"Resource Path to an Icon",
"Resource Path to an Icon",
NULL,
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
);
g_object_class_install_properties(gobject_class, N_BTN_PROPERTIES, btn_props);
}
@ -191,6 +201,8 @@ static void koto_button_init(KotoButton * self) {
self->left_click_gesture = gtk_gesture_click_new(); // Set up our left click gesture
self->right_click_gesture = gtk_gesture_click_new(); // Set up our right click gesture
self->resource_path = NULL;
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(self->left_click_gesture), (int) KOTO_BUTTON_CLICK_TYPE_PRIMARY); // Only allow left clicks on left click gesture
gtk_gesture_single_set_button(GTK_GESTURE_SINGLE(self->right_click_gesture), (int) KOTO_BUTTON_CLICK_TYPE_SECONDARY); // Only allow right clicks on right click gesture
@ -242,6 +254,9 @@ static void koto_button_get_property(
case PROP_ALT_ICON_NAME:
g_value_set_string(val, self->alt_icon_name);
break;
case PROP_RESOURCE_PATH:
g_value_set_string(val, self->resource_path);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
@ -261,7 +276,7 @@ static void koto_button_set_property(
koto_button_set_pixbuf_size(self, g_value_get_uint(val));
break;
case PROP_TEXT:
if (val != NULL) {
if (koto_utils_string_is_valid(g_value_get_string(val))) {
koto_button_set_text(self, (gchar*) g_value_get_string(val));
}
@ -287,6 +302,9 @@ static void koto_button_set_property(
koto_button_show_image(self, TRUE);
}
break;
case PROP_RESOURCE_PATH:
koto_button_set_resource_path(self, g_strdup(g_value_get_string(val)));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID(obj, prop_id, spec);
break;
@ -424,13 +442,22 @@ void koto_button_set_badge_text(
return;
}
if ((text == NULL) || (strcmp(text, "") == 0)) { // If the text is empty
self->badge_text = g_strdup("");
} else {
if (koto_utils_string_is_valid(self->badge_text)) { // Have existing text
g_free(self->badge_text);
self->badge_text = g_strdup(text);
}
if (!koto_utils_string_is_valid(text)) { // If the text is empty
self->badge_text = NULL;
if (GTK_IS_LABEL(self->badge_label)) { // If badge label already exists
gtk_widget_hide(self->badge_label); // Hide the label
}
return;
}
self->badge_text = g_strdup(text);
if (GTK_IS_LABEL(self->badge_label)) { // If badge label already exists
gtk_label_set_text(GTK_LABEL(self->badge_label), self->badge_text);
} else {
@ -438,12 +465,6 @@ void koto_button_set_badge_text(
gtk_box_append(GTK_BOX(self), self->badge_label);
}
if (strcmp(self->badge_text, "") != 0) { // Empty badge
gtk_widget_hide(self->badge_label); // Hide our badge
} else { // Have some text
gtk_widget_show(self->badge_label); // Show our badge
}
g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_BADGE_TEXT]);
}
@ -561,6 +582,34 @@ void koto_button_set_pixbuf_size(
g_object_notify_by_pspec(G_OBJECT(self), btn_props[PROP_PIX_SIZE]);
}
void koto_button_set_resource_path(
KotoButton * self,
gchar * resource_path
) {
if (!KOTO_IS_BUTTON(self)) {
return;
}
if (!koto_utils_string_is_valid(resource_path)) { // Not a valid string
return;
}
if (koto_utils_string_is_valid(self->resource_path)) { // Have a resource path already
g_free(self->resource_path); // Free it
}
self->resource_path = g_strdup(resource_path);
if (GTK_IS_IMAGE(self->button_pic)) { // Already have a button image
gtk_image_set_from_resource(GTK_IMAGE(self->button_pic), self->resource_path);
} else {
self->button_pic = gtk_image_new_from_resource(self->resource_path); // Create a new image from the resource
gtk_image_set_pixel_size(GTK_IMAGE(self->button_pic), self->pix_size);
gtk_image_set_icon_size(GTK_IMAGE(self->button_pic), GTK_ICON_SIZE_INHERIT); // Inherit height of parent widget
gtk_box_prepend(GTK_BOX(self), self->button_pic); // Prepend to the box
}
}
void koto_button_set_text(
KotoButton * self,
gchar * text
@ -590,6 +639,7 @@ void koto_button_set_text(
} else { // If we do not have a button label
if (koto_utils_string_is_valid(self->text)) { // If we have text
self->button_label = gtk_label_new(self->text); // Create our label
gtk_widget_add_css_class(self->button_label, "button-label");
gtk_widget_set_hexpand(self->button_label, TRUE);
gtk_label_set_xalign(GTK_LABEL(self->button_label), 0);
@ -752,3 +802,17 @@ KotoButton * koto_button_new_with_file(
NULL
);
}
KotoButton * koto_button_new_with_resource (
gchar * resource_path,
KotoButtonPixbufSize size
) {
return g_object_new(
KOTO_TYPE_BUTTON,
"resource-path",
resource_path,
"pixbuf-size",
koto_get_pixbuf_size(size),
NULL
);
}

View file

@ -66,6 +66,11 @@ KotoButton * koto_button_new_with_file(
KotoButtonPixbufSize size
);
KotoButton * koto_button_new_with_resource(
gchar * resource_path,
KotoButtonPixbufSize size
);
void koto_button_add_click_handler(
KotoButton * self,
KotoButtonClickType button,
@ -127,6 +132,11 @@ void koto_button_set_image_position(
KotoButtonImagePosition pos
);
void koto_button_set_resource_path(
KotoButton * self,
gchar * resource_path
);
void koto_button_set_pixbuf(
KotoButton * self,
GdkPixbuf * pix

View file

@ -37,6 +37,8 @@ enum {
PROP_PLAYBACK_CONTINUE_ON_PLAYLIST,
PROP_PLAYBACK_LAST_USED_VOLUME,
PROP_PLAYBACK_MAINTAIN_SHUFFLE,
PROP_PLAYBACK_JUMP_BACKWARDS_INCREMENT,
PROP_PLAYBACK_JUMP_FORWARDS_INCREMENT,
PROP_PREFERRED_ALBUM_SORT_TYPE,
PROP_UI_ALBUM_INFO_SHOW_DESCRIPTION,
PROP_UI_ALBUM_INFO_SHOW_GENRES,
@ -73,6 +75,8 @@ struct _KotoConfig {
gboolean playback_continue_on_playlist;
gdouble playback_last_used_volume;
gboolean playback_maintain_shuffle;
guint playback_jump_backwards_increment;
guint playback_jump_forwards_increment;
/* Misc Prefs */
@ -146,6 +150,26 @@ static void koto_config_class_init(KotoConfigClass * c) {
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
);
config_props[PROP_PLAYBACK_JUMP_BACKWARDS_INCREMENT] = g_param_spec_uint(
"playback-jump-backwards-increment",
"Jump Backwards Increment",
"Jump Backwards Increment",
5, // 5s
90, // 1min30s
10, // 10s
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
);
config_props[PROP_PLAYBACK_JUMP_FORWARDS_INCREMENT] = g_param_spec_uint(
"playback-jump-forwards-increment",
"Jump Forwards Increment",
"Jump Forwards Increment",
5, // 5s
90, // 1min30s
30, // 30s
G_PARAM_CONSTRUCT | G_PARAM_EXPLICIT_NOTIFY | G_PARAM_READWRITE
);
config_props[PROP_PREFERRED_ALBUM_SORT_TYPE] = g_param_spec_string(
"artist-preferred-album-sort-type",
"Preferred album sort type (chronological or alphabetical-only)",
@ -235,6 +259,12 @@ static void koto_config_get_property(
case PROP_PLAYBACK_MAINTAIN_SHUFFLE:
g_value_set_boolean(val, self->playback_maintain_shuffle);
break;
case PROP_PLAYBACK_JUMP_BACKWARDS_INCREMENT:
g_value_set_uint(val, self->playback_jump_backwards_increment);
break;
case PROP_PLAYBACK_JUMP_FORWARDS_INCREMENT:
g_value_set_uint(val, self->playback_jump_forwards_increment);
break;
case PROP_UI_ALBUM_INFO_SHOW_DESCRIPTION:
g_value_set_boolean(val, self->ui_album_info_show_description);
break;
@ -277,6 +307,12 @@ static void koto_config_set_property(
case PROP_PLAYBACK_MAINTAIN_SHUFFLE:
self->playback_maintain_shuffle = g_value_get_boolean(val);
break;
case PROP_PLAYBACK_JUMP_BACKWARDS_INCREMENT:
self->playback_jump_backwards_increment = g_value_get_uint(val);
break;
case PROP_PLAYBACK_JUMP_FORWARDS_INCREMENT:
self->playback_jump_forwards_increment = g_value_get_uint(val);
break;
case PROP_PREFERRED_ALBUM_SORT_TYPE:
self->preferred_album_sort_type = g_strcmp0(g_value_get_string(val), "alphabetical") ? KOTO_PREFERRED_ALBUM_ALWAYS_ALPHABETICAL : KOTO_PREFERRED_ALBUM_SORT_TYPE_DEFAULT;
break;
@ -434,6 +470,8 @@ void koto_config_load(
if (playback_section) { // Have playback section
toml_datum_t continue_on_playlist = toml_bool_in(playback_section, "continue-on-playlist");
toml_datum_t jump_backwards_increment = toml_int_in(playback_section, "jump-backwards-increment");
toml_datum_t jump_forwards_increment = toml_int_in(playback_section, "jump-forwards-increment");
toml_datum_t last_used_volume = toml_double_in(playback_section, "last-used-volume");
toml_datum_t maintain_shuffle = toml_bool_in(playback_section, "maintain-shuffle");
@ -441,6 +479,14 @@ void koto_config_load(
g_object_set(self, "playback-continue-on-playlist", continue_on_playlist.u.b, NULL);
}
if (jump_backwards_increment.ok && (self->playback_jump_backwards_increment != jump_backwards_increment.u.i)) { // If we have a jump-backwards-increment set and it is different
g_object_set(self, "playback-jump-backwards-increment", (guint) jump_backwards_increment.u.i, NULL);
}
if (jump_forwards_increment.ok && (self->playback_jump_forwards_increment != jump_forwards_increment.u.i)) { // If we have a jump-backwards-increment set and it is different
g_object_set(self, "playback-jump-forwards-increment", (guint) jump_forwards_increment.u.i, NULL);
}
if (last_used_volume.ok && (self->playback_last_used_volume != last_used_volume.u.d)) { // If we have last-used-volume set and they are different
g_object_set(self, "playback-last-used-volume", last_used_volume.u.d, NULL);
}

View file

@ -64,6 +64,12 @@ struct _KotoPlayerBar {
GtkWidget * playback_album;
GtkWidget * playback_artist;
/* Advanced Controls (Jump Back / Forwards, Speed) */
GtkWidget * advanced_controls_section;
KotoButton * backwards_jump_button;
KotoButton * forwards_jump_button;
GtkWidget * playback_speed_input;
/* Misc Widgets */
GtkWidget * playback_position_label;
@ -236,6 +242,40 @@ void koto_playerbar_create_primary_controls(KotoPlayerBar * self) {
gtk_box_append(GTK_BOX(self->primary_controls_section), GTK_WIDGET(self->forward_button));
koto_button_add_click_handler(self->forward_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playerbar_go_forwards), self);
}
self->advanced_controls_section = gtk_center_box_new();
gtk_widget_add_css_class(self->advanced_controls_section, "playerbar-advanced-controls");
self->backwards_jump_button = koto_button_new_with_resource("/com/github/joshstrobl/koto/multimedia-backwards-jump", KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
koto_button_add_click_handler(self->backwards_jump_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playerbar_jump_backwards), self);
self->forwards_jump_button = koto_button_new_with_resource("/com/github/joshstrobl/koto/multimedia-forwards-jump", KOTO_BUTTON_PIXBUF_SIZE_NORMAL);
koto_button_add_click_handler(self->forwards_jump_button, KOTO_BUTTON_CLICK_TYPE_PRIMARY, G_CALLBACK(koto_playerbar_jump_forwards), self);
self->playback_speed_input = gtk_entry_new_with_buffer(gtk_entry_buffer_new("1.0", -1)); // Create an input that defaults to 1.0
gtk_entry_set_max_length(GTK_ENTRY(self->playback_speed_input), 4); // Max of 4 characters (e.g. "0.25" but allow for "2" or "2.0")
gtk_editable_set_max_width_chars(GTK_EDITABLE(self->playback_speed_input), 4); // Set to be as wide as 4 characters
gtk_widget_set_valign(self->playback_speed_input, GTK_ALIGN_CENTER); // Only align center, don't vexpand
gtk_center_box_set_start_widget(GTK_CENTER_BOX(self->advanced_controls_section), GTK_WIDGET(self->backwards_jump_button));
gtk_center_box_set_center_widget(GTK_CENTER_BOX(self->advanced_controls_section), GTK_WIDGET(self->playback_speed_input));
gtk_center_box_set_end_widget(GTK_CENTER_BOX(self->advanced_controls_section), GTK_WIDGET(self->forwards_jump_button));
gtk_box_append(GTK_BOX(self->primary_controls_section), self->advanced_controls_section);
gtk_entry_set_input_hints(
GTK_ENTRY(self->playback_speed_input),
GTK_INPUT_HINT_NONE | GTK_INPUT_HINT_NO_SPELLCHECK | GTK_INPUT_HINT_NO_EMOJI // No fanciness please
);
gtk_entry_set_input_purpose(
GTK_ENTRY(self->playback_speed_input),
GTK_INPUT_PURPOSE_NUMBER // Numbers allow . so use that
);
gtk_entry_set_placeholder_text(GTK_ENTRY(self->playback_speed_input), "1.0");
g_signal_connect(self->playback_speed_input, "activate", G_CALLBACK(koto_playerbar_handle_speed_input_activate), self); // Handle our activate (enter key on entry)
}
void koto_playerbar_create_secondary_controls(KotoPlayerBar * self) {
@ -313,6 +353,29 @@ void koto_playerbar_go_forwards(
koto_playback_engine_forwards(playback_engine);
}
void koto_playerbar_handle_speed_input_activate(
GtkEntry * playback_speed_input,
gpointer user_data
) {
KotoPlayerBar * self = user_data;
if (!KOTO_IS_PLAYERBAR(self)) {
return;
}
GtkEntryBuffer * entry_buffer = gtk_entry_get_buffer(playback_speed_input); // Get the entry buffer
const gchar * text = gtk_entry_buffer_get_text(entry_buffer); // Get the text for the buffer
gdouble rate = g_strtod(text, NULL); // Attempt to convert
gdouble fixed_rate = koto_playback_engine_get_sane_playback_rate(rate);
gchar * conv = g_strdup_printf("%f", fixed_rate);
gtk_entry_buffer_set_text(entry_buffer, g_utf8_substring(conv, 0, 4), -1); // Set our entry value to the converted string of the new rate, or if it is something like 2 then it sets it to 2.0
g_free(conv);
koto_playback_engine_set_playback_rate(playback_engine, fixed_rate);
}
void koto_playerbar_handle_is_playing(
KotoPlaybackEngine * engine,
gpointer user_data
@ -538,6 +601,38 @@ void koto_playerbar_handle_volume_button_change(
koto_playback_engine_set_volume(playback_engine, (double) value / 100);
}
void koto_playerbar_jump_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_jump_backwards(playback_engine);
}
void koto_playerbar_jump_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_jump_forwards(playback_engine);
}
void koto_playerbar_reset_progressbar(KotoPlayerBar * self) {
if (!KOTO_IS_PLAYERBAR(self)) {
return;

View file

@ -105,6 +105,11 @@ void koto_playerbar_handle_progressbar_value_changed(
gpointer data
);
void koto_playerbar_handle_speed_input_activate(
GtkEntry * playback_speed_input,
gpointer user_data
);
void koto_playerbar_handle_tick_duration(
KotoPlaybackEngine * engine,
gpointer user_data
@ -131,6 +136,22 @@ void koto_playerbar_handle_volume_button_change(
gpointer user_data
);
void koto_playerbar_jump_backwards(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_jump_forwards(
GtkGestureClick * gesture,
int n_press,
double x,
double y,
gpointer data
);
void koto_playerbar_reset_progressbar(KotoPlayerBar * self);
void koto_playerbar_set_progressbar_duration(

View file

@ -3,6 +3,8 @@
<gresource prefix="/com/github/joshstrobl/koto">
<file alias="business-and-personal-finance.png">../data/genres/business-and-personal-finance.png</file>
<file alias="foreign-languages.png">../data/genres/foreign-languages.png</file>
<file alias="multimedia-backwards-jump">../data/vectors/multimedia-backwards-jump.svg</file>
<file alias="multimedia-forwards-jump">../data/vectors/multimedia-forwards-jump.svg</file>
<file alias="mystery-and-thriller.png">../data/genres/mystery-and-thriller.png</file>
<file alias="sci-fi.png">../data/genres/sci-fi.png</file>
<file alias="travel.png">../data/genres/travel.png</file>

View file

@ -81,6 +81,7 @@ struct _KotoPlaybackEngine {
guint playback_position;
gint requested_playback_position;
gdouble rate;
gdouble volume;
};
@ -210,6 +211,7 @@ static void koto_playback_engine_init(KotoPlaybackEngine * self) {
self->suppress_video = gst_element_factory_make("fakesink", "suppress-video");
g_object_set(self->playbin, "video-sink", self->suppress_video, NULL);
self->rate = 1.0;
self->volume = 0.5;
koto_playback_engine_set_volume(self, 0.5);
@ -408,6 +410,18 @@ gdouble koto_playback_engine_get_progress(KotoPlaybackEngine * self) {
return progress;
}
gdouble koto_playback_engine_get_sane_playback_rate(gdouble rate) {
if ((rate < 0.25) && (rate != 0)) { // 0.25 is probably the most reasonable limit
return 0.25;
} else if (rate > 2.5) { // Anything greater than 2.5
return 2.5;
} else if (rate == 0) { // Possible conversion failure
return 1.0;
} else {
return rate;
}
}
GstState koto_playback_engine_get_state(KotoPlaybackEngine * self) {
return GST_STATE(self->player);
}
@ -424,6 +438,55 @@ gdouble koto_playback_engine_get_volume(KotoPlaybackEngine * self) {
return KOTO_IS_PLAYBACK_ENGINE(self) ? self->volume : 0;
}
void koto_playback_engine_jump_backwards(KotoPlaybackEngine * self) {
if (!KOTO_IS_PLAYBACK_ENGINE(self)) { // Not an engine
return;
}
if (!self->is_playing) { // Is not playing
return;
}
gdouble cur_pos = koto_playback_engine_get_progress(self); // Get the current position
guint backwards_increment = 10;
g_object_get(
config,
"playback-jump-backwards-increment", // Get our backwards increment from our settings
&backwards_increment,
NULL
);
gint new_pos = (gint) cur_pos - backwards_increment;
if (new_pos < 0) { // Before the beginning of time
new_pos = 0;
}
koto_playback_engine_set_position(self, new_pos);
}
void koto_playback_engine_jump_forwards(KotoPlaybackEngine * self) {
if (!KOTO_IS_PLAYBACK_ENGINE(self)) { // Not an engine
return;
}
if (!self->is_playing) { // Is not playing
return;
}
gdouble cur_pos = koto_playback_engine_get_progress(self); // Get the current position
guint forwards_increment = 30;
g_object_get(
config,
"playback-jump-forwards-increment", // Get our forward increment from our settings
&forwards_increment,
NULL
);
gint new_pos = (gint) cur_pos + forwards_increment;
koto_playback_engine_set_position(self, new_pos);
}
gboolean koto_playback_engine_monitor_changed(
GstBus * bus,
GstMessage * msg,
@ -516,6 +579,20 @@ void koto_playback_engine_pause(KotoPlaybackEngine * self) {
koto_update_mpris_playback_state(GST_STATE_PAUSED);
}
void koto_playback_engine_set_playback_rate(
KotoPlaybackEngine * self,
gdouble rate
) {
if (!KOTO_IS_PLAYBACK_ENGINE(self)) { // Not a playback engine
return;
}
self->rate = koto_playback_engine_get_sane_playback_rate(rate); // Get a fixed rate and set our current rate to it
gst_element_set_state(self->player, GST_STATE_PAUSED); // Set our state to paused
koto_playback_engine_set_position(self, koto_playback_engine_get_progress(self)); // Basically creates a new seek event
gst_element_set_state(self->player, GST_STATE_PLAYING); // Set our state to play
}
void koto_playback_engine_set_position(
KotoPlaybackEngine * self,
int position
@ -526,7 +603,7 @@ void koto_playback_engine_set_position(
gst_element_seek(
self->playbin,
1.0,
self->rate,
GST_FORMAT_TIME,
GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_ACCURATE,
GST_SEEK_TYPE_SET,

View file

@ -66,16 +66,24 @@ KotoTrack * koto_playback_engine_get_current_track(KotoPlaybackEngine * self);
gint64 koto_playback_engine_get_duration(KotoPlaybackEngine * self);
GstState koto_playback_engine_get_state(KotoPlaybackEngine * self);
gdouble koto_playback_engine_get_progress(KotoPlaybackEngine * self);
;
gdouble koto_playback_engine_get_sane_playback_rate(gdouble rate);
GstState koto_playback_engine_get_state(KotoPlaybackEngine * self);
gboolean koto_playback_engine_get_track_repeat(KotoPlaybackEngine * self);
gboolean koto_playback_engine_get_track_shuffle(KotoPlaybackEngine * self);
gdouble koto_playback_engine_get_volume(KotoPlaybackEngine * self);
void koto_playback_engine_jump_backwards(KotoPlaybackEngine * self);
void koto_playback_engine_jump_forwards(KotoPlaybackEngine * self);
void koto_playback_engine_mute(KotoPlaybackEngine * self);
gboolean koto_playback_engine_monitor_changed(
@ -90,6 +98,11 @@ void koto_playback_engine_play(KotoPlaybackEngine * self);
void koto_playback_engine_toggle(KotoPlaybackEngine * self);
void koto_playback_engine_set_playback_rate(
KotoPlaybackEngine * self,
gdouble rate
);
void koto_playback_engine_set_position(
KotoPlaybackEngine * self,
int position

View file

@ -3,8 +3,8 @@
.koto-button {
border-width: 0;
& > image {
margin-right: 10px;
& > .button-label {
margin-left: 10px;
}
&:not(.active) {

View file

@ -47,6 +47,6 @@ window {
.artist-view-content, // Has the albums
.playlist-page, // Individual playlists
.writer-page { // Writer page in Audiobook
padding: $itempadding;
padding: $padding;
}
}

View file

@ -15,6 +15,23 @@
}
}
.playerbar-primary-controls, // Primary
.playerbar-secondary-controls { // Secondary
& > .koto-button { // Direct descendents
margin: 0 $quarterpadding;
}
}
.playerbar-primary-controls { // Primary Controls
.playerbar-advanced-controls { // Advanced controls
margin-left: $padding;
& > entry { // Inner GtkEntry
margin: 0 $halvedpadding;
}
}
}
.playerbar-info { // Central info section
& > box { // Info labels
margin-left: 2ex;

View file

@ -4,6 +4,6 @@ $green: #60E078;
$palewhite: #cccccc;
$red : #FF4652;
$itempadding: 40px;
$halvedpadding: $itempadding / 2;
$quarterpadding: $itempadding / 4;
$padding: 40px;
$halvedpadding: $padding / 2;
$quarterpadding: $padding / 4;

View file

@ -7,6 +7,6 @@
color: $text-color-faded;
font-size: 4em;
font-weight: bold;
padding-bottom: $itempadding;
padding-bottom: $padding;
}
}

View file

@ -5,7 +5,7 @@
.audiobook-library { // Library page
.genres-banner { // Banner for genres list
.large-banner { // Large banner with art for each genre
padding: $itempadding;
padding: $padding;
.audiobook-genre-button { // Genre buttons
.koto-button {
@ -17,7 +17,7 @@
}
.writers-button-flow { // Flowbox of buttons for writers
padding: 0 $itempadding; // Horizontal padding of our standard item padding
padding: 0 $padding; // Horizontal padding of our standard item padding
flowboxchild {
padding: 0;

View file

@ -19,7 +19,7 @@
& > .album-list {
& > flowboxchild > .album-view {
& > overlay {
margin-right: $itempadding;
margin-right: $padding;
}
}
}