Perspective example

This example demonstrates how someone can set a perspective to be used by an Edje object, but setting a global perspective.

The API for setting a perspective for just one Edje object is almost the same and it's trivial, so we are not doing that on this example.

Let's go first to the main function, where we start creating our objects and loading the theme. We also set some variables that will be used globally in our program:

main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
{
const char *edje_file = PACKAGE_DATA_DIR"/perspective.edj";
struct _App app;
Ecore_Evas *ee;
Evas *evas;
return EXIT_FAILURE;
if (!edje_init())
goto shutdown_ecore_evas;
edje_frametime_set(1.0 / 60.0);
/* this will give you a window with an Evas canvas under the first
* engine available */
app.animating = EINA_FALSE;
app.x = 0;
app.y = 0;
app.focal = 50;

A boolean is used to indicate that we are animating.

We also set the app.x and app.y to (0, 0) because the original position of our text + rectangle part will be on top left. This is a convention that we are using in this example, and setting x, y to 1, 1 would mean bottom right. We do this to later define the name of the signals that we are sending to the theme.

After this, some boilerplate code to load the theme:

ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
if (!ee) goto shutdown_edje;
ecore_evas_callback_resize_set(ee, _on_canvas_resize);
ecore_evas_title_set(ee, "Edje Perspective Example");
ecore_evas_data_set(ee, "app", &app);
evas = ecore_evas_get(ee);
app.bg = evas_object_rectangle_add(evas);
evas_object_color_set(app.bg, 255, 255, 255, 255);
evas_object_resize(app.bg, WIDTH, HEIGHT);

Now we are going to setup a callback to tell us that the animation has ended. We do this just to avoid sending signals to the theme while it's animating.

evas_object_event_callback_add(app.bg, EVAS_CALLBACK_KEY_DOWN, _on_bg_key_down, &app);
app.edje_obj = edje_object_add(evas);
edje_object_file_set(app.edje_obj, edje_file, "example/group");
evas_object_move(app.edje_obj, 0, 0);
evas_object_resize(app.edje_obj, WIDTH, HEIGHT);
evas_object_show(app.edje_obj);
edje_object_signal_callback_add(app.edje_obj, "animation,end", "", _animation_end_cb, &app);

Finally, let's create our perspective object, define its position, focal distance and z plane position, and set it as global:

app.ps = edje_perspective_new(evas);
edje_perspective_set(app.ps, 240, 160, 0, app.focal);

Notice that if we wanted to set it just to our edje object, instead of setting the perspective as global to the entire canvas, we could just use edje_object_perspective_set() instead of edje_perspective_global_set(). The rest of the code would be exactly the same.

Now, let's take a look at what we do in our callbacks.

The callback for key_down is converting the arrow keys to a signal that represents where we want our text and rectangle moved to. It does that by using the following function:

_part_move(struct _App *app, int dx, int dy)
{
char emission[64];
if (app->animating)
return;
app->x += dx;
app->y += dy;
if (app->x > 1)
app->x = 1;
if (app->x < 0)
app->x = 0;
if (app->y > 1)
app->y = 1;
if (app->y < 0)
app->y = 0;
snprintf(emission, sizeof(emission), "move,%d,%d", app->x, app->y);
edje_object_signal_emit(app->edje_obj, emission, "");
app->animating = EINA_TRUE;
}

Notice that, after sending the signal to the Edje object, we set our boolean to store that we are animating now. It will only be unset when we receive a signal from the theme that the animation has ended.

Now, on the key_down code, we just call this function when the arrows or "PgUp" or "PgDown" keys are pressed:

static void
_on_bg_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
{
struct _App *app = data;
Evas_Event_Key_Down *ev = event_info;
if (!strcmp(ev->key, "h"))
{
printf(commands);
return;
}
// just moving the part and text
else if (!strcmp(ev->key, "Down"))
{
_part_move(app, 0, 1);
}
else if (!strcmp(ev->key, "Up"))
{
_part_move(app, 0, -1);
}
else if (!strcmp(ev->key, "Left"))
{
_part_move(app, -1, 0);
}
else if (!strcmp(ev->key, "Right"))
{
_part_move(app, 1, 0);
}
else if (!strcmp(ev->key, "Prior"))
{
_part_move(app, -1, -1);
}
else if (!strcmp(ev->key, "Next"))
{
_part_move(app, 1, 1);
}
// adjusting the perspective focal point distance
else if (!strcmp(ev->key, "KP_Add"))
{
app->focal += 5;
edje_perspective_set(app->ps, 240, 160, 0, app->focal);
edje_object_calc_force(app->edje_obj);
}
else if (!strcmp(ev->key, "KP_Subtract"))
{
app->focal -= 5;
if (app->focal < 5)
app->focal = 5;
edje_perspective_set(app->ps, 240, 160, 0, app->focal);
edje_object_calc_force(app->edje_obj);
}
// exiting
else if (!strcmp(ev->key, "Escape"))
else
{
printf("unhandled key: %s\n", ev->key);
printf(commands);
}

Notice that we also do something else when the numeric keyboard "+" and "-" keys are pressed. We change the focal distance of our global perspective, and that will affect the part that has a map rotation applied to it, with perspective enabled. We also need to call edje_object_calc_force(), otherwise the Edje object has no way to know that we changed the global perspective.

Try playing with these keys and see what happens to the animation when the value of the focal distance changes.

Finally we add a callback for the animation ended signal:

_animation_end_cb(void *data, Evas_Object *o EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
struct _App *app = data;
app->animating = EINA_FALSE;
}

The example's window should look like this picture:

The full source code follows:

#ifdef HAVE_CONFIG_H
# include "config.h"
#else
# define EINA_UNUSED
#endif
#ifndef PACKAGE_DATA_DIR
#define PACKAGE_DATA_DIR "."
#endif
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Edje.h>
#define WIDTH 480
#define HEIGHT 320
static const char commands[] = \
"commands are:\n"
"\tDown - move part down\n"
"\tUp - move part up\n"
"\tLeft - move part left\n"
"\tRight - move part right\n"
"\tPrior - move part up-left\n"
"\tNext - move part down-right\n"
"\tInsert - increase focal\n"
"\tSuppr - decrease focal\n"
"\tEsc - exit\n"
"\th - print help\n";
struct _App {
Evas_Object *edje_obj;
Eina_Bool animating;
int x, y; // relative position of part in the screen
int focal;
};
static void
_on_destroy(Ecore_Evas *ee EINA_UNUSED)
{
}
/* here just to keep our example's window size and background image's
* size in synchrony */
static void
_on_canvas_resize(Ecore_Evas *ee)
{
int w, h;
struct _App *app = ecore_evas_data_get(ee, "app");
ecore_evas_geometry_get(ee, NULL, NULL, &w, &h);
evas_object_resize(app->bg, w, h);
evas_object_resize(app->edje_obj, w, h);
}
static void
_part_move(struct _App *app, int dx, int dy)
{
char emission[64];
if (app->animating)
return;
app->x += dx;
app->y += dy;
if (app->x > 1)
app->x = 1;
if (app->x < 0)
app->x = 0;
if (app->y > 1)
app->y = 1;
if (app->y < 0)
app->y = 0;
snprintf(emission, sizeof(emission), "move,%d,%d", app->x, app->y);
edje_object_signal_emit(app->edje_obj, emission, "");
app->animating = EINA_TRUE;
}
static void
_on_bg_key_down(void *data, Evas *e EINA_UNUSED, Evas_Object *o EINA_UNUSED, void *event_info)
{
struct _App *app = data;
Evas_Event_Key_Down *ev = event_info;
if (!strcmp(ev->key, "h"))
{
printf(commands);
return;
}
// just moving the part and text
else if (!strcmp(ev->key, "Down"))
{
_part_move(app, 0, 1);
}
else if (!strcmp(ev->key, "Up"))
{
_part_move(app, 0, -1);
}
else if (!strcmp(ev->key, "Left"))
{
_part_move(app, -1, 0);
}
else if (!strcmp(ev->key, "Right"))
{
_part_move(app, 1, 0);
}
else if (!strcmp(ev->key, "Prior"))
{
_part_move(app, -1, -1);
}
else if (!strcmp(ev->key, "Next"))
{
_part_move(app, 1, 1);
}
// adjusting the perspective focal point distance
else if (!strcmp(ev->key, "KP_Add"))
{
app->focal += 5;
edje_perspective_set(app->ps, 240, 160, 0, app->focal);
edje_object_calc_force(app->edje_obj);
}
else if (!strcmp(ev->key, "KP_Subtract"))
{
app->focal -= 5;
if (app->focal < 5)
app->focal = 5;
edje_perspective_set(app->ps, 240, 160, 0, app->focal);
edje_object_calc_force(app->edje_obj);
}
// exiting
else if (!strcmp(ev->key, "Escape"))
else
{
printf("unhandled key: %s\n", ev->key);
printf(commands);
}
}
static void
_animation_end_cb(void *data, Evas_Object *o EINA_UNUSED, const char *emission EINA_UNUSED, const char *source EINA_UNUSED)
{
struct _App *app = data;
app->animating = EINA_FALSE;
}
int
main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
{
const char *edje_file = PACKAGE_DATA_DIR"/perspective.edj";
struct _App app;
Ecore_Evas *ee;
Evas *evas;
return EXIT_FAILURE;
if (!edje_init())
goto shutdown_ecore_evas;
edje_frametime_set(1.0 / 60.0);
/* this will give you a window with an Evas canvas under the first
* engine available */
app.animating = EINA_FALSE;
app.x = 0;
app.y = 0;
app.focal = 50;
ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
if (!ee) goto shutdown_edje;
ecore_evas_callback_resize_set(ee, _on_canvas_resize);
ecore_evas_title_set(ee, "Edje Perspective Example");
ecore_evas_data_set(ee, "app", &app);
evas = ecore_evas_get(ee);
app.bg = evas_object_rectangle_add(evas);
evas_object_color_set(app.bg, 255, 255, 255, 255);
evas_object_resize(app.bg, WIDTH, HEIGHT);
evas_object_event_callback_add(app.bg, EVAS_CALLBACK_KEY_DOWN, _on_bg_key_down, &app);
app.edje_obj = edje_object_add(evas);
edje_object_file_set(app.edje_obj, edje_file, "example/group");
evas_object_move(app.edje_obj, 0, 0);
evas_object_resize(app.edje_obj, WIDTH, HEIGHT);
evas_object_show(app.edje_obj);
edje_object_signal_callback_add(app.edje_obj, "animation,end", "", _animation_end_cb, &app);
app.ps = edje_perspective_new(evas);
edje_perspective_set(app.ps, 240, 160, 0, app.focal);
printf(commands);
return EXIT_SUCCESS;
shutdown_edje:
shutdown_ecore_evas:
return EXIT_FAILURE;
}

The full .edc file

collections {
group {
name: "example/group";
min: 480 320;
parts {
part {
name: "bg";
type: RECT;
mouse_events: 1;
description {
state: "default" 0.0;
}
} // bg
part {
name: "rectangle";
type: RECT;
mouse_events: 0;
description {
state: "default" 0.0;
color: 255 0 0 128;
rel1 {
offset: -5 -5;
to: "title";
}
rel2 {
offset: 4 4;
to: "title";
}
map {
on: 1;
perspective_on: 1;
rotation {
x: 45;
}
}
}
} // rectangle
part {
name: "title";
type: TEXT;
mouse_events: 0;
description {
state: "default" 0.0;
color: 200 200 200 255;
align: 0.0 0.5;
rel1.relative: 0.1 0.1;
rel2.relative: 0.1 0.1;
text {
text: "Perspective example";
font: "Sans";
size: 16;
min: 1 1;
}
map {
on: 1;
perspective_on: 1;
rotation {
x: 45;
}
}
}
description {
state: "right" 0.0;
inherit: "default" 0.0;
rel1.relative: 0.5 0.1;
rel2.relative: 0.5 0.1;
}
description {
state: "bottom" 0.0;
inherit: "default" 0.0;
rel1.relative: 0.1 0.9;
rel2.relative: 0.1 0.9;
}
description {
state: "bottomright" 0.0;
inherit: "default" 0.0;
rel1.relative: 0.5 0.9;
rel2.relative: 0.5 0.9;
}
} // title
}
programs {
program {
name: "move,right";
signal: "move,1,0";
action: STATE_SET "right" 0.0;
transition: SINUSOIDAL 1.0;
target: "title";
after: "animation,end";
}
program {
name: "move,bottom";
signal: "move,0,1";
action: STATE_SET "bottom" 0.0;
transition: SINUSOIDAL 1.0;
target: "title";
after: "animation,end";
}
program {
name: "move,bottomright";
signal: "move,1,1";
action: STATE_SET "bottomright" 0.0;
transition: SINUSOIDAL 1.0;
target: "title";
after: "animation,end";
}
program {
name: "move,default";
signal: "move,0,0";
action: STATE_SET "default" 0.0;
transition: SINUSOIDAL 1.0;
target: "title";
after: "animation,end";
}
program {
name: "animation,end";
action: SIGNAL_EMIT "animation,end" "";
}
}
}
}

To compile use this command:

* gcc -o edje-perspective edje-perspective.c -DPACKAGE_BIN_DIR=\"/Where/enlightenment/is/installed/bin\"
* -DPACKAGE_LIB_DIR=\"/Where/enlightenment/is/installed/lib\"
* -DPACKAGE_DATA_DIR=\"/Where/enlightenment/is/installed/share\"
* `pkg-config --cflags --libs evas ecore ecore-evas edje`
*
* edje_cc perspective.edc
* 
edje_init
int edje_init(void)
Initializes the Edje library.
Definition: edje_main.c:35
ecore_evas_new
EAPI Ecore_Evas * ecore_evas_new(const char *engine_name, int x, int y, int w, int h, const char *extra_options)
Creates a new Ecore_Evas based on engine name and common parameters.
Definition: ecore_evas.c:1065
ecore_evas_shutdown
EAPI int ecore_evas_shutdown(void)
Shuts down the Ecore_Evas system.
Definition: ecore_evas.c:674
edje_object_file_set
Eina_Bool edje_object_file_set(Evas_Object *obj, const char *file, const char *group)
Sets the EDJ file (and group within it) to load an Edje object's contents from.
Definition: edje_smart.c:467
ecore_evas_geometry_get
EAPI void ecore_evas_geometry_get(const Ecore_Evas *ee, int *x, int *y, int *w, int *h)
Gets the geometry of an Ecore_Evas.
Definition: ecore_evas.c:1388
edje_frametime_set
void edje_frametime_set(double t)
Sets edje transitions' frame time.
Definition: edje_program.c:216
ecore_evas_callback_resize_set
EAPI void ecore_evas_callback_resize_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func)
Sets a callback for Ecore_Evas resize events.
Definition: ecore_evas.c:1166
ecore_main_loop_quit
void ecore_main_loop_quit(void)
Quits the main loop once all the events currently on the queue have been processed.
Definition: ecore_main.c:1308
_Evas_Event_Key_Down
Key press event.
Definition: Evas_Legacy.h:314
edje_perspective_set
void edje_perspective_set(Edje_Perspective *ps, Evas_Coord px, Evas_Coord py, Evas_Coord z0, Evas_Coord foc)
Sets up the transform for this perspective object.
Definition: edje_util.c:5286
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition: eina_types.h:339
ecore_evas_free
EAPI void ecore_evas_free(Ecore_Evas *ee)
Frees an Ecore_Evas.
Definition: ecore_evas.c:1109
ecore_evas_title_set
EAPI void ecore_evas_title_set(Ecore_Evas *ee, const char *t)
Sets the title of an Ecore_Evas' window.
Definition: ecore_evas.c:1553
ecore_evas_init
EAPI int ecore_evas_init(void)
Inits the Ecore_Evas system.
Definition: ecore_evas.c:606
edje_object_signal_emit
void edje_object_signal_emit(Evas_Object *obj, const char *emission, const char *source)
Sends/emits an Edje signal to a given Edje object.
Definition: edje_legacy.c:147
edje_perspective_new
Edje_Perspective * edje_perspective_new(Evas *e)
Creates a new perspective in the given canvas.
Definition: edje_util.c:5259
edje_object_signal_callback_add
void edje_object_signal_callback_add(Evas_Object *obj, const char *emission, const char *source, Edje_Signal_Cb func, void *data)
Adds a callback for an arriving Edje signal, emitted by a given Edje object.
Definition: edje_legacy.c:85
EINA_FALSE
#define EINA_FALSE
boolean value FALSE (numerical value 0)
Definition: eina_types.h:533
Evas_Object
Efl_Canvas_Object Evas_Object
An Evas Object handle.
Definition: Evas_Common.h:185
evas_object_resize
void evas_object_resize(Evas_Object *obj, Evas_Coord w, Evas_Coord h)
Changes the size of the given Evas object.
Definition: evas_object_main.c:1236
Edje.h
Edje Graphical Design Library.
Ecore_Evas.h
Evas wrapper functions.
ecore_evas_get
EAPI Evas * ecore_evas_get(const Ecore_Evas *ee)
Gets an Ecore_Evas's Evas.
Definition: ecore_evas.c:1326
ecore_evas_data_get
EAPI void * ecore_evas_data_get(const Ecore_Evas *ee, const char *key)
Retrieves user data associated with an Ecore_Evas.
Definition: ecore_evas.c:1118
edje_object_add
Evas_Object * edje_object_add(Evas *evas)
Instantiates a new Edje object.
Definition: edje_smart.c:22
evas_object_event_callback_add
void evas_object_event_callback_add(Evas_Object *eo_obj, Evas_Callback_Type type, Evas_Object_Event_Cb func, const void *data)
Add (register) a callback function to a given Evas object event.
Definition: evas_callbacks.c:478
EVAS_CALLBACK_KEY_DOWN
@ EVAS_CALLBACK_KEY_DOWN
Key Press Event.
Definition: Evas_Common.h:430
_Evas_Event_Key_Down::key
const char * key
The logical key : (eg shift+1 == exclamation)
Definition: Evas_Legacy.h:320
ecore_main_loop_begin
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition: ecore_main.c:1298
evas_object_focus_set
void evas_object_focus_set(Efl_Canvas_Object *obj, Eina_Bool focus)
Indicates that this object is the keyboard event receiver on its canvas.
Definition: efl_canvas_object_eo.legacy.c:39
evas_object_show
void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition: evas_object_main.c:1814
Evas
Eo Evas
An opaque handle to an Evas canvas.
Definition: Evas_Common.h:163
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition: eina_types.h:539
Edje_Perspective
struct _Edje_Perspective Edje_Perspective
Perspective info for maps inside edje objects.
Definition: Edje_Common.h:140
edje_object_calc_force
void edje_object_calc_force(Edje_Object *obj)
Forces a Size/Geometry calculation.
Definition: edje_legacy.c:1110
Eina_Bool
unsigned char Eina_Bool
Type to mimic a boolean.
Definition: eina_types.h:527
edje_perspective_global_set
void edje_perspective_global_set(Edje_Perspective *ps, Eina_Bool global)
Makes this perspective object be global for its canvas.
Definition: edje_util.c:5327
evas_object_rectangle_add
Evas_Object * evas_object_rectangle_add(Evas *e)
Adds a rectangle to the given evas.
Definition: evas_object_rectangle.c:78
evas_object_move
void evas_object_move(Evas_Object *obj, Evas_Coord x, Evas_Coord y)
Move the given Evas object to the given location inside its canvas' viewport.
Definition: evas_object_main.c:1171
ecore_evas_data_set
EAPI void ecore_evas_data_set(Ecore_Evas *ee, const char *key, const void *data)
Stores user data in an Ecore_Evas structure.
Definition: ecore_evas.c:1129
ecore_evas_callback_destroy_set
EAPI void ecore_evas_callback_destroy_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func)
Sets a callback for Ecore_Evas destroy events.
Definition: ecore_evas.c:1211
ecore_evas_show
EAPI void ecore_evas_show(Ecore_Evas *ee)
Shows an Ecore_Evas' window.
Definition: ecore_evas.c:1506
evas_object_color_set
void evas_object_color_set(Evas_Object *obj, int r, int g, int b, int a)
Sets the general/main color of the given Evas object to the given one.
Definition: evas_object_main.c:2024
edje_shutdown
int edje_shutdown(void)
Shuts down the Edje library.
Definition: edje_main.c:264