Is there a default style that can be redefined?

Hi there,

Whenever I create a button, it is given a “default” style: blue background, white text, round box, no border.

This style seems to be hardcoded somewhere, because I haven’t defined it myself.

If I want to create a bunch of buttons with a non-default style, I have to change the style of every single button, with a line such as:

lv_obj_add_style(btn1, &style_btn, 0);

Is there a way of changing that “default” style, so that every new button that I add is created with the new, non-default style?

The default styles of all objects are defined in the theme.
Some parameters like, Primary and secondary palette, can be set when loading the theme.

lv_theme_t * th = lv_theme_default_init(display,  /*Use the DPI, size, etc from this display*/
                                        LV_COLOR_PALETTE_BLUE, LV_COLOR_PALETTE_CYAN,   /*Primary and secondary palette*/
                                        false,    /*Light or dark mode*/
                                        &lv_font_montserrat_10, &lv_font_montserrat_14, &lv_font_montserrat_18); /*Small, normal, large fonts*/

You can also extend an existing theme, or even create your own from scratch.

Adding on to that, there is an example in the documents about how you can extend the theme. Take a look at the code here - Extending the current theme

In a nutshell, the example registers a callback that is called by LVGL for each object as it is styled, so in that callback you can simple add:

if(lv_obj_check_type(obj, &lv_button_class)) {
        lv_obj_add_style(obj, &style_btn, 0);
}

And this will ‘automatically’ apply that style to every button, so you won’t need to set it manually for each button you create.

A quick and dirty way would be to directly edit the theme file: LVGL/src/themes/default/lv_theme_default.c, but I’d not recommend that, you’ll have to take steps not to lose your edits when you upgrade versions, and incorporate any changes made by LVGL each version. It’s worth looking at that file as a ‘documentation’ source of how it sets up and applies styles, too…

Hi egonbeermat,

Thank you for the suggestion. Unfortunately, this route results in “Segmentation fault” when I try to apply the new theme:

lv_display_set_theme(NULL, &th_new);

I don’t know why, as it should work.

I have tried redefining the call-back for the actual theme (th_act), instead of creating a new one. This works (no segmentation fault), but unfortunately it messes up all my screens - For some reason new screens do not clear the background.

Hard to tell, unless you post the full code around that. That statement works in the example in the simulator and, obviously, in my implementation, so you’d need to post the code around this or ensure you have fully instantiated and prepared everything, much like this in the example:

    /*Initialize the new theme from the current theme*/
    lv_theme_t * th_act = lv_display_get_theme(NULL);
    static lv_theme_t th_new;
    th_new = *th_act;

    /*Set the parent theme and the style apply callback for the new theme*/
    lv_theme_set_parent(&th_new, th_act);
    lv_theme_set_apply_cb(&th_new, new_theme_apply_cb);

    /*Assign the new theme to the current display*/
    lv_display_set_theme(NULL, &th_new);

Beyond that, I’ve only implemented this once, so not much experience of common issues around this :slight_smile:

Another alternative, and one design pattern I use a lot with LVGL for several reasons, is to define your own function to create a button, call that everywhere you need, and apply the style in that function.

I do this a lot with LVGL because with all the configuration options possible, there is a lot of boilerplate code, that is often repeated, so it reduces the amount of code, simplifies maintenance and, when LVGL major version changes, reduces the effort to migrate all the changed interfaces.

For instance, you could create your standard button with a standard style, a centered label and a callback with one line:

lv_obj_t * myButton = createButton(screen, "Cancel", my_callback);

and implemented as such:

lv_obj_t * createButton(lv_obj_t * parent, const char * labelText, lv_event_cb_t cb)
{
  lv_obj_t * button = lv_button_create(parent);
  lv_obj_add_style(button, &style_button, LV_PART_MAIN);
  lv_obj_add_event_cb(button, cb, LV_EVENT_CLICKED, NULL);

  lv_obj_t * label = lv_label_create(button);
  lv_label_set_text(label, labelText);
  lv_obj_center(label);

  return button;
}

I do this for pretty much every object type in LVGL, as I pretty much apply standard things to them all (like screen loaded/unloaded callbacks to each screen, etc)

Hi egonbeermat,

It’d be a lot of code to paste, and I am sure I have done everything fine. I suspect that the problem is how that NULL is handled in the call to:

lv_display_set_theme(NULL, &th_new);

I circumvented the problem by doing exactly what you suggest: implemented my own function, which, as you also say, eliminates a lot of extra coding.

Thank you very much!

If you send NULL for the display to lv_display_set_theme, it will get the default display, and log a warning if there is none, but that’s unlikely! You could specify the own display you initially created (I do), but it works for me with NULL too, just tried it. Guess it will remain a mystery :slight_smile: