whirled_peas 0.4.0 → 0.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (214) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -0
  3. data/CHANGELOG.md +38 -0
  4. data/README.md +450 -103
  5. data/Rakefile +28 -3
  6. data/examples/intro.rb +52 -0
  7. data/examples/scrolling.rb +53 -0
  8. data/lib/whirled_peas.rb +4 -8
  9. data/lib/whirled_peas/command_line.rb +44 -55
  10. data/lib/whirled_peas/errors.rb +2 -0
  11. data/lib/whirled_peas/frame.rb +0 -1
  12. data/lib/whirled_peas/frame/consumer.rb +30 -0
  13. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  14. data/lib/whirled_peas/frame/event_loop.rb +28 -29
  15. data/lib/whirled_peas/frame/producer.rb +11 -5
  16. data/lib/whirled_peas/graphics.rb +19 -0
  17. data/lib/whirled_peas/graphics/box_painter.rb +101 -0
  18. data/lib/whirled_peas/graphics/canvas.rb +118 -0
  19. data/lib/whirled_peas/graphics/composer.rb +80 -0
  20. data/lib/whirled_peas/graphics/container_coords.rb +72 -0
  21. data/lib/whirled_peas/graphics/container_dimensions.rb +93 -0
  22. data/lib/whirled_peas/graphics/container_painter.rb +272 -0
  23. data/lib/whirled_peas/graphics/debugger.rb +52 -0
  24. data/lib/whirled_peas/graphics/grid_painter.rb +69 -0
  25. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  26. data/lib/whirled_peas/graphics/painter.rb +23 -0
  27. data/lib/whirled_peas/graphics/renderer.rb +26 -0
  28. data/lib/whirled_peas/graphics/screen.rb +70 -0
  29. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  30. data/lib/whirled_peas/graphics/text_painter.rb +40 -0
  31. data/lib/whirled_peas/settings.rb +5 -0
  32. data/lib/whirled_peas/settings/alignment.rb +24 -0
  33. data/lib/whirled_peas/settings/bg_color.rb +24 -0
  34. data/lib/whirled_peas/settings/border.rb +101 -0
  35. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  36. data/lib/whirled_peas/settings/color.rb +68 -0
  37. data/lib/whirled_peas/settings/container_settings.rb +223 -0
  38. data/lib/whirled_peas/settings/debugger.rb +96 -0
  39. data/lib/whirled_peas/settings/display_flow.rb +27 -0
  40. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  41. data/lib/whirled_peas/settings/grid_settings.rb +19 -0
  42. data/lib/whirled_peas/settings/margin.rb +8 -0
  43. data/lib/whirled_peas/settings/padding.rb +8 -0
  44. data/lib/whirled_peas/settings/position.rb +15 -0
  45. data/lib/whirled_peas/settings/scrollbar.rb +15 -0
  46. data/lib/whirled_peas/settings/sizing.rb +19 -0
  47. data/lib/whirled_peas/settings/spacing.rb +58 -0
  48. data/lib/whirled_peas/settings/text_color.rb +21 -0
  49. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  50. data/lib/whirled_peas/settings/vert_alignment.rb +24 -0
  51. data/lib/whirled_peas/utils/ansi.rb +6 -56
  52. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  53. data/lib/whirled_peas/utils/title_font.rb +1 -1
  54. data/lib/whirled_peas/version.rb +1 -1
  55. data/screen_test/elements/box.frame +1 -0
  56. data/screen_test/elements/box.rb +20 -0
  57. data/screen_test/elements/grid.frame +1 -0
  58. data/screen_test/elements/grid.rb +13 -0
  59. data/screen_test/elements/screen_overflow_x.frame +1 -0
  60. data/screen_test/elements/screen_overflow_x.rb +9 -0
  61. data/screen_test/elements/screen_overflow_y.frame +1 -0
  62. data/screen_test/elements/screen_overflow_y.rb +9 -0
  63. data/screen_test/elements/text.frame +1 -0
  64. data/screen_test/elements/text.rb +9 -0
  65. data/screen_test/elements/text_multiline.frame +1 -0
  66. data/screen_test/elements/text_multiline.rb +9 -0
  67. data/screen_test/settings/align/box_around.frame +1 -0
  68. data/screen_test/settings/align/box_around.rb +16 -0
  69. data/screen_test/settings/align/box_between.frame +1 -0
  70. data/screen_test/settings/align/box_between.rb +16 -0
  71. data/screen_test/settings/align/box_center.frame +1 -0
  72. data/screen_test/settings/align/box_center.rb +21 -0
  73. data/screen_test/settings/align/box_default.frame +1 -0
  74. data/screen_test/settings/align/box_default.rb +20 -0
  75. data/screen_test/settings/align/box_evenly.frame +1 -0
  76. data/screen_test/settings/align/box_evenly.rb +16 -0
  77. data/screen_test/settings/align/box_left.frame +1 -0
  78. data/screen_test/settings/align/box_left.rb +21 -0
  79. data/screen_test/settings/align/box_right.frame +1 -0
  80. data/screen_test/settings/align/box_right.rb +21 -0
  81. data/screen_test/settings/align/children_center.frame +1 -0
  82. data/screen_test/settings/align/children_center.rb +15 -0
  83. data/screen_test/settings/align/children_left.frame +1 -0
  84. data/screen_test/settings/align/children_left.rb +15 -0
  85. data/screen_test/settings/align/children_right.frame +1 -0
  86. data/screen_test/settings/align/children_right.rb +15 -0
  87. data/screen_test/settings/align/grid_center.frame +1 -0
  88. data/screen_test/settings/align/grid_center.rb +18 -0
  89. data/screen_test/settings/align/grid_default.frame +1 -0
  90. data/screen_test/settings/align/grid_default.rb +17 -0
  91. data/screen_test/settings/align/grid_left.frame +1 -0
  92. data/screen_test/settings/align/grid_left.rb +18 -0
  93. data/screen_test/settings/align/grid_right.frame +1 -0
  94. data/screen_test/settings/align/grid_right.rb +18 -0
  95. data/screen_test/settings/ansi/bold.frame +1 -0
  96. data/screen_test/settings/ansi/bold.rb +14 -0
  97. data/screen_test/settings/ansi/color.frame +1 -0
  98. data/screen_test/settings/ansi/color.rb +37 -0
  99. data/screen_test/settings/ansi/underline.frame +1 -0
  100. data/screen_test/settings/ansi/underline.rb +14 -0
  101. data/screen_test/settings/border.frame +1 -0
  102. data/screen_test/settings/border.rb +13 -0
  103. data/screen_test/settings/flow/box_b2t.frame +1 -0
  104. data/screen_test/settings/flow/box_b2t.rb +26 -0
  105. data/screen_test/settings/flow/box_l2r.frame +1 -0
  106. data/screen_test/settings/flow/box_l2r.rb +26 -0
  107. data/screen_test/settings/flow/box_r2l.frame +1 -0
  108. data/screen_test/settings/flow/box_r2l.rb +26 -0
  109. data/screen_test/settings/flow/box_t2b.frame +1 -0
  110. data/screen_test/settings/flow/box_t2b.rb +26 -0
  111. data/screen_test/settings/flow/grid_b2t.frame +1 -0
  112. data/screen_test/settings/flow/grid_b2t.rb +14 -0
  113. data/screen_test/settings/flow/grid_l2r.frame +1 -0
  114. data/screen_test/settings/flow/grid_l2r.rb +14 -0
  115. data/screen_test/settings/flow/grid_r2l.frame +1 -0
  116. data/screen_test/settings/flow/grid_r2l.rb +14 -0
  117. data/screen_test/settings/flow/grid_t2b.frame +1 -0
  118. data/screen_test/settings/flow/grid_t2b.rb +14 -0
  119. data/screen_test/settings/height/box.frame +1 -0
  120. data/screen_test/settings/height/box.rb +13 -0
  121. data/screen_test/settings/height/box_border_sizing.frame +1 -0
  122. data/screen_test/settings/height/box_border_sizing.rb +15 -0
  123. data/screen_test/settings/height/grid.frame +1 -0
  124. data/screen_test/settings/height/grid.rb +14 -0
  125. data/screen_test/settings/height/overflow_box.frame +1 -0
  126. data/screen_test/settings/height/overflow_box.rb +13 -0
  127. data/screen_test/settings/height/overflow_box_l2r.frame +1 -0
  128. data/screen_test/settings/height/overflow_box_l2r.rb +17 -0
  129. data/screen_test/settings/height/overflow_box_t2b.frame +1 -0
  130. data/screen_test/settings/height/overflow_box_t2b.rb +16 -0
  131. data/screen_test/settings/height/overflow_grid.frame +1 -0
  132. data/screen_test/settings/height/overflow_grid.rb +16 -0
  133. data/screen_test/settings/margin.frame +1 -0
  134. data/screen_test/settings/margin.rb +16 -0
  135. data/screen_test/settings/padding.frame +1 -0
  136. data/screen_test/settings/padding.rb +13 -0
  137. data/screen_test/settings/position/box_left.frame +1 -0
  138. data/screen_test/settings/position/box_left.rb +17 -0
  139. data/screen_test/settings/position/box_left_negative.frame +1 -0
  140. data/screen_test/settings/position/box_left_negative.rb +17 -0
  141. data/screen_test/settings/position/box_top.frame +1 -0
  142. data/screen_test/settings/position/box_top.rb +17 -0
  143. data/screen_test/settings/position/box_top_negative.frame +1 -0
  144. data/screen_test/settings/position/box_top_negative.rb +17 -0
  145. data/screen_test/settings/position/grid_left.frame +1 -0
  146. data/screen_test/settings/position/grid_left.rb +18 -0
  147. data/screen_test/settings/position/grid_left_negative.frame +1 -0
  148. data/screen_test/settings/position/grid_left_negative.rb +18 -0
  149. data/screen_test/settings/position/grid_top.frame +1 -0
  150. data/screen_test/settings/position/grid_top.rb +18 -0
  151. data/screen_test/settings/position/grid_top_negative.frame +1 -0
  152. data/screen_test/settings/position/grid_top_negative.rb +18 -0
  153. data/screen_test/settings/scroll/horiz_box.frame +1 -0
  154. data/screen_test/settings/scroll/horiz_box.rb +17 -0
  155. data/screen_test/settings/scroll/horiz_box_align_center.rb +18 -0
  156. data/screen_test/settings/scroll/horiz_box_align_right.rb +18 -0
  157. data/screen_test/settings/scroll/vert_box.frame +1 -0
  158. data/screen_test/settings/scroll/vert_box.rb +20 -0
  159. data/screen_test/settings/title_font.frame +1 -0
  160. data/screen_test/settings/title_font.rb +12 -0
  161. data/screen_test/settings/valign/box_around.frame +1 -0
  162. data/screen_test/settings/valign/box_around.rb +17 -0
  163. data/screen_test/settings/valign/box_between.frame +1 -0
  164. data/screen_test/settings/valign/box_between.rb +17 -0
  165. data/screen_test/settings/valign/box_bottom.frame +1 -0
  166. data/screen_test/settings/valign/box_bottom.rb +17 -0
  167. data/screen_test/settings/valign/box_default.frame +1 -0
  168. data/screen_test/settings/valign/box_default.rb +16 -0
  169. data/screen_test/settings/valign/box_evenly.frame +1 -0
  170. data/screen_test/settings/valign/box_evenly.rb +17 -0
  171. data/screen_test/settings/valign/box_middle.frame +1 -0
  172. data/screen_test/settings/valign/box_middle.rb +17 -0
  173. data/screen_test/settings/valign/box_top.frame +1 -0
  174. data/screen_test/settings/valign/box_top.rb +17 -0
  175. data/screen_test/settings/valign/grid_bottom.frame +1 -0
  176. data/screen_test/settings/valign/grid_bottom.rb +15 -0
  177. data/screen_test/settings/valign/grid_default.frame +1 -0
  178. data/screen_test/settings/valign/grid_default.rb +14 -0
  179. data/screen_test/settings/valign/grid_middle.frame +1 -0
  180. data/screen_test/settings/valign/grid_middle.rb +15 -0
  181. data/screen_test/settings/valign/grid_top.frame +1 -0
  182. data/screen_test/settings/valign/grid_top.rb +15 -0
  183. data/screen_test/settings/width/box_border_sizing.frame +1 -0
  184. data/screen_test/settings/width/box_border_sizing.rb +15 -0
  185. data/screen_test/settings/width/box_content.frame +1 -0
  186. data/screen_test/settings/width/box_content.rb +15 -0
  187. data/screen_test/settings/width/box_default.frame +1 -0
  188. data/screen_test/settings/width/box_default.rb +14 -0
  189. data/screen_test/settings/width/grid.frame +1 -0
  190. data/screen_test/settings/width/grid.rb +14 -0
  191. data/screen_test/settings/width/overflow_align_center.frame +1 -0
  192. data/screen_test/settings/width/overflow_align_center.rb +14 -0
  193. data/screen_test/settings/width/overflow_align_right.frame +1 -0
  194. data/screen_test/settings/width/overflow_align_right.rb +14 -0
  195. data/screen_test/settings/width/overflow_box.frame +1 -0
  196. data/screen_test/settings/width/overflow_box.rb +13 -0
  197. data/screen_test/settings/width/overflow_box_l2r.frame +1 -0
  198. data/screen_test/settings/width/overflow_box_l2r.rb +16 -0
  199. data/screen_test/settings/width/overflow_box_t2b.frame +1 -0
  200. data/screen_test/settings/width/overflow_box_t2b.rb +17 -0
  201. data/screen_test/settings/width/overflow_grid.frame +1 -0
  202. data/screen_test/settings/width/overflow_grid.rb +14 -0
  203. data/tools/whirled_peas/tools/screen_tester.rb +233 -0
  204. metadata +191 -12
  205. data/bin/title_fonts +0 -6
  206. data/lib/whirled_peas/frame/print_consumer.rb +0 -33
  207. data/lib/whirled_peas/template.rb +0 -5
  208. data/lib/whirled_peas/template/element.rb +0 -230
  209. data/lib/whirled_peas/template/settings.rb +0 -530
  210. data/lib/whirled_peas/ui.rb +0 -5
  211. data/lib/whirled_peas/ui/canvas.rb +0 -68
  212. data/lib/whirled_peas/ui/painter.rb +0 -287
  213. data/lib/whirled_peas/ui/screen.rb +0 -63
  214. data/lib/whirled_peas/utils/color.rb +0 -101
@@ -1,15 +1,22 @@
1
1
  require 'socket'
2
2
  require 'json'
3
3
 
4
- require_relative '../null_logger'
4
+ require 'whirled_peas/null_logger'
5
5
 
6
6
  module WhirledPeas
7
7
  module Frame
8
+ # A Producer is the object given to the driver as the interface that allows
9
+ # the driver to emit frame events. The recommended way of creating a Producer
10
+ # is by invoking `Producer.produce` as it handles the lifecycle methods of
11
+ # the consumer.
8
12
  class Producer
9
13
  LOGGER_ID = 'PRODUCER'
10
14
 
11
- # Manages the EventLoop lifecycle and yields a Producer to send frames to the
12
- # EventLoop
15
+ # Manages the consumer lifecycle and yields a Producer to send frames to the
16
+ # consumer
17
+ #
18
+ # @param consumer [Consumer] instance that consumes frame events through
19
+ # `#enqueue`
13
20
  def self.produce(consumer, logger=NullLogger.new)
14
21
  producer = new(consumer, logger)
15
22
  consumer_thread = Thread.new do
@@ -17,14 +24,13 @@ module WhirledPeas
17
24
  consumer.start
18
25
  end
19
26
  yield producer
20
- producer.send_frame(Frame::EOF)
21
27
  producer.flush
22
28
  rescue => e
23
- consumer.stop if consumer
24
29
  logger.warn(LOGGER_ID) { 'Exited with error' }
25
30
  logger.error(LOGGER_ID) { e }
26
31
  raise
27
32
  ensure
33
+ consumer.stop
28
34
  consumer_thread.join if consumer_thread
29
35
  end
30
36
 
@@ -0,0 +1,19 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class << self
4
+ attr_accessor :debug
5
+
6
+ def debugger(string_or_proc)
7
+ return unless @debug
8
+ @debugger ||= Logger.new(STDOUT, level: Logger::DEBUG)
9
+ if string_or_proc.is_a?(Proc)
10
+ string = string_or_proc.call
11
+ else
12
+ string = string_or_proc
13
+ end
14
+ @debugger.debug(string)
15
+ end
16
+ end
17
+ end
18
+ private_constant :Graphics
19
+ end
@@ -0,0 +1,101 @@
1
+ require_relative 'container_painter'
2
+ require_relative 'container_dimensions'
3
+
4
+ module WhirledPeas
5
+ module Graphics
6
+ class BoxPainter < ContainerPainter
7
+ def paint(canvas, left, top, &block)
8
+ super
9
+ canvas_coords = coords(left, top)
10
+ content_canvas = canvas.child(
11
+ canvas_coords.content_left,
12
+ canvas_coords.content_top,
13
+ dimensions.content_width,
14
+ dimensions.content_height
15
+ )
16
+ return unless content_canvas.writable?
17
+ if settings.horizontal_flow?
18
+ paint_horizontally(content_canvas, canvas_coords, &block)
19
+ else
20
+ paint_vertically(content_canvas, canvas_coords, &block)
21
+ end
22
+ end
23
+
24
+ def dimensions
25
+ @dimensions ||= begin
26
+ content_width = 0
27
+ content_height = 0
28
+ if settings.horizontal_flow?
29
+ each_child do |child|
30
+ content_width += child.dimensions.outer_width
31
+ if child.dimensions.outer_height > content_height
32
+ content_height = child.dimensions.outer_height
33
+ end
34
+ end
35
+ else
36
+ each_child do |child|
37
+ if child.dimensions.outer_width > content_width
38
+ content_width = child.dimensions.outer_width
39
+ end
40
+ content_height += child.dimensions.outer_height
41
+ end
42
+ end
43
+ ContainerDimensions.new(settings, content_width, content_height)
44
+ end
45
+ end
46
+
47
+ def each_child(&block)
48
+ if settings.reverse_flow?
49
+ children.reverse.each(&block)
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def paint_horizontally(canvas, canvas_coords, &block)
58
+ stroke_left = canvas_coords.content_left
59
+ stroke_top = canvas_coords.content_top
60
+ children_width = 0
61
+ each_child { |c| children_width += c.dimensions.outer_width }
62
+ left_offset, spacing_offset = horiz_justify_offset(children_width)
63
+ stroke_left += left_offset
64
+ each_child do |child|
65
+ top_offset, _ = vert_justify_offset(child.dimensions.outer_height)
66
+ child_width = child.dimensions.outer_width
67
+ child_canvas = canvas.child(
68
+ stroke_left,
69
+ stroke_top + top_offset,
70
+ child_width,
71
+ child.dimensions.outer_height
72
+ )
73
+ child.paint(child_canvas, stroke_left, stroke_top + top_offset, &block)
74
+ stroke_left += child_width + spacing_offset
75
+ end
76
+ end
77
+
78
+ def paint_vertically(canvas, canvas_coords, &block)
79
+ stroke_left = canvas_coords.content_left
80
+ stroke_top = canvas_coords.content_top
81
+ children_height = 0
82
+ each_child { |c| children_height += c.dimensions.outer_height }
83
+ top_offset, spacing_offset = vert_justify_offset(children_height)
84
+ stroke_top += top_offset
85
+ each_child do |child|
86
+ left_offset, _ = horiz_justify_offset(child.dimensions.outer_width)
87
+ child_height = child.dimensions.outer_height
88
+ child_canvas = canvas.child(
89
+ stroke_left + left_offset,
90
+ stroke_top ,
91
+ child.dimensions.outer_width,
92
+ child_height
93
+ )
94
+ child.paint(child_canvas, stroke_left + left_offset, stroke_top, &block)
95
+ stroke_top += child_height + spacing_offset
96
+ end
97
+ end
98
+ end
99
+ private_constant :BoxPainter
100
+ end
101
+ end
@@ -0,0 +1,118 @@
1
+ require 'whirled_peas/utils/formatted_string'
2
+
3
+ module WhirledPeas
4
+ module Graphics
5
+ # Canvas represent the area of the screen a painter can paint on.
6
+ class Canvas
7
+ attr_reader :left, :top, :width, :height
8
+
9
+ def self.unwritable
10
+ new(-1, -1, 0, 0)
11
+ end
12
+
13
+ def initialize(left, top, width, height)
14
+ @left = left
15
+ @top = top
16
+ @width = width
17
+ @height = height
18
+ end
19
+
20
+ def writable?
21
+ width > 0 || height > 0
22
+ end
23
+
24
+ def child(child_left, child_top, child_width, child_height)
25
+ Graphics.debugger(
26
+ proc do
27
+ "Create child: #{self.inspect}.child(left=#{child_left}, top=#{child_top}, width=#{child_width}, height=#{child_height})"
28
+ end
29
+ )
30
+ if child_left >= left + width
31
+ self.class.unwritable
32
+ elsif child_left + child_width <= left
33
+ self.class.unwritable
34
+ elsif child_top >= top + height
35
+ self.class.unwritable
36
+ elsif child_top + child_height <= top
37
+ self.class.unwritable
38
+ else
39
+ if child_left < left
40
+ child_width -= left - child_left
41
+ child_left = left
42
+ end
43
+ child_width = [width - (child_left - left), child_width].min
44
+ if child_top < top
45
+ child_height -= top - child_top
46
+ child_top = top
47
+ end
48
+ child_height = [height - (child_top - top), child_height].min
49
+ child_canvas = self.class.new(
50
+ child_left,
51
+ child_top,
52
+ child_width,
53
+ child_height,
54
+ )
55
+ Graphics.debugger(proc { " -> #{child_canvas.inspect}" })
56
+ child_canvas
57
+ end
58
+ end
59
+
60
+ # Yields a single line of formatted characters positioned on the canvas,
61
+ # verifying only characters within the canvas are included.
62
+ def stroke(stroke_left, stroke_top, raw, formatting=[], &block)
63
+ Graphics.debugger(
64
+ proc do
65
+ "Stroke: #{self.inspect}.stroke(left=#{stroke_left}, top=#{stroke_top}, length=#{raw.length})"
66
+ end
67
+ )
68
+ if stroke_left >= left + width
69
+ # The stroke starts to the right of the canvas
70
+ fstring = Utils::FormattedString.blank
71
+ elsif stroke_left + raw.length <= left
72
+ # The stroke ends to the left of the canvas
73
+ fstring = Utils::FormattedString.blank
74
+ elsif stroke_top < top
75
+ # The stroke is above the canvas
76
+ fstring = Utils::FormattedString.blank
77
+ elsif stroke_top >= top + height
78
+ # The stroke is below the canvas
79
+ fstring = Utils::FormattedString.blank
80
+ else
81
+ # In this section, we know that at least part of the stroke should be visible
82
+ # on the canvas. Chop off parts of the raw string that aren't within the
83
+ # canvas boundary and ensure the stroke start position is also within the
84
+ # canvas boundary
85
+
86
+ # If the stroke starts to the left of the canvas, set the start index to the
87
+ # first value that will be on the canvas, then update stroke_left to be on
88
+ # the canvas
89
+ start_index = stroke_left < left ? left - stroke_left : 0
90
+ stroke_left = left if stroke_left <= left
91
+
92
+ # Determine how many characters from the stroke will fit on the canvas
93
+ visible_length = [raw.length, width - (stroke_left - left)].min
94
+ end_index = start_index + visible_length - 1
95
+ fstring = Utils::FormattedString.new(raw[start_index..end_index], formatting)
96
+ end
97
+ Graphics.debugger(
98
+ proc do
99
+ " -> Stroke(left=#{stroke_left}, top=#{stroke_top}, length=#{fstring.length})"
100
+ end
101
+ )
102
+ yield stroke_left, stroke_top, fstring
103
+ end
104
+
105
+ def hash
106
+ [left, top, width, height].hash
107
+ end
108
+
109
+ def ==(other)
110
+ other.is_a?(self.class) && hash == other.hash
111
+ end
112
+
113
+ def inspect
114
+ "Canvas(left=#{left}, top=#{top}, width=#{width}, height=#{height})"
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,80 @@
1
+ require 'whirled_peas/settings/box_settings'
2
+ require 'whirled_peas/settings/grid_settings'
3
+ require 'whirled_peas/settings/text_settings'
4
+
5
+ require_relative 'box_painter'
6
+ require_relative 'grid_painter'
7
+ require_relative 'text_painter'
8
+
9
+ module WhirledPeas
10
+ module Graphics
11
+ class Composer
12
+ # List of classes that convert nicely to a string
13
+ STRINGALBE_CLASSES = [FalseClass, Float, Integer, NilClass, String, Symbol, TrueClass]
14
+ private_constant :STRINGALBE_CLASSES
15
+
16
+ def self.stringable?(value)
17
+ STRINGALBE_CLASSES.include?(value.class)
18
+ end
19
+
20
+ def self.next_name
21
+ @counter ||= 0
22
+ @counter += 1
23
+ "Element-#{@counter}"
24
+ end
25
+
26
+ def self.build
27
+ settings = Settings::BoxSettings.new
28
+ template = BoxPainter.new('TEMPLATE', settings)
29
+ composer = Composer.new(template)
30
+ value = yield composer, settings
31
+ if !template.children? && stringable?(value)
32
+ composer.add_text { value.to_s }
33
+ end
34
+ template
35
+ end
36
+
37
+ attr_reader :painter
38
+
39
+ def initialize(painter)
40
+ @painter = painter
41
+ end
42
+
43
+ def add_text(name=self.class.next_name, &block)
44
+ child_settings = Settings::TextSettings.inherit(painter.settings)
45
+ child = TextPainter.new(name, child_settings)
46
+ # TextPainters are not composable, so yield nil
47
+ content = yield nil, child_settings
48
+ unless self.class.stringable?(content)
49
+ raise ArgumentError, "Unsupported type for text: #{content.class}"
50
+ end
51
+ child.content = content.to_s
52
+ painter.add_child(child)
53
+ end
54
+
55
+ def add_box(name=self.class.next_name, &block)
56
+ child_settings = Settings::BoxSettings.inherit(painter.settings)
57
+ child = BoxPainter.new(name, child_settings)
58
+ composer = self.class.new(child)
59
+ value = yield composer, child.settings
60
+ painter.add_child(child)
61
+ if !child.children? && self.class.stringable?(value)
62
+ composer.add_text("#{name}-Text") { value.to_s }
63
+ end
64
+ end
65
+
66
+ def add_grid(name=self.class.next_name, &block)
67
+ child_settings = Settings::GridSettings.inherit(painter.settings)
68
+ child = GridPainter.new(name, child_settings)
69
+ composer = self.class.new(child)
70
+ values = yield composer, child.settings
71
+ painter.add_child(child)
72
+ if !child.children? && values.is_a?(Array)
73
+ values.each.with_index do |value, index|
74
+ composer.add_text("#{name}-Text-#{index}") { value.to_s }
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,72 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class ContainerCoords
4
+ def initialize(dimensions, settings, start_left, start_top)
5
+ @dimensions = dimensions
6
+ @settings = settings
7
+ @start_left = start_left
8
+ @start_top = start_top
9
+ end
10
+
11
+ def left
12
+ start_left + settings.position.left
13
+ end
14
+
15
+ def top
16
+ start_top + settings.position.top
17
+ end
18
+
19
+ def border_left
20
+ left + settings.margin.left
21
+ end
22
+
23
+ def border_top
24
+ top + settings.margin.top
25
+ end
26
+
27
+ def padding_left
28
+ border_left + (settings.border.left? ? 1 : 0)
29
+ end
30
+
31
+ def padding_top
32
+ border_top + (settings.border.top? ? 1 : 0)
33
+ end
34
+
35
+ def content_left(col_index=0)
36
+ padding_left + settings.padding.left + col_index * grid_width
37
+ end
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
47
+ end
48
+
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
59
+ end
60
+
61
+ def grid_height
62
+ (settings.border.inner_horiz? ? 1 : 0) +
63
+ inner_grid_height +
64
+ (settings.scrollbar.horiz? ? 1 : 0)
65
+ end
66
+
67
+ private
68
+
69
+ attr_reader :settings, :dimensions, :start_left, :start_top
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,93 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class ContainerDimensions
4
+ attr_reader :children_width, :children_height, :num_cols, :num_rows
5
+
6
+ def initialize(settings, content_width, content_height, num_cols=1, num_rows=1)
7
+ @orig_content_width = content_width
8
+ @orig_content_height = content_height
9
+ @children_width = content_width
10
+ @children_height = content_height
11
+ @num_cols = num_cols
12
+ @num_rows = num_rows
13
+ @settings = settings
14
+ end
15
+
16
+ def content_width
17
+ return orig_content_width unless settings.width
18
+ if settings.border_sizing?
19
+ settings.width - outer_border_width - scrollbar_width - padding_width
20
+ else
21
+ settings.width
22
+ end
23
+ end
24
+
25
+ def content_height
26
+ return orig_content_height unless settings.height
27
+ if settings.border_sizing?
28
+ settings.height - outer_border_height - scrollbar_height - padding_height
29
+ else
30
+ settings.height
31
+ end
32
+ end
33
+
34
+ def outer_width
35
+ @outer_width ||= margin_width +
36
+ outer_border_width +
37
+ num_cols * (padding_width + content_width + scrollbar_width) +
38
+ (num_cols - 1) * inner_border_width
39
+ end
40
+
41
+ def outer_height
42
+ @outer_height ||= margin_height +
43
+ outer_border_height +
44
+ num_rows * (padding_height + content_height + scrollbar_height) +
45
+ (num_rows - 1) * inner_border_height
46
+ end
47
+
48
+ def margin_width
49
+ settings.margin.left + settings.margin.right
50
+ end
51
+
52
+ def margin_height
53
+ settings.margin.top + settings.margin.bottom
54
+ end
55
+
56
+ def outer_border_width
57
+ (settings.border.left? ? 1 : 0) + (settings.border.right? ? 1 : 0)
58
+ end
59
+
60
+ def outer_border_height
61
+ (settings.border.top? ? 1 : 0) + (settings.border.bottom? ? 1 : 0)
62
+ end
63
+
64
+ def inner_border_width
65
+ settings.border.inner_vert? ? 1 : 0
66
+ end
67
+
68
+ def inner_border_height
69
+ settings.border.inner_horiz? ? 1 : 0
70
+ end
71
+
72
+ def padding_width
73
+ settings.padding.left + settings.padding.right
74
+ end
75
+
76
+ def padding_height
77
+ settings.padding.top + settings.padding.bottom
78
+ end
79
+
80
+ def scrollbar_width
81
+ settings.scrollbar.vert? ? 1 : 0
82
+ end
83
+
84
+ def scrollbar_height
85
+ settings.scrollbar.horiz? ? 1 : 0
86
+ end
87
+
88
+ private
89
+
90
+ attr_reader :settings, :orig_content_width, :orig_content_height
91
+ end
92
+ end
93
+ end