whirled_peas 0.9.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/CHANGELOG.md +23 -0
  4. data/README.md +9 -1049
  5. data/doc/application.md +92 -0
  6. data/doc/cli.md +115 -0
  7. data/doc/components.md +51 -0
  8. data/doc/easing.md +257 -0
  9. data/doc/elements.md +69 -0
  10. data/doc/screen_test.md +22 -0
  11. data/doc/settings.md +466 -0
  12. data/doc/template_factory.md +53 -0
  13. data/doc/themes.md +50 -0
  14. data/examples/components.rb +33 -0
  15. data/examples/graph.rb +1 -1
  16. data/examples/scrolling.rb +8 -19
  17. data/lib/data/themes.yaml +15 -0
  18. data/lib/whirled_peas.rb +31 -9
  19. data/lib/whirled_peas/animator/debug_consumer.rb +2 -2
  20. data/lib/whirled_peas/animator/easing.rb +57 -0
  21. data/lib/whirled_peas/animator/frameset.rb +13 -8
  22. data/lib/whirled_peas/animator/producer.rb +7 -8
  23. data/lib/whirled_peas/animator/renderer_consumer.rb +10 -5
  24. data/lib/whirled_peas/command/config_command.rb +3 -3
  25. data/lib/whirled_peas/command/frames.rb +1 -3
  26. data/lib/whirled_peas/command/play.rb +9 -7
  27. data/lib/whirled_peas/command/record.rb +1 -3
  28. data/lib/whirled_peas/command/still.rb +6 -2
  29. data/lib/whirled_peas/command/themes.rb +78 -0
  30. data/lib/whirled_peas/command_line.rb +3 -1
  31. data/lib/whirled_peas/component.rb +35 -0
  32. data/lib/whirled_peas/component/list_with_active.rb +89 -0
  33. data/lib/whirled_peas/config.rb +1 -8
  34. data/lib/whirled_peas/device/null_device.rb +1 -1
  35. data/lib/whirled_peas/device/output_file.rb +2 -2
  36. data/lib/whirled_peas/device/rendered_frame.rb +12 -0
  37. data/lib/whirled_peas/device/screen.rb +7 -8
  38. data/lib/whirled_peas/graphics/box_painter.rb +4 -4
  39. data/lib/whirled_peas/graphics/composer.rb +9 -2
  40. data/lib/whirled_peas/graphics/container_coords.rb +23 -25
  41. data/lib/whirled_peas/graphics/container_dimensions.rb +24 -0
  42. data/lib/whirled_peas/graphics/container_painter.rb +27 -139
  43. data/lib/whirled_peas/graphics/debugger.rb +2 -7
  44. data/lib/whirled_peas/graphics/graph_painter.rb +31 -24
  45. data/lib/whirled_peas/graphics/pixel_grid.rb +81 -0
  46. data/lib/whirled_peas/graphics/renderer.rb +4 -5
  47. data/lib/whirled_peas/graphics/scrollbar_helper.rb +126 -0
  48. data/lib/whirled_peas/null_logger.rb +0 -1
  49. data/lib/whirled_peas/settings/bg_color.rb +5 -1
  50. data/lib/whirled_peas/settings/border.rb +26 -5
  51. data/lib/whirled_peas/settings/color.rb +8 -4
  52. data/lib/whirled_peas/settings/container_settings.rb +14 -7
  53. data/lib/whirled_peas/settings/debugger.rb +3 -1
  54. data/lib/whirled_peas/settings/element_settings.rb +18 -6
  55. data/lib/whirled_peas/settings/graph_settings.rb +1 -1
  56. data/lib/whirled_peas/settings/position.rb +17 -5
  57. data/lib/whirled_peas/settings/text_color.rb +5 -0
  58. data/lib/whirled_peas/settings/text_settings.rb +1 -0
  59. data/lib/whirled_peas/settings/theme.rb +61 -0
  60. data/lib/whirled_peas/settings/theme_library.rb +69 -0
  61. data/lib/whirled_peas/utils/ansi.rb +4 -2
  62. data/lib/whirled_peas/utils/file_handler.rb +14 -9
  63. data/lib/whirled_peas/utils/formatted_string.rb +5 -3
  64. data/lib/whirled_peas/version.rb +1 -1
  65. data/screen_test/components/list_with_active/l2r_position_end.frame +1 -0
  66. data/screen_test/components/list_with_active/l2r_position_end.rb +16 -0
  67. data/screen_test/components/list_with_active/l2r_position_middle.frame +1 -0
  68. data/screen_test/components/list_with_active/l2r_position_middle.rb +16 -0
  69. data/screen_test/components/list_with_active/l2r_position_start.frame +1 -0
  70. data/screen_test/components/list_with_active/l2r_position_start.rb +16 -0
  71. data/screen_test/components/list_with_active/l2r_separator.frame +1 -0
  72. data/screen_test/components/list_with_active/l2r_separator.rb +17 -0
  73. data/screen_test/components/list_with_active/t2b_position_end.frame +1 -0
  74. data/screen_test/components/list_with_active/t2b_position_end.rb +17 -0
  75. data/screen_test/components/list_with_active/t2b_position_middle.frame +1 -0
  76. data/screen_test/components/list_with_active/t2b_position_middle.rb +17 -0
  77. data/screen_test/components/list_with_active/t2b_position_start.frame +1 -0
  78. data/screen_test/components/list_with_active/t2b_position_start.rb +17 -0
  79. data/screen_test/components/list_with_active/t2b_separator.frame +1 -0
  80. data/screen_test/components/list_with_active/t2b_separator.rb +18 -0
  81. data/screen_test/elements/box.frame +1 -1
  82. data/screen_test/elements/box.rb +1 -1
  83. data/screen_test/elements/graph_asc.frame +1 -0
  84. data/screen_test/elements/{graph.rb → graph_asc.rb} +1 -1
  85. data/screen_test/elements/graph_desc.frame +1 -0
  86. data/screen_test/elements/graph_desc.rb +12 -0
  87. data/screen_test/elements/graph_horiz.frame +1 -0
  88. data/screen_test/elements/graph_horiz.rb +12 -0
  89. data/screen_test/elements/graph_sin.frame +1 -0
  90. data/screen_test/elements/graph_sin.rb +12 -0
  91. data/screen_test/elements/grid.frame +1 -1
  92. data/screen_test/elements/grid.rb +1 -1
  93. data/screen_test/elements/screen_overflow_x.frame +1 -1
  94. data/screen_test/elements/screen_overflow_x.rb +1 -1
  95. data/screen_test/elements/screen_overflow_y.frame +1 -1
  96. data/screen_test/elements/screen_overflow_y.rb +1 -1
  97. data/screen_test/elements/text.frame +1 -1
  98. data/screen_test/elements/text.rb +1 -1
  99. data/screen_test/elements/text_multiline.frame +1 -1
  100. data/screen_test/elements/text_multiline.rb +1 -1
  101. data/screen_test/elements/theme.frame +1 -0
  102. data/screen_test/elements/theme.rb +27 -0
  103. data/screen_test/settings/align/box_around.frame +1 -1
  104. data/screen_test/settings/align/box_around.rb +1 -1
  105. data/screen_test/settings/align/box_between.frame +1 -1
  106. data/screen_test/settings/align/box_between.rb +1 -1
  107. data/screen_test/settings/align/box_center.frame +1 -1
  108. data/screen_test/settings/align/box_center.rb +1 -1
  109. data/screen_test/settings/align/box_default.frame +1 -1
  110. data/screen_test/settings/align/box_default.rb +1 -1
  111. data/screen_test/settings/align/box_evenly.frame +1 -1
  112. data/screen_test/settings/align/box_evenly.rb +1 -1
  113. data/screen_test/settings/align/box_left.frame +1 -1
  114. data/screen_test/settings/align/box_left.rb +1 -1
  115. data/screen_test/settings/align/box_right.frame +1 -1
  116. data/screen_test/settings/align/box_right.rb +1 -1
  117. data/screen_test/settings/align/children_center.frame +1 -1
  118. data/screen_test/settings/align/children_center.rb +1 -1
  119. data/screen_test/settings/align/children_left.frame +1 -1
  120. data/screen_test/settings/align/children_left.rb +1 -1
  121. data/screen_test/settings/align/children_right.frame +1 -1
  122. data/screen_test/settings/align/children_right.rb +1 -1
  123. data/screen_test/settings/align/grid_center.frame +1 -1
  124. data/screen_test/settings/align/grid_center.rb +1 -1
  125. data/screen_test/settings/align/grid_default.frame +1 -1
  126. data/screen_test/settings/align/grid_default.rb +1 -1
  127. data/screen_test/settings/align/grid_left.frame +1 -1
  128. data/screen_test/settings/align/grid_left.rb +1 -1
  129. data/screen_test/settings/align/grid_right.frame +1 -1
  130. data/screen_test/settings/align/grid_right.rb +1 -1
  131. data/screen_test/settings/ansi/bold.frame +1 -1
  132. data/screen_test/settings/ansi/bold.rb +1 -1
  133. data/screen_test/settings/ansi/color.frame +1 -1
  134. data/screen_test/settings/ansi/color.rb +1 -1
  135. data/screen_test/settings/ansi/underline.frame +1 -1
  136. data/screen_test/settings/ansi/underline.rb +1 -1
  137. data/screen_test/settings/border.frame +1 -1
  138. data/screen_test/settings/border.rb +1 -1
  139. data/screen_test/settings/content_start/box_bottom.frame +1 -0
  140. data/screen_test/settings/content_start/box_bottom.rb +17 -0
  141. data/screen_test/settings/content_start/box_left.frame +1 -0
  142. data/screen_test/settings/{position → content_start}/box_left.rb +2 -2
  143. data/screen_test/settings/content_start/box_left_negative.frame +1 -0
  144. data/screen_test/settings/{position → content_start}/box_left_negative.rb +2 -2
  145. data/screen_test/settings/content_start/box_right.frame +1 -0
  146. data/screen_test/settings/content_start/box_right.rb +17 -0
  147. data/screen_test/settings/content_start/box_top.frame +1 -0
  148. data/screen_test/settings/{position → content_start}/box_top.rb +2 -2
  149. data/screen_test/settings/content_start/box_top_negative.frame +1 -0
  150. data/screen_test/settings/{position → content_start}/box_top_negative.rb +2 -2
  151. data/screen_test/settings/content_start/grid_left.frame +1 -0
  152. data/screen_test/settings/{position → content_start}/grid_left.rb +2 -2
  153. data/screen_test/settings/content_start/grid_left_negative.frame +1 -0
  154. data/screen_test/settings/{position → content_start}/grid_left_negative.rb +2 -2
  155. data/screen_test/settings/content_start/grid_top.frame +1 -0
  156. data/screen_test/settings/{position → content_start}/grid_top.rb +2 -2
  157. data/screen_test/settings/content_start/grid_top_negative.frame +1 -0
  158. data/screen_test/settings/{position → content_start}/grid_top_negative.rb +2 -2
  159. data/screen_test/settings/flow/box_b2t.frame +1 -1
  160. data/screen_test/settings/flow/box_b2t.rb +1 -1
  161. data/screen_test/settings/flow/box_l2r.frame +1 -1
  162. data/screen_test/settings/flow/box_l2r.rb +1 -1
  163. data/screen_test/settings/flow/box_r2l.frame +1 -1
  164. data/screen_test/settings/flow/box_r2l.rb +1 -1
  165. data/screen_test/settings/flow/box_t2b.frame +1 -1
  166. data/screen_test/settings/flow/box_t2b.rb +1 -1
  167. data/screen_test/settings/flow/grid_b2t.frame +1 -1
  168. data/screen_test/settings/flow/grid_b2t.rb +1 -1
  169. data/screen_test/settings/flow/grid_l2r.frame +1 -1
  170. data/screen_test/settings/flow/grid_l2r.rb +1 -1
  171. data/screen_test/settings/flow/grid_r2l.frame +1 -1
  172. data/screen_test/settings/flow/grid_r2l.rb +1 -1
  173. data/screen_test/settings/flow/grid_t2b.frame +1 -1
  174. data/screen_test/settings/flow/grid_t2b.rb +1 -1
  175. data/screen_test/settings/height/box.frame +1 -1
  176. data/screen_test/settings/height/box.rb +1 -1
  177. data/screen_test/settings/height/box_border_sizing.frame +1 -1
  178. data/screen_test/settings/height/box_border_sizing.rb +1 -1
  179. data/screen_test/settings/height/grid.frame +1 -1
  180. data/screen_test/settings/height/grid.rb +1 -1
  181. data/screen_test/settings/height/overflow_box.frame +1 -1
  182. data/screen_test/settings/height/overflow_box.rb +1 -1
  183. data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
  184. data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
  185. data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
  186. data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
  187. data/screen_test/settings/height/overflow_grid.frame +1 -1
  188. data/screen_test/settings/height/overflow_grid.rb +1 -1
  189. data/screen_test/settings/margin.frame +1 -1
  190. data/screen_test/settings/margin.rb +1 -1
  191. data/screen_test/settings/padding.frame +1 -1
  192. data/screen_test/settings/padding.rb +1 -1
  193. data/screen_test/settings/scroll/horiz_box.frame +1 -1
  194. data/screen_test/settings/scroll/horiz_box.rb +3 -5
  195. data/screen_test/settings/scroll/horiz_box_align_center.rb +3 -5
  196. data/screen_test/settings/scroll/horiz_box_align_right.rb +3 -5
  197. data/screen_test/settings/scroll/vert_box.frame +1 -1
  198. data/screen_test/settings/scroll/vert_box.rb +6 -8
  199. data/screen_test/settings/title_font.frame +1 -1
  200. data/screen_test/settings/title_font.rb +1 -1
  201. data/screen_test/settings/valign/box_around.frame +1 -1
  202. data/screen_test/settings/valign/box_around.rb +1 -1
  203. data/screen_test/settings/valign/box_between.frame +1 -1
  204. data/screen_test/settings/valign/box_between.rb +1 -1
  205. data/screen_test/settings/valign/box_bottom.frame +1 -1
  206. data/screen_test/settings/valign/box_bottom.rb +1 -1
  207. data/screen_test/settings/valign/box_default.frame +1 -1
  208. data/screen_test/settings/valign/box_default.rb +1 -1
  209. data/screen_test/settings/valign/box_evenly.frame +1 -1
  210. data/screen_test/settings/valign/box_evenly.rb +1 -1
  211. data/screen_test/settings/valign/box_middle.frame +1 -1
  212. data/screen_test/settings/valign/box_middle.rb +1 -1
  213. data/screen_test/settings/valign/box_top.frame +1 -1
  214. data/screen_test/settings/valign/box_top.rb +1 -1
  215. data/screen_test/settings/valign/grid_bottom.frame +1 -1
  216. data/screen_test/settings/valign/grid_bottom.rb +1 -1
  217. data/screen_test/settings/valign/grid_default.frame +1 -1
  218. data/screen_test/settings/valign/grid_default.rb +1 -1
  219. data/screen_test/settings/valign/grid_middle.frame +1 -1
  220. data/screen_test/settings/valign/grid_middle.rb +1 -1
  221. data/screen_test/settings/valign/grid_top.frame +1 -1
  222. data/screen_test/settings/valign/grid_top.rb +1 -1
  223. data/screen_test/settings/width/box_border_sizing.frame +1 -1
  224. data/screen_test/settings/width/box_border_sizing.rb +1 -1
  225. data/screen_test/settings/width/box_content.frame +1 -1
  226. data/screen_test/settings/width/box_content.rb +1 -1
  227. data/screen_test/settings/width/box_default.frame +1 -1
  228. data/screen_test/settings/width/box_default.rb +1 -1
  229. data/screen_test/settings/width/grid.frame +1 -1
  230. data/screen_test/settings/width/grid.rb +1 -1
  231. data/screen_test/settings/width/overflow_align_center.frame +1 -1
  232. data/screen_test/settings/width/overflow_align_center.rb +1 -1
  233. data/screen_test/settings/width/overflow_align_right.frame +1 -1
  234. data/screen_test/settings/width/overflow_align_right.rb +1 -1
  235. data/screen_test/settings/width/overflow_box.frame +1 -1
  236. data/screen_test/settings/width/overflow_box.rb +1 -1
  237. data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
  238. data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
  239. data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
  240. data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
  241. data/screen_test/settings/width/overflow_grid.frame +1 -1
  242. data/screen_test/settings/width/overflow_grid.rb +1 -1
  243. data/tools/whirled_peas/tools/screen_tester.rb +21 -3
  244. metadata +67 -22
  245. data/bin/easing +0 -33
  246. data/lib/whirled_peas/graphics/mock_screen.rb +0 -26
  247. data/screen_test/elements/graph.frame +0 -1
  248. data/screen_test/settings/position/box_left.frame +0 -1
  249. data/screen_test/settings/position/box_left_negative.frame +0 -1
  250. data/screen_test/settings/position/box_top.frame +0 -1
  251. data/screen_test/settings/position/box_top_negative.frame +0 -1
  252. data/screen_test/settings/position/grid_left.frame +0 -1
  253. data/screen_test/settings/position/grid_left_negative.frame +0 -1
  254. data/screen_test/settings/position/grid_top.frame +0 -1
  255. data/screen_test/settings/position/grid_top_negative.frame +0 -1
@@ -0,0 +1,81 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class PixelGrid
4
+ Pixel = Struct.new(:char, :formatting)
5
+
6
+ def initialize(width, height)
7
+ @pixels = Array.new(height) { Array.new(width) { Pixel.new } }
8
+ end
9
+
10
+ def add_stroke(left, top, fstring)
11
+ fstring.each_char.with_index do |char, offset|
12
+ pixels[top][left + offset].char = char
13
+ pixels[top][left + offset].formatting = fstring.formatting
14
+ end
15
+ end
16
+
17
+ def pixel_at(col, row)
18
+ pixels[row][col]
19
+ end
20
+
21
+ def diff(other)
22
+ str = ''
23
+ formatting = nil
24
+ chars = ''
25
+ pixels.each.with_index do |row, row_index|
26
+ row.each.with_index do |pixel, col_index|
27
+ if pixel == other.pixel_at(col_index, row_index)
28
+ if chars != ''
29
+ str += Utils::FormattedString.new(chars, formatting).to_s
30
+ chars = ''
31
+ formatting = nil
32
+ end
33
+ elsif chars == ''
34
+ str += Utils::Ansi.cursor_pos(left: col_index, top: row_index)
35
+ chars = pixel.char
36
+ formatting = pixel.formatting
37
+ elsif formatting != pixel.formatting
38
+ str += Utils::FormattedString.new(chars, formatting).to_s
39
+ chars = pixel.char
40
+ formatting = pixel.formatting
41
+ else
42
+ chars += pixel.char
43
+ end
44
+ end
45
+ end
46
+ if chars != ''
47
+ str += chars
48
+ chars = ''
49
+ formatting = nil
50
+ end
51
+ str
52
+ end
53
+
54
+ def to_s
55
+ str = Utils::Ansi.cursor_pos(left: 0, top: 0) + Utils::Ansi.clear_down
56
+ formatting = nil
57
+ chars = ''
58
+ pixels.each.with_index do |row, row_offset|
59
+ str += Utils::Ansi.cursor_pos(left: 0, top: row_offset) if row_offset > 0
60
+ row.each do |pixel|
61
+ if pixel.formatting != formatting
62
+ str += Utils::FormattedString.new(chars, formatting).to_s if chars != ''
63
+ chars = ''
64
+ formatting = pixel.formatting
65
+ end
66
+ chars << pixel.char
67
+ end
68
+ if chars != ''
69
+ str += Utils::FormattedString.new(chars, formatting).to_s
70
+ chars = ''
71
+ end
72
+ end
73
+ str
74
+ end
75
+
76
+ private
77
+
78
+ attr_reader :pixels
79
+ end
80
+ end
81
+ end
@@ -1,4 +1,5 @@
1
1
  require_relative 'canvas'
2
+ require_relative 'pixel_grid'
2
3
 
3
4
  module WhirledPeas
4
5
  module Graphics
@@ -15,13 +16,11 @@ module WhirledPeas
15
16
  template.settings.height = height
16
17
  template.settings.sizing = :border
17
18
  template.settings.set_margin(left: 0, top: 0, right: 0, bottom: 0)
18
- strokes = [Utils::Ansi.cursor_visible(false), Utils::Ansi.cursor_pos, Utils::Ansi.clear_down]
19
+ pixel_grid = PixelGrid.new(width, height)
19
20
  template.paint(Canvas.new(0, 0, width, height), 0, 0) do |left, top, fstring|
20
- next unless fstring.length > 0
21
- strokes << Utils::Ansi.cursor_pos(left: left, top: top)
22
- strokes << fstring
21
+ pixel_grid.add_stroke(left, top, fstring)
23
22
  end
24
- strokes.join
23
+ pixel_grid
25
24
  end
26
25
 
27
26
  private
@@ -0,0 +1,126 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ module ScrollbarHelper
4
+ # Contants to paint scrollbars
5
+ HORIZONTAL = [' ', '▗', '▖', '▄']
6
+ VERTICAL = [
7
+ ' ',
8
+ '▗',
9
+ '▝',
10
+ '▐'
11
+ ]
12
+
13
+ # The number of units of scrollbar a single character can be divided into
14
+ SCROLL_HANDLE_SCALE = 2
15
+ private_constant :SCROLL_HANDLE_SCALE
16
+
17
+ class << self
18
+ # Return the characters to paint the horizontal scroll bar with for the given column
19
+ #
20
+ # @see #scroll_chars for more details
21
+ def horiz(col_count, viewable_col_count, first_visible_col)
22
+ scroll_chars(col_count, viewable_col_count, first_visible_col, HORIZONTAL)
23
+ end
24
+
25
+ # Return the characters to paint the vertical scroll bar with for the given row
26
+ #
27
+ # @see #scroll_chars for more details
28
+ def vert(row_count, viewable_row_count, first_visible_row)
29
+ scroll_chars(row_count, viewable_row_count, first_visible_row, VERTICAL)
30
+ end
31
+
32
+ private
33
+
34
+ # Determine which character to paint a for a scrollbar
35
+ #
36
+ # @param total_count [Integer] total number of rows/columns in the content
37
+ # @param viewable_count [Integer] number of rows/columns visible in the viewport
38
+ # @param first_visible [Integer] zero-based index of the first row/column that is visible
39
+ # in the viewport
40
+ # @param chars [Array<String>] an array with four 1-character strings, the frist is the
41
+ # gutter (i.e. no scrolbar handle visible), then the "second half" scrollbar character,
42
+ # then second is the "first half" scrollbar character, and finally the "full"
43
+ def scroll_chars(total_count, viewable_count, first_visible, chars)
44
+ # Start by initializing the scroll back to to all gutters
45
+ scrollbar = Array.new(viewable_count) { chars[0] }
46
+
47
+ return scrollbar if total_count == 0
48
+ return scrollbar if viewable_count == 0
49
+ return scrollbar if viewable_count >= total_count
50
+
51
+ # The scroll handle has the exact same relative size and position in the scroll gutter
52
+ # that the viewable content has in the total content area. For example, a content area
53
+ # that is 50 columns wide with a view port that is 20 columns wide might look like
54
+ #
55
+ # +---------1-----****2*********3******---4---------+
56
+ # | * * |
57
+ # | hidden * viewable * hidden |
58
+ # | * * |
59
+ # +---------1-----****2*********3******---4---------+
60
+ #
61
+ # The scoll gutter, would look like
62
+ #
63
+ # |......********.....|
64
+ #
65
+ # Scrolling all the way to the right results in
66
+ #
67
+ # +---------1---------2---------3*********4*********+
68
+ # | * *
69
+ # | hidden * viewable *
70
+ # | * *
71
+ # +---------1---------2---------3*********4*********+
72
+ # |...........********|
73
+ #
74
+ # Returning to the first example, we can match up the arguments to this method to the
75
+ # diagram
76
+ #
77
+ # total_count = 50
78
+ # |<----------------------------------------------->|
79
+ # | |
80
+ # | veiwable_count = 20 |
81
+ # | |<----------------->| |
82
+ # ↓ ↓ ↓ ↓
83
+ # +---------1-----****2*********3******---4---------+
84
+ # | * * |
85
+ # | hidden * viewable * hidden |
86
+ # | * * |
87
+ # +---------1-----****2*********3******---4---------+
88
+ # |......****?***.....|
89
+ # ↑ ↑
90
+ # first_visible = 16 |
91
+ # curr = 11
92
+
93
+ # Always use the same length for the scrollbar so it does not give an inchworm effect
94
+ # as it scrolls along. This will calculate the "ideal" length of the scroll bar if we
95
+ # could infinitely divide a character.
96
+ length = (viewable_count ** 2).to_f / total_count
97
+
98
+ # Round the length to the nearst "scaled" value
99
+ length = (SCROLL_HANDLE_SCALE * length).round.to_f / SCROLL_HANDLE_SCALE
100
+
101
+ # Ensure we have a scrollbar!
102
+ length = 1.0 / SCROLL_HANDLE_SCALE if length == 0
103
+
104
+ # Find the "ideal" position of where the scrollbar should start.
105
+ start = first_visible * viewable_count.to_f / total_count
106
+
107
+ # Round the start to the nearest "scaled" value
108
+ start = (SCROLL_HANDLE_SCALE * start).round.to_f / SCROLL_HANDLE_SCALE
109
+
110
+ # Make sure we didn't scroll off the page!
111
+ start -= length if start == viewable_count
112
+
113
+ (start.floor..[(start + length).floor, viewable_count - 1].min).each do |curr_0|
114
+ curr_1 = curr_0 + 1.0 / SCROLL_HANDLE_SCALE
115
+
116
+ first_half = start <= curr_0 && curr_0 < start + length ? 2 : 0
117
+ second_half = start <= curr_1 && curr_1 < start + length ? 1 : 0
118
+
119
+ scrollbar[curr_0] = chars[second_half | first_half]
120
+ end
121
+ scrollbar
122
+ end
123
+ end
124
+ end
125
+ end
126
+ end
@@ -15,6 +15,5 @@ module WhirledPeas
15
15
  def warn(*)
