Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework layouts #1615

Closed
kisvegabor opened this issue Jun 27, 2020 · 51 comments
Closed

Rework layouts #1615

kisvegabor opened this issue Jun 27, 2020 · 51 comments

Comments

@kisvegabor
Copy link
Member

Continue from #1539

As it was discussed in the mentioned issue, it'd be great if the layouts would be supported by lv_obj directly a work similarly to flexbox in CSS.

Let's discuss the issues and related ideas here.

@kisvegabor
Copy link
Member Author

A related topic is automatic size setting. In CSS it's handled with width, height. display, and some similar properties. In LVGL there are special "fit" modes e.g. LV_FIT_PARENT.
Maybe instead of display: block/inline display: flex is enough with support to flex-basis:auto, height:auto, width:auto.

Now LVGL doesn't store the set width and height just modifies the coordinates. So if the coordinates change again this information is lost. It's important because if flex-grow changes the coordinates there is no way back, there is no information about the original size to decide how many elements can fit in row/column. (flex-basis still can help here)

I'm thinking about making possible to set width/height in the percentage of the parent. If it's reasonable shall we set it dynamically? (save the percentage and apply again if the parent's size changes)

min-width and max-width also can come in handy to create responsive designs. However, I'm not sure if it's required for the target audience of LVGL. I played a lot with the widgets demo to look good on the common screen sizes but in the practice probably people use a UI only on one screen.

What do you think?

@puzrin
Copy link
Contributor

puzrin commented Jul 5, 2020

Maybe instead of display: block/inline display: flex is enough with support to flex-basis:auto, height:auto, width:auto.

Could you provide use-cases you need to support? I guess, it's the grid with single row and multiple cells? Sometime this can be done via macros, without flexbox at all. If you support combinaion of (parent:position:relaive + child:position:absolute).

In general, full flexbox & grid seems too much for lvgl. But i can not say right now how much to cut. Full set of desired use cases needed.

I'm thinking about making possible to set width/height in the percentage of the parent. If it's reasonable shall we set it dynamically? (save the percentage and apply again if the parent's size changes)

That's ugly thing, and impossible for height a all. I know 2 use cases:

  1. emulate some kind of grid layout on webpage
  2. scale fixed content (video player) to max allowed width.

(1) without flexbox may be horrible. (2) is specific, i don't think needed for lvgl.

min-width and max-width also can come in handy to create responsive designs. However, I'm not sure if it's required for the target audience of LVGL.

Those are needed, but only if real display size changes and may become too big (~ if you resize browser window).

I played a lot with the widgets demo to look good on the common screen sizes but in the practice probably people use a UI only on one screen.

I agree. It's always good idea to remember where LVGL is effective. I'd say "where QT can not be used". This can give ideas about used hardware.


Also, you have "free" additional abstraction layer, not available in CSS - macros. Consider try to create a set of macros to calculate coords. For example, instead of width in % you could calculate absolute coords of each column.

@kisvegabor
Copy link
Member Author

Could you provide use-cases you need to support? I guess, it's the grid with single row and multiple cells? Sometime this can be done via macros, without flexbox at all. If you support combinaion of (parent:position:relaive + child:position:absolute).

Sure there are many examples where sizing to the content comes in handy:

  • There is a message box: title + text + buttons. You probably want the height to be set automatically based on the text's height. It's the default block behavior in HTML (width to the parent, height to the content)
  • Set the button size (width and height) according to the text on it
  • When you click a list element, it "opens" (get taller) to show extra details. The new height should be the height of the children automatically.

In general, full flexbox & grid seems too much for lvgl. But i can not say right now how much to cut. Full set of desired use cases needed.

Using flexbox only (no grid) seems suitable but I also don't know now which options to drop yet.

I'm thinking about making possible to set width/height in the percentage of the parent. If it's reasonable shall we set it dynamically? (save the percentage and apply again if the parent's size changes)

That's ugly thing, and impossible for height a all.

If there is a parent with a fixed height you can create a child with 50% parent height.

Also, you have "free" additional abstraction layer, not available in CSS - macros. Consider try to create a set of macros to calculate coords. For example, instead of width in % you could calculate absolute coords of each column.

Do you mean to use e.g. LV_PARENT_WIDTH_PCT(50) and set it once on the children? It'd simplify things a lot but makes users life more difficult if

  • the style changes the paddings and margins.
  • the size fo the parent changes

@puzrin
Copy link
Contributor

puzrin commented Jul 6, 2020

  • There is a message box: title + text + buttons. You probably want the height to be set automatically based on the text's height. It's the default block behavior in HTML (width to the parent, height to the content)
  • Set the button size (width and height) according to the text on it
  • When you click a list element, it "opens" (get taller) to show extra details. The new height should be the height of the children automatically.

Nobody use flexbox for that. That's an ordinary block / margin / padding / REM combinations.

https://getbootstrap.com/ - please investigate docs, there are lot of examples.

If there is a parent with a fixed height you can create a child with 50% parent height.

If there is parent with fixed height, i can create child with fixed height :). You described feature, but not use case. Where is this needed in real world?

Do you mean to use e.g. LV_PARENT_WIDTH_PCT(50) and set it once on the children? It'd simplify things a lot but makes users life more difficult if

  • the style changes the paddings and margins.
  • the size fo the parent changes

Everything required should be used as param. #define CHILD1_W (PARENT1_W / 2). And note, if width/height in % depends on padding/margin - something goes wrong with your layout. See twitter bootstrap samples and sources.

@kisvegabor
Copy link
Member Author

kisvegabor commented Jul 6, 2020

Nobody use flexbox for that. That's an ordinary block / margin / padding / REM combinations.

It seemed it was a reflection to "Maybe instead of display: block/inline display: flex is enough with support to flex-basis:auto, height:auto, width:auto." That's why I described use cases for width/height:auto

If there is parent with fixed height, i can create child with fixed height :). You described feature, but not use case. Where is this needed in real world?

True, but if the parent height changes you need to change children's height manually. Or if the styles change the size has to be updated too.

And note, if width/height in % depends on padding/margin - something goes wrong with your layout. See twitter bootstrap samples and sources.

Yes, I know, width set/get uses the content area without padding and margin. It's quite counterintuitive to me. If there are some padding and margin it's really difficult to create two 50-50% columns next to each other.

@embeddedt
Copy link
Member

Yes, I know, width set/get uses the content area without padding and margin.

I believe this is what box-sizing is intended to address. Most frameworks just set it to border-box nowadays.

@kisvegabor
Copy link
Member Author

Oh really, I forgot that. Once supporting one, supporting the other too shouldn't be too complicated.

@embeddedt
Copy link
Member

It might be more efficient and take less time to only support border-box. I have never found a case yet when designing a web application where I'd want the content-box model.

@puzrin
Copy link
Contributor

puzrin commented Jul 6, 2020

It seemed it was a reflection to "Maybe instead of display: block/inline display: flex is enough with support to flex-basis:auto, height:auto, width:auto." That's why I described use cases for width/height:auto

  • block/inline/inline-block are to describe object behaviour.
  • flex is to describe grouping of children behaviour

That's very primitive and inaccurate explanation. Only to show those modes can not replace each other.

True, but if the parent height changes you need to change children's height manually. Or if the styles change the size has to be updated too.

This splits to 2 cases:

  • User hardcode constants in code instead of headers? Good luck :)
  • User wish to change layout via themes? That looks overkill for LVGL

It might be more efficient and take less time to only support border-box. I have never found a case yet when designing a web application where I'd want the content-box model.

Yes, box-sizing is outdated and not needed.

@kisvegabor
Copy link
Member Author

block/inline/inline-block are to describe object behaviour.
flex is to describe grouping of children behaviour

I think the gist is to find out something smart for this. Here is my view to have something concrete to discuss. I don't say it's perfect as it is so please tell your opinion.

Element size

Use: width/height with the following units

  • px: simple pixel count
  • %: percentage of the parent. IMO it's useful to calculate it dynamically.
  • vw/vh: percentage of the display (maybe with a different name)
  • auto: to the content size + padding

The set width/height are stored in objects as it is to keep them available for further calculation if the object's size changes (E.g. to know when to wrap in flex layout if the objects are stretched). So these are object properties, not style properties.

Set element position

I think x/y is more intuitive for our users than left/right. As we will have only one layout type there are 2 options: the object is aligned by the layout or not. We can make life easier if the object has any x/y coordinates set directly then treated as position:absolute else it's position:static:

  • px: simply distance from left/top side+padding (so 0;0 is on content area)
  • %: parent percentage
  • vw/vh display percentage
  • auto: place according to the layout

Similarly to width/height x/y are also stored in to object for further calculation. E.g. when padding changes the (0;0) coordinate also changes.

Margin

To allow flushing element to the right/bottom use margin:auto

Layout.

Every element has flex-box enabled by deafult with flex-flow:row wrap (to align children in a row) and flex:0 1 auto (CSS deafult, not grow, just shrink if nowrap, use width/height in calculations)

  • have only the combined flex-flow: none | row | row wrap | culomn | culomn wrap options
  • justfy content: flex-start | start | flex-end | end | center | space-between | space-around | space-evenly. I vote to have start flex-start too becasue it's easy to handle and improves RTL experience.
  • align-items: stretch | flex-start | flex-end | center (no baseline alignment)
  • align-content: flex-start | flex-end | center | space-between | space-around | space-evenly | stretch | start | end
  • flex-basis: can be omitted, always auto
  • order: can be omitted
  • flex-grow/shrink implemented normally but in one flex property
  • aling-self:: auto | flex-start | start | flex-end | end | center | stretch

Not obvious to store it as style or object property. IMO we should make a property related to styles if

  1. describes only the appearance and no functionality/behavior.
  2. valid for many elements and independent from the element type.

E.g. padding, colors, border width, etc are typical style properties. Size, position, list of options, label text, min-max range, current value, etc are typical object properties.

Following this, I think the layout is rather an object property because probably the developer wants to set it on a per-object basis. E.g. "I want this layout on this box and other here".

Yes, box-sizing is outdated and not needed.

There are some discussions about "Why there is no margin-box". It really seems reasonable to have. With border-box and some margin, you can't create a simple 2 column layout without calculations. What do you think about it?

The usual answer is that it doesn't work with margin collapse. (I'm not sure we need margin collapse in LVGL though)

@embeddedt
Copy link
Member

Layout

It's not inherited so there's no functional difference between making it an object property or a style property. I'd say it's up to you.

With border-box and some margin, you can't create a simple 2 column layout without calculations. What do you think about it?

Can you give a specific example? I'm pretty sure with flexbox you can use margins without problems.

@puzrin
Copy link
Contributor

puzrin commented Jul 7, 2020

I don't understand discussion point. What is the goal?

There are some discussions about "Why there is no margin-box". It really seems reasonable to have. With border-box and some margin, you can't create a simple 2 column layout without calculations. What do you think about it?

I think product development is not right place to demonstrate acrobatics skills :). It's not rational to spend time for implementing all features. Rational is to implement minimal set, allowing well known programming patterns.

@kisvegabor
Copy link
Member Author

I don't understand discussion point. What is the goal?

Tell if you agree or disagree with this direction. :)

Can you give a specific example? I'm pretty sure with flexbox you can use margins without problems.

Yes, it works well and easily in flexbox but not in "manual placement". If you set width:50% for 2 siblings and they have margins they can't be placed next to each other while respecting margin.

We can say to use layout for this case though. So I don't insist on margin-box.

@puzrin
Copy link
Contributor

puzrin commented Jul 7, 2020

Tell if you agree or disagree with this direction. :)

I would agree in advance with direction to implement subset of CSS without deviation (except some defaults like border-box or scroller style/behaviour). I can help to say if some feature is widely used in html/css or implemented somehow different.

Yes, it works well and easily in flexbox but not in "manual placement". If you set width:50% for 2 siblings and they have margins they can't be placed next to each other while respecting margin.

Just don't do so :). If someone design sibbling for embed, it would be not clever to hardcode outer margins. There is no magical way to combine anything ugly into something perfect.

@kisvegabor
Copy link
Member Author

I would agree in advance with direction to implement subset of CSS without deviation (except some defaults like border-box or scroller style/behaviour). I can help to say if some feature is widely used in html/css or implemented somehow different.

Okay, according to this my summary above is not that bad.

@kisvegabor
Copy link
Member Author

Do you have an idea what's going on here?https://codepen.io/kisvegabor/pen/XWXGaMO
If every children's height is auto all works as expected, the heights are set to 100 px automatically

But if I set one child's height manually to 100px the siblings in the same row will have 115px calculated height.

@puzrin
Copy link
Contributor

puzrin commented Jul 24, 2020

I guess, it calculates every line height first, then expand proportionally. Without that special cell, initial state is:

75
75
75

=> 100 each.

But

100
75
75

=> ...

That's only my quick-guess. Should be verified in w3c specs.

@embeddedt
Copy link
Member

It may be related to cross-axis alignment as well (align-items). By default it's set to stretch which will make it stretch items without an explicit height as far as it can on the Y-axis.

@kisvegabor
Copy link
Member Author

@puzrin That makes sens, thank you!

@embeddedt I've already learned that but didn't understand how it stretches.

@puzrin
Copy link
Contributor

puzrin commented Jul 24, 2020

Note, flexbox is cool thing, but not mandatory in many cases. If it increase code size significantly, consider option to disable it. For many simple interfaces block and inline modes will be ok.

