glimmer-dsl-opal 0.27.0 → 0.28.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +71 -3
  4. data/VERSION +1 -1
  5. data/lib/glimmer/dsl/opal/dsl.rb +3 -0
  6. data/lib/glimmer/dsl/opal/image_expression.rb +64 -0
  7. data/lib/glimmer/dsl/opal/property_expression.rb +1 -2
  8. data/lib/glimmer/dsl/opal/shape_expression.rb +35 -6
  9. data/lib/glimmer/swt/canvas_proxy.rb +27 -0
  10. data/lib/glimmer/swt/composite_proxy.rb +0 -2
  11. data/lib/glimmer/swt/custom/shape/arc.rb +75 -0
  12. data/lib/glimmer/swt/custom/shape/image.rb +98 -0
  13. data/lib/glimmer/swt/custom/shape/line.rb +53 -0
  14. data/lib/glimmer/swt/custom/shape/oval.rb +58 -0
  15. data/lib/glimmer/swt/custom/shape/point.rb +53 -0
  16. data/lib/glimmer/swt/custom/shape/polygon.rb +53 -0
  17. data/lib/glimmer/swt/custom/shape/polyline.rb +53 -0
  18. data/lib/glimmer/swt/custom/shape/rectangle.rb +101 -0
  19. data/lib/glimmer/swt/custom/shape/text.rb +114 -0
  20. data/lib/glimmer/swt/custom/shape.rb +205 -0
  21. data/lib/glimmer/swt/image_proxy.rb +29 -0
  22. data/lib/glimmer/swt/menu_proxy.rb +1 -1
  23. data/lib/glimmer/swt/point.rb +2 -0
  24. data/lib/glimmer/swt/rectangle.rb +7 -0
  25. data/lib/glimmer/swt/widget_proxy.rb +15 -4
  26. data/lib/glimmer-dsl-opal/samples/elaborate/tetris/view/block.rb +44 -0
  27. data/lib/glimmer-dsl-opal/samples/elaborate/tetris.rb +4 -8
  28. data/lib/glimmer-dsl-opal/samples/elaborate/weather.rb +4 -3
  29. data/lib/glimmer-dsl-opal/samples/hello/hello_canvas.rb +106 -0
  30. data/lib/glimmer-dsl-opal/samples/hello/hello_text.rb +1 -1
  31. data/lib/glimmer-dsl-opal/samples/hello/images/scaffold_app.png +0 -0
  32. data/lib/glimmer-dsl-opal/vendor/two.min.js +24 -0
  33. data/lib/glimmer-dsl-opal.rb +1 -0
  34. metadata +19 -2
@@ -0,0 +1,114 @@
1
+ # Copyright (c) 2020-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/swt/custom/shape'
23
+ require 'glimmer/swt/swt_proxy'
24
+ require 'glimmer/swt/display_proxy'
25
+ require 'glimmer/swt/color_proxy'
26
+ # require 'glimmer/swt/transform_proxy'
27
+
28
+ module Glimmer
29
+ module SWT
30
+ module Custom
31
+ class Shape
32
+ class Text < Shape
33
+ def parameter_names
34
+ if text_parameter_names.size == @args.size
35
+ @parameter_names = text_parameter_names
36
+ elsif text_transparent_parameter_names.size == @args.size
37
+ @parameter_names = text_transparent_parameter_names
38
+ elsif text_flags_parameter_names.size == @args.size
39
+ @parameter_names = text_flags_parameter_names
40
+ end
41
+ @parameter_names || text_parameter_names
42
+ end
43
+
44
+ def possible_parameter_names
45
+ # TODO refactor and improve this method through meta-programming (and share across other shapes)
46
+ (text_parameter_names + text_transparent_parameter_names + text_flags_parameter_names).uniq
47
+ end
48
+
49
+ def text_parameter_names
50
+ [:string, :x, :y]
51
+ end
52
+
53
+ def text_transparent_parameter_names
54
+ [:string, :x, :y, :is_transparent]
55
+ end
56
+
57
+ def text_flags_parameter_names
58
+ [:string, :x, :y, :flags]
59
+ end
60
+
61
+ def set_parameter_attribute(attribute_name, *args)
62
+ if @parameter_names.to_a.map(&:to_s).include?(attribute_name.to_s)
63
+ super(attribute_name, *args)
64
+ build_dom
65
+ reattach(dom_element)
66
+ return
67
+ end
68
+ if text_parameter_names.map(&:to_s).include?(attribute_name.to_s)
69
+ @parameter_names = text_parameter_names
70
+ elsif text_transparent_parameter_names.map(&:to_s).include?(attribute_name.to_s)
71
+ @parameter_names = text_transparent_parameter_names
72
+ elsif text_flags_parameter_names.map(&:to_s).include?(attribute_name.to_s)
73
+ @parameter_names = text_flags_parameter_names
74
+ end
75
+ super(attribute_name, *args)
76
+ end
77
+
78
+ def reattach(old_element)
79
+ old_element.attr('x', Element[@dom].attr('x'))
80
+ old_element.attr('y', Element[@dom].attr('y'))
81
+ old_element.text(Element[@dom].html)
82
+ end
83
+
84
+ def background=(value)
85
+ # TODO override background= to fill a rectangle containing text, matching its size
86
+ # For now, disable background when foreground is not set
87
+ super(value) unless foreground.nil?
88
+ end
89
+
90
+ def foreground=(value)
91
+ super(value)
92
+ self.background = value
93
+ end
94
+
95
+ def element
96
+ 'text'
97
+ end
98
+
99
+ def dom
100
+ shape_id = id
101
+ shape_class = name
102
+ @dom ||= xml {
103
+ tag(:_name => 'text', id: shape_id, class: shape_class, x: @args[1], y: @args[2]) {
104
+ @args[0]
105
+ }
106
+ }.to_s
107
+ end
108
+
109
+ end
110
+ String = Text
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,205 @@
1
+ # Copyright (c) 2007-2021 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'glimmer/swt/property_owner'
23
+ require 'glimmer/swt/swt_proxy'
24
+ require 'glimmer/swt/display_proxy'
25
+ require 'glimmer/swt/color_proxy'
26
+ require 'glimmer/swt/font_proxy'
27
+ # require 'glimmer/swt/transform_proxy'
28
+ require 'glimmer/swt/point'
29
+ require 'glimmer/swt/rectangle'
30
+
31
+ module Glimmer
32
+ module SWT
33
+ module Custom
34
+ # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
35
+ # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
36
+ class Shape < WidgetProxy
37
+ include PropertyOwner
38
+
39
+ class << self
40
+ def create(parent, keyword, args, &property_block)
41
+ potential_shape_class_name = keyword.to_s.camelcase(:upper).to_sym
42
+ if constants.include?(potential_shape_class_name)
43
+ const_get(potential_shape_class_name).new(parent, args, &property_block)
44
+ else
45
+ new(parent, args, &property_block)
46
+ end
47
+ end
48
+
49
+ def valid?(parent, keyword, args, &block)
50
+ return true if keyword.to_s == 'shape'
51
+ keywords.include?(keyword.to_s)
52
+ end
53
+
54
+ def keywords
55
+ constants.select do |constant|
56
+ constant_value = const_get(constant)
57
+ constant_value.respond_to?(:ancestors) && constant_value.ancestors.include?(Glimmer::SWT::Custom::Shape)
58
+ end.map {|constant| constant.to_s.underscore}
59
+ end
60
+
61
+ end
62
+
63
+ def background=(value)
64
+ value = ColorProxy.new(value) if value.is_a?(String) || value.is_a?(Symbol)
65
+ @background = value
66
+ dom_element.css('fill', background.to_css) unless background.nil?
67
+ end
68
+
69
+ def foreground=(value)
70
+ value = ColorProxy.new(value) if value.is_a?(String) || value.is_a?(Symbol)
71
+ @foreground = value
72
+ dom_element.css('stroke', foreground.to_css) unless foreground.nil?
73
+ dom_element.css('fill', 'transparent') if background.nil?
74
+ end
75
+
76
+ def post_add_content
77
+ # TODO avoid rendering unless args changed from initialize args (due to setting of piecemeal attributes)
78
+ render
79
+ end
80
+
81
+ def render(custom_parent_dom_element: nil, brand_new: false)
82
+ super(custom_parent_dom_element: nil, brand_new: false)
83
+ self.background = background
84
+ self.foreground = foreground
85
+ self.font = font
86
+ end
87
+
88
+ # parameter names for arguments to pass to SWT GC.xyz method for rendering shape (e.g. draw_image(image, x, y) yields :image, :x, :y parameter names)
89
+ def parameter_names
90
+ [:x, :y, :width, :height]
91
+ end
92
+
93
+ # subclasses may override to specify location parameter names if different from x and y (e.g. all polygon points are location parameters)
94
+ # used in calculating movement changes
95
+ def location_parameter_names
96
+ [:x, :y]
97
+ end
98
+
99
+ def possible_parameter_names
100
+ parameter_names
101
+ end
102
+
103
+ def parameter_name?(attribute_name)
104
+ possible_parameter_names.map(&:to_s).include?(attribute_getter(attribute_name))
105
+ end
106
+
107
+ def current_parameter_name?(attribute_name)
108
+ parameter_names.include?(attribute_name.to_s.to_sym)
109
+ end
110
+
111
+ def parameter_index(attribute_name)
112
+ parameter_names.index(attribute_name.to_s.to_sym)
113
+ end
114
+
115
+ def get_parameter_attribute(attribute_name)
116
+ @args[parameter_index(attribute_getter(attribute_name))]
117
+ end
118
+
119
+ def set_parameter_attribute(attribute_name, *args)
120
+ @args[parameter_index(attribute_getter(attribute_name))] = args.size == 1 ? args.first : args
121
+ end
122
+
123
+ def has_attribute?(attribute_name, *args)
124
+ parameter_name?(attribute_name) or
125
+ (respond_to?(attribute_name, super: true) and respond_to?(attribute_setter(attribute_name), super: true))
126
+ end
127
+
128
+ def set_attribute(attribute_name, *args)
129
+ attribute_getter_name = attribute_getter(attribute_name)
130
+ attribute_setter_name = attribute_setter(attribute_name)
131
+ if parameter_name?(attribute_name)
132
+ return if attribute_getter_name == (args.size == 1 ? args.first : args)
133
+ set_parameter_attribute(attribute_getter_name, *args)
134
+ elsif (respond_to?(attribute_name, super: true) and respond_to?(attribute_setter_name, super: true))
135
+ return if self.send(attribute_getter_name) == (args.size == 1 ? args.first : args)
136
+ self.send(attribute_setter_name, *args)
137
+ end
138
+ end
139
+
140
+ def get_attribute(attribute_name)
141
+ if parameter_name?(attribute_name)
142
+ arg_index = parameter_index(attribute_name)
143
+ @args[arg_index] if arg_index
144
+ elsif (respond_to?(attribute_name, super: true) and respond_to?(attribute_setter(attribute_name), super: true))
145
+ self.send(attribute_name)
146
+ end
147
+ end
148
+
149
+ # TODO look why image is not working with the method_missing and respond_to? on shape
150
+ def method_missing(method_name, *args, &block)
151
+ if method_name.to_s.end_with?('=')
152
+ set_attribute(method_name, *args)
153
+ elsif has_attribute?(method_name) && args.empty?
154
+ get_attribute(method_name)
155
+ else # TODO support proxying calls to handle_observation_request for listeners just like WidgetProxy
156
+ super(method_name, *args, &block)
157
+ end
158
+ end
159
+
160
+ def respond_to?(method_name, *args, &block)
161
+ options = args.last if args.last.is_a?(Hash)
162
+ super_invocation = options && options[:super]
163
+ if !super_invocation && has_attribute?(method_name)
164
+ true
165
+ else
166
+ super(method_name, *args, &block)
167
+ end
168
+ end
169
+
170
+ def attach(the_parent_dom_element)
171
+ the_parent_dom_element.html("#{the_parent_dom_element.html()}\n#{@dom}")
172
+ end
173
+
174
+ def element
175
+ 'g'
176
+ end
177
+
178
+ def dom
179
+ shape_id = id
180
+ shape_class = name
181
+ @dom ||= xml {
182
+ g(id: shape_id, class: shape_class) {
183
+ }
184
+ }.to_s
185
+ end
186
+
187
+ # TODO implement shape_at_location potentially with document.elementFromPoint(x, y);
188
+
189
+ end
190
+
191
+ end
192
+
193
+ end
194
+
195
+ end
196
+
197
+ require 'glimmer/swt/custom/shape/arc'
198
+ require 'glimmer/swt/custom/shape/image'
199
+ require 'glimmer/swt/custom/shape/line'
200
+ require 'glimmer/swt/custom/shape/oval'
201
+ require 'glimmer/swt/custom/shape/point'
202
+ require 'glimmer/swt/custom/shape/polygon'
203
+ require 'glimmer/swt/custom/shape/polyline'
204
+ require 'glimmer/swt/custom/shape/rectangle'
205
+ require 'glimmer/swt/custom/shape/text'
@@ -0,0 +1,29 @@
1
+ module Glimmer
2
+ module SWT
3
+ # emulating org.eclipse.swt.graphics.Image
4
+ class ImageProxy
5
+ class << self
6
+ def create(*args, &content)
7
+ if args.size == 1 && args.first.is_a?(ImageProxy)
8
+ args.first
9
+ else
10
+ new(*args, &content)
11
+ end
12
+ end
13
+ end
14
+
15
+ attr_reader :file_path, :width, :height
16
+
17
+ def initialize(*args)
18
+ options = args.last.is_a?(Hash) ? args.last : {}
19
+ # TODO support a parent as a first argument before the file path
20
+ @file_path = args.first
21
+ @width = options[:width]
22
+ @height = options[:height]
23
+ end
24
+
25
+ # TODO implement scale_to
26
+ end
27
+ # TODO alias as org.eclipse.swt.graphics.Image
28
+ end
29
+ end
@@ -198,6 +198,7 @@ module Glimmer
198
198
  # delay this till all children rendered (perhaps post_add_content block)
199
199
  parent_dom_element.css('position', 'relative')
200
200
  parent_dom_element.css('margin-top', '30px')
201
+ parent_dom_element.css('height', '114%')
201
202
  redraw
202
203
  `$(#{path}).menu({
203
204
  position: { my: "top", at: "bottom" },
@@ -222,7 +223,6 @@ module Glimmer
222
223
  @visible = value
223
224
  if @visible
224
225
  parent.menu_requested = true
225
- parent.dom_element.css('position', 'relative')
226
226
  render
227
227
  dom_element.css('position', 'absolute')
228
228
  dom_element.css('left', parent.menu_x - parent.dom_element.offset.left)
@@ -1,5 +1,7 @@
1
1
  module Glimmer
2
2
  module SWT
3
+ # emulating org.eclipse.swt.graphics.Point
3
4
  Point = Struct.new(:x, :y)
5
+ # TODO alias as org.eclipse.swt.graphics.Point
4
6
  end
5
7
  end
@@ -0,0 +1,7 @@
1
+ module Glimmer
2
+ module SWT
3
+ # emulating org.eclipse.swt.graphics.Rectangle
4
+ Rectangle = Struct.new(:x, :y, :width, :height)
5
+ # TODO alias as org.eclipse.swt.graphics.Rectangle
6
+ end
7
+ end
@@ -30,7 +30,7 @@ module Glimmer
30
30
  class WidgetProxy
31
31
  include Glimmer
32
32
  include PropertyOwner
33
-
33
+
34
34
  Event = Struct.new(:widget, keyword_init: true)
35
35
 
36
36
  JS_KEY_CODE_TO_SWT_KEY_CODE_MAP = {
@@ -295,6 +295,7 @@ module Glimmer
295
295
  end
296
296
 
297
297
  def font=(value)
298
+ return if value.nil?
298
299
  @font = value.is_a?(FontProxy) ? value : FontProxy.new(self, value)
299
300
  dom_element.css('font-family', @font.name) unless @font.nil?
300
301
  dom_element.css('font-style', 'italic') if @font&.style == :italic || [@font&.style].flatten.compact.include?(:italic)
@@ -331,9 +332,10 @@ module Glimmer
331
332
  brand_new = @dom.nil? || old_element.empty? || brand_new
332
333
  build_dom(layout: !custom_parent_dom_element) # TODO handle custom parent layout by passing parent instead of parent dom element
333
334
  if brand_new
334
- the_parent_dom_element.append(@dom) # TODO make a method attach to allow subclasses to override if needed
335
+ # TODO make a method attach to allow subclasses to override if needed
336
+ attach(the_parent_dom_element)
335
337
  else
336
- old_element.replace_with(@dom)
338
+ reattach(old_element)
337
339
  end
338
340
  observation_requests&.each do |keyword, event_listener_set|
339
341
  event_listener_set.each do |event_listener|
@@ -347,6 +349,14 @@ module Glimmer
347
349
  content_on_render_blocks.each { |content_block| content(&content_block) } unless skip_content_on_render_blocks?
348
350
  end
349
351
  alias redraw render
352
+
353
+ def attach(the_parent_dom_element)
354
+ the_parent_dom_element.append(@dom)
355
+ end
356
+
357
+ def reattach(old_element)
358
+ old_element.replace_with(@dom)
359
+ end
350
360
 
351
361
  def content_on_render_blocks
352
362
  @content_on_render_blocks ||= []
@@ -367,7 +377,7 @@ module Glimmer
367
377
  def build_dom(layout: true)
368
378
  # TODO consider passing parent element instead and having table item include a table cell widget only for opal
369
379
  @dom = nil
370
- @dom = dom
380
+ @dom = dom # TODO unify how to build dom for most widgets based on element, id, and name (class)
371
381
  @dom = @parent.get_layout.dom(@dom) if @parent.respond_to?(:layout) && @parent.get_layout
372
382
  @dom
373
383
  end
@@ -1143,6 +1153,7 @@ require 'glimmer/swt/combo_proxy'
1143
1153
  require 'glimmer/swt/c_combo_proxy'
1144
1154
  require 'glimmer/swt/checkbox_proxy'
1145
1155
  require 'glimmer/swt/composite_proxy'
1156
+ require 'glimmer/swt/canvas_proxy'
1146
1157
  require 'glimmer/swt/date_time_proxy'
1147
1158
  require 'glimmer/swt/group_proxy'
1148
1159
  require 'glimmer/swt/label_proxy'
@@ -25,10 +25,54 @@ class Tetris
25
25
  include Glimmer::UI::CustomWidget
26
26
 
27
27
  options :game_playfield, :block_size, :row, :column
28
+
29
+ attr_accessor :bevel_pixel_size
30
+
31
+ before_body do
32
+ self.bevel_pixel_size = 0.16*block_size.to_f
33
+ end
28
34
 
29
35
  body {
30
36
  canvas { |canvas_proxy|
31
37
  background <= [game_playfield[row][column], :color]
38
+ polygon(0, 0, block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
39
+ background <= [game_playfield[row][column], :color, on_read: ->(color_value) {
40
+ unless color_value.nil?
41
+ color = color(color_value)
42
+ rgb(color.red + 4*BEVEL_CONSTANT, color.green + 4*BEVEL_CONSTANT, color.blue + 4*BEVEL_CONSTANT)
43
+ end
44
+ }]
45
+ }
46
+ polygon(block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size, block_size) {
47
+ background <= [game_playfield[row][column], :color, on_read: ->(color_value) {
48
+ unless color_value.nil?
49
+ color = color(color_value)
50
+ rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
51
+ end
52
+ }]
53
+ }
54
+ polygon(block_size, block_size, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size) {
55
+ background <= [game_playfield[row][column], :color, on_read: ->(color_value) {
56
+ unless color_value.nil?
57
+ color = color(color_value)
58
+ rgb(color.red - 2*BEVEL_CONSTANT, color.green - 2*BEVEL_CONSTANT, color.blue - 2*BEVEL_CONSTANT)
59
+ end
60
+ }]
61
+ }
62
+ polygon(0, 0, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size) {
63
+ background <= [game_playfield[row][column], :color, on_read: ->(color_value) {
64
+ unless color_value.nil?
65
+ color = color(color_value)
66
+ rgb(color.red - BEVEL_CONSTANT, color.green - BEVEL_CONSTANT, color.blue - BEVEL_CONSTANT)
67
+ end
68
+ }]
69
+ }
70
+ rectangle(0, 0, block_size, block_size) {
71
+ foreground <= [game_playfield[row][column], :color, on_read: ->(color_value) {
72
+ color_value == Model::Block::COLOR_CLEAR ? :gray : color_value
73
+ }]
74
+ }
75
+
32
76
  }
33
77
  }
34
78
  end