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 +4 -4
- data/{README.markdown → README.md} +103 -29
- data/VERSION +1 -1
- data/lib/glimmer/data_binding/model_binding.rb +1 -0
- data/lib/glimmer/data_binding/observable_model.rb +1 -3
- data/lib/glimmer/data_binding/table_items_binding.rb +1 -0
- data/lib/glimmer/data_binding/tree_items_binding.rb +30 -11
- data/lib/glimmer/dsl/swt/widget_expression.rb +8 -1
- data/lib/glimmer/launcher.rb +6 -2
- data/lib/glimmer/scaffold.rb +52 -16
- data/lib/glimmer/swt/shell_proxy.rb +36 -32
- data/lib/glimmer/swt/tab_item_proxy.rb +6 -0
- data/lib/glimmer/swt/tree_proxy.rb +120 -0
- data/lib/glimmer/swt/widget_listener_proxy.rb +21 -4
- data/lib/glimmer/swt/widget_proxy.rb +60 -25
- data/lib/glimmer/ui/custom_widget.rb +4 -0
- metadata +7 -36
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76d86f5e3e322edba85aee815e28a0bb19b6ed56694ff44b1311a759b249b189
|
4
|
+
data.tar.gz: ad84ddf98c7a9da150f1dd4897e7a7df0e7d8cf06d317a1a748929c1446895ce
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dbbd46186a07700fed156566a5a0de4fb5ad90720ae2e24eb0662bc1ef3a64850f29d4f91fa577f388136511e3ec20689a97e1686e25f34112fabf35c05c2981
|
7
|
+
data.tar.gz: f484803847e17525090f815ea117d429f10abc87f12a5c38343c9d6e2f56ff6bf055b153f5fa3f250ae60adde2b5ff4cba7b209b0b1693bc98d0f619cc1d15db
|
@@ -1,12 +1,17 @@
|
|
1
|
-
# Glimmer 0.7.
|
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
|
-
[![
|
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-
|
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
|
-
|
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
|
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
|
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.
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
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
|
-
* [
|
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.
|
1
|
+
0.7.7
|
@@ -40,9 +40,7 @@ module Glimmer
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def property_observer_hash
|
43
|
-
|
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)
|
@@ -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
|
-
|
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(
|
29
|
-
|
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
|
-
|
44
|
-
|
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
|
-
|
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
|
-
|
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'
|
data/lib/glimmer/launcher.rb
CHANGED
@@ -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(
|
data/lib/glimmer/scaffold.rb
CHANGED
@@ -77,7 +77,7 @@ class Scaffold
|
|
77
77
|
GEMFILE_APP = <<~MULTI_LINE_STRING
|
78
78
|
# frozen_string_literal: true
|
79
79
|
|
80
|
-
source
|
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
|
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
|
102
|
-
gem
|
103
|
-
gem
|
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
|
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
|
-
|
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
|
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
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
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.
|
102
|
-
|
103
|
-
|
104
|
-
|
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.
|
108
|
-
@swt_widget.
|
111
|
+
@swt_widget.setSize(width, height)
|
112
|
+
@swt_widget.setLocation(x, y)
|
109
113
|
end
|
110
114
|
|
111
115
|
def content(&block)
|
@@ -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
|
-
|
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" =>
|
37
|
+
"composite" => lambda do |composite|
|
35
38
|
composite.setLayout(GridLayout.new)
|
36
39
|
end,
|
37
|
-
"table" =>
|
40
|
+
"table" => lambda do |table|
|
38
41
|
table.setHeaderVisible(true)
|
39
42
|
table.setLinesVisible(true)
|
40
43
|
end,
|
41
|
-
"table_column" =>
|
44
|
+
"table_column" => lambda do |table_column|
|
42
45
|
table_column.setWidth(80)
|
43
46
|
end,
|
44
|
-
"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 =>
|
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 =>
|
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 =>
|
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
|
-
:
|
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 =>
|
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 =>
|
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 =>
|
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 =>
|
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 =>
|
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
|
-
|
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
|
-
|
365
|
-
|
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 =
|
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 =>
|
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 =>
|
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 =>
|
472
|
+
:items => lambda do |value|
|
438
473
|
value.to_java :string
|
439
474
|
end,
|
440
|
-
:text =>
|
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 =>
|
482
|
+
:visible => lambda do |value|
|
448
483
|
!!value
|
449
484
|
end,
|
450
485
|
}
|
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
|
+
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-
|
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:
|
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.
|
175
|
+
- README.md
|
206
176
|
files:
|
207
177
|
- LICENSE.txt
|
208
|
-
- README.
|
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
|
301
|
+
summary: Desktop Development Library for Ruby
|
331
302
|
test_files: []
|