whirled_peas 0.9.0 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/CHANGELOG.md +21 -0
  4. data/README.md +9 -1053
  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 +23 -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 +17 -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 +7 -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 +88 -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 +12 -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 -22
  45. data/lib/whirled_peas/graphics/pixel_grid.rb +44 -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 +25 -4
  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 +26 -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 +20 -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
@@ -9,11 +9,11 @@ module WhirledPeas
9
9
  end
10
10
 
11
11
  def left
12
- start_left + settings.position.left
12
+ start_left
13
13
  end
14
14
 
15
15
  def top
16
- start_top + settings.position.top
16
+ start_top
17
17
  end
18
18
 
19
19
  def border_left
@@ -33,35 +33,33 @@ module WhirledPeas
33
33
  end
34
34
 
35
35
  def content_left(col_index=0)
36
- padding_left + settings.padding.left + col_index * grid_width
36
+ padding_left + settings.padding.left + col_index * dimensions.grid_width
37
37
  end
38
38
 
39
- def content_top(row_index=0)
40
- padding_top + settings.padding.top + row_index * grid_height
41
- end
42
-
43
- def inner_grid_width
44
- settings.padding.left +
45
- dimensions.content_width +
46
- settings.padding.right
39
+ def offset_content_left(col_index=0)
40
+ if settings.content_start.left
41
+ content_left(col_index) + settings.content_start.left
42
+ elsif settings.content_start.right
43
+ left_offset = dimensions.content_width - dimensions.children_width
44
+ content_left(col_index) + left_offset - settings.content_start.right
45
+ else
46
+ content_left(col_index)
47
+ end
47
48
  end
48
49
 
49
- def grid_width
50
- (settings.border.inner_vert? ? 1 : 0) +
51
- inner_grid_width +
52
- (settings.scrollbar.vert? ? 1 : 0)
53
- end
54
-
55
- def inner_grid_height
56
- settings.padding.top +
57
- dimensions.content_height +
58
- settings.padding.bottom
50
+ def content_top(row_index=0)
51
+ padding_top + settings.padding.top + row_index * dimensions.grid_height
59
52
  end
60
53
 
61
- def grid_height
62
- (settings.border.inner_horiz? ? 1 : 0) +
63
- inner_grid_height +
64
- (settings.scrollbar.horiz? ? 1 : 0)
54
+ def offset_content_top(row_index=0)
55
+ if settings.content_start.top
56
+ content_top(row_index) + settings.content_start.top
57
+ elsif settings.content_start.bottom
58
+ top_offset = dimensions.content_height - dimensions.children_height
59
+ content_top(row_index) + top_offset - settings.content_start.bottom
60
+ else
61
+ content_top(row_index)
62
+ end
65
63
  end
66
64
 
67
65
  private
@@ -31,6 +31,30 @@ module WhirledPeas
31
31
  end
32
32
  end
33
33
 
34
+ def inner_grid_width
35
+ settings.padding.left +
36
+ content_width +
37
+ settings.padding.right
38
+ end
39
+
40
+ def grid_width
41
+ (settings.border.inner_vert? ? 1 : 0) +
42
+ inner_grid_width +
43
+ (settings.scrollbar.vert? ? 1 : 0)
44
+ end
45
+
46
+ def inner_grid_height
47
+ settings.padding.top +
48
+ content_height +
49
+ settings.padding.bottom
50
+ end
51
+
52
+ def grid_height
53
+ (settings.border.inner_horiz? ? 1 : 0) +
54
+ inner_grid_height +
55
+ (settings.scrollbar.horiz? ? 1 : 0)
56
+ end
57
+
34
58
  def outer_width
35
59
  @outer_width ||= margin_width +
36
60
  outer_border_width +
@@ -2,6 +2,7 @@ require 'whirled_peas/utils/formatted_string'
2
2
 
3
3
  require_relative 'container_coords'
4
4
  require_relative 'painter'
5
+ require_relative 'scrollbar_helper'
5
6
 
6
7
  module WhirledPeas
7
8
  module Graphics
@@ -34,11 +35,17 @@ module WhirledPeas
34
35
 
35
36
  # Paint the top border if the settings call for it
36
37
  if settings.border.top?
37
- canvas.stroke(stroke_left, stroke_top, top_border_stroke(canvas_coords), formatting, &block)
38
+ canvas.stroke(stroke_left, stroke_top, top_border_stroke, formatting, &block)
38
39
  stroke_top += 1
39
40
  end
40
41
  # Precalculate the middle border container grids with more than 1 row
41
- middle_border = dimensions.num_rows > 1 ? middle_border_stroke(canvas_coords) : ''
42
+ middle_border = dimensions.num_rows > 1 ? middle_border_stroke : ''
43
+
44
+ vert_scrollbar = settings.scrollbar.vert? ? ScrollbarHelper.vert(
45
+ dimensions.children_height + dimensions.padding_height,
46
+ dimensions.inner_grid_height,
47
+ canvas_coords.content_top - canvas_coords.offset_content_top,
48
+ ) : []
42
49
 
43
50
  # Paint each grid row by row
44
51
  dimensions.num_rows.times do |row_num|
@@ -52,8 +59,9 @@ module WhirledPeas
52
59
 
53
60
  # Paint the interior of each row (horizontal borders, veritical scroll bar and
54
61
  # background color for the padding and content area)
55
- canvas_coords.inner_grid_height.times do |row_within_cell|
56
- canvas.stroke(stroke_left, stroke_top, content_line_stroke(canvas_coords, row_within_cell), formatting, &block)
62
+ dimensions.inner_grid_height.times do |row_within_cell|
63
+ content_line = content_line_stroke(row_within_cell, vert_scrollbar)
64
+ canvas.stroke(stroke_left, stroke_top, content_line, formatting, &block)
57
65
  stroke_top += 1
58
66
  end
59
67
 
@@ -66,7 +74,7 @@ module WhirledPeas
66
74
 
67
75
  # Paint the bottom border if the settings call for it
68
76
  if settings.border.bottom?
69
- canvas.stroke(stroke_left, stroke_top, bottom_border_stroke(canvas_coords), formatting, &block)
77
+ canvas.stroke(stroke_left, stroke_top, bottom_border_stroke, formatting, &block)
70
78
  stroke_top += 1
71
79
  end
72
80
  end
@@ -176,59 +184,49 @@ module WhirledPeas
176
184
  end
177
185
 
178
186
  # Return the stroke for the top border
179
- def top_border_stroke(canvas_coords)
187
+ def top_border_stroke
180
188
  line_stroke(
181
189
  settings.border.style.top_left,
182
190
  settings.border.style.top_junc,
183
191
  settings.border.style.top_right
184
192
  ) do
185
- settings.border.style.top_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
193
+ settings.border.style.top_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
186
194
  end
187
195
  end
188
196
 
189
197
  # Return the stroke for an inner horizontal border
190
- def middle_border_stroke(canvas_coords)
198
+ def middle_border_stroke
191
199
  line_stroke(
192
200
  settings.border.style.left_junc,
193
201
  settings.border.style.cross_junc,
194
202
  settings.border.style.right_junc
195
203
  ) do
196
- settings.border.style.middle_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
204
+ settings.border.style.middle_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
197
205
  end
198
206
  end
199
207
 
200
208
  # Return the stroke for the bottom border
201
- def bottom_border_stroke(canvas_coords)
209
+ def bottom_border_stroke
202
210
  line_stroke(
203
211
  settings.border.style.bottom_left,
204
212
  settings.border.style.bottom_junc,
205
213
  settings.border.style.bottom_right
206
214
  ) do
207
- settings.border.style.bottom_horiz * (canvas_coords.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
215
+ settings.border.style.bottom_horiz * (dimensions.inner_grid_width + (settings.scrollbar.vert? ? 1 : 0))
208
216
  end
209
217
  end
210
218
 
211
219
  # Return the stroke for a grid row between any borders
212
- def content_line_stroke(canvas_coords, row_within_cell)
220
+ def content_line_stroke(row_within_cell, vert_scrollbar)
213
221
  line_stroke(
214
222
  settings.border.style.left_vert,
215
223
  settings.border.style.middle_vert,
216
224
  settings.border.style.right_vert,
217
225
  ) do
218
226
  if settings.scrollbar.vert?
219
- if dimensions.children_height <= canvas_coords.grid_height || children.first.settings.position.top > 0
220
- scrollbar_char = GUTTER
221
- else
222
- scrollbar_char = vert_scroll_char(
223
- dimensions.children_height + dimensions.padding_height,
224
- canvas_coords.inner_grid_height,
225
- -children.first.settings.position.top,
226
- row_within_cell
227
- )
228
- end
229
- PADDING * canvas_coords.inner_grid_width + scrollbar_char
227
+ PADDING * dimensions.inner_grid_width + vert_scrollbar[row_within_cell]
230
228
  else
231
- PADDING * canvas_coords.inner_grid_width
229
+ PADDING * dimensions.inner_grid_width
232
230
  end
233
231
  end
234
232
  end
@@ -240,121 +238,11 @@ module WhirledPeas
240
238
  settings.border.style.middle_vert,
241
239
  settings.border.style.right_vert,
242
240
  ) do
243
- canvas_coords.inner_grid_width.times.map do |col_within_cell|
244
- horiz_scroll_char(
245
- dimensions.children_width + dimensions.padding_width,
246
- canvas_coords.inner_grid_width,
247
- -children.first.settings.position.left,
248
- col_within_cell
249
- )
250
- end.join
251
- end
252
- end
253
-
254
- # Contants to paint scrollbars
255
- GUTTER = ' '
256
- HORIZONTAL = %w[▗ ▄ ▖]
257
- VERTICAL = %w[
258
-
259
-
260
-
261
- ]
262
-
263
- # Determine the character to paint the horizontal scroll bar with for the given column
264
- #
265
- # @see #scroll_char for more details
266
- def horiz_scroll_char(col_count, viewable_col_count, first_visible_col, curr_col)
267
- scroll_char(col_count, viewable_col_count, first_visible_col, curr_col, HORIZONTAL)
268
- end
269
-
270
- # Determine the character to paint the vertical scroll bar with for the given row
271
- #
272
- # @see #scroll_char for more details
273
- def vert_scroll_char(row_count, viewable_row_count, first_visible_row, curr_row)
274
- scroll_char(row_count, viewable_row_count, first_visible_row, curr_row, VERTICAL)
275
- end
276
-
277
- private
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.
290
- def scroll_char(total_count, viewable_count, first_visible, curr, chars)
291
- return GUTTER unless total_count > 0 && viewable_count > 0
292
- # The scroll handle has the exact same relative size and position in the scroll gutter
293
- # that the viewable content has in the total content area. For example, a content area
294
- # that is 50 columns wide with a view port that is 20 columns wide might look like
295
- #
296
- # +---------1-----****2*********3******---4---------+
297
- # | * * |
298
- # | hidden * viewable * hidden |
299
- # | * * |
300
- # +---------1-----****2*********3******---4---------+
301
- #
302
- # The scoll gutter, would look like
303
- #
304
- # |......********.....|
305
- #
306
- # Scrolling all the way to the right results in
307
- #
308
- # +---------1---------2---------3*********4*********+
309
- # | * *
310
- # | hidden * viewable *
311
- # | * *
312
- # +---------1---------2---------3*********4*********+
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
333
-
334
- # The first task of determining how much of the handle is visible in a row/column is to
335
- # calculate the range (as a precentage of the total) of viewable items
336
- viewable_start = first_visible.to_f / total_count
337
- viewable_end = (first_visible + viewable_count).to_f / total_count
338
-
339
- # Always use the same length for the scroll bar so it does not give an inchworm effect
340
- # as it scrolls along.
341
- #
342
- # Also, double the value now to get granularity for half width
343
- # scrollbar characters.
344
- scrollbar_length = ((2 * viewable_count ** 2).to_f / total_count).ceil
345
- scrollbar_start = ((2 * first_visible * viewable_count).to_f / total_count).floor
346
-
347
- first_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr)
348
- second_half = (scrollbar_start...scrollbar_start + scrollbar_length).include?(2 * curr + 1)
349
-
350
- if first_half && second_half
351
- chars[1]
352
- elsif first_half
353
- chars[2]
354
- elsif second_half
355
- chars[0]
356
- else
357
- GUTTER
241
+ ScrollbarHelper.horiz(
242
+ dimensions.children_width + dimensions.padding_width,
243
+ dimensions.inner_grid_width,
244
+ canvas_coords.content_left - canvas_coords.offset_content_left
245
+ ).join
358
246
  end
359
247
  end
360
248
  end
@@ -15,16 +15,11 @@ module WhirledPeas
15
15
  "#{indent}* #{painter.class}(#{painter.name})",
16
16
  ]
17
17
  info << "#{indent + ' '}- Dimensions(#{dimensions})"
18
+ info << "#{indent + ' '}- Theme=#{painter.settings.theme.inspect}" if indent == ''
18
19
  info << "#{indent + ' '}- Settings"
19
20
  info << Settings::Debugger.new(painter.settings).debug(indent + ' ')
20
21
  if painter.is_a?(TextPainter)
21
- content = if painter.content.length > 1
22
- '<multiline>'
23
- elsif painter.content.first.length > 12
24
- "#{painter.content.first[0..9]}...".inspect
25
- else
26
- painter.content.first.inspect
27
- end
22
+ content = painter.content
28
23
  info << "#{indent + ' '}- Content(value=#{content})"
29
24
  elsif painter.is_a?(ContainerPainter)
30
25
  info << "#{indent + ' '}- Children"
@@ -5,6 +5,10 @@ require_relative 'content_painter'
5
5
  module WhirledPeas
6
6
  module Graphics
7
7
  class GraphPainter < ContentPainter
8
+ # The number of units along the Y-axis a single character can be divided into
9
+ Y_AXIS_SCALE = 2
10
+ private_constant :Y_AXIS_SCALE
11
+
8
12
  def paint(canvas, left, top, &block)
9
13
  axis_formatting = [*settings.axis_color, *settings.bg_color]
10
14
  plot_formatting = [*settings.color, *settings.bg_color]
@@ -26,8 +30,8 @@ module WhirledPeas
26
30
  min_y = 1.0 / 0
27
31
  max_y = -1.0 / 0
28
32
  if settings.width
29
- interpolated = inner_width.times.map do |i|
30
- x = (i * (content.length - 1).to_f / inner_width).floor
33
+ interpolated = (inner_width + 1).times.map do |i|
34
+ x = (i * (content.length - 1).to_f / (inner_width + 1)).floor
31
35
  max_y = content[x] if content[x] > max_y
32
36
  min_y = content[x] if content[x] < min_y
33
37
  content[x]
@@ -39,29 +43,34 @@ module WhirledPeas
39
43
  min_y = y if y < min_y
40
44
  end
41
45
  end
46
+ if min_y == max_y
47
+ min_y -= 1
48
+ max_y += 1
49
+ end
42
50
  scaled = interpolated.map do |y|
43
- (2 * inner_height * (y - min_y).to_f / (max_y - min_y)).floor
51
+ inner_height * (y - min_y).to_f / (max_y - min_y)
44
52
  end
53
+
45
54
  @plot_lines = Array.new(inner_height) { '' }
46
- scaled.each.with_index do |y, x_index|
55
+ scaled[0..-2].each.with_index do |y, x_index|
56
+ last = x_index == scaled.length - 2
57
+
58
+ prev_y = x_index == 0 ? y : scaled[x_index - 1]
59
+ next_y = scaled[x_index + 1]
60
+
61
+ min_y = [(prev_y + y) / 2, y, last ? next_y : (y + next_y) / 2].min
62
+ min_y = (Y_AXIS_SCALE * min_y).round.to_f / Y_AXIS_SCALE
63
+
64
+ max_y = [(prev_y + y) / 2, y, last ? next_y : (y + next_y) / 2].max
65
+ max_y = (Y_AXIS_SCALE * max_y).round.to_f / Y_AXIS_SCALE
66
+
47
67
  @plot_lines.each.with_index do |row, row_index|
48
- y_index = inner_height - row_index - 1
49
- asc, next_y = if scaled.length == 1
50
- [true, y]
51
- elsif x_index == scaled.length - 1
52
- y >= scaled[x_index - 1]
53
- [true, y]
54
- else
55
- scaled[x_index + 1] >= y
56
- [true, scaled[x_index + 1]]
57
- end
58
- if asc
59
- top_half = (y...next_y).include?(2 * y_index + 1)
60
- bottom_half = (y...next_y).include?(2 * y_index)
61
- else
62
- top_half = (next_y...y).include?(2 * y_index + 1)
63
- bottom_half = (next_y...y).include?(2 * y_index)
64
- end
68
+ bottom_value = inner_height - row_index - 1
69
+ top_value = bottom_value + 1.0 / Y_AXIS_SCALE
70
+
71
+ top_half = min_y == top_value || (min_y <= top_value && top_value < max_y)
72
+ bottom_half = min_y == bottom_value || (min_y <= bottom_value && bottom_value < max_y)
73
+
65
74
  row << if top_half && bottom_half
66
75
  '█'
67
76
  elsif top_half
@@ -81,7 +90,7 @@ module WhirledPeas
81
90
  end
82
91
 
83
92
  def inner_width
84
- settings.width.nil? ? content.length : settings.width - 1
93
+ (settings.width.nil? ? content.length : settings.width) - 1
85
94
  end
86
95
 
87
96
  def axes_lines
@@ -0,0 +1,44 @@
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 to_s
18
+ str = Utils::Ansi.cursor_pos(left: 0, top: 0) + Utils::Ansi.clear_down
19
+ formatting = nil
20
+ chars = ''
21
+ pixels.each.with_index do |row, row_offset|
22
+ str += Utils::Ansi.cursor_pos(left: 0, top: row_offset) if row_offset > 0
23
+ row.each do |pixel|
24
+ if pixel.formatting != formatting
25
+ str += Utils::FormattedString.new(chars, formatting).to_s if chars != ''
26
+ chars = ''
27
+ formatting = pixel.formatting
28
+ end
29
+ chars << pixel.char
30
+ end
31
+ if chars != ''
32
+ str += Utils::FormattedString.new(chars, formatting).to_s
33
+ chars = ''
34
+ end
35
+ end
36
+ str
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :pixels
42
+ end
43
+ end
44
+ end