panel.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314
  1. package door
  2. import (
  3. "bytes"
  4. "log"
  5. "strings"
  6. )
  7. type BorderStyle int
  8. const (
  9. NONE BorderStyle = iota
  10. SINGLE
  11. DOUBLE
  12. DOUBLE_SINGLE
  13. SINGLE_DOUBLE
  14. )
  15. type Panel struct {
  16. X int
  17. Y int
  18. Width int
  19. Style BorderStyle
  20. BorderColor []byte
  21. Lines []*Line
  22. Title string
  23. TitleColor []byte
  24. TitleOffset int
  25. output *bytes.Buffer
  26. }
  27. /*
  28. func (p *Panel) Range(withBorder bool) (sx, sy, ex, ey int) {
  29. sx = p.X
  30. sy = p.Y
  31. ex = p.X + p.Width
  32. ey = p.Y + len(p.Lines)
  33. if p.Style > 0 {
  34. if withBorder {
  35. ex += 2
  36. ey += 2
  37. } else {
  38. sx += 1
  39. sy += 1
  40. }
  41. }
  42. return
  43. }
  44. */
  45. func (p *Panel) Length() int {
  46. return len(p.Lines)
  47. }
  48. func (p *Panel) RightBottomPos() (rx, by int) {
  49. rx = p.X + p.Width
  50. by = p.Y + len(p.Lines)
  51. if p.HasBorder() {
  52. rx += 1
  53. by += 1
  54. }
  55. return
  56. }
  57. func (p *Panel) HasBorder() bool {
  58. return int(p.Style) > 0
  59. }
  60. /*
  61. Is X,Y within the Panel?
  62. */
  63. func (p *Panel) Within(x, y int) (bool, int, int) {
  64. if x < p.X || y < p.Y {
  65. return false, 0, 0
  66. }
  67. mx, my := p.RightBottomPos()
  68. if x > mx || y > my {
  69. return false, 0, 0
  70. }
  71. // Ok, it must be within:
  72. return true, x - p.X, y - p.Y
  73. }
  74. /*
  75. // Panel Clicked?
  76. // If onBorder == true, returns true, 0 for border click.
  77. // Otherwise true, line clicked on (starting with 1)/
  78. func (p *Panel) Clicked(m Mouse, onBorder bool) (bool, int) {
  79. var sx, sy, ex, ey = p.Range(onBorder)
  80. var mx = int(m.X)
  81. var my = int(m.Y)
  82. if (mx >= sx) && (mx <= ex) {
  83. if (my >= sy) && (my <= ey) {
  84. if onBorder {
  85. if my == sy || my == ey {
  86. return true, 0
  87. }
  88. return true, my - sy
  89. } else {
  90. return true, (my - sy) + 1
  91. }
  92. }
  93. }
  94. return false, 0
  95. }
  96. */
  97. // Clear out the panel
  98. func (p *Panel) Clear() []byte {
  99. var style int = int(p.Style)
  100. if p.output == nil {
  101. p.output = &bytes.Buffer{}
  102. }
  103. p.output.Reset()
  104. var row int = p.Y
  105. var blank []byte
  106. if p.HasBorder() {
  107. blank = bytes.Repeat([]byte{' '}, p.Width+2)
  108. p.output.Write(Goto(p.X, row))
  109. p.output.Write(blank)
  110. row++
  111. } else {
  112. blank = bytes.Repeat([]byte{' '}, p.Width)
  113. }
  114. for _ = range p.Lines {
  115. p.output.Write(Goto(p.X, row))
  116. p.output.Write(blank)
  117. row++
  118. }
  119. if style > 0 {
  120. p.output.Write(Goto(p.X, row))
  121. p.output.Write(blank)
  122. }
  123. return p.output.Bytes()
  124. }
  125. // Output the panel
  126. func (p *Panel) Output() []byte {
  127. var style int = int(p.Style)
  128. var box_style *BoxStyle
  129. if p.output == nil {
  130. p.output = &bytes.Buffer{}
  131. }
  132. p.output.Reset()
  133. if p.HasBorder() {
  134. box_style = &BOXES[style-1]
  135. }
  136. var row int = p.Y
  137. if p.HasBorder() {
  138. // Top line / border
  139. // p.output.Write(Goto(p.X, row))
  140. GotoW(p.X, row, p.output)
  141. p.output.Write(p.BorderColor)
  142. p.output.WriteString(box_style.Top_Left)
  143. if p.Title != "" {
  144. if p.TitleOffset+len(p.Title) > p.Width {
  145. log.Panicf("Panel (not wide enough) Width %d : Title size %d + offset %d = %d\n",
  146. p.Width, len(p.Title), p.TitleOffset, p.TitleOffset+len(p.Title))
  147. }
  148. p.output.WriteString(strings.Repeat(box_style.Top, p.TitleOffset))
  149. p.output.Write(p.TitleColor)
  150. p.output.WriteString(p.Title)
  151. p.output.Write(p.BorderColor)
  152. }
  153. p.output.WriteString(strings.Repeat(box_style.Top, p.Width-(p.TitleOffset+len(p.Title))) + box_style.Top_Right)
  154. row++
  155. }
  156. for _, line := range p.Lines {
  157. // p.output.Write(Goto(p.X, row))
  158. GotoW(p.X, row, p.output)
  159. line.LineLength(line.Text)
  160. var joined bool = false
  161. if p.HasBorder() {
  162. top := box_style.Top
  163. // This only works in non-unicode mode
  164. if bytes.Compare(line.Text.Bytes()[0:len(top)], []byte(top)) == 0 {
  165. // Yes, this line needs to be joined...
  166. p.output.Write(p.BorderColor)
  167. p.output.WriteString(box_style.Middle_Left)
  168. p.output.Write(line.Output())
  169. p.output.Write(p.BorderColor)
  170. p.output.WriteString(box_style.Middle_Right)
  171. joined = true
  172. }
  173. }
  174. if !joined {
  175. if p.HasBorder() {
  176. p.output.Write(p.BorderColor)
  177. p.output.WriteString(box_style.Side)
  178. }
  179. p.output.Write(line.Output())
  180. if style > 0 {
  181. p.output.Write(p.BorderColor)
  182. p.output.WriteString(box_style.Side)
  183. }
  184. }
  185. row++
  186. }
  187. if p.HasBorder() {
  188. // Bottom / border
  189. // p.output.Write(Goto(p.X, row))
  190. GotoW(p.X, row, p.output)
  191. p.output.Write(p.BorderColor)
  192. p.output.WriteString(box_style.Bottom_Left)
  193. p.output.WriteString(strings.Repeat(box_style.Top, p.Width) + box_style.Bottom_Right)
  194. }
  195. return p.output.Bytes()
  196. }
  197. // Output anything that has updated
  198. func (p *Panel) Update() []byte {
  199. if p.output == nil {
  200. p.output = &bytes.Buffer{}
  201. }
  202. p.output.Reset()
  203. var style int = int(p.Style)
  204. var row, col int
  205. row = p.Y
  206. col = p.X
  207. if style > 0 {
  208. row++
  209. col++
  210. }
  211. for idx := range p.Lines {
  212. if p.Lines[idx].Update() {
  213. // Yes, line was updated
  214. p.output.Write(Goto(col, row))
  215. p.output.Write(p.Lines[idx].Output())
  216. }
  217. row++
  218. }
  219. return p.output.Bytes()
  220. }
  221. // Output the updated line
  222. func (p *Panel) UpdateLine(index int) []byte {
  223. if p.output == nil {
  224. p.output = &bytes.Buffer{}
  225. }
  226. p.output.Reset()
  227. // var style int = int(p.Style)
  228. p.Lines[index].Update()
  229. var row, col int
  230. row = p.Y + index
  231. col = p.X
  232. if p.HasBorder() {
  233. row++
  234. col++
  235. }
  236. var line *Line = p.Lines[index]
  237. p.output.Write(Goto(col, row))
  238. p.output.Write(line.Output())
  239. return p.output.Bytes()
  240. }
  241. // Position Cursor at the "end" of the panel
  242. func (p *Panel) GotoEnd() []byte {
  243. rx, by := p.RightBottomPos()
  244. return Goto(rx+1, by)
  245. }
  246. // Is the top line of this style a single line?
  247. func Single(bs BorderStyle) bool {
  248. switch bs {
  249. case SINGLE, SINGLE_DOUBLE:
  250. return true
  251. default:
  252. return false
  253. }
  254. }
  255. // Create a spacer line that will be connected maybe to the sides.
  256. func (p *Panel) Spacer() *Line {
  257. var l *Line = &Line{Text: &bytes.Buffer{}}
  258. var pos int
  259. if Single(p.Style) {
  260. pos = 0
  261. } else {
  262. pos = 1
  263. }
  264. l.Text.WriteString(strings.Repeat(BOXES[pos].Top, p.Width))
  265. return l
  266. }
  267. func (p *Panel) Center() {
  268. p.CenterX()
  269. p.CenterY()
  270. }
  271. func (p *Panel) CenterX() {
  272. p.X = (Width - p.Width) / 2
  273. }
  274. func (p *Panel) CenterY() {
  275. p.Y = (Height - len(p.Lines)) / 2
  276. }