whirled_peas 0.9.1 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (255) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +0 -2
  3. data/CHANGELOG.md +23 -0
  4. data/README.md +9 -1049
  5. data/doc/application.md +92 -0
  6. data/doc/cli.md +115 -0
  7. data/doc/components.md +51 -0
  8. data/doc/easing.md +257 -0
  9. data/doc/elements.md +69 -0
  10. data/doc/screen_test.md +22 -0
  11. data/doc/settings.md +466 -0
  12. data/doc/template_factory.md +53 -0
  13. data/doc/themes.md +50 -0
  14. data/examples/components.rb +33 -0
  15. data/examples/graph.rb +1 -1
  16. data/examples/scrolling.rb +8 -19
  17. data/lib/data/themes.yaml +15 -0
  18. data/lib/whirled_peas.rb +31 -9
  19. data/lib/whirled_peas/animator/debug_consumer.rb +2 -2
  20. data/lib/whirled_peas/animator/easing.rb +57 -0
  21. data/lib/whirled_peas/animator/frameset.rb +13 -8
  22. data/lib/whirled_peas/animator/producer.rb +7 -8
  23. data/lib/whirled_peas/animator/renderer_consumer.rb +10 -5
  24. data/lib/whirled_peas/command/config_command.rb +3 -3
  25. data/lib/whirled_peas/command/frames.rb +1 -3
  26. data/lib/whirled_peas/command/play.rb +9 -7
  27. data/lib/whirled_peas/command/record.rb +1 -3
  28. data/lib/whirled_peas/command/still.rb +6 -2
  29. data/lib/whirled_peas/command/themes.rb +78 -0
  30. data/lib/whirled_peas/command_line.rb +3 -1
  31. data/lib/whirled_peas/component.rb +35 -0
  32. data/lib/whirled_peas/component/list_with_active.rb +89 -0
  33. data/lib/whirled_peas/config.rb +1 -8
  34. data/lib/whirled_peas/device/null_device.rb +1 -1
  35. data/lib/whirled_peas/device/output_file.rb +2 -2
  36. data/lib/whirled_peas/device/rendered_frame.rb +12 -0
  37. data/lib/whirled_peas/device/screen.rb +7 -8
  38. data/lib/whirled_peas/graphics/box_painter.rb +4 -4
  39. data/lib/whirled_peas/graphics/composer.rb +9 -2
  40. data/lib/whirled_peas/graphics/container_coords.rb +23 -25
  41. data/lib/whirled_peas/graphics/container_dimensions.rb +24 -0
  42. data/lib/whirled_peas/graphics/container_painter.rb +27 -139
  43. data/lib/whirled_peas/graphics/debugger.rb +2 -7
  44. data/lib/whirled_peas/graphics/graph_painter.rb +31 -24
  45. data/lib/whirled_peas/graphics/pixel_grid.rb +81 -0
  46. data/lib/whirled_peas/graphics/renderer.rb +4 -5
  47. data/lib/whirled_peas/graphics/scrollbar_helper.rb +126 -0
  48. data/lib/whirled_peas/null_logger.rb +0 -1
  49. data/lib/whirled_peas/settings/bg_color.rb +5 -1
  50. data/lib/whirled_peas/settings/border.rb +26 -5
  51. data/lib/whirled_peas/settings/color.rb +8 -4
  52. data/lib/whirled_peas/settings/container_settings.rb +14 -7
  53. data/lib/whirled_peas/settings/debugger.rb +3 -1
  54. data/lib/whirled_peas/settings/element_settings.rb +18 -6
  55. data/lib/whirled_peas/settings/graph_settings.rb +1 -1
  56. data/lib/whirled_peas/settings/position.rb +17 -5
  57. data/lib/whirled_peas/settings/text_color.rb +5 -0
  58. data/lib/whirled_peas/settings/text_settings.rb +1 -0
  59. data/lib/whirled_peas/settings/theme.rb +61 -0
  60. data/lib/whirled_peas/settings/theme_library.rb +69 -0
  61. data/lib/whirled_peas/utils/ansi.rb +4 -2
  62. data/lib/whirled_peas/utils/file_handler.rb +14 -9
  63. data/lib/whirled_peas/utils/formatted_string.rb +5 -3
  64. data/lib/whirled_peas/version.rb +1 -1
  65. data/screen_test/components/list_with_active/l2r_position_end.frame +1 -0
  66. data/screen_test/components/list_with_active/l2r_position_end.rb +16 -0
  67. data/screen_test/components/list_with_active/l2r_position_middle.frame +1 -0
  68. data/screen_test/components/list_with_active/l2r_position_middle.rb +16 -0
  69. data/screen_test/components/list_with_active/l2r_position_start.frame +1 -0
  70. data/screen_test/components/list_with_active/l2r_position_start.rb +16 -0
  71. data/screen_test/components/list_with_active/l2r_separator.frame +1 -0
  72. data/screen_test/components/list_with_active/l2r_separator.rb +17 -0
  73. data/screen_test/components/list_with_active/t2b_position_end.frame +1 -0
  74. data/screen_test/components/list_with_active/t2b_position_end.rb +17 -0
  75. data/screen_test/components/list_with_active/t2b_position_middle.frame +1 -0
  76. data/screen_test/components/list_with_active/t2b_position_middle.rb +17 -0
  77. data/screen_test/components/list_with_active/t2b_position_start.frame +1 -0
  78. data/screen_test/components/list_with_active/t2b_position_start.rb +17 -0
  79. data/screen_test/components/list_with_active/t2b_separator.frame +1 -0
  80. data/screen_test/components/list_with_active/t2b_separator.rb +18 -0
  81. data/screen_test/elements/box.frame +1 -1
  82. data/screen_test/elements/box.rb +1 -1
  83. data/screen_test/elements/graph_asc.frame +1 -0
  84. data/screen_test/elements/{graph.rb → graph_asc.rb} +1 -1
  85. data/screen_test/elements/graph_desc.frame +1 -0
  86. data/screen_test/elements/graph_desc.rb +12 -0
  87. data/screen_test/elements/graph_horiz.frame +1 -0
  88. data/screen_test/elements/graph_horiz.rb +12 -0
  89. data/screen_test/elements/graph_sin.frame +1 -0
  90. data/screen_test/elements/graph_sin.rb +12 -0
  91. data/screen_test/elements/grid.frame +1 -1
  92. data/screen_test/elements/grid.rb +1 -1
  93. data/screen_test/elements/screen_overflow_x.frame +1 -1
  94. data/screen_test/elements/screen_overflow_x.rb +1 -1
  95. data/screen_test/elements/screen_overflow_y.frame +1 -1
  96. data/screen_test/elements/screen_overflow_y.rb +1 -1
  97. data/screen_test/elements/text.frame +1 -1
  98. data/screen_test/elements/text.rb +1 -1
  99. data/screen_test/elements/text_multiline.frame +1 -1
  100. data/screen_test/elements/text_multiline.rb +1 -1
  101. data/screen_test/elements/theme.frame +1 -0
  102. data/screen_test/elements/theme.rb +27 -0
  103. data/screen_test/settings/align/box_around.frame +1 -1
  104. data/screen_test/settings/align/box_around.rb +1 -1
  105. data/screen_test/settings/align/box_between.frame +1 -1
  106. data/screen_test/settings/align/box_between.rb +1 -1
  107. data/screen_test/settings/align/box_center.frame +1 -1
  108. data/screen_test/settings/align/box_center.rb +1 -1
  109. data/screen_test/settings/align/box_default.frame +1 -1
  110. data/screen_test/settings/align/box_default.rb +1 -1
  111. data/screen_test/settings/align/box_evenly.frame +1 -1
  112. data/screen_test/settings/align/box_evenly.rb +1 -1
  113. data/screen_test/settings/align/box_left.frame +1 -1
  114. data/screen_test/settings/align/box_left.rb +1 -1
  115. data/screen_test/settings/align/box_right.frame +1 -1
  116. data/screen_test/settings/align/box_right.rb +1 -1
  117. data/screen_test/settings/align/children_center.frame +1 -1
  118. data/screen_test/settings/align/children_center.rb +1 -1
  119. data/screen_test/settings/align/children_left.frame +1 -1
  120. data/screen_test/settings/align/children_left.rb +1 -1
  121. data/screen_test/settings/align/children_right.frame +1 -1
  122. data/screen_test/settings/align/children_right.rb +1 -1
  123. data/screen_test/settings/align/grid_center.frame +1 -1
  124. data/screen_test/settings/align/grid_center.rb +1 -1
  125. data/screen_test/settings/align/grid_default.frame +1 -1
  126. data/screen_test/settings/align/grid_default.rb +1 -1
  127. data/screen_test/settings/align/grid_left.frame +1 -1
  128. data/screen_test/settings/align/grid_left.rb +1 -1
  129. data/screen_test/settings/align/grid_right.frame +1 -1
  130. data/screen_test/settings/align/grid_right.rb +1 -1
  131. data/screen_test/settings/ansi/bold.frame +1 -1
  132. data/screen_test/settings/ansi/bold.rb +1 -1
  133. data/screen_test/settings/ansi/color.frame +1 -1
  134. data/screen_test/settings/ansi/color.rb +1 -1
  135. data/screen_test/settings/ansi/underline.frame +1 -1
  136. data/screen_test/settings/ansi/underline.rb +1 -1
  137. data/screen_test/settings/border.frame +1 -1
  138. data/screen_test/settings/border.rb +1 -1
  139. data/screen_test/settings/content_start/box_bottom.frame +1 -0
  140. data/screen_test/settings/content_start/box_bottom.rb +17 -0
  141. data/screen_test/settings/content_start/box_left.frame +1 -0
  142. data/screen_test/settings/{position → content_start}/box_left.rb +2 -2
  143. data/screen_test/settings/content_start/box_left_negative.frame +1 -0
  144. data/screen_test/settings/{position → content_start}/box_left_negative.rb +2 -2
  145. data/screen_test/settings/content_start/box_right.frame +1 -0
  146. data/screen_test/settings/content_start/box_right.rb +17 -0
  147. data/screen_test/settings/content_start/box_top.frame +1 -0
  148. data/screen_test/settings/{position → content_start}/box_top.rb +2 -2
  149. data/screen_test/settings/content_start/box_top_negative.frame +1 -0
  150. data/screen_test/settings/{position → content_start}/box_top_negative.rb +2 -2
  151. data/screen_test/settings/content_start/grid_left.frame +1 -0
  152. data/screen_test/settings/{position → content_start}/grid_left.rb +2 -2
  153. data/screen_test/settings/content_start/grid_left_negative.frame +1 -0
  154. data/screen_test/settings/{position → content_start}/grid_left_negative.rb +2 -2
  155. data/screen_test/settings/content_start/grid_top.frame +1 -0
  156. data/screen_test/settings/{position → content_start}/grid_top.rb +2 -2
  157. data/screen_test/settings/content_start/grid_top_negative.frame +1 -0
  158. data/screen_test/settings/{position → content_start}/grid_top_negative.rb +2 -2
  159. data/screen_test/settings/flow/box_b2t.frame +1 -1
  160. data/screen_test/settings/flow/box_b2t.rb +1 -1
  161. data/screen_test/settings/flow/box_l2r.frame +1 -1
  162. data/screen_test/settings/flow/box_l2r.rb +1 -1
  163. data/screen_test/settings/flow/box_r2l.frame +1 -1
  164. data/screen_test/settings/flow/box_r2l.rb +1 -1
  165. data/screen_test/settings/flow/box_t2b.frame +1 -1
  166. data/screen_test/settings/flow/box_t2b.rb +1 -1
  167. data/screen_test/settings/flow/grid_b2t.frame +1 -1
  168. data/screen_test/settings/flow/grid_b2t.rb +1 -1
  169. data/screen_test/settings/flow/grid_l2r.frame +1 -1
  170. data/screen_test/settings/flow/grid_l2r.rb +1 -1
  171. data/screen_test/settings/flow/grid_r2l.frame +1 -1
  172. data/screen_test/settings/flow/grid_r2l.rb +1 -1
  173. data/screen_test/settings/flow/grid_t2b.frame +1 -1
  174. data/screen_test/settings/flow/grid_t2b.rb +1 -1
  175. data/screen_test/settings/height/box.frame +1 -1
  176. data/screen_test/settings/height/box.rb +1 -1
  177. data/screen_test/settings/height/box_border_sizing.frame +1 -1
  178. data/screen_test/settings/height/box_border_sizing.rb +1 -1
  179. data/screen_test/settings/height/grid.frame +1 -1
  180. data/screen_test/settings/height/grid.rb +1 -1
  181. data/screen_test/settings/height/overflow_box.frame +1 -1
  182. data/screen_test/settings/height/overflow_box.rb +1 -1
  183. data/screen_test/settings/height/overflow_box_l2r.frame +1 -1
  184. data/screen_test/settings/height/overflow_box_l2r.rb +1 -1
  185. data/screen_test/settings/height/overflow_box_t2b.frame +1 -1
  186. data/screen_test/settings/height/overflow_box_t2b.rb +1 -1
  187. data/screen_test/settings/height/overflow_grid.frame +1 -1
  188. data/screen_test/settings/height/overflow_grid.rb +1 -1
  189. data/screen_test/settings/margin.frame +1 -1
  190. data/screen_test/settings/margin.rb +1 -1
  191. data/screen_test/settings/padding.frame +1 -1
  192. data/screen_test/settings/padding.rb +1 -1
  193. data/screen_test/settings/scroll/horiz_box.frame +1 -1
  194. data/screen_test/settings/scroll/horiz_box.rb +3 -5
  195. data/screen_test/settings/scroll/horiz_box_align_center.rb +3 -5
  196. data/screen_test/settings/scroll/horiz_box_align_right.rb +3 -5
  197. data/screen_test/settings/scroll/vert_box.frame +1 -1
  198. data/screen_test/settings/scroll/vert_box.rb +6 -8
  199. data/screen_test/settings/title_font.frame +1 -1
  200. data/screen_test/settings/title_font.rb +1 -1
  201. data/screen_test/settings/valign/box_around.frame +1 -1
  202. data/screen_test/settings/valign/box_around.rb +1 -1
  203. data/screen_test/settings/valign/box_between.frame +1 -1
  204. data/screen_test/settings/valign/box_between.rb +1 -1
  205. data/screen_test/settings/valign/box_bottom.frame +1 -1
  206. data/screen_test/settings/valign/box_bottom.rb +1 -1
  207. data/screen_test/settings/valign/box_default.frame +1 -1
  208. data/screen_test/settings/valign/box_default.rb +1 -1
  209. data/screen_test/settings/valign/box_evenly.frame +1 -1
  210. data/screen_test/settings/valign/box_evenly.rb +1 -1
  211. data/screen_test/settings/valign/box_middle.frame +1 -1
  212. data/screen_test/settings/valign/box_middle.rb +1 -1
  213. data/screen_test/settings/valign/box_top.frame +1 -1
  214. data/screen_test/settings/valign/box_top.rb +1 -1
  215. data/screen_test/settings/valign/grid_bottom.frame +1 -1
  216. data/screen_test/settings/valign/grid_bottom.rb +1 -1
  217. data/screen_test/settings/valign/grid_default.frame +1 -1
  218. data/screen_test/settings/valign/grid_default.rb +1 -1
  219. data/screen_test/settings/valign/grid_middle.frame +1 -1
  220. data/screen_test/settings/valign/grid_middle.rb +1 -1
  221. data/screen_test/settings/valign/grid_top.frame +1 -1
  222. data/screen_test/settings/valign/grid_top.rb +1 -1
  223. data/screen_test/settings/width/box_border_sizing.frame +1 -1
  224. data/screen_test/settings/width/box_border_sizing.rb +1 -1
  225. data/screen_test/settings/width/box_content.frame +1 -1
  226. data/screen_test/settings/width/box_content.rb +1 -1
  227. data/screen_test/settings/width/box_default.frame +1 -1
  228. data/screen_test/settings/width/box_default.rb +1 -1
  229. data/screen_test/settings/width/grid.frame +1 -1
  230. data/screen_test/settings/width/grid.rb +1 -1
  231. data/screen_test/settings/width/overflow_align_center.frame +1 -1
  232. data/screen_test/settings/width/overflow_align_center.rb +1 -1
  233. data/screen_test/settings/width/overflow_align_right.frame +1 -1
  234. data/screen_test/settings/width/overflow_align_right.rb +1 -1
  235. data/screen_test/settings/width/overflow_box.frame +1 -1
  236. data/screen_test/settings/width/overflow_box.rb +1 -1
  237. data/screen_test/settings/width/overflow_box_l2r.frame +1 -1
  238. data/screen_test/settings/width/overflow_box_l2r.rb +1 -1
  239. data/screen_test/settings/width/overflow_box_t2b.frame +1 -1
  240. data/screen_test/settings/width/overflow_box_t2b.rb +1 -1
  241. data/screen_test/settings/width/overflow_grid.frame +1 -1
  242. data/screen_test/settings/width/overflow_grid.rb +1 -1
  243. data/tools/whirled_peas/tools/screen_tester.rb +21 -3
  244. metadata +67 -22
  245. data/bin/easing +0 -33
  246. data/lib/whirled_peas/graphics/mock_screen.rb +0 -26
  247. data/screen_test/elements/graph.frame +0 -1
  248. data/screen_test/settings/position/box_left.frame +0 -1
  249. data/screen_test/settings/position/box_left_negative.frame +0 -1
  250. data/screen_test/settings/position/box_top.frame +0 -1
  251. data/screen_test/settings/position/box_top_negative.frame +0 -1
  252. data/screen_test/settings/position/grid_left.frame +0 -1
  253. data/screen_test/settings/position/grid_left_negative.frame +0 -1
  254. data/screen_test/settings/position/grid_top.frame +0 -1
  255. data/screen_test/settings/position/grid_top_negative.frame +0 -1
@@ -8,9 +8,9 @@ module WhirledPeas
8
8
  class ConfigCommand < Base
9
9
  def start
10
10
  require config_file
11
- rescue => e
12
- puts "Error loading #{config_file}"
13
- puts e.message
11
+ rescue LoadError => e
12
+ puts e
13
+ puts e.backtrace.join("\n")
14
14
  exit(1)
15
15
  end
16
16
 
@@ -13,9 +13,7 @@ module WhirledPeas
13
13
  require 'whirled_peas/animator/debug_consumer'
14
14
  require 'whirled_peas/animator/producer'
15
15
 
16
- Animator::Producer.produce(
17
- Animator::DebugConsumer.new, WhirledPeas.config.refresh_rate
18
- ) do |producer|
16
+ Animator::Producer.produce(Animator::DebugConsumer.new) do |producer|
19
17
  config.application.start(producer)
20
18
  end
21
19
  end
@@ -28,16 +28,18 @@ module WhirledPeas
28
28
  Utils::Ansi.with_screen do |width, height|
29
29
  consumer = Animator::RendererConsumer.new(
30
30
  WhirledPeas.config.template_factory,
31
- Device::Screen.new(WhirledPeas.config.refresh_rate),
31
+ Device::Screen.new,
32
32
  width,
33
33
  height
34
34
  )
35
- Animator::Producer.produce(
36
- consumer, WhirledPeas.config.refresh_rate
37
- ) do |producer|
35
+ Animator::Producer.produce(consumer) do |producer|
38
36
  config.application.start(producer)
39
37
  end
40
38
  end
39
+ rescue LoadError => e
40
+ puts e
41
+ puts e.backtrace.join("\n")
42
+ exit(1)
41
43
  end
42
44
 
43
45
  private
@@ -56,9 +58,9 @@ module WhirledPeas
56
58
  require 'whirled_peas/utils/file_handler'
57
59
 
58
60
  Utils::Ansi.with_screen do
59
- screen = Device::Screen.new(WhirledPeas.config.refresh_rate)
60
- renders = Utils::FileHandler.read(wpz_file)
61
- screen.handle_renders(renders)
61
+ screen = Device::Screen.new
62
+ rendered_frames = Utils::FileHandler.read(wpz_file)
63
+ screen.handle_rendered_frames(rendered_frames)
62
64
  end
63
65
  end
64
66
 
@@ -21,9 +21,7 @@ module WhirledPeas
21
21
  width,
22
22
  height
23
23
  )
24
- Animator::Producer.produce(
25
- consumer, WhirledPeas.config.refresh_rate
26
- ) do |producer|
24
+ Animator::Producer.produce(consumer) do |producer|
27
25
  config.application.start(producer)
28
26
  end
29
27
  end
@@ -1,3 +1,5 @@
1
+ require 'whirled_peas/device/rendered_frame'
2
+
1
3
  require_relative 'frame_command'
2
4
 
3
5
  module WhirledPeas
@@ -16,12 +18,14 @@ module WhirledPeas
16
18
  require 'whirled_peas/utils/ansi'
17
19
 
18
20
  Utils::Ansi.with_screen do |width, height|
19
- rendered = Graphics::Renderer.new(
21
+ strokes = Graphics::Renderer.new(
20
22
  WhirledPeas.config.template_factory.build(frame, frame_args),
21
23
  width,
22
24
  height
23
25
  ).paint
24
- Device::Screen.new(10000).handle_renders([rendered])
26
+ Device::Screen.new.handle_rendered_frames(
27
+ [Device::RenderedFrame.new(strokes, 0)]
28
+ )
25
29
  end
26
30
  end
27
31
  end
@@ -0,0 +1,78 @@
1
+ require 'whirled_peas/device/screen'
2
+ require 'whirled_peas/graphics/renderer'
3
+ require 'whirled_peas/settings/theme_library'
4
+
5
+ require_relative 'base'
6
+
7
+ module WhirledPeas
8
+ module Command
9
+ # Display a still frame with the specified arguments.
10
+ class Themes < Base
11
+ def self.description
12
+ 'List all themes with sample template rendered in the theme'
13
+ end
14
+
15
+ def start
16
+ super
17
+
18
+ require config_file unless config_file.nil?
19
+
20
+ theme_names = Settings::ThemeLibrary.theme_names
21
+ theme_names.each.with_index do |name, index|
22
+ template = WhirledPeas.template(name) do |composer, settings|
23
+ settings.full_border
24
+ settings.flow = :t2b
25
+ composer.add_text do |_, settings|
26
+ settings.title_font = :theme
27
+ name
28
+ end
29
+ composer.add_text do
30
+ name == Settings::ThemeLibrary.default_name ? ' (default)' : ''
31
+ end
32
+ composer.add_graph do |_, settings|
33
+ settings.height = 12
34
+ 20.times.map { |i| Math.sqrt(i) }
35
+ end
36
+ end
37
+ Utils::Ansi.with_screen do |width, height|
38
+ strokes = Graphics::Renderer.new(
39
+ template,
40
+ 80,
41
+ 30
42
+ ).paint
43
+ Device::Screen.new.handle_rendered_frames(
44
+ [Device::RenderedFrame.new(strokes, 0)]
45
+ )
46
+ end
47
+
48
+ if index < theme_names.length - 1
49
+ print 'See next theme? [Y/q] '
50
+ STDOUT.flush
51
+ break if gets.chomp == 'q'
52
+ end
53
+ end
54
+ rescue LoadError => e
55
+ puts e
56
+ puts e.backtrace.join("\n")
57
+ exit(1)
58
+ end
59
+
60
+ private
61
+
62
+ attr_reader :config_file
63
+
64
+ def validate!
65
+ super
66
+ config_file = args.shift
67
+ unless config_file.nil?
68
+ # We think we have a valid ruby config file, set the absolute path to @config
69
+ @config_file = config_file[0] == '/' ? config_file : File.join(Dir.pwd, config_file)
70
+ end
71
+ end
72
+
73
+ def options_usage
74
+ '[config file]'
75
+ end
76
+ end
77
+ end
78
+ end
@@ -5,6 +5,7 @@ require 'whirled_peas/command/help'
5
5
  require 'whirled_peas/command/play'
6
6
  require 'whirled_peas/command/record'
7
7
  require 'whirled_peas/command/still'
8
+ require 'whirled_peas/command/themes'
8
9
 
9
10
  module WhirledPeas
10
11
  class CommandLine
@@ -15,7 +16,8 @@ module WhirledPeas
15
16
  Command::Help,
16
17
  Command::Play,
17
18
  Command::Record,
18
- Command::Still
19
+ Command::Still,
20
+ Command::Themes
19
21
  ].map.with_object({}) { |c, h| h[c.command_name] = c }
20
22
 
21
23
  def initialize(args)
@@ -0,0 +1,35 @@
1
+ module WhirledPeas
2
+ module Component
3
+ module Factory
4
+ class << self
5
+ def register(name, klass)
6
+ classes[name] = klass
7
+ end
8
+
9
+ def build(name)
10
+ unless classes.key?(name)
11
+ expected = classes.keys.join(', ')
12
+ raise ArgumentError, "Unrecognized component: #{name.inspect}, expecting one of #{expected}"
13
+ end
14
+ @classes[name].new
15
+ end
16
+
17
+ private
18
+
19
+ def classes
20
+ return @classes if @classes
21
+ @classes = {}
22
+ Component.constants.each do |const|
23
+ next if const == name
24
+ klass = Component.const_get(const)
25
+ next unless klass.is_a?(Class)
26
+ name = const.to_s.gsub(/([a-z])([A-Z])/, '\1_\2').downcase.to_sym
27
+ @classes[name] = klass
28
+ end
29
+ @classes
30
+ end
31
+ end
32
+ end
33
+ end
34
+ private_constant :Component
35
+ end
@@ -0,0 +1,89 @@
1
+ module WhirledPeas
2
+ module Component
3
+ class ListWithActive
4
+ attr_accessor :active_index, :separator
5
+
6
+ attr_reader :items
7
+
8
+ attr_writer :flow, :viewport_size
9
+
10
+ def items=(value)
11
+ @items = value.map(&:to_s)
12
+ end
13
+
14
+ def flow
15
+ @flow || :l2r
16
+ end
17
+
18
+ def total_size
19
+ @total_size ||= if flow == :l2r
20
+ size = separator.nil? ? 0 : (items.count - 1) * separator.length
21
+ items.each { |item| size += item.length }
22
+ size
23
+ else
24
+ items.count + (separator.nil? ? 0 : items.count - 1)
25
+ end
26
+ end
27
+
28
+ def viewport_size
29
+ @viewport_size || total_size
30
+ end
31
+
32
+ def compose(composer, settings)
33
+ %i[items].each do |required_attr|
34
+ if send(required_attr).nil?
35
+ raise ArgumentError, "Required field #{required_attr} missing"
36
+ end
37
+ end
38
+
39
+ composer.add_box('ListWithActive') do |composer, settings|
40
+ settings.flow = flow
41
+ settings.align = :left
42
+ settings.full_border
43
+ curr_index = active_index || 0
44
+ if flow == :l2r
45
+ settings.width = viewport_size
46
+ active_start = separator.nil? ? 0 : curr_index * separator.length
47
+ items.first(curr_index).each do |item|
48
+ active_start += item.length
49
+ end
50
+ curr_size = items[curr_index].length
51
+ else
52
+ settings.height = viewport_size
53
+ active_start = curr_index + (separator.nil? ? 0 : curr_index)
54
+ curr_size = 1
55
+ end
56
+
57
+ if viewport_size < total_size
58
+ front_padding = (viewport_size - curr_size) * 0.667
59
+ offset = (active_start - front_padding).round
60
+ if offset < 0
61
+ offset = 0
62
+ elsif offset > total_size - viewport_size
63
+ offset = total_size - viewport_size
64
+ end
65
+
66
+ if flow == :l2r
67
+ settings.content_start.left = -offset
68
+ settings.scrollbar.horiz = true
69
+ else
70
+ settings.content_start.top = -offset
71
+ settings.scrollbar.vert = true
72
+ end
73
+ end
74
+
75
+ items.each.with_index do |item, index|
76
+ composer.add_text { separator } if !separator.nil? && index > 0
77
+ composer.add_text do |_, settings|
78
+ if index == active_index
79
+ settings.bg_color = :highlight
80
+ settings.color = :highlight
81
+ end
82
+ item
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -2,9 +2,6 @@ require 'logger'
2
2
 
3
3
  module WhirledPeas
4
4
  class Config
5
- # Refreshed rate measured in frames per second
6
- DEFAULT_REFRESH_RATE = 30
7
-
8
5
  DEFAULT_LOG_LEVEL = Logger::INFO
9
6
  DEFAULT_LOG_FILE = 'whirled_peas.log'
10
7
 
@@ -35,7 +32,7 @@ module WhirledPeas
35
32
  "[#{severity}] #{datetime.strftime('%Y-%m-%dT%H:%M:%S.%L')} (#{progname}) - #{msg}\n"
36
33
  end
37
34
 
38
- attr_writer :application, :template_factory, :refresh_rate, :log_level, :log_formatter, :log_file
35
+ attr_writer :application, :template_factory, :log_level, :log_formatter, :log_file
39
36
 
40
37
  def application
41
38
  unless @application
@@ -51,10 +48,6 @@ module WhirledPeas
51
48
  @template_factory
52
49
  end
53
50
 
54
- def refresh_rate
55
- @refresh_rate || DEFAULT_REFRESH_RATE
56
- end
57
-
58
51
  def log_level
59
52
  @log_level || DEFAULT_LOG_LEVEL
60
53
  end
@@ -1,7 +1,7 @@
1
1
  module WhirledPeas
2
2
  module Device
3
3
  class NullDevice
4
- def handle_renders(*)
4
+ def handle_rendered_frames(*)
5
5
  end
6
6
  end
7
7
  end
@@ -7,8 +7,8 @@ module WhirledPeas
7
7
  @file = file
8
8
  end
9
9
 
10
- def handle_renders(renders)
11
- Utils::FileHandler.write(file, renders)
10
+ def handle_rendered_frames(rendered_frames)
11
+ Utils::FileHandler.write(file, rendered_frames)
12
12
  end
13
13
 
14
14
  private
@@ -0,0 +1,12 @@
1
+ module WhirledPeas
2
+ module Device
3
+ class RenderedFrame
4
+ attr_reader :strokes, :duration
5
+
6
+ def initialize(strokes, duration)
7
+ @strokes = strokes
8
+ @duration = duration
9
+ end
10
+ end
11
+ end
12
+ end
@@ -3,24 +3,23 @@ require 'highline'
3
3
  module WhirledPeas
4
4
  module Device
5
5
  class Screen
6
- def initialize(refresh_rate, output: STDOUT)
7
- @refresh_rate = refresh_rate
6
+ def initialize(output: STDOUT)
8
7
  @output = output
9
8
  end
10
9
 
11
- def handle_renders(renders)
12
- renders.each do |strokes|
13
- frame_at = Time.now
14
- output.print(strokes)
10
+ def handle_rendered_frames(rendered_frames)
11
+ next_frame_at = Time.now
12
+ rendered_frames.each do |rendered_frame|
13
+ next_frame_at += rendered_frame.duration
14
+ output.print(rendered_frame.strokes)
15
15
  output.flush
16
- next_frame_at = frame_at + 1.0 / refresh_rate
17
16
  sleep([0, next_frame_at - Time.now].max)
18
17
  end
19
18
  end
20
19
 
21
20
  private
22
21
 
23
- attr_reader :refresh_rate, :output
22
+ attr_reader :output
24
23
  end
25
24
  end
26
25
  end
@@ -55,8 +55,8 @@ module WhirledPeas
55
55
  private
56
56
 
57
57
  def paint_horizontally(canvas, canvas_coords, &block)
58
- stroke_left = canvas_coords.content_left
59
- stroke_top = canvas_coords.content_top
58
+ stroke_left = canvas_coords.offset_content_left
59
+ stroke_top = canvas_coords.offset_content_top
60
60
  children_width = 0
61
61
  each_child { |c| children_width += c.dimensions.outer_width }
62
62
  left_offset, spacing_offset = horiz_justify_offset(children_width)
@@ -76,8 +76,8 @@ module WhirledPeas
76
76
  end
77
77
 
78
78
  def paint_vertically(canvas, canvas_coords, &block)
79
- stroke_left = canvas_coords.content_left
80
- stroke_top = canvas_coords.content_top
79
+ stroke_left = canvas_coords.offset_content_left
80
+ stroke_top = canvas_coords.offset_content_top
81
81
  children_height = 0
82
82
  each_child { |c| children_height += c.dimensions.outer_height }
83
83
  top_offset, spacing_offset = vert_justify_offset(children_height)
@@ -2,6 +2,7 @@ require 'whirled_peas/settings/box_settings'
2
2
  require 'whirled_peas/settings/graph_settings'
3
3
  require 'whirled_peas/settings/grid_settings'
4
4
  require 'whirled_peas/settings/text_settings'
5
+ require 'whirled_peas/settings/theme_library'
5
6
 
6
7
  require_relative 'box_painter'
7
8
  require_relative 'graph_painter'
@@ -25,8 +26,10 @@ module WhirledPeas
25
26
  "Element-#{@counter}"
26
27
  end
27
28
 
28
- def self.build
29
- settings = Settings::BoxSettings.new
29
+ def self.build(theme_name=nil, &block)
30
+ theme_name ||= Settings::ThemeLibrary.default_name
31
+ theme = Settings::ThemeLibrary.get(theme_name)
32
+ settings = Settings::BoxSettings.new(theme)
30
33
  template = BoxPainter.new('TEMPLATE', settings)
31
34
  composer = Composer.new(template)
32
35
  value = yield composer, settings
@@ -68,6 +71,10 @@ module WhirledPeas
68
71
  painter.add_child(child)
69
72
  end
70
73
 
74
+ def add_component(component)
75
+ component.compose(self, settings)
76
+ end
77
+
71
78
  def add_box(name=self.class.next_name, &block)
72
79
  child_settings = Settings::BoxSettings.inherit(painter.settings)
73
80
  child = BoxPainter.new(name, child_settings)