spirali / nelsie

Framework for Creating Slides
MIT License
39 stars 4 forks source link

flex_grow not working as expected inside nested boxes. #11

Closed fgelm01 closed 5 months ago

fgelm01 commented 5 months ago

When using flex_grow=1 on a nested box, that box does not grow to take up remaining space. Instead it is sized just large enough to fit its content, or completely invisible if there is no content.

Consider this example:

from nelsie import Slide, SlideDeck

slides = SlideDeck(width=100, height=100)

@slides.slide()
def slide1(slide: Slide) -> None:
    slide.box(width=40, height=25, bg_color="#ff0000")
    slide.box(width=40, flex_grow=1, bg_color="#00ff00")
    slide.box(width=40, height=25, bg_color="#0000ff")

I expect a red box sized 40x25, a green box sized 40x50, and a blue box sized 40x25. That's exactly what happens. 0-1

But not when I try to do the same thing in a more deeply nested box:

@slides.slide()
def slide2(slide: Slide) -> None:
    parent = slide.box(row=True)

    left = parent.box()
    right = parent.box()

    left.box(width=40, height=25, bg_color="#ff6060")
    left.box(width=40, flex_grow=1, bg_color="#60ff60")
    left.box(width=40, height=25, bg_color="#6060ff")

    right.box(width=40, height=25, bg_color="#df4040")
    right.box(width=40, flex_grow=1, bg_color="#40df40")
    right.box(width=40, height=25, bg_color="#4040df")

I expect two versions of the same arrangement as above, side by side. But that's not what happens. 1-1

fgelm01 commented 5 months ago

For now I'll be using a silly workaround:

@slides.slide()
def slide2(slide: Slide) -> None:
    root = slide.box(row=True)
    left = root.box()
    right = root.box()

    left.box(width=40, height=25, bg_color="#ff6060")
    left.box(width=40, height="100%", flex_shrink=1, bg_color="#60ff60")
    left.box(width=40, flex_shrink=1, height=25, bg_color="#6060ff")

    right.box(width=40, height=25, bg_color="#df4040")
    right.box(width=40, height="100%", flex_shrink=1, bg_color="#40df40")
    right.box(width=40, height=25, bg_color="#4040df")

1-1

It would have helped if flex_shrink were documented in https://spirali.github.io/nelsie/guide/box/#layout-parameters https://spirali.github.io/nelsie/guide/layout/#box-size

spirali commented 5 months ago

Hi, It is caused by a fact that a box (by default) tries to occupy as minimal space as possible. So it tries not use flex_shrink at all if it can. The root box is different because it has a fixed size. This can be fixed by setting a size to parent box (height in your case):

@slides.slide()
def slide1(slide: Slide):
    parent = slide.box(height="100%")  # <-- Here set height so it will not occupy minimal space
    parent.box(width=40, height=25, bg_color="#ff0000")
    parent.box(width=40, flex_grow=1, bg_color="#00ff00")
    parent.box(width=40, height=25, bg_color="#0000ff")

0-1

Your two column layout can be done as follows:

@slides.slide()
def slide2(slide: Slide) -> None:
    parent = slide.box(row=True, height="100%")  # <---- setting relative height

    left = parent.box(height="100%")  # <--- setting relative height
    right = parent.box(height="100%")  # <--- setting relative height

    left.box(width=40, height=25, bg_color="#ff6060")
    left.box(width=40, flex_grow=1, bg_color="#60ff60")
    left.box(width=40, height=25, bg_color="#6060ff")

    right.box(width=40, height=25, bg_color="#df4040")
    right.box(width=40, flex_grow=1, bg_color="#40df40")
    right.box(width=40, height=25, bg_color="#4040df")

Or alternatively setting an absolute height:

@slides.slide()
def slide2(slide: Slide) -> None:
    parent = slide.box(row=True)

    left = parent.box(height=100)  # <--- setting absolute height
    right = parent.box(height=100)  # <--- setting absolute height

    left.box(width=40, height=25, bg_color="#ff6060")
    left.box(width=40, flex_grow=1, bg_color="#60ff60")
    left.box(width=40, height=25, bg_color="#6060ff")

    right.box(width=40, height=25, bg_color="#df4040")
    right.box(width=40, flex_grow=1, bg_color="#40df40")
    right.box(width=40, height=25, bg_color="#4040df")

Or alternatively you can set "flex_grow" to parent boxes so it will not try to occupy minimal space.

@slides.slide()
def slide1(slide: Slide):
    parent = slide.box(flex_grow=1)  # <--- flex grow here
    parent.box(width=40, height=25, bg_color="#ff0000")
    parent.box(width=40, flex_grow=1, bg_color="#00ff00")
    parent.box(width=40, height=25, bg_color="#0000ff")

Thanks for your note about flex_shrink, I will update the documentation.

fgelm01 commented 5 months ago

Thank you very much. I really appreciate the prompt and detailed response. Your project is my first exposure to flexbox, so my mental model was incorrect.