@kisvegabor
Copy link
Member Author

True, it should be optional.

With width/height:auto block and inline can be easily "simulated".

@excitedbox
Copy link

I believe grid layouts might be a better option than flex box for embedded devices. Grid allows not only the dynamic resizing but also dynamic rearranging of the layout components. This comes in handy when turning your device and switching from a horizontal to vertical layout. You may want certain parts of your UI to change the location altogether instead of changing their size or just wrapping to the next line.

You could for instance have the menu stretch across the top in horizontal mode or go down the side in vertical mode. I see it being very useful for a dashboard app where you might have many small components that are not the same height as your menu.

This is a great talk demonstrating how useful grid layouts are.
https://www.youtube.com/watch?v=7kVeCqQCxlk

@kisvegabor
Copy link
Member Author

@excitedbox
It's really reasonable to consider the "sibling" layout of the flexbox too. Actually flexbox vs grid wasn't discussed publicly so let's do it! 🙂

When I've checked it I found it a little bit complicated to implement due to the flexible references of areas and naming of rows/columns. However, if implement only a carefully chosen subset it might work.

I used this poster to see the summary of features

This my initial view about the required, not sure, and not required features:

Required

  • On container:
    • grid-template-columns/rows with support to px, %, auto and fr units.
    • gap
    • place-items
    • place-content
    • grid-auto-flow: row | column (no dense)
  • On items:
    • grid-column/row
    • place-self

Not sure

  • On container:
    • grid-auto-columns/rows

Not required

  • On container:
    • naming in grid-template-columns/rows
    • grid-template-areas
  • On item:
    • grid-area

What do you think?

@puzrin
Copy link
Contributor

puzrin commented Aug 3, 2020

This comes in handy when turning your device and switching from a horizontal to vertical layout. You may want certain parts of your UI to change the location altogether instead of changing their size or just wrapping to the next line.

Convenient thing, but if not supported, can be done via duplicaed blocks and visibility switch. Not a big deal.

@kisvegabor i'd suggest to postpone everything if it's not breaking / blocking. Sorry, have no practice with grids.

@kisvegabor
Copy link
Member Author

This comes in handy when turning your device and switching from a horizontal to vertical layout. You may want certain parts of your UI to change the location altogether instead of changing their size or just wrapping to the next line.

Convenient thing, but if not supported, can be done via duplicaed blocks and visibility switch. Not a big deal.

I think the goal is to avoid duplication. Finally, everything can be done without layouts if user calculates the position of the objects himself.

BTW, the "grid" can work very well with lv_table and lv_btnmatrix too.

@puzrin
Copy link
Contributor

puzrin commented Aug 3, 2020

I think the goal is to avoid duplication.

No. From my point of view, that's not a problem at all. My goal is to not rewrie app everytime when lvgl inroduces next bunch of breaking changes.

Finally, everything can be done without layouts if user calculates the position of the objects himself.

Such generic statement can not be used for any conclusion. If you need decision about feature value, estimate list of use cases and their weighs.

For example, there are lot of cases for flexbox, and it can not be emulated well via more simple styles. Grid is not so critical, IMO, and will be not a breaking change if postponed.

@embeddedt
Copy link
Member

This is not necessarily representative of an embedded app, but in all the web projects I've worked on thus far, I've never used CSS grid. I've only used flexbox and the classic block layout.

@kisvegabor
Copy link
Member Author

kisvegabor commented Aug 4, 2020

To see the advantages/disadvantages lets see an example UI:
preview
Source

The right (light) UI can be created with one large grid without any transparent wrapper elements.

On the left UI the list elements with an image and 3 labels are interesting. They are also a single grid.

With flexbox both cases would require a lot of wrapper divs to set different flex directions and other parameters.


I was missing one thing from grid: the ability to automatically wrap elements as flexbox does. But it turned out it is supported like this: https://codepen.io/Weldebob/pen/WjMLav

What do you think?

@puzrin
Copy link
Contributor

puzrin commented Aug 4, 2020

The right (light) UI can be created with one large grid without any transparent wrapper elements.

can != should. Usually each component of web-page should be self-sufficient (including inner layout & css). Or you will have problems with modificaions.

Of cause, nobody can prohibit you create monolytic solutions, but that's not good argument for consideration.


As i said, i see no reasons to discuss technical aspects of grid. I'll be ok both with and without grid (i don't decline to use it). The only critical thing for me is to include to v8.x all big breaking things. Text nodes and RGBA in styles still have floating satus - seems more significant (in scope of api stability).

@kisvegabor
Copy link
Member Author

can != should.

Good point.

