glimmer 0.7.4 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7b9a101d9c4af1b6fb9aa58f816db089b0643d9928027cbfcf62af3198c75db9
4
- data.tar.gz: 27cb07e2eb9ee19129d5a12a82e534006707884ff1541b411f03ab1ad7cc485e
3
+ metadata.gz: 711519083aeec2914eccf820d3c43a799b2f688f04f7ae99403bee8a1bd0985c
4
+ data.tar.gz: f9121704b98f1f0186be043275873d80c15cd93427b6bfc5580473c379135d0b
5
5
  SHA512:
6
- metadata.gz: 2b5db460b17ba88d155861a38cb8412944ca39423c2ccf6ea834ba5dabdf8708fa3fbae7ebca46de16119105504d5684a91efd0c4900740a2444d2dd69e3ccf3
7
- data.tar.gz: 58da5246e785736c649d6d32cec412bcf4dabd7d474e842a9c78a149aef48d6893b68e157a43254462069d4e3f81f5ce51297e08f6dda7ab35e2287719059047
6
+ metadata.gz: cfa41f350b10c401ab3da3bcb8ddd065a53742771b09569f9502c0b57b492637a1d1333ebe8fc1123813772ed016d64130093c5f78518a196b8cf813446dc51b
7
+ data.tar.gz: 85fae54ea7fdaa91f709bae749725f4b8b2bd3cd6c7c23889e1a812a0f5739f8d659e699fc8cb6d75fb6914bcde1f4ab9ff7145e60150732692d9a9d9d729773
@@ -1,9 +1,12 @@
1
- # Glimmer 0.7.4 Beta (Desktop Development Library for Ruby)
1
+ # Glimmer 0.7.5 Beta (Desktop Development Library for Ruby)
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer)
3
3
  [![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
4
4
 
5
5
  Glimmer is a native-UI cross-platform desktop development library written in Ruby. Glimmer's main innovation is a JRuby DSL that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support to greatly facilitate synchronizing the UI with domain models. As a result, that achieves true decoupling of object oriented components, enabling developers to solve business problems without worrying about UI concerns, or alternatively drive development UI-first, and then write clean business models test-first afterwards.
6
6
 
7
+ [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
8
+ Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
9
+
7
10
  ## Examples
8
11
 
9
12
  ### Hello, World!
@@ -71,7 +74,7 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
71
74
  ## Table of Contents
72
75
 
73
76
  <!-- TOC START min:1 max:3 link:true asterisk:false update:true -->
74
- - [Glimmer 0.7.4 Beta (JRuby Desktop UI DSL + Data-Binding)](#glimmer-058-beta-jruby-desktop-ui-dsl--data-binding)
77
+ - [Glimmer 0.7.5 Beta (JRuby Desktop UI DSL + Data-Binding)](#glimmer-058-beta-jruby-desktop-ui-dsl--data-binding)
75
78
  - [Examples](#examples)
76
79
  - [Hello World](#hello-world)
77
80
  - [Tic Tac Toe](#tic-tac-toe)
@@ -164,7 +167,7 @@ Please follow these instructions to make the `glimmer` command available on your
164
167
 
165
168
  Run this command to install directly:
166
169
  ```
167
- jgem install glimmer -v 0.7.4
170
+ jgem install glimmer -v 0.7.5
168
171
  ```
169
172
 
170
173
  `jgem` is JRuby's version of `gem` command.
@@ -175,7 +178,7 @@ Otherwise, you may also run `jruby -S gem install ...`
175
178
 
176
179
  Add the following to `Gemfile`:
177
180
  ```
178
- gem 'glimmer', '~> 0.7.4'
181
+ gem 'glimmer', '~> 0.7.5'
179
182
  ```
180
183
 
181
184
  And, then run:
@@ -734,6 +737,7 @@ Glimmer ships with SWT style **smart defaults** so you wouldn't have to set them
734
737
 
735
738
  - `text(:border)`
736
739
  - `table(:border)`
740
+ - `tree(:border, :virtual, :v_scroll, :h_scroll)`
737
741
  - `spinner(:border)`
738
742
  - `list(:border, :v_scroll)`
739
743
  - `button(:push)`
@@ -1075,7 +1079,7 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
1075
1079
 
1076
1080
  Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
1077
1081
 
1078
- Data-binding examples:
1082
+ #### General data-binding examples:
1079
1083
 
1080
1084
  `text bind(contact, :first_name)`
1081
1085
 
@@ -1098,6 +1102,10 @@ This example also specifies a converter on read of the model property, but via a
1098
1102
 
1099
1103
  This is a block shortcut version of the syntax above it. It facilitates formatting model data for read-only widgets since it's a very common view concern. It also saves the developer from having to create a separate formatter/presenter for the model when the view can be an active view that handles common simple formatting operations directly.
1100
1104
 
1105
+ `text bind(contact, 'address.street', read_only: true)
1106
+
1107
+ This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
1108
+
1101
1109
  `text bind(contact, 'addresses[1].street')`
1102
1110
 
1103
1111
  This example binds the text property of a widget like `label` to the nested indexed address street of a contact. This is called nested indexed property data binding.
@@ -1116,6 +1124,8 @@ This example demonstrates nested indexed computed value data binding whereby the
1116
1124
 
1117
1125
  Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1118
1126
 
1127
+ #### Combo
1128
+
1119
1129
  ![Hello Combo](images/glimmer-hello-combo.png)
1120
1130
 
1121
1131
  ![Hello Combo](images/glimmer-hello-combo-expanded.png)
@@ -1159,6 +1169,8 @@ HelloCombo.new.launch
1159
1169
 
1160
1170
  `combo` widget is data-bound to the country of a person. Note that it expects `person` object to have `:country` attribute and `:country_options` attribute containing all available countries.
1161
1171
 
1172
+ #### List
1173
+
1162
1174
  Example from [samples/hello/hello_list_single_selection.rb](samples/hello_list_single_selection.rb) sample:
1163
1175
 
1164
1176
  ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
@@ -1240,6 +1252,45 @@ Note that in all the data-binding examples above, there was also an observer att
1240
1252
 
1241
1253
  You may learn more about Glimmer's data-binding syntax by reading the [Eclipse Zone Tutorial](http://eclipse.dzone.com/articles/an-introduction-glimmer) mentioned in resources and opening up the samples under the [samples](samples) directory.
1242
1254
 
1255
+ #### Tree
1256
+
1257
+ The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
1258
+
1259
+ To data-bind a Tree, you need the root model, the children querying method, and the text display attribute on each child.
1260
+
1261
+ This involves using the `bind` keyword mentioned above in addition to a special `tree_properties` keyword that takes the children and text attribute methods.
1262
+
1263
+ Example:
1264
+
1265
+ ```ruby
1266
+ shell {
1267
+ @tree = tree {
1268
+ items bind(company, :owner), tree_properties(children: :coworkers, text: :name)
1269
+ selection bind(company, :selected_coworker)
1270
+ }
1271
+ }
1272
+ ```
1273
+
1274
+ The code above includes two data-bindings:
1275
+ - Tree `items`, which first bind to the root node (company.owner), and then dig down via `coworkers` `children` method, using the `name` `text` attribute for displaying each tree item.
1276
+ - Tree `selection`, which binds the single tree item selected by the user to the attribute denoted by the `bind` keyword
1277
+
1278
+ Additionally, Tree `items` data-binding automatically stores each node model unto the SWT TreeItem object via `setData` method. This enables things like searchability.
1279
+
1280
+ The tree widget in Glimmer is represented by a subclass of `WidgetProxy` called `TreeProxy`.
1281
+ TreeProxy includes a `depth_first_search` method that takes a block to look for a tree item.
1282
+
1283
+ Example:
1284
+
1285
+ ```ruby
1286
+ found_array = @tree.depth_first_search { |tree_item| tree_item.getData == company.owner }
1287
+ ```
1288
+
1289
+ This finds the root node. The array is a Java array. This enables easy passing of it to SWT `Tree#setSelection` method, which expects a Java array of `TreeItem` objects.
1290
+
1291
+ To edit a tree, you must invoke `TreeProxy#edit_selected_tree_item` or `TreeProxy#edit_tree_item`. This automatically leverages the SWT TreeEditor custom class behind the scenes, displaying
1292
+ a text widget to the user to change the selected or passed tree item text into something else. It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
1293
+
1243
1294
  ### Observer
1244
1295
 
1245
1296
  Glimmer comes with `Observer` module, which is used internally for data-binding, but can also be used externally for custom use of the Observer Pattern. It is hidden when observing widgets, and used explicitly when observing models.
@@ -2198,6 +2249,8 @@ Exec failed with code 2 command [[/usr/bin/SetFile, -c, icnC, /var/folders/4_/g1
2198
2249
  * [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
2199
2250
  * [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
2200
2251
  * [Code Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
2252
+ * [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
2253
+
2201
2254
 
2202
2255
  ## Feature Suggestions
2203
2256
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.4
1
+ 0.7.5
@@ -26,6 +26,7 @@ module Glimmer
26
26
 
27
27
  def call(model_collection=nil)
28
28
  if model_collection and model_collection.is_a?(Array)
29
+ # TODO clean observer registrations
29
30
  observe(model_collection, @column_properties)
30
31
  @model_collection = model_collection
31
32
  end
@@ -17,7 +17,12 @@ module Glimmer
17
17
  @tree = parent
18
18
  @model_binding = model_binding
19
19
  @tree_properties = [tree_properties].flatten.first.to_h
20
- call(@model_binding.evaluate_property)
20
+ if @tree.respond_to?(:tree_properties=)
21
+ @tree.tree_properties = @tree_properties
22
+ else # assume custom widget
23
+ @tree.body_root.tree_properties = @tree_properties
24
+ end
25
+ call
21
26
  model = model_binding.base_model
22
27
  observe(model, model_binding.property_name_expression)
23
28
  @tree.on_widget_disposed do |dispose_event|
@@ -25,27 +30,37 @@ module Glimmer
25
30
  end
26
31
  end
27
32
 
28
- def call(model_tree_root_node=nil)
29
- if model_tree_root_node and model_tree_root_node.respond_to?(@tree_properties[:children])
30
- observe(model_tree_root_node, @tree_properties[:text])
31
- observe(model_tree_root_node, @tree_properties[:children])
32
- @model_tree_root_node = model_tree_root_node
33
- end
33
+ def call(new_value=nil)
34
+ @model_tree_root_node = @model_binding.evaluate_property
34
35
  populate_tree(@model_tree_root_node, @tree, @tree_properties)
35
36
  end
36
37
 
37
38
  def populate_tree(model_tree_root_node, parent, tree_properties)
39
+ # TODO make it change things by delta instead of removing all
40
+ selected_tree_item_model = parent.swt_widget.getSelection.map(&:getData).first
41
+ parent.all_tree_items.each do |tree_item|
42
+ tree_item.getData('observer_registrations').each do |key, observer_registration|
43
+ observer_registration.unregister
44
+ end
45
+ end
38
46
  parent.swt_widget.removeAll
39
47
  populate_tree_node(model_tree_root_node, parent.swt_widget, tree_properties)
48
+ tree_item_to_select = parent.depth_first_search {|ti| ti.getData == selected_tree_item_model}
49
+ parent.swt_widget.setSelection(tree_item_to_select)
40
50
  end
41
51
 
42
52
  def populate_tree_node(model_tree_node, parent, tree_properties)
43
- table_item = TreeItem.new(parent, SWT::SWTProxy[:none])
44
- table_item.setData(model_tree_node)
45
- table_item.setText((model_tree_node && model_tree_node.send(tree_properties[:text])).to_s)
53
+ return if model_tree_node.nil?
54
+ # TODO anticipate default tree properties if none were passed (like literal values text and children)
55
+ tree_item = TreeItem.new(parent, SWT::SWTProxy[:none])
56
+ observer_registrations = @tree_properties.reduce({}) do |hash, key_value_pair|
57
+ hash.merge(key_value_pair.first => observe(model_tree_node, key_value_pair.last))
58
+ end
59
+ tree_item.setData('observer_registrations', observer_registrations)
60
+ tree_item.setData(model_tree_node)
61
+ tree_item.setText((model_tree_node && model_tree_node.send(tree_properties[:text])).to_s)
46
62
  [model_tree_node && model_tree_node.send(tree_properties[:children])].flatten.to_a.compact.each do |child|
47
- observe(child, @tree_properties[:text])
48
- populate_tree_node(child, table_item, tree_properties)
63
+ populate_tree_node(child, tree_item, tree_properties)
49
64
  end
50
65
  end
51
66
  end
@@ -116,6 +116,11 @@ module Glimmer
116
116
  end
117
117
  end
118
118
  end
119
+
120
+ attr_reader :application_paths
121
+ attr_reader :env_vars
122
+ attr_reader :glimmer_options
123
+ attr_reader :jruby_options
119
124
 
120
125
  def initialize(raw_options)
121
126
  @application_paths = extract_application_paths(raw_options)
@@ -54,6 +54,12 @@ module Glimmer
54
54
  super(attribute_name)
55
55
  end
56
56
  end
57
+
58
+ def dispose
59
+ swt_tab_item.setControl(nil)
60
+ swt_widget.dispose
61
+ swt_tab_item.dispose
62
+ end
57
63
  end
58
64
  end
59
65
  end
@@ -3,22 +3,118 @@ require 'glimmer/swt/widget_proxy'
3
3
  module Glimmer
4
4
  module SWT
5
5
  class TreeProxy < Glimmer::SWT::WidgetProxy
6
+ include Glimmer
7
+
8
+ attr_reader :tree_editor, :tree_editor_text_proxy
9
+ attr_accessor :tree_properties
10
+
11
+ def initialize(underscored_widget_name, parent, args)
12
+ super
13
+ @tree_editor = TreeEditor.new(swt_widget)
14
+ @tree_editor.horizontalAlignment = SWTProxy[:left]
15
+ @tree_editor.grabHorizontal = true
16
+ @tree_editor.minimumHeight = 20
17
+ end
18
+
6
19
  # Performs depth first search for tree items matching block condition
7
- # Returns a Java TreeItem array to easily set as selection on org.eclipse.swt.Tree when needed
20
+ # If no condition block is passed, returns all tree items
21
+ # Returns a Java TreeItem array to easily set as selection on org.eclipse.swt.Tree if needed
8
22
  def depth_first_search(&condition)
9
23
  found = []
10
24
  recursive_depth_first_search(swt_widget.getItems.first, found, &condition)
11
25
  found.to_java(TreeItem)
12
26
  end
27
+
28
+ # Returns all tree items including descendants
29
+ def all_tree_items
30
+ depth_first_search
31
+ end
13
32
 
33
+ def widget_property_listener_installers
34
+ super.merge({
35
+ Java::OrgEclipseSwtWidgets::Tree => {
36
+ selection: lambda do |observer|
37
+ on_widget_selected { |selection_event|
38
+ observer.call(@swt_widget.getSelection)
39
+ }
40
+ end
41
+ },
42
+ })
43
+ end
44
+
45
+ def edit_in_progress?
46
+ !!@edit_in_progress
47
+ end
48
+
49
+ def edit_selected_tree_item(before_write: nil, after_write: nil, after_cancel: nil)
50
+ edit_tree_item(swt_widget.getSelection.first, before_write: before_write, after_write: after_write, after_cancel: after_cancel)
51
+ end
52
+
53
+ def edit_tree_item(tree_item, before_write: nil, after_write: nil, after_cancel: nil)
54
+ return if tree_item.nil?
55
+ content {
56
+ @tree_editor_text_proxy = text {
57
+ focus true
58
+ text tree_item.getText
59
+ action_taken = false
60
+ cancel = lambda {
61
+ @tree_editor_text_proxy.swt_widget.dispose
62
+ @tree_editor_text_proxy = nil
63
+ after_cancel&.call
64
+ @edit_in_progress = false
65
+ }
66
+ action = lambda { |event|
67
+ if !action_taken && !@edit_in_progress
68
+ action_taken = true
69
+ @edit_in_progress = true
70
+ new_text = @tree_editor_text_proxy.swt_widget.getText
71
+ if new_text == tree_item.getText
72
+ cancel.call
73
+ else
74
+ before_write&.call
75
+ tree_item.setText(new_text)
76
+ model = tree_item.getData
77
+ model.send("#{tree_properties[:text]}=", new_text) # makes tree update itself, so must search for selected tree item again
78
+ edited_tree_item = depth_first_search { |ti| ti.getData == model }.first
79
+ swt_widget.showItem(edited_tree_item)
80
+ @tree_editor_text_proxy.swt_widget.dispose
81
+ @tree_editor_text_proxy = nil
82
+ after_write&.call(edited_tree_item)
83
+ @edit_in_progress = false
84
+ end
85
+ end
86
+ }
87
+ on_focus_lost(&action)
88
+ on_key_pressed { |key_event|
89
+ if key_event.keyCode == swt(:cr)
90
+ action.call(key_event)
91
+ elsif key_event.keyCode == swt(:esc)
92
+ cancel.call
93
+ end
94
+ }
95
+ }
96
+ @tree_editor_text_proxy.swt_widget.selectAll
97
+ }
98
+ @tree_editor.setEditor(@tree_editor_text_proxy.swt_widget, tree_item);
99
+ end
100
+
14
101
  private
15
102
 
16
103
  def recursive_depth_first_search(tree_item, found, &condition)
104
+ return if tree_item.nil?
17
105
  found << tree_item if condition.nil? || condition.call(tree_item)
18
106
  tree_item.getItems.each do |child_tree_item|
19
107
  recursive_depth_first_search(child_tree_item, found, &condition)
20
108
  end
21
109
  end
110
+
111
+ def property_type_converters
112
+ super.merge({
113
+ selection: lambda do |value|
114
+ depth_first_search {|ti| ti.getData == value}
115
+ end,
116
+ })
117
+ end
22
118
  end
23
119
  end
24
120
  end
@@ -5,12 +5,29 @@ module Glimmer
5
5
  # Follows the Proxy Design Pattern
6
6
  class WidgetListenerProxy
7
7
 
8
- attr_reader :swt_listener
8
+ attr_reader :swt_widget, :swt_listener, :widget_add_listener_method, :swt_listener_class, :swt_listener_method, :event_type, :swt_constant
9
9
 
10
- # TODO capture its widget and support unregistering
11
-
12
- def initialize(swt_listener)
10
+ def initialize(swt_widget:, swt_listener:, widget_add_listener_method: nil, swt_listener_class: nil, swt_listener_method: nil, event_type: nil, swt_constant: nil)
11
+ @swt_widget = swt_widget
13
12
  @swt_listener = swt_listener
13
+ @widget_add_listener_method = widget_add_listener_method
14
+ @swt_listener_class = swt_listener_class
15
+ @swt_listener_method = swt_listener_method
16
+ @event_type = event_type
17
+ @swt_constant = swt_constant
18
+ end
19
+
20
+ def widget_remove_listener_method
21
+ @widget_add_listener_method.sub('add', 'remove')
22
+ end
23
+
24
+ def unregister
25
+ # TODO consider renaming to deregister (and in Observer too)
26
+ if @event_type
27
+ @swt_widget.removeListener(@event_type, @swt_listener)
28
+ else
29
+ @swt_widget.send(widget_remove_listener_method, @swt_listener)
30
+ end
14
31
  end
15
32
  end
16
33
  end
@@ -5,6 +5,8 @@ require 'glimmer/swt/font_proxy'
5
5
  require 'glimmer/swt/swt_proxy'
6
6
  require 'glimmer/data_binding/observable_widget'
7
7
 
8
+ # TODO refactor to make file smaller and extract sub-widget-proxies out of this
9
+
8
10
  module Glimmer
9
11
  module SWT
10
12
  # Proxy for SWT Widget objects
@@ -23,6 +25,7 @@ module Glimmer
23
25
  DEFAULT_STYLES = {
24
26
  "text" => [:border],
25
27
  "table" => [:border],
28
+ "tree" => [:virtual, :border, :h_scroll, :v_scroll],
26
29
  "spinner" => [:border],
27
30
  "styled_text" => [:border],
28
31
  "list" => [:border, :v_scroll],
@@ -31,17 +34,17 @@ module Glimmer
31
34
  }
32
35
 
33
36
  DEFAULT_INITIALIZERS = {
34
- "composite" => proc do |composite|
37
+ "composite" => lambda do |composite|
35
38
  composite.setLayout(GridLayout.new)
36
39
  end,
37
- "table" => proc do |table|
40
+ "table" => lambda do |table|
38
41
  table.setHeaderVisible(true)
39
42
  table.setLinesVisible(true)
40
43
  end,
41
- "table_column" => proc do |table_column|
44
+ "table_column" => lambda do |table_column|
42
45
  table_column.setWidth(80)
43
46
  end,
44
- "group" => proc do |group|
47
+ "group" => lambda do |group|
45
48
  group.setLayout(GridLayout.new)
46
49
  end,
47
50
  }
@@ -119,7 +122,7 @@ module Glimmer
119
122
  def widget_property_listener_installers
120
123
  @swt_widget_property_listener_installers ||= {
121
124
  Java::OrgEclipseSwtWidgets::Control => {
122
- :focus => proc do |observer|
125
+ :focus => lambda do |observer|
123
126
  on_focus_gained { |focus_event|
124
127
  observer.call(true)
125
128
  }
@@ -129,12 +132,12 @@ module Glimmer
129
132
  end,
130
133
  },
131
134
  Java::OrgEclipseSwtWidgets::Text => {
132
- :text => proc do |observer|
135
+ :text => lambda do |observer|
133
136
  on_modify_text { |modify_event|
134
137
  observer.call(@swt_widget.getText)
135
138
  }
136
139
  end,
137
- :caret_position => proc do |observer|
140
+ :caret_position => lambda do |observer|
138
141
  on_event_keydown { |event|
139
142
  observer.call(@swt_widget.getCaretPosition)
140
143
  }
@@ -148,7 +151,7 @@ module Glimmer
148
151
  observer.call(@swt_widget.getCaretPosition)
149
152
  }
150
153
  end,
151
- :selection_count => proc do |observer|
154
+ :selection_count => lambda do |observer|
152
155
  on_event_keydown { |event|
153
156
  observer.call(@swt_widget.getSelectionCount)
154
157
  }
@@ -162,7 +165,7 @@ module Glimmer
162
165
  observer.call(@swt_widget.getSelectionCount)
163
166
  }
164
167
  end,
165
- :top_index => proc do |observer|
168
+ :top_index => lambda do |observer|
166
169
  @last_top_index = @swt_widget.getTopIndex
167
170
  on_paint_control { |event|
168
171
  if @swt_widget.getTopIndex != @last_top_index
@@ -173,33 +176,33 @@ module Glimmer
173
176
  end,
174
177
  },
175
178
  Java::OrgEclipseSwtCustom::StyledText => {
176
- :text => proc do |observer|
179
+ :text => lambda do |observer|
177
180
  on_modify_text { |modify_event|
178
181
  observer.call(@swt_widget.getText)
179
182
  }
180
183
  end,
181
184
  },
182
185
  Java::OrgEclipseSwtWidgets::Button => {
183
- :selection => proc do |observer|
186
+ :selection => lambda do |observer|
184
187
  on_widget_selected { |selection_event|
185
188
  observer.call(@swt_widget.getSelection)
186
189
  }
187
190
  end
188
191
  },
189
192
  Java::OrgEclipseSwtWidgets::MenuItem => {
190
- :selection => proc do |observer|
193
+ :selection => lambda do |observer|
191
194
  on_widget_selected { |selection_event|
192
195
  observer.call(@swt_widget.getSelection)
193
196
  }
194
197
  end
195
198
  },
196
199
  Java::OrgEclipseSwtWidgets::Spinner => {
197
- :selection => proc do |observer|
200
+ :selection => lambda do |observer|
198
201
  on_widget_selected { |selection_event|
199
202
  observer.call(@swt_widget.getSelection)
200
203
  }
201
204
  end
202
- }
205
+ },
203
206
  }
204
207
  end
205
208
 
@@ -321,10 +324,12 @@ module Glimmer
321
324
  end
322
325
 
323
326
  def add_listener(underscored_listener_name, &block)
324
- widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
325
- listener = listener_class.new(listener_method => block)
327
+ widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
328
+ widget_listener_proxy = nil
329
+ safe_block = lambda { |event| block.call(event) unless @swt_widget.isDisposed }
330
+ listener = listener_class.new(listener_method => safe_block)
326
331
  @swt_widget.send(widget_add_listener_method, listener)
327
- WidgetListenerProxy.new(listener)
332
+ widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: listener, widget_add_listener_method: widget_add_listener_method, swt_listener_class: listener_class, swt_listener_method: listener_method)
328
333
  end
329
334
 
330
335
  # Looks through SWT class add***Listener methods till it finds one for which
@@ -375,8 +380,10 @@ module Glimmer
375
380
 
376
381
  def add_swt_event_listener(swt_constant, &block)
377
382
  event_type = SWTProxy[swt_constant]
378
- @swt_widget.addListener(event_type, &block)
379
- WidgetListenerProxy.new(@swt_widget.getListeners(event_type).last)
383
+ widget_listener_proxy = nil
384
+ safe_block = lambda { |event| block.call(event) unless @swt_widget.isDisposed }
385
+ @swt_widget.addListener(event_type, &safe_block)
386
+ widget_listener_proxy = WidgetListenerProxy.new(swt_widget: @swt_widget, swt_listener: @swt_widget.getListeners(event_type).last, event_type: event_type, swt_constant: swt_constant)
380
387
  end
381
388
 
382
389
  def widget_custom_attribute_mapping
@@ -409,7 +416,7 @@ module Glimmer
409
416
  end
410
417
 
411
418
  def property_type_converters
412
- color_converter = proc do |value|
419
+ color_converter = lambda do |value|
413
420
  if value.is_a?(Symbol) || value.is_a?(String)
414
421
  ColorProxy.new(value).swt_color
415
422
  else
@@ -419,7 +426,7 @@ module Glimmer
419
426
  # TODO consider detecting type on widget method and automatically invoking right converter (e.g. :to_s for String, :to_i for Integer)
420
427
  @property_type_converters ||= {
421
428
  :background => color_converter,
422
- :background_image => proc do |value|
429
+ :background_image => lambda do |value|
423
430
  if value.is_a?(String)
424
431
  if value.start_with?('uri:classloader')
425
432
  value = value.sub(/^uri\:classloader\:\//, '')
@@ -440,7 +447,7 @@ module Glimmer
440
447
  end
441
448
  end,
442
449
  :foreground => color_converter,
443
- :font => proc do |value|
450
+ :font => lambda do |value|
444
451
  if value.is_a?(Hash)
445
452
  font_properties = value
446
453
  FontProxy.new(self, font_properties).swt_font
@@ -448,17 +455,17 @@ module Glimmer
448
455
  value
449
456
  end
450
457
  end,
451
- :items => proc do |value|
458
+ :items => lambda do |value|
452
459
  value.to_java :string
453
460
  end,
454
- :text => proc do |value|
461
+ :text => lambda do |value|
455
462
  if swt_widget.is_a?(Browser)
456
463
  value.to_s
457
464
  else
458
465
  value.to_s
459
466
  end
460
467
  end,
461
- :visible => proc do |value|
468
+ :visible => lambda do |value|
462
469
  !!value
463
470
  end,
464
471
  }
@@ -224,6 +224,10 @@ module Glimmer
224
224
  def dispose
225
225
  body_root.dispose
226
226
  end
227
+
228
+ def method_missing(method, *args, &block)
229
+ body_root.send(method, *args, &block)
230
+ end
227
231
 
228
232
  private
229
233
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.4
4
+ version: 0.7.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-19 00:00:00.000000000 Z
11
+ date: 2020-05-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement