line.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292
  1. package door
  2. import (
  3. "bytes"
  4. "log"
  5. "strings"
  6. "unicode"
  7. )
  8. /*
  9. door.Line - Display a line of text
  10. Example:
  11. var basicLine door.Line = {Text: "Welcome",
  12. DefaultColor: door.Color("BRIGHT YELLOW"),
  13. }
  14. d.Write(basicLine.Output() + door.Reset + door.CRNL)
  15. This outputs Welcome in Bright/Bold Yellow.
  16. Example Render:
  17. var renderAlphaDigit ColorRender = func(text string) string {
  18. var r door.Render{Text: text}
  19. var alpha string = door.ColorText("BOLD YELLOW")
  20. var digit string = door.ColorText("BOLD GREEN")
  21. var other string = door.Reset
  22. for _, letter := range text {
  23. if unicode.IsAlpha(letter) {
  24. r.Append(alpha, 1)
  25. } else {
  26. if unicode.IsDigit(letter) {
  27. r.Append(digit, 1)
  28. } else {
  29. r.Append(other, 1)
  30. }
  31. }
  32. }
  33. return r.Result
  34. }
  35. var renderLine door.Line = {Text: "Render - 12345",
  36. RenderF: renderAlphaDigit,
  37. }
  38. d.Write(renderLine.Output() + door.Reset + door.CRNL)
  39. This outputs "Render" in Yellow, "12345" in Green, and " - " in White.
  40. Example Update:
  41. var updateTime() string {
  42. var now time.Time = time.Now()
  43. var result string = now.Format("3:04:05 PM")
  44. return result
  45. }
  46. var timeLine door.Line = {Text: updateTime(),
  47. UpdateF: updateTime,
  48. }
  49. d.Write(timeLine.Output() + door.CRNL)
  50. time.Sleep(time.Second)
  51. // Check timeLine for update
  52. if timeLine.Update() {
  53. // Yes, there's an update to the time, output it.
  54. d.Write(timeLine.Output() + door.CRNL)
  55. }
  56. if timeLine.Update() {
  57. // This isn't called. There were no changes.
  58. d.Write(timeLine.Output() + door.CRNL)
  59. }
  60. This outputs the Current time in 12 hour format. It pauses for a second,
  61. and outputs the new time.
  62. */
  63. /*
  64. Line of text to display.
  65. */
  66. type Line struct {
  67. Text *bytes.Buffer // Text to be displayed
  68. update *bytes.Buffer // Buffer for updates
  69. DefaultColor []byte // Default Color to use
  70. RenderF ColorRender // Render function (displays string with colors)
  71. render *bytes.Buffer // Buffer for rendering
  72. UpdateF Updater // Update function updates the text
  73. Width int // Line length
  74. }
  75. func NewLine(text string) *Line {
  76. return &Line{Text: bytes.NewBuffer([]byte(text))}
  77. }
  78. /*
  79. Line Update - This calls the UpdateF if present.
  80. Returns true if the line has been updated, and there's an Update function.
  81. */
  82. func (l *Line) Update() bool {
  83. if l.Text == nil {
  84. l.Text = &bytes.Buffer{}
  85. }
  86. if l.UpdateF == nil {
  87. return false
  88. }
  89. if l.update == nil {
  90. l.update = &bytes.Buffer{}
  91. }
  92. l.update.Reset()
  93. l.UpdateF(l.update)
  94. l.LineLength(l.update)
  95. // Has the line changed (update)?
  96. if bytes.Compare(l.update.Bytes(), l.Text.Bytes()) != 0 {
  97. // Yes, copy into Text.
  98. l.Text.Reset()
  99. l.Text.Write(l.update.Bytes())
  100. return true
  101. }
  102. return false
  103. }
  104. // If a line Width has been set, make sure we match it.
  105. func (l *Line) LineLength(text *bytes.Buffer) {
  106. if l.Width == 0 {
  107. return
  108. }
  109. var length int
  110. if Unicode {
  111. if l.render == nil {
  112. l.render = &bytes.Buffer{}
  113. }
  114. l.render.Reset()
  115. l.render.Write(text.Bytes())
  116. /*
  117. var ubuff *bytes.Buffer = bytes.NewBuffer(text.Bytes())
  118. */
  119. var e error
  120. var r rune
  121. for {
  122. r, _, e = l.render.ReadRune()
  123. if e != nil {
  124. break
  125. }
  126. length += UnicodeWidth(r)
  127. }
  128. l.render.Reset()
  129. } else {
  130. length = text.Len()
  131. }
  132. if length > l.Width {
  133. log.Printf("ERROR: Line Width %d: Have %d\n", l.Width, length)
  134. } else {
  135. for length < l.Width {
  136. text.WriteByte(' ')
  137. length++
  138. }
  139. // *text += strings.Repeat(" ", l.Width-length)
  140. }
  141. }
  142. /*
  143. Line Output - returns a string with ANSI Color codes.
  144. If there is no RenderF, we use the DefaultColor. Otherwise we pass the text
  145. to RenderF and return what it returns.
  146. */
  147. func (l *Line) Output() []byte {
  148. if l.UpdateF == nil {
  149. l.LineLength(l.Text)
  150. }
  151. if l.render == nil {
  152. l.render = &bytes.Buffer{}
  153. /*
  154. // No profiling changes here.
  155. if l.RenderF == nil {
  156. var cap int
  157. if l.DefaultColor != "" {
  158. cap += len(l.DefaultColor)
  159. }
  160. cap += l.Text.Len()
  161. l.render.Grow(cap)
  162. }
  163. */
  164. }
  165. if l.RenderF == nil {
  166. l.render.Reset()
  167. if len(l.DefaultColor) != 0 {
  168. l.render.Write(l.DefaultColor)
  169. }
  170. l.render.Write(l.Text.Bytes())
  171. return l.render.Bytes()
  172. // return l.DefaultColor + l.Text
  173. } else {
  174. l.RenderF(l.render, l.Text.Bytes())
  175. return l.render.Bytes()
  176. }
  177. }
  178. // Make Uppercase RenderF
  179. func RenderUppercase(Upper string, NonUpper string) ColorRender {
  180. var UpperColor, NonUpperColor []byte
  181. if strings.HasPrefix(Upper, "\x1b") {
  182. UpperColor = []byte(Upper)
  183. } else {
  184. UpperColor = []byte(ColorText(Upper))
  185. }
  186. if strings.HasPrefix(NonUpper, "\x1b") {
  187. NonUpperColor = []byte(NonUpper)
  188. } else {
  189. NonUpperColor = []byte(ColorText(NonUpper))
  190. }
  191. if Unicode {
  192. var runeBuffer *bytes.Buffer = &bytes.Buffer{}
  193. return func(output *bytes.Buffer, text []byte) {
  194. var lastColor *[]byte
  195. output.Reset()
  196. runeBuffer.Reset()
  197. runeBuffer.Write(text)
  198. for r, _, err := runeBuffer.ReadRune(); err != nil; r, _, err = runeBuffer.ReadRune() {
  199. // for _, letter := range text {
  200. if unicode.IsUpper(r) {
  201. if lastColor != &UpperColor {
  202. output.Write(UpperColor)
  203. lastColor = &UpperColor
  204. }
  205. } else {
  206. if lastColor != &NonUpperColor {
  207. output.Write(NonUpperColor)
  208. lastColor = &UpperColor
  209. }
  210. }
  211. output.WriteRune(r)
  212. }
  213. }
  214. } else {
  215. return func(output *bytes.Buffer, text []byte) {
  216. var lastColor *[]byte
  217. output.Reset()
  218. for _, letter := range text {
  219. if unicode.IsUpper(rune(letter)) {
  220. if lastColor != &UpperColor {
  221. output.Write(UpperColor)
  222. lastColor = &UpperColor
  223. }
  224. } else {
  225. if lastColor != &NonUpperColor {
  226. output.Write(NonUpperColor)
  227. lastColor = &UpperColor
  228. }
  229. }
  230. output.WriteByte(letter)
  231. }
  232. }
  233. }
  234. }
  235. func RenderBlueYellow(output *bytes.Buffer, text []byte) {
  236. output.Reset()
  237. // var output = RenderPool.Get().(*strings.Builder)
  238. // output.Reset()
  239. var blue []byte = ColorText("BOLD BLUE")
  240. var yellow []byte = ColorText("BOLD YELLOW")
  241. var last *[]byte
  242. for _, letter := range text {
  243. if unicode.IsUpper(rune(letter)) {
  244. if last != &blue {
  245. output.Write(blue)
  246. last = &blue
  247. }
  248. } else {
  249. if last != &yellow {
  250. output.Write(yellow)
  251. last = &yellow
  252. }
  253. }
  254. output.WriteByte(letter)
  255. }
  256. // var result = output.String()
  257. // RenderPool.Put(output)
  258. // return result
  259. }