]> Pileus Git - ~andy/gtk/commitdiff
stylecontext: Add an animating framework
authorBenjamin Otte <otte@redhat.com>
Tue, 10 Apr 2012 13:37:35 +0000 (15:37 +0200)
committerBenjamin Otte <otte@redhat.com>
Tue, 17 Apr 2012 06:59:22 +0000 (08:59 +0200)
The design principles were:

- synchronized
If multiple style contexts are animating, they should all do an
animation step at the same time.

- degrades well
Even when there's thousands of style contexts all animating at the same
time, the animation steps don't starve the CPU. This is achieved by
making sure the timeout is really fast. It just sets a bunch of flags.

- no hidden bottlenecks
Turning animatability on or off on a style context is O(1).

So far it is unused.

gtk/gtkcsstypes.c
gtk/gtkcsstypesprivate.h
gtk/gtkstylecontext.c

index f83880afab7964215ee7f117d8897350f4a33d2b..b69b0feccafb591fdb0bc30be7098400f2b2b270 100644 (file)
@@ -56,7 +56,8 @@ _gtk_css_change_for_sibling (GtkCssChange match)
     { GTK_CSS_CHANGE_NAME, GTK_CSS_CHANGE_SIBLING_NAME },
     { GTK_CSS_CHANGE_POSITION, GTK_CSS_CHANGE_SIBLING_POSITION },
     { GTK_CSS_CHANGE_STATE, GTK_CSS_CHANGE_SIBLING_STATE },
-    { GTK_CSS_CHANGE_SOURCE, 0 }
+    { GTK_CSS_CHANGE_SOURCE, 0 },
+    { GTK_CSS_CHANGE_ANIMATE, 0 }
   };
 
   return gtk_css_change_translate (match, table, G_N_ELEMENTS (table)); 
@@ -75,6 +76,7 @@ _gtk_css_change_for_child (GtkCssChange match)
     { GTK_CSS_CHANGE_SIBLING_POSITION, GTK_CSS_CHANGE_PARENT_SIBLING_POSITION },
     { GTK_CSS_CHANGE_SIBLING_STATE, GTK_CSS_CHANGE_PARENT_SIBLING_STATE },
     { GTK_CSS_CHANGE_SOURCE, 0 },
+    { GTK_CSS_CHANGE_ANIMATE, 0 }
   };
 
   return gtk_css_change_translate (match, table, G_N_ELEMENTS (table)); 
index 8890a5cce90aa10555e052e6f5a838c4a3e6f52d..b0db18d0b9bd4e384355a4c8e175c3692b642267 100644 (file)
@@ -43,10 +43,11 @@ typedef enum { /*< skip >*/
   GTK_CSS_CHANGE_PARENT_SIBLING_POSITION  = (1 << 14),
   GTK_CSS_CHANGE_PARENT_SIBLING_STATE     = (1 << 15),
   /* add more */
-  GTK_CSS_CHANGE_SOURCE                   = (1 << 16)
+  GTK_CSS_CHANGE_SOURCE                   = (1 << 16),
+  GTK_CSS_CHANGE_ANIMATE                  = (1 << 17)
 } GtkCssChange;
 
-#define GTK_CSS_CHANGE_ANY ((1 << 17) - 1)
+#define GTK_CSS_CHANGE_ANY ((1 << 18) - 1)
 #define GTK_CSS_CHANGE_ANY_SELF (GTK_CSS_CHANGE_CLASS | GTK_CSS_CHANGE_NAME | GTK_CSS_CHANGE_POSITION | GTK_CSS_CHANGE_STATE)
 #define GTK_CSS_CHANGE_ANY_SIBLING (GTK_CSS_CHANGE_SIBLING_CLASS | GTK_CSS_CHANGE_SIBLING_NAME | \
                                     GTK_CSS_CHANGE_SIBLING_POSITION | GTK_CSS_CHANGE_SIBLING_STATE)
index 0ac8eb3a96747ace09de41c4e6a3b07c357c9981..8375328f94b91d6e10bfebe80a0375fef2d8d15d 100644 (file)
@@ -350,6 +350,9 @@ struct _GtkStyleContextPrivate
 
   GtkStyleCascade *cascade;
 
+  GtkStyleContext *animation_list_prev;
+  GtkStyleContext *animation_list_next;
+
   GtkStyleContext *parent;
   GSList *children;
   GtkWidget *widget;            
@@ -379,6 +382,8 @@ enum {
 };
 
 static guint signals[LAST_SIGNAL] = { 0 };
+static GtkStyleContext *_running_animations = NULL;
+guint _running_animations_timer_id = 0;
 
 static void gtk_style_context_finalize (GObject *object);
 
@@ -675,6 +680,82 @@ gtk_style_context_init (GtkStyleContext *style_context)
                                  _gtk_style_cascade_get_for_screen (priv->screen));
 }
 
+static gboolean
+gtk_style_context_do_animations (gpointer unused)
+{
+  GtkStyleContext *context;
+
+  for (context = _running_animations;
+       context != NULL;
+       context = context->priv->animation_list_next)
+    {
+      _gtk_style_context_queue_invalidate (context, GTK_CSS_CHANGE_ANIMATE);
+    }
+
+  return TRUE;
+}
+
+static gboolean
+gtk_style_context_is_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  return priv->animation_list_prev != NULL
+      || _running_animations == context;
+}
+
+static void
+gtk_style_context_stop_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (!gtk_style_context_is_animating (context))
+    return;
+
+  if (priv->animation_list_prev == NULL)
+    {
+      _running_animations = priv->animation_list_next;
+
+      if (_running_animations == NULL)
+        {
+          /* we were the last animation */
+          g_source_remove (_running_animations_timer_id);
+          _running_animations_timer_id = 0;
+        }
+    }
+  else
+    priv->animation_list_prev->priv->animation_list_next = priv->animation_list_next;
+
+  if (priv->animation_list_next)
+    priv->animation_list_next->priv->animation_list_prev = priv->animation_list_prev;
+
+  priv->animation_list_next = NULL;
+  priv->animation_list_prev = NULL;
+}
+
+static void G_GNUC_UNUSED
+gtk_style_context_start_animating (GtkStyleContext *context)
+{
+  GtkStyleContextPrivate *priv = context->priv;
+
+  if (gtk_style_context_is_animating (context))
+    return;
+
+  if (_running_animations == NULL)
+    {
+      _running_animations_timer_id = gdk_threads_add_timeout (25,
+                                                              gtk_style_context_do_animations,
+                                                              NULL);
+      _running_animations = context;
+    }
+  else
+    {
+      priv->animation_list_next = _running_animations;
+      _running_animations->priv->animation_list_prev = context;
+      _running_animations = context;
+    }
+}
+
 static void
 gtk_style_context_finalize (GObject *object)
 {
@@ -684,6 +765,8 @@ gtk_style_context_finalize (GObject *object)
   style_context = GTK_STYLE_CONTEXT (object);
   priv = style_context->priv;
 
+  gtk_style_context_stop_animating (style_context);
+
   /* children hold a reference to us */
   g_assert (priv->children == NULL);