glimmer-dsl-swt 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (79) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +20 -0
  3. data/README.md +47 -0
  4. data/RUBY_VERSION +1 -0
  5. data/VERSION +1 -0
  6. data/bin/girb +10 -0
  7. data/bin/girb_runner.rb +13 -0
  8. data/bin/glimmer +5 -0
  9. data/icons/scaffold_app.icns +0 -0
  10. data/lib/ext/glimmer.rb +13 -0
  11. data/lib/ext/glimmer/config.rb +18 -0
  12. data/lib/glimmer-dsl-swt.rb +12 -0
  13. data/lib/glimmer/data_binding/list_selection_binding.rb +52 -0
  14. data/lib/glimmer/data_binding/model_binding.rb +248 -0
  15. data/lib/glimmer/data_binding/observable.rb +21 -0
  16. data/lib/glimmer/data_binding/observable_array.rb +107 -0
  17. data/lib/glimmer/data_binding/observable_model.rb +108 -0
  18. data/lib/glimmer/data_binding/observable_widget.rb +17 -0
  19. data/lib/glimmer/data_binding/observer.rb +124 -0
  20. data/lib/glimmer/data_binding/shine.rb +23 -0
  21. data/lib/glimmer/data_binding/table_items_binding.rb +56 -0
  22. data/lib/glimmer/data_binding/tree_items_binding.rb +71 -0
  23. data/lib/glimmer/data_binding/widget_binding.rb +33 -0
  24. data/lib/glimmer/dsl/swt/async_exec_expression.rb +14 -0
  25. data/lib/glimmer/dsl/swt/bind_expression.rb +37 -0
  26. data/lib/glimmer/dsl/swt/color_expression.rb +19 -0
  27. data/lib/glimmer/dsl/swt/column_properties_expression.rb +24 -0
  28. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +42 -0
  29. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +39 -0
  30. data/lib/glimmer/dsl/swt/data_binding_expression.rb +34 -0
  31. data/lib/glimmer/dsl/swt/dialog_expression.rb +26 -0
  32. data/lib/glimmer/dsl/swt/display_expression.rb +19 -0
  33. data/lib/glimmer/dsl/swt/dsl.rb +34 -0
  34. data/lib/glimmer/dsl/swt/exec_expression.rb +28 -0
  35. data/lib/glimmer/dsl/swt/layout_data_expression.rb +25 -0
  36. data/lib/glimmer/dsl/swt/layout_expression.rb +27 -0
  37. data/lib/glimmer/dsl/swt/list_selection_data_binding_expression.rb +44 -0
  38. data/lib/glimmer/dsl/swt/menu_bar_expression.rb +33 -0
  39. data/lib/glimmer/dsl/swt/menu_expression.rb +32 -0
  40. data/lib/glimmer/dsl/swt/message_box_expression.rb +29 -0
  41. data/lib/glimmer/dsl/swt/observe_expression.rb +32 -0
  42. data/lib/glimmer/dsl/swt/property_expression.rb +22 -0
  43. data/lib/glimmer/dsl/swt/rgb_expression.rb +12 -0
  44. data/lib/glimmer/dsl/swt/rgba_expression.rb +12 -0
  45. data/lib/glimmer/dsl/swt/shell_expression.rb +25 -0
  46. data/lib/glimmer/dsl/swt/swt_expression.rb +25 -0
  47. data/lib/glimmer/dsl/swt/sync_exec_expression.rb +15 -0
  48. data/lib/glimmer/dsl/swt/tab_item_expression.rb +33 -0
  49. data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +31 -0
  50. data/lib/glimmer/dsl/swt/tree_items_data_binding_expression.rb +31 -0
  51. data/lib/glimmer/dsl/swt/tree_properties_expression.rb +26 -0
  52. data/lib/glimmer/dsl/swt/widget_expression.rb +35 -0
  53. data/lib/glimmer/dsl/swt/widget_listener_expression.rb +32 -0
  54. data/lib/glimmer/launcher.rb +196 -0
  55. data/lib/glimmer/package.rb +57 -0
  56. data/lib/glimmer/rake_task.rb +62 -0
  57. data/lib/glimmer/scaffold.rb +582 -0
  58. data/lib/glimmer/swt/color_proxy.rb +53 -0
  59. data/lib/glimmer/swt/display_proxy.rb +88 -0
  60. data/lib/glimmer/swt/font_proxy.rb +72 -0
  61. data/lib/glimmer/swt/layout_data_proxy.rb +84 -0
  62. data/lib/glimmer/swt/layout_proxy.rb +82 -0
  63. data/lib/glimmer/swt/menu_proxy.rb +101 -0
  64. data/lib/glimmer/swt/message_box_proxy.rb +48 -0
  65. data/lib/glimmer/swt/packages.rb +13 -0
  66. data/lib/glimmer/swt/shell_proxy.rb +152 -0
  67. data/lib/glimmer/swt/swt_proxy.rb +106 -0
  68. data/lib/glimmer/swt/tab_item_proxy.rb +65 -0
  69. data/lib/glimmer/swt/table_proxy.rb +150 -0
  70. data/lib/glimmer/swt/tree_proxy.rb +120 -0
  71. data/lib/glimmer/swt/widget_listener_proxy.rb +34 -0
  72. data/lib/glimmer/swt/widget_proxy.rb +489 -0
  73. data/lib/glimmer/ui/custom_shell.rb +45 -0
  74. data/lib/glimmer/ui/custom_widget.rb +244 -0
  75. data/lib/glimmer/util/proc_tracker.rb +16 -0
  76. data/vendor/swt/linux/swt.jar +0 -0
  77. data/vendor/swt/mac/swt.jar +0 -0
  78. data/vendor/swt/windows/swt.jar +0 -0
  79. metadata +307 -0
@@ -0,0 +1,48 @@
1
+ require 'glimmer/swt/swt_proxy'
2
+ require 'glimmer/swt/widget_proxy'
3
+ require 'glimmer/swt/display_proxy'
4
+ require 'glimmer/swt/shell_proxy'
5
+
6
+ module Glimmer
7
+ module SWT
8
+ # Proxy for org.eclipse.swt.widgets.Shell
9
+ #
10
+ # Follows the Proxy Design Pattern
11
+ class MessageBoxProxy
12
+ include_package 'org.eclipse.swt.widgets'
13
+
14
+ attr_reader :swt_widget
15
+
16
+ def initialize(parent, style)
17
+ parent = parent.swt_widget if parent.respond_to?(:swt_widget) && parent.swt_widget.is_a?(Shell)
18
+ @swt_widget = MessageBox.new(parent, style)
19
+ end
20
+
21
+ def open
22
+ @swt_widget.open
23
+ end
24
+
25
+ # TODO refactor the following methods to put in a JavaBean mixin or somethin (perhaps contribute to OSS project too)
26
+
27
+ def attribute_setter(attribute_name)
28
+ "set#{attribute_name.to_s.camelcase(:upper)}"
29
+ end
30
+
31
+ def attribute_getter(attribute_name)
32
+ "get#{attribute_name.to_s.camelcase(:upper)}"
33
+ end
34
+
35
+ def has_attribute?(attribute_name, *args)
36
+ @swt_widget.respond_to?(attribute_setter(attribute_name), args)
37
+ end
38
+
39
+ def set_attribute(attribute_name, *args)
40
+ @swt_widget.send(attribute_setter(attribute_name), *args) unless @swt_widget.send(attribute_getter(attribute_name)) == args.first
41
+ end
42
+
43
+ def get_attribute(attribute_name)
44
+ @swt_widget.send(attribute_getter(attribute_name))
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,13 @@
1
+ module Glimmer
2
+ module SWT
3
+ # This contains Java imports of SWT Java packages
4
+ module Packages
5
+ include_package 'org.eclipse.swt'
6
+ include_package 'org.eclipse.swt.widgets'
7
+ include_package 'org.eclipse.swt.layout'
8
+ include_package 'org.eclipse.swt.graphics'
9
+ include_package 'org.eclipse.swt.browser'
10
+ include_package 'org.eclipse.swt.custom'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,152 @@
1
+ require 'glimmer/swt/swt_proxy'
2
+ require 'glimmer/swt/widget_proxy'
3
+ require 'glimmer/swt/display_proxy'
4
+ require 'glimmer/swt/swt_proxy'
5
+
6
+ module Glimmer
7
+ module SWT
8
+ # Proxy for org.eclipse.swt.widgets.Shell
9
+ #
10
+ # Follows the Proxy Design Pattern
11
+ class ShellProxy < WidgetProxy
12
+ include_package 'org.eclipse.swt.widgets'
13
+ include_package 'org.eclipse.swt.layout'
14
+
15
+ WIDTH_MIN = 130
16
+ HEIGHT_MIN = 0
17
+
18
+ attr_reader :opened_before
19
+ alias opened_before? opened_before
20
+
21
+ # Instantiates ShellProxy with same arguments expected by SWT Shell
22
+ # if swt_widget keyword arg was passed, then it is assumed the shell has already been instantiated
23
+ # and the proxy wraps it instead of creating a new one.
24
+ def initialize(*args, swt_widget: nil)
25
+ if swt_widget
26
+ @swt_widget = swt_widget
27
+ else
28
+ if args.first.is_a?(ShellProxy)
29
+ args[0] = args[0].swt_widget
30
+ end
31
+ style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
32
+ if style_args.any?
33
+ style_arg_start_index = args.index(style_args.first)
34
+ style_arg_last_index = args.index(style_args.last)
35
+ args[style_arg_start_index..style_arg_last_index] = SWTProxy[style_args]
36
+ end
37
+ if args.first.nil? || (!args.first.is_a?(Display) && !args.first.is_a?(Shell))
38
+ @display = DisplayProxy.instance.swt_display
39
+ args = [@display] + args
40
+ end
41
+ args = args.compact
42
+ @swt_widget = Shell.new(*args)
43
+ @swt_widget.setLayout(FillLayout.new)
44
+ @swt_widget.setMinimumSize(WIDTH_MIN, HEIGHT_MIN)
45
+ on_event_show do
46
+ Thread.new do
47
+ sleep(0.25)
48
+ async_exec do
49
+ @swt_widget.setActive unless @swt_widget.isDisposed
50
+ end
51
+ end
52
+ end
53
+ end
54
+ @display ||= @swt_widget.getDisplay
55
+ end
56
+
57
+ # Centers shell within monitor it is in
58
+ def center
59
+ primary_monitor = @display.getPrimaryMonitor()
60
+ monitor_bounds = primary_monitor.getBounds()
61
+ shell_bounds = @swt_widget.getBounds()
62
+ location_x = monitor_bounds.x + (monitor_bounds.width - shell_bounds.width) / 2
63
+ location_y = monitor_bounds.y + (monitor_bounds.height - shell_bounds.height) / 2
64
+ @swt_widget.setLocation(location_x, location_y)
65
+ end
66
+
67
+ # Opens shell and starts SWT's UI thread event loop
68
+ def open
69
+ if @opened_before
70
+ @swt_widget.setVisible(true)
71
+ # notify_observers('visible')
72
+ else
73
+ @opened_before = true
74
+ @swt_widget.pack
75
+ center
76
+ @swt_widget.open
77
+ start_event_loop
78
+ end
79
+ end
80
+ alias show open
81
+
82
+ def hide
83
+ @swt_widget.setVisible(false)
84
+ end
85
+
86
+ def close
87
+ @swt_widget.close
88
+ end
89
+
90
+ def visible?
91
+ @swt_widget.isDisposed ? false : @swt_widget.isVisible
92
+ end
93
+
94
+ # Setting to true opens/shows shell. Setting to false hides the shell.
95
+ def visible=(visibility)
96
+ visibility ? show : hide
97
+ end
98
+
99
+ def pack
100
+ @swt_widget.pack
101
+ end
102
+
103
+ def pack_same_size
104
+ bounds = @swt_widget.getBounds
105
+ if OS.mac?
106
+ @swt_widget.pack
107
+ @swt_widget.setBounds(bounds)
108
+ elsif OS.windows? || OS::Underlying.windows?
109
+ minimum_size = @swt_widget.getMinimumSize
110
+ @swt_widget.setMinimumSize(bounds.width, bounds.height)
111
+ listener = on_control_resized { @swt_widget.setBounds(bounds) }
112
+ @swt_widget.pack
113
+ @swt_widget.removeControlListener(listener.swt_listener)
114
+ @swt_widget.setMinimumSize(minimum_size)
115
+ elsif OS.linux?
116
+ @swt_widget.layout(true, true)
117
+ @swt_widget.setBounds(bounds)
118
+ end
119
+ end
120
+
121
+ def content(&block)
122
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::ShellExpression.new, &block)
123
+ end
124
+
125
+ # (happens as part of `#open`)
126
+ # Starts SWT Event Loop.
127
+ #
128
+ # You may learn more about the SWT Event Loop here:
129
+ # https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html
130
+ # This method is not needed except in rare circumstances where there is a need to start the SWT Event Loop before opening the shell.
131
+ def start_event_loop
132
+ until @swt_widget.isDisposed
133
+ @display.sleep unless @display.readAndDispatch
134
+ end
135
+ end
136
+
137
+ def add_observer(observer, property_name)
138
+ case property_name.to_s
139
+ when 'visible?' #TODO see if you must handle non-? version and/or move elsewhere
140
+ visibility_notifier = proc do
141
+ observer.call(visible?)
142
+ end
143
+ on_event_show(&visibility_notifier)
144
+ on_event_hide(&visibility_notifier)
145
+ on_event_close(&visibility_notifier)
146
+ else
147
+ super
148
+ end
149
+ end
150
+ end
151
+ end
152
+ end
@@ -0,0 +1,106 @@
1
+ require 'glimmer/error'
2
+
3
+ module Glimmer
4
+ module SWT # TODO Consider making this the class below to ease calling it
5
+ # Proxy for org.eclipse.swt.SWT
6
+ #
7
+ # Follows the Proxy Design Pattern
8
+ class SWTProxy
9
+ class << self
10
+ java_import 'org.eclipse.swt.SWT'
11
+
12
+ ERROR_INVALID_STYLE = " is an invalid SWT style! Please choose a style from org.eclipse.swt.SWT class constants."
13
+ REGEX_SYMBOL_NEGATIVITY = /^([^!]+)(!)?$/
14
+
15
+ # Gets SWT constants as if calling SWT::CONSTANT where constant is
16
+ # passed in as a lower case symbol
17
+ def [](*symbols)
18
+ symbols = symbols.first if symbols.size == 1 && symbols.first.is_a?(Array)
19
+ result = symbols.compact.map do |symbol|
20
+ constant(symbol).tap do |constant_value|
21
+ raise Error, symbol.to_s + ERROR_INVALID_STYLE unless constant_value.is_a?(Integer)
22
+ end
23
+ end.reduce do |output, constant_value|
24
+ if constant_value < 0
25
+ output & constant_value
26
+ else
27
+ output | constant_value
28
+ end
29
+ end
30
+ result.nil? ? SWT::NONE : result
31
+ end
32
+
33
+ # Returns SWT style integer value for passed in symbol or allows
34
+ # passed in object to pass through (e.g. Integer). This makes is convenient
35
+ # to use symbols or actual SWT style integers in Glimmer
36
+ # Does not raise error for invalid values. Just lets them pass as is.
37
+ # (look into [] operator if you want an error raised on invalid values)
38
+ def constant(symbol)
39
+ return symbol unless symbol.is_a?(Symbol) || symbol.is_a?(String)
40
+ symbol_string, negative = extract_symbol_string_negativity(symbol)
41
+ swt_constant_symbol = symbol_string.downcase == symbol_string ? symbol_string.upcase.to_sym : symbol_string.to_sym
42
+ bit_value = SWT.const_get(swt_constant_symbol)
43
+ negative ? ~bit_value : bit_value
44
+ rescue => e
45
+ begin
46
+ # Glimmer::Config.logger&.debug(e.full_message)
47
+ alternative_swt_constant_symbol = SWT.constants.find {|c| c.to_s.upcase == swt_constant_symbol.to_s.upcase}
48
+ bit_value = SWT.const_get(alternative_swt_constant_symbol)
49
+ negative ? ~bit_value : bit_value
50
+ rescue => e
51
+ # Glimmer::Config.logger&.debug(e.full_message)
52
+ bit_value = Glimmer::SWT::SWTProxy::EXTRA_STYLES[swt_constant_symbol]
53
+ if bit_value
54
+ negative ? ~bit_value : bit_value
55
+ else
56
+ symbol
57
+ end
58
+ end
59
+ end
60
+
61
+ def extract_symbol_string_negativity(symbol)
62
+ if symbol.is_a?(Symbol) || symbol.is_a?(String)
63
+ symbol_negativity_match = symbol.to_s.match(REGEX_SYMBOL_NEGATIVITY)
64
+ symbol = symbol_negativity_match[1]
65
+ negative = !!symbol_negativity_match[2]
66
+ [symbol, negative]
67
+ else
68
+ negative = symbol < 0
69
+ [symbol, negative]
70
+ end
71
+ end
72
+
73
+ def negative?(symbol)
74
+ extract_symbol_string_negativity(symbol)[1]
75
+ end
76
+
77
+ def has_constant?(symbol)
78
+ return false unless symbol.is_a?(Symbol) || symbol.is_a?(String)
79
+ constant(symbol).is_a?(Integer)
80
+ end
81
+
82
+ def constantify_args(args)
83
+ args.map {|arg| constant(arg)}
84
+ end
85
+
86
+ # Deconstructs a style integer into symbols
87
+ # Useful for debugging
88
+ def deconstruct(integer)
89
+ SWT.constants.reduce([]) do |found, c|
90
+ constant_value = SWT.const_get(c) rescue -1
91
+ is_found = constant_value.is_a?(Integer) && (constant_value & integer) == constant_value
92
+ is_found ? found += [c] : found
93
+ end
94
+ end
95
+
96
+ def include?(swt_constant, *symbols)
97
+ swt_constant & self[symbols] == self[symbols]
98
+ end
99
+ end
100
+
101
+ EXTRA_STYLES = {
102
+ NO_RESIZE: self[:shell_trim, :resize!, :max!]
103
+ }
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,65 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ # Proxy for org.eclipse.swt.widgets.TabItem
6
+ #
7
+ # Functions differently from other widget proxies.
8
+ #
9
+ # Glimmer instantiates an SWT Composite alongside the SWT TabItem
10
+ # and returns it for `#swt_widget` to allow adding widgets into it.
11
+ #
12
+ # In order to get the SWT TabItem object, one must call `#swt_tab_item`.
13
+ #
14
+ # Behind the scenes, this creates a tab item widget proxy separately from a composite that
15
+ # is set as the control of the tab item and `#swt_widget`.
16
+ #
17
+ # In order to retrieve the tab item widget proxy, one must call `#widget_proxy`
18
+ #
19
+ # Follows the Proxy Design Pattern
20
+ class TabItemProxy < WidgetProxy
21
+ include_package 'org.eclipse.swt.widgets'
22
+
23
+ attr_reader :widget_proxy, :swt_tab_item
24
+
25
+ def initialize(parent, style, &contents)
26
+ super("composite", parent, style, &contents)
27
+ @widget_proxy = SWT::WidgetProxy.new('tab_item', parent, style)
28
+ @swt_tab_item = @widget_proxy.swt_widget
29
+ @widget_proxy.swt_widget.control = self.swt_widget
30
+ end
31
+
32
+ def has_attribute?(attribute_name, *args)
33
+ if attribute_name.to_s == "text"
34
+ true
35
+ else
36
+ super(attribute_name, *args)
37
+ end
38
+ end
39
+
40
+ def set_attribute(attribute_name, *args)
41
+ attribute_name
42
+ if attribute_name.to_s == "text"
43
+ text_value = args[0]
44
+ @swt_tab_item.setText text_value
45
+ else
46
+ super(attribute_name, *args)
47
+ end
48
+ end
49
+
50
+ def get_attribute(attribute_name)
51
+ if attribute_name.to_s == "text"
52
+ @swt_tab_item.getText
53
+ else
54
+ super(attribute_name)
55
+ end
56
+ end
57
+
58
+ def dispose
59
+ swt_tab_item.setControl(nil)
60
+ swt_widget.dispose
61
+ swt_tab_item.dispose
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,150 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
3
+ module Glimmer
4
+ module SWT
5
+ class TableProxy < Glimmer::SWT::WidgetProxy
6
+ include Glimmer
7
+
8
+ module TableListenerEvent
9
+ def table_item
10
+ table_item_and_column_index[:table_item]
11
+ end
12
+
13
+ def column_index
14
+ table_item_and_column_index[:column_index]
15
+ end
16
+
17
+ private
18
+
19
+ def table_item_and_column_index
20
+ @table_item_and_column_index ||= find_table_item_and_column_index
21
+ end
22
+
23
+ def find_table_item_and_column_index
24
+ {}.tap do |result|
25
+ if respond_to?(:x) && respond_to?(:y)
26
+ result[:table_item] = widget.getItems.detect do |ti|
27
+ result[:column_index] = widget.getColumnCount.times.to_a.detect do |ci|
28
+ ti.getBounds(ci).contains(x, y)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ attr_reader :table_editor, :table_editor_text_proxy
37
+ attr_accessor :column_properties
38
+
39
+ def initialize(underscored_widget_name, parent, args)
40
+ super
41
+ @table_editor = TableEditor.new(swt_widget)
42
+ @table_editor.horizontalAlignment = SWTProxy[:left]
43
+ @table_editor.grabHorizontal = true
44
+ @table_editor.minimumHeight = 20
45
+ end
46
+
47
+ # Performs a search for table items matching block condition
48
+ # If no condition block is passed, returns all table items
49
+ # Returns a Java TableItem array to easily set as selection on org.eclipse.swt.Table if needed
50
+ def search(&condition)
51
+ swt_widget.getItems.select {|item| condition.nil? || condition.call(item)}.to_java(TableItem)
52
+ end
53
+
54
+ # Returns all table items including descendants
55
+ def all_table_items
56
+ search
57
+ end
58
+
59
+ def widget_property_listener_installers
60
+ super.merge({
61
+ Java::OrgEclipseSwtWidgets::Table => {
62
+ selection: lambda do |observer|
63
+ on_widget_selected { |selection_event|
64
+ observer.call(@swt_widget.getSelection)
65
+ }
66
+ end
67
+ },
68
+ })
69
+ end
70
+
71
+ def edit_in_progress?
72
+ !!@edit_in_progress
73
+ end
74
+
75
+ def edit_selected_table_item(column_index, before_write: nil, after_write: nil, after_cancel: nil)
76
+ edit_table_item(swt_widget.getSelection.first, column_index, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
77
+ end
78
+
79
+ def edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)
80
+ return if table_item.nil?
81
+ content {
82
+ @table_editor_text_proxy = text {
83
+ focus true
84
+ text table_item.getText(column_index)
85
+ action_taken = false
86
+ cancel = lambda {
87
+ @table_editor_text_proxy.swt_widget.dispose
88
+ @table_editor_text_proxy = nil
89
+ after_cancel&.call
90
+ @edit_in_progress = false
91
+ }
92
+ action = lambda { |event|
93
+ if !action_taken && !@edit_in_progress
94
+ action_taken = true
95
+ @edit_in_progress = true
96
+ new_text = @table_editor_text_proxy.swt_widget.getText
97
+ if new_text == table_item.getText(column_index)
98
+ cancel.call
99
+ else
100
+ before_write&.call
101
+ table_item.setText(column_index, new_text)
102
+ model = table_item.getData
103
+ model.send("#{column_properties[column_index]}=", new_text) # makes table update itself, so must search for selected table item again
104
+ edited_table_item = search { |ti| ti.getData == model }.first
105
+ swt_widget.showItem(edited_table_item)
106
+ @table_editor_text_proxy.swt_widget.dispose
107
+ @table_editor_text_proxy = nil
108
+ after_write&.call(edited_table_item)
109
+ @edit_in_progress = false
110
+ end
111
+ end
112
+ }
113
+ on_focus_lost(&action)
114
+ on_key_pressed { |key_event|
115
+ if key_event.keyCode == swt(:cr)
116
+ action.call(key_event)
117
+ elsif key_event.keyCode == swt(:esc)
118
+ cancel.call
119
+ end
120
+ }
121
+ }
122
+ @table_editor_text_proxy.swt_widget.selectAll
123
+ }
124
+ @table_editor.setEditor(@table_editor_text_proxy.swt_widget, table_item, column_index)
125
+ end
126
+
127
+ def add_listener(underscored_listener_name, &block)
128
+ enhanced_block = lambda do |event|
129
+ event.extend(TableListenerEvent)
130
+ block.call(event)
131
+ end
132
+ super(underscored_listener_name, &enhanced_block)
133
+ end
134
+
135
+ private
136
+
137
+ def property_type_converters
138
+ super.merge({
139
+ selection: lambda do |value|
140
+ if value.is_a?(Array)
141
+ search {|ti| value.include?(ti.getData) }
142
+ else
143
+ search {|ti| ti.getData == value}
144
+ end
145
+ end,
146
+ })
147
+ end
148
+ end
149
+ end
150
+ end