Fine(r) control over themes (eg. label + heading, table row + odd)

Description

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

Simulator, GCC.

What LVGL version are you using?

v7.1.0

What do you want to achieve?

I’d like to be able to apply styles in a theme (that I create) in a more fine-grained way to basic widgets.

For example, if I have a label that’s a heading, I’d like to say mylabel = lv_label_create(...), “tag” it somehow, and have the theme apply a style that’s appropriate (eg. larger font, different colour).

Another application is alternating colours on table rows. Instead of having a loop that manually sets a specific colour, I could have the loop tag each row “odd” and “even”, and the theme applies the relevant background colour.

What have you tried so far?

I’ve defined an enum type that uses the typical bit-as-flag idiom. Each bit is a tag eg. 0x01 means odd table row. Kind of like:

typedef enum
{
    MY_THEME_TAG_EVEN   = 1U << 0,
    MY_THEME_TAG_ODD    = 1U << 1,
    MY_THEME_TAG_HEADER = 1U << 2
} my_theme_tag_t;

In lv_conf.h I’ve set the type for user_data on the base object to be this type ie.

typedef my_theme_tag_t lv_obj_user_data_t;

I apply the tag after creating the object eg.

myobj = lv_(whatever)_create(...);
myobj.user_data = MY_THEME_TAG_HEADER;

In my custom theme, I have the theme_apply() function read these tags and apply some extra logic eg.

void theme_apply(lv_obj_t * obj, lv_theme_style_t name)
{
    lv_style_list_t * list;

    const my_theme_tag_t tags = obj->user_data;

    switch(name) {
    // ...
        case LV_THEME_LABEL:
            lv_obj_clean_style_list(obj, LV_LABEL_PART_MAIN);
            list = lv_obj_get_style_list(obj, LV_LABEL_PART_MAIN);

            if (tags & MY_THEME_TAG_HEADER)
            {
                _lv_style_list_add_style(list, &styles->header);
            }
            else
            {
                _lv_style_list_add_style(list, &styles->bg);
            }

            break;

            // ...
}

But since the theme is applied to the object in the create function (actually in the init function), I need to apply the theme again after tagging it, so finally:

lv_theme_apply(myobj, LV_THEME_WHATEVER);

This seems a bit clunky, and not very performant. Each style for each widget has to be applied twice, sometimes in a loop (in the case of table rows). It’s also an extra line of code for each object, which I need to remember and someone else needs to read.

But I can’t really think of a better way to do this (without modifying LVGL in my own project, which I don’t want to do). The next best thing I can think of is to ignore themes and just put all my custom styles on each object manually, which seems worse.

Code to reproduce

I didn’t want to dump my entire theme source etc. etc. in here, so I just put snippets up there. Let me know if you want more info.

I think what you want to do here is use the theme as the base/default styling for your object, and then have a second set of styles which you apply as needed. For example, you should have the theme style set up for normal labels, and then an lv_style_t that overrides the appropriate properties for header labels. Does that make sense?

It does. It would be nice to separate things a bit more (like in HTML + CSS), but it’s also obviously a tradeoff in how complex LVGL gets. Thanks!

The approach I eventually went with was to embed the extra styles in the theme C file (in the static theme_styles_t * styles structure), and have some extra functions in the theme’s API (like mytheme_apply_extra(obj, name, tag) and mytheme_accentuate(...)) that do some extra logic to apply the theme extras. That way I can keep all of the colours, etc. in one place, and ensure consistency, while still having semantics in the UI itself.

Hi,

Regarding the header-like styling. In CSS it works because you create texts with a wide variety of markups h1, h2, p, em, b, span. In LVGL you have only a “label” (like you had only p in HTML).

I also vote to simply add extra styles after creation.

Regarding the odd/even idea: In past weeks I was thinking a lot about supporting something like nth-child selector of CSS. I have some ideas but nothing concrete yet. Finally, it could be used to style individual cells of tables or buttons of a button matrix.

Maybe it wasn’t clear, but I wasn’t just talking about text components. Say we’re talking about having a header area, I’d have objects/containers have a particular colour, buttons might also have a different colour, labels would have a different font styling, etc.

I didn’t want to have a tonne of extra styling code in amongst the layout code, so I added it to the theme source to keep it as close to the other stuff as possible.

Got it. In the printer demo I solved it by adding custom theme elements.

They simply add other styles.

And used like this.

What do you think?

Ooh, I didn’t think of that. I like that. I have other ideas for how to add basic (non-inheriting) classes to objects that don’t require a great deal of change to the current theme architecture, but I might use it a bit more before I suggest any changes.

Looking forward to hearing your other idea! :slightly_smiling_face: