nt

A sensible note-taking program
git clone git://git.laack.co/nt.git
Log | Files | Refs | README

frame.go (6252B)


      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 
    224 // PasteHandler returns the handler for this primitive.
    225 func (f *Frame) PasteHandler() func(pastedText string, setFocus func(p Primitive)) {
    226 	return f.WrapPasteHandler(func(pastedText string, setFocus func(p Primitive)) {
    227 		if f.primitive == nil {
    228 			return
    229 		}
    230 		if handler := f.primitive.PasteHandler(); handler != nil {
    231 			handler(pastedText, setFocus)
    232 			return
    233 		}
    234 	})
    235 }