Skip to content

Latest commit

 

History

History
59 lines (37 loc) · 4.82 KB

LayoutEngine.md

File metadata and controls

59 lines (37 loc) · 4.82 KB

Layout engine

Declare nodes between imdui::newFrame() and imdui::endFrame(). The engine solves their positions and sizes at frame end; renderFrame() draws the result. Declarations made outside a frame assert in debug builds and are absorbed by a scratch node (never rendered) in release builds. The public declarations are in include/IMDUI/Layout.h, with text helpers in include/IMDUI/Text.h.

Containers and flow

node() creates an invisible container. Shape, text, and SVG drawing functions create visible nodes. Setters return the same NodeRef, so size, position, style, and event handlers can be chained.

Use enter() to make a node the parent of subsequent declarations. The returned ScopedParent restores the previous parent when it leaves scope. Every guard must leave scope before endFrame().

if (auto panel = imdui::drawRectangle(24, 24, 320, 180)
                     .asColumn(12)
                     .color(imdui::Color::fromHex(0x18222D))
                     .enter())
{
    imdui::drawText(L"Preferences").fontSize(20);
    imdui::drawRectangle(0, 0, 240, 32)
        .color(imdui::Color::fromHex(0x28587A));
}

A parent defaults to no flow: children use parent-relative coordinates. asRow(gap) and asColumn(gap) arrange children along one axis. asAbsolute() places a node in viewport coordinates and removes it from its parent's flow.

Size and position

Width and height can independently use pixels, a fraction of the parent, content fitting, a ratio of the other axis, or expansion into unused flow space. Auto sizing derives a container's size from its children. Text defaults to its measured glyph extent; SVG defaults to its raster dimensions.

widthExpand(weight) and heightExpand(weight) divide the remaining space along a row or column's main axis. Min/max setters clamp the resolved dimensions. Avoid circular dependencies, such as a content-sized parent containing only percentage-sized children; the solver cannot derive a useful size without an independent dimension.

x and y set pixel offsets. positionRelativeTo anchors to another node, while Offset expresses pixels or fractions of the anchor, parent, or node's own size. Keep anchor references within the current frame. Read resolvedX, resolvedY, resolvedWidth, and resolvedHeight after endFrame().

Appearance and layers

Shapes support fill color, corner radii, borders, and shadows. opacity multiplies down the tree, and fadeIn adds a retained opacity animation. See animation.

clipChildren() intersects descendant rendering with the parent's rectangle and any ancestor clips. overlay() draws the node and its descendants after normal content. The first overlay node escapes ancestor clipping, allowing a popup to extend beyond a scrolling panel; the popup can still clip its own children.

Text and SVG nodes borrow their strings, fonts, and images. Keep those objects alive and unchanged through renderFrame(). The bundled font covers printable ASCII. Text rendering skips glyphs that are absent from the selected atlas and does not perform multiline layout, kerning, or complex-script shaping.

Input

onClick and onHover callbacks run during endFrame(), after solving. Click dispatch checks overlays first, then normal nodes, with later siblings taking priority. Clicks use the left-button press edge. Callback userdata must remain alive through dispatch.

isHovered() and isClicked() query resolved bounds. Consume their results after layout has been solved. mouseWheel() returns and clears the accumulated delta: the first caller consumes it. A custom nested scroll system must decide which hovered control reads it.

Lifetime and implementation

A NodeRef points into a frame arena. The engine alternates two arenas so the previous frame's solved geometry remains readable while the next frame is built. A reference becomes invalid at the start of the second subsequent frame, or when layout is shut down. Copying it does not extend its lifetime. Only modify or enter nodes created in the current frame. Retained controls should store application values separately and declare their nodes each frame.

The internal files under src/layout divide the work:

  • Arena.h allocates linked chunks and rewinds them for reuse each frame. Nodes must be trivially destructible; allocation failure throws std::bad_alloc when constructing a node.
  • Node.h defines properties, tree links, resolved geometry, and node kinds for containers, shapes, text, and SVG images.
  • Engine.cpp builds the tree, assigns animation IDs, and dispatches input callbacks.
  • Solver.cpp resolves dimensions and positions.
  • Renderer.cpp builds and draws GPU batches.

destroyContext() shuts down the layout arena, animation store, and renderer. See core and input for context ownership and the complete frame lifecycle.