bar.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. package door
  2. import (
  3. "bytes"
  4. "fmt"
  5. "strings"
  6. )
  7. // TODO: Break out the progress bar characters into structures.
  8. // Write tests to verify that CP437 matches Unicode.
  9. // See box_test. ;)
  10. type BarStyle int8
  11. const (
  12. SOLID BarStyle = iota
  13. HALF_STEP
  14. GRADIENT
  15. )
  16. type Percent_Style int8
  17. const (
  18. PERCENT_NONE Percent_Style = iota
  19. PERCENT
  20. PERCENT_SPACE
  21. )
  22. type BarRange struct {
  23. Percent int64
  24. Color []byte
  25. }
  26. type BarLine struct {
  27. Width int
  28. Style BarStyle
  29. Percent int64 // percentage * 100
  30. PercentStyle Percent_Style
  31. ColorRange []BarRange
  32. UpdateP func() int64
  33. Line
  34. }
  35. type BarCharacters struct {
  36. Solid string
  37. Half [2]string
  38. Gradient [4]string
  39. }
  40. var BARS_CP437 = BarCharacters{
  41. "\xdb",
  42. [2]string{"\xdb", "\xdd"},
  43. [4]string{"\xdb", "\xb0", "\xb1", "\xb2"},
  44. }
  45. var BARS_UNICODE = BarCharacters{
  46. "\u2588",
  47. [2]string{"\u2588", "\u258c"},
  48. [4]string{"\u2588", "\u2591", "\u2592", "\u2593"},
  49. }
  50. var BARS *BarCharacters = &BARS_CP437
  51. func (bl *BarLine) CheckRange() {
  52. if len(bl.ColorRange) != 0 {
  53. // Ok, there is a color range. Get checking
  54. for _, br := range bl.ColorRange {
  55. if bl.Percent <= br.Percent {
  56. bl.DefaultColor = br.Color
  57. break
  58. }
  59. }
  60. }
  61. }
  62. func (bl *BarLine) Output() []byte {
  63. // bl.Text & bl.render & bl.update
  64. if bl.render == nil {
  65. bl.render = &bytes.Buffer{}
  66. }
  67. bl.render.Reset()
  68. var step_width int64
  69. if bl.UpdateP != nil {
  70. bl.Percent = bl.UpdateP()
  71. }
  72. bl.CheckRange()
  73. // CheckRange can change the DefaultColor.
  74. bl.render.Write(bl.DefaultColor)
  75. if bl.update == nil {
  76. bl.update = &bytes.Buffer{}
  77. }
  78. bl.update.Reset()
  79. switch bl.Style {
  80. case SOLID:
  81. step_width = int64(100 * 100 / bl.Width)
  82. var steps int = int(bl.Percent / step_width)
  83. bl.update.WriteString(strings.Repeat(BARS.Solid, steps))
  84. // This will work, because we aren't trying to len(output) with unicode.
  85. bl.update.WriteString(strings.Repeat(" ", int(bl.Width-steps)))
  86. case HALF_STEP:
  87. step_width = int64(100 * 100 / bl.Width)
  88. var steps int = int(bl.Percent * 2 / step_width)
  89. bl.update.WriteString(strings.Repeat(BARS.Half[0], steps/2))
  90. if steps%2 == 1 {
  91. bl.update.WriteString(BARS.Half[1])
  92. steps++
  93. }
  94. bl.update.WriteString(strings.Repeat(" ", bl.Width-(steps/2)))
  95. case GRADIENT:
  96. step_width = int64(100 * 100 / bl.Width)
  97. var steps int = int(bl.Percent * 4 / step_width)
  98. bl.update.WriteString(strings.Repeat(BARS.Gradient[0], steps/4))
  99. if steps%4 != 0 {
  100. switch steps % 4 {
  101. case 1, 2, 3:
  102. bl.update.WriteString(BARS.Gradient[steps%4])
  103. }
  104. for steps%4 != 0 {
  105. steps++
  106. }
  107. }
  108. bl.update.WriteString(strings.Repeat(" ", bl.Width-(steps/4)))
  109. }
  110. if bl.PercentStyle != PERCENT_NONE {
  111. var pctbuff [10]byte
  112. var percent = pctbuff[0:0]
  113. percent = fmt.Appendf(percent, "%d", bl.Percent/100)
  114. var pos int = bl.Width/2 - 1
  115. if bytes.Compare(percent, []byte("100")) != 0 {
  116. percent = append(percent, '%')
  117. if len(percent) < 3 {
  118. percent = append(percent, ' ')
  119. copy(percent[1:], percent[0:])
  120. percent[0] = ' '
  121. // percent = " " + percent
  122. }
  123. }
  124. if bl.PercentStyle == PERCENT_SPACE {
  125. percent = append(percent, ' ')
  126. copy(percent[1:], percent[0:])
  127. percent = append(percent, ' ')
  128. percent[0] = ' '
  129. // percent = " " + percent + " "
  130. pos--
  131. }
  132. // to process/slice the string (with unicode) do this:
  133. // convert to []rune, slice that, convert back to string
  134. //
  135. // sliceable := []rune(output)
  136. // newString := string(sliceable[:5]) + " new content " + (sliceable[10:])
  137. // fmt.Printf("%d %d [%s] %d [%s]\n", bl.Width, pos, percent, len(output), output)
  138. // The % part -- isn't working at the moment.
  139. // I need to insert the percent into the buffer. (but how)?
  140. // and unicode? hmm!
  141. // Note: This takes ownership of update.
  142. bl.Text = bytes.NewBuffer(bl.update.Bytes())
  143. var idx int = 0
  144. if Unicode {
  145. for {
  146. rune, _, err := bl.Text.ReadRune()
  147. if err != nil {
  148. break
  149. }
  150. if (idx >= pos) && (idx < len(percent)+pos) {
  151. bl.render.WriteByte(percent[idx-pos])
  152. } else {
  153. bl.render.WriteRune(rune)
  154. }
  155. idx++
  156. }
  157. /*
  158. runes := []rune(output)
  159. for idx, b := range percent {
  160. runes[pos+idx] = rune(b)
  161. }
  162. output = string(runes[:pos]) + percent + string(runes[pos+len(percent):])
  163. */
  164. } else {
  165. for {
  166. b, err := bl.Text.ReadByte()
  167. if err != nil {
  168. break
  169. }
  170. if (idx >= pos) && (idx < len(percent)+pos) {
  171. bl.render.WriteByte(percent[idx-pos])
  172. } else {
  173. bl.render.WriteByte(b)
  174. }
  175. idx++
  176. }
  177. /*
  178. for idx, b := range percent {
  179. render[pos+idx] = b
  180. }
  181. */
  182. // output = output[:pos] + percent + output[pos+len(percent):]
  183. }
  184. _ = idx
  185. } else {
  186. bl.render.Write(bl.update.Bytes())
  187. }
  188. // return bl.DefaultColor + output
  189. return bl.render.Bytes()
  190. }