16
16
  end
17
17
  end
18
-
19
18
  private_constant :NullLogger
20
19
  end
@@ -1,12 +1,12 @@
1
1
  require 'whirled_peas/utils/ansi'
2
2
 
3
3
  require_relative 'color'
4
+ require_relative 'text_color'
4
5
 
5
6
  module WhirledPeas
6
7
  module Settings
7
8
  class BgColor < Color
8
9
  BG_OFFSET = 10
9
- private_constant :BG_OFFSET
10
10
 
11
11
  BLACK = new(Utils::Ansi::BLACK + BG_OFFSET)
12
12
  RED = new(Utils::Ansi::RED + BG_OFFSET)
@@ -19,6 +19,10 @@ module WhirledPeas
19
19
  GRAY = BLACK.bright
20
20
 
21
21
  DEFAULT = nil # Use system defined default
22
+
23
+ def as_text_color
24
+ TextColor.new(code - BG_OFFSET, bright?)
25
+ end
22
26
  end
23
27
  end
24
28
  end
@@ -34,7 +34,7 @@ module WhirledPeas
34
34
  '╚', '═', '╩', '╝'
35
35
  )
36
36
 
37
- DEFAULT = BOLD
37
+ DEFAULT = SOFT
38
38
 
39
39
  def self.validate!(style)
