whirled_peas 0.6.0 → 0.9.1

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 (291) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +30 -1
  4. data/README.md +710 -136
  5. data/Rakefile +3 -32
  6. data/bin/easing +33 -0
  7. data/bin/reset_cursor +11 -0
  8. data/bin/screen_test +68 -0
  9. data/examples/graph.rb +54 -0
  10. data/examples/intro.rb +3 -3
  11. data/examples/scrolling.rb +5 -4
  12. data/lib/whirled_peas.rb +2 -4
  13. data/lib/whirled_peas/animator.rb +5 -0
  14. data/lib/whirled_peas/animator/debug_consumer.rb +17 -0
  15. data/lib/whirled_peas/animator/easing.rb +72 -0
  16. data/lib/whirled_peas/animator/frame.rb +5 -0
  17. data/lib/whirled_peas/animator/frameset.rb +33 -0
  18. data/lib/whirled_peas/animator/producer.rb +35 -0
  19. data/lib/whirled_peas/animator/renderer_consumer.rb +31 -0
  20. data/lib/whirled_peas/command.rb +5 -0
  21. data/lib/whirled_peas/command/base.rb +86 -0
  22. data/lib/whirled_peas/command/config_command.rb +44 -0
  23. data/lib/whirled_peas/command/debug.rb +21 -0
  24. data/lib/whirled_peas/command/fonts.rb +22 -0
  25. data/lib/whirled_peas/command/frame_command.rb +34 -0
  26. data/lib/whirled_peas/command/frames.rb +24 -0
  27. data/lib/whirled_peas/command/help.rb +38 -0
  28. data/lib/whirled_peas/command/play.rb +108 -0
  29. data/lib/whirled_peas/command/record.rb +57 -0
  30. data/lib/whirled_peas/command/still.rb +29 -0
  31. data/lib/whirled_peas/command_line.rb +22 -212
  32. data/lib/whirled_peas/config.rb +56 -6
  33. data/lib/whirled_peas/device.rb +5 -0
  34. data/lib/whirled_peas/device/null_device.rb +8 -0
  35. data/lib/whirled_peas/device/output_file.rb +19 -0
  36. data/lib/whirled_peas/device/screen.rb +26 -0
  37. data/lib/whirled_peas/graphics.rb +14 -0
  38. data/lib/whirled_peas/graphics/box_painter.rb +39 -46
  39. data/lib/whirled_peas/graphics/canvas.rb +22 -11
  40. data/lib/whirled_peas/graphics/composer.rb +18 -0
  41. data/lib/whirled_peas/graphics/container_coords.rb +6 -5
  42. data/lib/whirled_peas/graphics/container_dimensions.rb +26 -8
  43. data/lib/whirled_peas/graphics/container_painter.rb +133 -4
  44. data/lib/whirled_peas/graphics/content_dimensions.rb +19 -0
  45. data/lib/whirled_peas/graphics/content_painter.rb +20 -0
  46. data/lib/whirled_peas/graphics/graph_dimensions.rb +12 -0
  47. data/lib/whirled_peas/graphics/graph_painter.rb +97 -0
  48. data/lib/whirled_peas/graphics/grid_painter.rb +12 -14
  49. data/lib/whirled_peas/graphics/painter.rb +15 -1
  50. data/lib/whirled_peas/graphics/renderer.rb +13 -2
  51. data/lib/whirled_peas/graphics/text_painter.rb +9 -13
  52. data/lib/whirled_peas/settings/alignment.rb +24 -0
  53. data/lib/whirled_peas/settings/bg_color.rb +2 -0
  54. data/lib/whirled_peas/settings/border.rb +16 -16
  55. data/lib/whirled_peas/settings/color.rb +13 -14
  56. data/lib/whirled_peas/settings/container_settings.rb +126 -44
  57. data/lib/whirled_peas/settings/display_flow.rb +13 -11
  58. data/lib/whirled_peas/settings/element_settings.rb +8 -3
  59. data/lib/whirled_peas/settings/graph_settings.rb +21 -0
  60. data/lib/whirled_peas/settings/grid_settings.rb +12 -1
  61. data/lib/whirled_peas/settings/sizing.rb +19 -0
  62. data/lib/whirled_peas/settings/spacing.rb +34 -0
  63. data/lib/whirled_peas/settings/text_color.rb +2 -0
  64. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  65. data/lib/whirled_peas/utils/ansi.rb +13 -0
  66. data/lib/whirled_peas/utils/file_handler.rb +57 -0
  67. data/lib/whirled_peas/utils/title_font.rb +1 -1
  68. data/lib/whirled_peas/version.rb +1 -1
  69. data/screen_test/{rendered/elements → elements}/box.frame +1 -1
  70. data/screen_test/{rendered/elements → elements}/box.rb +0 -0
  71. data/screen_test/elements/graph.frame +1 -0
  72. data/screen_test/elements/graph.rb +12 -0
  73. data/screen_test/elements/grid.frame +1 -0
  74. data/screen_test/{rendered/elements → elements}/grid.rb +0 -0
  75. data/screen_test/elements/screen_overflow_x.frame +1 -0
  76. data/screen_test/{rendered/elements/screen_overflow.rb → elements/screen_overflow_x.rb} +0 -0
  77. data/screen_test/elements/screen_overflow_y.frame +1 -0
  78. data/screen_test/elements/screen_overflow_y.rb +9 -0
  79. data/screen_test/elements/text.frame +1 -0
  80. data/screen_test/{rendered/elements → elements}/text.rb +0 -0
  81. data/screen_test/elements/text_multiline.frame +1 -0
  82. data/screen_test/{rendered/elements → elements}/text_multiline.rb +0 -0
  83. data/screen_test/settings/align/box_around.frame +1 -0
  84. data/screen_test/settings/align/box_around.rb +16 -0
  85. data/screen_test/settings/align/box_between.frame +1 -0
  86. data/screen_test/settings/align/box_between.rb +16 -0
  87. data/screen_test/settings/align/box_center.frame +1 -0
  88. data/screen_test/{rendered/settings/align/grid.rb → settings/align/box_center.rb} +7 -6
  89. data/screen_test/settings/align/box_default.frame +1 -0
  90. data/screen_test/settings/align/box_default.rb +20 -0
  91. data/screen_test/settings/align/box_evenly.frame +1 -0
  92. data/screen_test/settings/align/box_evenly.rb +16 -0
  93. data/screen_test/settings/align/box_left.frame +1 -0
  94. data/screen_test/settings/align/box_left.rb +21 -0
  95. data/screen_test/settings/align/box_right.frame +1 -0
  96. data/screen_test/settings/align/box_right.rb +21 -0
  97. data/screen_test/settings/align/children_center.frame +1 -0
  98. data/screen_test/settings/align/children_center.rb +15 -0
  99. data/screen_test/settings/align/children_left.frame +1 -0
  100. data/screen_test/settings/align/children_left.rb +15 -0
  101. data/screen_test/settings/align/children_right.frame +1 -0
  102. data/screen_test/settings/align/children_right.rb +15 -0
  103. data/screen_test/settings/align/grid_center.frame +1 -0
  104. data/screen_test/settings/align/grid_center.rb +18 -0
  105. data/screen_test/settings/align/grid_default.frame +1 -0
  106. data/screen_test/settings/align/grid_default.rb +17 -0
  107. data/screen_test/settings/align/grid_left.frame +1 -0
  108. data/screen_test/settings/align/grid_left.rb +18 -0
  109. data/screen_test/settings/align/grid_right.frame +1 -0
  110. data/screen_test/settings/align/grid_right.rb +18 -0
  111. data/screen_test/settings/ansi/bold.frame +1 -0
  112. data/screen_test/{rendered/settings/height/overflow_box_t2b.rb → settings/ansi/bold.rb} +4 -4
  113. data/screen_test/{rendered/settings → settings}/ansi/color.frame +1 -1
  114. data/screen_test/{rendered/settings → settings}/ansi/color.rb +0 -0
  115. data/screen_test/settings/ansi/underline.frame +1 -0
  116. data/screen_test/{rendered/settings → settings}/ansi/underline.rb +4 -5
  117. data/screen_test/{rendered/settings → settings}/border.frame +1 -1
  118. data/screen_test/{rendered/settings → settings}/border.rb +1 -1
  119. data/screen_test/settings/flow/box_b2t.frame +1 -0
  120. data/screen_test/settings/flow/box_b2t.rb +26 -0
  121. data/screen_test/settings/flow/box_l2r.frame +1 -0
  122. data/screen_test/settings/flow/box_l2r.rb +26 -0
  123. data/screen_test/settings/flow/box_r2l.frame +1 -0
  124. data/screen_test/settings/flow/box_r2l.rb +26 -0
  125. data/screen_test/settings/flow/box_t2b.frame +1 -0
  126. data/screen_test/settings/flow/box_t2b.rb +26 -0
  127. data/screen_test/settings/flow/grid_b2t.frame +1 -0
  128. data/screen_test/{rendered/settings → settings}/flow/grid_b2t.rb +1 -1
  129. data/screen_test/settings/flow/grid_l2r.frame +1 -0
  130. data/screen_test/{rendered/settings → settings}/flow/grid_l2r.rb +1 -1
  131. data/screen_test/settings/flow/grid_r2l.frame +1 -0
  132. data/screen_test/{rendered/settings → settings}/flow/grid_r2l.rb +1 -1
  133. data/screen_test/settings/flow/grid_t2b.frame +1 -0
  134. data/screen_test/{rendered/settings → settings}/flow/grid_t2b.rb +1 -1
  135. data/screen_test/settings/height/box.frame +1 -0
  136. data/screen_test/{rendered/settings → settings}/height/box.rb +0 -0
  137. data/screen_test/settings/height/box_border_sizing.frame +1 -0
  138. data/screen_test/settings/height/box_border_sizing.rb +15 -0
  139. data/screen_test/settings/height/grid.frame +1 -0
  140. data/screen_test/{rendered/settings → settings}/height/grid.rb +0 -0
  141. data/screen_test/settings/height/overflow_box.frame +1 -0
  142. data/screen_test/settings/height/overflow_box.rb +13 -0
  143. data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
  144. data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
  145. data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
  146. data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
  147. data/screen_test/settings/height/overflow_grid.frame +1 -0
  148. data/screen_test/{rendered/settings → settings}/height/overflow_grid.rb +0 -0
  149. data/screen_test/settings/margin.frame +1 -0
  150. data/screen_test/settings/margin.rb +16 -0
  151. data/screen_test/settings/padding.frame +1 -0
  152. data/screen_test/settings/padding.rb +13 -0
  153. data/screen_test/settings/position/box_left.frame +1 -0
  154. data/screen_test/{rendered/settings → settings}/position/box_left.rb +2 -2
  155. data/screen_test/settings/position/box_left_negative.frame +1 -0
  156. data/screen_test/{rendered/settings → settings}/position/box_left_negative.rb +2 -2
  157. data/screen_test/settings/position/box_top.frame +1 -0
  158. data/screen_test/{rendered/settings → settings}/position/box_top.rb +1 -1
  159. data/screen_test/settings/position/box_top_negative.frame +1 -0
  160. data/screen_test/{rendered/settings → settings}/position/box_top_negative.rb +2 -2
  161. data/screen_test/settings/position/grid_left.frame +1 -0
  162. data/screen_test/{rendered/settings → settings}/position/grid_left.rb +1 -1
  163. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  164. data/screen_test/{rendered/settings → settings}/position/grid_left_negative.rb +1 -1
  165. data/screen_test/settings/position/grid_top.frame +1 -0
  166. data/screen_test/{rendered/settings → settings}/position/grid_top.rb +1 -1
  167. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  168. data/screen_test/{rendered/settings → settings}/position/grid_top_negative.rb +1 -1
  169. data/screen_test/settings/scroll/horiz_box.frame +1 -0
  170. data/screen_test/settings/scroll/horiz_box.rb +17 -0
  171. data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
  172. data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
  173. data/screen_test/settings/scroll/vert_box.frame +1 -0
  174. data/screen_test/settings/scroll/vert_box.rb +20 -0
  175. data/screen_test/settings/title_font.frame +1 -0
  176. data/screen_test/{rendered/settings → settings}/title_font.rb +1 -1
  177. data/screen_test/settings/valign/box_around.frame +1 -0
  178. data/screen_test/settings/valign/box_around.rb +17 -0
  179. data/screen_test/settings/valign/box_between.frame +1 -0
  180. data/screen_test/settings/valign/box_between.rb +17 -0
  181. data/screen_test/settings/valign/box_bottom.frame +1 -0
  182. data/screen_test/settings/valign/box_bottom.rb +17 -0
  183. data/screen_test/settings/valign/box_default.frame +1 -0
  184. data/screen_test/settings/valign/box_default.rb +16 -0
  185. data/screen_test/settings/valign/box_evenly.frame +1 -0
  186. data/screen_test/settings/valign/box_evenly.rb +17 -0
  187. data/screen_test/settings/valign/box_middle.frame +1 -0
  188. data/screen_test/settings/valign/box_middle.rb +17 -0
  189. data/screen_test/settings/valign/box_top.frame +1 -0
  190. data/screen_test/settings/valign/box_top.rb +17 -0
  191. data/screen_test/settings/valign/grid_bottom.frame +1 -0
  192. data/screen_test/settings/valign/grid_bottom.rb +15 -0
  193. data/screen_test/settings/valign/grid_default.frame +1 -0
  194. data/screen_test/settings/valign/grid_default.rb +14 -0
  195. data/screen_test/settings/valign/grid_middle.frame +1 -0
  196. data/screen_test/settings/valign/grid_middle.rb +15 -0
  197. data/screen_test/settings/valign/grid_top.frame +1 -0
  198. data/screen_test/settings/valign/grid_top.rb +15 -0
  199. data/screen_test/settings/width/box_border_sizing.frame +1 -0
  200. data/screen_test/settings/width/box_border_sizing.rb +15 -0
  201. data/screen_test/settings/width/box_content.frame +1 -0
  202. data/screen_test/settings/width/box_content.rb +15 -0
  203. data/screen_test/settings/width/box_default.frame +1 -0
  204. data/screen_test/{rendered/settings/width/box.rb → settings/width/box_default.rb} +3 -2
  205. data/screen_test/settings/width/grid.frame +1 -0
  206. data/screen_test/{rendered/settings → settings}/width/grid.rb +2 -2
  207. data/screen_test/settings/width/overflow_align_center.frame +1 -0
  208. data/screen_test/settings/width/overflow_align_center.rb +14 -0
  209. data/screen_test/settings/width/overflow_align_right.frame +1 -0
  210. data/screen_test/settings/width/overflow_align_right.rb +14 -0
  211. data/screen_test/settings/width/overflow_box.frame +1 -0
  212. data/screen_test/settings/width/overflow_box.rb +13 -0
  213. data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
  214. data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
  215. data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
  216. data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
  217. data/screen_test/settings/width/overflow_grid.frame +1 -0
  218. data/screen_test/{rendered/settings → settings}/width/overflow_grid.rb +0 -0
  219. data/tools/whirled_peas/tools/screen_tester.rb +285 -0
  220. metadata +188 -105
  221. data/lib/whirled_peas/frame.rb +0 -6
  222. data/lib/whirled_peas/frame/consumer.rb +0 -30
  223. data/lib/whirled_peas/frame/debug_consumer.rb +0 -30
  224. data/lib/whirled_peas/frame/event_loop.rb +0 -90
  225. data/lib/whirled_peas/frame/producer.rb +0 -67
  226. data/lib/whirled_peas/graphics/screen.rb +0 -70
  227. data/lib/whirled_peas/graphics/text_dimensions.rb +0 -15
  228. data/lib/whirled_peas/settings/text_align.rb +0 -19
  229. data/screen_test/rendered/elements/grid.frame +0 -1
  230. data/screen_test/rendered/elements/screen_overflow.frame +0 -1
  231. data/screen_test/rendered/elements/text.frame +0 -1
  232. data/screen_test/rendered/elements/text_multiline.frame +0 -1
  233. data/screen_test/rendered/settings/align/box.frame +0 -1
  234. data/screen_test/rendered/settings/align/box.rb +0 -24
  235. data/screen_test/rendered/settings/align/children_center.frame +0 -1
  236. data/screen_test/rendered/settings/align/children_center.rb +0 -13
  237. data/screen_test/rendered/settings/align/children_left.frame +0 -1
  238. data/screen_test/rendered/settings/align/children_left.rb +0 -13
  239. data/screen_test/rendered/settings/align/children_right.frame +0 -1
  240. data/screen_test/rendered/settings/align/children_right.rb +0 -13
  241. data/screen_test/rendered/settings/align/grid.frame +0 -1
  242. data/screen_test/rendered/settings/ansi/bold.frame +0 -1
  243. data/screen_test/rendered/settings/ansi/bold.rb +0 -15
  244. data/screen_test/rendered/settings/ansi/underline.frame +0 -1
  245. data/screen_test/rendered/settings/flow/box_b2t.frame +0 -1
  246. data/screen_test/rendered/settings/flow/box_b2t.rb +0 -24
  247. data/screen_test/rendered/settings/flow/box_l2r.frame +0 -1
  248. data/screen_test/rendered/settings/flow/box_l2r.rb +0 -24
  249. data/screen_test/rendered/settings/flow/box_r2l.frame +0 -1
  250. data/screen_test/rendered/settings/flow/box_r2l.rb +0 -24
  251. data/screen_test/rendered/settings/flow/box_t2b.frame +0 -1
  252. data/screen_test/rendered/settings/flow/box_t2b.rb +0 -24
  253. data/screen_test/rendered/settings/flow/grid_b2t.frame +0 -1
  254. data/screen_test/rendered/settings/flow/grid_l2r.frame +0 -1
  255. data/screen_test/rendered/settings/flow/grid_r2l.frame +0 -1
  256. data/screen_test/rendered/settings/flow/grid_t2b.frame +0 -1
  257. data/screen_test/rendered/settings/height/box.frame +0 -1
  258. data/screen_test/rendered/settings/height/grid.frame +0 -1
  259. data/screen_test/rendered/settings/height/overflow_box.frame +0 -1
  260. data/screen_test/rendered/settings/height/overflow_box.rb +0 -13
  261. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +0 -1
  262. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +0 -15
  263. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +0 -1
  264. data/screen_test/rendered/settings/height/overflow_grid.frame +0 -1
  265. data/screen_test/rendered/settings/margin.frame +0 -1
  266. data/screen_test/rendered/settings/margin.rb +0 -14
  267. data/screen_test/rendered/settings/padding.frame +0 -1
  268. data/screen_test/rendered/settings/padding.rb +0 -11
  269. data/screen_test/rendered/settings/position/box_left.frame +0 -1
  270. data/screen_test/rendered/settings/position/box_left_negative.frame +0 -1
  271. data/screen_test/rendered/settings/position/box_top.frame +0 -1
  272. data/screen_test/rendered/settings/position/box_top_negative.frame +0 -1
  273. data/screen_test/rendered/settings/position/grid_left.frame +0 -1
  274. data/screen_test/rendered/settings/position/grid_left_negative.frame +0 -1
  275. data/screen_test/rendered/settings/position/grid_top.frame +0 -1
  276. data/screen_test/rendered/settings/position/grid_top_negative.frame +0 -1
  277. data/screen_test/rendered/settings/scroll/horiz_box.frame +0 -1
  278. data/screen_test/rendered/settings/scroll/horiz_box.rb +0 -15
  279. data/screen_test/rendered/settings/scroll/vert_box.frame +0 -1
  280. data/screen_test/rendered/settings/scroll/vert_box.rb +0 -18
  281. data/screen_test/rendered/settings/title_font.frame +0 -1
  282. data/screen_test/rendered/settings/width/box.frame +0 -1
  283. data/screen_test/rendered/settings/width/grid.frame +0 -1
  284. data/screen_test/rendered/settings/width/overflow_box.frame +0 -1
  285. data/screen_test/rendered/settings/width/overflow_box.rb +0 -11
  286. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +0 -1
  287. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +0 -14
  288. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +0 -1
  289. data/screen_test/rendered/settings/width/overflow_box_t2b.rb +0 -15
  290. data/screen_test/rendered/settings/width/overflow_grid.frame +0 -1
  291. data/screen_test/screen_tester.rb +0 -201
@@ -4,28 +4,29 @@ module WhirledPeas
4
4
  module Graphics
5
5
  # Canvas represent the area of the screen a painter can paint on.
6
6
  class Canvas
7
- attr_reader :left, :top, :width, :height, :start_left, :start_top
7
+ attr_reader :left, :top, :width, :height
8
8
 
9
9
  def self.unwritable
10
- new(-1, -1, 0, 0, -1, -1)
10
+ new(-1, -1, 0, 0)
11
11
  end
12
12
 
13
- def initialize(left, top, width, height, start_left, start_top)
13
+ def initialize(left, top, width, height)
14
14
  @left = left
15
15
  @top = top
16
16
  @width = width
17
17
  @height = height
18
- @start_left = start_left
19
- @start_top = start_top
20
18
  end
21
19
 
22
20
  def writable?
23
21
  width > 0 || height > 0
24
22
  end
25
23
 
26
- def child(start_left, start_top, child_width, child_height)
27
- child_left = start_left
28
- child_top = start_top
24
+ def child(child_left, child_top, child_width, child_height)
25
+ Graphics.debugger(
26
+ proc do
27
+ "Create child: #{self.inspect}.child(left=#{child_left}, top=#{child_top}, width=#{child_width}, height=#{child_height})"
28
+ end
29
+ )
29
30
  if child_left >= left + width
