...

Source file src/github.com/rivo/tview/treeview.go

Documentation: github.com/rivo/tview

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

View as plain text