[St] Add StThemeNodeTransition
Introduce a small helper class in order to add transitions between theme nodes to StWidget. https://bugzilla.gnome.org/show_bug.cgi?id=619025 transition update
This commit is contained in:
parent
af3ca027a1
commit
0deffbaaf0
@ -110,7 +110,8 @@ st_source_private_h = \
|
||||
st/st-private.h \
|
||||
st/st-table-private.h \
|
||||
st/st-theme-private.h \
|
||||
st/st-theme-node-private.h
|
||||
st/st-theme-node-private.h \
|
||||
st/st-theme-node-transition.h
|
||||
|
||||
# please, keep this sorted alphabetically
|
||||
st_source_c = \
|
||||
@ -142,6 +143,7 @@ st_source_c = \
|
||||
st/st-theme-context.c \
|
||||
st/st-theme-node.c \
|
||||
st/st-theme-node-drawing.c \
|
||||
st/st-theme-node-transition.c \
|
||||
st/st-tooltip.c \
|
||||
st/st-widget.c \
|
||||
$(NULL)
|
||||
|
411
src/st/st-theme-node-transition.c
Normal file
411
src/st/st-theme-node-transition.c
Normal file
@ -0,0 +1,411 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
/* Theme node transitions for StWidget.
|
||||
*
|
||||
* Copyright (C) 2010 Florian Müllner <fmuellner@gnome.org>
|
||||
*
|
||||
* The St is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Library General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* The St is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Library General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Library General Public
|
||||
* License along with the St; see the file COPYING.LIB.
|
||||
* If not, write to the Free Software Foundation, Inc., 59 Temple Place -
|
||||
* Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#include "st-theme-node-transition.h"
|
||||
|
||||
enum {
|
||||
COMPLETED,
|
||||
NEW_FRAME,
|
||||
LAST_SIGNAL
|
||||
};
|
||||
|
||||
#define ST_THEME_NODE_TRANSITION_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE ((obj), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionPrivate))
|
||||
|
||||
struct _StThemeNodeTransitionPrivate {
|
||||
StThemeNode *old_theme_node;
|
||||
StThemeNode *new_theme_node;
|
||||
|
||||
CoglHandle *old_texture;
|
||||
CoglHandle *new_texture;
|
||||
|
||||
CoglHandle *old_offscreen;
|
||||
CoglHandle *new_offscreen;
|
||||
|
||||
CoglHandle *material;
|
||||
|
||||
ClutterAlpha *alpha;
|
||||
ClutterTimeline *timeline;
|
||||
|
||||
guint timeline_completed_id;
|
||||
guint timeline_new_frame_id;
|
||||
|
||||
ClutterActorBox last_allocation;
|
||||
ClutterActorBox offscreen_box;
|
||||
guint8 last_opacity;
|
||||
|
||||
gboolean needs_setup;
|
||||
};
|
||||
|
||||
static guint signals[LAST_SIGNAL] = { 0 };
|
||||
|
||||
G_DEFINE_TYPE (StThemeNodeTransition, st_theme_node_transition, G_TYPE_OBJECT);
|
||||
|
||||
|
||||
static void
|
||||
on_timeline_completed (ClutterTimeline *timeline,
|
||||
StThemeNodeTransition *transition)
|
||||
{
|
||||
g_signal_emit (transition, signals[COMPLETED], 0);
|
||||
}
|
||||
|
||||
static void
|
||||
on_timeline_new_frame (ClutterTimeline *timeline,
|
||||
gint frame_num,
|
||||
StThemeNodeTransition *transition)
|
||||
{
|
||||
g_signal_emit (transition, signals[NEW_FRAME], 0);
|
||||
}
|
||||
|
||||
StThemeNodeTransition *
|
||||
st_theme_node_transition_new (StThemeNode *from_node,
|
||||
StThemeNode *to_node,
|
||||
guint duration)
|
||||
{
|
||||
StThemeNodeTransition *transition;
|
||||
|
||||
g_return_val_if_fail (ST_IS_THEME_NODE (from_node), NULL);
|
||||
g_return_val_if_fail (ST_IS_THEME_NODE (to_node), NULL);
|
||||
|
||||
duration = st_theme_node_get_transition_duration (to_node);
|
||||
|
||||
transition = g_object_new (ST_TYPE_THEME_NODE_TRANSITION,
|
||||
NULL);
|
||||
|
||||
transition->priv->old_theme_node = g_object_ref (from_node);
|
||||
transition->priv->new_theme_node = g_object_ref (to_node);
|
||||
|
||||
transition->priv->alpha = clutter_alpha_new ();
|
||||
transition->priv->timeline = clutter_timeline_new (duration);
|
||||
|
||||
transition->priv->timeline_completed_id =
|
||||
g_signal_connect (transition->priv->timeline, "completed",
|
||||
G_CALLBACK (on_timeline_completed), transition);
|
||||
transition->priv->timeline_new_frame_id =
|
||||
g_signal_connect (transition->priv->timeline, "new-frame",
|
||||
G_CALLBACK (on_timeline_new_frame), transition);
|
||||
|
||||
clutter_alpha_set_mode (transition->priv->alpha, CLUTTER_EASE_IN_OUT_QUAD);
|
||||
clutter_alpha_set_timeline (transition->priv->alpha,
|
||||
transition->priv->timeline);
|
||||
|
||||
clutter_timeline_start (transition->priv->timeline);
|
||||
|
||||
return transition;
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_transition_update (StThemeNodeTransition *transition,
|
||||
StThemeNode *new_node)
|
||||
{
|
||||
StThemeNodeTransitionPrivate *priv = transition->priv;
|
||||
StThemeNode *old_node;
|
||||
ClutterTimelineDirection direction;
|
||||
|
||||
g_return_if_fail (ST_IS_THEME_NODE_TRANSITION (transition));
|
||||
g_return_if_fail (ST_IS_THEME_NODE (new_node));
|
||||
|
||||
direction = clutter_timeline_get_direction (priv->timeline);
|
||||
old_node = (direction == CLUTTER_TIMELINE_FORWARD) ? priv->old_theme_node
|
||||
: priv->new_theme_node;
|
||||
|
||||
/* If the update is the reversal of the current transition,
|
||||
* we reverse the timeline.
|
||||
* Otherwise, we should initiate a new transition from the
|
||||
* current state to the new one; this is hard to do, so we
|
||||
* just cancel the ongoing transition in that case.
|
||||
* Note that reversing a timeline before any time elapsed
|
||||
* results in the timeline's time position being set to the
|
||||
* full duration - this is not what we want, so we cancel the
|
||||
* transition as well in that case.
|
||||
*/
|
||||
if (st_theme_node_equal (new_node, old_node)
|
||||
&& clutter_timeline_get_elapsed_time (priv->timeline) > 0)
|
||||
{
|
||||
if (direction == CLUTTER_TIMELINE_FORWARD)
|
||||
clutter_timeline_set_direction (priv->timeline,
|
||||
CLUTTER_TIMELINE_BACKWARD);
|
||||
else
|
||||
clutter_timeline_set_direction (priv->timeline,
|
||||
CLUTTER_TIMELINE_FORWARD);
|
||||
}
|
||||
else
|
||||
{
|
||||
clutter_timeline_stop (priv->timeline);
|
||||
g_signal_emit (transition, signals[COMPLETED], 0);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
calculate_offscreen_box (StThemeNodeTransition *transition,
|
||||
const ClutterActorBox *allocation)
|
||||
{
|
||||
StThemeNodeTransitionPrivate *priv = transition->priv;
|
||||
ClutterActorBox old_node_box, new_node_box;
|
||||
|
||||
st_theme_node_get_paint_box (priv->old_theme_node,
|
||||
allocation,
|
||||
&old_node_box);
|
||||
|
||||
st_theme_node_get_paint_box (priv->new_theme_node,
|
||||
allocation,
|
||||
&new_node_box);
|
||||
|
||||
priv->offscreen_box.x1 = MIN (old_node_box.x1, new_node_box.x1)
|
||||
- allocation->x1;
|
||||
priv->offscreen_box.y1 = MIN (old_node_box.y1, new_node_box.y1)
|
||||
- allocation->y1;
|
||||
priv->offscreen_box.x2 = MAX (old_node_box.x2, new_node_box.x2)
|
||||
- allocation->x1;
|
||||
priv->offscreen_box.y2 = MAX (old_node_box.y2, new_node_box.y2)
|
||||
- allocation->y1;
|
||||
}
|
||||
|
||||
static void
|
||||
setup_framebuffers (StThemeNodeTransition *transition,
|
||||
const ClutterActorBox *allocation,
|
||||
guint8 paint_opacity)
|
||||
{
|
||||
StThemeNodeTransitionPrivate *priv = transition->priv;
|
||||
CoglColor clear_color = { 0, 0, 0, 0 };
|
||||
guint width, height;
|
||||
|
||||
width = priv->offscreen_box.x2 - priv->offscreen_box.x1;
|
||||
height = priv->offscreen_box.y2 - priv->offscreen_box.y1;
|
||||
|
||||
if (priv->old_texture)
|
||||
cogl_handle_unref (priv->old_texture);
|
||||
priv->old_texture = cogl_texture_new_with_size (width, height,
|
||||
COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_ANY);
|
||||
|
||||
if (priv->new_texture)
|
||||
cogl_handle_unref (priv->new_texture);
|
||||
priv->new_texture = cogl_texture_new_with_size (width, height,
|
||||
COGL_TEXTURE_NO_SLICING,
|
||||
COGL_PIXEL_FORMAT_ANY);
|
||||
|
||||
if (priv->old_offscreen)
|
||||
cogl_handle_unref (priv->old_offscreen);
|
||||
priv->old_offscreen = cogl_offscreen_new_to_texture (priv->old_texture);
|
||||
|
||||
if (priv->new_offscreen)
|
||||
cogl_handle_unref (priv->new_offscreen);
|
||||
priv->new_offscreen = cogl_offscreen_new_to_texture (priv->new_texture);
|
||||
|
||||
if (priv->material)
|
||||
cogl_handle_unref (priv->material);
|
||||
priv->material = cogl_material_new ();
|
||||
|
||||
cogl_material_set_layer_combine (priv->material, 1,
|
||||
"RGBA = INTERPOLATE (PREVIOUS, "
|
||||
"TEXTURE, "
|
||||
"CONSTANT[A])",
|
||||
NULL);
|
||||
|
||||
cogl_material_set_layer (priv->material, 0, priv->new_texture);
|
||||
cogl_material_set_layer (priv->material, 1, priv->old_texture);
|
||||
|
||||
cogl_push_framebuffer (priv->old_offscreen);
|
||||
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
|
||||
cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
|
||||
priv->offscreen_box.y2, priv->offscreen_box.y1,
|
||||
0.0, 1.0);
|
||||
st_theme_node_paint (priv->old_theme_node,
|
||||
allocation,
|
||||
paint_opacity);
|
||||
cogl_pop_framebuffer ();
|
||||
|
||||
cogl_push_framebuffer (priv->new_offscreen);
|
||||
cogl_clear (&clear_color, COGL_BUFFER_BIT_COLOR);
|
||||
cogl_ortho (priv->offscreen_box.x1, priv->offscreen_box.x2,
|
||||
priv->offscreen_box.y2, priv->offscreen_box.y1,
|
||||
0.0, 1.0);
|
||||
st_theme_node_paint (priv->new_theme_node,
|
||||
allocation,
|
||||
paint_opacity);
|
||||
cogl_pop_framebuffer ();
|
||||
}
|
||||
|
||||
void
|
||||
st_theme_node_transition_paint (StThemeNodeTransition *transition,
|
||||
ClutterActorBox *allocation,
|
||||
guint8 paint_opacity)
|
||||
{
|
||||
StThemeNodeTransitionPrivate *priv = transition->priv;
|
||||
|
||||
guint width, height;
|
||||
CoglColor constant = { 0, 0, 0, 0 };
|
||||
float tex_coords[] = {
|
||||
0.0, 0.0, 1.0, 1.0,
|
||||
0.0, 0.0, 1.0, 1.0,
|
||||
};
|
||||
|
||||
g_return_if_fail (ST_IS_THEME_NODE (priv->old_theme_node));
|
||||
g_return_if_fail (ST_IS_THEME_NODE (priv->new_theme_node));
|
||||
|
||||
if (!clutter_actor_box_equal (allocation, &priv->last_allocation) ||
|
||||
paint_opacity != priv->last_opacity)
|
||||
priv->needs_setup = TRUE;
|
||||
|
||||
if (priv->needs_setup)
|
||||
{
|
||||
priv->last_allocation = *allocation;
|
||||
priv->last_opacity = paint_opacity;
|
||||
|
||||
calculate_offscreen_box (transition, allocation);
|
||||
setup_framebuffers (transition, allocation, paint_opacity);
|
||||
|
||||
priv->needs_setup = FALSE;
|
||||
}
|
||||
|
||||
width = cogl_texture_get_width (priv->old_texture);
|
||||
height = cogl_texture_get_height (priv->old_texture);
|
||||
|
||||
constant.alpha = clutter_alpha_get_alpha (priv->alpha) * paint_opacity;
|
||||
|
||||
cogl_material_set_layer_combine_constant (priv->material, 1, &constant);
|
||||
|
||||
cogl_set_source (priv->material);
|
||||
cogl_rectangle_with_multitexture_coords (priv->offscreen_box.x1,
|
||||
priv->offscreen_box.y1,
|
||||
priv->offscreen_box.x2,
|
||||
priv->offscreen_box.y2,
|
||||
tex_coords, 8);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_transition_dispose (GObject *object)
|
||||
{
|
||||
StThemeNodeTransitionPrivate *priv = ST_THEME_NODE_TRANSITION (object)->priv;
|
||||
|
||||
if (priv->old_theme_node)
|
||||
{
|
||||
g_object_unref (priv->old_theme_node);
|
||||
priv->old_theme_node = NULL;
|
||||
}
|
||||
|
||||
if (priv->new_theme_node)
|
||||
{
|
||||
g_object_unref (priv->new_theme_node);
|
||||
priv->new_theme_node = NULL;
|
||||
}
|
||||
|
||||
if (priv->old_texture)
|
||||
{
|
||||
cogl_handle_unref (priv->old_texture);
|
||||
priv->old_texture = NULL;
|
||||
}
|
||||
|
||||
if (priv->new_texture)
|
||||
{
|
||||
cogl_handle_unref (priv->new_texture);
|
||||
priv->new_texture = NULL;
|
||||
}
|
||||
|
||||
if (priv->old_offscreen)
|
||||
{
|
||||
cogl_handle_unref (priv->old_offscreen);
|
||||
priv->old_offscreen = NULL;
|
||||
}
|
||||
|
||||
if (priv->new_offscreen)
|
||||
{
|
||||
cogl_handle_unref (priv->new_offscreen);
|
||||
priv->new_offscreen = NULL;
|
||||
}
|
||||
|
||||
if (priv->material)
|
||||
{
|
||||
cogl_handle_unref (priv->material);
|
||||
priv->material = NULL;
|
||||
}
|
||||
|
||||
if (priv->timeline)
|
||||
{
|
||||
if (priv->timeline_completed_id != 0)
|
||||
g_signal_handler_disconnect (priv->timeline,
|
||||
priv->timeline_completed_id);
|
||||
if (priv->timeline_new_frame_id != 0)
|
||||
g_signal_handler_disconnect (priv->timeline,
|
||||
priv->timeline_new_frame_id);
|
||||
|
||||
g_object_unref (priv->timeline);
|
||||
priv->timeline = NULL;
|
||||
}
|
||||
|
||||
priv->timeline_completed_id = 0;
|
||||
priv->timeline_new_frame_id = 0;
|
||||
|
||||
if (priv->alpha)
|
||||
{
|
||||
g_object_unref (priv->alpha);
|
||||
priv->alpha = NULL;
|
||||
}
|
||||
|
||||
G_OBJECT_CLASS (st_theme_node_transition_parent_class)->dispose (object);
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_transition_init (StThemeNodeTransition *transition)
|
||||
{
|
||||
transition->priv = ST_THEME_NODE_TRANSITION_GET_PRIVATE (transition);
|
||||
|
||||
transition->priv->old_theme_node = NULL;
|
||||
transition->priv->new_theme_node = NULL;
|
||||
|
||||
transition->priv->old_texture = NULL;
|
||||
transition->priv->new_texture = NULL;
|
||||
|
||||
transition->priv->old_offscreen = NULL;
|
||||
transition->priv->new_offscreen = NULL;
|
||||
|
||||
transition->priv->needs_setup = TRUE;
|
||||
|
||||
transition->priv->alpha = NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
st_theme_node_transition_class_init (StThemeNodeTransitionClass *klass)
|
||||
{
|
||||
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
||||
|
||||
g_type_class_add_private (klass, sizeof (StThemeNodeTransitionPrivate));
|
||||
|
||||
object_class->dispose = st_theme_node_transition_dispose;
|
||||
|
||||
signals[COMPLETED] =
|
||||
g_signal_new ("completed",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StThemeNodeTransitionClass, completed),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
|
||||
signals[NEW_FRAME] =
|
||||
g_signal_new ("new-frame",
|
||||
G_TYPE_FROM_CLASS (klass),
|
||||
G_SIGNAL_RUN_LAST,
|
||||
G_STRUCT_OFFSET (StThemeNodeTransitionClass, new_frame),
|
||||
NULL, NULL,
|
||||
g_cclosure_marshal_VOID__VOID,
|
||||
G_TYPE_NONE, 0);
|
||||
}
|
51
src/st/st-theme-node-transition.h
Normal file
51
src/st/st-theme-node-transition.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
|
||||
#ifndef __ST_THEME_NODE_TRANSITION_H__
|
||||
#define __ST_THEME_NODE_TRANSITION_H__
|
||||
|
||||
#include <clutter/clutter.h>
|
||||
|
||||
#include "st-widget.h"
|
||||
#include "st-theme-node.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define ST_TYPE_THEME_NODE_TRANSITION (st_theme_node_transition_get_type ())
|
||||
#define ST_THEME_NODE_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransition))
|
||||
#define ST_IS_THEME_NODE_TRANSITION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), ST_TYPE_THEME_NODE_TRANSITION))
|
||||
#define ST_THEME_NODE_TRANSITION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), ST_TYPE_THEME_NODE_TRANSITION, StThemeNodeTransitionClass))
|
||||
#define ST_IS_THEME_NODE_TRANSITION_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), ST_TYPE_THEME_NODE_TRANSITION))
|
||||
#define ST_THEME_NODE_TRANSITION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), ST_THEME_NODE_TRANSITION, StThemeNodeTransitionClass))
|
||||
|
||||
typedef struct _StThemeNodeTransition StThemeNodeTransition;
|
||||
typedef struct _StThemeNodeTransitionClass StThemeNodeTransitionClass;
|
||||
typedef struct _StThemeNodeTransitionPrivate StThemeNodeTransitionPrivate;
|
||||
|
||||
struct _StThemeNodeTransition {
|
||||
GObject parent;
|
||||
|
||||
StThemeNodeTransitionPrivate *priv;
|
||||
};
|
||||
|
||||
struct _StThemeNodeTransitionClass {
|
||||
GObjectClass parent_class;
|
||||
|
||||
void (*completed) (StThemeNodeTransition *transition);
|
||||
void (*new_frame) (StThemeNodeTransition *transition);
|
||||
};
|
||||
|
||||
GType st_theme_node_transition_get_type (void) G_GNUC_CONST;
|
||||
|
||||
StThemeNodeTransition *st_theme_node_transition_new (StThemeNode *from_node,
|
||||
StThemeNode *to_node,
|
||||
guint duration);
|
||||
|
||||
void st_theme_node_transition_update (StThemeNodeTransition *transition,
|
||||
StThemeNode *new_node);
|
||||
|
||||
void st_theme_node_transition_paint (StThemeNodeTransition *transition,
|
||||
ClutterActorBox *allocation,
|
||||
guint8 paint_opacity);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue
Block a user