Hook GtkTimeline up to GdkFrameClock

Use GdkFrameClock for the timing of GtkTimeline. This require the
user to provide either a GtkWidget or a GdkFrameClock when creating
the timeline. The default constructor now takes a GtkWidget. If you
want to create a GdkFrameClock without a widget, you need to use
g_object_new() and pass in a GdkFrameClock and GdkScreen.

https://bugzilla.gnome.org/show_bug.cgi?id=685460
This commit is contained in:
Owen W. Taylor 2012-09-27 16:27:36 -04:00
parent cbce5bcd0b
commit 314dadca82
3 changed files with 254 additions and 75 deletions

View File

@ -194,6 +194,7 @@
#include <gtk/gtktexttagtable.h> #include <gtk/gtktexttagtable.h>
#include <gtk/gtktextview.h> #include <gtk/gtktextview.h>
#include <gtk/gtkthemingengine.h> #include <gtk/gtkthemingengine.h>
#include <gtk/gtktimeline.h>
#include <gtk/gtktoggleaction.h> #include <gtk/gtktoggleaction.h>
#include <gtk/gtktogglebutton.h> #include <gtk/gtktogglebutton.h>
#include <gtk/gtktoggletoolbutton.h> #include <gtk/gtktoggletoolbutton.h>

View File

