Theme - Using extensions

Using extensions is extremely easy, discarding the part where you have to write the theme for them.

In the following example we'll be creating two buttons, one to load or unload our extension theme and one to cycle around three possible styles, one of which we created.

After including our one and only header we'll jump to the callback for the buttons. First one takes care of loading or unloading our extension file, relative to the default theme set (thus the `NULL` in the functions first parameter).

#include <Elementary.h>
static void
btn_extension_click_cb(void *data EINA_UNUSED, Evas_Object *btn, void *ev EINA_UNUSED)
{
const char *lbl = elm_object_text_get(btn);
if (!strncmp(lbl, "Load", 4))
{
elm_theme_extension_add(NULL, edj_path);
elm_object_text_set(btn, "Unload extension");
}
EINA_UNUSED
#define EINA_UNUSED
Used to indicate that a function parameter is purposely unused.
Definition eina_types.h:339
elm_theme_extension_add
void elm_theme_extension_add(Elm_Theme *th, const char *item)
Appends a theme extension to the list of extensions.
Definition elm_theme.c:768
Evas_Object
Efl_Canvas_Object Evas_Object
An Evas Object handle.
Definition Evas_Common.h:185
else if (!strncmp(lbl, "Unload", 6))
{
elm_theme_extension_del(NULL, edj_path);
elm_object_text_set(btn, "Load extension");
}
elm_theme_extension_del
void elm_theme_extension_del(Elm_Theme *th, const char *item)
Deletes a theme extension from the list of extensions.
Definition elm_theme.c:776
}

The second button, as we said before, will just switch around different styles. In this case we have three of them. The first one is our custom style, named after something very unlikely to find in the default theme. The other two styles are the standard and one more, anchor, which exists in the default and is similar to the default, except the button vanishes when the mouse is not over it.

static void
btn_style_click_cb(void *data EINA_UNUSED, Evas_Object *btn, void *ev EINA_UNUSED)
{
const char *styles[] = {
"chucknorris",
"default",
"anchor"
};
static int sel_style = 0;
sel_style = (sel_style + 1) % 3;
elm_object_style_set(btn, styles[sel_style]);
}
elm_object_style_set
Eina_Bool elm_object_style_set(Evas_Object *obj, const char *style)
Set the style to used by a given widget.
Definition elm_main.c:1583

So what happens if the style switches to our custom one when the extension is loaded? Elementary falls back to the default for the widget.

And the main function, simply enough, will create the window, set the buttons and their callbacks, and just to begin with our button styled we're also loading our extension at the beginning.

