How can I have some layout children stretch to fill remaining space?

Description

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

Simulator/GCC.

What LVGL version are you using?

v7.2.0

What do you want to achieve?

I have a GUI that’s laid out basically with:

  • a region at the top that should shrink to fit the contents (think a container with LV_FIT_TIGHT)
  • a similar region at the bottom
  • some stuff in the middle that should be centered between them

The regions at the top and bottom are set up when the GUI is created and don’t change, but the actual heights might be different from one deployment to another.

I’d like to be able to add components to the area between them so that it’s vertically centered between them, while keeping the header at the top and the footer at the bottom. Is there a way to structure containers and their layouts so that some children have a “tight” layout and others fill the leftover space? Or, to put it another way, so the centre region “pushes” the header and footer to the upper and lower edges of the screen?

stretch

What have you tried so far?

First approach

  • Create a top level container with LV_FIT_PARENT.
  • To this, add:
    • a container (top region) with LV_FIT_PARENT horizontally and LV_FIT_TIGHT vertically
    • a container (centre region) `LV_FIT_PARENT
    • a container (bottom region) LV_FIT_PARENT/LV_FIT_TIGHT.

This results in the centre region pushing the footer off the screen. If I try to add containers to the centre region with LV_FIT_PARENT I get segfaults, but I haven’t had time to debug this more.

Second approach:

Set up the top and bottom regions with LV_LAYOUT_OFF and align them to the top and bottom instead, measure their heights, create a container to fit the remaining space. This is annoying because I want similar functionality for other parts of the UI and I have to do it in a lot of places.

Code to reproduce

Here’s some UI code (hardware code omitted). Play around with adding containers or other widgets to the centre area and you’ll see what I mean.

void ui_init(void)
{
    // Screen.
    lv_obj_t * screen = lv_disp_get_scr_act(NULL);

    // Cover the whole screen with a container.
    lv_obj_t * page = lv_cont_create(screen, NULL);
    lv_cont_set_fit(page, LV_FIT_PARENT);
    lv_cont_set_layout(page, LV_LAYOUT_COLUMN_MID);

    // Title area.
    lv_obj_t * top = lv_cont_create(page, NULL);
    lv_obj_set_auto_realign(top, true);
    lv_cont_set_fit2(top, LV_FIT_PARENT, LV_FIT_TIGHT);
    lv_cont_set_layout(top, LV_LAYOUT_COLUMN_MID);

    lv_obj_t * title = lv_label_create(top, NULL);
    lv_label_set_text(title, "TITLE");
    lv_label_set_align(title, LV_LABEL_ALIGN_CENTER);

    // Centre area.
    lv_obj_t * main_area = lv_cont_create(page, NULL);
    lv_obj_set_auto_realign(main_area, true);
    lv_cont_set_fit2(main_area, LV_FIT_PARENT, LV_FIT_PARENT);
    lv_cont_set_layout(main_area, LV_LAYOUT_COLUMN_MID);

    // Footer area.
    lv_obj_t * bottom = lv_cont_create(page, NULL);
    lv_obj_set_auto_realign(bottom, true);
    lv_cont_set_fit2(bottom, LV_FIT_PARENT, LV_FIT_TIGHT);
    lv_cont_set_layout(bottom, LV_LAYOUT_COLUMN_MID);

    lv_obj_t * footer = lv_label_create(bottom, NULL);
    lv_label_set_text(footer, "FOOTER");
    lv_label_set_align(footer, LV_LABEL_ALIGN_CENTER);
}

Quick example of the segfaulting, add:

    lv_obj_t * crashy = lv_cont_create(main_area, NULL);
    lv_obj_set_auto_realign(crashy, true);
    lv_cont_set_fit2(crashy, LV_FIT_PARENT, LV_FIT_TIGHT);

…just before the footer code.

Update: running this in GDB it seems like a recursion issue, because I have tens of thousands of stack frames :grimacing:

Hi,

Unfortunately, “filling the space” is not supported by the layouts now.

However we are reworking the layouts based on CSS flexbox or grid (still under consideration). I tend to believe in grid more which do support this with the special fr width unit.
Maybe others know how to do it flexbox too.

It will be part of v8 (planned to ~October), so I suggest calculating the szes manually for now.

Flexbox also supports this - in fact it’s one of the main selling points for the idea.

Can you put together a simple Codepen to illustrate this?

Sure; here you go: https://codepen.io/embeddedt/pen/abNOvvj

Hah, grid layouts was going to be my next question (as in, fixed number of rows/cols, not the grid layout as per current containers). I will do it manually and try out the new layouts when they drop. Thanks!

@embeddedt
Ah really, it can be done with column direction. However, if the content div would consist of 2 columns it wouldn’t work with flexbox, right?

No; it still works fine: https://codepen.io/embeddedt/pen/abNOvvj

The paradigm with flexbox is that you nest it as many times as you need for your desired layout. At least, that’s what I’ve found from working with it.

I see, by nesting it’s really flexible.

The value of grid in my eyes is you can avoid some wrapper containers just for layout.

Anyway, whichever we choose, LVGL will have a much better layout system in the end.

That’s true, and quite important in our case, since we have limited memory/CPU resources to be processing lots of nested layouts.

1 Like