whirled_peas 0.2.0 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (166) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +38 -0
  4. data/Gemfile +1 -0
  5. data/README.md +275 -81
  6. data/Rakefile +38 -3
  7. data/examples/intro.rb +52 -0
  8. data/examples/scrolling.rb +53 -0
  9. data/exe/whirled_peas +7 -0
  10. data/lib/whirled_peas.rb +12 -42
  11. data/lib/whirled_peas/command_line.rb +255 -0
  12. data/lib/whirled_peas/config.rb +21 -0
  13. data/lib/whirled_peas/errors.rb +7 -0
  14. data/lib/whirled_peas/frame.rb +0 -8
  15. data/lib/whirled_peas/frame/consumer.rb +14 -50
  16. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  17. data/lib/whirled_peas/frame/event_loop.rb +68 -38
  18. data/lib/whirled_peas/frame/producer.rb +36 -32
  19. data/lib/whirled_peas/graphics.rb +5 -0
  20. data/lib/whirled_peas/graphics/box_painter.rb +108 -0
  21. data/lib/whirled_peas/graphics/canvas.rb +107 -0
  22. data/lib/whirled_peas/graphics/composer.rb +80 -0
  23. data/lib/whirled_peas/graphics/container_coords.rb +71 -0
  24. data/lib/whirled_peas/graphics/container_dimensions.rb +75 -0
  25. data/lib/whirled_peas/graphics/container_painter.rb +234 -0
  26. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  27. data/lib/whirled_peas/graphics/grid_painter.rb +68 -0
  28. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  29. data/lib/whirled_peas/graphics/painter.rb +19 -0
  30. data/lib/whirled_peas/graphics/renderer.rb +21 -0
  31. data/lib/whirled_peas/graphics/screen.rb +70 -0
  32. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  33. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  34. data/lib/whirled_peas/settings.rb +5 -0
  35. data/lib/whirled_peas/settings/bg_color.rb +22 -0
  36. data/lib/whirled_peas/settings/border.rb +101 -0
  37. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  38. data/lib/whirled_peas/settings/color.rb +69 -0
  39. data/lib/whirled_peas/settings/container_settings.rb +139 -0
  40. data/lib/whirled_peas/settings/debugger.rb +96 -0
  41. data/lib/whirled_peas/settings/display_flow.rb +25 -0
  42. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  43. data/lib/whirled_peas/settings/grid_settings.rb +15 -0
  44. data/lib/whirled_peas/settings/margin.rb +8 -0
  45. data/lib/whirled_peas/settings/padding.rb +8 -0
  46. data/lib/whirled_peas/settings/position.rb +15 -0
  47. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  48. data/lib/whirled_peas/settings/spacing.rb +24 -0
  49. data/lib/whirled_peas/settings/text_align.rb +19 -0
  50. data/lib/whirled_peas/settings/text_color.rb +19 -0
  51. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  52. data/lib/whirled_peas/utils.rb +5 -0
  53. data/lib/whirled_peas/utils/ansi.rb +53 -0
  54. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  55. data/lib/whirled_peas/utils/title_font.rb +75 -0
  56. data/lib/whirled_peas/version.rb +1 -1
  57. data/screen_test/rendered/elements/box.frame +1 -0
  58. data/screen_test/rendered/elements/box.rb +20 -0
  59. data/screen_test/rendered/elements/grid.frame +1 -0
  60. data/screen_test/rendered/elements/grid.rb +13 -0
  61. data/screen_test/rendered/elements/screen_overflow.frame +1 -0
  62. data/screen_test/rendered/elements/screen_overflow.rb +9 -0
  63. data/screen_test/rendered/elements/text.frame +1 -0
  64. data/screen_test/rendered/elements/text.rb +9 -0
  65. data/screen_test/rendered/elements/text_multiline.frame +1 -0
  66. data/screen_test/rendered/elements/text_multiline.rb +9 -0
  67. data/screen_test/rendered/settings/align/box.frame +1 -0
  68. data/screen_test/rendered/settings/align/box.rb +24 -0
  69. data/screen_test/rendered/settings/align/children_center.frame +1 -0
  70. data/screen_test/rendered/settings/align/children_center.rb +13 -0
  71. data/screen_test/rendered/settings/align/children_left.frame +1 -0
  72. data/screen_test/rendered/settings/align/children_left.rb +13 -0
  73. data/screen_test/rendered/settings/align/children_right.frame +1 -0
  74. data/screen_test/rendered/settings/align/children_right.rb +13 -0
  75. data/screen_test/rendered/settings/align/grid.frame +1 -0
  76. data/screen_test/rendered/settings/align/grid.rb +20 -0
  77. data/screen_test/rendered/settings/ansi/bold.frame +1 -0
  78. data/screen_test/rendered/settings/ansi/bold.rb +15 -0
  79. data/screen_test/rendered/settings/ansi/color.frame +1 -0
  80. data/screen_test/rendered/settings/ansi/color.rb +37 -0
  81. data/screen_test/rendered/settings/ansi/underline.frame +1 -0
  82. data/screen_test/rendered/settings/ansi/underline.rb +15 -0
  83. data/screen_test/rendered/settings/border.frame +1 -0
  84. data/screen_test/rendered/settings/border.rb +13 -0
  85. data/screen_test/rendered/settings/flow/box_b2t.frame +1 -0
  86. data/screen_test/rendered/settings/flow/box_b2t.rb +24 -0
  87. data/screen_test/rendered/settings/flow/box_l2r.frame +1 -0
  88. data/screen_test/rendered/settings/flow/box_l2r.rb +24 -0
  89. data/screen_test/rendered/settings/flow/box_r2l.frame +1 -0
  90. data/screen_test/rendered/settings/flow/box_r2l.rb +24 -0
  91. data/screen_test/rendered/settings/flow/box_t2b.frame +1 -0
  92. data/screen_test/rendered/settings/flow/box_t2b.rb +24 -0
  93. data/screen_test/rendered/settings/flow/grid_b2t.frame +1 -0
  94. data/screen_test/rendered/settings/flow/grid_b2t.rb +14 -0
  95. data/screen_test/rendered/settings/flow/grid_l2r.frame +1 -0
  96. data/screen_test/rendered/settings/flow/grid_l2r.rb +14 -0
  97. data/screen_test/rendered/settings/flow/grid_r2l.frame +1 -0
  98. data/screen_test/rendered/settings/flow/grid_r2l.rb +14 -0
  99. data/screen_test/rendered/settings/flow/grid_t2b.frame +1 -0
  100. data/screen_test/rendered/settings/flow/grid_t2b.rb +14 -0
  101. data/screen_test/rendered/settings/height/box.frame +1 -0
  102. data/screen_test/rendered/settings/height/box.rb +13 -0
  103. data/screen_test/rendered/settings/height/grid.frame +1 -0
  104. data/screen_test/rendered/settings/height/grid.rb +14 -0
  105. data/screen_test/rendered/settings/height/overflow_box.frame +1 -0
  106. data/screen_test/rendered/settings/height/overflow_box.rb +13 -0
  107. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +1 -0
  108. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +15 -0
  109. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +1 -0
  110. data/screen_test/rendered/settings/height/overflow_box_t2b.rb +14 -0
  111. data/screen_test/rendered/settings/height/overflow_grid.frame +1 -0
  112. data/screen_test/rendered/settings/height/overflow_grid.rb +16 -0
  113. data/screen_test/rendered/settings/margin.frame +1 -0
  114. data/screen_test/rendered/settings/margin.rb +14 -0
  115. data/screen_test/rendered/settings/padding.frame +1 -0
  116. data/screen_test/rendered/settings/padding.rb +11 -0
  117. data/screen_test/rendered/settings/position/box_left.frame +1 -0
  118. data/screen_test/rendered/settings/position/box_left.rb +17 -0
  119. data/screen_test/rendered/settings/position/box_left_negative.frame +1 -0
  120. data/screen_test/rendered/settings/position/box_left_negative.rb +17 -0
  121. data/screen_test/rendered/settings/position/box_top.frame +1 -0
  122. data/screen_test/rendered/settings/position/box_top.rb +17 -0
  123. data/screen_test/rendered/settings/position/box_top_negative.frame +1 -0
  124. data/screen_test/rendered/settings/position/box_top_negative.rb +17 -0
  125. data/screen_test/rendered/settings/position/grid_left.frame +1 -0
  126. data/screen_test/rendered/settings/position/grid_left.rb +18 -0
  127. data/screen_test/rendered/settings/position/grid_left_negative.frame +1 -0
  128. data/screen_test/rendered/settings/position/grid_left_negative.rb +18 -0
  129. data/screen_test/rendered/settings/position/grid_top.frame +1 -0
  130. data/screen_test/rendered/settings/position/grid_top.rb +18 -0
  131. data/screen_test/rendered/settings/position/grid_top_negative.frame +1 -0
  132. data/screen_test/rendered/settings/position/grid_top_negative.rb +18 -0
  133. data/screen_test/rendered/settings/scroll/horiz_box.frame +1 -0
  134. data/screen_test/rendered/settings/scroll/horiz_box.rb +15 -0
  135. data/screen_test/rendered/settings/scroll/vert_box.frame +1 -0
  136. data/screen_test/rendered/settings/scroll/vert_box.rb +18 -0
  137. data/screen_test/rendered/settings/title_font.frame +1 -0
  138. data/screen_test/rendered/settings/title_font.rb +12 -0
  139. data/screen_test/rendered/settings/width/box.frame +1 -0
  140. data/screen_test/rendered/settings/width/box.rb +13 -0
  141. data/screen_test/rendered/settings/width/grid.frame +1 -0
  142. data/screen_test/rendered/settings/width/grid.rb +14 -0
  143. data/screen_test/rendered/settings/width/overflow_box.frame +1 -0
  144. data/screen_test/rendered/settings/width/overflow_box.rb +11 -0
  145. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +1 -0
  146. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +14 -0
  147. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +1 -0
  148. data/screen_test/rendered/settings/width/overflow_box_t2b.rb +15 -0
  149. data/screen_test/rendered/settings/width/overflow_grid.frame +1 -0
  150. data/screen_test/rendered/settings/width/overflow_grid.rb +14 -0
  151. data/screen_test/screen_tester.rb +201 -0
  152. data/whirled_peas.gemspec +4 -2
  153. metadata +147 -20
  154. data/lib/whirled_peas/ui.rb +0 -7
  155. data/lib/whirled_peas/ui/ansi.rb +0 -154
  156. data/lib/whirled_peas/ui/canvas.rb +0 -35
  157. data/lib/whirled_peas/ui/element.rb +0 -225
  158. data/lib/whirled_peas/ui/painter.rb +0 -283
  159. data/lib/whirled_peas/ui/screen.rb +0 -62
  160. data/lib/whirled_peas/ui/settings.rb +0 -521
  161. data/lib/whirled_peas/ui/stroke.rb +0 -29
  162. data/sandbox/auto.rb +0 -13
  163. data/sandbox/box.rb +0 -19
  164. data/sandbox/grid.rb +0 -13
  165. data/sandbox/sandbox.rb +0 -17
  166. data/sandbox/text.rb +0 -33
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: caed061285ef515e70f20c18153a16eedb3876a98842f2029db069c4a14dcd7f
4
- data.tar.gz: c07b7daeaa8c048d0068c1150ad248099c335a2a9e8fd70bd47ad8f9bfc99174
3
+ metadata.gz: 4a2a839ef0ed84dd94f346be47c38d65c0d23b6faa399abdf8f56023138788a4
4
+ data.tar.gz: 04a43d7c60d2e3896adea98036012bdba20448ecdf530947b72ca535ee5c3464
5
5
  SHA512:
6
- metadata.gz: f4c3c1003793b47a99413aed446f0935da5a866819336c6b58e02f5b90ae1d69eeefae6b8ea92a8460fda3fcd89843faeeadf9824a4ba5502dd7ca5539a390d5
7
- data.tar.gz: dccb23acdc4a580e3b319e17f8d0deeac981be86482c9426b5306c40dbed0691b0308bb5300a764830bb3832700b34db0f99beb98796992521294f295623bcda
6
+ metadata.gz: dfff87fa6a87ed784b35b400ad9ac737508b4ded1e791840a3a3194a0ef22eae1ad517c9f8db9459b929e37ce132cfa97662f85762b0b02df691f58ececa7d19
7
+ data.tar.gz: 504e279d386d56d3b476220c4f8a015134c446b7a0cdcebc5c65db50ee750f48ce2b8b5e6a72ce0d3844bdca916f832230b7974dfdd5d982140b8bc15db003c6
@@ -3,4 +3,7 @@ language: ruby
3
3
  cache: bundler
4
4
  rvm:
5
5
  - 2.6.6
6
+ - 2.7.2
7
+ - 3.0.0
6
8
  before_install: gem install bundler -v 2.1.4
9
+ script: bundle exec rake ci
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## v0.5.0 - 2021-01-25
4
+
5
+ - [73df2af](https://github.com/tcollier/whirled_peas/tree/73df2af1f1eac37fe94f720dce16da1ed568dade): Add support for scroll bars along both axes
6
+ - [f4f44e1](https://github.com/tcollier/whirled_peas/tree/f4f44e1ff6c75ed03cd682e3fa9921401dcd2d00): Implement remaining 3 flow directions for grids
7
+ - [c01d083](https://github.com/tcollier/whirled_peas/tree/c01d083e10bf70983109a8def836bb181099f59c): Implement reverse flow directions for grids
8
+
9
+ ## v0.5.0 - 2021-01-25
10
+
11
+ BREAKING: there was a significant amount of structural refactoring in this release. While that aspect did not add or remove any features, it fixed several layout bugs (some known and some unknown), so most template with moderate complexity will now be laid out slightly differently.
12
+
13
+ - [3ddfede](https://github.com/tcollier/whirled_peas/tree/3ddfedee4ab2fadeecbe82c7c9caf25c2988f095): Allow relative positioning of containers
14
+ - [507e77c](https://github.com/tcollier/whirled_peas/tree/507e77c551bcb9cc832d5c24e2f24eb1afe4eddc): Add height attribute for container settings
15
+ - [af8ef19](https://github.com/tcollier/whirled_peas/tree/af8ef1950edebcd23a57a04982b22a56296ee09b): Add automated screen testing
16
+
17
+ ## v0.4.1 - 2021-01-22
18
+
19
+ - [0f7aa6c](https://github.com/tcollier/whirled_peas/tree/0f7aa6ccc07323230dd602cb43e0341de5a69ad8): Allow relative path for config files
20
+
21
+ ## v0.4.0 - 2021-01-22
22
+
23
+ - [7fd6712](https://github.com/tcollier/whirled_peas/tree/7fd6712818c94cdbfd81828277ca67c705e01793): BREAKING: replace `WhirledPeas.start` with command line executable
24
+ - [2535342](https://github.com/tcollier/whirled_peas/tree/25353424f1ab4af4880f44eb7ddd28afefbbb9b2): Add support for loading screen
25
+ - [7388fc2](https://github.com/tcollier/whirled_peas/tree/7388fc2eacdc8045b725311c11d650d6b8654be8): Add support for title fonts
26
+ - [b345155](https://github.com/tcollier/whirled_peas/tree/b345155b1c212cabe73f9a2562ac8dbbedbbb6df): Add command to list title fonts to executable
27
+ - [d3a8324](https://github.com/tcollier/whirled_peas/tree/d3a832496c36985993217ff11b6d83dd4697c4ed): Add commands for debugging application to executable
28
+
29
+ ## v0.3.0 - 2021-01-21
30
+
31
+ - [617f802](https://github.com/tcollier/whirled_peas/tree/617f8027d6688a2ec81a3e594e529c94485cee85): BREAKING: send frames directly to EventLoop (`Producer#send` renamed to `Producer#send_frame`)
32
+
33
+ ## v0.2.0 - 2021-01-20
34
+
35
+ - [73eb326](https://github.com/tcollier/whirled_peas/tree/73eb326426f9814e91e3bc7a60dfd87be3d69f7e): Convert "primitive" data types to strings
36
+ - [f28b69d](https://github.com/tcollier/whirled_peas/tree/f28b69df8b6cfc973da2ebc0b8da29b278f23433): Give elements names
37
+ - [1ae1c929](https://github.com/tcollier/whirled_peas/tree/1ae1c929429c2f8520054d33a064c2b6d71955fe): Consistently format exceptions in logs
38
+ - [4c2114f](https://github.com/tcollier/whirled_peas/tree/4c2114fd360fd98c65e6e32f905a377f09b919ee): Fix bug with negative sleep times
39
+ - [627bf12](https://github.com/tcollier/whirled_peas/tree/627bf126dd7f9c845f65105e0826d14a35a0a953): Don't reraise pipe error
40
+
3
41
  ## v0.1.1 - 2021-01-20
4
42
 
5
43
  - [3852c8c](https://github.com/tcollier/whirled_peas/tree/3852c8c700c2e8fb92e65bbca1c99be74304c6d0): Improve error handling
data/Gemfile CHANGED
@@ -6,3 +6,4 @@ gemspec
6
6
  gem 'rake', '~> 12.0'
7
7
  gem 'rspec', '~> 3.0'
8
8
  gem 'pry-byebug'
9
+ gem 'tty-cursor', '~> 0.7'
data/README.md CHANGED
@@ -1,6 +1,24 @@
1
- # WhirledPeas
1
+ [![Build Status](https://travis-ci.com/tcollier/whirled_peas.svg?branch=main)](https://travis-ci.com/tcollier/whirled_peas)
2
2
 
3
- Visualize your code's execution with Whirled Peas!
3
+ ```
4
+ ██╗ ██╗██╗███████╗██╗ ██╗ █████╗ ██╗ ██╗███████╗███████╗
5
+ ██║ ██║██║██╔════╝██║ ██║██╔══██╗██║ ██║╚══███╔╝██╔════╝
6
+ ██║ ██║██║███████╗██║ ██║███████║██║ ██║ ███╔╝ █████╗
7
+ ╚██╗ ██╔╝██║╚════██║██║ ██║██╔══██║██║ ██║ ███╔╝ ██╔══╝
8
+ ╚████╔╝ ██║███████║╚██████╔╝██║ ██║███████╗██║███████╗███████╗
9
+ ╚═══╝ ╚═╝╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝╚═╝╚══════╝╚══════╝
10
+
11
+ your code's execution with
12
+
13
+ ██╗ ██╗██╗ ██╗██╗██████╗ ██╗ ███████╗██████╗ ██████╗ ███████╗ █████╗ ███████╗
14
+ ██║ ██║██║ ██║██║██╔══██╗██║ ██╔════╝██╔══██╗ ██╔══██╗██╔════╝██╔══██╗██╔════╝
15
+ ██║ █╗ ██║███████║██║██████╔╝██║ █████╗ ██║ ██║ ██████╔╝█████╗ ███████║███████╗
16
+ ██║███╗██║██╔══██║██║██╔══██╗██║ ██╔══╝ ██║ ██║ ██╔═══╝ ██╔══╝ ██╔══██║╚════██║
17
+ ╚███╔███╔╝██║ ██║██║██║ ██║███████╗███████╗██████╔╝ ██║ ███████╗██║ ██║███████║
18
+ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝╚═╝ ╚═╝╚══════╝╚══════╝╚═════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚══════╝
19
+ ```
20
+
21
+ Easily create terminal-based graphics to visual the execution of your code. Whirled Peas offers templating inspired by HTML and CSS and provides a lightweight tie-in for your code to produce visual animations with these templates.
4
22
 
5
23
  ## Installation
6
24
 
@@ -20,15 +38,24 @@ Or install it yourself as:
20
38
 
21
39
  ## Usage
22
40
 
41
+ A Whirled Peas application consists of the following pieces
42
+
43
+ - The driver (required) - the code that is to be visualized, it emits lightweight frame events through a producer
44
+ - The main template factory (required) - builds templates to convert frame events from the driver into terminal graphics
45
+ - A loading screen template factory (optional) - builds templates to display while content is loading
46
+
47
+ These pieces are configured as following
48
+
23
49
  ```ruby
50
+ # visualize.rb
24
51
  require 'whirled_peas'
25
52
 
26
53
  class TemplateFactory
27
54
  def build(frame, args)
28
- WhirledPeas.template do |body|
29
- body.add_box do |_, settings|
55
+ WhirledPeas.template do |composer|
56
+ composer.add_box('Title') do |_, settings|
30
57
  settings.underline = true
31
- "Hello #{args['name']}"
58
+ "Hello #{args[:name]}"
32
59
  end
33
60
  # ...
34
61
  end
@@ -37,18 +64,44 @@ end
37
64
 
38
65
  class Driver
39
66
  def start(producer)
40
- producer.send('starting', args: { 'name' => 'World' })
67
+ producer.send_frame('starting', args: { name: 'World' })
41
68
  # ...
42
69
  end
43
70
  end
44
71
 
45
- WhirledPeas.start(Driver.new, TemplateFactory.new)
72
+ WhirledPeas.configure do |config|
73
+ config.driver = Driver.new
74
+ config.template_factory = TemplateFactory.new
75
+ end
76
+ ```
77
+
78
+ Then the visualizer is started on the command line with
79
+
80
+ ```
81
+ $ whirled_peas start visualize.rb
46
82
  ```
47
83
 
48
- A Whirled Peas application consists of two pieces
84
+ The optional loading screen can be configured like
49
85
 
50
- 1. The driver, which emits lightweight frame events
51
- 1. The template factory, which builds templates to convert frame events from the driver into terminal graphics
86
+ ```ruby
87
+ class LoadingTemplateFactory
88
+ def build
89
+ WhirledPeas.template do |composer|
90
+ composer.add_box('Loading') do |_, settings|
91
+ settings.set_margin(top: 15)
92
+ settings.align = :center
93
+ settings.full_border(color: :blue, style: :double)
94
+ "Loading..."
95
+ end
96
+ end
97
+ end
98
+ end
99
+
100
+ WhirledPeas.configure do |config|
101
+ # ...
102
+ config.loading_template_factory = LoadingTemplateFactory.new
103
+ end
104
+ ```
52
105
 
53
106
  ### Driver
54
107
 
@@ -70,13 +123,14 @@ The producer provides a single method
70
123
  #
71
124
  # @param name [String] application defined name for the frame. The template factory will be provided this name
72
125
  # @param duration [Number] time in seconds this frame should be displayed for (defaults to 1 frame)
73
- # @param args [Hash] key value pairs to send as arguments to the template factory, these values will be
74
- # serialized/deserialized
75
- def send(name, duration:, args:)
126
+ # @param args [Hash<Symbol, Object>] key value pairs to send as arguments to the template factory
127
+ def send_frame(name, duration:, args:)
76
128
  # implementation
77
129
  end
78
130
  ```
79
131
 
132
+ **IMPORTANT**: the keys in the `args` hash must be symbols!
133
+
80
134
  #### Example
81
135
 
82
136
  Simple application that loads a set of numbers and looks for a pair that adds up to 1,000
@@ -85,52 +139,56 @@ Simple application that loads a set of numbers and looks for a pair that adds up
85
139
  class Driver
86
140
  def start(producer)
87
141
  numbers = File.readlines('/path/to/numbers.txt').map(&:to_i)
88
- producer.send('load-numbers', duration: 3, args: { numbers: numbers })
142
+ producer.send_frame('load-numbers', duration: 3, args: { numbers: numbers })
89
143
  numbers.sort!
90
- producer.send('sort-numbers', duration: 3, args: { numbers: numbers })
144
+ producer.send_frame('sort-numbers', duration: 3, args: { numbers: numbers })
91
145
  low = 0
92
146
  high = numbers.length - 1
93
147
  while low < high
94
148
  sum = numbers[low] + numbers[high]
95
149
  if sum == 1000
96
- producer.send('found-pair', duration: 5, args: { low: low, high: high, sum: sum })
150
+ producer.send_frame('found-pair', duration: 5, args: { low: low, high: high, sum: sum })
97
151
  return
98
152
  elsif sum < 1000
99
- producer.send('too-low', args: { low: low, high: high, sum: sum })
153
+ producer.send_frame('too-low', args: { low: low, high: high, sum: sum })
100
154
  low += 1
101
155
  else
102
- producer.send('too-high', args: { low: low, high: high, sum: sum })
156
+ producer.send_frame('too-high', args: { low: low, high: high, sum: sum })
103
157
  high -= 1
104
158
  end
105
159
  end
106
- producer.send('no-solution', duration: 5)
160
+ producer.send_frame('no-solution', duration: 5)
107
161
  end
108
162
  end
109
163
  ```
110
164
 
111
165
  ### Template Factory
112
166
 
113
- To render the frame events sent by the driver, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the driver. A template factory can be a simple ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs.
167
+ To render the frame events sent by the driver, the application requires a template factory. This factory will be called for each frame event, with the frame name and the arguments supplied by the driver. A template factory can be an instance of ruby class and thus can maintain state. Whirled Peas provides a few basic building blocks to make simple, yet elegant terminal-based UIs.
168
+
169
+ #### Loading Template Factory
170
+
171
+ `WhirledPeas.configure` takes an optional template factory to build a loading screen. This instance must implement `#build` (taking no arguments). The template returned by that method will be painted while the event loop is waiting for frames. The factory method will be called once per refresh cycle, so it's possible to implement animation.
114
172
 
115
173
  #### Building Blocks
116
174
 
117
- A template is created with `WhirledPeas.template`, which yields a `Template` object and `TemplateSettings`. This template object is a `ComposableElement`, which allows for attaching child elements and setting layout options. `GridElement` and `BoxElement` are two other composable elements and `TextElement` is a simple element that can hold a text/number value and has layout options, but cannot have any child elements.
175
+ A template is created with `WhirledPeas.template`, which yields a `Composer` object for a `Box` and `BoxSettings`. The composer allows for attaching child elements and the settings for setting layout options.
118
176
 
119
- A `ComposableElement` provides the following methods to add child elements
177
+ A `Composer` provides the following methods to add child elements, each of these takes an optional string argument that is set as the name of the element (which can be useful when debugging).
120
178
 
121
- - `add_box` - yields a `ComposableElement` and a `BoxSettings`, which will be added to the parent's children
122
- - `add_grid` - yields a `ComposableElement` and a `GridSettings`, which will be added to the parent's children
179
+ - `add_box` - yields a `Composer` for a `Box` and a `BoxSettings`, which will be added to the parent's children
180
+ - `add_grid` - yields a `Composer` for a `Grid` and a `GridSettings`, which will be added to the parent's children
123
181
  - `add_text` - yields `nil` and a `TextSettings`, which will be added to the parent's children
124
182
 
125
183
  E.g.
126
184
 
127
185
  ```ruby
128
- WhirledPeas.template do |template, template_settings|
129
- template_settings.bg_color = :blue
130
- template.add_grid do |grid, grid_settings|
131
- grid_settings.num_cols = 10
186
+ WhirledPeas.template do |composer, settings|
187
+ settings.bg_color = :blue
188
+ composer.add_grid do |composer, settings|
189
+ settings.num_cols = 10
132
190
  100.times do |i|
133
- grid.add_text { i }
191
+ composer.add_text { i }
134
192
  end
135
193
  end
136
194
  end
@@ -139,79 +197,100 @@ end
139
197
  The above template can also be broken down into more manageable methods, e.g.
140
198
 
141
199
  ```ruby
142
- def number_grid(grid, settings)
200
+ def number_grid(_composer, settings)
143
201
  settings.num_cols = 10
144
- 100.times do |i|
145
- grid.add_text { i }
146
- end
202
+ 100.times.map(&:itself)
147
203
  end
148
204
 
149
- WhirledPeas.template do |template, settings|
205
+ WhirledPeas.template do |composer, settings|
150
206
  settings.bg_color = :blue
151
- template.add_grid(&method(:number_grid))
207
+ composer.add_grid(&method(:number_grid))
152
208
  end
153
209
  ```
154
210
 
155
- Additionally, if no child element is explicitly added to a `GridElement`, but the block returns an array of strings or numbers, those will be converted to `TextElements` and added as children to the `GridElement`. For example, these are identical ways to create a grid of strings
211
+ Additionally, if no child element is explicitly added to a `Grid`, but the block returns an array of strings or numbers, those will be converted to `Text` elements and added as children to the `Grid`. For example, these are identical ways to create a grid of strings
156
212
 
157
213
  ```ruby
158
- template.add_grid do |g|
214
+ template.add_grid do |composer|
159
215
  100.times do |i|
160
- g.add_text { i }
216
+ composer.add_text { i }
161
217
  end
162
218
  end
163
219
 
164
- template.add_grid do |g|
220
+ template.add_grid do
165
221
  100.times.map(&:itself)
166
222
  end
167
223
  ```
168
224
 
169
- Similarly, if no child element is explicilty added to a `BoxElement`, but the block returns a string or number, that value will be converted to a `TextElement` and added as a child. For example, these are identical ways to create a box with string content
225
+ Similarly, if no child element is explicilty added to a `Box`, but the block returns a string or number, that value will be converted to a `Text` and added as a child. For example, these are identical ways to create a box with string content
170
226
 
171
227
  ```ruby
172
- template.add_box do |b|
173
- b.add_text { "Hello!" }
228
+ template.add_box do |composer|
229
+ composer.add_text { "Hello!" }
174
230
  end
175
231
 
176
- template.add_box do |b|
232
+ template.add_box do
177
233
  "Hello!"
178
234
  end
179
235
  ```
180
236
 
181
237
  #### Settings
182
238
 
183
- Each element type has an associated settings type, which provide a custom set of options to format the output. Parent settings may be merged into child settings (assuming the child supports those settings)
239
+ Each element type has an associated settings type, which provide a custom set of options to format the output. Child settings will inherit from the parent, where applicable
184
240
  The available settigs are
185
241
 
186
- | Setting | Description | Default | Availability | Merged? |
187
- | ------------- | ------------------------------------------------------------------ | ------- | --------------------------------- | ------- |
188
- | `align` | Justifies the text (allowed values: `:left`, `:center`, `:right`) | `:left` | `Box`, `Grid`, `Text` | Yes |
189
- | `auto_margin` | Evenly distribute side margin (overrides left/right in `margin`) | `false` | `Box`, `Grid` | Yes |
190
- | `bg_color` | Background color (see [Colors](#colors)) | | `Box`, `Grid`, `Template`, `Text` | Yes |
191
- | `bold` | `true` makes the font bold | `false` | `Box`, `Grid`, `Template`, `Text` | Yes |
192
- | `border` | Set the border for the lements | none | `Box`, `Grid`, | Yes |
193
- | `color` | Foreground text color (see [Colors](#colors)) | | `Box`, `Grid`, `Template`, `Text` | Yes |
194
- | `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box` | Yes |
195
- | `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | Yes |
196
- | `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | Yes |
197
- | `transpose` | Display grid elements top-to-bottom, then left-to-right | `false` | `Grid` | No |
198
- | `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Template`, `Text` | Yes |
199
- | `width` | Override the calculated with of an element | | `Box`, `Grid`, `Text` | No |
200
-
201
- ##### Margin and Padding
202
-
203
- Margin and padding settings allow for setting the spacing on each of the 4 sides of the element independently. The set these values, use
242
+ | Setting | Description | Default | Availability | Inherited |
243
+ | ------------ | ------------------------------------------------------------------------------- | ------- | --------------------- | -------------------- |
244
+ | `align` | Justifies the content (allowed values: `:left`, `:center`, `:right`) | `:left` | `Box`, `Grid` | Yes |
245
+ | `bg_color` | Background color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes |
246
+ | `bold` | `true` makes the font bold | `false` | `Box`, `Grid`, `Text` | Yes |
247
+ | `border` | Set the border for the lements | none | `Box`, `Grid`, | Only style and color |
248
+ | `color` | Foreground text color (see [Colors](#colors)) | | `Box`, `Grid`, `Text` | Yes |
249
+ | `flow` | Flow to display child elements (see [Display Flow](#display-flow)) | `:l2r` | `Box`, `Grid` | Yes |
250
+ | `height` | Override the calculated height of an element's content area | | `Box`, `Grid` | No |
251
+ | `margin` | Set the (left, top, right, bottom) margin of the element | `0` | `Box`, `Grid` | No |
252
+ | `num_cols` | Number of columns in the grid (must be set!) | | `Grid` | No |
253
+ | `padding` | Set the (left, top, right, bottom) padding of the element | `0` | `Box`, `Grid` | No |
254
+ | `position` | Set the (left, top) position of the element relative to parent content area | `0` | `Box`, `Grid` | No |
255
+ | `scrollbar` | Display a scroll bar for vertical or horizontal scrolling | | `Box` | No |
256
+ | `title_font` | Font used for "large" text (see [Large Text](#large-text), ignores `underline`) | | `Text` | No |
257
+ | `underline` | `true` underlines the font | `false` | `Box`, `Grid`, `Text` | Yes |
258
+ | `width` | Override the calculated width of an element's content area | | `Box`, `Grid` | No |
259
+
260
+ ##### Position
261
+
262
+ Position settings dictate the relative position from where the painter would have preferred to place the container. Negative numbers move the container left/up and positive numbers move it right/down. To set these values, use
263
+
264
+ - `set_position(left:, top:)`
265
+
266
+ ##### Margin
267
+
268
+ Margin settings dictate the spacing on the outside (i.e. outside of the border) of each of the 4 sides of the container independently. To set these values, use
204
269
 
205
270
  - `set_margin(left:, top:, right:, bottom:)`
271
+
272
+ Note: values cannot be negative
273
+
274
+ ##### Padding
275
+
276
+ Padding settings dictate the spacing on the inside (i.e. inside of the border) of each of the 4 sides of the container independently. To set these values, use
277
+
206
278
  - `set_padding(left:, top:, right:, bottom:)`
207
279
 
208
- Any argument value not provided will result in that value being 0.
280
+ Note: values cannot be negative
281
+
282
+ ##### Scrollbar
283
+
284
+ Scroll settings dictate whether the scrollbar will be shown when child content is larger the the container's viewport. A scrollbar adds a unit to the dimensions of a container (as opposed to overwriting the leftmost/bottommost padding)
285
+
286
+ - `set_padding(horiz:, vert:)`
209
287
 
210
288
  ##### Border
211
289
 
212
290
  The border settings consist of 6 boolean values (border are either width 1 or not shown), the 4 obvious values (`left`, `top`, `right`, and `bottom`) along with 2 other values for inner borders (`inner_horiz` and `inner_vert`) in a grid. A border also has a foreground color (defaults to `:white`) and a style. The background color is determined by the `bg_color` of the element. Border values can be set with
213
291
 
214
292
  - `set_border(left:, top:, right:, bottom:, inner_horiz:, inner_vert:, color:, style:)`
293
+ - `full_border(style:, color:)`
215
294
 
216
295
  Available border styles are
217
296
 
@@ -252,31 +331,53 @@ Child elements can flow in one of 4 directions
252
331
  - `:l2r` left-to-right
253
332
 
254
333
  ```
334
+ # In a Box
255
335
  [child 1] [child 2] ... [child N]
336
+
337
+ # In a Grid
338
+ [child 1] [child 2] [child 3]
339
+ [chiid 4] [child 5]
256
340
  ```
257
341
 
258
342
  - `:r2l` right-to-left
259
343
 
260
344
  ```
345
+ # In a Box
261
346
  [child N] [child N - 1] ... [child 1]
347
+
348
+ # In a Grid
349
+ [child 3] [child 2] [child 1]
350
+ [chiid 5] [child 4]
262
351
  ```
263
352
 
264
353
  - `:t2b` top-to-bottom
265
354
 
266
355
  ```
356
+ # In a Box
267
357
  [child 1]
268
358
  [child 2]
269
359
  ...
270
360
  [child N]
361
+
362
+ # In a Grid
363
+ [child 1] [child 4]
364
+ [child 2] [child 5]
365
+ [child 3]
271
366
  ```
272
367
 
273
368
  - `:b2t` bottom-to-top
274
369
 
275
370
  ```
371
+ # In a Box
276
372
  [child N]
277
373
  [child N - 1]
278
374
  ...
279
375
  [child 1]
376
+
377
+ # In a Grid
378
+ [child 3]
379
+ [child 2] [child 5]
380
+ [child 1] [child 4]
280
381
  ```
281
382
 
282
383
  ##### Colors
@@ -302,14 +403,40 @@ Many of these also have a "bright" option:
302
403
  - `:bright_red`
303
404
  - `:bright_yellow`
304
405
 
406
+ ##### Large Text
407
+
408
+ The `title_font` setting for `Text`s converts the standard terminal font into a large block font. The available fonts vary from system to system. Every system will have a `:default` font available, this font could look like
409
+
410
+ ```
411
+ ██████╗ ███████╗███████╗ █████╗ ██╗ ██╗██╗ ████████╗
412
+ ██╔══██╗██╔════╝██╔════╝██╔══██╗██║ ██║██║ ╚══██╔══╝
413
+ ██║ ██║█████╗ █████╗ ███████║██║ ██║██║ ██║
414
+ ██║ ██║██╔══╝ ██╔══╝ ██╔══██║██║ ██║██║ ██║
415
+ ██████╔╝███████╗██║ ██║ ██║╚██████╔╝███████╗ ██║
416
+ ╚═════╝ ╚══════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝
417
+ ```
418
+
419
+ To print out a list of all available fonts as well as sample text in that font, run
420
+
421
+ ```
422
+ $ whirled_peas title_fonts
423
+ ```
424
+
425
+ Note: when using a title font with WhirledPeas for the first time on a system, the gem loads all fonts to check which ones are available. This can be a slow process and may cause a noticeable delay when running a visualization. Running the command above will cache the results and thus when a WhirledPeas visualization is run, there will be no lag from loading fonts.
426
+
305
427
  ### Example
306
428
 
307
429
  ```ruby
308
430
  class TemplateFactory
309
431
  def build(frame, args)
310
432
  set_state(frame, args)
311
- WhirledPeas.template do |t|
312
- t.add_box(&method(:body))
433
+ WhirledPeas.template do |composer, settings|
434
+ settings.flow = :l2r
435
+ settings.align = :center
436
+
437
+ composer.add_box('Title', &method(:title))
438
+ composer.add_box('Sum', &method(:sum))
439
+ composer.add_grid('NumberGrid', &method(:number_grid))
313
440
  end
314
441
  end
315
442
 
@@ -317,41 +444,92 @@ class TemplateFactory
317
444
 
318
445
  def set_state(frame, args)
319
446
  @frame = frame
320
- @numbers = args.key?('numbers') ? args['numbers'] || []
321
- @sum = args['sum'] if args.key?('sum')
322
- @low = args['low'] if args.key?('low')
323
- @high = args['high'] if args.key?('high')
447
+ @numbers = args.key?(:numbers) ? args[:numbers] || []
448
+ @sum = args[:sum] if args.key?(:sum)
449
+ @low = args[:low] if args.key?(:low)
450
+ @high = args[:high] if args.key?(:high)
324
451
  end
325
452
 
326
- def title(_elem, settings)
453
+ def title(_composer, settings)
327
454
  settings.underline = true
328
455
  "Pair Finder"
329
456
  end
330
457
 
331
- def sum(_elem, settings)
458
+ def sum(_composer, settings)
332
459
  settings.color = @frame == 'found-pair' ? :green : :red
333
460
  @sum ? "Sum: #{@sum}" : 'N/A'
334
461
  end
335
462
 
336
- def number_grid(elem, settings)
463
+ def number_grid(composer, settings)
337
464
  settings.full_border
338
465
  @numbers.each.with_index do |num, index|
339
- g.add_text do |_, settings|
466
+ composer.add_text do |_, settings|
340
467
  settings.bg_color = (@low == index || @high == index) ? :cyan : :white
341
468
  num
342
469
  end
343
470
  end
344
471
  end
472
+ end
473
+ ```
345
474
 
346
- def body(elem, settings)
347
- settings.flow = :l2r
348
- settings.auto_margin = true
475
+ ### Debugging
349
476
 
350
- elem.add_box(&method(:title))
351
- elem.add_box(&method(:sum))
352
- elem.add_grid(&method(:number_grid))
353
- end
354
- end
477
+ The `whirled_peas` executable provides some commands that are helpful for debugging.
478
+
479
+ #### list_frames
480
+
481
+ List the frames sent by the driver
482
+
483
+ ```
484
+ $ whirled_peas <config file> list_frames
485
+ Frame 'start' displayed for 5 second(s)
486
+ Frame 'move' displayed for 1 frame ({:direction=>'N'})
487
+ ...
488
+ EOF frame detected
489
+ ```
490
+
491
+ #### play_frame
492
+
493
+ Displays a single frame for several seconds
494
+
495
+ ```
496
+ $ whirled_peas <config file> play_frame move '{"direction":"N"}'
497
+ ```
498
+
499
+ Adding the `--template` flag will result in printing out debug information for the template, e.g.
500
+
501
+ ```
502
+ $ whirled_peas <config file> play_frame move '{"direction":"N"}' --template
503
+ + TEMPLATE [WhirledPeas::Graphics::BoxPainter]
504
+ - Settings
505
+ WhirledPeas::Settings::BoxSettings
506
+ <default>
507
+ - Children
508
+ + TitleContainer [WhirledPeas::Graphics::BoxPainter]
509
+ ...
510
+ ```
511
+
512
+ Note: the `list_frames` command will print out frame names and arguments formatted for this command
513
+
514
+ #### loading
515
+
516
+ Displays the configured loading screen for several seconds
517
+
518
+ ```
519
+ $ whirled_peas <config file> loading
520
+ ```
521
+
522
+ Adding the `--template` flag will result in just printing out the loading template's debug information, e.g.
523
+
524
+ ```
525
+ $ whirled_peas <config file> loading --template
526
+ + TEMPLATE [WhirledPeas::Graphics::BoxPainter]
527
+ - Settings
528
+ WhirledPeas::Settings::BoxSettings
529
+ <default>
530
+ - Children
531
+ + TitleContainer [WhirledPeas::Graphics::BoxPainter]
532
+ ...
355
533
  ```
356
534
 
357
535
  ## Development
@@ -360,6 +538,22 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run
360
538
 
361
539
  To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
362
540
 
541
+ ### Testing
542
+
543
+ In addition to standard RSpec tests, WhirledPeas has custom tests for rendered templates. These files live in `screen_test/rendered`. Each ruby file is expected to define a class named `TemplateFactory` that responds to `#build(name, args)` returning a template (the standard template factory role). Each file should also be accompanied by a `.frame` file with the same base name. This file will contain the output of the rendered screen and is considered the correct output when running tests.
544
+
545
+ Note: viewing `.frame` files with `cat` works better than most other text editors.
546
+
547
+ The following rake tasks are provided to interact with the screen tests
548
+
549
+ - `screen_test` runs all screen tests in the `screen_test/rendered` directory
550
+ - `screen_test:debug[path/to/file.rb]` render the screen without printing it, this allows for debug print statments in the code to appear in the terminal
551
+ - `screen_test:template[path/to/file.rb]` print the rendered template debug tree
552
+ - `screen_test:run[path/to/file.rb]` runs a single screen test
553
+ - `screen_test:save[path/to/file.rb]` saves the output generated by the template in the `.frame` file, overwriting any existing file
554
+ - `screen_test:view[path/to/file.rb]` views the output generated by the template
555
+ - `screen_test:update_all[path/to/file.rb]` interactively step through each pending or failed screen test to compare/set the expected output
556
+
363
557
  ## Contributing
364
558
 
365
559
  Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/whirled_peas. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/[USERNAME]/whirled_peas/blob/master/CODE_OF_CONDUCT.md).