July 11, 2016 - Mike Blumenkrantz
How to Create Enlightenment Gadgets
Creating desktop widgets, aka “gadgets,” has never been easier for Enlightenment enthusiasts than it is after the E21 release. The new E_Gadget system provides an updated API for integrating objects into the compositor, removing most of the overhead from the E_Gadcon system. This makes writing gadgets nearly identical to ordinary application writing. This post will serve as an introduction on the topic of writing gadgets with a focus on the basics; it will use the Start gadget as a reference.
How to Create a Gadget
The first step to integrating a new gadget is to add the new gadget type to the subsystem so the user can access it. This is done with the following function:
void e_gadget_type_add(const char *type, E_Gadget_Create_Cb callback, E_Gadget_Wizard_Cb wizard)
This function coincides with related callbacks:
Evas_Object *(*E_Gadget_Create_Cb)(Evas_Object *parent, int *id, E_Gadget_Site_Orient orient);
void (*E_Gadget_Wizard_Cb)(E_Gadget_Wizard_End_Cb cb, void *data);
Using e_gadget_type_add, a developer can implement gadgets of type, calling callback to create the gadget object, and optionally providing wizard callback to run configuration options for the user before creating a new gadget. Gadget types can be added and removed at any time; adding a type will populate any existing gadgets for that type, and removing a type will similarly destroy any existing instances of that gadget. No additional cleanup is needed for gadget objects when removing a gadget type.
The wizard callback will be discussed in a subsequent article, but for now let’s look at a gadget creation callback.
EINTERN Evas_Object *
start_create(Evas_Object *parent, int *id EINA_UNUSED, E_Gadget_Site_Orient orient)
inst = E_NEW(Instance, 1);
o = elm_layout_add(parent);
e_theme_edje_object_set(o, NULL, "e/gadget/start/main");
elm_layout_signal_emit(o, "e,state,unfocused", "e");
inst->o_button = o;
evas_object_size_hint_aspect_set(o, EVAS_ASPECT_CONTROL_BOTH, 1, 1);
evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, start_del, inst);
evas_object_smart_callback_add(parent, "gadget_site_anchor", _anchor_change, inst);
evas_object_smart_callback_add(parent, "gadget_created", _gadget_created, inst);
do_orient(inst, orient, e_gadget_site_anchor_get(parent));
Breaking the Gadget Down
This function returns an object that is used as a gadget with various policies and utilities applied to it. When this object is deleted, the gadget instance is considered destroyed and any related cleanup should be performed at this time. The creation function above hooks the EVAS_CALLBACK_DEL callback
A parent object is passed to the create function. This object can be considered the “owner” of the gadget, and its lifetime is guaranteed to exceed the lifetime of the gadget object. The parent object is the gadget site.
The id parameter refers to the specific instance of a gadget that should be used. It is passed as a pointer to the id so the gadget can change the returned id if necessary. The gadget subsystem uses ids as follows:
- If the passed id is < 0, the created gadget will be used for demo purposes only, (e.g., a gadget site configuration wizard), and optimal display configurations should be applied to make the gadget look neat.
- If the id is 0, the gadget should create a new config instance to use for this gadget object and set the id parameter to this value.
- If the id is > 0, the created gadget should use the config instance which corresponds to the passed id.
Multiple instances of a gadget can exist using the same config instance.
Lastly, the orient parameter determines the orientation of the gadget. The orientation of a gadget matches the orientation of its parent site at all times, and it is guaranteed that the orientation of a gadget object will not change during that object’s lifetime.
Continuing through the function, a layout object gets created with the “e/gadget/start/main” theme applied to it. This object then has a 1:1 aspect hint set. Aspect hints are utilized for the E_Gadget system to handle all sizing; a gadget should never need to manually size itself or set its own explicit size hints. Sizing is handled automatically based on the aspect the gadget has set and then the gadget site’s own sizing policies.
A “gadget_site_anchor” callback is set here. This callback is triggered any time the anchor of a gadget site changes. Anchors indicate the place where the gadget site attaches to something, if such an attachment exists. For example, a rectangular gadget container on the left side of the screen would be considered as being anchored to the left side of the screen. This anchoring can be used in conjunction with orientation to present the user with a more detailed view of a gadget in some cases.
Finally, a “gadget_created” callback is set. This callback is triggered after the creation of any gadget on the site, and it can be used to catch cases where the gadget site changes its anchoring during the startup of Enlightenment.
Returning the created object at this point will yield a displayable gadget that the subsystem manages. It will be created automatically on the owner gadget site every time the site populates, and it will be appropriately destroyed either through site destruction or gadget type removal.
This concludes the introduction of gadget development in E21. Stay tuned for more tutorials in the future!
About Mike Blumenkrantz
Mike is the release manager for Enlightenment as well as a core developer of the EFL toolkit. He sometimes contributes to the Servo application embedding API/ABI, and in his free time he attempts to write desktop applications.
Image Credits: EFL Project