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:
parent
22d5bcc777
commit
62f2883849
17 changed files with 387 additions and 24 deletions
9
data/vectors/multimedia-backwards-jump.svg
Normal file
9
data/vectors/multimedia-backwards-jump.svg
Normal 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 |
9
data/vectors/multimedia-forwards-jump.svg
Normal file
9
data/vectors/multimedia-forwards-jump.svg
Normal 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 |
|
@ -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
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
.koto-button {
|
||||
border-width: 0;
|
||||
|
||||
& > image {
|
||||
margin-right: 10px;
|
||||
& > .button-label {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
&:not(.active) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
|
@ -7,6 +7,6 @@
|
|||
color: $text-color-faded;
|
||||
font-size: 4em;
|
||||
font-weight: bold;
|
||||
padding-bottom: $itempadding;
|
||||
padding-bottom: $padding;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
& > .album-list {
|
||||
& > flowboxchild > .album-view {
|
||||
& > overlay {
|
||||
margin-right: $itempadding;
|
||||
margin-right: $padding;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue