shoes-core 4.0.0.pre3

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