glimmer 0.7.2 → 0.7.7

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: a991d3df0f065a0caa7cd20d43c026561b99b334b4db9d67d37d54a9aa125a70
4
- data.tar.gz: c9660908ac2b7b8d2c00a8af33c793ad8f2789bb513553fb1e8e2b4abf2f3db1
3
+ metadata.gz: 76d86f5e3e322edba85aee815e28a0bb19b6ed56694ff44b1311a759b249b189
4
+ data.tar.gz: ad84ddf98c7a9da150f1dd4897e7a7df0e7d8cf06d317a1a748929c1446895ce
5
5
  SHA512:
6
- metadata.gz: 289e423f490a3bb338fa91ab5502b03936c5e03088f48dafbb74aa4b31e606637e873c8b26a46167cb10306cab22ae09a733318ef98382ff424207fb06c93fac
7
- data.tar.gz: d6d865f226ab24151983c5c9a3c0dc18bd73d7d4c8ca02a4e99fe4d231500ade3215d245add615e11d1713e39b8a9da5e47006bc13d8406ad4b7208679e38d07
6
+ metadata.gz: dbbd46186a07700fed156566a5a0de4fb5ad90720ae2e24eb0662bc1ef3a64850f29d4f91fa577f388136511e3ec20689a97e1686e25f34112fabf35c05c2981
7
+ data.tar.gz: f484803847e17525090f815ea117d429f10abc87f12a5c38343c9d6e2f56ff6bf055b153f5fa3f250ae60adde2b5ff4cba7b209b0b1693bc98d0f619cc1d15db
@@ -1,12 +1,17 @@
1
- # Glimmer 0.7.2 Beta (JRuby Desktop UI DSL + Data-Binding)
1
+ # Glimmer 0.7.7 Beta (Desktop Development Library for Ruby)
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer)
3
- [![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
3
+ [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer)
4
+ [![Test Coverage](https://api.codeclimate.com/v1/badges/38fbc278022862794414/test_coverage)](https://codeclimate.com/github/AndyObtiva/glimmer/test_coverage)
5
+ [![Maintainability](https://api.codeclimate.com/v1/badges/38fbc278022862794414/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer/maintainability)
4
6
 
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 components test-first afterwards.
7
+ Glimmer is a native-GUI 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 GUI with domain models. As a result, that achieves true decoupling of object oriented components, enabling developers to solve business problems without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models test-first afterwards.
8
+
9
+ [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
10
+ Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
6
11
 
7
12
  ## Examples
8
13
 
9
- ### Hello World
14
+ ### Hello, World!
10
15
 
11
16
  Glimmer code (from `samples/hello/hello_world.rb`):
12
17
  ```ruby
@@ -70,12 +75,10 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
70
75
 
71
76
  ## Table of Contents
72
77
 
73
- <!-- TOC START min:1 max:3 link:true asterisk:false update:true -->
74
- - [Glimmer 0.7.2 Beta (JRuby Desktop UI DSL + Data-Binding)](#glimmer-058-beta-jruby-desktop-ui-dsl--data-binding)
78
+ - [Glimmer 0.7.7 Beta (Desktop Development Library for Ruby)](#glimmer-075-beta-desktop-development-library-for-ruby)
75
79
  - [Examples](#examples)
76
- - [Hello World](#hello-world)
80
+ - [Hello, World!](#hello-world)
77
81
  - [Tic Tac Toe](#tic-tac-toe)
78
- - [Table of Contents](#table-of-contents)
79
82
  - [Background](#background)
80
83
  - [Platform Support](#platform-support)
81
84
  - [Pre-requisites](#pre-requisites)
@@ -85,6 +88,7 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
85
88
  - [Glimmer Command](#glimmer-command)
86
89
  - [Basic Usage](#basic-usage)
87
90
  - [Advanced Usage](#advanced-usage)
91
+ - [Scaffolding](#scaffolding)
88
92
  - [Girb (Glimmer irb) Command](#girb-glimmer-irb-command)
89
93
  - [Glimmer DSL Syntax](#glimmer-dsl-syntax)
90
94
  - [Widgets](#widgets)
@@ -99,6 +103,9 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
99
103
  - [Miscellaneous](#miscellaneous)
100
104
  - [Glimmer Style Guide](#glimmer-style-guide)
101
105
  - [Samples](#samples)
106
+ - [Hello Samples](#hello-samples)
107
+ - [Elaborate Samples](#elaborate-samples)
108
+ - [In Production](#in-production)
102
109
  - [SWT Reference](#swt-reference)
103
110
  - [SWT Packages](#swt-packages)
104
111
  - [Logging](#logging)
@@ -111,14 +118,14 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
111
118
  - [Self Signed Certificate](#self-signed-certificate)
112
119
  - [Gotchas](#gotchas)
113
120
  - [Resources](#resources)
121
+ - [Help](#help)
122
+ - [Issues](#issues)
123
+ - [IRC Channel](#irc-channel)
114
124
  - [Feature Suggestions](#feature-suggestions)
115
125
  - [Change Log](#change-log)
116
126
  - [Contributing](#contributing)
117
127
  - [Contributors](#contributors)
118
128
  - [License](#license)
119
- <!-- TOC END -->
120
-
121
-
122
129
 
123
130
  ## Background
124
131
 
@@ -131,12 +138,12 @@ Glimmer runs on the following platforms:
131
138
  - Windows
132
139
  - Linux
133
140
 
134
- Glimmer's UI has the native look and feel of each operating system it runs on since it uses SWT behind the scenes, which leverages the following native libraries:
141
+ Glimmer's GUI has the native look and feel of each operating system it runs on since it uses SWT behind the scenes, which leverages the following native libraries:
135
142
  - Win32 on Windows
136
143
  - Cocoa on Mac
137
144
  - GTK on Linux
138
145
 
139
- More info about the SWT UI on various platforms can be found on the Eclipse WIKI and SWT FAQ:
146
+ More info about the SWT GUI on various platforms can be found on the Eclipse WIKI and SWT FAQ:
140
147
 
141
148
  https://wiki.eclipse.org/SWT/Devel/Gtk/Dev_guide#Win32.2FCocoa.2FGTK
142
149
  https://www.eclipse.org/swt/faq.php
@@ -164,7 +171,7 @@ Please follow these instructions to make the `glimmer` command available on your
164
171
 
165
172
  Run this command to install directly:
166
173
  ```
167
- jgem install glimmer -v 0.7.2
174
+ jgem install glimmer -v 0.7.7
168
175
  ```
169
176
 
170
177
  `jgem` is JRuby's version of `gem` command.
@@ -175,7 +182,7 @@ Otherwise, you may also run `jruby -S gem install ...`
175
182
 
176
183
  Add the following to `Gemfile`:
177
184
  ```
178
- gem 'glimmer', '~> 0.7.2'
185
+ gem 'glimmer', '~> 0.7.7'
179
186
  ```
180
187
 
181
188
  And, then run:
@@ -336,9 +343,11 @@ glimmer scaffold:custom_widget[custom_widget_name]
336
343
 
337
344
  #### Custom Shell Gem
338
345
 
339
- Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
346
+ Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
347
+ As such, they are packaged as both a native executable (e.g. Mac DMG/PKG/APP) and a Ruby gem.
348
+ Of course, you can build a Ruby gem and disregard its native executable packaging if you do not need it.
340
349
 
341
- To scaffold a Glimmer custom shell gem (full window view externalized into a gem) for an existing Glimmer app, run the following command:
350
+ To scaffold a Glimmer custom shell gem (full window view distributed as a Ruby gem), run the following command:
342
351
 
343
352
  ```
344
353
  glimmer scaffold:custom_shell_gem[custom_shell_name, namespace]
@@ -346,7 +355,7 @@ glimmer scaffold:custom_shell_gem[custom_shell_name, namespace]
346
355
 
347
356
  It is important to specify a namespace to avoid having your gem clash with existing gems.
348
357
 
349
- The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell)
358
+ The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell).
350
359
 
351
360
  Only official Glimmer gems created by the Glimmer project committers will have no namespace (e.g. [glimmer-cs-gladiator](https://rubygems.org/gems/glimmer-cs-gladiator) Ruby gem)
352
361
 
@@ -354,7 +363,7 @@ Example: [https://github.com/AndyObtiva/glimmer-cs-gladiator](https://github.com
354
363
 
355
364
  #### Custom Widget Gem
356
365
 
357
- To scaffold a Glimmer custom widget gem (part of a view externalized into a gem) for an existing Glimmer app, run the following command:
366
+ To scaffold a Glimmer custom widget gem (part of a view distributed as a Ruby gem), run the following command:
358
367
 
359
368
  ```
360
369
  glimmer scaffold:custom_widget_gem[custom_widget_name, namespace]
@@ -404,7 +413,7 @@ You will learn more about widgets next.
404
413
 
405
414
  ### Widgets
406
415
 
407
- Glimmer UIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
416
+ Glimmer GUIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
408
417
 
409
418
  https://www.eclipse.org/swt/widgets/
410
419
 
@@ -513,7 +522,7 @@ shell {
513
522
 
514
523
  #### Display
515
524
 
516
- SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage UI globally
525
+ SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage GUI globally
517
526
  and access available monitors.
518
527
  It is automatically instantiated upon first instantiation of a `shell` widget.
519
528
  Alternatively, for advanced use cases, it can be created explicitly with Glimmer `display` keyword. When a `shell` is later declared, it
@@ -541,7 +550,7 @@ Glimmer follows Proxy Design Pattern by having Ruby proxy wrappers for all SWT o
541
550
  - `Glimmer::SWT:TabItemProxy` wraps `org.eclipse.swt.widget.TabItem` (also adds a composite to enable adding content under tab items directly in Glimmer)
542
551
  - `Glimmer::SWT:LayoutProxy` wraps all descendants of `org.eclipse.swt.widget.Layout`
543
552
  - `Glimmer::SWT:LayoutDataProxy` wraps all layout data objects
544
- - `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying UI)
553
+ - `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying GUI)
545
554
  - `Glimmer::SWT:ColorProxy` wraps `org.eclipse.swt.graphics.Color`
546
555
  - `Glimmer::SWT:FontProxy` wraps `org.eclipse.swt.graphics.Font`
547
556
  - `Glimmer::SWT::WidgetListenerProxy` wraps all widget listeners
@@ -734,6 +743,7 @@ Glimmer ships with SWT style **smart defaults** so you wouldn't have to set them
734
743
 
735
744
  - `text(:border)`
736
745
  - `table(:border)`
746
+ - `tree(:border, :virtual, :v_scroll, :h_scroll)`
737
747
  - `spinner(:border)`
738
748
  - `list(:border, :v_scroll)`
739
749
  - `button(:push)`
@@ -1075,7 +1085,7 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
1075
1085
 
1076
1086
  Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
1077
1087
 
1078
- Data-binding examples:
1088
+ #### General data-binding examples:
1079
1089
 
1080
1090
  `text bind(contact, :first_name)`
1081
1091
 
@@ -1098,6 +1108,10 @@ This example also specifies a converter on read of the model property, but via a
1098
1108
 
1099
1109
  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
1110
 
1111
+ `text bind(contact, 'address.street', read_only: true)
1112
+
1113
+ This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
1114
+
1101
1115
  `text bind(contact, 'addresses[1].street')`
1102
1116
 
1103
1117
  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 +1130,8 @@ This example demonstrates nested indexed computed value data binding whereby the
1116
1130
 
1117
1131
  Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1118
1132
 
1133
+ #### Combo
1134
+
1119
1135
  ![Hello Combo](images/glimmer-hello-combo.png)
1120
1136
 
1121
1137
  ![Hello Combo](images/glimmer-hello-combo-expanded.png)
@@ -1159,6 +1175,8 @@ HelloCombo.new.launch
1159
1175
 
1160
1176
  `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
1177
 
1178
+ #### List
1179
+
1162
1180
  Example from [samples/hello/hello_list_single_selection.rb](samples/hello_list_single_selection.rb) sample:
1163
1181
 
1164
1182
  ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
@@ -1240,6 +1258,45 @@ Note that in all the data-binding examples above, there was also an observer att
1240
1258
 
1241
1259
  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
1260
 
1261
+ #### Tree
1262
+
1263
+ The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
1264
+
1265
+ To data-bind a Tree, you need the root model, the children querying method, and the text display attribute on each child.
1266
+
1267
+ This involves using the `bind` keyword mentioned above in addition to a special `tree_properties` keyword that takes the children and text attribute methods.
1268
+
1269
+ Example:
1270
+
1271
+ ```ruby
1272
+ shell {
1273
+ @tree = tree {
1274
+ items bind(company, :owner), tree_properties(children: :coworkers, text: :name)
1275
+ selection bind(company, :selected_coworker)
1276
+ }
1277
+ }
1278
+ ```
1279
+
1280
+ The code above includes two data-bindings:
1281
+ - 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.
1282
+ - Tree `selection`, which binds the single tree item selected by the user to the attribute denoted by the `bind` keyword
1283
+
1284
+ Additionally, Tree `items` data-binding automatically stores each node model unto the SWT TreeItem object via `setData` method. This enables things like searchability.
1285
+
1286
+ The tree widget in Glimmer is represented by a subclass of `WidgetProxy` called `TreeProxy`.
1287
+ TreeProxy includes a `depth_first_search` method that takes a block to look for a tree item.
1288
+
1289
+ Example:
1290
+
1291
+ ```ruby
1292
+ found_array = @tree.depth_first_search { |tree_item| tree_item.getData == company.owner }
1293
+ ```
1294
+
1295
+ 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.
1296
+
1297
+ 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
1298
+ 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.
1299
+
1243
1300
  ### Observer
1244
1301
 
1245
1302
  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.
@@ -1352,7 +1409,7 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1352
1409
 
1353
1410
  The shell declared above has been modified so that the minimize button works just like the close button. Once you minimize the shell (iconify it), it closes.
1354
1411
 
1355
- The alternative syntax can be helpful if you prefer to separate Glimmer observer declarations from Glimmer UI declarations, or would like to add observers dynamically based on some logic later on.
1412
+ The alternative syntax can be helpful if you prefer to separate Glimmer observer declarations from Glimmer GUI declarations, or would like to add observers dynamically based on some logic later on.
1356
1413
 
1357
1414
  #### Observing Models
1358
1415
 
@@ -1886,7 +1943,7 @@ This relies on Glimmer's [Multi-DSL Support](https://github.com/AndyObtiva/glimm
1886
1943
  - Widget property declarations always have arguments and never take a block
1887
1944
  - Widget property arguments are never wrapped inside parentheses
1888
1945
  - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
1889
- - Widget listeners are always followed by a block using curly braces (Only when declared in DSL. When invoked on widget object directly outside of UI declarations, standard Ruby conventions apply)
1946
+ - Widget listeners are always followed by a block using curly braces (Only when declared in DSL. When invoked on widget object directly outside of GUI declarations, standard Ruby conventions apply)
1890
1947
  - Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
1891
1948
  - Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
1892
1949
  - Custom widgets receive additional arguments to SWT style called options. These are passed as the last argument inside the parentheses, a hash of option names pointing to values.
@@ -1925,7 +1982,7 @@ glimmer samples/elaborate/contact_manager.rb # demonstrates table data-binding
1925
1982
  glimmer samples/elaborate/tic_tac_toe.rb # demonstrates a full MVC application
1926
1983
  ```
1927
1984
 
1928
- ![Gladiator](images/glimmer-gladiator.png)
1985
+ ![Gladiator](https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/v0.1.5/images/glimmer-gladiator.png)
1929
1986
 
1930
1987
  [Gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator) (short for Glimmer Editor) is a Glimmer sample project under on-going development.
1931
1988
  You may check it out to learn how to build a Glimmer Custom Shell gem.
@@ -2194,10 +2251,25 @@ Exec failed with code 2 command [[/usr/bin/SetFile, -c, icnC, /var/folders/4_/g1
2194
2251
 
2195
2252
  ## Resources
2196
2253
 
2254
+ * [Code Master Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
2197
2255
  * [Eclipse Zone Tutorial](http://eclipse.dzone.com/articles/an-introduction-glimmer)
2198
2256
  * [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
2199
2257
  * [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
2200
- * [Code Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
2258
+ * [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
2259
+
2260
+ ## Help
2261
+
2262
+ ### Issues
2263
+
2264
+ You may submit [issues](https://github.com/AndyObtiva/glimmer/issues) on [GitHub](https://github.com/AndyObtiva/glimmer/issues).
2265
+
2266
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer/issues)
2267
+
2268
+ ### IRC Channel
2269
+
2270
+ If you need live help, try the [#glimmer](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer) IRC channel on [irc.mibbit.net](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer). If no one was available, you may [leave a GitHub issue](https://github.com/AndyObtiva/glimmer/issues) to schedule a meetup on IRC.
2271
+
2272
+ [Click here to connect to #glimmer IRC channel immediately via a web interface.](http://widget.mibbit.com/?settings=7514b8a196f8f1de939a351245db7aa8&server=irc.mibbit.net&channel=%23glimmer)
2201
2273
 
2202
2274
  ## Feature Suggestions
2203
2275
 
@@ -2215,8 +2287,10 @@ These features have been suggested. You might see them in a future version of Gl
2215
2287
 
2216
2288
  ## Contributors
2217
2289
 
2218
- * Andy Maleh (Founder)
2219
- * Dennis Theisen
2290
+ * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
2291
+ * [Dennis Theisen](https://github.com/Soleone) (Contributor)
2292
+
2293
+ [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer/graphs/contributors)
2220
2294
 
2221
2295
  ## License
2222
2296
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.7.2
1
+ 0.7.7
@@ -232,6 +232,7 @@ module Glimmer
232
232
  end
233
233
 
234
234
  def invoke_property_writer(object, property_expression, value)
235
+ return if @binding_options[:read_only]
235
236
  value = convert_on_write(value)
236
237
  if property_indexed?(property_expression)
237
238
  property_method = '[]='
@@ -40,9 +40,7 @@ module Glimmer
40
40
  end
41
41
 
42
42
  def property_observer_hash
43
- # TODO simplify with ||=
44
- @property_observers = Hash.new unless @property_observers
45
- @property_observers
43
+ @property_observers ||= Hash.new
46
44
  end
47
45
 
48
46
  def property_observer_list(property_name)
@@ -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,26 +30,40 @@ 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
+ old_tree_items = parent.all_tree_items
42
+ old_tree_item_expansion_by_data = old_tree_items.reduce({}) {|hash, ti| hash.merge(ti.getData => ti.getExpanded)}
43
+ old_tree_items.each do |tree_item|
44
+ tree_item.getData('observer_registrations').each do |key, observer_registration|
45
+ observer_registration.unregister
46
+ end
47
+ end
38
48
  parent.swt_widget.removeAll
39
49
  populate_tree_node(model_tree_root_node, parent.swt_widget, tree_properties)
50
+ parent.all_tree_items.each { |ti| ti.setExpanded(!!old_tree_item_expansion_by_data[ti.getData]) }
51
+ tree_item_to_select = parent.depth_first_search {|ti| ti.getData == selected_tree_item_model}
52
+ parent.swt_widget.setSelection(tree_item_to_select)
40
53
  end
41
54
 
42
55
  def populate_tree_node(model_tree_node, parent, tree_properties)
43
- table_item = TreeItem.new(parent, SWT::SWTProxy[:none])
44
- table_item.setText((model_tree_node && model_tree_node.send(tree_properties[:text])).to_s)
56
+ return if model_tree_node.nil?
57
+ # TODO anticipate default tree properties if none were passed (like literal values text and children)
58
+ tree_item = TreeItem.new(parent, SWT::SWTProxy[:none])
59
+ observer_registrations = @tree_properties.reduce({}) do |hash, key_value_pair|
60
+ hash.merge(key_value_pair.first => observe(model_tree_node, key_value_pair.last))
61
+ end
62
+ tree_item.setData('observer_registrations', observer_registrations)
63
+ tree_item.setData(model_tree_node)
64
+ tree_item.setText((model_tree_node && model_tree_node.send(tree_properties[:text])).to_s)
45
65
  [model_tree_node && model_tree_node.send(tree_properties[:children])].flatten.to_a.compact.each do |child|
46
- observe(child, @tree_properties[:text])
47
- populate_tree_node(child, table_item, tree_properties)
66
+ populate_tree_node(child, tree_item, tree_properties)
48
67
  end
49
68
  end
50
69
  end
@@ -17,7 +17,13 @@ module Glimmer
17
17
  end
18
18
 
19
19
  def interpret(parent, keyword, *args, &block)
20
- Glimmer::SWT::WidgetProxy.new(keyword, parent, args)
20
+ begin
21
+ class_name = "#{keyword.camelcase(:upper)}Proxy".to_sym
22
+ widget_class = Glimmer::SWT.const_get(class_name)
23
+ rescue
24
+ widget_class = Glimmer::SWT::WidgetProxy
25
+ end
26
+ widget_class.new(keyword, parent, args)
21
27
  end
22
28
  end
23
29
  end
@@ -25,3 +31,4 @@ module Glimmer
25
31
  end
26
32
 
27
33
  require 'glimmer/swt/widget_proxy'
34
+ require 'glimmer/swt/tree_proxy'
@@ -3,8 +3,6 @@ require 'rake'
3
3
 
4
4
  require_relative 'rake_task'
5
5
 
6
- load File.expand_path('./Rakefile') if File.exist?(File.expand_path('./Rakefile'))
7
-
8
6
  module Glimmer
9
7
  class Launcher
10
8
  OPERATING_SYSTEMS_SUPPORTED = ["mac", "windows", "linux"]
@@ -118,6 +116,11 @@ module Glimmer
118
116
  end
119
117
  end
120
118
  end
119
+
120
+ attr_reader :application_paths
121
+ attr_reader :env_vars
122
+ attr_reader :glimmer_options
123
+ attr_reader :jruby_options
121
124
 
122
125
  def initialize(raw_options)
123
126
  @application_paths = extract_application_paths(raw_options)
@@ -137,6 +140,7 @@ module Glimmer
137
140
  private
138
141
 
139
142
  def launch_application
143
+ load File.expand_path('./Rakefile') if File.exist?(File.expand_path('./Rakefile'))
140
144
  threads = @application_paths.map do |application_path|
141
145
  Thread.new do
142
146
  self.class.launch(
@@ -77,7 +77,7 @@ class Scaffold
77
77
  GEMFILE_APP = <<~MULTI_LINE_STRING
78
78
  # frozen_string_literal: true
79
79
 
80
- source "https://rubygems.org"
80
+ source 'https://rubygems.org'
81
81
 
82
82
  git_source(:github) {|repo_name| "https://github.com/\#{repo_name}" }
83
83
 
@@ -91,16 +91,16 @@ class Scaffold
91
91
  GEMFILE_GEM = <<~MULTI_LINE_STRING
92
92
  # frozen_string_literal: true
93
93
 
94
- source "https://rubygems.org"
94
+ source 'https://rubygems.org'
95
95
 
96
96
  git_source(:github) {|repo_name| "https://github.com/\#{repo_name}" }
97
97
 
98
98
  gem 'glimmer', '~> #{VERSION}'
99
99
 
100
100
  group :development do
101
- gem "rspec", "~> 3.5.0"
102
- gem "jeweler", "2.3.9"
103
- gem "simplecov", ">= 0"
101
+ gem 'rspec', '~> 3.5.0'
102
+ gem 'jeweler', '2.3.9'
103
+ gem 'simplecov', '>= 0'
104
104
  end
105
105
  MULTI_LINE_STRING
106
106
 
@@ -155,7 +155,7 @@ class Scaffold
155
155
  end
156
156
  mkdir 'bin'
157
157
  write "bin/#{file_name(app_name)}", app_bin_file(app_name)
158
- FileUtils.chmod 0755, "bin/#{app_name.underscore}"
158
+ FileUtils.chmod 0755, "bin/#{file_name(app_name)}"
159
159
  system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n glimmer package\n'"
160
160
  system "open packages/bundles/#{human_name(app_name).gsub(' ', '\ ')}.app"
161
161
  # TODO generate rspec test suite
@@ -193,11 +193,21 @@ class Scaffold
193
193
  write '.ruby-gemset', gem_name
194
194
  write 'VERSION', '1.0.0'
195
195
  write 'Gemfile', GEMFILE_GEM
196
- write 'Rakefile', gem_rakefile
196
+ write 'Rakefile', gem_rakefile(custom_shell_name, namespace)
197
197
  append "lib/#{gem_name}.rb", gem_main_file(custom_shell_name, namespace)
198
198
  mkdir 'lib/views'
199
199
  custom_shell(custom_shell_name, namespace)
200
- system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n'"
200
+ mkdir 'bin'
201
+ write "bin/#{file_name(custom_shell_name)}", gem_bin_file(gem_name, custom_shell_name, namespace)
202
+ FileUtils.chmod 0755, "bin/#{file_name(custom_shell_name)}"
203
+ if OS.mac?
204
+ mkdir_p 'package/macosx'
205
+ icon_file = "package/macosx/#{human_name(custom_shell_name)}.icns"
206
+ cp File.expand_path('../../../icons/scaffold_app.icns', __FILE__), icon_file
207
+ puts "Created #{current_dir_name}/#{icon_file}"
208
+ end
209
+ system "bash -c '#{RVM_FUNCTION}\n cd .\n bundle\n glimmer package\n'"
210
+ system "open packages/bundles/#{human_name(custom_shell_name).gsub(' ', '\ ')}.app"
201
211
  puts "Finished creating #{gem_name} Ruby gem."
202
212
  puts 'Edit Rakefile to configure gem details.'
203
213
  puts 'Run `rake` to execute specs.'
@@ -253,19 +263,20 @@ class Scaffold
253
263
  end
254
264
 
255
265
  def class_name(app_name)
256
- app_name.camelcase(:upper)
266
+ app_name.underscore.camelcase(:upper)
257
267
  end
258
268
 
259
269
  def file_name(app_name)
260
270
  app_name.underscore
261
271
  end
272
+ alias dsl_widget_name file_name
262
273
 
263
274
  def human_name(app_name)
264
275
  app_name.underscore.titlecase
265
276
  end
266
277
 
267
278
  def compact_name(gem_name)
268
- gem_name.camelcase.downcase
279
+ gem_name.underscore.camelcase.downcase
269
280
  end
270
281
 
271
282
  def app_main_file(app_name)
@@ -286,8 +297,6 @@ class Scaffold
286
297
  app_view.open
287
298
  end
288
299
  end
289
-
290
- #{class_name(app_name)}.new.open
291
300
  MULTI_LINE_STRING
292
301
  end
293
302
 
@@ -307,12 +316,26 @@ class Scaffold
307
316
  def app_bin_file(app_name)
308
317
  <<~MULTI_LINE_STRING
309
318
  #!/usr/bin/env ruby
310
-
319
+
311
320
  require_relative '../app/#{file_name(app_name)}'
321
+
322
+ #{class_name(app_name)}.new.open
312
323
  MULTI_LINE_STRING
313
324
  end
314
325
 
315
- def gem_rakefile
326
+ def gem_bin_file(gem_name, custom_shell_name, namespace)
327
+ <<~MULTI_LINE_STRING
328
+ #!/usr/bin/env ruby
329
+
330
+ require_relative '../lib/#{gem_name}'
331
+
332
+ include Glimmer
333
+
334
+ #{dsl_widget_name(custom_shell_name)}.open
335
+ MULTI_LINE_STRING
336
+ end
337
+
338
+ def gem_rakefile(custom_shell_name = nil, namespace = nil)
316
339
  rakefile_content = File.read('Rakefile')
317
340
  lines = rakefile_content.split("\n")
318
341
  require_rake_line_index = lines.index(lines.detect {|l| l.include?("require 'rake'") })
@@ -321,8 +344,21 @@ class Scaffold
321
344
  lines.insert(gem_files_line_index, " gem.files = Dir['lib/**/*.rb']")
322
345
  spec_pattern_line_index = lines.index(lines.detect {|l| l.include?('spec.pattern =') })
323
346
  lines.insert(spec_pattern_line_index+1, " spec.ruby_opts = [Glimmer::Launcher.jruby_swt_options]")
324
- lines << "\nrequire 'glimmer/rake_task'\n"
325
- lines.join("\n")
347
+ lines << "\nrequire 'glimmer/rake_task'\n"
348
+ file_content = lines.join("\n")
349
+ if custom_shell_name
350
+ file_content << <<~MULTI_LINE_STRING
351
+ Glimmer::Package.javapackager_extra_args =
352
+ " -name '#{human_name(custom_shell_name)}'" +
353
+ " -title '#{human_name(custom_shell_name)}'" +
354
+ " -Bmac.CFBundleName='#{human_name(custom_shell_name)}'" +
355
+ " -Bmac.CFBundleIdentifier='org.#{namespace ? compact_name(namespace) : compact_name(custom_shell_name)}.application.#{compact_name(custom_shell_name)}'"
356
+ # " -BlicenseType=" +
357
+ # " -Bmac.category=" +
358
+ # " -Bmac.signing-key-developer-id-app="
359
+ MULTI_LINE_STRING
360
+ end
361
+ file_content
326
362
  end
327
363
 
328
364
  def spec_helper_file
@@ -20,33 +20,39 @@ module Glimmer
20
20
  alias opened_before? opened_before
21
21
 
22
22
  # Instantiates ShellProxy with same arguments expected by SWT Shell
23
- def initialize(*args)
24
- if args.first.is_a?(ShellProxy)
25
- args[0] = args[0].swt_widget
26
- end
27
- style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
28
- if style_args.any?
29
- style_arg_start_index = args.index(style_args.first)
30
- style_arg_last_index = args.index(style_args.last)
31
- args[style_arg_start_index..style_arg_last_index] = SWTProxy[style_args]
32
- end
33
- if args.first.nil? || (!args.first.is_a?(Display) && !args.first.is_a?(Shell))
34
- @display = DisplayProxy.instance.swt_display
35
- args = [@display] + args
36
- end
37
- args = args.compact
38
- @swt_widget = Shell.new(*args)
39
- @display ||= @swt_widget.getDisplay
40
- @swt_widget.setLayout(FillLayout.new)
41
- @swt_widget.setMinimumSize(WIDTH_MIN, HEIGHT_MIN)
42
- on_event_show do
43
- Thread.new do
44
- sleep(0.25)
45
- async_exec do
46
- @swt_widget.setActive
23
+ # if swt_widget keyword arg was passed, then it is assumed the shell has already been instantiated
24
+ # and the proxy wraps it instead of creating a new one.
25
+ def initialize(*args, swt_widget: nil)
26
+ if swt_widget
27
+ @swt_widget = swt_widget
28
+ else
29
+ if args.first.is_a?(ShellProxy)
30
+ args[0] = args[0].swt_widget
31
+ end
32
+ style_args = args.select {|arg| arg.is_a?(Symbol) || arg.is_a?(String)}
33
+ if style_args.any?
34
+ style_arg_start_index = args.index(style_args.first)
35
+ style_arg_last_index = args.index(style_args.last)
36
+ args[style_arg_start_index..style_arg_last_index] = SWTProxy[style_args]
37
+ end
38
+ if args.first.nil? || (!args.first.is_a?(Display) && !args.first.is_a?(Shell))
39
+ @display = DisplayProxy.instance.swt_display
40
+ args = [@display] + args
41
+ end
42
+ args = args.compact
43
+ @swt_widget = Shell.new(*args)
44
+ @swt_widget.setLayout(FillLayout.new)
45
+ @swt_widget.setMinimumSize(WIDTH_MIN, HEIGHT_MIN)
46
+ on_event_show do
47
+ Thread.new do
48
+ sleep(0.25)
49
+ async_exec do
50
+ @swt_widget.setActive unless @swt_widget.isDisposed
51
+ end
47
52
  end
48
53
  end
49
54
  end
55
+ @display ||= @swt_widget.getDisplay
50
56
  end
51
57
 
52
58
  # Centers shell within monitor it is in
@@ -96,16 +102,14 @@ module Glimmer
96
102
  end
97
103
 
98
104
  def pack_same_size
99
- minimum_size = @swt_widget.getMinimumSize
100
105
  bounds = @swt_widget.getBounds
101
- @swt_widget.setMinimumSize(bounds.width, bounds.height)
102
- listener = on_control_resized {
103
- @swt_widget.setSize(bounds.width, bounds.height)
104
- @swt_widget.setLocation(bounds.x, bounds.y)
105
- }
106
+ width = @swt_widget.getBounds.width
107
+ height = @swt_widget.getBounds.height
108
+ x = @swt_widget.getBounds.x
109
+ y = @swt_widget.getBounds.y
106
110
  @swt_widget.pack
107
- @swt_widget.removeControlListener(listener.swt_listener)
108
- @swt_widget.setMinimumSize(minimum_size)
111
+ @swt_widget.setSize(width, height)
112
+ @swt_widget.setLocation(x, y)
109
113
  end
110
114
 
111
115
  def content(&block)
@@ -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
@@ -0,0 +1,120 @@
1
+ require 'glimmer/swt/widget_proxy'
2
+
3
+ module Glimmer
4
+ module SWT
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
+
19
+ # Performs depth first search for tree items matching block condition
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
22
+ def depth_first_search(&condition)
23
+ found = []
24
+ recursive_depth_first_search(swt_widget.getItems.first, found, &condition)
25
+ found.to_java(TreeItem)
26
+ end
27
+
28
+ # Returns all tree items including descendants
29
+ def all_tree_items
30
+ depth_first_search
31
+ end
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
+
101
+ private
102
+
103
+ def recursive_depth_first_search(tree_item, found, &condition)
104
+ return if tree_item.nil?
105
+ found << tree_item if condition.nil? || condition.call(tree_item)
106
+ tree_item.getItems.each do |child_tree_item|
107
+ recursive_depth_first_search(child_tree_item, found, &condition)
108
+ end
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
118
+ end
119
+ end
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
  }
@@ -102,10 +105,24 @@ module Glimmer
102
105
  end
103
106
  end
104
107
 
108
+ def pack_same_size
109
+ bounds = @swt_widget.getBounds
110
+ listener = on_control_resized {
111
+ @swt_widget.setSize(bounds.width, bounds.height)
112
+ @swt_widget.setLocation(bounds.x, bounds.y)
113
+ }
114
+ if @swt_widget.is_a?(Composite)
115
+ @swt_widget.layout(true, true)
116
+ else
117
+ @swt_widget.pack(true)
118
+ end
119
+ @swt_widget.removeControlListener(listener.swt_listener)
120
+ end
121
+
105
122
  def widget_property_listener_installers
106
123
  @swt_widget_property_listener_installers ||= {
107
124
  Java::OrgEclipseSwtWidgets::Control => {
108
- :focus => proc do |observer|
125
+ :focus => lambda do |observer|
109
126
  on_focus_gained { |focus_event|
110
127
  observer.call(true)
111
128
  }
@@ -115,12 +132,12 @@ module Glimmer
115
132
  end,
116
133
  },
117
134
  Java::OrgEclipseSwtWidgets::Text => {
118
- :text => proc do |observer|
135
+ :text => lambda do |observer|
119
136
  on_modify_text { |modify_event|
120
137
  observer.call(@swt_widget.getText)
121
138
  }
122
139
  end,
123
- :caret_position => proc do |observer|
140
+ :caret_position => lambda do |observer|
124
141
  on_event_keydown { |event|
125
142
  observer.call(@swt_widget.getCaretPosition)
126
143
  }
@@ -134,7 +151,21 @@ module Glimmer
134
151
  observer.call(@swt_widget.getCaretPosition)
135
152
  }
136
153
  end,
137
- :selection_count => proc do |observer|
154
+ :selection => lambda do |observer|
155
+ on_event_keydown { |event|
156
+ observer.call(@swt_widget.getSelection)
157
+ }
158
+ on_event_keyup { |event|
159
+ observer.call(@swt_widget.getSelection)
160
+ }
161
+ on_event_mousedown { |event|
162
+ observer.call(@swt_widget.getSelection)
163
+ }
164
+ on_event_mouseup { |event|
165
+ observer.call(@swt_widget.getSelection)
166
+ }
167
+ end,
168
+ :selection_count => lambda do |observer|
138
169
  on_event_keydown { |event|
139
170
  observer.call(@swt_widget.getSelectionCount)
140
171
  }
@@ -148,7 +179,7 @@ module Glimmer
148
179
  observer.call(@swt_widget.getSelectionCount)
149
180
  }
150
181
  end,
151
- :top_index => proc do |observer|
182
+ :top_index => lambda do |observer|
152
183
  @last_top_index = @swt_widget.getTopIndex
153
184
  on_paint_control { |event|
154
185
  if @swt_widget.getTopIndex != @last_top_index
@@ -159,33 +190,33 @@ module Glimmer
159
190
  end,
160
191
  },
161
192
  Java::OrgEclipseSwtCustom::StyledText => {
162
- :text => proc do |observer|
193
+ :text => lambda do |observer|
163
194
  on_modify_text { |modify_event|
164
195
  observer.call(@swt_widget.getText)
165
196
  }
166
197
  end,
167
198
  },
168
199
  Java::OrgEclipseSwtWidgets::Button => {
169
- :selection => proc do |observer|
200
+ :selection => lambda do |observer|
170
201
  on_widget_selected { |selection_event|
171
202
  observer.call(@swt_widget.getSelection)
172
203
  }
173
204
  end
174
205
  },
175
206
  Java::OrgEclipseSwtWidgets::MenuItem => {
176
- :selection => proc do |observer|
207
+ :selection => lambda do |observer|
177
208
  on_widget_selected { |selection_event|
178
209
  observer.call(@swt_widget.getSelection)
179
210
  }
180
211
  end
181
212
  },
182
213
  Java::OrgEclipseSwtWidgets::Spinner => {
183
- :selection => proc do |observer|
214
+ :selection => lambda do |observer|
184
215
  on_widget_selected { |selection_event|
185
216
  observer.call(@swt_widget.getSelection)
186
217
  }
187
218
  end
188
- }
219
+ },
189
220
  }
190
221
  end
191
222
 
@@ -307,10 +338,12 @@ module Glimmer
307
338
  end
308
339
 
309
340
  def add_listener(underscored_listener_name, &block)
310
- widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
311
- listener = listener_class.new(listener_method => block)
341
+ widget_add_listener_method, listener_class, listener_method = self.class.find_listener(@swt_widget.getClass, underscored_listener_name)
342
+ widget_listener_proxy = nil
343
+ safe_block = lambda { |event| block.call(event) unless @swt_widget.isDisposed }
344
+ listener = listener_class.new(listener_method => safe_block)
312
345
  @swt_widget.send(widget_add_listener_method, listener)
313
- WidgetListenerProxy.new(listener)
346
+ 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)
314
347
  end
315
348
 
316
349
  # Looks through SWT class add***Listener methods till it finds one for which
@@ -361,8 +394,10 @@ module Glimmer
361
394
 
362
395
  def add_swt_event_listener(swt_constant, &block)
363
396
  event_type = SWTProxy[swt_constant]
364
- @swt_widget.addListener(event_type, &block)
365
- WidgetListenerProxy.new(@swt_widget.getListeners(event_type).last)
397
+ widget_listener_proxy = nil
398
+ safe_block = lambda { |event| block.call(event) unless @swt_widget.isDisposed }
399
+ @swt_widget.addListener(event_type, &safe_block)
400
+ 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)
366
401
  end
367
402
 
368
403
  def widget_custom_attribute_mapping
@@ -395,7 +430,7 @@ module Glimmer
395
430
  end
396
431
 
397
432
  def property_type_converters
398
- color_converter = proc do |value|
433
+ color_converter = lambda do |value|
399
434
  if value.is_a?(Symbol) || value.is_a?(String)
400
435
  ColorProxy.new(value).swt_color
401
436
  else
@@ -405,7 +440,7 @@ module Glimmer
405
440
  # TODO consider detecting type on widget method and automatically invoking right converter (e.g. :to_s for String, :to_i for Integer)
406
441
  @property_type_converters ||= {
407
442
  :background => color_converter,
408
- :background_image => proc do |value|
443
+ :background_image => lambda do |value|
409
444
  if value.is_a?(String)
410
445
  if value.start_with?('uri:classloader')
411
446
  value = value.sub(/^uri\:classloader\:\//, '')
@@ -426,7 +461,7 @@ module Glimmer
426
461
  end
427
462
  end,
428
463
  :foreground => color_converter,
429
- :font => proc do |value|
464
+ :font => lambda do |value|
430
465
  if value.is_a?(Hash)
431
466
  font_properties = value
432
467
  FontProxy.new(self, font_properties).swt_font
@@ -434,17 +469,17 @@ module Glimmer
434
469
  value
435
470
  end
436
471
  end,
437
- :items => proc do |value|
472
+ :items => lambda do |value|
438
473
  value.to_java :string
439
474
  end,
440
- :text => proc do |value|
475
+ :text => lambda do |value|
441
476
  if swt_widget.is_a?(Browser)
442
477
  value.to_s
443
478
  else
444
479
  value.to_s
445
480
  end
446
481
  end,
447
- :visible => proc do |value|
482
+ :visible => lambda do |value|
448
483
  !!value
449
484
  end,
450
485
  }
@@ -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.2
4
+ version: 0.7.7
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-13 00:00:00.000000000 Z
11
+ date: 2020-05-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  requirement: !ruby/object:Gem::Requirement
@@ -150,34 +150,6 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: 3.5.0
153
- - !ruby/object:Gem::Dependency
154
- requirement: !ruby/object:Gem::Requirement
155
- requirements:
156
- - - '='
157
- - !ruby/object:Gem::Version
158
- version: 0.8.5
159
- name: coveralls
160
- type: :development
161
- prerelease: false
162
- version_requirements: !ruby/object:Gem::Requirement
163
- requirements:
164
- - - '='
165
- - !ruby/object:Gem::Version
166
- version: 0.8.5
167
- - !ruby/object:Gem::Dependency
168
- requirement: !ruby/object:Gem::Requirement
169
- requirements:
170
- - - "~>"
171
- - !ruby/object:Gem::Version
172
- version: 0.10.0
173
- name: simplecov
174
- type: :development
175
- prerelease: false
176
- version_requirements: !ruby/object:Gem::Requirement
177
- requirements:
178
- - - "~>"
179
- - !ruby/object:Gem::Version
180
- version: 0.10.0
181
153
  - !ruby/object:Gem::Dependency
182
154
  requirement: !ruby/object:Gem::Requirement
183
155
  requirements:
@@ -192,9 +164,7 @@ dependencies:
192
164
  - - "~>"
193
165
  - !ruby/object:Gem::Version
194
166
  version: 0.8.1
195
- description: JRuby Desktop UI DSL + Data-Binding native-UI cross-platform library
196
- that enables productive and efficient authoring of desktop user-interfaces using
197
- the robust Eclipse SWT library
167
+ description: Desktop Development Library for Ruby
198
168
  email: andy.am@gmail.com
199
169
  executables:
200
170
  - glimmer
@@ -202,10 +172,10 @@ executables:
202
172
  extensions: []
203
173
  extra_rdoc_files:
204
174
  - LICENSE.txt
205
- - README.markdown
175
+ - README.md
206
176
  files:
207
177
  - LICENSE.txt
208
- - README.markdown
178
+ - README.md
209
179
  - RUBY_VERSION
210
180
  - VERSION
211
181
  - bin/girb
@@ -292,6 +262,7 @@ files:
292
262
  - lib/glimmer/swt/shell_proxy.rb
293
263
  - lib/glimmer/swt/swt_proxy.rb
294
264
  - lib/glimmer/swt/tab_item_proxy.rb
265
+ - lib/glimmer/swt/tree_proxy.rb
295
266
  - lib/glimmer/swt/widget_listener_proxy.rb
296
267
  - lib/glimmer/swt/widget_proxy.rb
297
268
  - lib/glimmer/ui/custom_shell.rb
@@ -327,5 +298,5 @@ requirements: []
327
298
  rubygems_version: 3.0.6
328
299
  signing_key:
329
300
  specification_version: 4
330
- summary: Desktop application development library
301
+ summary: Desktop Development Library for Ruby
331
302
  test_files: []