diff options
author | Owen W. Taylor <otaylor@fishsoup.net> | 2012-12-19 12:03:16 -0500 |
---|---|---|
committer | Owen W. Taylor <otaylor@fishsoup.net> | 2013-02-14 17:19:52 -0500 |
commit | 449e8c88565b444d1399a9080bde5141d054bde6 (patch) | |
tree | c8455ce3e8bde1098cbe326084b4ac31ddfbaa67 | |
parent | 06c4598fc52cf21c7c0355761f3fa2e7e5923e9c (diff) | |
download | gtk+-449e8c88565b444d1399a9080bde5141d054bde6.tar.gz |
Add gtk_widget_add_tick_callback(), remove GtkTimeline, etc.
Add a very simple GtkWidget function for an "tick" callback, which
is connected to the ::update signal of GdkFrameClock.
Remove:
- GtkTimeline. The consensus is that it is too complex.
- GdkPaintClockTarget. In the rare cases where tick callbacks
aren't sufficient, it's possible to track the
paint clock with ::realize/::unrealize/::hierarchy-changed.
GtkTimeline is kept using ::update directly to allow using a GtkTimeline
with a paint clock but no widget.
-rw-r--r-- | gdk/gdkframeclock.c | 13 | ||||
-rw-r--r-- | gdk/gdkframeclock.h | 20 | ||||
-rw-r--r-- | gtk/Makefile.am | 2 | ||||
-rw-r--r-- | gtk/gtk.h | 1 | ||||
-rw-r--r-- | gtk/gtkstylecontext.c | 112 | ||||
-rw-r--r-- | gtk/gtkstylecontext.h | 7 | ||||
-rw-r--r-- | gtk/gtktimeline.c | 909 | ||||
-rw-r--r-- | gtk/gtktimeline.h | 119 | ||||
-rw-r--r-- | gtk/gtkwidget.c | 329 | ||||
-rw-r--r-- | gtk/gtkwidget.h | 32 | ||||
-rw-r--r-- | tests/animated-resizing.c | 37 |
11 files changed, 388 insertions, 1193 deletions
diff --git a/gdk/gdkframeclock.c b/gdk/gdkframeclock.c index fd41d94533..08789ebb04 100644 --- a/gdk/gdkframeclock.c +++ b/gdk/gdkframeclock.c @@ -28,19 +28,6 @@ #include "gdkframeclock.h" -G_DEFINE_INTERFACE (GdkFrameClockTarget, gdk_frame_clock_target, G_TYPE_OBJECT) - -static void -gdk_frame_clock_target_default_init (GdkFrameClockTargetInterface *iface) -{ -} - -void gdk_frame_clock_target_set_clock (GdkFrameClockTarget *target, - GdkFrameClock *clock) -{ - GDK_FRAME_CLOCK_TARGET_GET_IFACE (target)->set_clock (target, clock); -} - /** * SECTION:frameclock * @Short_description: Frame clock syncs painting to a window or display diff --git a/gdk/gdkframeclock.h b/gdk/gdkframeclock.h index 8d79f90a96..ba6a59f40a 100644 --- a/gdk/gdkframeclock.h +++ b/gdk/gdkframeclock.h @@ -37,26 +37,6 @@ G_BEGIN_DECLS typedef struct _GdkFrameClock GdkFrameClock; typedef struct _GdkFrameClockInterface GdkFrameClockInterface; -typedef struct _GdkFrameClockTarget GdkFrameClockTarget; -typedef struct _GdkFrameClockTargetInterface GdkFrameClockTargetInterface; - -#define GDK_TYPE_FRAME_CLOCK_TARGET (gdk_frame_clock_target_get_type ()) -#define GDK_FRAME_CLOCK_TARGET(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK_TARGET, GdkFrameClockTarget)) -#define GDK_IS_FRAME_CLOCK_TARGET(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDK_TYPE_FRAME_CLOCK_TARGET)) -#define GDK_FRAME_CLOCK_TARGET_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), GDK_TYPE_FRAME_CLOCK_TARGET, GdkFrameClockTargetInterface)) - -struct _GdkFrameClockTargetInterface -{ - GTypeInterface base_iface; - - void (*set_clock) (GdkFrameClockTarget *target, - GdkFrameClock *clock); -}; - -GType gdk_frame_clock_target_get_type (void) G_GNUC_CONST; - -void gdk_frame_clock_target_set_clock (GdkFrameClockTarget *target, - GdkFrameClock *clock); #define GDK_TYPE_FRAME_CLOCK (gdk_frame_clock_get_type ()) #define GDK_FRAME_CLOCK(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDK_TYPE_FRAME_CLOCK, GdkFrameClock)) diff --git a/gtk/Makefile.am b/gtk/Makefile.am index bc1dc7ccba..973f3c8994 100644 --- a/gtk/Makefile.am +++ b/gtk/Makefile.am @@ -348,7 +348,6 @@ gtk_public_h_sources = \ gtktexttagtable.h \ gtktextview.h \ gtkthemingengine.h \ - gtktimeline.h \ gtktoggleaction.h \ gtktogglebutton.h \ gtktoggletoolbutton.h \ @@ -851,7 +850,6 @@ gtk_base_c_sources = \ gtktextview.c \ gtkthemingbackground.c \ gtkthemingengine.c \ - gtktimeline.c \ gtktoggleaction.c \ gtktogglebutton.c \ gtktoggletoolbutton.c \ @@ -194,7 +194,6 @@ #include <gtk/gtktexttagtable.h> #include <gtk/gtktextview.h> #include <gtk/gtkthemingengine.h> -#include <gtk/gtktimeline.h> #include <gtk/gtktoggleaction.h> #include <gtk/gtktogglebutton.h> #include <gtk/gtktoggletoolbutton.h> diff --git a/gtk/gtkstylecontext.c b/gtk/gtkstylecontext.c index 343cf1f1bc..a57873cae7 100644 --- a/gtk/gtkstylecontext.c +++ b/gtk/gtkstylecontext.c @@ -380,6 +380,7 @@ enum { PROP_0, PROP_SCREEN, PROP_DIRECTION, + PROP_FRAME_CLOCK, PROP_PARENT }; @@ -392,8 +393,6 @@ static guint signals[LAST_SIGNAL] = { 0 }; static void gtk_style_context_finalize (GObject *object); -static void frame_clock_target_iface_init (GdkFrameClockTargetInterface *target); - static void gtk_style_context_impl_set_property (GObject *object, guint prop_id, const GValue *value, @@ -408,8 +407,7 @@ static StyleData *style_data_lookup (GtkStyleContext *context); static void gtk_style_context_disconnect_update (GtkStyleContext *context); static void gtk_style_context_connect_update (GtkStyleContext *context); -G_DEFINE_TYPE_WITH_CODE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT, - G_IMPLEMENT_INTERFACE (GDK_TYPE_FRAME_CLOCK_TARGET, frame_clock_target_iface_init)) +G_DEFINE_TYPE (GtkStyleContext, gtk_style_context, G_TYPE_OBJECT) static void gtk_style_context_real_changed (GtkStyleContext *context) @@ -448,6 +446,13 @@ gtk_style_context_class_init (GtkStyleContextClass *klass) GDK_TYPE_SCREEN, GTK_PARAM_READWRITE)); g_object_class_install_property (object_class, + PROP_FRAME_CLOCK, + g_param_spec_object ("paint-clock", + P_("FrameClock"), + P_("The associated GdkFrameClock"), + GDK_TYPE_FRAME_CLOCK, + GTK_PARAM_READWRITE)); + g_object_class_install_property (object_class, PROP_DIRECTION, g_param_spec_enum ("direction", P_("Direction"), @@ -474,25 +479,6 @@ gtk_style_context_class_init (GtkStyleContextClass *klass) g_type_class_add_private (object_class, sizeof (GtkStyleContextPrivate)); } -static void -gtk_style_context_set_clock (GdkFrameClockTarget *target, - GdkFrameClock *clock) -{ - GtkStyleContext *context = GTK_STYLE_CONTEXT (target); - GtkStyleContextPrivate *priv = context->priv; - - gtk_style_context_disconnect_update (context); - priv->frame_clock = clock; - if (priv->animating) - gtk_style_context_connect_update (context); -} - -static void -frame_clock_target_iface_init (GdkFrameClockTargetInterface *iface) -{ - iface->set_clock = gtk_style_context_set_clock; -} - static StyleData * style_data_new (void) { @@ -805,11 +791,6 @@ gtk_style_context_stop_animating (GtkStyleContext *context) priv->animating = FALSE; gtk_style_context_disconnect_update (context); - if (priv->widget) - { - gtk_widget_remove_frame_clock_target (priv->widget, - GDK_FRAME_CLOCK_TARGET (context)); - } } static void @@ -823,11 +804,6 @@ gtk_style_context_start_animating (GtkStyleContext *context) priv->animating = TRUE; gtk_style_context_connect_update (context); - if (priv->widget) - { - gtk_widget_add_frame_clock_target (priv->widget, - GDK_FRAME_CLOCK_TARGET (context)); - } } static gboolean @@ -916,6 +892,10 @@ gtk_style_context_impl_set_property (GObject *object, g_value_get_enum (value)); G_GNUC_END_IGNORE_DEPRECATIONS; break; + case PROP_FRAME_CLOCK: + gtk_style_context_set_frame_clock (style_context, + g_value_get_object (value)); + break; case PROP_PARENT: gtk_style_context_set_parent (style_context, g_value_get_object (value)); @@ -948,6 +928,9 @@ gtk_style_context_impl_get_property (GObject *object, g_value_set_enum (value, gtk_style_context_get_direction (style_context)); G_GNUC_END_IGNORE_DEPRECATIONS; break; + case PROP_FRAME_CLOCK: + g_value_set_object (value, priv->frame_clock); + break; case PROP_PARENT: g_value_set_object (value, priv->parent); break; @@ -2645,6 +2628,69 @@ gtk_style_context_get_screen (GtkStyleContext *context) } /** + * gtk_style_context_set_frame_clock: + * @context: a #GdkFrameClock + * @frame_clock: a #GdkFrameClock + * + * Attaches @context to the given frame clock. + * + * The frame clock is used for the timing of animations. + * + * If you are using a #GtkStyleContext returned from + * gtk_widget_get_style_context(), you do not need to + * call this yourself. + * + * Since: 3.8 + **/ +void +gtk_style_context_set_frame_clock (GtkStyleContext *context, + GdkFrameClock *frame_clock) +{ + GtkStyleContextPrivate *priv; + + g_return_if_fail (GTK_IS_STYLE_CONTEXT (context)); + g_return_if_fail (frame_clock == NULL || GDK_IS_FRAME_CLOCK (frame_clock)); + + priv = context->priv; + if (priv->frame_clock == frame_clock) + return; + + if (priv->animating) + gtk_style_context_disconnect_update (context); + + 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->animating) + gtk_style_context_connect_update (context); + + g_object_notify (G_OBJECT (context), "paint-clock"); +} + +/** + * gtk_style_context_get_frame_clock: + * @context: a #GtkStyleContext + * + * Returns the #GdkFrameClock to which @context is attached. + * + * Returns: (transfer none): a #GdkFrameClock. + * Since: 3.8 + **/ +GdkFrameClock * +gtk_style_context_get_frame_clock (GtkStyleContext *context) +{ + GtkStyleContextPrivate *priv; + + g_return_val_if_fail (GTK_IS_STYLE_CONTEXT (context), NULL); + + priv = context->priv; + return priv->frame_clock; +} + +/** * gtk_style_context_set_direction: * @context: a #GtkStyleContext * @direction: the new direction. diff --git a/gtk/gtkstylecontext.h b/gtk/gtkstylecontext.h index dab758a5fb..35655f28d6 100644 --- a/gtk/gtkstylecontext.h +++ b/gtk/gtkstylecontext.h @@ -836,6 +836,13 @@ void gtk_style_context_set_screen (GtkStyleContext *context, GdkScreen *screen); GdkScreen * gtk_style_context_get_screen (GtkStyleContext *context); +GDK_AVAILABLE_IN_3_8 +void gtk_style_context_set_frame_clock (GtkStyleContext *context, + GdkFrameClock *frame_clock); +GDK_AVAILABLE_IN_3_8 +GdkFrameClock *gtk_style_context_get_frame_clock (GtkStyleContext *context); + + GDK_DEPRECATED_IN_3_8_FOR(gtk_style_context_set_state) void gtk_style_context_set_direction (GtkStyleContext *context, GtkTextDirection direction); diff --git a/gtk/gtktimeline.c b/gtk/gtktimeline.c deleted file mode 100644 index ad2e7dfcd4..0000000000 --- a/gtk/gtktimeline.c +++ /dev/null @@ -1,909 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#include <gtk/gtktimeline.h> -#include <gtk/gtktypebuiltins.h> -#include <gtk/gtksettings.h> -#include <math.h> - -typedef struct GtkTimelinePriv GtkTimelinePriv; - -struct GtkTimelinePriv -{ - guint duration; - - guint64 last_time; - gdouble elapsed_time; - - gdouble progress; - gdouble last_progress; - - GtkWidget *widget; - GdkFrameClock *frame_clock; - GdkScreen *screen; - - guint update_id; - - GtkTimelineProgressType progress_type; - - guint animations_enabled : 1; - guint loop : 1; - guint direction : 1; - guint running : 1; -}; - -enum { - PROP_0, - PROP_DURATION, - PROP_LOOP, - PROP_DIRECTION, - PROP_FRAME_CLOCK, - PROP_PROGRESS_TYPE, - PROP_SCREEN, - PROP_WIDGET -}; - -enum { - STARTED, - PAUSED, - FINISHED, - FRAME, - LAST_SIGNAL -}; - -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, - guint prop_id, - const GValue *value, - GParamSpec *pspec); -static void gtk_timeline_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec); -static void gtk_timeline_finalize (GObject *object); - -static void gtk_timeline_set_clock (GdkFrameClockTarget *target, - GdkFrameClock *frame_clock); - -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 -gtk_timeline_class_init (GtkTimelineClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS (klass); - - object_class->set_property = gtk_timeline_set_property; - object_class->get_property = gtk_timeline_get_property; - object_class->finalize = gtk_timeline_finalize; - - g_object_class_install_property (object_class, - PROP_DURATION, - g_param_spec_uint ("duration", - "Animation Duration", - "Animation Duration", - 0, G_MAXUINT, - 0, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_LOOP, - g_param_spec_boolean ("loop", - "Loop", - "Whether the timeline loops or not", - FALSE, - 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, - PROP_PROGRESS_TYPE, - g_param_spec_enum ("progress-type", - "Progress Type", - "Easing function for animation progress", - GTK_TYPE_TIMELINE_PROGRESS_TYPE, - GTK_TIMELINE_PROGRESS_EASE_OUT, - G_PARAM_READWRITE)); - g_object_class_install_property (object_class, - PROP_SCREEN, - g_param_spec_object ("screen", - "Screen", - "Screen to get the settings from (not needed if :widget is set)", - GDK_TYPE_SCREEN, - 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] = - g_signal_new ("started", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTimelineClass, started), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[PAUSED] = - g_signal_new ("paused", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTimelineClass, paused), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[FINISHED] = - g_signal_new ("finished", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTimelineClass, finished), - NULL, NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, 0); - - signals[FRAME] = - g_signal_new ("frame", - G_TYPE_FROM_CLASS (object_class), - G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GtkTimelineClass, frame), - NULL, NULL, - g_cclosure_marshal_VOID__DOUBLE, - G_TYPE_NONE, 1, - G_TYPE_DOUBLE); - - 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 -gtk_timeline_init (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - priv = timeline->priv = G_TYPE_INSTANCE_GET_PRIVATE (timeline, - GTK_TYPE_TIMELINE, - GtkTimelinePriv); - - priv->duration = 0.0; - priv->direction = GTK_TIMELINE_DIRECTION_FORWARD; - priv->progress_type = GTK_TIMELINE_PROGRESS_EASE_OUT; - priv->screen = gdk_screen_get_default (); - - priv->last_progress = 0; -} - -static void -gtk_timeline_set_property (GObject *object, - guint prop_id, - const GValue *value, - GParamSpec *pspec) -{ - GtkTimeline *timeline; - - timeline = GTK_TIMELINE (object); - - switch (prop_id) - { - case PROP_DURATION: - gtk_timeline_set_duration (timeline, g_value_get_uint (value)); - break; - case PROP_LOOP: - gtk_timeline_set_loop (timeline, g_value_get_boolean (value)); - break; - case PROP_DIRECTION: - gtk_timeline_set_direction (timeline, g_value_get_enum (value)); - break; - case PROP_FRAME_CLOCK: - gtk_timeline_set_frame_clock (timeline, - GDK_FRAME_CLOCK (g_value_get_object (value))); - break; - case PROP_PROGRESS_TYPE: - gtk_timeline_set_progress_type (timeline, - g_value_get_enum (value)); - break; - case PROP_SCREEN: - gtk_timeline_set_screen (timeline, - GDK_SCREEN (g_value_get_object (value))); - break; - case PROP_WIDGET: - gtk_timeline_set_widget (timeline, - GTK_WIDGET (g_value_get_object (value))); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_timeline_get_property (GObject *object, - guint prop_id, - GValue *value, - GParamSpec *pspec) -{ - GtkTimeline *timeline; - GtkTimelinePriv *priv; - - timeline = GTK_TIMELINE (object); - priv = timeline->priv; - - switch (prop_id) - { - case PROP_DURATION: - g_value_set_uint (value, priv->duration); - break; - case PROP_LOOP: - g_value_set_boolean (value, priv->loop); - break; - case PROP_DIRECTION: - g_value_set_enum (value, priv->direction); - break; - case PROP_FRAME_CLOCK: - g_value_set_object (value, priv->frame_clock); - break; - case PROP_PROGRESS_TYPE: - g_value_set_enum (value, priv->progress_type); - break; - case PROP_SCREEN: - g_value_set_object (value, priv->screen); - break; - case PROP_WIDGET: - g_value_set_object (value, priv->widget); - break; - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - } -} - -static void -gtk_timeline_finalize (GObject *object) -{ - GtkTimelinePriv *priv; - GtkTimeline *timeline; - - timeline = (GtkTimeline *) object; - priv = timeline->priv; - - if (priv->running) - { - gtk_timeline_stop_running (timeline); - priv->running = FALSE; - } - - 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 -calculate_progress (gdouble linear_progress, - GtkTimelineProgressType progress_type) -{ - gdouble progress; - - progress = linear_progress; - - switch (progress_type) - { - case GTK_TIMELINE_PROGRESS_LINEAR: - break; - case GTK_TIMELINE_PROGRESS_EASE_IN_OUT: - progress *= 2; - - if (progress < 1) - progress = pow (progress, 3) / 2; - else - progress = (pow (progress - 2, 3) + 2) / 2; - - break; - case GTK_TIMELINE_PROGRESS_EASE: - progress = (sin ((progress - 0.5) * G_PI) + 1) / 2; - break; - case GTK_TIMELINE_PROGRESS_EASE_IN: - progress = pow (progress, 3); - break; - case GTK_TIMELINE_PROGRESS_EASE_OUT: - progress = pow (progress - 1, 3) + 1; - break; - default: - g_warning ("Timeline progress type not implemented"); - } - - return progress; -} - -static void -gtk_timeline_on_update (GdkFrameClock *clock, - GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - gdouble delta_progress, progress, adjust; - guint64 now; - - /* the user may unref us during the signals, so save ourselves */ - g_object_ref (timeline); - - priv = timeline->priv; - - now = gdk_frame_clock_get_frame_time (clock); - priv->elapsed_time = (now - priv->last_time) / 1000; - priv->last_time = now; - - if (priv->animations_enabled) - { - delta_progress = (gdouble) priv->elapsed_time / priv->duration; - progress = priv->last_progress; - - if (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD) - progress -= delta_progress; - else - progress += delta_progress; - - priv->last_progress = progress; - - /* When looping, if we go past the end, start that much into the - * next cycle */ - if (progress < 0.0) - { - adjust = progress - ceil(progress); - progress = 0.0; - } - else if (progress > 1.0) - { - adjust = progress - floor(progress); - progress = 1.0; - } - else - adjust = 0.0; - - progress = CLAMP (progress, 0., 1.); - } - else - progress = (priv->direction == GTK_TIMELINE_DIRECTION_FORWARD) ? 1.0 : 0.0; - - priv->progress = progress; - g_signal_emit (timeline, signals [FRAME], 0, - calculate_progress (progress, priv->progress_type)); - - if ((priv->direction == GTK_TIMELINE_DIRECTION_FORWARD && progress == 1.0) || - (priv->direction == GTK_TIMELINE_DIRECTION_BACKWARD && progress == 0.0)) - { - gboolean loop; - - loop = priv->loop && priv->animations_enabled; - - if (loop) - { - gtk_timeline_rewind (timeline); - priv->progress += adjust; - } - else - { - gtk_timeline_stop_running (timeline); - priv->running = FALSE; - - g_signal_emit (timeline, signals [FINISHED], 0); - g_object_unref (timeline); - return; - } - } - - g_object_unref (timeline); - gdk_frame_clock_request_phase (clock, GDK_FRAME_CLOCK_PHASE_UPDATE); -} - -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: - * @widget: a widget the timeline will be used with - * @duration: duration in milliseconds for the timeline - * - * Creates a new #GtkTimeline with the specified duration - * - * Return Value: the newly created #GtkTimeline - */ -GtkTimeline * -gtk_timeline_new (GtkWidget *widget, - guint duration) -{ - return g_object_new (GTK_TYPE_TIMELINE, - "widget", widget, - "duration", duration, - NULL); -} - -/** - * gtk_timeline_start: - * @timeline: A #GtkTimeline - * - * Runs the timeline from the current frame. - */ -void -gtk_timeline_start (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - GtkSettings *settings; - gboolean enable_animations = FALSE; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - - if (!priv->running) - { - if (priv->screen) - { - settings = gtk_settings_get_for_screen (priv->screen); - g_object_get (settings, "gtk-enable-animations", &enable_animations, NULL); - } - - priv->animations_enabled = enable_animations; - - priv->running = TRUE; - gtk_timeline_start_running (timeline); - - g_signal_emit (timeline, signals [STARTED], 0); - } -} - -/** - * gtk_timeline_pause: - * @timeline: A #GtkTimeline - * - * Pauses the timeline. - */ -void -gtk_timeline_pause (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - - if (priv->running) - { - gtk_timeline_stop_running (timeline); - priv->running = FALSE; - - g_signal_emit (timeline, signals [PAUSED], 0); - } -} - -/** - * gtk_timeline_rewind: - * @timeline: A #GtkTimeline - * - * Rewinds the timeline. - */ -void -gtk_timeline_rewind (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - - if (gtk_timeline_get_direction (timeline) != GTK_TIMELINE_DIRECTION_FORWARD) - priv->progress = priv->last_progress = 1.; - else - priv->progress = priv->last_progress = 0.; - - if (priv->running && priv->frame_clock) - priv->last_time = gdk_frame_clock_get_frame_time (priv->frame_clock); -} - -/** - * gtk_timeline_is_running: - * @timeline: A #GtkTimeline - * - * Returns whether the timeline is running or not. - * - * Return Value: %TRUE if the timeline is running - */ -gboolean -gtk_timeline_is_running (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE); - - priv = timeline->priv; - - return priv->running; -} - -/** - * gtk_timeline_get_elapsed_time: - * @timeline: A #GtkTimeline - * - * Returns the elapsed time since the last GtkTimeline::frame signal - * - * Return Value: elapsed time in milliseconds since the last frame - */ -guint -gtk_timeline_get_elapsed_time (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0); - - priv = timeline->priv; - return priv->elapsed_time; -} - -/** - * gtk_timeline_get_loop: - * @timeline: A #GtkTimeline - * - * Returns whether the timeline loops to the - * beginning when it has reached the end. - * - * Return Value: %TRUE if the timeline loops - */ -gboolean -gtk_timeline_get_loop (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), FALSE); - - priv = timeline->priv; - return priv->loop; -} - -/** - * gtk_timeline_set_loop: - * @timeline: A #GtkTimeline - * @loop: %TRUE to make the timeline loop - * - * Sets whether the timeline loops to the beginning - * when it has reached the end. - */ -void -gtk_timeline_set_loop (GtkTimeline *timeline, - gboolean loop) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - - if (loop != priv->loop) - { - priv->loop = loop; - g_object_notify (G_OBJECT (timeline), "loop"); - } -} - -void -gtk_timeline_set_duration (GtkTimeline *timeline, - guint duration) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - - if (duration != priv->duration) - { - priv->duration = duration; - g_object_notify (G_OBJECT (timeline), "duration"); - } -} - -guint -gtk_timeline_get_duration (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0); - - priv = timeline->priv; - - return priv->duration; -} - -/** - * gtk_timeline_set_direction: - * @timeline: A #GtkTimeline - * @direction: direction - * - * Sets the direction of the timeline. - */ -void -gtk_timeline_set_direction (GtkTimeline *timeline, - GtkTimelineDirection direction) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - priv->direction = direction; -} - -/* - * gtk_timeline_get_direction: - * @timeline: A #GtkTimeline - * - * Returns the direction of the timeline. - * - * Return Value: direction - */ -GtkTimelineDirection -gtk_timeline_get_direction (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_DIRECTION_FORWARD); - - priv = timeline->priv; - 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"); -} - -/** - * gtk_timeline_get_frame_clock: - * - * Returns: (transfer none): - */ -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 -gtk_timeline_set_screen (GtkTimeline *timeline, - GdkScreen *screen) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - g_return_if_fail (screen == NULL || GDK_IS_SCREEN (screen)); - - priv = timeline->priv; - - if (screen == priv->screen) - return; - - if (priv->screen) - g_object_unref (priv->screen); - - priv->screen = screen; - - if (priv->screen) - g_object_ref (priv->screen); - - g_object_notify (G_OBJECT (timeline), "screen"); -} - -/** - * gtk_timeline_get_screen: - * - * Returns: (transfer none): - */ -GdkScreen * -gtk_timeline_get_screen (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), NULL); - - priv = timeline->priv; - 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"); -} - -/** - * gtk_timeline_get_widget: - * - * Returns: (transfer none): - */ -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 -gtk_timeline_get_progress (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), 0.); - - priv = timeline->priv; - return calculate_progress (priv->progress, priv->progress_type); -} - -GtkTimelineProgressType -gtk_timeline_get_progress_type (GtkTimeline *timeline) -{ - GtkTimelinePriv *priv; - - g_return_val_if_fail (GTK_IS_TIMELINE (timeline), GTK_TIMELINE_PROGRESS_LINEAR); - - priv = timeline->priv; - return priv->progress_type; -} - -void -gtk_timeline_set_progress_type (GtkTimeline *timeline, - GtkTimelineProgressType progress_type) -{ - GtkTimelinePriv *priv; - - g_return_if_fail (GTK_IS_TIMELINE (timeline)); - - priv = timeline->priv; - priv->progress_type = progress_type; -} diff --git a/gtk/gtktimeline.h b/gtk/gtktimeline.h deleted file mode 100644 index 5a69e43e5f..0000000000 --- a/gtk/gtktimeline.h +++ /dev/null @@ -1,119 +0,0 @@ -/* GTK - The GIMP Toolkit - * Copyright (C) 2007 Carlos Garnacho <carlos@imendio.com> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. - * - * You should have received a copy of the GNU Library General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - */ - -#ifndef __GTK_TIMELINE_H__ -#define __GTK_TIMELINE_H__ - -#include <glib-object.h> -#include <gtk/gtkenums.h> -#include <gtk/gtkwidget.h> -#include <gdk/gdk.h> - -G_BEGIN_DECLS - -#define GTK_TYPE_TIMELINE (gtk_timeline_get_type ()) -#define GTK_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GTK_TYPE_TIMELINE, GtkTimeline)) -#define GTK_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GTK_TYPE_TIMELINE, GtkTimelineClass)) -#define GTK_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GTK_TYPE_TIMELINE)) -#define GTK_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GTK_TYPE_TIMELINE)) -#define GTK_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GTK_TYPE_TIMELINE, GtkTimelineClass)) - -typedef struct GtkTimeline GtkTimeline; -typedef struct GtkTimelineClass GtkTimelineClass; - -typedef enum { - GTK_TIMELINE_DIRECTION_FORWARD, - GTK_TIMELINE_DIRECTION_BACKWARD -} GtkTimelineDirection; - -typedef enum { - GTK_TIMELINE_PROGRESS_LINEAR, - GTK_TIMELINE_PROGRESS_EASE, - GTK_TIMELINE_PROGRESS_EASE_IN, - GTK_TIMELINE_PROGRESS_EASE_OUT, - GTK_TIMELINE_PROGRESS_EASE_IN_OUT -} GtkTimelineProgressType; - -struct GtkTimeline -{ - GObject parent_instance; - gpointer priv; -}; - -struct GtkTimelineClass -{ - GObjectClass parent_class; - - void (* started) (GtkTimeline *timeline); - void (* finished) (GtkTimeline *timeline); - void (* paused) (GtkTimeline *timeline); - - void (* frame) (GtkTimeline *timeline, - gdouble progress); - - void (* __gtk_reserved1) (void); - void (* __gtk_reserved2) (void); - void (* __gtk_reserved3) (void); - void (* __gtk_reserved4) (void); -}; - - -GType gtk_timeline_get_type (void) G_GNUC_CONST; - -GtkTimeline * gtk_timeline_new (GtkWidget *widget, - guint duration); - -void gtk_timeline_start (GtkTimeline *timeline); -void gtk_timeline_pause (GtkTimeline *timeline); -void gtk_timeline_rewind (GtkTimeline *timeline); - -gboolean gtk_timeline_is_running (GtkTimeline *timeline); -guint gtk_timeline_get_elapsed_time (GtkTimeline *timeline); - -gboolean gtk_timeline_get_loop (GtkTimeline *timeline); -void gtk_timeline_set_loop (GtkTimeline *timeline, - gboolean loop); - -guint gtk_timeline_get_duration (GtkTimeline *timeline); -void gtk_timeline_set_duration (GtkTimeline *timeline, - 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); -void gtk_timeline_set_screen (GtkTimeline *timeline, - 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); -void gtk_timeline_set_direction (GtkTimeline *timeline, - GtkTimelineDirection direction); - -gdouble gtk_timeline_get_progress (GtkTimeline *timeline); - -GtkTimelineProgressType gtk_timeline_get_progress_type (GtkTimeline *timeline); -void gtk_timeline_set_progress_type (GtkTimeline *timeline, - GtkTimelineProgressType progress_type); - -G_END_DECLS - -#endif /* _GTK_TIMELINE_H__ */ diff --git a/gtk/gtkwidget.c b/gtk/gtkwidget.c index 1c7e538245..4b460661ec 100644 --- a/gtk/gtkwidget.c +++ b/gtk/gtkwidget.c @@ -410,7 +410,7 @@ struct _GtkWidgetPrivate GtkWidget *parent; /* Animations and other things to update on clock ticks */ - GList *frame_clock_targets; + GList *tick_callbacks; #ifdef G_ENABLE_DEBUG /* Number of gtk_widget_push_verify_invariants () */ @@ -713,6 +713,10 @@ static void gtk_widget_set_device_enabled_internal (GtkWidget *widget, GdkDevice *device, gboolean recurse, gboolean enabled); + +static void gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock, + GtkWidget *widget); + static gboolean event_window_is_still_viewable (GdkEvent *event); static void gtk_cairo_set_event (cairo_t *cr, GdkEventExpose *event); @@ -4508,6 +4512,216 @@ gtk_widget_update_devices_mask (GtkWidget *widget, gtk_widget_set_device_enabled_internal (widget, GDK_DEVICE (l->data), recurse, TRUE); } +typedef struct _GtkTickCallbackInfo GtkTickCallbackInfo; + +struct _GtkTickCallbackInfo +{ + guint refcount; + + guint id; + GtkTickCallback callback; + gpointer user_data; + GDestroyNotify notify; + + guint destroyed : 1; +}; + +static void +ref_tick_callback_info (GtkTickCallbackInfo *info) +{ + info->refcount++; +} + +static void +unref_tick_callback_info (GtkWidget *widget, + GtkTickCallbackInfo *info, + GList *link) +{ + GtkWidgetPrivate *priv = widget->priv; + + info->refcount--; + if (info->refcount == 0) + { + priv->tick_callbacks = g_list_delete_link (priv->tick_callbacks, link); + if (info->notify) + info->notify (info->user_data); + g_slice_free (GtkTickCallbackInfo, info); + } + + if (priv->tick_callbacks == NULL && priv->realized) + { + GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget); + g_signal_handlers_disconnect_by_func (frame_clock, + (gpointer) gtk_widget_on_frame_clock_update, + widget); + } +} + +static void +destroy_tick_callback_info (GtkWidget *widget, + GtkTickCallbackInfo *info, + GList *link) +{ + if (!info->destroyed) + { + info->destroyed = TRUE; + unref_tick_callback_info (widget, info, link); + } +} + +static void +gtk_widget_on_frame_clock_update (GdkFrameClock *frame_clock, + GtkWidget *widget) +{ + GtkWidgetPrivate *priv = widget->priv; + GList *l; + + for (l = priv->tick_callbacks; l;) + { + GtkTickCallbackInfo *info = l->data; + GList *next; + + ref_tick_callback_info (info); + if (!info->destroyed) + { + if (!info->callback (widget, + frame_clock, + info->user_data)) + { + destroy_tick_callback_info (widget, info, l); + } + } + + next = l->next; + unref_tick_callback_info (widget, info, l); + l = next; + } + + if (priv->tick_callbacks != NULL) + gdk_frame_clock_request_phase (frame_clock, + GDK_FRAME_CLOCK_PHASE_UPDATE); +} + +static guint tick_callback_id; + +/** + * gtk_widget_add_tick_callback: + * @widget: a #GtkWidget + * @callback: function to call for updating animations + * @user_data: data to pass to @callback + * @notify: function to call to free @user_data when the callback is removed. + * + * Queues a animation frame update and adds a callback to be called + * before each frame. Until the tick callback is removed, it will be + * called frequently (usually at the frame rate of the output device + * or as quickly as the application an be repainted, whichever is + * slower). For this reason, is most suitable for handling graphics + * that change every frame or every few frames. The tick callback does + * not automatically imply a relayout or repaint. If you want a + * repaint or relayout, and aren't changing widget properties that + * would trigger that (for example, changing the text of a #GtkLabel), + * then you will have to call gtk_widget_queue_resize() or + * gtk_widget_queue_draw_area() yourself. + * + * gtk_frame_clock_get_frame_time() should generally be used for timing + * continuous animations and + * gtk_frame_timings_get_predicted_presentation_time() if you are + * trying to display isolated frames particular times. + * + * This is a more convenient alternative to connecting directly to the + * ::update signal of GdkFrameClock, since you don't have to worry about + * when a #GdkFrameClock is assigned to a widget. + */ +guint +gtk_widget_add_tick_callback (GtkWidget *widget, + GtkTickCallback callback, + gpointer user_data, + GDestroyNotify notify) +{ + GtkWidgetPrivate *priv; + GtkTickCallbackInfo *info; + + g_return_val_if_fail (GTK_IS_WIDGET (widget), 0); + + priv = widget->priv; + + if (priv->tick_callbacks == NULL && priv->realized) + { + GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget); + g_signal_connect (frame_clock, "update", + G_CALLBACK (gtk_widget_on_frame_clock_update), + widget); + gdk_frame_clock_request_phase (frame_clock, + GDK_FRAME_CLOCK_PHASE_UPDATE); + } + + info = g_slice_new (GtkTickCallbackInfo); + + info->refcount = 1; + info->id = ++tick_callback_id; + info->callback = callback; + info->user_data = user_data; + info->notify = notify; + + priv->tick_callbacks = g_list_prepend (priv->tick_callbacks, + info); + + return info->id; +} + +void +gtk_widget_remove_tick_callback (GtkWidget *widget, + guint id) +{ + GtkWidgetPrivate *priv; + GList *l; + + g_return_if_fail (GTK_IS_WIDGET (widget)); + + priv = widget->priv; + + for (l = priv->tick_callbacks; l; l = l->next) + { + GtkTickCallbackInfo *info = l->data; + if (info->id == id) + destroy_tick_callback_info (widget, info, l); + } +} + +static void +gtk_widget_connect_frame_clock (GtkWidget *widget, + GdkFrameClock *frame_clock) +{ + GtkWidgetPrivate *priv = widget->priv; + + if (priv->tick_callbacks != NULL) + { + g_signal_connect (frame_clock, "update", + G_CALLBACK (gtk_widget_on_frame_clock_update), + widget); + gdk_frame_clock_request_phase (frame_clock, + GDK_FRAME_CLOCK_PHASE_UPDATE); + } + + if (priv->context) + gtk_style_context_set_frame_clock (priv->context, frame_clock); +} + +static void +gtk_widget_disconnect_frame_clock (GtkWidget *widget, + GdkFrameClock *frame_clock) +{ + GtkWidgetPrivate *priv = widget->priv; + + if (priv->tick_callbacks) + g_signal_handlers_disconnect_by_func (frame_clock, + (gpointer) gtk_widget_on_frame_clock_update, + widget); + + if (priv->context) + gtk_style_context_set_frame_clock (priv->context, NULL); +} + /** * gtk_widget_realize: * @widget: a #GtkWidget @@ -4536,7 +4750,6 @@ gtk_widget_realize (GtkWidget *widget) { GtkWidgetPrivate *priv; cairo_region_t *region; - GList *tmp_list; g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (widget->priv->anchored || @@ -4592,11 +4805,8 @@ gtk_widget_realize (GtkWidget *widget) if (GTK_IS_CONTAINER (widget)) _gtk_container_maybe_start_idle_sizer (GTK_CONTAINER (widget)); - for (tmp_list = priv->frame_clock_targets; tmp_list; tmp_list = tmp_list->next) - { - GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (widget); - gdk_frame_clock_target_set_clock (tmp_list->data, frame_clock); - } + gtk_widget_connect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); gtk_widget_pop_verify_invariants (widget); } @@ -4630,6 +4840,9 @@ gtk_widget_unrealize (GtkWidget *widget) if (widget->priv->mapped) gtk_widget_unmap (widget); + gtk_widget_disconnect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); + g_signal_emit (widget, widget_signals[UNREALIZE], 0); g_assert (!widget->priv->mapped); gtk_widget_set_realized (widget, FALSE); @@ -4817,7 +5030,7 @@ gtk_widget_queue_resize_no_redraw (GtkWidget *widget) * Unrealized widgets do not have a frame clock. * * Since: 3.0 - * Return value: a #GdkFrameClock (or #NULL if widget is unrealized) + * Return value: (transfer none): a #GdkFrameClock (or #NULL if widget is unrealized) */ GdkFrameClock* gtk_widget_get_frame_clock (GtkWidget *widget) @@ -4826,10 +5039,16 @@ gtk_widget_get_frame_clock (GtkWidget *widget) if (widget->priv->realized) { - GdkWindow *window; - - window = gtk_widget_get_window (widget); + /* We use gtk_widget_get_toplevel() here to make it explicit that + * the frame clock is a property of the toplevel that a widget + * is anchored to; gdk_window_get_toplevel() will go up the + * hierarchy anyways, but should squash any funny business with + * reparenting windows and widgets. + */ + GtkWidget *toplevel = gtk_widget_get_toplevel (widget); + GdkWindow *window = gtk_widget_get_window (toplevel); g_assert (window != NULL); + return gdk_window_get_frame_clock (window); } else @@ -8514,6 +8733,17 @@ gtk_widget_propagate_hierarchy_changed_recurse (GtkWidget *widget, priv->anchored = new_anchored; + /* This can only happen with gtk_widget_reparent() */ + if (priv->realized) + { + if (new_anchored) + gtk_widget_connect_frame_clock (widget, + gtk_widget_get_frame_clock (widget)); + else + gtk_widget_disconnect_frame_clock (widget, + gtk_widget_get_frame_clock (info->previous_toplevel)); + } + g_signal_emit (widget, widget_signals[HIERARCHY_CHANGED], 0, info->previous_toplevel); do_screen_change (widget, info->previous_screen, info->new_screen); @@ -10522,6 +10752,7 @@ gtk_widget_real_destroy (GtkWidget *object) /* gtk_object_destroy() will already hold a refcount on object */ GtkWidget *widget = GTK_WIDGET (object); GtkWidgetPrivate *priv = widget->priv; + GList *l; if (GTK_WIDGET_GET_CLASS (widget)->priv->accessible_type != GTK_TYPE_ACCESSIBLE) { @@ -10543,9 +10774,12 @@ gtk_widget_real_destroy (GtkWidget *object) gtk_grab_remove (widget); - g_list_foreach (priv->frame_clock_targets, (GFunc)g_object_unref, NULL); - g_list_free (priv->frame_clock_targets); - priv->frame_clock_targets = NULL; + for (l = priv->tick_callbacks; l;) + { + GList *next = l->next; + destroy_tick_callback_info (widget, l->data, l); + l = next; + } if (priv->style) g_object_unref (priv->style); @@ -14461,6 +14695,7 @@ gtk_widget_get_style_context (GtkWidget *widget) if (G_UNLIKELY (priv->context == NULL)) { GdkScreen *screen; + GdkFrameClock *frame_clock; priv->context = gtk_style_context_new (); @@ -14470,6 +14705,10 @@ gtk_widget_get_style_context (GtkWidget *widget) if (screen) gtk_style_context_set_screen (priv->context, screen); + frame_clock = gtk_widget_get_frame_clock (widget); + if (frame_clock) + gtk_style_context_set_frame_clock (priv->context, frame_clock); + if (priv->parent) gtk_style_context_set_parent (priv->context, gtk_widget_get_style_context (priv->parent)); @@ -14596,65 +14835,3 @@ gtk_widget_insert_action_group (GtkWidget *widget, else g_action_muxer_remove (muxer, name); } - -/** - * gtk_widget_add_frame_clock_target: - * @widget: a #GtkWidget - * @target: the #GdkClockTarget - * - * Associates a #GdkClockTarget with the widget. When the widget - * is realized and gets a #GdkFrameClock the clock target will be - * added to that frame clock. - */ -void -gtk_widget_add_frame_clock_target (GtkWidget *widget, - GdkFrameClockTarget *target) -{ - GtkWidgetPrivate *priv; - priv = widget->priv; - - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (GDK_IS_FRAME_CLOCK_TARGET (target)); - - priv->frame_clock_targets = g_list_prepend (priv->frame_clock_targets, target); - g_object_ref (target); - - if (gtk_widget_get_realized (widget)) - { - GdkFrameClock *clock; - clock = gtk_widget_get_frame_clock (widget); - gdk_frame_clock_target_set_clock (target, clock); - } -} - -/** - * gtk_widget_remove_frame_clock_target: - * @widget: a #GtkWidget - * @target: the #GdkClockTarget - * - * Removes a #GdkClockTarget previously added with - * gtk_widget_add_frame_clock_target. - */ -void -gtk_widget_remove_frame_clock_target (GtkWidget *widget, - GdkFrameClockTarget *target) -{ - GtkWidgetPrivate *priv; - GList *tmp_list; - - g_return_if_fail (GTK_IS_WIDGET (widget)); - g_return_if_fail (GDK_IS_FRAME_CLOCK_TARGET (target)); - - priv = widget->priv; - - tmp_list = g_list_find (priv->frame_clock_targets, target); - if (tmp_list == NULL) - return; - - priv->frame_clock_targets = g_list_delete_link (priv->frame_clock_targets, tmp_list); - - if (gtk_widget_get_realized (widget)) - gdk_frame_clock_target_set_clock (target, NULL); - - g_object_unref (target); -} diff --git a/gtk/gtkwidget.h b/gtk/gtkwidget.h index 9f12bf94cb..c082c13510 100644 --- a/gtk/gtkwidget.h +++ b/gtk/gtkwidget.h @@ -87,6 +87,21 @@ typedef void (*GtkCallback) (GtkWidget *widget, gpointer data); /** + * GtkTickCallback: + * @widget: the widget + * @frame_clock: the frame clock for the widget (same as calling gtk_widget_get_frame_clock()) + * @user_data: user data passed to gtk_widget_add_tick_callback(). + * + * Callback type for adding a function to update animations. See gtk_widget_add_tick_callback(). + * + * Returns: %TRUE if the tick callback should continue to be called, + * %FALSE if the tick callback should be removed. + */ +typedef gboolean (*GtkTickCallback) (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer user_data); + +/** * GtkRequisition: * @width: the widget's desired width * @height: the widget's desired height @@ -905,12 +920,17 @@ void gtk_widget_insert_action_group (GtkWidg const gchar *name, GActionGroup *group); -GDK_AVAILABLE_IN_3_2 -void gtk_widget_add_frame_clock_target (GtkWidget *widget, - GdkFrameClockTarget *target); -GDK_AVAILABLE_IN_3_2 -void gtk_widget_remove_frame_clock_target (GtkWidget *widget, - GdkFrameClockTarget *target); + + +GDK_AVAILABLE_IN_3_8 +guint gtk_widget_add_tick_callback (GtkWidget *widget, + GtkTickCallback callback, + gpointer user_data, + GDestroyNotify notify); + +GDK_AVAILABLE_IN_3_8 +void gtk_widget_remove_tick_callback (GtkWidget *widget, + guint id); G_END_DECLS diff --git a/tests/animated-resizing.c b/tests/animated-resizing.c index f2bbc1013c..9aaa3662cf 100644 --- a/tests/animated-resizing.c +++ b/tests/animated-resizing.c @@ -16,6 +16,7 @@ static GtkWidget *window; static int window_width = WIDTH, window_height = HEIGHT; +gint64 start_frame_time; static double angle; static int max_stats = -1; @@ -213,8 +214,7 @@ handle_frame_stats (GdkFrameHistory *frame_history) } static void -on_frame (GtkTimeline *timeline, - double progress) +on_frame (double progress) { GdkFrameClock *frame_clock = gtk_widget_get_frame_clock (window); int jitter; @@ -241,11 +241,27 @@ on_frame (GtkTimeline *timeline, } static gboolean +tick_callback (GtkWidget *widget, + GdkFrameClock *frame_clock, + gpointer user_data) +{ + gint64 frame_time = gdk_frame_clock_get_frame_time (frame_clock); + double scaled_time; + + if (start_frame_time == 0) + start_frame_time = frame_time; + + scaled_time = (frame_time - start_frame_time) / (CYCLE_TIME * 1000000); + on_frame (scaled_time - floor (scaled_time)); + + return TRUE; +} + +static gboolean on_map_event (GtkWidget *widget, - GdkEventAny *event, - GtkTimeline *timeline) + GdkEventAny *event) { - gtk_timeline_start (timeline); + gtk_widget_add_tick_callback (window, tick_callback, NULL, NULL); return FALSE; } @@ -265,7 +281,6 @@ main(int argc, char **argv) GError *error = NULL; GdkScreen *screen; GdkRectangle monitor_bounds; - GtkTimeline *timeline; if (!gtk_init_with_args (&argc, &argv, "", options, NULL, &error)) @@ -291,15 +306,9 @@ main(int argc, char **argv) g_signal_connect (window, "destroy", G_CALLBACK (gtk_main_quit), NULL); - timeline = gtk_timeline_new (window, CYCLE_TIME * 1000); - gtk_timeline_set_loop (timeline, TRUE); - gtk_timeline_set_progress_type (timeline, GTK_TIMELINE_PROGRESS_LINEAR); - - g_signal_connect (timeline, "frame", - G_CALLBACK (on_frame), NULL); g_signal_connect (window, "map-event", - G_CALLBACK (on_map_event), timeline); - on_frame (timeline, 0.); + G_CALLBACK (on_map_event), NULL); + on_frame (0.); screen = gtk_widget_get_screen (window); gdk_screen_get_monitor_geometry (screen, |