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 }