glimmer-dsl-swt 0.1.0

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