glimmer-dsl-swt 4.18.2.5 → 4.18.3.4

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 (44) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +56 -0
  3. data/README.md +344 -35
  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/dsl.rb +1 -0
  10. data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
  11. data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
  12. data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
  13. data/lib/glimmer/dsl/swt/multiply_expression.rb +53 -0
  14. data/lib/glimmer/dsl/swt/property_expression.rb +4 -2
  15. data/{samples/elaborate/tetris/view/game_over_dialog.rb → lib/glimmer/dsl/swt/transform_expression.rb} +29 -46
  16. data/lib/glimmer/swt/custom/drawable.rb +4 -3
  17. data/lib/glimmer/swt/custom/shape.rb +37 -14
  18. data/lib/glimmer/swt/directory_dialog_proxy.rb +3 -3
  19. data/lib/glimmer/swt/display_proxy.rb +26 -5
  20. data/lib/glimmer/swt/file_dialog_proxy.rb +3 -3
  21. data/lib/glimmer/swt/image_proxy.rb +68 -1
  22. data/lib/glimmer/swt/shell_proxy.rb +23 -3
  23. data/lib/glimmer/swt/table_proxy.rb +31 -7
  24. data/lib/glimmer/swt/transform_proxy.rb +109 -0
  25. data/lib/glimmer/swt/widget_listener_proxy.rb +14 -5
  26. data/lib/glimmer/swt/widget_proxy.rb +7 -3
  27. data/lib/glimmer/ui/custom_shell.rb +11 -9
  28. data/lib/glimmer/ui/custom_widget.rb +32 -17
  29. data/samples/elaborate/meta_sample.rb +81 -24
  30. data/samples/elaborate/tetris.rb +146 -42
  31. data/samples/elaborate/tetris/model/game.rb +258 -137
  32. data/samples/elaborate/tetris/model/past_game.rb +26 -0
  33. data/samples/elaborate/tetris/model/tetromino.rb +45 -29
  34. data/samples/elaborate/tetris/view/block.rb +8 -13
  35. data/samples/elaborate/tetris/view/high_score_dialog.rb +133 -0
  36. data/samples/elaborate/tetris/view/playfield.rb +1 -1
  37. data/samples/elaborate/tetris/view/score_lane.rb +11 -11
  38. data/samples/elaborate/tetris/view/tetris_menu_bar.rb +139 -0
  39. data/samples/elaborate/tic_tac_toe.rb +4 -4
  40. data/samples/hello/hello_canvas.rb +4 -4
  41. data/samples/hello/hello_canvas_animation.rb +3 -3
  42. data/samples/hello/hello_canvas_transform.rb +40 -0
  43. data/samples/hello/hello_link.rb +1 -1
  44. metadata +11 -5
@@ -35,11 +35,12 @@ module Glimmer
35
35
  end
36
36
  end
37
37
 
38
- def resetup_shape_paint_listeners
39
- # TODO consider performance optimizations relating to order of shape rendering (affecting only further shapes not previous ones)
38
+ def resetup_shape_painting
39
+ # TODO consider performance optimization relating to order of shape rendering (affecting only further shapes not previous ones)
40
+ reset_gc if respond_to?(:reset_gc)
40
41
  shapes.each do |shape|
41
42
  shape.paint_listener_proxy&.unregister
42
- shape.setup_paint_listener
43
+ shape.setup_painting
43
44
  end
44
45
  end
45
46
  end
@@ -24,12 +24,12 @@ 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
30
31
  module Custom
31
32
  # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
32
- # swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
33
33
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
34
34
  class Shape
35
35
  include Packages
@@ -77,9 +77,11 @@ module Glimmer
77
77
  def flyweight_method_names
78
78
  @flyweight_method_names ||= {}
79
79
  end
80
+
81
+
80
82
  end
81
83
 
82
- attr_reader :parent, :name, :args, :options, :swt_widget, :paint_listener_proxy
84
+ attr_reader :parent, :name, :args, :options, :paint_listener_proxy
83
85
 
84
86
  def initialize(parent, keyword, *args, &property_block)
85
87
  @parent = parent
