glimmer 0.7.6 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +239 -107
- data/VERSION +1 -1
- data/lib/glimmer/data_binding/table_items_binding.rb +14 -5
- data/lib/glimmer/dsl/swt/dialog_expression.rb +26 -0
- data/lib/glimmer/dsl/swt/message_box_expression.rb +29 -0
- data/lib/glimmer/dsl/swt/widget_expression.rb +1 -0
- data/lib/glimmer/launcher.rb +13 -10
- data/lib/glimmer/package.rb +48 -0
- data/lib/glimmer/rake_task.rb +14 -45
- data/lib/glimmer/scaffold.rb +205 -65
- data/lib/glimmer/swt/display_proxy.rb +14 -0
- data/lib/glimmer/swt/menu_proxy.rb +17 -0
- data/lib/glimmer/swt/message_box_proxy.rb +48 -0
- data/lib/glimmer/swt/shell_proxy.rb +14 -32
- data/lib/glimmer/swt/table_proxy.rb +150 -0
- data/lib/glimmer/swt/tree_proxy.rb +1 -1
- data/lib/glimmer/ui/custom_widget.rb +9 -6
- metadata +11 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fbe97823fd25586221485191db661a4cb6fe71d8d9be9e89b60d617d5f496540
|
4
|
+
data.tar.gz: d81b11d52ce3f0fe1fdb46d9f1a6c286ccd95c3021118f71b4f72d7724cd0f41
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 930a0914fc83db952df2eba9321779ee34a3d2819a0dabb05a31615b97b1eb57ff222321bd9db9fa4d6950e0f4e68f6455a66223ac1a1126a1c72be75a8a8b46
|
7
|
+
data.tar.gz: 5ab308d9b3403a9876d8f40f8a51d277cf0c88527f257977f87a21fc530a95a0d3e92d64a6b4025a56b2ed857f3d6b1c4d0d3df10bed7999c25cf90e589d6934
|
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
# Glimmer 0.
|
1
|
+
# Glimmer 0.8.2 Beta (Ruby Desktop Development GUI Library)
|
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
|
-
|
4
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/38fbc278022862794414/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer/maintainability)
|
5
5
|
|
6
|
-
Glimmer is a native-
|
6
|
+
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.
|
7
7
|
|
8
8
|
[<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
|
9
9
|
Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
|
@@ -70,11 +70,11 @@ Glimmer app:
|
|
70
70
|
|
71
71
|
![Tic Tac Toe](images/glimmer-tic-tac-toe.png)
|
72
72
|
|
73
|
-
NOTE: Glimmer is in beta mode. Please help make better by adopting for small or low risk projects and providing feedback.
|
73
|
+
NOTE: Glimmer is in beta mode. Please help make better by [contributing](#contributing), adopting for small or low risk projects, and providing feedback.
|
74
74
|
|
75
|
-
## Table of
|
75
|
+
## Table of contents
|
76
76
|
|
77
|
-
- [Glimmer 0.
|
77
|
+
- [Glimmer 0.8.2 Beta (Ruby Desktop Development GUI Library)](#glimmer-082-beta-ruby-desktop-development-gui-library)
|
78
78
|
- [Examples](#examples)
|
79
79
|
- [Hello, World!](#hello-world)
|
80
80
|
- [Tic Tac Toe](#tic-tac-toe)
|
@@ -88,22 +88,55 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
|
|
88
88
|
- [Basic Usage](#basic-usage)
|
89
89
|
- [Advanced Usage](#advanced-usage)
|
90
90
|
- [Scaffolding](#scaffolding)
|
91
|
+
- [App](#app)
|
92
|
+
- [Custom Shell](#custom-shell)
|
93
|
+
- [Custom Widget](#custom-widget)
|
94
|
+
- [Custom Shell Gem](#custom-shell-gem)
|
95
|
+
- [Custom Widget Gem](#custom-widget-gem)
|
91
96
|
- [Girb (Glimmer irb) Command](#girb-glimmer-irb-command)
|
92
97
|
- [Glimmer DSL Syntax](#glimmer-dsl-syntax)
|
93
98
|
- [Widgets](#widgets)
|
99
|
+
- [Display](#display)
|
100
|
+
- [SWT Proxies](#swt-proxies)
|
101
|
+
- [Dialog](#dialog)
|
102
|
+
- [Menus](#menus)
|
94
103
|
- [Widget Styles](#widget-styles)
|
104
|
+
- [Explicit SWT Style Bit](#explicit-swt-style-bit)
|
105
|
+
- [Negative SWT Style Bits](#negative-swt-style-bits)
|
106
|
+
- [Extra SWT Styles](#extra-swt-styles)
|
95
107
|
- [Widget Properties](#widget-properties)
|
108
|
+
- [Colors](#colors)
|
109
|
+
- [Fonts](#fonts)
|
96
110
|
- [Layouts](#layouts)
|
97
111
|
- [Layout Data](#layout-data)
|
98
112
|
- [Data-Binding](#data-binding)
|
113
|
+
- [General Examples](#general-examples)
|
114
|
+
- [Combo](#combo)
|
115
|
+
- [List](#list)
|
116
|
+
- [Table](#table)
|
117
|
+
- [Tree](#tree)
|
99
118
|
- [Observer](#observer)
|
119
|
+
- [Observing Widgets](#observing-widgets)
|
120
|
+
- [Observing Models](#observing-models)
|
100
121
|
- [Custom Widgets](#custom-widgets)
|
122
|
+
- [Simple Example](#simple-example)
|
123
|
+
- [Hook Example](#hook-example)
|
124
|
+
- [Content/Options Example](#contentoptions-example)
|
125
|
+
- [Gotcha](#gotcha)
|
101
126
|
- [Custom Shells](#custom-shells)
|
102
127
|
- [Miscellaneous](#miscellaneous)
|
128
|
+
- [Application Menu Items (About/Preferences)](#application-menu-items-aboutpreferences)
|
129
|
+
- [App Name and Version](#app-name-and-version)
|
130
|
+
- [Multi-DSL Support](#multi-dsl-support)
|
131
|
+
- [Video Widget](#video-widget)
|
132
|
+
- [Browser Widget](#browser-widget)
|
103
133
|
- [Glimmer Style Guide](#glimmer-style-guide)
|
104
134
|
- [Samples](#samples)
|
105
135
|
- [Hello Samples](#hello-samples)
|
106
136
|
- [Elaborate Samples](#elaborate-samples)
|
137
|
+
- [External Samples](#external-samples)
|
138
|
+
- [Glimmer Calculator](#glimmer-calculator)
|
139
|
+
- [Gladiator](#gladiator)
|
107
140
|
- [In Production](#in-production)
|
108
141
|
- [SWT Reference](#swt-reference)
|
109
142
|
- [SWT Packages](#swt-packages)
|
@@ -111,7 +144,8 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
|
|
111
144
|
- [Raw JRuby Command](#raw-jruby-command)
|
112
145
|
- [Mac Support](#mac-support)
|
113
146
|
- [Packaging & Distribution](#packaging--distribution)
|
114
|
-
- [Defaults](#defaults)
|
147
|
+
- [Packaging Defaults](#packaging-defaults)
|
148
|
+
- [Packaging Configuration](#packaging-configuration)
|
115
149
|
- [javapackager Extra Arguments](#javapackager-extra-arguments)
|
116
150
|
- [Mac Application Distribution](#mac-application-distribution)
|
117
151
|
- [Self Signed Certificate](#self-signed-certificate)
|
@@ -125,6 +159,7 @@ NOTE: Glimmer is in beta mode. Please help make better by adopting for small or
|
|
125
159
|
- [Contributing](#contributing)
|
126
160
|
- [Contributors](#contributors)
|
127
161
|
- [License](#license)
|
162
|
+
|
128
163
|
## Background
|
129
164
|
|
130
165
|
Ruby is a dynamically-typed object-oriented language, which provides great productivity gains due to its powerful expressive syntax and dynamic nature. While it is proven by the Ruby on Rails framework for web development, it currently lacks a robust platform-independent framework for building desktop applications. Given that Java libraries can now be utilized in Ruby code through JRuby, Eclipse technologies, such as SWT, JFace, and RCP can help fill the gap of desktop application development with Ruby.
|
@@ -136,22 +171,22 @@ Glimmer runs on the following platforms:
|
|
136
171
|
- Windows
|
137
172
|
- Linux
|
138
173
|
|
139
|
-
Glimmer's
|
174
|
+
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:
|
140
175
|
- Win32 on Windows
|
141
176
|
- Cocoa on Mac
|
142
177
|
- GTK on Linux
|
143
178
|
|
144
|
-
More info about the SWT
|
179
|
+
More info about the SWT GUI on various platforms can be found on the Eclipse WIKI and SWT FAQ:
|
145
180
|
|
146
181
|
https://wiki.eclipse.org/SWT/Devel/Gtk/Dev_guide#Win32.2FCocoa.2FGTK
|
147
182
|
https://www.eclipse.org/swt/faq.php
|
148
183
|
|
149
|
-
|
150
184
|
## Pre-requisites
|
151
185
|
|
152
186
|
- SWT 4.15 (comes included in Glimmer gem)
|
153
|
-
- JRuby 9.2.11.1 (supporting Ruby 2.5.x syntax) (find at https://www.jruby.org/download)
|
154
|
-
-
|
187
|
+
- JRuby 9.2.11.1 (supporting Ruby 2.5.x syntax) (find at [https://www.jruby.org/download](https://www.jruby.org/download))
|
188
|
+
- JDK 8 - 10 (find at [https://www.oracle.com/java/technologies/javase-downloads.html](https://www.oracle.com/java/technologies/javase-downloads.html))
|
189
|
+
- (Optional) RVM is needed for [Scaffolding](#scaffolding) only (find at [https://rvm.io/](https://rvm.io/))
|
155
190
|
|
156
191
|
On **Mac** and **Linux**, an easy way to obtain JRuby is through [RVM](http://rvm.io) by running:
|
157
192
|
|
@@ -169,7 +204,7 @@ Please follow these instructions to make the `glimmer` command available on your
|
|
169
204
|
|
170
205
|
Run this command to install directly:
|
171
206
|
```
|
172
|
-
jgem install glimmer -v 0.
|
207
|
+
jgem install glimmer -v 0.8.2
|
173
208
|
```
|
174
209
|
|
175
210
|
`jgem` is JRuby's version of `gem` command.
|
@@ -180,7 +215,7 @@ Otherwise, you may also run `jruby -S gem install ...`
|
|
180
215
|
|
181
216
|
Add the following to `Gemfile`:
|
182
217
|
```
|
183
|
-
gem 'glimmer', '~> 0.
|
218
|
+
gem 'glimmer', '~> 0.8.2'
|
184
219
|
```
|
185
220
|
|
186
221
|
And, then run:
|
@@ -217,7 +252,7 @@ bin/glimmer samples/hello/hello_world.rb
|
|
217
252
|
Below are the full usage instructions that come up when running `glimmer` without args.
|
218
253
|
|
219
254
|
```
|
220
|
-
Usage: glimmer [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
|
255
|
+
Usage: glimmer [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
|
221
256
|
|
222
257
|
Runs Glimmer applications/tasks.
|
223
258
|
|
@@ -232,7 +267,7 @@ glimmer package:jar # Generate JAR
|
|
232
267
|
glimmer package:native # Generate Native files (DMG/PKG/APP on the Mac)
|
233
268
|
glimmer scaffold[app_name] # Scaffold a Glimmer application directory structure to begin building a new app
|
234
269
|
glimmer scaffold:custom_shell[custom_shell_name,namespace] # Scaffold a Glimmer::UI::CustomShell subclass (represents a full window view) under app/views (namespace is optional)
|
235
|
-
glimmer scaffold:custom_shell_gem[
|
270
|
+
glimmer scaffold:custom_shell_gem[custom_shell_name,namespace] # Scaffold a Glimmer::UI::CustomShell subclass (represents a full window view) under its own Ruby gem + app project (namespace is required)
|
236
271
|
glimmer scaffold:custom_widget[custom_widget_name,namespace] # Scaffold a Glimmer::UI::CustomWidget subclass (represents a part of a view) under app/views (namespace is optional)
|
237
272
|
glimmer scaffold:custom_widget_gem[custom_widget_name,namespace] # Scaffold a Glimmer::UI::CustomWidget subclass (represents a part of a view) under its own Ruby gem project (namespace is required)
|
238
273
|
|
@@ -242,8 +277,9 @@ automatically preloading the glimmer Ruby gem and SWT jar dependency.
|
|
242
277
|
Optionally, extra Glimmer options, JRuby options and environment variables may be passed in.
|
243
278
|
|
244
279
|
Glimmer options:
|
245
|
-
- "--
|
246
|
-
- "--
|
280
|
+
- "--quiet" : Does not announce file path of Glimmer application being launched
|
281
|
+
- "--debug" : Displays extra debugging information, passes "--debug" to JRuby, and enables debug logging
|
282
|
+
- "--log-level=VALUE" : Sets Glimmer's Ruby logger level ("ERROR" / "WARN" / "INFO" / "DEBUG"; default is none)
|
247
283
|
|
248
284
|
Example: glimmer samples/hello_world.rb
|
249
285
|
|
@@ -275,7 +311,7 @@ getting you to a running and delivered state of an advanced "Hello, World!" Glim
|
|
275
311
|
This should greatly facilitate building a new Glimmer app by helping you be productive and focus on app details while
|
276
312
|
letting Glimmer scaffolding take care of initial app file structure concerns, such as adding:
|
277
313
|
- Main application class that includes Glimmer
|
278
|
-
- Main application view that houses main window content
|
314
|
+
- Main application view that houses main window content, about dialog, and preferences dialog
|
279
315
|
- View and Model directories
|
280
316
|
- Rakefile including Glimmer tasks
|
281
317
|
- Version
|
@@ -283,7 +319,7 @@ letting Glimmer scaffolding take care of initial app file structure concerns, su
|
|
283
319
|
- Icon
|
284
320
|
- Bin file for starting application
|
285
321
|
|
286
|
-
NOTE: Scaffolding currently supports Mac packaging only at the moment.
|
322
|
+
NOTE: Scaffolding requires RVM and currently supports Mac packaging only at the moment.
|
287
323
|
|
288
324
|
#### App
|
289
325
|
|
@@ -341,9 +377,13 @@ glimmer scaffold:custom_widget[custom_widget_name]
|
|
341
377
|
|
342
378
|
#### Custom Shell Gem
|
343
379
|
|
344
|
-
Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
|
380
|
+
Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
|
381
|
+
They have everything scaffolded Glimmer apps come with in addition to gem content like a Jeweler Rakefile that can build gemspec and release gems.
|
382
|
+
Unlike scaffolded Glimmer apps, custom shell gem content lives under the `lib` directory (not `app`).
|
383
|
+
They can be packaged as both a native executable (e.g. Mac DMG/PKG/APP) and a Ruby gem.
|
384
|
+
Of course, you can just build a Ruby gem and disregard native executable packaging if you do not need it.
|
345
385
|
|
346
|
-
To scaffold a Glimmer custom shell gem (full window view
|
386
|
+
To scaffold a Glimmer custom shell gem (full window view distributed as a Ruby gem), run the following command:
|
347
387
|
|
348
388
|
```
|
349
389
|
glimmer scaffold:custom_shell_gem[custom_shell_name, namespace]
|
@@ -351,15 +391,18 @@ glimmer scaffold:custom_shell_gem[custom_shell_name, namespace]
|
|
351
391
|
|
352
392
|
It is important to specify a namespace to avoid having your gem clash with existing gems.
|
353
393
|
|
354
|
-
The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell)
|
394
|
+
The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell).
|
355
395
|
|
356
396
|
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)
|
357
397
|
|
358
|
-
|
398
|
+
Examples:
|
399
|
+
|
400
|
+
- [glimmer-cs-gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator): Gladiator (Glimmer Editor)
|
401
|
+
- [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator): Glimmer Calculator
|
359
402
|
|
360
403
|
#### Custom Widget Gem
|
361
404
|
|
362
|
-
To scaffold a Glimmer custom widget gem (part of a view
|
405
|
+
To scaffold a Glimmer custom widget gem (part of a view distributed as a Ruby gem), run the following command:
|
363
406
|
|
364
407
|
```
|
365
408
|
glimmer scaffold:custom_widget_gem[custom_widget_name, namespace]
|
@@ -409,7 +452,7 @@ You will learn more about widgets next.
|
|
409
452
|
|
410
453
|
### Widgets
|
411
454
|
|
412
|
-
Glimmer
|
455
|
+
Glimmer GUIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
|
413
456
|
|
414
457
|
https://www.eclipse.org/swt/widgets/
|
415
458
|
|
@@ -518,7 +561,7 @@ shell {
|
|
518
561
|
|
519
562
|
#### Display
|
520
563
|
|
521
|
-
SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage
|
564
|
+
SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage GUI globally
|
522
565
|
and access available monitors.
|
523
566
|
It is automatically instantiated upon first instantiation of a `shell` widget.
|
524
567
|
Alternatively, for advanced use cases, it can be created explicitly with Glimmer `display` keyword. When a `shell` is later declared, it
|
@@ -546,7 +589,7 @@ Glimmer follows Proxy Design Pattern by having Ruby proxy wrappers for all SWT o
|
|
546
589
|
- `Glimmer::SWT:TabItemProxy` wraps `org.eclipse.swt.widget.TabItem` (also adds a composite to enable adding content under tab items directly in Glimmer)
|
547
590
|
- `Glimmer::SWT:LayoutProxy` wraps all descendants of `org.eclipse.swt.widget.Layout`
|
548
591
|
- `Glimmer::SWT:LayoutDataProxy` wraps all layout data objects
|
549
|
-
- `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying
|
592
|
+
- `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying GUI)
|
550
593
|
- `Glimmer::SWT:ColorProxy` wraps `org.eclipse.swt.graphics.Color`
|
551
594
|
- `Glimmer::SWT:FontProxy` wraps `org.eclipse.swt.graphics.Font`
|
552
595
|
- `Glimmer::SWT::WidgetListenerProxy` wraps all widget listeners
|
@@ -615,6 +658,12 @@ Shell widget proxy has extra methods specific to SWT Shell:
|
|
615
658
|
- `#pack`: Packs contained widgets using SWT's `Shell#pack` method
|
616
659
|
- `#pack_same_size`: Packs contained widgets without changing shell's size when widget sizes change
|
617
660
|
|
661
|
+
#### Dialog
|
662
|
+
|
663
|
+
Dialog is a variation on Shell. It is basically a shell that is modal (blocks what's behind it) and belongs to another shell. It only has a close button.
|
664
|
+
|
665
|
+
Glimmer facilitates building dialogs by using the `dialog` keyword, which automatically adds the SWT.DIALOG_TRIM and SWT.APPLICATION_MODAL [widget styles](#widget-styles) needed for a dialog.
|
666
|
+
|
618
667
|
#### Menus
|
619
668
|
|
620
669
|
Glimmer DSL provides support for SWT Menu and MenuItem widgets.
|
@@ -1081,7 +1130,7 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
|
|
1081
1130
|
|
1082
1131
|
Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
|
1083
1132
|
|
1084
|
-
#### General
|
1133
|
+
#### General Examples
|
1085
1134
|
|
1086
1135
|
`text bind(contact, :first_name)`
|
1087
1136
|
|
@@ -1252,7 +1301,62 @@ The Glimmer code is not much different from above except for passing the `:multi
|
|
1252
1301
|
|
1253
1302
|
Note that in all the data-binding examples above, there was also an observer attached to the `button` widget to trigger an action on the model, which in turn triggers a data-binding update on the `list` or `combo`. Observers will be discussed in more details in the [next section](#observer).
|
1254
1303
|
|
1255
|
-
You may learn more about Glimmer's data-binding syntax by reading the
|
1304
|
+
You may learn more about Glimmer's data-binding syntax by reading the code under the [samples](samples) directory.
|
1305
|
+
|
1306
|
+
#### Table
|
1307
|
+
|
1308
|
+
The SWT Tree widget renders a multi-column data table, such as a contact listing or a sales report.
|
1309
|
+
|
1310
|
+
To data-bind a Table, you need the main model, the collection property, and the text display attribute for each table column.
|
1311
|
+
|
1312
|
+
This involves using the `bind` keyword mentioned above in addition to a special `column_properties` keyword that takes the table column text attribute methods.
|
1313
|
+
|
1314
|
+
It assumes you have defined the table columns via `table_column` widget.
|
1315
|
+
|
1316
|
+
Example:
|
1317
|
+
|
1318
|
+
```ruby
|
1319
|
+
shell {
|
1320
|
+
@table = table {
|
1321
|
+
table_column {
|
1322
|
+
text "Name"
|
1323
|
+
width 120
|
1324
|
+
}
|
1325
|
+
table_column {
|
1326
|
+
text "Age"
|
1327
|
+
width 120
|
1328
|
+
}
|
1329
|
+
table_column {
|
1330
|
+
text "Adult"
|
1331
|
+
width 120
|
1332
|
+
}
|
1333
|
+
items bind(group, :people), column_properties(:name, :age, :adult)
|
1334
|
+
selection bind(group, :selected_person)
|
1335
|
+
}
|
1336
|
+
}
|
1337
|
+
```
|
1338
|
+
|
1339
|
+
The code above includes two data-bindings:
|
1340
|
+
- Table `items`, which first bind to the model collection property (group.people), and then maps each column property (name, age, adult) for displaying each table item column.
|
1341
|
+
- Table `selection`, which binds the single table item selected by the user to the attribute denoted by the `bind` keyword (or binds multiple table items selected for a table with `:multi` SWT style)
|
1342
|
+
|
1343
|
+
Additionally, Table `items` data-binding automatically stores each node model unto the SWT TableItem object via `setData` method. This enables things like searchability.
|
1344
|
+
|
1345
|
+
The table widget in Glimmer is represented by a subclass of `WidgetProxy` called `TableProxy`.
|
1346
|
+
TableProxy includes a `search` method that takes a block to look for a table item.
|
1347
|
+
|
1348
|
+
Example:
|
1349
|
+
|
1350
|
+
```ruby
|
1351
|
+
found_array = @table.search { |table_item| table_item.getData == company.owner }
|
1352
|
+
```
|
1353
|
+
|
1354
|
+
This finds a person. The array is a Java array. This enables easy passing of it to SWT `Table#setSelection` method, which expects a Java array of `TableItem` objects.
|
1355
|
+
|
1356
|
+
To edit a table, you must invoke `TableProxy#edit_selected_table_item(column_index, before_write: nil, after_write: nil, after_cancel: nil)` or `TableProxy#edit_table_item(table_item, column_index, before_write: nil, after_write: nil, after_cancel: nil)`.
|
1357
|
+
This automatically leverages the SWT TableEditor custom class behind the scenes, displaying a text widget to the user to change the selected or
|
1358
|
+
passed table item text into something else.
|
1359
|
+
It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
|
1256
1360
|
|
1257
1361
|
#### Tree
|
1258
1362
|
|
@@ -1305,7 +1409,7 @@ Glimmer supports observing widgets with two main types of events:
|
|
1305
1409
|
|
1306
1410
|
Additionally, there are two more types of events:
|
1307
1411
|
- SWT `display` supports global listeners called filters that run on any widget. They are hooked via `on_event_{swt-event-constant}`
|
1308
|
-
-
|
1412
|
+
- SWT `display` supports Mac application menu item observers (`on_about` and `on_preferences`), which you can read about under [Miscellaneous](#miscellaneous).
|
1309
1413
|
|
1310
1414
|
Number 1 is more commonly used in SWT applications, so make it your starting point. Number 2 covers events not found in number 1, so look into it if you don't find an SWT listener you need in number 1.
|
1311
1415
|
|
@@ -1405,7 +1509,7 @@ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
|
1405
1509
|
|
1406
1510
|
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.
|
1407
1511
|
|
1408
|
-
The alternative syntax can be helpful if you prefer to separate Glimmer observer declarations from Glimmer
|
1512
|
+
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.
|
1409
1513
|
|
1410
1514
|
#### Observing Models
|
1411
1515
|
|
@@ -1468,7 +1572,9 @@ Glimmer supports creating custom widgets with minimal code, which automatically
|
|
1468
1572
|
|
1469
1573
|
Simply create a new class that includes `Glimmer::UI::CustomWidget` and put Glimmer DSL code in its `#body` block (its return value is stored in `#body_root` attribute). Glimmer will then automatically recognize this class by convention when it encounters a keyword matching the class name converted to underscored lowercase (and namespace double-colons `::` replaced with double-underscores `__`)
|
1470
1574
|
|
1471
|
-
#### Example
|
1575
|
+
#### Simple Example
|
1576
|
+
|
1577
|
+
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
1472
1578
|
|
1473
1579
|
Definition:
|
1474
1580
|
```ruby
|
@@ -1494,7 +1600,9 @@ shell {
|
|
1494
1600
|
|
1495
1601
|
As you can see, `RedLabel` became Glimmer DSL keyword: `red_label`
|
1496
1602
|
|
1497
|
-
####
|
1603
|
+
#### Hook Example
|
1604
|
+
|
1605
|
+
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
1498
1606
|
|
1499
1607
|
Definition:
|
1500
1608
|
```ruby
|
@@ -1531,19 +1639,21 @@ Notice how `Red::Composite` became `red__composite` with double-underscore, whic
|
|
1531
1639
|
|
1532
1640
|
Keep in mind that namespaces are not needed to be specified if the Custom Widget class has a unique name, not clashing with a basic SWT widget or another custom widget name.
|
1533
1641
|
|
1534
|
-
Custom Widgets have the following attributes
|
1642
|
+
Custom Widgets have the following attributes available to call from inside the `#body` method:
|
1535
1643
|
- `#parent`: Glimmer object parenting custom widget
|
1536
1644
|
- `#swt_style`: SWT style integer. Can be useful if you want to allow consumers to customize a widget inside the custom widget body
|
1537
|
-
- `#options`: a hash of options passed in parentheses when declaring a custom widget (useful for passing in model data) (e.g. `calendar(events: events)`). Custom widget class can declare option names (array) with
|
1645
|
+
- `#options`: a hash of options passed in parentheses when declaring a custom widget (useful for passing in model data) (e.g. `calendar(events: events)`). Custom widget class can declare option names (array) with `::options` class method as shown below, which generates attribute accessors for every option (not to be confused with `#options` instance method for retrieving options hash containing names & values)
|
1538
1646
|
- `#content`: nested block underneath custom widget. It will be automatically called at the end of processing the custom widget body. Alternatively, the custom widget body may call `content.call` at the place where the content is needed to show up as shown in the following example.
|
1539
1647
|
- `#body_root`: top-most (root) widget returned from `#body` method.
|
1540
1648
|
- `#swt_widget`: actual SWT widget for `body_root`
|
1541
1649
|
|
1542
1650
|
Additionally, custom widgets can call the following class methods:
|
1543
|
-
-
|
1544
|
-
-
|
1651
|
+
- `::options(*option_names)`: declares a list of options by taking an option name array (symbols/strings). This generates option attribute accessors (e.g. `options :orientation, :bg_color` generates `#orientation`, `#orientation=(v)`, `#bg_color`, and `#bg_color=(v)` attribute accessors)
|
1652
|
+
- `::option(option_name, default: nil)`: declares a single option taking option name and default value as arguments (also generates attribute accessors just like `::options`)
|
1545
1653
|
|
1546
|
-
#### Content/Options Example
|
1654
|
+
#### Content/Options Example
|
1655
|
+
|
1656
|
+
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
1547
1657
|
|
1548
1658
|
Definition:
|
1549
1659
|
```ruby
|
@@ -1551,7 +1661,7 @@ class Sandwich
|
|
1551
1661
|
include Glimmer::UI::CustomWidget
|
1552
1662
|
|
1553
1663
|
options :orientation, :bg_color
|
1554
|
-
option :fg_color, :black
|
1664
|
+
option :fg_color, default: :black
|
1555
1665
|
|
1556
1666
|
body {
|
1557
1667
|
composite(swt_style) { # gets custom widget style
|
@@ -1686,52 +1796,61 @@ shell { |app_shell|
|
|
1686
1796
|
|
1687
1797
|
#### Application Menu Items (About/Preferences)
|
1688
1798
|
|
1689
|
-
Mac applications always have About and Preferences menu items. Glimmer provides widget observer hooks for them on the `
|
1799
|
+
Mac applications always have About and Preferences menu items. Glimmer provides widget observer hooks for them on the `display`:
|
1690
1800
|
- `on_about`: executes code when user selects App Name -> About
|
1691
1801
|
- `on_preferences`: executes code when user selects App Name -> Preferences or hits 'CMD+,' on the Mac
|
1692
1802
|
|
1693
1803
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1694
1804
|
|
1695
1805
|
```ruby
|
1696
|
-
|
1697
|
-
|
1698
|
-
|
1699
|
-
|
1700
|
-
|
1701
|
-
|
1702
|
-
|
1703
|
-
|
1704
|
-
font height: 30
|
1705
|
-
}
|
1706
|
-
on_about {
|
1707
|
-
message_box = MessageBox.new(shell_proxy.swt_widget)
|
1708
|
-
message_box.setText("About")
|
1709
|
-
message_box.setMessage("About Application")
|
1710
|
-
message_box.open
|
1711
|
-
}
|
1712
|
-
on_preferences {
|
1713
|
-
preferences_dialog = shell(:dialog_trim, :application_modal) {
|
1714
|
-
text 'Preferences'
|
1715
|
-
row_layout {
|
1716
|
-
type :vertical
|
1717
|
-
margin_left 15
|
1718
|
-
margin_top 15
|
1719
|
-
margin_right 15
|
1720
|
-
margin_bottom 15
|
1806
|
+
class Example
|
1807
|
+
def initialize
|
1808
|
+
display {
|
1809
|
+
on_about {
|
1810
|
+
message_box = MessageBox.new(@shell_proxy.swt_widget)
|
1811
|
+
message_box.setText("About")
|
1812
|
+
message_box.setMessage("About Application")
|
1813
|
+
message_box.open
|
1721
1814
|
}
|
1722
|
-
|
1723
|
-
|
1815
|
+
on_preferences {
|
1816
|
+
preferences_dialog = dialog {
|
1817
|
+
text 'Preferences'
|
1818
|
+
row_layout {
|
1819
|
+
type :vertical
|
1820
|
+
margin_left 15
|
1821
|
+
margin_top 15
|
1822
|
+
margin_right 15
|
1823
|
+
margin_bottom 15
|
1824
|
+
}
|
1825
|
+
label {
|
1826
|
+
text 'Check one of these options:'
|
1827
|
+
}
|
1828
|
+
button(:radio) {
|
1829
|
+
text 'Option 1'
|
1830
|
+
}
|
1831
|
+
button(:radio) {
|
1832
|
+
text 'Option 2'
|
1833
|
+
}
|
1834
|
+
}
|
1835
|
+
preferences_dialog.open
|
1724
1836
|
}
|
1725
|
-
|
1726
|
-
|
1837
|
+
}
|
1838
|
+
@shell_proxy = shell {
|
1839
|
+
text 'Application Menu Items'
|
1840
|
+
fill_layout {
|
1841
|
+
margin_width 15
|
1842
|
+
margin_height 15
|
1727
1843
|
}
|
1728
|
-
|
1729
|
-
text '
|
1844
|
+
label {
|
1845
|
+
text 'Application Menu Items'
|
1846
|
+
font height: 30
|
1730
1847
|
}
|
1731
1848
|
}
|
1732
|
-
|
1733
|
-
|
1734
|
-
|
1849
|
+
@shell_proxy.open
|
1850
|
+
end
|
1851
|
+
end
|
1852
|
+
|
1853
|
+
Example.new
|
1735
1854
|
```
|
1736
1855
|
|
1737
1856
|
#### App Name and Version
|
@@ -1939,7 +2058,7 @@ This relies on Glimmer's [Multi-DSL Support](https://github.com/AndyObtiva/glimm
|
|
1939
2058
|
- Widget property declarations always have arguments and never take a block
|
1940
2059
|
- Widget property arguments are never wrapped inside parentheses
|
1941
2060
|
- Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
|
1942
|
-
- Widget listeners are always followed by a block using curly braces (Only when declared in DSL. When invoked on widget object directly outside of
|
2061
|
+
- 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)
|
1943
2062
|
- Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
|
1944
2063
|
- Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
|
1945
2064
|
- 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.
|
@@ -1973,12 +2092,22 @@ glimmer samples/hello/hello_computed.rb # demonstrates computed data-binding
|
|
1973
2092
|
For more elaborate samples, check the following:
|
1974
2093
|
|
1975
2094
|
```
|
1976
|
-
glimmer samples/elaborate/login.rb # demonstrates
|
2095
|
+
glimmer samples/elaborate/login.rb # demonstrates basic data-binding
|
1977
2096
|
glimmer samples/elaborate/contact_manager.rb # demonstrates table data-binding
|
1978
2097
|
glimmer samples/elaborate/tic_tac_toe.rb # demonstrates a full MVC application
|
1979
2098
|
```
|
1980
2099
|
|
1981
|
-
|
2100
|
+
### External Samples
|
2101
|
+
|
2102
|
+
#### Glimmer Calculator
|
2103
|
+
|
2104
|
+
[<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/v1.0.0/glimmer-cs-calculator-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-calculator)
|
2105
|
+
|
2106
|
+
[Glimmer Calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) is a basic calculator sample project demonstrating data-binding and TDD (test-driven-development) with Glimmer following the MVP pattern (Model-View-Presenter).
|
2107
|
+
|
2108
|
+
#### Gladiator
|
2109
|
+
|
2110
|
+
[<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/v0.1.5/images/glimmer-gladiator.png" />](https://github.com/AndyObtiva/glimmer-cs-gladiator)
|
1982
2111
|
|
1983
2112
|
[Gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator) (short for Glimmer Editor) is a Glimmer sample project under on-going development.
|
1984
2113
|
You may check it out to learn how to build a Glimmer Custom Shell gem.
|
@@ -2107,33 +2236,20 @@ Glimmer apps may be packaged and distributed on the Mac, Windows, and Linux via
|
|
2107
2236
|
- Warbler (https://github.com/jruby/warbler): Enables bundling a Glimmer app into a JAR file
|
2108
2237
|
- javapackager (https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html): Enables packaging a JAR file as a DMG file on Mac, EXE on Windows, and multiple Linux supported formats on Linux.
|
2109
2238
|
|
2110
|
-
Glimmer simplifies the process
|
2111
|
-
|
2112
|
-
To use:
|
2113
|
-
- Create `Rakefile` in your app root directory
|
2114
|
-
- Add the following line to it: `require 'glimmer/rake_task'`
|
2115
|
-
- Create a Ruby script under bin (e.g. `bin/math_bowling`) to require the application file that uses Glimmer (e.g. `'../app/my_application.rb'`):
|
2116
|
-
```ruby
|
2117
|
-
require_relative '../app/my_application.rb'
|
2118
|
-
```
|
2119
|
-
- Include Icon (Optional): If you'd like to include an icon for your app (.icns format on the Mac), place it under `package/macosx` matching the humanized application local directory name (e.g. 'Math Bowling.icns' [containing space] for MathBowling or math_bowling). You may generate your Mac icon easily using tools like Image2Icon (http://www.img2icnsapp.com/) or manually using the Mac terminal command `iconutil` (iconutil guide: https://applehelpwriter.com/tag/iconutil/)
|
2120
|
-
- Include Version (Optional): Create a `VERSION` file in your application and fill it your app version on one line (e.g. `1.1.0`)
|
2121
|
-
- Include License (Optional): Create a `LICENSE.txt` file in your application and fill it up with your license (e.g. MIT). It will show up to people when installing your app. Note that, you may optionally also specify license type, but you'd have to do so manually via `-BlicenseType=MIT` shown in an [example below](#javapackager-extra-arguments).
|
2122
|
-
- Extra args (Optional): You may optionally add the following to `Rakefile` to configure extra arguments for javapackager: `Glimmer::Packager.javapackager_extra_args = "..."` (Useful to avoid re-entering extra arguments on every run of rake task.). Read about them in [their section below](#javapackager-extra-arguments).
|
2239
|
+
Glimmer simplifies the process of Mac packaging via the `glimmer package` command. It works out of the box for any application generated by [Glimmer Scaffolding](https://github.com/AndyObtiva/glimmer/blob/master/README.md#scaffolding):
|
2123
2240
|
|
2124
|
-
Now, you can run the following rake command to package your app into a Mac DMG file (using both Warbler and javapackager):
|
2125
2241
|
```
|
2126
|
-
|
2242
|
+
glimmer package
|
2127
2243
|
```
|
2128
2244
|
|
2129
|
-
This will generate a JAR file under `./dist` directory, which is then used to generate a DMG file (and pkg/app) under `./packages/bundles`.
|
2245
|
+
This will automatically generate a JAR file under `./dist` directory using Warbler, which is then used to automatically generate a DMG file (and pkg/app) under `./packages/bundles` using `javapackager`.
|
2130
2246
|
JAR file name will match your application local directory name (e.g. `MathBowling.jar` for `~/code/MathBowling`)
|
2131
2247
|
DMG file name will match the humanized local directory name + dash + application version (e.g. `Math Bowling-1.0.dmg` for `~/code/MathBowling` with version 1.0 or unspecified)
|
2132
2248
|
|
2133
|
-
|
2249
|
+
The `glimmer package` command will automatically set "mac.CFBundleIdentifier" to ="org.#{project_name}.application.#{project_name}".
|
2134
2250
|
You may override by configuring as an extra argument for javapackger (e.g. Glimmer::Package.javapackager_extra_args = " -Bmac.CFBundleIdentifier=org.andymaleh.application.MathBowling")
|
2135
2251
|
|
2136
|
-
### Defaults
|
2252
|
+
### Packaging Defaults
|
2137
2253
|
|
2138
2254
|
Glimmer employs smart defaults in packaging.
|
2139
2255
|
|
@@ -2141,13 +2257,24 @@ The package application name (shows up in top menu bar on the Mac) will be a hum
|
|
2141
2257
|
|
2142
2258
|
Also, the package will only include these directories: app, config, db, lib, script, bin, docs, fonts, images, sounds, videos
|
2143
2259
|
|
2144
|
-
After running once, you will find a `config/warble.rb` file. It has the JAR packaging configuration. You may adjust included directories in it if needed, and then rerun `
|
2260
|
+
After running once, you will find a `config/warble.rb` file. It has the JAR packaging configuration. You may adjust included directories in it if needed, and then rerun `glimmer package` and it will pick up your custom configuration. Alternatively, if you'd like to customize the included directories to begin with, don't run `glimmer package` right away. Run this command first:
|
2145
2261
|
|
2146
2262
|
```
|
2147
|
-
|
2263
|
+
glimmer package:config
|
2148
2264
|
```
|
2149
2265
|
|
2150
|
-
This will generate `config/warble.rb`, which you may configure and then run `
|
2266
|
+
This will generate `config/warble.rb`, which you may configure and then run `glimmer package` afterwards.
|
2267
|
+
|
2268
|
+
### Packaging Configuration
|
2269
|
+
|
2270
|
+
- Ensure you have a Ruby script under `bin` directory that launches the application, preferably matching your project directory name (e.g. `bin/math_bowling`) :
|
2271
|
+
```ruby
|
2272
|
+
require_relative '../app/my_application.rb'
|
2273
|
+
```
|
2274
|
+
- Include Icon (Optional): If you'd like to include an icon for your app (.icns format on the Mac), place it under `package/macosx` matching the humanized application local directory name (e.g. 'Math Bowling.icns' [containing space] for MathBowling or math_bowling). You may generate your Mac icon easily using tools like Image2Icon (http://www.img2icnsapp.com/) or manually using the Mac terminal command `iconutil` (iconutil guide: https://applehelpwriter.com/tag/iconutil/)
|
2275
|
+
- Include Version (Optional): Create a `VERSION` file in your application and fill it your app version on one line (e.g. `1.1.0`)
|
2276
|
+
- Include License (Optional): Create a `LICENSE.txt` file in your application and fill it up with your license (e.g. MIT). It will show up to people when installing your app. Note that, you may optionally also specify license type, but you'd have to do so manually via `-BlicenseType=MIT` shown in an [example below](#javapackager-extra-arguments).
|
2277
|
+
- Extra args (Optional): You may optionally add the following to `Rakefile` to configure extra arguments for javapackager: `Glimmer::Packager.javapackager_extra_args = "..."` (Useful to avoid re-entering extra arguments on every run of rake task.). Read about them in [their section below](#javapackager-extra-arguments).
|
2151
2278
|
|
2152
2279
|
### javapackager Extra Arguments
|
2153
2280
|
|
@@ -2165,6 +2292,8 @@ The Glimmer rake task allows passing extra options to javapackager via:
|
|
2165
2292
|
Example (Rakefile):
|
2166
2293
|
|
2167
2294
|
```ruby
|
2295
|
+
require 'glimmer/rake_task'
|
2296
|
+
|
2168
2297
|
Glimmer::Package.javapackager_extra_args = '-BlicenseType="MIT" -Bmac.category="public.app-category.business" -Bmac.signing-key-developer-id-app="Andy Maleh"'
|
2169
2298
|
```
|
2170
2299
|
|
@@ -2175,7 +2304,7 @@ https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMac
|
|
2175
2304
|
Example (env var):
|
2176
2305
|
|
2177
2306
|
```
|
2178
|
-
JAVAPACKAGER_EXTRA_ARGS='-Bmac.CFBundleName="Math Bowling Game"'
|
2307
|
+
JAVAPACKAGER_EXTRA_ARGS='-Bmac.CFBundleName="Math Bowling Game"' glimmer package
|
2179
2308
|
```
|
2180
2309
|
|
2181
2310
|
That overrides the default application display name.
|
@@ -2220,7 +2349,7 @@ Example:
|
|
2220
2349
|
Glimmer::Package.javapackager_extra_args = '-Bmac.signing-key-developer-id-app="Andy Maleh"'
|
2221
2350
|
```
|
2222
2351
|
|
2223
|
-
Now, when you run `
|
2352
|
+
Now, when you run `glimmer package`, it builds a self-signed DMG file. When you make available online, and users download, upon launching application, they are presented with your certificate, which they have to sign if they trust you in order to use the application.
|
2224
2353
|
|
2225
2354
|
### Gotchas
|
2226
2355
|
|
@@ -2237,7 +2366,7 @@ Glimmer::Package.javapackager_extra_args = '-srcfiles "ACME.txt" -BlicenseFile="
|
|
2237
2366
|
|
2238
2367
|
2. Mounted DMG Residue
|
2239
2368
|
|
2240
|
-
If you run `
|
2369
|
+
If you run `glimmer package` multiple times, sometimes it leaves a mounted DMG project in your finder. Unmount before you run the command again or it might fail with an error saying: "Error: Bundler "DMG Installer" (dmg) failed to produce a bundle."
|
2241
2370
|
|
2242
2371
|
By the way, keep in mind that during normal operation, it does also indicate a false-negative while completing successfully similar to the following (please ignore):
|
2243
2372
|
|
@@ -2248,10 +2377,11 @@ Exec failed with code 2 command [[/usr/bin/SetFile, -c, icnC, /var/folders/4_/g1
|
|
2248
2377
|
## Resources
|
2249
2378
|
|
2250
2379
|
* [Code Master Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
|
2251
|
-
* [Eclipse Zone Tutorial](http://eclipse.dzone.com/articles/an-introduction-glimmer)
|
2252
|
-
* [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
|
2253
|
-
* [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
|
2254
2380
|
* [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
|
2381
|
+
* [MountainWest RubyConf 2011 Video](https://confreaks.tv/videos/mwrc2011-whatever-happened-to-desktop-development-in-ruby)
|
2382
|
+
* [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
|
2383
|
+
* [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
|
2384
|
+
* [DZone Tutorial](https://dzone.com/articles/an-introduction-glimmer)
|
2255
2385
|
|
2256
2386
|
## Help
|
2257
2387
|
|
@@ -2283,8 +2413,10 @@ These features have been suggested. You might see them in a future version of Gl
|
|
2283
2413
|
|
2284
2414
|
## Contributors
|
2285
2415
|
|
2286
|
-
* Andy Maleh (Founder)
|
2287
|
-
* Dennis Theisen
|
2416
|
+
* [Andy Maleh](https://github.com/AndyObtiva) (Founder)
|
2417
|
+
* [Dennis Theisen](https://github.com/Soleone) (Contributor)
|
2418
|
+
|
2419
|
+
[Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer/graphs/contributors)
|
2288
2420
|
|
2289
2421
|
## License
|
2290
2422
|
|