glimmer-dsl-swt 4.18.2.1 → 4.18.3.0

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 (36) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +38 -0
  3. data/README.md +175 -32
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +18 -6
  6. data/lib/glimmer/dsl/swt/color_expression.rb +4 -4
  7. data/lib/glimmer/dsl/swt/data_binding_expression.rb +3 -3
  8. data/lib/glimmer/dsl/swt/display_expression.rb +3 -3
  9. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  10. data/lib/glimmer/dsl/swt/multiply_expression.rb +53 -0
  11. data/lib/glimmer/dsl/swt/property_expression.rb +4 -2
  12. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -4
  13. data/lib/glimmer/dsl/swt/transform_expression.rb +55 -0
  14. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -1
  15. data/lib/glimmer/rake_task/scaffold.rb +4 -3
  16. data/lib/glimmer/swt/color_proxy.rb +28 -6
  17. data/lib/glimmer/swt/custom/drawable.rb +8 -0
  18. data/lib/glimmer/swt/custom/shape.rb +66 -26
  19. data/lib/glimmer/swt/display_proxy.rb +1 -1
  20. data/lib/glimmer/swt/layout_data_proxy.rb +3 -3
  21. data/lib/glimmer/swt/shell_proxy.rb +4 -3
  22. data/lib/glimmer/swt/transform_proxy.rb +109 -0
  23. data/lib/glimmer/swt/widget_proxy.rb +34 -25
  24. data/lib/glimmer/ui/custom_shell.rb +15 -2
  25. data/lib/glimmer/ui/custom_widget.rb +44 -31
  26. data/samples/elaborate/meta_sample.rb +21 -0
  27. data/samples/elaborate/tetris.rb +106 -0
  28. data/samples/elaborate/tetris/model/block.rb +48 -0
  29. data/samples/elaborate/tetris/model/game.rb +185 -0
  30. data/samples/elaborate/tetris/model/tetromino.rb +313 -0
  31. data/samples/elaborate/tetris/view/block.rb +70 -0
  32. data/samples/elaborate/tetris/view/game_over_dialog.rb +72 -0
  33. data/samples/elaborate/tetris/view/playfield.rb +56 -0
  34. data/samples/elaborate/tetris/view/score_lane.rb +87 -0
  35. data/samples/hello/hello_canvas_transform.rb +40 -0
  36. metadata +16 -4
@@ -24,6 +24,7 @@ require 'glimmer/swt/swt_proxy'
24
24
  require 'glimmer/swt/display_proxy'
25
25
  require 'glimmer/swt/color_proxy'
26
26
  require 'glimmer/swt/font_proxy'
27
+ require 'glimmer/swt/transform_proxy'
27
28
 
28
29
  module Glimmer
29
30
  module SWT
@@ -32,6 +33,7 @@ module Glimmer
32
33
  # swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
33
34
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
34
35
  class Shape
36
+ include Packages
35
37
  include Properties
36
38
  # TODO support textExtent sized shapes nested within text/string
37
39
  # TODO support a Pattern DSL for methods that take Pattern arguments
@@ -42,7 +44,17 @@ module Glimmer
42
44
  end
43
45
 
44
46
  def gc_instance_methods
45
- org.eclipse.swt.graphics.GC.instance_methods.map(&:to_s)
47
+ @gc_instance_methods ||= org.eclipse.swt.graphics.GC.instance_methods.map(&:to_s)
48
+ end
49
+
50
+ def keywords
51
+ @keywords ||= gc_instance_methods.select do |method_name|
52
+ !method_name.end_with?('=') && (method_name.start_with?('draw_') || method_name.start_with?('fill_'))
53
+ end.reject do |method_name|
54
+ gc_instance_methods.include?("#{method_name}=") || gc_instance_methods.include?("set_#{method_name}")
55
+ end.map do |method_name|
56
+ method_name.gsub(/(draw|fill|gradient|round)_/, '')
57
+ end.uniq.compact.to_a
46
58
  end
47
59
 
48
60
  def arg_options(args, extract: false)
@@ -52,11 +64,19 @@ module Glimmer
52
64
  end
53
65
 
54
66
  def method_name(keyword, args)
55
- keyword = keyword.to_s
56
- gradient = 'gradient_' if arg_options(args)[:gradient]
57
- round = 'round_' if arg_options(args)[:round]
58
- gc_instance_method_name_prefix = !['polyline', 'point', 'image', 'focus'].include?(keyword) && (arg_options(args)[:fill] || arg_options(args)[:gradient]) ? 'fill_' : 'draw_'
59
- "#{gc_instance_method_name_prefix}#{gradient}#{round}#{keyword}"
67
+ method_arg_options = arg_options(args)
68
+ unless flyweight_method_names.keys.include?([keyword, method_arg_options])
69
+ keyword = keyword.to_s
70
+ gradient = 'gradient_' if method_arg_options[:gradient]
71
+ round = 'round_' if method_arg_options[:round]
72
+ gc_instance_method_name_prefix = !['polyline', 'point', 'image', 'focus'].include?(keyword) && (method_arg_options[:fill] || method_arg_options[:gradient]) ? 'fill_' : 'draw_'
73
+ flyweight_method_names[[keyword, method_arg_options]] = "#{gc_instance_method_name_prefix}#{gradient}#{round}#{keyword}"
74
+ end
75
+ flyweight_method_names[[keyword, method_arg_options]]
76
+ end
77
+
78
+ def flyweight_method_names
79
+ @flyweight_method_names ||= {}
60
80
  end
61
81
  end
62
82
 
@@ -91,27 +111,12 @@ module Glimmer
91
111
  end
92
112
 
93
113
  def post_add_content
94
- event_handler = lambda do |event|
95
- @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
96
- @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
97
- @properties.each do |property, args|
98
- method_name = attribute_setter(property)
99
- apply_property_arg_conversions(method_name, args)
100
- event.gc.send(method_name, *args)
101
- end
102
- apply_shape_arg_conversions(@method_name, @args)
103
- apply_shape_arg_defaults(@method_name, @args)
104
- tolerate_shape_extra_args(@method_name, @args)
105
- event.gc.send(@method_name, *@args)
106
- end
107
- if parent.respond_to?(:swt_display)
108
- @paint_listener_proxy = @parent.on_swt_paint(&event_handler)
109
- else
110
- @paint_listener_proxy = @parent.on_paint_control(&event_handler)
111
- end
114
+ setup_paint_listener
115
+ @content_added = true
112
116
  end
113
117
 
114
- def apply_property_arg_conversions(method_name, args)
118
+ def apply_property_arg_conversions(method_name, property, args)
119
+ args = args.dup
115
120
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
116
121
  if (args.first.is_a?(Symbol) || args.first.is_a?(String))
117
122
  if the_java_method.parameter_types.first == Color.java_class
@@ -130,6 +135,9 @@ module Glimmer
130
135
  if args.first.is_a?(FontProxy)
131
136
  args[0] = args[0].swt_font
132
137
  end
138
+ if args.first.is_a?(TransformProxy)
139
+ args[0] = args[0].swt_transform
140
+ end
133
141
  if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
134
142
  args.each_with_index do |arg, i|
135
143
  if arg.is_a?(Symbol) || arg.is_a?(String)
@@ -142,6 +150,7 @@ module Glimmer
142
150
  args[0] = org.eclipse.swt.graphics.Pattern.new(*new_args)
143
151
  args[1..-1] = []
144
152
  end
153
+ args
145
154
  end
146
155
 
147
156
  def apply_shape_arg_conversions(method_name, args)
@@ -184,11 +193,42 @@ module Glimmer
184
193
 
185
194
  def set_attribute(attribute_name, *args)
186
195
  @properties[attribute_name] = args
196
+ if @content_added
197
+ @parent.resetup_shape_paint_listeners
198
+ @parent.redraw
199
+ end
187
200
  end
188
-
201
+
189
202
  def get_attribute(attribute_name)
190
203
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
191
204
  end
205
+
206
+ def setup_paint_listener
207
+ if parent.respond_to?(:swt_display)
208
+ @paint_listener_proxy = @parent.on_swt_paint(&method(:paint))
209
+ else
210
+ @paint_listener_proxy = @parent.on_paint_control(&method(:paint))
211
+ end
212
+ end
213
+
214
+ def paint(paint_event)
215
+ @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
216
+ @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
217
+ @properties['font'] = [@parent.font] if draw? && !@properties.keys.map(&:to_s).include?('font')
218
+ @properties['transform'] = [nil] if !@properties.keys.map(&:to_s).include?('transform')
219
+ @properties.each do |property, args|
220
+ method_name = attribute_setter(property)
221
+ converted_args = apply_property_arg_conversions(method_name, property, args)
222
+ paint_event.gc.send(method_name, *converted_args)
223
+ if property == 'transform' && args.first.is_a?(TransformProxy)
224
+ args.first.swt_transform.dispose
225
+ end
226
+ end
227
+ apply_shape_arg_conversions(@method_name, @args)
228
+ apply_shape_arg_defaults(@method_name, @args)
229
+ tolerate_shape_extra_args(@method_name, @args)
230
+ paint_event.gc.send(@method_name, *@args)
231
+ end
192
232
 
193
233
  end
194
234
 
@@ -45,7 +45,7 @@ module Glimmer
45
45
  class << self
46
46
  # Returns singleton instance
47
47
  def instance(*args)
48
- if @instance.nil? || @instance.swt_display.isDisposed
48
+ if @instance.nil? || @instance.swt_display.nil? || @instance.swt_display.isDisposed
49
49
  @instance = new(*args)
50
50
  end
51
51
  @instance
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -46,6 +46,7 @@ module Glimmer
46
46
  @swt_widget = swt_widget
47
47
  else
48
48
  if args.first.is_a?(ShellProxy)
49
+ @parent_proxy = args[0]
49
50
  args[0] = args[0].swt_widget
50
51
  end
51
52
  style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
@@ -78,7 +79,7 @@ module Glimmer
78
79
  end
79
80
 
80
81
  # Centers shell within monitor it is in
81
- def center
82
+ def center_within_display
82
83
  primary_monitor = @display.getPrimaryMonitor()
83
84
  monitor_bounds = primary_monitor.getBounds()
84
85
  shell_bounds = @swt_widget.getBounds()
@@ -101,13 +102,13 @@ module Glimmer
101
102
  else
102
103
  @opened_before = true
103
104
  @swt_widget.pack
104
- center
105
+ center_within_display
105
106
  @swt_widget.open
106
107
  end
107
108
  end
108
109
 
109
110
  def nested?
110
- !parent.nil?
111
+ !swt_widget&.parent.nil?
111
112
  end
112
113
 
113
114
  def hide
@@ -0,0 +1,109 @@
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/display_proxy'
23
+ require 'glimmer/swt/properties'
24
+ require 'glimmer/swt/custom/shape'
25
+
26
+ module Glimmer
27
+ module SWT
28
+ # Proxy for org.eclipse.swt.graphics.Transform
29
+ #
30
+ # Follows the Proxy Design Pattern
31
+ class TransformProxy
32
+ include Properties
33
+
34
+ include_package 'org.eclipse.swt.graphics'
35
+ include_package 'org.eclipse.swt.widgets'
36
+
37
+ attr_reader :swt_transform, :parent
38
+
39
+ def initialize(parent, *args, swt_transform: nil, multiply: false)
40
+ @parent = parent
41
+ @multiply = multiply
42
+ if swt_transform.nil?
43
+ if !args.first.is_a?(Display) && !args.first.is_a?(DisplayProxy)
44
+ args.prepend DisplayProxy.instance.swt_display
45
+ end
46
+ if args.first.is_a?(DisplayProxy)
47
+ args[0] = args[0].swt_display
48
+ end
49
+ if args.last.is_a?(TransformProxy)
50
+ args[-1] = args[-1].swt_transform
51
+ end
52
+ if args.last.nil? || args.last.is_a?(Transform)
53
+ @swt_transform = args.last
54
+ @parent&.set_attribute('transform', self)
55
+ else
56
+ @swt_transform = Transform.new(*args)
57
+ end
58
+ else
59
+ @swt_transform = swt_transform
60
+ end
61
+ end
62
+
63
+ def post_add_content
64
+ if @multiply
65
+ @parent.multiply(@swt_transform)
66
+ else
67
+ @parent&.set_attribute('transform', self)
68
+ end
69
+ end
70
+
71
+ def content(&block)
72
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::TransformExpression.new, &block)
73
+ end
74
+
75
+ def has_attribute?(attribute_name, *args)
76
+ @swt_transform.respond_to?(attribute_name) || @swt_transform.respond_to?(attribute_setter(attribute_name))
77
+ end
78
+
79
+ def set_attribute(attribute_name, *args)
80
+ if @swt_transform.respond_to?(attribute_name)
81
+ @swt_transform.send(attribute_name, *args)
82
+ elsif @swt_transform.respond_to?(attribute_setter(attribute_name))
83
+ @swt_transform.send(attribute_setter(attribute_name), *args)
84
+ end
85
+ end
86
+
87
+ def get_attribute(attribute_name)
88
+ if @swt_transform.respond_to?(attribute_getter(attribute_name))
89
+ @swt_transform.send(attribute_getter(attribute_name))
90
+ else
91
+ @swt_transform.send(attribute_name)
92
+ end
93
+ end
94
+
95
+ def method_missing(method_name, *args, &block)
96
+ result = @swt_transform.send(method_name, *args, &block)
97
+ result.nil? ? self : result
98
+ rescue => e
99
+ Glimmer::Config.logger.debug {"Neither MessageBoxProxy nor #{@swt_transform.class.name} can handle the method ##{method}"}
100
+ super
101
+ end
102
+
103
+ def respond_to?(method, *args, &block)
104
+ super ||
105
+ @swt_transform.respond_to?(method, *args, &block)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -74,12 +74,10 @@ module Glimmer
74
74
 
75
75
  DEFAULT_INITIALIZERS = {
76
76
  composite: lambda do |composite|
77
- if composite.get_layout.nil?
78
- layout = GridLayout.new
79
- layout.marginWidth = 15
80
- layout.marginHeight = 15
81
- composite.layout = layout
82
- end
77
+ composite.layout = GridLayout.new if composite.get_layout.nil?
78
+ end,
79
+ canvas: lambda do |canvas|
80
+ canvas.layout = nil unless canvas.get_layout.nil?
83
81
  end,
84
82
  scrolled_composite: lambda do |scrolled_composite|
85
83
  scrolled_composite.expand_horizontal = true
@@ -160,7 +158,7 @@ module Glimmer
160
158
  underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
161
159
  parent_proxy_class = self.class.widget_proxy_class(self.class.underscored_widget_name(@swt_widget.parent))
162
160
  parent = swt_widget.parent
163
- @parent_proxy = parent.get_data('proxy') || parent_proxy_class.new(swt_widget: parent)
161
+ @parent_proxy = parent&.get_data('proxy') || parent_proxy_class.new(swt_widget: parent)
164
162
  end
165
163
  if @swt_widget&.get_data('proxy').nil?
166
164
  @swt_widget.set_data('proxy', self)
@@ -499,26 +497,37 @@ module Glimmer
499
497
 
500
498
  # This supports widgets in and out of basic SWT
501
499
  def self.swt_widget_class_for(underscored_widget_name)
502
- underscored_widget_name = KEYWORD_ALIASES[underscored_widget_name] if KEYWORD_ALIASES[underscored_widget_name]
503
- swt_widget_name = underscored_widget_name.camelcase(:upper)
504
- swt_widget_class = eval(swt_widget_name)
505
- # TODO fix issue with not detecting DateTime because it's conflicting with the Ruby DateTime
506
- unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
507
- swt_widget_class = swt_widget_class_manual_entries[underscored_widget_name]
508
- if swt_widget_class.nil?
509
- Glimmer::Config.logger.debug {"Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget"}
510
- return nil
500
+ # TODO clear memoization for a keyword if a custom widget was defined with that keyword
501
+ unless flyweight_swt_widget_classes.keys.include?(underscored_widget_name)
502
+ begin
503
+ underscored_widget_name = KEYWORD_ALIASES[underscored_widget_name] if KEYWORD_ALIASES[underscored_widget_name]
504
+ swt_widget_name = underscored_widget_name.camelcase(:upper)
505
+ swt_widget_class = eval(swt_widget_name)
506
+ # TODO fix issue with not detecting DateTime because it's conflicting with the Ruby DateTime
507
+ unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
508
+ swt_widget_class = swt_widget_class_manual_entries[underscored_widget_name]
509
+ if swt_widget_class.nil?
510
+ Glimmer::Config.logger.debug {"Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget"}
511
+ return nil
512
+ end
513
+ end
514
+ flyweight_swt_widget_classes[underscored_widget_name] = swt_widget_class
515
+ rescue SyntaxError, NameError => e
516
+ Glimmer::Config.logger.debug {e.full_message}
517
+ # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
518
+ nil
519
+ rescue => e
520
+ Glimmer::Config.logger.debug {e.full_message}
521
+ # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
522
+ nil
511
523
  end
512
524
  end
513
- swt_widget_class
514
- rescue SyntaxError, NameError => e
515
- Glimmer::Config.logger.debug {e.full_message}
516
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
517
- nil
518
- rescue => e
519
- Glimmer::Config.logger.debug {e.full_message}
520
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
521
- nil
525
+ flyweight_swt_widget_classes[underscored_widget_name]
526
+ end
527
+
528
+ # Flyweight Design Pattern memoization cache. Can be cleared if memory is needed.
529
+ def self.flyweight_swt_widget_classes
530
+ @flyweight_swt_widget_classes ||= {}
522
531
  end
523
532
 
524
533
  def async_exec(&block)
@@ -27,6 +27,19 @@ module Glimmer
27
27
  include SuperModule
28
28
  include Glimmer::UI::CustomWidget
29
29
 
30
+ class << self
31
+ attr_reader :custom_shell
32
+
33
+ def launch
34
+ @custom_shell = send(self.name.underscore.gsub('::', '__'))
35
+ @custom_shell.open
36
+ end
37
+
38
+ def shutdown
39
+ @custom_shell.close
40
+ end
41
+ end
42
+
30
43
  def initialize(parent, *swt_constants, options, &content)
31
44
  super
32
45
  @swt_widget.set_data('custom_shell', self)
@@ -55,8 +68,8 @@ module Glimmer
55
68
  body_root.visible?
56
69
  end
57
70
 
58
- def center
59
- body_root.center
71
+ def center_within_display
72
+ body_root.center_within_display
60
73
  end
61
74
 
62
75
  def start_event_loop
@@ -1,5 +1,5 @@
1
1
  # Copyright (c) 2007-2021 Andy Maleh
2
- #
2
+ #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining
4
4
  # a copy of this software and associated documentation files (the
5
5
  # "Software"), to deal in the Software without restriction, including
@@ -7,10 +7,10 @@
7
7
  # distribute, sublicense, and/or sell copies of the Software, and to
8
8
  # permit persons to whom the Software is furnished to do so, subject to
9
9
  # the following conditions:
10
- #
10
+ #
11
11
  # The above copyright notice and this permission notice shall be
12
12
  # included in all copies or substantial portions of the Software.
13
- #
13
+ #
14
14
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
15
  # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
16
  # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
@@ -34,37 +34,48 @@ module Glimmer
34
34
  include DataBinding::ObservableModel
35
35
 
36
36
  super_module_included do |klass|
37
+ # TODO clear memoization of WidgetProxy.swt_widget_class_for for a keyword if a custom widget was defined with that keyword
37
38
  klass.include(Glimmer) unless klass.name.include?('Glimmer::UI::CustomShell')
38
39
  Glimmer::UI::CustomWidget.add_custom_widget_namespaces_for(klass) unless klass.name.include?('Glimmer::UI::CustomShell')
39
40
  end
40
41
 
41
42
  class << self
42
43
  def for(underscored_custom_widget_name)
43
- extracted_namespaces = underscored_custom_widget_name.
44
- to_s.
45
- split(/__/).map do |namespace|
46
- namespace.camelcase(:upper)
47
- end
48
- custom_widget_namespaces.each do |base|
49
- extracted_namespaces.reduce(base) do |result, namespace|
50
- if !result.constants.include?(namespace)
51
- namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
52
- end
53
- begin
54
- constant = result.const_get(namespace)
55
- return constant if constant.ancestors.include?(Glimmer::UI::CustomWidget)
56
- constant
57
- rescue => e
58
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
59
- result
44
+ unless flyweight_custom_widget_classes.keys.include?(underscored_custom_widget_name)
45
+ begin
46
+ extracted_namespaces = underscored_custom_widget_name.
47
+ to_s.
48
+ split(/__/).map do |namespace|
49
+ namespace.camelcase(:upper)
50
+ end
51
+ custom_widget_namespaces.each do |base|
52
+ extracted_namespaces.reduce(base) do |result, namespace|
53
+ if !result.constants.include?(namespace)
54
+ namespace = result.constants.detect {|c| c.to_s.upcase == namespace.to_s.upcase } || namespace
55
+ end
56
+ begin
57
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = constant = result.const_get(namespace)
58
+ return constant if constant.ancestors.include?(Glimmer::UI::CustomWidget)
59
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = constant
60
+ rescue => e
61
+ # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
62
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = result
63
+ end
64
+ end
60
65
  end
66
+ raise "#{underscored_custom_widget_name} has no custom widget class!"
67
+ rescue => e
68
+ Glimmer::Config.logger.debug {e.message}
69
+ Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
70
+ flyweight_custom_widget_classes[underscored_custom_widget_name] = nil
61
71
  end
62
72
  end
63
- raise "#{underscored_custom_widget_name} has no custom widget class!"
64
- rescue => e
65
- Glimmer::Config.logger.debug {e.message}
66
- Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
67
- nil
73
+ flyweight_custom_widget_classes[underscored_custom_widget_name]
74
+ end
75
+
76
+ # Flyweight Design Pattern memoization cache. Can be cleared if memory is needed.
77
+ def flyweight_custom_widget_classes
78
+ @flyweight_custom_widget_classes ||= {}
68
79
  end
69
80
 
70
81
  def add_custom_widget_namespaces_for(klass)
@@ -141,10 +152,9 @@ module Glimmer
141
152
  end
142
153
  end
143
154
 
144
- attr_reader :body_root, :swt_widget, :parent, :swt_style, :options
155
+ attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
145
156
 
146
157
  def initialize(parent, *swt_constants, options, &content)
147
- @parent = parent
148
158
  @swt_style = SWT::SWTProxy[*swt_constants]
149
159
  options ||= {}
150
160
  @options = self.class.options.merge(options)
@@ -156,6 +166,9 @@ module Glimmer
156
166
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
157
167
  @swt_widget = @body_root.swt_widget
158
168
  @swt_widget.set_data('custom_widget', self)
169
+ @parent = parent
170
+ @parent ||= @swt_widget.parent
171
+ @parent_proxy ||= @parent&.get_data('proxy')
159
172
  execute_hooks('after_body')
160
173
  end
161
174
 
@@ -211,10 +224,10 @@ module Glimmer
211
224
 
212
225
  # This method ensures it has an instance method not coming from Glimmer DSL
213
226
  def has_instance_method?(method_name)
214
- respond_to?(method_name) and
227
+ respond_to?(method_name) and
215
228
  !swt_widget&.respond_to?(method_name) and
216
229
  (method(method_name) rescue nil) and
217
- !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
230
+ !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
218
231
  !method(method_name)&.source_location&.first&.include?('glimmer/swt/widget_proxy.rb')
219
232
  end
220
233
 
@@ -265,13 +278,13 @@ module Glimmer
265
278
  end
266
279
  end
267
280
 
268
- alias local_respond_to? respond_to?
281
+ alias local_respond_to? respond_to?
269
282
  def respond_to?(method, *args, &block)
270
283
  super or
271
284
  can_handle_observation_request?(method) or
272
285
  body_root.respond_to?(method, *args, &block)
273
286
  end
274
-
287
+
275
288
  private
276
289
 
277
290
  def execute_hooks(hook_name)