@@ -87,7 +89,6 @@ module Glimmer
87
89
  @method_name = self.class.method_name(keyword, args)
88
90
  @options = self.class.arg_options(args, extract: true)
89
91
  @args = args
90
- @swt_widget = parent.respond_to?(:swt_display) ? parent.swt_display : parent.swt_widget
91
92
  @properties = {}
92
93
  @parent.shapes << self
93
94
  post_add_content if property_block.nil?
@@ -110,11 +111,13 @@ module Glimmer
110
111
  end
111
112
 
112
113
  def post_add_content
113
- setup_paint_listener
114
+ amend_method_name_options_based_on_properties
115
+ setup_painting
114
116
  @content_added = true
115
117
  end
116
118
 
117
119
  def apply_property_arg_conversions(method_name, property, args)
120
+ args = args.dup
118
121
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
119
122
  if (args.first.is_a?(Symbol) || args.first.is_a?(String))
120
123
  if the_java_method.parameter_types.first == Color.java_class
@@ -133,6 +136,9 @@ module Glimmer
133
136
  if args.first.is_a?(FontProxy)
134
137
  args[0] = args[0].swt_font
135
138
  end
139
+ if args.first.is_a?(TransformProxy)
140
+ args[0] = args[0].swt_transform
141
+ end
136
142
  if ['setBackgroundPattern', 'setForegroundPattern'].include?(method_name.to_s)
137
143
  args.each_with_index do |arg, i|
138
144
  if arg.is_a?(Symbol) || arg.is_a?(String)
@@ -145,7 +151,7 @@ module Glimmer
145
151
  args[0] = org.eclipse.swt.graphics.Pattern.new(*new_args)
146
152
  args[1..-1] = []
147
153
  end
148
- @properties[property] = args
154
+ args
149
155
  end
150
156
 
151
157
  def apply_shape_arg_conversions(method_name, args)
@@ -181,6 +187,15 @@ module Glimmer
181
187
  args[the_java_method_arg_count..-1] = []
182
188
  end
183
189
  end
190
+
191
+ def amend_method_name_options_based_on_properties
192
+ if @properties.keys.map(&:to_s).include?('background') && !@properties.keys.map(&:to_s).include?('foreground')
193
+ @options[:fill] = true
194
+ elsif @properties.keys.map(&:to_s).include?('foreground') && !@properties.keys.map(&:to_s).include?('background')
195
+ @options[:fill] = false
196
+ end
197
+ @method_name = self.class.method_name(@name, @args + [@options])
198
+ end
184
199
 
185
200
  def has_attribute?(attribute_name, *args)
186
201
  self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
@@ -188,32 +203,40 @@ module Glimmer
188
203
 
189
204
  def set_attribute(attribute_name, *args)
190
205
  @properties[attribute_name] = args
191
- if @content_added
192
- @parent.resetup_shape_paint_listeners
206
+ if @content_added && !@parent.is_disposed
207
+ @parent.resetup_shape_painting
193
208
  @parent.redraw
194
209
  end
195
210
  end
196
-
211
+
197
212
  def get_attribute(attribute_name)
198
213
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
199
214
  end
200
215
 
201
- def setup_paint_listener
216
+ def setup_painting
217
+ # TODO consider moving this method to parent (making the logic polymorphic)
218
+ return if @parent.is_disposed
202
219
  if parent.respond_to?(:swt_display)
203
220
  @paint_listener_proxy = @parent.on_swt_paint(&method(:paint))
204
- else
221
+ elsif parent.respond_to?(:swt_image)
222
+ paint(parent) # treat parent as paint event since you don't do repaints with images, it's a one time deal.
223
+ elsif parent.respond_to?(:swt_widget)
205
224
  @paint_listener_proxy = @parent.on_paint_control(&method(:paint))
206
225
  end
207
226
  end
208
227
 
209
228
  def paint(paint_event)
210
229
  @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
211
- @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
212
- @properties['font'] = [@parent.font] if draw? && !@properties.keys.map(&:to_s).include?('font')
230
+ @properties['foreground'] = [@parent.foreground] if @parent.respond_to?(:foreground) && draw? && !@properties.keys.map(&:to_s).include?('foreground')
231
+ @properties['font'] = [@parent.font] if @parent.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
232
+ @properties['transform'] = [nil] if @parent.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
213
233
  @properties.each do |property, args|
214
234
  method_name = attribute_setter(property)
215
- apply_property_arg_conversions(method_name, property, args)
216
- paint_event.gc.send(method_name, *args)
235
+ converted_args = apply_property_arg_conversions(method_name, property, args)
236
+ paint_event.gc.send(method_name, *converted_args)
237
+ if property == 'transform' && args.first.is_a?(TransformProxy)
238
+ args.first.swt_transform.dispose
239
+ end
217
240
  end
218
241
  apply_shape_arg_conversions(@method_name, @args)
219
242
  apply_shape_arg_defaults(@method_name, @args)
@@ -47,13 +47,13 @@ module Glimmer
47
47
  style_arg_last_index = args.index(style_args.last)
48
48
  args[style_arg_start_index..style_arg_last_index] = SWTProxy[style_args]
49
49
  end
50
+ if args.first.respond_to?(:swt_widget) && args.first.swt_widget.is_a?(Shell)
51
+ args[0] = args[0].swt_widget
52
+ end
50
53
  if !args.first.is_a?(Shell)
51
54
  current_shell = DisplayProxy.instance.swt_display.shells.first
52
55
  args.unshift(current_shell.nil? ? ShellProxy.new : current_shell)
53
56
  end
54
- if args.first.is_a?(ShellProxy)
55
- args[0] = args[0].swt_widget
56
- end
57
57
  parent = args[0]
58
58
  @parent_proxy = parent.is_a?(Shell) ? ShellProxy.new(swt_widget: parent) : parent
59
59
  @swt_widget = DirectoryDialog.new(*args)
@@ -39,8 +39,20 @@ module Glimmer
39
39
  include_package 'org.eclipse.swt.widgets'
40
40
 
41
41
  include Custom::Drawable
42
-
43
- OBSERVED_MENU_ITEMS = ['about', 'preferences']
42
+
43
+ OBSERVED_MENU_ITEMS = ['about', 'preferences', 'quit']
44
+
45
+ class FilterListener
46
+ include org.eclipse.swt.widgets.Listener
47
+
48
+ def initialize(&listener_block)
49
+ @listener_block = listener_block
50
+ end
51
+
52
+ def handleEvent(event)
53
+ @listener_block.call(event)
54
+ end
55
+ end
44
56
 
45
57
  class << self
46
58
  # Returns singleton instance
@@ -111,7 +123,7 @@ module Glimmer
111
123
  observation_request = observation_request.to_s
112
124
  if observation_request.start_with?('on_swt_')
113
125
  constant_name = observation_request.sub(/^on_swt_/, '')
114
- add_swt_event_listener(constant_name, &block)
126
+ add_swt_event_filter(constant_name, &block)
115
127
  elsif observation_request.start_with?('on_')
116
128
  event_name = observation_request.sub(/^on_/, '')
117
129
  if OBSERVED_MENU_ITEMS.include?(event_name)
@@ -124,10 +136,19 @@ module Glimmer
124
136
  end
125
137
  end
126
138
 
127
- def add_swt_event_listener(swt_constant, &block)
139
+ def add_swt_event_filter(swt_constant, &block)
128
140
  event_type = SWTProxy[swt_constant]
129
- @swt_display.addFilter(event_type, &block)
141
+ @swt_display.addFilter(event_type, FilterListener.new(&block))
130
142
  #WidgetListenerProxy.new(@swt_display.getListeners(event_type).last)
143
+ WidgetListenerProxy.new(
144
+ swt_display: @swt_display,
145
+ event_type: event_type,
146
+ filter: true,
147
+ swt_listener: block,
148
+ widget_add_listener_method: 'addFilter',
149
+ swt_listener_class: FilterListener,
150
+ swt_listener_method: 'handleEvent'
151
+ )
131
152
  end
132
153
  end
133
154
  end
@@ -48,13 +48,13 @@ module Glimmer
48
48
  style_arg_last_index = args.index(style_args.last)
