glimmer-dsl-swt 4.18.3.0 → 4.18.3.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -0
  3. data/README.md +440 -18
  4. data/VERSION +1 -1
  5. data/glimmer-dsl-swt.gemspec +11 -7
  6. data/lib/ext/glimmer/config.rb +24 -7
  7. data/lib/ext/rouge/themes/glimmer.rb +29 -0
  8. data/lib/glimmer-dsl-swt.rb +0 -1
  9. data/lib/glimmer/data_binding/table_items_binding.rb +8 -5
  10. data/lib/glimmer/data_binding/widget_binding.rb +22 -4
  11. data/lib/glimmer/dsl/swt/image_expression.rb +14 -6
  12. data/lib/glimmer/dsl/swt/layout_data_expression.rb +4 -4
  13. data/lib/glimmer/dsl/swt/layout_expression.rb +5 -3
  14. data/lib/glimmer/swt/custom/code_text.rb +132 -37
  15. data/lib/glimmer/swt/custom/drawable.rb +3 -2
  16. data/lib/glimmer/swt/custom/shape.rb +25 -11
  17. data/lib/glimmer/swt/date_time_proxy.rb +1 -3
  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/font_proxy.rb +1 -0
  22. data/lib/glimmer/swt/image_proxy.rb +68 -1
  23. data/lib/glimmer/swt/shell_proxy.rb +23 -3
  24. data/lib/glimmer/swt/table_proxy.rb +43 -15
  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 +259 -137
  32. data/samples/elaborate/tetris/{view/game_over_dialog.rb → model/past_game.rb} +11 -44
  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 +131 -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 +1 -1
  43. data/samples/hello/hello_code_text.rb +84 -0
  44. data/samples/hello/hello_link.rb +1 -1
  45. metadata +9 -5
@@ -35,11 +35,12 @@ module Glimmer
35
35
  end
36
36
  end
37
37
 
38
- def resetup_shape_paint_listeners
38
+ def resetup_shape_painting
39
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
@@ -30,7 +30,6 @@ module Glimmer
30
30
  module SWT
31
31
  module Custom
32
32
  # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
33
- # swt_widget returns the parent (e.g. a `canvas` WidgetProxy), equivalent to `parent.swt_widget`
34
33
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
35
34
  class Shape
36
35
  include Packages
@@ -78,9 +77,11 @@ module Glimmer
78
77
  def flyweight_method_names
79
78
  @flyweight_method_names ||= {}
80
79
  end
80
+
81
+
81
82
  end
82
83
 
83
- attr_reader :parent, :name, :args, :options, :swt_widget, :paint_listener_proxy
84
+ attr_reader :parent, :name, :args, :options, :paint_listener_proxy
84
85
 
85
86
  def initialize(parent, keyword, *args, &property_block)
86
87
  @parent = parent
@@ -88,7 +89,6 @@ module Glimmer
88
89
  @method_name = self.class.method_name(keyword, args)
89
90
  @options = self.class.arg_options(args, extract: true)
90
91
  @args = args
91
- @swt_widget = parent.respond_to?(:swt_display) ? parent.swt_display : parent.swt_widget
92
92
  @properties = {}
93
93
  @parent.shapes << self
94
94
  post_add_content if property_block.nil?
@@ -111,7 +111,8 @@ module Glimmer
111
111
  end
112
112
 
113
113
  def post_add_content
114
- setup_paint_listener
114
+ amend_method_name_options_based_on_properties
115
+ setup_painting
115
116
  @content_added = true
116
117
  end
117
118
 
@@ -186,6 +187,15 @@ module Glimmer
186
187
  args[the_java_method_arg_count..-1] = []
187
188
  end
188
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
189
199
 
190
200
  def has_attribute?(attribute_name, *args)
191
201
  self.class.gc_instance_methods.include?(attribute_setter(attribute_name))
@@ -193,8 +203,8 @@ module Glimmer
193
203
 
194
204
  def set_attribute(attribute_name, *args)
195
205
  @properties[attribute_name] = args
196
- if @content_added
197
- @parent.resetup_shape_paint_listeners
206
+ if @content_added && !@parent.is_disposed
207
+ @parent.resetup_shape_painting
198
208
  @parent.redraw
199
209
  end
200
210
  end
@@ -203,19 +213,23 @@ module Glimmer
203
213
  @properties.symbolize_keys[attribute_name.to_s.to_sym]
204
214
  end
205
215
 
206
- 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
207
219
  if parent.respond_to?(:swt_display)
208
220
  @paint_listener_proxy = @parent.on_swt_paint(&method(:paint))
209
- 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)
210
224
  @paint_listener_proxy = @parent.on_paint_control(&method(:paint))
211
225
  end
212
226
  end
213
227
 
214
228
  def paint(paint_event)
215
229
  @properties['background'] = [@parent.background] if fill? && !@properties.keys.map(&:to_s).include?('background')
216
- @properties['foreground'] = [@parent.foreground] if draw? && !@properties.keys.map(&:to_s).include?('foreground')
217
- @properties['font'] = [@parent.font] if draw? && !@properties.keys.map(&:to_s).include?('font')
218
- @properties['transform'] = [nil] if !@properties.keys.map(&:to_s).include?('transform')
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')
219
233
  @properties.each do |property, args|
220
234
  method_name = attribute_setter(property)
221
235
  converted_args = apply_property_arg_conversions(method_name, property, args)
@@ -43,9 +43,7 @@ module Glimmer
43
43
  end
44
44
 
45
45
  def date=(date_value)
46
- self.year = date_value.year
47
- self.month = date_value.month
48
- self.day = date_value.day
46
+ swt_widget.setDate(date_value.year, date_value.month - 1, date_value.day)
49
47
  end
50
48
 
51
49
  def time
@@ -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)
@@ -43,6 +43,7 @@ module Glimmer
43
43
 
44
44
  attr_reader :widget_proxy, :swt_font, :font_properties
45
45
 
46
+
46
47
  # Builds a new font proxy from passed in widget_proxy and font_properties hash,
47
48
  #
48
49
  # It begins with existing SWT widget font and amends it with font properties.
@@ -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)
@@ -444,8 +457,7 @@ module Glimmer
444
457
  edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
445
458
  end
446
459
 
447
- def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
448
- require 'facets/hash/symbolize_keys'
460
+ def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil, write_on_cancel: false)
449
461
  return if table_item.nil?
450
462
  model = table_item.data
451
463
  property = column_properties[column_index]
@@ -464,14 +476,22 @@ module Glimmer
464
476
  widget_value_property = TableProxy::editors.symbolize_keys[editor_widget][:widget_value_property]
465
477
 
466
478
  @cancel_edit = lambda do |event=nil|
467
- @cancel_in_progress = true
468
- @table_editor_widget_proxy&.swt_widget&.dispose
469
- @table_editor_widget_proxy = nil
470
- after_cancel&.call
471
- @edit_in_progress = false
472
- @cancel_in_progress = false
473
- @cancel_edit = nil
474
- @edit_mode = false
479
+ if write_on_cancel
480
+ @finish_edit.call(event)
481
+ else
482
+ @cancel_in_progress = true
483
+ @table_editor_widget_proxy&.swt_widget&.dispose
484
+ @table_editor_widget_proxy = nil
485
+ if after_cancel&.arity == 0
486
+ after_cancel&.call
487
+ else
488
+ after_cancel&.call(table_item)
489
+ end
490
+ @edit_in_progress = false
491
+ @cancel_in_progress = false
492
+ @cancel_edit = nil
493
+ @edit_mode = false
494
+ end
475
495
  end
476
496
 
477
497
  @finish_edit = lambda do |event=nil|
@@ -484,14 +504,22 @@ module Glimmer
484
504
  if new_value == model.send(model_editing_property)
485
505
  @cancel_edit.call
486
506
  else
487
- before_write&.call
507
+ if before_write&.arity == 0
508
+ before_write&.call
509
+ else
510
+ before_write&.call(edited_table_item)
511
+ end
488
512
  model.send("#{model_editing_property}=", new_value) # makes table update itself, so must search for selected table item again
489
513
  # Table refresh happens here because of model update triggering observers, so must retrieve table item again
490
514
  edited_table_item = search { |ti| ti.getData == model }.first
491
515
  swt_widget.showItem(edited_table_item)
492
516
  @table_editor_widget_proxy&.swt_widget&.dispose
493
517
  @table_editor_widget_proxy = nil
494
- after_write&.call(edited_table_item)
518
+ if after_write&.arity == 0
519
+ after_write&.call
520
+ else
521
+ after_write&.call(edited_table_item)
522
+ end
495
523
  @edit_in_progress = false
496
524
  end
497
525
  end