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

Improve shrink-to-fit layout (fit-content sizing) #552

Open
amlinux opened this issue Dec 26, 2023 · 1 comment
Open

Improve shrink-to-fit layout (fit-content sizing) #552

amlinux opened this issue Dec 26, 2023 · 1 comment
Labels
enhancement New feature or request layout Layout engine issues and enhancements

Comments

@amlinux
Copy link

amlinux commented Dec 26, 2023

Absolutely positioned div

<rml>
	<head>
		<style>
			* {
				box-sizing: border-box;
		    	        font-family: Play;
			}

			.block1 {
			        display: block;
				border: 1px red;
				width: 100px;
			}

			.block2 {
			        display: block;
				border: 1px blue;
				width: 100px;
			}

			.block3 {
			        display: block;
				border: 1px green;
				width: 100px;
			}

			#wrapper {
				position: absolute;
				display: block;
				left: 100px;
				top: 100px;
				border: 1px white;
			}
		</style>
	</head>
	<body>
		<div id="wrapper">
			<div class="block1">Block 1</div>
			<div class="block2">Block 2</div>
			<div class="block3">Block 3</div>
		</div>
	</body>
</rml>

Note that white #wrapper that was supposed to wrap all nested blocks, in fact has width = 2 (empty content + 2 pixels for borders):

image

If I remove "position: absolute", it does the right thing:

image

Blocks with display: flex

<rml>
	<head>
		<style>
			* {
				box-sizing: border-box;
		    	        font-family: Play;
			}

			.outer {
			        display: flex;
			        border: 1px red;
                                padding: 50px;
			}

                       .inner {
                                border: 1px blue;
                                padding: 50px;
                       }
		</style>
	</head>
	<body>
                <div class="outer"><div class="inner">Block 1</div></div>
	</body>
</rml>

Note that outer (red) block doesn't "see" that its inner (blue) block has any width:

image

Complex grid layouts

<rml>
    <head>
        <style>
            * {
                box-sizing: border-box;
                font-family: Play;
            }

            .block {
                display: block;
                width: 50px;
                height: 50px;
                margin: 10px;
            }

            .red {
                background-color: red;
            }

            .green {
                background-color: green;
            }

            .blue {
                background-color: blue;
            }

            .outer {
                display: flex;
                flex-direction: row;
            }

            .inner {
                display: flex;
                flex-direction: column;
            }
        </style>
    </head>
    <body>
        <div class="outer">
            <div class="inner">
                <div class="block red"></div>
                <div class="block green"></div>
                <div class="block blue"></div>
            </div>
            <div class="inner">
                <div class="block red"></div>
                <div class="block green"></div>
                <div class="block blue"></div>
            </div>
            <div class="inner">
                <div class="block red"></div>
                <div class="block green"></div>
                <div class="block blue"></div>
            </div>
        </div>
    </body>
</rml>

This is how RmlUi renders it:

image

Equivalent layout in HTML/CSS (expected result):

image

Interesting is that if I swap flex-direction in outer and inner blocks (outer to column, inner to row), then RmlUi doesn't render anything:

image

While HTML/CSS renders it as expected:

image

I believe that the root cause is the same as in #520, and while 8a94ab7 fixed it for a trivial case, a larger body of cases is still not supported.

@mikke89 mikke89 added enhancement New feature or request layout Layout engine issues and enhancements labels Dec 26, 2023
@mikke89 mikke89 changed the title RmlUi doesn't always compute dimensions of wrapping elements correctly Improve shrink-to-fit layout (fit-content sizing) Dec 26, 2023
@mikke89
Copy link
Owner

mikke89 commented Dec 26, 2023

Thanks for the detailed report, and the reproducible cases. This is certainly an area of the layout engine that can be improved, that I would fully support. These situations are all related to calculations of shrink-to-fit width as its called in CSS2, or more generically fit-content size in CSS3.

Currently, this is done quite ad-hoc within the layout engine, and not supported for tables and flexboxes in particular.

Some notes for implementing this:

  • We need to implement some notion of max-content sizing, as seen in the definition of fit-content size in the above link.
  • In my view, the min-content sizing from CSS is not particularly important for us, since content in RmlUi layouts are considered to be better controlled than on the web in general. I.e. we explicitly don't try to "fix" broken layouts.
  • In particular, for flexbox, we need to implement intrinsic sizing of flex containers. For tables, see max-content here.
  • We could need a more robust approach to determine intrinsic sizing in the layout engine. We may want some state that keeps track of any max-content constraint, the current available width, definite vs indefinite sizes, and similar. This could also help guide line breaks and early-outs for performance reasons.
  • Currently, we only consider width direction, we may also need a notion of fit-content height, in particular when working with flexboxes with column direction.

Performance considerations:

  • Each layout element that invokes "shrink-to-fit" essentially has to do layout twice. Once to determine the max-content width, and once more for the actually determined width.
  • For nested elements that invoke shrink-to-fit, this essentially means the number of layout operations for the most-descendant elements grows by O(n^2), where n is the number of nested shrink-to-fit elements.
  • If we implement min-content as well, then that expands to doing layout three times per shrink-to-fit element.
  • I'm sure we can be much smarter about this, such that these calculations can either break early under certain situations, or some ways of avoiding to do the layout multiple times in some situations. However, in general this sounds like it would increase complexity, require a lot of testing, and probably a lot of development efforts. But there could be some low-hanging fruits.
    • In particular, we could break out early if the max-content width exceeds the available width under a fit-content constraint.

Hopefully, this gives some notion of how we could get started. I'm sure this is something that needs a lot of iterations to solve more generally, we'll have to test it against a lot of different situations as it evolves. But these examples seem like good starting points.

alml added a commit to alml/RmlUi that referenced this issue Jan 1, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 2, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 2, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 4, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 14, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 18, 2024
alml added a commit to alml/RmlUi that referenced this issue Jan 18, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request layout Layout engine issues and enhancements
Projects
None yet
Development

No branches or pull requests

2 participants