Added a Move() function to the TreeView. Resolves #432

pull/787/head
Oliver 2 years ago
parent 19d943d595
commit 968b7463f7

@ -9,14 +9,10 @@ const (
treeNone int = iota
treeHome
treeEnd
treeUp
treeDown
treePageUp
treePageDown
treeMove
treeParent
treeChild
treeScrollUp // Move up without changing the selection, even when off screen.
treeScrollDown
treeScroll // Move without changing the selection, even when off screen.
)
// TreeNode represents one node in a tree view.
@ -251,8 +247,8 @@ func (n *TreeNode) GetLevel() int {
// - k, up arrow, left arrow: Move (the selection) up by one node.
// - g, home: Move (the selection) to the top.
// - G, end: Move (the selection) to the bottom.
// - J: Move (the selection) up one level.
// - K: Move (the selection) down one level (if it is shown).
// - J: Move (the selection) up one level (if that node is selectable).
// - K: Move (the selection) to the last node one level down (if any).
// - Ctrl-F, page down: Move (the selection) down by one page.
// - Ctrl-B, page up: Move (the selection) up by one page.
//
@ -282,6 +278,11 @@ type TreeView struct {
// constants defined above.
movement int
// The number of nodes to move down or up, when movement is treeMove,
// excluding non-selectable nodes for selection movement, including them for
// scrolling.
step int
// The top hierarchical level shown. (0 corresponds to the root level.)
topLevel int
@ -313,6 +314,10 @@ type TreeView struct {
// The visible nodes, top-down, as set by process().
nodes []*TreeNode
// Temporarily set to true while we know that the tree has not changed and
// therefore does not need to be reprocessed.
stableNodes bool
}
// NewTreeView returns a new tree view.
@ -431,19 +436,37 @@ func (t *TreeView) GetRowCount() int {
return len(t.nodes)
}
// Move moves the selection (if a node is currently selected) or scrolls the
// tree view (if there is no selection), by the given offset (positive values to
// move/scroll down, negative values to move/scroll up). For selection changes,
// the offset refers to the number selectable, visible nodes. For scrolling, the
// offset refers to the number of visible nodes.
//
// If the offset is 0, nothing happens.
func (t *TreeView) Move(offset int) *TreeView {
if offset == 0 {
return t
}
t.movement = treeMove
t.step = offset
return t
}
// process builds the visible tree, populates the "nodes" slice, and processes
// pending selection actions.
func (t *TreeView) process() {
// pending movement actions. Set "drawingAfter" to true if you know that [Draw]
// will be called immediately after this function (to avoid having [Draw] call
// it again).
func (t *TreeView) process(drawingAfter bool) {
t.stableNodes = drawingAfter
_, _, _, height := t.GetInnerRect()
// Determine visible nodes and their placement.
var graphicsOffset, maxTextX, parentSelectedIndex int
t.nodes = nil
if t.root == nil {
return
}
selectedIndex := -1
topLevelGraphicsX := -1
parentSelectedIndex, selectedIndex, topLevelGraphicsX := -1, -1, -1
var graphicsOffset, maxTextX int
if t.graphics {
graphicsOffset = 1
}
@ -477,6 +500,14 @@ func (t *TreeView) process() {
}
if node == t.currentNode && node.selectable {
selectedIndex = len(t.nodes)
// Also find parent node.
for index := len(t.nodes) - 1; index >= 0; index-- {
if t.nodes[index] == parent && t.nodes[index].selectable {
parentSelectedIndex = index
break
}
}
}
// Maybe we want to skip this level.
@ -487,11 +518,6 @@ func (t *TreeView) process() {
t.nodes = append(t.nodes, node)
}
// Keep track of the parent of the selected node.
if selectedIndex < 0 && node.selectable && len(node.children) > 0 && node.expanded {
parentSelectedIndex = len(t.nodes) - 1
}
// Recurse if desired.
return node.expanded
})
@ -514,73 +540,44 @@ func (t *TreeView) process() {
if selectedIndex >= 0 {
// Move the selection.
newSelectedIndex := selectedIndex
MovementSwitch:
switch t.movement {
case treeUp:
for newSelectedIndex > 0 {
newSelectedIndex--
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
}
}
newSelectedIndex = selectedIndex
case treeDown:
for newSelectedIndex < len(t.nodes)-1 {
newSelectedIndex++
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
}
}
newSelectedIndex = selectedIndex
case treeHome:
for newSelectedIndex = 0; newSelectedIndex < len(t.nodes); newSelectedIndex++ {
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
}
}
newSelectedIndex = selectedIndex
case treeEnd:
for newSelectedIndex = len(t.nodes) - 1; newSelectedIndex >= 0; newSelectedIndex-- {
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
}
}
newSelectedIndex = selectedIndex
case treePageDown:
if newSelectedIndex+height < len(t.nodes) {
newSelectedIndex += height
} else {
newSelectedIndex = len(t.nodes) - 1
}
for ; newSelectedIndex < len(t.nodes); newSelectedIndex++ {
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
case treeMove:
for t.step < 0 { // Going up.
index := newSelectedIndex
for index > 0 {
index--
if t.nodes[index].selectable {
newSelectedIndex = index
break
}
}
t.step++
}
newSelectedIndex = selectedIndex
case treePageUp:
if newSelectedIndex >= height {
newSelectedIndex -= height
} else {
newSelectedIndex = 0
}
for ; newSelectedIndex >= 0; newSelectedIndex-- {
if t.nodes[newSelectedIndex].selectable {
break MovementSwitch
for t.step > 0 { // Going down.
index := newSelectedIndex
for index < len(t.nodes)-1 {
index++
if t.nodes[index].selectable {
newSelectedIndex = index
break
}
}
t.step--
}
newSelectedIndex = selectedIndex
case treeParent:
newSelectedIndex = parentSelectedIndex
if parentSelectedIndex >= 0 {
newSelectedIndex = parentSelectedIndex
}
case treeChild:
for newSelectedIndex < len(t.nodes)-1 {
newSelectedIndex++
if t.nodes[newSelectedIndex].selectable && t.nodes[newSelectedIndex].parent == t.nodes[selectedIndex] {
break MovementSwitch
index := newSelectedIndex
for index < len(t.nodes)-1 {
index++
if t.nodes[index].selectable && t.nodes[index].parent == t.nodes[selectedIndex] {
newSelectedIndex = index
}
}
newSelectedIndex = selectedIndex
}
t.step = 0
t.currentNode = t.nodes[newSelectedIndex]
if newSelectedIndex != selectedIndex {
t.movement = treeNone
@ -591,7 +588,7 @@ func (t *TreeView) process() {
selectedIndex = newSelectedIndex
// Move selection into viewport.
if t.movement != treeScrollDown && t.movement != treeScrollUp {
if t.movement != treeScroll {
if selectedIndex-t.offsetY >= height {
t.offsetY = selectedIndex - height + 1
}
@ -624,23 +621,22 @@ func (t *TreeView) Draw(screen tcell.Screen) {
}
_, totalHeight := screen.Size()
t.process()
if !t.stableNodes {
t.process(false)
} else {
t.stableNodes = false
}
// Scroll the tree.
// Scroll the tree, t.movement is treeNone after process() when there is a
// selection.
x, y, width, height := t.GetInnerRect()
switch t.movement {
case treeUp, treeScrollUp:
t.offsetY--
case treeDown, treeScrollDown:
t.offsetY++
case treeMove, treeScroll:
t.offsetY += t.step
case treeHome:
t.offsetY = 0
case treeEnd:
t.offsetY = len(t.nodes)
case treePageUp:
t.offsetY -= height
case treePageDown:
t.offsetY += height
}
t.movement = treeNone
@ -747,17 +743,23 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
t.done(key)
}
case tcell.KeyDown, tcell.KeyRight:
t.movement = treeDown
t.movement = treeMove
t.step = 1
case tcell.KeyUp, tcell.KeyLeft:
t.movement = treeUp
t.movement = treeMove
t.step = -1
case tcell.KeyHome:
t.movement = treeHome
case tcell.KeyEnd:
t.movement = treeEnd
case tcell.KeyPgDn, tcell.KeyCtrlF:
t.movement = treePageDown
_, _, _, height := t.GetInnerRect()
t.movement = treeMove
t.step = height
case tcell.KeyPgUp, tcell.KeyCtrlB:
t.movement = treePageUp
_, _, _, height := t.GetInnerRect()
t.movement = treeMove
t.step = -height
case tcell.KeyRune:
switch event.Rune() {
case 'g':
@ -765,11 +767,13 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
case 'G':
t.movement = treeEnd
case 'j':
t.movement = treeDown
t.movement = treeMove
t.step = 1
case 'J':
t.movement = treeChild
case 'k':
t.movement = treeUp
t.movement = treeMove
t.step = -1
case 'K':
t.movement = treeParent
case ' ':
@ -779,7 +783,7 @@ func (t *TreeView) InputHandler() func(event *tcell.EventKey, setFocus func(p Pr
selectNode()
}
t.process()
t.process(true)
})
}
@ -816,10 +820,12 @@ func (t *TreeView) MouseHandler() func(action MouseAction, event *tcell.EventMou
}
consumed = true
case MouseScrollUp:
t.movement = treeScrollUp
t.movement = treeScroll
t.step = -1
consumed = true
case MouseScrollDown:
t.movement = treeScrollDown
t.movement = treeScroll
t.step = 1
consumed = true
}

Loading…
Cancel
Save