...

Source file src/code.rocketnine.space/tslocum/cview/frame.go

Documentation: code.rocketnine.space/tslocum/cview

     1  package cview
     2  
     3  import (
     4  	"sync"
     5  
     6  	"github.com/gdamore/tcell/v2"
     7  )
     8  
     9  // frameText holds information about a line of text shown in the frame.
    10  type frameText struct {
    11  	Text   string      // The text to be displayed.
    12  	Header bool        // true = place in header, false = place in footer.
    13  	Align  int         // One of the Align constants.
    14  	Color  tcell.Color // The text color.
    15  }
    16  
    17  // Frame is a wrapper which adds space around another primitive. In addition,
    18  // the top area (header) and the bottom area (footer) may also contain text.
    19  type Frame struct {
    20  	*Box
    21  
    22  	// The contained primitive.
    23  	primitive Primitive
    24  
    25  	// The lines of text to be displayed.
    26  	text []*frameText
    27  
    28  	// Border spacing.
    29  	top, bottom, header, footer, left, right int
    30  
    31  	sync.RWMutex
    32  }
    33  
    34  // NewFrame returns a new frame around the given primitive. The primitive's
    35  // size will be changed to fit within this frame.
    36  func NewFrame(primitive Primitive) *Frame {
    37  	box := NewBox()
    38  
    39  	f := &Frame{
    40  		Box:       box,
    41  		primitive: primitive,
    42  		top:       1,
    43  		bottom:    1,
    44  		header:    1,
    45  		footer:    1,
    46  		left:      1,
    47  		right:     1,
    48  	}
    49  
    50  	f.focus = f
    51  
    52  	return f
    53  }
    54  
    55  // AddText adds text to the frame. Set "header" to true if the text is to appear
    56  // in the header, above the contained primitive. Set it to false for it to
    57  // appear in the footer, below the contained primitive. "align" must be one of
    58  // the Align constants. Rows in the header are printed top to bottom, rows in
    59  // the footer are printed bottom to top. Note that long text can overlap as
    60  // different alignments will be placed on the same row.
    61  func (f *Frame) AddText(text string, header bool, align int, color tcell.Color) {
    62  	f.Lock()
    63  	defer f.Unlock()
    64  
    65  	f.text = append(f.text, &frameText{
    66  		Text:   text,
    67  		Header: header,
    68  		Align:  align,
    69  		Color:  color,
    70  	})
    71  }
    72  
    73  // Clear removes all text from the frame.
    74  func (f *Frame) Clear() {
    75  	f.Lock()
    76  	defer f.Unlock()
    77  
    78  	f.text = nil
    79  }
    80  
    81  // SetBorders sets the width of the frame borders as well as "header" and
    82  // "footer", the vertical space between the header and footer text and the
    83  // contained primitive (does not apply if there is no text).
    84  func (f *Frame) SetBorders(top, bottom, header, footer, left, right int) {
    85  	f.Lock()
    86  	defer f.Unlock()
    87  
    88  	f.top, f.bottom, f.header, f.footer, f.left, f.right = top, bottom, header, footer, left, right
    89  }
    90  
    91  // Draw draws this primitive onto the screen.
    92  func (f *Frame) Draw(screen tcell.Screen) {
    93  	if !f.GetVisible() {
    94  		return
    95  	}
    96  
    97  	f.Box.Draw(screen)
    98  
    99  	f.Lock()
   100  	defer f.Unlock()
   101  
   102  	// Calculate start positions.
   103  	x, top, width, height := f.GetInnerRect()
   104  	bottom := top + height - 1
   105  	x += f.left
   106  	top += f.top
   107  	bottom -= f.bottom
   108  	width -= f.left + f.right
   109  	if width <= 0 || top >= bottom {
   110  		return // No space left.
   111  	}
   112  
   113  	// Draw text.
   114  	var rows [6]int // top-left, top-center, top-right, bottom-left, bottom-center, bottom-right.
   115  	topMax := top
   116  	bottomMin := bottom
   117  	for _, text := range f.text {
   118  		// Where do we place this text?
   119  		var y int
   120  		if text.Header {
   121  			y = top + rows[text.Align]
   122  			rows[text.Align]++
   123  			if y >= bottomMin {
   124  				continue
   125  			}
   126  			if y+1 > topMax {
   127  				topMax = y + 1
   128  			}
   129  		} else {
   130  			y = bottom - rows[3+text.Align]
   131  			rows[3+text.Align]++
   132  			if y <= topMax {
   133  				continue
   134  			}
   135  			if y-1 < bottomMin {
   136  				bottomMin = y - 1
   137  			}
   138  		}
   139  
   140  		// Draw text.
   141  		Print(screen, []byte(text.Text), x, y, width, text.Align, text.Color)
   142  	}
   143  
   144  	// Set the size of the contained primitive.
   145  	if topMax > top {
   146  		top = topMax + f.header
   147  	}
   148  	if bottomMin < bottom {
   149  		bottom = bottomMin - f.footer
   150  	}
   151  	if top > bottom {
   152  		return // No space for the primitive.
   153  	}
   154  	f.primitive.SetRect(x, top, width, bottom+1-top)
   155  
   156  	// Finally, draw the contained primitive.
   157  	f.primitive.Draw(screen)
   158  }
   159  
   160  // Focus is called when this primitive receives focus.
   161  func (f *Frame) Focus(delegate func(p Primitive)) {
   162  	f.Lock()
   163  	primitive := f.primitive
   164  	defer f.Unlock()
   165  
   166  	delegate(primitive)
   167  }
   168  
   169  // HasFocus returns whether or not this primitive has focus.
   170  func (f *Frame) HasFocus() bool {
   171  	f.RLock()
   172  	defer f.RUnlock()
   173  
   174  	focusable, ok := f.primitive.(Focusable)
   175  	if ok {
   176  		return focusable.HasFocus()
   177  	}
   178  	return false
   179  }
   180  
   181  // MouseHandler returns the mouse handler for this primitive.
   182  func (f *Frame) MouseHandler() func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   183  	return f.WrapMouseHandler(func(action MouseAction, event *tcell.EventMouse, setFocus func(p Primitive)) (consumed bool, capture Primitive) {
   184  		if !f.InRect(event.Position()) {
   185  			return false, nil
   186  		}
   187  
   188  		// Pass mouse events on to contained primitive.
   189  		return f.primitive.MouseHandler()(action, event, setFocus)
   190  	})
   191  }
   192  

View as plain text