Skip to content

NodeTreeView

@Composable



fun <T> NodeTreeView(
    roots: List<T>, 
    children: (T) -> List<T>, 
    parents: (T) -> List<T>? = null, 
    modifier: Modifier = Modifier, 
    columnGap: Int = DEFAULT_COLUMN_GAP, 
    rowGap: Int = DEFAULT_ROW_GAP, 
    connectorStyle: (T) -> ConnectorStyle = { ConnectorStyle.SOLID }, 
    connectorColor: (T) -> Int = { DEFAULT_LINE_COLOR }, 
    connectorThickness: (T) -> Int = { DEFAULT_LINE_THICKNESS }, 
    connectorAnimation: (T) -> ConnectorAnimation = { ConnectorAnimation.NONE }, 
    connectorShape: (T) -> ConnectorShape = { ConnectorShape.ELBOW }, 
    connectorDirection: (T) -> ConnectorDirection = { ConnectorDirection.CHILD_TO_PARENT }, 
    nodeAnimation: (T) -> NodeAnimation = { NodeAnimation.NONE }, 
    rootAlignment: RootAlignment = RootAlignment.START, 
    dedupe: Boolean = false, 
    showNodeHints: Boolean = true, 
    key: (T) -> Any? = { it }, 
    visible: (T) -> Boolean = { true }, 
    onVisibilityChanged: (T, Boolean) -> Unit = { _, _ -> }, 
    state: PannableCanvasState = rememberPannableCanvasState(), 
    backgroundTexture: ResourceLocation? = null, 
    backgroundTextureSize: Int = 32, 
    backgroundTint: Int = -1, 
    backgroundParallax: Float = 1.0f, 
    panelTexture: String = "surface", 
    panelVariant: String? = "inset_transparent", 
    panelContentPadding: Int = 2, 
    content: @Composable



 (T) -> Unit
)

A pannable, zoomable, node-based forest view - one or more independent trees (roots, plural), one column per depth, tidily packed rows connected by elbow connectors (see assignRows). children extracts a node's own children; content draws one node's own visual content at its measured natural size. Connectors draw (and, for ConnectorStyle.ARROW/ ConnectorAnimation.FLOWING, animate) child-to-parent by default - see connectorDirection to flip that - and route around, never behind, whatever else sits between their two ends (see pointsFor). Draws a genuine tree by default (a node reachable through more than one parent is drawn once per parent); pass dedupe = true (with key) to collapse repeats into one visual node with multiple incoming connectors instead.

Parameters

  • parents: An optional second way to declare an edge, alongside children - a caller whose own domain model tracks both directions can declare a node's extra prerequisites from that node itself. Only adds connectors between nodes children already reached from roots; a declared parent it never reaches draws as a short dead-end stub instead of being dropped.

  • connectorStyle: Chooses a ConnectorStyle per node, keyed by the child end of its own connector to its parent. Overridden by ConnectorStyle.DISCONNECTED whenever either end is rejected by visible.

  • connectorColor: /connectorThickness Per-node color/thickness for that node's connector.

  • connectorAnimation: Per-node ConnectorAnimation, layered on top of connectorStyle.

  • connectorShape: Per-node ConnectorShape - the connector's path shape, orthogonal to connectorStyle/connectorAnimation.

  • connectorDirection: Per-node ConnectorDirection - which end the arrowhead/flow lands on.

  • nodeAnimation: Per-node NodeAnimation tinting that node's own content directly.

  • rootAlignment: Which side of the drawn forest every tree's root sits on.

  • visible: A node this rejects is still fully discovered/measured/laid out, so the forest's shape never shifts depending on what's hidden - see layoutForest. Its content never draws, and every connector touching it draws as ConnectorStyle.DISCONNECTED instead.

  • onVisibilityChanged: Called whenever a node already admitted by visible scrolls into or out of the current pan/zoom viewport - a screen-space fact, unrelated to tree membership.

  • state: Exposed so a caller can reset/center the pan/zoom itself.

  • backgroundParallax: /backgroundTint/panelTexture/panelVariant/panelContentPadding Forwarded straight to PannableCanvas's own parameters of the same names.

@Composable



fun <K : Any, V, B : NodeBuilder<K, V, B>> NodeTreeView(
    roots: TreeRegistry<K, V, B>, 
    modifier: Modifier = Modifier, 
    columnGap: Int = 26, 
    rowGap: Int = 6, 
    connectorStyle: (TreeNode<K, V>) -> ConnectorStyle = { ConnectorStyle.SOLID }, 
    connectorColor: (TreeNode<K, V>) -> Int = { 0xFF808080.toInt() }, 
    connectorThickness: (TreeNode<K, V>) -> Int = { 1 }, 
    connectorAnimation: (TreeNode<K, V>) -> ConnectorAnimation = { ConnectorAnimation.NONE }, 
    connectorShape: (TreeNode<K, V>) -> ConnectorShape = { ConnectorShape.ELBOW }, 
    connectorDirection: (TreeNode<K, V>) -> ConnectorDirection = { ConnectorDirection.CHILD_TO_PARENT }, 
    nodeAnimation: (TreeNode<K, V>) -> NodeAnimation = { NodeAnimation.NONE }, 
    rootAlignment: RootAlignment = RootAlignment.START, 
    visible: (TreeNode<K, V>) -> Boolean = { true }, 
    onVisibilityChanged: (TreeNode<K, V>, Boolean) -> Unit = { _, _ -> }, 
    state: PannableCanvasState = rememberPannableCanvasState(), 
    backgroundTexture: ResourceLocation? = null, 
    backgroundTextureSize: Int = 32, 
    backgroundTint: Int = -1, 
    backgroundParallax: Float = 1.0f, 
    panelTexture: String = "surface", 
    panelVariant: String? = "inset_transparent", 
    panelContentPadding: Int = 5, 
    content: @Composable



 (TreeNode<K, V>) -> Unit
)

NodeTreeView's general-purpose List<T>/children:/parents:/key: surface, specialized for a forest already built as a TreeRegistry. Every structural parameter reads TreeNode.children/ TreeNode.parents/TreeNode.id directly; a caller only supplies cosmetic per-node callbacks and content. dedupe is always on, since a TreeNode's own id already identifies it.

Parameters