whirled_peas 0.6.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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