Merge pull request #565 from Rafostar/timeline-improvements

Timeline improvements
This commit is contained in:
Rafał Dzięgiel
2025-06-24 19:22:12 +02:00
committed by GitHub
7 changed files with 217 additions and 73 deletions

View File

@@ -395,6 +395,47 @@ _update_duration_label (ClapperGtkSeekBar *self, gdouble duration)
gtk_adjustment_set_upper (adjustment, duration);
}
static gboolean
_find_marks_in_widget (GtkWidget *widget, GtkWidget **top_marks, GtkWidget **bottom_marks)
{
GtkWidget *child;
if (g_strcmp0 (gtk_widget_get_css_name (widget), "marks") == 0) {
if (gtk_widget_has_css_class (widget, "top"))
*top_marks = widget;
else if (gtk_widget_has_css_class (widget, "bottom"))
*bottom_marks = widget;
/* Its unexpected to have marks within marks,
* so do not iterate children of marks widget */
return (*top_marks && *bottom_marks);
}
child = gtk_widget_get_first_child (widget);
while (child != NULL) {
if (_find_marks_in_widget (child, top_marks, bottom_marks))
return TRUE;
child = gtk_widget_get_next_sibling (child);
}
return FALSE;
}
static gboolean
_find_last_mark_in_marks (GtkWidget *marks, GtkWidget **last_mark)
{
GtkWidget *widget = gtk_widget_get_last_child (marks);
if (widget && g_strcmp0 (gtk_widget_get_css_name (widget), "mark") == 0) {
*last_mark = widget;
return TRUE;
}
return FALSE;
}
static void
_update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
{
@@ -423,11 +464,43 @@ _update_scale_marks (ClapperGtkSeekBar *self, ClapperTimeline *timeline)
for (i = 0; i < n_markers; ++i) {
ClapperMarker *marker = clapper_timeline_get_marker (timeline, i);
ClapperMarkerType marker_type = clapper_marker_get_marker_type (marker);
gdouble start = clapper_marker_get_start (marker);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_TOP, NULL);
gtk_scale_add_mark (GTK_SCALE (self->scale), start, GTK_POS_BOTTOM, NULL);
if (marker_type >= CLAPPER_MARKER_TYPE_CUSTOM_1) {
GtkWidget *top_marks = NULL, *bottom_marks = NULL;
GtkWidget *top_mark = NULL, *bottom_mark = NULL;
if (_find_marks_in_widget (self->scale, &top_marks, &bottom_marks)
&& _find_last_mark_in_marks (top_marks, &top_mark)
&& _find_last_mark_in_marks (bottom_marks, &bottom_mark)) {
const gchar *custom_name;
switch (marker_type) {
case CLAPPER_MARKER_TYPE_CUSTOM_1:
custom_name = "custom1";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_2:
custom_name = "custom2";
break;
case CLAPPER_MARKER_TYPE_CUSTOM_3:
custom_name = "custom3";
break;
default:
custom_name = NULL;
break;
}
if (G_LIKELY (custom_name != NULL)) {
gtk_widget_add_css_class (top_mark, custom_name);
gtk_widget_add_css_class (bottom_mark, custom_name);
}
}
}
gst_object_unref (marker);
}

View File

