PanelContainer: Implement cycling selection

This commit is contained in:
Luke I. Wilson 2021-04-07 13:56:32 -05:00
parent 459087e6d8
commit 56b89c6079
3 changed files with 61 additions and 12 deletions

View File

@ -295,8 +295,12 @@ func main() {
panelMenu := ui.NewMenu("Panel", 0, &theme) panelMenu := ui.NewMenu("Panel", 0, &theme)
panelMenu.AddItems([]ui.Item{&ui.ItemEntry{Name: "Focus Up", QuickChar: -1, Shortcut: "Alt+Up", Callback: func() { panelMenu.AddItems([]ui.Item{&ui.ItemEntry{Name: "Focus Next", Shortcut: "Alt+.", Callback: func() {
panelContainer.SelectNext()
}}, &ui.ItemEntry{Name: "Focus Prev", Shortcut: "Alt+,", Callback: func() {
panelContainer.SelectPrev()
}}, &ui.ItemEntry{Name: "Focus Up", QuickChar: -1, Shortcut: "Alt+Up", Callback: func() {
}}, &ui.ItemEntry{Name: "Focus Down", QuickChar: -1, Shortcut: "Alt+Down", Callback: func() { }}, &ui.ItemEntry{Name: "Focus Down", QuickChar: -1, Shortcut: "Alt+Down", Callback: func() {
}}, &ui.ItemEntry{Name: "Focus Left", QuickChar: -1, Shortcut: "Alt+Left", Callback: func() { }}, &ui.ItemEntry{Name: "Focus Left", QuickChar: -1, Shortcut: "Alt+Left", Callback: func() {

View File

@ -69,27 +69,42 @@ func (p *Panel) UpdateSplits() {
} }
// Same as EachLeaf, but returns true if any call to `f` returned true. // Same as EachLeaf, but returns true if any call to `f` returned true.
func (p *Panel) eachLeaf(f func(*Component) bool) bool { func (p *Panel) eachLeaf(rightMost bool, f func(*Panel) bool) bool {
switch p.Kind { switch p.Kind {
case PanelKindEmpty:
fallthrough
case PanelKindSingle: case PanelKindSingle:
return f(&p.Left) return f(p)
case PanelKindSplitVert: case PanelKindSplitVert:
fallthrough fallthrough
case PanelKindSplitHor: case PanelKindSplitHor:
if p.Left.(*Panel).eachLeaf(f) { if rightMost {
return true if p.Right.(*Panel).eachLeaf(rightMost, f) {
return true
}
return p.Left.(*Panel).eachLeaf(rightMost, f)
} else {
if p.Left.(*Panel).eachLeaf(rightMost, f) {
return true
}
return p.Right.(*Panel).eachLeaf(rightMost, f)
} }
return p.Right.(*Panel).eachLeaf(f)
default: default:
return false return false
} }
} }
// EachLeaf visits the entire tree in left-most order, and calls function `f` // EachLeaf visits the entire tree, and calls function `f` at each leaf Panel.
// at each individual Component (never for Panels). If the function `f` returns // If the function `f` returns true, then visiting stops. if `rtl` is true,
// true, then visiting stops. // the tree is traversed in right-most order. The default is to traverse
func (p *Panel) EachLeaf(f func(*Component) bool) { // in left-most order.
p.eachLeaf(f) //
// The caller of this function can safely assert that Panel's Kind is always
// either `PanelKindSingle` or `PanelKindEmpty`.
func (p *Panel) EachLeaf(rightMost bool, f func(*Panel) bool) {
p.eachLeaf(rightMost, f)
} }
// IsLeaf returns whether the Panel is a leaf or not. A leaf is a panel with // IsLeaf returns whether the Panel is a leaf or not. A leaf is a panel with

View File

@ -266,6 +266,36 @@ func (c *PanelContainer) UnfloatSelected(kind SplitKind) bool {
return c.SetFloatingFocused(true) return c.SetFloatingFocused(true)
} }
func (c *PanelContainer) selectNext(rightMost bool) {
var nextIsIt bool
c.root.EachLeaf(rightMost, func(p *Panel) bool {
if nextIsIt {
c.selected = &p
nextIsIt = false
return true
} else if p == *c.selected {
nextIsIt = true
}
return false
})
// This boolean must be false if we found the next leaf.
// Therefore, if it is true, c.selected was the last leaf
// of the tree. We need to wrap around to the first leaf.
if nextIsIt {
// This gets the first leaf in left-most or right-most order
c.root.EachLeaf(rightMost, func(p *Panel) bool { c.selected = &p; return true })
}
}
func (c *PanelContainer) SelectNext() {
c.selectNext(false)
}
func (c *PanelContainer) SelectPrev() {
c.selectNext(true)
}
func (c *PanelContainer) Draw(s tcell.Screen) { func (c *PanelContainer) Draw(s tcell.Screen) {
c.root.Draw(s) c.root.Draw(s)
for i := len(c.floating)-1; i >= 0; i-- { for i := len(c.floating)-1; i >= 0; i-- {