...

Source file src/github.com/rivo/tview/pages.go

Documentation: github.com/rivo/tview

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

View as plain text