glimmer 0.10.1 → 1.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +365 -0
- data/CONTRIBUTING.md +62 -0
- data/LICENSE.txt +1 -1
- data/PROCESS.md +35 -0
- data/README.md +485 -2871
- data/VERSION +1 -1
- data/glimmer.gemspec +81 -0
- data/lib/glimmer.rb +37 -19
- data/lib/glimmer/config.rb +31 -9
- data/lib/glimmer/data_binding/model_binding.rb +37 -27
- data/lib/glimmer/data_binding/observable.rb +21 -0
- data/lib/glimmer/data_binding/observable_array.rb +228 -29
- data/lib/glimmer/data_binding/observable_model.rb +43 -22
- data/lib/glimmer/data_binding/observer.rb +22 -0
- data/lib/glimmer/dsl/engine.rb +66 -58
- data/lib/glimmer/dsl/expression.rb +21 -0
- data/lib/glimmer/dsl/expression_handler.rb +21 -0
- data/lib/glimmer/dsl/parent_expression.rb +21 -0
- data/lib/glimmer/dsl/static_expression.rb +21 -0
- data/lib/glimmer/dsl/top_level_expression.rb +21 -0
- data/lib/glimmer/error.rb +21 -0
- data/lib/glimmer/invalid_keyword_error.rb +21 -0
- metadata +47 -20
- data/lib/glimmer/excluded_keyword_error.rb +0 -5
data/README.md
CHANGED
@@ -1,27 +1,76 @@
|
|
1
|
-
# <img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 style="position: relative; top: 20px;" /> Glimmer
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 style="position: relative; top: 20px;" />](https://rubygems.org/gems/glimmer) Glimmer 1.0.1 - DSL Framework
|
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
|
[![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
|
5
|
+
[![Maintainability](https://api.codeclimate.com/v1/badges/38fbc278022862794414/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer/maintainability)
|
5
6
|
[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
6
7
|
|
7
8
|
**[Contributors Wanted! (Submit a Glimmer App Sample to Get Started)](#contributing)**
|
8
9
|
|
9
10
|
(The Original Glimmer Library Since 2007. Beware of Imitators!)
|
10
11
|
|
11
|
-
[**Glimmer**](https://rubygems.org/gems/glimmer)
|
12
|
+
[**Glimmer**](https://rubygems.org/gems/glimmer) started out as [GUI Library](https://github.com/AndyObtiva/glimmer-dsl-swt) and grew into a full-fledged [DSL Framework](#multi-dsl-support). Glimmer's namesake is referring to the Glimmer of Ruby in Graphical User Interfaces (contrary to popular myth perpetrated by [Charles Nutter](http://blog.headius.com/2007/11/tab-sweep.html), Glimmer has nothing to do with the ill-fated Whitney Houston movie, which does not in fact share the same name)
|
12
13
|
|
13
|
-
[
|
14
|
-
|
14
|
+
[**Glimmer**](https://rubygems.org/gems/glimmer) is a DSL Framework that consists of:
|
15
|
+
- DSL Engine: enables building DSLs for desktop GUI, XML/HTML documents, CSS styling, and webification of desktop apps.
|
16
|
+
- Data-Binding/Observer/Observable Library: enables synchronizing GUI with Model Attributes bidirectionally.
|
15
17
|
|
16
|
-
Glimmer
|
17
|
-
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
|
18
|
+
[**Glimmer**](https://rubygems.org/gems/glimmer) supports the following DSLs:
|
19
|
+
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Library)
|
20
|
+
- [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
|
18
21
|
- [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
|
19
22
|
- [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
|
20
23
|
- [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
|
21
24
|
|
22
|
-
|
25
|
+
[Glimmer and/or Glimmer DSLs receive two updates per month](https://rubygems.org/gems/glimmer-dsl-swt/versions). You can trust [Glimmer](https://rubygems.org/gems/glimmer) with your Ruby development needs.
|
26
|
+
|
27
|
+
[<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
|
28
|
+
Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
|
29
|
+
|
30
|
+
## Table of contents
|
31
|
+
|
32
|
+
- [Glimmer 1.0.1](#-glimmer-101)
|
33
|
+
- [Glimmer DSL for SWT (JRuby Desktop Development GUI Library)](#glimmer-dsl-for-swt-jruby-desktop-development-gui-library)
|
34
|
+
- [Glimmer DSL for SWT Samples](#glimmer-dsl-for-swt-samples)
|
35
|
+
- [Hello, World!](#hello-world)
|
36
|
+
- [Tic Tac Toe](#tic-tac-toe)
|
37
|
+
- [Contact Manager](#contact-manager)
|
38
|
+
- [Production Desktop Apps Built with Glimmer DSL for SWT](#production-desktop-apps-built-with-glimmer-dsl-for-swt)
|
39
|
+
- [Glimmer DSL for Tk (Ruby Desktop Development GUI Library)](#glimmer-dsl-for-tk-ruby-desktop-development-gui-library)
|
40
|
+
- [Glimmer DSL for Tk Samples](#glimmer-dsl-for-tk-samples)
|
41
|
+
- [Hello, World!](#hello-world)
|
42
|
+
- [Hello, Tab!](#hello-tab)
|
43
|
+
- [Hello, Combo!](#hello-combo)
|
44
|
+
- [Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)](#glimmer-dsl-for-opal-web-gui-adapter-for-desktop-apps)
|
45
|
+
- [Glimmer DSL for Opal Samples](#glimmer-dsl-for-opal-samples)
|
46
|
+
- [Hello, Computed!](#hello-computed)
|
47
|
+
- [Hello, List Single Selection!](#hello-list-single-selection)
|
48
|
+
- [Hello, List Multi Selection!](#hello-list-multi-selection)
|
49
|
+
- [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
|
50
|
+
- [XML DSL](#xml-dsl)
|
51
|
+
- [Glimmer DSL for CSS](#glimmer-dsl-for-css)
|
52
|
+
- [CSS DSL](#css-dsl)
|
53
|
+
- [Multi-DSL Support](#multi-dsl-support)
|
54
|
+
- [Glimmer Supporting Libraries](#glimmer-supporting-libraries)
|
55
|
+
- [Glimmer Process](#glimmer-process)
|
56
|
+
- [Resources](#resources)
|
57
|
+
- [Help](#help)
|
58
|
+
- [Issues](#issues)
|
59
|
+
- [Chat](#chat)
|
60
|
+
- [Feature Suggestions](#feature-suggestions)
|
61
|
+
- [Change Log](#change-log)
|
62
|
+
- [Contributing](#contributing)
|
63
|
+
- [Contributors](#contributors)
|
64
|
+
- [Hire Me](#hire-me)
|
65
|
+
- [License](#license)
|
66
|
+
|
67
|
+
## Glimmer DSL for SWT (JRuby Desktop Development GUI Library)
|
68
|
+
|
69
|
+
[Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) is a native-GUI cross-platform desktop development library written in [JRuby](https://www.jruby.org/), an OS-threaded faster version of [Ruby](https://www.ruby-lang.org/en/). [Glimmer](https://rubygems.org/gems/glimmer)'s main innovation is a declarative [Ruby DSL](https://github.com/AndyObtiva/glimmer-dsl-swt#glimmer-dsl-syntax) that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust [Eclipse SWT library](https://www.eclipse.org/swt/). [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) additionally innovates by having built-in [data-binding](https://github.com/AndyObtiva/glimmer-dsl-swt#data-binding) support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns. To get started quickly, [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) offers [scaffolding](https://github.com/AndyObtiva/glimmer-dsl-swt#scaffolding) options for [Apps](https://github.com/AndyObtiva/glimmer-dsl-swt#in-production), [Gems](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-shell-gem), and [Custom Widgets](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-widgets). [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) also includes native-executable [packaging](https://github.com/AndyObtiva/glimmer-dsl-swt#packaging--distribution) support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in [Ruby](https://www.ruby-lang.org/en/) as truly native DMG/PKG/APP files on the [Mac](https://www.apple.com/ca/macos) + [App Store](https://developer.apple.com/macos/distribution/) and MSI/EXE files on [Windows](https://www.microsoft.com/en-ca/windows).
|
70
|
+
|
71
|
+
### Glimmer DSL for SWT Samples
|
23
72
|
|
24
|
-
|
73
|
+
#### Hello, World!
|
25
74
|
|
26
75
|
Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)):
|
27
76
|
```ruby
|
@@ -37,21 +86,24 @@ shell {
|
|
37
86
|
|
38
87
|
Run:
|
39
88
|
```
|
40
|
-
glimmer
|
89
|
+
glimmer sample:run[hello_world]
|
41
90
|
```
|
42
91
|
|
43
92
|
Glimmer app:
|
44
93
|
|
45
94
|
![Hello World](images/glimmer-hello-world.png)
|
46
95
|
|
47
|
-
|
96
|
+
#### Tic Tac Toe
|
48
97
|
|
49
98
|
Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)):
|
50
99
|
|
51
100
|
```ruby
|
52
101
|
# ...
|
102
|
+
@tic_tac_toe_board = Board.new
|
103
|
+
|
53
104
|
@shell = shell {
|
54
105
|
text "Tic-Tac-Toe"
|
106
|
+
minimum_size 150, 178
|
55
107
|
composite {
|
56
108
|
grid_layout 3, true
|
57
109
|
(1..3).each { |row|
|
@@ -60,6 +112,7 @@ Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObt
|
|
60
112
|
layout_data :fill, :fill, true, true
|
61
113
|
text bind(@tic_tac_toe_board[row, column], :sign)
|
62
114
|
enabled bind(@tic_tac_toe_board[row, column], :empty)
|
115
|
+
font style: :bold, height: 20
|
63
116
|
on_widget_selected {
|
64
117
|
@tic_tac_toe_board.mark(row, column)
|
65
118
|
}
|
@@ -68,20 +121,25 @@ Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObt
|
|
68
121
|
}
|
69
122
|
}
|
70
123
|
}
|
124
|
+
|
125
|
+
observe(@tic_tac_toe_board, :game_status) { |game_status|
|
126
|
+
display_win_message if game_status == Board::WIN
|
127
|
+
display_draw_message if game_status == Board::DRAW
|
128
|
+
}
|
71
129
|
# ...
|
72
130
|
```
|
73
131
|
|
74
132
|
Run:
|
75
133
|
|
76
134
|
```
|
77
|
-
glimmer
|
135
|
+
glimmer sample:run[tic_tac_toe]
|
78
136
|
```
|
79
137
|
|
80
138
|
Glimmer app:
|
81
139
|
|
82
140
|
![Tic Tac Toe](images/glimmer-tic-tac-toe-in-progress.png)
|
83
141
|
|
84
|
-
|
142
|
+
#### Contact Manager
|
85
143
|
|
86
144
|
Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)):
|
87
145
|
|
@@ -90,41 +148,76 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
|
|
90
148
|
shell {
|
91
149
|
text "Contact Manager"
|
92
150
|
composite {
|
93
|
-
|
94
|
-
grid_layout
|
95
|
-
|
151
|
+
group {
|
152
|
+
grid_layout(2, false) {
|
153
|
+
margin_width 0
|
154
|
+
margin_height 0
|
155
|
+
}
|
156
|
+
layout_data :fill, :center, true, false
|
157
|
+
text 'Lookup Contacts'
|
158
|
+
font height: 24
|
159
|
+
|
160
|
+
label {
|
161
|
+
layout_data :right, :center, false, false
|
162
|
+
text "First &Name: "
|
163
|
+
font height: 16
|
164
|
+
}
|
96
165
|
text {
|
166
|
+
layout_data :fill, :center, true, false
|
97
167
|
text bind(@contact_manager_presenter, :first_name)
|
98
168
|
on_key_pressed {|key_event|
|
99
169
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
100
170
|
}
|
101
171
|
}
|
102
|
-
|
172
|
+
|
173
|
+
label {
|
174
|
+
layout_data :right, :center, false, false
|
175
|
+
text "&Last Name: "
|
176
|
+
font height: 16
|
177
|
+
}
|
103
178
|
text {
|
179
|
+
layout_data :fill, :center, true, false
|
104
180
|
text bind(@contact_manager_presenter, :last_name)
|
105
181
|
on_key_pressed {|key_event|
|
106
182
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
107
183
|
}
|
108
184
|
}
|
109
|
-
|
185
|
+
|
186
|
+
label {
|
187
|
+
layout_data :right, :center, false, false
|
188
|
+
text "&Email: "
|
189
|
+
font height: 16
|
190
|
+
}
|
110
191
|
text {
|
192
|
+
layout_data :fill, :center, true, false
|
111
193
|
text bind(@contact_manager_presenter, :email)
|
112
194
|
on_key_pressed {|key_event|
|
113
195
|
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
114
196
|
}
|
115
197
|
}
|
198
|
+
|
116
199
|
composite {
|
117
|
-
|
200
|
+
row_layout {
|
201
|
+
margin_width 0
|
202
|
+
margin_height 0
|
203
|
+
}
|
204
|
+
layout_data(:right, :center, false, false) {
|
205
|
+
horizontal_span 2
|
206
|
+
}
|
207
|
+
|
118
208
|
button {
|
119
209
|
text "&Find"
|
120
|
-
on_widget_selected {
|
121
|
-
|
210
|
+
on_widget_selected { @contact_manager_presenter.find }
|
211
|
+
on_key_pressed {|key_event|
|
212
|
+
@contact_manager_presenter.find if key_event.keyCode == swt(:cr)
|
122
213
|
}
|
123
214
|
}
|
215
|
+
|
124
216
|
button {
|
125
217
|
text "&List All"
|
126
|
-
on_widget_selected {
|
127
|
-
|
218
|
+
on_widget_selected { @contact_manager_presenter.list }
|
219
|
+
on_key_pressed {|key_event|
|
220
|
+
@contact_manager_presenter.list if key_event.keyCode == swt(:cr)
|
128
221
|
}
|
129
222
|
}
|
130
223
|
}
|
@@ -151,7 +244,7 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
|
|
151
244
|
width 200
|
152
245
|
}
|
153
246
|
items bind(@contact_manager_presenter, :results),
|
154
|
-
|
247
|
+
column_properties(:first_name, :last_name, :email)
|
155
248
|
on_mouse_up { |event|
|
156
249
|
table_proxy.edit_table_item(event.table_item, event.column_index)
|
157
250
|
}
|
@@ -164,2990 +257,511 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
|
|
164
257
|
Run:
|
165
258
|
|
166
259
|
```
|
167
|
-
glimmer
|
260
|
+
glimmer sample:run[contact_manager]
|
168
261
|
```
|
169
262
|
|
170
263
|
Glimmer App:
|
171
264
|
|
172
265
|
![Contact Manager](images/glimmer-contact-manager.png)
|
173
266
|
|
174
|
-
|
175
|
-
|
176
|
-
## Table of contents
|
177
|
-
|
178
|
-
- [Glimmer (Ruby Desktop Development GUI Library)](#-glimmer-ruby-desktop-development-gui-library)
|
179
|
-
- [Examples](#examples)
|
180
|
-
- [Hello, World!](#hello-world)
|
181
|
-
- [Tic Tac Toe](#tic-tac-toe)
|
182
|
-
- [Background](#background)
|
183
|
-
- [Platform Support](#platform-support)
|
184
|
-
- [Pre-requisites](#pre-requisites)
|
185
|
-
- [Setup](#setup)
|
186
|
-
- [Option 1: Direct Install](#option-1-direct-install)
|
187
|
-
- [Option 2: Bundler](#option-2-bundler)
|
188
|
-
- [Glimmer Command](#glimmer-command)
|
189
|
-
- [Basic Usage](#basic-usage)
|
190
|
-
- [Advanced Usage](#advanced-usage)
|
191
|
-
- [Scaffolding](#scaffolding)
|
192
|
-
- [App](#app)
|
193
|
-
- [Custom Shell](#custom-shell)
|
194
|
-
- [Custom Widget](#custom-widget)
|
195
|
-
- [Custom Shell Gem](#custom-shell-gem)
|
196
|
-
- [Custom Widget Gem](#custom-widget-gem)
|
197
|
-
- [Gem Listing](#gem-listing)
|
198
|
-
- [Listing Custom Shell Gems](#listing-custom-shell-gems)
|
199
|
-
- [Listing Custom Widget Gems](#listing-custom-widget-gems)
|
200
|
-
- [Listing DSL Gems](#listing-dsl-gems)
|
201
|
-
- [Packaging](#packaging)
|
202
|
-
- [Raw JRuby Command](#raw-jruby-command)
|
203
|
-
- [Mac Support](#mac-support)
|
204
|
-
- [Girb (Glimmer irb) Command](#girb-glimmer-irb-command)
|
205
|
-
- [Glimmer DSL Syntax](#glimmer-dsl-syntax)
|
206
|
-
- [Widgets](#widgets)
|
207
|
-
- [Display](#display)
|
208
|
-
- [SWT Proxies](#swt-proxies)
|
209
|
-
- [Dialog](#dialog)
|
210
|
-
- [Menus](#menus)
|
211
|
-
- [ScrolledComposite](#scrolledcomposite)
|
212
|
-
- [Widget Styles](#widget-styles)
|
213
|
-
- [Explicit SWT Style Bit](#explicit-swt-style-bit)
|
214
|
-
- [Negative SWT Style Bits](#negative-swt-style-bits)
|
215
|
-
- [Extra SWT Styles](#extra-swt-styles)
|
216
|
-
- [Widget Properties](#widget-properties)
|
217
|
-
- [Colors](#colors)
|
218
|
-
- [Fonts](#fonts)
|
219
|
-
- [Layouts](#layouts)
|
220
|
-
- [Layout Data](#layout-data)
|
221
|
-
- [Data-Binding](#data-binding)
|
222
|
-
- [General Examples](#general-examples)
|
223
|
-
- [Combo](#combo)
|
224
|
-
- [List](#list)
|
225
|
-
- [Table](#table)
|
226
|
-
- [Tree](#tree)
|
227
|
-
- [Observer](#observer)
|
228
|
-
- [Observing Widgets](#observing-widgets)
|
229
|
-
- [Observing Models](#observing-models)
|
230
|
-
- [Custom Widgets](#custom-widgets)
|
231
|
-
- [Simple Example](#simple-example)
|
232
|
-
- [Lifecycle Hook Example](#lifecycle-hook-example)
|
233
|
-
- [Custom Widget API](#custom-widget-api)
|
234
|
-
- [Content/Options Example](#contentoptions-example)
|
235
|
-
- [Custom Widget Lifecycle Hooks](#custom-widget-lifecycle-hooks)
|
236
|
-
- [Gotcha](#gotcha)
|
237
|
-
- [Final Notes](#final-notes)
|
238
|
-
- [Custom Shells](#custom-shells)
|
239
|
-
- [Drag and Drop](#drag-and-drop)
|
240
|
-
- [Miscellaneous](#miscellaneous)
|
241
|
-
- [Multi-DSL Support](#multi-dsl-support)
|
242
|
-
- [Application Menu Items (About/Preferences)](#application-menu-items-aboutpreferences)
|
243
|
-
- [App Name and Version](#app-name-and-version)
|
244
|
-
- [Video Widget](#video-widget)
|
245
|
-
- [Browser Widget](#browser-widget)
|
246
|
-
- [Glimmer Configuration](#glimmer-configuration)
|
247
|
-
- [logger](#logger)
|
248
|
-
- [import_swt_packages](#importswtpackages)
|
249
|
-
- [loop_max_count](#loopmaxcount)
|
250
|
-
- [Glimmer Style Guide](#glimmer-style-guide)
|
251
|
-
- [SWT Reference](#swt-reference)
|
252
|
-
- [Samples](#samples)
|
253
|
-
- [Hello Samples](#hello-samples)
|
254
|
-
- [Hello, World! Sample](#hello-world-sample)
|
255
|
-
- [Hello, Tab!](#hello-tab)
|
256
|
-
- [Hello, Combo!](#hello-combo)
|
257
|
-
- [Hello, List Single Selection!](#hello-list-single-selection)
|
258
|
-
- [Hello, List Multi Selection!](#hello-list-multi-selection)
|
259
|
-
- [Hello, Computed!](#hello-computed)
|
260
|
-
- [Hello, Message Box!](#hello-message-box)
|
261
|
-
- [Hello, Browser!](#hello-browser)
|
262
|
-
- [Hello, Drag and Drop!](#hello-drag-and-drop)
|
263
|
-
- [Hello, Menu Bar!](#hello-menu-bar)
|
264
|
-
- [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu)
|
265
|
-
- [Elaborate Samples](#elaborate-samples)
|
266
|
-
- [Login](#login)
|
267
|
-
- [Tic Tac Toe Sample](#tic-tac-toe-sample)
|
268
|
-
- [Contact Manager](#contact-manager-sample)
|
269
|
-
- [External Samples](#external-samples)
|
270
|
-
- [Glimmer Calculator](#glimmer-calculator)
|
271
|
-
- [Gladiator](#gladiator)
|
272
|
-
- [In Production](#in-production)
|
273
|
-
- [Math Bowling](#math-bowling)
|
274
|
-
- [Are We There Yet?](#are-we-there-yet)
|
275
|
-
- [Packaging & Distribution](#packaging--distribution)
|
276
|
-
- [Packaging Defaults](#packaging-defaults)
|
277
|
-
- [Packaging Configuration](#packaging-configuration)
|
278
|
-
- [javapackager Extra Arguments](#javapackager-extra-arguments)
|
279
|
-
- [Mac Application Distribution](#mac-application-distribution)
|
280
|
-
- [Self Signed Certificate](#self-signed-certificate)
|
281
|
-
- [Gotchas](#gotchas)
|
282
|
-
- [Resources](#resources)
|
283
|
-
- [Help](#help)
|
284
|
-
- [Issues](#issues)
|
285
|
-
- [Chat](#chat)
|
286
|
-
- [Feature Suggestions](#feature-suggestions)
|
287
|
-
- [Change Log](#change-log)
|
288
|
-
- [Contributing](#contributing)
|
289
|
-
- [Contributors](#contributors)
|
290
|
-
- [Hire Me](#hire-me)
|
291
|
-
- [License](#license)
|
292
|
-
|
293
|
-
## Background
|
267
|
+
### Production Desktop Apps Built with Glimmer DSL for SWT
|
294
268
|
|
295
|
-
|
269
|
+
[<img alt="Are We There Yet Logo" src="https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-logo.svg" width="40" />Are We There Yet?](https://github.com/AndyObtiva/are-we-there-yet) - Small Project Tracking App
|
296
270
|
|
297
|
-
|
271
|
+
![Are We There Yet? App Screenshot](https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-screenshot-windows.png)
|
298
272
|
|
299
|
-
|
300
|
-
- Mac
|
301
|
-
- Windows
|
302
|
-
- Linux
|
273
|
+
[<img alt="Math Bowling Logo" src="https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/images/math-bowling-logo.png" width="40" />Math Bowling](https://github.com/AndyObtiva/MathBowling) - Elementary Level Math Game Featuring Bowling Rules
|
303
274
|
|
304
|
-
|
305
|
-
- Win32 on Windows
|
306
|
-
- Cocoa on Mac
|
307
|
-
- GTK on Linux
|
275
|
+
![Math Bowling App Screenshot](https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/Math-Bowling-Screenshot.png)
|
308
276
|
|
309
|
-
|
277
|
+
## Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
|
310
278
|
|
311
|
-
https://
|
312
|
-
https://www.eclipse.org/swt/faq.php
|
279
|
+
[Tcl/Tk](https://www.tcl.tk/) has evolved into a practical desktop GUI toolkit due to gaining truely native looking widgets on Mac, Windows, and Linux in [Tk version 8.5](https://www.tcl.tk/software/tcltk/8.5.html#:~:text=Highlights%20of%20Tk%208.5&text=Font%20rendering%3A%20Now%20uses%20anti,and%20window%20layout%2C%20and%20more.).
|
313
280
|
|
314
|
-
|
281
|
+
Additionally, [Ruby](https://www.ruby-lang.org/en/) 3.0 Ractor (formerly known as [Guilds](https://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/)) supports truly parallel multi-threading, making both [MRI](https://github.com/ruby/ruby) and [Tk](https://www.tcl.tk/) finally viable for support in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library) as an alternative to [JRuby on SWT](https://github.com/AndyObtiva/glimmer-dsl-swt).
|
315
282
|
|
316
|
-
- SWT
|
317
|
-
- JRuby 9.2.12.0 (supporting Ruby 2.5.x syntax) (find at [https://www.jruby.org/download](https://www.jruby.org/download))
|
318
|
-
- JDK 8 (find at [https://www.oracle.com/java/technologies/javase-downloads.html](https://www.oracle.com/java/technologies/javase-downloads.html))
|
319
|
-
- (Optional) RVM is needed for [Scaffolding](#scaffolding) only (find at [https://rvm.io/](https://rvm.io/))
|
283
|
+
The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a plethora of high quality reusable widgets for the Enterprise (such as [Nebula](https://www.eclipse.org/nebula/)), [Tk](https://www.tcl.tk/) enables very fast app startup time via [MRI Ruby](https://www.ruby-lang.org/en/).
|
320
284
|
|
321
|
-
|
285
|
+
[Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
|
286
|
+
- Declarative DSL syntax that visually maps to the GUI widget hierarchy
|
287
|
+
- Convention over configuration via smart defaults and automation of low-level details
|
288
|
+
- Requiring the least amount of syntax possible to build GUI
|
289
|
+
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
290
|
+
- Custom Widget support
|
291
|
+
- Scaffolding for new custom widgets, apps, and gems
|
292
|
+
- Native-Executable packaging on Mac, Windows, and Linux
|
322
293
|
|
323
|
-
|
324
|
-
rvm install jruby-9.2.12.0
|
325
|
-
```
|
326
|
-
|
327
|
-
Glimmer might still work on lower versions of Java, JRuby and SWT, but there are no guarantees, so it is best to stick to the pre-requisites outlined above.
|
294
|
+
### Glimmer DSL for Tk Samples
|
328
295
|
|
329
|
-
|
296
|
+
#### Hello, World!
|
330
297
|
|
331
|
-
|
298
|
+
Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
|
332
299
|
|
333
|
-
|
334
|
-
|
335
|
-
If you intend to build a Glimmer app from scratch on the Mac, pick Option 1 ([Direct Install](#option-1-direct-install)) to leverage [Glimmer Scaffolding](#scaffolding) (only available on the Mac).
|
300
|
+
```ruby
|
301
|
+
include Glimmer
|
336
302
|
|
337
|
-
|
303
|
+
root {
|
304
|
+
label {
|
305
|
+
text 'Hello, World!'
|
306
|
+
}
|
307
|
+
}.open
|
308
|
+
```
|
338
309
|
|
339
|
-
|
340
|
-
(Use for [Scaffolding](#scaffolding) on the Mac)
|
310
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
341
311
|
|
342
|
-
Run this command to install directly:
|
343
312
|
```
|
344
|
-
|
313
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
|
345
314
|
```
|
346
315
|
|
347
|
-
|
348
|
-
RVM allows running `gem` as an alias.
|
349
|
-
Otherwise, you may also run `jruby -S gem install ...`
|
316
|
+
Glimmer app:
|
350
317
|
|
351
|
-
|
318
|
+
![glimmer dsl tk screenshot sample hello world](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-world.png)
|
352
319
|
|
353
|
-
|
320
|
+
#### Hello, Tab!
|
354
321
|
|
355
|
-
|
322
|
+
Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
|
356
323
|
|
357
|
-
|
358
|
-
|
324
|
+
```ruby
|
325
|
+
include Glimmer
|
359
326
|
|
360
|
-
|
361
|
-
|
362
|
-
|
327
|
+
root {
|
328
|
+
title 'Hello, Tab!'
|
329
|
+
|
330
|
+
notebook {
|
331
|
+
frame(text: 'English') {
|
332
|
+
label {
|
333
|
+
text 'Hello, World!'
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
frame(text: 'French') {
|
338
|
+
label {
|
339
|
+
text 'Bonjour, Univers!'
|
340
|
+
}
|
341
|
+
}
|
342
|
+
}
|
343
|
+
}.open
|
363
344
|
```
|
364
345
|
|
365
|
-
|
346
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
347
|
+
|
366
348
|
```
|
367
|
-
|
349
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_tab.rb'"
|
368
350
|
```
|
369
351
|
|
370
|
-
|
352
|
+
Glimmer app:
|
371
353
|
|
372
|
-
|
354
|
+
![glimmer dsl tk screenshot sample hello tab English](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-english.png)
|
355
|
+
![glimmer dsl tk screenshot sample hello tab French](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-tab-french.png)
|
373
356
|
|
374
|
-
|
357
|
+
#### Hello, Combo!
|
375
358
|
|
376
|
-
|
359
|
+
Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combo.rb)):
|
377
360
|
|
378
|
-
|
361
|
+
```ruby
|
362
|
+
# ... more code precedes
|
363
|
+
root {
|
364
|
+
title 'Hello, Combo!'
|
365
|
+
|
366
|
+
combobox { |proxy|
|
367
|
+
state 'readonly'
|
368
|
+
text bind(person, :country)
|
369
|
+
}
|
370
|
+
|
371
|
+
button { |proxy|
|
372
|
+
text "Reset Selection"
|
373
|
+
command {
|
374
|
+
person.reset_country
|
375
|
+
}
|
376
|
+
}
|
377
|
+
}.open
|
378
|
+
# ... more code follows
|
379
|
+
```
|
379
380
|
|
380
|
-
|
381
|
+
Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
|
381
382
|
|
382
383
|
```
|
383
|
-
glimmer
|
384
|
+
ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combo.rb'"
|
384
385
|
```
|
385
386
|
|
386
|
-
|
387
|
-
the glimmer ruby gem and SWT jar dependency.
|
387
|
+
Glimmer app:
|
388
388
|
|
389
|
-
|
390
|
-
|
391
|
-
glimmer samples/hello/hello_world.rb
|
392
|
-
```
|
393
|
-
This runs the Glimmer "Hello, World!" sample.
|
389
|
+
![glimmer dsl tk screenshot sample hello combo](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combo.png)
|
390
|
+
![glimmer dsl tk screenshot sample hello combo dropdown](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-tk/master/images/glimmer-dsl-tk-screenshot-sample-hello-combo-dropdown.png)
|
394
391
|
|
395
|
-
|
392
|
+
## Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
|
396
393
|
|
397
|
-
|
398
|
-
```
|
399
|
-
bin/glimmer samples/hello/hello_world.rb
|
400
|
-
```
|
394
|
+
[Glimmer DSL for Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) is an experimental proof-of-concept web GUI adapter for [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps (i.e. apps built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)). It webifies them via [Rails](https://rubyonrails.org/), allowing Ruby desktop apps to run on the web via [Opal Ruby](https://opalrb.com/) without changing a line of code. Apps may then be custom-styled for the web with standard CSS.
|
401
395
|
|
402
|
-
|
396
|
+
Glimmer DSL for Opal webifier successfully reuses the entire [Glimmer](https://github.com/AndyObtiva/glimmer) core DSL engine in [Opal Ruby](https://opalrb.com/) inside a web browser, and as such inherits the full range of powerful Glimmer desktop [data-binding](https://github.com/AndyObtiva/glimmer#data-binding) capabilities for the web.
|
403
397
|
|
404
|
-
|
398
|
+
### Glimmer DSL for Opal Samples
|
405
399
|
|
406
|
-
|
407
|
-
Usage: glimmer [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
|
400
|
+
#### Hello, Computed!
|
408
401
|
|
409
|
-
|
402
|
+
Add the following require statement to `app/assets/javascripts/application.rb`
|
410
403
|
|
411
|
-
Either a single task or one or more applications may be specified.
|
412
404
|
|
413
|
-
|
405
|
+
```ruby
|
406
|
+
require 'samples/hello/hello_computed'
|
407
|
+
```
|
414
408
|
|
415
|
-
|
416
|
-
glimmer list:custom_shell_gems[query] # List Glimmer custom shell gems available at rubygems.org (query is optional)
|
417
|
-
glimmer list:custom_widget_gems[query] # List Glimmer custom widget gems available at rubygems.org (query is optional)
|
418
|
-
glimmer list:dsl_gems[query] # List Glimmer DSL gems available at rubygems.org (query is optional)
|
419
|
-
glimmer package # Package app for distribution (generating config, jar, and native files)
|
420
|
-
glimmer package:clean # Clean by removing "dist" and "packages" directories
|
421
|
-
glimmer package:config # Generate JAR config file
|
422
|
-
glimmer package:jar # Generate JAR file
|
423
|
-
glimmer package:native # Generate Native files (DMG/PKG/APP on the Mac)
|
424
|
-
glimmer scaffold[app_name] # Scaffold a Glimmer application directory structure to begin building a new app
|
425
|
-
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)
|
426
|
-
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)
|
427
|
-
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)
|
428
|
-
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)
|
409
|
+
Or add the Glimmer code directly if you prefer to play around with it:
|
429
410
|
|
430
|
-
|
431
|
-
|
411
|
+
```ruby
|
412
|
+
class HelloComputed
|
413
|
+
class Contact
|
414
|
+
attr_accessor :first_name, :last_name, :year_of_birth
|
415
|
+
|
416
|
+
def initialize(attribute_map)
|
417
|
+
@first_name = attribute_map[:first_name]
|
418
|
+
@last_name = attribute_map[:last_name]
|
419
|
+
@year_of_birth = attribute_map[:year_of_birth]
|
420
|
+
end
|
421
|
+
|
422
|
+
def name
|
423
|
+
"#{last_name}, #{first_name}"
|
424
|
+
end
|
425
|
+
|
426
|
+
def age
|
427
|
+
Time.now.year - year_of_birth.to_i
|
428
|
+
rescue
|
429
|
+
0
|
430
|
+
end
|
431
|
+
end
|
432
|
+
end
|
432
433
|
|
433
|
-
|
434
|
+
class HelloComputed
|
435
|
+
include Glimmer
|
434
436
|
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
437
|
+
def initialize
|
438
|
+
@contact = Contact.new(
|
439
|
+
first_name: 'Barry',
|
440
|
+
last_name: 'McKibbin',
|
441
|
+
year_of_birth: 1985
|
442
|
+
)
|
443
|
+
end
|
439
444
|
|
440
|
-
|
445
|
+
def launch
|
446
|
+
shell {
|
447
|
+
text 'Hello, Computed!'
|
448
|
+
composite {
|
449
|
+
grid_layout {
|
450
|
+
num_columns 2
|
451
|
+
make_columns_equal_width true
|
452
|
+
horizontal_spacing 20
|
453
|
+
vertical_spacing 10
|
454
|
+
}
|
455
|
+
label {text 'First &Name: '}
|
456
|
+
text {
|
457
|
+
text bind(@contact, :first_name)
|
458
|
+
layout_data {
|
459
|
+
horizontal_alignment :fill
|
460
|
+
grab_excess_horizontal_space true
|
461
|
+
}
|
462
|
+
}
|
463
|
+
label {text '&Last Name: '}
|
464
|
+
text {
|
465
|
+
text bind(@contact, :last_name)
|
466
|
+
layout_data {
|
467
|
+
horizontal_alignment :fill
|
468
|
+
grab_excess_horizontal_space true
|
469
|
+
}
|
470
|
+
}
|
471
|
+
label {text '&Year of Birth: '}
|
472
|
+
text {
|
473
|
+
text bind(@contact, :year_of_birth)
|
474
|
+
layout_data {
|
475
|
+
horizontal_alignment :fill
|
476
|
+
grab_excess_horizontal_space true
|
477
|
+
}
|
478
|
+
}
|
479
|
+
label {text 'Name: '}
|
480
|
+
label {
|
481
|
+
text bind(@contact, :name, computed_by: [:first_name, :last_name])
|
482
|
+
layout_data {
|
483
|
+
horizontal_alignment :fill
|
484
|
+
grab_excess_horizontal_space true
|
485
|
+
}
|
486
|
+
}
|
487
|
+
label {text 'Age: '}
|
488
|
+
label {
|
489
|
+
text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
|
490
|
+
layout_data {
|
491
|
+
horizontal_alignment :fill
|
492
|
+
grab_excess_horizontal_space true
|
493
|
+
}
|
494
|
+
}
|
495
|
+
}
|
496
|
+
}.open
|
497
|
+
end
|
498
|
+
end
|
441
499
|
|
442
|
-
|
500
|
+
HelloComputed.new.launch
|
443
501
|
```
|
502
|
+
Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
444
503
|
|
445
|
-
|
446
|
-
```
|
447
|
-
glimmer --debug samples/hello/hello_world.rb
|
448
|
-
```
|
504
|
+
![Glimmer DSL for SWT Hello Computed](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-computed.png)
|
449
505
|
|
450
|
-
|
506
|
+
Glimmer app on the web (using `glimmer-dsl-opal` gem):
|
451
507
|
|
452
|
-
|
508
|
+
Start the Rails server:
|
453
509
|
```
|
454
|
-
|
510
|
+
rails s
|
455
511
|
```
|
456
512
|
|
457
|
-
|
513
|
+
Visit `http://localhost:3000`
|
458
514
|
|
459
|
-
|
515
|
+
You should see "Hello, Computed!"
|
460
516
|
|
461
|
-
Glimmer
|
462
|
-
helps you get started just like true building scaffolding helps construction workers, civil engineers, and architects.
|
517
|
+
![Glimmer DSL for Opal Hello Computed](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-computed.png)
|
463
518
|
|
464
|
-
|
465
|
-
getting you to a running and delivered state of an advanced "Hello, World!" Glimmer application right off the bat.
|
519
|
+
#### Hello, List Single Selection!
|
466
520
|
|
467
|
-
|
468
|
-
letting Glimmer scaffolding take care of initial app file structure concerns, such as adding:
|
469
|
-
- Main application class that includes Glimmer
|
470
|
-
- Main application view that houses main window content, about dialog, and preferences dialog
|
471
|
-
- View and Model directories
|
472
|
-
- Rakefile including Glimmer tasks
|
473
|
-
- Version
|
474
|
-
- License
|
475
|
-
- Icon
|
476
|
-
- Bin file for starting application
|
521
|
+
Add the following require statement to `app/assets/javascripts/application.rb`
|
477
522
|
|
478
|
-
NOTE: Scaffolding requires RVM and currently supports Mac packaging only at the moment.
|
479
523
|
|
480
|
-
|
524
|
+
```ruby
|
525
|
+
require 'samples/hello/hello_list_single_selection'
|
526
|
+
```
|
481
527
|
|
482
|
-
|
528
|
+
Or add the Glimmer code directly if you prefer to play around with it:
|
483
529
|
|
484
|
-
|
530
|
+
```ruby
|
531
|
+
class Person
|
532
|
+
attr_accessor :country, :country_options
|
485
533
|
|
486
|
-
|
487
|
-
|
488
|
-
|
534
|
+
def initialize
|
535
|
+
self.country_options=["", "Canada", "US", "Mexico"]
|
536
|
+
self.country = "Canada"
|
537
|
+
end
|
489
538
|
|
490
|
-
|
539
|
+
def reset_country
|
540
|
+
self.country = "Canada"
|
541
|
+
end
|
542
|
+
end
|
491
543
|
|
492
|
-
|
544
|
+
class HelloListSingleSelection
|
545
|
+
include Glimmer
|
546
|
+
def launch
|
547
|
+
person = Person.new
|
548
|
+
shell {
|
549
|
+
composite {
|
550
|
+
list {
|
551
|
+
selection bind(person, :country)
|
552
|
+
}
|
553
|
+
button {
|
554
|
+
text "Reset"
|
555
|
+
on_widget_selected do
|
556
|
+
person.reset_country
|
557
|
+
end
|
558
|
+
}
|
559
|
+
}
|
560
|
+
}.open
|
561
|
+
end
|
562
|
+
end
|
493
563
|
|
564
|
+
HelloListSingleSelection.new.launch
|
494
565
|
```
|
495
|
-
|
496
|
-
|
566
|
+
Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
567
|
+
|
568
|
+
![Glimmer DSL for SWT Hello List Single Selection](https://github.com/AndyObtiva/glimmer/raw/master/images/glimmer-hello-list-single-selection.png)
|
497
569
|
|
498
|
-
|
570
|
+
Glimmer app on the web (using `glimmer-dsl-opal` gem):
|
499
571
|
|
572
|
+
Start the Rails server:
|
500
573
|
```
|
501
|
-
|
502
|
-
Created CarMaker/.ruby-gemset
|
503
|
-
Created CarMaker/VERSION
|
504
|
-
Created CarMaker/LICENSE.txt
|
505
|
-
Created CarMaker/Gemfile
|
506
|
-
Created CarMaker/Rakefile
|
507
|
-
Created CarMaker/app/car_maker.rb
|
508
|
-
Created CarMaker/app/views/car_maker/app_view.rb
|
509
|
-
Created CarMaker/package/macosx/Car Maker.icns
|
510
|
-
Created CarMaker/bin/car_maker
|
511
|
-
...
|
574
|
+
rails s
|
512
575
|
```
|
513
576
|
|
514
|
-
|
577
|
+
Visit `http://localhost:3000`
|
515
578
|
|
516
|
-
|
579
|
+
You should see "Hello, List Single Selection!"
|
517
580
|
|
518
|
-
|
581
|
+
![Glimmer DSL for Opal Hello List Single Selection](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-list-single-selection.png)
|
519
582
|
|
520
|
-
|
521
|
-
|
522
|
-
#### Custom Shell
|
583
|
+
#### Hello, List Multi Selection!
|
523
584
|
|
524
|
-
|
585
|
+
Add the following require statement to `app/assets/javascripts/application.rb`
|
525
586
|
|
526
|
-
```
|
527
|
-
|
587
|
+
```ruby
|
588
|
+
require 'samples/hello/hello_list_multi_selection'
|
528
589
|
```
|
529
590
|
|
530
|
-
|
591
|
+
Or add the Glimmer code directly if you prefer to play around with it:
|
531
592
|
|
532
|
-
|
533
|
-
|
534
|
-
|
535
|
-
glimmer scaffold:custom_widget[custom_widget_name]
|
536
|
-
```
|
593
|
+
```ruby
|
594
|
+
class Person
|
595
|
+
attr_accessor :provinces, :provinces_options
|
537
596
|
|
538
|
-
|
597
|
+
def initialize
|
598
|
+
self.provinces_options=[
|
599
|
+
"",
|
600
|
+
"Quebec",
|
601
|
+
"Ontario",
|
602
|
+
"Manitoba",
|
603
|
+
"Saskatchewan",
|
604
|
+
"Alberta",
|
605
|
+
"British Columbia",
|
606
|
+
"Nova Skotia",
|
607
|
+
"Newfoundland"
|
608
|
+
]
|
609
|
+
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
610
|
+
end
|
539
611
|
|
540
|
-
|
541
|
-
|
542
|
-
|
543
|
-
|
544
|
-
Of course, you can just build a Ruby gem and disregard native executable packaging if you do not need it.
|
612
|
+
def reset_provinces
|
613
|
+
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
614
|
+
end
|
615
|
+
end
|
545
616
|
|
546
|
-
|
617
|
+
class HelloListMultiSelection
|
618
|
+
include Glimmer
|
619
|
+
def launch
|
620
|
+
person = Person.new
|
621
|
+
shell {
|
622
|
+
composite {
|
623
|
+
list(:multi) {
|
624
|
+
selection bind(person, :provinces)
|
625
|
+
}
|
626
|
+
button {
|
627
|
+
text "Reset"
|
628
|
+
on_widget_selected do
|
629
|
+
person.reset_provinces
|
630
|
+
end
|
631
|
+
}
|
632
|
+
}
|
633
|
+
}.open
|
634
|
+
end
|
635
|
+
end
|
547
636
|
|
637
|
+
HelloListMultiSelection.new.launch
|
548
638
|
```
|
549
|
-
glimmer
|
550
|
-
```
|
551
|
-
|
552
|
-
It is important to specify a namespace to avoid having your gem clash with existing gems.
|
639
|
+
Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
|
553
640
|
|
554
|
-
|
641
|
+
![Glimmer DSL for SWT Hello List Multi Selection](https://github.com/AndyObtiva/glimmer/raw/master/images/glimmer-hello-list-multi-selection.png)
|
555
642
|
|
556
|
-
|
643
|
+
Glimmer app on the web (using `glimmer-dsl-opal` gem):
|
557
644
|
|
558
|
-
|
645
|
+
Start the Rails server:
|
646
|
+
```
|
647
|
+
rails s
|
648
|
+
```
|
559
649
|
|
560
|
-
|
561
|
-
- [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator): Glimmer Calculator
|
650
|
+
Visit `http://localhost:3000`
|
562
651
|
|
563
|
-
|
652
|
+
You should see "Hello, List Multi Selection!"
|
564
653
|
|
565
|
-
|
654
|
+
![Glimmer DSL for Opal Hello List Multi Selection](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-list-multi-selection.png)
|
566
655
|
|
567
|
-
|
568
|
-
glimmer scaffold:custom_widget_gem[custom_widget_name, namespace]
|
569
|
-
```
|
656
|
+
## Glimmer DSL for XML (& HTML)
|
570
657
|
|
571
|
-
|
658
|
+
[Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
|
572
659
|
|
573
|
-
|
660
|
+
Within the context of desktop development, Glimmer DSL for XML is useful in providing XML data for the [SWT Browser widget](https://github.com/AndyObtiva/glimmer/tree/master#browser-widget).
|
574
661
|
|
575
|
-
|
662
|
+
### XML DSL
|
576
663
|
|
577
|
-
|
664
|
+
Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
|
665
|
+
Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
|
578
666
|
|
579
|
-
-
|
580
|
-
-
|
667
|
+
Here are all the Glimmer XML DSL top-level keywords:
|
668
|
+
- `html`
|
669
|
+
- `tag`: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
|
670
|
+
- `name_space`: enables namespacing html tags
|
581
671
|
|
582
|
-
|
672
|
+
Element properties are typically passed as a key/value hash (e.g. `section(id: 'main', class: 'accordion')`) . However, for properties like "selected" or "checked", you must leave value `nil` or otherwise pass in front of the hash (e.g. `input(:checked, type: 'checkbox')` )
|
583
673
|
|
584
|
-
|
674
|
+
Example (basic HTML):
|
585
675
|
|
586
|
-
|
676
|
+
```ruby
|
677
|
+
@xml = html {
|
678
|
+
head {
|
679
|
+
meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
|
680
|
+
}
|
681
|
+
body {
|
682
|
+
h1 { "Hello, World!" }
|
683
|
+
}
|
684
|
+
}
|
685
|
+
puts @xml
|
686
|
+
```
|
587
687
|
|
588
|
-
|
688
|
+
Output:
|
589
689
|
|
590
690
|
```
|
591
|
-
|
691
|
+
<html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
|
592
692
|
```
|
593
693
|
|
594
|
-
|
595
|
-
|
596
|
-
```
|
597
|
-
glimmer list:custom_shell_gems
|
598
|
-
```
|
599
|
-
|
600
|
-
Output:
|
601
|
-
|
602
|
-
```
|
603
|
-
|
604
|
-
Glimmer Custom Shell Gems at rubygems.org:
|
605
|
-
|
606
|
-
Name Gem Version Author Description
|
607
|
-
|
608
|
-
Calculator glimmer-cs-calculator 1.0.1 Andy Maleh Calculator - Glimmer Custom Shell
|
609
|
-
Gladiator glimmer-cs-gladiator 0.2.0 Andy Maleh Gladiator (Glimmer Editor) - Glimmer Custom Shell
|
610
|
-
|
611
|
-
```
|
612
|
-
|
613
|
-
#### Listing Custom Widget Gems
|
614
|
-
|
615
|
-
The following command lists available Glimmer [Custom Widget Gems](#custom-widget-gem) (prefixed with "glimmer-cw-" by scaffolding convention) created by the the Glimmer community and published on [rubygems.org](http://www.rubygems.org):
|
616
|
-
|
617
|
-
```
|
618
|
-
glimmer list:custom_widget_gems[query] # List Glimmer custom widget gems available at rubygems.org (query is optional)
|
619
|
-
```
|
620
|
-
|
621
|
-
Example:
|
622
|
-
|
623
|
-
Check if there is a custom video widget for Glimmer.
|
624
|
-
|
625
|
-
```
|
626
|
-
glimmer list:custom_widget_gems[video]
|
627
|
-
```
|
628
|
-
|
629
|
-
Output:
|
630
|
-
|
631
|
-
```
|
632
|
-
|
633
|
-
Glimmer Custom Widget Gems matching [video] at rubygems.org:
|
634
|
-
|
635
|
-
Name Gem Version Author Description
|
636
|
-
|
637
|
-
Video glimmer-cw-video 0.1.1 Andy Maleh Glimmer Custom Widget - Video
|
638
|
-
|
639
|
-
```
|
640
|
-
|
641
|
-
#### Listing DSL Gems
|
642
|
-
|
643
|
-
The following command lists available Glimmer [DSL Gems](#multi-dsl-support) (prefixed with "glimmer-dsl-" by convention) created by the the Glimmer community and published on [rubygems.org](http://www.rubygems.org):
|
644
|
-
|
645
|
-
```
|
646
|
-
glimmer list:dsl_gems[query] # List Glimmer DSL gems available at rubygems.org (query is optional)
|
647
|
-
```
|
648
|
-
|
649
|
-
Example:
|
650
|
-
|
651
|
-
```
|
652
|
-
glimmer list:dsl_gems
|
653
|
-
```
|
654
|
-
|
655
|
-
Output:
|
656
|
-
|
657
|
-
```
|
658
|
-
|
659
|
-
Glimmer DSL Gems at rubygems.org:
|
660
|
-
|
661
|
-
Name Gem Version Author Description
|
662
|
-
|
663
|
-
Css glimmer-dsl-css 0.1.0 AndyMaleh Glimmer DSL for CSS
|
664
|
-
Opal glimmer-dsl-opal 0.0.9 AndyMaleh Glimmer DSL for Opal
|
665
|
-
Swt glimmer-dsl-swt 0.4.1 AndyMaleh Glimmer DSL for SWT
|
666
|
-
Xml glimmer-dsl-xml 0.1.0 AndyMaleh Glimmer DSL for XML
|
667
|
-
|
668
|
-
```
|
669
|
-
|
670
|
-
### Packaging
|
671
|
-
|
672
|
-
Glimmer packaging tasks are detailed under [Packaging & Distribution](#packaging--distribution).
|
673
|
-
|
674
|
-
### Raw JRuby Command
|
675
|
-
|
676
|
-
If there is a need to run Glimmer directly via the `jruby` command, you
|
677
|
-
may run the following:
|
678
|
-
|
679
|
-
```
|
680
|
-
jruby -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
|
681
|
-
```
|
682
|
-
|
683
|
-
The `-J-classpath` option specifies the `swt.jar` file path, which can be a
|
684
|
-
manually downloaded version of SWT, or otherwise the one included in the gem. You can lookup the one included in the gem by running `jgem which glimmer` to find the gem path and then look through the `vendor` directory.
|
685
|
-
|
686
|
-
The `-r` option preloads (requires) the `glimmer` library in Ruby.
|
687
|
-
|
688
|
-
The `-S` option specifies a script to run.
|
689
|
-
|
690
|
-
#### Mac Support
|
691
|
-
|
692
|
-
The Mac is well supported with the `glimmer` command. The advice below is not needed if you are using it.
|
693
|
-
|
694
|
-
However, if there is a reason to use the raw `jruby` command directly instead of the `glimmer` command, you need to pass an extra option (`-J-XstartOnFirstThread`) to JRuby on the Mac (Glimmer automatically passes it for you when using the `glimmer` command).
|
695
|
-
|
696
|
-
Example:
|
697
|
-
```
|
698
|
-
jruby -J-XstartOnFirstThread -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
|
699
|
-
```
|
700
|
-
|
701
|
-
## Girb (Glimmer irb) Command
|
702
|
-
|
703
|
-
With `glimmer-dsl-swt` installed, you may want to run `girb` instead of standard `irb` to have SWT preloaded and the Glimmer library required and included for quick Glimmer coding/testing.
|
704
|
-
|
705
|
-
```
|
706
|
-
girb
|
707
|
-
```
|
708
|
-
|
709
|
-
If you cloned [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) project locally, you may run `bin/girb` instead.
|
710
|
-
|
711
|
-
```
|
712
|
-
bin/girb
|
713
|
-
```
|
714
|
-
|
715
|
-
Watch out for hands-on examples in this README indicated by "you may copy/paste in [`girb`](#girb-glimmer-irb-command)"
|
716
|
-
|
717
|
-
Keep in mind that all samples live under [https://github.com/AndyObtiva/glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt)
|
718
|
-
|
719
|
-
## Glimmer DSL Syntax
|
720
|
-
|
721
|
-
Glimmer DSL syntax consists of static keywords and dynamic keywords to build and bind user-interface objects.
|
722
|
-
|
723
|
-
Static keywords are pre-identified keywords in the Glimmer DSL, such as `shell`, `message_box`, `async_exec`, and `bind`.
|
724
|
-
|
725
|
-
Dynamic keywords are dynamically figured out from available SWT widgets, custom widgets, and properties. Examples are: `label`, `combo`, and `list`.
|
726
|
-
|
727
|
-
The only reason to distinguish between the two types of Glimmer DSL keywords is to realize that importing new Glimmer [custom widgets](#custom-widgets) and Java SWT custom widget libraries automatically expands Glimmer's DSL vocabulary via new dynamic keywords.
|
728
|
-
|
729
|
-
For example, if a project adds this custom Java SWT library:
|
730
|
-
|
731
|
-
https://www.eclipse.org/nebula/widgets/cdatetime/cdatetime.php?page=operation
|
732
|
-
|
733
|
-
Glimmer will automatically support using the keyword `c_date_time`
|
734
|
-
|
735
|
-
You will learn more about widgets next.
|
736
|
-
|
737
|
-
### Widgets
|
738
|
-
|
739
|
-
Glimmer GUIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
|
740
|
-
|
741
|
-
https://www.eclipse.org/swt/widgets/
|
742
|
-
|
743
|
-
This screenshot taken from the link above should give a glimpse of how SWT widgets look and feel:
|
744
|
-
|
745
|
-
![SWT Widgets](images/glimmer-swt-widgets.png)
|
746
|
-
|
747
|
-
In Glimmer DSL, widgets are declared with lowercase underscored names mirroring their SWT names minus the package name:
|
748
|
-
|
749
|
-
- `shell` instantiates `org.eclipse.swt.widgets.Shell`
|
750
|
-
- `text` instantiates `org.eclipse.swt.widgets.Text`
|
751
|
-
- `button` instantiates `org.eclipse.swt.widgets.Button`
|
752
|
-
- `label` instantiates `org.eclipse.swt.widgets.Label`
|
753
|
-
- `composite` instantiates `org.eclipse.swt.widgets.Composite`
|
754
|
-
- `tab_folder` instantiates `org.eclipse.swt.widgets.TabFolder`
|
755
|
-
- `tab_item` instantiates `org.eclipse.swt.widgets.TabItem`
|
756
|
-
- `table` instantiates `org.eclipse.swt.widgets.Table`
|
757
|
-
- `table_column` instantiates `org.eclipse.swt.widgets.TableColumn`
|
758
|
-
- `tree` instantiates `org.eclipse.swt.widgets.Tree`
|
759
|
-
- `combo` instantiates `org.eclipse.swt.widgets.Combo`
|
760
|
-
- `list` instantiates `org.eclipse.swt.widgets.List`
|
761
|
-
|
762
|
-
Every **widget** is sufficiently declared by name, but may optionally be accompanied with:
|
763
|
-
- SWT **style** ***argument*** wrapped by parenthesis according to [Glimmer Style Guide](#glimmer-style-guide) (see [next section](#widget-styles) for details).
|
764
|
-
- Ruby block containing **properties** (widget attributes) and **content** (nested widgets)
|
765
|
-
|
766
|
-
For example, if we were to revisit `samples/hello/hello_world.rb` above (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
767
|
-
|
768
|
-
```ruby
|
769
|
-
shell {
|
770
|
-
text "Glimmer"
|
771
|
-
label {
|
772
|
-
text "Hello, World!"
|
773
|
-
}
|
774
|
-
}.open
|
775
|
-
```
|
776
|
-
|
777
|
-
Note that `shell` instantiates the outer shell **widget**, in other words, the window that houses all of the desktop graphical user interface.
|
778
|
-
|
779
|
-
`shell` is then followed by a ***block*** that contains
|
780
|
-
|
781
|
-
```ruby
|
782
|
-
# ...
|
783
|
-
text "Glimmer" # text property of shell
|
784
|
-
label { # label widget declaration as content of shell
|
785
|
-
text "Hello, World!" # text property of label
|
786
|
-
}
|
787
|
-
# ...
|
788
|
-
```
|
789
|
-
|
790
|
-
The first line declares a **property** called `text`, which sets the title of the shell (window) to `"Glimmer"`. **Properties** always have ***arguments*** (not wrapped by parenthesis according to [Glimmer Style Guide](#glimmer-style-guide)), such as the text `"Glimmer"` in this case, and do **NOT** have a ***block*** (this distinguishes them from **widget** declarations).
|
791
|
-
|
792
|
-
The second line declares the `label` **widget**, which is followed by a Ruby **content** ***block*** that contains its `text` **property** with value `"Hello, World!"`
|
793
|
-
|
794
|
-
The **widget** ***block*** may optionally receive an argument representing the widget proxy object that the block content is for. This is useful in rare cases when the content code needs to refer to parent widget during declaration. You may leave that argument out most of the time and only add when absolutely needed.
|
795
|
-
|
796
|
-
Example:
|
797
|
-
|
798
|
-
```ruby
|
799
|
-
shell {|shell_proxy|
|
800
|
-
#...
|
801
|
-
}
|
802
|
-
```
|
803
|
-
|
804
|
-
Remember that The `shell` widget is always the outermost widget containing all others in a Glimmer desktop windowed application.
|
805
|
-
|
806
|
-
After it is declared, a `shell` must be opened with the `#open` method, which can be called on the block directly as in the example above, or by capturing `shell` in a `@shell` variable (shown in example below), and calling `#open` on it independently (recommended in actual apps)
|
807
|
-
|
808
|
-
```ruby
|
809
|
-
@shell = shell {
|
810
|
-
# properties and content
|
811
|
-
# ...
|
812
|
-
}
|
813
|
-
@shell.open
|
814
|
-
```
|
815
|
-
|
816
|
-
It is centered upon initial display and has a minimum width of 130 (can be re-centered when needed with `@shell.center` method after capturing `shell` in a `@shell` variable as per samples)
|
817
|
-
|
818
|
-
Check out the [samples](samples) directory for more examples.
|
819
|
-
|
820
|
-
Example from [hello_tab.rb](samples/hello/hello_tab.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
821
|
-
|
822
|
-
![Hello Tab English](images/glimmer-hello-tab-english.png)
|
823
|
-
|
824
|
-
![Hello Tab French](images/glimmer-hello-tab-french.png)
|
825
|
-
|
826
|
-
```ruby
|
827
|
-
shell {
|
828
|
-
text "Hello, Tab!"
|
829
|
-
tab_folder {
|
830
|
-
tab_item {
|
831
|
-
text "English"
|
832
|
-
label {
|
833
|
-
text "Hello, World!"
|
834
|
-
}
|
835
|
-
}
|
836
|
-
tab_item {
|
837
|
-
text "French"
|
838
|
-
label {
|
839
|
-
text "Bonjour Univers!"
|
840
|
-
}
|
841
|
-
}
|
842
|
-
}
|
843
|
-
}.open
|
844
|
-
```
|
845
|
-
|
846
|
-
#### Display
|
847
|
-
|
848
|
-
SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage GUI globally
|
849
|
-
and access available monitors.
|
850
|
-
It is automatically instantiated upon first instantiation of a `shell` widget.
|
851
|
-
Alternatively, for advanced use cases, it can be created explicitly with Glimmer `display` keyword. When a `shell` is later declared, it
|
852
|
-
automatically uses the display created earlier without having to explicitly hook it.
|
853
|
-
|
854
|
-
```ruby
|
855
|
-
@display = display {
|
856
|
-
cursor_location 300, 300
|
857
|
-
on_swt_keydown {
|
858
|
-
# ...
|
859
|
-
}
|
860
|
-
# ...
|
861
|
-
}
|
862
|
-
@shell = shell { # uses display created above
|
863
|
-
}
|
864
|
-
```
|
865
|
-
The benefit of instantiating an SWT Display explicitly is to set [Properties](#widget-properties) or [Observers](#observer).
|
866
|
-
Although SWT Display is not technically a widget, it has similar APIs in SWT and similar DSL support in Glimmer.
|
867
|
-
|
868
|
-
#### SWT Proxies
|
869
|
-
|
870
|
-
Glimmer follows Proxy Design Pattern by having Ruby proxy wrappers for all SWT objects:
|
871
|
-
- `Glimmer::SWT:WidgetProxy` wraps all descendants of `org.eclipse.swt.widgets.Widget` except the ones that have their own wrappers.
|
872
|
-
- `Glimmer::SWT::ShellProxy` wraps `org.eclipse.swt.widgets.Shell`
|
873
|
-
- `Glimmer::SWT:TabItemProxy` wraps `org.eclipse.swt.widget.TabItem` (also adds a composite to enable adding content under tab items directly in Glimmer)
|
874
|
-
- `Glimmer::SWT:LayoutProxy` wraps all descendants of `org.eclipse.swt.widget.Layout`
|
875
|
-
- `Glimmer::SWT:LayoutDataProxy` wraps all layout data objects
|
876
|
-
- `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying GUI)
|
877
|
-
- `Glimmer::SWT:ColorProxy` wraps `org.eclipse.swt.graphics.Color`
|
878
|
-
- `Glimmer::SWT:FontProxy` wraps `org.eclipse.swt.graphics.Font`
|
879
|
-
- `Glimmer::SWT::WidgetListenerProxy` wraps all widget listeners
|
880
|
-
|
881
|
-
These proxy objects have an API and provide some convenience methods, some of which are mentioned below.
|
882
|
-
|
883
|
-
##### `#content { ... }`
|
884
|
-
|
885
|
-
Glimmer allows re-opening any widget and adding properties or extra content after it has been constructed already by using the `#content` method.
|
886
|
-
|
887
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
888
|
-
|
889
|
-
```ruby
|
890
|
-
@shell = shell {
|
891
|
-
text "Application"
|
892
|
-
row_layout
|
893
|
-
@label1 = label {
|
894
|
-
text "Hello,"
|
895
|
-
}
|
896
|
-
}
|
897
|
-
@shell.content {
|
898
|
-
minimum_size 130, 130
|
899
|
-
label {
|
900
|
-
text "World!"
|
901
|
-
}
|
902
|
-
}
|
903
|
-
@label1.content {
|
904
|
-
foreground :red
|
905
|
-
}
|
906
|
-
@shell.open
|
907
|
-
```
|
908
|
-
|
909
|
-
##### `message_box`
|
910
|
-
|
911
|
-
The Glimmer DSL `message_box` keyword is similar to `shell`, but renders a modal dialog with a title `text` property and main body `message` property. It may also be opened via the `#open` method.
|
912
|
-
|
913
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
914
|
-
|
915
|
-
```ruby
|
916
|
-
include Glimmer
|
917
|
-
|
918
|
-
@shell = shell {
|
919
|
-
text 'Hello, Message Box!'
|
920
|
-
button {
|
921
|
-
text 'Please Click To Win a Surprise'
|
922
|
-
on_widget_selected {
|
923
|
-
message_box(@shell) {
|
924
|
-
text 'Surprise'
|
925
|
-
message "Congratulations!\n\nYou have won $1,000,000!"
|
926
|
-
}.open
|
927
|
-
}
|
928
|
-
}
|
929
|
-
}
|
930
|
-
@shell.open
|
931
|
-
```
|
932
|
-
|
933
|
-
##### `#swt_widget`
|
934
|
-
|
935
|
-
Glimmer widget objects come with an instance method `#swt_widget` that returns the actual SWT `Widget` object wrapped by the Glimmer widget object. It is useful in cases you'd like to do some custom SWT programming outside of Glimmer.
|
936
|
-
|
937
|
-
##### Shell widget proxy methods
|
938
|
-
|
939
|
-
Shell widget proxy has extra methods specific to SWT Shell:
|
940
|
-
- `#open`: Opens the shell, making it visible and active, and starting the SWT Event Loop (you may learn more about it here: https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html). If shell was already open, but hidden, it makes the shell visible.
|
941
|
-
- `#show`: Alias for `#open`
|
942
|
-
- `#hide`: Hides a shell setting "visible" property to false
|
943
|
-
- `#close`: Closes the shell
|
944
|
-
- `#center`: Centers the shell within monitor it is in
|
945
|
-
- `#start_event_loop`: (happens as part of `#open`) Starts SWT Event Loop (you may learn more about it here: https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/Display.html). This method is not needed except in rare circumstances where there is a need to start the SWT Event Loop before opening the shell.
|
946
|
-
- `#visible?`: Returns whether a shell is visible
|
947
|
-
- `#opened_before?`: Returns whether a shell has been opened at least once before (additionally implying the SWT Event Loop has been started already)
|
948
|
-
- `#visible=`: Setting to true opens/shows shell. Setting to false hides the shell.
|
949
|
-
- `#pack`: Packs contained widgets using SWT's `Shell#pack` method
|
950
|
-
- `#pack_same_size`: Packs contained widgets without changing shell's size when widget sizes change
|
951
|
-
|
952
|
-
#### Dialog
|
953
|
-
|
954
|
-
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.
|
955
|
-
|
956
|
-
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.
|
957
|
-
|
958
|
-
#### Menus
|
959
|
-
|
960
|
-
Glimmer DSL provides support for SWT Menu and MenuItem widgets.
|
961
|
-
|
962
|
-
There are 2 main types of menus in SWT:
|
963
|
-
- Menu Bar (shows up on top)
|
964
|
-
- Pop Up Context Menu (shows up when right-clicking a widget)
|
965
|
-
|
966
|
-
Underneath both types, there can be a 3rd menu type called Drop Down.
|
967
|
-
|
968
|
-
Glimmer provides special support for Drop Down menus as it automatically instantiates associated Cascade menu items and wires together with proper parenting, swt styles, and calling setMenu.
|
969
|
-
|
970
|
-
The ampersand symbol indicates the keyboard shortcut key for the menu item (e.g. '&Help' can be triggered on Windows by hitting ALT+H)
|
971
|
-
|
972
|
-
Example of a Menu Bar (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
973
|
-
|
974
|
-
```ruby
|
975
|
-
shell { |shell_proxy|
|
976
|
-
text 'Hello, Menu Bar!'
|
977
|
-
grid_layout
|
978
|
-
label(:center) {
|
979
|
-
font height: 16
|
980
|
-
text 'Check Out The File Menu and History Menu in The Menu Bar Above!'
|
981
|
-
}
|
982
|
-
menu_bar {
|
983
|
-
menu {
|
984
|
-
text '&File'
|
985
|
-
menu_item {
|
986
|
-
text 'E&xit'
|
987
|
-
}
|
988
|
-
menu_item(0) {
|
989
|
-
text '&New'
|
990
|
-
on_widget_selected {
|
991
|
-
message_box(shell_proxy) {
|
992
|
-
text 'New File'
|
993
|
-
message 'New File Contents'
|
994
|
-
}.open
|
995
|
-
}
|
996
|
-
}
|
997
|
-
menu(1) {
|
998
|
-
text '&Options'
|
999
|
-
menu_item(:radio) {
|
1000
|
-
text 'Option 1'
|
1001
|
-
}
|
1002
|
-
menu_item(:separator)
|
1003
|
-
menu_item(:check) {
|
1004
|
-
text 'Option 3'
|
1005
|
-
}
|
1006
|
-
}
|
1007
|
-
}
|
1008
|
-
menu {
|
1009
|
-
text '&History'
|
1010
|
-
menu {
|
1011
|
-
text '&Recent'
|
1012
|
-
menu_item {
|
1013
|
-
text 'File 1'
|
1014
|
-
on_widget_selected {
|
1015
|
-
message_box(shell_proxy) {
|
1016
|
-
text 'File 1'
|
1017
|
-
message 'File 1 Contents'
|
1018
|
-
}.open
|
1019
|
-
}
|
1020
|
-
}
|
1021
|
-
menu_item {
|
1022
|
-
text 'File 2'
|
1023
|
-
on_widget_selected {
|
1024
|
-
message_box(shell_proxy) {
|
1025
|
-
text 'File 2'
|
1026
|
-
message 'File 2 Contents'
|
1027
|
-
}.open
|
1028
|
-
}
|
1029
|
-
}
|
1030
|
-
}
|
1031
|
-
}
|
1032
|
-
}
|
1033
|
-
}.open
|
1034
|
-
```
|
1035
|
-
|
1036
|
-
Example of a Pop Up Context Menu (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1037
|
-
|
1038
|
-
```ruby
|
1039
|
-
shell { |shell_proxy|
|
1040
|
-
text 'Hello, Pop Up Context Menu!'
|
1041
|
-
grid_layout
|
1042
|
-
label {
|
1043
|
-
font height: 16
|
1044
|
-
text 'Right-Click To Pop Up a Context Menu'
|
1045
|
-
menu {
|
1046
|
-
menu {
|
1047
|
-
text '&History'
|
1048
|
-
menu {
|
1049
|
-
text '&Recent'
|
1050
|
-
menu_item {
|
1051
|
-
text 'File 1'
|
1052
|
-
on_widget_selected {
|
1053
|
-
message_box(shell_proxy) {
|
1054
|
-
text 'File 1'
|
1055
|
-
message 'File 1 Contents'
|
1056
|
-
}.open
|
1057
|
-
}
|
1058
|
-
}
|
1059
|
-
menu_item {
|
1060
|
-
text 'File 2'
|
1061
|
-
on_widget_selected {
|
1062
|
-
message_box(shell_proxy) {
|
1063
|
-
text 'File 2'
|
1064
|
-
message 'File 2 Contents'
|
1065
|
-
}.open
|
1066
|
-
}
|
1067
|
-
}
|
1068
|
-
}
|
1069
|
-
}
|
1070
|
-
}
|
1071
|
-
}
|
1072
|
-
}.open
|
1073
|
-
```
|
1074
|
-
|
1075
|
-
#### ScrolledComposite
|
1076
|
-
|
1077
|
-
Glimmer provides smart defaults for the `scrolled_composite` widget by:
|
1078
|
-
- Automatically setting the nested widget as its content (meaning use can just like a plain old `composite` to add scrolling)
|
1079
|
-
- Automatically setting the :h_scroll and :v_scroll SWT styles (can be set manually if only one of either :h_scroll or :v_scroll is desired )
|
1080
|
-
- Automatically setting the expand horizontal and expand vertical SWT properties to `true`
|
1081
|
-
|
1082
|
-
### Widget Styles
|
1083
|
-
|
1084
|
-
SWT widgets receive `SWT` styles in their constructor as per this guide:
|
1085
|
-
|
1086
|
-
https://wiki.eclipse.org/SWT_Widget_Style_Bits
|
1087
|
-
|
1088
|
-
Glimmer DSL facilitates that by passing symbols representing `SWT` constants as widget method arguments (i.e. inside widget `()` parentheses according to [Glimmer Style Guide](#glimmer-style-guide). See example below) in lower case version (e.g. `SWT::MULTI` becomes `:multi`).
|
1089
|
-
|
1090
|
-
These styles customize widget look, feel, and behavior.
|
1091
|
-
|
1092
|
-
Example:
|
1093
|
-
|
1094
|
-
```ruby
|
1095
|
-
# ...
|
1096
|
-
list(:multi) { # SWT styles go inside ()
|
1097
|
-
# ...
|
1098
|
-
}
|
1099
|
-
# ...
|
1100
|
-
```
|
1101
|
-
Passing `:multi` to `list` widget enables list element multi-selection.
|
1102
|
-
|
1103
|
-
```ruby
|
1104
|
-
# ...
|
1105
|
-
composite(:border) { # SWT styles go inside ()
|
1106
|
-
# ...
|
1107
|
-
}
|
1108
|
-
# ...
|
1109
|
-
```
|
1110
|
-
Passing `:border` to `composite` widget ensures it has a border.
|
1111
|
-
|
1112
|
-
When you need to pass in **multiple SWT styles**, simply separate by commas.
|
1113
|
-
|
1114
|
-
Example:
|
1115
|
-
|
1116
|
-
```ruby
|
1117
|
-
# ...
|
1118
|
-
text(:center, :border) { # Multiple SWT styles separated by comma
|
1119
|
-
# ...
|
1120
|
-
}
|
1121
|
-
# ...
|
1122
|
-
```
|
1123
|
-
|
1124
|
-
Glimmer ships with SWT style **smart defaults** so you wouldn't have to set them yourself most of the time (albeit you can always override them):
|
1125
|
-
|
1126
|
-
- `text(:border)`
|
1127
|
-
- `table(:border)`
|
1128
|
-
- `tree(:border, :virtual, :v_scroll, :h_scroll)`
|
1129
|
-
- `spinner(:border)`
|
1130
|
-
- `list(:border, :v_scroll)`
|
1131
|
-
- `button(:push)`
|
1132
|
-
|
1133
|
-
You may check out all available `SWT` styles here:
|
1134
|
-
|
1135
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
|
1136
|
-
|
1137
|
-
#### Explicit SWT Style Bit
|
1138
|
-
|
1139
|
-
When building a widget-related SWT object manually (e.g. `GridData.new(...)`), you are expected to use `SWT::CONSTANT` directly or BIT-OR a few SWT constants together like `SWT::BORDER | SWT::V_SCROLL`.
|
1140
|
-
|
1141
|
-
Glimmer facilitates that with `swt` keyword by allowing you to pass multiple styles as an argument array of symbols instead of dealing with BIT-OR.
|
1142
|
-
Example:
|
1143
|
-
|
1144
|
-
```ruby
|
1145
|
-
style = swt(:border, :v_scroll)
|
1146
|
-
```
|
1147
|
-
|
1148
|
-
#### Negative SWT Style Bits
|
1149
|
-
|
1150
|
-
In rare occasions, you might need to apply & with a negative (not) style bit to negate it from another style bit that includes it.
|
1151
|
-
Glimmer facilitates that by declaring the negative style bit via postfixing a symbol with `!`.
|
1152
|
-
|
1153
|
-
Example:
|
1154
|
-
|
1155
|
-
```ruby
|
1156
|
-
style = swt(:shell_trim, :max!) # creates a shell trim style without the maximize button (negated)
|
1157
|
-
```
|
1158
|
-
|
1159
|
-
#### Extra SWT Styles
|
1160
|
-
|
1161
|
-
##### Non-resizable Window
|
1162
|
-
|
1163
|
-
SWT Shell widget by default is resizable. To make it non-resizable, one must pass a complicated style bit concoction like `swt(:shell_trim, :resize!, :max!)`.
|
1164
|
-
|
1165
|
-
Glimmer makes this easier by alternatively offering a `:no_resize` extra SWT style, added for convenience.
|
1166
|
-
This makes declaring a non-resizable window as easy as:
|
1167
|
-
|
1168
|
-
```ruby
|
1169
|
-
shell(:no_resize) {
|
1170
|
-
# ...
|
1171
|
-
}
|
1172
|
-
```
|
1173
|
-
|
1174
|
-
### Widget Properties
|
1175
|
-
|
1176
|
-
Widget properties such as text value, enablement, visibility, and layout details are set within the widget block using methods matching SWT widget property names in lower snakecase. You may refer to SWT widget guide for details on available widget properties:
|
1177
|
-
|
1178
|
-
https://help.eclipse.org/2019-12/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_controls.htm?cp=2_0_7_0_0
|
1179
|
-
|
1180
|
-
|
1181
|
-
Code examples:
|
1182
|
-
|
1183
|
-
```ruby
|
1184
|
-
# ...
|
1185
|
-
label {
|
1186
|
-
text "Hello, World!" # SWT properties go inside {} block
|
1187
|
-
}
|
1188
|
-
# ...
|
1189
|
-
```
|
1190
|
-
|
1191
|
-
In the above example, the `label` widget `text` property was set to "Hello, World!".
|
1192
|
-
|
1193
|
-
```ruby
|
1194
|
-
# ...
|
1195
|
-
button {
|
1196
|
-
enabled bind(@tic_tac_toe_board.box(row, column), :empty)
|
1197
|
-
}
|
1198
|
-
# ...
|
1199
|
-
```
|
1200
|
-
|
1201
|
-
In the above example, the `text` widget `enabled` property was data-bound to `#empty` method on `@tic_tac_toe_board.box(row, column)` (learn more about data-binding below)
|
1202
|
-
|
1203
|
-
#### Colors
|
1204
|
-
|
1205
|
-
Colors make up a subset of widget properties. SWT accepts color objects created with RGB (Red Green Blue) or RGBA (Red Green Blue Alpha). Glimmer supports constructing color objects using the `rgb` and `rgba` DSL keywords.
|
1206
|
-
|
1207
|
-
Example:
|
1208
|
-
|
1209
|
-
```ruby
|
1210
|
-
# ...
|
1211
|
-
label {
|
1212
|
-
background rgb(144, 240, 244)
|
1213
|
-
foreground rgba(38, 92, 232, 255)
|
1214
|
-
}
|
1215
|
-
# ...
|
1216
|
-
```
|
1217
|
-
|
1218
|
-
SWT also supports standard colors available as constants under the `SWT` namespace with the `COLOR_` prefix (e.g. `SWT::COLOR_BLUE`)
|
1219
|
-
|
1220
|
-
Glimmer supports constructing colors for these constants as lowercase Ruby symbols (with or without `color_` prefix) passed to `color` DSL keyword
|
1221
|
-
|
1222
|
-
Example:
|
1223
|
-
|
1224
|
-
```ruby
|
1225
|
-
# ...
|
1226
|
-
label {
|
1227
|
-
background color(:black)
|
1228
|
-
foreground color(:yellow)
|
1229
|
-
}
|
1230
|
-
label {
|
1231
|
-
background color(:color_white)
|
1232
|
-
foreground color(:color_red)
|
1233
|
-
}
|
1234
|
-
# ...
|
1235
|
-
```
|
1236
|
-
|
1237
|
-
You may check out all available standard colors in `SWT` over here (having `COLOR_` prefix):
|
1238
|
-
|
1239
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
|
1240
|
-
|
1241
|
-
|
1242
|
-
##### `#swt_color`
|
1243
|
-
|
1244
|
-
Glimmer color objects come with an instance method `#swt_color` that returns the actual SWT `Color` object wrapped by the Glimmer color object. It is useful in cases you'd like to do some custom SWT programming outside of Glimmer.
|
1245
|
-
|
1246
|
-
Example:
|
1247
|
-
|
1248
|
-
```ruby
|
1249
|
-
color(:black).swt_color # returns SWT Color object
|
1250
|
-
```
|
1251
|
-
|
1252
|
-
#### Fonts
|
1253
|
-
|
1254
|
-
Fonts are represented in Glimmer as a hash of name, height, and style keys.
|
1255
|
-
|
1256
|
-
The style can be one (or more) of 3 values: `:normal`, `:bold`, and `:italic`
|
1257
|
-
|
1258
|
-
Example:
|
1259
|
-
|
1260
|
-
```ruby
|
1261
|
-
# ...
|
1262
|
-
label {
|
1263
|
-
font name: 'Arial', height: 36, style: :normal
|
1264
|
-
}
|
1265
|
-
# ...
|
1266
|
-
```
|
1267
|
-
|
1268
|
-
Keys are optional, so some of them may be left off.
|
1269
|
-
When passing multiple styles, they are included in an array.
|
1270
|
-
|
1271
|
-
Example:
|
1272
|
-
|
1273
|
-
```ruby
|
1274
|
-
# ...
|
1275
|
-
label {
|
1276
|
-
font style: [:bold, :italic]
|
1277
|
-
}
|
1278
|
-
# ...
|
1279
|
-
```
|
1280
|
-
|
1281
|
-
### Layouts
|
1282
|
-
|
1283
|
-
Glimmer lays widgets out visually using SWT layouts, which can only be set on composite widget and subclasses.
|
1284
|
-
|
1285
|
-
The most common SWT layouts are:
|
1286
|
-
- `FillLayout`: lays widgets out in equal proportion horizontally or vertically with spacing/margin options. This is the ***default*** layout for ***shell*** (with `:horizontal` option) in Glimmer.
|
1287
|
-
- `RowLayout`: lays widgets out horizontally or vertically in varying proportions with advanced spacing/margin/justify options
|
1288
|
-
- `GridLayout`: lays widgets out in a grid with advanced spacing/margin/alignment/indentation options. This is the **default** layout for **composite** in Glimmer. It is important to master.
|
1289
|
-
|
1290
|
-
In Glimmer DSL, just like widgets, layouts can be specified with lowercase underscored names followed by a block containing properties, also lowercase underscored names (e.g. `RowLayout` is `row_layout`).
|
1291
|
-
|
1292
|
-
Example:
|
1293
|
-
|
1294
|
-
```ruby
|
1295
|
-
# ...
|
1296
|
-
composite {
|
1297
|
-
row_layout {
|
1298
|
-
wrap true
|
1299
|
-
pack false
|
1300
|
-
justify true
|
1301
|
-
type :vertical
|
1302
|
-
margin_left 1
|
1303
|
-
margin_top 2
|
1304
|
-
margin_right 3
|
1305
|
-
margin_bottom 4
|
1306
|
-
spacing 5
|
1307
|
-
}
|
1308
|
-
# ... widgets follow
|
1309
|
-
}
|
1310
|
-
# ...
|
1311
|
-
```
|
1312
|
-
|
1313
|
-
If you data-bind any layout properties, when they change, the shell containing their widget re-packs its children (calls `#pack` method automatically) to ensure proper relayout of all widgets.
|
1314
|
-
|
1315
|
-
Alternatively, a layout may be constructed by following the SWT API for the layout object. For example, a `RowLayout` can be constructed by passing it an SWT style constant (Glimmer automatically accepts symbols (e.g. `:horizontal`) for SWT style arguments like `SWT::HORIZONTAL`.)
|
1316
|
-
|
1317
|
-
```ruby
|
1318
|
-
# ...
|
1319
|
-
composite {
|
1320
|
-
row_layout :horizontal
|
1321
|
-
# ... widgets follow
|
1322
|
-
}
|
1323
|
-
# ...
|
1324
|
-
```
|
1325
|
-
|
1326
|
-
Here is a more sophisticated example taken from [hello_computed.rb](samples/hello/hello_computed.rb) sample:
|
1327
|
-
|
1328
|
-
![Hello Computed](images/glimmer-hello-computed.png)
|
1329
|
-
|
1330
|
-
```ruby
|
1331
|
-
shell {
|
1332
|
-
text 'Hello, Computed!'
|
1333
|
-
composite {
|
1334
|
-
grid_layout {
|
1335
|
-
num_columns 2
|
1336
|
-
make_columns_equal_width true
|
1337
|
-
horizontal_spacing 20
|
1338
|
-
vertical_spacing 10
|
1339
|
-
}
|
1340
|
-
label {text 'First &Name: '}
|
1341
|
-
text {
|
1342
|
-
text bind(@contact, :first_name)
|
1343
|
-
layout_data {
|
1344
|
-
horizontal_alignment :fill
|
1345
|
-
grab_excess_horizontal_space true
|
1346
|
-
}
|
1347
|
-
}
|
1348
|
-
label {text '&Last Name: '}
|
1349
|
-
text {
|
1350
|
-
text bind(@contact, :last_name)
|
1351
|
-
layout_data {
|
1352
|
-
horizontal_alignment :fill
|
1353
|
-
grab_excess_horizontal_space true
|
1354
|
-
}
|
1355
|
-
}
|
1356
|
-
label {text '&Year of Birth: '}
|
1357
|
-
text {
|
1358
|
-
text bind(@contact, :year_of_birth)
|
1359
|
-
layout_data {
|
1360
|
-
horizontal_alignment :fill
|
1361
|
-
grab_excess_horizontal_space true
|
1362
|
-
}
|
1363
|
-
}
|
1364
|
-
label {text 'Name: '}
|
1365
|
-
label {
|
1366
|
-
text bind(@contact, :name, computed_by: [:first_name, :last_name])
|
1367
|
-
layout_data {
|
1368
|
-
horizontal_alignment :fill
|
1369
|
-
grab_excess_horizontal_space true
|
1370
|
-
}
|
1371
|
-
}
|
1372
|
-
label {text 'Age: '}
|
1373
|
-
label {
|
1374
|
-
text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
|
1375
|
-
layout_data {
|
1376
|
-
horizontal_alignment :fill
|
1377
|
-
grab_excess_horizontal_space true
|
1378
|
-
}
|
1379
|
-
}
|
1380
|
-
}
|
1381
|
-
}.open
|
1382
|
-
```
|
1383
|
-
|
1384
|
-
Check out the samples directory for more advanced examples of layouts in Glimmer.
|
1385
|
-
|
1386
|
-
**Defaults**:
|
1387
|
-
|
1388
|
-
Glimmer composites always come with `grid_layout` by default, but you can still specify explicitly if you'd like to set specific properties on it.
|
1389
|
-
|
1390
|
-
Glimmer shell always comes with `fill_layout` having `:horizontal` type.
|
1391
|
-
|
1392
|
-
This is a great guide for learning more about SWT layouts:
|
1393
|
-
|
1394
|
-
https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
|
1395
|
-
|
1396
|
-
Also, for a reference, check the SWT API:
|
1397
|
-
|
1398
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
|
1399
|
-
|
1400
|
-
### Layout Data
|
1401
|
-
|
1402
|
-
Layouts organize widgets following common rules for all widgets directly under a composite. But, what if a specific widget needs its own rules. That's where layout data comes into play.
|
1403
|
-
|
1404
|
-
By convention, SWT layouts expect widgets to set layout data with a class matching their class name with the word "Data" replacing "Layout":
|
1405
|
-
- `GridLayout` on a composite demands `GridData` on contained widgets
|
1406
|
-
- `RowLayout` on a composite demands `RowData` on contained widgets
|
1407
|
-
|
1408
|
-
Not all layouts support layout data to further customize widget layouts. For example, `FillLayout` supports no layout data.
|
1409
|
-
|
1410
|
-
Unlike widgets and layouts in Glimmer DSL, layout data is simply specified with `layout_data` keyword nested inside a widget block body, and followed by arguments and/or a block of its own properties (lowercase underscored names).
|
1411
|
-
|
1412
|
-
Glimmer automatically deduces layout data class name by convention as per rule above, with the assumption that the layout data class lives under the same exact Java package as the layout (one can set custom layout data that breaks convention if needed in rare cases. See code below for an example)
|
1413
|
-
|
1414
|
-
Glimmer also automatically accepts symbols (e.g. `:fill`) for SWT style arguments like `SWT::FILL`.
|
1415
|
-
|
1416
|
-
Examples:
|
1417
|
-
|
1418
|
-
```ruby
|
1419
|
-
# ...
|
1420
|
-
composite {
|
1421
|
-
row_layout :horizontal
|
1422
|
-
label {
|
1423
|
-
layout_data { # followed by properties
|
1424
|
-
width 50
|
1425
|
-
height 30
|
1426
|
-
}
|
1427
|
-
}
|
1428
|
-
# ... more widgets follow
|
1429
|
-
}
|
1430
|
-
# ...
|
1431
|
-
```
|
1432
|
-
|
1433
|
-
```ruby
|
1434
|
-
# ...
|
1435
|
-
composite {
|
1436
|
-
grid_layout 3, false # grid layout with 3 columns not of equal width
|
1437
|
-
label {
|
1438
|
-
# layout data followed by arguments passed to SWT GridData constructor
|
1439
|
-
layout_data :fill, :end, true, false
|
1440
|
-
}
|
1441
|
-
}
|
1442
|
-
# ...
|
1443
|
-
```
|
1444
|
-
|
1445
|
-
```ruby
|
1446
|
-
# ...
|
1447
|
-
composite {
|
1448
|
-
grid_layout 3, false # grid layout with 3 columns not of equal width
|
1449
|
-
label {
|
1450
|
-
# layout data set explicitly via an object (helps in rare cases that break convention)
|
1451
|
-
layout_data GridData.new(swt(:fill), swt(:end), true, false)
|
1452
|
-
}
|
1453
|
-
}
|
1454
|
-
# ...
|
1455
|
-
```
|
1456
|
-
|
1457
|
-
If you data-bind any layout data properties, when they change, the shell containing their widget re-packs its children (calls `#pack` method automatically) to ensure proper relayout of all widgets.
|
1458
|
-
|
1459
|
-
**NOTE**: Layout data must never be reused between widgets. Always specify or clone again for every widget.
|
1460
|
-
|
1461
|
-
This is a great guide for learning more about SWT layouts:
|
1462
|
-
|
1463
|
-
https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
|
1464
|
-
|
1465
|
-
Also, for a reference, check the SWT API:
|
1466
|
-
|
1467
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
|
1468
|
-
|
1469
|
-
### Data-Binding
|
1470
|
-
|
1471
|
-
Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
|
1472
|
-
|
1473
|
-
#### General Examples
|
1474
|
-
|
1475
|
-
`text bind(contact, :first_name)`
|
1476
|
-
|
1477
|
-
This example binds the text property of a widget like `label` to the first name of a contact model.
|
1478
|
-
|
1479
|
-
`text bind(contact, 'address.street')`
|
1480
|
-
|
1481
|
-
This example binds the text property of a widget like `label` to the nested street of
|
1482
|
-
the address of a contact. This is called nested property data binding.
|
1483
|
-
|
1484
|
-
`text bind(contact, 'address.street', on_read: :upcase, on_write: :downcase)`
|
1485
|
-
|
1486
|
-
This example adds on the one above it by specifying converters on read and write of the model property, like in the case of a `text` widget. The text widget will then displays the street upper case and the model will store it lower case. When specifying converters, read and write operations must be symmetric (to avoid an infinite update loop between the widget and the model since the widget checks first if value changed before updating)
|
1487
|
-
|
1488
|
-
`text bind(contact, 'address.street', on_read: lambda { |s| s[0..10] })`
|
1489
|
-
|
1490
|
-
This example also specifies a converter on read of the model property, but via a lambda, which truncates the street to 10 characters only. Note that the read and write operations are assymetric. This is fine in the case of formatting data for a read-only widget like `label`
|
1491
|
-
|
1492
|
-
`text bind(contact, 'address.street') { |s| s[0..10] }`
|
1493
|
-
|
1494
|
-
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.
|
1495
|
-
|
1496
|
-
`text bind(contact, 'address.street', read_only: true)
|
1497
|
-
|
1498
|
-
This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
|
1499
|
-
|
1500
|
-
`text bind(contact, 'addresses[1].street')`
|
1501
|
-
|
1502
|
-
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.
|
1503
|
-
|
1504
|
-
`text bind(contact, :age, computed_by: :date_of_birth)`
|
1505
|
-
|
1506
|
-
This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
|
1507
|
-
|
1508
|
-
`text bind(contact, :name, computed_by: [:first_name, :last_name])`
|
1509
|
-
|
1510
|
-
This example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
|
1511
|
-
|
1512
|
-
`text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
|
1513
|
-
|
1514
|
-
This example demonstrates nested indexed computed value data binding whereby the value of `profiles[0].name` depends on changes to both nested `profiles[0].first_name` and `profiles[0].last_name`.
|
1515
|
-
|
1516
|
-
Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1517
|
-
|
1518
|
-
#### Combo
|
1519
|
-
|
1520
|
-
The `combo` widget provides a dropdown of options. By default, it also allows typing in a new option. To disable that behavior, you may use with the `:read_only` SWT style.
|
1521
|
-
|
1522
|
-
When data-binding a `combo` widget, Glimmer can automatically deduce available options from data-bound model by convention: `{attribute_name}_options` method.
|
1523
|
-
|
1524
|
-
![Hello Combo](images/glimmer-hello-combo.png)
|
1525
|
-
|
1526
|
-
![Hello Combo](images/glimmer-hello-combo-expanded.png)
|
1527
|
-
|
1528
|
-
```ruby
|
1529
|
-
class Person
|
1530
|
-
attr_accessor :country, :country_options
|
1531
|
-
|
1532
|
-
def initialize
|
1533
|
-
self.country_options=["", "Canada", "US", "Mexico"]
|
1534
|
-
self.country = "Canada"
|
1535
|
-
end
|
1536
|
-
|
1537
|
-
def reset_country
|
1538
|
-
self.country = "Canada"
|
1539
|
-
end
|
1540
|
-
end
|
1541
|
-
|
1542
|
-
class HelloCombo
|
1543
|
-
include Glimmer
|
1544
|
-
def launch
|
1545
|
-
person = Person.new
|
1546
|
-
shell {
|
1547
|
-
composite {
|
1548
|
-
combo(:read_only) {
|
1549
|
-
selection bind(person, :country)
|
1550
|
-
}
|
1551
|
-
button {
|
1552
|
-
text "Reset"
|
1553
|
-
on_widget_selected do
|
1554
|
-
person.reset_country
|
1555
|
-
end
|
1556
|
-
}
|
1557
|
-
}
|
1558
|
-
}.open
|
1559
|
-
end
|
1560
|
-
end
|
1561
|
-
|
1562
|
-
HelloCombo.new.launch
|
1563
|
-
```
|
1564
|
-
|
1565
|
-
`combo` widget is data-bound to the country of a person. Note that it expects the `person` object to have the `:country` attribute and `:country_options` attribute containing all available countries (aka options). Glimmer reads these attributes by convention.
|
1566
|
-
|
1567
|
-
#### List
|
1568
|
-
|
1569
|
-
Example from [samples/hello/hello_list_single_selection.rb](samples/hello_list_single_selection.rb) sample:
|
1570
|
-
|
1571
|
-
![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
|
1572
|
-
|
1573
|
-
```ruby
|
1574
|
-
shell {
|
1575
|
-
composite {
|
1576
|
-
list {
|
1577
|
-
selection bind(person, :country)
|
1578
|
-
}
|
1579
|
-
button {
|
1580
|
-
text "Reset"
|
1581
|
-
on_widget_selected do
|
1582
|
-
person.reset_country
|
1583
|
-
end
|
1584
|
-
}
|
1585
|
-
}
|
1586
|
-
}.open
|
1587
|
-
```
|
1588
|
-
|
1589
|
-
`list` widget is also data-bound to the country of a person similarly to the combo widget. Not much difference here (the rest of the code not shown is the same).
|
1590
|
-
|
1591
|
-
Nonetheless, in the next example, a multi-selection list is declared instead allowing data-binding of multiple selection values to the bindable attribute on the model.
|
1592
|
-
|
1593
|
-
Example from [samples/hello/hello_list_multi_selection.rb](samples/hello_list_multi_selection.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1594
|
-
|
1595
|
-
![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
|
1596
|
-
|
1597
|
-
```ruby
|
1598
|
-
class Person
|
1599
|
-
attr_accessor :provinces, :provinces_options
|
1600
|
-
|
1601
|
-
def initialize
|
1602
|
-
self.provinces_options=[
|
1603
|
-
"",
|
1604
|
-
"Quebec",
|
1605
|
-
"Ontario",
|
1606
|
-
"Manitoba",
|
1607
|
-
"Saskatchewan",
|
1608
|
-
"Alberta",
|
1609
|
-
"British Columbia",
|
1610
|
-
"Nova Skotia",
|
1611
|
-
"Newfoundland"
|
1612
|
-
]
|
1613
|
-
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
1614
|
-
end
|
1615
|
-
|
1616
|
-
def reset_provinces
|
1617
|
-
self.provinces = ["Quebec", "Manitoba", "Alberta"]
|
1618
|
-
end
|
1619
|
-
end
|
1620
|
-
|
1621
|
-
class HelloListMultiSelection
|
1622
|
-
include Glimmer
|
1623
|
-
def launch
|
1624
|
-
person = Person.new
|
1625
|
-
shell {
|
1626
|
-
composite {
|
1627
|
-
list(:multi) {
|
1628
|
-
selection bind(person, :provinces)
|
1629
|
-
}
|
1630
|
-
button {
|
1631
|
-
text "Reset"
|
1632
|
-
on_widget_selected do
|
1633
|
-
person.reset_provinces
|
1634
|
-
end
|
1635
|
-
}
|
1636
|
-
}
|
1637
|
-
}.open
|
1638
|
-
end
|
1639
|
-
end
|
1640
|
-
|
1641
|
-
HelloListMultiSelection.new.launch
|
1642
|
-
```
|
1643
|
-
|
1644
|
-
The Glimmer code is not much different from above except for passing the `:multi` style to the `list` widget. However, the model code behind the scenes is quite different as it is a `provinces` array bindable to the selection of multiple values on a `list` widget. `provinces_options` contains all available province values just as expected by a single selection `list` and `combo`.
|
1645
|
-
|
1646
|
-
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).
|
1647
|
-
|
1648
|
-
You may learn more about Glimmer's data-binding syntax by reading the code under the [samples](samples) directory.
|
1649
|
-
|
1650
|
-
#### Table
|
1651
|
-
|
1652
|
-
The SWT Tree widget renders a multi-column data table, such as a contact listing or a sales report.
|
1653
|
-
|
1654
|
-
To data-bind a Table, you need the main model, the collection property, and the text display attribute for each table column.
|
1655
|
-
|
1656
|
-
This involves using the `bind` keyword mentioned above in addition to a special `column_properties` keyword that takes the table column text attribute methods.
|
1657
|
-
|
1658
|
-
It assumes you have defined the table columns via `table_column` widget.
|
1659
|
-
|
1660
|
-
Example:
|
1661
|
-
|
1662
|
-
```ruby
|
1663
|
-
shell {
|
1664
|
-
@table = table {
|
1665
|
-
table_column {
|
1666
|
-
text "Name"
|
1667
|
-
width 120
|
1668
|
-
}
|
1669
|
-
table_column {
|
1670
|
-
text "Age"
|
1671
|
-
width 120
|
1672
|
-
}
|
1673
|
-
table_column {
|
1674
|
-
text "Adult"
|
1675
|
-
width 120
|
1676
|
-
}
|
1677
|
-
items bind(group, :people), column_properties(:name, :age, :adult)
|
1678
|
-
selection bind(group, :selected_person)
|
1679
|
-
on_mouse_up { |event|
|
1680
|
-
@table.edit_table_item(event.table_item, event.column_index)
|
1681
|
-
}
|
1682
|
-
}
|
1683
|
-
}
|
1684
|
-
```
|
1685
|
-
|
1686
|
-
The code above includes two data-bindings:
|
1687
|
-
- 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.
|
1688
|
-
- 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)
|
1689
|
-
- The `on_mouse_up` event handler invokes `@table.edit_table_item(event.table_item, event.column_index)` to start edit mode on the clicked table item cell, and then saves or cancel depending on whether the user hits ENTER or ESC once done editing (or focus-out after either making a change or not making any changes.)
|
1690
|
-
|
1691
|
-
Additionally, Table `items` data-binding automatically stores each node model unto the SWT TableItem object via `setData` method. This enables things like searchability.
|
1692
|
-
|
1693
|
-
The table widget in Glimmer is represented by a subclass of `WidgetProxy` called `TableProxy`.
|
1694
|
-
TableProxy includes a `search` method that takes a block to look for a table item.
|
1695
|
-
|
1696
|
-
Example:
|
1697
|
-
|
1698
|
-
```ruby
|
1699
|
-
found_array = @table.search { |table_item| table_item.getData == company.owner }
|
1700
|
-
```
|
1701
|
-
|
1702
|
-
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.
|
1703
|
-
|
1704
|
-
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)`.
|
1705
|
-
This automatically leverages the SWT TableEditor custom class behind the scenes, displaying a text widget to the user to change the selected or
|
1706
|
-
passed table item text into something else.
|
1707
|
-
It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
|
1708
|
-
|
1709
|
-
##### Table Sorting
|
1710
|
-
|
1711
|
-
Glimmer automatically adds sorting support to the SWT `Table` widget.
|
1712
|
-
|
1713
|
-
Check out the [Contact Manager](#contact-manager) sample for an example.
|
1714
|
-
You may click on any column and it will sort by ascending order first and descending if you click again.
|
1715
|
-
|
1716
|
-
Glimmer automatic table sorting supports `String`, `Integer`, and `Float` columns out of the box as well as any column data that is comparable.
|
1717
|
-
|
1718
|
-
In cases where data is nil, depending on the data-type, it is automatically converted to `Float` with `to_f`, `Integer` with `to_i`, or `String` with `to_s`.
|
1719
|
-
|
1720
|
-
Should you have a special data type that could not be compared automatically, Glimmer offers the following 3 alternatives for custom sorting:
|
1721
|
-
- `sort_property`: this may be set to an alternative property to the one data-bound to the table column. For example, a table column called 'adult', which returns `true` or `false` may be sorted with `sort_property :dob` instead. This also support multi-property (aka multi-column) sorting (e.g. `sort_property :dob, :name`).
|
1722
|
-
- `sort_by(&block)`: this works just like Ruby `Enumerable` `sort_by`. The block receives the table column data as argument.
|
1723
|
-
- `sort(&comparator)`: this works just like Ruby `Enumerable` `sort`. The comparator block receives two objects from the table column data.
|
1724
|
-
|
1725
|
-
You may also set `additional_sort_properties` on the parent `table` widget to have secondary sorting applied. For example, if you set `additional_sort_properties :name, :project_name`, then whenever you sort by `:name`, it additionally sorts by `:project_name` afterwards, and vice versa. This only works for columns that either have no custom sort set or have a `sort_property` with one property only (but no sort or sort_by block)
|
1726
|
-
|
1727
|
-
Example:
|
1728
|
-
|
1729
|
-
```ruby
|
1730
|
-
# ...
|
1731
|
-
table {
|
1732
|
-
table_column {
|
1733
|
-
text 'Task'
|
1734
|
-
width 120
|
1735
|
-
}
|
1736
|
-
table_column {
|
1737
|
-
text 'Project'
|
1738
|
-
width 120
|
1739
|
-
}
|
1740
|
-
table_column {
|
1741
|
-
text 'Duration (hours)'
|
1742
|
-
width 120
|
1743
|
-
sort_property :duration_in_hours
|
1744
|
-
}
|
1745
|
-
table_column {
|
1746
|
-
text 'Priority'
|
1747
|
-
width 120
|
1748
|
-
sort_by { |value| ['High', 'Medium', 'Low'].index(value) }
|
1749
|
-
}
|
1750
|
-
table_column {
|
1751
|
-
text 'Start Date'
|
1752
|
-
width 120
|
1753
|
-
sort { |d1, d2| d1.to_date <=> d2.to_date }
|
1754
|
-
}
|
1755
|
-
additional_sort_properties :project_name, :duration_in_hours, :name
|
1756
|
-
items bind(Task, :list), column_properties(:name, :project_name, :duration, :priority, :start_date)
|
1757
|
-
# ...
|
1758
|
-
}
|
1759
|
-
# ...
|
1760
|
-
```
|
1761
|
-
|
1762
|
-
Here is an explanation of the example above:
|
1763
|
-
- Task and Project table columns are data-bound to the `:name` and `:project_name` properties and sorted through them automatically
|
1764
|
-
- Task Duration table column is data-bound to the `:duration` property, but sorted via the `:duration_in_hours` property instead
|
1765
|
-
- Task Priority table column has a custom sort_by block
|
1766
|
-
- Task Start Date table column has a custom sort comparator block
|
1767
|
-
- Additional (secondary) sort properties are applied when sorting by Task, Project, or Duration in the order specified
|
1768
|
-
|
1769
|
-
|
1770
|
-
#### Tree
|
1771
|
-
|
1772
|
-
The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
|
1773
|
-
|
1774
|
-
To data-bind a Tree, you need the root model, the children querying method, and the text display attribute on each child.
|
1775
|
-
|
1776
|
-
This involves using the `bind` keyword mentioned above in addition to a special `tree_properties` keyword that takes the children and text attribute methods.
|
1777
|
-
|
1778
|
-
Example:
|
1779
|
-
|
1780
|
-
```ruby
|
1781
|
-
shell {
|
1782
|
-
@tree = tree {
|
1783
|
-
items bind(company, :owner), tree_properties(children: :coworkers, text: :name)
|
1784
|
-
selection bind(company, :selected_coworker)
|
1785
|
-
}
|
1786
|
-
}
|
1787
|
-
```
|
1788
|
-
|
1789
|
-
The code above includes two data-bindings:
|
1790
|
-
- 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.
|
1791
|
-
- Tree `selection`, which binds the single tree item selected by the user to the attribute denoted by the `bind` keyword
|
1792
|
-
|
1793
|
-
Additionally, Tree `items` data-binding automatically stores each node model unto the SWT TreeItem object via `setData` method. This enables things like searchability.
|
1794
|
-
|
1795
|
-
The tree widget in Glimmer is represented by a subclass of `WidgetProxy` called `TreeProxy`.
|
1796
|
-
TreeProxy includes a `depth_first_search` method that takes a block to look for a tree item.
|
1797
|
-
|
1798
|
-
Example:
|
1799
|
-
|
1800
|
-
```ruby
|
1801
|
-
found_array = @tree.depth_first_search { |tree_item| tree_item.getData == company.owner }
|
1802
|
-
```
|
1803
|
-
|
1804
|
-
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.
|
1805
|
-
|
1806
|
-
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
|
1807
|
-
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.
|
1808
|
-
|
1809
|
-
### Observer
|
1810
|
-
|
1811
|
-
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.
|
1812
|
-
|
1813
|
-
#### Observing Widgets
|
1814
|
-
|
1815
|
-
Glimmer supports observing widgets with two main types of events:
|
1816
|
-
1. `on_{swt-listener-method-name}`: where {swt-listener-method-name} is replaced with the lowercase underscored event method name on an SWT listener class (e.g. `on_verify_text` for `org.eclipse.swt.events.VerifyListener#verifyText`).
|
1817
|
-
2. `on_swt_{swt-event-constant}`: where {swt-event-constant} is replaced with an [`org.eclipse.swt.SWT`](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html) event constant (e.g. `on_swt_show` for `SWT.Show` to observe when widget becomes visible)
|
1818
|
-
|
1819
|
-
Additionally, there are two more types of events:
|
1820
|
-
- SWT `display` supports global listeners called filters that run on any widget. They are hooked via `on_swt_{swt-event-constant}`
|
1821
|
-
- SWT `display` supports Mac application menu item observers (`on_about` and `on_preferences`), which you can read about under [Miscellaneous](#miscellaneous).
|
1822
|
-
|
1823
|
-
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.
|
1824
|
-
|
1825
|
-
**Regarding number 1**, to figure out what the available events for an SWT widget are, check out all of its `add***Listener` API methods, and then open the listener class argument to check its "event methods".
|
1826
|
-
|
1827
|
-
For example, if you look at the `Button` SWT API:
|
1828
|
-
https://help.eclipse.org/2019-12/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fswt%2Fbrowser%2FBrowser.html
|
1829
|
-
|
1830
|
-
It has `addSelectionListener`. Additionally, under its `Control` super class, it has `addControlListener`, `addDragDetectListener`, `addFocusListener`, `addGestureListener`, `addHelpListener`, `addKeyListener`, `addMenuDetectListener`, `addMouseListener`, `addMouseMoveListener`, `addMouseTrackListener`, `addMouseWheelListener`, `addPaintListener`, `addTouchListener`, and `addTraverseListener`
|
1831
|
-
|
1832
|
-
Suppose, we select `addSelectionListener`, which is responsible for what happens when a user selects a button (clicks it). Then, open its argument `SelectionListener` SWT API, and you find the event (instance) methods: `widgetDefaultSelected` and `widgetSelected`. Let's select the second one, which is what gets invoked when a button is clicked.
|
1833
|
-
|
1834
|
-
Now, Glimmer simplifies the process of hooking into that listener (observer) by neither requiring you to call the `addSelectionListener` method nor requiring you to implement/extend the `SelectionListener` API.
|
1835
|
-
|
1836
|
-
Instead, simply add a `on_widget_selected` followed by a Ruby block containing the logic to perform. Glimmer figures out the rest.
|
1837
|
-
|
1838
|
-
Let's revisit the Tic Tac Toe example shown near the beginning of the page:
|
1839
|
-
|
1840
|
-
```ruby
|
1841
|
-
shell {
|
1842
|
-
text "Tic-Tac-Toe"
|
1843
|
-
composite {
|
1844
|
-
grid_layout 3, true
|
1845
|
-
(1..3).each { |row|
|
1846
|
-
(1..3).each { |column|
|
1847
|
-
button {
|
1848
|
-
layout_data :fill, :fill, true, true
|
1849
|
-
text bind(@tic_tac_toe_board[row, column], :sign)
|
1850
|
-
enabled bind(@tic_tac_toe_board[row, column], :empty)
|
1851
|
-
on_widget_selected {
|
1852
|
-
@tic_tac_toe_board.mark(row, column)
|
1853
|
-
}
|
1854
|
-
}
|
1855
|
-
}
|
1856
|
-
}
|
1857
|
-
}
|
1858
|
-
}
|
1859
|
-
```
|
1860
|
-
|
1861
|
-
Note that every Tic Tac Toe grid cell has its `text` and `enabled` properties data-bound to the `sign` and `empty` attributes on the `TicTacToe::Board` model respectively.
|
1862
|
-
|
1863
|
-
Next however, each of these Tic Tac Toe grid cells, which are clickable buttons, have an `on_widget_selected` observer, which once triggered, marks the cell on the `TicTacToe::Board` to make a move.
|
1864
|
-
|
1865
|
-
**Regarding number 2**, you can figure out all available events by looking at the [`org.eclipse.swt.SWT`](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html) API:
|
1866
|
-
|
1867
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
|
1868
|
-
|
1869
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1870
|
-
|
1871
|
-
`SWT.Show` - hooks a listener for showing a widget (using `on_swt_show` in Glimmer)
|
1872
|
-
`SWT.Hide` - hooks a listener for hiding a widget (using `on_swt_hide` in Glimmer)
|
1873
|
-
|
1874
|
-
```ruby
|
1875
|
-
shell {
|
1876
|
-
@button1 = button {
|
1877
|
-
text "Show 2nd Button"
|
1878
|
-
visible true
|
1879
|
-
on_swt_show {
|
1880
|
-
@button2.swt_widget.setVisible(false)
|
1881
|
-
}
|
1882
|
-
on_widget_selected {
|
1883
|
-
@button2.swt_widget.setVisible(true)
|
1884
|
-
}
|
1885
|
-
}
|
1886
|
-
@button2 = button {
|
1887
|
-
text "Show 1st Button"
|
1888
|
-
visible false
|
1889
|
-
on_swt_show {
|
1890
|
-
@button1.swt_widget.setVisible(false)
|
1891
|
-
}
|
1892
|
-
on_widget_selected {
|
1893
|
-
@button1.swt_widget.setVisible(true)
|
1894
|
-
}
|
1895
|
-
}
|
1896
|
-
}.open
|
1897
|
-
```
|
1898
|
-
|
1899
|
-
**Gotcha:** SWT.Resize event needs to be hooked using **`on_swt_Resize`** because [`org.eclipse.swt.SWT`](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html) has 2 constants for resize: `RESIZE` and `Resize`, so it cannot infer the right one automatically from the underscored version `on_swt_resize`
|
1900
|
-
|
1901
|
-
##### Alternative Syntax
|
1902
|
-
|
1903
|
-
Instead of declaring a widget observer using `on_***` syntax inside a widget content block, you may also do so after the widget declaration by invoking directly on the widget object.
|
1904
|
-
|
1905
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1906
|
-
|
1907
|
-
```
|
1908
|
-
@shell = shell {
|
1909
|
-
label {
|
1910
|
-
text "Hello, World!"
|
1911
|
-
}
|
1912
|
-
}
|
1913
|
-
@shell.on_shell_iconified {
|
1914
|
-
@shell.close
|
1915
|
-
}
|
1916
|
-
@shell.open
|
1917
|
-
```
|
1918
|
-
|
1919
|
-
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.
|
1920
|
-
|
1921
|
-
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.
|
1922
|
-
|
1923
|
-
#### Observing Models
|
1924
|
-
|
1925
|
-
Glimmer DSL includes an `observe` keyword used to register an observer by passing in the observable and the property(ies) to observe, and then specifying in a block what happens on notification.
|
1926
|
-
|
1927
|
-
```ruby
|
1928
|
-
class TicTacToe
|
1929
|
-
include Glimmer
|
1930
|
-
|
1931
|
-
def initialize
|
1932
|
-
# ...
|
1933
|
-
observe(@tic_tac_toe_board, :game_status) { |game_status|
|
1934
|
-
display_win_message if game_status == Board::WIN
|
1935
|
-
display_draw_message if game_status == Board::DRAW
|
1936
|
-
}
|
1937
|
-
end
|
1938
|
-
# ...
|
1939
|
-
end
|
1940
|
-
```
|
1941
|
-
|
1942
|
-
Observers can be a good mechanism for displaying dialog messages in Glimmer (using SWT's [`MessageBox`](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/widgets/MessageBox.html) class).
|
1943
|
-
|
1944
|
-
Look at [`samples/elaborate/tictactoe/tic_tac_toe.rb`](samples/tictactoe/tic_tac_toe.rb) for more details starting with the code included below.
|
1945
|
-
|
1946
|
-
```ruby
|
1947
|
-
class TicTacToe
|
1948
|
-
include Glimmer
|
1949
|
-
include Observer
|
1950
|
-
|
1951
|
-
def initialize
|
1952
|
-
# ...
|
1953
|
-
observe(@tic_tac_toe_board, :game_status) { |game_status|
|
1954
|
-
display_win_message if game_status == Board::WIN
|
1955
|
-
display_draw_message if game_status == Board::DRAW
|
1956
|
-
}
|
1957
|
-
end
|
1958
|
-
|
1959
|
-
def display_win_message
|
1960
|
-
display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
|
1961
|
-
end
|
1962
|
-
|
1963
|
-
def display_draw_message
|
1964
|
-
display_game_over_message("Draw!")
|
1965
|
-
end
|
1966
|
-
|
1967
|
-
def display_game_over_message(message)
|
1968
|
-
message_box(@shell) {
|
1969
|
-
text 'Game Over'
|
1970
|
-
message message_text
|
1971
|
-
}.open
|
1972
|
-
@tic_tac_toe_board.reset
|
1973
|
-
end
|
1974
|
-
# ...
|
1975
|
-
end
|
1976
|
-
```
|
1977
|
-
|
1978
|
-
### Custom Widgets
|
1979
|
-
|
1980
|
-
Glimmer supports creating custom widgets with minimal code, which automatically extends Glimmer's DSL syntax with an underscored lowercase keyword.
|
1981
|
-
|
1982
|
-
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 `__`)
|
1983
|
-
|
1984
|
-
#### Simple Example
|
1985
|
-
|
1986
|
-
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
1987
|
-
|
1988
|
-
Definition:
|
1989
|
-
```ruby
|
1990
|
-
class RedLabel
|
1991
|
-
include Glimmer::UI::CustomWidget
|
1992
|
-
|
1993
|
-
body {
|
1994
|
-
label(swt_style) {
|
1995
|
-
background :red
|
1996
|
-
}
|
1997
|
-
}
|
1998
|
-
end
|
1999
|
-
```
|
2000
|
-
|
2001
|
-
Usage:
|
2002
|
-
```ruby
|
2003
|
-
shell {
|
2004
|
-
red_label {
|
2005
|
-
text 'Red Label'
|
2006
|
-
}
|
2007
|
-
}.open
|
2008
|
-
```
|
2009
|
-
|
2010
|
-
As you can see, `RedLabel` became Glimmer DSL keyword: `red_label`
|
2011
|
-
|
2012
|
-
#### Lifecycle Hook Example
|
2013
|
-
|
2014
|
-
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
2015
|
-
|
2016
|
-
Definition:
|
2017
|
-
```ruby
|
2018
|
-
module Red
|
2019
|
-
class Composite
|
2020
|
-
include Glimmer::UI::CustomWidget
|
2021
|
-
|
2022
|
-
before_body {
|
2023
|
-
@color = :red
|
2024
|
-
}
|
2025
|
-
|
2026
|
-
body {
|
2027
|
-
composite(swt_style) {
|
2028
|
-
background @color
|
2029
|
-
}
|
2030
|
-
}
|
2031
|
-
end
|
2032
|
-
end
|
2033
|
-
```
|
2034
|
-
|
2035
|
-
Usage:
|
2036
|
-
```ruby
|
2037
|
-
shell {
|
2038
|
-
red__composite {
|
2039
|
-
label {
|
2040
|
-
foreground :white
|
2041
|
-
text 'This is showing inside a Red Composite'
|
2042
|
-
}
|
2043
|
-
}
|
2044
|
-
}.open
|
2045
|
-
```
|
2046
|
-
|
2047
|
-
Notice how `Red::Composite` became `red__composite` with double-underscore, which is how Glimmer Custom Widgets signify namespaces by convention. Additionally, the `before_body` lifecycle hook was utilized to set a `@color` variable and use inside the `body`.
|
2048
|
-
|
2049
|
-
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.
|
2050
|
-
|
2051
|
-
#### Custom Widget API
|
2052
|
-
|
2053
|
-
Custom Widgets have the following attributes available to call from inside the `#body` method:
|
2054
|
-
- `#parent`: Glimmer object parenting custom widget
|
2055
|
-
- `#swt_style`: SWT style integer. Can be useful if you want to allow consumers to customize a widget inside the custom widget body
|
2056
|
-
- `#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)
|
2057
|
-
- `#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.
|
2058
|
-
- `#body_root`: top-most (root) widget returned from `#body` method.
|
2059
|
-
- `#swt_widget`: actual SWT widget for `body_root`
|
2060
|
-
|
2061
|
-
Additionally, custom widgets can call the following class methods:
|
2062
|
-
- `::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)
|
2063
|
-
- `::option(option_name, default: nil)`: declares a single option taking option name and default value as arguments (also generates attribute accessors just like `::options`)
|
2064
|
-
|
2065
|
-
#### Content/Options Example
|
2066
|
-
|
2067
|
-
(you may copy/paste in [`girb`](#girb-glimmer-irb-command))
|
2068
|
-
|
2069
|
-
Definition:
|
2070
|
-
```ruby
|
2071
|
-
class Sandwich
|
2072
|
-
include Glimmer::UI::CustomWidget
|
2073
|
-
|
2074
|
-
options :orientation, :bg_color
|
2075
|
-
option :fg_color, default: :black
|
2076
|
-
|
2077
|
-
body {
|
2078
|
-
composite(swt_style) { # gets custom widget style
|
2079
|
-
fill_layout orientation # using orientation option
|
2080
|
-
background bg_color # using container_background option
|
2081
|
-
label {
|
2082
|
-
text 'SANDWICH TOP'
|
2083
|
-
}
|
2084
|
-
content.call # this is where content block is called
|
2085
|
-
label {
|
2086
|
-
text 'SANDWICH BOTTOM'
|
2087
|
-
}
|
2088
|
-
}
|
2089
|
-
}
|
2090
|
-
end
|
2091
|
-
```
|
2092
|
-
|
2093
|
-
Usage:
|
2094
|
-
```ruby
|
2095
|
-
shell {
|
2096
|
-
sandwich(:no_focus, orientation: :vertical, bg_color: :red) {
|
2097
|
-
label {
|
2098
|
-
background :green
|
2099
|
-
text 'SANDWICH CONTENT'
|
2100
|
-
}
|
2101
|
-
}
|
2102
|
-
}.open
|
2103
|
-
```
|
2104
|
-
|
2105
|
-
Notice how `:no_focus` was the `swt_style` value, followed by the `options` hash `{orientation: :horizontal, bg_color: :white}`, and finally the `content` block containing the label with `'SANDWICH CONTENT'`
|
2106
|
-
|
2107
|
-
#### Custom Widget Lifecycle Hooks
|
2108
|
-
|
2109
|
-
Last but not least, these are the available lifecycle hooks:
|
2110
|
-
- `before_body`: takes a block that executes in the custom widget instance scope before calling `body`. Useful for initializing variables to later use in `body`
|
2111
|
-
- `after_body`: takes a block that executes in the custom widget instance scope after calling `body`. Useful for setting up observers on widgets built in `body` (set in instance variables) and linking to other shells.
|
2112
|
-
|
2113
|
-
#### Gotcha
|
2114
|
-
|
2115
|
-
Beware of defining a custom attribute that is a common SWT widget property name.
|
2116
|
-
For example, if you define `text=` and `text` methods to accept a custom text and then later you write this body:
|
2117
|
-
|
2118
|
-
```ruby
|
2119
|
-
# ...
|
2120
|
-
def text
|
2121
|
-
# ...
|
2122
|
-
end
|
2123
|
-
|
2124
|
-
def text=(value)
|
2125
|
-
# ...
|
2126
|
-
end
|
2127
|
-
|
2128
|
-
body {
|
2129
|
-
composite {
|
2130
|
-
label {
|
2131
|
-
text "Hello"
|
2132
|
-
}
|
2133
|
-
label {
|
2134
|
-
text "World"
|
2135
|
-
}
|
2136
|
-
}
|
2137
|
-
}
|
2138
|
-
# ...
|
2139
|
-
```
|
2140
|
-
|
2141
|
-
The `text` method invoked in the custom widget body will call the one you defined above it. To avoid this gotcha, simply name the text property above something else, like `custom_text`.
|
2142
|
-
|
2143
|
-
#### Final Notes
|
2144
|
-
|
2145
|
-
This [Eclipse guide](https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm) for how to write custom SWT widgets is also applicable to Glimmer Custom Widgets written in Ruby. I recommend reading it:
|
2146
|
-
[https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm](https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm)
|
2147
|
-
|
2148
|
-
### Custom Shells
|
2149
|
-
|
2150
|
-
Custom shells are a kind of custom widgets that have shells only as the body root. They can be self-contained applications that may be opened and hidden/closed independently of the main app.
|
2151
|
-
|
2152
|
-
They may also be chained in a wizard fashion.
|
2153
|
-
|
2154
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2155
|
-
|
2156
|
-
```ruby
|
2157
|
-
class WizardStep
|
2158
|
-
include Glimmer::UI::CustomShell
|
2159
|
-
|
2160
|
-
options :number, :step_count
|
2161
|
-
|
2162
|
-
before_body {
|
2163
|
-
@title = "Step #{number}"
|
2164
|
-
}
|
2165
|
-
|
2166
|
-
body {
|
2167
|
-
shell {
|
2168
|
-
text "Wizard - #{@title}"
|
2169
|
-
minimum_size 200, 100
|
2170
|
-
fill_layout :vertical
|
2171
|
-
label(:center) {
|
2172
|
-
text @title
|
2173
|
-
font height: 30
|
2174
|
-
}
|
2175
|
-
if number < step_count
|
2176
|
-
button {
|
2177
|
-
text "Go To Next Step"
|
2178
|
-
on_widget_selected {
|
2179
|
-
body_root.hide
|
2180
|
-
}
|
2181
|
-
}
|
2182
|
-
end
|
2183
|
-
}
|
2184
|
-
}
|
2185
|
-
end
|
2186
|
-
|
2187
|
-
shell { |app_shell|
|
2188
|
-
text "Wizard"
|
2189
|
-
minimum_size 200, 100
|
2190
|
-
@current_step_number = 1
|
2191
|
-
@wizard_steps = 5.times.map { |n|
|
2192
|
-
wizard_step(number: n+1, step_count: 5) {
|
2193
|
-
on_swt_hide {
|
2194
|
-
if @current_step_number < 5
|
2195
|
-
@current_step_number += 1
|
2196
|
-
app_shell.hide
|
2197
|
-
@wizard_steps[@current_step_number - 1].open
|
2198
|
-
end
|
2199
|
-
}
|
2200
|
-
}
|
2201
|
-
}
|
2202
|
-
button {
|
2203
|
-
text "Start"
|
2204
|
-
font height: 40
|
2205
|
-
on_widget_selected {
|
2206
|
-
app_shell.hide
|
2207
|
-
@wizard_steps[@current_step_number - 1].open
|
2208
|
-
}
|
2209
|
-
}
|
2210
|
-
}.open
|
2211
|
-
```
|
2212
|
-
|
2213
|
-
### Drag and Drop
|
2214
|
-
|
2215
|
-
Glimmer offers Drag and Drop support, thanks to [SWT](https://www.eclipse.org/swt/) and Glimmer's lightweight [DSL syntax](#glimmer-dsl-syntax).
|
2216
|
-
|
2217
|
-
You may learn more about SWT Drag and Drop support over here: [https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html](https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html)
|
2218
|
-
|
2219
|
-
To get started, simply follow these steps:
|
2220
|
-
1. On the drag source widget, add `on_drag_set_data` [DragSourceListener](https://help.eclipse.org/2020-03/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/DragSourceListener.html) event handler block at minimum (you may also add `on_drag_start` and `on_drag_finished` if needed)
|
2221
|
-
1. Set `event.data` to transfer via drag and drop inside the `on_drag_set_data` event handler block (defaults to `transfer` type of `:text`, as in a Ruby String)
|
2222
|
-
1. On the drop target widget, add `on_drop` [DropTargetListener](https://help.eclipse.org/2020-03/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/DropTargetListener.html) event handler block at minimum (you may also add `on_drag_enter` [must set [`event.detail`](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/DropTargetEvent.html#detail) if added], `on_drag_over`, `on_drag_leave`, `on_drag_operation_changed` and `on_drop_accept` if needed)
|
2223
|
-
1. Read `event.data` and consume it (e.g. change widget text) inside the `on_drop` event handler block.
|
2224
|
-
|
2225
|
-
Example (taken from [samples/hello/hello_drag_and_drop.rb](#hello-drag-and-drop) / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2226
|
-
|
2227
|
-
```ruby
|
2228
|
-
class Location
|
2229
|
-
attr_accessor :country
|
2230
|
-
|
2231
|
-
def country_options
|
2232
|
-
%w[USA Canada Mexico Columbia UK Australia Germany Italy Spain]
|
2233
|
-
end
|
2234
|
-
end
|
2235
|
-
|
2236
|
-
@location = Location.new
|
2237
|
-
|
2238
|
-
include Glimmer
|
2239
|
-
|
2240
|
-
shell {
|
2241
|
-
text 'Hello, Drag and Drop!'
|
2242
|
-
list {
|
2243
|
-
selection bind(@location, :country)
|
2244
|
-
on_drag_set_data { |event|
|
2245
|
-
list = event.widget.getControl
|
2246
|
-
event.data = list.getSelection.first
|
2247
|
-
}
|
2248
|
-
}
|
2249
|
-
label(:center) {
|
2250
|
-
text 'Drag a country here!'
|
2251
|
-
font height: 20
|
2252
|
-
on_drop { |event|
|
2253
|
-
event.widget.getControl.setText(event.data)
|
2254
|
-
}
|
2255
|
-
}
|
2256
|
-
}.open
|
2257
|
-
```
|
2258
|
-
|
2259
|
-
![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
|
2260
|
-
|
2261
|
-
Optional steps:
|
2262
|
-
- Set a `transfer` property (defaults to `:text`). Values may be: :text (default), :html :image, :rtf, :url, and :file, or an array of multiple values. The `transfer` property will automatically convert your option into a [Transfer](https://help.eclipse.org/2020-03/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/Transfer.html) object as per the SWT API.
|
2263
|
-
- Specify `drag_source_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
|
2264
|
-
- Specify `drag_source_effect` (Check [DragSourceEffect](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/DragSourceEffect.html) SWT API for details)
|
2265
|
-
- Specify `drop_target_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
|
2266
|
-
- Specify `drop_target_effect` (Check [DropTargetEffect](https://help.eclipse.org/2020-06/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/dnd/DropTargetEffect.html) SWT API for details)
|
2267
|
-
- Set drag operation in `event.detail` (e.g. DND::DROP_COPY) inside `on_drag_enter`
|
2268
|
-
|
2269
|
-
### Miscellaneous
|
2270
|
-
|
2271
|
-
#### Multi-DSL Support
|
2272
|
-
|
2273
|
-
Glimmer is a DSL engine that supports multiple DSLs (Domain Specific Languages):
|
2274
|
-
- [SWT](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
|
2275
|
-
- [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
|
2276
|
-
- [XML](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML) - Useful with [SWT Browser Widget](#browser-widget)
|
2277
|
-
- [CSS](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets) - Useful with [SWT Browser Widget](#browser-widget)
|
2278
|
-
|
2279
|
-
Glimmer automatically recognizes top-level keywords in each DSL and activates DSL accordingly. Glimmer allows mixing DSLs, which comes in handy when doing things like using the SWT Browser widget with XML and CSS. Once done processing a nested DSL top-level keyword, Glimmer switches back to the prior DSL automatically.
|
2280
|
-
|
2281
|
-
##### SWT
|
2282
|
-
|
2283
|
-
The SWT DSL was already covered in detail. However, for the sake of mixing DSLs, you need to know that the SWT DSL has the following top-level keywords:
|
2284
|
-
- `shell`
|
2285
|
-
- `display`
|
2286
|
-
- `color`
|
2287
|
-
- `observe`
|
2288
|
-
- `async_exec`
|
2289
|
-
- `sync_exec`
|
2290
|
-
|
2291
|
-
##### Opal
|
2292
|
-
|
2293
|
-
Full instructions are found in the [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) DSL page.
|
2294
|
-
|
2295
|
-
The [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) DSL is simply a web GUI adapter for desktop apps written in Glimmer. As such, it supports all the DSL keywords of the SWT DSL and shares the same top-level keywords.
|
2296
|
-
|
2297
|
-
##### XML
|
2298
|
-
|
2299
|
-
Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
|
2300
|
-
Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
|
2301
|
-
|
2302
|
-
Here are all the Glimmer XML DSL top-level keywords:
|
2303
|
-
- `html`
|
2304
|
-
- `tag`: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
|
2305
|
-
- `name_space`: enables namespacing html tags
|
2306
|
-
|
2307
|
-
Element properties are typically passed as a key/value hash (e.g. `section(id: 'main', class: 'accordion')`) . However, for properties like "selected" or "checked", you must leave value `nil` or otherwise pass in front of the hash (e.g. `input(:checked, type: 'checkbox')` )
|
2308
|
-
|
2309
|
-
Example (basic HTML / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2310
|
-
|
2311
|
-
```ruby
|
2312
|
-
@xml = html {
|
2313
|
-
head {
|
2314
|
-
meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
|
2315
|
-
}
|
2316
|
-
body {
|
2317
|
-
h1 { "Hello, World!" }
|
2318
|
-
}
|
2319
|
-
}
|
2320
|
-
puts @xml
|
2321
|
-
```
|
2322
|
-
|
2323
|
-
Output:
|
2324
|
-
|
2325
|
-
```
|
2326
|
-
<html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
|
2327
|
-
```
|
2328
|
-
|
2329
|
-
Example (explicit XML tag / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2330
|
-
|
2331
|
-
```ruby
|
2332
|
-
puts tag(:_name => "DOCUMENT")
|
2333
|
-
```
|
2334
|
-
|
2335
|
-
Output:
|
2336
|
-
|
2337
|
-
```
|
2338
|
-
<DOCUMENT/>
|
2339
|
-
```
|
2340
|
-
|
2341
|
-
Example (XML namespaces using `name_space` keyword / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2342
|
-
|
2343
|
-
```ruby
|
2344
|
-
@xml = name_space(:w3c) {
|
2345
|
-
html(:id => "thesis", :class => "document") {
|
2346
|
-
body(:id => "main") {
|
2347
|
-
}
|
2348
|
-
}
|
2349
|
-
}
|
2350
|
-
puts @xml
|
2351
|
-
```
|
2352
|
-
|
2353
|
-
Output:
|
2354
|
-
|
2355
|
-
```
|
2356
|
-
<w3c:html id="thesis" class="document"><w3c:body id="main"></w3c:body></w3c:html>
|
2357
|
-
```
|
2358
|
-
|
2359
|
-
Example (XML namespaces using dot operator / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2360
|
-
|
2361
|
-
```ruby
|
2362
|
-
@xml = tag(:_name => "DOCUMENT") {
|
2363
|
-
document.body(document.id => "main") {
|
2364
|
-
}
|
2365
|
-
}
|
2366
|
-
puts @xml
|
2367
|
-
```
|
2368
|
-
|
2369
|
-
Output:
|
2370
|
-
|
2371
|
-
```
|
2372
|
-
<DOCUMENT><document:body document:id="main"></document:body></DOCUMENT>
|
2373
|
-
```
|
2374
|
-
|
2375
|
-
##### CSS
|
2376
|
-
|
2377
|
-
Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
|
2378
|
-
Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
|
2379
|
-
|
2380
|
-
`css` is the only top-level keyword in the Glimmer CSS DSL
|
2381
|
-
|
2382
|
-
Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
|
2383
|
-
Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
|
2384
|
-
|
2385
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2386
|
-
|
2387
|
-
```ruby
|
2388
|
-
@css = css {
|
2389
|
-
body {
|
2390
|
-
font_size '1.1em'
|
2391
|
-
pv 'background', 'white'
|
2392
|
-
}
|
2393
|
-
|
2394
|
-
s('body > h1') {
|
2395
|
-
background_color :red
|
2396
|
-
pv 'font-size', '2em'
|
2397
|
-
}
|
2398
|
-
}
|
2399
|
-
puts @css
|
2400
|
-
```
|
2401
|
-
|
2402
|
-
##### Listing / Enabling / Disabling DSLs
|
2403
|
-
|
2404
|
-
Glimmer provides a number of methods on Glimmer::DSL::Engine to configure DSL support or inquire about it:
|
2405
|
-
- `Glimmer::DSL::Engine.dsls`: Lists available Glimmer DSLs
|
2406
|
-
- `Glimmer::DSL::Engine.disable_dsl(dsl_name)`: Disables a specific DSL. Useful when there is no need for certain DSLs in a certain application.
|
2407
|
-
- `Glimmer::DSL::Engine.disabled_dsls': Lists disabled DSLs
|
2408
|
-
- `Glimmer::DSL::Engine.enable_dsl(dsl_name)`: Re-enables disabled DSL
|
2409
|
-
- `Glimmer::DSL::Engine.enabled_dsls=(dsl_names)`: Disables all DSLs except the ones specified.
|
2410
|
-
|
2411
|
-
#### Application Menu Items (About/Preferences)
|
2412
|
-
|
2413
|
-
Mac applications always have About and Preferences menu items. Glimmer provides widget observer hooks for them on the `display`:
|
2414
|
-
- `on_about`: executes code when user selects App Name -> About
|
2415
|
-
- `on_preferences`: executes code when user selects App Name -> Preferences or hits 'CMD+,' on the Mac
|
2416
|
-
|
2417
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2418
|
-
|
2419
|
-
```ruby
|
2420
|
-
class Example
|
2421
|
-
def initialize
|
2422
|
-
display {
|
2423
|
-
on_about {
|
2424
|
-
message_box(@shell_proxy) {
|
2425
|
-
text 'About'
|
2426
|
-
message 'About Application'
|
2427
|
-
}.open
|
2428
|
-
}
|
2429
|
-
on_preferences {
|
2430
|
-
preferences_dialog = dialog {
|
2431
|
-
text 'Preferences'
|
2432
|
-
row_layout {
|
2433
|
-
type :vertical
|
2434
|
-
margin_left 15
|
2435
|
-
margin_top 15
|
2436
|
-
margin_right 15
|
2437
|
-
margin_bottom 15
|
2438
|
-
}
|
2439
|
-
label {
|
2440
|
-
text 'Check one of these options:'
|
2441
|
-
}
|
2442
|
-
button(:radio) {
|
2443
|
-
text 'Option 1'
|
2444
|
-
}
|
2445
|
-
button(:radio) {
|
2446
|
-
text 'Option 2'
|
2447
|
-
}
|
2448
|
-
}
|
2449
|
-
preferences_dialog.open
|
2450
|
-
}
|
2451
|
-
}
|
2452
|
-
@shell_proxy = shell {
|
2453
|
-
text 'Application Menu Items'
|
2454
|
-
fill_layout {
|
2455
|
-
margin_width 15
|
2456
|
-
margin_height 15
|
2457
|
-
}
|
2458
|
-
label {
|
2459
|
-
text 'Application Menu Items'
|
2460
|
-
font height: 30
|
2461
|
-
}
|
2462
|
-
}
|
2463
|
-
@shell_proxy.open
|
2464
|
-
end
|
2465
|
-
end
|
2466
|
-
|
2467
|
-
Example.new
|
2468
|
-
```
|
2469
|
-
|
2470
|
-
#### App Name and Version
|
2471
|
-
|
2472
|
-
Application name (shows up on the Mac in top menu bar) and version may be specified upon [packaging](#packaging--distribution) by specifying "-Bmac.CFBundleName" and "-Bmac.CFBundleVersion" options.
|
2473
|
-
|
2474
|
-
Still, if you would like proper application name to show up on the Mac top menu bar during development, you may do so by invoking the SWT Display.setAppName method before any Display object has been instantiated (i.e. before any Glimmer widget like shell has been declared).
|
2475
|
-
|
2476
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2477
|
-
|
2478
|
-
```ruby
|
2479
|
-
Display.setAppName('Glimmer Demo')
|
2480
|
-
|
2481
|
-
shell(:no_resize) {
|
2482
|
-
text "Glimmer"
|
2483
|
-
label {
|
2484
|
-
text "Hello, World!"
|
2485
|
-
}
|
2486
|
-
}.open
|
2487
|
-
```
|
2488
|
-
|
2489
|
-
Also, you may invoke `Display.setAppVersion('1.0.0')` if needed for OS app version identification reasons during development, replacing `'1.0.0'` with your application version.
|
2490
|
-
|
2491
|
-
#### Video Widget
|
2492
|
-
|
2493
|
-
[![Video Widget](images/glimmer-video-widget.png)](https://github.com/AndyObtiva/glimmer-cw-video)
|
2494
|
-
|
2495
|
-
Glimmer supports a [video custom widget](https://github.com/AndyObtiva/glimmer-cw-video) not in SWT.
|
2496
|
-
|
2497
|
-
You may obtain via `glimmer-cw-video` gem.
|
2498
|
-
|
2499
|
-
#### Browser Widget
|
2500
|
-
|
2501
|
-
![Hello Browser](images/glimmer-hello-browser.png)
|
2502
|
-
|
2503
|
-
Glimmer supports the SWT Browser widget, which can load URLs or render HTML. It can even be instrumented with JavaScript when needed (though highly discouraged since it defeats the purpose of using Ruby except in very rare cases like leveraging a pre-existing web codebase in a desktop app).
|
2504
|
-
|
2505
|
-
Example loading a URL (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
2506
|
-
|
2507
|
-
```ruby
|
2508
|
-
shell {
|
2509
|
-
minimum_size 1024, 860
|
2510
|
-
browser {
|
2511
|
-
url 'http://brightonresort.com/about'
|
2512
|
-
}
|
2513
|
-
}.open
|
2514
|
-
```
|
2515
|
-
|
2516
|
-
Example rendering HTML with JavaScript on document ready (you may copy/paste in [`girb`](#girb-glimmer-irb-command) provided you install and require [glimmer-dsl-xml gem](https://github.com/AndyObtiva/glimmer-dsl-xml)):
|
2517
|
-
|
2518
|
-
```ruby
|
2519
|
-
shell {
|
2520
|
-
minimum_size 130, 130
|
2521
|
-
@browser = browser {
|
2522
|
-
text html {
|
2523
|
-
head {
|
2524
|
-
meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
|
2525
|
-
}
|
2526
|
-
body {
|
2527
|
-
h1 { "Hello, World!" }
|
2528
|
-
}
|
2529
|
-
}
|
2530
|
-
on_completed { # on load of the page execute this JavaScript
|
2531
|
-
@browser.swt_widget.execute("alert('Hello, World!');")
|
2532
|
-
}
|
2533
|
-
}
|
2534
|
-
}.open
|
2535
|
-
```
|
2536
|
-
|
2537
|
-
This relies on Glimmer's [Multi-DSL Support](#multi-dsl-support) for building the HTML text using [Glimmer XML DSL](https://github.com/AndyObtiva/glimmer-dsl-xml).
|
2538
|
-
|
2539
|
-
## Glimmer Configuration
|
2540
|
-
|
2541
|
-
Glimmer configuration may be done via the `Glimmer::Config` module.
|
2542
|
-
|
2543
|
-
### logger
|
2544
|
-
|
2545
|
-
Glimmer supports logging via a standard `STDOUT` Ruby `Logger` configured in the `Glimmer::Config.logger` config option.
|
2546
|
-
It is set to level Logger::ERROR by default.
|
2547
|
-
Log level may be adjusted via `Glimmer::Config.logger.level` just like any other Ruby Logger.
|
2548
|
-
It may be replaced with a custom logger via `Glimmer::Config.logger = custom_logger`
|
2549
|
-
All logging is done lazily via blocks (e.g. `logger.debug {message}`) to avoid affecting app performance with logging when below the configured logging level threshold.
|
2550
|
-
|
2551
|
-
Example:
|
2552
|
-
|
2553
|
-
```ruby
|
2554
|
-
Glimmer::Config.logger.level = :debug
|
2555
|
-
```
|
2556
|
-
This results in more verbose debug loggging to `STDOUT`, which is very helpful in troubleshooting Glimmer DSL syntax when needed.
|
2557
|
-
|
2558
|
-
Example log:
|
2559
|
-
```
|
2560
|
-
D, [2017-07-21T19:23:12.587870 #35707] DEBUG -- : method: shell and args: []
|
2561
|
-
D, [2017-07-21T19:23:12.594405 #35707] DEBUG -- : ShellCommandHandler will handle command: shell with arguments []
|
2562
|
-
D, [2017-07-21T19:23:12.844775 #35707] DEBUG -- : method: composite and args: []
|
2563
|
-
D, [2017-07-21T19:23:12.845388 #35707] DEBUG -- : parent is a widget: true
|
2564
|
-
D, [2017-07-21T19:23:12.845833 #35707] DEBUG -- : on listener?: false
|
2565
|
-
D, [2017-07-21T19:23:12.864395 #35707] DEBUG -- : WidgetCommandHandler will handle command: composite with arguments []
|
2566
|
-
D, [2017-07-21T19:23:12.864893 #35707] DEBUG -- : widget styles are: []
|
2567
|
-
D, [2017-07-21T19:23:12.874296 #35707] DEBUG -- : method: list and args: [:multi]
|
2568
|
-
D, [2017-07-21T19:23:12.874969 #35707] DEBUG -- : parent is a widget: true
|
2569
|
-
D, [2017-07-21T19:23:12.875452 #35707] DEBUG -- : on listener?: false
|
2570
|
-
D, [2017-07-21T19:23:12.878434 #35707] DEBUG -- : WidgetCommandHandler will handle command: list with arguments [:multi]
|
2571
|
-
D, [2017-07-21T19:23:12.878798 #35707] DEBUG -- : widget styles are: [:multi]
|
2572
|
-
```
|
2573
|
-
|
2574
|
-
### import_swt_packages
|
2575
|
-
|
2576
|
-
Glimmer automatically imports all SWT Java packages upon adding `include Glimmer`, `include Glimmer::UI::CustomWidget`, or `include Glimmer::UI::CustomShell` to a class or module. It relies on JRuby's `include_package` for lazy-importing upon first reference of a Java class.
|
2577
|
-
|
2578
|
-
As a result, you may call SWT Java classes from Glimmer Ruby code without mentioning Java package references explicitly.
|
2579
|
-
|
2580
|
-
For example, `org.eclipse.swt.graphics.Color` can be referenced as just `Color`
|
2581
|
-
|
2582
|
-
The Java packages imported come from the [`Glimmer::Config.import_swt_packages`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/lib/ext/glimmer/config.rb) config option, which defaults to `Glimmer::Config::DEFAULT_IMPORT_SWT_PACKAGES`, importing the following Java packages:
|
2583
|
-
```
|
2584
|
-
org.eclipse.swt.*
|
2585
|
-
org.eclipse.swt.widgets.*
|
2586
|
-
org.eclipse.swt.layout.*
|
2587
|
-
org.eclipse.swt.graphics.*
|
2588
|
-
org.eclipse.swt.browser.*
|
2589
|
-
org.eclipse.swt.custom.*
|
2590
|
-
org.eclipse.swt.dnd.*
|
2591
|
-
```
|
2592
|
-
|
2593
|
-
If you need to import additional Java packages as extra Glimmer widgets, you may add more packages to [`Glimmer::Config.import_swt_packages`](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/lib/ext/glimmer/config.rb) by using the `+=` operator (or alternatively limit to certain packages via `=` operator).
|
2594
|
-
|
2595
|
-
Example:
|
2596
|
-
|
2597
|
-
```ruby
|
2598
|
-
Glimmer::Config.import_swt_packages += [
|
2599
|
-
'org.eclipse.nebula.widgets.ganttchart'
|
2600
|
-
]
|
2601
|
-
```
|
2602
|
-
|
2603
|
-
Another alternative is to simply add a `java_import` call to your code (e.g. `java_import 'org.eclipse.nebula.widgets.ganttchart.GanttChart'`). Glimmer will automatically take advantage of it (e.g. when invoking `gantt_chart` keyword)
|
2604
|
-
|
2605
|
-
Nonetheless, you can disable automatic Java package import if needed via this Glimmer configuration option:
|
2606
|
-
|
2607
|
-
```ruby
|
2608
|
-
Glimmer::Config.import_swt_packages = false
|
2609
|
-
```
|
2610
|
-
|
2611
|
-
Once disabled, to import SWT Java packages manually, you may simply:
|
2612
|
-
|
2613
|
-
1. `include Glimmer::SWT::Packages`: lazily imports all SWT Java packages to your class, lazy-loading SWT Java class constants on first reference.
|
2614
|
-
|
2615
|
-
2. `java_import swt_package_class_string`: immediately imports a specific Java class where `swt_package_class_string` is the Java full package reference of a Java class (e.g. `java_import 'org.eclipse.swt.SWT'`)
|
2616
|
-
|
2617
|
-
Note: Glimmer relies on [`nested_imported_jruby_include_package`](https://github.com/AndyObtiva/nested_inherited_jruby_include_package), which automatically brings packages to nested-modules/nested-classes and sub-modules/sub-classes.
|
2618
|
-
|
2619
|
-
You can learn more about importing Java packages into Ruby code at this JRuby WIKI page:
|
2620
|
-
|
2621
|
-
https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby
|
2622
|
-
|
2623
|
-
### loop_max_count
|
2624
|
-
|
2625
|
-
Glimmer has infinite loop detection support.
|
2626
|
-
It can detect when an infinite loop is about to occur in method_missing and stops it.
|
2627
|
-
It detects potential infinite loops when the same keyword and args repeat more than 100 times, which is unusual in a GUI app.
|
2628
|
-
|
2629
|
-
The max limit can be changed via the `Glimmer::Config::loop_max_count=(count)` config option.
|
2630
|
-
|
2631
|
-
Infinite loop detection may be disabled altogether if needed by setting `Glimmer::Config::loop_max_count` to `-1`
|
2632
|
-
|
2633
|
-
### excluded_keyword_checkers
|
2634
|
-
|
2635
|
-
Glimmer permits consumers to exclude keywords from DSL processing by its engine via the `excluded_keyword_checkers` config option.
|
2636
|
-
|
2637
|
-
To do so, add a proc to it that returns a boolean indicating if a keyword is excluded or not.
|
2638
|
-
|
2639
|
-
Note that this proc runs within the context of the Glimmer object (as in the object mixing in the Glimmer module), so checker can can pretend to run there with its `self` object assumption.
|
2640
|
-
|
2641
|
-
Example of keywords excluded by [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt):
|
2642
|
-
|
2643
|
-
```ruby
|
2644
|
-
Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
|
2645
|
-
method = method_symbol.to_s
|
2646
|
-
result = false
|
2647
|
-
result ||= method.start_with?('on_swt_') && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
|
2648
|
-
result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
|
2649
|
-
result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
|
2650
|
-
result ||= method == 'post_initialize_child'
|
2651
|
-
result ||= method.end_with?('=')
|
2652
|
-
result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
|
2653
|
-
end
|
2654
|
-
```
|
2655
|
-
|
2656
|
-
## Glimmer Style Guide
|
2657
|
-
|
2658
|
-
- Widgets are declared with underscored lowercase versions of their SWT names minus the SWT package name.
|
2659
|
-
- Widget declarations may optionally have arguments and be followed by a block (to contain properties and content)
|
2660
|
-
- Widget blocks are always declared with curly braces
|
2661
|
-
- Widget arguments are always wrapped inside parentheses
|
2662
|
-
- Widget properties are declared with underscored lowercase versions of the SWT properties
|
2663
|
-
- Widget property declarations always have arguments and never take a block
|
2664
|
-
- Widget property arguments are never wrapped inside parentheses
|
2665
|
-
- Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
|
2666
|
-
- 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)
|
2667
|
-
- Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
|
2668
|
-
- Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
|
2669
|
-
- 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.
|
2670
|
-
|
2671
|
-
## SWT Reference
|
2672
|
-
|
2673
|
-
https://www.eclipse.org/swt/docs.php
|
2674
|
-
|
2675
|
-
Here is the SWT API:
|
2676
|
-
|
2677
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
|
2678
|
-
|
2679
|
-
Here is a visual list of SWT widgets:
|
2680
|
-
|
2681
|
-
https://www.eclipse.org/swt/widgets/
|
694
|
+
## Glimmer DSL for CSS
|
2682
695
|
|
2683
|
-
|
696
|
+
[Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) provides Ruby syntax for building CSS (Cascading Style Sheets).
|
2684
697
|
|
2685
|
-
https://
|
698
|
+
Within the context of [Glimmer](https://github.com/AndyObtiva/glimmer) app development, Glimmer DSL for CSS is useful in providing CSS for the [SWT Browser widget](https://github.com/AndyObtiva/glimmer/tree/master#browser-widget).
|
2686
699
|
|
2687
|
-
|
700
|
+
### CSS DSL
|
2688
701
|
|
2689
|
-
|
2690
|
-
|
2691
|
-
Here is a SWT style bit constant reference:
|
2692
|
-
|
2693
|
-
https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
|
2694
|
-
|
2695
|
-
Here is an SWT Drag and Drop guide:
|
2696
|
-
|
2697
|
-
https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
|
2698
|
-
|
2699
|
-
Here is an SWT Custom Widget guide:
|
2700
|
-
|
2701
|
-
https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm
|
2702
|
-
|
2703
|
-
## Samples
|
2704
|
-
|
2705
|
-
Check the [samples](samples) directory in [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) for examples on how to write Glimmer applications. To run a sample, make sure to install the `glimmer` gem first and then use the `glimmer` command to run it (alternatively, you may clone the repo, follow [CONTRIBUTING.md](CONTRIBUTING.md) instructions, and run samples locally with development glimmer command: `bin/glimmer`).
|
2706
|
-
|
2707
|
-
If you cloned the project and followed [CONTRIBUTING.md](CONTRIBUTING.md) instructions, you may run all samples in [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) at once via `samples/launch` command:
|
2708
|
-
|
2709
|
-
```
|
2710
|
-
samples/launch
|
2711
|
-
```
|
2712
|
-
|
2713
|
-
### Hello Samples
|
2714
|
-
|
2715
|
-
For hello-type simple samples, check the following.
|
2716
|
-
|
2717
|
-
#### Hello, World! Sample
|
2718
|
-
|
2719
|
-
Code:
|
2720
|
-
|
2721
|
-
[samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)
|
2722
|
-
|
2723
|
-
Run:
|
2724
|
-
|
2725
|
-
```
|
2726
|
-
glimmer samples/hello/hello_world.rb
|
2727
|
-
```
|
2728
|
-
|
2729
|
-
![Hello World](images/glimmer-hello-world.png)
|
2730
|
-
|
2731
|
-
#### Hello, Tab!
|
2732
|
-
|
2733
|
-
Code:
|
2734
|
-
|
2735
|
-
[samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_tab.rb)
|
2736
|
-
|
2737
|
-
Run:
|
2738
|
-
|
2739
|
-
```
|
2740
|
-
glimmer samples/hello/hello_tab.rb
|
2741
|
-
```
|
2742
|
-
|
2743
|
-
![Hello Tab English](images/glimmer-hello-tab-english.png)
|
2744
|
-
![Hello Tab French](images/glimmer-hello-tab-french.png)
|
2745
|
-
|
2746
|
-
#### Hello, Combo!
|
2747
|
-
|
2748
|
-
This sample demonstrates combo data-binding.
|
2749
|
-
|
2750
|
-
Code:
|
2751
|
-
|
2752
|
-
[samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_combo.rb)
|
2753
|
-
|
2754
|
-
Run:
|
2755
|
-
|
2756
|
-
```
|
2757
|
-
glimmer samples/hello/hello_combo.rb
|
2758
|
-
```
|
2759
|
-
|
2760
|
-
![Hello Combo](images/glimmer-hello-combo.png)
|
2761
|
-
![Hello Combo Expanded](images/glimmer-hello-combo-expanded.png)
|
2762
|
-
|
2763
|
-
#### Hello, List Single Selection!
|
2764
|
-
|
2765
|
-
This sample demonstrates list single-selection data-binding.
|
2766
|
-
|
2767
|
-
Code:
|
2768
|
-
|
2769
|
-
[samples/hello/hello_list_single_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_single_selection.rb)
|
2770
|
-
|
2771
|
-
Run:
|
2772
|
-
|
2773
|
-
```
|
2774
|
-
glimmer samples/hello/hello_list_single_selection.rb
|
2775
|
-
```
|
2776
|
-
|
2777
|
-
![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
|
2778
|
-
|
2779
|
-
#### Hello, List Multi Selection!
|
2780
|
-
|
2781
|
-
This sample demonstrates list multi-selection data-binding.
|
2782
|
-
|
2783
|
-
Code:
|
2784
|
-
|
2785
|
-
[samples/hello/hello_list_multi_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_multi_selection.rb)
|
2786
|
-
|
2787
|
-
Run:
|
2788
|
-
|
2789
|
-
```
|
2790
|
-
glimmer samples/hello/hello_list_multi_selection.rb
|
2791
|
-
```
|
2792
|
-
|
2793
|
-
![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
|
2794
|
-
|
2795
|
-
#### Hello, Computed!
|
2796
|
-
|
2797
|
-
This sample demonstrates computed data-binding.
|
2798
|
-
|
2799
|
-
Code:
|
2800
|
-
|
2801
|
-
[samples/hello/hello_computed.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_computed.rb)
|
2802
|
-
|
2803
|
-
Run:
|
2804
|
-
|
2805
|
-
```
|
2806
|
-
glimmer samples/hello/hello_computed.rb
|
2807
|
-
```
|
2808
|
-
|
2809
|
-
![Hello Browser](images/glimmer-hello-computed.png)
|
2810
|
-
|
2811
|
-
#### Hello, Message Box!
|
2812
|
-
|
2813
|
-
This sample demonstrates a `message_box` dialog.
|
2814
|
-
|
2815
|
-
Code:
|
2816
|
-
|
2817
|
-
[samples/hello/hello_message_box.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_message_box.rb)
|
2818
|
-
|
2819
|
-
Run:
|
2820
|
-
|
2821
|
-
```
|
2822
|
-
glimmer samples/hello/hello_message_box.rb
|
2823
|
-
```
|
2824
|
-
|
2825
|
-
![Hello Message Box](images/glimmer-hello-message-box.png)
|
2826
|
-
![Hello Message Box Dialog](images/glimmer-hello-message-box-dialog.png)
|
2827
|
-
|
2828
|
-
#### Hello, Browser!
|
2829
|
-
|
2830
|
-
This sample demonstrates the `browser` widget.
|
2831
|
-
|
2832
|
-
Code:
|
2833
|
-
|
2834
|
-
[samples/hello/hello_browser.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_browser.rb)
|
2835
|
-
|
2836
|
-
Run:
|
2837
|
-
|
2838
|
-
```
|
2839
|
-
glimmer samples/hello/hello_browser.rb
|
2840
|
-
```
|
2841
|
-
|
2842
|
-
![Hello Browser](images/glimmer-hello-browser.png)
|
2843
|
-
|
2844
|
-
#### Hello, Drag and Drop!
|
2845
|
-
|
2846
|
-
This sample demonstrates drag and drop in Glimmer.
|
2847
|
-
|
2848
|
-
Code:
|
2849
|
-
|
2850
|
-
[samples/hello/hello_drag_and_drop.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_drag_and_drop.rb)
|
2851
|
-
|
2852
|
-
Run:
|
2853
|
-
|
2854
|
-
```
|
2855
|
-
glimmer samples/hello/hello_drag_and_drop.rb
|
2856
|
-
```
|
2857
|
-
|
2858
|
-
![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
|
2859
|
-
|
2860
|
-
#### Hello, Menu Bar!
|
2861
|
-
|
2862
|
-
This sample demonstrates menus in Glimmer.
|
2863
|
-
|
2864
|
-
Code:
|
2865
|
-
|
2866
|
-
[samples/hello/hello_menu_bar.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_menu_bar.rb)
|
2867
|
-
|
2868
|
-
Run:
|
2869
|
-
|
2870
|
-
```
|
2871
|
-
glimmer samples/hello/hello_menu_bar.rb
|
2872
|
-
```
|
2873
|
-
|
2874
|
-
![Hello Menu Bar](images/glimmer-hello-menu-bar.png)
|
2875
|
-
![Hello Menu Bar File Menu](images/glimmer-hello-menu-bar-file-menu.png)
|
2876
|
-
![Hello Menu Bar History Menu](images/glimmer-hello-menu-bar-history-menu.png)
|
2877
|
-
|
2878
|
-
#### Hello, Pop Up Context Menu!
|
2879
|
-
|
2880
|
-
This sample demonstrates pop up context menus in Glimmer.
|
2881
|
-
|
2882
|
-
Code:
|
2883
|
-
|
2884
|
-
[samples/hello/hello_pop_up_context_menu.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_pop_up_context_menu.rb)
|
2885
|
-
|
2886
|
-
Run:
|
2887
|
-
|
2888
|
-
```
|
2889
|
-
glimmer samples/hello/hello_pop_up_context_menu.rb
|
2890
|
-
```
|
2891
|
-
|
2892
|
-
![Hello Pop Up Context Menu](images/glimmer-hello-pop-up-context-menu.png)
|
2893
|
-
![Hello Pop Up Context Menu Popped Up](images/glimmer-hello-pop-up-context-menu-popped-up.png)
|
2894
|
-
|
2895
|
-
### Elaborate Samples
|
2896
|
-
|
2897
|
-
For more elaborate samples, check the following:
|
2898
|
-
|
2899
|
-
#### Login
|
2900
|
-
|
2901
|
-
This sample demonstrates basic data-binding, password and text fields, and field enablement data-binding.
|
2902
|
-
|
2903
|
-
Code:
|
2904
|
-
|
2905
|
-
[samples/elaborate/login.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/login.rb)
|
2906
|
-
|
2907
|
-
Run:
|
2908
|
-
|
2909
|
-
```
|
2910
|
-
glimmer samples/elaborate/login.rb
|
2911
|
-
```
|
2912
|
-
|
2913
|
-
![Login](images/glimmer-login.png)
|
2914
|
-
![Login Filled In](images/glimmer-login-filled-in.png)
|
2915
|
-
![Login Logged In](images/glimmer-login-logged-in.png)
|
2916
|
-
|
2917
|
-
#### Tic Tac Toe Sample
|
2918
|
-
|
2919
|
-
This sample demonstrates a full MVC application, including GUI layout, text and enablement data-binding, and test-driven development (has [specs](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/spec/samples/elaborate/tic_tac_toe/board_spec.rb)).
|
2920
|
-
|
2921
|
-
Code:
|
2922
|
-
|
2923
|
-
[samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)
|
2924
|
-
|
2925
|
-
Run:
|
2926
|
-
|
2927
|
-
```
|
2928
|
-
glimmer samples/elaborate/tic_tac_toe.rb
|
2929
|
-
```
|
2930
|
-
|
2931
|
-
![Tic Tac Toe](images/glimmer-tic-tac-toe.png)
|
2932
|
-
![Tic Tac Toe In Progress](images/glimmer-tic-tac-toe-in-progress.png)
|
2933
|
-
![Tic Tac Toe Game Over](images/glimmer-tic-tac-toe-game-over.png)
|
2934
|
-
|
2935
|
-
#### Contact Manager Sample
|
2936
|
-
|
2937
|
-
This sample demonstrates table data-binding, sorting, filtering, GUI layout, MVP pattern, and test-driven development (has [specs](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/spec/samples/elaborate/contact_manager/contact_manager_presenter_spec.rb)).
|
2938
|
-
|
2939
|
-
Code:
|
2940
|
-
|
2941
|
-
[samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)
|
2942
|
-
|
2943
|
-
Run:
|
2944
|
-
|
2945
|
-
```
|
2946
|
-
glimmer samples/elaborate/contact_manager.rb
|
2947
|
-
```
|
2948
|
-
|
2949
|
-
Contact Manager
|
2950
|
-
|
2951
|
-
![Contact Manager](images/glimmer-contact-manager.png)
|
2952
|
-
|
2953
|
-
Contact Manager - Find
|
2954
|
-
|
2955
|
-
![Contact Manager](images/glimmer-contact-manager-find.png)
|
2956
|
-
|
2957
|
-
Contact Manager - Edit Started
|
2958
|
-
|
2959
|
-
![Contact Manager](images/glimmer-contact-manager-edit-started.png)
|
2960
|
-
|
2961
|
-
Contact Manager - Edit In Progress
|
2962
|
-
|
2963
|
-
![Contact Manager](images/glimmer-contact-manager-edit-in-progress.png)
|
2964
|
-
|
2965
|
-
Contact Manager - Edit Done
|
2966
|
-
|
2967
|
-
![Contact Manager](images/glimmer-contact-manager-edit-done.png)
|
2968
|
-
|
2969
|
-
### External Samples
|
2970
|
-
|
2971
|
-
#### Glimmer Calculator
|
2972
|
-
|
2973
|
-
[<img alt="Glimmer Calculator Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-icon.png" height=40 /> 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).
|
2974
|
-
|
2975
|
-
[<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-calculator)
|
2976
|
-
|
2977
|
-
#### Gladiator
|
2978
|
-
|
2979
|
-
[<img src='https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-cs-gladiator-logo.svg' height=40 /> Gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator) (short for Glimmer Editor) is a Glimmer sample project under on-going development.
|
2980
|
-
You may check it out to learn how to build a Glimmer Custom Shell gem.
|
2981
|
-
|
2982
|
-
[<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-gladiator.png" />](https://github.com/AndyObtiva/glimmer-cs-gladiator)
|
2983
|
-
|
2984
|
-
Gladiator is a good demonstration of:
|
2985
|
-
- MVP Pattern
|
2986
|
-
- Tree data-binding
|
2987
|
-
- List data-binding
|
2988
|
-
- Text selection data-binding
|
2989
|
-
- Tabs
|
2990
|
-
- Context menus
|
2991
|
-
- Custom Shell
|
2992
|
-
- Custom widget
|
2993
|
-
|
2994
|
-
## In Production
|
2995
|
-
|
2996
|
-
The following production apps have been built with Glimmer.
|
2997
|
-
|
2998
|
-
### Math Bowling
|
2999
|
-
|
3000
|
-
[<img alt="Math Bowling Logo" src="https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/images/math-bowling-logo.png" width="40" />Math Bowling](https://github.com/AndyObtiva/MathBowling): an educational math game for elementary level kids
|
3001
|
-
|
3002
|
-
### Are We There Yet?
|
3003
|
-
|
3004
|
-
[<img alt="Are We There Yet Logo" src="https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-logo.svg" width="40" />Are We There Yet?](https://github.com/AndyObtiva/are-we-there-yet): A tool that helps you learn when your small projects will finish
|
3005
|
-
|
3006
|
-
If you have a Glimmer app you would like referenced here, please mention in a Pull Request.
|
3007
|
-
|
3008
|
-
## Packaging & Distribution
|
3009
|
-
|
3010
|
-
Glimmer apps may be packaged and distributed on the Mac, Windows, and Linux via these tools:
|
3011
|
-
- Warbler (https://github.com/jruby/warbler): Enables bundling a Glimmer app into a JAR file
|
3012
|
-
- 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.
|
3013
|
-
|
3014
|
-
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](#scaffolding):
|
3015
|
-
|
3016
|
-
```
|
3017
|
-
glimmer package
|
3018
|
-
```
|
3019
|
-
|
3020
|
-
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`.
|
3021
|
-
JAR file name will match your application local directory name (e.g. `MathBowling.jar` for `~/code/MathBowling`)
|
3022
|
-
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)
|
3023
|
-
|
3024
|
-
The `glimmer package` command will automatically set "mac.CFBundleIdentifier" to ="org.#{project_name}.application.#{project_name}".
|
3025
|
-
You may override by configuring as an extra argument for javapackger (e.g. Glimmer::Package.javapackager_extra_args = " -Bmac.CFBundleIdentifier=org.andymaleh.application.MathBowling")
|
3026
|
-
|
3027
|
-
### Packaging Defaults
|
3028
|
-
|
3029
|
-
Glimmer employs smart defaults in packaging.
|
3030
|
-
|
3031
|
-
The package application name (shows up in top menu bar on the Mac) will be a human form of the app root directory name (e.g. "Math Bowling" for "MathBowling" or "math_bowling" app root directory name). However, application name and version may be specified explicitly via "-Bmac.CFBundleName" and "-Bmac.CFBundleVersion" options.
|
3032
|
-
|
3033
|
-
Also, the package will only include these directories: app, config, db, lib, script, bin, docs, fonts, images, sounds, videos
|
3034
|
-
|
3035
|
-
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:
|
3036
|
-
|
3037
|
-
```
|
3038
|
-
glimmer package:config
|
3039
|
-
```
|
3040
|
-
|
3041
|
-
This will generate `config/warble.rb`, which you may configure and then run `glimmer package` afterwards.
|
3042
|
-
|
3043
|
-
### Packaging Configuration
|
3044
|
-
|
3045
|
-
- Ensure you have a Ruby script under `bin` directory that launches the application, preferably matching your project directory name (e.g. `bin/math_bowling`) :
|
3046
|
-
```ruby
|
3047
|
-
require_relative '../app/my_application.rb'
|
3048
|
-
```
|
3049
|
-
- 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/)
|
3050
|
-
- Include Version (Optional): Create a `VERSION` file in your application and fill it your app version on one line (e.g. `1.1.0`)
|
3051
|
-
- 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).
|
3052
|
-
- 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).
|
3053
|
-
|
3054
|
-
### javapackager Extra Arguments
|
702
|
+
Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
|
703
|
+
Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
|
3055
704
|
|
3056
|
-
|
3057
|
-
- https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
|
3058
|
-
- https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html
|
3059
|
-
- https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html#BCGICFDB
|
3060
|
-
- https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html
|
3061
|
-
- https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMacAppStore/index.html#//apple_ref/doc/uid/TP40010572-CH16-SW8
|
705
|
+
`css` is the only top-level keyword in the Glimmer CSS DSL
|
3062
706
|
|
3063
|
-
|
3064
|
-
|
3065
|
-
- Environment variable: `JAVAPACKAGER_EXTRA_ARGS`
|
707
|
+
Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
|
708
|
+
Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
|
3066
709
|
|
3067
|
-
Example
|
710
|
+
Example:
|
3068
711
|
|
3069
712
|
```ruby
|
3070
|
-
|
3071
|
-
|
3072
|
-
|
3073
|
-
|
3074
|
-
|
3075
|
-
|
3076
|
-
|
3077
|
-
|
3078
|
-
|
3079
|
-
|
3080
|
-
|
3081
|
-
|
3082
|
-
JAVAPACKAGER_EXTRA_ARGS='-Bmac.CFBundleName="Math Bowling Game"' glimmer package
|
3083
|
-
```
|
3084
|
-
|
3085
|
-
That overrides the default application display name.
|
3086
|
-
|
3087
|
-
### Mac Application Distribution
|
3088
|
-
|
3089
|
-
Recent macOS versions (starting with Catalina) have very stringent security requirements requiring all applications to be signed before running (unless the user goes to System Preferences -> Privacy -> General tab and clicks "Open Anyway" after failing to open application the first time they run it). So, to release a desktop application on the Mac, it is recommended to enroll in the [Apple Developer Program](https://developer.apple.com/programs/) to distribute on the [Mac App Store](https://developer.apple.com/distribute/) or otherwise request [app notarization from Apple](https://developer.apple.com/documentation/xcode/notarizing_macos_software_before_distribution) to distribute independently.
|
3090
|
-
|
3091
|
-
Afterwards, you may add developer-id/signing-key arguments to `javapackager` via `Glimmer::Package.javapackager_extra_args` or `JAVAPACKAGER_EXTRA_ARGS` according to this webpage: https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
|
3092
|
-
|
3093
|
-
DMG signing key argument:
|
3094
|
-
```
|
3095
|
-
-Bmac.signing-key-developer-id-app="..."
|
713
|
+
@css = css {
|
714
|
+
body {
|
715
|
+
font_size '1.1em'
|
716
|
+
pv 'background', 'white'
|
717
|
+
}
|
718
|
+
|
719
|
+
s('body > h1') {
|
720
|
+
background_color :red
|
721
|
+
pv 'font-size', '2em'
|
722
|
+
}
|
723
|
+
}
|
724
|
+
puts @css
|
3096
725
|
```
|
3097
726
|
|
3098
|
-
|
3099
|
-
```
|
3100
|
-
-Bmac.signing-key-developer-id-installer="..."
|
3101
|
-
```
|
727
|
+
Output:
|
3102
728
|
|
3103
|
-
Mac App Store signing key arguments:
|
3104
729
|
```
|
3105
|
-
-
|
3106
|
-
-Bmac.signing-key-pkg="..."
|
3107
|
-
```
|
3108
|
-
|
3109
|
-
### Self Signed Certificate
|
3110
|
-
|
3111
|
-
You may still release a signed DMG file without enrolling into the Apple Developer Program with the caveat that users will always fail in opening the app the first time, and have to go to System Preferences -> Privacy -> General tab to "Open Anyway".
|
3112
|
-
|
3113
|
-
To do so, you may follow these steps (abbreviated version from https://developer.apple.com/library/archive/documentation/Security/Conceptual/CodeSigningGuide/Procedures/Procedures.html#//apple_ref/doc/uid/TP40005929-CH4-SW2):
|
3114
|
-
- Open Keychain Access
|
3115
|
-
- Choose Keychain Access > Certificate Assistant > Create Certificate ...
|
3116
|
-
- Enter Name (referred to below as "CertificateName")
|
3117
|
-
- Set 'Certificate Type' to 'Code Signing'
|
3118
|
-
- Create (if you alternatively override defaults, make sure to enable all capabilities)
|
3119
|
-
- Add the following option to javapackager: `-Bmac.signing-key-developer-id-app="CertificateName"` via `Glimmer::Package.javapackager_extra_args` or `JAVAPACKAGER_EXTRA_ARGS`
|
3120
|
-
|
3121
|
-
Example:
|
3122
|
-
|
3123
|
-
```ruby
|
3124
|
-
Glimmer::Package.javapackager_extra_args = '-Bmac.signing-key-developer-id-app="Andy Maleh"'
|
730
|
+
body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}
|
3125
731
|
```
|
3126
732
|
|
3127
|
-
|
733
|
+
## Multi-DSL Support
|
3128
734
|
|
3129
|
-
|
735
|
+
Glimmer official DSL gems always start with `glimmer-dsl-`. That said, other libraries may use the Glimmer DSL engine too not for building GUI apps (e.g. `bundler-download`)
|
3130
736
|
|
3131
|
-
|
737
|
+
Glimmer allows mixing DSLs, which comes in handy when doing things like using a desktop Browser widget with HTML and CSS.
|
3132
738
|
|
3133
|
-
|
3134
|
-
|
739
|
+
Glimmer DSL syntax consists mainly of:
|
740
|
+
- keywords (e.g. `table` for a table widget)
|
741
|
+
- style/args (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
|
742
|
+
- content (e.g. `{ table_column { text 'Name'} }` as in `table(:multi) { table_column { text 'name'} }` for a multi-line selection table widget with a table column having header text property `'Name'` as content)
|
3135
743
|
|
3136
|
-
|
744
|
+
DSLs are activated by specific keywords. For example, the `html` keyword activates the Glimmer DSL for XML. Glimmer automatically recognizes top-level keywords in each DSL and activates the DSL accordingly. Once done processing a nested DSL top-level keyword, Glimmer switches back to the prior DSL automatically.
|
3137
745
|
|
3138
|
-
|
3139
|
-
Glimmer::Package.javapackager_extra_args = '-srcfiles "ACME.txt" -BlicenseFile="ACME.txt" -BlicenseType="ACME"'
|
3140
|
-
```
|
746
|
+
## Glimmer Supporting Libraries
|
3141
747
|
|
3142
|
-
|
748
|
+
Here is a list of notable 3rd party gems used by Glimmer and Glimmer DSLs:
|
749
|
+
- [jeweler](https://github.com/technicalpickles/jeweler): generates app gems during [Glimmer Scaffolding](https://github.com/AndyObtiva/glimmer-dsl-swt#scaffolding)
|
750
|
+
- [logging](https://github.com/TwP/logging): provides extra logging capabilities not available in Ruby Logger such as multi-threaded buffered asynchronous logging (to avoid affecting app performance) and support for multiple appenders such as stdout, syslog, and log files (the last one is needed on Windows where syslog is not supported)
|
751
|
+
- [nested_inherited_jruby_include_package](https://github.com/AndyObtiva/nested_inherited_jruby_include_package): makes included [SWT](https://www.eclipse.org/swt/)/[Java](https://www.java.com/en/) packages available to all classes/modules that mix in the Glimmer module without having to manually reimport
|
752
|
+
- [os](https://github.com/rdp/os): provides OS detection capabilities (e.g. `OS.mac?` or `OS.windows?`) to write cross-platform code inexpensively
|
753
|
+
- [puts_debuggerer](https://github.com/AndyObtiva/puts_debuggerer): helps in troubleshooting when adding `require 'pd'` and using the `pd` command instead of `puts` or `p` (also `#pd_inspect` or `#pdi` instead of `#inspect`)
|
754
|
+
- [rake](https://github.com/ruby/rake): used to implement and execute `glimmer` commands
|
755
|
+
- [rake-tui](https://github.com/AndyObtiva/rake-tui): Rake Text-based User Interface. Allows navigating rake tasks with arrow keys and filtering task list by typing to quickly find an run a rake task.
|
756
|
+
- [super_module](https://github.com/AndyObtiva/super_module): used to cleanly write the Glimmer::UI:CustomWidget and Glimmer::UI::CustomShell modules
|
757
|
+
- [text-table](https://github.com/aptinio/text-table): renders textual data in a textual table for the command-line interface of Glimmer
|
758
|
+
- [warbler](https://github.com/jruby/warbler): converts a Glimmer app into a Java JAR file during packaging
|
3143
759
|
|
3144
|
-
|
760
|
+
## Glimmer Process
|
3145
761
|
|
3146
|
-
|
762
|
+
[Glimmer Process](PROCESS.md) is the lightweight software development process used for building Glimmer libraries and Glimmer apps, which goes beyond Agile, rendering all Agile processes obsolete. [Glimmer Process](PROCESS.md) is simply made up of 7 guidelines to pick and choose as necessary until software development needs are satisfied.
|
3147
763
|
|
3148
|
-
|
3149
|
-
Exec failed with code 2 command [[/usr/bin/SetFile, -c, icnC, /var/folders/4_/g1sw__tx6mjdgyh3mky7vydc0000gp/T/fxbundler4076750801763032201/images/MathBowling/.VolumeIcon.icns] in unspecified directory
|
3150
|
-
```
|
764
|
+
Learn more by reading the [GPG](PROCESS.md) (Glimmer Process Guidelines)
|
3151
765
|
|
3152
766
|
## Resources
|
3153
767
|
|
@@ -3190,13 +804,14 @@ Glimmer DSL Engine specific tasks are at:
|
|
3190
804
|
|
3191
805
|
**Contributors Wanted!**
|
3192
806
|
|
3193
|
-
If you would like to contribute to Glimmer, please study up on Glimmer and [SWT](#swt-reference), run all Glimmer [samples](#samples), and build a small sample app (perhaps from [this TODO list](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/TODO.md#samples)) to add to [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) Hello or Elaborate samples via a Pull Request. Once done, contact me on [Chat](#chat).
|
807
|
+
If you would like to contribute to Glimmer, please study up on Glimmer and [SWT](https://github.com/AndyObtiva/glimmer-dsl-swt#swt-reference), run all Glimmer [samples](https://github.com/AndyObtiva/glimmer-dsl-swt#samples), and build a small sample app (perhaps from [this TODO list](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/TODO.md#samples)) to add to [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) Hello or Elaborate samples via a Pull Request. Once done, contact me on [Chat](#chat).
|
3194
808
|
|
3195
809
|
You may apply for contributing to any of these Glimmer DSL gems whether you prefer to focus on the desktop or web:
|
3196
|
-
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
|
810
|
+
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Library)
|
811
|
+
- [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
|
3197
812
|
- [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
|
3198
813
|
- [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
|
3199
|
-
- [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
|
814
|
+
- [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
|
3200
815
|
|
3201
816
|
[CONTRIBUTING.md](CONTRIBUTING.md)
|
3202
817
|
|
@@ -3213,10 +828,9 @@ If your company would like to invest fulltime in further development of the Glim
|
|
3213
828
|
|
3214
829
|
## License
|
3215
830
|
|
3216
|
-
[MIT](
|
831
|
+
[MIT](LICENSE.txt)
|
3217
832
|
|
3218
833
|
Copyright (c) 2007-2020 - Andy Maleh.
|
3219
|
-
See [LICENSE.txt](LICENSE.txt) for further details.
|
3220
834
|
|
3221
835
|
--
|
3222
836
|
|