40
40
  return if style.nil?
@@ -51,8 +51,20 @@ module WhirledPeas
51
51
  end
52
52
  end
53
53
 
54
+ def self.inherit(parent)
55
+ border = new(parent.theme)
56
+ border.inherit(parent)
57
+ border
58
+ end
59
+
60
+ attr_reader :theme
61
+
54
62
  attr_writer :left, :top, :right, :bottom, :inner_horiz, :inner_vert
55
63
 
64
+ def initialize(theme)
65
+ @theme = theme
66
+ end
67
+
56
68
  def left?
57
69
  @left == true
58
70
  end
@@ -82,20 +94,29 @@ module WhirledPeas
82
94
  end
83
95
 
84
96
  def style
85
- @style || Styles::DEFAULT
97
+ @_style || theme.border_style || Styles::DEFAULT
86
98
  end
87
99
 
88
100
  def style=(val)
89
- @style = Styles.validate!(val)
101
+ @_style = Styles.validate!(val)
90
102
  end
91
103
 
92
104
  def color=(val)
93
- @color = TextColor.validate!(val)
105
+ @_color = TextColor.validate!(val)
94
106
  end
95
107
 
96
108
  def color
97
- @color || TextColor::DEFAULT
109
+ @_color || theme.border_color
98
110
  end
