[gnome-builder/wip/libide] libide: add theatrics helpers
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gnome-builder/wip/libide] libide: add theatrics helpers
- Date: Wed, 25 Feb 2015 20:21:55 +0000 (UTC)
commit a27b0d4dcc4675953d8726b3b29168b585e54cf2
Author: Christian Hergert <christian hergert me>
Date: Wed Feb 25 11:39:49 2015 -0800
libide: add theatrics helpers
libide/Makefile.am | 7 +
libide/theatrics/ide-animation.c | 1129 +++++++++++++++++++++++++++++++++++
libide/theatrics/ide-animation.h | 89 +++
libide/theatrics/ide-box-theatric.c | 336 +++++++++++
libide/theatrics/ide-box-theatric.h | 55 ++
libide/theatrics/ide-frame-source.c | 134 +++++
libide/theatrics/ide-frame-source.h | 32 +
7 files changed, 1782 insertions(+), 0 deletions(-)
---
diff --git a/libide/Makefile.am b/libide/Makefile.am
index 4253676..fda3bf6 100644
--- a/libide/Makefile.am
+++ b/libide/Makefile.am
@@ -189,6 +189,12 @@ libide_1_0_la_SOURCES = \
libide/ide-search-reducer.h \
libide/tasks/ide-load-directory-task.c \
libide/tasks/ide-load-directory-task.h \
+ libide/theatrics/ide-animation.c \
+ libide/theatrics/ide-animation.h \
+ libide/theatrics/ide-box-theatric.c \
+ libide/theatrics/ide-box-theatric.h \
+ libide/theatrics/ide-frame-source.c \
+ libide/theatrics/ide-frame-source.h \
libide/trie/trie.c \
libide/trie/trie.h \
libide/util/ide-cairo.c \
@@ -214,6 +220,7 @@ libide_1_0_la_includes = \
-I$(top_srcdir)/libide/pygobject \
-I$(top_srcdir)/libide/python \
-I$(top_srcdir)/libide/tasks \
+ -I$(top_srcdir)/libide/theatrics \
-I$(top_srcdir)/libide/util \
-I$(top_srcdir)/libide/xml \
$(CLANG_CFLAGS) \
diff --git a/libide/theatrics/ide-animation.c b/libide/theatrics/ide-animation.c
new file mode 100644
index 0000000..fc0b192
--- /dev/null
+++ b/libide/theatrics/ide-animation.c
@@ -0,0 +1,1129 @@
+/* ide-animation.c
+ *
+ * Copyright (C) 2013 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <glib/gi18n.h>
+#include <gobject/gvaluecollector.h>
+#include <gtk/gtk.h>
+#include <string.h>
+
+#include "ide-animation.h"
+#include "ide-frame-source.h"
+
+#define FALLBACK_FRAME_RATE 60
+
+G_DEFINE_TYPE (IdeAnimation, ide_animation, G_TYPE_INITIALLY_UNOWNED)
+
+typedef gdouble (*AlphaFunc) (gdouble offset);
+typedef void (*TweenFunc) (const GValue *begin,
+ const GValue *end,
+ GValue *value,
+ gdouble offset);
+
+typedef struct
+{
+ gboolean is_child; /* Does GParamSpec belong to parent widget */
+ GParamSpec *pspec; /* GParamSpec of target property */
+ GValue begin; /* Begin value in animation */
+ GValue end; /* End value in animation */
+} Tween;
+
+
+struct _IdeAnimationPrivate
+{
+ gpointer target; /* Target object to animate */
+ guint64 begin_msec; /* Time in which animation started */
+ guint duration_msec; /* Duration of animation */
+ guint mode; /* Tween mode */
+ guint tween_handler; /* GSource performing tweens */
+ GArray *tweens; /* Array of tweens to perform */
+ GdkFrameClock *frame_clock; /* An optional frame-clock for sync. */
+};
+
+
+enum {
+ PROP_0,
+ PROP_DURATION,
+ PROP_FRAME_CLOCK,
+ PROP_MODE,
+ PROP_TARGET,
+ LAST_PROP
+};
+
+
+enum {
+ TICK,
+ LAST_SIGNAL
+};
+
+
+/*
+ * Helper macros.
+ */
+#define LAST_FUNDAMENTAL 64
+#define TWEEN(type) \
+ static void \
+ tween_ ## type (const GValue * begin, \
+ const GValue * end, \
+ GValue * value, \
+ gdouble offset) \
+ { \
+ g ## type x = g_value_get_ ## type (begin); \
+ g ## type y = g_value_get_ ## type (end); \
+ g_value_set_ ## type (value, x + ((y - x) * offset)); \
+ }
+
+
+/*
+ * Globals.
+ */
+static AlphaFunc gAlphaFuncs[IDE_ANIMATION_LAST];
+static gboolean gDebug;
+static GParamSpec *gParamSpecs[LAST_PROP];
+static guint gSignals[LAST_SIGNAL];
+static TweenFunc gTweenFuncs[LAST_FUNDAMENTAL];
+
+
+/*
+ * Tweeners for basic types.
+ */
+TWEEN (int);
+TWEEN (uint);
+TWEEN (long);
+TWEEN (ulong);
+TWEEN (float);
+TWEEN (double);
+
+
+/**
+ * ide_animation_alpha_ease_in_cubic:
+ * @offset: (in): The position within the animation; 0.0 to 1.0.
+ *
+ * An alpha function to transform the offset within the animation.
+ * @IDE_ANIMATION_CUBIC means the valu ewill be transformed into
+ * cubic acceleration (x * x * x).
+ */
+static gdouble
+ide_animation_alpha_ease_in_cubic (gdouble offset)
+{
+ return offset * offset * offset;
+}
+
+
+static gdouble
+ide_animation_alpha_ease_out_cubic (gdouble offset)
+{
+ gdouble p = offset - 1.0;
+
+ return p * p * p + 1.0;
+}
+
+
+/**
+ * ide_animation_alpha_linear:
+ * @offset: (in): The position within the animation; 0.0 to 1.0.
+ *
+ * An alpha function to transform the offset within the animation.
+ * @IDE_ANIMATION_LINEAR means no tranformation will be made.
+ *
+ * Returns: @offset.
+ * Side effects: None.
+ */
+static gdouble
+ide_animation_alpha_linear (gdouble offset)
+{
+ return offset;
+}
+
+
+/**
+ * ide_animation_alpha_ease_in_quad:
+ * @offset: (in): The position within the animation; 0.0 to 1.0.
+ *
+ * An alpha function to transform the offset within the animation.
+ * @IDE_ANIMATION_EASE_IN_QUAD means that the value will be transformed
+ * into a quadratic acceleration.
+ *
+ * Returns: A tranformation of @offset.
+ * Side effects: None.
+ */
+static gdouble
+ide_animation_alpha_ease_in_quad (gdouble offset)
+{
+ return offset * offset;
+}
+
+
+/**
+ * ide_animation_alpha_ease_out_quad:
+ * @offset: (in): The position within the animation; 0.0 to 1.0.
+ *
+ * An alpha function to transform the offset within the animation.
+ * @IDE_ANIMATION_EASE_OUT_QUAD means that the value will be transformed
+ * into a quadratic deceleration.
+ *
+ * Returns: A tranformation of @offset.
+ * Side effects: None.
+ */
+static gdouble
+ide_animation_alpha_ease_out_quad (gdouble offset)
+{
+ return -1.0 * offset * (offset - 2.0);
+}
+
+
+/**
+ * ide_animation_alpha_ease_in_out_quad:
+ * @offset: (in): The position within the animation; 0.0 to 1.0.
+ *
+ * An alpha function to transform the offset within the animation.
+ * @IDE_ANIMATION_EASE_IN_OUT_QUAD means that the value will be transformed
+ * into a quadratic acceleration for the first half, and quadratic
+ * deceleration the second half.
+ *
+ * Returns: A tranformation of @offset.
+ * Side effects: None.
+ */
+static gdouble
+ide_animation_alpha_ease_in_out_quad (gdouble offset)
+{
+ offset *= 2.0;
+ if (offset < 1.0)
+ return 0.5 * offset * offset;
+ offset -= 1.0;
+ return -0.5 * (offset * (offset - 2.0) - 1.0);
+}
+
+
+/**
+ * ide_animation_load_begin_values:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Load the begin values for all the properties we are about to
+ * animate.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+static void
+ide_animation_load_begin_values (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+ GtkContainer *container;
+ Tween *tween;
+ gint i;
+
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+
+ priv = animation->priv;
+
+ for (i = 0; i < priv->tweens->len; i++)
+ {
+ tween = &g_array_index (priv->tweens, Tween, i);
+ g_value_reset (&tween->begin);
+ if (tween->is_child)
+ {
+ container = GTK_CONTAINER (gtk_widget_get_parent (priv->target));
+ gtk_container_child_get_property (container,
+ priv->target,
+ tween->pspec->name,
+ &tween->begin);
+ }
+ else
+ {
+ g_object_get_property (priv->target,
+ tween->pspec->name,
+ &tween->begin);
+ }
+ }
+}
+
+
+/**
+ * ide_animation_unload_begin_values:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Unloads the begin values for the animation. This might be particularly
+ * useful once we support pointer types.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+static void
+ide_animation_unload_begin_values (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+ Tween *tween;
+ gint i;
+
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+
+ priv = animation->priv;
+
+ for (i = 0; i < priv->tweens->len; i++)
+ {
+ tween = &g_array_index (priv->tweens, Tween, i);
+ g_value_reset (&tween->begin);
+ }
+}
+
+
+/**
+ * ide_animation_get_offset:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Retrieves the position within the animation from 0.0 to 1.0. This
+ * value is calculated using the msec of the beginning of the animation
+ * and the current time.
+ *
+ * Returns: The offset of the animation from 0.0 to 1.0.
+ * Side effects: None.
+ */
+static gdouble
+ide_animation_get_offset (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+ GdkFrameClock *frame_clock;
+ gdouble offset;
+ gint64 frame_msec;
+
+ g_return_val_if_fail (IDE_IS_ANIMATION (animation), 0.0);
+
+ priv = animation->priv;
+
+ if (GTK_IS_WIDGET (priv->target) &&
+ (frame_clock = gtk_widget_get_frame_clock (priv->target)))
+ frame_msec = gdk_frame_clock_get_frame_time (frame_clock) / 1000UL;
+ else
+ frame_msec = g_get_monotonic_time () / 1000UL;
+
+ offset = (gdouble) (frame_msec - priv->begin_msec) /
+ (gdouble) priv->duration_msec;
+
+ return CLAMP (offset, 0.0, 1.0);
+}
+
+
+/**
+ * ide_animation_update_property:
+ * @animation: (in): A #IdeAnimation.
+ * @target: (in): A #GObject.
+ * @tween: (in): a #Tween containing the property.
+ * @value: (in) The new value for the property.
+ *
+ * Updates the value of a property on an object using @value.
+ *
+ * Returns: None.
+ * Side effects: The property of @target is updated.
+ */
+static void
+ide_animation_update_property (IdeAnimation *animation,
+ gpointer target,
+ Tween *tween,
+ const GValue *value)
+{
+ g_assert (IDE_IS_ANIMATION (animation));
+ g_assert (G_IS_OBJECT (target));
+ g_assert (tween);
+ g_assert (value);
+
+ g_object_set_property (target, tween->pspec->name, value);
+}
+
+
+/**
+ * ide_animation_update_child_property:
+ * @animation: (in): A #IdeAnimation.
+ * @target: (in): A #GObject.
+ * @tween: (in): A #Tween containing the property.
+ * @value: (in): The new value for the property.
+ *
+ * Updates the value of the parent widget of the target to @value.
+ *
+ * Returns: None.
+ * Side effects: The property of @target<!-- -->'s parent widget is updated.
+ */
+static void
+ide_animation_update_child_property (IdeAnimation *animation,
+ gpointer target,
+ Tween *tween,
+ const GValue *value)
+{
+ GtkWidget *parent;
+
+ g_assert (IDE_IS_ANIMATION (animation));
+ g_assert (G_IS_OBJECT (target));
+ g_assert (tween);
+ g_assert (value);
+
+ parent = gtk_widget_get_parent (GTK_WIDGET (target));
+ gtk_container_child_set_property (GTK_CONTAINER (parent),
+ target,
+ tween->pspec->name,
+ value);
+}
+
+
+/**
+ * ide_animation_get_value_at_offset:
+ * @animation: (in): A #IdeAnimation.
+ * @offset: (in): The offset in the animation from 0.0 to 1.0.
+ * @tween: (in): A #Tween containing the property.
+ * @value: (out): A #GValue in which to store the property.
+ *
+ * Retrieves a value for a particular position within the animation.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+static void
+ide_animation_get_value_at_offset (IdeAnimation *animation,
+ gdouble offset,
+ Tween *tween,
+ GValue *value)
+{
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+ g_return_if_fail (tween != NULL);
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (value->g_type == tween->pspec->value_type);
+
+ if (value->g_type < LAST_FUNDAMENTAL)
+ {
+ /*
+ * If you hit the following assertion, you need to add a function
+ * to create the new value at the given offset.
+ */
+ g_assert (gTweenFuncs[value->g_type]);
+ gTweenFuncs[value->g_type](&tween->begin, &tween->end, value, offset);
+ }
+ else
+ {
+ /*
+ * TODO: Support complex transitions.
+ */
+ if (offset >= 1.0)
+ g_value_copy (&tween->end, value);
+ }
+}
+
+static void
+ide_animation_set_frame_clock (IdeAnimation *animation,
+ GdkFrameClock *frame_clock)
+{
+ IdeAnimationPrivate *priv = animation->priv;
+
+ if (priv->frame_clock != frame_clock)
+ {
+ g_clear_object (&priv->frame_clock);
+ priv->frame_clock = frame_clock ? g_object_ref (frame_clock) : NULL;
+ }
+}
+
+static void
+ide_animation_set_target (IdeAnimation *animation,
+ gpointer target)
+{
+ IdeAnimationPrivate *priv = animation->priv;
+
+ g_assert (!priv->target);
+
+ priv->target = g_object_ref (target);
+
+ if (GTK_IS_WIDGET (priv->target))
+ ide_animation_set_frame_clock (animation,
+ gtk_widget_get_frame_clock (priv->target));
+}
+
+
+/**
+ * ide_animation_tick:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Moves the object properties to the next position in the animation.
+ *
+ * Returns: %TRUE if the animation has not completed; otherwise %FALSE.
+ * Side effects: None.
+ */
+static gboolean
+ide_animation_tick (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+ gdouble offset;
+ gdouble alpha;
+ GValue value = { 0 };
+ Tween *tween;
+ gint i;
+
+ g_return_val_if_fail (IDE_IS_ANIMATION (animation), FALSE);
+
+ priv = animation->priv;
+
+ offset = ide_animation_get_offset (animation);
+ alpha = gAlphaFuncs[priv->mode](offset);
+
+ /*
+ * Update property values.
+ */
+ for (i = 0; i < priv->tweens->len; i++)
+ {
+ tween = &g_array_index (priv->tweens, Tween, i);
+ g_value_init (&value, tween->pspec->value_type);
+ ide_animation_get_value_at_offset (animation, alpha, tween, &value);
+ if (!tween->is_child)
+ {
+ ide_animation_update_property (animation,
+ priv->target,
+ tween,
+ &value);
+ }
+ else
+ {
+ ide_animation_update_child_property (animation,
+ priv->target,
+ tween,
+ &value);
+ }
+ g_value_unset (&value);
+ }
+
+ /*
+ * Notify anyone interested in the tick signal.
+ */
+ g_signal_emit (animation, gSignals[TICK], 0);
+
+ /*
+ * Flush any outstanding events to the graphics server (in the case of X).
+ */
+#if !GTK_CHECK_VERSION (3, 13, 0)
+ if (GTK_IS_WIDGET (priv->target))
+ {
+ GdkWindow *window;
+
+ if ((window = gtk_widget_get_window (GTK_WIDGET (priv->target))))
+ gdk_window_flush (window);
+ }
+#endif
+
+ return offset < 1.0;
+}
+
+
+/**
+ * ide_animation_timeout_cb:
+ * @user_data: (in): A #IdeAnimation.
+ *
+ * Timeout from the main loop to move to the next step of the animation.
+ *
+ * Returns: %TRUE until the animation has completed; otherwise %FALSE.
+ * Side effects: None.
+ */
+static gboolean
+ide_animation_timeout_cb (gpointer user_data)
+{
+ IdeAnimation *animation = user_data;
+ gboolean ret;
+
+ if (!(ret = ide_animation_tick (animation)))
+ ide_animation_stop (animation);
+
+ return ret;
+}
+
+
+static gboolean
+ide_animation_widget_tick_cb (GdkFrameClock *frame_clock,
+ IdeAnimation *animation)
+{
+ gboolean ret = G_SOURCE_REMOVE;
+
+ g_assert (GDK_IS_FRAME_CLOCK (frame_clock));
+ g_assert (IDE_IS_ANIMATION (animation));
+
+ if (animation->priv->tween_handler)
+ if (!(ret = ide_animation_tick (animation)))
+ ide_animation_stop (animation);
+
+ return ret;
+}
+
+
+/**
+ * ide_animation_start:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Start the animation. When the animation stops, the internal reference will
+ * be dropped and the animation may be finalized.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+void
+ide_animation_start (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+ g_return_if_fail (!animation->priv->tween_handler);
+
+ priv = animation->priv;
+
+ g_object_ref_sink (animation);
+ ide_animation_load_begin_values (animation);
+
+ if (priv->frame_clock)
+ {
+ priv->begin_msec = gdk_frame_clock_get_frame_time (priv->frame_clock) / 1000UL;
+ priv->tween_handler =
+ g_signal_connect (priv->frame_clock,
+ "update",
+ G_CALLBACK (ide_animation_widget_tick_cb),
+ animation);
+ gdk_frame_clock_begin_updating (priv->frame_clock);
+ }
+ else
+ {
+ priv->begin_msec = g_get_monotonic_time () / 1000UL;
+ priv->tween_handler = ide_frame_source_add (FALLBACK_FRAME_RATE,
+ ide_animation_timeout_cb,
+ animation);
+ }
+}
+
+
+/**
+ * ide_animation_stop:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Stops a running animation. The internal reference to the animation is
+ * dropped and therefore may cause the object to finalize.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+void
+ide_animation_stop (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+
+ priv = animation->priv;
+
+ if (priv->tween_handler)
+ {
+ if (priv->frame_clock)
+ {
+ gdk_frame_clock_end_updating (priv->frame_clock);
+ g_signal_handler_disconnect (priv->frame_clock, priv->tween_handler);
+ priv->tween_handler = 0;
+ }
+ else
+ {
+ g_source_remove (priv->tween_handler);
+ priv->tween_handler = 0;
+ }
+ ide_animation_unload_begin_values (animation);
+ g_object_unref (animation);
+ }
+}
+
+
+/**
+ * ide_animation_add_property:
+ * @animation: (in): A #IdeAnimation.
+ * @pspec: (in): A #ParamSpec of @target or a #GtkWidget<!-- -->'s parent.
+ * @value: (in): The new value for the property at the end of the animation.
+ *
+ * Adds a new property to the set of properties to be animated during the
+ * lifetime of the animation.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+void
+ide_animation_add_property (IdeAnimation *animation,
+ GParamSpec *pspec,
+ const GValue *value)
+{
+ IdeAnimationPrivate *priv;
+ Tween tween = { 0 };
+ GType type;
+
+ g_return_if_fail (IDE_IS_ANIMATION (animation));
+ g_return_if_fail (pspec != NULL);
+ g_return_if_fail (value != NULL);
+ g_return_if_fail (value->g_type);
+ g_return_if_fail (animation->priv->target);
+ g_return_if_fail (!animation->priv->tween_handler);
+
+ priv = animation->priv;
+
+ type = G_TYPE_FROM_INSTANCE (priv->target);
+ tween.is_child = !g_type_is_a (type, pspec->owner_type);
+ if (tween.is_child)
+ {
+ if (!GTK_IS_WIDGET (priv->target))
+ {
+ g_critical (_("Cannot locate property %s in class %s"),
+ pspec->name, g_type_name (type));
+ return;
+ }
+ }
+
+ tween.pspec = g_param_spec_ref (pspec);
+ g_value_init (&tween.begin, pspec->value_type);
+ g_value_init (&tween.end, pspec->value_type);
+ g_value_copy (value, &tween.end);
+ g_array_append_val (priv->tweens, tween);
+}
+
+
+/**
+ * ide_animation_dispose:
+ * @object: (in): A #IdeAnimation.
+ *
+ * Releases any object references the animation contains.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+static void
+ide_animation_dispose (GObject *object)
+{
+ IdeAnimationPrivate *priv = IDE_ANIMATION (object)->priv;
+
+ g_clear_object (&priv->target);
+ g_clear_object (&priv->frame_clock);
+
+ G_OBJECT_CLASS (ide_animation_parent_class)->dispose (object);
+}
+
+
+/**
+ * ide_animation_finalize:
+ * @object: (in): A #IdeAnimation.
+ *
+ * Finalizes the object and releases any resources allocated.
+ *
+ * Returns: None.
+ * Side effects: None.
+ */
+static void
+ide_animation_finalize (GObject *object)
+{
+ IdeAnimationPrivate *priv = IDE_ANIMATION (object)->priv;
+ Tween *tween;
+ gint i;
+
+ for (i = 0; i < priv->tweens->len; i++)
+ {
+ tween = &g_array_index (priv->tweens, Tween, i);
+ g_value_unset (&tween->begin);
+ g_value_unset (&tween->end);
+ g_param_spec_unref (tween->pspec);
+ }
+
+ g_array_unref (priv->tweens);
+
+ G_OBJECT_CLASS (ide_animation_parent_class)->finalize (object);
+}
+
+
+/**
+ * ide_animation_set_property:
+ * @object: (in): A #GObject.
+ * @prop_id: (in): The property identifier.
+ * @value: (in): The given property.
+ * @pspec: (in): A #ParamSpec.
+ *
+ * Set a given #GObject property.
+ */
+static void
+ide_animation_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeAnimation *animation = IDE_ANIMATION (object);
+
+ switch (prop_id)
+ {
+ case PROP_DURATION:
+ animation->priv->duration_msec = g_value_get_uint (value);
+ break;
+
+ case PROP_FRAME_CLOCK:
+ ide_animation_set_frame_clock (animation, g_value_get_object (value));
+ break;
+
+ case PROP_MODE:
+ animation->priv->mode = g_value_get_enum (value);
+ break;
+
+ case PROP_TARGET:
+ ide_animation_set_target (animation, g_value_get_object (value));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+
+/**
+ * ide_animation_class_init:
+ * @klass: (in): A #IdeAnimationClass.
+ *
+ * Initializes the GObjectClass.
+ *
+ * Returns: None.
+ * Side effects: Properties, signals, and vtables are initialized.
+ */
+static void
+ide_animation_class_init (IdeAnimationClass *klass)
+{
+ GObjectClass *object_class;
+
+ gDebug = !!g_getenv ("IDE_ANIMATION_DEBUG");
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = ide_animation_dispose;
+ object_class->finalize = ide_animation_finalize;
+ object_class->set_property = ide_animation_set_property;
+ g_type_class_add_private (object_class, sizeof (IdeAnimationPrivate));
+
+ /**
+ * IdeAnimation:duration:
+ *
+ * The "duration" property is the total number of milliseconds that the
+ * animation should run before being completed.
+ */
+ gParamSpecs[PROP_DURATION] =
+ g_param_spec_uint ("duration",
+ _("Duration"),
+ _("The duration of the animation"),
+ 0,
+ G_MAXUINT,
+ 250,
+ (G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_DURATION,
+ gParamSpecs[PROP_DURATION]);
+
+ gParamSpecs[PROP_FRAME_CLOCK] =
+ g_param_spec_object ("frame-clock",
+ _("Frame Clock"),
+ _("An optional frame-clock to synchronize with."),
+ GDK_TYPE_FRAME_CLOCK,
+ (G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_FRAME_CLOCK,
+ gParamSpecs[PROP_FRAME_CLOCK]);
+
+ /**
+ * IdeAnimation:mode:
+ *
+ * The "mode" property is the Alpha function that should be used to
+ * determine the offset within the animation based on the current
+ * offset in the animations duration.
+ */
+ gParamSpecs[PROP_MODE] =
+ g_param_spec_enum ("mode",
+ _("Mode"),
+ _("The animation mode"),
+ IDE_TYPE_ANIMATION_MODE,
+ IDE_ANIMATION_LINEAR,
+ (G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_MODE,
+ gParamSpecs[PROP_MODE]);
+
+ /**
+ * IdeAnimation:target:
+ *
+ * The "target" property is the #GObject that should have it's properties
+ * animated.
+ */
+ gParamSpecs[PROP_TARGET] =
+ g_param_spec_object ("target",
+ _("Target"),
+ _("The target of the animation"),
+ G_TYPE_OBJECT,
+ (G_PARAM_WRITABLE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_TARGET,
+ gParamSpecs[PROP_TARGET]);
+
+ /**
+ * IdeAnimation::tick:
+ *
+ * The "tick" signal is emitted on each frame in the animation.
+ */
+ gSignals[TICK] = g_signal_new ("tick",
+ IDE_TYPE_ANIMATION,
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL,
+ NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE,
+ 0);
+
+#define SET_ALPHA(_T, _t) \
+ gAlphaFuncs[IDE_ANIMATION_ ## _T] = ide_animation_alpha_ ## _t
+
+ SET_ALPHA (LINEAR, linear);
+ SET_ALPHA (EASE_IN_QUAD, ease_in_quad);
+ SET_ALPHA (EASE_OUT_QUAD, ease_out_quad);
+ SET_ALPHA (EASE_IN_OUT_QUAD, ease_in_out_quad);
+ SET_ALPHA (EASE_IN_CUBIC, ease_in_cubic);
+ SET_ALPHA (EASE_OUT_CUBIC, ease_out_cubic);
+
+#define SET_TWEEN(_T, _t) \
+ G_STMT_START { \
+ guint idx = G_TYPE_ ## _T; \
+ gTweenFuncs[idx] = tween_ ## _t; \
+ } G_STMT_END
+
+ SET_TWEEN (INT, int);
+ SET_TWEEN (UINT, uint);
+ SET_TWEEN (LONG, long);
+ SET_TWEEN (ULONG, ulong);
+ SET_TWEEN (FLOAT, float);
+ SET_TWEEN (DOUBLE, double);
+}
+
+
+/**
+ * ide_animation_init:
+ * @animation: (in): A #IdeAnimation.
+ *
+ * Initializes the #IdeAnimation instance.
+ *
+ * Returns: None.
+ * Side effects: Everything.
+ */
+static void
+ide_animation_init (IdeAnimation *animation)
+{
+ IdeAnimationPrivate *priv;
+
+ priv = G_TYPE_INSTANCE_GET_PRIVATE (animation, IDE_TYPE_ANIMATION, IdeAnimationPrivate);
+
+ animation->priv = priv;
+
+ priv->duration_msec = 250;
+ priv->mode = IDE_ANIMATION_EASE_IN_OUT_QUAD;
+ priv->tweens = g_array_new (FALSE, FALSE, sizeof (Tween));
+}
+
+
+/**
+ * ide_animation_mode_get_type:
+ *
+ * Retrieves the GType for #IdeAnimationMode.
+ *
+ * Returns: A GType.
+ * Side effects: GType registered on first call.
+ */
+GType
+ide_animation_mode_get_type (void)
+{
+ static GType type_id = 0;
+ static const GEnumValue values[] = {
+ { IDE_ANIMATION_LINEAR, "IDE_ANIMATION_LINEAR", "LINEAR" },
+ { IDE_ANIMATION_EASE_IN_QUAD, "IDE_ANIMATION_EASE_IN_QUAD", "EASE_IN_QUAD" },
+ { IDE_ANIMATION_EASE_IN_OUT_QUAD, "IDE_ANIMATION_EASE_IN_OUT_QUAD", "EASE_IN_OUT_QUAD" },
+ { IDE_ANIMATION_EASE_OUT_QUAD, "IDE_ANIMATION_EASE_OUT_QUAD", "EASE_OUT_QUAD" },
+ { IDE_ANIMATION_EASE_IN_CUBIC, "IDE_ANIMATION_EASE_IN_CUBIC", "EASE_IN_CUBIC" },
+ { IDE_ANIMATION_EASE_OUT_CUBIC, "IDE_ANIMATION_EASE_OUT_CUBIC", "EASE_OUT_CUBIC" },
+ { 0 }
+ };
+
+ if (G_UNLIKELY (!type_id))
+ type_id = g_enum_register_static ("IdeAnimationMode", values);
+ return type_id;
+}
+
+/**
+ * ide_object_animatev:
+ * @object: A #GObject.
+ * @mode: The animation mode.
+ * @duration_msec: The duration in milliseconds.
+ * @frame_rate: The target frame rate.
+ * @first_property: The first property to animate.
+ * @args: A variadac list of arguments
+ *
+ * Returns: (transfer none): A #IdeAnimation.
+ */
+IdeAnimation *
+ide_object_animatev (gpointer object,
+ IdeAnimationMode mode,
+ guint duration_msec,
+ GdkFrameClock *frame_clock,
+ const gchar *first_property,
+ va_list args)
+{
+ IdeAnimation *animation;
+ GObjectClass *klass;
+ GObjectClass *pklass;
+ const gchar *name;
+ GParamSpec *pspec;
+ GtkWidget *parent;
+ GValue value = { 0 };
+ gchar *error = NULL;
+ GType type;
+ GType ptype;
+
+ g_return_val_if_fail (first_property != NULL, NULL);
+ g_return_val_if_fail (mode < IDE_ANIMATION_LAST, NULL);
+
+ if (!frame_clock && GTK_IS_WIDGET (object))
+ {
+ frame_clock = gtk_widget_get_frame_clock (GTK_WIDGET (object));
+ }
+
+ name = first_property;
+ type = G_TYPE_FROM_INSTANCE (object);
+ klass = G_OBJECT_GET_CLASS (object);
+ animation = g_object_new (IDE_TYPE_ANIMATION,
+ "duration", duration_msec,
+ "frame-clock", frame_clock,
+ "mode", mode,
+ "target", object,
+ NULL);
+
+ do
+ {
+ /*
+ * First check for the property on the object. If that does not exist
+ * then check if the object has a parent and look at its child
+ * properties (if its a GtkWidget).
+ */
+ if (!(pspec = g_object_class_find_property (klass, name)))
+ {
+ if (!g_type_is_a (type, GTK_TYPE_WIDGET))
+ {
+ g_critical (_("Failed to find property %s in %s"),
+ name, g_type_name (type));
+ goto failure;
+ }
+ if (!(parent = gtk_widget_get_parent (object)))
+ {
+ g_critical (_("Failed to find property %s in %s"),
+ name, g_type_name (type));
+ goto failure;
+ }
+ pklass = G_OBJECT_GET_CLASS (parent);
+ ptype = G_TYPE_FROM_INSTANCE (parent);
+ if (!(pspec = gtk_container_class_find_child_property (pklass, name)))
+ {
+ g_critical (_("Failed to find property %s in %s or parent %s"),
+ name, g_type_name (type), g_type_name (ptype));
+ goto failure;
+ }
+ }
+
+ g_value_init (&value, pspec->value_type);
+ G_VALUE_COLLECT (&value, args, 0, &error);
+ if (error != NULL)
+ {
+ g_critical (_("Failed to retrieve va_list value: %s"), error);
+ g_free (error);
+ goto failure;
+ }
+
+ ide_animation_add_property (animation, pspec, &value);
+ g_value_unset (&value);
+ }
+ while ((name = va_arg (args, const gchar *)));
+
+ ide_animation_start (animation);
+
+ return animation;
+
+failure:
+ g_object_ref_sink (animation);
+ g_object_unref (animation);
+ return NULL;
+}
+
+/**
+ * ide_object_animate:
+ * @object: (in): A #GObject.
+ * @mode: (in): The animation mode.
+ * @duration_msec: (in): The duration in milliseconds.
+ * @first_property: (in): The first property to animate.
+ *
+ * Animates the properties of @object. The can be set in a similar manner to g_object_set(). They
+ * will be animated from their current value to the target value over the time period.
+ *
+ * Return value: (transfer none): A #IdeAnimation.
+ * Side effects: None.
+ */
+IdeAnimation*
+ide_object_animate (gpointer object,
+ IdeAnimationMode mode,
+ guint duration_msec,
+ GdkFrameClock *frame_clock,
+ const gchar *first_property,
+ ...)
+{
+ IdeAnimation *animation;
+ va_list args;
+
+ va_start (args, first_property);
+ animation = ide_object_animatev (object, mode, duration_msec, frame_clock, first_property, args);
+ va_end (args);
+ return animation;
+}
+
+/**
+ * ide_object_animate_full:
+ *
+ * Return value: (transfer none): A #IdeAnimation.
+ */
+IdeAnimation*
+ide_object_animate_full (gpointer object,
+ IdeAnimationMode mode,
+ guint duration_msec,
+ GdkFrameClock *frame_clock,
+ GDestroyNotify notify,
+ gpointer notify_data,
+ const gchar *first_property,
+ ...)
+{
+ IdeAnimation *animation;
+ va_list args;
+
+ va_start (args, first_property);
+ animation = ide_object_animatev (object,
+ mode,
+ duration_msec,
+ frame_clock,
+ first_property,
+ args);
+ va_end (args);
+
+ g_object_weak_ref (G_OBJECT (animation), (GWeakNotify) notify, notify_data);
+
+ return animation;
+}
diff --git a/libide/theatrics/ide-animation.h b/libide/theatrics/ide-animation.h
new file mode 100644
index 0000000..0f3fabe
--- /dev/null
+++ b/libide/theatrics/ide-animation.h
@@ -0,0 +1,89 @@
+/* ide-animation.h
+ *
+ * Copyright (C) 2013 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_ANIMATION_H
+#define IDE_ANIMATION_H
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_ANIMATION (ide_animation_get_type())
+#define IDE_TYPE_ANIMATION_MODE (ide_animation_mode_get_type())
+#define IDE_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_ANIMATION, IdeAnimation))
+#define IDE_ANIMATION_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_ANIMATION, IdeAnimation
const))
+#define IDE_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDE_TYPE_ANIMATION,
IdeAnimationClass))
+#define IDE_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_ANIMATION))
+#define IDE_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDE_TYPE_ANIMATION))
+#define IDE_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDE_TYPE_ANIMATION,
IdeAnimationClass))
+
+typedef struct _IdeAnimation IdeAnimation;
+typedef struct _IdeAnimationClass IdeAnimationClass;
+typedef struct _IdeAnimationPrivate IdeAnimationPrivate;
+typedef enum _IdeAnimationMode IdeAnimationMode;
+
+enum _IdeAnimationMode
+{
+ IDE_ANIMATION_LINEAR,
+ IDE_ANIMATION_EASE_IN_QUAD,
+ IDE_ANIMATION_EASE_OUT_QUAD,
+ IDE_ANIMATION_EASE_IN_OUT_QUAD,
+ IDE_ANIMATION_EASE_IN_CUBIC,
+ IDE_ANIMATION_EASE_OUT_CUBIC,
+
+ IDE_ANIMATION_LAST
+};
+
+struct _IdeAnimation
+{
+ GInitiallyUnowned parent;
+
+ /*< private >*/
+ IdeAnimationPrivate *priv;
+};
+
+struct _IdeAnimationClass
+{
+ GInitiallyUnownedClass parent_class;
+};
+
+GType ide_animation_get_type (void);
+GType ide_animation_mode_get_type (void);
+void ide_animation_start (IdeAnimation *animation);
+void ide_animation_stop (IdeAnimation *animation);
+void ide_animation_add_property (IdeAnimation *animation,
+ GParamSpec *pspec,
+ const GValue *value);
+IdeAnimation* ide_object_animate (gpointer object,
+ IdeAnimationMode mode,
+ guint duration_msec,
+ GdkFrameClock *frame_clock,
+ const gchar *first_property,
+ ...) G_GNUC_NULL_TERMINATED;
+IdeAnimation* ide_object_animate_full (gpointer object,
+ IdeAnimationMode mode,
+ guint duration_msec,
+ GdkFrameClock *frame_clock,
+ GDestroyNotify notify,
+ gpointer notify_data,
+ const gchar *first_property,
+ ...) G_GNUC_NULL_TERMINATED;
+
+G_END_DECLS
+
+#endif /* IDE_ANIMATION_H */
diff --git a/libide/theatrics/ide-box-theatric.c b/libide/theatrics/ide-box-theatric.c
new file mode 100644
index 0000000..ee51a79
--- /dev/null
+++ b/libide/theatrics/ide-box-theatric.c
@@ -0,0 +1,336 @@
+/* ide-box-theatric.c
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#define G_LOG_DOMAIN "theatrics"
+
+#include <glib/gi18n.h>
+
+#include "ide-animation.h"
+#include "ide-box-theatric.h"
+#include "ide-cairo.h"
+
+struct _IdeBoxTheatricPrivate
+{
+ GtkWidget *target;
+ GtkWidget *toplevel;
+ GdkRectangle area;
+ GdkRectangle last_area;
+ GdkRGBA background_rgba;
+
+ guint draw_handler;
+};
+
+enum {
+ PROP_0,
+ PROP_ALPHA,
+ PROP_BACKGROUND,
+ PROP_HEIGHT,
+ PROP_TARGET,
+ PROP_WIDTH,
+ PROP_X,
+ PROP_Y,
+ LAST_PROP
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE (IdeBoxTheatric, ide_box_theatric, G_TYPE_OBJECT)
+
+static GParamSpec *gParamSpecs[LAST_PROP];
+
+static void
+get_toplevel_rect (IdeBoxTheatric *theatric,
+ GdkRectangle *area)
+{
+ IdeBoxTheatricPrivate *priv = theatric->priv;
+
+ gtk_widget_translate_coordinates (priv->target, priv->toplevel,
+ priv->area.x, priv->area.y,
+ &area->x, &area->y);
+
+ area->width = priv->area.width;
+ area->height = priv->area.height;
+}
+
+static gboolean
+on_toplevel_draw (GtkWidget *widget,
+ cairo_t *cr,
+ IdeBoxTheatric *theatric)
+{
+ IdeBoxTheatricPrivate *priv = theatric->priv;
+ GdkRectangle area;
+
+ get_toplevel_rect (theatric, &area);
+
+#if 0
+ g_print ("Drawing on %s at %d,%d %dx%d\n",
+ g_type_name (G_TYPE_FROM_INSTANCE (widget)),
+ area.x, area.y, area.width, area.height);
+#endif
+
+ ide_cairo_rounded_rectangle (cr, &area, 3, 3);
+ gdk_cairo_set_source_rgba (cr, &priv->background_rgba);
+ cairo_fill (cr);
+
+ priv->last_area = area;
+
+ return FALSE;
+}
+
+static void
+ide_box_theatric_notify (GObject *object,
+ GParamSpec *pspec)
+{
+ IdeBoxTheatricPrivate *priv = IDE_BOX_THEATRIC (object)->priv;
+
+ if (G_OBJECT_CLASS (ide_box_theatric_parent_class)->notify)
+ G_OBJECT_CLASS (ide_box_theatric_parent_class)->notify (object, pspec);
+
+ if (priv->target && priv->toplevel)
+ {
+ GdkWindow *window;
+ GdkRectangle area;
+
+ get_toplevel_rect (IDE_BOX_THEATRIC (object), &area);
+
+#if 0
+ g_print (" Invalidate : %d,%d %dx%d\n",
+ area.x, area.y, area.width, area.height);
+#endif
+
+ window = gtk_widget_get_window (priv->toplevel);
+
+ gdk_window_invalidate_rect (window, &priv->last_area, TRUE);
+ gdk_window_invalidate_rect (window, &area, TRUE);
+ }
+}
+
+static void
+ide_box_theatric_dispose (GObject *object)
+{
+ IdeBoxTheatricPrivate *priv = IDE_BOX_THEATRIC (object)->priv;
+
+ if (priv->target)
+ {
+ if (priv->draw_handler && priv->toplevel)
+ {
+ g_signal_handler_disconnect (priv->toplevel, priv->draw_handler);
+ priv->draw_handler = 0;
+ }
+ g_object_remove_weak_pointer (G_OBJECT (priv->target),
+ (gpointer *) &priv->target);
+ priv->target = NULL;
+ }
+
+ G_OBJECT_CLASS (ide_box_theatric_parent_class)->dispose (object);
+}
+
+static void
+ide_box_theatric_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ IdeBoxTheatric *theatric = IDE_BOX_THEATRIC (object);
+
+ switch (prop_id)
+ {
+ case PROP_ALPHA:
+ g_value_set_double (value, theatric->priv->background_rgba.alpha);
+ break;
+
+ case PROP_BACKGROUND:
+ g_value_take_string (value,
+ gdk_rgba_to_string (&theatric->priv->background_rgba));
+ break;
+
+ case PROP_HEIGHT:
+ g_value_set_int (value, theatric->priv->area.height);
+ break;
+
+ case PROP_TARGET:
+ g_value_set_object (value, theatric->priv->target);
+ break;
+
+ case PROP_WIDTH:
+ g_value_set_int (value, theatric->priv->area.width);
+ break;
+
+ case PROP_X:
+ g_value_set_int (value, theatric->priv->area.x);
+ break;
+
+ case PROP_Y:
+ g_value_set_int (value, theatric->priv->area.y);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+ide_box_theatric_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ IdeBoxTheatric *theatric = IDE_BOX_THEATRIC (object);
+
+ switch (prop_id)
+ {
+ case PROP_ALPHA:
+ theatric->priv->background_rgba.alpha = g_value_get_double (value);
+ break;
+
+ case PROP_BACKGROUND:
+ {
+ gdouble old_alpha = theatric->priv->background_rgba.alpha;
+
+ gdk_rgba_parse (&theatric->priv->background_rgba,
+ g_value_get_string (value));
+ theatric->priv->background_rgba.alpha = old_alpha;
+ }
+ break;
+
+ case PROP_HEIGHT:
+ theatric->priv->area.height = g_value_get_int (value);
+ break;
+
+ case PROP_TARGET:
+ theatric->priv->target = g_value_get_object (value);
+ theatric->priv->toplevel =
+ gtk_widget_get_toplevel (theatric->priv->target);
+ g_object_add_weak_pointer (G_OBJECT (theatric->priv->target),
+ (gpointer *) &theatric->priv->target);
+ theatric->priv->draw_handler =
+ g_signal_connect_after (theatric->priv->toplevel,
+ "draw",
+ G_CALLBACK (on_toplevel_draw),
+ theatric);
+ break;
+
+ case PROP_WIDTH:
+ theatric->priv->area.width = g_value_get_int (value);
+ break;
+
+ case PROP_X:
+ theatric->priv->area.x = g_value_get_int (value);
+ break;
+
+ case PROP_Y:
+ theatric->priv->area.y = g_value_get_int (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+
+ g_object_notify_by_pspec (object, pspec);
+}
+
+static void
+ide_box_theatric_class_init (IdeBoxTheatricClass *klass)
+{
+ GObjectClass *object_class;
+
+ object_class = G_OBJECT_CLASS (klass);
+ object_class->dispose = ide_box_theatric_dispose;
+ object_class->notify = ide_box_theatric_notify;
+ object_class->get_property = ide_box_theatric_get_property;
+ object_class->set_property = ide_box_theatric_set_property;
+
+ gParamSpecs[PROP_ALPHA] =
+ g_param_spec_double ("alpha",
+ _("Alpha"),
+ _("Alpha"),
+ 0.0,
+ 1.0,
+ 1.0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_ALPHA,
+ gParamSpecs[PROP_ALPHA]);
+
+ gParamSpecs[PROP_BACKGROUND] =
+ g_param_spec_string ("background",
+ _("background"),
+ _("background"),
+ "#000000",
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_BACKGROUND,
+ gParamSpecs[PROP_BACKGROUND]);
+
+ gParamSpecs[PROP_HEIGHT] =
+ g_param_spec_int ("height",
+ _("height"),
+ _("height"),
+ 0,
+ G_MAXINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_HEIGHT,
+ gParamSpecs[PROP_HEIGHT]);
+
+ gParamSpecs[PROP_TARGET] =
+ g_param_spec_object ("target",
+ _("Target"),
+ _("Target"),
+ GTK_TYPE_WIDGET,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_TARGET,
+ gParamSpecs[PROP_TARGET]);
+
+ gParamSpecs[PROP_WIDTH] =
+ g_param_spec_int ("width",
+ _("width"),
+ _("width"),
+ 0,
+ G_MAXINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_WIDTH,
+ gParamSpecs[PROP_WIDTH]);
+
+ gParamSpecs[PROP_X] =
+ g_param_spec_int ("x",
+ _("x"),
+ _("x"),
+ G_MININT,
+ G_MAXINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_X,
+ gParamSpecs[PROP_X]);
+
+ gParamSpecs[PROP_Y] =
+ g_param_spec_int ("y",
+ _("y"),
+ _("y"),
+ G_MININT,
+ G_MAXINT,
+ 0,
+ (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (object_class, PROP_Y,
+ gParamSpecs[PROP_Y]);
+}
+
+static void
+ide_box_theatric_init (IdeBoxTheatric *theatric)
+{
+ theatric->priv = ide_box_theatric_get_instance_private (theatric);
+}
diff --git a/libide/theatrics/ide-box-theatric.h b/libide/theatrics/ide-box-theatric.h
new file mode 100644
index 0000000..94a4276
--- /dev/null
+++ b/libide/theatrics/ide-box-theatric.h
@@ -0,0 +1,55 @@
+/* ide-box-theatric.h
+ *
+ * Copyright (C) 2014 Christian Hergert <christian hergert me>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_BOX_THEATRIC_H
+#define IDE_BOX_THEATRIC_H
+
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define IDE_TYPE_BOX_THEATRIC (ide_box_theatric_get_type())
+#define IDE_BOX_THEATRIC(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_BOX_THEATRIC,
IdeBoxTheatric))
+#define IDE_BOX_THEATRIC_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), IDE_TYPE_BOX_THEATRIC,
IdeBoxTheatric const))
+#define IDE_BOX_THEATRIC_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), IDE_TYPE_BOX_THEATRIC,
IdeBoxTheatricClass))
+#define IDE_IS_BOX_THEATRIC(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), IDE_TYPE_BOX_THEATRIC))
+#define IDE_IS_BOX_THEATRIC_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), IDE_TYPE_BOX_THEATRIC))
+#define IDE_BOX_THEATRIC_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), IDE_TYPE_BOX_THEATRIC,
IdeBoxTheatricClass))
+
+typedef struct _IdeBoxTheatric IdeBoxTheatric;
+typedef struct _IdeBoxTheatricClass IdeBoxTheatricClass;
+typedef struct _IdeBoxTheatricPrivate IdeBoxTheatricPrivate;
+
+struct _IdeBoxTheatric
+{
+ GObject parent;
+
+ /*< private >*/
+ IdeBoxTheatricPrivate *priv;
+};
+
+struct _IdeBoxTheatricClass
+{
+ GObjectClass parent_class;
+};
+
+GType ide_box_theatric_get_type (void);
+
+G_END_DECLS
+
+#endif /* IDE_BOX_THEATRIC_H */
diff --git a/libide/theatrics/ide-frame-source.c b/libide/theatrics/ide-frame-source.c
new file mode 100644
index 0000000..9aebfae
--- /dev/null
+++ b/libide/theatrics/ide-frame-source.c
@@ -0,0 +1,134 @@
+/*
+ * Based upon code from Clutter:
+ *
+ * Authored By Neil Roberts <neil linux intel com>
+ *
+ * Copyright (C) 2009 Intel Corporation.
+ * Copyright (C) 2012 Christian Hergert.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "ide-frame-source.h"
+
+typedef struct
+{
+ GSource parent;
+ guint fps;
+ guint frame_count;
+ gint64 start_time;
+} IdeFrameSource;
+
+static gboolean
+ide_frame_source_prepare (GSource *source,
+ gint *timeout_)
+{
+ IdeFrameSource *fsource = (IdeFrameSource *)(gpointer)source;
+ gint64 current_time;
+ guint elapsed_time;
+ guint new_frame_num;
+ guint frame_time;
+
+ current_time = g_source_get_time(source) / 1000;
+ elapsed_time = current_time - fsource->start_time;
+ new_frame_num = elapsed_time * fsource->fps / 1000;
+
+ /* If time has gone backwards or the time since the last frame is
+ * greater than the two frames worth then reset the time and do a
+ * frame now */
+ if (new_frame_num < fsource->frame_count ||
+ new_frame_num - fsource->frame_count > 2) {
+ /* Get the frame time rounded up to the nearest ms */
+ frame_time = (1000 + fsource->fps - 1) / fsource->fps;
+
+ /* Reset the start time */
+ fsource->start_time = current_time;
+
+ /* Move the start time as if one whole frame has elapsed */
+ fsource->start_time -= frame_time;
+ fsource->frame_count = 0;
+ *timeout_ = 0;
+ return TRUE;
+ } else if (new_frame_num > fsource->frame_count) {
+ *timeout_ = 0;
+ return TRUE;
+ } else {
+ *timeout_ = (fsource->frame_count + 1) * 1000 / fsource->fps - elapsed_time;
+ return FALSE;
+ }
+}
+
+static gboolean
+ide_frame_source_check (GSource *source)
+{
+ gint timeout_;
+ return ide_frame_source_prepare(source, &timeout_);
+}
+
+static gboolean
+ide_frame_source_dispatch (GSource *source,
+ GSourceFunc source_func,
+ gpointer user_data)
+{
+ IdeFrameSource *fsource = (IdeFrameSource *)(gpointer)source;
+ gboolean ret;
+
+ if ((ret = source_func(user_data)))
+ fsource->frame_count++;
+ return ret;
+}
+
+static GSourceFuncs source_funcs = {
+ ide_frame_source_prepare,
+ ide_frame_source_check,
+ ide_frame_source_dispatch,
+};
+
+/**
+ * ide_frame_source_add:
+ * @frames_per_sec: (in): Target frames per second.
+ * @callback: (in) (scope notified): A #GSourceFunc to execute.
+ * @user_data: (in): User data for @callback.
+ *
+ * Creates a new frame source that will execute when the timeout interval
+ * for the source has elapsed. The timing will try to synchronize based
+ * on the end time of the animation.
+ *
+ * Returns: A source id that can be removed with g_source_remove().
+ */
+guint
+ide_frame_source_add (guint frames_per_sec,
+ GSourceFunc callback,
+ gpointer user_data)
+{
+ IdeFrameSource *fsource;
+ GSource *source;
+ guint ret;
+
+ g_return_val_if_fail (frames_per_sec > 0, 0);
+ g_return_val_if_fail (frames_per_sec <= 120, 0);
+
+ source = g_source_new(&source_funcs, sizeof(IdeFrameSource));
+ fsource = (IdeFrameSource *)(gpointer)source;
+ fsource->fps = frames_per_sec;
+ fsource->frame_count = 0;
+ fsource->start_time = g_get_monotonic_time() / 1000;
+ g_source_set_callback(source, callback, user_data, NULL);
+ g_source_set_name(source, "IdeFrameSource");
+
+ ret = g_source_attach(source, NULL);
+ g_source_unref(source);
+
+ return ret;
+}
diff --git a/libide/theatrics/ide-frame-source.h b/libide/theatrics/ide-frame-source.h
new file mode 100644
index 0000000..720b1bb
--- /dev/null
+++ b/libide/theatrics/ide-frame-source.h
@@ -0,0 +1,32 @@
+/* ide-frame-source.h
+ *
+ * Copyright (C) 2013 Christian Hergert <christian hergert me>
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef IDE_FRAME_SOURCE_H
+#define IDE_FRAME_SOURCE_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+guint ide_frame_source_add (guint frames_per_sec,
+ GSourceFunc callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* IDE_FRAME_SOURCE_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]