core/treeview/treeview.go

Functions Structs

Functions

func AccessibilityActions

AccessibilityActions returns the list of supported actions.

func (w *Widget) AccessibilityActions() []a11y.Action {
	return []a11y.Action{a11y.ActionScrollUp, a11y.ActionScrollDown}
}

func AccessibilityHint

AccessibilityHint returns the accessibility hint.

func (w *Widget) AccessibilityHint() string {
	return ""
}

func AccessibilityLabel

AccessibilityLabel returns the accessibility label.

func (w *Widget) AccessibilityLabel() string {
	if w.cfg.a11yLabel != "" {
		return w.cfg.a11yLabel
	}
	return "Tree"
}

func AccessibilityRole

AccessibilityRole returns the ARIA role for this widget.

func (w *Widget) AccessibilityRole() a11y.Role {
	return a11y.RoleTree
}

func AccessibilityState

AccessibilityState returns the current accessibility state.

func (w *Widget) AccessibilityState() a11y.State {
	return a11y.State{
		Disabled: w.cfg.ResolvedDisabled(),
	}
}

func AccessibilityValue

AccessibilityValue returns the current tree state as a string.

func (w *Widget) AccessibilityValue() string {
	selectedID := w.cfg.ResolvedSelectedNodeID()
	root := w.cfg.ResolvedRoot()
	if selectedID != "" && root != nil {
		if node := findNodeByID(root, selectedID); node != nil {
			return fmt.Sprintf("Selected: %s, %d visible rows", node.Label, len(w.rows))
		}
	}
	return fmt.Sprintf("%d visible rows", len(w.rows))
}

func Children

Children returns nil (tree view is a leaf widget with internal rendering).

func (w *Widget) Children() []widget.Widget {
	return nil
}

func CollapseAll

CollapseAll collapses all branch nodes in the tree.

func (w *Widget) CollapseAll() {
	root := w.cfg.ResolvedRoot()
	if root == nil {
		return
	}
	setExpandedAll(root, false)
	w.rebuildRows()
	w.SetNeedsRedraw(true)
}

func Draw

Draw renders the tree view to the canvas.

func (w *Widget) Draw(ctx widget.Context, canvas widget.Canvas) {
	if !w.IsVisible() {
		return
	}

	bounds := w.Bounds()
	if bounds.IsEmpty() {
		return
	}

	// Empty state.
	if len(w.rows) == 0 {
		w.painter.PaintEmptyState(canvas, bounds)
		return
	}

	// Clip to bounds.
	canvas.PushClip(bounds)

	// Determine visible range.
	startIdx, endIdx := w.visibleRange()

	selectedID := w.cfg.ResolvedSelectedNodeID()
	isFocused := w.IsFocused()
	isDisabled := w.cfg.ResolvedDisabled()

	for i := startIdx; i < endIdx; i++ {
		row := w.rows[i]
		rowBounds := w.rowBounds(i, bounds)

		isSelected := selectedID != "" && row.node.ID == selectedID
		isHovered := i == w.hoveredIndex

		rowState := RowPaintState{
			Bounds:		rowBounds,
			Node:		row.node,
			Depth:		row.depth,
			Selected:	isSelected,
			Focused:	isFocused && isSelected,
			Hovered:	isHovered,
			Disabled:	isDisabled,
		}

		// Paint backgrounds.
		w.painter.PaintRowBackground(canvas, rowState)
		w.painter.PaintSelection(canvas, rowState)

		// Paint connector lines if enabled.
		if w.cfg.showLines && row.depth > 0 {
			connState := w.buildConnectorState(i, rowBounds)
			w.painter.PaintConnectorLines(canvas, connState)
		}

		// Paint expand icon for branch nodes.
		if !row.node.IsLeaf() {
			iconBounds := w.expandIconBounds(row.depth, rowBounds)
			w.painter.PaintExpandIcon(canvas, ExpandIconState{
				Bounds:		iconBounds,
				Expanded:	row.node.Expanded,
				Hovered:	isHovered,
			})
		}

		// Paint label.
		labelBounds := w.labelBounds(row.depth, rowBounds)
		w.painter.PaintLabel(canvas, LabelState{
			Bounds:		labelBounds,
			Text:		row.node.Label,
			Selected:	isSelected,
			Disabled:	isDisabled,
		})
	}

	canvas.PopClip()
}

func Event

Event handles an input event and returns true if consumed.

func (w *Widget) Event(ctx widget.Context, e event.Event) bool {
	if !w.IsVisible() || !w.IsEnabled() {
		return false
	}
	return handleEvent(w, ctx, e)
}

func ExpandAll

ExpandAll expands all branch nodes in the tree.

func (w *Widget) ExpandAll() {
	root := w.cfg.ResolvedRoot()
	if root == nil {
		return
	}
	setExpandedAll(root, true)
	w.rebuildRows()
	w.SetNeedsRedraw(true)
}

func InvalidateData

InvalidateData signals that the tree data has changed.

Rebuilds the flattened row list.

func (w *Widget) InvalidateData() {
	w.rebuildRows()
	w.SetNeedsRedraw(true)
}

func IsFocusable

IsFocusable reports whether the tree view can currently receive focus.

func (w *Widget) IsFocusable() bool {
	return w.IsVisible() && w.IsEnabled() && !w.cfg.ResolvedDisabled()
}

func Layout

Layout calculates the tree view's size within the given constraints.

func (w *Widget) Layout(_ widget.Context, constraints geometry.Constraints) geometry.Size {
	// Rebuild rows in case root changed via signal.
	w.rebuildRows()

	size := constraints.BiggestFinite(defaultViewportWidth, defaultViewportHeight)

	// For unconstrained height, use content height clamped to default.
	if constraints.HasInfiniteHeight() {
		totalH := float32(len(w.rows)) * w.cfg.itemHeight
		if totalH > defaultViewportHeight {
			totalH = defaultViewportHeight
		}
		if totalH <= 0 {
			totalH = defaultViewportHeight
		}
		size.Height = totalH
	}

	if size.Width <= 0 {
		size.Width = defaultViewportWidth
	}
	if size.Height <= 0 {
		size.Height = defaultViewportHeight
	}

	w.viewportWidth = size.Width
	w.viewportHeight = size.Height

	return size
}

func Mount

Mount creates signal bindings for push-based invalidation.

Implements [widget.Lifecycle].

func (w *Widget) Mount(ctx widget.Context) {
	sched := ctx.Scheduler()
	if sched == nil {
		return
	}

	// Bind root signals.
	if w.cfg.readonlyRootSignal != nil {
		b := state.BindToScheduler(w.cfg.readonlyRootSignal, w, sched)
		w.AddBinding(b)
	} else if w.cfg.rootSignal != nil {
		b := state.BindToScheduler(w.cfg.rootSignal, w, sched)
		w.AddBinding(b)
	}

	// Bind selected node signals.
	if w.cfg.readonlySelectedNodeSignal != nil {
		b := state.BindToScheduler(w.cfg.readonlySelectedNodeSignal, w, sched)
		w.AddBinding(b)
	} else if w.cfg.selectedNodeIDSignal != nil {
		b := state.BindToScheduler(w.cfg.selectedNodeIDSignal, w, sched)
		w.AddBinding(b)
	}

	// Bind disabled signals.
	if w.cfg.readonlyDisabledSignal != nil {
		b := state.BindToScheduler(w.cfg.readonlyDisabledSignal, w, sched)
		w.AddBinding(b)
	} else if w.cfg.disabledSignal != nil {
		b := state.BindToScheduler(w.cfg.disabledSignal, w, sched)
		w.AddBinding(b)
	}
}

func New

New creates a new tree view Widget with the given options.

 

The returned widget is visible, enabled, and focusable by default.

func New(opts ...Option) *Widget {
	cfg := defaultConfig()
	for _, opt := range opts {
		opt(&cfg)
	}

	w := &Widget{
		cfg:		cfg,
		painter:	DefaultPainter{},
		hoveredIndex:	noHoveredIndex,
	}
	w.SetVisible(true)
	w.SetEnabled(true)

	if w.cfg.painter != nil {
		w.painter = w.cfg.painter
	}

	// Build initial flattened rows.
	w.rebuildRows()

	return w
}

func RowCount

RowCount returns the number of currently visible (flattened) rows.

func (w *Widget) RowCount() int {
	return len(w.rows)
}

func ScrollToNode

ScrollToNode scrolls to make the node with the given ID visible.

If the node is not in the flattened visible list, this is a no-op.

func (w *Widget) ScrollToNode(id string) {
	idx := w.findRowIndex(id)
	if idx < 0 {
		return
	}
	w.scrollToIndex(idx)
}

func Unmount

Unmount is called when the tree view is removed from the widget tree.

Implements [widget.Lifecycle].

func (w *Widget) Unmount() {
	// Bindings are cleaned up automatically by WidgetBase.CleanupBindings().
}

func VisibleRange

VisibleRange returns the indices [start, end) of currently visible rows.

func (w *Widget) VisibleRange() (start, end int) {
	return w.visibleRange()
}

Structs

type Widget struct

Widget implements a hierarchical tree view with expand/collapse per node,

keyboard navigation, virtualization, and pluggable painting.

 

Create with [New] using functional options:

 

tree := treeview.New(

treeview.Root(root),

treeview.ItemHeight(28),

treeview.IndentWidth(20),

treeview.SelectionModeOpt(treeview.SelectionSingle),

treeview.OnSelect(func(node *treeview.TreeNode) { ... }),

)

type Widget struct {
	widget.WidgetBase
	cfg	config
	painter	Painter

	// Flattened visible rows (rebuilt on expand/collapse).
	rows	[]flatRow

	// Viewport state for virtualization.
	scrollY		float32
	viewportWidth	float32
	viewportHeight	float32

	// Interaction state.
	hoveredIndex	int
}