111
+
112
+ def inherit(parent)
113
+ @_style = parent._style
114
+ @_color = parent._color
115
+ end
116
+
117
+ protected
118
+
119
+ attr_reader :_style, :_color
99
120
  end
100
121
  end
101
122
  end
@@ -43,11 +43,11 @@ module WhirledPeas
43
43
  end
44
44
 
45
45
  def bright
46
- bright? ? self : self.class.new(@code + Utils::Ansi::BRIGHT_OFFSET, true)
46
+ bright? ? self : self.class.new(code + Utils::Ansi::BRIGHT_OFFSET, true)
47
47
  end
48
48
 
49
49
  def hash
50
- [@code, @bright].hash
50
+ [code, bright].hash
51
51
  end
52
52
 
53
53
  def ==(other)
@@ -56,12 +56,16 @@ module WhirledPeas
56
56
  alias_method :eq?, :==
57
57
 
58
58
  def to_s
59
- @code.to_s
59
+ code.to_s
60
60
  end
61
61
 
62
62
  def inspect
63
- "#{self.class.name.split('::').last}(code=#{@code}, bright=#{@bright})"
63
+ "#{self.class.name.split('::').last}(code=#{code}, bright=#{bright?})"
64
64
  end
65
+
66
+ private
67
+
68
+ attr_reader :code
65
69
  end
