This is the absolute bomb semi-graphical text user interface library, I had to mirror it.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

treeview.go 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694
  1. package tview
  2. import (
  3. "git.parallelcoin.io/dev/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://git.parallelcoin.io/dev/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. // The visible nodes, top-down, as set by process().
  234. nodes []*TreeNode
  235. }
  236. // NewTreeView returns a new tree view.
  237. func NewTreeView() *TreeView {
  238. return &TreeView{
  239. Box: NewBox(),
  240. graphics: true,
  241. graphicsColor: Styles.GraphicsColor,
  242. }
  243. }
  244. // SetRoot sets the root node of the tree.
  245. func (t *TreeView) SetRoot(root *TreeNode) *TreeView {
  246. t.root = root
  247. return t
  248. }
  249. // GetRoot returns the root node of the tree. If no such node was previously
  250. // set, nil is returned.
  251. func (t *TreeView) GetRoot() *TreeNode {
  252. return t.root
  253. }
  254. // SetCurrentNode sets the currently selected node. Provide nil to clear all
  255. // selections. Selected nodes must be visible and selectable, or else the
  256. // selection will be changed to the top-most selectable and visible node.
  257. //
  258. // This function does NOT trigger the "changed" callback.
  259. func (t *TreeView) SetCurrentNode(node *TreeNode) *TreeView {
  260. t.currentNode = node
  261. return t
  262. }
  263. // GetCurrentNode returns the currently selected node or nil of no node is
  264. // currently selected.
  265. func (t *TreeView) GetCurrentNode() *TreeNode {
  266. return t.currentNode
  267. }
  268. // SetTopLevel sets the first tree level that is visible with 0 referring to the
  269. // root, 1 to the root's child nodes, and so on. Nodes above the top level are
  270. // not displayed.
  271. func (t *TreeView) SetTopLevel(topLevel int) *TreeView {
  272. t.topLevel = topLevel
  273. return t
  274. }
  275. // SetPrefixes defines the strings drawn before the nodes' texts. This is a
  276. // slice of strings where each element corresponds to a node's hierarchy level,
  277. // i.e. 0 for the root, 1 for the root's children, and so on (levels will
  278. // cycle).
  279. //
  280. // For example, to display a hierarchical list with bullet points:
  281. //
  282. // treeView.SetGraphics(false).
  283. // SetPrefixes([]string{"* ", "- ", "x "})
  284. func (t *TreeView) SetPrefixes(prefixes []string) *TreeView {
  285. t.prefixes = prefixes
  286. return t
  287. }
  288. // SetAlign controls the horizontal alignment of the node texts. If set to true,
  289. // all texts except that of top-level nodes will be placed in the same column.
  290. // If set to false, they will indent with the hierarchy.
  291. func (t *TreeView) SetAlign(align bool) *TreeView {
  292. t.align = align
  293. return t
  294. }
  295. // SetGraphics sets a flag which determines whether or not line graphics are
  296. // drawn to illustrate the tree's hierarchy.
  297. func (t *TreeView) SetGraphics(showGraphics bool) *TreeView {
  298. t.graphics = showGraphics
  299. return t
  300. }
  301. // SetGraphicsColor sets the colors of the lines used to draw the tree structure.
  302. func (t *TreeView) SetGraphicsColor(color tcell.Color) *TreeView {
  303. t.graphicsColor = color
  304. return t
  305. }
  306. // SetChangedFunc sets the function which is called when the user navigates to
  307. // a new tree node.
  308. func (t *TreeView) SetChangedFunc(handler func(node *TreeNode)) *TreeView {
  309. t.changed = handler
  310. return t
  311. }
  312. // SetSelectedFunc sets the function which is called when the user selects a
  313. // node by pressing Enter on the current selection.
  314. func (t *TreeView) SetSelectedFunc(handler func(node *TreeNode)) *TreeView {
  315. t.selected = handler
  316. return t
  317. }
  318. // process builds the visible tree, populates the "nodes" slice, and processes
  319. // pending selection actions.
  320. func (t *TreeView) process() {
  321. _, _, _, height := t.GetInnerRect()
  322. // Determine visible nodes and their placement.
  323. var graphicsOffset, maxTextX int
  324. t.nodes = nil
  325. selectedIndex := -1
  326. topLevelGraphicsX := -1
  327. if t.graphics {
  328. graphicsOffset = 1
  329. }
  330. t.root.Walk(func(node, parent *TreeNode) bool {
  331. // Set node attributes.
  332. node.parent = parent
  333. if parent == nil {
  334. node.level = 0
  335. node.graphicsX = 0
  336. node.textX = 0
  337. } else {
  338. node.level = parent.level + 1
  339. node.graphicsX = parent.textX
  340. node.textX = node.graphicsX + graphicsOffset + node.indent
  341. }
  342. if !t.graphics && t.align {
  343. // Without graphics, we align nodes on the first column.
  344. node.textX = 0
  345. }
  346. if node.level == t.topLevel {
  347. // No graphics for top level nodes.
  348. node.graphicsX = 0
  349. node.textX = 0
  350. }
  351. if node.textX > maxTextX {
  352. maxTextX = node.textX
  353. }
  354. if node == t.currentNode && node.selectable {
  355. selectedIndex = len(t.nodes)
  356. }
  357. // Maybe we want to skip this level.
  358. if t.topLevel == node.level && (topLevelGraphicsX < 0 || node.graphicsX < topLevelGraphicsX) {
  359. topLevelGraphicsX = node.graphicsX
  360. }
  361. // Add and recurse (if desired).
  362. if node.level >= t.topLevel {
  363. t.nodes = append(t.nodes, node)
  364. }
  365. return node.expanded
  366. })
  367. // Post-process positions.
  368. for _, node := range t.nodes {
  369. // If text must align, we correct the positions.
  370. if t.align && node.level > t.topLevel {
  371. node.textX = maxTextX
  372. }
  373. // If we skipped levels, shift to the left.
  374. if topLevelGraphicsX > 0 {
  375. node.graphicsX -= topLevelGraphicsX
  376. node.textX -= topLevelGraphicsX
  377. }
  378. }
  379. // Process selection. (Also trigger events if necessary.)
  380. if selectedIndex >= 0 {
  381. // Move the selection.
  382. newSelectedIndex := selectedIndex
  383. MovementSwitch:
  384. switch t.movement {
  385. case treeUp:
  386. for newSelectedIndex > 0 {
  387. newSelectedIndex--
  388. if t.nodes[newSelectedIndex].selectable {
  389. break MovementSwitch
  390. }
  391. }
  392. newSelectedIndex = selectedIndex
  393. case treeDown:
  394. for newSelectedIndex < len(t.nodes)-1 {
  395. newSelectedIndex++
  396. if t.nodes[newSelectedIndex].selectable {
  397. break MovementSwitch
  398. }
  399. }
  400. newSelectedIndex = selectedIndex
  401. case treeHome:
  402. for newSelectedIndex = 0; newSelectedIndex < len(t.nodes); newSelectedIndex++ {
  403. if t.nodes[newSelectedIndex].selectable {
  404. break MovementSwitch
  405. }
  406. }
  407. newSelectedIndex = selectedIndex
  408. case treeEnd:
  409. for newSelectedIndex = len(t.nodes) - 1; newSelectedIndex >= 0; newSelectedIndex-- {
  410. if t.nodes[newSelectedIndex].selectable {
  411. break MovementSwitch
  412. }
  413. }
  414. newSelectedIndex = selectedIndex
  415. case treePageUp:
  416. if newSelectedIndex+height < len(t.nodes) {
  417. newSelectedIndex += height
  418. } else {
  419. newSelectedIndex = len(t.nodes) - 1
  420. }
  421. for ; newSelectedIndex < len(t.nodes); newSelectedIndex++ {
  422. if t.nodes[newSelectedIndex].selectable {
  423. break MovementSwitch
  424. }
  425. }
  426. newSelectedIndex = selectedIndex
  427. case treePageDown:
  428. if newSelectedIndex >= height {
  429. newSelectedIndex -= height
  430. } else {
  431. newSelectedIndex = 0
  432. }
  433. for ; newSelectedIndex >= 0; newSelectedIndex-- {
  434. if t.nodes[newSelectedIndex].selectable {
  435. break MovementSwitch
  436. }
  437. }
  438. newSelectedIndex = selectedIndex
  439. }
  440. t.currentNode = t.nodes[newSelectedIndex]
  441. if newSelectedIndex != selectedIndex {
  442. t.movement = treeNone
  443. if t.changed != nil {
  444. t.changed(t.currentNode)
  445. }
  446. }
  447. selectedIndex = newSelectedIndex
  448. // Move selection into viewport.
  449. if selectedIndex-t.offsetY >= height {
  450. t.offsetY = selectedIndex - height + 1
  451. }
  452. if selectedIndex < t.offsetY {
  453. t.offsetY = selectedIndex
  454. }
  455. } else {
  456. // If selection is not visible or selectable, select the first candidate.
  457. if t.currentNode != nil {
  458. for index, node := range t.nodes {
  459. if node.selectable {
  460. selectedIndex = index
  461. t.currentNode = node
  462. break
  463. }
  464. }
  465. }
  466. if selectedIndex < 0 {
  467. t.currentNode = nil
  468. }
  469. }
  470. }
  471. // Draw draws this primitive onto the screen.
  472. func (t *TreeView) Draw(screen tcell.Screen) {
  473. t.Box.Draw(screen)
  474. if t.root == nil {
  475. return
  476. }
  477. // Build the tree if necessary.
  478. if t.nodes == nil {
  479. t.process()
  480. }
  481. defer func() {
  482. t.nodes = nil // Rebuild during next call to Draw()
  483. }()
  484. // Scroll the tree.
  485. x, y, width, height := t.GetInnerRect()
  486. switch t.movement {
  487. case treeUp:
  488. t.offsetY--
  489. case treeDown:
  490. t.offsetY++
  491. case treeHome:
  492. t.offsetY = 0
  493. case treeEnd:
  494. t.offsetY = len(t.nodes)
  495. case treePageUp:
  496. t.offsetY -= height
  497. case treePageDown:
  498. t.offsetY += height
  499. }
  500. t.movement = treeNone
  501. // Fix invalid offsets.
  502. if t.offsetY >= len(t.nodes)-height {
  503. t.offsetY = len(t.nodes) - height
  504. }
  505. if t.offsetY < 0 {
  506. t.offsetY = 0
  507. }
  508. // Draw the tree.
  509. posY := y
  510. lineStyle := tcell.StyleDefault.Background(t.backgroundColor).Foreground(t.graphicsColor)
  511. for index, node := range t.nodes {
  512. // Skip invisible parts.
  513. if posY >= y+height+1 {
  514. break
  515. }
  516. if index < t.offsetY {
  517. continue
  518. }
  519. // Draw the graphics.
  520. if t.graphics {
  521. // Draw ancestor branches.
  522. ancestor := node.parent
  523. for ancestor != nil && ancestor.parent != nil && ancestor.parent.level >= t.topLevel {
  524. if ancestor.graphicsX >= width {
  525. continue
  526. }
  527. // Draw a branch if this ancestor is not a last child.
  528. if ancestor.parent.children[len(ancestor.parent.children)-1] != ancestor {
  529. if posY-1 >= y && ancestor.textX > ancestor.graphicsX {
  530. PrintJoinedSemigraphics(screen, x+ancestor.graphicsX, posY-1, Borders.Vertical, t.graphicsColor)
  531. }
  532. if posY < y+height {
  533. screen.SetContent(x+ancestor.graphicsX, posY, Borders.Vertical, nil, lineStyle)
  534. }
  535. }
  536. ancestor = ancestor.parent
  537. }
  538. if node.textX > node.graphicsX && node.graphicsX < width {
  539. // Connect to the node above.
  540. if posY-1 >= y && t.nodes[index-1].graphicsX <= node.graphicsX && t.nodes[index-1].textX > node.graphicsX {
  541. PrintJoinedSemigraphics(screen, x+node.graphicsX, posY-1, Borders.TopLeft, t.graphicsColor)
  542. }
  543. // Join this node.
  544. if posY < y+height {
  545. screen.SetContent(x+node.graphicsX, posY, Borders.BottomLeft, nil, lineStyle)
  546. for pos := node.graphicsX + 1; pos < node.textX && pos < width; pos++ {
  547. screen.SetContent(x+pos, posY, Borders.Horizontal, nil, lineStyle)
  548. }
  549. }
  550. }
  551. }
  552. // Draw the prefix and the text.
  553. if node.textX < width && posY < y+height {
  554. // Prefix.
  555. var prefixWidth int
  556. if len(t.prefixes) > 0 {
  557. _, prefixWidth = Print(screen, t.prefixes[(node.level-t.topLevel)%len(t.prefixes)], x+node.textX, posY, width-node.textX, AlignLeft, node.color)
  558. }
  559. // Text.
  560. if node.textX+prefixWidth < width {
  561. style := tcell.StyleDefault.Foreground(node.color)
  562. if node == t.currentNode {
  563. style = tcell.StyleDefault.Background(node.color).Foreground(t.backgroundColor)
  564. }
  565. printWithStyle(screen, node.text, x+node.textX+prefixWidth, posY, width-node.textX-prefixWidth, AlignLeft, style)
  566. }
  567. }
  568. // Advance.
  569. posY++
  570. }
  571. }
  572. // InputHandler returns the handler for this primitive.
  573. func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Primitive)) {
  574. return t.WrapInputHandler(func(event *tcell.EventKey, setFocus func(p Primitive)) {
  575. // Because the tree is flattened into a list only at drawing time, we also
  576. // postpone the (selection) movement to drawing time.
  577. switch key := event.Key(); key {
  578. case tcell.KeyTab, tcell.KeyDown, tcell.KeyRight:
  579. t.movement = treeDown
  580. case tcell.KeyBacktab, tcell.KeyUp, tcell.KeyLeft:
  581. t.movement = treeUp
  582. case tcell.KeyHome:
  583. t.movement = treeHome
  584. case tcell.KeyEnd:
  585. t.movement = treeEnd
  586. case tcell.KeyPgDn, tcell.KeyCtrlF:
  587. t.movement = treePageDown
  588. case tcell.KeyPgUp, tcell.KeyCtrlB:
  589. t.movement = treePageUp
  590. case tcell.KeyRune:
  591. switch event.Rune() {
  592. case 'g':
  593. t.movement = treeHome
  594. case 'G':
  595. t.movement = treeEnd
  596. case 'j':
  597. t.movement = treeDown
  598. case 'k':
  599. t.movement = treeUp
  600. }
  601. case tcell.KeyEnter:
  602. if t.currentNode != nil {
  603. if t.selected != nil {
  604. t.selected(t.currentNode)
  605. }
  606. if t.currentNode.selected != nil {
  607. t.currentNode.selected()
  608. }
  609. }
  610. }
  611. t.process()
  612. })
  613. }