Usually each component of web-page should be self-sufficient (including inner layout & css). Or you will have problems with modificaions.

In case of embedded design, I see higher value in saving some containers (100-150 byte each) than loosing a little on flexibility. Anyway, if flexibility is preferred you are free to create more containers for local layouts.

The only critical thing for me is to include to v8.x all big breaking things.

Actually that's the gist. Adding flexbox now and grid beside it later (or vice-versa) is certainly a wrong direction. LVGL is too small to have 2 very similar and large features in it. So we have to choose one to live with for a (hopefully) long time.


I'm getting closer to choose grid because (in our case) it knows what flexbox knows and more. But we don't have to decide now. Let some days for remarks to come.

@puzrin
Copy link
Contributor

puzrin commented Aug 4, 2020

In case of embedded design, I see higher value in saving some containers (100-150 byte each) than loosing a little on flexibility.

To be honest, i don't beleive in arguments about savings in phase of architecture design. From my experience - if you have good modular architecture, it can be optimized later effectively. Premature optimizations cause tight-coupling of data/code, and become a problem very soon.

For example, if memory problem is with long list - you can provide explicit custom solution for list.

Anyway, if flexibility is preferred you are free to create more containers for local layouts.

I have a bit different view. Flexbox is minimal (mandatory) requirement to express "everything". Grid is really nice, but not mandatory, if flexbox available.

In scope of "restriced human resources", priorities are shifted from "what can be done" to "what can't be avoided".

I'm getting closer to choose grid because (in our case) it knows what flexbox knows and more. But we don't have to decide now. Let some days for remarks to come.

I can not estimate cost of grid implementation, because it's not trivial thing (much more complicated than flexbox). But if you are sure - i'm ok to use grid instead of flexbox. It's your time, and if you have fun with grid implementation - no problem. At first glance, if you will be able to implement full-featured grid - it will possible cut it to "flexbox mode" to save flash [if needed].

@puzrin
Copy link
Contributor

puzrin commented Aug 6, 2020

One more checkpoint. It was no mentioned in this topic, but position: relative and position: absolute sometime required. Mostly, to organise sticky header/footer. If it's already done - sorry for buzz.

@kisvegabor
Copy link
Member Author

One more checkpoint. It was no mentioned in this topic, but position: relative and position: absolute sometime required. Mostly, to organise sticky header/footer.

position:absolute hardly fits into the concept of LVGL because LVGL crops children out of the parent. To do something like this you need to create the object on lv_scr_act() to make its coordinates relative to the screen.

However, I faced a problem when mouse cursor caused overflow (i.e. made scrollbars appear) when it was in the bottom-most position and its bottom part was out of the screen. So some setting is required to "let objects live their own life" regarding causing overflow at least. I don't know which HTML property can be used for this yet.


