glimmer-dsl-swt 4.20.0.4 → 4.20.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 (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +30 -0
  3. data/README.md +13 -7
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +5 -2
  6. data/docs/reference/GLIMMER_SAMPLES.md +13 -0
  7. data/docs/reference/GLIMMER_STYLE_GUIDE.md +4 -3
  8. data/glimmer-dsl-swt.gemspec +0 -0
  9. data/lib/glimmer/data_binding/shine.rb +2 -1
  10. data/lib/glimmer/data_binding/table_items_binding.rb +2 -2
  11. data/lib/glimmer/data_binding/tree_items_binding.rb +2 -2
  12. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +1 -1
  13. data/lib/glimmer/dsl/swt/radio_group_selection_data_binding_expression.rb +1 -1
  14. data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +6 -5
  15. data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +2 -2
  16. data/lib/glimmer/dsl/swt/tree_items_data_binding_expression.rb +17 -12
  17. data/lib/glimmer/dsl/swt/widget_listener_expression.rb +3 -3
  18. data/lib/glimmer/rake_task/scaffold.rb +2 -2
  19. data/lib/glimmer/swt/custom/animation.rb +6 -0
  20. data/lib/glimmer/swt/custom/checkbox_group.rb +1 -1
  21. data/lib/glimmer/swt/custom/radio_group.rb +2 -1
  22. data/lib/glimmer/swt/display_proxy.rb +11 -8
  23. data/lib/glimmer/swt/proxy_properties.rb +7 -6
  24. data/lib/glimmer/swt/table_proxy.rb +15 -8
  25. data/lib/glimmer/swt/widget_proxy.rb +5 -2
  26. data/lib/glimmer/ui/custom_shape.rb +1 -1
  27. data/lib/glimmer/ui/custom_shell.rb +3 -3
  28. data/lib/glimmer/ui/custom_widget.rb +6 -3
  29. data/samples/elaborate/calculator.rb +116 -0
  30. data/samples/elaborate/calculator/model/command.rb +105 -0
  31. data/samples/elaborate/calculator/model/command/all_clear.rb +17 -0
  32. data/samples/elaborate/calculator/model/command/command_history.rb +0 -0
  33. data/samples/elaborate/calculator/model/command/equals.rb +18 -0
  34. data/samples/elaborate/calculator/model/command/number.rb +20 -0
  35. data/samples/elaborate/calculator/model/command/operation.rb +27 -0
  36. data/samples/elaborate/calculator/model/command/operation/add.rb +15 -0
  37. data/samples/elaborate/calculator/model/command/operation/divide.rb +15 -0
  38. data/samples/elaborate/calculator/model/command/operation/multiply.rb +15 -0
  39. data/samples/elaborate/calculator/model/command/operation/subtract.rb +15 -0
  40. data/samples/elaborate/calculator/model/command/point.rb +20 -0
  41. data/samples/elaborate/calculator/model/presenter.rb +30 -0
  42. data/samples/elaborate/contact_manager.rb +2 -2
  43. data/samples/elaborate/meta_sample.rb +5 -5
  44. data/samples/elaborate/tetris.rb +6 -6
  45. data/samples/elaborate/tetris/view/bevel.rb +11 -11
  46. data/samples/elaborate/tetris/view/block.rb +1 -1
  47. data/samples/elaborate/tetris/view/high_score_dialog.rb +4 -4
  48. data/samples/elaborate/tetris/view/score_lane.rb +3 -3
  49. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +9 -9
  50. data/samples/elaborate/timer.rb +233 -0
  51. data/samples/elaborate/timer/alarm1.wav +0 -0
  52. data/samples/elaborate/timer/sounds/alarm1.wav +0 -0
  53. data/samples/elaborate/user_profile.rb +4 -2
  54. data/samples/elaborate/weather.rb +1 -1
  55. data/samples/hello/hello_canvas_animation_data_binding.rb +1 -1
  56. data/samples/hello/hello_checkbox_group.rb +1 -1
  57. data/samples/hello/hello_code_text.rb +3 -3
  58. data/samples/hello/hello_cool_bar.rb +5 -5
  59. data/samples/hello/hello_cursor.rb +1 -1
  60. data/samples/hello/hello_custom_shell.rb +1 -1
  61. data/samples/hello/hello_directory_dialog.rb +1 -1
  62. data/samples/hello/hello_radio_group.rb +2 -2
  63. data/samples/hello/hello_table.rb +4 -4
  64. data/samples/hello/hello_text.rb +120 -0
  65. data/samples/hello/hello_tool_bar.rb +5 -5
  66. data/samples/hello/hello_tree.rb +11 -11
  67. metadata +19 -2
@@ -33,6 +33,7 @@ module Glimmer
33
33
  # default implementation of attribute setters/getters
34
34
  # It tries swt_widget, swt_display, swt_image, and swt_dialog by default.
35
35
  def proxy_source_object
36
+ # TODO the logic here should not be needed if derived with polymorphism. Consider removing.
36
37
  if respond_to?(:swt_widget)
37
38
  swt_widget
38
39
  elsif respond_to?(:swt_display)
@@ -62,7 +63,7 @@ module Glimmer
62
63
 
63
64
  def has_attribute?(attribute_name, *args)
64
65
  Glimmer::SWT::DisplayProxy.instance.auto_exec do
65
- proxy_source_object&.respond_to?(attribute_setter(attribute_name), args) ||
66
+ proxy_source_object&.respond_to?(attribute_setter(attribute_name), args) or
66
67
  respond_to?(ruby_attribute_setter(attribute_name), args)
67
68
  end
68
69
  end
@@ -80,7 +81,7 @@ module Glimmer
80
81
  end
81
82
  end
82
83
  unless swt_widget_operation
83
- result = send(ruby_attribute_setter(attribute_name), args)
84
+ result = send(ruby_attribute_setter(attribute_name), args) if respond_to?(ruby_attribute_setter(attribute_name), args)
84
85
  end
85
86
  result
86
87
  end
@@ -101,10 +102,10 @@ module Glimmer
101
102
  end
102
103
  end
103
104
  unless swt_widget_operation
104
- result = if respond_to?(ruby_attribute_getter(attribute_name))
105
- send(ruby_attribute_getter(attribute_name))
106
- else
107
- send(attribute_name)
105
+ if respond_to?(ruby_attribute_getter(attribute_name))
106
+ result = send(ruby_attribute_getter(attribute_name))
107
+ elsif respond_to?(attribute_name)
108
+ result = send(attribute_name)
108
109
  end
109
110
  end
110
111
  result
@@ -250,19 +250,13 @@ module Glimmer
250
250
  alias editable? editable
251
251
 
252
252
  def initialize(underscored_widget_name, parent, args)
253
- @editable = args.delete(:editable)
253
+ editable_style = args.delete(:editable)
254
254
  super
255
255
  @table_editor = TableEditor.new(swt_widget)
256
256
  @table_editor.horizontalAlignment = SWTProxy[:left]
257
257
  @table_editor.grabHorizontal = true
258
258
  @table_editor.minimumHeight = 20
259
- if editable?
260
- content {
261
- on_mouse_up { |event|
262
- edit_table_item(event.table_item, event.column_index)
263
- }
264
- }
265
- end
259
+ self.editable = editable_style
266
260
  end
267
261
 
268
262
  def items
@@ -283,6 +277,19 @@ module Glimmer
283
277
  end
284
278
  end
285
279
 
280
+ def editable=(value)
281
+ @editable = value
282
+ if @editable
283
+ content {
284
+ @editable_on_mouse_up = on_mouse_up { |event|
285
+ edit_table_item(event.table_item, event.column_index)
286
+ }
287
+ }
288
+ else
289
+ @editable_on_mouse_up.deregister if @editable_on_mouse_up
290
+ end
291
+ end
292
+
286
293
  def sort_block=(comparator)
287
294
  @sort_block = comparator
288
295
  end
@@ -232,9 +232,12 @@ module Glimmer
232
232
  def has_attribute?(attribute_name, *args)
233
233
  # TODO test that attribute getter responds too
234
234
  widget_custom_attribute = widget_custom_attribute_mapping[attribute_name.to_s]
235
+ property_type_converter = property_type_converters[attribute_name.to_s.to_sym]
235
236
  auto_exec do
236
237
  if widget_custom_attribute
237
238
  @swt_widget.respond_to?(widget_custom_attribute[:setter][:name])
239
+ elsif property_type_converter
240
+ true
238
241
  else
239
242
  super
240
243
  end
@@ -765,7 +768,7 @@ module Glimmer
765
768
 
766
769
  def can_add_listener?(underscored_listener_name)
767
770
  auto_exec do
768
- !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
771
+ @swt_widget && !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
769
772
  end
770
773
  end
771
774
 
@@ -838,7 +841,7 @@ module Glimmer
838
841
  widget_listener_proxy = nil
839
842
  safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
840
843
  @swt_widget.addListener(event_type, &safe_block)
841
- widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
844
+ WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
842
845
  end
843
846
  end
844
847
 
@@ -248,7 +248,7 @@ module Glimmer
248
248
  # Otherwise, if a block is passed, it adds it as content to this custom shape
249
249
  def content(&block)
250
250
  if block_given?
251
- body_root.content(&block)
251
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::CustomShapeExpression.new, self.class.keyword, &block)
252
252
  else
253
253
  @content
254
254
  end
@@ -31,9 +31,9 @@ module Glimmer
31
31
  class << self
32
32
  def launch(*args, &content)
33
33
  auto_exec do
34
- @launched_custom_shell = send(keyword, *args, &content)
35
- @launched_custom_shell.swt_widget.set_data('launched', true)
36
- @launched_custom_shell.open
34
+ launched_custom_shell = send(keyword, *args, &content)
35
+ launched_custom_shell.swt_widget.set_data('launched', true)
36
+ launched_custom_shell.open
37
37
  end
38
38
  end
39
39
  end
@@ -186,8 +186,11 @@ module Glimmer
186
186
  @swt_widget.set_data('custom_widget', self)
187
187
  end
188
188
  execute_hook('after_body')
189
- @dispose_listener_registration = @body_root.on_widget_disposed do
190
- observer_registrations.each(&:deregister)
189
+ auto_exec do
190
+ @dispose_listener_registration = @body_root.on_widget_disposed do
191
+ observer_registrations.compact.each(&:deregister)
192
+ observer_registrations.clear
193
+ end
191
194
  end
192
195
  end
193
196
 
@@ -293,7 +296,7 @@ module Glimmer
293
296
  # Otherwise, if a block is passed, it adds it as content to this custom widget
294
297
  def content(&block)
295
298
  if block_given?
296
- body_root.content(&block)
299
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::CustomWidgetExpression.new, self.class.keyword, &block)
297
300
  else
298
301
  @content
299
302
  end
@@ -0,0 +1,116 @@
1
+ require 'glimmer-dsl-swt'
2
+ require 'bigdecimal'
3
+
4
+ require_relative 'calculator/model/presenter'
5
+
6
+ # This sample demonstrates use of MVP (Model-View-Presenter) Architectural Pattern
7
+ # to data-bind View widgets to object-oriented Models taking advantage of design patterns
8
+ # like Command Design Pattern.
9
+ class Calculator
10
+ include Glimmer::UI::CustomShell
11
+
12
+ BUTTON_FONT = {height: 14}
13
+ BUTTON_FONT_OPERATION = {height: 18}
14
+ BUTTON_FONT_BIG = {height: 28}
15
+
16
+ attr_reader :presenter
17
+
18
+ before_body {
19
+ @presenter = Model::Presenter.new
20
+
21
+ Display.setAppName('Glimmer Calculator')
22
+
23
+ display {
24
+ on_swt_keydown { |key_event|
25
+ char = key_event.character.chr rescue nil
26
+ @presenter.press(char)
27
+ }
28
+
29
+ on_about {
30
+ display_about_dialog
31
+ }
32
+ }
33
+ }
34
+
35
+ body {
36
+ shell {
37
+ grid_layout 4, true
38
+
39
+ minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
40
+ text "Glimmer Calculator"
41
+
42
+ on_shell_closed do
43
+ @presenter.purge_command_history
44
+ end
45
+
46
+ # Setting styled_text to multi in order for alignment options to activate
47
+ styled_text(:multi, :wrap, :border) {
48
+ text <= [@presenter, :result]
49
+ alignment swt(:right)
50
+ right_margin 5
51
+ font height: 40
52
+ layout_data(:fill, :fill, true, true) {
53
+ horizontal_span 4
54
+ }
55
+ editable false
56
+ caret nil
57
+ }
58
+ command_button('AC')
59
+ operation_button('÷')
60
+ operation_button('×')
61
+ operation_button('−')
62
+ (7..9).each { |number|
63
+ number_button(number)
64
+ }
65
+ operation_button('+', font: BUTTON_FONT_BIG, vertical_span: 2)
66
+ (4..6).each { |number|
67
+ number_button(number)
68
+ }
69
+ (1..3).each { |number|
70
+ number_button(number)
71
+ }
72
+ command_button('=', font: BUTTON_FONT_BIG, vertical_span: 2)
73
+ number_button(0, horizontal_span: 2)
74
+ operation_button('.')
75
+ }
76
+ }
77
+
78
+ def number_button(number, options = {})
79
+ command_button(number, options)
80
+ end
81
+
82
+ def operation_button(operation, options = {})
83
+ command_button(operation, options.merge(font: BUTTON_FONT_OPERATION))
84
+ end
85
+
86
+ def command_button(command, options = {})
87
+ command = command.to_s
88
+ options[:font] ||= BUTTON_FONT
89
+ options[:horizontal_span] ||= 1
90
+ options[:vertical_span] ||= 1
91
+
92
+ button { |proxy|
93
+ text command
94
+ font options[:font]
95
+
96
+ layout_data(:fill, :fill, true, true) {
97
+ horizontal_span options[:horizontal_span]
98
+ vertical_span options[:vertical_span]
99
+ }
100
+
101
+ on_widget_selected {
102
+ @presenter.press(command)
103
+ }
104
+ }
105
+ end
106
+
107
+ def display_about_dialog
108
+ message_box(body_root) {
109
+ text 'About'
110
+ message "Glimmer - Calculator\n\nCopyright (c) 2007-2021 Andy Maleh"
111
+ }.open
112
+ end
113
+
114
+ end
115
+
116
+ Calculator.launch
@@ -0,0 +1,105 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class << self
5
+ attr_accessor :number1, :number2, :operation
6
+
7
+ # Keyword string representing calculator command (e.g. '+' for Add command)
8
+ # Subclasses must call to define a single keyword
9
+ def keyword(keyword_text)
10
+ Command.keyword_to_command_class_mapping[keyword_text] = self
11
+ end
12
+
13
+ # Keyword string array representing calculator command (e.g. ('0'..'9').to_a)
14
+ # Subclasses must call to define multiple keywords
15
+ def keywords(*keyword_text_array)
16
+ keyword_text_array.flatten.each do |keyword_text|
17
+ keyword(keyword_text)
18
+ end
19
+ end
20
+
21
+ def keyword_to_command_class_mapping
22
+ @keyword_to_command_class_mapping ||= {}
23
+ end
24
+
25
+ def command_history
26
+ @command_history ||= []
27
+ end
28
+
29
+ def purge_command_history
30
+ Command.command_history.clear
31
+ self.number1 = nil
32
+ self.number2 = nil
33
+ self.operation = nil
34
+ end
35
+
36
+ def for(button)
37
+ command_class = keyword_to_command_class_mapping[button]
38
+ command_class&.new(button)&.tap do |command|
39
+ command.execute
40
+ command_history << command
41
+ end
42
+ end
43
+ end
44
+
45
+ attr_reader :button
46
+ attr_accessor :result
47
+
48
+ def initialize(button)
49
+ @button = button
50
+ end
51
+
52
+ def number1
53
+ Command.number1
54
+ end
55
+
56
+ def number1=(value)
57
+ Command.number1 = value.to_f
58
+ end
59
+
60
+ def number2
61
+ Command.number2
62
+ end
63
+
64
+ def number2=(value)
65
+ Command.number2 = value.to_f
66
+ end
67
+
68
+ def operation
69
+ Command.operation
70
+ end
71
+
72
+ def operation=(op)
73
+ Command.operation = op
74
+ end
75
+
76
+ def last_result
77
+ last_command&.result
78
+ end
79
+
80
+ def last_command
81
+ command_history.last
82
+ end
83
+
84
+ def command_history
85
+ Command.command_history
86
+ end
87
+
88
+ def execute
89
+ raise 'Not implemented! Please override in a subclass.'
90
+ end
91
+ end
92
+ end
93
+ end
94
+
95
+ # Dir[File.join(File.dirname(__FILE__), 'command', '**', '*.rb')].each {|f| require(f)} # disabled for Opal compatibility
96
+
97
+ require_relative 'command/all_clear'
98
+ require_relative 'command/equals'
99
+ require_relative 'command/number'
100
+ require_relative 'command/operation'
101
+ require_relative 'command/point'
102
+ require_relative 'command/operation/add'
103
+ require_relative 'command/operation/divide'
104
+ require_relative 'command/operation/multiply'
105
+ require_relative 'command/operation/subtract'
@@ -0,0 +1,17 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class AllClear < Command
5
+ keywords 'AC', 'c', 'C', 8.chr, 27.chr, 127.chr
6
+
7
+ def execute
8
+ self.result = '0'
9
+ self.number1 = nil
10
+ self.number2 = nil
11
+ self.operation = nil
12
+ command_history.clear
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,18 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Equals < Command
5
+ keywords '=', "\r"
6
+
7
+ def execute
8
+ if number1 && number2 && operation
9
+ self.result = operation.calculate.to_s
10
+ self.number1 = self.result
11
+ else
12
+ self.result = last_result || '0'
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ class Calculator
2
+ module Model
3
+ class Command
4
+ class Number < Command
5
+ keywords ('0'..'9').to_a
6
+
7
+ def execute
8
+ self.result = last_result.nil? || (!last_command.is_a?(Number) && !last_command.is_a?(Point)) ? button : last_result + button
9
+ if operation.nil? || last_command.is_a?(Equals)
10
+ self.number1 = self.result
11
+ self.number2 = nil
12
+ self.operation = nil
13
+ else
14
+ self.number2 = self.result
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end