[gnome-shell] [St] Add StThemeNodeTransition



commit 0deffbaaf05bff5da2ca987546d4539887fea4cb
Author: Florian Müllner <fmuellner gnome org>
Date:   Tue May 25 16:10:37 2010 +0200

    [St] Add StThemeNodeTransition
    
    Introduce a small helper class in order to add transitions between
    theme nodes to StWidget.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=619025
    transition update

 src/Makefile-st.am                |    4 +-
 src/st/st-theme-node-transition.c |  411 +++++++++++++++++++++++++++++++++++++
 src/st/st-theme-node-transition.h |   51 +++++
 3 files changed, 465 insertions(+), 1 deletions(-)
---
diff --git a/src/Makefile-st.am b/src/Makefile-st.am
index b3cc7aa..5cefa7b 100644
--- a/src/Makefile-st.am
+++ b/src/Makefile-st.am
@@ -110,7 +110,8 @@ st_source_private_h =				\
     st/st-private.h				\
     st/st-table-private.h			\
     st/st-theme-private.h			\
-    st/st-theme-node-private.h
+    st/st-theme-node-private.h			\
+    st/st-theme-node-transition.h
 
 # please, keep this sorted alphabetically
 st_source_c =					\
@@ -142,6 +143,7 @@ st_source_c =					\
     st/st-theme-context.c			\
     st/st-theme-node.c				\
     st/st-theme-node-drawing.c		\
+    st/st-theme-node-transition.c		\
     st/st-tooltip.c				\
     st/st-widget.c				\
     $(NULL)
diff --git a/src/st/st-theme-node-transition.c b/src/st/st-theme-node-transition.c
new file mode 100644
index 0000000..379d04d
--- /dev/null
+++ b/src/st/st-theme-node-transition.c
@@ -0,0 +1,411 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+/* Theme node transitions for StWidget.
+ *
+ * Copyright (C) 2010 Florian Müllner <fmuellner gnome org>
+ *
+ * The St 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.
+ *
+ * The St 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 the St; see the file COPYING.LIB.
+ * If not, write to the Free Software Foundation, Inc., 59 Temple Place -
+ * Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+#include "st-theme-node-transition.h"
+
+enum {
+  COMPLETED,
+  NEW_FRAME,
+  LAST_SIGNAL
+};
+
+#define ST_THEME_NODE_TRANSITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionPrivate))
+
+struct _StThemeNodeTransitionPrivate {
+  StThemeNode *old_theme_node;
+  StThemeNode *new_theme_node;
+
+  CoglHandle *old_texture;
+  CoglHandle *new_texture;
+
+  CoglHandle *old_offscreen;
+  CoglHandle *new_offscreen;
+
+  CoglHandle *material;
+
+  ClutterAlpha    *alpha;
+  ClutterTimeline *timeline;
+
+  guint timeline_completed_id;
+  guint timeline_new_frame_id;
+
+  ClutterActorBox last_allocation;
+  ClutterActorBox offscreen_box;
+  guint8          last_opacity;
+
+  gboolean needs_setup;
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (StThemeNodeTransition, st_theme_node_transition, G_TYPE_OBJECT);
+
+
+static void
+on_timeline_completed (ClutterTimeline       *timeline,
+                       StThemeNodeTransition *transition)
+{
+  g_signal_emit (transition, signals[COMPLETED], 0);
+}
+
+static void
+on_timeline_new_frame (ClutterTimeline       *timeline,
+                       gint                   frame_num,
+                       StThemeNodeTransition *transition)
+{
+  g_signal_emit (transition, signals[NEW_FRAME], 0);
+}
+
+StThemeNodeTransition *
+st_theme_node_transition_new (StThemeNode *from_node,
+                              StThemeNode *to_node,
+                              guint        duration)
+{
+  StThemeNodeTransition *transition;
+
+  g_return_val_if_fail (ST_IS_THEME_NODE (from_node), NULL);
+  g_return_val_if_fail (ST_IS_THEME_NODE (to_node), NULL);
+
+  duration = st_theme_node_get_transition_duration (to_node);
+
+  transition = g_object_new (ST_TYPE_THEME_NODE_TRANSITION,
+                             NULL);
+
+  transition->priv->old_theme_node = g_object_ref (from_node);
+  transition->priv->new_theme_node = g_object_ref (to_node);
+
+  transition->priv->alpha = clutter_alpha_new ();
+  transition->priv->timeline = clutter_timeline_new (duration);
+
+  transition->priv->timeline_completed_id =
+    g_signal_connect (transition->priv->timeline, "completed",
+                      G_CALLBACK (on_timeline_completed), transition);
+  transition->priv->timeline_new_frame_id =
+    g_signal_connect (transition->priv->timeline, "new-frame",
+                      G_CALLBACK (on_timeline_new_frame), transition);
+
+  clutter_alpha_set_mode (transition->priv->alpha, CLUTTER_EASE_IN_OUT_QUAD);
+  clutter_alpha_set_timeline (transition->priv->alpha,
+                              transition->priv->timeline);
+
+  clutter_timeline_start (transition->priv->timeline);
+
+  return transition;
+}
+
+void
+st_theme_node_transition_update (StThemeNodeTransition *transition,
+                                 StThemeNode           *new_node)
+{
+  StThemeNodeTransitionPrivate *priv = transition->priv;
+  StThemeNode *old_node;
+  ClutterTimelineDirection direction;
+
+  g_return_if_fail (ST_IS_THEME_NODE_TRANSITION (transition));
+  g_return_if_fail (ST_IS_THEME_NODE (new_node));
+
+  direction = clutter_timeline_get_direction (priv->timeline);
+  old_node = (direction == CLUTTER_TIMELINE_FORWARD) ? priv->old_theme_node
+                                                     : priv->new_theme_node;
+
+  /* If the update is the reversal of the current transition,
+   * we reverse the timeline.
+   * Otherwise, we should initiate a new transition from the
+   * current state to the new one; this is hard to do, so we
+   * just cancel the ongoing transition in that case.
+   * Note that reversing a timeline before any time elapsed
+   * results in the timeline's time position being set to the
+   * full duration - this is not what we want, so we cancel the
+   * transition as well in that case.
+   */
+  if (st_theme_node_equal (new_node, old_node)
+      && clutter_timeline_get_elapsed_time (priv->timeline) > 0)
+    {
+      if (direction == CLUTTER_TIMELINE_FORWARD)
+        clutter_timeline_set_direction (priv->timeline,
+                                        CLUTTER_TIMELINE_BACKWARD);
+      else
+        clutter_timeline_set_direction (priv->timeline,
+                                        CLUTTER_TIMELINE_FORWARD);
+    }
+  else
+    {
+      clutter_timeline_stop (priv->timeline);
+      g_signal_emit (transition, signals[COMPLETED], 0);
+    }
+}
+
+static void
+calculate_offscreen_box (StThemeNodeTransition *transition,
+                         const ClutterActorBox *allocation)
+{
+  StThemeNodeTransitionPrivate *priv = transition->priv;
+  ClutterActorBox old_node_box, new_node_box;
+
+  st_theme_node_get_paint_box (priv->old_theme_node,
+                               allocation,
+                               &old_node_box);
+
+  st_theme_node_get_paint_box (priv->new_theme_node,
+                               allocation,
+                               &new_node_box);
+
+  priv->offscreen_box.x1 = MIN (old_node_box.x1, new_node_box.x1)
+                           - allocation->x1;
+  priv->offscreen_box.y1 = MIN (old_node_box.y1, new_node_box.y1)
+                           - allocation->y1;
+  priv->offscreen_box.x2 = MAX (old_node_box.x2, new_node_box.x2)
+                           - allocation->x1;
+  priv->offscreen_box.y2 = MAX (old_node_box.y2, new_node_box.y2)
+                           - allocation->y1;
+}
+
+static void
+setup_framebuffers (StThemeNodeTransition *transition,
+                    const ClutterActorBox *allocation,
+                    guint8                 paint_opacity)
+{
+  StThemeNodeTransitionPrivate *priv = transition->priv;
+  CoglColor clear_color = { 0, 0, 0, 0 };
+  guint width, height;
+
+  width  = priv->offscreen_box.x2 - priv->offscreen_box.x1;
+  height = priv->offscreen_box.y2 - priv->offscreen_box.y1;
+
+  if (priv->old_texture)
+    cogl_handle_unref (priv->old_texture);
+  priv->old_texture = cogl_texture_new_with_size (width, height,
+                                                  COGL_TEXTURE_NO_SLICING,
+                                                  COGL_PIXEL_FORMAT_ANY);
+
+  if (priv->new_texture)
+    cogl_handle_unref (priv->new_texture);
+  priv->new_texture = cogl_texture_new_with_size (width, height,
+                                                  COGL_TEXTURE_NO_SLICING,
+                                                  COGL_PIXEL_FORMAT_ANY);
+
+  if (priv->old_offscreen)
+    cogl_handle_unref (priv->old_offscreen);
+  priv->old_offscreen = cogl_offscreen_new_to_texture (priv->old_texture);
+
+  if (priv->new_offscreen)
+    cogl_handle_unref (priv->new_offscreen);
+  priv->new_offscreen = cogl_offscreen_new_to_texture (priv->new_texture);
+
+  if (priv->material)
+    cogl_handle_unref (priv->material);
+  priv->material = cogl_material_new ();
+
+  cogl_material_set_layer_combine (priv->material, 1,
+                                   "RGBA = INTERPOLATE (PREVIOUS, "
+                                                       "TEXTURE, "
+                                                       "CONSTANT[A])",
+                                   NULL);
+
+  cogl_material_set_layer (priv->material, 0, priv->new_texture);
+  cogl_material_set_layer (priv->material, 1, priv->old_texture);
+
+  cogl_push_framebuffer (priv->old_offscreen);
+  cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
+  cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
+              priv->offscreen_box.y2, priv->offscreen_box.y1,
+              0.0, 1.0);
+  st_theme_node_paint (priv->old_theme_node,
+                       allocation,
+                       paint_opacity);
+  cogl_pop_framebuffer ();
+
+  cogl_push_framebuffer (priv->new_offscreen);
+  cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
+  cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
+              priv->offscreen_box.y2, priv->offscreen_box.y1,
+              0.0, 1.0);
+  st_theme_node_paint (priv->new_theme_node,
+                       allocation,
+                       paint_opacity);
+  cogl_pop_framebuffer ();
+}
+
+void
+st_theme_node_transition_paint (StThemeNodeTransition *transition,
+                                ClutterActorBox       *allocation,
+                                guint8                 paint_opacity)
+{
+  StThemeNodeTransitionPrivate *priv = transition->priv;
+
+  guint width, height;
+  CoglColor constant = { 0, 0, 0, 0 };
+  float tex_coords[] = {
+    0.0, 0.0, 1.0, 1.0,
+    0.0, 0.0, 1.0, 1.0,
+  };
+
+  g_return_if_fail (ST_IS_THEME_NODE (priv->old_theme_node));
+  g_return_if_fail (ST_IS_THEME_NODE (priv->new_theme_node));
+
+  if (!clutter_actor_box_equal (allocation, &priv->last_allocation) ||
+      paint_opacity != priv->last_opacity)
+    priv->needs_setup = TRUE;
+
+  if (priv->needs_setup)
+    {
+      priv->last_allocation = *allocation;
+      priv->last_opacity = paint_opacity;
+
+      calculate_offscreen_box (transition, allocation);
+      setup_framebuffers (transition, allocation, paint_opacity);
+
+      priv->needs_setup = FALSE;
+    }
+
+  width = cogl_texture_get_width (priv->old_texture);
+  height = cogl_texture_get_height (priv->old_texture);
+
+  constant.alpha = clutter_alpha_get_alpha (priv->alpha) * paint_opacity;
+
+  cogl_material_set_layer_combine_constant (priv->material, 1, &constant);
+
+  cogl_set_source (priv->material);
+  cogl_rectangle_with_multitexture_coords (priv->offscreen_box.x1,
+                                           priv->offscreen_box.y1,
+                                           priv->offscreen_box.x2,
+                                           priv->offscreen_box.y2,
+                                           tex_coords, 8);
+}
+
+static void
+st_theme_node_transition_dispose (GObject *object)
+{
+  StThemeNodeTransitionPrivate *priv = ST_THEME_NODE_TRANSITION (object)->priv;
+
+  if (priv->old_theme_node)
+    {
+      g_object_unref (priv->old_theme_node);
+      priv->old_theme_node = NULL;
+    }
+
+  if (priv->new_theme_node)
+    {
+      g_object_unref (priv->new_theme_node);
+      priv->new_theme_node = NULL;
+    }
+
+  if (priv->old_texture)
+    {
+      cogl_handle_unref (priv->old_texture);
+      priv->old_texture = NULL;
+    }
+
+  if (priv->new_texture)
+    {
+      cogl_handle_unref (priv->new_texture);
+      priv->new_texture = NULL;
+    }
+
+  if (priv->old_offscreen)
+    {
+      cogl_handle_unref (priv->old_offscreen);
+      priv->old_offscreen = NULL;
+    }
+
+  if (priv->new_offscreen)
+    {
+      cogl_handle_unref (priv->new_offscreen);
+      priv->new_offscreen = NULL;
+    }
+
+  if (priv->material)
+    {
+      cogl_handle_unref (priv->material);
+      priv->material = NULL;
+    }
+
+  if (priv->timeline)
+    {
+      if (priv->timeline_completed_id != 0)
+        g_signal_handler_disconnect (priv->timeline,
+                                     priv->timeline_completed_id);
+      if (priv->timeline_new_frame_id != 0)
+        g_signal_handler_disconnect (priv->timeline,
+                                     priv->timeline_new_frame_id);
+
+      g_object_unref (priv->timeline);
+      priv->timeline = NULL;
+    }
+
+  priv->timeline_completed_id = 0;
+  priv->timeline_new_frame_id = 0;
+
+  if (priv->alpha)
+    {
+      g_object_unref (priv->alpha);
+      priv->alpha = NULL;
+    }
+
+  G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object);
+}
+
+static void
+st_theme_node_transition_init (StThemeNodeTransition *transition)
+{
+  transition->priv = ST_THEME_NODE_TRANSITION_GET_PRIVATE (transition);
+
+  transition->priv->old_theme_node = NULL;
+  transition->priv->new_theme_node = NULL;
+
+  transition->priv->old_texture = NULL;
+  transition->priv->new_texture = NULL;
+
+  transition->priv->old_offscreen = NULL;
+  transition->priv->new_offscreen = NULL;
+
+  transition->priv->needs_setup = TRUE;
+
+  transition->priv->alpha = NULL;
+}
+
+static void
+st_theme_node_transition_class_init (StThemeNodeTransitionClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  g_type_class_add_private (klass, sizeof (StThemeNodeTransitionPrivate));
+
+  object_class->dispose = st_theme_node_transition_dispose;
+
+  signals[COMPLETED] =
+    g_signal_new ("completed",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (StThemeNodeTransitionClass, completed),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+
+  signals[NEW_FRAME] =
+    g_signal_new ("new-frame",
+                  G_TYPE_FROM_CLASS (klass),
+                  G_SIGNAL_RUN_LAST,
+                  G_STRUCT_OFFSET (StThemeNodeTransitionClass, new_frame),
+                  NULL, NULL,
+                  g_cclosure_marshal_VOID__VOID,
+                  G_TYPE_NONE, 0);
+}
diff --git a/src/st/st-theme-node-transition.h b/src/st/st-theme-node-transition.h
new file mode 100644
index 0000000..2f237b1
--- /dev/null
+++ b/src/st/st-theme-node-transition.h
@@ -0,0 +1,51 @@
+/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
+#ifndef __ST_THEME_NODE_TRANSITION_H__
+#define __ST_THEME_NODE_TRANSITION_H__
+
+#include <clutter/clutter.h>
+
+#include "st-widget.h"
+#include "st-theme-node.h"
+
+G_BEGIN_DECLS
+
+#define ST_TYPE_THEME_NODE_TRANSITION         (st_theme_node_transition_get_type ())
+#define ST_THEME_NODE_TRANSITION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransition))
+#define ST_IS_THEME_NODE_TRANSITION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_THEME_NODE_TRANSITION))
+#define ST_THEME_NODE_TRANSITION_CLASS(c)     (G_TYPE_CHECK_CLASS_CAST ((c),    ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionClass))
+#define ST_IS_THEME_NODE_TRANSITION_CLASS(c)  (G_TYPE_CHECK_CLASS_TYPE ((c),    ST_TYPE_THEME_NODE_TRANSITION))
+#define ST_THEME_NODE_TRANSITION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o),  ST_THEME_NODE_TRANSITION, StThemeNodeTransitionClass))
+
+typedef struct _StThemeNodeTransition        StThemeNodeTransition;
+typedef struct _StThemeNodeTransitionClass   StThemeNodeTransitionClass;
+typedef struct _StThemeNodeTransitionPrivate StThemeNodeTransitionPrivate;
+
+struct _StThemeNodeTransition {
+  GObject parent;
+
+  StThemeNodeTransitionPrivate *priv;
+};
+
+struct _StThemeNodeTransitionClass {
+  GObjectClass parent_class;
+
+  void (*completed) (StThemeNodeTransition *transition);
+  void (*new_frame) (StThemeNodeTransition *transition);
+};
+
+GType st_theme_node_transition_get_type (void) G_GNUC_CONST;
+
+StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node,
+                                                     StThemeNode *to_node,
+                                                     guint        duration);
+
+void  st_theme_node_transition_update   (StThemeNodeTransition *transition,
+                                         StThemeNode           *new_node);
+
+void  st_theme_node_transition_paint    (StThemeNodeTransition *transition,
+                                         ClutterActorBox       *allocation,
+                                         guint8                 paint_opacity);
+
+G_END_DECLS
+
+#endif



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]