...

Source file src/gitlab.com/tslocum/cview/panels.go

Documentation: gitlab.com/tslocum/cview

     1  package cview
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/gdamore/tcell/v2"
     7  )
     8  
     9  // panel represents a single panel of a Panels object.
    10  type panel struct {
    11  	Name    string    // The panel's name.
    12  	Item    Primitive // The panel's primitive.
    13  	Resize  bool      // Whether or not to resize the panel when it is drawn.
    14  	Visible bool      // Whether or not this panel is visible.
    15  }
    16  
    17  // Panels is a container for other primitives often used as the application's
    18  // root primitive. It allows to easily switch the visibility of the contained
    19  // primitives.
    20  type Panels struct {
    21  	*Box
    22  
    23  	// The contained panels. (Visible) panels are drawn from back to front.
    24  	panels []*panel
    25  
    26  	// We keep a reference to the function which allows us to set the focus to
    27  	// a newly visible panel.
    28  	setFocus func(p Primitive)
    29  
    30  	// An optional handler which is called whenever the visibility or the order of
    31  	// panels changes.
    32  	changed func()
    33  
    34  	sync.RWMutex
    35  }
    36  
    37  // NewPanels returns a new Panels object.
    38  func NewPanels() *Panels {
    39  	p := &Panels{
    40  		Box: NewBox(),
    41  	}
    42  	p.focus = p
    43  	return p
    44  }
    45  
    46  // SetChangedFunc sets a handler which is called whenever the visibility or the
    47  // order of any visible panels changes. This can be used to redraw the panels.
    48  func (p *Panels) SetChangedFunc(handler func()) {
    49  	p.Lock()
    50  	defer p.Unlock()
    51  
    52  	p.changed = handler
    53  }
    54  
    55  // GetPanelCount returns the number of panels currently stored in this object.
    56  func (p *Panels) GetPanelCount() int {
    57  	p.RLock()
    58  	defer p.RUnlock()
    59  
    60  	return len(p.panels)
    61  }
    62  
    63  // AddPanel adds a new panel with the given name and primitive. If there was
    64  // previously a panel with the same name, it is overwritten. Leaving the name
    65  // empty may cause conflicts in other functions so always specify a non-empty
    66  // name.
    67  //
    68  // Visible panels will be drawn in the order they were added (unless that order
    69  // was changed in one of the other functions). If "resize" is set to true, the
    70  // primitive will be set to the size available to the Panels primitive whenever
    71  // the panels are drawn.
    72  func (p *Panels) AddPanel(name string, item Primitive, resize, visible bool) {
    73  	hasFocus := p.HasFocus()
    74  
    75  	p.Lock()
    76  	defer p.Unlock()
    77  
    78  	for index, pg := range p.panels {
    79  		if pg.Name == name {
    80  			p.panels = append(p.panels[:index], p.panels[index+1:]...)
    81  			break
    82  		}
    83  	}
    84  	p.panels = append(p.panels, &panel{Item: item, Name: name, Resize: resize, Visible: visible})
    85  	if p.changed != nil {
    86  		p.Unlock()
    87  		p.changed()
    88  		p.Lock()
    89  	}
    90  	if hasFocus {
    91  		p.Unlock()
    92  		p.Focus(p.setFocus)
    93  		p.Lock()
    94  	}
    95  }
    96  
    97  // RemovePanel removes the panel with the given name. If that panel was the only
    98  // visible panel, visibility is assigned to the last panel.
    99  func (p *Panels) RemovePanel(name string) {
   100  	hasFocus := p.HasFocus()
   101  
   102  	p.Lock()
   103  	defer p.Unlock()
   104  
   105  	var isVisible bool
   106  	for index, panel := range p.panels {
   107  		if panel.Name == name {
   108  			isVisible = panel.Visible
   109  			p.panels = append(p.panels[:index], p.panels[index+1:]...)
   110  			if panel.Visible && p.changed != nil {
   111  				p.Unlock()
   112  				p.changed()
   113  				p.Lock()
   114  			}
   115  			break
   116  		}
   117  	}
   118  	if isVisible {
   119  		for index, panel := range p.panels {
   120  			if index < len(p.panels)-1 {
   121  				if panel.Visible {
   122  					break // There is a remaining visible panel.
   123  				}
   124  			} else {
   125  				panel.Visible = true // We need at least one visible panel.
   126  			}
   127  		}
   128  	}
   129  	if hasFocus {
   130  		p.Unlock()
   131  		p.Focus(p.setFocus)
   132  		p.Lock()
   133  	}
   134  }
   135  
   136  // HasPanel returns true if a panel with the given name exists in this object.
   137  func (p *Panels) HasPanel(name string) bool {
   138  	p.RLock()
   139  	defer p.RUnlock()
   140  
   141  	for _, panel := range p.panels {
   142  		if panel.Name == name {
   143  			return true
   144  		}
   145  	}
   146  	return false
   147  }
   148  
   149  // ShowPanel sets a panel's visibility to "true" (in addition to any other panels
   150  // which are already visible).
   151  func (p *Panels) ShowPanel(name string) {
   152  	hasFocus := p.HasFocus()
   153  
   154  	p.Lock()
   155  	defer p.Unlock()
   156  
   157  	for _, panel := range p.panels {
   158  		if panel.Name == name {
   159  			panel.Visible = true
   160  			if p.changed != nil {
   161  				p.Unlock()
   162  				p.changed()
   163  				p.Lock()
   164  			}
   165  			break
   166  		}
   167  	}
   168  	if hasFocus {
   169  		p.Unlock()
   170  		p.Focus(p.setFocus)
   171  		p.Lock()
   172  	}
   173  }
   174  
   175  // HidePanel sets a panel's visibility to "false".
   176  func (p *Panels) HidePanel(name string) {
   177  	hasFocus := p.HasFocus()
   178  
   179  	p.Lock()
   180  	defer p.Unlock()
   181  
   182  	for _, panel := range p.panels {
   183  		if panel.Name == name {
   184  			panel.Visible = false
   185  			if p.changed != nil {
   186  				p.Unlock()
   187  				p.changed()
   188  				p.Lock()
   189  			}
   190  			break
   191  		}
   192  	}
   193  	if hasFocus {
   194  		p.Unlock()
   195  		p.Focus(p.setFocus)
   196  		p.Lock()
   197  	}
   198  }
   199  
   200  // SetCurrentPanel sets a panel's visibility to "true" and all other panels'
   201  // visibility to "false".
   202  func (p *Panels) SetCurrentPanel(name string) {
   203  	hasFocus := p.HasFocus()
   204  
   205  	p.Lock()
   206  	defer p.Unlock()
   207  
   208  	for _, panel := range p.panels {
   209  		if panel.Name == name {
   210  			panel.Visible = true
   211  		} else {
   212  			panel.Visible = false
   213  		}
   214  	}
   215  	if p.changed != nil {
   216  		p.Unlock()
   217  		p.changed()
   218  		p.Lock()
   219  	}
   220  	if hasFocus {
   221  		p.Unlock()
   222  		p.Focus(p.setFocus)
   223  		p.Lock()
   224  	}
   225  }
   226  
   227  // SendToFront changes the order of the panels such that the panel with the given
   228  // name comes last, causing it to be drawn last with the next update (if
   229  // visible).
   230  func (p *Panels) SendToFront(name string) {
   231  	hasFocus := p.HasFocus()
   232  
   233  	p.Lock()
   234  	defer p.Unlock()
   235  
   236  	for index, panel := range p.panels {
   237  		if panel.Name == name {
   238  			if index < len(p.panels)-1 {
   239  				p.panels = append(append(p.panels[:index], p.panels[index+1:]...), panel)
   240  			}
   241  			if panel.Visible && p.changed != nil {
   242  				p.Unlock()
   243  				p.changed()
   244  				p.Lock()
   245  			}
   246  			break
   247  		}
   248  	}
   249  	if hasFocus {
   250  		p.Unlock()
   251  		p.Focus(p.setFocus)
   252  		p.Lock()
   253  	}
   254  }
   255  
   256  // SendToBack changes the order of the panels such that the panel with the given
   257  // name comes first, causing it to be drawn first with the next update (if
   258  // visible).
   259  func (p *Panels) SendToBack(name string) {
   260  	hasFocus := p.HasFocus()
   261  
   262  	p.Lock()
   263  	defer p.Unlock()
   264  
   265  	for index, pg := range p.panels {
   266  		if pg.Name == name {
   267  			if index > 0 {
   268  				p.panels = append(append([]*panel{pg}, p.panels[:index]...), p.panels[index+1:]...)
   269  			}
   270  			if pg.Visible && p.changed != nil {
   271  				p.Unlock()
   272  				p.changed()
   273  				p.Lock()
   274  			}
   275  			break
   276  		}
   277  	}
   278  	if hasFocus {
   279  		p.Unlock()
   280  		p.Focus(p.setFocus)
   281  		p.Lock()
   282  	}
   283  }
   284  
   285  // GetFrontPanel returns the front-most visible panel. If there are no visible
   286  // panels, ("", nil) is returned.
   287  func (p *Panels) GetFrontPanel() (name string, item Primitive) {
   288  	p.RLock()
   289  	defer p.RUnlock()
   290  
   291  	for index := len(p.panels) - 1; index >= 0; index-- {
   292  		if p.panels[index].Visible {
   293  			return p.panels[index].Name, p.panels[index].Item
   294  		}
   295  	}
   296  	return
   297  }
   298  
   299  // HasFocus returns whether or not this primitive has focus.
   300  func (p *Panels) HasFocus() bool {
   301  	p.RLock()
   302  	defer p.RUnlock()
   303  
   304  	for _, panel := range p.panels {
   305  		if panel.Item.GetFocusable().HasFocus() {
   306  			return true
   307  		}
   308  	}
   309  	return false
   310  }
   311  
   312  // Focus is called by the application when the primitive receives focus.
   313  func (p *Panels) Focus(delegate func(p Primitive)) {
   314  	p.Lock()
   315  	defer p.Unlock()
   316  
   317  	if delegate == nil {
   318  		return // We cannot delegate so we cannot focus.
   319  	}
   320  	p.setFocus = delegate
   321  	var topItem Primitive
   322  	for _, panel := range p.panels {
   323  		if panel.Visible {
   324  			topItem = panel.Item
   325  		}
   326  	}
   327  	if topItem != nil {
   328  		p.Unlock()
   329  		delegate(topItem)
   330  		p.Lock()
   331  	}
   332  }
   333  
   334  // Draw draws this primitive onto the screen.
   335  func (p *Panels) Draw(screen tcell.Screen) {
   336  	if !p.GetVisible() {
   337  		return
   338  	}
   339  
   340  	p.Box.Draw(screen)
   341  
   342  	p.Lock()
   343  	defer p.Unlock()
   344  
   345  	x, y, width, height := p.GetInnerRect()
   346  
   347  	for _, panel := range p.panels {
   348  		if !panel.Visible {
   349  			continue
   350  		}
   351  		if panel.Resize {
   352  			panel.Item.SetRect(x, y, width, height)
   353  		}
   354  		panel.Item.Draw(screen)
   355  	}
   356  }
   357  
   358  // MouseHandler returns the mouse handler for this primitive.
   359  func (p *Panels) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   360  	return p.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   361  		if !p.InRect(event.Position()) {
   362  			return false, nil
   363  		}
   364  
   365  		// Pass mouse events along to the last visible panel item that takes it.
   366  		for index := len(p.panels) - 1; index >= 0; index-- {
   367  			panel := p.panels[index]
   368  			if panel.Visible {
   369  				consumed, capture = panel.Item.MouseHandler()(action, event, setFocus)
   370  				if consumed {
   371  					return
   372  				}
   373  			}
   374  		}
   375  
   376  		return
   377  	})
   378  }
   379  
   380  // Support backwards compatibility with Pages.
   381  type page = panel
   382  
   383  // Pages is a wrapper around Panels.
   384  //
   385  // Deprecated: This type is provided for backwards compatibility.
   386  // Developers should use Panels instead.
   387  type Pages struct {
   388  	*Panels
   389  }
   390  
   391  // NewPages returns a new Panels object.
   392  //
   393  // Deprecated: This function is provided for backwards compatibility.
   394  // Developers should use NewPanels instead.
   395  func NewPages() *Pages {
   396  	return &Pages{NewPanels()}
   397  }
   398  
   399  // GetPageCount returns the number of panels currently stored in this object.
   400  func (p *Pages) GetPageCount() int {
   401  	return p.GetPanelCount()
   402  }
   403  
   404  // AddPage adds a new panel with the given name and primitive.
   405  func (p *Pages) AddPage(name string, item Primitive, resize, visible bool) {
   406  	p.AddPanel(name, item, resize, visible)
   407  }
   408  
   409  // AddAndSwitchToPage calls Add(), then SwitchTo() on that newly added panel.
   410  func (p *Pages) AddAndSwitchToPage(name string, item Primitive, resize bool) {
   411  	p.AddPanel(name, item, resize, true)
   412  	p.SetCurrentPanel(name)
   413  }
   414  
   415  // RemovePage removes the panel with the given name.
   416  func (p *Pages) RemovePage(name string) {
   417  	p.RemovePanel(name)
   418  }
   419  
   420  // HasPage returns true if a panel with the given name exists in this object.
   421  func (p *Pages) HasPage(name string) bool {
   422  	return p.HasPanel(name)
   423  }
   424  
   425  // ShowPage sets a panel's visibility to "true".
   426  func (p *Pages) ShowPage(name string) {
   427  	p.ShowPanel(name)
   428  }
   429  
   430  // HidePage sets a panel's visibility to "false".
   431  func (p *Pages) HidePage(name string) {
   432  	p.HidePanel(name)
   433  }
   434  
   435  // SwitchToPage sets a panel's visibility to "true" and all other panels'
   436  // visibility to "false".
   437  func (p *Pages) SwitchToPage(name string) {
   438  	p.SetCurrentPanel(name)
   439  }
   440  
   441  // GetFrontPage returns the front-most visible panel.
   442  func (p *Pages) GetFrontPage() (name string, item Primitive) {
   443  	return p.GetFrontPanel()
   444  }
   445  

View as plain text