30
31
  self.class.unwritable
31
32
  elsif child_left + child_width <= left
@@ -45,20 +46,25 @@ module WhirledPeas
45
46
  child_top = top
46
47
  end
47
48
  child_height = [height - (child_top - top), child_height].min
48
- self.class.new(
49
+ child_canvas = self.class.new(
49
50
  child_left,
50
51
  child_top,
51
52
  child_width,
52
53
  child_height,
53
- start_left,
54
- start_top
55
54
  )
55
+ Graphics.debugger(proc { " -> #{child_canvas.inspect}" })
56
+ child_canvas
56
57
  end
57
58
  end
58
59
 
59
60
  # Yields a single line of formatted characters positioned on the canvas,
60
61
  # verifying only characters within the canvas are included.
61
62
  def stroke(stroke_left, stroke_top, raw, formatting=[], &block)
63
+ Graphics.debugger(
64
+ proc do
65
+ "Stroke: #{self.inspect}.stroke(left=#{stroke_left}, top=#{stroke_top}, length=#{raw.length})"
66
+ end
67
+ )
62
68
  if stroke_left >= left + width
63
69
  # The stroke starts to the right of the canvas
64
70
  fstring = Utils::FormattedString.blank
@@ -88,6 +94,11 @@ module WhirledPeas
88
94
  end_index = start_index + visible_length - 1
89
95
  fstring = Utils::FormattedString.new(raw[start_index..end_index], formatting)
90
96
  end
97
+ Graphics.debugger(
98
+ proc do
99
+ " -> Stroke(left=#{stroke_left}, top=#{stroke_top}, length=#{fstring.length})"
100
+ end
101
+ )
91
102
  yield stroke_left, stroke_top, fstring
92
103
  end
93
104
 
@@ -1,8 +1,10 @@
1
1
  require 'whirled_peas/settings/box_settings'
2
+ require 'whirled_peas/settings/graph_settings'
2
3
  require 'whirled_peas/settings/grid_settings'
3
4
  require 'whirled_peas/settings/text_settings'
4
5
 
5
6
  require_relative 'box_painter'
7
+ require_relative 'graph_painter'
6
8
  require_relative 'grid_painter'
7
9
  require_relative 'text_painter'
8
10
 
@@ -45,6 +47,7 @@ module WhirledPeas
45
47
  child = TextPainter.new(name, child_settings)
46
48
  # TextPainters are not composable, so yield nil
47
49
  content = yield nil, child_settings
50
+ child_settings.validate!
48
51
  unless self.class.stringable?(content)
49
52
  raise ArgumentError, "Unsupported type for text: #{content.class}"
50
53
  end
@@ -52,11 +55,25 @@ module WhirledPeas
52
55
  painter.add_child(child)
53
56
  end
54
57
 
58
+ def add_graph(name=self.class.next_name, &block)
59
+ child_settings = Settings::GraphSettings.inherit(painter.settings)
60
+ child = GraphPainter.new(name, child_settings)
61
+ # GraphPainters are not composable, so yield nil
62
+ content = yield nil, child_settings
63
+ child_settings.validate!
64
+ unless content.is_a?(Array) && content.length > 0
65
+ raise ArgumentError, 'Graphs require a non-empty array as the content'
66
+ end
67
+ child.content = content
68
+ painter.add_child(child)
69
+ end
70
+
55
71
  def add_box(name=self.class.next_name, &block)
56
72
  child_settings = Settings::BoxSettings.inherit(painter.settings)
57
73
  child = BoxPainter.new(name, child_settings)
58
74
  composer = self.class.new(child)
59
75
  value = yield composer, child.settings
76
+ child_settings.validate!
60
77
  painter.add_child(child)
61
78
  if !child.children? && self.class.stringable?(value)
62
79
  composer.add_text("#{name}-Text") { value.to_s }
@@ -68,6 +85,7 @@ module WhirledPeas
68
85
  child = GridPainter.new(name, child_settings)
69
86
  composer = self.class.new(child)
70
87
  values = yield composer, child.settings
88
+ child_settings.validate!
71
89
  painter.add_child(child)
72
90
  if !child.children? && values.is_a?(Array)
73
91
  values.each.with_index do |value, index|
@@ -1,18 +1,19 @@
1
1
  module WhirledPeas
2
2
  module Graphics
3
3
  class ContainerCoords
4
- def initialize(canvas, dimensions, settings)
5
- @canvas = canvas
4
+ def initialize(dimensions, settings, start_left, start_top)
6
5
  @dimensions = dimensions
7
6
  @settings = settings
7
+ @start_left = start_left
8
+ @start_top = start_top
8
9
  end
9
10
 
10
11
  def left
11
- canvas.start_left + settings.position.left
12
+ start_left + settings.position.left
12
13
  end
13
14
 
14
15
  def top
15
- canvas.start_top + settings.position.top
16
+ start_top + settings.position.top
16
17
  end
17
18
 
18
19
  def border_left
@@ -65,7 +66,7 @@ module WhirledPeas
65
66
 
66
67
  private
67
68
 
68
- attr_reader :canvas, :settings, :dimensions
69
+ attr_reader :settings, :dimensions, :start_left, :start_top
69
70
  end
70
71
  end
71
72
  end
@@ -1,11 +1,11 @@
1
1
  module WhirledPeas
2
2
  module Graphics
3
3
  class ContainerDimensions
4
- attr_reader :content_width, :content_height, :children_width, :children_height, :num_cols, :num_rows
4
+ attr_reader :children_width, :children_height, :num_cols, :num_rows
5
5
 
6
6
  def initialize(settings, content_width, content_height, num_cols=1, num_rows=1)
7
- @content_width = settings.width || content_width
8
- @content_height = settings.height || content_height
7
+ @orig_content_width = content_width
8
+ @orig_content_height = content_height
9
9
  @children_width = content_width
10
10
  @children_height = content_height
11
11
  @num_cols = num_cols
@@ -13,17 +13,35 @@ module WhirledPeas
13
13
  @settings = settings
14
14
  end
15
15
 
16
+ def content_width
17
+ return orig_content_width unless settings.width
18
+ if settings.border_sizing?
19
+ settings.width - outer_border_width - scrollbar_width - padding_width
20
+ else
21
+ settings.width
22
+ end
23
+ end
24
+
25
+ def content_height
26
+ return orig_content_height unless settings.height
27
+ if settings.border_sizing?
28
+ settings.height - outer_border_height - scrollbar_height - padding_height
29
+ else
30
+ settings.height
31
+ end
32
+ end
33
+
16
34
  def outer_width
17
35
  @outer_width ||= margin_width +
18
36
  outer_border_width +
19
- num_cols * (padding_width + content_width + vert_scroll_width) +
37
+ num_cols * (padding_width + content_width + scrollbar_width) +
20
38
  (num_cols - 1) * inner_border_width
21
39
  end
22
40
 
23
41
  def outer_height
24
42
  @outer_height ||= margin_height +
25
43
  outer_border_height +
26
- num_rows * (padding_height + content_height + horiz_scroll_height) +
44
+ num_rows * (padding_height + content_height + scrollbar_height) +
27
45
  (num_rows - 1) * inner_border_height
28
46
  end
29
47
 
@@ -59,17 +77,17 @@ module WhirledPeas
59
77
  settings.padding.top + settings.padding.bottom
60
78
  end
61
79
 
62
- def vert_scroll_width
80
+ def scrollbar_width
63
81
  settings.scrollbar.vert? ? 1 : 0
64
82
  end
65
83
 
66
- def horiz_scroll_height
84
+ def scrollbar_height
67
85
  settings.scrollbar.horiz? ? 1 : 0
68
86
  end
69
87
 
70
88
  private
71
89
 
72
- attr_reader :settings
90
+ attr_reader :settings, :orig_content_width, :orig_content_height
73
91
  end
74
92
  end
75
93
  end
@@ -5,6 +5,8 @@ require_relative 'painter'
5
5
 
6
6
  module WhirledPeas
7
7
  module Graphics
8
+ # Abstract Painter for containers. Containers (as the name implies) contain other child
9
+ # elements and must delegate painting of the children to the children themselves.
8
10
  class ContainerPainter < Painter
9
11
  PADDING = ' '
10
12
 
@@ -13,38 +15,65 @@ module WhirledPeas
13
15
  @children = []
14
16
  end
15
17
 
16
- def paint(canvas, &block)
18
+ # Paint the common attributes of containers (e.g. border and background color). Any
19
+ # class that inherits from this one should call `super` at the start of its #paint
20
+ # method, before painting its children.
21
+ def paint(canvas, left, top, &block)
17
22
  return unless canvas.writable?
18
23
  return unless needs_printing?
19
- canvas_coords = coords(canvas)
24
+ canvas_coords = coords(left, top)
25
+
26
+ # Paint the border, background color, and scrollbar starting from the top left
27
+ # border position, moving down row by row until we reach the bottom border
28
+ # position
20
29
  stroke_left = canvas_coords.border_left
21
30
  stroke_top = canvas_coords.border_top
31
+
32
+ # All strokes will have the same formatting options
22
33
  formatting = [*settings.border.color, *settings.bg_color]
34
+
35
+ # Paint the top border if the settings call for it
23
36
  if settings.border.top?
24
37
  canvas.stroke(stroke_left, stroke_top, top_border_stroke(canvas_coords), formatting, &block)
25
38
  stroke_top += 1
26
39
  end
40
+ # Precalculate the middle border container grids with more than 1 row
27
41
  middle_border = dimensions.num_rows > 1 ? middle_border_stroke(canvas_coords) : ''
42
+
43
+ # Paint each grid row by row
28
44
  dimensions.num_rows.times do |row_num|
45
+ # In a grid with N rows, we will need to paint N - 1 inner horizontal borders.
46
+ # This code treats the inner horizontal border as the top of each row except for
47
+ # the first one.
29
48
  if row_num > 0 && settings.border.inner_horiz?
30
49
  canvas.stroke(stroke_left, stroke_top, middle_border, formatting, &block)
31
50
  stroke_top += 1
32
51
  end
52
+
53
+ # Paint the interior of each row (horizontal borders, veritical scroll bar and
54
+ # background color for the padding and content area)
33
55
  canvas_coords.inner_grid_height.times do |row_within_cell|
34
56
  canvas.stroke(stroke_left, stroke_top, content_line_stroke(canvas_coords, row_within_cell), formatting, &block)
35
57
  stroke_top += 1
36
58
  end
59
+
60
+ # Paint the horizontal scroll bar is the settings call for it
37
61
  if settings.scrollbar.horiz?
38
62
  canvas.stroke(stroke_left, stroke_top, bottom_scroll_stroke(canvas_coords), formatting, &block)
39
63
  stroke_top += 1
40
64
  end
41
65
  end
66
+
67
+ # Paint the bottom border if the settings call for it
42
68
  if settings.border.bottom?
43
69
  canvas.stroke(stroke_left, stroke_top, bottom_border_stroke(canvas_coords), formatting, &block)
44
70
  stroke_top += 1
45
71
  end
46
72
  end
47
73
 
74
+ # Tightly manage access to the children (rather than simply exposing the underlying
75
+ # array). This allows subclasses to easily modify behavior based on that element's
76
+ # specific settings.
48
77
  def add_child(child)
49
78
  children << child
50
79
  end
@@ -65,6 +94,8 @@ module WhirledPeas
65
94
 
66
95
  attr_reader :children
67
96
 
97
+ # Determine if there is anything to print for the container (this does not accont for
98
+ # children, just the border, scrollbar, and background color)
68
99
  def needs_printing?
69
100
  return true if settings.bg_color
70
101
  return true if settings.border.outer?
@@ -73,10 +104,66 @@ module WhirledPeas
73
104
  settings.scrollbar.horiz? || settings.scrollbar.vert?
74
105
  end
75
106
 
76
- def coords(canvas)
77
- ContainerCoords.new(canvas, dimensions, settings)
107
+ # Return an object that allows easy access to important coordinates within the container,
108
+ # e.g. the left position where the left border is printed
109
+ def coords(left, top)
110
+ ContainerCoords.new(dimensions, settings, left, top)
111
+ end
112
+
113
+ # @return [Array<Integer>] a two-item array, the first being the amount of horizontal
114
+ # spacing to paint *before the first* child and the second being the amount of spacing
115
+ # to paint *between each* child
116
+ def horiz_justify_offset(containing_width)
117
+ if settings.align_center?
118
+ [(dimensions.content_width - containing_width) / 2, 0]
119
+ elsif settings.align_right?
120
+ [dimensions.content_width - containing_width, 0]
121
+ elsif settings.align_between?
122
+ return [0, 0] if num_children == 1
123
+ [0, (dimensions.content_width - containing_width) / (num_children - 1)]
124
+ elsif settings.align_around?
125
+ full_spacing = (dimensions.content_width - containing_width) / num_children
126
+ [full_spacing / 2, full_spacing]
127
+ elsif settings.align_evenly?
128
+ spacing = (dimensions.content_width - containing_width) / (num_children + 1)
129
+ [spacing, spacing]
130
+ else
131
+ [0, 0]
132
+ end
133
+ end
134
+
135
+ # @return [Array<Integer>] a two-item array, the first being the amount of vertical
136
+ # spacing to paint *above the first* child and the second being the amount of spacing
137
+ # to paint *between each* child
138
+ def vert_justify_offset(containing_height)
139
+ if settings.valign_middle?
140
+ [(dimensions.content_height - containing_height) / 2, 0]
141
+ elsif settings.valign_bottom?
142
+ [dimensions.content_height - containing_height, 0]
143
+ elsif settings.valign_between?
144
+ return [0, 0] if num_children == 1
145
+ [0, (dimensions.content_height - containing_height) / (num_children - 1)]
146
+ elsif settings.valign_around?
147
+ full_spacing = (dimensions.content_height - containing_height) / num_children
148
+ [full_spacing / 2, full_spacing]
149
+ elsif settings.valign_evenly?
150
+ spacing = (dimensions.content_height - containing_height) / (num_children + 1)
151
+ [spacing, spacing]
152
+ else
153
+ [0, 0]
154
+ end
78
155
  end
79
156
 
157
+ # Return a stroke for one line of the container
158
+ #
159
+ # @param left_border [String] the character to print as the first character if there
160
+ # is a left border
161
+ # @param junc_border [String] the character to print as the junction between two grid
162
+ # columns if there is an inner vertical border
163
+ # @param right_border [String] the character to print as the last character if there
164
+ # is a right border
165
+ # @block [String] the block should yield a string that represents the interior
166
+ # (including padding) of a grid cell
80
167
  def line_stroke(left_border, junc_border, right_border, &block)
81
168
  stroke = ''
82
169
  stroke += left_border if settings.border.left?
@@ -88,6 +175,7 @@ module WhirledPeas
88
175
  stroke
89
176
  end
90
177
 
178
+ # Return the stroke for the top border
91
179
  def top_border_stroke(canvas_coords)
92
180
  line_stroke(
93
181
  settings.border.style.top_left,
@@ -98,6 +186,7 @@ module WhirledPeas
98
186
  end
99
187
  end
100
188
 
189
+ # Return the stroke for an inner horizontal border
101
190
  def middle_border_stroke(canvas_coords)
102
191
  line_stroke(
103
192
  settings.border.style.left_junc,
@@ -108,6 +197,7 @@ module WhirledPeas
108
197
  end
109
198
  end
110
199
 
200
+ # Return the stroke for the bottom border
111
201
  def bottom_border_stroke(canvas_coords)
112
202
  line_stroke(
113
203
  settings.border.style.bottom_left,
@@ -118,6 +208,7 @@ module WhirledPeas
118
208
  end
119
209
  end
120
210
 
211
+ # Return the stroke for a grid row between any borders
121
212
  def content_line_stroke(canvas_coords, row_within_cell)
122
213
  line_stroke(
123
214
  settings.border.style.left_vert,
@@ -142,6 +233,7 @@ module WhirledPeas
142
233
  end
143
234
  end
144
235
 
236
+ # Return the stroke for the horizontal scroll bar
145
237
  def bottom_scroll_stroke(canvas_coords)
146
238
  line_stroke(
147
239
  settings.border.style.left_vert,
@@ -159,6 +251,7 @@ module WhirledPeas
159
251
  end
160
252
  end
161
253
 
254
+ # Contants to paint scrollbars
162
255
  GUTTER = ' '
163
256
  HORIZONTAL = %w[▗ ▄ ▖]
164
257
  VERTICAL = %w[
@@ -167,16 +260,33 @@ module WhirledPeas
167
260
 
168
261
  ]
169
262
 
263
+ # Determine the character to paint the horizontal scroll bar with for the given column
264
+ #
265
+ # @see #scroll_char for more details
170
266
  def horiz_scroll_char(col_count, viewable_col_count, first_visible_col, curr_col)
171
267
  scroll_char(col_count, viewable_col_count, first_visible_col, curr_col, HORIZONTAL)
172
268
  end
173
269
 
270
+ # Determine the character to paint the vertical scroll bar with for the given row
271
+ #
272
+ # @see #scroll_char for more details
174
273
  def vert_scroll_char(row_count, viewable_row_count, first_visible_row, curr_row)
175
274
  scroll_char(row_count, viewable_row_count, first_visible_row, curr_row, VERTICAL)
176
275
  end
177
276
 
178
277
  private
179
278
 
279
+ # Determine which character to paint a for a scrollbar
280
+ #
281
+ # @param total_count [Integer] total number of rows/columns in the content
282
+ # @param viewable_count [Integer] number of rows/columns visible in the viewport
283
+ # @param first_visible [Integer] zero-based index of the first row/column that is visible
284
+ # in the viewport
285
+ # @param curr [Integer] zero-based index of the row/column (relative to the first visible
286
+ # row/column) that the painted character is being requested for
287
+ # @param chars [Array<String>] an array with three 1-character strings, the frist is the
288
+ # "second half" scrollbar character, the second is the "full" scrollbar character, and
289
+ # the third is the "first half" scrollbar character.
180
290
  def scroll_char(total_count, viewable_count, first_visible, curr, chars)
181
291
  return GUTTER unless total_count > 0 && viewable_count > 0
182
292
  # The scroll handle has the exact same relative size and position in the scroll gutter
@@ -201,6 +311,25 @@ module WhirledPeas
201
311
  # | * *
202
312
  # +---------1---------2---------3*********4*********+
203
313
  # |...........********|
314
+ #
315
+ # Returning to the first example, we can match up the arguments to this method to the
316
+ # diagram
317
+ #
318
+ # total_count = 50
319
+ # |<----------------------------------------------->|
320
+ # | |
321
+ # | veiwable_count = 20 |
322
+ # | |<----------------->| |
323
+ # ↓ ↓ ↓ ↓
324
+ # +---------1-----****2*********3******---4---------+
325
+ # | * * |
326
+ # | hidden * viewable * hidden |
327
+ # | * * |
328
+ # +---------1-----****2*********3******---4---------+
329
+ # |......****?***.....|
330
+ # ↑ ↑
331
+ # first_visible = 16 |
332
+ # curr = 11
204
333
 
205
334
  # The first task of determining how much of the handle is visible in a row/column is to
206
335
  # calculate the range (as a precentage of the total) of viewable items