shoes-core 4.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
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