[Xfce4-commits] [apps/xfdashboard] 01/01: Add hard-coded animation for expand and collapse of XfdashboardCollapse
noreply at xfce.org
noreply at xfce.org
Wed Oct 23 18:46:25 CEST 2019
This is an automated email from the git hooks/post-receive script.
n o m a d p u s h e d a c o m m i t t o b r a n c h m a s t e r
in repository apps/xfdashboard.
commit 0fa7008e0e8c7485347135fdd6b037561ef31937
Author: Stephan Haller <nomad at froevel.de>
Date: Wed Oct 23 18:45:42 2019 +0200
Add hard-coded animation for expand and collapse of XfdashboardCollapse
---
libxfdashboard/Makefile.am | 6 +
libxfdashboard/animation.c | 683 ++++++++++++++++++++++++++
libxfdashboard/animation.h | 105 ++++
libxfdashboard/application.c | 2 +
libxfdashboard/collapse-box.c | 42 ++
libxfdashboard/debug.h | 2 +
libxfdashboard/theme-animation.c | 998 ++++++++++++++++++++++++++++++++++++++
libxfdashboard/theme-animation.h | 108 +++++
libxfdashboard/theme.c | 81 +++-
libxfdashboard/theme.h | 2 +
libxfdashboard/transition-group.c | 683 ++++++++++++++++++++++++++
libxfdashboard/transition-group.h | 95 ++++
12 files changed, 2806 insertions(+), 1 deletion(-)
diff --git a/libxfdashboard/Makefile.am b/libxfdashboard/Makefile.am
index fe6ae6c..1d18cb0 100644
--- a/libxfdashboard/Makefile.am
+++ b/libxfdashboard/Makefile.am
@@ -28,6 +28,7 @@ libxfdashboard_la_built_sources = \
libxfdashboard_la_headers = \
action-button.h \
actor.h \
+ animation.h \
application.h \
application-button.h \
application-database.h \
@@ -80,11 +81,13 @@ libxfdashboard_la_headers = \
stylable.h \
text-box.h \
theme.h \
+ theme-animation.h \
theme-css.h \
theme-effects.h \
theme-layout.h \
toggle-button.h \
tooltip-action.h \
+ transition-group.h \
types.h \
utils.h \
view.h \
@@ -105,6 +108,7 @@ libxfdashboard_la_SOURCES = \
$(xfdashboard_headers) \
action-button.c \
actor.c \
+ animation.c \
application.c \
application-button.c \
application-database.c \
@@ -156,11 +160,13 @@ libxfdashboard_la_SOURCES = \
stylable.c \
text-box.c \
theme.c \
+ theme-animation.c \
theme-css.c \
theme-effects.c \
theme-layout.c \
toggle-button.c \
tooltip-action.c \
+ transition-group.c \
utils.c \
view.c \
view-manager.c \
diff --git a/libxfdashboard/animation.c b/libxfdashboard/animation.c
new file mode 100644
index 0000000..a70a71f
--- /dev/null
+++ b/libxfdashboard/animation.c
@@ -0,0 +1,683 @@
+/*
+ * animation: A animation for an actor
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+/**
+ * SECTION:animation
+ * @short_description: An animation for an actor
+ * @include: xfdashboard/animation.h
+ *
+ * This actor is a #XfdashboardButton and behaves exactly like a key binding which
+ * performs a specified action on a specific actor when the associated key
+ * combination is pressed. But instead of a key combination a button is displayed
+ * and the action performed when this button is clicked.
+ *
+ * A #XfdashboardAnimation is usually created in the layout definition
+ * of a theme but it can also be created with xfdashboard_animation_new()
+ * followed by a call to xfdashboard_animation_set_target() and
+ * xfdashboard_animation_set_action() to configure it.
+ *
+ * For example a #XfdashboardAnimation can be created which will quit the
+ * application when clicked:
+ *
+ * |[<!-- language="C" -->
+ * ClutterActor *actionButton;
+ *
+ * actionButton=xfdashboard_animation_new();
+ * xfdashboard_animation_set_target(XFDASHBOARD_ANIMATION(actionButton), "XfdashboardApplication");
+ * xfdashboard_animation_set_action(XFDASHBOARD_ANIMATION(actionButton), "exit");
+ * ]|
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/animation.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <libxfdashboard/transition-group.h>
+#include <libxfdashboard/application.h>
+#include <libxfdashboard/theme.h>
+#include <libxfdashboard/compat.h>
+#include <libxfdashboard/debug.h>
+
+
+/* Define this class in GObject system */
+struct _XfdashboardAnimationPrivate
+{
+ /* Properties related */
+ gchar *id;
+
+ /* Instance related */
+ GSList *entries;
+
+ XfdashboardAnimationDoneCallback doneCallback;
+ gpointer doneUserData;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardAnimation,
+ xfdashboard_animation,
+ G_TYPE_OBJECT);
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_ID,
+
+ PROP_LAST
+};
+
+static GParamSpec* XfdashboardAnimationProperties[PROP_LAST]={ 0, };
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+typedef struct _XfdashboardAnimationEntry XfdashboardAnimationEntry;
+struct _XfdashboardAnimationEntry
+{
+ XfdashboardAnimation *self;
+ ClutterActor *actor;
+ ClutterTransition *transition;
+ guint actorDestroyID;
+ guint transitionStoppedID;
+ guint newFrameSignalID;
+};
+
+/* Time at a transition has elapsed. This signal is only catched once for
+ * a transition, so complete missing "to" values at the transition(s).
+ */
+static void _xfdashboard_animation_on_transition_new_frame(ClutterTransition *inTransition,
+ gint inElapsed,
+ gpointer inUserData)
+{
+ XfdashboardAnimationEntry *entry;
+ GSList *transitions;
+ GSList *iter;
+
+ g_return_if_fail(CLUTTER_IS_TRANSITION(inTransition));
+ g_return_if_fail(inUserData);
+
+ entry=(XfdashboardAnimationEntry*)inUserData;
+
+ /* Check if we have to handle a transition group */
+ if(XFDASHBOARD_IS_TRANSITION_GROUP(entry->transition))
+ {
+ /* Get list of transitions from group */
+ transitions=xfdashboard_transition_group_get_transitions(XFDASHBOARD_TRANSITION_GROUP(entry->transition));
+
+ /* Iterate through transitions of group and complete missing "to" values */
+ for(iter=transitions; iter; iter=g_slist_next(iter))
+ {
+ ClutterPropertyTransition *transition;
+ ClutterInterval *interval;
+ GValue *intervalToValue;
+ ClutterAnimatable *animatable;
+ GValue toValue=G_VALUE_INIT;
+
+ /* Skip invalid transitions or transitions not modifiing properties */
+ if(!iter->data) continue;
+
+ if(!CLUTTER_IS_PROPERTY_TRANSITION(iter->data))
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "Transition %s@%p is not a ClutterPropertyTransition",
+ G_OBJECT_TYPE_NAME(iter->data), iter->data);
+#endif
+ continue;
+ }
+
+ transition=CLUTTER_PROPERTY_TRANSITION(iter->data);
+
+ /* Check if "to" value is missed at property-modifiing transition */
+ interval=clutter_transition_get_interval(CLUTTER_TRANSITION(transition));
+ if(!interval)
+ {
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "No interval set at transition %s@%p for property %s",
+ G_OBJECT_TYPE_NAME(transition), transition,
+ clutter_property_transition_get_property_name(transition));
+ continue;
+ }
+
+ intervalToValue=clutter_interval_peek_final_value(interval);
+ if(!intervalToValue)
+ {
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "Could not get final value from interval set at transition %s@%p for property %s",
+ G_OBJECT_TYPE_NAME(transition), transition,
+ clutter_property_transition_get_property_name(transition));
+ continue;
+ }
+
+ if(clutter_interval_is_valid(interval))
+ {
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "Valid interval set at transition %s@%p for property %s - no need to complete final value",
+ G_OBJECT_TYPE_NAME(transition), transition,
+ clutter_property_transition_get_property_name(transition));
+ continue;
+ }
+
+ /* Complete missing "to" value */
+ animatable=clutter_transition_get_animatable(CLUTTER_TRANSITION(transition));
+ if(!animatable)
+ {
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "Cannot determine final value from interval set at transition %s@%p for property %s as no animatable actor was set",
+ G_OBJECT_TYPE_NAME(transition), transition,
+ clutter_property_transition_get_property_name(transition));
+ continue;
+ }
+
+ g_object_get_property(G_OBJECT(animatable),
+ clutter_property_transition_get_property_name(transition),
+ &toValue);
+ if(G_VALUE_TYPE(&toValue)!=G_TYPE_INVALID)
+ {
+#ifdef DEBUG
+ gchar *valueString;
+
+ valueString=g_strdup_value_contents(&toValue);
+ XFDASHBOARD_DEBUG(entry->self, ANIMATION,
+ "Set final value %s (type %s) at interval set of transition %s@%p for property %s",
+ valueString, G_VALUE_TYPE_NAME(&toValue),
+ G_OBJECT_TYPE_NAME(transition), transition,
+ clutter_property_transition_get_property_name(transition));
+ g_free(valueString);
+#endif
+ clutter_interval_set_final_value(interval, &toValue);
+ }
+ g_value_unset(&toValue);
+ }
+
+ /* Release allocated resources */
+ g_slist_free_full(transitions, g_object_unref);
+ }
+
+ /* We handled the transition so remove signal handler */
+ g_signal_handler_disconnect(entry->transition, entry->newFrameSignalID);
+ entry->newFrameSignalID=0;
+}
+
+/* Frees an animation entry */
+static void _xfdashboard_animation_entry_free(XfdashboardAnimationEntry *inData)
+{
+ g_return_if_fail(inData);
+
+ /* Release allocated resources */
+ if(inData->newFrameSignalID) g_signal_handler_disconnect(inData->transition, inData->newFrameSignalID);
+ if(inData->transitionStoppedID) g_signal_handler_disconnect(inData->transition, inData->transitionStoppedID);
+ if(inData->actorDestroyID) g_signal_handler_disconnect(inData->actor, inData->actorDestroyID);
+ if(inData->transition) g_object_unref(inData->transition);
+ if(inData->actor)
+ {
+ clutter_actor_remove_transition(inData->actor, inData->self->priv->id);
+ g_object_unref(inData->actor);
+ }
+ g_free(inData);
+}
+
+/* The transition, we added to an actor, has stopped. If transition reached end of
+ * timeline, remove entry from. When removing entry from list of entries, we unref
+ * objects and disconnect signal handlers automatically. No need to do it here.
+ */
+static void _xfdashboard_animation_on_transition_stopped(XfdashboardAnimation *self,
+ gboolean isFinished,
+ gpointer inUserData)
+{
+ XfdashboardAnimationPrivate *priv;
+ ClutterTransition *destroyedTransition;
+ GSList *iter;
+ XfdashboardAnimationEntry *entry;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(self));
+ g_return_if_fail(CLUTTER_IS_TRANSITION(inUserData));
+
+ priv=self->priv;
+ destroyedTransition=CLUTTER_TRANSITION(inUserData);
+
+ /* Only handle stopped transition if it reached end of timeline because this
+ * signal is emitted either the transition has been stopped manually or
+ * programmically or it has reached the end of timeline even after all repeats
+ * has passed. So we have to check the "is-finished" parameter.
+ */
+ if(!isFinished)
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Do not remove entry for manually stopped transition of animation '%s'",
+ priv->id);
+#endif
+ return;
+ }
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Stopped animation '%s'",
+ priv->id);
+
+ /* Find entries to remove from list of entries */
+ for(iter=priv->entries; iter; iter=g_slist_next(iter))
+ {
+ /* Get entry */
+ entry=(XfdashboardAnimationEntry*)iter->data;
+ if(!entry) continue;
+
+ /* Check if the currently iterated entry must be freed */
+ if(entry->transition==destroyedTransition)
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Transition %s@%p of actor %s@%p stopped, removing entry %p from animation list of animation '%s'",
+ G_OBJECT_TYPE_NAME(destroyedTransition),
+ destroyedTransition,
+ G_OBJECT_TYPE_NAME(entry->actor),
+ entry->actor,
+ iter->data,
+ priv->id);
+#endif
+
+ priv->entries=g_slist_remove_link(priv->entries, iter);
+ _xfdashboard_animation_entry_free(entry);
+ g_slist_free_1(iter);
+ }
+ }
+
+ /* If list of entries is empty now, remove animation */
+ if(g_slist_length(priv->entries)==0)
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Animation list is empty after stopped transition, unreffing animation '%s'",
+ priv->id);
+#endif
+ g_object_unref(self);
+ }
+}
+
+/* The actor, we added a transition to, is going to be destroyed, so remove entry
+ * from list. When removing entry from list of entries, we unref objects and
+ * disconnect signal handlers automatically. No need to do it here.
+ */
+static void _xfdashboard_animation_on_actor_destroyed(XfdashboardAnimation *self,
+ gpointer inUserData)
+{
+ XfdashboardAnimationPrivate *priv;
+ ClutterActor *destroyedActor;
+ GSList *iter;
+ XfdashboardAnimationEntry *entry;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(self));
+ g_return_if_fail(CLUTTER_IS_ACTOR(inUserData));
+
+ priv=self->priv;
+ destroyedActor=CLUTTER_ACTOR(inUserData);
+
+ /* Find entries to remove from list of entries */
+ for(iter=priv->entries; iter; iter=g_slist_next(iter))
+ {
+ /* Get entry */
+ entry=(XfdashboardAnimationEntry*)iter->data;
+ if(!entry) continue;
+
+ /* Check if the currently iterated entry must be freed */
+ if(entry->actor==destroyedActor)
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Actor %s@%p destroyed, removing entry %p from animation list of animation '%s'",
+ G_OBJECT_TYPE_NAME(destroyedActor),
+ destroyedActor,
+ iter->data,
+ priv->id);
+#endif
+
+ priv->entries=g_slist_remove_link(priv->entries, iter);
+ _xfdashboard_animation_entry_free(entry);
+ g_slist_free_1(iter);
+ }
+ }
+
+ /* If list of entries is empty now, remove animation */
+ if(g_slist_length(priv->entries)==0)
+ {
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Animation list is empty after destroyed actor, unreffing animation '%s'",
+ priv->id);
+#endif
+ g_object_unref(self);
+ }
+}
+
+/* Adds a transition group to an actor for this animation */
+static void _xfdashboard_animation_real_add_animation(XfdashboardAnimation *self,
+ ClutterActor *inActor,
+ ClutterTransition *inTransition)
+{
+ XfdashboardAnimationPrivate *priv;
+ XfdashboardAnimationEntry *data;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(self));
+ g_return_if_fail(CLUTTER_IS_ACTOR(inActor));
+ g_return_if_fail(CLUTTER_IS_TRANSITION(inTransition));
+
+ priv=self->priv;
+
+ /* Create animation entry data */
+ data=g_new0(XfdashboardAnimationEntry, 1);
+ if(!data)
+ {
+ g_critical(_("Cannot allocate memory for animation entry with actor '%s' at animation '%s'"),
+ G_OBJECT_TYPE_NAME(inActor),
+ priv->id);
+ return;
+ }
+
+ data->self=self;
+ data->actor=g_object_ref(inActor);
+ data->transition=g_object_ref(inTransition);
+ data->actorDestroyID=g_signal_connect_swapped(inActor,
+ "destroy",
+ G_CALLBACK(_xfdashboard_animation_on_actor_destroyed),
+ self);
+ data->transitionStoppedID=g_signal_connect_swapped(inTransition,
+ "stopped",
+ G_CALLBACK(_xfdashboard_animation_on_transition_stopped),
+ self);
+ data->newFrameSignalID=g_signal_connect(inTransition,
+ "new-frame",
+ G_CALLBACK(_xfdashboard_animation_on_transition_new_frame),
+ data);
+
+ /* Add animation entry data to list to entries */
+ priv->entries=g_slist_prepend(priv->entries, data);
+}
+
+/* Set animation ID */
+static void _xfdashboard_animation_set_id(XfdashboardAnimation *self, const gchar *inID)
+{
+ XfdashboardAnimationPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(self));
+ g_return_if_fail(!inID || *inID);
+
+ priv=self->priv;
+
+ /* Set value if changed */
+ if(g_strcmp0(priv->id, inID)!=0)
+ {
+ if(priv->id)
+ {
+ g_free(priv->id);
+ priv->id=NULL;
+ }
+
+ if(inID)
+ {
+ priv->id=g_strdup(inID);
+ }
+
+ /* Notify about property change */
+ g_object_notify_by_pspec(G_OBJECT(self), XfdashboardAnimationProperties[PROP_ID]);
+ }
+}
+
+/* IMPLEMENTATION: GObject */
+
+/* Dispose this object */
+static void _xfdashboard_animation_dispose(GObject *inObject)
+{
+ XfdashboardAnimation *self=XFDASHBOARD_ANIMATION(inObject);
+ XfdashboardAnimationPrivate *priv=self->priv;
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Destroying animation '%s'",
+ priv->id);
+
+ /* Call done callback first before any private data is destroyed */
+ if(priv->doneCallback)
+ {
+ /* Call done callback function */
+ (priv->doneCallback)(self, priv->doneUserData);
+
+ /* Unset done callback data */
+ priv->doneCallback=NULL;
+ priv->doneUserData=NULL;
+ }
+
+ /* Release our allocated variables
+ * Order is important as the ID MUST be released at last!
+ */
+ if(priv->entries)
+ {
+ g_slist_free_full(priv->entries, (GDestroyNotify)_xfdashboard_animation_entry_free);
+ priv->entries=NULL;
+ }
+
+ if(priv->id)
+ {
+ g_free(priv->id);
+ priv->id=NULL;
+ }
+
+ /* Call parent's class dispose method */
+ G_OBJECT_CLASS(xfdashboard_animation_parent_class)->dispose(inObject);
+}
+
+/* Set/get properties */
+static void _xfdashboard_animation_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardAnimation *self=XFDASHBOARD_ANIMATION(inObject);
+
+ switch(inPropID)
+ {
+ case PROP_ID:
+ _xfdashboard_animation_set_id(self, g_value_get_string(inValue));
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void _xfdashboard_animation_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardAnimation *self=XFDASHBOARD_ANIMATION(inObject);
+ XfdashboardAnimationPrivate *priv=self->priv;
+
+ switch(inPropID)
+ {
+ case PROP_ID:
+ g_value_set_string(outValue, priv->id);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+void xfdashboard_animation_class_init(XfdashboardAnimationClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ klass->add_animation=_xfdashboard_animation_real_add_animation;
+
+ gobjectClass->dispose=_xfdashboard_animation_dispose;
+ gobjectClass->set_property=_xfdashboard_animation_set_property;
+ gobjectClass->get_property=_xfdashboard_animation_get_property;
+
+ /* Define properties */
+ /**
+ * XfdashboardAnimation:id:
+ *
+ * A string with the animation ID
+ */
+ XfdashboardAnimationProperties[PROP_ID]=
+ g_param_spec_string("id",
+ _("ID"),
+ _("The animation ID"),
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardAnimationProperties);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+void xfdashboard_animation_init(XfdashboardAnimation *self)
+{
+ XfdashboardAnimationPrivate *priv;
+
+ priv=self->priv=xfdashboard_animation_get_instance_private(self);
+
+ /* Set up default values */
+ priv->id=NULL;
+ priv->entries=NULL;
+}
+
+
+/* IMPLEMENTATION: Public API */
+
+/**
+ * xfdashboard_animation_new:
+ * @inSender: A #ClutterActor emitting the animation signal
+ * @inSignal: A string containing the signal emitted at sending actor
+ *
+ * Creates a new animation of type #XfdashboardAnimation matching the sending
+ * actor at @inSender and the emitted signal at @inSignal.
+ *
+ * This function is the logical equivalent of:
+ *
+ * |[<!-- language="C" -->
+ * XfdashboardTheme *theme;
+ * XfdashboardThemeAnimation *theme_animations;
+ *
+ * theme=xfdashboard_application_get_theme(NULL);
+ * theme_animations=xfdashboard_theme_get_animation(theme);
+ * animation=xfdashboard_theme_animation_create(theme_animations, inSender, inSignal);
+ * ]|
+ *
+ * Return value: (transfer full): The instance of #XfdashboardAnimation.
+ */
+XfdashboardAnimation* xfdashboard_animation_new(XfdashboardActor *inSender, const gchar *inSignal)
+{
+ XfdashboardTheme *theme;
+ XfdashboardThemeAnimation *themeAnimation;
+ XfdashboardAnimation *animation;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
+ g_return_val_if_fail(inSignal && *inSignal, NULL);
+
+ theme=xfdashboard_application_get_theme(NULL);
+ themeAnimation=xfdashboard_theme_get_animation(theme);
+ animation=xfdashboard_theme_animation_create(themeAnimation, inSender, inSignal);
+
+ return(animation);
+}
+
+/**
+ * xfdashboard_animation_get_id:
+ * @self: A #XfdashboardAnimation
+ *
+ * Retrieves the animation ID of @self.
+ *
+ * Return value: A string with animations's ID
+ */
+const gchar* xfdashboard_animation_get_id(XfdashboardAnimation *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_ANIMATION(self), NULL);
+
+ return(self->priv->id);
+}
+
+/**
+ * xfdashboard_animation_run:
+ * @self: A #XfdashboardAnimation
+ * @inCallback: Function to call when animation is destroyed
+ * @inUserData: Data to pass to callback function
+ *
+ * Starts the animation of @self. The callback function @inCallback
+ * with this animation and the user-data at @inUserData is called
+ * when the animation is destroyed, either is has reached the end
+ * of its timeline or was stopped before.
+ */
+void xfdashboard_animation_run(XfdashboardAnimation *self,
+ XfdashboardAnimationDoneCallback inCallback,
+ gpointer inUserData)
+{
+ XfdashboardAnimationPrivate *priv;
+ GSList *iter;
+ XfdashboardAnimationEntry *entry;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(self));
+
+ priv=self->priv;
+
+ /* Store callback function and its user data which is called
+ * in destruction function when animation is done, e.g.
+ * animation completed or was removed before it completed.
+ */
+ priv->doneCallback=inCallback;
+ priv->doneUserData=inUserData;
+
+ /* Add all transition to their actors now */
+ for(iter=priv->entries; iter; iter=g_slist_next(iter))
+ {
+ /* Get entry */
+ entry=(XfdashboardAnimationEntry*)iter->data;
+ if(!entry) continue;
+
+ /* Add transition to actor which will start immediately */
+ clutter_actor_add_transition(entry->actor, priv->id, entry->transition);
+#ifdef DEBUG
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Animation '%s' added transition %p to actor %s@%p",
+ priv->id,
+ entry->transition,
+ G_OBJECT_TYPE_NAME(entry->actor),
+ entry->actor);
+#endif
+ }
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Started animation '%s'",
+ priv->id);
+}
diff --git a/libxfdashboard/animation.h b/libxfdashboard/animation.h
new file mode 100644
index 0000000..9380e00
--- /dev/null
+++ b/libxfdashboard/animation.h
@@ -0,0 +1,105 @@
+/*
+ * animation: A animation for an actor
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef __LIBXFDASHBOARD_ANIMATION__
+#define __LIBXFDASHBOARD_ANIMATION__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <clutter/clutter.h>
+
+#include <libxfdashboard/actor.h>
+
+G_BEGIN_DECLS
+
+/* Object declaration */
+#define XFDASHBOARD_TYPE_ANIMATION (xfdashboard_animation_get_type())
+#define XFDASHBOARD_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_ANIMATION, XfdashboardAnimation))
+#define XFDASHBOARD_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_ANIMATION))
+#define XFDASHBOARD_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_ANIMATION, XfdashboardAnimationClass))
+#define XFDASHBOARD_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_ANIMATION))
+#define XFDASHBOARD_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_ANIMATION, XfdashboardAnimationClass))
+
+typedef struct _XfdashboardAnimation XfdashboardAnimation;
+typedef struct _XfdashboardAnimationClass XfdashboardAnimationClass;
+typedef struct _XfdashboardAnimationPrivate XfdashboardAnimationPrivate;
+
+/**
+ * XfdashboardAnimation:
+ *
+ * The #XfdashboardAnimation structure contains only private data and
+ * should be accessed using the provided API
+ */struct _XfdashboardAnimation
+{
+ /*< private >*/
+ /* Parent instance */
+ GObject parent_instance;
+
+ /* Private structure */
+ XfdashboardAnimationPrivate *priv;
+};
+
+/**
+ * XfdashboardAnimationClass:
+ *
+ * The #XfdashboardAnimationClass structure contains only private data
+ */
+struct _XfdashboardAnimationClass
+{
+ /*< private >*/
+ /* Parent class */
+ GObjectClass parent_class;
+
+ /*< public >*/
+ /* Virtual functions */
+ void (*add_animation)(XfdashboardAnimation *self, ClutterActor *inActor, ClutterTransition *inTransition);
+ void (*started)(XfdashboardAnimation *self);
+ void (*stopped)(XfdashboardAnimation *self);
+};
+
+/* Public API */
+GType xfdashboard_animation_get_type(void) G_GNUC_CONST;
+
+XfdashboardAnimation* xfdashboard_animation_new(XfdashboardActor *inSender, const gchar *inSignal);
+
+const gchar* xfdashboard_animation_get_id(XfdashboardAnimation *self);
+
+/**
+ * XfdashboardAnimationDoneCallback:
+ * @inAnimation: The animation which completed
+ * @inUserData: Data passed to the function, set with xfdashboard_animation_run()
+ *
+ * A callback called when animation, started by xfdashboard_animation_run(),
+ * has completed and will be destroyed.
+ */
+typedef void (*XfdashboardAnimationDoneCallback)(XfdashboardAnimation *inAnimation, gpointer inUserData);
+
+void xfdashboard_animation_run(XfdashboardAnimation *self,
+ XfdashboardAnimationDoneCallback inCallback,
+ gpointer inUserData);
+
+G_END_DECLS
+
+#endif /* __LIBXFDASHBOARD_ANIMATION__ */
diff --git a/libxfdashboard/application.c b/libxfdashboard/application.c
index 7c87afb..85a241f 100644
--- a/libxfdashboard/application.c
+++ b/libxfdashboard/application.c
@@ -645,6 +645,8 @@ static gint _xfdashboard_application_handle_command_line_arguments(XfdashboardAp
{ "images", XFDASHBOARD_DEBUG_IMAGES },
{ "windows", XFDASHBOARD_DEBUG_WINDOWS },
{ "window-tracker", XFDASHBOARD_DEBUG_WINDOWS },
+ { "animation", XFDASHBOARD_DEBUG_ANIMATION },
+ { "animations", XFDASHBOARD_DEBUG_ANIMATION },
};
/* Parse debug flags */
diff --git a/libxfdashboard/collapse-box.c b/libxfdashboard/collapse-box.c
index 2d538ea..71820c9 100644
--- a/libxfdashboard/collapse-box.c
+++ b/libxfdashboard/collapse-box.c
@@ -34,6 +34,7 @@
#include <libxfdashboard/focus-manager.h>
#include <libxfdashboard/utils.h>
#include <libxfdashboard/compat.h>
+#include <libxfdashboard/animation.h>
/* Define this class in GObject system */
@@ -55,6 +56,7 @@ struct _XfdashboardCollapseBoxPrivate
gboolean expandedByPointer;
gboolean expandedByFocus;
+ XfdashboardAnimation *expandCollapseAnimation;
};
G_DEFINE_TYPE_WITH_CODE(XfdashboardCollapseBox,
@@ -239,6 +241,22 @@ static void _xfdashboard_collapse_box_on_focus_changed(XfdashboardCollapseBox *s
}
}
+/* Collapse or expand animation has stopped and is done */
+static void _xfdashboard_collapse_box_animation_done(XfdashboardAnimation *inAnimation, gpointer inUserData)
+{
+ XfdashboardCollapseBox *self;
+ XfdashboardCollapseBoxPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_ANIMATION(inAnimation));
+ g_return_if_fail(XFDASHBOARD_IS_COLLAPSE_BOX(inUserData));
+
+ self=XFDASHBOARD_COLLAPSE_BOX(inUserData);
+ priv=self->priv;
+
+ /* Collapse or expand animation is done, so reset pointer to running animation */
+ priv->expandCollapseAnimation=NULL;
+}
+
/* IMPLEMENTATION: Interface ClutterContainer */
/* An actor was added to container */
@@ -629,6 +647,7 @@ static void xfdashboard_collapse_box_init(XfdashboardCollapseBox *self)
priv->focusChangedSignalID=0;
priv->expandedByPointer=FALSE;
priv->expandedByFocus=FALSE;
+ priv->expandCollapseAnimation=NULL;
/* Set up actor */
clutter_actor_set_reactive(CLUTTER_ACTOR(self), TRUE);
@@ -666,6 +685,7 @@ gboolean xfdashboard_collapse_box_get_collapsed(XfdashboardCollapseBox *self)
void xfdashboard_collapse_box_set_collapsed(XfdashboardCollapseBox *self, gboolean inCollapsed)
{
XfdashboardCollapseBoxPrivate *priv;
+ XfdashboardAnimation *animation;
g_return_if_fail(XFDASHBOARD_IS_COLLAPSE_BOX(self));
@@ -674,6 +694,25 @@ void xfdashboard_collapse_box_set_collapsed(XfdashboardCollapseBox *self, gboole
/* Only set value if it changes */
if(inCollapsed!=priv->isCollapsed)
{
+ /* Create animation for new expand/collapse state before stopping
+ * any possibly running animation for the old expand/collapse state,
+ * so that the new animation will take over any property value in
+ * the middle of currently running animation. If we stop the animation
+ * beforehand then the new animation would take the initial value in
+ * a state assuming that the current animation has completed.
+ */
+ animation=xfdashboard_animation_new(XFDASHBOARD_ACTOR(self), inCollapsed ? "collapse" : "expand");
+
+ /* Expand/collapse state changed, so stop any running animation */
+ if(priv->expandCollapseAnimation)
+ {
+ g_object_unref(priv->expandCollapseAnimation);
+ priv->expandCollapseAnimation=NULL;
+ }
+
+ /* Store new animation */
+ priv->expandCollapseAnimation=animation;
+
/* Set new value */
priv->isCollapsed=inCollapsed;
clutter_actor_queue_relayout(CLUTTER_ACTOR(self));
@@ -683,6 +722,9 @@ void xfdashboard_collapse_box_set_collapsed(XfdashboardCollapseBox *self, gboole
/* Emit signal */
g_signal_emit(self, XfdashboardCollapseBoxSignals[SIGNAL_COLLAPSED_CHANGED], 0, priv->isCollapsed);
+
+ /* Start animation */
+ xfdashboard_animation_run(priv->expandCollapseAnimation, _xfdashboard_collapse_box_animation_done, self);
}
}
diff --git a/libxfdashboard/debug.h b/libxfdashboard/debug.h
index a43302b..7510016 100644
--- a/libxfdashboard/debug.h
+++ b/libxfdashboard/debug.h
@@ -44,6 +44,7 @@ G_BEGIN_DECLS
* @XFDASHBOARD_DEBUG_IMAGES: Images related debug message (image cache etc.)
* @XFDASHBOARD_DEBUG_WINDOWS: Windows related debug message (window tracker, workspaces, windows, monitors etc.)
* @XFDASHBOARD_DEBUG_PLUGINS: Plug-ins related debug message (plugin manager and plugin base class)
+ * @XFDASHBOARD_DEBUG_ANIMATION: Animation related debug message
*
* Debug categories
*/
@@ -57,6 +58,7 @@ typedef enum /*< skip,flags,prefix=XFDASHBOARD_DEBUG >*/
XFDASHBOARD_DEBUG_IMAGES = 1 << 5,
XFDASHBOARD_DEBUG_WINDOWS = 1 << 6,
XFDASHBOARD_DEBUG_PLUGINS = 1 << 7,
+ XFDASHBOARD_DEBUG_ANIMATION = 1 << 8
} XfdashboardDebugFlags;
#ifdef XFDASHBOARD_ENABLE_DEBUG
diff --git a/libxfdashboard/theme-animation.c b/libxfdashboard/theme-animation.c
new file mode 100644
index 0000000..b90537b
--- /dev/null
+++ b/libxfdashboard/theme-animation.c
@@ -0,0 +1,998 @@
+/*
+ * theme-animation: A theme used for building animations by XML files
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/theme-animation.h>
+
+#include <glib/gi18n-lib.h>
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <libxfdashboard/transition-group.h>
+#include <libxfdashboard/stylable.h>
+#include <libxfdashboard/enums.h>
+#include <libxfdashboard/utils.h>
+#include <libxfdashboard/compat.h>
+#include <libxfdashboard/debug.h>
+
+
+/* Define this class in GObject system */
+struct _XfdashboardThemeAnimationPrivate
+{
+ /* Instance related */
+ GSList *specs;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardThemeAnimation,
+ xfdashboard_theme_animation,
+ G_TYPE_OBJECT)
+
+
+/* IMPLEMENTATION: Private variables and methods */
+enum
+{
+ XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER=0,
+ XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE=1
+};
+
+typedef struct _XfdashboardThemeAnimationSpec XfdashboardThemeAnimationSpec;
+struct _XfdashboardThemeAnimationSpec
+{
+ gint refCount;
+ gchar *id;
+ XfdashboardCssSelector *senderSelector;
+ gchar *signal;
+ GSList *targets;
+};
+
+typedef struct _XfdashboardThemeAnimationTargets XfdashboardThemeAnimationTargets;
+struct _XfdashboardThemeAnimationTargets
+{
+ gint refCount;
+ XfdashboardCssSelector *targetSelector;
+ guint origin;
+ ClutterTimeline *timeline;
+ GSList *properties;
+};
+
+typedef struct _XfdashboardThemeAnimationTargetsProperty XfdashboardThemeAnimationTargetsProperty;
+struct _XfdashboardThemeAnimationTargetsProperty
+{
+ gint refCount;
+ gchar *name;
+ GValue from;
+ GValue to;
+};
+
+/* Create, destroy, ref and unref animation targets data */
+static XfdashboardThemeAnimationTargetsProperty* _xfdashboard_theme_animation_targets_property_new(const gchar *inPropertyName,
+ const gchar *inPropertyFrom,
+ const gchar *inPropertyTo)
+{
+ XfdashboardThemeAnimationTargetsProperty *data;
+
+ g_return_val_if_fail(inPropertyName && *inPropertyName, NULL);
+ g_return_val_if_fail(!inPropertyFrom || *inPropertyFrom, NULL);
+ g_return_val_if_fail(!inPropertyTo || *inPropertyTo, NULL);
+
+ /* Create animation targets property data */
+ data=g_new0(XfdashboardThemeAnimationTargetsProperty, 1);
+ if(!data)
+ {
+ g_critical(_("Cannot allocate memory for animation targets property '%s'"), inPropertyName);
+ return(NULL);
+ }
+
+ data->refCount=1;
+ data->name=g_strdup(inPropertyName);
+
+ if(inPropertyFrom)
+ {
+ g_value_init(&data->from, G_TYPE_STRING);
+ g_value_set_string(&data->from, inPropertyFrom);
+ }
+
+ if(inPropertyTo)
+ {
+ g_value_init(&data->to, G_TYPE_STRING);
+ g_value_set_string(&data->to, inPropertyTo);
+ }
+
+ return(data);
+}
+
+static void _xfdashboard_theme_animation_targets_property_free(XfdashboardThemeAnimationTargetsProperty *inData)
+{
+ g_return_if_fail(inData);
+
+ /* Free property data */
+ if(inData->name) g_free(inData->name);
+ g_value_unset(&inData->from);
+ g_value_unset(&inData->to);
+ g_free(inData);
+}
+
+static XfdashboardThemeAnimationTargetsProperty* _xfdashboard_theme_animation_targets_property_ref(XfdashboardThemeAnimationTargetsProperty *inData)
+{
+ g_return_val_if_fail(inData, NULL);
+
+ inData->refCount++;
+ return(inData);
+}
+
+static void _xfdashboard_theme_animation_targets_property_unref(XfdashboardThemeAnimationTargetsProperty *inData)
+{
+ g_return_if_fail(inData);
+
+ if(inData->refCount==1) _xfdashboard_theme_animation_targets_property_free(inData);
+ else inData->refCount--;
+}
+
+/* Create, destroy, ref and unref animation targets data */
+static XfdashboardThemeAnimationTargets* _xfdashboard_theme_animation_targets_new(XfdashboardCssSelector *inTargetSelector,
+ gint inOrigin,
+ ClutterTimeline *inTimelineSource)
+{
+ XfdashboardThemeAnimationTargets *data;
+
+ g_return_val_if_fail(!inTargetSelector || XFDASHBOARD_IS_CSS_SELECTOR(inTargetSelector), NULL);
+ g_return_val_if_fail(inOrigin>=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER && inOrigin<=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_STAGE, NULL);
+ g_return_val_if_fail(CLUTTER_IS_TIMELINE(inTimelineSource), NULL);
+
+ /* Create animation targets data */
+ data=g_new0(XfdashboardThemeAnimationTargets, 1);
+ if(!data)
+ {
+ gchar *selector;
+
+ selector=xfdashboard_css_selector_to_string(inTargetSelector);
+ g_critical(_("Cannot allocate memory for animation targets data with selector '%s'"), selector);
+ g_free(selector);
+
+ return(NULL);
+ }
+
+ data->refCount=1;
+ data->targetSelector=(inTargetSelector ? g_object_ref(inTargetSelector) : NULL);
+ data->origin=inOrigin;
+ data->timeline=g_object_ref(inTimelineSource);
+ data->properties=NULL;
+
+ return(data);
+}
+
+static void _xfdashboard_theme_animation_targets_free(XfdashboardThemeAnimationTargets *inData)
+{
+ g_return_if_fail(inData);
+
+#ifdef DEBUG
+ if(inData->refCount>1)
+ {
+ g_critical(_("Freeing animation targets data at %p with a reference counter of %d greater than one"),
+ inData,
+ inData->refCount);
+ }
+#endif
+
+ /* Release allocated resources */
+ if(inData->targetSelector) g_object_unref(inData->targetSelector);
+ if(inData->timeline) g_object_unref(inData->timeline);
+ if(inData->properties) g_slist_free_full(inData->properties, (GDestroyNotify)_xfdashboard_theme_animation_targets_property_unref);
+ g_free(inData);
+}
+
+static XfdashboardThemeAnimationTargets* _xfdashboard_theme_animation_targets_ref(XfdashboardThemeAnimationTargets *inData)
+{
+ g_return_val_if_fail(inData, NULL);
+
+ inData->refCount++;
+ return(inData);
+}
+
+static void _xfdashboard_theme_animation_targets_unref(XfdashboardThemeAnimationTargets *inData)
+{
+ g_return_if_fail(inData);
+
+ if(inData->refCount==1) _xfdashboard_theme_animation_targets_free(inData);
+ else inData->refCount--;
+}
+
+/* Add property name and from-to values to animation targets data */
+static void _xfdashboard_theme_animation_targets_add_property(XfdashboardThemeAnimationTargets *inData,
+ XfdashboardThemeAnimationTargetsProperty *inProperty)
+{
+ g_return_if_fail(inData);
+ g_return_if_fail(inProperty);
+
+ /* Add property to animation targets data */
+ inData->properties=g_slist_prepend(inData->properties, _xfdashboard_theme_animation_targets_property_ref(inProperty));
+}
+
+
+/* Create, destroy, ref and unref animation specification data */
+static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_spec_new(const gchar *inID,
+ XfdashboardCssSelector *inSenderSelector,
+ const gchar *inSignal)
+{
+ XfdashboardThemeAnimationSpec *data;
+
+ g_return_val_if_fail(inID && *inID, NULL);
+ g_return_val_if_fail(XFDASHBOARD_IS_CSS_SELECTOR(inSenderSelector), NULL);
+ g_return_val_if_fail(inSignal && *inSignal, NULL);
+
+ /* Create animation specification data */
+ data=g_new0(XfdashboardThemeAnimationSpec, 1);
+ if(!data)
+ {
+ gchar *selector;
+
+ selector=xfdashboard_css_selector_to_string(inSenderSelector);
+ g_critical(_("Cannot allocate memory for animation specification data with sender '%s' and signal '%s'"),
+ selector,
+ inSignal);
+ g_free(selector);
+
+ return(NULL);
+ }
+
+ data->refCount=1;
+ data->id=g_strdup(inID);
+ data->senderSelector=g_object_ref(inSenderSelector);
+ data->signal=g_strdup(inSignal);
+ data->targets=NULL;
+
+ return(data);
+}
+
+static void _xfdashboard_theme_animation_spec_free(XfdashboardThemeAnimationSpec *inData)
+{
+ g_return_if_fail(inData);
+
+#ifdef DEBUG
+ if(inData->refCount>1)
+ {
+ g_critical(_("Freeing animation specification data at %p with a reference counter of %d greater than one"),
+ inData,
+ inData->refCount);
+ }
+#endif
+
+ /* Release allocated resources */
+ if(inData->id) g_free(inData->id);
+ if(inData->senderSelector) g_object_unref(inData->senderSelector);
+ if(inData->signal) g_free(inData->signal);
+ if(inData->targets) g_slist_free_full(inData->targets, (GDestroyNotify)_xfdashboard_theme_animation_targets_unref);
+ g_free(inData);
+}
+
+static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_spec_ref(XfdashboardThemeAnimationSpec *inData)
+{
+ g_return_val_if_fail(inData, NULL);
+
+ inData->refCount++;
+ return(inData);
+}
+
+static void _xfdashboard_theme_animation_spec_unref(XfdashboardThemeAnimationSpec *inData)
+{
+ g_return_if_fail(inData);
+
+ if(inData->refCount==1) _xfdashboard_theme_animation_spec_free(inData);
+ else inData->refCount--;
+}
+
+/* Add a animation target to animation specification */
+static void _xfdashboard_theme_animation_spec_add_targets(XfdashboardThemeAnimationSpec *inData,
+ XfdashboardThemeAnimationTargets *inTargets)
+{
+ g_return_if_fail(inData);
+ g_return_if_fail(inTargets);
+
+ /* Add target to specification */
+ inData->targets=g_slist_prepend(inData->targets, _xfdashboard_theme_animation_targets_ref(inTargets));
+}
+
+/* Find best matching animation for sender and signal */
+static XfdashboardThemeAnimationSpec* _xfdashboard_theme_animation_find_animation_spec(XfdashboardThemeAnimation *self,
+ XfdashboardStylable *inSender,
+ const gchar *inSignal)
+{
+ XfdashboardThemeAnimationPrivate *priv;
+ GSList *iter;
+ gint score;
+ XfdashboardThemeAnimationSpec *spec;
+ gint bestScore;
+ XfdashboardThemeAnimationSpec *bestAnimation;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
+ g_return_val_if_fail(XFDASHBOARD_IS_STYLABLE(inSender), NULL);
+ g_return_val_if_fail(inSignal && *inSignal, NULL);
+
+ priv=self->priv;
+ bestScore=0;
+ bestAnimation=NULL;
+
+ /* Iterate through all animation specification and get its score against sender.
+ * If the iterated specification gets a higher score than the previous one,
+ * remember this specification for return value.
+ */
+ for(iter=priv->specs; iter; iter=g_slist_next(iter))
+ {
+ /* Get currently iterated specification */
+ spec=(XfdashboardThemeAnimationSpec*)iter->data;
+ if(!spec) continue;
+
+ /* Skip animation specification if its signal definition does not match
+ * the emitted signal.
+ */
+ if(g_strcmp0(spec->signal, inSignal)!=0) continue;
+
+ /* Get score of currently iterated specification against sender.
+ * Skip this iterated specification if score is zero or lower.
+ */
+ score=xfdashboard_css_selector_score(spec->senderSelector, inSender);
+ if(score<=0) continue;
+
+ /* If score is higher than the previous best matching one, then release
+ * old specificationr remembered and ref new one.
+ */
+ if(score>bestScore)
+ {
+ /* Release old remembered specification */
+ if(bestAnimation) _xfdashboard_theme_animation_spec_unref(bestAnimation);
+
+ /* Remember new one and take a reference */
+ bestScore=score;
+ bestAnimation=_xfdashboard_theme_animation_spec_ref(spec);
+ }
+ }
+
+ /* Return best matching animation specification found */
+ return(bestAnimation);
+}
+
+/* Find actors matching an animation target data */
+static gboolean _xfdashboard_theme_animation_find_actors_for_animation_targets_traverse_callback(ClutterActor *inActor, gpointer inUserData)
+{
+ GSList **actors=(GSList**)inUserData;
+
+ *actors=g_slist_prepend(*actors, inActor);
+
+ return(XFDASHBOARD_TRAVERSAL_CONTINUE);
+}
+
+static GSList* _xfdashboard_theme_animation_find_actors_for_animation_targets(XfdashboardThemeAnimation *self,
+ XfdashboardThemeAnimationTargets *inTargetSpec,
+ ClutterActor *inSender)
+{
+ ClutterActor *rootNode;
+ GSList *actors;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
+ g_return_val_if_fail(inTargetSpec, NULL);
+ g_return_val_if_fail(CLUTTER_IS_ACTOR(inSender), NULL);
+
+
+ /* Depending on origin at animation target data select root node to start
+ * traversal and collecting matching actors.
+ */
+ rootNode=NULL;
+ if(inTargetSpec->origin==XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER)
+ {
+ rootNode=inSender;
+ }
+
+ /* Traverse through actors beginning at the root node and collect each actor
+ * matching the target selector but only if a target selector is set. If
+ * target selector is NULL then set up a single-item list containing only
+ * the sender actor as list of actors found.
+ */
+ actors=NULL;
+ if(inTargetSpec->targetSelector)
+ {
+ xfdashboard_traverse_actor(rootNode,
+ inTargetSpec->targetSelector,
+ _xfdashboard_theme_animation_find_actors_for_animation_targets_traverse_callback,
+ &actors);
+ }
+ else
+ {
+ actors=g_slist_prepend(actors, inSender);
+ }
+
+ /* Correct order in list */
+ actors=g_slist_reverse(actors);
+
+ /* Return list of actors found */
+ return(actors);
+}
+
+
+/* IMPLEMENTATION: GObject */
+
+/* Dispose this object */
+static void _xfdashboard_theme_animation_dispose(GObject *inObject)
+{
+ XfdashboardThemeAnimation *self=XFDASHBOARD_THEME_ANIMATION(inObject);
+ XfdashboardThemeAnimationPrivate *priv=self->priv;
+
+ /* Release allocated resources */
+ if(priv->specs)
+ {
+ g_slist_free_full(priv->specs, (GDestroyNotify)_xfdashboard_theme_animation_spec_unref);
+ priv->specs=NULL;
+ }
+
+ /* Call parent's class dispose method */
+ G_OBJECT_CLASS(xfdashboard_theme_animation_parent_class)->dispose(inObject);
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+void xfdashboard_theme_animation_class_init(XfdashboardThemeAnimationClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->dispose=_xfdashboard_theme_animation_dispose;
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+void xfdashboard_theme_animation_init(XfdashboardThemeAnimation *self)
+{
+ XfdashboardThemeAnimationPrivate *priv;
+
+ priv=self->priv=xfdashboard_theme_animation_get_instance_private(self);
+
+ /* Set default values */
+ priv->specs=NULL;
+}
+
+/* IMPLEMENTATION: Errors */
+
+GQuark xfdashboard_theme_animation_error_quark(void)
+{
+ return(g_quark_from_static_string("xfdashboard-theme-animation-error-quark"));
+}
+
+/* IMPLEMENTATION: Public API */
+
+/* Create new instance */
+XfdashboardThemeAnimation* xfdashboard_theme_animation_new(void)
+{
+ return(XFDASHBOARD_THEME_ANIMATION(g_object_new(XFDASHBOARD_TYPE_THEME_ANIMATION, NULL)));
+}
+
+
+/* Load a XML file into theme */
+gboolean xfdashboard_theme_animation_add_file(XfdashboardThemeAnimation *self,
+ const gchar *inPath,
+ GError **outError)
+{
+ XfdashboardThemeAnimationPrivate *priv;
+ XfdashboardThemeAnimationSpec *spec;
+ gchar *id;
+ gchar *sender;
+ gchar *signal;
+ XfdashboardCssSelector *senderSelector;
+ ClutterTimeline *timeline;
+ gint timelineDelay;
+ gint timelineDuration;
+ guint timelineMode;
+ gint timelineRepeat;
+ XfdashboardThemeAnimationTargets *targets;
+ XfdashboardCssSelector *targetsSelector;
+ gint targetsOrigin;
+ XfdashboardThemeAnimationTargetsProperty *property;
+ gchar *propertyName;
+ gchar *propertyFrom;
+ gchar *propertyTo;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), FALSE);
+ g_return_val_if_fail(inPath!=NULL && *inPath!=0, FALSE);
+ g_return_val_if_fail(outError==NULL || *outError==NULL, FALSE);
+
+ priv=self->priv;
+
+ /* TODO: Replace with real loading code ;) */
+
+ /* <trigger id="expand-workspace" sender="#workspace-selector-collapse-box" signal="expand"> */
+ id="expand-workspace";
+ sender="XfdashboardCollapseBox";
+ signal="expand";
+
+ senderSelector=xfdashboard_css_selector_new_from_string(sender);
+ spec=_xfdashboard_theme_animation_spec_new(id, senderSelector, signal);
+ g_object_unref(senderSelector);
+
+ /* <timeline delay="0" duration="500" mode=ease-out-cubic" repeat="0" (optional, default=0) > */
+ timelineDelay=0;
+ timelineDuration=500;
+ timelineMode=CLUTTER_EASE_OUT_CUBIC;
+ timelineRepeat=0;
+
+ timeline=clutter_timeline_new(timelineDuration);
+ clutter_timeline_set_delay(timeline, timelineDelay);
+ clutter_timeline_set_progress_mode(timeline, timelineMode);
+ clutter_timeline_set_repeat_count(timeline, timelineRepeat);
+
+ /* <apply to="" (optional, default="" -> sender) origin="sender" (optional, default="sender") > */
+ targetsSelector=NULL;
+ targetsOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
+
+ targets=_xfdashboard_theme_animation_targets_new(targetsSelector, targetsOrigin, timeline);
+
+ /* <property name="collapse-fraction" from="" (optional, default="") to="" (optional, default="")> */
+ propertyName="width";
+ propertyFrom=NULL;
+ propertyTo=NULL;
+
+ property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
+
+ /* </property> */
+ _xfdashboard_theme_animation_targets_add_property(targets, property);
+ _xfdashboard_theme_animation_targets_property_unref(property);
+ property=NULL;
+
+ /* </apply> */
+ _xfdashboard_theme_animation_spec_add_targets(spec, targets);
+ _xfdashboard_theme_animation_targets_unref(targets);
+ targets=NULL;
+
+ /* </timeline> */
+ g_object_unref(timeline);
+ timeline=NULL;
+
+ /* </trigger> */
+ priv->specs=g_slist_prepend(priv->specs, spec);
+
+
+
+ /* <trigger id="collapse-workspace" sender="#workspace-selector-collapse-box" signal="collapse"> */
+ id="collapse-workspace";
+ sender="XfdashboardCollapseBox";
+ signal="collapse";
+
+ senderSelector=xfdashboard_css_selector_new_from_string(sender);
+ spec=_xfdashboard_theme_animation_spec_new(id, senderSelector, signal);
+ g_object_unref(senderSelector);
+
+ /* <timeline delay="0" duration="500" mode=ease-in-cubic" repeat="0" (optional, default=0) > */
+ timelineDelay=0;
+ timelineDuration=500;
+ timelineMode=CLUTTER_EASE_OUT_CUBIC;
+ timelineRepeat=0;
+
+ timeline=clutter_timeline_new(timelineDuration);
+ clutter_timeline_set_delay(timeline, timelineDelay);
+ clutter_timeline_set_progress_mode(timeline, timelineMode);
+ clutter_timeline_set_repeat_count(timeline, timelineRepeat);
+
+ /* <apply to="" (optional, default="" -> sender) origin="sender" (optional, default="sender") > */
+ targetsSelector=NULL;
+ targetsOrigin=XFDASHBOARD_THEME_ANIMATION_APPLY_TO_ORIGIN_SENDER;
+
+ targets=_xfdashboard_theme_animation_targets_new(targetsSelector, targetsOrigin, timeline);
+
+ /* <property name="collapse-fraction" from="" (optional, default="") to="" (optional, default="")> */
+ propertyName="width";
+ propertyFrom=NULL;
+ propertyTo=NULL;
+
+ property=_xfdashboard_theme_animation_targets_property_new(propertyName, propertyFrom, propertyTo);
+
+ /* </property> */
+ _xfdashboard_theme_animation_targets_add_property(targets, property);
+ _xfdashboard_theme_animation_targets_property_unref(property);
+ property=NULL;
+
+ /* </apply> */
+ _xfdashboard_theme_animation_spec_add_targets(spec, targets);
+ _xfdashboard_theme_animation_targets_unref(targets);
+ targets=NULL;
+
+ /* </timeline> */
+ g_object_unref(timeline);
+ timeline=NULL;
+
+ /* </trigger> */
+ priv->specs=g_slist_prepend(priv->specs, spec);
+
+
+ /* TODO: Just for testing animations */
+ g_message("Loading animations done!");
+
+ /* If we get here loading and parsing XML file was successful
+ * so return TRUE here
+ */
+ return(TRUE);
+}
+
+/* Build requested animation */
+XfdashboardAnimation* xfdashboard_theme_animation_create(XfdashboardThemeAnimation *self,
+ XfdashboardActor *inSender,
+ const gchar *inSignal)
+{
+ XfdashboardThemeAnimationSpec *spec;
+ XfdashboardAnimation *animation;
+ XfdashboardAnimationClass *animationClass;
+ GSList *iterTargets;
+ gint counterTargets;
+#ifdef DEBUG
+ gboolean doDebug=TRUE;
+#endif
+
+ g_return_val_if_fail(XFDASHBOARD_IS_THEME_ANIMATION(self), NULL);
+ g_return_val_if_fail(XFDASHBOARD_IS_ACTOR(inSender), NULL);
+ g_return_val_if_fail(inSignal && *inSignal, NULL);
+
+ animation=NULL;
+
+ /* Create empty animation */
+ animation=g_object_new(XFDASHBOARD_TYPE_ANIMATION,
+ NULL);
+ if(!animation)
+ {
+ g_critical(_("Cannot allocate memory for animation of sender '%s' and signal '%s'"),
+ G_OBJECT_TYPE_NAME(inSender),
+ inSignal);
+ return(NULL);
+ }
+
+ /* Get best matching animation specification for sender and signal.
+ * If no matching animation specification is found, return the empty one.
+ */
+ spec=_xfdashboard_theme_animation_find_animation_spec(self, XFDASHBOARD_STYLABLE(inSender), inSignal);
+ if(!spec)
+ {
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Could not find an animation specification for sender '%s' and signal '%s'",
+ G_OBJECT_TYPE_NAME(inSender),
+ inSignal);
+
+ /* Return NULL as no matching animation specification was found */
+ return(animation);
+ }
+
+ /* Create new animation for animation specification */
+ g_object_unref(animation);
+
+ animation=g_object_new(XFDASHBOARD_TYPE_ANIMATION,
+ "id", spec->id,
+ NULL);
+ if(!animation)
+ {
+ g_critical(_("Cannot allocate memory for animation of sender '%s' and signal '%s'"),
+ G_OBJECT_TYPE_NAME(inSender),
+ inSignal);
+ return(NULL);
+ }
+
+ animationClass=XFDASHBOARD_ANIMATION_GET_CLASS(animation);
+ if(!animationClass->add_animation)
+ {
+ g_warning(_("Will not be able to add animations to actors as object of type %s does not implement required virtual function XfdashboardAnimation::%s"), \
+ G_OBJECT_TYPE_NAME(self), \
+ "add_animation");
+ }
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Found animation specification '%s' for sender '%s' and signal '%s' with %d targets",
+ spec->id,
+ G_OBJECT_TYPE_NAME(inSender),
+ inSignal,
+ g_slist_length(spec->targets));
+
+ /* Iterate through animation targets of animation specification and create
+ * property transition for each target and property found.
+ */
+ for(counterTargets=0, iterTargets=spec->targets; iterTargets; iterTargets=g_slist_next(iterTargets), counterTargets++)
+ {
+ XfdashboardThemeAnimationTargets *targets;
+ GSList *actors;
+ GSList *iterActors;
+ gint counterActors;
+
+ /* Get currently iterate animation targets */
+ targets=(XfdashboardThemeAnimationTargets*)iterTargets->data;
+ if(!targets) continue;
+
+ /* Find targets to apply property transitions to */
+ actors=_xfdashboard_theme_animation_find_actors_for_animation_targets(self, targets, CLUTTER_ACTOR(inSender));
+ if(!actors) continue;
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Target #%d of animation specification '%s' applies to %d actors",
+ counterTargets,
+ spec->id,
+ g_slist_length(actors));
+
+ /* Iterate through actor, create a transition group to collect
+ * property transitions at, create a property transition for each
+ * property specified in animation target, determine "from" value
+ * where missing, add property transition to transition group and
+ * finally add transition group with currently iterated actor to
+ * animation object.
+ */
+ for(counterActors=0, iterActors=actors; iterActors; iterActors=g_slist_next(iterActors), counterActors++)
+ {
+ GSList *iterProperties;
+ ClutterActor *actor;
+ ClutterTransition *transitionGroup;
+ int counterProperties;
+
+ /* Get actor */
+ actor=(ClutterActor*)iterActors->data;
+ if(!actor) continue;
+
+ /* Create transition group to collect property transitions at */
+ transitionGroup=xfdashboard_transition_group_new();
+ if(!transitionGroup)
+ {
+ g_critical(_("Cannot allocate memory for transition group of animation specification '%s'"),
+ spec->id);
+
+ /* Release allocated resources */
+ g_slist_free(actors);
+ g_object_unref(animation);
+
+ return(NULL);
+ }
+
+ /* Clone timeline configuration from animation target */
+ clutter_timeline_set_duration(CLUTTER_TIMELINE(transitionGroup), clutter_timeline_get_duration(targets->timeline));
+ clutter_timeline_set_delay(CLUTTER_TIMELINE(transitionGroup), clutter_timeline_get_delay(targets->timeline));
+ clutter_timeline_set_progress_mode(CLUTTER_TIMELINE(transitionGroup), clutter_timeline_get_progress_mode(targets->timeline));
+ clutter_timeline_set_repeat_count(CLUTTER_TIMELINE(transitionGroup), clutter_timeline_get_repeat_count(targets->timeline));
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Created transition group at %p for %d properties for target #%d and actor #%d (%s@%p) of animation specification '%s'",
+ transitionGroup,
+ g_slist_length(targets->properties),
+ counterTargets,
+ counterActors,
+ G_OBJECT_TYPE_NAME(actor),
+ actor,
+ spec->id);
+
+ /* Iterate through properties and create a property transition
+ * with cloned timeline from animation target. Determine "from"
+ * value if missing in animation targets property specification
+ * and add the property transition to transition group.
+ */
+ for(counterProperties=0, iterProperties=targets->properties; iterProperties; iterProperties=g_slist_next(iterProperties), counterProperties++)
+ {
+ XfdashboardThemeAnimationTargetsProperty *propertyTargetSpec;
+ GParamSpec *propertySpec;
+ GValue fromValue=G_VALUE_INIT;
+ GValue toValue=G_VALUE_INIT;
+
+ /* Get target's property data to animate */
+ propertyTargetSpec=(XfdashboardThemeAnimationTargetsProperty*)iterProperties->data;
+ if(!propertyTargetSpec) continue;
+
+ /* Check if actor has property to animate */
+ propertySpec=g_object_class_find_property(G_OBJECT_GET_CLASS(actor), propertyTargetSpec->name);
+ if(!propertySpec)
+ {
+ g_warning(_("Cannot create animation for non-existing property '%s' at actor of type '%s'"),
+ propertyTargetSpec->name,
+ G_OBJECT_TYPE_NAME(actor));
+
+ /* Skip this property as it does not exist at actor */
+ continue;
+ }
+
+ /* If no "from" value is set at target's property data, get
+ * current value. Otherwise convert "from" value from target's
+ * property data to expected type of property.
+ */
+ if(G_VALUE_TYPE(&propertyTargetSpec->from)!=G_TYPE_INVALID)
+ {
+ g_value_init(&fromValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
+ if(!g_value_transform(&propertyTargetSpec->from, &fromValue))
+ {
+ g_warning(_("Could not transform 'from'-value of '%s' for property '%s' to type %s of class %s"),
+ g_value_get_string(&propertyTargetSpec->from),
+ propertyTargetSpec->name,
+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
+ G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)));
+
+ /* Unset "from" value to skip it, means it will no transition will be
+ * create and it will not be added to transition group.
+ */
+ g_value_unset(&fromValue);
+ }
+#ifdef DEBUG
+ if(doDebug)
+ {
+ gchar *valueString;
+
+ valueString=g_strdup_value_contents(&propertyTargetSpec->from);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "%s 'from'-value %s of type %s for property '%s' to type %s of class %s for target #%d and actor #%d (%s@%p) of animation specification '%s'",
+ (G_VALUE_TYPE(&fromValue)!=G_TYPE_INVALID) ? "Converted" : "Could not convert",
+ valueString,
+ G_VALUE_TYPE_NAME(&propertyTargetSpec->from),
+ propertyTargetSpec->name,
+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
+ G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)),
+ counterTargets,
+ counterActors,
+ G_OBJECT_TYPE_NAME(actor),
+ actor,
+ spec->id);
+ g_free(valueString);
+ }
+#endif
+ }
+ else
+ {
+ g_value_init(&fromValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
+ g_object_get_property(G_OBJECT(actor),
+ propertyTargetSpec->name,
+ &fromValue);
+#ifdef DEBUG
+ if(doDebug)
+ {
+ gchar *valueString;
+
+ valueString=g_strdup_value_contents(&fromValue);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Fetching current 'from'-value %s for property '%s' from target #%d and actor #%d (%s@%p) of animation specification '%s' as no 'from' value was specified",
+ valueString,
+ propertyTargetSpec->name,
+ counterTargets,
+ counterActors,
+ G_OBJECT_TYPE_NAME(actor),
+ actor,
+ spec->id);
+ g_free(valueString);
+ }
+#endif
+ }
+
+ /* If "to" value is set at target's property data, convert it
+ * from target's property data to expected type of property.
+ */
+ if(G_VALUE_TYPE(&propertyTargetSpec->to)!=G_TYPE_INVALID)
+ {
+ g_value_init(&toValue, G_PARAM_SPEC_VALUE_TYPE(propertySpec));
+ if(!g_value_transform(&propertyTargetSpec->to, &toValue))
+ {
+ g_warning(_("Could not transform 'to'-value of '%s' for property '%s' to type %s of class %s"),
+ g_value_get_string(&propertyTargetSpec->to),
+ propertyTargetSpec->name,
+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
+ G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)));
+
+ /* Unset "to" value to prevent setting it at transition.
+ * The animation will set a value when starting the
+ * animation.
+ */
+ g_value_unset(&toValue);
+ }
+#ifdef DEBUG
+ if(doDebug)
+ {
+ gchar *valueString;
+
+ valueString=g_strdup_value_contents(&propertyTargetSpec->to);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "%s 'to'-value %s of type %s for property '%s' to type %s of class %s for target #%d and actor #%d (%s@%p) of animation specification '%s'",
+ (G_VALUE_TYPE(&toValue)!=G_TYPE_INVALID) ? "Converted" : "Could not convert",
+ valueString,
+ G_VALUE_TYPE_NAME(&propertyTargetSpec->to),
+ propertyTargetSpec->name,
+ g_type_name(G_PARAM_SPEC_VALUE_TYPE(propertySpec)),
+ G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(actor)),
+ counterTargets,
+ counterActors,
+ G_OBJECT_TYPE_NAME(actor),
+ actor,
+ spec->id);
+ g_free(valueString);
+ }
+#endif
+ }
+
+ /* Create property transition for property with cloned timeline
+ * and add this new transition to transition group if from value
+ * is not invalid.
+ */
+ if(G_VALUE_TYPE(&fromValue)!=G_TYPE_INVALID)
+ {
+ ClutterTransition *propertyTransition;
+
+ /* Create property transition */
+ propertyTransition=clutter_property_transition_new(propertyTargetSpec->name);
+ if(!propertyTransition)
+ {
+ g_critical(_("Cannot allocate memory for transition of property '%s' of animation specification '%s'"),
+ propertyTargetSpec->name,
+ spec->id);
+
+ /* Release allocated resources */
+ g_value_unset(&fromValue);
+ g_value_unset(&toValue);
+
+ /* Skip this property transition */
+ continue;
+ }
+
+ /* Set "from" value */
+ clutter_transition_set_from_value(propertyTransition, &fromValue);
+
+ /* Set "to" value if valid */
+ if(G_VALUE_TYPE(&toValue)!=G_TYPE_INVALID)
+ {
+ clutter_transition_set_to_value(propertyTransition, &toValue);
+ }
+
+ /* Add property transition to transition group */
+ xfdashboard_transition_group_add_transition(XFDASHBOARD_TRANSITION_GROUP(transitionGroup), propertyTransition);
+
+ /* Release allocated resources */
+ g_object_unref(propertyTransition);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Created transition for property '%s' at target #%d and actor #%d (%s@%p) of animation specification '%s'",
+ propertyTargetSpec->name,
+ counterTargets,
+ counterActors,
+ G_OBJECT_TYPE_NAME(actor),
+ actor,
+ spec->id);
+ }
+
+ /* Release allocated resources */
+ g_value_unset(&fromValue);
+ g_value_unset(&toValue);
+ }
+
+ /* Add transition group with collected property transitions
+ * to actor.
+ */
+ if(animationClass->add_animation)
+ {
+ animationClass->add_animation(animation, actor, transitionGroup);
+ }
+
+ /* Release allocated resources */
+ g_object_unref(transitionGroup);
+ }
+
+ /* Release allocated resources */
+ g_slist_free(actors);
+ }
+
+ /* Release allocated resources */
+ if(spec) _xfdashboard_theme_animation_spec_unref(spec);
+
+ return(animation);
+}
diff --git a/libxfdashboard/theme-animation.h b/libxfdashboard/theme-animation.h
new file mode 100644
index 0000000..f838035
--- /dev/null
+++ b/libxfdashboard/theme-animation.h
@@ -0,0 +1,108 @@
+/*
+ * theme-animation: A theme used for building animations by XML files
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef __LIBXFDASHBOARD_THEME_ANIMATION__
+#define __LIBXFDASHBOARD_THEME_ANIMATION__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <clutter/clutter.h>
+
+#include <libxfdashboard/actor.h>
+#include <libxfdashboard/animation.h>
+
+G_BEGIN_DECLS
+
+
+/* Object declaration */
+#define XFDASHBOARD_TYPE_THEME_ANIMATION (xfdashboard_theme_animation_get_type())
+#define XFDASHBOARD_THEME_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_THEME_ANIMATION, XfdashboardThemeAnimation))
+#define XFDASHBOARD_IS_THEME_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_THEME_ANIMATION))
+#define XFDASHBOARD_THEME_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_THEME_ANIMATION, XfdashboardThemeAnimationClass))
+#define XFDASHBOARD_IS_THEME_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_THEME_ANIMATION))
+#define XFDASHBOARD_THEME_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_THEME_ANIMATION, XfdashboardThemeAnimationClass))
+
+typedef struct _XfdashboardThemeAnimation XfdashboardThemeAnimation;
+typedef struct _XfdashboardThemeAnimationClass XfdashboardThemeAnimationClass;
+typedef struct _XfdashboardThemeAnimationPrivate XfdashboardThemeAnimationPrivate;
+
+/**
+ * XfdashboardThemeAnimation:
+ *
+ * The #XfdashboardThemeAnimation structure contains only private data and
+ * should be accessed using the provided API
+ */
+struct _XfdashboardThemeAnimation
+{
+ /*< private >*/
+ /* Parent instance */
+ GObject parent_instance;
+
+ /* Private structure */
+ XfdashboardThemeAnimationPrivate *priv;
+};
+
+/**
+ * XfdashboardThemeAnimationClass:
+ *
+ * The #XfdashboardThemeAnimationClass structure contains only private data
+ */
+struct _XfdashboardThemeAnimationClass
+{
+ /*< private >*/
+ /* Parent class */
+ GObjectClass parent_class;
+
+ /*< public >*/
+ /* Virtual functions */
+};
+
+/* Errors */
+#define XFDASHBOARD_THEME_ANIMATION_ERROR (xfdashboard_theme_animation_error_quark())
+
+GQuark xfdashboard_theme_animation_error_quark(void);
+
+typedef enum /*< prefix=XFDASHBOARD_THEME_ANIMATION_ERROR >*/
+{
+ XFDASHBOARD_THEME_ANIMATION_ERROR_ERROR,
+ XFDASHBOARD_THEME_ANIMATION_ERROR_MALFORMED,
+} XfdashboardThemeAnimationErrorEnum;
+
+/* Public API */
+GType xfdashboard_theme_animation_get_type(void) G_GNUC_CONST;
+
+XfdashboardThemeAnimation* xfdashboard_theme_animation_new(void);
+
+gboolean xfdashboard_theme_animation_add_file(XfdashboardThemeAnimation *self,
+ const gchar *inPath,
+ GError **outError);
+
+XfdashboardAnimation* xfdashboard_theme_animation_create(XfdashboardThemeAnimation *self,
+ XfdashboardActor *inSender,
+ const gchar *inSignal);
+
+G_END_DECLS
+
+#endif /* __LIBXFDASHBOARD_THEME_ANIMATION__ */
diff --git a/libxfdashboard/theme.c b/libxfdashboard/theme.c
index f90888f..69cb1fb 100644
--- a/libxfdashboard/theme.c
+++ b/libxfdashboard/theme.c
@@ -52,6 +52,7 @@ struct _XfdashboardThemePrivate
XfdashboardThemeCSS *styling;
XfdashboardThemeLayout *layout;
XfdashboardThemeEffects *effects;
+ XfdashboardThemeAnimation *animation;
gchar *userThemeStyleFile;
gchar *userGlobalStyleFile;
@@ -367,7 +368,7 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
/* Get path and file for effect resource */
resourceFile=g_build_filename(priv->themePath, *resource, NULL);
- /* Try to load style resource */
+ /* Try to load effects resource */
XFDASHBOARD_DEBUG(self, THEME,
"Loading XML effects file %s for theme %s",
resourceFile,
@@ -397,9 +398,71 @@ static gboolean _xfdashboard_theme_load_resources(XfdashboardTheme *self,
g_strfreev(resources);
}
+ /* Create XML parser and load animation resources which are optional */
+ if(g_key_file_has_key(themeKeyFile,
+ XFDASHBOARD_THEME_GROUP,
+ "Animation",
+ NULL))
+ {
+ resources=g_key_file_get_string_list(themeKeyFile,
+ XFDASHBOARD_THEME_GROUP,
+ "Animation",
+ NULL,
+ &error);
+ if(!resources)
+ {
+ /* Set error */
+ g_propagate_error(outError, error);
+
+ /* Release allocated resources */
+ if(themeKeyFile) g_key_file_free(themeKeyFile);
+
+ /* Return FALSE to indicate error */
+ return(FALSE);
+ }
+
+ resource=resources;
+ while(*resource)
+ {
+ /* Get path and file for animation resource */
+ resourceFile=g_build_filename(priv->themePath, *resource, NULL);
+
+ /* Try to load animation resource */
+ XFDASHBOARD_DEBUG(self, THEME,
+ "Loading XML animation file %s for theme %s",
+ resourceFile,
+ priv->themeName);
+
+ if(!xfdashboard_theme_animation_add_file(priv->animation, resourceFile, &error))
+ {
+ /* Set error */
+ g_propagate_error(outError, error);
+
+ /* Release allocated resources */
+ if(resources) g_strfreev(resources);
+ if(resourceFile) g_free(resourceFile);
+ if(themeKeyFile) g_key_file_free(themeKeyFile);
+
+ /* Return FALSE to indicate error */
+ return(FALSE);
+ }
+
+ /* Release allocated resources */
+ if(resourceFile) g_free(resourceFile);
+
+ /* Continue with next entry */
+ resource++;
+ counter++;
+ }
+ g_strfreev(resources);
+ }
+
/* Release allocated resources */
if(themeKeyFile) g_key_file_free(themeKeyFile);
+ /* TODO: Just for testing animations */
+ xfdashboard_theme_animation_add_file(priv->animation, "dummy", &error);
+
/* Return TRUE to indicate success */
return(TRUE);
}
@@ -549,6 +612,7 @@ static void _xfdashboard_theme_set_theme_name(XfdashboardTheme *self, const gcha
priv->styling=xfdashboard_theme_css_new(priv->themePath);
priv->layout=xfdashboard_theme_layout_new();
priv->effects=xfdashboard_theme_effects_new();
+ priv->animation=xfdashboard_theme_animation_new();
/* Check for user resource files */
resourceFile=g_build_filename(g_get_user_config_dir(), "xfdashboard", "themes", XFDASHBOARD_USER_GLOBAL_CSS_FILE, NULL);
@@ -651,6 +715,12 @@ static void _xfdashboard_theme_dispose(GObject *inObject)
priv->effects=NULL;
}
+ if(priv->animation)
+ {
+ g_object_unref(priv->animation);
+ priv->animation=NULL;
+ }
+
/* Call parent's class dispose method */
G_OBJECT_CLASS(xfdashboard_theme_parent_class)->dispose(inObject);
}
@@ -772,6 +842,7 @@ void xfdashboard_theme_init(XfdashboardTheme *self)
priv->styling=NULL;
priv->layout=NULL;
priv->effects=NULL;
+ priv->animation=NULL;
}
/* IMPLEMENTATION: Errors */
@@ -891,3 +962,11 @@ XfdashboardThemeEffects* xfdashboard_theme_get_effects(XfdashboardTheme *self)
return(self->priv->effects);
}
+
+/* Get theme animation */
+XfdashboardThemeAnimation* xfdashboard_theme_get_animation(XfdashboardTheme *self)
+{
+ g_return_val_if_fail(XFDASHBOARD_IS_THEME(self), NULL);
+
+ return(self->priv->animation);
+}
diff --git a/libxfdashboard/theme.h b/libxfdashboard/theme.h
index 5ead020..dcdac4e 100644
--- a/libxfdashboard/theme.h
+++ b/libxfdashboard/theme.h
@@ -34,6 +34,7 @@
#include <libxfdashboard/theme-css.h>
#include <libxfdashboard/theme-layout.h>
#include <libxfdashboard/theme-effects.h>
+#include <libxfdashboard/theme-animation.h>
G_BEGIN_DECLS
@@ -96,6 +97,7 @@ gboolean xfdashboard_theme_load(XfdashboardTheme *self,
XfdashboardThemeCSS* xfdashboard_theme_get_css(XfdashboardTheme *self);
XfdashboardThemeLayout* xfdashboard_theme_get_layout(XfdashboardTheme *self);
XfdashboardThemeEffects* xfdashboard_theme_get_effects(XfdashboardTheme *self);
+XfdashboardThemeAnimation* xfdashboard_theme_get_animation(XfdashboardTheme *self);
G_END_DECLS
diff --git a/libxfdashboard/transition-group.c b/libxfdashboard/transition-group.c
new file mode 100644
index 0000000..1723867
--- /dev/null
+++ b/libxfdashboard/transition-group.c
@@ -0,0 +1,683 @@
+/*
+ * transition-group: A grouping transition
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+/**
+ * SECTION:transition-group
+ * @short_description: Group transitions together
+ * @include: xfdashboard/transition-group.h
+ *
+ * The #XfdashboardTransitionGroup allows running multiple #ClutterTransition
+ * instances concurrently.
+ *
+ * The #XfdashboardTransitionGroup is a one-to-one copy of #ClutterTransitionGroup
+ * with the only enhancements that API functions to get a list of all transitions
+ * grouped together in that grouping transition was added as well as all timeline
+ * configuration, e.g. duration, direction, repeats etc., that are set on an
+ * #XfdashboardTransitionGroup will also applied to all transitions added to this
+ * group.
+ *
+ * In addition it will also reset "min-width-set", "min-height-set" and other
+ * "*-set" properties to their values before the #XfdashboardTransitionGroup
+ * was attached the animatable actor. This will reset commonly statically set
+ * size and transformation properties, like "min-width", "min-height", "natural-width"
+ * etc., which will prevent a layout manager to use the natural width and/or height
+ * of an actor. This behaviour can be turned off via setting property "reset-flags"
+ * to boolean FALSE (enabled and set to TRUE by default).
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <libxfdashboard/transition-group.h>
+
+#include <glib/gi18n-lib.h>
+
+#include <libxfdashboard/compat.h>
+#include <libxfdashboard/debug.h>
+
+
+/* Forward declarations */
+typedef struct _XfdashboardTransitionActorSetFlags XfdashboardTransitionActorSetFlags;
+struct _XfdashboardTransitionActorSetFlags
+{
+ /* Value of property "*-set" at animatable actor */
+ gboolean fixedPosition;
+ gboolean minWidth;
+ gboolean minHeight;
+ gboolean naturalWidth;
+ gboolean naturalHeight;
+ gboolean transform;
+ gboolean childTransform;
+ gboolean backgroundColor;
+};
+
+
+/* Define this class in GObject system */
+struct _XfdashboardTransitionGroupPrivate
+{
+ /* Properties related */
+ gboolean resetFlags;
+
+ /* Instance related */
+ GHashTable *transitions;
+ GList *bindings;
+ XfdashboardTransitionActorSetFlags flags;
+};
+
+G_DEFINE_TYPE_WITH_PRIVATE(XfdashboardTransitionGroup,
+ xfdashboard_transition_group,
+ CLUTTER_TYPE_TRANSITION);
+
+
+/* Properties */
+enum
+{
+ PROP_0,
+
+ PROP_RESET_FLAGS,
+
+ PROP_LAST
+};
+
+static GParamSpec* XfdashboardTransitionGroupProperties[PROP_LAST]={ 0, };
+
+
+/* IMPLEMENTATION: Private variables and methods */
+
+typedef struct _XfdashboardTransitionGroupBindingEntry XfdashboardTransitionGroupBindingEntry;
+struct _XfdashboardTransitionGroupBindingEntry
+{
+ XfdashboardTransitionGroup *self;
+ GBinding *binding;
+};
+
+/* Property names to bind when transition is added */
+static const gchar *_xfdashboard_transition_group_bindable_properties[]=
+{
+ "auto-reverse",
+ "delay",
+ "duration",
+ "loop",
+ "progress-mode",
+ "repeat-count",
+ NULL
+};
+
+/* A binding entry is notified about its destruction */
+static void _xfdashboard_transition_group_binding_notified(gpointer inData)
+{
+ XfdashboardTransitionGroup *self;
+ XfdashboardTransitionGroupPrivate *priv;
+ XfdashboardTransitionGroupBindingEntry *entry;
+
+ entry=(XfdashboardTransitionGroupBindingEntry*)inData;
+ self=entry->self;
+ priv=self->priv;
+
+ /* Remove binding from list */
+ priv->bindings=g_list_remove(priv->bindings, entry->binding);
+
+ /* Release allocated resources of entry */
+ g_free(inData);
+}
+
+
+/* IMPLEMENTATION: ClutterTimeline */
+
+/* Time at transition has elapsed, so advance all transitions as well */
+static void _xfdashboard_transition_group_timeline_new_frame(ClutterTimeline *inTimeline,
+ gint inElapsed)
+{
+ XfdashboardTransitionGroup *self;
+ XfdashboardTransitionGroupPrivate *priv;
+ GHashTableIter iter;
+ gpointer element;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(inTimeline));
+
+ self=XFDASHBOARD_TRANSITION_GROUP(inTimeline);
+ priv=self->priv;
+
+ /* Iterate through transitions and advance their timeline */
+ g_hash_table_iter_init(&iter, priv->transitions);
+ while(g_hash_table_iter_next(&iter, &element, NULL))
+ {
+ clutter_timeline_advance(CLUTTER_TIMELINE(element), clutter_timeline_get_elapsed_time(inTimeline));
+ g_signal_emit_by_name(element, "new-frame", 0, clutter_timeline_get_elapsed_time(inTimeline));
+ }
+}
+
+/* This transition group was started */
+static void _xfdashboard_transition_group_timeline_started(ClutterTimeline *inTimeline)
+{
+ XfdashboardTransitionGroup *self;
+ XfdashboardTransitionGroupPrivate *priv;
+ GHashTableIter iter;
+ gpointer element;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(inTimeline));
+
+ self=XFDASHBOARD_TRANSITION_GROUP(inTimeline);
+ priv=self->priv;
+
+ /* Iterate through transitions and emit start signal */
+ g_hash_table_iter_init(&iter, priv->transitions);
+ while(g_hash_table_iter_next(&iter, &element, NULL))
+ {
+ g_signal_emit_by_name(element, "started");
+ }
+}
+
+
+/* IMPLEMENTATION: ClutterTransition */
+
+/* Transition group was attached to an animatable actor */
+static void _xfdashboard_transition_group_transition_attached(ClutterTransition *inTransition,
+ ClutterAnimatable *inAnimatable)
+{
+ XfdashboardTransitionGroup *self;
+ XfdashboardTransitionGroupPrivate *priv;
+ GHashTableIter iter;
+ gpointer element;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(inTransition));
+ g_return_if_fail(CLUTTER_IS_ANIMATABLE(inAnimatable));
+
+ self=XFDASHBOARD_TRANSITION_GROUP(inTransition);
+ priv=self->priv;
+
+ /* Iterate through transitions and set the animatable actor
+ * it is attached to.
+ */
+ g_hash_table_iter_init(&iter, priv->transitions);
+ while(g_hash_table_iter_next(&iter, &element, NULL))
+ {
+ clutter_transition_set_animatable(CLUTTER_TRANSITION(element), inAnimatable);
+ }
+
+ /* Get current value of "*-set" properties */
+ memset(&priv->flags, 0, sizeof(priv->flags));
+ g_object_get(inAnimatable,
+ "fixed-position-set", &priv->flags.fixedPosition,
+ "min-width-set", &priv->flags.minWidth,
+ "min-height-set", &priv->flags.minHeight,
+ "natural-width-set", &priv->flags.naturalWidth,
+ "natural-height-set", &priv->flags.naturalHeight,
+ "transform-set", &priv->flags.transform,
+ "child-transform-set", &priv->flags.childTransform,
+ "background-color-set", &priv->flags.backgroundColor,
+ NULL);
+}
+
+/* Transition group was detached from an animatable actor */
+static void _xfdashboard_transition_group_transition_detached(ClutterTransition *inTransition,
+ ClutterAnimatable *inAnimatable)
+{
+ XfdashboardTransitionGroup *self;
+ XfdashboardTransitionGroupPrivate *priv;
+ GHashTableIter iter;
+ gpointer element;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(inTransition));
+ g_return_if_fail(CLUTTER_IS_ANIMATABLE(inAnimatable));
+
+ self=XFDASHBOARD_TRANSITION_GROUP(inTransition);
+ priv=self->priv;
+
+ /* Iterate through transitions and reset the animatable actor */
+ g_hash_table_iter_init(&iter, priv->transitions);
+ while(g_hash_table_iter_next(&iter, &element, NULL))
+ {
+ clutter_transition_set_animatable(CLUTTER_TRANSITION(element), NULL);
+ }
+
+ /* Restore "*-set" properties if requested */
+ if(priv->resetFlags)
+ {
+ XfdashboardTransitionActorSetFlags newFlags;
+
+ /* Get current value of "*-set" properties */
+ memset(&newFlags, 0, sizeof(newFlags));
+ g_object_get(inAnimatable,
+ "fixed-position-set", &newFlags.fixedPosition,
+ "min-width-set", &newFlags.minWidth,
+ "min-height-set", &newFlags.minHeight,
+ "natural-width-set", &newFlags.naturalWidth,
+ "natural-height-set", &newFlags.naturalHeight,
+ "transform-set", &newFlags.transform,
+ "child-transform-set", &newFlags.childTransform,
+ "background-color-set", &newFlags.backgroundColor,
+ NULL);
+
+ /* Restore the values which have changed to their old value */
+ if(newFlags.fixedPosition!=priv->flags.fixedPosition)
+ {
+ g_object_set(inAnimatable, "fixed-position-set", priv->flags.fixedPosition, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'fixed-position-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.minWidth!=priv->flags.minWidth)
+ {
+ g_object_set(inAnimatable, "min-width-set", priv->flags.minWidth, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'min-width-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.minHeight!=priv->flags.minHeight)
+ {
+ g_object_set(inAnimatable, "min-height-set", priv->flags.minHeight, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'min-height-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.naturalWidth!=priv->flags.naturalWidth)
+ {
+ g_object_set(inAnimatable, "natural-width-set", priv->flags.naturalWidth, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'natural-width-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.naturalHeight!=priv->flags.naturalHeight)
+ {
+ g_object_set(inAnimatable, "natural-height-set", priv->flags.naturalHeight, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'natural-height-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.transform!=priv->flags.transform)
+ {
+ g_object_set(inAnimatable, "transform-set", priv->flags.transform, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'transform-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.childTransform!=priv->flags.childTransform)
+ {
+ g_object_set(inAnimatable, "child-transform-set", priv->flags.childTransform, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'child-transform-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+
+ if(newFlags.backgroundColor!=priv->flags.backgroundColor)
+ {
+ g_object_set(inAnimatable, "background-color-set", priv->flags.backgroundColor, NULL);
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Restoring property 'background-color-set' at actor %s@%p",
+ G_OBJECT_TYPE_NAME(inAnimatable), inAnimatable);
+ }
+ }
+}
+
+
+/* IMPLEMENTATION: GObject */
+
+/* Finalize this object */
+static void _xfdashboard_transition_group_finalize(GObject *inObject)
+{
+ XfdashboardTransitionGroup *self=XFDASHBOARD_TRANSITION_GROUP(inObject);
+ XfdashboardTransitionGroupPrivate *priv=self->priv;
+
+ /* Release our allocated variables */
+ if(priv->bindings)
+ {
+ GList *iter;
+ GBinding *binding;
+
+ for(iter=priv->bindings; iter; iter=g_list_next(iter))
+ {
+ /* Release valid bindings */
+ binding=(GBinding*)iter->data;
+ if(!binding) continue;
+
+ /* Unbind binding and release object */
+ g_object_unref(binding);
+ }
+ g_list_free(priv->bindings);
+ priv->bindings=NULL;
+ }
+
+ if(priv->transitions)
+ {
+ g_hash_table_unref(priv->transitions);
+ priv->transitions=NULL;
+ }
+
+ /* Call parent's class dispose method */
+ G_OBJECT_CLASS(xfdashboard_transition_group_parent_class)->finalize(inObject);
+}
+
+/* Set/get properties */
+static void _xfdashboard_transition_group_set_property(GObject *inObject,
+ guint inPropID,
+ const GValue *inValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardTransitionGroup *self=XFDASHBOARD_TRANSITION_GROUP(inObject);
+ XfdashboardTransitionGroupPrivate *priv=self->priv;
+
+ switch(inPropID)
+ {
+ case PROP_RESET_FLAGS:
+ priv->resetFlags=g_value_get_boolean(inValue);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+static void _xfdashboard_transition_group_get_property(GObject *inObject,
+ guint inPropID,
+ GValue *outValue,
+ GParamSpec *inSpec)
+{
+ XfdashboardTransitionGroup *self=XFDASHBOARD_TRANSITION_GROUP(inObject);
+ XfdashboardTransitionGroupPrivate *priv=self->priv;
+
+ switch(inPropID)
+ {
+ case PROP_RESET_FLAGS:
+ g_value_set_boolean(outValue, priv->resetFlags);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
+ break;
+ }
+}
+
+/* Class initialization
+ * Override functions in parent classes and define properties
+ * and signals
+ */
+void xfdashboard_transition_group_class_init(XfdashboardTransitionGroupClass *klass)
+{
+ GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
+ ClutterTimelineClass *timelineClass=CLUTTER_TIMELINE_CLASS(klass);
+ ClutterTransitionClass *transitionClass=CLUTTER_TRANSITION_CLASS(klass);
+
+ /* Override functions */
+ gobjectClass->set_property=_xfdashboard_transition_group_set_property;
+ gobjectClass->get_property=_xfdashboard_transition_group_get_property;
+ gobjectClass->finalize=_xfdashboard_transition_group_finalize;
+
+ timelineClass->started=_xfdashboard_transition_group_timeline_started;
+ timelineClass->new_frame=_xfdashboard_transition_group_timeline_new_frame;
+
+ transitionClass->attached=_xfdashboard_transition_group_transition_attached;
+ transitionClass->detached=_xfdashboard_transition_group_transition_detached;
+
+ /* Define properties */
+ XfdashboardTransitionGroupProperties[PROP_RESET_FLAGS]=
+ g_param_spec_boolean("reset-flags",
+ _("Reset flags"),
+ _("If TRUE the flags for static sizes, transformations etc. at animatable actor is resetted to old state"),
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS);
+
+ g_object_class_install_properties(gobjectClass, PROP_LAST, XfdashboardTransitionGroupProperties);
+}
+
+/* Object initialization
+ * Create private structure and set up default values
+ */
+void xfdashboard_transition_group_init(XfdashboardTransitionGroup *self)
+{
+ XfdashboardTransitionGroupPrivate *priv;
+
+ priv=self->priv=xfdashboard_transition_group_get_instance_private(self);
+
+ /* Set up default values */
+ priv->transitions=g_hash_table_new_full(NULL,
+ NULL,
+ (GDestroyNotify)g_object_unref,
+ NULL);
+ priv->bindings=NULL;
+}
+
+
+/* IMPLEMENTATION: Public API */
+/**
+ * xfdashboard_transition_group_new:
+ *
+ * Creates a new #XfdashboardTransitionGroup instance.
+ *
+ * Return value: The newly created #XfdashboardTransitionGroup. Use
+ * g_object_unref() when done to deallocate the resources it uses
+ */
+ClutterTransition* xfdashboard_transition_group_new(void)
+{
+ return(CLUTTER_TRANSITION(g_object_new(XFDASHBOARD_TYPE_TRANSITION_GROUP, NULL)));
+}
+
+/**
+ * xfdashboard_transition_group_add_transition:
+ * @self: A #XfdashboardTransitionGroup
+ * @inTransition: A #ClutterTransition
+ *
+ * Adds transition @inTransition to group @self.
+ *
+ * This function acquires a reference on @inTransition that will be released
+ * when calling xfdashboard_transition_group_remove_transition() or
+ * xfdashboard_transition_group_remove_all().
+ */
+void xfdashboard_transition_group_add_transition(XfdashboardTransitionGroup *self,
+ ClutterTransition *inTransition)
+{
+ XfdashboardTransitionGroupPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(self));
+ g_return_if_fail(CLUTTER_IS_TRANSITION(inTransition));
+
+ priv=self->priv;
+
+ /* Add and set up transition */
+ if(g_hash_table_add(priv->transitions, g_object_ref(inTransition)))
+ {
+ const gchar **iter;
+ GBinding *binding;
+ XfdashboardTransitionGroupBindingEntry *entry;
+
+ /* Clone timeline configuration to transition and bind properties
+ * to update them at added transition when this timeline changes
+ * as this transition is new to this group.
+ */
+ for(iter=_xfdashboard_transition_group_bindable_properties; *iter; iter++)
+ {
+ entry=g_new0(XfdashboardTransitionGroupBindingEntry, 1);
+ binding=g_object_bind_property_full(self, *iter,
+ inTransition, *iter,
+ G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE,
+ NULL,
+ NULL,
+ entry,
+ _xfdashboard_transition_group_binding_notified);
+
+ entry->self=self;
+ entry->binding=binding;
+
+ priv->bindings=g_list_prepend(priv->bindings, binding);
+
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Bind property '%s' from transition group %s@%p to actor %s@%p via %s@%p",
+ *iter,
+ G_OBJECT_TYPE_NAME(self),
+ self,
+ G_OBJECT_TYPE_NAME(inTransition),
+ inTransition,
+ G_OBJECT_TYPE_NAME(binding),
+ binding);
+ }
+ }
+}
+
+/**
+ * xfdashboard_transition_group_remove_transition:
+ * @self: A #XfdashboardTransitionGroup
+ * @inTransition: A #ClutterTransition
+ *
+ * Removes transition @inTransition from group @self.
+ *
+ * This function releases the reference acquired on @inTransition when
+ * calling xfdashboard_transition_group_add_transition().
+ */
+void xfdashboard_transition_group_remove_transition(XfdashboardTransitionGroup *self,
+ ClutterTransition *inTransition)
+{
+ XfdashboardTransitionGroupPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(self));
+ g_return_if_fail(CLUTTER_IS_TRANSITION(inTransition));
+
+ priv=self->priv;
+
+ /* First remove bindings to transition as long as a reference is held */
+ if(priv->bindings)
+ {
+ GList *iter;
+ GList *nextIter;
+
+ iter=priv->bindings;
+ while(iter)
+ {
+ GBinding *binding;
+
+ /* Get next element to iterate */
+ nextIter=g_list_next(iter);
+
+ /* Get currently iterated binding */
+ binding=(GBinding*)(iter->data);
+ if(!binding)
+ {
+ iter=nextIter;
+ continue;
+ }
+
+ /* If binding's target refers to transition removed,
+ * unbind it.
+ */
+ if(g_binding_get_target(binding)==G_OBJECT(inTransition))
+ {
+ /* Remove binding from list */
+ priv->bindings=g_list_remove_link(priv->bindings, iter);
+
+ /* Unbind property binding */
+ XFDASHBOARD_DEBUG(self, ANIMATION,
+ "Unbinding property '%s' from transition group %s@%p at actor %s@%p via %s@%p",
+ g_binding_get_source_property(binding),
+ G_OBJECT_TYPE_NAME(self),
+ self,
+ G_OBJECT_TYPE_NAME(inTransition),
+ inTransition,
+ G_OBJECT_TYPE_NAME(binding),
+ binding);
+ g_binding_unbind(binding);
+
+ /* Free list element currently iterated */
+ g_list_free(iter);
+ }
+
+ /* Move to next element */
+ iter=nextIter;
+ }
+ }
+
+ /* Remove transition */
+ g_hash_table_remove(priv->transitions, inTransition);
+}
+
+/**
+ * xfdashboard_transition_group_remove_all:
+ * @self: A #XfdashboardTransitionGroup
+ *
+ * Removes all transitions from group @self.
+ *
+ * This function releases all references acquired when calling
+ * xfdashboard_transition_group_add_transition().
+ */
+void xfdashboard_transition_group_remove_all(XfdashboardTransitionGroup *self)
+{
+ XfdashboardTransitionGroupPrivate *priv;
+
+ g_return_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(self));
+
+ priv=self->priv;
+
+ /* First remove all binding as long as a reference to transition is held */
+ if(priv->bindings)
+ {
+ g_list_free_full(priv->bindings, (GDestroyNotify)g_binding_unbind);
+ priv->bindings=NULL;
+ }
+
+ /* Remove all transitions */
+ g_hash_table_remove_all(priv->transitions);
+}
+
+/**
+ * xfdashboard_transition_group_get_transitions:
+ * @self: A #XfdashboardTransitionGroup
+ *
+ * Returns a list of all transitions of group @self. The list is a #GSList
+ * and each element contains a #ClutterTransition. When creating the list
+ * an additional reference is taken on the element, so the caller is responsible
+ * to release the reference.
+ *
+ * Return value: (element-type XfdashboardWindowTrackerWindow) (transfer none):
+ * The list of #ClutterTransition added to this group.
+ * Use g_slist_free_full(list, g_object_unref) when done to release the
+ * additional reference taken.
+ */
+GSList* xfdashboard_transition_group_get_transitions(XfdashboardTransitionGroup *self)
+{
+ XfdashboardTransitionGroupPrivate *priv;
+ GSList *list;
+ GHashTableIter iter;
+ gpointer element;
+
+ g_return_val_if_fail(XFDASHBOARD_IS_TRANSITION_GROUP(self), NULL);
+
+ priv=self->priv;
+ list=NULL;
+
+ /* Iterate through transitions and add to list but take a extra reference */
+ g_hash_table_iter_init(&iter, priv->transitions);
+ while(g_hash_table_iter_next(&iter, &element, NULL))
+ {
+ list=g_slist_prepend(list, g_object_ref(element));
+ }
+ list=g_slist_reverse(list);
+
+ /* Return created list */
+ return(list);
+}
diff --git a/libxfdashboard/transition-group.h b/libxfdashboard/transition-group.h
new file mode 100644
index 0000000..7cdf65c
--- /dev/null
+++ b/libxfdashboard/transition-group.h
@@ -0,0 +1,95 @@
+/*
+ * transition-group: A grouping transition
+ *
+ * Copyright 2012-2019 Stephan Haller <nomad at froevel.de>
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ */
+
+#ifndef __LIBXFDASHBOARD_TRANSITION_GROUP__
+#define __LIBXFDASHBOARD_TRANSITION_GROUP__
+
+#if !defined(__LIBXFDASHBOARD_H_INSIDE__) && !defined(LIBXFDASHBOARD_COMPILATION)
+#error "Only <libxfdashboard/libxfdashboard.h> can be included directly."
+#endif
+
+#include <clutter/clutter.h>
+
+#include <libxfdashboard/actor.h>
+
+G_BEGIN_DECLS
+
+/* Object declaration */
+#define XFDASHBOARD_TYPE_TRANSITION_GROUP (xfdashboard_transition_group_get_type())
+#define XFDASHBOARD_TRANSITION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), XFDASHBOARD_TYPE_TRANSITION_GROUP, XfdashboardTransitionGroup))
+#define XFDASHBOARD_IS_TRANSITION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), XFDASHBOARD_TYPE_TRANSITION_GROUP))
+#define XFDASHBOARD_TRANSITION_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), XFDASHBOARD_TYPE_TRANSITION_GROUP, XfdashboardTransitionGroupClass))
+#define XFDASHBOARD_IS_TRANSITION_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), XFDASHBOARD_TYPE_TRANSITION_GROUP))
+#define XFDASHBOARD_TRANSITION_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), XFDASHBOARD_TYPE_TRANSITION_GROUP, XfdashboardTransitionGroupClass))
+
+typedef struct _XfdashboardTransitionGroup XfdashboardTransitionGroup;
+typedef struct _XfdashboardTransitionGroupClass XfdashboardTransitionGroupClass;
+typedef struct _XfdashboardTransitionGroupPrivate XfdashboardTransitionGroupPrivate;
+
+/**
+ * XfdashboardTransitionGroup:
+ *
+ * The #XfdashboardTransitionGroup structure contains only private data and
+ * should be accessed using the provided API
+ */
+ struct _XfdashboardTransitionGroup
+{
+ /*< private >*/
+ /* Parent instance */
+ ClutterTransition parent_instance;
+
+ /* Private structure */
+ XfdashboardTransitionGroupPrivate *priv;
+};
+
+/**
+ * XfdashboardTransitionGroupClass:
+ *
+ * The #XfdashboardTransitionGroupClass structure contains only private data
+ */
+struct _XfdashboardTransitionGroupClass
+{
+ /*< private >*/
+ /* Parent class */
+ ClutterTransitionClass parent_class;
+
+ /*< public >*/
+ /* Virtual functions */
+};
+
+/* Public API */
+GType xfdashboard_transition_group_get_type(void) G_GNUC_CONST;
+
+ClutterTransition* xfdashboard_transition_group_new(void);
+
+void xfdashboard_transition_group_add_transition(XfdashboardTransitionGroup *self,
+ ClutterTransition *inTransition);
+void xfdashboard_transition_group_remove_transition(XfdashboardTransitionGroup *self,
+ ClutterTransition *inTransition);
+void xfdashboard_transition_group_remove_all(XfdashboardTransitionGroup *self);
+
+GSList* xfdashboard_transition_group_get_transitions(XfdashboardTransitionGroup *self);
+
+G_END_DECLS
+
+#endif /* __LIBXFDASHBOARD_TRANSITION_GROUP__ */
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the Xfce4-commits
mailing list