whirled_peas 0.4.1 → 0.5.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 (141) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +12 -0
  3. data/README.md +111 -73
  4. data/Rakefile +37 -3
  5. data/bin/debug +35 -0
  6. data/lib/whirled_peas.rb +9 -7
  7. data/lib/whirled_peas/command_line.rb +25 -32
  8. data/lib/whirled_peas/debugger.rb +80 -0
  9. data/lib/whirled_peas/errors.rb +2 -0
  10. data/lib/whirled_peas/frame.rb +0 -1
  11. data/lib/whirled_peas/frame/consumer.rb +30 -0
  12. data/lib/whirled_peas/frame/debug_consumer.rb +30 -0
  13. data/lib/whirled_peas/frame/event_loop.rb +29 -30
  14. data/lib/whirled_peas/frame/producer.rb +11 -5
  15. data/lib/whirled_peas/graphics.rb +5 -0
  16. data/lib/whirled_peas/graphics/box_painter.rb +100 -0
  17. data/lib/whirled_peas/graphics/canvas.rb +107 -0
  18. data/lib/whirled_peas/graphics/container_coords.rb +61 -0
  19. data/lib/whirled_peas/graphics/container_dimensions.rb +65 -0
  20. data/lib/whirled_peas/graphics/container_painter.rb +116 -0
  21. data/lib/whirled_peas/graphics/debugger.rb +43 -0
  22. data/lib/whirled_peas/graphics/grid_painter.rb +56 -0
  23. data/lib/whirled_peas/graphics/mock_screen.rb +26 -0
  24. data/lib/whirled_peas/graphics/painter.rb +24 -0
  25. data/lib/whirled_peas/graphics/renderer.rb +49 -0
  26. data/lib/whirled_peas/graphics/screen.rb +65 -0
  27. data/lib/whirled_peas/graphics/text_dimensions.rb +15 -0
  28. data/lib/whirled_peas/graphics/text_painter.rb +38 -0
  29. data/lib/whirled_peas/settings.rb +5 -0
  30. data/lib/whirled_peas/settings/bg_color.rb +22 -0
  31. data/lib/whirled_peas/settings/border.rb +101 -0
  32. data/lib/whirled_peas/settings/box_settings.rb +8 -0
  33. data/lib/whirled_peas/{utils → settings}/color.rb +3 -35
  34. data/lib/whirled_peas/settings/container_settings.rb +128 -0
  35. data/lib/whirled_peas/settings/debugger.rb +87 -0
  36. data/lib/whirled_peas/settings/display_flow.rb +25 -0
  37. data/lib/whirled_peas/settings/element_settings.rb +61 -0
  38. data/lib/whirled_peas/settings/grid_settings.rb +11 -0
  39. data/lib/whirled_peas/settings/margin.rb +8 -0
  40. data/lib/whirled_peas/settings/padding.rb +8 -0
  41. data/lib/whirled_peas/settings/position.rb +15 -0
  42. data/lib/whirled_peas/settings/spacing.rb +24 -0
  43. data/lib/whirled_peas/settings/text_align.rb +19 -0
  44. data/lib/whirled_peas/settings/text_color.rb +19 -0
  45. data/lib/whirled_peas/settings/text_settings.rb +15 -0
  46. data/lib/whirled_peas/template/box_element.rb +8 -0
  47. data/lib/whirled_peas/template/composer.rb +68 -0
  48. data/lib/whirled_peas/template/container.rb +28 -0
  49. data/lib/whirled_peas/template/debugger.rb +34 -0
  50. data/lib/whirled_peas/template/element.rb +1 -218
  51. data/lib/whirled_peas/template/grid_element.rb +8 -0
  52. data/lib/whirled_peas/template/text_element.rb +24 -0
  53. data/lib/whirled_peas/utils/ansi.rb +6 -56
  54. data/lib/whirled_peas/utils/formatted_string.rb +64 -0
  55. data/lib/whirled_peas/version.rb +1 -1
  56. data/screen_test/rendered/elements/box.frame +1 -0
  57. data/screen_test/rendered/elements/box.rb +20 -0
  58. data/screen_test/rendered/elements/grid.frame +1 -0
  59. data/screen_test/rendered/elements/grid.rb +13 -0
  60. data/screen_test/rendered/elements/screen_overflow.rb +9 -0
  61. data/screen_test/rendered/elements/text.frame +1 -0
  62. data/screen_test/rendered/elements/text.rb +9 -0
  63. data/screen_test/rendered/elements/text_multiline.frame +1 -0
  64. data/screen_test/rendered/elements/text_multiline.rb +9 -0
  65. data/screen_test/rendered/settings/align/box.frame +1 -0
  66. data/screen_test/rendered/settings/align/box.rb +20 -0
  67. data/screen_test/rendered/settings/align/children_center.frame +1 -0
  68. data/screen_test/rendered/settings/align/children_center.rb +13 -0
  69. data/screen_test/rendered/settings/align/children_left.frame +1 -0
  70. data/screen_test/rendered/settings/align/children_left.rb +13 -0
  71. data/screen_test/rendered/settings/align/children_right.frame +1 -0
  72. data/screen_test/rendered/settings/align/children_right.rb +13 -0
  73. data/screen_test/rendered/settings/align/grid.frame +1 -0
  74. data/screen_test/rendered/settings/align/grid.rb +20 -0
  75. data/screen_test/rendered/settings/ansi/bold.frame +1 -0
  76. data/screen_test/rendered/settings/ansi/bold.rb +15 -0
  77. data/screen_test/rendered/settings/ansi/color.frame +1 -0
  78. data/screen_test/rendered/settings/ansi/color.rb +37 -0
  79. data/screen_test/rendered/settings/ansi/underline.frame +1 -0
  80. data/screen_test/rendered/settings/ansi/underline.rb +15 -0
  81. data/screen_test/rendered/settings/border.frame +1 -0
  82. data/screen_test/rendered/settings/border.rb +13 -0
  83. data/screen_test/rendered/settings/flow/l2r.frame +1 -0
  84. data/screen_test/rendered/settings/flow/l2r.rb +24 -0
  85. data/screen_test/rendered/settings/flow/t2b.frame +1 -0
  86. data/screen_test/rendered/settings/flow/t2b.rb +24 -0
  87. data/screen_test/rendered/settings/height/box.frame +1 -0
  88. data/screen_test/rendered/settings/height/box.rb +13 -0
  89. data/screen_test/rendered/settings/height/grid.frame +1 -0
  90. data/screen_test/rendered/settings/height/grid.rb +14 -0
  91. data/screen_test/rendered/settings/height/overflow_box.frame +1 -0
  92. data/screen_test/rendered/settings/height/overflow_box.rb +13 -0
  93. data/screen_test/rendered/settings/height/overflow_box_l2r.frame +1 -0
  94. data/screen_test/rendered/settings/height/overflow_box_l2r.rb +15 -0
  95. data/screen_test/rendered/settings/height/overflow_box_t2b.frame +1 -0
  96. data/screen_test/rendered/settings/height/overflow_box_t2b.rb +14 -0
  97. data/screen_test/rendered/settings/height/overflow_grid.frame +1 -0
  98. data/screen_test/rendered/settings/height/overflow_grid.rb +16 -0
  99. data/screen_test/rendered/settings/margin.frame +1 -0
  100. data/screen_test/rendered/settings/margin.rb +14 -0
  101. data/screen_test/rendered/settings/padding.frame +1 -0
  102. data/screen_test/rendered/settings/padding.rb +11 -0
  103. data/screen_test/rendered/settings/position/box_left.frame +1 -0
  104. data/screen_test/rendered/settings/position/box_left.rb +17 -0
  105. data/screen_test/rendered/settings/position/box_left_negative.frame +1 -0
  106. data/screen_test/rendered/settings/position/box_left_negative.rb +17 -0
  107. data/screen_test/rendered/settings/position/box_top.frame +1 -0
  108. data/screen_test/rendered/settings/position/box_top.rb +17 -0
  109. data/screen_test/rendered/settings/position/box_top_negative.frame +1 -0
  110. data/screen_test/rendered/settings/position/box_top_negative.rb +17 -0
  111. data/screen_test/rendered/settings/position/grid_left.frame +1 -0
  112. data/screen_test/rendered/settings/position/grid_left.rb +18 -0
  113. data/screen_test/rendered/settings/position/grid_left_negative.frame +1 -0
  114. data/screen_test/rendered/settings/position/grid_left_negative.rb +18 -0
  115. data/screen_test/rendered/settings/position/grid_top.frame +1 -0
  116. data/screen_test/rendered/settings/position/grid_top.rb +18 -0
  117. data/screen_test/rendered/settings/position/grid_top_negative.frame +1 -0
  118. data/screen_test/rendered/settings/position/grid_top_negative.rb +18 -0
  119. data/screen_test/rendered/settings/title_font.frame +1 -0
  120. data/screen_test/rendered/settings/title_font.rb +12 -0
  121. data/screen_test/rendered/settings/width/box.frame +1 -0
  122. data/screen_test/rendered/settings/width/box.rb +13 -0
  123. data/screen_test/rendered/settings/width/grid.frame +1 -0
  124. data/screen_test/rendered/settings/width/grid.rb +14 -0
  125. data/screen_test/rendered/settings/width/overflow_box.frame +1 -0
  126. data/screen_test/rendered/settings/width/overflow_box.rb +11 -0
  127. data/screen_test/rendered/settings/width/overflow_box_l2r.frame +1 -0
  128. data/screen_test/rendered/settings/width/overflow_box_l2r.rb +14 -0
  129. data/screen_test/rendered/settings/width/overflow_box_t2b.frame +1 -0
  130. data/screen_test/rendered/settings/width/overflow_box_t2b.rb +15 -0
  131. data/screen_test/rendered/settings/width/overflow_grid.frame +1 -0
  132. data/screen_test/rendered/settings/width/overflow_grid.rb +14 -0
  133. data/screen_test/screen_tester.rb +191 -0
  134. metadata +122 -10
  135. data/bin/title_fonts +0 -6
  136. data/lib/whirled_peas/frame/print_consumer.rb +0 -33
  137. data/lib/whirled_peas/template/settings.rb +0 -530
  138. data/lib/whirled_peas/ui.rb +0 -5
  139. data/lib/whirled_peas/ui/canvas.rb +0 -68
  140. data/lib/whirled_peas/ui/painter.rb +0 -287
  141. data/lib/whirled_peas/ui/screen.rb +0 -63
@@ -0,0 +1,107 @@
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, :start_left, :start_top
8
+
9
+ def self.unwritable
10
+ new(-1, -1, 0, 0, -1, -1)
11
+ end
12
+
13
+ def initialize(left, top, width, height, start_left, start_top)
14
+ @left = left
15
+ @top = top
16
+ @width = width
17
+ @height = height
18
+ @start_left = start_left
19
+ @start_top = start_top
20
+ end
21
+
22
+ def writable?
23
+ width > 0 || height > 0
24
+ end
25
+
26
+ def child(start_left, start_top, child_width, child_height)
27
+ child_left = start_left
28
+ child_top = start_top
29
+ if child_left >= left + width
30
+ self.class.unwritable
31
+ elsif child_left + child_width <= left
32
+ self.class.unwritable
33
+ elsif child_top >= top + height
34
+ self.class.unwritable
35
+ elsif child_top + child_height <= top
36
+ self.class.unwritable
37
+ else
38
+ if child_left < left
39
+ child_width -= left - child_left
40
+ child_left = left
41
+ end
42
+ child_width = [width - (child_left - left), child_width].min
43
+ if child_top < top
44
+ child_height -= top - child_top
45
+ child_top = top
46
+ end
47
+ child_height = [height - (child_top - top), child_height].min
48
+ self.class.new(
49
+ child_left,
50
+ child_top,
51
+ child_width,
52
+ child_height,
53
+ start_left,
54
+ start_top
55
+ )
56
+ end
57
+ end
58
+
59
+ # Yields a single line of formatted characters positioned on the canvas,
60
+ # verifying only characters within the canvas are included.
61
+ def stroke(stroke_left, stroke_top, raw, formatting=[], &block)
62
+ if stroke_left >= left + width
63
+ # The stroke starts to the right of the canvas
64
+ fstring = Utils::FormattedString.blank
65
+ elsif stroke_left + raw.length <= left
66
+ # The stroke ends to the left of the canvas
67
+ fstring = Utils::FormattedString.blank
68
+ elsif stroke_top < top
69
+ # The stroke is above the canvas
70
+ fstring = Utils::FormattedString.blank
71
+ elsif stroke_top >= top + height
72
+ # The stroke is below the canvas
73
+ fstring = Utils::FormattedString.blank
74
+ else
75
+ # In this section, we know that at least part of the stroke should be visible
76
+ # on the canvas. Chop off parts of the raw string that aren't within the
77
+ # canvas boundary and ensure the stroke start position is also within the
78
+ # canvas boundary
79
+
80
+ # If the stroke starts to the left of the canvas, set the start index to the
81
+ # first value that will be on the canvas, then update stroke_left to be on
82
+ # the canvas
83
+ start_index = stroke_left < left ? left - stroke_left : 0
84
+ stroke_left = left if stroke_left <= left
85
+
86
+ # Determine how many characters from the stroke will fit on the canvas
87
+ visible_length = [raw.length, width - (stroke_left - left)].min
88
+ end_index = start_index + visible_length - 1
89
+ fstring = Utils::FormattedString.new(raw[start_index..end_index], formatting)
90
+ end
91
+ yield stroke_left, stroke_top, fstring
92
+ end
93
+
94
+ def hash
95
+ [left, top, width, height].hash
96
+ end
97
+
98
+ def ==(other)
99
+ other.is_a?(self.class) && hash == other.hash
100
+ end
101
+
102
+ def inspect
103
+ "Canvas(left=#{left}, top=#{top}, width=#{width}, height=#{height})"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,61 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class ContainerCoords
4
+ def initialize(canvas, dimensions, settings)
5
+ @canvas = canvas
6
+ @dimensions = dimensions
7
+ @settings = settings
8
+ end
9
+
10
+ def left
11
+ canvas.start_left + settings.position.left
12
+ end
13
+
14
+ def top
15
+ canvas.start_top + settings.position.top
16
+ end
17
+
18
+ def border_left
19
+ left + settings.margin.left
20
+ end
21
+
22
+ def border_top
23
+ top + settings.margin.top
24
+ end
25
+
26
+ def padding_left
27
+ border_left + (settings.border.left? ? 1 : 0)
28
+ end
29
+
30
+ def padding_top
31
+ border_top + (settings.border.top? ? 1 : 0)
32
+ end
33
+
34
+ def content_left(col_index=0)
35
+ padding_left + settings.padding.left + col_index * grid_width(true)
36
+ end
37
+
38
+ def content_top(row_index=0)
39
+ padding_top + settings.padding.top + row_index * grid_height(true)
40
+ end
41
+
42
+ def grid_width(include_border)
43
+ (include_border && settings.border.inner_vert? ? 1 : 0) +
44
+ settings.padding.left +
45
+ dimensions.content_width +
46
+ settings.padding.right
47
+ end
48
+
49
+ def grid_height(include_border)
50
+ (include_border && settings.border.inner_horiz? ? 1 : 0) +
51
+ settings.padding.top +
52
+ dimensions.content_height +
53
+ settings.padding.bottom
54
+ end
55
+
56
+ private
57
+
58
+ attr_reader :canvas, :settings, :dimensions
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,65 @@
1
+ module WhirledPeas
2
+ module Graphics
3
+ class ContainerDimensions
4
+ attr_reader :content_width, :content_height, :num_cols, :num_rows
5
+
6
+ def initialize(settings, content_width, content_height, num_cols=1, num_rows=1)
7
+ @content_width = settings.width || content_width
8
+ @content_height = settings.height || content_height
9
+ @num_cols = num_cols
10
+ @num_rows = num_rows
11
+ @settings = settings
12
+ end
13
+
14
+ def outer_width
15
+ @outer_width ||= margin_width +
16
+ outer_border_width +
17
+ num_cols * (padding_width + content_width) +
18
+ (num_cols - 1) * inner_border_width
19
+ end
20
+
21
+ def outer_height
22
+ @outer_height ||= margin_height +
23
+ outer_border_height +
24
+ num_rows * (padding_height + content_height) +
25
+ (num_rows - 1) * inner_border_height
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader :settings
31
+
32
+ def margin_width
33
+ settings.margin.left + settings.margin.right
34
+ end
35
+
36
+ def margin_height
37
+ settings.margin.top + settings.margin.bottom
38
+ end
39
+
40
+ def outer_border_width
41
+ (settings.border.left? ? 1 : 0) + (settings.border.right? ? 1 : 0)
42
+ end
43
+
44
+ def outer_border_height
45
+ (settings.border.top? ? 1 : 0) + (settings.border.bottom? ? 1 : 0)
46
+ end
47
+
48
+ def inner_border_width
49
+ settings.border.inner_vert? ? 1 : 0
50
+ end
51
+
52
+ def inner_border_height
53
+ settings.border.inner_horiz? ? 1 : 0
54
+ end
55
+
56
+ def padding_width
57
+ settings.padding.left + settings.padding.right
58
+ end
59
+
60
+ def padding_height
61
+ settings.padding.top + settings.padding.bottom
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,116 @@
1
+ require 'whirled_peas/utils/formatted_string'
2
+
3
+ require_relative 'container_coords'
4
+ require_relative 'painter'
5
+
6
+ module WhirledPeas
7
+ module Graphics
8
+ class ContainerPainter < Painter
9
+ PADDING = ' '
10
+
11
+ def initialize(element, settings, name)
12
+ super
13
+ @children = []
14
+ end
15
+
16
+ def paint(canvas, &block)
17
+ return unless canvas.writable?
18
+ has_inner_vert = dimensions.num_cols > 1 && settings.border.inner_vert?
19
+ has_inner_horiz = dimensions.num_rows > 1 && settings.border.inner_horiz?
20
+ has_border = settings.border.outer? || has_inner_vert || has_inner_horiz
21
+ return unless settings.bg_color || has_border
22
+ stroke_left = coords(canvas).border_left
23
+ stroke_top = coords(canvas).border_top
24
+ formatting = [*settings.border.color, *settings.bg_color]
25
+ if settings.border.top?
26
+ canvas.stroke(stroke_left, stroke_top, top_border_stroke, formatting, &block)
27
+ stroke_top += 1
28
+ end
29
+ content_line = content_line_stroke
30
+ middle_border = dimensions.num_rows > 1 ? middle_border_stroke : ''
31
+ dimensions.num_rows.times do |row_num|
32
+ if row_num > 0 && settings.border.inner_horiz?
33
+ canvas.stroke(stroke_left, stroke_top, middle_border, formatting, &block)
34
+ stroke_top += 1
35
+ end
36
+ coords(canvas).grid_height(false).times do
37
+ canvas.stroke(stroke_left, stroke_top, content_line, formatting, &block)
38
+ stroke_top += 1
39
+ end
40
+ end
41
+ if settings.border.bottom?
42
+ canvas.stroke(stroke_left, stroke_top, bottom_border_stroke, formatting, &block)
43
+ stroke_top += 1
44
+ end
45
+ end
46
+
47
+ def add_child(child)
48
+ children << child
49
+ end
50
+
51
+ def num_children
52
+ children.length
53
+ end
54
+
55
+ def each_child(&block)
56
+ children.each(&block)
57
+ end
58
+
59
+ private
60
+
61
+ attr_reader :children
62
+
63
+ def coords(canvas)
64
+ ContainerCoords.new(canvas, dimensions, settings)
65
+ end
66
+
67
+ def line_stroke(left_border, horiz_border, junc_border, right_border)
68
+ stroke = ''
69
+ stroke += left_border if settings.border.left?
70
+ dimensions.num_cols.times do |col_num|
71
+ stroke += junc_border if col_num > 0 && settings.border.inner_vert?
72
+ stroke += horiz_border * (dimensions.content_width + settings.padding.left + settings.padding.right)
73
+ end
74
+ stroke += right_border if settings.border.right?
75
+ stroke
76
+ end
77
+
78
+ def top_border_stroke
79
+ line_stroke(
80
+ settings.border.style.top_left,
81
+ settings.border.style.top_horiz,
82
+ settings.border.style.top_junc,
83
+ settings.border.style.top_right
84
+ )
85
+ end
86
+
87
+ def content_line_stroke
88
+ line_stroke(
89
+ settings.border.style.left_vert,
90
+ PADDING,
91
+ settings.border.style.middle_vert,
92
+ settings.border.style.right_vert
93
+ )
94
+ end
95
+
96
+ def middle_border_stroke
97
+ line_stroke(
98
+ settings.border.style.left_junc,
99
+ settings.border.style.middle_horiz,
100
+ settings.border.style.cross_junc,
101
+ settings.border.style.right_junc
102
+ )
103
+ end
104
+
105
+ def bottom_border_stroke
106
+ line_stroke(
107
+ settings.border.style.bottom_left,
108
+ settings.border.style.bottom_horiz,
109
+ settings.border.style.bottom_junc,
110
+ settings.border.style.bottom_right
111
+ )
112
+ end
113
+ end
114
+ private_constant :ContainerPainter
115
+ end
116
+ end
@@ -0,0 +1,43 @@
1
+ require 'whirled_peas/settings/debugger'
2
+
3
+ require_relative 'container_painter'
4
+ require_relative 'text_painter'
5
+
6
+ module WhirledPeas
7
+ module Graphics
8
+ class Debugger
9
+ def initialize(painter)
10
+ @painter = painter
11
+ end
12
+
13
+ def debug(indent='')
14
+ info = [
15
+ "#{indent}* #{painter.class}(#{painter.name})",
16
+ ]
17
+ info << "#{indent + ' '}- Dimensions(#{dimensions})"
18
+ info << "#{indent + ' '}- Settings"
19
+ info << Settings::Debugger.new(painter.settings).debug(indent + ' ')
20
+ if painter.is_a?(ContainerPainter)
21
+ info << "#{indent + ' '}- Children"
22
+ info += painter.each_child.map { |c| Debugger.new(c).debug(indent + ' ') }
23
+ end
24
+ info.join("\n")
25
+ end
26
+
27
+ private
28
+
29
+ attr_reader :painter
30
+
31
+ def dimensions
32
+ outer = "#{painter.dimensions.outer_width}x#{painter.dimensions.outer_height}"
33
+ if painter.is_a?(ContainerPainter)
34
+ content = "#{painter.dimensions.content_width}x#{painter.dimensions.content_height}"
35
+ grid = "#{painter.dimensions.num_cols}x#{painter.dimensions.num_rows}"
36
+ "outer=#{outer}, content=#{content}, grid=#{grid}"
37
+ else
38
+ "outer=#{outer}"
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,56 @@
1
+ require_relative 'container_dimensions'
2
+ require_relative 'container_painter'
3
+
4
+ module WhirledPeas
5
+ module Graphics
6
+ class GridPainter < ContainerPainter
7
+ def paint(canvas, &block)
8
+ super
9
+ return unless canvas.writable?
10
+ each_child.with_index do |child, index|
11
+ col_index = index % dimensions.num_cols
12
+ row_index = index / dimensions.num_cols
13
+ content_left = coords(canvas).content_left(col_index)
14
+ if settings.align_center?
15
+ content_left += (dimensions.content_width - child.dimensions.outer_width) / 2
16
+ elsif settings.align_right?
17
+ content_left += dimensions.content_width - child.dimensions.outer_width
18
+ end
19
+ child_canvas = canvas.child(
20
+ content_left,
21
+ coords(canvas).content_top(row_index),
22
+ [dimensions.content_width, child.dimensions.outer_width].min,
23
+ [dimensions.content_height, child.dimensions.outer_height].min
24
+ )
25
+ child.paint(child_canvas, &block)
26
+ end
27
+ end
28
+
29
+ def dimensions
30
+ @dimensions ||= begin
31
+ if settings.num_cols.nil? || settings.num_cols == 0
32
+ raise SettingsError, "`num_cols` must be set for GridSettings(#{name})"
33
+ end
34
+ num_cols = settings.num_cols
35
+ num_rows = (num_children.to_f / num_cols).ceil
36
+ content_width = 0
37
+ each_child do |child|
38
+ if child.dimensions.outer_width > content_width
39
+ content_width = child.dimensions.outer_width
40
+ end
41
+ end
42
+ content_height = 0
43
+ each_child do |child|
44
+ if child.dimensions.outer_height > content_height
45
+ content_height = child.dimensions.outer_height
46
+ end
47
+ end
48
+ ContainerDimensions.new(
49
+ settings, content_width, content_height, num_cols, num_rows
50
+ )
51
+ end
52
+ end
53
+ end
54
+ private_constant :BoxPainter
55
+ end
56
+ end