@@ -129,6 +129,15 @@ clapper-gtk-seek-bar label {
margin-left: 2px;
margin-right: 2px;
}
clapper-gtk-seek-bar scale marks .custom1 indicator {
color: tomato;
}
clapper-gtk-seek-bar scale marks .custom2 indicator {
color: goldenrod;
}
clapper-gtk-seek-bar scale marks .custom3 indicator {
color: limegreen;
}
clapper-gtk-extra-menu-button popover .spinsidebutton {
min-width: 28px;

View File

@@ -34,4 +34,10 @@ gboolean clapper_timeline_set_toc (ClapperTimeline *timeline, GstToc *toc, gbool
G_GNUC_INTERNAL
void clapper_timeline_refresh (ClapperTimeline *timeline);
G_GNUC_INTERNAL
void clapper_timeline_insert_marker_internal (ClapperTimeline *timeline, ClapperMarker *marker);
G_GNUC_INTERNAL
void clapper_timeline_remove_marker_internal (ClapperTimeline *timeline, ClapperMarker *marker);
G_END_DECLS

View File

@@ -30,6 +30,7 @@
#include "clapper-player-private.h"
#include "clapper-reactables-manager-private.h"
#include "clapper-features-manager-private.h"
#include "clapper-utils-private.h"
#define GST_CAT_DEFAULT clapper_timeline_debug
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
@@ -187,25 +188,12 @@ _take_marker_unlocked (ClapperTimeline *self, ClapperMarker *marker)
return g_sequence_iter_get_position (iter);
}
/**
* clapper_timeline_insert_marker:
* @timeline: a #ClapperTimeline
* @marker: a #ClapperMarker
*
* Insert the #ClapperMarker into @timeline.
*
* Returns: %TRUE if inserted, %FALSE if marker was
* already inserted into timeline.
*/
gboolean
clapper_timeline_insert_marker (ClapperTimeline *self, ClapperMarker *marker)
void
clapper_timeline_insert_marker_internal (ClapperTimeline *self, ClapperMarker *marker)
{
gboolean success;
gint position = 0;
g_return_val_if_fail (CLAPPER_IS_TIMELINE (self), FALSE);
g_return_val_if_fail (CLAPPER_IS_MARKER (marker), FALSE);
GST_OBJECT_LOCK (self);
if ((success = !g_sequence_lookup (self->markers_seq, marker,
@@ -220,30 +208,34 @@ clapper_timeline_insert_marker (ClapperTimeline *self, ClapperMarker *marker)
clapper_timeline_post_item_updated (self);
}
return success;
}
/**
* clapper_timeline_remove_marker:
* clapper_timeline_insert_marker:
* @timeline: a #ClapperTimeline
* @marker: a #ClapperMarker
*
* Removes #ClapperMarker from the timeline.
*
* If marker was not in the @timeline, this function will do nothing,
* so it is safe to call if unsure.
* Insert the #ClapperMarker into @timeline.
*/
void
clapper_timeline_remove_marker (ClapperTimeline *self, ClapperMarker *marker)
clapper_timeline_insert_marker (ClapperTimeline *self, ClapperMarker *marker)
{
g_return_if_fail (CLAPPER_IS_TIMELINE (self));
g_return_if_fail (CLAPPER_IS_MARKER (marker));
if (g_main_context_is_owner (g_main_context_default ()))
clapper_timeline_insert_marker_internal (self, marker);
else
clapper_utils_timeline_insert_on_main_sync (self, marker);
}
void
clapper_timeline_remove_marker_internal (ClapperTimeline *self, ClapperMarker *marker)
{
GSequenceIter *iter;
gint position = 0;
gboolean success = FALSE;
g_return_if_fail (CLAPPER_IS_TIMELINE (self));
g_return_if_fail (CLAPPER_IS_MARKER (marker));
GST_OBJECT_LOCK (self);
if ((iter = g_sequence_lookup (self->markers_seq, marker,
@@ -264,6 +256,25 @@ clapper_timeline_remove_marker (ClapperTimeline *self, ClapperMarker *marker)
}
}
/**
* clapper_timeline_remove_marker:
* @timeline: a #ClapperTimeline
* @marker: a #ClapperMarker
*
* Removes #ClapperMarker from the timeline if present.
*/
void
clapper_timeline_remove_marker (ClapperTimeline *self, ClapperMarker *marker)
{
g_return_if_fail (CLAPPER_IS_TIMELINE (self));
g_return_if_fail (CLAPPER_IS_MARKER (marker));
if (g_main_context_is_owner (g_main_context_default ()))
clapper_timeline_remove_marker_internal (self, marker);
else
clapper_utils_timeline_remove_on_main_sync (self, marker);
}
/**
* clapper_timeline_get_marker:
* @timeline: a #ClapperTimeline

View File

@@ -38,7 +38,7 @@ CLAPPER_API
G_DECLARE_FINAL_TYPE (ClapperTimeline, clapper_timeline, CLAPPER, TIMELINE, GstObject)
CLAPPER_API
gboolean clapper_timeline_insert_marker (ClapperTimeline *timeline, ClapperMarker *marker);
void clapper_timeline_insert_marker (ClapperTimeline *timeline, ClapperMarker *marker);
CLAPPER_API
void clapper_timeline_remove_marker (ClapperTimeline *timeline, ClapperMarker *marker);

View File

@@ -26,6 +26,8 @@
#include "clapper-utils.h"
#include "clapper-queue.h"
#include "clapper-media-item.h"
#include "clapper-timeline.h"
#include "clapper-marker.h"
G_BEGIN_DECLS
@@ -44,6 +46,12 @@ void clapper_utils_queue_remove_on_main_sync (ClapperQueue *queue, ClapperMediaI
G_GNUC_INTERNAL
void clapper_utils_queue_clear_on_main_sync (ClapperQueue *queue);
G_GNUC_INTERNAL
void clapper_utils_timeline_insert_on_main_sync (ClapperTimeline *timeline, ClapperMarker *marker);
G_GNUC_INTERNAL
void clapper_utils_timeline_remove_on_main_sync (ClapperTimeline *timeline, ClapperMarker *marker);
G_GNUC_INTERNAL
void clapper_utils_prop_notify_on_main_sync (GObject *object, GParamSpec *pspec);

View File

@@ -17,6 +17,7 @@
*/
#include "clapper-utils-private.h"
#include "clapper-timeline-private.h"
#include "../shared/clapper-shared-utils-private.h"
#define GST_CAT_DEFAULT clapper_utils_debug
@@ -24,19 +25,21 @@ GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
typedef enum
{
CLAPPER_UTILS_QUEUE_ALTER_APPEND = 1,
CLAPPER_UTILS_QUEUE_ALTER_INSERT,
CLAPPER_UTILS_QUEUE_ALTER_REMOVE,
CLAPPER_UTILS_QUEUE_ALTER_CLEAR
} ClapperUtilsQueueAlterMethod;
CLAPPER_UTILS_LIST_ALTER_QUEUE_APPEND = 1,
CLAPPER_UTILS_LIST_ALTER_QUEUE_INSERT,
CLAPPER_UTILS_LIST_ALTER_QUEUE_REMOVE,
CLAPPER_UTILS_LIST_ALTER_QUEUE_CLEAR,
CLAPPER_UTILS_LIST_ALTER_TIMELINE_INSERT,
CLAPPER_UTILS_LIST_ALTER_TIMELINE_REMOVE
} ClapperUtilsListAlterMethod;
typedef struct
{
ClapperQueue *queue;
ClapperMediaItem *item;
ClapperMediaItem *after_item;
ClapperUtilsQueueAlterMethod method;
} ClapperUtilsQueueAlterData;
GListModel *list;
GObject *item;
GObject *after_item;
ClapperUtilsListAlterMethod method;
} ClapperUtilsListAlterData;
typedef struct
{
@@ -51,27 +54,26 @@ clapper_utils_initialize (void)
"Clapper Utilities");
}
static ClapperUtilsQueueAlterData *
clapper_utils_queue_alter_data_new (ClapperQueue *queue,
ClapperMediaItem *item, ClapperMediaItem *after_item,
ClapperUtilsQueueAlterMethod method)
static ClapperUtilsListAlterData *
clapper_utils_list_alter_data_new (GListModel *list, GObject *item,
GObject *after_item, ClapperUtilsListAlterMethod method)
{
ClapperUtilsQueueAlterData *data = g_new (ClapperUtilsQueueAlterData, 1);
ClapperUtilsListAlterData *data = g_new (ClapperUtilsListAlterData, 1);
data->queue = queue;
data->list = list;
data->item = item;
data->after_item = after_item;
data->method = method;
GST_TRACE ("Created queue alter data: %p", data);
GST_TRACE ("Created list alter data: %p", data);
return data;
}
static void
clapper_utils_queue_alter_data_free (ClapperUtilsQueueAlterData *data)
clapper_utils_list_alter_data_free (ClapperUtilsListAlterData *data)
{
GST_TRACE ("Freeing queue alter data: %p", data);
GST_TRACE ("Freeing list alter data: %p", data);
g_free (data);
}
@@ -98,35 +100,48 @@ clapper_utils_prop_notify_data_free (ClapperUtilsPropNotifyData *data)
}
static gpointer
clapper_utils_queue_alter_on_main (ClapperUtilsQueueAlterData *data)
clapper_utils_list_alter_on_main (ClapperUtilsListAlterData *data)
{
GST_DEBUG ("Queue alter invoked");
switch (data->method) {
case CLAPPER_UTILS_QUEUE_ALTER_APPEND:
clapper_queue_add_item (data->queue, data->item);
case CLAPPER_UTILS_LIST_ALTER_QUEUE_APPEND:
clapper_queue_add_item (CLAPPER_QUEUE_CAST (data->list),
CLAPPER_MEDIA_ITEM_CAST (data->item));
break;
case CLAPPER_UTILS_QUEUE_ALTER_INSERT:{
case CLAPPER_UTILS_LIST_ALTER_QUEUE_INSERT:{
guint index;
/* If we have "after_item" then we need to insert after it, otherwise prepend */
if (data->after_item) {
if (clapper_queue_find_item (data->queue, data->after_item, &index))
if (clapper_queue_find_item (CLAPPER_QUEUE_CAST (data->list),
CLAPPER_MEDIA_ITEM_CAST (data->after_item), &index)) {
index++;
else // If not found, just append at the end
index = -1;
} else {
index = -1; // if not found, just append at the end
}
} else {
index = 0;
}
clapper_queue_insert_item (data->queue, data->item, index);
clapper_queue_insert_item (CLAPPER_QUEUE_CAST (data->list),
CLAPPER_MEDIA_ITEM_CAST (data->item), index);
break;
}
case CLAPPER_UTILS_QUEUE_ALTER_REMOVE:
clapper_queue_remove_item (data->queue, data->item);
case CLAPPER_UTILS_LIST_ALTER_QUEUE_REMOVE:
clapper_queue_remove_item (CLAPPER_QUEUE_CAST (data->list),
CLAPPER_MEDIA_ITEM_CAST (data->item));
break;
case CLAPPER_UTILS_QUEUE_ALTER_CLEAR:
clapper_queue_clear (data->queue);
case CLAPPER_UTILS_LIST_ALTER_QUEUE_CLEAR:
clapper_queue_clear (CLAPPER_QUEUE_CAST (data->list));
break;
case CLAPPER_UTILS_LIST_ALTER_TIMELINE_INSERT:
clapper_timeline_insert_marker_internal (CLAPPER_TIMELINE_CAST (data->list),
CLAPPER_MARKER_CAST (data->item));
break;
case CLAPPER_UTILS_LIST_ALTER_TIMELINE_REMOVE:
clapper_timeline_remove_marker_internal (CLAPPER_TIMELINE_CAST (data->list),
CLAPPER_MARKER_CAST (data->item));
break;
default:
g_assert_not_reached ();
@@ -146,13 +161,13 @@ clapper_utils_prop_notify_on_main (ClapperUtilsPropNotifyData *data)
}
static inline void
clapper_utils_queue_alter_invoke_on_main_sync_take (ClapperUtilsQueueAlterData *data)
clapper_utils_list_alter_invoke_on_main_sync_take (ClapperUtilsListAlterData *data)
{
GST_DEBUG ("Invoking queue alter on main...");
clapper_shared_utils_context_invoke_sync_full (g_main_context_default (),
(GThreadFunc) clapper_utils_queue_alter_on_main, data,
(GDestroyNotify) clapper_utils_queue_alter_data_free);
(GThreadFunc) clapper_utils_list_alter_on_main, data,
(GDestroyNotify) clapper_utils_list_alter_data_free);
GST_DEBUG ("Queue alter invoke finished");
}
@@ -160,34 +175,56 @@ clapper_utils_queue_alter_invoke_on_main_sync_take (ClapperUtilsQueueAlterData *
void
clapper_utils_queue_append_on_main_sync (ClapperQueue *queue, ClapperMediaItem *item)
{
ClapperUtilsQueueAlterData *data = clapper_utils_queue_alter_data_new (queue,
item, NULL, CLAPPER_UTILS_QUEUE_ALTER_APPEND);
clapper_utils_queue_alter_invoke_on_main_sync_take (data);
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) queue, (GObject *) item, NULL,
CLAPPER_UTILS_LIST_ALTER_QUEUE_APPEND);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void
clapper_utils_queue_insert_on_main_sync (ClapperQueue *queue,
ClapperMediaItem *item, ClapperMediaItem *after_item)
{
ClapperUtilsQueueAlterData *data = clapper_utils_queue_alter_data_new (queue,
item, after_item, CLAPPER_UTILS_QUEUE_ALTER_INSERT);
clapper_utils_queue_alter_invoke_on_main_sync_take (data);
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) queue, (GObject *) item, (GObject *) after_item,
CLAPPER_UTILS_LIST_ALTER_QUEUE_INSERT);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void
clapper_utils_queue_remove_on_main_sync (ClapperQueue *queue, ClapperMediaItem *item)
{
ClapperUtilsQueueAlterData *data = clapper_utils_queue_alter_data_new (queue,
item, NULL, CLAPPER_UTILS_QUEUE_ALTER_REMOVE);
clapper_utils_queue_alter_invoke_on_main_sync_take (data);
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) queue, (GObject *) item, NULL,
CLAPPER_UTILS_LIST_ALTER_QUEUE_REMOVE);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void
clapper_utils_queue_clear_on_main_sync (ClapperQueue *queue)
{
ClapperUtilsQueueAlterData *data = clapper_utils_queue_alter_data_new (queue,
NULL, NULL, CLAPPER_UTILS_QUEUE_ALTER_CLEAR);
clapper_utils_queue_alter_invoke_on_main_sync_take (data);
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) queue, NULL, NULL,
CLAPPER_UTILS_LIST_ALTER_QUEUE_CLEAR);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void
clapper_utils_timeline_insert_on_main_sync (ClapperTimeline *timeline, ClapperMarker *marker)
{
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) timeline, (GObject *) marker, NULL,
CLAPPER_UTILS_LIST_ALTER_TIMELINE_INSERT);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void
clapper_utils_timeline_remove_on_main_sync (ClapperTimeline *timeline, ClapperMarker *marker)
{
ClapperUtilsListAlterData *data = clapper_utils_list_alter_data_new (
(GListModel *) timeline, (GObject *) marker, NULL,
CLAPPER_UTILS_LIST_ALTER_TIMELINE_REMOVE);
clapper_utils_list_alter_invoke_on_main_sync_take (data);
}
void