shoes-dsl 4.0.0.pre2

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 (209) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG +84 -0
  3. data/Gemfile +24 -0
  4. data/Guardfile +11 -0
  5. data/LICENSE +31 -0
  6. data/README.md +201 -0
  7. data/ext/install/Rakefile +29 -0
  8. data/ext/install/shoes.bat +9 -0
  9. data/fonts/Coolvetica.ttf +0 -0
  10. data/fonts/Lacuna.ttf +0 -0
  11. data/lib/shoes/animation.rb +56 -0
  12. data/lib/shoes/app.rb +131 -0
  13. data/lib/shoes/arc.rb +25 -0
  14. data/lib/shoes/background.rb +24 -0
  15. data/lib/shoes/border.rb +24 -0
  16. data/lib/shoes/builtin_methods.rb +77 -0
  17. data/lib/shoes/button.rb +30 -0
  18. data/lib/shoes/check_button.rb +44 -0
  19. data/lib/shoes/color.rb +385 -0
  20. data/lib/shoes/common/background_element.rb +9 -0
  21. data/lib/shoes/common/changeable.rb +34 -0
  22. data/lib/shoes/common/clickable.rb +24 -0
  23. data/lib/shoes/common/inspect.rb +14 -0
  24. data/lib/shoes/common/positioning.rb +30 -0
  25. data/lib/shoes/common/registration.rb +33 -0
  26. data/lib/shoes/common/remove.rb +10 -0
  27. data/lib/shoes/common/state.rb +18 -0
  28. data/lib/shoes/common/style.rb +152 -0
  29. data/lib/shoes/common/style_normalizer.rb +16 -0
  30. data/lib/shoes/common/ui_element.rb +11 -0
  31. data/lib/shoes/common/visibility.rb +40 -0
  32. data/lib/shoes/configuration.rb +96 -0
  33. data/lib/shoes/dialog.rb +27 -0
  34. data/lib/shoes/dimension.rb +239 -0
  35. data/lib/shoes/dimensions.rb +209 -0
  36. data/lib/shoes/download.rb +121 -0
  37. data/lib/shoes/dsl.rb +591 -0
  38. data/lib/shoes/font.rb +49 -0
  39. data/lib/shoes/gradient.rb +31 -0
  40. data/lib/shoes/image.rb +29 -0
  41. data/lib/shoes/image_pattern.rb +12 -0
  42. data/lib/shoes/input_box.rb +60 -0
  43. data/lib/shoes/internal_app.rb +219 -0
  44. data/lib/shoes/key_event.rb +17 -0
  45. data/lib/shoes/line.rb +87 -0
  46. data/lib/shoes/link.rb +59 -0
  47. data/lib/shoes/link_hover.rb +5 -0
  48. data/lib/shoes/list_box.rb +50 -0
  49. data/lib/shoes/logger.rb +66 -0
  50. data/lib/shoes/logger/ruby.rb +18 -0
  51. data/lib/shoes/mock.rb +31 -0
  52. data/lib/shoes/mock/animation.rb +8 -0
  53. data/lib/shoes/mock/app.rb +47 -0
  54. data/lib/shoes/mock/arc.rb +9 -0
  55. data/lib/shoes/mock/background.rb +10 -0
  56. data/lib/shoes/mock/border.rb +7 -0
  57. data/lib/shoes/mock/button.rb +10 -0
  58. data/lib/shoes/mock/check.rb +25 -0
  59. data/lib/shoes/mock/clickable.rb +8 -0
  60. data/lib/shoes/mock/common_methods.rb +12 -0
  61. data/lib/shoes/mock/dialog.rb +13 -0
  62. data/lib/shoes/mock/download.rb +18 -0
  63. data/lib/shoes/mock/font.rb +17 -0
  64. data/lib/shoes/mock/image.rb +13 -0
  65. data/lib/shoes/mock/image_pattern.rb +9 -0
  66. data/lib/shoes/mock/input_box.rb +30 -0
  67. data/lib/shoes/mock/keypress.rb +10 -0
  68. data/lib/shoes/mock/keyrelease.rb +10 -0
  69. data/lib/shoes/mock/line.rb +14 -0
  70. data/lib/shoes/mock/link.rb +12 -0
  71. data/lib/shoes/mock/list_box.rb +19 -0
  72. data/lib/shoes/mock/oval.rb +12 -0
  73. data/lib/shoes/mock/progress.rb +10 -0
  74. data/lib/shoes/mock/radio.rb +27 -0
  75. data/lib/shoes/mock/rect.rb +14 -0
  76. data/lib/shoes/mock/shape.rb +20 -0
  77. data/lib/shoes/mock/slot.rb +16 -0
  78. data/lib/shoes/mock/sound.rb +8 -0
  79. data/lib/shoes/mock/star.rb +14 -0
  80. data/lib/shoes/mock/text_block.rb +36 -0
  81. data/lib/shoes/mock/timer.rb +8 -0
  82. data/lib/shoes/not_implemented_error.rb +4 -0
  83. data/lib/shoes/oval.rb +20 -0
  84. data/lib/shoes/point.rb +54 -0
  85. data/lib/shoes/progress.rb +25 -0
  86. data/lib/shoes/radio.rb +16 -0
  87. data/lib/shoes/rect.rb +21 -0
  88. data/lib/shoes/renamed_delegate.rb +15 -0
  89. data/lib/shoes/shape.rb +158 -0
  90. data/lib/shoes/slot.rb +271 -0
  91. data/lib/shoes/slot_contents.rb +50 -0
  92. data/lib/shoes/sound.rb +18 -0
  93. data/lib/shoes/span.rb +16 -0
  94. data/lib/shoes/star.rb +45 -0
  95. data/lib/shoes/text.rb +24 -0
  96. data/lib/shoes/text_block.rb +143 -0
  97. data/lib/shoes/text_block_dimensions.rb +52 -0
  98. data/lib/shoes/timer.rb +12 -0
  99. data/lib/shoes/url.rb +44 -0
  100. data/lib/shoes/version.rb +3 -0
  101. data/lib/shoes/widget.rb +69 -0
  102. data/manifests/common.rb +34 -0
  103. data/manifests/shoes-dsl.rb +34 -0
  104. data/shoes-dsl.gemspec +19 -0
  105. data/spec/code_coverage.rb +14 -0
  106. data/spec/shoes/animation_spec.rb +65 -0
  107. data/spec/shoes/app_spec.rb +484 -0
  108. data/spec/shoes/arc_spec.rb +51 -0
  109. data/spec/shoes/background_spec.rb +53 -0
  110. data/spec/shoes/border_spec.rb +47 -0
  111. data/spec/shoes/builtin_methods_spec.rb +110 -0
  112. data/spec/shoes/button_spec.rb +44 -0
  113. data/spec/shoes/check_spec.rb +35 -0
  114. data/spec/shoes/cli_spec.rb +15 -0
  115. data/spec/shoes/color_spec.rb +408 -0
  116. data/spec/shoes/common/inspect_spec.rb +26 -0
  117. data/spec/shoes/common/remove_spec.rb +38 -0
  118. data/spec/shoes/common/style_normalizer_spec.rb +28 -0
  119. data/spec/shoes/common/style_spec.rb +147 -0
  120. data/spec/shoes/configuration_spec.rb +36 -0
  121. data/spec/shoes/constants_spec.rb +38 -0
  122. data/spec/shoes/dialog_spec.rb +163 -0
  123. data/spec/shoes/dimension_spec.rb +407 -0
  124. data/spec/shoes/dimensions_spec.rb +837 -0
  125. data/spec/shoes/download_spec.rb +142 -0
  126. data/spec/shoes/flow_spec.rb +133 -0
  127. data/spec/shoes/font_spec.rb +37 -0
  128. data/spec/shoes/framework_learning_spec.rb +30 -0
  129. data/spec/shoes/gradient_spec.rb +32 -0
  130. data/spec/shoes/helpers/fake_element.rb +17 -0
  131. data/spec/shoes/helpers/inspect_helpers.rb +5 -0
  132. data/spec/shoes/helpers/sample17_helper.rb +66 -0
  133. data/spec/shoes/image_spec.rb +49 -0
  134. data/spec/shoes/images/shoe.jpg +0 -0
  135. data/spec/shoes/input_box_spec.rb +80 -0
  136. data/spec/shoes/integration_spec.rb +20 -0
  137. data/spec/shoes/internal_app_spec.rb +141 -0
  138. data/spec/shoes/keypress_spec.rb +11 -0
  139. data/spec/shoes/keyrelease_spec.rb +12 -0
  140. data/spec/shoes/line_spec.rb +49 -0
  141. data/spec/shoes/link_spec.rb +105 -0
  142. data/spec/shoes/list_box_spec.rb +74 -0
  143. data/spec/shoes/logger/ruby_spec.rb +8 -0
  144. data/spec/shoes/logger_spec.rb +45 -0
  145. data/spec/shoes/oval_spec.rb +24 -0
  146. data/spec/shoes/point_spec.rb +71 -0
  147. data/spec/shoes/progress_spec.rb +54 -0
  148. data/spec/shoes/radio_spec.rb +32 -0
  149. data/spec/shoes/rect_spec.rb +39 -0
  150. data/spec/shoes/renamed_delegate_spec.rb +70 -0
  151. data/spec/shoes/shape_spec.rb +95 -0
  152. data/spec/shoes/shared_examples/button.rb +6 -0
  153. data/spec/shoes/shared_examples/changeable.rb +26 -0
  154. data/spec/shoes/shared_examples/clickable.rb +5 -0
  155. data/spec/shoes/shared_examples/common_methods.rb +35 -0
  156. data/spec/shoes/shared_examples/dimensions.rb +32 -0
  157. data/spec/shoes/shared_examples/dsl.rb +44 -0
  158. data/spec/shoes/shared_examples/dsl/animate.rb +29 -0
  159. data/spec/shoes/shared_examples/dsl/arc.rb +45 -0
  160. data/spec/shoes/shared_examples/dsl/background.rb +26 -0
  161. data/spec/shoes/shared_examples/dsl/border.rb +10 -0
  162. data/spec/shoes/shared_examples/dsl/button.rb +5 -0
  163. data/spec/shoes/shared_examples/dsl/cap.rb +6 -0
  164. data/spec/shoes/shared_examples/dsl/check.rb +11 -0
  165. data/spec/shoes/shared_examples/dsl/edit_box.rb +8 -0
  166. data/spec/shoes/shared_examples/dsl/edit_line.rb +8 -0
  167. data/spec/shoes/shared_examples/dsl/editable_element.rb +29 -0
  168. data/spec/shoes/shared_examples/dsl/fill.rb +27 -0
  169. data/spec/shoes/shared_examples/dsl/flow.rb +15 -0
  170. data/spec/shoes/shared_examples/dsl/gradient.rb +62 -0
  171. data/spec/shoes/shared_examples/dsl/image.rb +21 -0
  172. data/spec/shoes/shared_examples/dsl/line.rb +9 -0
  173. data/spec/shoes/shared_examples/dsl/nofill.rb +6 -0
  174. data/spec/shoes/shared_examples/dsl/nostroke.rb +6 -0
  175. data/spec/shoes/shared_examples/dsl/oval.rb +88 -0
  176. data/spec/shoes/shared_examples/dsl/pattern.rb +34 -0
  177. data/spec/shoes/shared_examples/dsl/progress.rb +7 -0
  178. data/spec/shoes/shared_examples/dsl/rect.rb +92 -0
  179. data/spec/shoes/shared_examples/dsl/rgb.rb +26 -0
  180. data/spec/shoes/shared_examples/dsl/shape.rb +21 -0
  181. data/spec/shoes/shared_examples/dsl/star.rb +48 -0
  182. data/spec/shoes/shared_examples/dsl/stroke.rb +30 -0
  183. data/spec/shoes/shared_examples/dsl/strokewidth.rb +19 -0
  184. data/spec/shoes/shared_examples/dsl/style.rb +32 -0
  185. data/spec/shoes/shared_examples/dsl/text_elements.rb +81 -0
  186. data/spec/shoes/shared_examples/dsl/video.rb +5 -0
  187. data/spec/shoes/shared_examples/dsl_app_context.rb +8 -0
  188. data/spec/shoes/shared_examples/hover_leave.rb +11 -0
  189. data/spec/shoes/shared_examples/parent.rb +6 -0
  190. data/spec/shoes/shared_examples/scroll.rb +41 -0
  191. data/spec/shoes/shared_examples/shared_element_method.rb +60 -0
  192. data/spec/shoes/shared_examples/slot.rb +331 -0
  193. data/spec/shoes/shared_examples/state.rb +19 -0
  194. data/spec/shoes/shared_examples/style.rb +82 -0
  195. data/spec/shoes/slot_spec.rb +130 -0
  196. data/spec/shoes/sound_spec.rb +15 -0
  197. data/spec/shoes/span_spec.rb +112 -0
  198. data/spec/shoes/spec_helper.rb +24 -0
  199. data/spec/shoes/stack_spec.rb +79 -0
  200. data/spec/shoes/star_spec.rb +31 -0
  201. data/spec/shoes/text_block_dimensions_spec.rb +75 -0
  202. data/spec/shoes/text_block_spec.rb +270 -0
  203. data/spec/shoes/url_spec.rb +68 -0
  204. data/spec/shoes/widget_spec.rb +70 -0
  205. data/spec/shoes_spec.rb +44 -0
  206. data/spec/spec_helper.rb +18 -0
  207. data/static/Shoes.icns +0 -0
  208. data/static/shoes-icon.png +0 -0
  209. metadata +354 -0
@@ -0,0 +1,15 @@
1
+ module RenamedDelegate
2
+ include Forwardable
3
+
4
+ def renamed_delegate_to(getter, methods, renamings)
5
+ methods.each do |method|
6
+ method_name = method.to_s
7
+ renamed_method_name = renamings.inject(method_name) do |name, (word, sub)|
8
+ name.gsub word, sub
9
+ end
10
+ if renamed_method_name != method_name
11
+ def_delegator getter, method_name, renamed_method_name
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,158 @@
1
+ class Shoes
2
+ class Shape
3
+ include Common::UIElement
4
+ include Common::Style
5
+ include Common::Clickable
6
+
7
+ attr_reader :app, :parent, :dimensions, :gui, :blk, :x, :y
8
+ attr_reader :left_bound, :top_bound, :right_bound, :bottom_bound
9
+ style_with :art_styles, :center, :common_styles, :dimensions
10
+
11
+ # Creates a new Shoes::Shape
12
+ #
13
+ def initialize(app, parent, styles = {}, blk = nil)
14
+ @app = app
15
+ @parent = parent
16
+ style_init styles
17
+ @dimensions = AbsoluteDimensions.new @style
18
+ @parent.add_child self
19
+ @gui = Shoes.backend_for self
20
+ register_click
21
+
22
+ @blk = blk
23
+ # True until we've asked the pen to draw
24
+ @before_drawing = true
25
+ @app.eval_with_additional_context self, &blk
26
+ end
27
+
28
+ def width
29
+ @app.width
30
+ end
31
+
32
+ def height
33
+ @app.height
34
+ end
35
+
36
+ # Moves the shape
37
+ #
38
+ # @param [Integer] left The new left value
39
+ # @param [Integer] top The new top value
40
+ # @return [Shoes::Shape] This shape
41
+ def move(left, top)
42
+ self.left = left
43
+ self.top = top
44
+ @gui.update_position
45
+ self
46
+ end
47
+
48
+ # Draws a line from the current position to the given point
49
+ #
50
+ # @param [Integer] x The new point's x-value
51
+ # @param [Integer] y The new point's y-value
52
+ # @return [Shoes::Shape] This shape
53
+ def line_to(x, y)
54
+ update_bounds_rect(@x, @y, x, y)
55
+ @x, @y = x, y
56
+ @gui.line_to(x, y)
57
+ self
58
+ end
59
+
60
+ # Moves the drawing "pen" to the given point
61
+ #
62
+ # @param [Integer] x The new point's x-value
63
+ # @param [Integer] y The new point's y-value
64
+ # @return [Shoes::Shape] self This shape
65
+ def move_to(x, y)
66
+ @x, @y = x, y
67
+ @gui.move_to(x, y)
68
+ self
69
+ end
70
+
71
+ # Draws a curve
72
+ #
73
+ # @param [Integer] cx1 The first control point's x-value
74
+ # @param [Integer] cy1 The first control point's y-value
75
+ # @param [Integer] cx2 The second control point's x-value
76
+ # @param [Integer] cy2 The second control point's y-value
77
+ # @param [Integer] x The end point's x-value
78
+ # @param [Integer] y The end point's y-value
79
+ # @return [Shoes::Shape] This shape
80
+ def curve_to(cx1, cy1, cx2, cy2, x, y)
81
+ update_bounds([@x, cx1, cx2, x], [@y, cy1, cy2, y])
82
+ @x, @y = x, y
83
+ @gui.curve_to(cx1, cy1, cx2, cy2, x, y)
84
+ self
85
+ end
86
+
87
+ # Draws an arc
88
+ #
89
+ # @param [Integer] x The left position
90
+ # @param [Integer] y The top position
91
+ # @param [Integer] width The width of the arc
92
+ # @param [Integer] height The height of the arc
93
+ # @param [Integer] start_angle The start angle
94
+ # @param [Integer] arc_angle The angular extent of the arc, relative to the start angle
95
+ # @return [Shoes::Shape] This shape
96
+ def arc(x, y, width, height, start_angle, arc_angle)
97
+ update_bounds_rect(x-width/2, y-height/2, x+width/2, y+height/2)
98
+ @x, @y = x, y
99
+ @gui.arc(x, y, width, height, start_angle, arc_angle)
100
+ self
101
+ end
102
+
103
+ private
104
+ # Updates the bounds of this shape to include the rectangle described by
105
+ # (x1, y1) and (x2, y2)
106
+ #
107
+ # @param [Integer] x1 The x-value of the first coordinate
108
+ # @param [Integer] y1 The y-value of the first coordinate
109
+ # @param [Integer] x2 The x-value of the second coordinate
110
+ # @param [Integer] y2 The y-value of the second coordinate
111
+ # @return nil
112
+ def update_bounds_rect(x1, y1, x2, y2)
113
+ update_bounds( [x1, x2], [y1, y2] )
114
+ end
115
+
116
+ # Updates the bounds of this shape to the rectangle covering all
117
+ # the given coordinates.
118
+ #
119
+ # @param [Array<Integer>] xs Array of X coordinates
120
+ # @param [Array<Integer>] ys Array of Y coordinates
121
+ # @return nil
122
+ def update_bounds(xs, ys)
123
+ all_xs = all_x_values(xs)
124
+ all_ys = all_y_values(ys)
125
+ @left_bound = calculate_primary_dimension_value @left_bound, all_xs
126
+ @top_bound = calculate_primary_dimension_value @top_bound, all_ys
127
+ @right_bound = calculate_secondary_dimension_value @right_bound, all_xs
128
+ @bottom_bound = calculate_secondary_dimension_value @bottom_bound, all_ys
129
+ @before_drawing = false
130
+ nil
131
+ end
132
+
133
+ def all_x_values(xs)
134
+ all_values xs, @x, @left_bound, @right_bound
135
+ end
136
+
137
+ def all_y_values(ys)
138
+ all_values ys, @y, @top_bound, @bottom_bound
139
+ end
140
+
141
+ def all_values(values, current, min, max)
142
+ additional = @before_drawing ? [current] : [current, min, max]
143
+ (values + additional).map &:to_i
144
+ end
145
+
146
+ def calculate_primary_dimension_value(current, new)
147
+ new_min = Array(new).min
148
+ return new_min if @before_drawing || new_min < current
149
+ current
150
+ end
151
+
152
+ def calculate_secondary_dimension_value(current, new)
153
+ new_max = Array(new).max
154
+ return new_max if @before_drawing || new_max > current
155
+ current
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,271 @@
1
+ class Shoes
2
+ class Slot
3
+ include Common::UIElement
4
+ include Common::Clickable
5
+ include Common::Style
6
+
7
+ # We need that offset because otherwise element overlap e.g. occupy
8
+ # the same pixel - this way they start right next to each other
9
+ # See #update_current_position
10
+ NEXT_ELEMENT_OFFSET = 1
11
+
12
+ attr_reader :parent, :dimensions, :gui, :contents, :blk, :hover_proc, :leave_proc
13
+ style_with :art_styles, :attach, :common_styles, :dimensions, :scroll
14
+ STYLES = {scroll: false}
15
+
16
+ def initialize(app, parent, styles = {}, blk = nil)
17
+ init_attributes(app, parent, styles, blk)
18
+ @parent.add_child self
19
+ @gui = Shoes.configuration.backend_for self, @parent.gui
20
+ eval_block blk
21
+ contents_alignment
22
+ end
23
+
24
+ def init_attributes(app, parent, styles, blk)
25
+ @app = app
26
+ @parent = parent
27
+ @contents = SlotContents.new
28
+ @blk = blk
29
+ style_init styles
30
+ @dimensions = Dimensions.new parent, @style
31
+ @fixed_height = height || false
32
+ @scroll_top = 0
33
+ set_default_dimension_values
34
+ @pass_coordinates = true
35
+ end
36
+
37
+ def set_default_dimension_values
38
+ self.width ||= 1.0
39
+ self.height ||= 0
40
+ self.absolute_left ||= 0
41
+ self.absolute_top ||= 0
42
+ end
43
+
44
+ def clear(&blk)
45
+ contents.clear
46
+ eval_block blk
47
+ end
48
+
49
+ def eval_block blk
50
+ old_current_slot = @app.current_slot
51
+ @app.current_slot = self
52
+ blk.call if blk
53
+ @app.current_slot = old_current_slot
54
+ end
55
+
56
+ def add_child(element)
57
+ contents.add_element element
58
+ end
59
+
60
+ def remove_child(element)
61
+ contents.delete element
62
+ end
63
+
64
+ def append(&blk)
65
+ eval_block blk
66
+ end
67
+
68
+ def prepend(&blk)
69
+ contents.prepend do
70
+ eval_block blk
71
+ end
72
+ end
73
+
74
+ def contents_alignment(_=nil)
75
+ position_contents
76
+ determine_slot_height
77
+ end
78
+
79
+ def hovered?
80
+ @hovered
81
+ end
82
+
83
+ def hover(blk)
84
+ @hover_proc = blk
85
+ @app.add_mouse_hover_control self
86
+ end
87
+
88
+ def leave(blk)
89
+ @leave_proc = blk
90
+ @app.add_mouse_hover_control self
91
+ end
92
+
93
+ def mouse_hovered
94
+ @hovered = true
95
+ @hover_proc.call(self) if @hover_proc
96
+ end
97
+
98
+ def mouse_left
99
+ @hovered = false
100
+ @leave_proc.call(self) if @leave_proc
101
+ end
102
+
103
+ def scroll_height
104
+ position_contents.y
105
+ end
106
+
107
+ def scroll_max
108
+ scroll_height - height
109
+ end
110
+
111
+ def scroll_top
112
+ @scroll_top
113
+ end
114
+
115
+ def scroll_top=(position)
116
+ @scroll_top = position
117
+ end
118
+
119
+ def app
120
+ @app.app #return the Shoes::App not the internal app
121
+ end
122
+
123
+ def inspect
124
+ "#<#{self.class}:0x#{hash.to_s(16)} @contents=#{@contents.inspect} and so much stuff literally breaks the memory limit. Look at it selectively.>"
125
+ end
126
+
127
+ protected
128
+ CurrentPosition = Struct.new(:x, :y, :next_line_start)
129
+
130
+ def position_contents
131
+ current_position = CurrentPosition.new element_left,
132
+ element_top,
133
+ element_top
134
+
135
+ contents.each do |element|
136
+ next if element.hidden?
137
+ current_position = positioning(element, current_position)
138
+ end
139
+ current_position
140
+ end
141
+
142
+ def positioning(element, current_position)
143
+ return current_position unless element.needs_to_be_positioned?
144
+ position_element element, current_position
145
+ element.contents_alignment(current_position) if element.respond_to? :contents_alignment
146
+ if element.takes_up_space?
147
+ update_current_position(current_position, element)
148
+ else
149
+ current_position
150
+ end
151
+ end
152
+
153
+ def position_element(element, current_position)
154
+ raise 'position_element is a subclass responsibility'
155
+ end
156
+
157
+ def position_in_current_line(element, current_position)
158
+ position_element_at element,
159
+ position_x(current_position.x, element),
160
+ position_y(current_position.y, element)
161
+ end
162
+
163
+ def move_to_next_line(element, current_position)
164
+ position_element_at element,
165
+ position_x(self.element_left, element),
166
+ position_y(current_position.next_line_start, element)
167
+ end
168
+
169
+ def position_element_at(element, x, y)
170
+ return if element_did_not_move?(element, x, y)
171
+ element._position x, y
172
+ end
173
+
174
+ def element_did_not_move?(element, x, y)
175
+ element.absolute_left == x && element.absolute_top == y
176
+ end
177
+
178
+ def update_current_position(current_position, element)
179
+ return current_position if element.absolutely_positioned?
180
+ current_position.x = element.absolute_right + NEXT_ELEMENT_OFFSET
181
+ current_position.y = element.absolute_top
182
+ next_element_line_start = next_line_start_from element
183
+ if current_position.next_line_start < next_element_line_start
184
+ current_position.next_line_start = next_element_line_start
185
+ end
186
+ element.adjust_current_position current_position
187
+ current_position
188
+ end
189
+
190
+ def next_line_start_from element
191
+ element.absolute_bottom + NEXT_ELEMENT_OFFSET
192
+ end
193
+
194
+ def position_x(relative_x, element)
195
+ if element.absolute_x_position?
196
+ absolute_x_position(element)
197
+ else
198
+ relative_x
199
+ end
200
+ end
201
+
202
+ def absolute_x_position(element)
203
+ if element.absolute_left_position?
204
+ self.absolute_left + element.left
205
+ elsif element.absolute_right_position?
206
+ self.absolute_right - (element.right + element.width)
207
+ end
208
+ end
209
+
210
+ def position_y(relative_y, element)
211
+ if element.absolute_y_position?
212
+ absolute_y_position(element)
213
+ else
214
+ relative_y
215
+ end
216
+ end
217
+
218
+ def absolute_y_position(element)
219
+ if element.absolute_top_position?
220
+ self.absolute_top + element.top
221
+ elsif element.absolute_bottom_position?
222
+ # TODO: slots grow... to really position it relative to the bottom
223
+ # we probably need to position it after everything has been positioned
224
+ self.absolute_bottom - (element.bottom + element.height)
225
+ end
226
+ end
227
+
228
+ def fits_on_the_same_line?(element, current_x)
229
+ fitting_width = element.width
230
+ fitting_width = element.fitting_width if element.respond_to?(:fitting_width)
231
+ current_x + fitting_width - 1 <= element_right
232
+ end
233
+
234
+ def determine_slot_height
235
+ content_height = compute_content_height
236
+ self.height = content_height if has_variable_height?
237
+ content_height
238
+ end
239
+
240
+ def compute_content_height
241
+ max_bottom = contents.reject(&:hidden?).
242
+ map(&:absolute_bottom).
243
+ max
244
+ if max_bottom
245
+ max_bottom - self.absolute_top + NEXT_ELEMENT_OFFSET
246
+ else
247
+ 0
248
+ end
249
+ end
250
+
251
+ def has_variable_height?
252
+ not @fixed_height
253
+ end
254
+ end
255
+
256
+ class Flow < Slot
257
+ def position_element(element, current_position)
258
+ if fits_on_the_same_line?(element, current_position.x)
259
+ position_in_current_line(element, current_position)
260
+ else
261
+ move_to_next_line(element, current_position)
262
+ end
263
+ end
264
+ end
265
+
266
+ class Stack < Slot
267
+ def position_element(element, current_position)
268
+ move_to_next_line(element, current_position)
269
+ end
270
+ end
271
+ end
@@ -0,0 +1,50 @@
1
+ class Shoes
2
+ class SlotContents < SimpleDelegator
3
+ include Common::Inspect
4
+
5
+ def initialize
6
+ @contents = []
7
+ @prepending = false
8
+ @prepending_index = 0
9
+ super(@contents)
10
+ end
11
+
12
+ def add_element(element)
13
+ if @prepending
14
+ prepend_element element
15
+ else
16
+ append_element element
17
+ end
18
+ end
19
+
20
+ def prepend(&blk)
21
+ @prepending_index = 0
22
+ @prepending = true
23
+ blk.call
24
+ @prepending = false
25
+ end
26
+
27
+ def clear
28
+ # reverse_each is important as otherwise we always miss to delete one
29
+ # element
30
+ @contents.reverse_each do |element|
31
+ element.is_a?(Shoes::Slot) ? element.clear : element.remove
32
+ end
33
+ @contents.clear
34
+ end
35
+
36
+ def inspect
37
+ super.insert(-2, " @size=#{size} @prepending=#{@prepending} @prepending_index=#{@prepending_index}")
38
+ end
39
+
40
+ private
41
+ def append_element(element)
42
+ @contents << element
43
+ end
44
+
45
+ def prepend_element(element)
46
+ @contents.insert @prepending_index, element
47
+ @prepending_index += 1
48
+ end
49
+ end
50
+ end