49
49
  args[style_arg_start_index..style_arg_last_index] = SWTProxy[style_args]
50
50
  end
51
+ if args.first.respond_to?(:swt_widget) && args.first.swt_widget.is_a?(Shell)
52
+ args[0] = args[0].swt_widget
53
+ end
51
54
  if !args.first.is_a?(Shell)
52
55
  current_shell = DisplayProxy.instance.swt_display.shells.first
53
56
  args.unshift(current_shell.nil? ? ShellProxy.new : current_shell)
54
57
  end
55
- if args.first.is_a?(ShellProxy)
56
- args[0] = args[0].swt_widget
57
- end
58
58
  parent = args[0]
59
59
  @parent_proxy = parent.is_a?(Shell) ? ShellProxy.new(swt_widget: parent) : parent
60
60
  @swt_widget = FileDialog.new(*args)
@@ -19,6 +19,9 @@
19
19
  # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
20
  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
21
 
22
+ require 'glimmer/swt/custom/drawable'
23
+ require 'glimmer/swt/properties'
24
+
22
25
  module Glimmer
23
26
  module SWT
24
27
  # Proxy for org.eclipse.swt.graphics.Image
@@ -27,6 +30,9 @@ module Glimmer
27
30
  #
28
31
  # Follows the Proxy Design Pattern
29
32
  class ImageProxy
33
+ include Custom::Drawable
34
+ include Properties
35
+
30
36
  class << self
31
37
  def create(*args)
32
38
  if args.size == 1 && args.first.is_a?(ImageProxy)
@@ -37,6 +43,7 @@ module Glimmer
37
43
  end
38
44
  end
39
45
 
46
+ include_package 'org.eclipse.swt.widgets'
40
47
  include_package 'org.eclipse.swt.graphics'
41
48
 
42
49
  attr_reader :file_path, :jar_file_path, :image_data, :swt_image
@@ -46,8 +53,13 @@ module Glimmer
46
53
  # Takes the same args as the SWT Image class
47
54
  # Alternatively, takes a file path string or a uri:classloader file path string (generated by JRuby when invoking `File.expand_path` inside a JAR file)
48
55
  # and returns an image object.
49
- def initialize(*args)
56
+ def initialize(*args, &content)
50
57
  @args = args
58
+ @parent_proxy = nil
59
+ if @args.first.is_a?(WidgetProxy)
60
+ @parent_proxy = @args.shift
61
+ @parent = @parent_proxy.swt_widget
62
+ end
51
63
  options = @args.last.is_a?(Hash) ? @args.delete_at(-1) : {}
52
64
  options[:swt_image] = @args.first if @args.size == 1 && @args.first.is_a?(Image)
53
65
  @file_path = @args.first if @args.size == 1 && @args.first.is_a?(String)
@@ -55,6 +67,9 @@ module Glimmer
55
67
  if options&.keys&.include?(:swt_image)
56
68
  @swt_image = options[:swt_image]
57
69
  @original_image_data = @image_data = @swt_image.image_data
70
+ elsif args.size == 1 && args.first.is_a?(ImageProxy)
71
+ @swt_image = @args.first.swt_image
72
+ @original_image_data = @image_data = @swt_image.image_data
58
73
  elsif @file_path
59
74
  @original_image_data = @image_data = ImageData.new(input_stream || @file_path)
60
75
  @swt_image = Image.new(DisplayProxy.instance.swt_display, @image_data)
@@ -64,9 +79,15 @@ module Glimmer
64
79
  width = (@image_data.width.to_f / @image_data.height.to_f)*height.to_f if !height.nil? && width.nil?
65
80
  scale_to(width, height) unless width.nil? || height.nil?
66
81
  else
82
+ @args.prepend(DisplayProxy.instance.swt_display) unless @args.first.is_a?(Display)
67
83
  @swt_image = Image.new(*@args)
68
84
  @original_image_data = @image_data = @swt_image.image_data
69
85
  end
86
+ post_add_content if content.nil?
87
+ end
88
+
89
+ def post_add_content
90
+ @parent&.image = swt_image
70
91
  end
71
92
 
72
93
  def input_stream
