1package tview 2 3import ( 4 "github.com/gdamore/tcell" 5) 6 7// page represents one page of a Pages object. 8type page struct { 9 Name string // The page's name. 10 Item Primitive // The page's primitive. 11 Resize bool // Whether or not to resize the page when it is drawn. 12 Visible bool // Whether or not this page is visible. 13} 14 15// Pages is a container for other primitives often used as the application's 16// root primitive. It allows to easily switch the visibility of the contained 17// primitives. 18// 19// See https://github.com/rivo/tview/wiki/Pages for an example. 20type Pages struct { 21 *Box 22 23 // The contained pages. 24 pages []*page 25 26 // We keep a reference to the function which allows us to set the focus to 27 // a newly visible page. 28 setFocus func(p Primitive) 29 30 // An optional handler which is called whenever the visibility or the order of 31 // pages changes. 32 changed func() 33} 34 35// NewPages returns a new Pages object. 36func NewPages() *Pages { 37 p := &Pages{ 38 Box: NewBox(), 39 } 40 p.focus = p 41 return p 42} 43 44// SetChangedFunc sets a handler which is called whenever the visibility or the 45// order of any visible pages changes. This can be used to redraw the pages. 46func (p *Pages) SetChangedFunc(handler func()) *Pages { 47 p.changed = handler 48 return p 49} 50 51// GetPageCount returns the number of pages currently stored in this object. 52func (p *Pages) GetPageCount() int { 53 return len(p.pages) 54} 55 56// AddPage adds a new page with the given name and primitive. If there was 57// previously a page with the same name, it is overwritten. Leaving the name 58// empty may cause conflicts in other functions so always specify a non-empty 59// name. 60// 61// Visible pages will be drawn in the order they were added (unless that order 62// was changed in one of the other functions). If "resize" is set to true, the 63// primitive will be set to the size available to the Pages primitive whenever 64// the pages are drawn. 65func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) *Pages { 66 hasFocus := p.HasFocus() 67 for index, pg := range p.pages { 68 if pg.Name == name { 69 p.pages = append(p.pages[:index], p.pages[index+1:]...) 70 break 71 } 72 } 73 p.pages = append(p.pages, &page{Item: item, Name: name, Resize: resize, Visible: visible}) 74 if p.changed != nil { 75 p.changed() 76 } 77 if hasFocus { 78 p.Focus(p.setFocus) 79 } 80 return p 81} 82 83// AddAndSwitchToPage calls AddPage(), then SwitchToPage() on that newly added 84// page. 85func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) *Pages { 86 p.AddPage(name, item, resize, true) 87 p.SwitchToPage(name) 88 return p 89} 90 91// RemovePage removes the page with the given name. If that page was the only 92// visible page, visibility is assigned to the last page. 93func (p *Pages) RemovePage(name string) *Pages { 94 var isVisible bool 95 hasFocus := p.HasFocus() 96 for index, page := range p.pages { 97 if page.Name == name { 98 isVisible = page.Visible 99 p.pages = append(p.pages[:index], p.pages[index+1:]...) 100 if page.Visible && p.changed != nil { 101 p.changed() 102 } 103 break 104 } 105 } 106 if isVisible { 107 for index, page := range p.pages { 108 if index < len(p.pages)-1 { 109 if page.Visible { 110 break // There is a remaining visible page. 111 } 112 } else { 113 page.Visible = true // We need at least one visible page. 114 } 115 } 116 } 117 if hasFocus { 118 p.Focus(p.setFocus) 119 } 120 return p 121} 122 123// HasPage returns true if a page with the given name exists in this object. 124func (p *Pages) HasPage(name string) bool { 125 for _, page := range p.pages { 126 if page.Name == name { 127 return true 128 } 129 } 130 return false 131} 132 133// ShowPage sets a page's visibility to "true" (in addition to any other pages 134// which are already visible). 135func (p *Pages) ShowPage(name string) *Pages { 136 for _, page := range p.pages { 137 if page.Name == name { 138 page.Visible = true 139 if p.changed != nil { 140 p.changed() 141 } 142 break 143 } 144 } 145 if p.HasFocus() { 146 p.Focus(p.setFocus) 147 } 148 return p 149} 150 151// HidePage sets a page's visibility to "false". 152func (p *Pages) HidePage(name string) *Pages { 153 for _, page := range p.pages { 154 if page.Name == name { 155 page.Visible = false 156 if p.changed != nil { 157 p.changed() 158 } 159 break 160 } 161 } 162 if p.HasFocus() { 163 p.Focus(p.setFocus) 164 } 165 return p 166} 167 168// SwitchToPage sets a page's visibility to "true" and all other pages' 169// visibility to "false". 170func (p *Pages) SwitchToPage(name string) *Pages { 171 for _, page := range p.pages { 172 if page.Name == name { 173 page.Visible = true 174 } else { 175 page.Visible = false 176 } 177 } 178 if p.changed != nil { 179 p.changed() 180 } 181 if p.HasFocus() { 182 p.Focus(p.setFocus) 183 } 184 return p 185} 186 187// SendToFront changes the order of the pages such that the page with the given 188// name comes last, causing it to be drawn last with the next update (if 189// visible). 190func (p *Pages) SendToFront(name string) *Pages { 191 for index, page := range p.pages { 192 if page.Name == name { 193 if index < len(p.pages)-1 { 194 p.pages = append(append(p.pages[:index], p.pages[index+1:]...), page) 195 } 196 if page.Visible && p.changed != nil { 197 p.changed() 198 } 199 break 200 } 201 } 202 if p.HasFocus() { 203 p.Focus(p.setFocus) 204 } 205 return p 206} 207 208// SendToBack changes the order of the pages such that the page with the given 209// name comes first, causing it to be drawn first with the next update (if 210// visible). 211func (p *Pages) SendToBack(name string) *Pages { 212 for index, pg := range p.pages { 213 if pg.Name == name { 214 if index > 0 { 215 p.pages = append(append([]*page{pg}, p.pages[:index]...), p.pages[index+1:]...) 216 } 217 if pg.Visible && p.changed != nil { 218 p.changed() 219 } 220 break 221 } 222 } 223 if p.HasFocus() { 224 p.Focus(p.setFocus) 225 } 226 return p 227} 228 229// HasFocus returns whether or not this primitive has focus. 230func (p *Pages) HasFocus() bool { 231 for _, page := range p.pages { 232 if page.Item.GetFocusable().HasFocus() { 233 return true 234 } 235 } 236 return false 237} 238 239// Focus is called by the application when the primitive receives focus. 240func (p *Pages) Focus(delegate func(p Primitive)) { 241 if delegate == nil { 242 return // We cannot delegate so we cannot focus. 243 } 244 p.setFocus = delegate 245 var topItem Primitive 246 for _, page := range p.pages { 247 if page.Visible { 248 topItem = page.Item 249 } 250 } 251 if topItem != nil { 252 delegate(topItem) 253 } 254} 255 256// Draw draws this primitive onto the screen. 257func (p *Pages) Draw(screen tcell.Screen) { 258 p.Box.Draw(screen) 259 for _, page := range p.pages { 260 if !page.Visible { 261 continue 262 } 263 if page.Resize { 264 x, y, width, height := p.GetInnerRect() 265 page.Item.SetRect(x, y, width, height) 266 } 267 page.Item.Draw(screen) 268 } 269} 270