glimmer-dsl-swt 4.18.2.3 → 4.18.3.2

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 +59 -0
  3. data/README.md +267 -39
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +14 -6
  6. data/lib/ext/glimmer/config.rb +24 -7
  7. data/lib/glimmer/data_binding/widget_binding.rb +14 -4
  8. data/lib/glimmer/dsl/swt/color_expression.rb +4 -4
  9. data/lib/glimmer/dsl/swt/data_binding_expression.rb +3 -3
  10. data/lib/glimmer/dsl/swt/dsl.rb +1 -0
  11. data/lib/glimmer/dsl/swt/multiply_expression.rb +53 -0
  12. data/lib/glimmer/dsl/swt/property_expression.rb +4 -2
  13. data/lib/glimmer/dsl/swt/shape_expression.rb +2 -4
  14. data/lib/glimmer/dsl/swt/transform_expression.rb +55 -0
  15. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -1
  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/directory_dialog_proxy.rb +3 -3
  20. data/lib/glimmer/swt/display_proxy.rb +25 -4
  21. data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
  22. data/lib/glimmer/swt/layout_data_proxy.rb +3 -3
  23. data/lib/glimmer/swt/shell_proxy.rb +20 -5
  24. data/lib/glimmer/swt/table_proxy.rb +19 -4
  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 +31 -20
  28. data/lib/glimmer/ui/custom_shell.rb +13 -11
  29. data/lib/glimmer/ui/custom_widget.rb +68 -44
  30. data/samples/elaborate/meta_sample.rb +81 -24
  31. data/samples/elaborate/tetris.rb +102 -47
  32. data/samples/elaborate/tetris/model/block.rb +2 -2
  33. data/samples/elaborate/tetris/model/game.rb +236 -74
  34. data/samples/elaborate/tetris/model/past_game.rb +26 -0
  35. data/samples/elaborate/tetris/model/tetromino.rb +123 -35
  36. data/samples/elaborate/tetris/view/block.rb +34 -9
  37. data/samples/elaborate/tetris/view/high_score_dialog.rb +114 -0
  38. data/samples/elaborate/tetris/view/playfield.rb +12 -5
  39. data/samples/elaborate/tetris/view/score_lane.rb +87 -0
  40. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +123 -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 +12 -4
@@ -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
@@ -158,7 +158,7 @@ module Glimmer
158
158
  underscored_widget_name = self.class.underscored_widget_name(@swt_widget)
159
159
  parent_proxy_class = self.class.widget_proxy_class(self.class.underscored_widget_name(@swt_widget.parent))
160
160
  parent = swt_widget.parent
161
- @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)
162
162
  end
163
163
  if @swt_widget&.get_data('proxy').nil?
164
164
  @swt_widget.set_data('proxy', self)
@@ -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)
@@ -685,7 +696,7 @@ module Glimmer
685
696
  safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
686
697
  listener = listener_class.new(listener_method => safe_block)
687
698
  @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)
699
+ 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
700
  end
690
701
 
691
702
  # 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,8 +66,12 @@ module Glimmer
68
66
  body_root.visible?
69
67
  end
70
68
 
71
- def center
72
- body_root.center
69
+ def disposed?
70
+ swt_widget.is_disposed
71
+ end
72
+
73
+ def center_within_display
74
+ body_root.center_within_display
73
75
  end
74
76
 
75
77
  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,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 ||= {}
79
+ end
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
68
89
  end
69
-
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,27 +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
- attr_reader :body_root, :swt_widget, :parent, :swt_style, :options
163
+ attr_reader :body_root, :swt_widget, :parent, :parent_proxy, :swt_style, :options
145
164
 
146
165
  def initialize(parent, *swt_constants, options, &content)
147
166
  @parent = parent
167
+ @parent_proxy = @parent&.get_data('proxy')
148
168
  @swt_style = SWT::SWTProxy[*swt_constants]
149
169
  options ||= {}
150
170
  @options = self.class.options.merge(options)
151
171
  @content = Util::ProcTracker.new(content) if content
152
- execute_hooks('before_body')
172
+ execute_hook('before_body')
153
173
  body_block = self.class.instance_variable_get("@body_block")
154
174
  raise Glimmer::Error, 'Invalid custom widget for having no body! Please define body block!' if body_block.nil?
155
175
  @body_root = instance_exec(&body_block)
156
176
  raise Glimmer::Error, 'Invalid custom widget for having an empty body! Please fill body block!' if @body_root.nil?
157
177
  @swt_widget = @body_root.swt_widget
158
178
  @swt_widget.set_data('custom_widget', self)
159
- execute_hooks('after_body')
179
+ execute_hook('after_body')
160
180
  end
161
181
 
162
182
  # Subclasses may override to perform post initialization work on an added child
@@ -211,10 +231,10 @@ module Glimmer
211
231
 
212
232
  # This method ensures it has an instance method not coming from Glimmer DSL
213
233
  def has_instance_method?(method_name)
214
- respond_to?(method_name) and
234
+ respond_to?(method_name) and
215
235
  !swt_widget&.respond_to?(method_name) and
216
236
  (method(method_name) rescue nil) and
217
- !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
237
+ !method(method_name)&.source_location&.first&.include?('glimmer/dsl/engine.rb') and
218
238
  !method(method_name)&.source_location&.first&.include?('glimmer/swt/widget_proxy.rb')
219
239
  end
220
240
 
@@ -233,6 +253,10 @@ module Glimmer
233
253
  def has_style?(style)
234
254
  (swt_style & SWT::SWTProxy[style]) == SWT::SWTProxy[style]
235
255
  end
256
+
257
+ def pack(*args)
258
+ body_root.pack(*args)
259
+ end
236
260
 
237
261
  # TODO see if it is worth it to eliminate duplication of async_exec/sync_exec
238
262
  # delegation to DisplayProxy, via a module
@@ -265,22 +289,22 @@ module Glimmer
265
289
  end
266
290
  end
267
291
 
268
- alias local_respond_to? respond_to?
292
+ alias local_respond_to? respond_to?
269
293
  def respond_to?(method, *args, &block)
270
294
  super or
271
295
  can_handle_observation_request?(method) or
272
296
  body_root.respond_to?(method, *args, &block)
273
297
  end
274
-
298
+
275
299
  private
276
300
 
277
- def execute_hooks(hook_name)
278
- self.class.instance_variable_get("@#{hook_name}_blocks")&.each do |hook_block|
279
- temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
280
- singleton_class.define_method(temp_method_name, &hook_block)
281
- send(temp_method_name)
282
- singleton_class.send(:remove_method, temp_method_name)
283
- end
301
+ def execute_hook(hook_name)
302
+ hook_block = self.class.instance_variable_get("@#{hook_name}_block")
303
+ return if hook_block.nil?
304
+ temp_method_name = "#{hook_name}_block_#{hook_block.hash.abs}_#{(Time.now.to_f * 1_000_000).to_i}"
305
+ singleton_class.define_method(temp_method_name, &hook_block)
306
+ send(temp_method_name)
307
+ singleton_class.send(:remove_method, temp_method_name)
284
308
  end
285
309
  end
286
310
  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