Rename Pages as Panels

This commit is contained in:
Trevor Slocum 2020-10-15 09:12:10 -07:00
parent 3ad2fdd36a
commit 767e4e474c
10 changed files with 446 additions and 382 deletions

View File

@ -13,6 +13,7 @@ v1.5.1 (WIP)
- Optimize TextView (writing is 90% faster, drawing is 50% faster)
- Remove return values from methods which return their primitive (breaks chaining)
- Remove Application.ForceDraw (Application.Draw may be called anywhere)
- Rename Pages as Panels
v1.5.0 (2020-10-03)
- Add scroll bar to TextView

View File

@ -23,7 +23,7 @@ Available widgets:
- Selectable __lists__ with __context menus__
- Modal __dialogs__
- Horizontal and vertical __progress bars__
- __Grid__, __Flexbox__ and __page layouts__
- __Grid__, __Flexbox__ and __panel layouts__
- Sophisticated navigable __table views__
- Flexible __tree views__
- Draggable and resizable __windows__

View File

@ -1,4 +1,4 @@
// Demo code for the Pages primitive.
// Demo code for the Panels primitive.
package main
import (
@ -7,31 +7,31 @@ import (
"gitlab.com/tslocum/cview"
)
const pageCount = 5
const panelCount = 5
func main() {
app := cview.NewApplication()
app.EnableMouse(true)
pages := cview.NewPages()
for page := 0; page < pageCount; page++ {
func(page int) {
panels := cview.NewPanels()
for panel := 0; panel < panelCount; panel++ {
func(panel int) {
modal := cview.NewModal()
modal.SetText(fmt.Sprintf("This is page %d. Choose where to go next.", page+1))
modal.SetText(fmt.Sprintf("This is page %d. Choose where to go next.", panel+1))
modal.AddButtons([]string{"Next", "Quit"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
if buttonIndex == 0 {
pages.SwitchToPage(fmt.Sprintf("page-%d", (page+1)%pageCount))
panels.SwitchTo(fmt.Sprintf("panel-%d", (panel+1)%panelCount))
} else {
app.Stop()
}
})
pages.AddPage(fmt.Sprintf("page-%d", page), modal, false, page == 0)
}(page)
panels.Add(fmt.Sprintf("panel-%d", panel), modal, false, panel == 0)
}(panel)
}
app.SetRoot(pages, true)
app.SetRoot(panels, true)
if err := app.Run(); err != nil {
panic(err)
}

View File

@ -15,7 +15,7 @@ func demoBox(title string) *cview.Box {
// Flex demonstrates flexbox layout.
func Flex(nextSlide func()) (title string, content cview.Primitive) {
modalShown := false
pages := cview.NewPages()
panels := cview.NewPanels()
textView := cview.NewTextView()
textView.SetBorder(true)
@ -25,7 +25,7 @@ func Flex(nextSlide func()) (title string, content cview.Primitive) {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
panels.Show("modal")
modalShown = true
}
})
@ -45,10 +45,10 @@ func Flex(nextSlide func()) (title string, content cview.Primitive) {
modal.SetText("Resize the window to see the effect of the flexbox parameters")
modal.AddButtons([]string{"Ok"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
pages.HidePage("modal")
panels.Hide("modal")
})
pages.AddPage("flex", flex, true, true)
pages.AddPage("modal", modal, false, false)
return "Flex", pages
panels.Add("flex", flex, true, true)
panels.Add("modal", modal, false, false)
return "Flex", panels
}

View File

@ -8,7 +8,7 @@ import (
// Grid demonstrates the grid layout.
func Grid(nextSlide func()) (title string, content cview.Primitive) {
modalShown := false
pages := cview.NewPages()
panels := cview.NewPanels()
newPrimitive := func(text string) cview.Primitive {
tv := cview.NewTextView()
@ -19,7 +19,7 @@ func Grid(nextSlide func()) (title string, content cview.Primitive) {
nextSlide()
modalShown = false
} else {
pages.ShowPage("modal")
panels.Show("modal")
modalShown = true
}
})
@ -51,11 +51,11 @@ func Grid(nextSlide func()) (title string, content cview.Primitive) {
modal.SetText("Resize the window to see how the grid layout adapts")
modal.AddButtons([]string{"Ok"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
pages.HidePage("modal")
panels.Hide("modal")
})
pages.AddPage("grid", grid, true, true)
pages.AddPage("modal", modal, false, false)
panels.Add("grid", grid, true, true)
panels.Add("modal", modal, false, false)
return "Grid", pages
return "Grid", panels
}

View File

@ -64,7 +64,7 @@ func main() {
End,
}
pages := cview.NewPages()
panels := cview.NewPanels()
// The bottom row has some info on where we are.
info := cview.NewTextView()
@ -72,7 +72,7 @@ func main() {
info.SetRegions(true)
info.SetWrap(false)
info.SetHighlightedFunc(func(added, removed, remaining []string) {
pages.SwitchToPage(added[0])
panels.SwitchTo(added[0])
})
// Create the pages for all slides.
@ -95,7 +95,7 @@ func main() {
slideRegions = append(slideRegions, cursor)
title, primitive := slide(nextSlide)
pages.AddPage(strconv.Itoa(index), primitive, true, index == 0)
panels.Add(strconv.Itoa(index), primitive, true, index == 0)
fmt.Fprintf(info, `["%d"][darkcyan] %s [white][""]|`, index, title)
cursor += len(title) + 4
@ -105,7 +105,7 @@ func main() {
// Create the main layout.
layout := cview.NewFlex()
layout.SetDirection(cview.FlexRow)
layout.AddItem(pages, 0, 1, true)
layout.AddItem(panels, 0, 1, true)
layout.AddItem(info, 1, 1, false)
// Shortcuts to navigate the slides.

View File

@ -9,7 +9,7 @@ import (
func main() {
app := cview.NewApplication()
pages := cview.NewPages()
panels := cview.NewPanels()
form := cview.NewForm()
form.AddDropDownSimple("称谓", 0, nil, "先生", "女士", "博士", "老师", "师傅")
@ -20,7 +20,7 @@ func main() {
_, option := form.GetFormItem(0).(*cview.DropDown).GetCurrentOption()
userName := form.GetFormItem(1).(*cview.InputField).GetText()
alert(pages, "alert-dialog", fmt.Sprintf("保存成功,%s %s", userName, option.GetText()))
alert(panels, "alert-dialog", fmt.Sprintf("保存成功,%s %s", userName, option.GetText()))
})
form.AddButton("退出", func() {
app.Stop()
@ -28,23 +28,23 @@ func main() {
form.SetBorder(true)
form.SetTitle("输入一些内容")
form.SetTitleAlign(cview.AlignLeft)
pages.AddPage("base", form, true, true)
panels.Add("base", form, true, true)
app.SetRoot(pages, true)
app.SetRoot(panels, true)
if err := app.Run(); err != nil {
panic(err)
}
}
// alert shows a confirmation dialog.
func alert(pages *cview.Pages, id string, message string) {
func alert(panels *cview.Panels, id string, message string) {
modal := cview.NewModal()
modal.SetText(message)
modal.AddButtons([]string{"确定"})
modal.SetDoneFunc(func(buttonIndex int, buttonLabel string) {
pages.HidePage(id)
pages.RemovePage(id)
panels.Hide(id)
panels.Remove(id)
})
pages.AddPage(id, modal, false, true)
panels.Add(id, modal, false, true)
}

6
doc.go
View File

@ -33,7 +33,7 @@ The following widgets are available:
InputField - Single-line text entry field.
List - A navigable text list with optional keyboard shortcuts.
Modal - A centered window with a text message and one or more buttons.
Pages - A page based layout manager.
Panels - A panel based layout manager.
ProgressBar - Indicates the progress of an operation.
Table - A scrollable display of tabular data. Table cells, rows, or columns
may also be highlighted.
@ -107,8 +107,8 @@ square brackets. Examples:
A color tag changes the color of the characters following that color tag. This
applies to almost everything from box titles, list text, form item labels, to
table cells. In a TextView, this functionality has to be switched on explicitly.
See the TextView documentation for more information.
table cells. In a TextView, this functionality must be explicitly enabled. See
the TextView documentation for more information.
Color tags may contain not just the foreground (text) color but also the
background color and additional flags. In fact, the full definition of a color

368
pages.go
View File

@ -1,380 +1,60 @@
package cview
import (
"sync"
type page = panel
"github.com/gdamore/tcell/v2"
)
// page represents one page of a Pages object.
type page struct {
Name string // The page's name.
Item Primitive // The page's primitive.
Resize bool // Whether or not to resize the page when it is drawn.
Visible bool // Whether or not this page is visible.
}
// Pages is a container for other primitives often used as the application's
// root primitive. It allows to easily switch the visibility of the contained
// primitives.
// Pages is a wrapper around Panels. It is provided for backwards compatibility.
// Application developers should use Panels instead.
type Pages struct {
*Box
// The contained pages. (Visible) pages are drawn from back to front.
pages []*page
// We keep a reference to the function which allows us to set the focus to
// a newly visible page.
setFocus func(p Primitive)
// An optional handler which is called whenever the visibility or the order of
// pages changes.
changed func()
sync.RWMutex
*Panels
}
// NewPages returns a new Pages object.
// NewPages returns a new Panels object.
func NewPages() *Pages {
p := &Pages{
Box: NewBox(),
}
p.focus = p
return p
return &Pages{NewPanels()}
}
// SetChangedFunc sets a handler which is called whenever the visibility or the
// order of any visible pages changes. This can be used to redraw the pages.
func (p *Pages) SetChangedFunc(handler func()) {
p.Lock()
defer p.Unlock()
p.changed = handler
}
// GetPageCount returns the number of pages currently stored in this object.
// GetPageCount returns the number of panels currently stored in this object.
func (p *Pages) GetPageCount() int {
p.RLock()
defer p.RUnlock()
return len(p.pages)
return p.GetPanelCount()
}
// AddPage adds a new page with the given name and primitive. If there was
// previously a page with the same name, it is overwritten. Leaving the name
// empty may cause conflicts in other functions so always specify a non-empty
// name.
//
// Visible pages will be drawn in the order they were added (unless that order
// was changed in one of the other functions). If "resize" is set to true, the
// primitive will be set to the size available to the Pages primitive whenever
// the pages are drawn.
// AddPage adds a new panel with the given name and primitive.
func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, pg := range p.pages {
if pg.Name == name {
p.pages = append(p.pages[:index], p.pages[index+1:]...)
break
}
}
p.pages = append(p.pages, &page{Item: item, Name: name, Resize: resize, Visible: visible})
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
p.Add(name, item, resize, visible)
}
// AddAndSwitchToPage calls AddPage(), then SwitchToPage() on that newly added
// page.
// AddAndSwitchToPage calls Add(), then SwitchTo() on that newly added panel.
func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) {
p.AddPage(name, item, resize, true)
p.SwitchToPage(name)
p.AddAndSwitchTo(name, item, resize)
}
// RemovePage removes the page with the given name. If that page was the only
// visible page, visibility is assigned to the last page.
// RemovePage removes the panel with the given name.
func (p *Pages) RemovePage(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
var isVisible bool
for index, page := range p.pages {
if page.Name == name {
isVisible = page.Visible
p.pages = append(p.pages[:index], p.pages[index+1:]...)
if page.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if isVisible {
for index, page := range p.pages {
if index < len(p.pages)-1 {
if page.Visible {
break // There is a remaining visible page.
}
} else {
page.Visible = true // We need at least one visible page.
}
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
p.Remove(name)
}
// HasPage returns true if a page with the given name exists in this object.
// HasPage returns true if a panel with the given name exists in this object.
func (p *Pages) HasPage(name string) bool {
p.RLock()
defer p.RUnlock()
for _, page := range p.pages {
if page.Name == name {
return true
}
}
return false
return p.Has(name)
}
// ShowPage sets a page's visibility to "true" (in addition to any other pages
// which are already visible).
// ShowPage sets a panel's visibility to "true".
func (p *Pages) ShowPage(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, page := range p.pages {
if page.Name == name {
page.Visible = true
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
p.Show(name)
}
// HidePage sets a page's visibility to "false".
// HidePage sets a panel's visibility to "false".
func (p *Pages) HidePage(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, page := range p.pages {
if page.Name == name {
page.Visible = false
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
p.Hide(name)
}
// SwitchToPage sets a page's visibility to "true" and all other pages'
// SwitchToPage sets a panel's visibility to "true" and all other panels'
// visibility to "false".
func (p *Pages) SwitchToPage(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, page := range p.pages {
if page.Name == name {
page.Visible = true
} else {
page.Visible = false
}
}
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
p.SwitchTo(name)
}
// SendToFront changes the order of the pages such that the page with the given
// name comes last, causing it to be drawn last with the next update (if
// visible).
func (p *Pages) SendToFront(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, page := range p.pages {
if page.Name == name {
if index < len(p.pages)-1 {
p.pages = append(append(p.pages[:index], p.pages[index+1:]...), page)
}
if page.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// SendToBack changes the order of the pages such that the page with the given
// name comes first, causing it to be drawn first with the next update (if
// visible).
func (p *Pages) SendToBack(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, pg := range p.pages {
if pg.Name == name {
if index > 0 {
p.pages = append(append([]*page{pg}, p.pages[:index]...), p.pages[index+1:]...)
}
if pg.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// GetFrontPage returns the front-most visible page. If there are no visible
// pages, ("", nil) is returned.
// GetFrontPage returns the front-most visible panel.
func (p *Pages) GetFrontPage() (name string, item Primitive) {
p.RLock()
defer p.RUnlock()
for index := len(p.pages) - 1; index >= 0; index-- {
if p.pages[index].Visible {
return p.pages[index].Name, p.pages[index].Item
}
}
return
}
// HasFocus returns whether or not this primitive has focus.
func (p *Pages) HasFocus() bool {
p.RLock()
defer p.RUnlock()
for _, page := range p.pages {
if page.Item.GetFocusable().HasFocus() {
return true
}
}
return false
}
// Focus is called by the application when the primitive receives focus.
func (p *Pages) Focus(delegate func(p Primitive)) {
p.Lock()
defer p.Unlock()
if delegate == nil {
return // We cannot delegate so we cannot focus.
}
p.setFocus = delegate
var topItem Primitive
for _, page := range p.pages {
if page.Visible {
topItem = page.Item
}
}
if topItem != nil {
p.Unlock()
delegate(topItem)
p.Lock()
}
}
// Draw draws this primitive onto the screen.
func (p *Pages) Draw(screen tcell.Screen) {
p.Box.Draw(screen)
p.Lock()
defer p.Unlock()
for _, page := range p.pages {
if !page.Visible {
continue
}
if page.Resize {
x, y, width, height := p.GetInnerRect()
page.Item.SetRect(x, y, width, height)
}
page.Item.Draw(screen)
}
}
// MouseHandler returns the mouse handler for this primitive.
func (p *Pages) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return p.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
if !p.InRect(event.Position()) {
return false, nil
}
// Pass mouse events along to the last visible page item that takes it.
for index := len(p.pages) - 1; index >= 0; index-- {
page := p.pages[index]
if page.Visible {
consumed, capture = page.Item.MouseHandler()(action, event, setFocus)
if consumed {
return
}
}
}
return
})
return p.GetFrontPanel()
}

383
panels.go Normal file
View File

@ -0,0 +1,383 @@
package cview
import (
"sync"
"github.com/gdamore/tcell/v2"
)
// panel represents a single panel of a Panels object.
type panel struct {
Name string // The panel's name.
Item Primitive // The panel's primitive.
Resize bool // Whether or not to resize the panel when it is drawn.
Visible bool // Whether or not this panel is visible.
}
// Panels is a container for other primitives often used as the application's
// root primitive. It allows to easily switch the visibility of the contained
// primitives.
type Panels struct {
*Box
// The contained panels. (Visible) panels are drawn from back to front.
panels []*panel
// We keep a reference to the function which allows us to set the focus to
// a newly visible panel.
setFocus func(p Primitive)
// An optional handler which is called whenever the visibility or the order of
// panels changes.
changed func()
// TODO enable tabs
tabs *TextView
sync.RWMutex
}
// NewPanels returns a new Panels object.
func NewPanels() *Panels {
p := &Panels{
Box: NewBox(),
}
p.focus = p
return p
}
// SetChangedFunc sets a handler which is called whenever the visibility or the
// order of any visible panels changes. This can be used to redraw the panels.
func (p *Panels) SetChangedFunc(handler func()) {
p.Lock()
defer p.Unlock()
p.changed = handler
}
// GetPanelCount returns the number of panels currently stored in this object.
func (p *Panels) GetPanelCount() int {
p.RLock()
defer p.RUnlock()
return len(p.panels)
}
// Add adds a new panel with the given name and primitive. If there was
// previously a panel with the same name, it is overwritten. Leaving the name
// empty may cause conflicts in other functions so always specify a non-empty
// name.
//
// Visible panels will be drawn in the order they were added (unless that order
// was changed in one of the other functions). If "resize" is set to true, the
// primitive will be set to the size available to the Panels primitive whenever
// the panels are drawn.
func (p *Panels) Add(name string, item Primitive, resize, visible bool) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, pg := range p.panels {
if pg.Name == name {
p.panels = append(p.panels[:index], p.panels[index+1:]...)
break
}
}
p.panels = append(p.panels, &panel{Item: item, Name: name, Resize: resize, Visible: visible})
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// AddAndSwitchTo calls Add(), then SwitchTo() on the newly added panel.
func (p *Panels) AddAndSwitchTo(name string, item Primitive, resize bool) {
p.Add(name, item, resize, true)
p.SwitchTo(name)
}
// Remove removes the panel with the given name. If that panel was the only
// visible panel, visibility is assigned to the last panel.
func (p *Panels) Remove(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
var isVisible bool
for index, panel := range p.panels {
if panel.Name == name {
isVisible = panel.Visible
p.panels = append(p.panels[:index], p.panels[index+1:]...)
if panel.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if isVisible {
for index, panel := range p.panels {
if index < len(p.panels)-1 {
if panel.Visible {
break // There is a remaining visible panel.
}
} else {
panel.Visible = true // We need at least one visible panel.
}
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// Has returns true if a panel with the given name exists in this object.
func (p *Panels) Has(name string) bool {
p.RLock()
defer p.RUnlock()
for _, panel := range p.panels {
if panel.Name == name {
return true
}
}
return false
}
// Show sets a panel's visibility to "true" (in addition to any other panels
// which are already visible).
func (p *Panels) Show(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, panel := range p.panels {
if panel.Name == name {
panel.Visible = true
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// Hide sets a panel's visibility to "false".
func (p *Panels) Hide(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, panel := range p.panels {
if panel.Name == name {
panel.Visible = false
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// SwitchTo sets a panel's visibility to "true" and all other panels'
// visibility to "false".
func (p *Panels) SwitchTo(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for _, panel := range p.panels {
if panel.Name == name {
panel.Visible = true
} else {
panel.Visible = false
}
}
if p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// SendToFront changes the order of the panels such that the panel with the given
// name comes last, causing it to be drawn last with the next update (if
// visible).
func (p *Panels) SendToFront(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, panel := range p.panels {
if panel.Name == name {
if index < len(p.panels)-1 {
p.panels = append(append(p.panels[:index], p.panels[index+1:]...), panel)
}
if panel.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// SendToBack changes the order of the panels such that the panel with the given
// name comes first, causing it to be drawn first with the next update (if
// visible).
func (p *Panels) SendToBack(name string) {
hasFocus := p.HasFocus()
p.Lock()
defer p.Unlock()
for index, pg := range p.panels {
if pg.Name == name {
if index > 0 {
p.panels = append(append([]*panel{pg}, p.panels[:index]...), p.panels[index+1:]...)
}
if pg.Visible && p.changed != nil {
p.Unlock()
p.changed()
p.Lock()
}
break
}
}
if hasFocus {
p.Unlock()
p.Focus(p.setFocus)
p.Lock()
}
}
// GetFrontPanel returns the front-most visible panel. If there are no visible
// panels, ("", nil) is returned.
func (p *Panels) GetFrontPanel() (name string, item Primitive) {
p.RLock()
defer p.RUnlock()
for index := len(p.panels) - 1; index >= 0; index-- {
if p.panels[index].Visible {
return p.panels[index].Name, p.panels[index].Item
}
}
return
}
// HasFocus returns whether or not this primitive has focus.
func (p *Panels) HasFocus() bool {
p.RLock()
defer p.RUnlock()
for _, panel := range p.panels {
if panel.Item.GetFocusable().HasFocus() {
return true
}
}
return false
}
// Focus is called by the application when the primitive receives focus.
func (p *Panels) Focus(delegate func(p Primitive)) {
p.Lock()
defer p.Unlock()
if delegate == nil {
return // We cannot delegate so we cannot focus.
}
p.setFocus = delegate
var topItem Primitive
for _, panel := range p.panels {
if panel.Visible {
topItem = panel.Item
}
}
if topItem != nil {
p.Unlock()
delegate(topItem)
p.Lock()
}
}
// Draw draws this primitive onto the screen.
func (p *Panels) Draw(screen tcell.Screen) {
p.Box.Draw(screen)
p.Lock()
defer p.Unlock()
x, y, width, height := p.GetInnerRect()
for _, panel := range p.panels {
if !panel.Visible {
continue
}
if panel.Resize {
panel.Item.SetRect(x, y, width, height)
}
panel.Item.Draw(screen)
}
}
// MouseHandler returns the mouse handler for this primitive.
func (p *Panels) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
return p.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
if !p.InRect(event.Position()) {
return false, nil
}
// Pass mouse events along to the last visible panel item that takes it.
for index := len(p.panels) - 1; index >= 0; index-- {
panel := p.panels[index]
if panel.Visible {
consumed, capture = panel.Item.MouseHandler()(action, event, setFocus)
if consumed {
return
}
}
}
return
})
}