Julie's Document Website / Delphi Menu Engine / NLayout Engine
Publish Date
2025-01-13 16:08:00 +0200 UTC
Author
Julie

NLayout Engine

The biggest nightmare in this project.

Table Of Contents

Introduction

This document describes the process of writing the NLayout engine that executes layout for Delphi and how it works.

This part, the layout engine, has been the most difficult part of the Delphi project to date, executing layout with so many variables that interact in so many different ways depending on layout context and such.

The layout engine is part of the delphirender module which handles DOM rendering and Minecraft entity management.

The Name

NLayout just means "New Layout", after the Java class I started writing it in, which was the 2nd version of the layout engine. The first being very primitive, just taking in a layout-direction (Either X or Y) and tiling elements in that direction.

Layout passes

Layout is divided into passes, and this section describes the initial concept for the engine and the passes it executed.

Measurement

Iterate over each element, and measure it, calculate style properties, padding, margins, etc.

This pass uses a stack of vector2s called parentSizes, as elements are measured and we move onto their child elements, we push the element's content size onto the stack. And pop after we finish

If an element's size is set, then we use that as the element's size, otherwise, we calculate the size based off of it's child elements and layout algorithm.

Firstly, if an element's size is based off of it's child elements, we assume the element's initial size to be the screen's size and then calculate its child elements. We then adjust the element's size to be the measured result. Then, the whole process is executed over and over until the child elements stop changing their size, aka, until the layout stabilizes.

Layout

Layout is performed by Layout algorithms, each executing it differently. At time of writing only Flow is supported.

Flow layout

Take the child elements of the element we're laying out and break them into lines. To do that, we iterate over each one, skipping ones with display: hide;. If an element is not display: inline or its too big for the current line, a line break is triggered. If the element is display: block another line break is triggered after the element.

Then all the elements are laid out. Elements on the same line are stacked horizontally, while lines are stacked vertically.

After layout has finished, iterate over all child elements with display: block. And check if they have any horizontal auto margins. If they do, properly move the element according to those margins: Do nothing if only right margin is set to auto. Move element to the ride side of the parent's content if only left margin is set, and center element if both are set.

Sources / Docs / Tutorials