@ -20,30 +20,30 @@
#include <gtk/gtksettings.h> #include <gtk/gtksettings.h>
#include <math.h> #include <math.h>
#define MSECS_PER_SEC 1000
#define FRAME_INTERVAL(nframes) (MSECS_PER_SEC / nframes)
#define DEFAULT_FPS 30
typedef struct GtkTimelinePriv GtkTimelinePriv; typedef struct GtkTimelinePriv GtkTimelinePriv;
struct GtkTimelinePriv struct GtkTimelinePriv
{ {
guint duration; guint duration;
guint source_id;
GTimer *timer; guint64 last_time;
gdouble elapsed_time; gdouble elapsed_time;
gdouble progress; gdouble progress;
gdouble last_progress; gdouble last_progress;
GtkWidget *widget;
GdkFrameClock *frame_clock;
GdkScreen *screen; GdkScreen *screen;
guint update_id;
GtkTimelineProgressType progress_type; GtkTimelineProgressType progress_type;
guint animations_enabled : 1; guint animations_enabled : 1;
guint loop : 1; guint loop : 1;
guint direction : 1; guint direction : 1;
guint running : 1;
}; };
enum { enum {
@ -51,7 +51,9 @@ enum {
PROP_DURATION, PROP_DURATION,
PROP_LOOP, PROP_LOOP,
PROP_DIRECTION, PROP_DIRECTION,
PROP_SCREEN PROP_FRAME_CLOCK,
PROP_SCREEN,
PROP_WIDGET
}; };
enum { enum {
@ -65,6 +67,11 @@ enum {
static guint signals [LAST_SIGNAL] = { 0, }; static guint signals [LAST_SIGNAL] = { 0, };
static void gtk_timeline_start_running (GtkTimeline *timeline);
static void gtk_timeline_stop_running (GtkTimeline *timeline);
static void frame_clock_target_iface_init (GdkFrameClockTargetInterface *target);
static void gtk_timeline_set_property (GObject *object, static void gtk_timeline_set_property (GObject *object,
guint prop_id, guint prop_id,
const GValue *value, const GValue *value,
@ -75,9 +82,11 @@ static void gtk_timeline_get_property (GObject *object,
GParamSpec *pspec); GParamSpec *pspec);
static void gtk_timeline_finalize (GObject *object); static void gtk_timeline_finalize (GObject *object);
static void gtk_timeline_set_clock (GdkFrameClockTarget *target,
GdkFrameClock *frame_clock);
G_DEFINE_TYPE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_CODE (GtkTimeline, gtk_timeline, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (GDK_TYPE_FRAME_CLOCK_TARGET, frame_clock_target_iface_init))
static void static void
gtk_timeline_class_init (GtkTimelineClass *klass) gtk_timeline_class_init (GtkTimelineClass *klass)
@ -103,13 +112,27 @@ gtk_timeline_class_init (GtkTimelineClass *klass)
"Whether the timeline loops or not", "Whether the timeline loops or not",
FALSE, FALSE,
G_PARAM_READWRITE)); G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_FRAME_CLOCK,
g_param_spec_object ("paint-clock",
"Frame Clock",
"clock used for timing the animation (not needed if :widget is set)",
GDK_TYPE_FRAME_CLOCK,
G_PARAM_READWRITE));
g_object_class_install_property (object_class, g_object_class_install_property (object_class,
PROP_SCREEN, PROP_SCREEN,
g_param_spec_object ("screen", g_param_spec_object ("screen",
"Screen", "Screen",
"Screen to get the settings from", "Screen to get the settings from (not needed if :widget is set)",
GDK_TYPE_SCREEN, GDK_TYPE_SCREEN,
G_PARAM_READWRITE)); G_PARAM_READWRITE));
g_object_class_install_property (object_class,
PROP_WIDGET,
g_param_spec_object ("widget",
"Widget",
"Widget the timeline will be used with",
GTK_TYPE_WIDGET,
G_PARAM_READWRITE));
signals[STARTED] = signals[STARTED] =
g_signal_new ("started", g_signal_new ("started",
@ -151,6 +174,12 @@ gtk_timeline_class_init (GtkTimelineClass *klass)
g_type_class_add_private (klass, sizeof (GtkTimelinePriv)); g_type_class_add_private (klass, sizeof (GtkTimelinePriv));
} }
static void
frame_clock_target_iface_init (GdkFrameClockTargetInterface *iface)
{
iface->set_clock = gtk_timeline_set_clock;
}
static void static void
gtk_timeline_init (GtkTimeline *timeline) gtk_timeline_init (GtkTimeline *timeline)
{ {
@ -162,6 +191,7 @@ gtk_timeline_init (GtkTimeline *timeline)
priv->duration = 0.0; priv->duration = 0.0;
priv->direction = GTK_TIMELINE_DIRECTION_FORWARD; priv->direction = GTK_TIMELINE_DIRECTION_FORWARD;
priv->progress_type = GTK_TIMELINE_PROGRESS_EASE_OUT;
priv->screen = gdk_screen_get_default (); priv->screen = gdk_screen_get_default ();
priv->last_progress = 0; priv->last_progress = 0;
@ -188,10 +218,18 @@ gtk_timeline_set_property (GObject *object,
case PROP_DIRECTION: case PROP_DIRECTION:
gtk_timeline_set_direction (timeline, g_value_get_enum (value)); gtk_timeline_set_direction (timeline, g_value_get_enum (value));
break; break;
case PROP_FRAME_CLOCK:
gtk_timeline_set_frame_clock (timeline,
GDK_FRAME_CLOCK (g_value_get_object (value)));
break;
case PROP_SCREEN: case PROP_SCREEN:
gtk_timeline_set_screen (timeline, gtk_timeline_set_screen (timeline,
GDK_SCREEN (g_value_get_object (value))); GDK_SCREEN (g_value_get_object (value)));
break; break;
case PROP_WIDGET:
gtk_timeline_set_widget (timeline,
GTK_WIDGET (g_value_get_object (value)));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
@ -220,9 +258,15 @@ gtk_timeline_get_property (GObject *object,
case PROP_DIRECTION: case PROP_DIRECTION:
g_value_set_enum (value, priv->direction); g_value_set_enum (value, priv->direction);
break; break;
case PROP_FRAME_CLOCK:
g_value_set_object (value, priv->frame_clock);
break;
case PROP_SCREEN: case PROP_SCREEN:
g_value_set_object (value, priv->screen); g_value_set_object (value, priv->screen);
break; break;
case PROP_WIDGET:
g_value_set_object (value, priv->widget);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
@ -237,18 +281,24 @@ gtk_timeline_finalize (GObject *object)
timeline = (GtkTimeline *) object; timeline = (GtkTimeline *) object;
priv = timeline->priv; priv = timeline->priv;
if (priv->source_id) if (priv->running)
{ {
g_source_remove (priv->source_id); gtk_timeline_stop_running (timeline);
priv->source_id = 0; priv->running = FALSE;
} }
if (priv->timer)
g_timer_destroy (priv->timer);
G_OBJECT_CLASS (gtk_timeline_parent_class)->finalize (object); G_OBJECT_CLASS (gtk_timeline_parent_class)->finalize (object);
} }
/* Implementation of GdkFrameClockTarget method */
static void
gtk_timeline_set_clock (GdkFrameClockTarget *target,
GdkFrameClock *frame_clock)
{
gtk_timeline_set_frame_clock (GTK_TIMELINE (target),
frame_clock);
}
static gdouble static gdouble
calculate_progress (gdouble linear_progress, calculate_progress (gdouble linear_progress,
GtkTimelineProgressType progress_type) GtkTimelineProgressType progress_type)
@ -286,19 +336,22 @@ calculate_progress (gdouble linear_progress,
return progress; return progress;
} }
static gboolean static void
gtk_timeline_run_frame (GtkTimeline *timeline) gtk_timeline_on_update (GdkFrameClock *clock,
GtkTimeline *timeline)
{ {
GtkTimelinePriv *priv; GtkTimelinePriv *priv;
gdouble delta_progress, progress; gdouble delta_progress, progress;
guint64 now;
/* the user may unref us during the signals, so save ourselves */ /* the user may unref us during the signals, so save ourselves */
g_object_ref (timeline); g_object_ref (timeline);
priv = timeline->priv; priv = timeline->priv;
priv->elapsed_time = (guint) (g_timer_elapsed (priv->timer, NULL) * 1000); now = gdk_frame_clock_get_frame_time (clock);
g_timer_start (priv->timer); priv->elapsed_time = (now - priv->last_time) / 1000;
priv->last_time = now;
if (priv->animations_enabled) if (priv->animations_enabled)
{ {
@ -332,49 +385,94 @@ gtk_timeline_run_frame (GtkTimeline *timeline)
gtk_timeline_rewind (timeline); gtk_timeline_rewind (timeline);
else else
{ {
if (priv->source_id) gtk_timeline_stop_running (timeline);
{ priv->running = FALSE;
g_source_remove (priv->source_id);
priv->source_id = 0;
}
g_timer_stop (priv->timer);
g_signal_emit (timeline, signals [FINISHED], 0); g_signal_emit (timeline, signals [FINISHED], 0);
g_object_unref (timeline); g_object_unref (timeline);
return FALSE; return;
} }
} }
g_object_unref (timeline); g_object_unref (timeline);
gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
}
return TRUE; static void
gtk_timeline_start_updating (GtkTimeline *timeline)
{
GtkTimelinePriv *priv = timeline->priv;
g_assert (priv->running && priv->frame_clock && priv->update_id == 0);
priv->update_id = g_signal_connect (priv->frame_clock,
"update",
G_CALLBACK (gtk_timeline_on_update),
timeline);
gdk_frame_clock_request_phase (priv->frame_clock, GDK_FRAME_CLOCK_PHASE_UPDATE);
priv->last_time = gdk_frame_clock_get_frame_time (priv->frame_clock);
}
static void
gtk_timeline_stop_updating (GtkTimeline *timeline)
{
GtkTimelinePriv *priv = timeline->priv;
g_assert (priv->running && priv->frame_clock && priv->update_id != 0);
g_signal_handler_disconnect (priv->frame_clock,
priv->update_id);
priv->update_id = 0;
}
static void
gtk_timeline_start_running (GtkTimeline *timeline)
{
GtkTimelinePriv *priv = timeline->priv;
g_assert (priv->running);
if (priv->widget)
gtk_widget_add_frame_clock_target (priv->widget,
GDK_FRAME_CLOCK_TARGET (timeline));
else if (priv->frame_clock)
gtk_timeline_start_updating (timeline);
}
static void
gtk_timeline_stop_running (GtkTimeline *timeline)
{
GtkTimelinePriv *priv = timeline->priv;
g_assert (priv->running);
if (priv->widget)
gtk_widget_remove_frame_clock_target (priv->widget,
GDK_FRAME_CLOCK_TARGET (timeline));
else if (priv->frame_clock)
gtk_timeline_stop_updating (timeline);
} }
/* /*
* gtk_timeline_new: * gtk_timeline_new:
* @widget: a widget the timeline will be used with
* @duration: duration in milliseconds for the timeline * @duration: duration in milliseconds for the timeline
* *
* Creates a new #GtkTimeline with the specified number of frames. * Creates a new #GtkTimeline with the specified duration
* *
* Return Value: the newly created #GtkTimeline * Return Value: the newly created #GtkTimeline
*/ */
GtkTimeline * GtkTimeline *
gtk_timeline_new (guint duration) gtk_timeline_new (GtkWidget *widget,
guint duration)
{ {
return g_object_new (GTK_TYPE_TIMELINE, return g_object_new (GTK_TYPE_TIMELINE,
"widget", widget,
"duration", duration, "duration", duration,
NULL); NULL);
} }
GtkTimeline *
gtk_timeline_new_for_screen (guint duration,
GdkScreen *screen)
{
return g_object_new (GTK_TYPE_TIMELINE,
"duration", duration,
"screen", screen,
NULL);
}
/* /*
* gtk_timeline_start: * gtk_timeline_start:
* @timeline: A #GtkTimeline * @timeline: A #GtkTimeline
@ -392,13 +490,8 @@ gtk_timeline_start (GtkTimeline *timeline)
priv = timeline->priv; priv = timeline->priv;
if (!priv->source_id) if (!priv->running)
{ {
if (priv->timer)
g_timer_continue (priv->timer);
else
priv->timer = g_timer_new ();
if (priv->screen) if (priv->screen)
{ {
settings = gtk_settings_get_for_screen (priv->screen); settings = gtk_settings_get_for_screen (priv->screen);
@ -407,15 +500,10 @@ gtk_timeline_start (GtkTimeline *timeline)
priv->animations_enabled = enable_animations; priv->animations_enabled = enable_animations;
g_signal_emit (timeline, signals [STARTED], 0); priv->running = TRUE;
gtk_timeline_start_running (timeline);
if (enable_animations) g_signal_emit (timeline, signals [STARTED], 0);
priv->source_id = gdk_threads_add_timeout (FRAME_INTERVAL (DEFAULT_FPS),
(GSourceFunc) gtk_timeline_run_frame,
timeline);
else
priv->source_id = gdk_threads_add_idle ((GSourceFunc) gtk_timeline_run_frame,
timeline);
} }
} }
@ -434,11 +522,11 @@ gtk_timeline_pause (GtkTimeline *timeline)
priv = timeline->priv; priv = timeline->priv;
if (priv->source_id) if (priv->running)
{ {
g_timer_stop (priv->timer); gtk_timeline_stop_running (timeline);
g_source_remove (priv->source_id); priv->running = FALSE;
priv->source_id = 0;
g_signal_emit (timeline, signals [PAUSED], 0); g_signal_emit (timeline, signals [PAUSED], 0);
} }
} }
@ -463,14 +551,8 @@ gtk_timeline_rewind (GtkTimeline *timeline)
else else
priv->progress = priv->last_progress = 0.; priv->progress = priv->last_progress = 0.;
/* reset timer */ if (priv->running && priv->frame_clock)
if (priv->timer) priv->last_time = gdk_frame_clock_get_frame_time (priv->frame_clock);
{
g_timer_start (priv->timer);
if (!priv->source_id)
g_timer_stop (priv->timer);
}
} }
/* /*
@ -490,7 +572,7 @@ gtk_timeline_is_running (GtkTimeline *timeline)
priv = timeline->priv; priv = timeline->priv;
return (priv->source_id != 0); return priv->running;
} }
/* /*
@ -624,6 +706,48 @@ gtk_timeline_get_direction (GtkTimeline *timeline)
return priv->direction; return priv->direction;
} }
void
gtk_timeline_set_frame_clock (GtkTimeline *timeline,
GdkFrameClock *frame_clock)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock));
priv = timeline->priv;
if (frame_clock == priv->frame_clock)
return;
if (priv->running && priv->frame_clock)
gtk_timeline_stop_updating (timeline);
if (priv->frame_clock)
g_object_unref (priv->frame_clock);
priv->frame_clock = frame_clock;
if (priv->frame_clock)
g_object_ref (priv->frame_clock);
if (priv->running && priv->frame_clock)
gtk_timeline_start_updating (timeline);
g_object_notify (G_OBJECT (timeline), "paint-clock");
}
GdkFrameClock *
gtk_timeline_get_frame_clock (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
priv = timeline->priv;
return priv->frame_clock;
}
void void
gtk_timeline_set_screen (GtkTimeline *timeline, gtk_timeline_set_screen (GtkTimeline *timeline,
GdkScreen *screen) GdkScreen *screen)
@ -631,14 +755,20 @@ gtk_timeline_set_screen (GtkTimeline *timeline,
GtkTimelinePriv *priv; GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline)); g_return_if_fail (GTK_IS_TIMELINE (timeline));
g_return_if_fail (GDK_IS_SCREEN (screen)); g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen));
priv = timeline->priv; priv = timeline->priv;
if (screen == priv->screen)
return;
if (priv->screen) if (priv->screen)
g_object_unref (priv->screen); g_object_unref (priv->screen);
priv->screen = g_object_ref (screen); priv->screen = screen;
if (priv->screen)
g_object_ref (priv->screen);
g_object_notify (G_OBJECT (timeline), "screen"); g_object_notify (G_OBJECT (timeline), "screen");
} }
@ -653,6 +783,47 @@ gtk_timeline_get_screen (GtkTimeline *timeline)
priv = timeline->priv; priv = timeline->priv;
return priv->screen; return priv->screen;
} }
void
gtk_timeline_set_widget (GtkTimeline *timeline,
GtkWidget *widget)
{
GtkTimelinePriv *priv;
g_return_if_fail (GTK_IS_TIMELINE (timeline));
g_return_if_fail (widget == NULL || GTK_IS_WIDGET (widget));
priv = timeline->priv;
if (widget == priv->widget)
return;
if (priv->running)
gtk_timeline_stop_running (timeline);
if (priv->widget)
g_object_unref (priv->widget);
priv->widget = widget;
if (priv->widget)
g_object_ref (widget);
if (priv->running)
gtk_timeline_start_running (timeline);
g_object_notify (G_OBJECT (timeline), "widget");
}
GtkWidget *
gtk_timeline_get_widget (GtkTimeline *timeline)
{
GtkTimelinePriv *priv;
g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL);
priv = timeline->priv;
return priv->widget;
}
gdouble gdouble
gtk_timeline_get_progress (GtkTimeline *timeline) gtk_timeline_get_progress (GtkTimeline *timeline)

View File

@ -20,6 +20,7 @@
#include <glib-object.h> #include <glib-object.h>
#include <gtk/gtkenums.h> #include <gtk/gtkenums.h>
#include <gtk/gtkwidget.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
G_BEGIN_DECLS G_BEGIN_DECLS
@ -62,7 +63,7 @@ struct GtkTimelineClass
void (* paused) (GtkTimeline *timeline); void (* paused) (GtkTimeline *timeline);
void (* frame) (GtkTimeline *timeline, void (* frame) (GtkTimeline *timeline,
gdouble progress); gdouble progress);
void (* __gtk_reserved1) (void); void (* __gtk_reserved1) (void);
void (* __gtk_reserved2) (void); void (* __gtk_reserved2) (void);
@ -73,9 +74,8 @@ struct GtkTimelineClass
GType gtk_timeline_get_type (void) G_GNUC_CONST; GType gtk_timeline_get_type (void) G_GNUC_CONST;
GtkTimeline * gtk_timeline_new (guint duration); GtkTimeline * gtk_timeline_new (GtkWidget *widget,
GtkTimeline * gtk_timeline_new_for_screen (guint duration, guint duration);
GdkScreen *screen);
void gtk_timeline_start (GtkTimeline *timeline); void gtk_timeline_start (GtkTimeline *timeline);
void gtk_timeline_pause (GtkTimeline *timeline); void gtk_timeline_pause (GtkTimeline *timeline);
@ -90,12 +90,20 @@ void gtk_timeline_set_loop (GtkTimeline
guint gtk_timeline_get_duration (GtkTimeline *timeline); guint gtk_timeline_get_duration (GtkTimeline *timeline);
void gtk_timeline_set_duration (GtkTimeline *timeline, void gtk_timeline_set_duration (GtkTimeline *timeline,
guint duration); guint duration);
GdkFrameClock * gtk_timeline_get_frame_clock (GtkTimeline *timeline);
void gtk_timeline_set_frame_clock (GtkTimeline *timeline,
GdkFrameClock *frame_clock);
GdkScreen * gtk_timeline_get_screen (GtkTimeline *timeline); GdkScreen * gtk_timeline_get_screen (GtkTimeline *timeline);
void gtk_timeline_set_screen (GtkTimeline *timeline, void gtk_timeline_set_screen (GtkTimeline *timeline,
GdkScreen *screen); GdkScreen *screen);
GtkWidget * gtk_timeline_get_widget (GtkTimeline *timeline);
void gtk_timeline_set_widget (GtkTimeline *timeline,
GtkWidget *widget);
GtkTimelineDirection gtk_timeline_get_direction (GtkTimeline *timeline); GtkTimelineDirection gtk_timeline_get_direction (GtkTimeline *timeline);
void gtk_timeline_set_direction (GtkTimeline *timeline, void gtk_timeline_set_direction (GtkTimeline *timeline,
GtkTimelineDirection direction); GtkTimelineDirection direction);
@ -106,7 +114,6 @@ GtkTimelineProgressType gtk_timeline_get_progress_type (GtkTimeline
void gtk_timeline_set_progress_type (GtkTimeline *timeline, void gtk_timeline_set_progress_type (GtkTimeline *timeline,
GtkTimelineProgressType progress_type); GtkTimelineProgressType progress_type);
G_END_DECLS G_END_DECLS
#endif /* _GTK_TIMELINE_H__ */ #endif /* _GTK_TIMELINE_H__ */