How do I correctly implement my own custom theme, and include it in my projects folder without modifying any files in lvgl


I would like to properly implement a custom theme for use in my projects.
I have studied the lv_theme_template.h and lv_theme_template.c
I have decided to create my own theme derived from the lv_material_theme, as I will only for now be making some minor changes to it to suite my needs.

I understand that I can just use the material theme, and then apply style changes throughout my code, but I really want to get it to work this way.

I do not want it to reside in components/lvgl/, and placed my theme files in project/main.

What MCU/Processor/Board and compiler are you using?

VSCode 1.53.0-insider, ESP32-WROOM-32E Devkit 4.0
Espressif plugin 0.5.1
ESP-IDF release/4.2
Win 10
KConfig used.

What LVGL version are you using?

LVGL 7.8.1

What do you want to achieve?

Discussed above in description.

What have you tried so far?

Copied lv_theme_material.h and lv_theme_material.c to project/main folder.
Renamed to lv_theme_test.h and lv_theme_test.c
Modified both files changing all references to theme_material to theme_test, with find - replace maintaining case.

I added the lv_theme_test.c to my main/CMakeLists.txt set(Sources)

set(SOURCES  "main.c" "lv_theme_test.c")
idf_component_register(SRCS ${SOURCES}
                    INCLUDE_DIRS .
                    REQUIRES lvgl_esp32_drivers lvgl lvgl_touch lvgl_tft)

target_compile_definitions(${COMPONENT_LIB} PUBLIC -DLV_LVGL_H_INCLUDE_SIMPLE) 

It would not compile as the paths are incorrect for the includes.
I modified those and it still would not compile.

I had to comment out the following code:

/* This trick is required only to avoid the garbage collection of
     * styles' data if LVGL is used in a binding (e.g. Micropython)
     * In a general case styles could be simple `static lv_style_t my style` variables*/
    // if(!inited) {
    //     LV_GC_ROOT(_lv_theme_test_styles) = lv_mem_alloc(sizeof(theme_styles_t));
    //     styles = (theme_styles_t *)LV_GC_ROOT(_lv_theme_test_styles);
    // }

As obiously _lv_theme_test_styles is not defined in lv_gc.h

 *      DEFINES

#define LV_ITERATE_ROOTS(f) \
    f(lv_ll_t, _lv_task_ll)  /*Linked list to store the lv_tasks*/ \
    f(lv_ll_t, _lv_disp_ll)  /*Linked list of screens*/            \
    f(lv_ll_t, _lv_indev_ll) /*Linked list of screens*/            \
    f(lv_ll_t, _lv_drv_ll)                                         \
    f(lv_ll_t, _lv_file_ll)                                        \
    f(lv_ll_t, _lv_anim_ll)                                        \
    f(lv_ll_t, _lv_group_ll)                                       \
    f(lv_ll_t, _lv_img_defoder_ll)                                 \
    f(lv_ll_t, _lv_obj_style_trans_ll)                             \
    f(lv_img_cache_entry_t*, _lv_img_cache_array)                  \
    f(lv_task_t*, _lv_task_act)                                    \
    f(lv_mem_buf_arr_t , _lv_mem_buf)                              \
    f(_lv_draw_mask_saved_arr_t , _lv_draw_mask_list)              \
    f(void * , _lv_theme_material_styles)                          \
    f(void * , _lv_theme_template_styles)                          \
    f(void * , _lv_theme_mono_styles)                              \
    f(void * , _lv_theme_empty_styles)                             \
    f(uint8_t *, _lv_font_decompr_buf)                             \

I then had another look at lv_theme_template.c, noticed my mistake, and reintroduced the same code from lv_theme_template.c

lv_theme_t * lv_theme_loadassist_init(lv_color_t color_primary, lv_color_t color_secondary, uint32_t flags,
                                    const lv_font_t * font_small, const lv_font_t * font_normal, const lv_font_t * font_subtitle,
                                    const lv_font_t * font_title)
     //From lv_theme_template.c as lv_theme_material.c will not compile
    /* This trick is required only to avoid the garbage collection of
     * styles' data if LVGL is used in a binding (e.g. Micropython)
     * In a general case styles could be simple `static lv_style_t my style` variables*/
    if(!inited) {
#if defined(LV_GC_INCLUDE)
        LV_GC_ROOT(_lv_theme_template_styles) = lv_mem_alloc(sizeof(theme_styles_t));
        styles = (theme_styles_t *)LV_GC_ROOT(_lv_theme_template_styles);
        styles = lv_mem_alloc(sizeof(theme_styles_t));


obviously this time not renaming anything.

applied the following to KConfig:


After this it worked as desired.

The real issue

Was this the correct procedure to follow, or is there a more eloquent method to apply a custom theme following proper procedures?

A problem I didn’t pick up the first time round:

As per the image above, the Kconfig will result in
LV_THEME_DEFAULT being set incorrectly:
It will be set to:

LV_THEME_DEFAULT_INIT               lv_theme_template_init

I think this is by design, but now the following will always fail for a switch I use to implement changing from dark to light theme:

static void color_chg_event_cb(lv_obj_t * sw, lv_event_t e)
    /* The next if statement will always fail, as don't know how to
        fix Kconfig even at project level to make my lv_theme_test
        the default theme and set the flags, and initial fonts, 
        as for eg. the material theme.
    if(LV_THEME_DEFAULT_INIT != lv_theme_test_init) return; 
      if(e == LV_EVENT_VALUE_CHANGED) {
        uint32_t flag = LV_THEME_TEST_FLAG_LIGHT;
        if(lv_switch_get_state(sw)) flag = LV_THEME_TEST_FLAG_DARK;

     LV_THEME_DEFAULT_INIT(lv_theme_get_color_primary(), lv_theme_get_color_secondary(),
                lv_theme_get_font_small(), lv_theme_get_font_normal(), lv_theme_get_font_subtitle(), lv_theme_get_font_title());

So I have no way of changing the theme, and my LV_THEME_DEFUALT(s) are incorrectly set to the Template Theme values.

I would ask at the same time that you help me with this, as it still falls in my scope of my question above for properly implementing a Custom Theme.

Thanking you in advance. :blush:

Any Help ?