glimmer-dsl-swt 4.18.2.4 → 4.18.3.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +60 -0
  3. data/README.md +240 -30
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +13 -7
  6. data/lib/ext/glimmer/config.rb +24 -7
  7. data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
  8. data/lib/glimmer/data_binding/widget_binding.rb +22 -4
  9. data/lib/glimmer/dsl/swt/color_expression.rb +4 -4
  10. data/lib/glimmer/dsl/swt/data_binding_expression.rb +3 -3
  11. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  12. data/lib/glimmer/dsl/swt/multiply_expression.rb +53 -0
  13. data/lib/glimmer/dsl/swt/property_expression.rb +4 -2
  14. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -4
  15. data/{samples/elaborate/tetris/view/game_over_dialog.rb → lib/glimmer/dsl/swt/transform_expression.rb} +29 -46
  16. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -1
  17. data/lib/glimmer/swt/color_proxy.rb +28 -6
  18. data/lib/glimmer/swt/custom/drawable.rb +8 -0
  19. data/lib/glimmer/swt/custom/shape.rb +66 -26
  20. data/lib/glimmer/swt/directory_dialog_proxy.rb +3 -3
  21. data/lib/glimmer/swt/display_proxy.rb +26 -5
  22. data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
  23. data/lib/glimmer/swt/shell_proxy.rb +24 -4
  24. data/lib/glimmer/swt/table_proxy.rb +31 -7
  25. data/lib/glimmer/swt/transform_proxy.rb +109 -0
  26. data/lib/glimmer/swt/widget_listener_proxy.rb +14 -5
  27. data/lib/glimmer/swt/widget_proxy.rb +35 -20
  28. data/lib/glimmer/ui/custom_shell.rb +11 -9
  29. data/lib/glimmer/ui/custom_widget.rb +65 -39
  30. data/samples/elaborate/meta_sample.rb +81 -24
  31. data/samples/elaborate/tetris.rb +105 -44
  32. data/samples/elaborate/tetris/model/block.rb +1 -1
  33. data/samples/elaborate/tetris/model/game.rb +233 -137
  34. data/samples/elaborate/tetris/model/past_game.rb +26 -0
  35. data/samples/elaborate/tetris/model/tetromino.rb +46 -30
  36. data/samples/elaborate/tetris/view/block.rb +33 -5
  37. data/samples/elaborate/tetris/view/high_score_dialog.rb +133 -0
  38. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  39. data/samples/elaborate/tetris/view/score_lane.rb +11 -11
  40. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +121 -0
  41. data/samples/elaborate/tic_tac_toe.rb +4 -4
  42. data/samples/hello/hello_canvas_transform.rb +40 -0
  43. data/samples/hello/hello_link.rb +1 -1
  44. metadata +11 -5
@@ -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
@@ -26,10 +26,12 @@ module Glimmer
26
26
  # Follows the Proxy Design Pattern
27
27
  class WidgetListenerProxy
28
28
 
29
- attr_reader :swt_widget, :swt_listener, :widget_add_listener_method, :swt_listener_class, :swt_listener_method, :event_type, :swt_constant
29
+ attr_reader :swt_widget, :swt_display, :swt_listener, :widget_add_listener_method, :swt_listener_class, :swt_listener_method, :event_type, :swt_constant
30
30
 
31
- def initialize(swt_widget:, swt_listener:, widget_add_listener_method: nil, swt_listener_class: nil, swt_listener_method: nil, event_type: nil, swt_constant: nil)
31
+ def initialize(swt_widget:nil, swt_display:nil, swt_listener:, widget_add_listener_method: nil, swt_listener_class: nil, swt_listener_method: nil, event_type: nil, swt_constant: nil, filter: false)
32
32
  @swt_widget = swt_widget
33
+ @swt_display = swt_display
34
+ @filter = filter
33
35
  @swt_listener = swt_listener
34
36
  @widget_add_listener_method = widget_add_listener_method
35
37
  @swt_listener_class = swt_listener_class
@@ -42,14 +44,21 @@ module Glimmer
42
44
  @widget_add_listener_method.sub('add', 'remove')
43
45
  end
44
46
 
45
- def unregister
46
- # TODO consider renaming to deregister (and in Observer too)
47
- if @event_type
47
+ def deregister
48
+ return if @swt_widget&.is_disposed || @swt_display&.is_disposed
49
+ if @swt_display
50
+ if @filter
51
+ @swt_display.removeFilter(@event_type, @swt_listener)
52
+ else
53
+ @swt_display.removeListener(@event_type, @swt_listener)
54
+ end
55
+ elsif @event_type
48
56
  @swt_widget.removeListener(@event_type, @swt_listener)
49
57
  else
50
58
  @swt_widget.send(widget_remove_listener_method, @swt_listener)
51
59
  end
52
60
  end
61
+ alias unregister deregister # TODO consider dropping unregister (and in Observer too)
53
62
  end
54
63
  end
55
64
  end
@@ -497,26 +497,37 @@ module Glimmer
497
497
 
498
498
  # This supports widgets in and out of basic SWT
499
499
  def self.swt_widget_class_for(underscored_widget_name)
500
- underscored_widget_name = KEYWORD_ALIASES[underscored_widget_name] if KEYWORD_ALIASES[underscored_widget_name]
501
- swt_widget_name = underscored_widget_name.camelcase(:upper)
502
- swt_widget_class = eval(swt_widget_name)
503
- # TODO fix issue with not detecting DateTime because it's conflicting with the Ruby DateTime
504
- unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
505
- swt_widget_class = swt_widget_class_manual_entries[underscored_widget_name]
506
- if swt_widget_class.nil?
507
- Glimmer::Config.logger.debug {"Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget"}
508
- 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
509
523
  end
510
524
  end
511
- swt_widget_class
512
- rescue SyntaxError, NameError => e
513
- Glimmer::Config.logger.debug {e.full_message}
514
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
515
- nil
516
- rescue => e
517
- Glimmer::Config.logger.debug {e.full_message}
518
- # Glimmer::Config.logger.debug {"#{e.message}\n#{e.backtrace.join("\n")}"}
519
- 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 ||= {}
520
531
  end
521
532
 
522
533
  def async_exec(&block)
@@ -536,6 +547,10 @@ module Glimmer
536
547
  @swt_widget.dispose
537
548
  end
538
549
 
550
+ def disposed?
551
+ @swt_widget.isDisposed
552
+ end
553
+
539
554
  # TODO Consider renaming these methods as they are mainly used for data-binding
540
555
 
541
556
  def can_add_observer?(property_name)
@@ -652,7 +667,7 @@ module Glimmer
652
667
  can_handle_observation_request?(method) ||
653
668
  swt_widget.respond_to?(method, *args, &block)
654
669
  end
655
-
670
+
656
671
  private
657
672
 
658
673
  def style(underscored_widget_name, styles)
@@ -685,7 +700,7 @@ module Glimmer
685
700
  safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
686
701
  listener = listener_class.new(listener_method => safe_block)
687
702
  @swt_widget.send(widget_add_listener_method, listener)
688
- widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
703
+ WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
689
704
  end
690
705
 
691
706
  # Looks through SWT class add***Listener methods till it finds one for which
@@ -28,15 +28,12 @@ module Glimmer
28
28
  include Glimmer::UI::CustomWidget
29
29
 
30
30
  class << self
31
- attr_reader :custom_shell
31
+ attr_reader :launched_custom_shell
32
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
33
+ def launch(*args, &content)
34
+ @launched_custom_shell = send(keyword, *args, &content) if @launched_custom_shell.nil? || @launched_custom_shell.disposed?
35
+ @launched_custom_shell.swt_widget.set_data('launched', true)
36
+ @launched_custom_shell.open
40
37
  end
41
38
  end
42
39
 
@@ -45,7 +42,7 @@ module Glimmer
45
42
  @swt_widget.set_data('custom_shell', self)
46
43
  raise Error, 'Invalid custom shell body root! Must be a shell or another custom shell.' unless body_root.swt_widget.is_a?(org.eclipse.swt.widgets.Shell)
47
44
  end
48
-
45
+
49
46
  # Classes may override
50
47
  def open
51
48
  body_root.open
@@ -56,6 +53,7 @@ module Glimmer
56
53
  open
57
54
  end
58
55
 
56
+ # TODO consider using Forwardable instead
59
57
  def close
60
58
  body_root.close
61
59
  end
@@ -68,6 +66,10 @@ module Glimmer
68
66
  body_root.visible?
69
67
  end
70
68
 
69
+ def disposed?
70
+ swt_widget.is_disposed
71
+ end
72
+
71
73
  def center_within_display
72
74
  body_root.center_within_display
73
75
  end
@@ -34,39 +34,60 @@ 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
+
81
+ # Returns keyword to use for this custom widget
82
+ def keyword
83
+ self.name.underscore.gsub('::', '__')
84
+ end
85
+
86
+ # Returns shortcut keyword to use for this custom widget (keyword minus namespace)
87
+ def shortcut_keyword
88
+ self.name.underscore.gsub('::', '__').split('__').last
89
+ end
90
+
70
91
  def add_custom_widget_namespaces_for(klass)
71
92
  Glimmer::UI::CustomWidget.namespaces_for_class(klass).drop(1).each do |namespace|
72
93
  Glimmer::UI::CustomWidget.custom_widget_namespaces << namespace
@@ -127,8 +148,7 @@ module Glimmer
127
148
  end
128
149
 
129
150
  def before_body(&block)
130
- @before_body_blocks ||= []
131
- @before_body_blocks << block
151
+ @before_body_block = block
132
152
  end
133
153
 
134
154
  def body(&block)
@@ -136,29 +156,27 @@ module Glimmer
136
156
  end
137
157
 
138
158
  def after_body(&block)
139
- @after_body_blocks ||= []
140
- @after_body_blocks << block
159
+ @after_body_block = block
141
160
  end
142
161
  end
143
162
 
144
163
  attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
145
164
 
146
165
  def initialize(parent, *swt_constants, options, &content)
166
+ @parent = parent
167
+ @parent_proxy = @parent&.get_data('proxy')
147
168
  @swt_style = SWT::SWTProxy[*swt_constants]
148
169
  options ||= {}
149
170
  @options = self.class.options.merge(options)
150
171
  @content = Util::ProcTracker.new(content) if content
151
- execute_hooks('before_body')
172
+ execute_hook('before_body')
152
173
  body_block = self.class.instance_variable_get("@body_block")
153
174
  raise Glimmer::Error, 'Invalid custom widget for having no body! Please define body block!' if body_block.nil?
154
175
  @body_root = instance_exec(&body_block)
155
176
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
156
177
  @swt_widget = @body_root.swt_widget
157
178
  @swt_widget.set_data('custom_widget', self)
158
- @parent = parent
159
- @parent ||= @swt_widget.parent
160
- @parent_proxy ||= @parent&.get_data('proxy')
161
- execute_hooks('after_body')
179
+ execute_hook('after_body')
162
180
  end
163
181
 
164
182
  # Subclasses may override to perform post initialization work on an added child
@@ -231,10 +249,18 @@ module Glimmer
231
249
  def attribute_setter(attribute_name)
232
250
  "#{attribute_name}="
233
251
  end
252
+
253
+ def disposed?
254
+ swt_widget.isDisposed
255
+ end
234
256
 
235
257
  def has_style?(style)
236
258
  (swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
237
259
  end
260
+
261
+ def pack(*args)
262
+ body_root.pack(*args)
263
+ end
238
264
 
239
265
  # TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
240
266
  # delegation to DisplayProxy, via a module
@@ -276,13 +302,13 @@ module Glimmer
276
302
 
277
303
  private
278
304
 
279
- def execute_hooks(hook_name)
280
- self.class.instance_variable_get("@#{hook_name}_blocks")&.each do |hook_block|
281
- temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
282
- singleton_class.define_method(temp_method_name, &hook_block)
283
- send(temp_method_name)
284
- singleton_class.send(:remove_method, temp_method_name)
285
- end
305
+ def execute_hook(hook_name)
306
+ hook_block = self.class.instance_variable_get("@#{hook_name}_block")
307
+ return if hook_block.nil?
308
+ temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
309
+ singleton_class.define_method(temp_method_name, &hook_block)
310
+ send(temp_method_name)
311
+ singleton_class.send(:remove_method, temp_method_name)
286
312
  end
287
313
  end
288
314
  end
@@ -25,6 +25,24 @@ require 'etc'
25
25
  class Sample
26
26
  include Glimmer::DataBinding::ObservableModel
27
27
 
28
+ class << self
29
+ def glimmer_directory
30
+ File.expand_path('../../..', __FILE__)
31
+ end
32
+
33
+ def user_glimmer_directory
34
+ File.join(Etc.getpwuid.dir, '.glimmer-dsl-swt')
35
+ end
36
+
37
+ def ensure_user_glimmer_directory
38
+ unless @ensured_glimmer_directory
39
+ FileUtils.rm_rf(user_glimmer_directory)
40
+ FileUtils.cp_r(glimmer_directory, user_glimmer_directory)
41
+ @ensured_glimmer_directory = true
42
+ end
43
+ end
44
+ end
45
+
28
46
  attr_accessor :sample_directory, :file, :selected
29
47
 
30
48
  def initialize(file, sample_directory: )
@@ -59,25 +77,35 @@ class Sample
59
77
  File.basename(file) != 'meta_sample.rb'
60
78
  end
61
79
  alias launchable editable
80
+
81
+ def file_relative_path
82
+ file.sub(self.class.glimmer_directory, '')
83
+ end
84
+
85
+ def user_file
86
+ File.join(self.class.user_glimmer_directory, file_relative_path)
87
+ end
88
+
89
+ def user_file_parent_directory
90
+ File.dirname(user_file)
91
+ end
62
92
 
93
+ def directory
94
+ file.sub(/\.rb/, '')
95
+ end
96
+
63
97
  def launch(modified_code)
64
- parent_directory = File.basename(File.dirname(file))
65
- modified_file_parent_directory = File.join(Etc.getpwuid.dir, '.glimmer', 'samples', parent_directory)
66
- launch_file = modified_file = File.join(modified_file_parent_directory, File.basename(file))
98
+ launch_file = user_file
67
99
  begin
68
- FileUtils.mkdir_p(modified_file_parent_directory)
69
- FileUtils.cp_r(file, modified_file_parent_directory)
70
- FileUtils.cp_r(file.sub(/\.rb/, ''), modified_file_parent_directory) if File.exist?(file.sub(/\.rb/, ''))
71
- File.write(modified_file, modified_code)
100
+ FileUtils.cp_r(file, user_file_parent_directory)
101
+ FileUtils.cp_r(directory, user_file_parent_directory) if File.exist?(directory)
102
+ File.write(user_file, modified_code)
72
103
  rescue => e
73
104
  puts 'Error writing sample modifications. Launching original sample.'
74
105
  puts e.full_message
75
106
  launch_file = file # load original file if failed to write changes
76
107
  end
77
108
  load(launch_file)
78
- ensure
79
- FileUtils.rm_rf(modified_file)
80
- FileUtils.rm_rf(modified_file.sub(/\.rb/, ''))
81
109
  end
82
110
  end
83
111
 
@@ -162,17 +190,18 @@ class SampleDirectory
162
190
  end
163
191
 
164
192
  class MetaSampleApplication
165
- include Glimmer
193
+ include Glimmer::UI::CustomShell
166
194
 
167
- def initialize
195
+ before_body {
196
+ Sample.ensure_user_glimmer_directory
168
197
  selected_sample_directory = SampleDirectory.sample_directories.first
169
198
  selected_sample = selected_sample_directory.samples.first
170
199
  selected_sample_directory.selected_sample_name = selected_sample.name
171
- end
172
-
173
- def launch
174
200
  Display.app_name = 'Glimmer Meta-Sample'
175
- shell {
201
+ }
202
+
203
+ body {
204
+ shell(:fill_screen) {
176
205
  minimum_size 1280, 768
177
206
  text 'Glimmer Meta-Sample (The Sample of Samples)'
178
207
  image File.expand_path('../../icons/scaffold_app.png', __dir__)
@@ -218,11 +247,8 @@ class MetaSampleApplication
218
247
  on_widget_selected {
219
248
  begin
220
249
  SampleDirectory.selected_sample.launch(@code_text.text)
221
- rescue StandardError, SyntaxError => launch_error
222
- message_box {
223
- text 'Error Launching'
224
- message launch_error.full_message
225
- }.open
250
+ rescue LoadError, StandardError, SyntaxError => launch_error
251
+ error_dialog(message: launch_error.full_message).open
226
252
  end
227
253
  }
228
254
  }
@@ -243,10 +269,41 @@ class MetaSampleApplication
243
269
  editable bind(SampleDirectory, 'selected_sample.editable')
244
270
  }
245
271
 
246
- weights 4, 9
272
+ weights 4, 11
273
+ }
274
+ }
275
+ }
276
+
277
+ # Method-based error_dialog custom widget
278
+ def error_dialog(message:)
279
+ return if message.nil?
280
+ dialog(body_root) { |dialog_proxy|
281
+ row_layout(:vertical) {
282
+ center true
283
+ }
284
+
285
+ text 'Error Launching'
286
+
287
+ styled_text(:border, :h_scroll, :v_scroll) {
288
+ layout_data {
289
+ width body_root.bounds.width*0.75
290
+ height body_root.bounds.height*0.75
291
+ }
292
+
293
+ text message
294
+ editable false
295
+ caret nil
296
+ }
297
+
298
+ button {
299
+ text 'Close'
300
+
301
+ on_widget_selected {
302
+ dialog_proxy.close
303
+ }
247
304
  }
248
- }.open
305
+ }
249
306
  end
250
307
  end
251
308
 
252
- MetaSampleApplication.new.launch
309
+ MetaSampleApplication.launch