gemini-browser

A text-based gemini browser
git clone git://git.laack.co/gemini-browser.git
Log | Files | Refs | README

treeview.go (26072B)


      1 package tview
      2 
      3 import (
      4 	"github.com/gdamore/tcell/v2"
      5 )
      6 
      7 // Tree navigation events.
      8 const (
      9 	treeNone int = iota
     10 	treeHome
     11 	treeEnd
     12 	treeMove
     13 	treeParent
     14 	treeChild
     15 	treeScroll // Move without changing the selection, even when off screen.
     16 )
     17 
     18 // TreeNode represents one node in a tree view.
     19 type TreeNode struct {
     20 	// The reference object.
     21 	reference interface{}
     22 
     23 	// This node's child nodes.
     24 	children []*TreeNode
     25 
     26 	// The item's text.
     27 	text string
     28 
     29 	// The text style.
     30 	textStyle tcell.Style
     31 
     32 	// The style of selected text.
     33 	selectedTextStyle tcell.Style
     34 
     35 	// Whether or not this node can be selected.
     36 	selectable bool
     37 
     38 	// Whether or not this node's children should be displayed.
     39 	expanded bool
     40 
     41 	// The additional horizontal indent of this node's text.
     42 	indent int
     43 
     44 	// An optional function which is called when the user selects this node.
     45 	selected func()
     46 
     47 	// The hierarchy level (0 for the root, 1 for its children, and so on). This
     48 	// is only up to date immediately after a call to process() (e.g. via
     49 	// Draw()).
     50 	level int
     51 
     52 	// Temporary member variables.
     53 	parent    *TreeNode // The parent node (nil for the root).
     54 	graphicsX int       // The x-coordinate of the left-most graphics rune.
     55 	textX     int       // The x-coordinate of the first rune of the text.
     56 }
     57 
     58 // NewTreeNode returns a new tree node.
     59 func NewTreeNode(text string) *TreeNode {
     60 	return &TreeNode{
     61 		text:              text,
     62 		textStyle:         tcell.StyleDefault.Foreground(Styles.PrimaryTextColor).Background(Styles.PrimitiveBackgroundColor),
     63 		selectedTextStyle: tcell.StyleDefault.Foreground(Styles.PrimitiveBackgroundColor).Background(Styles.PrimaryTextColor),
     64 		indent:            2,
     65 		expanded:          true,
     66 		selectable:        true,
     67 	}
     68 }
     69 
     70 // Walk traverses this node's subtree in depth-first, pre-order (NLR) order and
     71 // calls the provided callback function on each traversed node (which includes
     72 // this node) with the traversed node and its parent node (nil for this node).
     73 // The callback returns whether traversal should continue with the traversed
     74 // node's child nodes (true) or not recurse any deeper (false).
     75 func (n *TreeNode) Walk(callback func(node, parent *TreeNode) bool) *TreeNode {
     76 	n.parent = nil
     77 	nodes := []*TreeNode{n}
     78 	for len(nodes) > 0 {
     79 		// Pop the top node and process it.
     80 		node := nodes[len(nodes)-1]
     81 		nodes = nodes[:len(nodes)-1]
     82 		if !callback(node, node.parent) {
     83 			// Don't add any children.
     84 			continue
     85 		}
     86 
     87 		// Add children in reverse order.
     88 		for index := len(node.children) - 1; index >= 0; index-- {
     89 			node.children[index].parent = node
     90 			nodes = append(nodes, node.children[index])
     91 		}
     92 	}
     93 
     94 	return n
     95 }
     96 
     97 // SetReference allows you to store a reference of any type in this node. This
     98 // will allow you to establish a mapping between the TreeView hierarchy and your
     99 // internal tree structure.
    100 func (n *TreeNode) SetReference(reference interface{}) *TreeNode {
    101 	n.reference = reference
    102 	return n
    103 }
    104 
    105 // GetReference returns this node's reference object.
    106 func (n *TreeNode) GetReference() interface{} {
    107 	return n.reference
    108 }
    109 
    110 // SetChildren sets this node's child nodes.
    111 func (n *TreeNode) SetChildren(childNodes []*TreeNode) *TreeNode {
    112 	n.children = childNodes
    113 	return n
    114 }
    115 
    116 // GetText returns this node's text.
    117 func (n *TreeNode) GetText() string {
    118 	return n.text
    119 }
    120 
    121 // GetChildren returns this node's children.
    122 func (n *TreeNode) GetChildren() []*TreeNode {
    123 	return n.children
    124 }
    125 
    126 // ClearChildren removes all child nodes from this node.
    127 func (n *TreeNode) ClearChildren() *TreeNode {
    128 	n.children = nil
    129 	return n
    130 }
    131 
    132 // AddChild adds a new child node to this node.
    133 func (n *TreeNode) AddChild(node *TreeNode) *TreeNode {
    134 	n.children = append(n.children, node)
    135 	return n
    136 }
    137 
    138 // RemoveChild removes a child node from this node. If the child node cannot be
    139 // found, nothing happens.
    140 func (n *TreeNode) RemoveChild(node *TreeNode) *TreeNode {
    141 	for index, child := range n.children {
    142 		if child == node {
    143 			n.children = append(n.children[:index], n.children[index+1:]...)
    144 			break
    145 		}
    146 	}
    147 	return n
    148 }
    149 
    150 // SetSelectable sets a flag indicating whether this node can be selected by
    151 // the user.
    152 func (n *TreeNode) SetSelectable(selectable bool) *TreeNode {
    153 	n.selectable = selectable
    154 	return n
    155 }
    156 
    157 // SetSelectedFunc sets a function which is called when the user selects this
    158 // node by hitting Enter when it is selected.
    159 func (n *TreeNode) SetSelectedFunc(handler func()) *TreeNode {
    160 	n.selected = handler
    161 	return n
    162 }
    163 
    164 // SetExpanded sets whether or not this node's child nodes should be displayed.
    165 func (n *TreeNode) SetExpanded(expanded bool) *TreeNode {
    166 	n.expanded = expanded
    167 	return n
    168 }
    169 
    170 // Expand makes the child nodes of this node appear.
    171 func (n *TreeNode) Expand() *TreeNode {
    172 	n.expanded = true
    173 	return n
    174 }
    175 
    176 // Collapse makes the child nodes of this node disappear.
    177 func (n *TreeNode) Collapse() *TreeNode {
    178 	n.expanded = false
    179 	return n
    180 }
    181 
    182 // ExpandAll expands this node and all descendent nodes.
    183 func (n *TreeNode) ExpandAll() *TreeNode {
    184 	n.Walk(func(node, parent *TreeNode) bool {
    185 		node.expanded = true
    186 		return true
    187 	})
    188 	return n
    189 }
    190 
    191 // CollapseAll collapses this node and all descendent nodes.
    192 func (n *TreeNode) CollapseAll() *TreeNode {
    193 	n.Walk(func(node, parent *TreeNode) bool {
    194 		node.expanded = false
    195 		return true
    196 	})
    197 	return n
    198 }
    199 
    200 // IsExpanded returns whether the child nodes of this node are visible.
    201 func (n *TreeNode) IsExpanded() bool {
    202 	return n.expanded
    203 }
    204 
    205 // SetText sets the node's text which is displayed.
    206 func (n *TreeNode) SetText(text string) *TreeNode {
    207 	n.text = text
    208 	return n
    209 }
    210 
    211 // GetColor returns the node's text color.
    212 func (n *TreeNode) GetColor() tcell.Color {
    213 	color, _, _ := n.textStyle.Decompose()
    214 	return color
    215 }
    216 
    217 // SetColor sets the node's text color. For compatibility reasons, this also
    218 // sets the background color of the selected text style. For more control over
    219 // styles, use [TreeNode.SetTextStyle] and [TreeNode.SetSelectedTextStyle].
    220 func (n *TreeNode) SetColor(color tcell.Color) *TreeNode {
    221 	n.textStyle = n.textStyle.Foreground(color)
    222 	n.selectedTextStyle = n.selectedTextStyle.Background(color)
    223 	return n
    224 }
    225 
    226 // SetTextStyle sets the text style for this node.
    227 func (n *TreeNode) SetTextStyle(style tcell.Style) *TreeNode {
    228 	n.textStyle = style
    229 	return n
    230 }
    231 
    232 // GetTextStyle returns the text style for this node.
    233 func (n *TreeNode) GetTextStyle() tcell.Style {
    234 	return n.textStyle
    235 }
    236 
    237 // SetSelectedTextStyle sets the text style for this node when it is selected.
    238 func (n *TreeNode) SetSelectedTextStyle(style tcell.Style) *TreeNode {
    239 	n.selectedTextStyle = style
    240 	return n
    241 }
    242 
    243 // GetSelectedTextStyle returns the text style for this node when it is
    244 // selected.
    245 func (n *TreeNode) GetSelectedTextStyle() tcell.Style {
    246 	return n.selectedTextStyle
    247 }
    248 
    249 // SetIndent sets an additional indentation for this node's text. A value of 0
    250 // keeps the text as far left as possible with a minimum of line graphics. Any
    251 // value greater than that moves the text to the right.
    252 func (n *TreeNode) SetIndent(indent int) *TreeNode {
    253 	n.indent = indent
    254 	return n
    255 }
    256 
    257 // GetLevel returns the node's level within the hierarchy, where 0 corresponds
    258 // to the root node, 1 corresponds to its children, and so on. This is only
    259 // guaranteed to be up to date immediately after the tree that contains this
    260 // node is drawn.
    261 func (n *TreeNode) GetLevel() int {
    262 	return n.level
    263 }
    264 
    265 // TreeView displays tree structures. A tree consists of nodes (TreeNode
    266 // objects) where each node has zero or more child nodes and exactly one parent
    267 // node (except for the root node which has no parent node).
    268 //
    269 // The SetRoot() function is used to specify the root of the tree. Other nodes
    270 // are added locally to the root node or any of its descendents. See the
    271 // TreeNode documentation for details on node attributes. (You can use
    272 // SetReference() to store a reference to nodes of your own tree structure.)
    273 //
    274 // Nodes can be selected by calling SetCurrentNode(). The user can navigate the
    275 // selection or the tree by using the following keys:
    276 //
    277 //   - j, down arrow, right arrow: Move (the selection) down by one node.
    278 //   - k, up arrow, left arrow: Move (the selection) up by one node.
    279 //   - g, home: Move (the selection) to the top.
    280 //   - G, end: Move (the selection) to the bottom.
    281 //   - J: Move (the selection) up one level (if that node is selectable).
    282 //   - K: Move (the selection) to the last node one level down (if any).
    283 //   - Ctrl-F, page down: Move (the selection) down by one page.
    284 //   - Ctrl-B, page up: Move (the selection) up by one page.
    285 //
    286 // Selected nodes can trigger the "selected" callback when the user hits Enter.
    287 //
    288 // The root node corresponds to level 0, its children correspond to level 1,
    289 // their children to level 2, and so on. Per default, the first level that is
    290 // displayed is 0, i.e. the root node. You can call SetTopLevel() to hide
    291 // levels.
    292 //
    293 // If graphics are turned on (see SetGraphics()), lines indicate the tree's
    294 // hierarchy. Alternative (or additionally), you can set different prefixes
    295 // using SetPrefixes() for different levels, for example to display hierarchical
    296 // bullet point lists.
    297 //
    298 // See https://github.com/rivo/tview/wiki/TreeView for an example.
    299 type TreeView struct {
    300 	*Box
    301 
    302 	// The root node.
    303 	root *TreeNode
    304 
    305 	// The currently selected node or nil if no node is selected.
    306 	currentNode *TreeNode
    307 
    308 	// The last note that was selected or nil of there is no such node.
    309 	lastNode *TreeNode
    310 
    311 	// The movement to be performed during the call to Draw(), one of the
    312 	// constants defined above.
    313 	movement int
    314 
    315 	// The number of nodes to move down or up, when movement is treeMove,
    316 	// excluding non-selectable nodes for selection movement, including them for
    317 	// scrolling.
    318 	step int
    319 
    320 	// The top hierarchical level shown. (0 corresponds to the root level.)
    321 	topLevel int
    322 
    323 	// Strings drawn before the nodes, based on their level.
    324 	prefixes []string
    325 
    326 	// Vertical scroll offset.
    327 	offsetY int
    328 
    329 	// If set to true, all node texts will be aligned horizontally.
    330 	align bool
    331 
    332 	// If set to true, the tree structure is drawn using lines.
    333 	graphics bool
    334 
    335 	// The color of the lines.
    336 	graphicsColor tcell.Color
    337 
    338 	// An optional function which is called when the user has navigated to a new
    339 	// tree node.
    340 	changed func(node *TreeNode)
    341 
    342 	// An optional function which is called when a tree item was selected.
    343 	selected func(node *TreeNode)
    344 
    345 	// An optional function which is called when the user moves away from this
    346 	// primitive.
    347 	done func(key tcell.Key)
    348 
    349 	// The visible nodes, top-down, as set by process().
    350 	nodes []*TreeNode
    351 
    352 	// Temporarily set to true while we know that the tree has not changed and
    353 	// therefore does not need to be reprocessed.
    354 	stableNodes bool
    355 }
    356 
    357 // NewTreeView returns a new tree view.
    358 func NewTreeView() *TreeView {
    359 	return &TreeView{
    360 		Box:           NewBox(),
    361 		graphics:      true,
    362 		graphicsColor: Styles.GraphicsColor,
    363 	}
    364 }
    365 
    366 // SetRoot sets the root node of the tree.
    367 func (t *TreeView) SetRoot(root *TreeNode) *TreeView {
    368 	t.root = root
    369 	return t
    370 }
    371 
    372 // GetRoot returns the root node of the tree. If no such node was previously
    373 // set, nil is returned.
    374 func (t *TreeView) GetRoot() *TreeNode {
    375 	return t.root
    376 }
    377 
    378 // SetCurrentNode sets the currently selected node. Provide nil to clear all
    379 // selections. Selected nodes must be visible and selectable, or else the
    380 // selection will be changed to the top-most selectable and visible node.
    381 //
    382 // This function does NOT trigger the "changed" callback because the actual node
    383 // that will be selected is not known until the tree is drawn. Triggering the
    384 // "changed" callback is thus deferred until the next call to [TreeView.Draw].
    385 func (t *TreeView) SetCurrentNode(node *TreeNode) *TreeView {
    386 	t.currentNode = node
    387 	return t
    388 }
    389 
    390 // GetCurrentNode returns the currently selected node or nil of no node is
    391 // currently selected.
    392 func (t *TreeView) GetCurrentNode() *TreeNode {
    393 	return t.currentNode
    394 }
    395 
    396 // GetPath returns all nodes located on the path from the root to the given
    397 // node, including the root and the node itself. If there is no root node, nil
    398 // is returned. If there are multiple paths to the node, a random one is chosen
    399 // and returned.
    400 func (t *TreeView) GetPath(node *TreeNode) []*TreeNode {
    401 	if t.root == nil {
    402 		return nil
    403 	}
    404 
    405 	var f func(current *TreeNode, path []*TreeNode) []*TreeNode
    406 	f = func(current *TreeNode, path []*TreeNode) []*TreeNode {
    407 		if current == node {
    408 			return path
    409 		}
    410 
    411 		for _, child := range current.children {
    412 			newPath := make([]*TreeNode, len(path), len(path)+1)
    413 			copy(newPath, path)
    414 			if p := f(child, append(newPath, child)); p != nil {
    415 				return p
    416 			}
    417 		}
    418 
    419 		return nil
    420 	}
    421 
    422 	return f(t.root, []*TreeNode{t.root})
    423 }
    424 
    425 // SetTopLevel sets the first tree level that is visible with 0 referring to the
    426 // root, 1 to the root's child nodes, and so on. Nodes above the top level are
    427 // not displayed.
    428 func (t *TreeView) SetTopLevel(topLevel int) *TreeView {
    429 	t.topLevel = topLevel
    430 	return t
    431 }
    432 
    433 // SetPrefixes defines the strings drawn before the nodes' texts. This is a
    434 // slice of strings where each element corresponds to a node's hierarchy level,
    435 // i.e. 0 for the root, 1 for the root's children, and so on (levels will
    436 // cycle).
    437 //
    438 // For example, to display a hierarchical list with bullet points:
    439 //
    440 //	treeView.SetGraphics(false).
    441 //	  SetPrefixes([]string{"* ", "- ", "x "})
    442 //
    443 // Deeper levels will cycle through the prefixes.
    444 func (t *TreeView) SetPrefixes(prefixes []string) *TreeView {
    445 	t.prefixes = prefixes
    446 	return t
    447 }
    448 
    449 // SetAlign controls the horizontal alignment of the node texts. If set to true,
    450 // all texts except that of top-level nodes will be placed in the same column.
    451 // If set to false, they will indent with the hierarchy.
    452 func (t *TreeView) SetAlign(align bool) *TreeView {
    453 	t.align = align
    454 	return t
    455 }
    456 
    457 // SetGraphics sets a flag which determines whether or not line graphics are
    458 // drawn to illustrate the tree's hierarchy.
    459 func (t *TreeView) SetGraphics(showGraphics bool) *TreeView {
    460 	t.graphics = showGraphics
    461 	return t
    462 }
    463 
    464 // SetGraphicsColor sets the colors of the lines used to draw the tree structure.
    465 func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView {
    466 	t.graphicsColor = color
    467 	return t
    468 }
    469 
    470 // SetChangedFunc sets the function which is called when the currently selected
    471 // node changes, for example when the user navigates to a new tree node.
    472 func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView {
    473 	t.changed = handler
    474 	return t
    475 }
    476 
    477 // SetSelectedFunc sets the function which is called when the user selects a
    478 // node by pressing Enter on the current selection.
    479 func (t *TreeView) SetSelectedFunc(handler func(node *TreeNode)) *TreeView {
    480 	t.selected = handler
    481 	return t
    482 }
    483 
    484 // GetSelectedFunc returns the function set with [TreeView.SetSelectedFunc]
    485 // or nil if no such function has been set.
    486 func (t *TreeView) GetSelectedFunc() func(node *TreeNode) {
    487 	return t.selected
    488 }
    489 
    490 // SetDoneFunc sets a handler which is called whenever the user presses the
    491 // Escape, Tab, or Backtab key.
    492 func (t *TreeView) SetDoneFunc(handler func(key tcell.Key)) *TreeView {
    493 	t.done = handler
    494 	return t
    495 }
    496 
    497 // GetScrollOffset returns the number of node rows that were skipped at the top
    498 // of the tree view. Note that when the user navigates the tree view, this value
    499 // is only updated after the tree view has been redrawn.
    500 func (t *TreeView) GetScrollOffset() int {
    501 	return t.offsetY
    502 }
    503 
    504 // GetRowCount returns the number of "visible" nodes. This includes nodes which
    505 // fall outside the tree view's box but notably does not include the children
    506 // of collapsed nodes. Note that this value is only up to date after the tree
    507 // view has been drawn.
    508 func (t *TreeView) GetRowCount() int {
    509 	return len(t.nodes)
    510 }
    511 
    512 // Move moves the selection (if a node is currently selected) or scrolls the
    513 // tree view (if there is no selection), by the given offset (positive values to
    514 // move/scroll down, negative values to move/scroll up). For selection changes,
    515 // the offset refers to the number selectable, visible nodes. For scrolling, the
    516 // offset refers to the number of visible nodes.
    517 //
    518 // If the offset is 0, nothing happens.
    519 func (t *TreeView) Move(offset int) *TreeView {
    520 	if offset == 0 {
    521 		return t
    522 	}
    523 	t.movement = treeMove
    524 	t.step = offset
    525 	t.process(false)
    526 	return t
    527 }
    528 
    529 // process builds the visible tree, populates the "nodes" slice, and processes
    530 // pending movement actions. Set "drawingAfter" to true if you know that
    531 // [TreeView.Draw] will be called immediately after this function (to avoid
    532 // having [TreeView.Draw] call it again).
    533 func (t *TreeView) process(drawingAfter bool) {
    534 	t.stableNodes = drawingAfter
    535 	_, _, _, height := t.GetInnerRect()
    536 
    537 	// Determine visible nodes and their placement.
    538 	t.nodes = nil
    539 	if t.root == nil {
    540 		return
    541 	}
    542 	parentSelectedIndex, selectedIndex, topLevelGraphicsX := -1, -1, -1
    543 	var graphicsOffset, maxTextX int
    544 	if t.graphics {
    545 		graphicsOffset = 1
    546 	}
    547 	t.root.Walk(func(node, parent *TreeNode) bool {
    548 		// Set node attributes.
    549 		node.parent = parent
    550 		if parent == nil {
    551 			node.level = 0
    552 			node.graphicsX = 0
    553 			node.textX = 0
    554 		} else {
    555 			node.level = parent.level + 1
    556 			node.graphicsX = parent.textX
    557 			node.textX = node.graphicsX + graphicsOffset + node.indent
    558 		}
    559 		if !t.graphics && t.align {
    560 			// Without graphics, we align nodes on the first column.
    561 			node.textX = 0
    562 		}
    563 		if node.level == t.topLevel {
    564 			// No graphics for top level nodes.
    565 			node.graphicsX = 0
    566 			node.textX = 0
    567 		}
    568 
    569 		// Add the node to the list.
    570 		if node.level >= t.topLevel {
    571 			// This node will be visible.
    572 			if node.textX > maxTextX {
    573 				maxTextX = node.textX
    574 			}
    575 			if node == t.currentNode && node.selectable {
    576 				selectedIndex = len(t.nodes)
    577 
    578 				// Also find parent node.
    579 				for index := len(t.nodes) - 1; index >= 0; index-- {
    580 					if t.nodes[index] == parent && t.nodes[index].selectable {
    581 						parentSelectedIndex = index
    582 						break
    583 					}
    584 				}
    585 			}
    586 
    587 			// Maybe we want to skip this level.
    588 			if t.topLevel == node.level && (topLevelGraphicsX < 0 || node.graphicsX < topLevelGraphicsX) {
    589 				topLevelGraphicsX = node.graphicsX
    590 			}
    591 
    592 			t.nodes = append(t.nodes, node)
    593 		}
    594 
    595 		// Recurse if desired.
    596 		return node.expanded
    597 	})
    598 
    599 	// Post-process positions.
    600 	for _, node := range t.nodes {
    601 		// If text must align, we correct the positions.
    602 		if t.align && node.level > t.topLevel {
    603 			node.textX = maxTextX
    604 		}
    605 
    606 		// If we skipped levels, shift to the left.
    607 		if topLevelGraphicsX > 0 {
    608 			node.graphicsX -= topLevelGraphicsX
    609 			node.textX -= topLevelGraphicsX
    610 		}
    611 	}
    612 
    613 	// Process selection. (Also trigger events if necessary.)
    614 	if selectedIndex >= 0 {
    615 		// Move the selection.
    616 		switch t.movement {
    617 		case treeMove:
    618 			for t.step < 0 { // Going up.
    619 				index := selectedIndex
    620 				for index > 0 {
    621 					index--
    622 					if t.nodes[index].selectable {
    623 						selectedIndex = index
    624 						break
    625 					}
    626 				}
    627 				t.step++
    628 			}
    629 			for t.step > 0 { // Going down.
    630 				index := selectedIndex
    631 				for index < len(t.nodes)-1 {
    632 					index++
    633 					if t.nodes[index].selectable {
    634 						selectedIndex = index
    635 						break
    636 					}
    637 				}
    638 				t.step--
    639 			}
    640 		case treeParent:
    641 			if parentSelectedIndex >= 0 {
    642 				selectedIndex = parentSelectedIndex
    643 			}
    644 		case treeChild:
    645 			index := selectedIndex
    646 			for index < len(t.nodes)-1 {
    647 				index++
    648 				if t.nodes[index].selectable && t.nodes[index].parent == t.nodes[selectedIndex] {
    649 					selectedIndex = index
    650 				}
    651 			}
    652 		}
    653 		t.currentNode = t.nodes[selectedIndex]
    654 
    655 		// Move selection into viewport.
    656 		if t.movement != treeScroll {
    657 			if selectedIndex-t.offsetY >= height {
    658 				t.offsetY = selectedIndex - height + 1
    659 			}
    660 			if selectedIndex < t.offsetY {
    661 				t.offsetY = selectedIndex
    662 			}
    663 			if t.movement != treeHome && t.movement != treeEnd {
    664 				// treeScroll, treeHome, and treeEnd are handled by Draw().
    665 				t.movement = treeNone
    666 				t.step = 0
    667 			}
    668 		}
    669 	} else {
    670 		// If selection is not visible or selectable, select the first candidate.
    671 		if t.currentNode != nil {
    672 			for index, node := range t.nodes {
    673 				if node.selectable {
    674 					selectedIndex = index
    675 					t.currentNode = node
    676 					break
    677 				}
    678 			}
    679 		}
    680 		if selectedIndex < 0 {
    681 			t.currentNode = nil
    682 		}
    683 	}
    684 
    685 	// Trigger "changed" callback.
    686 	if t.changed != nil && t.currentNode != nil && t.currentNode != t.lastNode {
    687 		t.changed(t.currentNode)
    688 	}
    689 	t.lastNode = t.currentNode
    690 }
    691 
    692 // Draw draws this primitive onto the screen.
    693 func (t *TreeView) Draw(screen tcell.Screen) {
    694 	t.Box.DrawForSubclass(screen, t)
    695 	if t.root == nil {
    696 		return
    697 	}
    698 	_, totalHeight := screen.Size()
    699 
    700 	if !t.stableNodes {
    701 		t.process(false)
    702 	} else {
    703 		t.stableNodes = false
    704 	}
    705 
    706 	// Scroll the tree, t.movement is treeNone after process() when there is a
    707 	// selection, except for treeScroll, treeHome, and treeEnd.
    708 	x, y, width, height := t.GetInnerRect()
    709 	switch t.movement {
    710 	case treeMove, treeScroll:
    711 		t.offsetY += t.step
    712 	case treeHome:
    713 		t.offsetY = 0
    714 	case treeEnd:
    715 		t.offsetY = len(t.nodes)
    716 	}
    717 	t.movement = treeNone
    718 
    719 	// Fix invalid offsets.
    720 	if t.offsetY >= len(t.nodes)-height {
    721 		t.offsetY = len(t.nodes) - height
    722 	}
    723 	if t.offsetY < 0 {
    724 		t.offsetY = 0
    725 	}
    726 
    727 	// Draw the tree.
    728 	posY := y
    729 	lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor)
    730 	for index, node := range t.nodes {
    731 		// Skip invisible parts.
    732 		if posY >= y+height+1 || posY >= totalHeight {
    733 			break
    734 		}
    735 		if index < t.offsetY {
    736 			continue
    737 		}
    738 
    739 		// Draw the graphics.
    740 		if t.graphics {
    741 			// Draw ancestor branches.
    742 			ancestor := node.parent
    743 			for ancestor != nil && ancestor.parent != nil && ancestor.parent.level >= t.topLevel {
    744 				if ancestor.graphicsX >= width {
    745 					continue
    746 				}
    747 
    748 				// Draw a branch if this ancestor is not a last child.
    749 				if ancestor.parent.children[len(ancestor.parent.children)-1] != ancestor {
    750 					if posY-1 >= y && ancestor.textX > ancestor.graphicsX {
    751 						PrintJoinedSemigraphics(screen, x+ancestor.graphicsX, posY-1, Borders.Vertical, lineStyle)
    752 					}
    753 					if posY < y+height {
    754 						screen.SetContent(x+ancestor.graphicsX, posY, Borders.Vertical, nil, lineStyle)
    755 					}
    756 				}
    757 				ancestor = ancestor.parent
    758 			}
    759 
    760 			if node.textX > node.graphicsX && node.graphicsX < width {
    761 				// Connect to the node above.
    762 				if posY-1 >= y && t.nodes[index-1].graphicsX <= node.graphicsX && t.nodes[index-1].textX > node.graphicsX {
    763 					PrintJoinedSemigraphics(screen, x+node.graphicsX, posY-1, Borders.TopLeft, lineStyle)
    764 				}
    765 
    766 				// Join this node.
    767 				if posY < y+height {
    768 					screen.SetContent(x+node.graphicsX, posY, Borders.BottomLeft, nil, lineStyle)
    769 					for pos := node.graphicsX + 1; pos < node.textX && pos < width; pos++ {
    770 						screen.SetContent(x+pos, posY, Borders.Horizontal, nil, lineStyle)
    771 					}
    772 				}
    773 			}
    774 		}
    775 
    776 		// Draw the prefix and the text.
    777 		if node.textX < width && posY < y+height {
    778 			// Prefix.
    779 			var prefixWidth int
    780 			if len(t.prefixes) > 0 {
    781 				_, _, prefixWidth = printWithStyle(screen, t.prefixes[(node.level-t.topLevel)%len(t.prefixes)], x+node.textX, posY, 0, width-node.textX, AlignLeft, node.textStyle, true)
    782 			}
    783 
    784 			// Text.
    785 			if node.textX+prefixWidth < width {
    786 				style := node.textStyle
    787 				if node == t.currentNode {
    788 					style = node.selectedTextStyle
    789 				}
    790 				printWithStyle(screen, node.text, x+node.textX+prefixWidth, posY, 0, width-node.textX-prefixWidth, AlignLeft, style, false)
    791 			}
    792 		}
    793 
    794 		// Advance.
    795 		posY++
    796 	}
    797 }
    798 
    799 // InputHandler returns the handler for this primitive.
    800 func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
    801 	return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
    802 		selectNode := func() {
    803 			node := t.currentNode
    804 			if node != nil {
    805 				if t.selected != nil {
    806 					t.selected(node)
    807 				}
    808 				if node.selected != nil {
    809 					node.selected()
    810 				}
    811 			}
    812 		}
    813 
    814 		// Because the tree is flattened into a list only at drawing time, we also
    815 		// postpone the (selection) movement to drawing time.
    816 		switch key := event.Key(); key {
    817 		case tcell.KeyTab, tcell.KeyBacktab, tcell.KeyEscape:
    818 			if t.done != nil {
    819 				t.done(key)
    820 			}
    821 		case tcell.KeyDown, tcell.KeyRight:
    822 			t.movement = treeMove
    823 			t.step = 1
    824 		case tcell.KeyUp, tcell.KeyLeft:
    825 			t.movement = treeMove
    826 			t.step = -1
    827 		case tcell.KeyHome:
    828 			t.movement = treeHome
    829 		case tcell.KeyEnd:
    830 			t.movement = treeEnd
    831 		case tcell.KeyPgDn, tcell.KeyCtrlF:
    832 			_, _, _, height := t.GetInnerRect()
    833 			t.movement = treeMove
    834 			t.step = height
    835 		case tcell.KeyPgUp, tcell.KeyCtrlB:
    836 			_, _, _, height := t.GetInnerRect()
    837 			t.movement = treeMove
    838 			t.step = -height
    839 		case tcell.KeyRune:
    840 			switch event.Rune() {
    841 			case 'g':
    842 				t.movement = treeHome
    843 			case 'G':
    844 				t.movement = treeEnd
    845 			case 'j':
    846 				t.movement = treeMove
    847 				t.step = 1
    848 			case 'J':
    849 				t.movement = treeChild
    850 			case 'k':
    851 				t.movement = treeMove
    852 				t.step = -1
    853 			case 'K':
    854 				t.movement = treeParent
    855 			case ' ':
    856 				selectNode()
    857 			}
    858 		case tcell.KeyEnter:
    859 			selectNode()
    860 		}
    861 
    862 		t.process(true)
    863 	})
    864 }
    865 
    866 // MouseHandler returns the mouse handler for this primitive.
    867 func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
    868 	return t.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
    869 		x, y := event.Position()
    870 		if !t.InRect(x, y) {
    871 			return false, nil
    872 		}
    873 
    874 		switch action {
    875 		case MouseLeftDown:
    876 			setFocus(t)
    877 			consumed = true
    878 		case MouseLeftClick:
    879 			_, rectY, _, _ := t.GetInnerRect()
    880 			y += t.offsetY - rectY
    881 			if y >= 0 && y < len(t.nodes) {
    882 				node := t.nodes[y]
    883 				if node.selectable {
    884 					previousNode := t.currentNode
    885 					t.currentNode = node
    886 					if previousNode != node && t.changed != nil {
    887 						t.changed(node)
    888 					}
    889 					if t.selected != nil {
    890 						t.selected(node)
    891 					}
    892 					if node.selected != nil {
    893 						node.selected()
    894 					}
    895 				}
    896 			}
    897 			consumed = true
    898 		case MouseScrollUp:
    899 			t.movement = treeScroll
    900 			t.step = -1
    901 			consumed = true
    902 		case MouseScrollDown:
    903 			t.movement = treeScroll
    904 			t.step = 1
    905 			consumed = true
    906 		}
    907 
    908 		return
    909 	})
    910 }