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:695
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:703
}

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_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:8992
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:6189
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:9576
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:751

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.