glimmer 0.7.4 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
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