Edje Signals, Callbacks and propagation.

As you may already know, interaction with an edje file is down mostly with signals.

You can set up a group with a part and some signal to detect when the part has been clicked

group {
  name: "my_group"
  parts {
    part {
      name: "button";
      type: RECT;
      description {
        state: "default" 0.0;
        color: 255 0 0 255;
        min: 50 50;
      }
    }
  }
  programs {
    program {
      signal: "mouse,up,*";
      source: "button";
      action: SIGNAL_EMIT "button_clicked" "";
    }
  }
}

When the red rectangle is clicked, it will emit a signal {“button_clicked”, “”}

Imagine you want to do something in your program when the button is clicked. You have to add a callback for this :

{
  Evas_Object *evas_obj = edje_object_add(evas);
  edje_object_file_set(evas_obj, "my_theme.edj", "my_group");
  /* needed evas resize/move/show */
  evas_object_resize(evas_obj, 800, 480);
  evas_object_move(evas_obj, 0, 0);
  evas_object_show(evas_obj);

  /* add a callback */
  edje_object_signal_callback_add(evas_obj, "button_clicked", "", &my_callback, NULL);
  /*  simulate a mouse signal on the button to see if it works */
  edje_object_signal_emit(evas_obj, "mouse,up,acme", "button");
}
...
void my_callback(void *data, Evas_Object *o, const char *emission, const char *source)
{
  if (strcmp(emission, "button_clicked") == 0 && strcmp(source, "") == 0) {
    /* do something */
  }
}

As you may notice, there are 4 arguments to the callback :
data, corresponds to the last field of callback_add. You can pass whatever you want to it, but be sure to have it allocated on the heap. You don’t know when the callback will be launched, so if you specify a pointer to a variable that was allocated in the function stack, you’re going to have some problems.
evas_object will be the object that emitted a signal (will correspond to evas_obj here)
emission and source correspond to the signal and the source. You can use the same callback for many signals and dispatch however you want after that.

This is basic stuff you will see in any tutorial, and may lead you to get a bad habit : allocating an Evas_Object for each group of your theme and integrating those in the code.
The ideal goal in an edje application is to have as little code as possible, and move the logic to the edje. That way, you can make a completely different interface (including the way it works, not just graphics) for the same program.
Instead of loading each group in an Evas_Object and use evas to show, hide, move, resize all the elements, juste do one big group with subparts.

group {
  name: "main";
  parts {
    part {
      name: "instance_of_my_group1";
      type: GROUP;
      source: "my_group";
      description {
        state: "default" 0.0;
      }
    }
    part {
      name: "instance_of_my_group2";
      type: GROUP;
      source: "my_group";
      description {
        state: "default" 0.0;
      }
    }
  }
}

What this does is create two instances of the same group, loaded into the “main” group. You only need to instantiate main (edje_object_add and edje_object_file_set) to create those two buttons.
Your next question will be : but if I don’t have an Evas_Object of the group, how do I emit signals to them, how do I add callbacks?

Signal. Propagation.

{
  Evas_Object *main_obj = edje_object_add(evas);
  edje_object_file_set(main_obj, "my_theme.edj", "main");
  /* needed evas resize/move/show */
  evas_object_resize(evas_obj, 800, 480);
  evas_object_move(evas_obj, 0, 0);
  evas_object_show(evas_obj);

  edje_object_signal_callback_add(main_obj, ""button_clicked", "instance_of_my_group1:", &my_callback, NULL);
  /*  simulate a mouse signal on the button to see if it works */
  edje_object_signal_emit(evas_obj, "instance_of_my_group1:mouse,up,acme", "button");
}
...
void my_callback(void *data, Evas_Object *o, const char *emission,  const char *source)
{
  if (strcmp(emission, "button_clicked") == 0 &&  strcmp(source, "instance_of_my_group1:") == 0) {
    /* do something */
  }
}

Understand this important thing :

If you want to emit a signal to a subgroup of an edje object, you have to prefix the signal with the subgroup instance name.
{ “mysubgroup:signal”, “source” }

If you want to add a callback to a signal emitted by a subgroup of an edje object, you have to prefix the source with the subgroup instance name.
{ “signal”, “mysubgroup:source” }

Edje will take care to dispatch those signals automagically. This works with an infinite number of group levels. You could emit {“group:subgroup:subsubgroup:signal”, “source”}.

Now, if you have elements put in a table, or a box, you cannot (at least for now, but it would be a good addition to edje) send signals or get signals from the sub-elements. No “mybox[4]:signal” … for now.

Edit (18/05/2010) :

When you send a signal from the object (if the item was inserted in Edje in the box.items{}), I think the object name is skipped, as if the table itself had sent the signal.

I’ve submitted a patch that would allow someone to send signals to box elements by using this syntax :
{“boxpartname:idx:signal”, “source”} where idx is the index of the element you want to send the signal too, let’s hope it gets accepted.

Edit (20/05/2010) :

Add the evas_object_resize/move/show when initializing the edje object.

Leave a comment

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.

%d bloggers like this: