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 @@
1
+ require 'shoes/dsl'
@@ -0,0 +1,5 @@
1
+ class Shoes
2
+ module Core
3
+ VERSION = "4.0.0.pre3"
4
+ end
5
+ end
@@ -0,0 +1,56 @@
1
+ class Shoes
2
+ class Dialog
3
+ def initialize
4
+ @gui = Shoes.backend::Dialog.new
5
+ end
6
+
7
+ def alert(msg = '')
8
+ @gui.alert msg
9
+ end
10
+
11
+ def confirm(msg = '')
12
+ @gui.confirm msg
13
+ end
14
+
15
+ def dialog_chooser(title, folder = false)
16
+ @gui.dialog_chooser title, folder
17
+ end
18
+
19
+ def ask(msg, args)
20
+ ask_me = Shoes.app(title: args[:title] || "Shoes asks:",
21
+ width: 300, height: 125,
22
+ modal: true) do
23
+ stack do
24
+ para msg, margin: 10
25
+
26
+ @e = edit_line margin_left: 10,
27
+ width: width - 20,
28
+ secret: args[:secret]
29
+
30
+ flow margin_top: 10 do
31
+ button "OK", margin_left: 150 do
32
+ @result = @e.text
33
+ quit
34
+ end
35
+
36
+ button "Cancel" do
37
+ @result = nil
38
+ quit
39
+ end
40
+ end
41
+ end
42
+
43
+ def result
44
+ @result
45
+ end
46
+ end
47
+
48
+ ask_me.wait_until_closed
49
+ ask_me.result
50
+ end
51
+
52
+ def ask_color(title)
53
+ @gui.ask_color title
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,269 @@
1
+ class Shoes
2
+ class Dimension
3
+ attr_reader :parent
4
+ attr_accessor :absolute_start
5
+ protected :parent # we shall not mess with parent,see #495
6
+
7
+ # in case you wonder about the -1... it is used to adjust the right and
8
+ # bottom values. Because right is not left + width but rather left + width -1
9
+ # Let me give you an example:
10
+ # Say left is 20 and we have a width of 100 then the right must be 119,
11
+ # because you have to take pixel number 20 into account so 20..119 is 100
12
+ # while 20..120 is 101. E.g.:
13
+ # (20..119).size => 100
14
+ PIXEL_COUNTING_ADJUSTMENT = -1
15
+
16
+ def initialize(parent = nil, start_as_center = false)
17
+ @parent = parent
18
+ @start_as_center = start_as_center
19
+ end
20
+
21
+ def start
22
+ value = basic_start_value
23
+ value = adjust_start_for_center(value) if start_as_center?
24
+ value
25
+ end
26
+
27
+ def end
28
+ @end || report_relative_to_parent_end
29
+ end
30
+
31
+ def extent
32
+ result = @extent
33
+ if @parent
34
+ result = calculate_relative(result) if is_relative?(result)
35
+ result = calculate_negative(result) if is_negative?(result)
36
+ end
37
+ result
38
+ end
39
+
40
+ def extent=(value)
41
+ @extent = value
42
+ @extent = parse_from_string @extent if is_string? @extent
43
+ @extent
44
+ end
45
+
46
+ def absolute_end
47
+ return absolute_start if extent.nil? || absolute_start.nil?
48
+ absolute_start + extent + PIXEL_COUNTING_ADJUSTMENT
49
+ end
50
+
51
+ def element_extent
52
+ my_extent = extent
53
+ if my_extent.nil?
54
+ nil
55
+ else
56
+ extent - (margin_start + margin_end)
57
+ end
58
+ end
59
+
60
+ def element_extent=(value)
61
+ self.extent = if value.nil?
62
+ nil
63
+ else
64
+ margin_start + value + margin_end
65
+ end
66
+ end
67
+
68
+ def element_start
69
+ return nil if absolute_start.nil?
70
+ absolute_start + margin_start + displace_start
71
+ end
72
+
73
+ def element_end
74
+ return nil if element_start.nil? || element_extent.nil?
75
+ element_start + element_extent + PIXEL_COUNTING_ADJUSTMENT
76
+ end
77
+
78
+ def absolute_start_position?
79
+ !@start.nil?
80
+ end
81
+
82
+ def absolute_end_position?
83
+ !@end.nil?
84
+ end
85
+
86
+ def absolute_position?
87
+ absolute_start_position? || absolute_end_position?
88
+ end
89
+
90
+ def positioned?
91
+ absolute_start
92
+ end
93
+
94
+ def in_bounds?(value)
95
+ (absolute_start <= value) && (value <= absolute_end)
96
+ end
97
+
98
+ # For... reasons it is important to have the value of the instance variable
99
+ # set to nil if it's not modified and then return a default value on the
100
+ # getter... reason being that for ParentDimensions we need to be able to
101
+ # figure out if a value has been modified or if we should consult the
102
+ # parent value - see ParentDimension implementation
103
+ [:margin_start, :margin_end, :displace_start].each do |method|
104
+ define_method method do
105
+ instance_variable_name = '@' + method.to_s
106
+ value = instance_variable_get(instance_variable_name) || 0
107
+ value = calculate_relative value if is_relative? value
108
+ value
109
+ end
110
+ end
111
+
112
+ def self.define_int_parsing_writer(name)
113
+ define_method "#{name}=" do |value|
114
+ instance_variable_set("@#{name}", parse_int_value(value))
115
+ end
116
+ end
117
+
118
+ %w(start end margin_start margin_end displace_start).each do |method|
119
+ define_int_parsing_writer method
120
+ end
121
+
122
+ private
123
+
124
+ def basic_start_value
125
+ value = @start
126
+ value = calculate_relative value if is_relative?(value)
127
+ value = report_relative_to_parent_start if value.nil?
128
+ value
129
+ end
130
+
131
+ def is_relative?(result)
132
+ # as the value is relative to the parent values bigger than one don't
133
+ # make much sense and are problematic. E.g. through calculations users
134
+ # might end up with values like 5.14 meaning 5 pixel which would get
135
+ # interpreted as 514% of the parent
136
+ # Also check for existance of parent because otherwise relative
137
+ # calculation makes no sense
138
+ result.is_a?(Float) && result <= 1 && @parent
139
+ end
140
+
141
+ def calculate_relative(result)
142
+ (result * @parent.element_extent).to_i
143
+ end
144
+
145
+ def is_string?(result)
146
+ result.is_a?(String)
147
+ end
148
+
149
+ def is_negative?(result)
150
+ result && result < 0
151
+ end
152
+
153
+ def calculate_negative(result)
154
+ @parent.element_extent + result
155
+ end
156
+
157
+ PERCENT_REGEX = /(-?\d+(\.\d+)*)%/
158
+
159
+ def parse_from_string(result)
160
+ match = result.gsub(/\s+/, "").match(PERCENT_REGEX)
161
+ if match
162
+ match[1].to_f / 100.0
163
+ elsif valid_integer_string?(result)
164
+ int_from_string(result)
165
+ else
166
+ nil
167
+ end
168
+ end
169
+
170
+ def parse_int_value(input)
171
+ if input.is_a?(Integer) || input.is_a?(Float)
172
+ input
173
+ elsif valid_integer_string?(input)
174
+ int_from_string(input)
175
+ else
176
+ nil
177
+ end
178
+ end
179
+
180
+ def int_from_string(result)
181
+ (result.gsub(' ', '')).to_i
182
+ end
183
+
184
+ NUMBER_REGEX = /^-?\s*\d+/
185
+
186
+ def valid_integer_string?(input)
187
+ input.is_a?(String) && input.match(NUMBER_REGEX)
188
+ end
189
+
190
+ def report_relative_to_parent_start
191
+ if element_start && parent && parent.element_start
192
+ element_start - parent.element_start
193
+ else
194
+ nil
195
+ end
196
+ end
197
+
198
+ def report_relative_to_parent_end
199
+ if element_end && parent && parent.element_end
200
+ parent.element_end - element_end
201
+ else
202
+ nil
203
+ end
204
+ end
205
+
206
+ def start_as_center?
207
+ @start_as_center
208
+ end
209
+
210
+ def adjust_start_for_center(value)
211
+ my_extent = extent
212
+ if my_extent && my_extent > 0
213
+ value - my_extent / 2
214
+ else
215
+ nil
216
+ end
217
+ end
218
+ end
219
+
220
+ class ParentDimension < Dimension
221
+ SIMPLE_DELEGATE_METHODS = [:absolute_start, :start]
222
+
223
+ SIMPLE_DELEGATE_METHODS.each do |method|
224
+ define_method method do
225
+ if value_modified? method
226
+ super
227
+ else
228
+ parent.public_send(method)
229
+ end
230
+ end
231
+ end
232
+
233
+ def extent
234
+ [extent_in_parent, raw_extent(super)].min
235
+ end
236
+
237
+ private
238
+
239
+ # Represents the extent, bounded by the parent container's sizing
240
+ def extent_in_parent
241
+ if parent.element_end
242
+ # Why subtracting an absolute from an element dimension value? A
243
+ # diagram helped me reason out what we wanted.
244
+ #
245
+ # parent. parent. self. self. parent. parent.
246
+ # abs_start elem_start abs_start abs_end elem_end abs_end
247
+ # | margin | | | | margin |
248
+ #
249
+ # To get our extent respecting the parent's margins, it's our absolute
250
+ # start, minus parent's element end (so we don't blow past the margin)
251
+ extent_in_parent = parent.element_end - self.absolute_start - PIXEL_COUNTING_ADJUSTMENT
252
+ else
253
+ # If we hit this, then the extent in parent isn't available and will be
254
+ # ignored by the min call below
255
+ extent_in_parent = Float::INFINITY
256
+ end
257
+ end
258
+
259
+ # Represents the raw value set for extent, either on element or on parent
260
+ def raw_extent(original_value)
261
+ original_value || parent.extent
262
+ end
263
+
264
+ def value_modified?(method)
265
+ instance_variable = ('@' + method.to_s).to_sym
266
+ instance_variable_get(instance_variable)
267
+ end
268
+ end
269
+ end
@@ -0,0 +1,203 @@
1
+ # Dimensions is a central class that most Shoes classes use to represent their
2
+ # dimensions, e.g. where they are and how much space they are taking up there.
3
+ # All the different position types might be confusing. So here is a little list:
4
+ #
5
+ # Position (left, top, right, bottom)
6
+ # plain (left, top, right, bottom)
7
+ # An offset relative to the parent (parents mostly are slots e.g.
8
+ # flows/stacks), e.g it isn't fully positioned/doesn't flow anymore when set
9
+ #
10
+ # absolute (absolute_left, absolute_top, absolute_right, absolute_bottom)
11
+ # The absolute position of an element in the app, set by positioning code (in
12
+ # slot.rb). Might not be the beginning of the element as it also takes margins
13
+ # into account, so it could be the beginning of the margin. Is also used in
14
+ # the positioning code.
15
+ #
16
+ # element_* (element_left, element_top, element_right, element_bottom)
17
+ # Derived from absolute_* but shows the real position of the object, e.g. it
18
+ # adds the margins to absolute_* (mostly used by backend drawing code).
19
+ #
20
+ # Space taken up (width/height)
21
+ # plain (width, height)
22
+ # The whole space taken up by this element with margins and everything. Used
23
+ # for positioning/by the user.
24
+ #
25
+ # element_* (element_width, element_height)
26
+ # Just the space taken up by the element itself without margins.
27
+ # Used by drawing.
28
+ #
29
+ # Note that this is NOT how margins work in the CSS box model. We diverge for
30
+ # reasons mentioned in this comment/thread:
31
+ # https://github.com/shoes/shoes4/pull/467#issuecomment-27655355
32
+
33
+ class Shoes
34
+ class Dimensions
35
+ extend RenamedDelegate
36
+ include Common::Inspect
37
+
38
+ attr_writer :width, :height, :margin_left, :margin_right, :margin_top,
39
+ :margin_bottom, :top, :left, :right, :bottom
40
+ attr_reader :parent, :x_dimension, :y_dimension
41
+ attr_accessor :absolute_left, :absolute_top,
42
+ :displace_left, :displace_top
43
+ protected :parent # we shall not mess with parent,see #495
44
+
45
+ # in case you wonder about the -1... it is used to adjust the right and
46
+ # bottom values. Because right is not left + width but rather left + width -1
47
+ # Let me give you an example:
48
+ # Say left is 20 and we have a width of 100 then the right must be 119,
49
+ # because you have to take pixel number 20 into account so 20..119 is 100
50
+ # while 20..120 is 101. E.g.:
51
+ # (20..119).size => 100
52
+ PIXEL_COUNTING_ADJUSTMENT = -1
53
+
54
+ def initialize(parent, left_or_hash = nil, top = nil, width = nil,
55
+ height = nil, opts = {})
56
+ @parent = parent
57
+ if hash_as_argument?(left_or_hash)
58
+ init_with_hash(left_or_hash)
59
+ else
60
+ init_with_arguments(left_or_hash, top, width, height, opts)
61
+ end
62
+ end
63
+
64
+ def absolute_x_position?
65
+ x_dimension.absolute_position?
66
+ end
67
+
68
+ def absolute_y_position?
69
+ y_dimension.absolute_position?
70
+ end
71
+
72
+ def absolutely_positioned?
73
+ absolute_x_position? || absolute_y_position?
74
+ end
75
+
76
+ def positioned?
77
+ x_dimension.positioned? && y_dimension.positioned?
78
+ end
79
+
80
+ def in_bounds?(x, y)
81
+ x_dimension.in_bounds?(x) && y_dimension.in_bounds?(y)
82
+ end
83
+
84
+ def margin
85
+ [margin_left, margin_top, margin_right, margin_bottom]
86
+ end
87
+
88
+ def margin=(margin)
89
+ margin = [margin, margin, margin, margin] unless margin.is_a? Array
90
+ self.margin_left, self.margin_top,
91
+ self.margin_right, self.margin_bottom = margin
92
+ end
93
+
94
+ def takes_up_space?
95
+ true
96
+ end
97
+
98
+ def inspect
99
+ nothing = '_'
100
+ super.insert(-2,
101
+ " relative:#{Point.new left, top}->#{Point.new right, bottom}" \
102
+ " absolute:#{Point.new absolute_left, absolute_top}->#{Point.new absolute_right, absolute_bottom}" \
103
+ " #{width || nothing}x#{height || nothing}")
104
+ end
105
+
106
+ def self.setup_delegations
107
+ methods_to_rename = Dimension.public_instance_methods false
108
+ renamed_delegate_to :x_dimension, methods_to_rename, 'start' => 'left',
109
+ 'end' => 'right',
110
+ 'extent' => 'width'
111
+ renamed_delegate_to :y_dimension, methods_to_rename, 'start' => 'top',
112
+ 'end' => 'bottom',
113
+ 'extent' => 'height'
114
+ end
115
+
116
+ setup_delegations
117
+
118
+ private
119
+
120
+ def hash_as_argument?(left)
121
+ left.respond_to? :fetch
122
+ end
123
+
124
+ def init_with_hash(hash)
125
+ init_with_arguments hash.fetch(:left, nil), hash.fetch(:top, nil),
126
+ hash.fetch(:width, nil), hash.fetch(:height, nil),
127
+ hash
128
+ end
129
+
130
+ def init_with_arguments(left, top, width, height, opts)
131
+ @left_top_as_center = opts.fetch(:center, false)
132
+ init_x_and_y_dimensions
133
+ general_options opts # order important for redrawing
134
+ self.displace_left = opts.fetch(:displace_left, nil)
135
+ self.displace_top = opts.fetch(:displace_top, nil)
136
+ self.left = left
137
+ self.top = top
138
+ self.width = width
139
+ self.height = height
140
+ end
141
+
142
+ def init_x_and_y_dimensions
143
+ parent_x_dimension = @parent ? @parent.x_dimension : nil
144
+ parent_y_dimension = @parent ? @parent.y_dimension : nil
145
+ @x_dimension = Dimension.new parent_x_dimension, @left_top_as_center
146
+ @y_dimension = Dimension.new parent_y_dimension, @left_top_as_center
147
+ end
148
+
149
+ def general_options(opts)
150
+ self.right = opts[:right]
151
+ self.bottom = opts[:bottom]
152
+ init_margins opts
153
+ end
154
+
155
+ def init_margins(opts)
156
+ new_opts = opts.reject { |_k, v| v.nil? }
157
+ self.margin = new_opts[:margin]
158
+ self.margin_left = new_opts.fetch(:margin_left, margin_left)
159
+ self.margin_top = new_opts.fetch(:margin_top, margin_top)
160
+ self.margin_right = new_opts.fetch(:margin_right, margin_right)
161
+ self.margin_bottom = new_opts.fetch(:margin_bottom, margin_bottom)
162
+ end
163
+ end
164
+
165
+ # for objects that do not depend on their parent (get 1.04 as real values)
166
+ class AbsoluteDimensions < Dimensions
167
+ def initialize(*args)
168
+ super(nil, *args)
169
+ end
170
+ end
171
+
172
+ # for objects that are more defined by their parents, delegates method calls
173
+ # to crucial methods to the parent if the instance variable isn't set
174
+ class ParentDimensions < Dimensions
175
+ def init_x_and_y_dimensions
176
+ @x_dimension = ParentDimension.new @parent.x_dimension,
177
+ @left_top_as_center
178
+ @y_dimension = ParentDimension.new @parent.y_dimension,
179
+ @left_top_as_center
180
+ end
181
+ end
182
+
183
+ # depends on a #dimensions method being present that returns a Dimensions object
184
+ module DimensionsDelegations
185
+ extend Forwardable
186
+
187
+ UNDELEGATED_METHODS = [:to_s]
188
+ DELEGATED_METHODS = Dimensions.public_instance_methods(false) - UNDELEGATED_METHODS
189
+
190
+ def_delegators :dimensions, *DELEGATED_METHODS
191
+
192
+ def adjust_current_position(*_)
193
+ # no-op by default for almost all elements
194
+ end
195
+ end
196
+
197
+ # depends on a #dsl method to forward to (e.g. for backend objects)
198
+ module BackendDimensionsDelegations
199
+ extend Forwardable
200
+
201
+ def_delegators :dsl, *DimensionsDelegations::DELEGATED_METHODS
202
+ end
203
+ end