Problem
The TreeLayout(com.top_logic.graphic.flow.data.TreeLayout) packs all children of a node into a single vertical column by default. With a high fan-out (many children under a parent node), the diagram height grows linearly with the number of children, so that the diagram can no longer be displayed on normal screens.
Solution: New configuration options on `TreeLayout
The following new properties are available on the TreeLayout (both in the TreeLayout configuration and as parameters of the TL script function tree(...)):
General
- childSplitThreshold(int, default 0): Threshold above which the children of a parent node are distributed into a 2D sub-grid instead of a single column. 0 deactivates the sub-grid mode (legacy behavior, one column per parent node).
- rowWise(boolean, default false): Selects between the two sub-grid algorithms. See below for the effect of both algorithms.
== Column-wise (default, rowWise=false) ==
Children are distributed column-major into a grid of R = childSplitThreshold rows × C = ⌈M/R⌉ sub-columns. Each sub-column has its own vertical bus; subsequent column buses are attached to the primary bus via a common bottom bridge.
- bridgeGapY(double, default 20): Y distance between the lowest sub-column bottom and the common bottom bridge. Only relevant in column-wise.
Property: Very compact. However, sub-tree nodes of a sub-grid child liebetween the sub-columns, so that the depth↔X correspondence ("nodes of the same depth lie at the same X position") is no longer maintained for depth ≥ 2.
== Row-wise(rowWise=true) ==
Children are distributed row-major to C sub-columns. The sub-grid contains only the direct children; all subtrees of the sub-grid children are moved toa common subsequent column to the right of the sub-grid(postGridX). There is exactly one vertical bus to the right outside the sub-grid, which carries both the parent→sub-grid-child connections and the sub-grid-child→subtree connections.
Additional options:
- subGridCols(int, default 0): Number of sub-columns in the row-wise sub-grid. If 0`, `childSplitThreshold is used as the sub-column number.
- subGridStartCol(int, default 0): Sub-column in which the first child (index 0) ends up; child n ends up in sub-column (n + subGridStartCol) mod C. Values outside [0, C-1] are normalized modulo C. Useful, for example, to leave the upper left sub-grid cell free for a better visual balance with the parent node.
Property: depth↔X is maintained from depth 2 - all subtree nodes (grandchildren of parent nodes) are at the same X position postGridX, depth-3 nodes further to the right, etc. Adaptive Y-stack: each child is packed as densely as possible, preserving box clearance, bus stub clearance, non-overlap of subgrid child→subtree stems with later subgrid boxes, and non-overlap of bus segments at the common childBusX. Anchor position in the node is taken into account(anchor.y, anchor.height), so that nodes with label-above-anchor (e.g. label above smaller symbol) are handled correctly: the parent stem hits the anchor right edge (not the box right edge), and the stub clearance uses the anchor centerline (not the box centerline).
When which variant?
- column-wise: For diagrams with uniform nodes and compact layout requirements; no requirement for depth↔X consistency.
- row-wise: For diagrams in which "grandchildren" nodes are to be clearly assigned to a depth level; for nodes with labels above the anchor area; if only a single bus to the right of the parent area is required.
Implementation (internal)
- data.proto: Properties childSplitThreshold, rowWise, subGridCols, subGridStartCol, bridgeGapY an TreeLayout.
- TreeRenderInfo: Bottom-up layout per subtree; GridInfo with child enum(COLUMN_WISE / ROW_WISE). For row-wise additionally postGridX, childBusX, adaptive Y-constraints (stub clearance, same-column-box clearance, bus-top clearance against prevBusBottom for anchor side and curYPost-Push for grandchild side, past-stem-crossing clearance).
- TreeNode: optional _busXOverride for sub-grid children whose own out connections are redirected to the common childBusX.
- TreeLayoutOperations: drawGridBuses dispatches to drawGridBusesColumnWise (primary bus + bottom bridge + subsequent column buses) or drawGridBusesRowWise (single bus); barPosition uses gi.getBarPositionFor(child) with fallback to _busXOverride. Parent stem now uses the anchor right edge.
- FlowFactory.tree(...): all new options as optional parameters.
Tests
TestTreeLayout (10 tests, all green) covers:
- Linear mode (legacy).
- testGridFanout: column-wise, 12 children, some with subtrees.
- testGridFanoutRowWise: row-wise, same topology.
- testGridFanoutRowWiseVaryingHeight: different node heights.
- testGridFanoutRowWiseLabelAboveAnchor: Box again as anchor; verifies anchor-right-edge connection.
- testGridFanoutRowWiseMixedTallNodeWithSubtree: Bus disjunction at the common childBusX with deep anchor and shallow subsequent subtrees.
- testGridFanoutRowWiseStartCol: subGridStartCol=1 variant.