Change font of a style - whats the best way?

I have a few wrapper classes I use for my project. Most of my elements are recurring so those classes make my code a lot cleaner. My label class has a setFont() function that is giving me some headache. Usually i would declare my styles as static, apparently in a class this would not work unless i want all elements to have the same style. But declaring the style as a variable of the class is giving me a bunch of crashes I am unable to track down. Odd sidenote: if I debug the code to see what’s crashing there is no crash happening (no kidding), but the fonts are also not applied.

So what would make most sense to me is to lv_style_init()the style in the class constructor and lv_obj_add_style() it to the classes lv_obj (which in this case is an lv_label). However if I do so my ESP32 crashes, I’ve read the docs and found lv_obj_refresh_style(this->obj, LV_STYLE_TEXT_FONT);, however calling this does not change the behavior I am observing.

The closest I can get to pinning down the crash is it’s likely happening in the draw code. When I restart the crashed ESP the backlight turns on briefly and shows a correctly drawn header of my main window and corrupted image data (color noise) in the rest of the display.

So, what would be the proper way to do what I would like to do?
thanks!
(v7 is great by the way!)

Please enable LV_USE_ASSERT_STYLE in lv_conf.h. It will assert if you have uninitalized styles.

Yes indeed the assertion triggers.

So apparently this sequence does not work

lv_style_init(&this->style);
lv_obj_add_style(this->obj, part &this->style);
lv_style_set_text_font(this->style, LV_STATE_DEFAULT, &font);

the assertion gets triggered at lv_style_set_text_font.

My impression was that with the new style system we can change styles on the fly that are already added to an object?
Why am I trying to do it this way? Because I’d like my class to be able to adjust a font to a new one on the fly, instead of assigning a new cascade every time the font is changed, as that seems rather inefficient, as it would end up with all the fonts assigned but only the last one used …

So the question is, how would this be done the proper way? Apparently I am doing it wrong :sweat_smile:

Did you type that code sample out by hand or is that what you’re currently using? I ask because I may have found the issue.

this->style should be an lv_style_t, not an lv_style_t *.
With the call to lv_style_set_text_font you aren’t taking it’s address and instead treating it as though it is already an lv_style_t *.

i am typing it out by hand, it would not compile otherwhise :sweat_smile:
also if I move the add style from the constructor to after set_text_font in the setFont() it works as expected. If I call lv_style_set_text_font() after lv_obj_add_style() the assertion triggers.

I’ll test it in a few minutes.

For me, both sequences work without crashing; however, if I add the style and then set the font, the label doesn’t resize to accommodate the larger text. I would expect that to be the case, as for performance reasons, you have to tell the label that the style actually changed.

It still doesn’t explain why it’s crashing for you though. (I have memory protection and asserts enabled, so I don’t think there is any uncaught undefined behavior, although one can never be too sure. :wink:)

I finally realized there’s a bunch of useful asserts, i fixed some string problems, but i am unable to solve this problem.

Even worse. To get more progress with my project I actually skipped this and accepted that I can only set the style once. All my attempts on fixing it resulted in behavior I cannot explain to myself - like errors in release build and none while debugging.

I now have another flavor of the problem to add that I have a feeling may contribute in finding out what’s wrong here …

This code inside my Spinbox class constructor:

    this->spinbox = lv_spinbox_create(this->obj, NULL);
    lv_spinbox_set_digit_format(spinbox, 6, 4);
    lv_obj_set_width(this->spinbox, 100);
    lv_group_add_obj(this->focusGroup, this->spinbox);

    lv_style_init(&spinboxStyle);
    lv_style_set_pad_top(&spinboxStyle, LV_STATE_DEFAULT, 10);
    lv_obj_add_style(this->spinbox, LV_SPINBOX_PART_BG, &spinboxStyle);

thows this assertion:

lib/lvgl/src/lv_core/lv_debug.c@97->Invalid style (local variable or not initialized?)
lib/lvgl/src/lv_core/lv_style.c@552->_lv_style_get_int
lib/lvgl/src/lv_core/lv_debug.c@188->Invalid style (0x3FFB1EF8)

this->obj is an lv_container instance. and spinboxStyle is an lv_style_t defined as a class member. The only sense I can make from this currently that it has to do with having an lv_style_t defined in a class, but I have absolutely no clue why :roll_eyes:

It really sounds like either there’s memory corruption or your class is being deallocated.

I am afraid memory corruption it is. trying to find out which portion of the code it is. already replaced all sprintfs with snprintf to make sure its no overflow. having little to no experience with this i am a bit f* right now i guess …

You can try setting a watchpoint on the style’s sentinel value. If it’s a repeatable issue the debugger will likely alert you as to what part of your code is corrupting it.

Just a suggestion. :slightly_smiling_face:

any suggestion is very very welcome at this point. I am not sure if my debugger is able of this … (vscode, platformio with open ocd and esp-prog). When I put the sentinel or the spinboxstyle in my watch list I am actually getting map 0x0 and sentinel 0, although in the local variables view it all looks correctly initialized.

Hmm… it looks like the underlying debug hardware supports watchpoints (as does OpenOCD), but I can’t find much information on how to make use of them with PlatformIO’s debugger.

If PlatformIO uses the same terminology as other debuggers, I’m pretty sure the watch list is just a handy way for you to see the values of other variables. It doesn’t actually add a breakpoint.

Are you familiar at all with GDB itself (the CLI equivalent)?

I know what it is but have never used it. Familiar would be stretching it I guess :sweat_smile:
But it sounds like a smart way to get hold of the problem!

Is there by chance a “debug console” window that appears when you’re debugging? If so, can you try typing help or a similar generic command in to see if it accepts GDB commands?

yes indeed there is! I can call watch Gui.qnhSpinbox.spinboxStyle.sentinel and it reports

Hardware watchpoint 3: Gui.qnhSpinbox.spinboxStyle.sentinel
{"token":383,"outOfBandRecord":[],"resultRecords":{"resultClass":"done","results":[]}}

All I’m getting is the debugger crashing o.O
What kind of expression would you suggest should work? Maybe even put in the memory address and number of bytes to watch?

I’ve managed to watch the address of the sentinel. What puzzles me is that inside the class constructor, the debugger returns a different address to the sentinel than after the debugger (&this->spinboxStyle->sentinel inside the constructor returns 0x3ffb1ef8, and &Gui.qnhSpinbox.spinboxStyle.sentinel after the constructor returns 0x3ffc0848. Also the 4 bytes starting at the first memory address get overwritten constantly in almost every function call.

Looking at this it’s no surprise it’s not working. But then if I am not mistaken this brings along a whole lot of other problems with using the constructor for initialization … this is not how I would expect a constructor to behave, but i also would not consider myself an experienced C/C++ developer :roll_eyes:

any ideas about this?
thanks!

It means that either spinboxStyle is changing, this is changing, or Gui.qnhSpinbox is changing. Try setting a watchpoint on one of those.

it is this. Apparently the constructor is running in a specific (fast access?) memory region (0x3ffb…) and after the constructor the instance is copied to a different memory region (0x3ffc…). So any address of an instance member that get’s passed during the constructor is invalid after the constructor is finished. I’ve solved it by renaming the constructor into an init() function. Instead of call the constructor I call the init function and everything works as expected.
Fortunately I learned a bunch about memory along the troubleshooting process so I can account it on study time instead of stale time. thanks for your help embeddedt!

No problem. The behavior you described is really strange; I don’t know if you are using virtual classes or something similar that might cause that. Anyways, we both learned something new! :slightly_smiling_face: