Containers
Structural nodes that arrange children — stacks, tables, and lists. Note
there is no DocraftLoomForeach node: <Foreach> is expanded away
during tree building (see Craft Language Parser), not represented in the node
tree at all.
DocraftLoomLayoutContainer
Shared base for Rectangle/VStack/HStack — factors out spacing() and the
CSS-margin-collapsing gap resolution between adjacent children (see
DocraftLoomNode::margin() in Node Tree — Base & Text).
-
class DocraftLoomLayoutContainer : public docraft::loom::nodes::DocraftLoomShape
Abstract base for loom nodes that stack children with a gap between each pair of them and can optionally paint a background/border behind that stack.
Factors out what
DocraftLoomRectangle,DocraftLoomVStack, andDocraftLoomHStackall need identically —spacing()/set_spacing(), plusstyle()/edit_style()inherited fromDocraftLoomShape— so those three don’t each duplicate the same getter/setter pairs. Never instantiated directly; each subclass picks its own default spacing (Rectangle and VStack want visible breathing room by default, HStack’s shrink-to-fit columns don’t).Subclassed by docraft::loom::nodes::DocraftLoomHStack, docraft::loom::nodes::DocraftLoomRectangle, docraft::loom::nodes::DocraftLoomVStack
Public Functions
-
~DocraftLoomLayoutContainer() override = default
-
float spacing() const
-
void set_spacing(float spacing)
-
virtual float resolve_outer_margin(const DocraftLoomNode &node, bool leading)
The gap this container must reserve outside its first (leading=true) or last (leading=false) child — that child’s own edge margin, with no sibling on that side to combine it with via resolve_child_gap().
0 if node has no children. Default reads the vertical edges (top/bottom), shared as-is by Rectangle/VStack; DocraftLoomHStack overrides this with its own (left/right) edges.
-
float effective_padding() const
The padding actually used for measurement/layout.
Public Static Functions
-
static float resolve_child_gap(float container_spacing, float margin_a, float margin_b)
The gap actually placed between two adjacent children, given the two touching edges (e.g.
child A’s bottom margin and child B’s top margin for a vertical stack, or A’s right and B’s left for a horizontal one).
Public Static Attributes
-
static constexpr float kDefaultPadding = 10.0F
Default inset (points) applied to padding() by the constructor below, so a Rectangle/VStack/HStack that paints a border/background doesn’t have its children’s content sit flush against that border unless the craft document explicitly overrides it via a
paddingattribute (set_padding()).
-
~DocraftLoomLayoutContainer() override = default
DocraftLoomRectangle
<Rectangle> — a DocraftLoomLayoutContainer with explicit
width/height that stacks children top-to-bottom, plus
background/border/padding (via DocraftLoomShape, see Shapes).
-
class DocraftLoomRectangle : public docraft::loom::nodes::DocraftLoomLayoutContainer
Represents a rectangular node in the Docraft Loom structure.
DocraftLoomRectangleis a specific implementation ofDocraftLoomLayoutContainerthat encapsulates properties and behavior for a rectangular shape. It supports visitor-based operations, background/border styling and inter-child spacing (both inherited from DocraftLoomLayoutContainer), and can hold children (inherited from DocraftLoomNode) that are laid out inside it, padded by padding() (also inherited from DocraftLoomNode — every node type has it, not just Rectangle).Subclassed by docraft::loom::nodes::DocraftLoomCanvas
DocraftLoomVStack / DocraftLoomHStack
<Layout orientation="vertical"|"horizontal"> — stack children top-to-
bottom or left-to-right. Only HStack supports weights (proportional
column sizing); a vertical Layout’s weights attribute is a no-op.
-
class DocraftLoomVStack : public docraft::loom::nodes::DocraftLoomLayoutContainer
Loom node that stacks its children vertically (top-to-bottom).
During the measure pass, width = max child width and height = sum of child heights plus spacing between each pair of adjacent children (both
spacing()and the optional background/borderstyle()are inherited from DocraftLoomLayoutContainer — this lets<Header>/<Body>/<Footer>sections, built as a DocraftLoomVStack by DocraftLoomTreeBuilder::build_section, keep supporting background_color/border_color/border_width without needing DocraftLoomRectangle’s unrelated width_/height_/padding_ fields). During the layout pass, each child is placed below the previous one.Public Functions
-
DocraftLoomVStack()
-
~DocraftLoomVStack() override = default
-
virtual void accept(interfaces::DocraftLoomIVisitor &visitor) override
-
DocraftLoomVStack()
-
class DocraftLoomHStack : public docraft::loom::nodes::DocraftLoomLayoutContainer
Loom node that places its children horizontally (left-to-right).
During the measure pass, height = max child height and width = sum of child widths plus spacing between each pair of adjacent children (both
spacing()and the optional background/borderstyle()are inherited from DocraftLoomLayoutContainer). During the layout pass, each child is placed to the right of the previous one, using its own natural width — unless weights() is non-empty, in which case the available content width is instead divided among children by weight (missing/non-positive entries default to 1.0, i.e. equal/homogeneous division), mirroring DocraftLoomTable’s column_weights.Public Functions
-
DocraftLoomHStack()
-
~DocraftLoomHStack() override = default
-
virtual void accept(interfaces::DocraftLoomIVisitor &visitor) override
-
void set_weights(std::vector<float> weights)
Sets per-child weights used to divide the available content width.
Empty (the default) keeps today’s shrink-to-fit behavior, where each child simply gets its own natural width.
-
const std::vector<float> &weights() const
-
std::vector<float> resolve_horizontal_child_gaps(const DocraftLoomNode &node, float container_spacing)
DocraftLoomLayoutContainer::resolve_child_gap() applied to every adjacent pair of node’s children at once, using each child’s right/left margin (this is the horizontal-stacking counterpart of the vertical, per-pair calls Rectangle/VStack make directly)
-
virtual float resolve_outer_margin(const DocraftLoomNode &node, bool leading) override
The gap this container must reserve outside its first (leading=true) or last (leading=false) child — that child’s own edge margin, with no sibling on that side to combine it with via resolve_child_gap().
0 if node has no children. Default reads the vertical edges (top/bottom), shared as-is by Rectangle/VStack; DocraftLoomHStack overrides this with its own (left/right) edges.
-
DocraftLoomHStack()
DocraftLoomTable / DocraftLoomTableCell
<Table> — a grid of cells with column weights, optional per-cell
background, title cells (painted last, over dividers), and
split_after_row() support for pagination across pages. Cell content must
be DocraftLoomText or DocraftLoomImage; absolute positioning inside a
cell throws InvalidInputException at layout time.
-
class DocraftLoomTable : public docraft::loom::nodes::DocraftLoomNode
Table node: a grid of DocraftLoomTableCell nodes.
There is no separate “header row/column” concept — a cell may be marked is_title() (a property on the cell itself), which only affects draw order in Rendering (title cells paint last, over any dividers). Measure/Layout treat every cell uniformly regardless of is_title — this is what lets the same table represent both a “column headers” (title row) and a “row labels” (title column) shape with one code path.
Column-width resolution is computed by the layout engine (DocraftLoomLayoutProcessor) and written into each cell’s own frame.size — the table itself holds no separate width/height cache, mirroring how every other container (VStack/HStack/Rectangle) derives its rendering geometry from its children’s own LayoutBox rather than a parallel cache.
Public Functions
-
DocraftLoomTable() = default
-
~DocraftLoomTable() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
- Throws:
exception::InvalidInputException – if any cell is null, or if the row’s column count doesn’t match previously added rows.
-
int row_count() const
-
int column_count() const
-
std::shared_ptr<DocraftLoomTableCell> cell(int row, int column) const
-
void set_column_weights(std::vector<float> weights)
-
const std::vector<float> &column_weights() const
-
void set_default_cell_background(const DocraftColor &color)
-
const std::optional<DocraftColor> &default_cell_background() const
-
float baseline_offset() const
-
void set_baseline_offset(float baseline_offset)
-
float padding() const
Outer spacing around the whole table (all four sides), mirroring how DocraftLoomRectangle’s own padding() insets its children from its border — here there’s no border to speak of, so it simply keeps the grid from sitting flush against whatever precedes/follows the table (or the page edge).
Wired to the common
paddingcraft-language attribute like any other node that exposes a setter (see DocraftLoomTreeBuilder::apply_common_attributes).
-
void set_padding(float padding)
-
std::shared_ptr<DocraftLoomTable> split_after_row(int row_index, bool repeat_header_rows = true)
Splits off rows [row_index, row_count()) into a new table (mirrors legacy’s DocraftTable::split_after_row), for use when a table doesn’t fit on one page.
Column weights/default background/baseline offset are copied onto the remainder. If repeat_header_rows is true, a leading contiguous run of all-title rows in this table (before row_index) is cloned onto the front of the remainder too, so a header row keeps repeating on the continuation.
- Returns:
The remainder table, or nullptr if row_index is 0 or >= row_count() (nothing to split).
-
DocraftLoomTable() = default
-
class DocraftLoomTableCell : public docraft::loom::nodes::DocraftLoomNode
A single table cell — a node like any other.
Its content is just its child (set via set_content(), which enforces the same Text/Image-only restriction as legacy tables). background/explicit_width/is_title are properties of the cell itself, not of the table.
Public Functions
-
DocraftLoomTableCell() = default
-
~DocraftLoomTableCell() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
Sets the cell’s content, replacing any existing child.
- Throws:
exception::InvalidInputException – if content is neither a DocraftLoomText nor a DocraftLoomImage.
-
std::shared_ptr<DocraftLoomNode> content()
-
const std::optional<DocraftColor> &background() const
-
void set_background(const DocraftColor &color)
-
std::optional<float> explicit_width() const
-
void set_explicit_width(float width)
-
bool is_title() const
-
void set_is_title(bool is_title)
-
DocraftLoomTableCell() = default
DocraftLoomList
<List>/<UList> — ordered (number/roman) or unordered (dash/star/
circle/box) markers; items are plain DocraftLoomText children.
-
class DocraftLoomList : public docraft::loom::nodes::DocraftLoomNode
Loom node representing a list.
Items are DocraftLoomText children (added via the inherited add_child()); each item gets a Marker computed during Measure (text/kind/width) and positioned during Layout.
Public Functions
-
DocraftLoomList() = default
-
~DocraftLoomList() override = default
-
virtual void accept(loom::interfaces::DocraftLoomIVisitor &visitor) override
-
OrderedListStyle ordered_style() const
-
void set_ordered_style(OrderedListStyle style)
-
UnorderedListDot unordered_dot() const
-
void set_unordered_dot(UnorderedListDot dot)
-
bool marker_is_box() const
-
std::string marker_text_for_index(int index) const
Returns the marker text for a 0-based item index (e.g.
“1.”, “IV.”, “*”), mirroring legacy’s DocraftList::prefix_for_index/roman_for. Box markers have no text (an empty string), the box itself is drawn instead.
-
float marker_gap() const
-
struct Marker
-
DocraftLoomList() = default