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// GetFrontPage returns the front-most visible page. If there are no visible 230// pages, ("", nil) is returned. 231func (p *Pages) GetFrontPage() (name string, item Primitive) { 232 for index := len(p.pages) - 1; index >= 0; index-- { 233 if p.pages[index].Visible { 234 return p.pages[index].Name, p.pages[index].Item 235 } 236 } 237 return 238} 239 240// HasFocus returns whether or not this primitive has focus. 241func (p *Pages) HasFocus() bool { 242 for _, page := range p.pages { 243 if page.Item.GetFocusable().HasFocus() { 244 return true 245 } 246 } 247 return false 248} 249 250// Focus is called by the application when the primitive receives focus. 251func (p *Pages) Focus(delegate func(p Primitive)) { 252 if delegate == nil { 253 return // We cannot delegate so we cannot focus. 254 } 255 p.setFocus = delegate 256 var topItem Primitive 257 for _, page := range p.pages { 258 if page.Visible { 259 topItem = page.Item 260 } 261 } 262 if topItem != nil { 263 delegate(topItem) 264 } 265} 266 267// Draw draws this primitive onto the screen. 268func (p *Pages) Draw(screen tcell.Screen) { 269 p.Box.Draw(screen) 270 for _, page := range p.pages { 271 if !page.Visible { 272 continue 273 } 274 if page.Resize { 275 x, y, width, height := p.GetInnerRect() 276 page.Item.SetRect(x, y, width, height) 277 } 278 page.Item.Draw(screen) 279 } 280} 281