treeview.go 23 KB

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