[LVGL] Add support for bottom, top, and system layers
Please can support for creating bottom, top, and system layers in the designer be added. https://docs.lvgl.io/master/details/main-components/display.html#screen-layers
I'm closing #638, since this feature request is more general.
Here is my comment from the #638:
"We can do this by marking some page as top layer and every widget in that page will be added to the top_layer. Studio will make the rest so that this works in both simulator and generated code."
This is a useful feature for implementing popup messages that can occur at any time. I tried to implement this in the most simple way (needed it for my project):
- In studio:
A messagebox widget on an empty screen, with a variable for hidden flag of the messagebox.
- After ui_init() I added:
lv_obj_set_parent(objects.popup_box, lv_layer_top());
This assigns the messagebox widget to the top layer (not the screen).
- After ui_tick() in the display loop:
tick_screen_popup();
This is needed to process the hidden variable (or other dynamic stuff in te widget).
This works very well: The boolean 'reverse_polarity' will determine if the messagebox is shown or not. This is independent of the active screen.
I also tried setting the parent object of the popup screen (not the message box widget), but that doesn't work. It has to be a widget, not a screen.
Adding a container behind the messagebox and assigning this as top layer also block all user input:
All using LVGL 9.1 on native code.
My suggestion for implementing this would be to add features 'set layer' to the LVGL action where the user can select a layer (sys, top, bottom) and a widget to be assigned to this layer. The action would assign the widget to the layer (step 2 above) and keep track of the screen the widget is on. In the generated ui_tick code the corresponding screen_ticks function would need to be called based on the last set layers, so just as with active screen we have to keep track of the screen of the layer assigned widgets to do this.
I also tried setting the parent object of the popup screen (not the message box widget), but that doesn't work. It has to be a widget, not a screen.
I don't like this. The reason why it doesn't work is because function lv_obj_set_parent has this check at the beginning:
if(obj->parent == NULL) {
LV_LOG_WARN("Can't set the parent of a screen");
return;
}
And also it later does this:
lv_obj_t * old_parent = obj->parent;
/*Remove the object from the old parent's child list*/
...
If I remove this check and also add check so it doesn't try to remove child if parent doesn't exists then it works even for the screen.
Maybe we should finally have an option for the screen "Create screen on start". When this option is disabled, the way to create such screen is with a new LVGL action called "Create Screen". This action can have optional parameter Layer (BOTTOM | TOP | SYSTEM). We will also need an action "Delete Screen". Also, we can have a way to control when flow state for the screen is created and destroyed. For the start, the most simple case is to create flow state when screen is created and destroy flow state when screen is deleted. And later add more control if we find a use case.
This can work but might cause confusion: I think we are deviating from LVGL structure/definitions here: bottom, top and sys screens are statically fixed to their layers in LVGL. There is no way to change the screen of these layers (with normal API) unlike the active screen.
In LVGL the layers screens are always present, created by LVGL and used to attach widgets to (and you can manipulate the properties of the layer screens). So adding stuff to layers is done by adding widgets to a static screen. In the UI you want to do this on a screen to manage the position of the widget, but from a concept point you are not creating a new screen.
I see some other ways to implement this in line with LVGL:
- in the UI a single screen can be assigned to one and only one of the layers. If the box is checked the screen in the UI becomes transparent by default but style can still be changed. It also can’t become the active layer anymore (so can’t be selected in change screen). In this case the tick of this screen is always processed and the user can determine if something is shown by (un)hiding widgets. The code generation is easy: instead of creating a new screen the existing screen of the layer is used all other code generation stays the same.
- always have the layers screens present in the UI screens tab (maybe make them optionally visible and default might be off not to confuse beginners) in a visual different way or with label in front of them. The working is the same as option 1.
- not use screens as the UI place to manage layers but user widgets. If we would have a LVGL action ‘add user widget to screen’ (and a remove) this dynamically adds a user widget to a screen and in the list of screens the layer screens are present. This would require the tick of the widget to be added and removed to the tick processing also. Problem with this solutions is that there is no place in UI to position the widget on screen so the user will have to mess with position parameters given in the action.