mirror of
https://github.com/rivo/tview.git
synced 2025-04-24 13:48:56 +08:00
Added a Move() function to the TreeView. Resolves #432
This commit is contained in:
parent
19d943d595
commit
968b7463f7
196
treeview.go
196
treeview.go
@ -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
|
||||
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 treeDown:
|
||||
for newSelectedIndex < len(t.nodes)-1 {
|
||||
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 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
|
||||
}
|
||||
}
|
||||
newSelectedIndex = selectedIndex
|
||||
case treePageUp:
|
||||
if newSelectedIndex >= height {
|
||||
newSelectedIndex -= height
|
||||
} else {
|
||||
newSelectedIndex = 0
|
||||
}
|
||||
for ; newSelectedIndex >= 0; newSelectedIndex-- {
|
||||
if t.nodes[newSelectedIndex].selectable {
|
||||
break MovementSwitch
|
||||
}
|
||||
}
|
||||
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…
x
Reference in New Issue
Block a user