EAPI_MAIN int
elm_main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
Evas_Object *win, *box, *btn;
elm_policy_set(ELM_POLICY_QUIT, ELM_POLICY_QUIT_LAST_WINDOW_CLOSED);
#ifdef PACKAGE_DATA_DIR
elm_app_compile_data_dir_set(PACKAGE_DATA_DIR);
#endif
if (ecore_file_exists("./theme_example.edj"))
{
strcpy(edj_path, "./theme_example.edj");
}
else
{
elm_app_info_set(elm_main, "elementary", "examples/theme_example.edj");
snprintf(edj_path, sizeof(edj_path), "%s/examples/theme_example.edj",
elm_app_data_dir_get());
}
elm_theme_extension_add(NULL, edj_path);
win = elm_win_util_standard_add("theme", "Theme example");
elm_win_autodel_set(win, EINA_TRUE);
box = elm_box_add(win);
evas_object_size_hint_weight_set(box, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND);
elm_win_resize_object_add(win, box);
evas_object_show(box);
btn = elm_button_add(win);
elm_object_text_set(btn, "Unload extension");
elm_box_pack_end(box, btn);
evas_object_show(btn);
evas_object_smart_callback_add(btn, "clicked", btn_extension_click_cb, NULL);
btn = elm_button_add(win);
elm_object_text_set(btn, "Switch style");
elm_object_style_set(btn, "chucknorris");
elm_box_pack_end(box, btn);
evas_object_show(btn);
evas_object_smart_callback_add(btn, "clicked", btn_style_click_cb, NULL);
evas_object_resize(win, 300, 320);
evas_object_show(win);
elm_run();
return 0;
}
ELM_MAIN()
EVAS_HINT_EXPAND
#define EVAS_HINT_EXPAND
Use with evas_object_size_hint_weight_set(), evas_object_size_hint_weight_get(), evas_object_size_hin...
Definition Evas_Common.h:297
ecore_file_exists
Eina_Bool ecore_file_exists(const char *file)
Checks if the given file exists.
Definition ecore_file.c:165
EINA_TRUE
#define EINA_TRUE
boolean value TRUE (numerical value 1)
Definition eina_types.h:539
elm_app_info_set
void elm_app_info_set(void *mainfunc, const char *dom, const char *checkfile)
Re-locate the application somewhere else after compilation, if the developer wishes for easier distri...
Definition elm_main.c:496
elm_app_compile_data_dir_set
void elm_app_compile_data_dir_set(const char *dir)
Provide information on the fallback application's data directory, on scenarios where they get overrid...
Definition elm_main.c:528
elm_app_data_dir_get
const char * elm_app_data_dir_get(void)
Get the application's run time data prefix directory, as set by elm_app_info_set() and the way (envir...
Definition elm_main.c:586
elm_box_add
Evas_Object * elm_box_add(Evas_Object *parent)
Add a new box to the parent.
Definition elm_box.c:363
elm_box_pack_end
void elm_box_pack_end(Elm_Box *obj, Efl_Canvas_Object *subobj)
Add an object at the end of the pack list.
Definition elm_box_eo.legacy.c:57
elm_button_add
Evas_Object * elm_button_add(Evas_Object *parent)
Add a new button to the parent's canvas.
Definition efl_ui_button.c:459
ELM_MAIN
#define ELM_MAIN()
macro to be used after the elm_main() function
Definition elm_general.h:556
elm_policy_set
Eina_Bool elm_policy_set(unsigned int policy, int value)
Set a new policy's value (for a given policy group/identifier).
Definition elm_main.c:1380
elm_run
void elm_run(void)
Run Elementary's main loop.
Definition elm_main.c:1357
ELM_POLICY_QUIT_LAST_WINDOW_CLOSED
@ ELM_POLICY_QUIT_LAST_WINDOW_CLOSED
quit when the application's last window is closed
Definition elm_general.h:248
ELM_POLICY_QUIT
@ ELM_POLICY_QUIT
under which circumstances the application should quit automatically.
Definition elm_general.h:227
elm_win_util_standard_add
Evas_Object * elm_win_util_standard_add(const char *name, const char *title)
Adds a window object with standard setup.
Definition efl_ui_win.c:9587
elm_win_resize_object_add
void elm_win_resize_object_add(Eo *obj, Evas_Object *subobj)
Add subobj as a resize object of window obj.
Definition efl_ui_win.c:9002
elm_win_autodel_set
void elm_win_autodel_set(Eo *obj, Eina_Bool autodel)
Set the window's autodel state.
Definition efl_ui_win.c:6199
evas_object_show
EVAS_API void evas_object_show(Evas_Object *eo_obj)
Makes the given Evas object visible.
Definition evas_object_main.c:1814
evas_object_size_hint_weight_set
EVAS_API void evas_object_size_hint_weight_set(Evas_Object *obj, double x, double y)
Sets the hints for an object's weight.
Definition evas_object_main.c:2638
evas_object_resize
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_object_smart_callback_add
EVAS_API void evas_object_smart_callback_add(Evas_Object *eo_obj, const char *event, Evas_Smart_Cb func, const void *data)
Add (register) a callback function to the smart event specified by event on the smart object obj.
Definition evas_object_smart.c:1040

In this case we wanted to easily remove extensions, but all adding an extension does is tell Elementary where else it should look for themes when it can't find them in the default theme. Another way to do this is to set the theme search order using elm_theme_set(), but this requires that the developer is careful not to override any user configuration. That can be helped by adding our theme to the end of whatever is already set, like in the following snippet.

char buf[4096];
snprintf(buf, sizeof(buf), "%s:./theme_example.edj", elme_theme_get(NULL);
elm_theme_set(NULL, buf);
elm_theme_set
void elm_theme_set(Elm_Theme *th, const char *theme)
Set the theme search order for the given theme.
Definition elm_theme.c:824

If we were using overlays instead of extensions, the same thing applies, but the custom theme must be added to the front of the search path.

In the end, we should be looking at something like this:

That's all. Boringly simple, and the full code in one piece can be found here.

And the code for our extension is here.