Edje signals and messages

In this example, we illustrate how Edje signals and Edje messages work.

We place, in the canvas, an Edje object along with a red border image to delimit its geometry. The object's group definition is so that we have four parts:

  • a blue rectangle, aligned to the right
  • a white rectangle, aligned to the left
  • a text part, aligned to the center
  • a clipper rectangle on the blue rectangle

The left rectangle is bound to a color class, so that we can multiply its colors by chosen values on the go:

#define MSG_COLOR 1
#define MSG_TEXT 2
collections {
group {
name: "example_group";
parts {
part {
name: "part_right";
type: RECT;
clip_to: "part_right_clipper";
description {
min: 50 50;
max: 50 50;
state: "default" 0.0;
color: 0 0 255 255; /* blue */
rel1.relative: 1.0 0.5;
rel1.offset: -49 0;
rel2.relative: 1.0 0.5;
}
}
part {
name: "part_left";
type: RECT;
description {
color_class: "cc";
min: 50 50;
max: 50 50;
state: "default" 0.0;
rel1.relative: 0.0 0.5;
rel2.relative: 0.0 0.5;
rel2.offset: 50 -1;
}
}
part {
name: "text";
type: TEXT;
description {
min: 150 50;
max: 150 50;
fixed: 1 1;
color: 0 0 0 255;
state: "default" 0.0;
rel1.relative: 0.5 0.5;
rel2.relative: 0.5 0.5;
text {
font: "Sans";
size: 20;
min: 1 1;
align: 0.5 0.5;
}
}
}
part {
name: "part_right_clipper";
type: RECT;
repeat_events: 1;
description {
min: 50 50;
max: 50 50;
state: "default" 0.0;
rel1.relative: 1.0 0.5;
rel1.offset: -49 0;
rel2.relative: 1.0 0.5;
}
description {
state: "hidden" 0.0;
inherit: "default" 0.0;
visible: 0;
}
}
}

The #define's on the beginning will serve as message identifiers, for our accorded message interface between the code and the this theme file.

Let's move to the code, then. After instantiating the Edje object, we register two signal callbacks on it. The first one uses globbing, making all of the wheel mouse actions over the left rectangle to trigger _mouse_wheel. Note that those kind of signals are generated internally (and automatically) in Edje. The second is a direct signal match, to a (custom) signal we defined in the EDC, ourselves:

edje_obj = edje_object_add(evas);
if (!edje_object_file_set(edje_obj, edje_file, "example_group"))
{
int err = edje_object_load_error_get(edje_obj);
const char *errmsg = edje_load_error_str(err);
fprintf(stderr, "Could not load 'example_group' from "
"signals-messages.edj: %s\n", errmsg);
evas_object_del(edje_obj);
goto shutdown_edje;
}
edje_object_signal_callback_add(edje_obj, "mouse,wheel,*", "part_left",
_on_mouse_wheel, NULL);
edje_object_signal_callback_add(edje_obj, "mouse,over", "part_right",
_on_mouse_over, NULL);
Edje_Load_Error edje_object_load_error_get(const Eo *obj)
Gets the (last) file loading error for a given Edje object.
Definition edje_legacy.c:15
Evas_Object * edje_object_add(Evas *evas)
Instantiates a new Edje object.
Definition edje_smart.c:22
const char * edje_load_error_str(Edje_Load_Error error)
Converts the given Edje file load error code into a string describing it in English.
Definition edje_load.c:108
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_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
EVAS_API void evas_object_del(Evas_Object *obj)
Marks the given Evas object for deletion (when Evas will free its memory).
Definition evas_object_main.c:928
/* print signals coming from theme */
static void
_sig_print(const char *emission,
const char *source)
{
printf("Signal %s coming from part %s!\n", emission, source);
}
static void
_on_mouse_wheel(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
const char *emission,
const char *source)
{
_sig_print(emission, source);
}
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition eina_types.h:339

That second callback is on a signal we emit on the theme, where we just translate Edje "mouse,move" internal events to the custom "mouse,over" one. When that signals reaches the code, we are, besides printing the signals' strings, sending a message back to the theme. We generate random values of color components and send them as an EDJE_MESSAGE_INT_SET message type:

program { /* custom signal */
name: "part_right,hovered";
signal: "mouse,move";
source: "part_right";
action: SIGNAL_EMIT "mouse,over" "part_right";
}
/* mouse over signals */
static void
_on_mouse_over(void *data EINA_UNUSED,
Evas_Object *edje_obj,
const char *emission,
const char *source)
{
int i;
_sig_print(emission, source);
Edje_Message_Int_Set *msg = malloc(sizeof(*msg) + 3 * sizeof(int));
msg->count = 4;
for (i = 0; i < 4; i++)
msg->val[i] = rand() % 256;
edje_object_message_send(edje_obj, EDJE_MESSAGE_INT_SET, MSG_COLOR, msg);
free(msg);
}
void edje_object_message_send(Evas_Object *obj, Edje_Message_Type type, int id, void *msg)
Sends an (Edje) message to a given Edje object.
Definition edje_message_queue.c:1016
@ EDJE_MESSAGE_INT_SET
A message with a list of integer numbers as value.
Definition Edje_Legacy.h:586
Structure passed as value on EDJE_MESSAGE_INT_SET messages.
Definition Edje_Legacy.h:527
int val[1]
The message's array of integers.
Definition Edje_Legacy.h:529
int count
The size of the message's array (may be greater than 1)
Definition Edje_Legacy.h:528

In our theme we'll be changing the "cc" color class' values with those integer values of the message, so that moving the mouse over the right rectangle will change the left one's colors:

public message(Msg_Type:type, id, ...) {
if ((type == MSG_INT_SET) && (id == MSG_COLOR)) {
new r, g, b, a;
r = getarg(2);
g = getarg(3);
b = getarg(4);
a = getarg(5);
set_color_class("cc", r, g, b, a);
}
}

Now we're also sending messages from the Edje object, besides signals. We do so when one clicks with the left button over the left rectangle. With that, we change the text part's text, cycling between 3 pre-set strings declared in the EDC. With each new text string attribution, we send a string message to our code, with the current string as argument:

programs {
program {
name: "bootstrap";
signal: "load";
source: "";
script {
set_str(global_str0, "String one");
set_str(global_str1, "String two");
set_str(global_str2, "String three");
set_int(str_idx, 0);
set_text_string();
}
}
program { /* change text part's string value */
name: "text,change";
signal: "mouse,clicked,1";
source: "part_left";
script {
new idx;
idx = get_int(str_idx);
idx = idx + 1;
if (idx > 2)
set_int(str_idx, 0);
else
set_int(str_idx, idx);
set_text_string();
}
}
public set_text_string() {
new tmp[1024];
new idx;
idx = get_int(str_idx);
if (idx == 0)
get_str(global_str0, tmp, 1024);
else if (idx == 1)
get_str(global_str1, tmp, 1024);
else if (idx == 2)
get_str(global_str2, tmp, 1024);
else return;
set_text(PART:"text", tmp);
send_message(MSG_STRING, MSG_TEXT, tmp);
}

To get the message in code, we have to register a message handler, as follows:

edje_object_message_handler_set(edje_obj, _message_handle, NULL);
void edje_object_message_handler_set(Evas_Object *obj, Edje_Message_Handler_Cb func, void *data)
Sets an Edje message handler function for a given Edje object.
Definition edje_message_queue.c:1028
/* print out received message string */
static void
_message_handle(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
int id,
void *msg)
{
if (type != EDJE_MESSAGE_STRING) return;
if (id != MSG_TEXT) return;
m = msg;
printf("String message received: %s\n", m->str);
}
Edje_Message_Type
Identifiers of Edje message types, which can be sent back and forth code and a given Edje object's th...
Definition Edje_Legacy.h:571
@ EDJE_MESSAGE_STRING
A message with a string as value.
Definition Edje_Legacy.h:574
Structure passed as value on EDJE_MESSAGE_STRING messages.
Definition Edje_Legacy.h:506

To interact with the last missing feature – emitting signals from code – there's a command line interface to exercise it. A help string can be asked for with the 'h' key:

static const char commands[] = \
"commands are:\n"
"\tt - toggle right rectangle's visibility\n"
"\tEsc - exit\n"
"\th - print help\n";

The 't' command will send either "part_right,show" or "part_right,hide" signals to the Edje object (those being the emission part of the signal), which was set to react on them as the names indicate. We'll set the right rectangle's visibility on/off, respectively, for those two signals:

program { /* hide right rectangle */
name: "part_right,hide";
signal: "part_right,hide";
source: "";
action: STATE_SET "hidden" 0.0;
target: "part_right_clipper";
}
program {
name: "part_right,show";
signal: "part_right,show";
source: "";
action: STATE_SET "default" 0.0;
target: "part_right_clipper";
}
else if (!strcmp(ev->key, "t")) /* toggle right rectangle's visibility */
{
char buf[1024];
right_rect_show = !right_rect_show;
snprintf(buf, sizeof(buf), "part_right,%s",
right_rect_show ? "show" : "hide");
printf("emitting %s\n", buf);
edje_object_signal_emit(edje_obj, buf, "");
return;
}
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

The example's window should look like this picture:

The full example follows, along with its EDC file.

#define MSG_COLOR 1
#define MSG_TEXT 2
collections {
group {
name: "example_group";
parts {
part {
name: "part_right";
type: RECT;
clip_to: "part_right_clipper";
description {
min: 50 50;
max: 50 50;
state: "default" 0.0;
color: 0 0 255 255; /* blue */
rel1.relative: 1.0 0.5;
rel1.offset: -49 0;
rel2.relative: 1.0 0.5;
}
}
part {
name: "part_left";
type: RECT;
description {
color_class: "cc";
min: 50 50;
max: 50 50;
state: "default" 0.0;
rel1.relative: 0.0 0.5;
rel2.relative: 0.0 0.5;
rel2.offset: 50 -1;
}
}
part {
name: "text";
type: TEXT;
description {
min: 150 50;
max: 150 50;
fixed: 1 1;
color: 0 0 0 255;
state: "default" 0.0;
rel1.relative: 0.5 0.5;
rel2.relative: 0.5 0.5;
text {
font: "Sans";
size: 20;
min: 1 1;
align: 0.5 0.5;
}
}
}
part {
name: "part_right_clipper";
type: RECT;
repeat_events: 1;
description {
min: 50 50;
max: 50 50;
state: "default" 0.0;
rel1.relative: 1.0 0.5;
rel1.offset: -49 0;
rel2.relative: 1.0 0.5;
}
description {
state: "hidden" 0.0;
inherit: "default" 0.0;
visible: 0;
}
}
}
script {
public global_str0;
public global_str1;
public global_str2;
public str_idx;
public set_text_string() {
new tmp[1024];
new idx;
idx = get_int(str_idx);
if (idx == 0)
get_str(global_str0, tmp, 1024);
else if (idx == 1)
get_str(global_str1, tmp, 1024);
else if (idx == 2)
get_str(global_str2, tmp, 1024);
else return;
set_text(PART:"text", tmp);
send_message(MSG_STRING, MSG_TEXT, tmp);
}
public message(Msg_Type:type, id, ...) {
if ((type == MSG_INT_SET) && (id == MSG_COLOR)) {
new r, g, b, a;
r = getarg(2);
g = getarg(3);
b = getarg(4);
a = getarg(5);
set_color_class("cc", r, g, b, a);
}
}
}
programs {
program {
name: "bootstrap";
signal: "load";
source: "";
script {
set_str(global_str0, "String one");
set_str(global_str1, "String two");
set_str(global_str2, "String three");
set_int(str_idx, 0);
set_text_string();
}
}
program { /* custom signal */
name: "part_right,hovered";
signal: "mouse,move";
source: "part_right";
action: SIGNAL_EMIT "mouse,over" "part_right";
}
program { /* hide right rectangle */
name: "part_right,hide";
signal: "part_right,hide";
source: "";
action: STATE_SET "hidden" 0.0;
target: "part_right_clipper";
}
program {
name: "part_right,show";
signal: "part_right,show";
source: "";
action: STATE_SET "default" 0.0;
target: "part_right_clipper";
}
program { /* change text part's string value */
name: "text,change";
signal: "mouse,clicked,1";
source: "part_left";
script {
new idx;
idx = get_int(str_idx);
idx = idx + 1;
if (idx > 2)
set_int(str_idx, 0);
else
set_int(str_idx, idx);
set_text_string();
}
}
}
}
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#else
#define PACKAGE_EXAMPLES_DIR "."
#define EINA_UNUSED
#endif
#ifndef PACKAGE_DATA_DIR
#define PACKAGE_DATA_DIR "."
#endif
#include <Ecore.h>
#include <Ecore_Evas.h>
#include <Edje.h>
#include <stdio.h>
#define WIDTH (300)
#define HEIGHT (300)
#define MSG_COLOR 1
#define MSG_TEXT 2
static const char commands[] = \
"commands are:\n"
"\tt - toggle right rectangle's visibility\n"
"\tEsc - exit\n"
"\th - print help\n";
static Eina_Bool right_rect_show = EINA_TRUE;
static void
_on_keydown(void *data,
Evas *evas EINA_UNUSED,
Evas_Object *o EINA_UNUSED,
void *einfo)
{
Evas_Object *edje_obj;
ev = (Evas_Event_Key_Down *)einfo;
edje_obj = (Evas_Object *)data;
if (!strcmp(ev->key, "h")) /* print help */
{
printf(commands);
return;
}
else if (!strcmp(ev->key, "t")) /* toggle right rectangle's visibility */
{
char buf[1024];
right_rect_show = !right_rect_show;
snprintf(buf, sizeof(buf), "part_right,%s",
right_rect_show ? "show" : "hide");
printf("emitting %s\n", buf);
edje_object_signal_emit(edje_obj, buf, "");
return;
}
else if (!strcmp(ev->key, "Escape"))
else
{
printf("unhandled key: %s\n", ev->key);
printf(commands);
}
}
static void
_on_delete(Ecore_Evas *ee EINA_UNUSED)
{
}
/* print signals coming from theme */
static void
_sig_print(const char *emission,
const char *source)
{
printf("Signal %s coming from part %s!\n", emission, source);
}
static void
_on_mouse_wheel(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
const char *emission,
const char *source)
{
_sig_print(emission, source);
}
/* mouse over signals */
static void
_on_mouse_over(void *data EINA_UNUSED,
Evas_Object *edje_obj,
const char *emission,
const char *source)
{
int i;
_sig_print(emission, source);
Edje_Message_Int_Set *msg = malloc(sizeof(*msg) + 3 * sizeof(int));
msg->count = 4;
for (i = 0; i < 4; i++)
msg->val[i] = rand() % 256;
edje_object_message_send(edje_obj, EDJE_MESSAGE_INT_SET, MSG_COLOR, msg);
free(msg);
}
/* print out received message string */
static void
_message_handle(void *data EINA_UNUSED,
Evas_Object *obj EINA_UNUSED,
int id,
void *msg)
{
if (type != EDJE_MESSAGE_STRING) return;
if (id != MSG_TEXT) return;
m = msg;
printf("String message received: %s\n", m->str);
}
int
main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
{
const char *img_file = PACKAGE_DATA_DIR"/red.png";
const char *edje_file = PACKAGE_DATA_DIR"/signals-messages.edj";
Ecore_Evas *ee;
Evas *evas;
Evas_Object *bg;
Evas_Object *edje_obj;
Evas_Object *border;
return EXIT_FAILURE;
if (!edje_init())
goto shutdown_ecore_evas;
/* this will give you a window with an Evas canvas under the first
* engine available */
ee = ecore_evas_new(NULL, 0, 0, WIDTH, HEIGHT, NULL);
if (!ee) goto shutdown_edje;
ecore_evas_title_set(ee, "Edje Signals and Messages Example");
evas = ecore_evas_get(ee);
evas_object_color_set(bg, 255, 255, 255, 255); /* white bg */
evas_object_move(bg, 0, 0); /* at canvas' origin */
evas_object_resize(bg, WIDTH, HEIGHT); /* covers full canvas */
ecore_evas_object_associate(ee, bg, ECORE_EVAS_OBJECT_ASSOCIATE_BASE);
edje_obj = edje_object_add(evas);
if (!edje_object_file_set(edje_obj, edje_file, "example_group"))
{
int err = edje_object_load_error_get(edje_obj);
const char *errmsg = edje_load_error_str(err);
fprintf(stderr, "Could not load 'example_group' from "
"signals-messages.edj: %s\n", errmsg);
evas_object_del(edje_obj);
goto shutdown_edje;
}
edje_object_signal_callback_add(edje_obj, "mouse,wheel,*", "part_left",
_on_mouse_wheel, NULL);
edje_object_signal_callback_add(edje_obj, "mouse,over", "part_right",
_on_mouse_over, NULL);
edje_object_message_handler_set(edje_obj, _message_handle, NULL);
evas_object_move(edje_obj, 20, 20);
evas_object_resize(edje_obj, WIDTH - 40, HEIGHT - 40);
evas_object_show(edje_obj);
/* this is a border around the Edje object above, here just to
* emphasize its geometry */
evas_object_image_file_set(border, img_file, NULL);
evas_object_image_border_set(border, 2, 2, 2, 2);
evas_object_resize(border, WIDTH - 40 + 4, HEIGHT - 40 + 4);
evas_object_move(border, 20 - 2, 20 - 2);
printf(commands);
return EXIT_SUCCESS;
shutdown_edje:
shutdown_ecore_evas:
return EXIT_FAILURE;
}
Evas wrapper functions.
Edje Graphical Design Library.
@ EVAS_CALLBACK_KEY_DOWN
Key Press Event.
Definition Evas_Common.h:430
EAPI int ecore_evas_init(void)
Inits the Ecore_Evas system.
Definition ecore_evas.c:608
EAPI void ecore_evas_title_set(Ecore_Evas *ee, const char *t)
Sets the title of an Ecore_Evas' window.
Definition ecore_evas.c:1541
EAPI void ecore_evas_callback_delete_request_set(Ecore_Evas *ee, Ecore_Evas_Event_Cb func)
Sets a callback for Ecore_Evas delete request events.
Definition ecore_evas.c:1190
EAPI void ecore_evas_show(Ecore_Evas *ee)
Shows an Ecore_Evas' window.
Definition ecore_evas.c:1494
EAPI Evas * ecore_evas_get(const Ecore_Evas *ee)
Gets an Ecore_Evas's Evas.
Definition ecore_evas.c:1314
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:1053
EAPI int ecore_evas_shutdown(void)
Shuts down the Ecore_Evas system.
Definition ecore_evas.c:672
EAPI Eina_Bool ecore_evas_object_associate(Ecore_Evas *ee, Evas_Object *obj, Ecore_Evas_Object_Associate_Flags flags)
Associates the given object to this ecore evas.
Definition ecore_evas_util.c:223
EAPI void ecore_evas_free(Ecore_Evas *ee)
Frees an Ecore_Evas.
Definition ecore_evas.c:1097
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:1321
void ecore_main_loop_begin(void)
Runs the application main loop.
Definition ecore_main.c:1311
int edje_shutdown(void)
Shuts down the Edje library.
Definition edje_main.c:262
int edje_init(void)
Initializes the Edje library.
Definition edje_main.c:35
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition eina_types.h:539
unsigned char Eina_Bool
Type to mimic a boolean.
Definition eina_types.h:527
EVAS_API void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition evas_object_main.c:1814
EVAS_API 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
EVAS_API 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_API 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
EVAS_API void evas_object_repeat_events_set(Efl_Canvas_Object *obj, Eina_Bool repeat)
Set whether an Evas object is to repeat events.
Definition efl_canvas_object_eo.legacy.c:27
EVAS_API 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_API 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
EVAS_API void evas_object_image_border_set(Evas_Object *obj, int l, int r, int t, int b)
Dimensions of this image's border, a region that does not scale with the center area.
Definition evas_image_legacy.c:117
EVAS_API void evas_object_image_border_center_fill_set(Evas_Object *obj, Evas_Border_Fill_Mode fill)
Specifies how the center part of the object (not the borders) should be drawn when EFL is rendering i...
Definition evas_image_legacy.c:145
EVAS_API void evas_object_image_file_set(Evas_Object *obj, const char *file, const char *key)
Set the source file from where an image object must fetch the real image data (it may be an Eet file,...
Definition evas_image_legacy.c:194
EVAS_API Evas_Object * evas_object_image_filled_add(Evas *eo_e)
Creates a new image object that automatically scales its bound image to the object's area,...
Definition evas_image_legacy.c:35
@ EVAS_BORDER_FILL_NONE
Image's center region is not to be rendered.
Definition Evas_Legacy.h:5721
EVAS_API Evas_Object * evas_object_rectangle_add(Evas *e)
Adds a rectangle to the given evas.
Definition evas_object_rectangle.c:78
Key press event.
Definition Evas_Legacy.h:314
const char * key
The logical key : (eg shift+1 == exclamation)
Definition Evas_Legacy.h:320

To compile use this command:

* gcc -o edje-signals-messages edje-signals-messages.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 signals-messages.edc
*