frame.go 4.4 KB

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