66
70
  private_constant :Color
67
71
  end
@@ -96,7 +96,7 @@ module WhirledPeas
96
96
  end
97
97
 
98
98
  def border
99
- @_border ||= Border.new
99
+ @_border ||= Border.new(theme)
100
100
  end
101
101
 
102
102
  def flow=(flow)
@@ -175,13 +175,20 @@ module WhirledPeas
175
175
  @_padding ||= Padding.new
176
176
  end
177
177
 
178
- def set_position(left: nil, top: nil)
179
- position.left = left if left
180
- position.top = top if top
178
+ def set_content_start(left: nil, top: nil, right: nil, bottom: nil)
179
+ if left && right
180
+ raise ArgumentError, 'Setting left and right content_start is not supported'
181
+ elsif top && bottom
182
+ raise ArgumentError, 'Setting top and bottom content_start is not supported'
183
+ end
184
+ content_start.left = left if left
185
+ content_start.top = top if top
186
+ content_start.right = right if right
187
+ content_start.bottom = bottom if bottom
181
188
  end
182
189
 
183
- def position
184
- @_position ||= Position.new
190
+ def content_start
191
+ @_content_start ||= Position.new
185
192
  end
186
193
 
187
194
  def set_scrollbar(horiz: nil, vert: nil)
@@ -212,7 +219,7 @@ module WhirledPeas
212
219
  def inherit(parent)
