panel.go 4.9 KB

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