block.go 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. // Copyright 2017 Zack Guo <zack.y.guo@gmail.com>. All rights reserved.
  2. // Use of this source code is governed by a MIT license that can
  3. // be found in the LICENSE file.
  4. package termui
  5. import "image"
  6. // Hline is a horizontal line.
  7. type Hline struct {
  8. X int
  9. Y int
  10. Len int
  11. Fg Attribute
  12. Bg Attribute
  13. }
  14. // Vline is a vertical line.
  15. type Vline struct {
  16. X int
  17. Y int
  18. Len int
  19. Fg Attribute
  20. Bg Attribute
  21. }
  22. // Buffer draws a horizontal line.
  23. func (l Hline) Buffer() Buffer {
  24. if l.Len <= 0 {
  25. return NewBuffer()
  26. }
  27. return NewFilledBuffer(l.X, l.Y, l.X+l.Len, l.Y+1, HORIZONTAL_LINE, l.Fg, l.Bg)
  28. }
  29. // Buffer draws a vertical line.
  30. func (l Vline) Buffer() Buffer {
  31. if l.Len <= 0 {
  32. return NewBuffer()
  33. }
  34. return NewFilledBuffer(l.X, l.Y, l.X+1, l.Y+l.Len, VERTICAL_LINE, l.Fg, l.Bg)
  35. }
  36. // Buffer draws a box border.
  37. func (b Block) drawBorder(buf Buffer) {
  38. if !b.Border {
  39. return
  40. }
  41. min := b.area.Min
  42. max := b.area.Max
  43. x0 := min.X
  44. y0 := min.Y
  45. x1 := max.X - 1
  46. y1 := max.Y - 1
  47. // draw lines
  48. if b.BorderTop {
  49. buf.Merge(Hline{x0, y0, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
  50. }
  51. if b.BorderBottom {
  52. buf.Merge(Hline{x0, y1, x1 - x0, b.BorderFg, b.BorderBg}.Buffer())
  53. }
  54. if b.BorderLeft {
  55. buf.Merge(Vline{x0, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
  56. }
  57. if b.BorderRight {
  58. buf.Merge(Vline{x1, y0, y1 - y0, b.BorderFg, b.BorderBg}.Buffer())
  59. }
  60. // draw corners
  61. if b.BorderTop && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 0 {
  62. buf.Set(x0, y0, Cell{TOP_LEFT, b.BorderFg, b.BorderBg})
  63. }
  64. if b.BorderTop && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 0 {
  65. buf.Set(x1, y0, Cell{TOP_RIGHT, b.BorderFg, b.BorderBg})
  66. }
  67. if b.BorderBottom && b.BorderLeft && b.area.Dx() > 0 && b.area.Dy() > 1 {
  68. buf.Set(x0, y1, Cell{BOTTOM_LEFT, b.BorderFg, b.BorderBg})
  69. }
  70. if b.BorderBottom && b.BorderRight && b.area.Dx() > 1 && b.area.Dy() > 1 {
  71. buf.Set(x1, y1, Cell{BOTTOM_RIGHT, b.BorderFg, b.BorderBg})
  72. }
  73. }
  74. func (b Block) drawBorderLabel(buf Buffer) {
  75. maxTxtW := b.area.Dx() - 2
  76. tx := DTrimTxCls(DefaultTxBuilder.Build(b.BorderLabel, b.BorderLabelFg, b.BorderLabelBg), maxTxtW)
  77. for i, w := 0, 0; i < len(tx); i++ {
  78. buf.Set(b.area.Min.X+1+w, b.area.Min.Y, tx[i])
  79. w += tx[i].Width()
  80. }
  81. }
  82. // Block is a base struct for all other upper level widgets,
  83. // consider it as css: display:block.
  84. // Normally you do not need to create it manually.
  85. type Block struct {
  86. area image.Rectangle
  87. innerArea image.Rectangle
  88. X int
  89. Y int
  90. Border bool
  91. BorderFg Attribute
  92. BorderBg Attribute
  93. BorderLeft bool
  94. BorderRight bool
  95. BorderTop bool
  96. BorderBottom bool
  97. BorderLabel string
  98. BorderLabelFg Attribute
  99. BorderLabelBg Attribute
  100. Display bool
  101. Bg Attribute
  102. Width int
  103. Height int
  104. PaddingTop int
  105. PaddingBottom int
  106. PaddingLeft int
  107. PaddingRight int
  108. id string
  109. Float Align
  110. }
  111. // NewBlock returns a *Block which inherits styles from current theme.
  112. func NewBlock() *Block {
  113. b := Block{}
  114. b.Display = true
  115. b.Border = true
  116. b.BorderLeft = true
  117. b.BorderRight = true
  118. b.BorderTop = true
  119. b.BorderBottom = true
  120. b.BorderBg = ThemeAttr("border.bg")
  121. b.BorderFg = ThemeAttr("border.fg")
  122. b.BorderLabelBg = ThemeAttr("label.bg")
  123. b.BorderLabelFg = ThemeAttr("label.fg")
  124. b.Bg = ThemeAttr("block.bg")
  125. b.Width = 2
  126. b.Height = 2
  127. b.id = GenId()
  128. b.Float = AlignNone
  129. return &b
  130. }
  131. func (b Block) Id() string {
  132. return b.id
  133. }
  134. // Align computes box model
  135. func (b *Block) Align() {
  136. // outer
  137. b.area.Min.X = 0
  138. b.area.Min.Y = 0
  139. b.area.Max.X = b.Width
  140. b.area.Max.Y = b.Height
  141. // float
  142. b.area = AlignArea(TermRect(), b.area, b.Float)
  143. b.area = MoveArea(b.area, b.X, b.Y)
  144. // inner
  145. b.innerArea.Min.X = b.area.Min.X + b.PaddingLeft
  146. b.innerArea.Min.Y = b.area.Min.Y + b.PaddingTop
  147. b.innerArea.Max.X = b.area.Max.X - b.PaddingRight
  148. b.innerArea.Max.Y = b.area.Max.Y - b.PaddingBottom
  149. if b.Border {
  150. if b.BorderLeft {
  151. b.innerArea.Min.X++
  152. }
  153. if b.BorderRight {
  154. b.innerArea.Max.X--
  155. }
  156. if b.BorderTop {
  157. b.innerArea.Min.Y++
  158. }
  159. if b.BorderBottom {
  160. b.innerArea.Max.Y--
  161. }
  162. }
  163. }
  164. // InnerBounds returns the internal bounds of the block after aligning and
  165. // calculating the padding and border, if any.
  166. func (b *Block) InnerBounds() image.Rectangle {
  167. b.Align()
  168. return b.innerArea
  169. }
  170. // Buffer implements Bufferer interface.
  171. // Draw background and border (if any).
  172. func (b *Block) Buffer() Buffer {
  173. b.Align()
  174. buf := NewBuffer()
  175. buf.SetArea(b.area)
  176. buf.Fill(' ', ColorDefault, b.Bg)
  177. b.drawBorder(buf)
  178. b.drawBorderLabel(buf)
  179. return buf
  180. }
  181. // GetHeight implements GridBufferer.
  182. // It returns current height of the block.
  183. func (b Block) GetHeight() int {
  184. return b.Height
  185. }
  186. // SetX implements GridBufferer interface, which sets block's x position.
  187. func (b *Block) SetX(x int) {
  188. b.X = x
  189. }
  190. // SetY implements GridBufferer interface, it sets y position for block.
  191. func (b *Block) SetY(y int) {
  192. b.Y = y
  193. }
  194. // SetWidth implements GridBuffer interface, it sets block's width.
  195. func (b *Block) SetWidth(w int) {
  196. b.Width = w
  197. }
  198. func (b Block) InnerWidth() int {
  199. return b.innerArea.Dx()
  200. }
  201. func (b Block) InnerHeight() int {
  202. return b.innerArea.Dy()
  203. }
  204. func (b Block) InnerX() int {
  205. return b.innerArea.Min.X
  206. }
  207. func (b Block) InnerY() int { return b.innerArea.Min.Y }