213
220
  super
214
221
  return unless parent.is_a?(ContainerSettings)
215
- set_border(color: parent.border.color, style: parent.border.style)
222
+ @_border = Border.inherit(parent.border)
216
223
  end
217
224
 
218
225
  protected
@@ -34,6 +34,8 @@ module WhirledPeas
34
34
  def debug_value(value)
35
35
  return if value.nil?
36
36
  case value
37
+ when Theme
38
+ nil
37
39
  when Position
38
40
  position_value(value)
39
41
  when Margin
@@ -73,7 +75,7 @@ module WhirledPeas
73
75
  def border_value(border)
74
76
  values = non_defaults(
75
77
  border,
76
- Border.new,
78
+ Border.new(settings.theme),
77
79
  %i[left? top? right? bottom? inner_horiz? inner_vert? style color]
78
80
  )
79
81
  return if values == ''
@@ -8,22 +8,30 @@ module WhirledPeas
8
8
  module Settings
9
9
  class ElementSettings
10
10
  def self.inherit(parent)
11
- child = self.new
11
+ child = self.new(parent.theme)
12
12
  child.inherit(parent)
13
13
  child
14
14
  end
15
15
 
16
- attr_accessor :width, :height
16
+ attr_accessor :theme, :width, :height
17
+
18
+ def initialize(theme)
19
+ @theme = theme
20
+ end
17
21
 
18
22
  def validate!
19
23
  end
20
24
 
21
25
  def bg_color
22
- @_bg_color || BgColor::DEFAULT
26
+ @_bg_color || theme.bg_color
23
27
  end
24
28
 
25
29
  def bg_color=(color)
26
- @_bg_color = BgColor.validate!(color)
30
+ if color == :highlight
31
+ @_bg_color = theme.highlight_bg_color
32
+ else
33
+ @_bg_color = BgColor.validate!(color)
34
+ end
27
35
  end
28
36
 
29
37
  def bold?
@@ -35,11 +43,15 @@ module WhirledPeas
35
43
  end
36
44
 
37
45
  def color
38
- @_color || TextColor::DEFAULT
46
+ @_color || theme.color
39
47
  end
40
48
 
41
49
  def color=(color)
42
- @_color = TextColor.validate!(color)
50
+ if color == :highlight
51
+ @_color = theme.highlight_color
52
+ else
53
+ @_color = TextColor.validate!(color)
54
+ end
43
55
  end
44
56
 
45
57
  def underline?