I was thinking a lot about grid. I found it very useful to create fixed flat layouts (like the music player I shown above. It's a clear advantage over flexbox.

However, explicitly placing some objects and let others flow automatically is quite complicated to handle. So I came up with a simplification. Do not mix "fix grid" and "dynamic grid". The idea is if grid-auto-flow: none only explicitly placed children will be arranged by the grid. In this case, both gird-template-rows and gird-template-columns need to be specified. Children also need to be in the specified grid. (as no autoflow).

In the other case, grid-auto-flow is rows or columns and only grid-template-rows or grid-template-columns can be specified. (The other is auto). Children are placed automatically and new rows/columns are added on demand. It is useful to arrange list items or to place icons in a file browser-like window.

Something like repeat(auto-fit, 200px) is also required to fill the required space automatically.

It misses one thing which can be done with flexbox: "flow" elements with different sizes with wrapping. It would be no like a grid after all. However, I can't imagine any use cases from the embedded UI world where it would be required.

What do you think about this approach?

@puzrin
Copy link
Contributor

puzrin commented Aug 10, 2020

position:absolute hardly fits into the concept of LVGL because LVGL crops children out of the parent.

screen =

  • body (if single screen)
  • div with { position: absolute; top: 0; bottom: 0; left:0; right:0; overflow: hidden; } inside of body.

I don't suggest to drop screens immediately. But in he same time, i don't understand the problem.

Top-level parent (virtual or real) should always exist. It's size will be equal to display size. If you don't plan to display object on 2 displays simultaniously - i see no collisions.

To do something like this you need to create the object on lv_scr_act() to make its coordinates relative to the screen.

My question is, can we do it more close to HTML/CSS, without enforsed top screen layer (user can do it by hand, if needed).

However, I faced a problem when mouse cursor caused overflow (i.e. made scrollbars appear) when it was in the bottom-most position and its bottom part was out of the screen. So some setting is required to "let objects live their own life" regarding causing overflow at least. I don't know which HTML property can be used for this yet.

AFAIK, cursor is not is not part of DOM an not part of layout. This should be considered separate (and that's about draw, not layout).

In html there are no way to "let objects live their own life" exactly. But this can be done with position: absolute in scope of nearest parent, having position: relative. So, for max freedom you can place obj to <body> - top level object.


I did not dived into grid spec, can't comment. The only note - it looks "too much" for sticky header/footer. You tend to invent complicated monolytic silver bullet instead of independent/optional things. Miracles not exist, i've checked :)

@excitedbox
Copy link

I have been a web dev for close to 20 years at this point so I have lived through everything from the Tables, Divs, Floats and Layers to the modern flexbox and grid layouts. Grid layouts really are the best option available for dynamic layouts.

Flexbox is great if all your components have the same dimensions but everything is arranged in either rows or columns so as soon as you have 1 tall element next to 2 short elements you need to use a container.

Grid layout splits your view port into a grid and elements are aligned by arranging them on that grid. I think it might even be easier to implement than flexbox because you only define a starting grid and ending grid space for each item on that grid which should be easy to calculate.

If you divide the screen in the number of grid spaces you could align elements by specifying which grid they should start in and allow them to overflow the edges in either vertical or horizontal direction as if they were on another layer. This gives them as much space as they need or you define a starting grid AND ending grid space to limit how big the item is.

Then you specify break points to switch to another layout. So for instance Draw Button starting in grid 1 if the screen is horizontal or draw it starting in grid 5 if it is vertical. This could be done just by adding a single rule for each item based on horizontal or vertical orientation. There would also be the possibility for other rules such as how much space there is for the element to consume using the remaining fraction of the screen

This link will show you how simple it is to define grid layouts and the flexibility they allow. Since you already have pixel positioning for elements you just need to transform grid square values into the pixel values based on the height and width of the spaces.
https://css-tricks.com/snippets/css/complete-guide-grid/

I was thinking the current layout style definitions should be made to be more like CSS by allowing the use of shorter keywords instead of a long rule name as it is right now (lv_style_set_radius would become radius for instance) this could be done with very basic parsing rules since there would be so little variance unlike in web design where HTML, CSS, JS etc. all co exist.
Something like (or .item1 { } like CSS) lets the parser know that there will be styles for item 1 now. Then a property / value separator such as : in CSS and an end property character like ; There would be no real need for for complex lexers etc. Just parsing through a style sheet and looping through all the items to set the style rules in the code.

This would make it quicker and easier to define styles and also allow quicker changing of styles. This would even make it easier to build additional tools such as a style generator for a wysiwyg editor down the road.

@excitedbox
Copy link

Grid is like using pixel positioning with reduced number of pixels. So overflow spills onto the next grid space if there is no end row or column defined. This is also what makes masonry layouts possible. By drawing the grid on it's own layer you can draw the grid to be smaller than an element and one element can cover 4x4 grid spaces while another only covers 2x2 spaces with 2 2x1 elements below it. For flex box you would need 2 containers (4x4 and 4x2) and another 2 containers (2x2 and 2x2) stacked on top of each other in the right one and another 2 containers for the 2 small elements. So the grid layout would eliminate 2 out of 6 containers.

Cursor does not belong to the grid and the only time you really need to worry about overflow with a grid is if a drop down or something that pops up goes off the screen in which case the best option is to start drawing at the screen edge - the width/height of the drop down or pop up.

In our case the grid is not really drawn or even used beyond as a divisor to make positioning easier. It is used to calculate a pixel value and set rules for how the pixel value should change based on dimensions or orientation. In reality the number of containers etc doesn't matter for Lvgl as long as you use pixel positioning instead of implementing a full DOM. You just need to calculate the pixel position for where to draw a widget. With a grid layout system that becomes very easy since if the screen is 600x400 and you divide it into a 12x8 grid each space is 50 pixels so an element starting in grid 1,4 would have a starting position of 400px horizontal and 0px vertical while 4,4 is 400 horizontal and 200 vertical. It is just row or column -1 times the grid space dimensions. So an element would have a start and end coordinate that gets multiplied by the space dimensions and to calculate the size of the element you subtract the difference of the start and end position in pixels.

@excitedbox
Copy link

excitedbox commented Aug 10, 2020

To summarize my 2 posts.

https://css-tricks.com/wp-content/uploads/2018/11/dddgrid-template-areas.svg

I propose:

  • Style sheet for defining style rules.
  • Grid based layout definition that is translated to pixel positions in the code. Which could either be done at compile time or using a layout editor that creates your style sheet on export and is included as a header.
  • Styles based on CSS syntax and using a simple parser to convert or fill in style rules as is currently implemented so using a new style language doesn't require throwing out what is already there, only adding to it.

example:

<screen1>{
<grid>{
grid-rows: 4;
grid-columns:4;

}
<button.btn1>{
hor-layout: start-grid(2,2), end-grid(2,3);
vert-layout: start-grid(3,2), end-grid(3,3);
bg-color:red;
}
<canvas.mycanvas>{
hor-layout: start-grid(4,2), end-grid(4,4);
vert-layout: start-grid(2,2), end-grid(2,4);
bg-color:blue;
}
}

These are just to show what it could look like and property values are not meant to be exact

Benefits are that everything is in 1 place, simple to configure, easy to learn, easy to update, flexible, less typing and fewer style rules to remember. It should only require minimal amounts of code to implement a parser and integrate into the current system. Positions are easy to calculate as long as screen dimensions are known.

@embeddedt
Copy link
Member

@excitedbox You may be interested in https://github.com/embeddedt/lv_css. I'm planning to extend it with layout support once the spec gets finalized.

@excitedbox
Copy link

Wow that is exactly what I had in mind 💯 :D

@embeddedt
Copy link
Member

The next thing I need to add to it is support for object type selectors, as opposed to just classes. That will enable people to quickly and easily create custom themes.

I'm not planning to support the combined versions of properties (e.g. background, flex) as these seemed a lot harder to parse and I want to keep the compiler as simple as possible.

Once that's done, all that's left to do is make a proper CLI for it and add a watch mode so it can be integrated with any build system.

@amirgon
Copy link
Contributor

amirgon commented Aug 11, 2020

You may be interested in https://github.com/embeddedt/lv_css. I'm planning to extend it with layout support once the spec gets finalized.

@embeddedt Do you have plans to generate Micropython code as well?

@kisvegabor
Copy link
Member Author

kisvegabor commented Aug 11, 2020

@embeddedt
Are you working on such great stuff and don't mention it!? 😄
Anyway, it's awesome! I wonder if could have an HTML binding once. That is to create the UI from a file and apply CSS. It might require a JaveScript-like binding too. (Now it can be done with micropython though.) Finally, it can be a simple embedded browser. I'm not sure it's worth the effort just thinking aloud.

@excitedbox

Grid based layout definition that is translated to pixel positions in the code. Which could either be done at compile time or using a layout editor that creates your style sheet on export and is included as a header.

I was also thinking about compile-time calculation in some form but found these problems:

  • can't use auto for track size as it depends on children size
  • can't use fr as depends on parent size which is not known at compile-time and can change.

In this comment, I summarized my view about "fix" and "dynamic" grid. According to this what you described is the "fix grid". However, we also need to layout an unknown number of elements.

@embeddedt
Copy link
Member

embeddedt commented Aug 11, 2020

Are you working on such great stuff and don't mention it!?

I was planning to mention it once it was more polished. It was initially a quick experiment but I think it'll be a pretty useful tool. I decided to link to it now because of @excitedbox's comment.

Do you have plans to generate Micropython code as well?

I can add that as well; it should only require changing the output syntax.

@excitedbox
Copy link

Have you seen this library? It is a pretty cool demo of a resizing stacking box layout. I haven't looked at how it is done but thought maybe you would be interested for inspiration.

https://github.com/randrew/layout

@kisvegabor
Copy link
Member Author

kisvegabor commented Sep 1, 2020

I've implemented grid in feat/new-scroll branch.

Of course, there are some limitations but in general, I think it's easy to use and versatile.

What is working

  • Positions take padding into account. I.e. (0;0) is (pad_left, pad_top)
  • Auto size to content with lv_obj_set_size(btn, LV_SIZE_AUTO, LV_SIZE_AUTO)
  • Grid item placement with lv_obj_set_pos(obj, LV_GRID_CELL_START/END/CENTER/STRETCH(pos, span), ...)
  • For templates: px and fr units
  • Nested grids
  • Auto flow (row/colum)
  • Grid is 4x faster to create lv_list (nested grid). Besides the current layout crashes after 600 items. I tested grid with 3000 items.

What is still planned

  • place-content: start | end | center | stretch | space-evenly | space-around | space-between
  • gap
  • Something like repeat(auto-fill, minmax(200px, 1fr));
  • Some API is not finshed yet

Limitations

  • The grid must be fully explicit (both column and row templates are set and all items are placed explicitly) or implicit (only row or column template is set and items are automatically placed in creation order)
  • In case of the implicit grid, the automatically added rows/columns size is set to the content's size
  • No auto size in templates
  • If the container has auto size fr tracks collapse to zero (in CSS they collapse to auto)
  • No global justify-items, align-items or place-items, it needs to be set for every child

List example
(color overlay on grid cells)
image

  /*List gird descriptor*/
  static lv_coord_t dsc_cont[] = {LV_GRID_FR(1)};
  lv_grid_t grid_cont;
  memset(&grid_cont, 0, sizeof(lv_grid_t));
  grid_cont.col_dsc = dsc_cont;
  grid_cont.col_dsc_len = 1;
  grid_cont.row_dsc = NULL;
  grid_cont.row_dsc_len = 0;
 
  /*Create the list container*/
  lv_obj_t * cont = lv_obj_create(lv_scr_act(), NULL);
  lv_obj_set_size(cont, 300, 400);
  cont->grid = &grid_cont;

  /*Item gird descriptor*/
  lv_coord_t dsc_item[] = {20, LV_GRID_FR(1)};
  lv_grid_t grid_item;
  memset(&grid_item, 0, sizeof(lv_grid_t));
  grid_item.col_dsc = dsc_item;
  grid_item.col_dsc_len = 2;
  grid_item.row_dsc = NULL;
  grid_item.row_dsc_len = 0;

  int i;
  for(i = 0;i < 30; i++) {
      /*Create list item: stretch to list width and set auto height to */
      lv_obj_t * item = lv_obj_create(cont, NULL);
      lv_obj_set_pos(item, LV_GRID_AUTO_STRETCH, LV_GRID_AUTO_START);
      lv_obj_set_height(item, LV_SIZE_AUTO);
      item->grid = &grid_item;

      /*Create an icon and align it to the center in its cell*/
      lv_obj_t * icon = lv_label_create(item, NULL);
      lv_label_set_text(icon, LV_SYMBOL_OK);
      lv_obj_set_pos(icon, LV_GRID_AUTO_CENTER, LV_GRID_AUTO_CENTER);

      /*Add a label, set auto scroll, and stretch to the cell width*/
      lv_obj_t * label = lv_label_create(item, NULL);
      lv_label_set_long_mode(label, LV_LABEL_LONG_SROLL_CIRC);
      lv_obj_set_pos(label, LV_GRID_AUTO_STRETCH, LV_GRID_AUTO_START);
      lv_label_set_text(label, "A very long list item to scroll");
  }

@bil-ash
Copy link

bil-ash commented Sep 8, 2020

Anyway, it's awesome! I wonder if could have an HTML binding once. That is to create the UI from a file and apply CSS. It might require a JaveScript-like binding too. (Now it can be done with micropython though.) Finally, it can be a simple embedded browser. I'm not sure it's worth the effort just thinking aloud.

This is something I would like to try out,after flexbox implementation is complete. For javascript binding jerryscript can be used as it supports a variety of embedded OS. But first of all a lightweight HTML parser will be required . Any suggestions regarding this? @embeddedt Any other aspect I am missing out?(Asking you since you are also a web developer -saw from your github profile)

@embeddedt
Copy link
Member

@excitedbox had previously mentioned this library in a forum post. I haven't experimented with it but it does have an HTML parser. Maybe that would be useful.

@bil-ash
Copy link

bil-ash commented Sep 17, 2020

Anyway, it's awesome! I wonder if could have an HTML binding once. That is to create the UI from a file and apply CSS. It might require a JaveScript-like binding too. (Now it can be done with micropython though.) Finally, it can be a simple embedded browser. I'm not sure it's worth the effort just thinking aloud.

This is something I would like to try out,after flexbox implementation is complete. For javascript binding jerryscript can be used as it supports a variety of embedded OS. But first of all a lightweight HTML parser will be required . Any suggestions regarding this? @embeddedt Any other aspect I am missing out?(Asking you since you are also a web developer -saw from your github profile)

I needed a lightweight webview which could write to framebuffer. Netsurf exactly fits my requirement and so I have abandoned my above plan.

@kisvegabor
Copy link
Member Author

I think all were discussed related to the new layouts so I close this issue.

Thank you very much for sharing your ideas.

@nickdima
Copy link

Maybe you can take some inspiration from Yoga, the layout engine used by React Native: https://yogalayout.com/

@embeddedt
Copy link
Member

@nickdima As a matter of fact, we did end up implementing flexbox as one of our layout options.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants