glimmer-dsl-swt 4.20.0.1 → 4.20.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +17 -13
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +10 -7
  6. data/docs/reference/GLIMMER_SAMPLES.md +168 -49
  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 +3 -1
  10. data/lib/glimmer/data_binding/table_items_binding.rb +2 -2
  11. data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +6 -3
  12. data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +2 -2
  13. data/lib/glimmer/dsl/swt/widget_expression.rb +2 -0
  14. data/lib/glimmer/dsl/swt/widget_listener_expression.rb +3 -3
  15. data/lib/glimmer/rake_task/scaffold.rb +0 -2
  16. data/lib/glimmer/swt/combo_proxy.rb +48 -0
  17. data/lib/glimmer/swt/display_proxy.rb +11 -8
  18. data/lib/glimmer/swt/proxy_properties.rb +2 -1
  19. data/lib/glimmer/swt/table_proxy.rb +15 -8
  20. data/lib/glimmer/swt/tool_bar_proxy.rb +51 -0
  21. data/lib/glimmer/swt/widget_proxy.rb +8 -2
  22. data/lib/glimmer/ui/custom_shell.rb +3 -3
  23. data/lib/glimmer/ui/custom_widget.rb +5 -2
  24. data/samples/elaborate/calculator.rb +116 -0
  25. data/samples/elaborate/calculator/model/command.rb +105 -0
  26. data/samples/elaborate/calculator/model/command/all_clear.rb +17 -0
  27. data/samples/elaborate/calculator/model/command/command_history.rb +0 -0
  28. data/samples/elaborate/calculator/model/command/equals.rb +18 -0
  29. data/samples/elaborate/calculator/model/command/number.rb +20 -0
  30. data/samples/elaborate/calculator/model/command/operation.rb +27 -0
  31. data/samples/elaborate/calculator/model/command/operation/add.rb +15 -0
  32. data/samples/elaborate/calculator/model/command/operation/divide.rb +15 -0
  33. data/samples/elaborate/calculator/model/command/operation/multiply.rb +15 -0
  34. data/samples/elaborate/calculator/model/command/operation/subtract.rb +15 -0
  35. data/samples/elaborate/calculator/model/command/point.rb +20 -0
  36. data/samples/elaborate/calculator/model/presenter.rb +30 -0
  37. data/samples/elaborate/login.rb +15 -13
  38. data/samples/elaborate/tetris.rb +4 -4
  39. data/samples/elaborate/tetris/model/game.rb +0 -3
  40. data/samples/elaborate/timer.rb +233 -0
  41. data/samples/elaborate/timer/alarm1.wav +0 -0
  42. data/samples/elaborate/timer/sounds/alarm1.wav +0 -0
  43. data/samples/elaborate/user_profile.rb +4 -2
  44. data/samples/elaborate/weather.rb +164 -0
  45. data/samples/hello/hello_cool_bar.rb +147 -0
  46. data/samples/hello/hello_layout.rb +6 -2
  47. data/samples/hello/hello_shell.rb +205 -0
  48. data/samples/hello/hello_table.rb +5 -5
  49. data/samples/hello/hello_text.rb +120 -0
  50. data/samples/hello/hello_tool_bar.rb +143 -0
  51. metadata +25 -2
@@ -7,8 +7,9 @@
7
7
  - Widget properties are declared with underscored lowercase versions of the SWT properties
8
8
  - Widget property declarations always have arguments and never take a block
9
9
  - Widget property arguments are never wrapped inside parentheses
10
- - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
10
+ - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form. Their multi-line blocks rely on the `do; end` style.
11
11
  - Widget listeners are always followed by a block using curly braces (Only when declared in DSL. When invoked on widget object directly outside of GUI declarations, standard Ruby conventions apply)
12
12
  - Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
13
- - Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
14
- - Custom widgets receive additional arguments to SWT style called options. These are passed as the last argument inside the parentheses, a hash of option names pointing to values.
13
+ - Custom widget `body`, `before_body`, and `after_body` blocks open their blocks and close them with curly braces.
14
+ - Custom widgets receive additional keyword arguments called options, which come after the SWT styles.
15
+ - Pure logic multi-line blocks that do not constitute GUI DSL view elements (such as `Thread.new`, `loop`, `each` and `observe` blocks) rely on the `do; end` style to clearly separate logic code from view code.
Binary file
@@ -33,8 +33,10 @@ module Glimmer
33
33
 
34
34
  def <=>(other)
35
35
  if other.is_a?(Array)
36
+ args_clone = other.clone
37
+ @parent.editable = true if @parent.is_a?(Glimmer::SWT::TableProxy) # TODO consider a polymorphic way to perform this
36
38
  @parent.content {
37
- send(@parent_attribute, bind(*other))
39
+ send(@parent_attribute, bind(*args_clone))
38
40
  }
39
41
  else # || other.is_a?(Hash) # TODO support hash e.g. {model: model_obj, attribute: :some_attribute, more-options...}
40
42
  original_compare(other)
@@ -34,11 +34,11 @@ module Glimmer
34
34
  include_package 'org.eclipse.swt'
35
35
  include_package 'org.eclipse.swt.widgets'
36
36
 
37
- def initialize(parent, model_binding, column_properties)
37
+ def initialize(parent, model_binding, column_properties = nil)
38
38
  @table = parent
39
39
  @model_binding = model_binding
40
40
  @read_only_sort = @model_binding.binding_options[:read_only_sort]
41
- @column_properties = column_properties
41
+ @column_properties = @model_binding.binding_options[:column_properties] || @model_binding.binding_options[:column_attributes] || column_properties
42
42
  if @table.respond_to?(:column_properties=)
43
43
  @table.column_properties = @column_properties
44
44
  else # assume custom widget
@@ -29,12 +29,15 @@ module Glimmer
29
29
  module DSL
30
30
  module SWT
31
31
  class ShineDataBindingExpression < Expression
32
+ include_package 'org.eclipse.swt.widgets'
33
+
32
34
  def can_interpret?(parent, keyword, *args, &block)
33
35
  args.size == 0 and
34
36
  block.nil? and
35
- parent.respond_to?(:set_attribute) and
36
- parent.respond_to?(:has_attribute?) and
37
- parent.has_attribute?(keyword, *args) and
37
+ (
38
+ (parent.respond_to?(:set_attribute) and parent.respond_to?(:has_attribute?) and parent.has_attribute?(keyword)) or
39
+ (parent.respond_to?(:swt_widget) and parent.swt_widget.is_a?(Table))
40
+ ) and
38
41
  !parent.is_a?(Glimmer::UI::CustomWidget) and
39
42
  !parent.is_a?(Glimmer::UI::CustomShape) and
40
43
  !(parent.respond_to?(:swt_widget) && parent.swt_widget.class == org.eclipse.swt.widgets.Canvas && keyword == 'image')
@@ -35,10 +35,10 @@ module Glimmer
35
35
  block.nil? and
36
36
  parent.respond_to?(:swt_widget) and
37
37
  parent.swt_widget.is_a?(Table) and
38
- args.size == 2 and
38
+ args.size.between?(1, 2) and
39
39
  args[0].is_a?(DataBinding::ModelBinding) and
40
40
  args[0].evaluate_property.is_a?(Array) and
41
- args[1].is_a?(Array)
41
+ (args[1].nil? or args[1].is_a?(Array))
42
42
  end
43
43
 
44
44
  def interpret(parent, keyword, *args, &block)
@@ -70,3 +70,5 @@ require 'glimmer/swt/sash_form_proxy'
70
70
  require 'glimmer/swt/styled_text_proxy'
71
71
  require 'glimmer/swt/date_time_proxy'
72
72
  require 'glimmer/swt/tab_folder_proxy'
73
+ require 'glimmer/swt/combo_proxy'
74
+ require 'glimmer/swt/tool_bar_proxy'
@@ -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
@@ -441,8 +441,6 @@ module Glimmer
441
441
  require '#{file_name(app_name)}/view/app_view'
442
442
 
443
443
  class #{class_name(app_name)}
444
- include Glimmer
445
-
446
444
  APP_ROOT = File.expand_path('../..', __FILE__)
447
445
  VERSION = File.read(File.join(APP_ROOT, 'VERSION'))
448
446
  LICENSE = File.read(File.join(APP_ROOT, 'LICENSE.txt'))
@@ -0,0 +1,48 @@
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/widget_proxy'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ # Proxy for org.eclipse.swt.widgets.Combo
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class ComboProxy < WidgetProxy
30
+ attr_accessor :tool_item_proxy, :swt_tool_item
31
+
32
+ def initialize(*init_args, &block)
33
+ super
34
+ self.tool_item_proxy = WidgetProxy.new("tool_item", parent_proxy, [:separator]) if parent_proxy.swt_widget.is_a?(ToolBar)
35
+ self.swt_tool_item = tool_item_proxy&.swt_widget
36
+ end
37
+
38
+ def post_add_content
39
+ if self.tool_item_proxy
40
+ self.swt_widget.pack
41
+ self.tool_item_proxy.text = 'filler' # text seems needed (any text works)
42
+ self.tool_item_proxy.width = swt_widget.size.x
43
+ self.tool_item_proxy.control = swt_widget
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -42,7 +42,7 @@ module Glimmer
42
42
 
43
43
  OBSERVED_MENU_ITEMS = ['about', 'preferences', 'quit']
44
44
 
45
- class FilterListener
45
+ class ConcreteListener
46
46
  include org.eclipse.swt.widgets.Listener
47
47
 
48
48
  def initialize(&listener_block)
@@ -194,15 +194,17 @@ module Glimmer
194
194
  observation_request = observation_request.to_s
195
195
  if observation_request.start_with?('on_swt_')
196
196
  constant_name = observation_request.sub(/^on_swt_/, '')
197
- add_swt_event_filter(constant_name, &block)
197
+ swt_event_reg = add_swt_event_filter(constant_name, &block)
198
+ Glimmer::UI::CustomWidget.current_custom_widgets.last&.observer_registrations&.push(swt_event_reg)
199
+ swt_event_reg
198
200
  elsif observation_request.start_with?('on_')
199
201
  event_name = observation_request.sub(/^on_/, '')
200
202
  if OBSERVED_MENU_ITEMS.include?(event_name) && OS.mac?
201
203
  system_menu = swt_display.getSystemMenu
202
204
  menu_item = system_menu.getItems.find {|menu_item| menu_item.getID == SWTProxy["ID_#{event_name.upcase}"]}
203
- display_mac_event_registration = menu_item.addListener(SWTProxy[:Selection], &block)
204
- # TODO enable this code and test on the Mac to ensure automatic cleanup of mac event registrations in custom widgets
205
- # Glimmer::UI::CustomWidget.current_custom_widgets.last&.observer_registrations&.push(display_mac_event_registration)
205
+ listener = ConcreteListener.new(&block)
206
+ display_mac_event_registration = menu_item.addListener(SWTProxy[:Selection], listener)
207
+ Glimmer::UI::CustomWidget.current_custom_widgets.last&.observer_registrations&.push(display_mac_event_registration)
206
208
  display_mac_event_registration
207
209
  end
208
210
  end
@@ -210,15 +212,16 @@ module Glimmer
210
212
 
211
213
  def add_swt_event_filter(swt_constant, &block)
212
214
  event_type = SWTProxy[swt_constant]
213
- @swt_display.addFilter(event_type, FilterListener.new(&block))
215
+ swt_listener = ConcreteListener.new(&block)
216
+ @swt_display.addFilter(event_type, swt_listener)
214
217
  #WidgetListenerProxy.new(@swt_display.getListeners(event_type).last)
215
218
  WidgetListenerProxy.new(
216
219
  swt_display: @swt_display,
217
220
  event_type: event_type,
218
221
  filter: true,
219
- swt_listener: block,
222
+ swt_listener: swt_listener,
220
223
  widget_add_listener_method: 'addFilter',
221
- swt_listener_class: FilterListener,
224
+ swt_listener_class: ConcreteListener,
222
225
  swt_listener_method: 'handleEvent'
223
226
  )
224
227
  end
@@ -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
@@ -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
@@ -0,0 +1,51 @@
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/widget_proxy'
23
+
24
+ module Glimmer
25
+ module SWT
26
+ # Proxy for org.eclipse.swt.widgets.ToolBarProxy
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class ToolBarProxy < WidgetProxy
30
+ attr_accessor :cool_item_proxy, :swt_cool_item
31
+
32
+ def initialize(*init_args, &block)
33
+ super
34
+ self.cool_item_proxy = WidgetProxy.new("cool_item", parent_proxy, []) if parent_proxy.swt_widget.is_a?(CoolBar)
35
+ self.swt_cool_item = cool_item_proxy&.swt_widget
36
+ end
37
+
38
+ def post_add_content
39
+ apply_preferred_size if cool_item_proxy
40
+ end
41
+
42
+ def apply_preferred_size
43
+ swt_widget.pack
44
+ size = swt_widget.size
45
+ cool_item_proxy.control = swt_widget
46
+ preferred = swt_cool_item.computeSize(size.x, size.y)
47
+ swt_cool_item.setPreferredSize(preferred)
48
+ end
49
+ end
50
+ end
51
+ end
@@ -765,7 +765,7 @@ module Glimmer
765
765
 
766
766
  def can_add_listener?(underscored_listener_name)
767
767
  auto_exec do
768
- !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
768
+ @swt_widget && !self.class.find_listener(@swt_widget.getClass, underscored_listener_name).empty?
769
769
  end
770
770
  end
771
771
 
@@ -838,7 +838,7 @@ module Glimmer
838
838
  widget_listener_proxy = nil
839
839
  safe_block = lambda { |*args| block.call(*args) unless @swt_widget.isDisposed }
840
840
  @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)
841
+ WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
842
842
  end
843
843
  end
844
844
 
@@ -1006,6 +1006,12 @@ module Glimmer
1006
1006
  image: lambda do |*value|
1007
1007
  ImageProxy.create(*value).swt_image
1008
1008
  end,
1009
+ disabled_image: lambda do |*value|
1010
+ ImageProxy.create(*value).swt_image
1011
+ end,
1012
+ hot_image: lambda do |*value|
1013
+ ImageProxy.create(*value).swt_image
1014
+ end,
1009
1015
  images: lambda do |array|
1010
1016
  array.to_a.map do |value|
1011
1017
  ImageProxy.create(value).swt_image
@@ -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
 
@@ -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 bind(@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