glimmer-dsl-swt 4.18.3.0 → 4.18.3.5

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 (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