treeview.go 21 KB

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