...

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

Documentation: github.com/rivo/tview

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

View as plain text