@@ -91,6 +112,52 @@ module Glimmer
91
112
  self
92
113
  end
93
114
 
115
+ def gc
116
+ @gc ||= reset_gc
117
+ end
118
+
119
+ def reset_gc
120
+ @gc = org.eclipse.swt.graphics.GC.new(swt_image)
121
+ end
122
+
123
+ def has_attribute?(attribute_name, *args)
124
+ @swt_image.respond_to?(attribute_setter(attribute_name), args) || respond_to?(ruby_attribute_setter(attribute_name), args)
125
+ end
126
+
127
+ def set_attribute(attribute_name, *args)
128
+ # TODO consider refactoring/unifying this code with WidgetProxy and elsewhere
129
+ if args.count == 1
130
+ if args.first.is_a?(Symbol) || args.first.is_a?(String)
131
+ args[0] = ColorProxy.new(args.first).swt_color
132
+ end
133
+ if args.first.is_a?(ColorProxy)
134
+ args[0] = args.first.swt_color
135
+ end
136
+ end
137
+
138
+ if @swt_image.respond_to?(attribute_setter(attribute_name))
139
+ @swt_image.send(attribute_setter(attribute_name), *args) unless @swt_image.send(attribute_getter(attribute_name)) == args.first
140
+ elsif @swt_image.respond_to?(ruby_attribute_setter(attribute_name))
141
+ @swt_image.send(ruby_attribute_setter(attribute_name), args)
142
+ else
143
+ send(ruby_attribute_setter(attribute_name), args)
144
+ end
145
+ end
146
+
147
+ def get_attribute(attribute_name)
148
+ if @swt_image.respond_to?(attribute_getter(attribute_name))
149
+ @swt_image.send(attribute_getter(attribute_name))
150
+ elsif @swt_image.respond_to?(ruby_attribute_getter(attribute_name))
151
+ @swt_image.send(ruby_attribute_getter(attribute_name))
152
+ elsif @swt_image.respond_to?(attribute_name)
153
+ @swt_image.send(attribute_name)
154
+ elsif respond_to?(ruby_attribute_getter(attribute_name))
155
+ send(ruby_attribute_getter(attribute_name))
156
+ else
157
+ send(attribute_name)
158
+ end
159
+ end
160
+
94
161
  def method_missing(method, *args, &block)
95
162
  swt_image.send(method, *args, &block)
96
163
  rescue => e
@@ -45,11 +45,17 @@ module Glimmer
45
45
  if swt_widget
46
46
  @swt_widget = swt_widget
47
47
  else
48
- if args.first.is_a?(ShellProxy)
48
+ if args.first.respond_to?(:swt_widget) && args.first.swt_widget.is_a?(Shell)
49
49
  @parent_proxy = args[0]
50
50
  args[0] = args[0].swt_widget
51
51
  end
52
- style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
52
+ style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}.map(&:to_sym)
53
+ fill_screen = nil
54
+ if style_args.include?(:fill_screen)
55
+ args.delete(:fill_screen)
56
+ style_args.delete(:fill_screen)
57
+ fill_screen = true
58
+ end
53
59
  if style_args.any?
54
60
  style_arg_start_index = args.index(style_args.first)
55
61
  style_arg_last_index = args.index(style_args.last)
@@ -67,6 +73,7 @@ module Glimmer
67
73
  # TODO make this an option not the default
68
74
  shell_swt_display = Glimmer::SWT::DisplayProxy.instance.swt_display
69
75
  on_swt_show do
76
+ @swt_widget.set_size(@display.bounds.width, @display.bounds.height) if fill_screen
70
77
  Thread.new do
71
78
  sleep(0.25)
72
79
  shell_swt_display.async_exec do
@@ -110,14 +117,27 @@ module Glimmer
110
117
  def nested?
111
118
  !swt_widget&.parent.nil?
112
119
  end
120
+ alias nested nested?
121
+
122
+ def disposed?
123
+ swt_widget.isDisposed
124
+ end
125
+ alias disposed disposed?
113
126
 
127
+ # Hides shell. Automatically checks if widget is disposed to avoid crashing.
114
128
  def hide
115
- @swt_widget.setVisible(false)
129
+ @swt_widget.setVisible(false) unless @swt_widget.isDisposed
130
+ end
131
+
132
+ # Closes shell. Automatically checks if widget is disposed to avoid crashing.
133
+ def close
134
+ @swt_widget.close unless @swt_widget.isDisposed
116
135
  end
117
136
 
118
137
  def visible?
119
138
  @swt_widget.isDisposed ? false : @swt_widget.isVisible
120
139
  end
140
+ alias visible visible?
121
141
 
122
142
  # Setting to true opens/shows shell. Setting to false hides the shell.
123
143
  def visible=(visibility)
@@ -265,10 +265,18 @@ module Glimmer
265
265
  end
266
266
  end
267
267
 
268
+ def items
269
+ swt_widget.get_items
270
+ end
271
+
268
272
  def model_binding
269
273
  swt_widget.data
270
274
  end
271
275
 
276
+ def table_items_binding
277
+ swt_widget.get_data('table_items_binding')
278
+ end
279
+
272
280
  def sort_block=(comparator)
273
281
  @sort_block = comparator
274
282
  end
@@ -353,9 +361,9 @@ module Glimmer
353
361
  @additional_sort_properties = args unless args.empty?
354
362
  end
355
363
 
356
- def sort!
364
+ def sort!(internal_sort: false)
357
365
  return unless sort_property && (sort_type || sort_block || sort_by_block)
358
- array = model_binding.evaluate_property
366
+ original_array = array = model_binding.evaluate_property
359
367
  array = array.sort_by(&:hash) # this ensures consistent subsequent sorting in case there are equivalent sorts to avoid an infinite loop
360
368
  # Converting value to_s first to handle nil cases. Should work with numeric, boolean, and date fields
361
369
  if sort_block
@@ -379,7 +387,12 @@ module Glimmer
379
387
  end
380
388
  end
381
389
  sorted_array = sorted_array.reverse if sort_direction == :descending
382
- model_binding.call(sorted_array)
390
+ if model_binding.binding_options.symbolize_keys[:read_only_sort]
391
+ table_items_binding.call(sorted_array, internal_sort: true) unless internal_sort
392
+ else
393
+ model_binding.call(sorted_array)
394
+ end
395
+ sorted_array
383
396
  end
384
397
 
385
398
  def editor=(args)
@@ -445,7 +458,6 @@ module Glimmer
445
458
  end
446
459
 
447
460
  def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
448
- require 'facets/hash/symbolize_keys'
449
461
  return if table_item.nil?
450
462
  model = table_item.data
451
463
  property = column_properties[column_index]
@@ -467,7 +479,11 @@ module Glimmer
467
479
  @cancel_in_progress = true
468
480
  @table_editor_widget_proxy&.swt_widget&.dispose
469
481
  @table_editor_widget_proxy = nil
470
- after_cancel&.call
482
+ if after_cancel&.arity == 0
483
+ after_cancel&.call
484
+ else
485
+ after_cancel&.call(table_item)
486
+ end
471
487
  @edit_in_progress = false
472
488
  @cancel_in_progress = false
473
489
  @cancel_edit = nil
@@ -484,14 +500,22 @@ module Glimmer
484
500
  if new_value == model.send(model_editing_property)
485
501
  @cancel_edit.call
486
502
  else
487
- before_write&.call
503
+ if before_write&.arity == 0
504
+ before_write&.call
505
+ else
506
+ before_write&.call(edited_table_item)
507
+ end
488
508
  model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
489
509
  # Table refresh happens here because of model update triggering observers, so must retrieve table item again
490
510
  edited_table_item = search { |ti| ti.getData == model }.first
491
511
  swt_widget.showItem(edited_table_item)
492
512
  @table_editor_widget_proxy&.swt_widget&.dispose
493
513
  @table_editor_widget_proxy = nil
494
- after_write&.call(edited_table_item)
514
+ if after_write&.arity == 0
515
+ after_write&.call
516
+ else
517
+ after_write&.call(edited_table_item)
518
+ end
495
519
  @edit_in_progress = false
496
520
  end
497
521
  end