glimmer 0.10.3 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,35 @@
1
+ # Glimmer Process (Beyond Agile)
2
+
3
+ **Glimmer Process** is the lightweight software development process used for building [Glimmer](https://github.com/AndyObtiva/glimmer) libraries and [Glimmer](https://github.com/AndyObtiva/glimmer) apps, which goes beyond Agile, rendering all Agile processes obsolete. **Glimmer Process** is simply made up of 7 guidelines to pick and choose as necessary until software development needs are satisfied. Not all guidelines need to be incorporated into every project, but it is still important to think through every one of them before ruling any out. Guidelines can be followed in any order.
4
+
5
+ ## GPG (Glimmer Process Guidelines):
6
+ 1. **Requirements Gathering**: Spend no more than a few hours writing the initial requirements you know from the business owner, gathering missing requirements, and planning to elicit more requirements from users, customers, and stakeholders. Requirements are not set in stone, but serve as a good starting point in understanding what a project is about. After initial release, only document small tasks going forward.
7
+ 2. **Software Architecture and Design Diagrams**: Perform software architecture and design activities as necessary by analyzing requirements and creating diagrams.
8
+ 3. **Initial Release Plan**: This guideline's motto is "Plans are Nothing. Planning is Everything" (said by Dwight D. Eisenhower) because the initial release plan is not set in stone and might change completely, but is still invaluable in launching a new project forward. Consider including alpha releases (for internal testing) and beta releases (for customer testing).
9
+ 4. **Small Releases**: Develop and release in small increments. Do not release more than 3-weeks worth of work, preferring releases that are shorter than a week worth of work whenever possible. Break releases down. If you need to support multiple platforms, release for a platform at a time. If a feature includes a number of other features, then release them one by one instead of all at once. If a feature involves multiple options, release the default version first, and then release extra options later.
10
+ 5. **Usability Testing**: Make sure to observe a user using your software the first few releases or when releasing critical brand new unproven features. Ask them to complete a list of goals using the software, but do not help them to complete them unless they get very stuck. Write down notes silently while observing them use your software. Once done with usability testing, you may then ask further questions about any of the notes you wrote down, and afterwards add the notes as feature enhancements and bug fixes. Implement them and then do another usability test with the improvements in place. Repeat as necessary only.
11
+ 6. **Automated Tests**: Cover sensitive parts of the code with automated tests at the appropriate level of testing needed. Run tests in a continuous integration server.
12
+ 7. **Refactoring**: Simplify code, eliminate code duplication, improve code organization, extract reusable components, and extract reusable libraries at every opportunity possible.
13
+
14
+ --
15
+
16
+ Copyright (c) 2020 Andy Maleh
17
+
18
+ Permission is hereby granted, free of charge, to any person obtaining
19
+ a copy of this software and associated documentation files (the
20
+ "Software"), to deal in the Software without restriction, including
21
+ without limitation the rights to use, copy, modify, merge, publish,
22
+ distribute, sublicense, and/or sell copies of the Software, and to
23
+ permit persons to whom the Software is furnished to do so, subject to
24
+ the following conditions:
25
+
26
+ The above copyright notice and this permission notice shall be
27
+ included in all copies or substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
30
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
31
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
32
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
33
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
34
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
35
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,6 +1,6 @@
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 (Ruby Desktop Development GUI Library)
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.3 - DSL Framework
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer.svg)](http://badge.fury.io/rb/glimmer)
3
- [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer)
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
5
  [![Maintainability](https://api.codeclimate.com/v1/badges/38fbc278022862794414/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer/maintainability)
6
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)
@@ -9,22 +9,67 @@
9
9
 
10
10
  (The Original Glimmer Library Since 2007. Beware of Imitators!)
11
11
 
12
- [**Glimmer**](https://rubygems.org/gems/glimmer) 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](#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](https://rubygems.org/gems/glimmer) additionally innovates by having built-in [data-binding](#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](https://rubygems.org/gems/glimmer) offers [scaffolding](#scaffolding) options for [Apps](#in-production), [Gems](#custom-shell-gem), and [Custom Widgets](#custom-widgets). [Glimmer](https://rubygems.org/gems/glimmer) also includes native-executable [packaging](#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). Unlike libraries like TK, [Glimmer](https://rubygems.org/gems/glimmer) does not require recompilation of [Ruby](https://www.ruby-lang.org/en/). [Glimmer](https://rubygems.org/gems/glimmer) runs native GUI out of the box on every platform thanks to the [JVM](https://java.com/en/download/faq/whatis_java.xml), [JRuby](https://www.jruby.org/), and the [Eclipse SWT library](https://www.eclipse.org/swt/).
12
+ [**Glimmer**](https://rubygems.org/gems/glimmer) is a DSL Framework that consists of:
13
+ - DSL Engine: enables building DSLs for desktop GUI, XML/HTML documents, CSS styling, and webification of desktop apps.
14
+ - Data-Binding/Observer/Observable Library: enables synchronizing GUI with Model Attributes bidirectionally.
13
15
 
14
- NOTE: Glimmer is in beta mode. Please help make better by [contributing](#contributing), adopting for small or low risk projects, and providing feedback. While a lot of hard work has gone into it, it is by no means polished, and definitely has bugs waiting to be reported. The more feedback and issues you report the better.
16
+ [**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)
15
17
 
16
- [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
17
- Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
18
-
19
- Glimmer DSL gems:
20
- - [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)
21
21
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
22
22
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
23
23
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
24
24
 
25
- ## Examples
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.3](#-glimmer-103)
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
+ - [Glimmer Calculator](#glimmer-calculator)
48
+ - [Glimmer DSL for XML (& HTML)](#glimmer-dsl-for-xml--html)
49
+ - [XML DSL](#xml-dsl)
50
+ - [Glimmer DSL for CSS](#glimmer-dsl-for-css)
51
+ - [CSS DSL](#css-dsl)
52
+ - [Multi-DSL Support](#multi-dsl-support)
53
+ - [Glimmer Supporting Libraries](#glimmer-supporting-libraries)
54
+ - [Glimmer Process](#glimmer-process)
55
+ - [Resources](#resources)
56
+ - [Help](#help)
57
+ - [Issues](#issues)
58
+ - [Chat](#chat)
59
+ - [Feature Suggestions](#feature-suggestions)
60
+ - [Change Log](#change-log)
61
+ - [Contributing](#contributing)
62
+ - [Contributors](#contributors)
63
+ - [Hire Me](#hire-me)
64
+ - [License](#license)
65
+
66
+ ## Glimmer DSL for SWT (JRuby Desktop Development GUI Library)
67
+
68
+ [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).
69
+
70
+ ### Glimmer DSL for SWT Samples
26
71
 
27
- ### Hello, World!
72
+ #### Hello, World!
28
73
 
29
74
  Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)):
30
75
  ```ruby
@@ -40,21 +85,24 @@ shell {
40
85
 
41
86
  Run:
42
87
  ```
43
- glimmer samples/hello/hello_world.rb
88
+ glimmer sample:run[hello_world]
44
89
  ```
45
90
 
46
91
  Glimmer app:
47
92
 
48
93
  ![Hello World](images/glimmer-hello-world.png)
49
94
 
50
- ### Tic Tac Toe
95
+ #### Tic Tac Toe
51
96
 
52
97
  Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)):
53
98
 
54
99
  ```ruby
55
100
  # ...
101
+ @tic_tac_toe_board = Board.new
102
+
56
103
  @shell = shell {
57
104
  text "Tic-Tac-Toe"
105
+ minimum_size 150, 178
58
106
  composite {
59
107
  grid_layout 3, true
60
108
  (1..3).each { |row|
@@ -63,6 +111,7 @@ Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObt
63
111
  layout_data :fill, :fill, true, true
64
112
  text bind(@tic_tac_toe_board[row, column], :sign)
65
113
  enabled bind(@tic_tac_toe_board[row, column], :empty)
114
+ font style: :bold, height: 20
66
115
  on_widget_selected {
67
116
  @tic_tac_toe_board.mark(row, column)
68
117
  }
@@ -71,20 +120,25 @@ Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObt
71
120
  }
72
121
  }
73
122
  }
123
+
124
+ observe(@tic_tac_toe_board, :game_status) { |game_status|
125
+ display_win_message if game_status == Board::WIN
126
+ display_draw_message if game_status == Board::DRAW
127
+ }
74
128
  # ...
75
129
  ```
76
130
 
77
131
  Run:
78
132
 
79
133
  ```
80
- glimmer samples/elaborate/tic_tac_toe.rb
134
+ glimmer sample:run[tic_tac_toe]
81
135
  ```
82
136
 
83
137
  Glimmer app:
84
138
 
85
139
  ![Tic Tac Toe](images/glimmer-tic-tac-toe-in-progress.png)
86
140
 
87
- ### Contact Manager
141
+ #### Contact Manager
88
142
 
89
143
  Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)):
90
144
 
@@ -93,41 +147,76 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
93
147
  shell {
94
148
  text "Contact Manager"
95
149
  composite {
96
- composite {
97
- grid_layout 2, false
98
- label {text "First &Name: "}
150
+ group {
151
+ grid_layout(2, false) {
152
+ margin_width 0
153
+ margin_height 0
154
+ }
155
+ layout_data :fill, :center, true, false
156
+ text 'Lookup Contacts'
157
+ font height: 24
158
+
159
+ label {
160
+ layout_data :right, :center, false, false
161
+ text "First &Name: "
162
+ font height: 16
163
+ }
99
164
  text {
165
+ layout_data :fill, :center, true, false
100
166
  text bind(@contact_manager_presenter, :first_name)
101
167
  on_key_pressed {|key_event|
102
168
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
103
169
  }
104
170
  }
105
- label {text "&Last Name: "}
171
+
172
+ label {
173
+ layout_data :right, :center, false, false
174
+ text "&Last Name: "
175
+ font height: 16
176
+ }
106
177
  text {
178
+ layout_data :fill, :center, true, false
107
179
  text bind(@contact_manager_presenter, :last_name)
108
180
  on_key_pressed {|key_event|
109
181
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
110
182
  }
111
183
  }
112
- label {text "&Email: "}
184
+
185
+ label {
186
+ layout_data :right, :center, false, false
187
+ text "&Email: "
188
+ font height: 16
189
+ }
113
190
  text {
191
+ layout_data :fill, :center, true, false
114
192
  text bind(@contact_manager_presenter, :email)
115
193
  on_key_pressed {|key_event|
116
194
  @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
117
195
  }
118
196
  }
197
+
119
198
  composite {
120
- grid_layout 2, false
199
+ row_layout {
200
+ margin_width 0
201
+ margin_height 0
202
+ }
203
+ layout_data(:right, :center, false, false) {
204
+ horizontal_span 2
205
+ }
206
+
121
207
  button {
122
208
  text "&Find"
123
- on_widget_selected {
124
- @contact_manager_presenter.find
209
+ on_widget_selected { @contact_manager_presenter.find }
210
+ on_key_pressed {|key_event|
211
+ @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
125
212
  }
126
213
  }
214
+
127
215
  button {
128
216
  text "&List All"
129
- on_widget_selected {
130
- @contact_manager_presenter.list
217
+ on_widget_selected { @contact_manager_presenter.list }
218
+ on_key_pressed {|key_event|
219
+ @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
131
220
  }
132
221
  }
133
222
  }
@@ -154,7 +243,7 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
154
243
  width 200
155
244
  }
156
245
  items bind(@contact_manager_presenter, :results),
157
- column_properties(:first_name, :last_name, :email)
246
+ column_properties(:first_name, :last_name, :email)
158
247
  on_mouse_up { |event|
159
248
  table_proxy.edit_table_item(event.table_item, event.column_index)
160
249
  }
@@ -167,3329 +256,486 @@ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/And
167
256
  Run:
168
257
 
169
258
  ```
170
- glimmer samples/elaborate/contact_manager.rb
259
+ glimmer sample:run[contact_manager]
171
260
  ```
172
261
 
173
262
  Glimmer App:
174
263
 
175
264
  ![Contact Manager](images/glimmer-contact-manager.png)
176
265
 
177
- ## Table of contents
178
-
179
- - [Glimmer (Ruby Desktop Development GUI Library)](#-glimmer-ruby-desktop-development-gui-library)
180
- - [Examples](#examples)
181
- - [Hello, World!](#hello-world)
182
- - [Tic Tac Toe](#tic-tac-toe)
183
- - [Background](#background)
184
- - [Platform Support](#platform-support)
185
- - [Pre-requisites](#pre-requisites)
186
- - [Setup](#setup)
187
- - [Option 1: Direct Install](#option-1-direct-install)
188
- - [Option 2: Bundler](#option-2-bundler)
189
- - [Glimmer Command](#glimmer-command)
190
- - [Basic Usage](#basic-usage)
191
- - [Advanced Usage](#advanced-usage)
192
- - [Scaffolding](#scaffolding)
193
- - [App](#app)
194
- - [Custom Shell](#custom-shell)
195
- - [Custom Widget](#custom-widget)
196
- - [Custom Shell Gem](#custom-shell-gem)
197
- - [Custom Widget Gem](#custom-widget-gem)
198
- - [Gem Listing](#gem-listing)
199
- - [Listing Custom Shell Gems](#listing-custom-shell-gems)
200
- - [Listing Custom Widget Gems](#listing-custom-widget-gems)
201
- - [Listing DSL Gems](#listing-dsl-gems)
202
- - [Packaging](#packaging)
203
- - [Raw JRuby Command](#raw-jruby-command)
204
- - [Mac Support](#mac-support)
205
- - [Girb (Glimmer irb) Command](#girb-glimmer-irb-command)
206
- - [Glimmer GUI DSL Syntax](#glimmer-gui-dsl-syntax)
207
- - [DSL Auto-Expansion](#dsl-auto-expansion)
208
- - [Widgets](#widgets)
209
- - [Display](#display)
210
- - [SWT Proxies](#swt-proxies)
211
- - [Dialog](#dialog)
212
- - [Menus](#menus)
213
- - [ScrolledComposite](#scrolledcomposite)
214
- - [Widget Styles](#widget-styles)
215
- - [Explicit SWT Style Bit](#explicit-swt-style-bit)
216
- - [Negative SWT Style Bits](#negative-swt-style-bits)
217
- - [Extra SWT Styles](#extra-swt-styles)
218
- - [Widget Properties](#widget-properties)
219
- - [Colors](#colors)
220
- - [Fonts](#fonts)
221
- - [Layouts](#layouts)
222
- - [Layout Data](#layout-data)
223
- - [Data-Binding](#data-binding)
224
- - [General Examples](#general-examples)
225
- - [Combo](#combo)
226
- - [List](#list)
227
- - [Table](#table)
228
- - [Tree](#tree)
229
- - [Observer](#observer)
230
- - [Observing Widgets](#observing-widgets)
231
- - [Observing Models](#observing-models)
232
- - [Custom Widgets](#custom-widgets)
233
- - [Simple Example](#simple-example)
234
- - [Lifecycle Hook Example](#lifecycle-hook-example)
235
- - [Custom Widget API](#custom-widget-api)
236
- - [Content/Options Example](#contentoptions-example)
237
- - [Custom Widget Lifecycle Hooks](#custom-widget-lifecycle-hooks)
238
- - [Gotcha](#gotcha)
239
- - [Final Notes](#final-notes)
240
- - [Custom Shells](#custom-shells)
241
- - [Drag and Drop](#drag-and-drop)
242
- - [Miscellaneous](#miscellaneous)
243
- - [Multi-DSL Support](#multi-dsl-support)
244
- - [Application Menu Items (About/Preferences)](#application-menu-items-aboutpreferences)
245
- - [App Name and Version](#app-name-and-version)
246
- - [Video Widget](#video-widget)
247
- - [Browser Widget](#browser-widget)
248
- - [Glimmer Configuration](#glimmer-configuration)
249
- - [logger](#logger)
250
- - [import_swt_packages](#importswtpackages)
251
- - [loop_max_count](#loopmaxcount)
252
- - [Glimmer Style Guide](#glimmer-style-guide)
253
- - [SWT Reference](#swt-reference)
254
- - [Samples](#samples)
255
- - [Hello Samples](#hello-samples)
256
- - [Hello, World! Sample](#hello-world-sample)
257
- - [Hello, Tab!](#hello-tab)
258
- - [Hello, Combo!](#hello-combo)
259
- - [Hello, List Single Selection!](#hello-list-single-selection)
260
- - [Hello, List Multi Selection!](#hello-list-multi-selection)
261
- - [Hello, Computed!](#hello-computed)
262
- - [Hello, Message Box!](#hello-message-box)
263
- - [Hello, Browser!](#hello-browser)
264
- - [Hello, Drag and Drop!](#hello-drag-and-drop)
265
- - [Hello, Menu Bar!](#hello-menu-bar)
266
- - [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu)
267
- - [Elaborate Samples](#elaborate-samples)
268
- - [Login](#login)
269
- - [Tic Tac Toe Sample](#tic-tac-toe-sample)
270
- - [Contact Manager](#contact-manager-sample)
271
- - [External Samples](#external-samples)
272
- - [Glimmer Calculator](#glimmer-calculator)
273
- - [Gladiator](#gladiator)
274
- - [Timer](#timer)
275
- - [In Production](#in-production)
276
- - [Math Bowling](#math-bowling)
277
- - [Are We There Yet?](#are-we-there-yet)
278
- - [Garderie Rainbow Daily Agenda](#garderie-rainbow-daily-agenda)
279
- - [Packaging & Distribution](#packaging--distribution)
280
- - [Packaging Defaults](#packaging-defaults)
281
- - [Packaging Configuration](#packaging-configuration)
282
- - [javapackager Extra Arguments](#javapackager-extra-arguments)
283
- - [Mac Application Distribution](#mac-application-distribution)
284
- - [Self Signed Certificate](#self-signed-certificate)
285
- - [Gotchas](#gotchas)
286
- - [App Updates](#app-updates)
287
- - [Glimmer Supporting Libraries](#glimmer-supporting-libraries)
288
- - [Glimmer Process](#glimmer-process)
289
- - [Resources](#resources)
290
- - [Help](#help)
291
- - [Issues](#issues)
292
- - [Chat](#chat)
293
- - [Feature Suggestions](#feature-suggestions)
294
- - [Change Log](#change-log)
295
- - [Contributing](#contributing)
296
- - [Contributors](#contributors)
297
- - [Hire Me](#hire-me)
298
- - [License](#license)
299
-
300
- ## Background
301
-
302
- Ruby is a dynamically-typed object-oriented language, which provides great productivity gains due to its powerful expressive syntax and dynamic nature. While it is proven by the Ruby on Rails framework for web development, it currently lacks a robust platform-independent framework for building desktop applications. Given that Java libraries can now be utilized in Ruby code through JRuby, Eclipse technologies, such as SWT, JFace, and RCP can help fill the gap of desktop application development with Ruby.
303
-
304
- ## Platform Support
305
-
306
- Glimmer runs on the following platforms:
307
- - Mac
308
- - Windows
309
- - Linux
310
-
311
- Glimmer's GUI has the native look and feel of each operating system it runs on since it uses SWT behind the scenes, which leverages the following native libraries:
312
- - Win32 on Windows
313
- - Cocoa on Mac
314
- - GTK on Linux
266
+ ### Production Desktop Apps Built with Glimmer DSL for SWT
315
267
 
316
- More info about the SWT GUI on various platforms can be found on the Eclipse WIKI and SWT FAQ:
268
+ [<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
317
269
 
318
- https://wiki.eclipse.org/SWT/Devel/Gtk/Dev_guide#Win32.2FCocoa.2FGTK
319
- https://www.eclipse.org/swt/faq.php
270
+ ![Are We There Yet? App Screenshot](https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-screenshot-windows.png)
320
271
 
321
- ## Pre-requisites
272
+ [<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
322
273
 
323
- - JDK 8u241 (1.8.0_241) (find at https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html)
324
- - JRuby 9.2.13.0 (supporting Ruby 2.5.x syntax) (get via [RVM](http://rvm.io) on Mac and Linux or find at [https://www.jruby.org/download](https://www.jruby.org/download) for Windows)
325
- - SWT 4.16 (already included in the [glimmer-dsl-swt](https://rubygems.org/gems/glimmer-dsl-swt) gem)
274
+ ![Math Bowling App Screenshot](https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/Math-Bowling-Screenshot.png)
326
275
 
327
- To obtain JRuby through [RVM](http://rvm.io), you may run:
276
+ ## Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
328
277
 
329
- ```bash
330
- rvm install jruby-9.2.13.0
331
- ```
278
+ [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.).
332
279
 
333
- Glimmer might still work on other versions of Java, JRuby and SWT, but there are no guarantees, so it is best to stick to the pre-requisites outlined above.
280
+ 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).
334
281
 
335
- ## Setup
282
+ 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/).
336
283
 
337
- Please follow these instructions to make the `glimmer` command available on your system via the [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem.
284
+ [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:
285
+ - Declarative DSL syntax that visually maps to the GUI widget hierarchy
286
+ - Convention over configuration via smart defaults and automation of low-level details
287
+ - Requiring the least amount of syntax possible to build GUI
288
+ - Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
289
+ - Custom Widget support
290
+ - Scaffolding for new custom widgets, apps, and gems
291
+ - Native-Executable packaging on Mac, Windows, and Linux
338
292
 
339
- If you intend to learn the basics of Glimmer but are not ready to build a Glimmer app yet, pick Option 1 ([Direct Install](#option-1-direct-install)).
293
+ ### Glimmer DSL for Tk Samples
340
294
 
341
- If you intend to build a Glimmer app from scratch with [scaffolding](#scaffolding), pick Option 1 ([Direct Install](#option-1-direct-install)) as well.
295
+ #### Hello, World!
342
296
 
343
- Otherwise, Option 2 ([Bundler](#option-2-bundler)) can be followed in rare cases where you want to build an app without [scaffolding](#scaffolding).
297
+ Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_world.rb)):
344
298
 
345
- ### Option 1: Direct Install
346
- (Use for [Scaffolding](#scaffolding))
299
+ ```ruby
300
+ include Glimmer
347
301
 
348
- Run this command to install directly:
349
- ```
350
- jgem install glimmer-dsl-swt
302
+ root {
303
+ label {
304
+ text 'Hello, World!'
305
+ }
306
+ }.open
351
307
  ```
352
308
 
353
- Or this command if you want a specific version:
309
+ Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
310
+
354
311
  ```
355
- jgem install glimmer-dsl-swt -v 0.6.3
312
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
356
313
  ```
357
314
 
358
- `jgem` is JRuby's version of `gem` command.
359
- RVM allows running `gem install` directly as an alias.
360
- Otherwise, you may also run `jruby -S gem install ...`
315
+ Glimmer app:
361
316
 
362
- If you are new to Glimmer and would like to continue learning the basics, you may continue to the [Glimmer Command](https://github.com/AndyObtiva/glimmer#glimmer-command) section.
317
+ ![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)
363
318
 
364
- Otherwise, if you are ready to build a Glimmer app on the Mac, you can jump to the [Glimmer Scaffolding](https://github.com/AndyObtiva/glimmer#scaffolding) section next.
319
+ #### Hello, Tab!
365
320
 
366
- Note: if you're using activerecord or activesupport, keep in mind that Glimmer unhooks ActiveSupport::Dependencies as it does not rely on it.
321
+ Glimmer code (from [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_tab.rb)):
367
322
 
368
- ### Option 2: Bundler
369
- (Use for Manual App Creation)
323
+ ```ruby
324
+ include Glimmer
370
325
 
371
- Add the following to `Gemfile`:
372
- ```
373
- gem 'glimmer-dsl-swt', '~> 0.6.3'
326
+ root {
327
+ title 'Hello, Tab!'
328
+
329
+ notebook {
330
+ frame(text: 'English') {
331
+ label {
332
+ text 'Hello, World!'
333
+ }
334
+ }
335
+
336
+ frame(text: 'French') {
337
+ label {
338
+ text 'Bonjour, Univers!'
339
+ }
340
+ }
341
+ }
342
+ }.open
374
343
  ```
375
344
 
376
- And, then run:
345
+ Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
346
+
377
347
  ```
378
- jruby -S bundle install
348
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_tab.rb'"
379
349
  ```
380
350
 
381
- Note: if you're using activerecord or activesupport, keep in mind that Glimmer unhooks ActiveSupport::Dependencies as it does not rely on it.
351
+ Glimmer app:
382
352
 
383
- You may learn more about other Glimmer related gems ([`glimmer-dsl-opal`](https://github.com/AndyObtiva/glimmer-dsl-opal), [`glimmer-dsl-xml`](https://github.com/AndyObtiva/glimmer-dsl-xml), and [`glimmer-dsl-css`](https://github.com/AndyObtiva/glimmer-dsl-css)) at [Multi-DSL Support](#multi-dsl-support)
353
+ ![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)
354
+ ![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)
384
355
 
385
- ## Glimmer Command
356
+ #### Hello, Combo!
386
357
 
387
- The `glimmer` command allows you to run, scaffold, package, and list Glimmer applications/gems.
358
+ Glimmer code (from [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-tk/blob/master/samples/hello/hello_combo.rb)):
388
359
 
389
- If you are new to Glimmer, you may read the Basic Usage section and skip the rest until you have gone through [Girb (Glimmer irb) Command](#girb-glimmer-irb-command), [Glimmer GUI DSL Syntax](#glimmer-gui-dsl-syntax), and [Samples](#samples).
360
+ ```ruby
361
+ # ... more code precedes
362
+ root {
363
+ title 'Hello, Combo!'
364
+
365
+ combobox { |proxy|
366
+ state 'readonly'
367
+ text bind(person, :country)
368
+ }
369
+
370
+ button { |proxy|
371
+ text "Reset Selection"
372
+ command {
373
+ person.reset_country
374
+ }
375
+ }
376
+ }.open
377
+ # ... more code follows
378
+ ```
390
379
 
391
- ### Basic Usage
380
+ Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
392
381
 
393
382
  ```
394
- glimmer application.rb
383
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combo.rb'"
395
384
  ```
396
385
 
397
- Runs a Glimmer application using JRuby, automatically preloading
398
- the glimmer ruby gem and SWT jar dependency.
386
+ Glimmer app:
399
387
 
400
- Example:
401
- ```
402
- glimmer samples/hello/hello_world.rb
403
- ```
404
- This runs the Glimmer "Hello, World!" sample.
388
+ ![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)
389
+ ![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)
405
390
 
406
- If you cloned this project locally, you may run `bin/glimmer` instead.
391
+ ## Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
407
392
 
408
- Example:
409
- ```
410
- bin/glimmer samples/hello/hello_world.rb
411
- ```
393
+ [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.
412
394
 
413
- ### Advanced Usage
395
+ 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.
414
396
 
415
- Below are the full usage instructions that come up when running `glimmer` without args.
397
+ ### Glimmer DSL for Opal Samples
416
398
 
417
- ```
418
- Usage: glimmer [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
399
+ #### Hello, Computed!
419
400
 
420
- Runs Glimmer applications/tasks.
401
+ Add the following require statement to `app/assets/javascripts/application.rb`
421
402
 
422
- Either a single task or one or more applications may be specified.
423
403
 
424
- When a task is specified, it runs via rake. Some tasks take arguments in square brackets.
404
+ ```ruby
405
+ require 'samples/hello/hello_computed'
406
+ ```
425
407
 
426
- Available tasks are below (you may also lookup by adding `require 'glimmer/rake_task'` in Rakefile and running rake -T):
427
- glimmer list:gems:customshell[query] # List Glimmer custom shell gems available at rubygems.org (query is optional) [alt: list:gems:cs]
428
- glimmer list:gems:customwidget[query] # List Glimmer custom widget gems available at rubygems.org (query is optional) [alt: list:gems:cw]
429
- glimmer list:gems:dsl[query] # List Glimmer DSL gems available at rubygems.org (query is optional)
430
- glimmer package[type] # Package app for distribution (generating config, jar, and native files) (type is optional)
431
- glimmer package:clean # Clean by removing "dist" and "packages" directories
432
- glimmer package:config # Generate JAR config file
433
- glimmer package:jar # Generate JAR file
434
- glimmer package:lock_jars # Lock JARs
435
- glimmer package:native[type] # Generate Native files (DMG/PKG/APP on the Mac, MSI/EXE/IMAGE on Windows, RPM/DEB on Linux) (type is optional)
436
- glimmer scaffold[app_name] # Scaffold Glimmer application directory structure to build a new app
437
- glimmer scaffold:customshell[name,namespace] # Scaffold Glimmer::UI::CustomShell subclass (full window view) under app/views (namespace is optional) [alt: scaffold:cs]
438
- glimmer scaffold:customwidget[name,namespace] # Scaffold Glimmer::UI::CustomWidget subclass (part of a view) under app/views (namespace is optional) [alt: scaffold:cw]
439
- glimmer scaffold:gem:customshell[name,namespace] # Scaffold Glimmer::UI::CustomShell subclass (full window view) under its own Ruby gem + app project (namespace is required) [alt: scaffold:gem:cs]
440
- glimmer scaffold:gem:customwidget[name,namespace] # Scaffold Glimmer::UI::CustomWidget subclass (part of a view) under its own Ruby gem project (namespace is required) [alt: scaffold:gem:cw]
408
+ Or add the Glimmer code directly if you prefer to play around with it:
441
409
 
442
- When applications are specified, they are run using JRuby,
443
- automatically preloading the glimmer Ruby gem and SWT jar dependency.
410
+ ```ruby
411
+ class HelloComputed
412
+ class Contact
413
+ attr_accessor :first_name, :last_name, :year_of_birth
414
+
415
+ def initialize(attribute_map)
416
+ @first_name = attribute_map[:first_name]
417
+ @last_name = attribute_map[:last_name]
418
+ @year_of_birth = attribute_map[:year_of_birth]
419
+ end
420
+
421
+ def name
422
+ "#{last_name}, #{first_name}"
423
+ end
424
+
425
+ def age
426
+ Time.now.year - year_of_birth.to_i
427
+ rescue
428
+ 0
429
+ end
430
+ end
431
+ end
444
432
 
445
- Optionally, extra Glimmer options, JRuby options and environment variables may be passed in.
433
+ class HelloComputed
434
+ include Glimmer
446
435
 
447
- Glimmer options:
448
- - "--quiet" : Does not announce file path of Glimmer application being launched
449
- - "--debug" : Displays extra debugging information, passes "--debug" to JRuby, and enables debug logging
450
- - "--log-level=VALUE" : Sets Glimmer's Ruby logger level ("ERROR" / "WARN" / "INFO" / "DEBUG"; default is none)
436
+ def initialize
437
+ @contact = Contact.new(
438
+ first_name: 'Barry',
439
+ last_name: 'McKibbin',
440
+ year_of_birth: 1985
441
+ )
442
+ end
451
443
 
452
- Example: glimmer samples/hello_world.rb
444
+ def launch
445
+ shell {
446
+ text 'Hello, Computed!'
447
+ composite {
448
+ grid_layout {
449
+ num_columns 2
450
+ make_columns_equal_width true
451
+ horizontal_spacing 20
452
+ vertical_spacing 10
453
+ }
454
+ label {text 'First &Name: '}
455
+ text {
456
+ text bind(@contact, :first_name)
457
+ layout_data {
458
+ horizontal_alignment :fill
459
+ grab_excess_horizontal_space true
460
+ }
461
+ }
462
+ label {text '&Last Name: '}
463
+ text {
464
+ text bind(@contact, :last_name)
465
+ layout_data {
466
+ horizontal_alignment :fill
467
+ grab_excess_horizontal_space true
468
+ }
469
+ }
470
+ label {text '&Year of Birth: '}
471
+ text {
472
+ text bind(@contact, :year_of_birth)
473
+ layout_data {
474
+ horizontal_alignment :fill
475
+ grab_excess_horizontal_space true
476
+ }
477
+ }
478
+ label {text 'Name: '}
479
+ label {
480
+ text bind(@contact, :name, computed_by: [:first_name, :last_name])
481
+ layout_data {
482
+ horizontal_alignment :fill
483
+ grab_excess_horizontal_space true
484
+ }
485
+ }
486
+ label {text 'Age: '}
487
+ label {
488
+ text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
489
+ layout_data {
490
+ horizontal_alignment :fill
491
+ grab_excess_horizontal_space true
492
+ }
493
+ }
494
+ }
495
+ }.open
496
+ end
497
+ end
453
498
 
454
- This runs the Glimmer application samples/hello_world.rb
499
+ HelloComputed.new.launch
455
500
  ```
501
+ Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
456
502
 
457
- Example (Glimmer/JRuby option specified):
458
- ```
459
- glimmer --debug samples/hello/hello_world.rb
460
- ```
503
+ ![Glimmer DSL for SWT Hello Computed](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-computed.png)
461
504
 
462
- Runs Glimmer application with JRuby debug option to enable JRuby debugging.
505
+ Glimmer app on the web (using `glimmer-dsl-opal` gem):
463
506
 
464
- Example (Multiple apps):
507
+ Start the Rails server:
465
508
  ```
466
- glimmer samples/hello/hello_world.rb samples/hello_tab.rb
509
+ rails s
467
510
  ```
468
511
 
469
- Launches samples/hello/hello_world.rb and samples/hello_tab.rb at the same time, each in a separate JRuby thread.
512
+ Visit `http://localhost:3000`
470
513
 
471
- ### Scaffolding
514
+ You should see "Hello, Computed!"
472
515
 
473
- Glimmer borrows from Rails the idea of Scaffolding, that is generating a structure for your app files that
474
- helps you get started just like true building scaffolding helps construction workers, civil engineers, and architects.
516
+ ![Glimmer DSL for Opal Hello Computed](https://raw.githubusercontent.com/AndyObtiva/glimmer-dsl-opal/master/images/glimmer-dsl-opal-hello-computed.png)
475
517
 
476
- Glimmer scaffolding goes beyond just scaffolding the app files that Rails does. It also packages it and launches it,
477
- getting you to a running and delivered state of an advanced "Hello, World!" Glimmer application right off the bat.
518
+ #### Glimmer Calculator
478
519
 
479
- This should greatly facilitate building a new Glimmer app by helping you be productive and focus on app details while
480
- letting Glimmer scaffolding take care of initial app file structure concerns, such as adding:
481
- - Main application class that includes Glimmer (`app/{app_name}.rb`)
482
- - Main application view that houses main window content, menu, about dialog, and preferences dialog
483
- - View and Model directories (`app/views` and `app/models`)
484
- - Rakefile including Glimmer tasks (`Rakefile`)
485
- - Version (`VERSION`)
486
- - License (`LICENSE.txt`)
487
- - Icon (under `package/{platform}/{App Name}.{icon_extension}` for `macosx` .icns, `windows` .ico, and `linux` .png)
488
- - Bin file for starting application (`bin/{app_name}.rb`)
520
+ Add the [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) gem to `Gemfile` (without requiring):
489
521
 
490
- NOTE: Scaffolding supports Mac and Windows packaging at the moment.
522
+ ```
523
+ gem 'glimmer-cs-calculator', require: false
524
+ ```
491
525
 
492
- #### App
526
+ Add the following require statement to `app/assets/javascripts/application.rb`
493
527
 
494
- Before you start, make sure you are in a JRuby environment with Glimmer gem installed as per "Direct Install" pre-requisites.
528
+ ```ruby
529
+ require 'glimmer-cs-calculator/launch'
530
+ ```
495
531
 
496
- To scaffold a Glimmer app from scratch, run the following command:
532
+ Sample GUI code (relies on custom widgets `command_button`, `operation_button`, and `number_button`):
497
533
 
498
- ```
499
- glimmer scaffold[AppName]
534
+ ```ruby
535
+ shell {
536
+ minimum_size (OS.mac? ? 320 : (OS.windows? ? 390 : 520)), 240
537
+ image File.join(APP_ROOT, 'package', 'windows', "Glimmer Calculator.ico") if OS.windows?
538
+ text "Glimmer - Calculator"
539
+ grid_layout 4, true
540
+ # Setting styled_text to multi in order for alignment options to activate
541
+ styled_text(:multi, :wrap, :border) {
542
+ text bind(@presenter, :result)
543
+ alignment swt(:right)
544
+ right_margin 5
545
+ font height: 40
546
+ layout_data(:fill, :fill, true, true) {
547
+ horizontal_span 4
548
+ }
549
+ editable false
550
+ caret nil
551
+ }
552
+ command_button('AC')
553
+ operation_button('÷')
554
+ operation_button('×')
555
+ operation_button('−')
556
+ (7..9).each { |number|
557
+ number_button(number)
558
+ }
559
+ operation_button('+', font: @button_font_big, vertical_span: 2)
560
+ (4..6).each { |number|
561
+ number_button(number)
562
+ }
563
+ (1..3).each { |number|
564
+ number_button(number)
565
+ }
566
+ command_button('=', font: @button_font_big, vertical_span: 2)
567
+ number_button(0, horizontal_span: 2)
568
+ operation_button('.')
569
+ }
500
570
  ```
501
571
 
502
- This will generate an advanced "Hello, World!" app, package it as a Mac native file (DMG/PKG/APP), and launch it all in one fell swoop.
572
+ Glimmer app on the desktop (using the [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
503
573
 
504
- Suppose you run:
574
+ ![Glimmer Calculator Linux](https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot-linux.png)
505
575
 
576
+ Glimmer app on the web (using `glimmer-dsl-opal` gem):
577
+
578
+ Start the Rails server:
506
579
  ```
507
- glimmer scaffold[greeter]
580
+ rails s
508
581
  ```
509
582
 
510
- You should see output like the following:
583
+ Visit `http://localhost:3000`
584
+ (or visit: http://glimmer-cs-calculator-server.herokuapp.com)
511
585
 
512
- ```
513
- $ glimmer scaffold[greeter]
514
- create .gitignore
515
- create Rakefile
516
- create Gemfile
517
- create LICENSE.txt
518
- create README.rdoc
519
- create .document
520
- create lib
521
- create lib/greeter.rb
522
- create spec
523
- create spec/spec_helper.rb
524
- create spec/greeter_spec.rb
525
- create .rspec
526
- Jeweler has prepared your gem in ./greeter
527
- Created greeter/.gitignore
528
- Created greeter/.ruby-version
529
- Created greeter/.ruby-gemset
530
- Created greeter/VERSION
531
- Created greeter/LICENSE.txt
532
- Created greeter/Gemfile
533
- Created greeter/Rakefile
534
- Created greeter/app/greeter.rb
535
- Created greeter/app/views/greeter/app_view.rb
536
- Created greeter/package/windows/Greeter.ico
537
- Created greeter/package/macosx/Greeter.icns
538
- Created greeter/package/linux/Greeter.png
539
- Created greeter/bin/greeter
540
- Created greeter/spec/spec_helper.rb
541
- ...
542
- ```
586
+ You should see "Glimmer Calculator"
543
587
 
544
- Eventually, it will launch an advanced "Hello, World!" app window having the title of your application.
588
+ ![Glimmer Calculator Opal](https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot-opal.png)
545
589
 
546
- ![Glimmer Scaffold App](images/glimmer-scaffolding-app.png)
590
+ Here is an Apple Calculator CSS themed version (with [CSS only](https://github.com/AndyObtiva/glimmer-cs-calculator/blob/master/server/glimmer-cs-calculator-server/app/assets/stylesheets/welcomes_apple.scss), no app code changes):
547
591
 
548
- It also comes with a boilerplate Preferences dialog.
592
+ Visit http://glimmer-cs-calculator-server.herokuapp.com/welcomes/apple
549
593
 
550
- ![Glimmer Scaffold App Preferences](images/glimmer-scaffolding-app-preferences.png)
594
+ You should see "Apple Calculator Theme"
551
595
 
552
- Here is a Windows scaffolded app called Greeter:
596
+ ![Glimmer Calculator Opal Apple Calculator Theme](https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot-opal-apple.png)
553
597
 
554
- ![Glimmer Scaffold App Windows](images/glimmer-scaffolding-app-windows.png)
598
+ ## Glimmer DSL for XML (& HTML)
555
599
 
556
- Here is the Windows version of the boilerplate Preferences dialog.
600
+ [Glimmer DSL for XML](https://github.com/AndyObtiva/glimmer-dsl-xml) provides Ruby syntax for building XML (eXtensible Markup Language) documents.
557
601
 
558
- ![Glimmer Scaffold App Windows Preferences](images/glimmer-scaffolding-app-windows-preferences.png)
602
+ 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).
559
603
 
604
+ ### XML DSL
560
605
 
561
- #### Custom Shell
606
+ Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
607
+ Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
562
608
 
563
- To scaffold a Glimmer custom shell (full window view) for an existing Glimmer app, run the following command:
609
+ Here are all the Glimmer XML DSL top-level keywords:
610
+ - `html`
611
+ - `tag`: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
612
+ - `name_space`: enables namespacing html tags
564
613
 
565
- ```
566
- glimmer scaffold:customshell[name]
614
+ 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')` )
615
+
616
+ Example (basic HTML):
617
+
618
+ ```ruby
619
+ @xml = html {
620
+ head {
621
+ meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
622
+ }
623
+ body {
624
+ h1 { "Hello, World!" }
625
+ }
626
+ }
627
+ puts @xml
567
628
  ```
568
629
 
569
- Or the following alternative abbreviation:
630
+ Output:
570
631
 
571
632
  ```
572
- glimmer scaffold:cs[name]
633
+ <html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
573
634
  ```
574
635
 
575
- #### Custom Widget
636
+ ## Glimmer DSL for CSS
576
637
 
577
- To scaffold a Glimmer custom widget (part of a view) for an existing Glimmer app, run the following command:
638
+ [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) provides Ruby syntax for building CSS (Cascading Style Sheets).
578
639
 
579
- ```
580
- glimmer scaffold:customwidget[name]
581
- ```
640
+ 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).
582
641
 
583
- Or the following alternative abbreviation:
642
+ ### CSS DSL
584
643
 
585
- ```
586
- glimmer scaffold:cw[name]
587
- ```
644
+ Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
645
+ Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
588
646
 
589
- #### Custom Shell Gem
647
+ `css` is the only top-level keyword in the Glimmer CSS DSL
590
648
 
591
- Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
592
- They have everything scaffolded Glimmer apps come with in addition to gem content like a [jeweler](https://github.com/technicalpickles/jeweler) Rakefile that can build gemspec and release gems.
593
- Unlike scaffolded Glimmer apps, custom shell gem content lives under the `lib` directory (not `app`).
594
- They can be packaged as both a native executable (e.g. Mac DMG/PKG/APP) and a Ruby gem.
595
- Of course, you can just build a Ruby gem and disregard native executable packaging if you do not need it.
649
+ Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
650
+ Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
596
651
 
597
- To scaffold a Glimmer custom shell gem (full window view distributed as a Ruby gem), run the following command:
652
+ Example:
598
653
 
599
- ```
600
- glimmer scaffold:gem:customshell[name,namespace]
654
+ ```ruby
655
+ @css = css {
656
+ body {
657
+ font_size '1.1em'
658
+ pv 'background', 'white'
659
+ }
660
+
661
+ s('body > h1') {
662
+ background_color :red
663
+ pv 'font-size', '2em'
664
+ }
665
+ }
666
+ puts @css
601
667
  ```
602
668
 
603
- Or the following alternative abbreviation:
669
+ Output:
604
670
 
605
671
  ```
606
- glimmer scaffold:gem:cs[name,namespace]
672
+ body{font-size:1.1em;background:white}body > h1{background-color:red;font-size:2em}
607
673
  ```
608
674
 
609
- It is important to specify a namespace to avoid having your gem clash with existing gems.
675
+ ## Multi-DSL Support
610
676
 
611
- The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell).
677
+ 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`)
612
678
 
613
- Only official Glimmer gems created by the Glimmer project committers will have no namespace (e.g. [glimmer-cs-gladiator](https://rubygems.org/gems/glimmer-cs-gladiator) Ruby gem)
679
+ Glimmer allows mixing DSLs, which comes in handy when doing things like using a desktop Browser widget with HTML and CSS.
614
680
 
615
- Examples:
681
+ Glimmer DSL syntax consists mainly of:
682
+ - keywords (e.g. `table` for a table widget)
683
+ - style/args (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
684
+ - 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)
616
685
 
617
- - [glimmer-cs-gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator): Gladiator (Glimmer Editor)
618
- - [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator): Glimmer Calculator
686
+ 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.
619
687
 
620
- #### Custom Widget Gem
688
+ ## Glimmer Supporting Libraries
621
689
 
622
- To scaffold a Glimmer custom widget gem (part of a view distributed as a Ruby gem), run the following command:
690
+ Here is a list of notable 3rd party gems used by Glimmer and Glimmer DSLs:
691
+ - [jeweler](https://github.com/technicalpickles/jeweler): generates app gems during [Glimmer Scaffolding](https://github.com/AndyObtiva/glimmer-dsl-swt#scaffolding)
692
+ - [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)
693
+ - [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
694
+ - [os](https://github.com/rdp/os): provides OS detection capabilities (e.g. `OS.mac?` or `OS.windows?`) to write cross-platform code inexpensively
695
+ - [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`)
696
+ - [rake](https://github.com/ruby/rake): used to implement and execute `glimmer` commands
697
+ - [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.
698
+ - [rouge](https://github.com/rouge-ruby/rouge): Ruby syntax highlighter used in the `code_text` [Glimmer DSL for SWT custom widget](https://github.com/AndyObtiva/glimmer-dsl-swt#custom-widgets) leveraged by the [Glimmer Meta-Sample](https://github.com/AndyObtiva/glimmer-dsl-swt#samples)
699
+ - [super_module](https://github.com/AndyObtiva/super_module): used to cleanly write the Glimmer::UI:CustomWidget and Glimmer::UI::CustomShell modules
700
+ - [text-table](https://github.com/aptinio/text-table): renders textual data in a textual table for the command-line interface of Glimmer
701
+ - [warbler](https://github.com/jruby/warbler): converts a Glimmer app into a Java JAR file during packaging
623
702
 
624
- ```
625
- glimmer scaffold:gem:customwidget[name,namespace]
626
- ```
703
+ ## Glimmer Process
627
704
 
628
- Or the following alternative abbreviation:
705
+ [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.
629
706
 
630
- ```
631
- glimmer scaffold:gem:cw[name,namespace]
632
- ```
707
+ Learn more by reading the [GPG](PROCESS.md) (Glimmer Process Guidelines)
633
708
 
709
+ ## Resources
634
710
 
635
- It is important to specify a namespace to avoid having your gem clash with existing gems.
711
+ * [Code Master Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
712
+ * [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
713
+ * [MountainWest RubyConf 2011 Video](https://confreaks.tv/videos/mwrc2011-whatever-happened-to-desktop-development-in-ruby)
714
+ * [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
715
+ * [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
716
+ * [DZone Tutorial](https://dzone.com/articles/an-introduction-glimmer)
636
717
 
637
- The Ruby gem name will follow the convention "glimmer-cw-customwidgetname-namespace" (the 'cw' is for Custom Widget)
718
+ ## Help
638
719
 
639
- Only official Glimmer gems created by the Glimmer project committers will have no namespace (e.g. [glimmer-cw-video](https://rubygems.org/gems/glimmer-cw-video) Ruby gem)
720
+ ### Issues
640
721
 
641
- Examples:
722
+ You may submit [issues](https://github.com/AndyObtiva/glimmer/issues) on [GitHub](https://github.com/AndyObtiva/glimmer/issues).
642
723
 
643
- - [glimmer-cw-video](https://github.com/AndyObtiva/glimmer-cw-video): Video Widget
644
- - [glimmer-cw-cdatetime-nebula](https://github.com/AndyObtiva/glimmer-cw-cdatetime-nebula): Nebula CDateTime Widget
724
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer/issues)
645
725
 
646
- ### Gem Listing
726
+ ### Chat
647
727
 
648
- The `glimmer` command comes with tasks for listing Glimmer related gems to make it easy to find Glimmer Custom Shells, Custom Widgets, and DSLs published by others in the Glimmer community on [rubygems.org](http://www.rubygems.org).
728
+ If you need live help, try to [![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)
649
729
 
650
- #### Listing Custom Shell Gems
730
+ ## Feature Suggestions
651
731
 
652
- The following command lists available Glimmer [Custom Shell Gems](#custom-shell-gem) (prefixed with "glimmer-cs-" by scaffolding convention) created by the the Glimmer community and published on [rubygems.org](http://www.rubygems.org):
732
+ These features have been suggested. You might see them in a future version of Glimmer. You are welcome to contribute more feature suggestions.
653
733
 
654
- ```
655
- glimmer list:gems:customshell[query]
656
- ```
734
+ [glimmer-dsl-swt/TODO.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/TODO.md)
657
735
 
658
- Or the following alternative abbreviation:
736
+ Glimmer DSL Engine specific tasks are at:
659
737
 
660
- ```
661
- glimmer list:gems:cs[query]
662
- ```
663
-
664
- Example:
665
-
666
- ```
667
- glimmer list:gems:cs
668
- ```
669
-
670
- Output:
671
-
672
- ```
673
-
674
- Glimmer Custom Shell Gems at rubygems.org:
675
-
676
- Name Gem Version Author Description
677
-
678
- Calculator glimmer-cs-calculator 1.0.2 Andy Maleh Calculator - Glimmer Custom Shell
679
- Gladiator glimmer-cs-gladiator 0.2.4 Andy Maleh Gladiator (Glimmer Editor) - Glimmer Custom Shell
680
- Timer glimmer-cs-timer 1.0.0 Andy Maleh Timer - Glimmer Custom Shell
681
-
682
- ```
683
-
684
- #### Listing Custom Widget Gems
685
-
686
- 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):
687
-
688
- ```
689
- glimmer list:gems:customwidget[query]
690
- ```
691
-
692
- Or the following alternative abbreviation:
693
-
694
- ```
695
- glimmer list:gems:cw[query]
696
- ```
697
-
698
- Example:
699
-
700
- Check if there is a custom video widget for Glimmer.
701
-
702
- ```
703
- glimmer list:gems:cw[video]
704
- ```
705
-
706
- Output:
707
-
708
- ```
709
-
710
- Glimmer Custom Widget Gems matching [video] at rubygems.org:
711
-
712
- Name Gem Version Author Description
713
-
714
- Video glimmer-cw-video 0.1.3 Andy Maleh Glimmer Custom Widget - Video
715
-
716
- ```
717
-
718
- #### Listing DSL Gems
719
-
720
- 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):
721
-
722
- ```
723
- glimmer list:gems:dsl[query]
724
- ```
725
-
726
- Example:
727
-
728
- ```
729
- glimmer list:gems:dsl
730
- ```
731
-
732
- Output:
733
-
734
- ```
735
-
736
- Glimmer DSL Gems at rubygems.org:
737
-
738
- Name Gem Version Author Description
739
-
740
- Css glimmer-dsl-css 0.2.0 AndyMaleh Glimmer DSL for CSS
741
- Opal glimmer-dsl-opal 0.1.0 AndyMaleh Glimmer DSL for Opal
742
- Swt glimmer-dsl-swt 0.6.3 AndyMaleh Glimmer DSL for SWT
743
- Xml glimmer-dsl-xml 0.2.0 AndyMaleh Glimmer DSL for XML
744
-
745
- ```
746
-
747
- ### Packaging
748
-
749
- Glimmer packaging tasks are detailed under [Packaging & Distribution](#packaging--distribution).
750
-
751
- ### Raw JRuby Command
752
-
753
- If there is a need to run Glimmer directly via the `jruby` command, you
754
- may run the following:
755
-
756
- ```
757
- jruby -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
758
- ```
759
-
760
- The `-J-classpath` option specifies the `swt.jar` file path, which can be a
761
- 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.
762
-
763
- The `-r` option preloads (requires) the `glimmer` library in Ruby.
764
-
765
- The `-S` option specifies a script to run.
766
-
767
- #### Mac Support
768
-
769
- The Mac is well supported with the `glimmer` command. The advice below is not needed if you are using it.
770
-
771
- 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).
772
-
773
- Example:
774
- ```
775
- jruby -J-XstartOnFirstThread -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
776
- ```
777
-
778
- ## Girb (Glimmer irb) Command
779
-
780
- 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.
781
-
782
- ```
783
- girb
784
- ```
785
-
786
- If you cloned [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) project locally, you may run `bin/girb` instead.
787
-
788
- ```
789
- bin/girb
790
- ```
791
-
792
- Watch out for hands-on examples in this README indicated by "you may copy/paste in [`girb`](#girb-glimmer-irb-command)"
793
-
794
- Keep in mind that all samples live under [https://github.com/AndyObtiva/glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt)
795
-
796
- ## Glimmer GUI DSL Syntax
797
-
798
- Glimmer's core is a GUI DSL with a lightweight visual syntax that makes it easy to visualize the nesting of widgets in the GUI hierarchy tree.
799
-
800
- It is available through mixing in the `Glimmer` module, which makes Glimmer GUI DSL keywords available to both the instance scope and class scope:
801
-
802
- ```ruby
803
- include Glimmer
804
- ```
805
-
806
- For example, here is the basic "Hello, World!" sample code (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
807
-
808
- ```ruby
809
- include Glimmer
810
-
811
- shell {
812
- text "Glimmer"
813
- label {
814
- text "Hello, World!"
815
- }
816
- }.open
817
- ```
818
-
819
- The `include Glimmer` declaration on top mixed the `Glimmer` module into the Ruby global main object making the Glimmer GUI DSL available at the top-level global scope.
820
-
821
- While this works well enough for mini-samples, it is better to isolate Glimmer in a class or module during production application development to create a clean separation between view code (GUI) and model code (business domain). Here is the "Hello, World!" sample re-written in a class to illustrate how mixing in the `Glimmer` module (via `include Glimmer`) makes the Glimmer GUI DSL available in both the instance scope and class scope. That is clearly demonstrated by pre-initializing a color constant in the class scope and building the GUI in the `#open` instance method (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
822
-
823
- ```ruby
824
- class HelloWorld
825
- include Glimmer # makes the GUI DSL available in both the class scope and instance scope
826
-
827
- COLOR_FOREGROUND_DEFAULT = rgb(255, 0, 0) # rgb is a GUI DSL keyword used in the class scope
828
-
829
- def open
830
- # the following are GUI DSL keywords (shell, text, and label) used in the instance scope
831
- shell {
832
- text "Glimmer"
833
- label {
834
- text "Hello, World!"
835
- foreground COLOR_FOREGROUND_DEFAULT
836
- }
837
- }.open
838
- end
839
- end
840
-
841
- HelloWorld.new.open
842
- ```
843
-
844
- This renders "Hello, World!" with a red foreground color:
845
-
846
- ![Hello World Red Foreground Color](images/glimmer-hello-world-red-foreground-color.png)
847
-
848
- The GUI DSL intentionally avoids overly verbose syntax, requiring as little declarative code as possible to describe what GUI to render, how to style it, and what properties to data-bind to the Models.
849
-
850
- As such, it breaks off from Ruby's convention of using `do end` for multi-line blocks, opting instead for the lightweight and visual `{ }` curly brace blocks everywhere inside the GUI DSL. More details about Glimmer's syntax conventions may be found in the [Glimmer Style Guide](#glimmer-style-guide)
851
-
852
- Glimmer DSL syntax consists mainly of:
853
- - keywords (e.g. `table` for a table widget)
854
- - style/args (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
855
- - 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)
856
-
857
- ### DSL Auto-Expansion
858
-
859
- Glimmer supports a new and radical Ruby DSL concept called DSL Auto-Expansion. It is explained by first mentioning the two types of Glimmer GUI DSL keywords: static and dynamic.
860
-
861
- Static keywords are pre-identified keywords in the Glimmer DSL, such as `shell`, `display`, `message_box`, `async_exec`, `sync_exec`, and `bind`.
862
-
863
- Dynamic keywords are dynamically figured out from currently imported (aka required/loaded) SWT widgets, custom widgets, and widget properties. Examples are: `label`, `combo`, and `list` for widgets and `enabled`, `text`, and `selection` for properties.
864
-
865
- 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.
866
-
867
- For example, if a project adds this custom Java SWT library:
868
-
869
- https://www.eclipse.org/nebula/widgets/cdatetime/cdatetime.php?page=operation
870
-
871
- Glimmer will automatically support using the keyword `c_date_time`
872
-
873
- You will learn more about widgets next.
874
-
875
- ### Widgets
876
-
877
- Glimmer GUIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
878
-
879
- https://www.eclipse.org/swt/widgets/
880
-
881
- This screenshot taken from the link above should give a glimpse of how SWT widgets look and feel:
882
-
883
- [![SWT Widgets](images/glimmer-swt-widgets.png)](https://www.eclipse.org/swt/widgets/)
884
-
885
- In Glimmer DSL, widgets are declared with lowercase underscored names mirroring their SWT names minus the package name. For example, here are some Glimmer widgets and their SWT counterparts:
886
-
887
- - `shell` instantiates `org.eclipse.swt.widgets.Shell`
888
- - `text` instantiates `org.eclipse.swt.widgets.Text`
889
- - `button` instantiates `org.eclipse.swt.widgets.Button`
890
- - `label` instantiates `org.eclipse.swt.widgets.Label`
891
- - `composite` instantiates `org.eclipse.swt.widgets.Composite`
892
- - `tab_folder` instantiates `org.eclipse.swt.widgets.TabFolder`
893
- - `tab_item` instantiates `org.eclipse.swt.widgets.TabItem`
894
- - `table` instantiates `org.eclipse.swt.widgets.Table`
895
- - `table_column` instantiates `org.eclipse.swt.widgets.TableColumn`
896
- - `tree` instantiates `org.eclipse.swt.widgets.Tree`
897
- - `combo` instantiates `org.eclipse.swt.widgets.Combo`
898
- - `list` instantiates `org.eclipse.swt.widgets.List`
899
-
900
- Every **widget** is sufficiently declared by name, but may optionally be accompanied with:
901
- - SWT **style**/***arguments*** wrapped by parenthesis according to [Glimmer Style Guide](#glimmer-style-guide) (see [next section](#widget-styles) for details).
902
- - Ruby block containing **content**, which may be **properties** (e.g. `enabled false`) or nested **widgets** (e.g. `table_column` nested inside `table`)
903
-
904
- For example, if we were to revisit `samples/hello/hello_world.rb` above (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
905
-
906
- ```ruby
907
- shell {
908
- text "Glimmer"
909
- label {
910
- text "Hello, World!"
911
- }
912
- }.open
913
- ```
914
-
915
- Note that `shell` instantiates the outer shell **widget**, in other words, the window that houses all of the desktop graphical user interface.
916
-
917
- `shell` is then followed by a ***block*** that contains
918
-
919
- ```ruby
920
- # ...
921
- text "Glimmer" # text property of shell
922
- label { # label widget declaration as content of shell
923
- text "Hello, World!" # text property of label
924
- }
925
- # ...
926
- ```
927
-
928
- 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).
929
-
930
- The second line declares the `label` **widget**, which is followed by a Ruby **content** ***block*** that contains its `text` **property** with value `"Hello, World!"`
931
-
932
- 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.
933
-
934
- Example:
935
-
936
- ```ruby
937
- shell {|shell_proxy|
938
- #...
939
- }
940
- ```
941
-
942
- Remember that The `shell` widget is always the outermost widget containing all others in a Glimmer desktop windowed application.
943
-
944
- 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)
945
-
946
- ```ruby
947
- @shell = shell {
948
- # properties and content
949
- # ...
950
- }
951
- @shell.open
952
- ```
953
-
954
- 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)
955
-
956
- Check out the [samples](samples) directory for more examples.
957
-
958
- Example from [hello_tab.rb](samples/hello/hello_tab.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
959
-
960
- ![Hello Tab English](images/glimmer-hello-tab-english.png)
961
-
962
- ![Hello Tab French](images/glimmer-hello-tab-french.png)
963
-
964
- ```ruby
965
- shell {
966
- text "Hello, Tab!"
967
- tab_folder {
968
- tab_item {
969
- text "English"
970
- label {
971
- text "Hello, World!"
972
- }
973
- }
974
- tab_item {
975
- text "French"
976
- label {
977
- text "Bonjour Univers!"
978
- }
979
- }
980
- }
981
- }.open
982
- ```
983
-
984
- If you are new to Glimmer, you have learned enough to start running some [samples](#samples). Go ahead and run all Glimmer [samples](#samples), and come back to read the rest in any order you like since this material is more organized like a reference.
985
-
986
- #### Display
987
-
988
- SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage GUI globally
989
- and access available monitors.
990
- It is automatically instantiated upon first instantiation of a `shell` widget.
991
- Alternatively, for advanced use cases, it can be created explicitly with Glimmer `display` keyword. When a `shell` is later declared, it
992
- automatically uses the display created earlier without having to explicitly hook it.
993
-
994
- ```ruby
995
- @display = display {
996
- cursor_location 300, 300
997
- on_swt_keydown {
998
- # ...
999
- }
1000
- # ...
1001
- }
1002
- @shell = shell { # uses display created above
1003
- }
1004
- ```
1005
- The benefit of instantiating an SWT Display explicitly is to set [Properties](#widget-properties) or [Observers](#observer).
1006
- Although SWT Display is not technically a widget, it has similar APIs in SWT and similar DSL support in Glimmer.
1007
-
1008
- #### SWT Proxies
1009
-
1010
- Glimmer follows Proxy Design Pattern by having Ruby proxy wrappers for all SWT objects:
1011
- - `Glimmer::SWT:WidgetProxy` wraps all descendants of `org.eclipse.swt.widgets.Widget` except the ones that have their own wrappers.
1012
- - `Glimmer::SWT::ShellProxy` wraps `org.eclipse.swt.widgets.Shell`
1013
- - `Glimmer::SWT:TabItemProxy` wraps `org.eclipse.swt.widget.TabItem` (also adds a composite to enable adding content under tab items directly in Glimmer)
1014
- - `Glimmer::SWT:LayoutProxy` wraps all descendants of `org.eclipse.swt.widget.Layout`
1015
- - `Glimmer::SWT:LayoutDataProxy` wraps all layout data objects
1016
- - `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying GUI)
1017
- - `Glimmer::SWT:ColorProxy` wraps `org.eclipse.swt.graphics.Color`
1018
- - `Glimmer::SWT:FontProxy` wraps `org.eclipse.swt.graphics.Font`
1019
- - `Glimmer::SWT::WidgetListenerProxy` wraps all widget listeners
1020
-
1021
- These proxy objects have an API and provide some convenience methods, some of which are mentioned below.
1022
-
1023
- ##### `#content { ... }`
1024
-
1025
- Glimmer allows re-opening any widget and adding properties or extra content after it has been constructed already by using the `#content` method.
1026
-
1027
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1028
-
1029
- ```ruby
1030
- @shell = shell {
1031
- text "Application"
1032
- row_layout
1033
- @label1 = label {
1034
- text "Hello,"
1035
- }
1036
- }
1037
- @shell.content {
1038
- minimum_size 130, 130
1039
- label {
1040
- text "World!"
1041
- }
1042
- }
1043
- @label1.content {
1044
- foreground :red
1045
- }
1046
- @shell.open
1047
- ```
1048
-
1049
- ##### `message_box`
1050
-
1051
- 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.
1052
-
1053
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1054
-
1055
- ```ruby
1056
- include Glimmer
1057
-
1058
- @shell = shell {
1059
- text 'Hello, Message Box!'
1060
- button {
1061
- text 'Please Click To Win a Surprise'
1062
- on_widget_selected {
1063
- message_box(@shell) {
1064
- text 'Surprise'
1065
- message "Congratulations!\n\nYou have won $1,000,000!"
1066
- }.open
1067
- }
1068
- }
1069
- }
1070
- @shell.open
1071
- ```
1072
-
1073
- ![Hello Message Box Dialog](images/glimmer-hello-message-box-dialog.png)
1074
-
1075
- It is also possible to use `message_box` even before instantiating the first `shell` ([Glimmer](https://rubygems.org/gems/glimmer) builds a throwaway `shell` parent automatically for it):
1076
-
1077
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1078
-
1079
- ```ruby
1080
- include Glimmer
1081
-
1082
- message_box {
1083
- text 'Greeting'
1084
- message "Hello, World!"
1085
- }.open
1086
- ```
1087
-
1088
- ##### `#swt_widget`
1089
-
1090
- 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.
1091
-
1092
- ##### Shell widget proxy methods
1093
-
1094
- Shell widget proxy has extra methods specific to SWT Shell:
1095
- - `#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.
1096
- - `#show`: Alias for `#open`
1097
- - `#hide`: Hides a shell setting "visible" property to false
1098
- - `#close`: Closes the shell
1099
- - `#center`: Centers the shell within monitor it is in
1100
- - `#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.
1101
- - `#visible?`: Returns whether a shell is visible
1102
- - `#opened_before?`: Returns whether a shell has been opened at least once before (additionally implying the SWT Event Loop has been started already)
1103
- - `#visible=`: Setting to true opens/shows shell. Setting to false hides the shell.
1104
- - `#pack`: Packs contained widgets using SWT's `Shell#pack` method
1105
- - `#pack_same_size`: Packs contained widgets without changing shell's size when widget sizes change
1106
-
1107
- ##### Shell Icon
1108
-
1109
- To set the shell icon, simply set the `image` property under the `shell` widget. This shows up in the operating system toolbar and app-switcher (e.g. CMD+TAB) (and application window top-left corner in Windows)
1110
-
1111
- Example:
1112
-
1113
- ```ruby
1114
- shell {
1115
- # ...
1116
- image 'path/to/image.png'
1117
- # ...
1118
- }
1119
- ```
1120
-
1121
- ###### Shell Icon Tip for Packaging on Windows
1122
-
1123
- When setting shell icon for a [packaged](#packaging--distribution) app, which has a JAR file at its core, you can reference the `ico` file that ships with the app by going one level up (e.g. `'../AppName.ico'`)
1124
-
1125
- #### Dialog
1126
-
1127
- 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.
1128
-
1129
- 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.
1130
-
1131
- #### Menus
1132
-
1133
- Glimmer DSL provides support for SWT Menu and MenuItem widgets.
1134
-
1135
- There are 2 main types of menus in SWT:
1136
- - Menu Bar (shows up on top)
1137
- - Pop Up Context Menu (shows up when right-clicking a widget)
1138
-
1139
- Underneath both types, there can be a 3rd menu type called Drop Down.
1140
-
1141
- 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.
1142
-
1143
- The ampersand symbol indicates the keyboard shortcut key for the menu item (e.g. '&Help' can be triggered on Windows by hitting ALT+H)
1144
-
1145
- Example of a Menu Bar (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1146
-
1147
- ```ruby
1148
- shell { |shell_proxy|
1149
- text 'Hello, Menu Bar!'
1150
- grid_layout
1151
- label(:center) {
1152
- font height: 16
1153
- text 'Check Out The File Menu and History Menu in The Menu Bar Above!'
1154
- }
1155
- menu_bar {
1156
- menu {
1157
- text '&File'
1158
- menu_item {
1159
- text 'E&xit'
1160
- }
1161
- menu_item(0) {
1162
- text '&New'
1163
- on_widget_selected {
1164
- message_box(shell_proxy) {
1165
- text 'New File'
1166
- message 'New File Contents'
1167
- }.open
1168
- }
1169
- }
1170
- menu(1) {
1171
- text '&Options'
1172
- menu_item(:radio) {
1173
- text 'Option 1'
1174
- }
1175
- menu_item(:separator)
1176
- menu_item(:check) {
1177
- text 'Option 3'
1178
- }
1179
- }
1180
- }
1181
- menu {
1182
- text '&History'
1183
- menu {
1184
- text '&Recent'
1185
- menu_item {
1186
- text 'File 1'
1187
- on_widget_selected {
1188
- message_box(shell_proxy) {
1189
- text 'File 1'
1190
- message 'File 1 Contents'
1191
- }.open
1192
- }
1193
- }
1194
- menu_item {
1195
- text 'File 2'
1196
- on_widget_selected {
1197
- message_box(shell_proxy) {
1198
- text 'File 2'
1199
- message 'File 2 Contents'
1200
- }.open
1201
- }
1202
- }
1203
- }
1204
- }
1205
- }
1206
- }.open
1207
- ```
1208
-
1209
- Example of a Pop Up Context Menu (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1210
-
1211
- ```ruby
1212
- shell { |shell_proxy|
1213
- text 'Hello, Pop Up Context Menu!'
1214
- grid_layout
1215
- label {
1216
- font height: 16
1217
- text 'Right-Click To Pop Up a Context Menu'
1218
- menu {
1219
- menu {
1220
- text '&History'
1221
- menu {
1222
- text '&Recent'
1223
- menu_item {
1224
- text 'File 1'
1225
- on_widget_selected {
1226
- message_box(shell_proxy) {
1227
- text 'File 1'
1228
- message 'File 1 Contents'
1229
- }.open
1230
- }
1231
- }
1232
- menu_item {
1233
- text 'File 2'
1234
- on_widget_selected {
1235
- message_box(shell_proxy) {
1236
- text 'File 2'
1237
- message 'File 2 Contents'
1238
- }.open
1239
- }
1240
- }
1241
- }
1242
- }
1243
- }
1244
- }
1245
- }.open
1246
- ```
1247
-
1248
- #### ScrolledComposite
1249
-
1250
- Glimmer provides smart defaults for the `scrolled_composite` widget by:
1251
- - Automatically setting the nested widget as its content (meaning use can just like a plain old `composite` to add scrolling)
1252
- - 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 )
1253
- - Automatically setting the expand horizontal and expand vertical SWT properties to `true`
1254
-
1255
- ### Widget Styles
1256
-
1257
- SWT widgets receive `SWT` styles in their constructor as per this guide:
1258
-
1259
- https://wiki.eclipse.org/SWT_Widget_Style_Bits
1260
-
1261
- 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`).
1262
-
1263
- These styles customize widget look, feel, and behavior.
1264
-
1265
- Example:
1266
-
1267
- ```ruby
1268
- # ...
1269
- list(:multi) { # SWT styles go inside ()
1270
- # ...
1271
- }
1272
- # ...
1273
- ```
1274
- Passing `:multi` to `list` widget enables list element multi-selection.
1275
-
1276
- ```ruby
1277
- # ...
1278
- composite(:border) { # SWT styles go inside ()
1279
- # ...
1280
- }
1281
- # ...
1282
- ```
1283
- Passing `:border` to `composite` widget ensures it has a border.
1284
-
1285
- When you need to pass in **multiple SWT styles**, simply separate by commas.
1286
-
1287
- Example:
1288
-
1289
- ```ruby
1290
- # ...
1291
- text(:center, :border) { # Multiple SWT styles separated by comma
1292
- # ...
1293
- }
1294
- # ...
1295
- ```
1296
-
1297
- 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):
1298
-
1299
- - `text(:border)`
1300
- - `table(:border, :virtual, :full_selection)`
1301
- - `tree(:border, :virtual, :v_scroll, :h_scroll)`
1302
- - `spinner(:border)`
1303
- - `list(:border, :v_scroll)`
1304
- - `button(:push)`
1305
-
1306
- You may check out all available `SWT` styles here:
1307
-
1308
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
1309
-
1310
- #### Explicit SWT Style Bit
1311
-
1312
- 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`.
1313
-
1314
- 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.
1315
- Example:
1316
-
1317
- ```ruby
1318
- style = swt(:border, :v_scroll)
1319
- ```
1320
-
1321
- #### Negative SWT Style Bits
1322
-
1323
- In rare occasions, you might need to apply & with a negative (not) style bit to negate it from another style bit that includes it.
1324
- Glimmer facilitates that by declaring the negative style bit via postfixing a symbol with `!`.
1325
-
1326
- Example:
1327
-
1328
- ```ruby
1329
- style = swt(:shell_trim, :max!) # creates a shell trim style without the maximize button (negated)
1330
- ```
1331
-
1332
- #### Extra SWT Styles
1333
-
1334
- ##### Non-resizable Window
1335
-
1336
- 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!)`.
1337
-
1338
- Glimmer makes this easier by alternatively offering a `:no_resize` extra SWT style, added for convenience.
1339
- This makes declaring a non-resizable window as easy as:
1340
-
1341
- ```ruby
1342
- shell(:no_resize) {
1343
- # ...
1344
- }
1345
- ```
1346
-
1347
- ### Widget Properties
1348
-
1349
- 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:
1350
-
1351
- https://help.eclipse.org/2019-12/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_controls.htm?cp=2_0_7_0_0
1352
-
1353
-
1354
- Code examples:
1355
-
1356
- ```ruby
1357
- # ...
1358
- label {
1359
- text "Hello, World!" # SWT properties go inside {} block
1360
- }
1361
- # ...
1362
- ```
1363
-
1364
- In the above example, the `label` widget `text` property was set to "Hello, World!".
1365
-
1366
- ```ruby
1367
- # ...
1368
- button {
1369
- enabled bind(@tic_tac_toe_board.box(row, column), :empty)
1370
- }
1371
- # ...
1372
- ```
1373
-
1374
- 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)
1375
-
1376
- #### Color
1377
-
1378
- 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.
1379
-
1380
- Example:
1381
-
1382
- ```ruby
1383
- # ...
1384
- label {
1385
- background rgb(144, 240, 244)
1386
- foreground rgba(38, 92, 232, 255)
1387
- }
1388
- # ...
1389
- ```
1390
-
1391
- SWT also supports standard colors available as constants under the `SWT` namespace with the `COLOR_` prefix (e.g. `SWT::COLOR_BLUE`)
1392
-
1393
- Glimmer supports constructing colors for these constants as lowercase Ruby symbols (with or without `color_` prefix) passed to `color` DSL keyword
1394
-
1395
- Example:
1396
-
1397
- ```ruby
1398
- # ...
1399
- label {
1400
- background color(:black)
1401
- foreground color(:yellow)
1402
- }
1403
- label {
1404
- background color(:color_white)
1405
- foreground color(:color_red)
1406
- }
1407
- # ...
1408
- ```
1409
-
1410
- You may check out all available standard colors in `SWT` over here (having `COLOR_` prefix):
1411
-
1412
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
1413
-
1414
-
1415
- ##### `#swt_color`
1416
-
1417
- 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.
1418
-
1419
- Example:
1420
-
1421
- ```ruby
1422
- color(:black).swt_color # returns SWT Color object
1423
- ```
1424
-
1425
- #### Font
1426
-
1427
- Fonts are represented in Glimmer as a hash of name, height, and style keys.
1428
-
1429
- The style can be one (or more) of 3 values: `:normal`, `:bold`, and `:italic`
1430
-
1431
- Example:
1432
-
1433
- ```ruby
1434
- # ...
1435
- label {
1436
- font name: 'Arial', height: 36, style: :normal
1437
- }
1438
- # ...
1439
- ```
1440
-
1441
- Keys are optional, so some of them may be left off.
1442
- When passing multiple styles, they are included in an array.
1443
-
1444
- Example:
1445
-
1446
- ```ruby
1447
- # ...
1448
- label {
1449
- font style: [:bold, :italic]
1450
- }
1451
- # ...
1452
- ```
1453
-
1454
- You may simply use the standalone `font` keyword without nesting in a parent if there is a need to build a Font object to use in manual SWT programming outside of widget font property setting.
1455
-
1456
- Example:
1457
-
1458
- ```ruby
1459
- @font = font(name: 'Arial', height: 36, style: :normal)
1460
- ```
1461
-
1462
- ### Cursor
1463
-
1464
- SWT widget `cursor` property represents the mouse cursor you see on the screen when you hover over that widget.
1465
-
1466
- The `Display` class provides a way to obtain standard system cursors matching of the SWT style constants starting with prefix `CURSOR_` (e.g. `SWT::CURSOR_HELP` shows a question mark mouse cursor)
1467
-
1468
- Glimmer provides an easier way to obtain and set `cursor` property on a widget by simply mentioning the SWT style constant as an abbreviated symbol excluding the "CURSOR_" suffix.
1469
-
1470
- Example:
1471
-
1472
- ```ruby
1473
- shell {
1474
- minimum_size 128, 128
1475
- cursor :appstarting
1476
- }
1477
- ```
1478
-
1479
- This sets the shell `cursor` to that of `SWT::CURSOR_APPSTARTING`
1480
-
1481
- ### Layouts
1482
-
1483
- Glimmer lays widgets out visually using SWT layouts, which can only be set on composite widget and subclasses.
1484
-
1485
- The most common SWT layouts are:
1486
- - `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.
1487
- - `RowLayout`: lays widgets out horizontally or vertically in varying proportions with advanced spacing/margin/justify options
1488
- - `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.
1489
-
1490
- 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`).
1491
-
1492
- Example:
1493
-
1494
- ```ruby
1495
- # ...
1496
- composite {
1497
- row_layout {
1498
- wrap true
1499
- pack false
1500
- justify true
1501
- type :vertical
1502
- margin_left 1
1503
- margin_top 2
1504
- margin_right 3
1505
- margin_bottom 4
1506
- spacing 5
1507
- }
1508
- # ... widgets follow
1509
- }
1510
- # ...
1511
- ```
1512
-
1513
- 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.
1514
-
1515
- 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`.)
1516
-
1517
- ```ruby
1518
- # ...
1519
- composite {
1520
- row_layout :horizontal
1521
- # ... widgets follow
1522
- }
1523
- # ...
1524
- ```
1525
-
1526
- Here is a more sophisticated example taken from [hello_computed.rb](samples/hello/hello_computed.rb) sample:
1527
-
1528
- ![Hello Computed](images/glimmer-hello-computed.png)
1529
-
1530
- ```ruby
1531
- shell {
1532
- text 'Hello, Computed!'
1533
- composite {
1534
- grid_layout {
1535
- num_columns 2
1536
- make_columns_equal_width true
1537
- horizontal_spacing 20
1538
- vertical_spacing 10
1539
- }
1540
- label {text 'First &Name: '}
1541
- text {
1542
- text bind(@contact, :first_name)
1543
- layout_data {
1544
- horizontal_alignment :fill
1545
- grab_excess_horizontal_space true
1546
- }
1547
- }
1548
- label {text '&Last Name: '}
1549
- text {
1550
- text bind(@contact, :last_name)
1551
- layout_data {
1552
- horizontal_alignment :fill
1553
- grab_excess_horizontal_space true
1554
- }
1555
- }
1556
- label {text '&Year of Birth: '}
1557
- text {
1558
- text bind(@contact, :year_of_birth)
1559
- layout_data {
1560
- horizontal_alignment :fill
1561
- grab_excess_horizontal_space true
1562
- }
1563
- }
1564
- label {text 'Name: '}
1565
- label {
1566
- text bind(@contact, :name, computed_by: [:first_name, :last_name])
1567
- layout_data {
1568
- horizontal_alignment :fill
1569
- grab_excess_horizontal_space true
1570
- }
1571
- }
1572
- label {text 'Age: '}
1573
- label {
1574
- text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
1575
- layout_data {
1576
- horizontal_alignment :fill
1577
- grab_excess_horizontal_space true
1578
- }
1579
- }
1580
- }
1581
- }.open
1582
- ```
1583
-
1584
- Check out the samples directory for more advanced examples of layouts in Glimmer.
1585
-
1586
- **Defaults**:
1587
-
1588
- 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.
1589
-
1590
- Glimmer shell always comes with `fill_layout` having `:horizontal` type.
1591
-
1592
- This is a great guide for learning more about SWT layouts:
1593
-
1594
- https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
1595
-
1596
- Also, for a reference, check the SWT API:
1597
-
1598
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
1599
-
1600
- ### Layout Data
1601
-
1602
- 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.
1603
-
1604
- By convention, SWT layouts expect widgets to set layout data with a class matching their class name with the word "Data" replacing "Layout":
1605
- - `GridLayout` on a composite demands `GridData` on contained widgets
1606
- - `RowLayout` on a composite demands `RowData` on contained widgets
1607
-
1608
- Not all layouts support layout data to further customize widget layouts. For example, `FillLayout` supports no layout data.
1609
-
1610
- 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).
1611
-
1612
- 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)
1613
-
1614
- Glimmer also automatically accepts symbols (e.g. `:fill`) for SWT style arguments like `SWT::FILL`.
1615
-
1616
- Examples:
1617
-
1618
- ```ruby
1619
- # ...
1620
- composite {
1621
- row_layout :horizontal
1622
- label {
1623
- layout_data { # followed by properties
1624
- width 50
1625
- height 30
1626
- }
1627
- }
1628
- # ... more widgets follow
1629
- }
1630
- # ...
1631
- ```
1632
-
1633
- ```ruby
1634
- # ...
1635
- composite {
1636
- grid_layout 3, false # grid layout with 3 columns not of equal width
1637
- label {
1638
- # layout data followed by arguments passed to SWT GridData constructor
1639
- layout_data :fill, :end, true, false
1640
- }
1641
- }
1642
- # ...
1643
- ```
1644
-
1645
- ```ruby
1646
- # ...
1647
- composite {
1648
- grid_layout 3, false # grid layout with 3 columns not of equal width
1649
- label {
1650
- # layout data set explicitly via an object (helps in rare cases that break convention)
1651
- layout_data GridData.new(swt(:fill), swt(:end), true, false)
1652
- }
1653
- }
1654
- # ...
1655
- ```
1656
-
1657
- 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.
1658
-
1659
- **NOTE**: Layout data must never be reused between widgets. Always specify or clone again for every widget.
1660
-
1661
- This is a great guide for learning more about SWT layouts:
1662
-
1663
- https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
1664
-
1665
- Also, for a reference, check the SWT API:
1666
-
1667
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
1668
-
1669
- ### Data-Binding
1670
-
1671
- Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
1672
-
1673
- #### General Examples
1674
-
1675
- `text bind(contact, :first_name)`
1676
-
1677
- This example binds the text property of a widget like `label` to the first name of a contact model.
1678
-
1679
- `text bind(contact, 'address.street')`
1680
-
1681
- This example binds the text property of a widget like `label` to the nested street of
1682
- the address of a contact. This is called nested property data binding.
1683
-
1684
- `text bind(contact, 'address.street', on_read: :upcase, on_write: :downcase)`
1685
-
1686
- 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)
1687
-
1688
- `text bind(contact, 'address.street', on_read: lambda { |s| s[0..10] })`
1689
-
1690
- 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`
1691
-
1692
- `text bind(contact, 'address.street') { |s| s[0..10] }`
1693
-
1694
- 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.
1695
-
1696
- `text bind(contact, 'address.street', read_only: true)
1697
-
1698
- This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
1699
-
1700
- `text bind(contact, 'addresses[1].street')`
1701
-
1702
- 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.
1703
-
1704
- `text bind(contact, :age, computed_by: :date_of_birth)`
1705
-
1706
- This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
1707
-
1708
- `text bind(contact, :name, computed_by: [:first_name, :last_name])`
1709
-
1710
- This example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
1711
-
1712
- `text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
1713
-
1714
- 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`.
1715
-
1716
- Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1717
-
1718
- #### Combo
1719
-
1720
- 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.
1721
-
1722
- When data-binding a `combo` widget, Glimmer can automatically deduce available options from data-bound model by convention: `{attribute_name}_options` method.
1723
-
1724
- ![Hello Combo](images/glimmer-hello-combo.png)
1725
-
1726
- ![Hello Combo](images/glimmer-hello-combo-expanded.png)
1727
-
1728
- ```ruby
1729
- class Person
1730
- attr_accessor :country, :country_options
1731
-
1732
- def initialize
1733
- self.country_options=["", "Canada", "US", "Mexico"]
1734
- self.country = "Canada"
1735
- end
1736
-
1737
- def reset_country
1738
- self.country = "Canada"
1739
- end
1740
- end
1741
-
1742
- class HelloCombo
1743
- include Glimmer
1744
- def launch
1745
- person = Person.new
1746
- shell {
1747
- composite {
1748
- combo(:read_only) {
1749
- selection bind(person, :country)
1750
- }
1751
- button {
1752
- text "Reset"
1753
- on_widget_selected do
1754
- person.reset_country
1755
- end
1756
- }
1757
- }
1758
- }.open
1759
- end
1760
- end
1761
-
1762
- HelloCombo.new.launch
1763
- ```
1764
-
1765
- `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.
1766
-
1767
- #### List
1768
-
1769
- Example from [samples/hello/hello_list_single_selection.rb](samples/hello_list_single_selection.rb) sample:
1770
-
1771
- ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
1772
-
1773
- ```ruby
1774
- shell {
1775
- composite {
1776
- list {
1777
- selection bind(person, :country)
1778
- }
1779
- button {
1780
- text "Reset"
1781
- on_widget_selected do
1782
- person.reset_country
1783
- end
1784
- }
1785
- }
1786
- }.open
1787
- ```
1788
-
1789
- `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).
1790
-
1791
- 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.
1792
-
1793
- 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)):
1794
-
1795
- ![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
1796
-
1797
- ```ruby
1798
- class Person
1799
- attr_accessor :provinces, :provinces_options
1800
-
1801
- def initialize
1802
- self.provinces_options=[
1803
- "",
1804
- "Quebec",
1805
- "Ontario",
1806
- "Manitoba",
1807
- "Saskatchewan",
1808
- "Alberta",
1809
- "British Columbia",
1810
- "Nova Skotia",
1811
- "Newfoundland"
1812
- ]
1813
- self.provinces = ["Quebec", "Manitoba", "Alberta"]
1814
- end
1815
-
1816
- def reset_provinces
1817
- self.provinces = ["Quebec", "Manitoba", "Alberta"]
1818
- end
1819
- end
1820
-
1821
- class HelloListMultiSelection
1822
- include Glimmer
1823
- def launch
1824
- person = Person.new
1825
- shell {
1826
- composite {
1827
- list(:multi) {
1828
- selection bind(person, :provinces)
1829
- }
1830
- button {
1831
- text "Reset"
1832
- on_widget_selected do
1833
- person.reset_provinces
1834
- end
1835
- }
1836
- }
1837
- }.open
1838
- end
1839
- end
1840
-
1841
- HelloListMultiSelection.new.launch
1842
- ```
1843
-
1844
- 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`.
1845
-
1846
- 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).
1847
-
1848
- You may learn more about Glimmer's data-binding syntax by reading the code under the [samples](samples) directory.
1849
-
1850
- #### Table
1851
-
1852
- The SWT Tree widget renders a multi-column data table, such as a contact listing or a sales report.
1853
-
1854
- To data-bind a Table, you need the main model, the collection property, and the text display attribute for each table column.
1855
-
1856
- This involves using the `bind` keyword mentioned above in addition to a special `column_properties` keyword that takes the table column text attribute methods.
1857
-
1858
- It assumes you have defined the table columns via `table_column` widget.
1859
-
1860
- Example:
1861
-
1862
- ```ruby
1863
- shell {
1864
- @table = table {
1865
- table_column {
1866
- text "Name"
1867
- width 120
1868
- }
1869
- table_column {
1870
- text "Age"
1871
- width 120
1872
- }
1873
- table_column {
1874
- text "Adult"
1875
- width 120
1876
- }
1877
- items bind(group, :people), column_properties(:name, :age, :adult)
1878
- selection bind(group, :selected_person)
1879
- on_mouse_up { |event|
1880
- @table.edit_table_item(event.table_item, event.column_index)
1881
- }
1882
- }
1883
- }
1884
- ```
1885
-
1886
- The code above includes two data-bindings:
1887
- - 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.
1888
- - 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)
1889
- - 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.)
1890
-
1891
- Additionally, Table `items` data-binding automatically stores each node model unto the SWT TableItem object via `setData` method. This enables things like searchability.
1892
-
1893
- The table widget in Glimmer is represented by a subclass of `WidgetProxy` called `TableProxy`.
1894
- TableProxy includes a `search` method that takes a block to look for a table item.
1895
-
1896
- Example:
1897
-
1898
- ```ruby
1899
- found_array = @table.search { |table_item| table_item.getData == company.owner }
1900
- ```
1901
-
1902
- 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.
1903
-
1904
- 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)`.
1905
- This automatically leverages the SWT TableEditor custom class behind the scenes, displaying a text widget to the user to change the selected or
1906
- passed table item text into something else.
1907
- It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
1908
-
1909
- ##### Table Sorting
1910
-
1911
- Glimmer automatically adds sorting support to the SWT `Table` widget.
1912
-
1913
- Check out the [Contact Manager](#contact-manager) sample for an example.
1914
- You may click on any column and it will sort by ascending order first and descending if you click again.
1915
-
1916
- Glimmer automatic table sorting supports `String`, `Integer`, and `Float` columns out of the box as well as any column data that is comparable.
1917
-
1918
- 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`.
1919
-
1920
- Should you have a special data type that could not be compared automatically, Glimmer offers the following 3 alternatives for custom sorting:
1921
- - `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`).
1922
- - `sort_by(&block)`: this works just like Ruby `Enumerable` `sort_by`. The block receives the table column data as argument.
1923
- - `sort(&comparator)`: this works just like Ruby `Enumerable` `sort`. The comparator block receives two objects from the table column data.
1924
-
1925
- 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)
1926
-
1927
- Example:
1928
-
1929
- ```ruby
1930
- # ...
1931
- table {
1932
- table_column {
1933
- text 'Task'
1934
- width 120
1935
- }
1936
- table_column {
1937
- text 'Project'
1938
- width 120
1939
- }
1940
- table_column {
1941
- text 'Duration (hours)'
1942
- width 120
1943
- sort_property :duration_in_hours
1944
- }
1945
- table_column {
1946
- text 'Priority'
1947
- width 120
1948
- sort_by { |value| ['High', 'Medium', 'Low'].index(value) }
1949
- }
1950
- table_column {
1951
- text 'Start Date'
1952
- width 120
1953
- sort { |d1, d2| d1.to_date <=> d2.to_date }
1954
- }
1955
- additional_sort_properties :project_name, :duration_in_hours, :name
1956
- items bind(Task, :list), column_properties(:name, :project_name, :duration, :priority, :start_date)
1957
- # ...
1958
- }
1959
- # ...
1960
- ```
1961
-
1962
- Here is an explanation of the example above:
1963
- - Task and Project table columns are data-bound to the `:name` and `:project_name` properties and sorted through them automatically
1964
- - Task Duration table column is data-bound to the `:duration` property, but sorted via the `:duration_in_hours` property instead
1965
- - Task Priority table column has a custom sort_by block
1966
- - Task Start Date table column has a custom sort comparator block
1967
- - Additional (secondary) sort properties are applied when sorting by Task, Project, or Duration in the order specified
1968
-
1969
-
1970
- #### Tree
1971
-
1972
- The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
1973
-
1974
- To data-bind a Tree, you need the root model, the children querying method, and the text display attribute on each child.
1975
-
1976
- This involves using the `bind` keyword mentioned above in addition to a special `tree_properties` keyword that takes the children and text attribute methods.
1977
-
1978
- Example:
1979
-
1980
- ```ruby
1981
- shell {
1982
- @tree = tree {
1983
- items bind(company, :owner), tree_properties(children: :coworkers, text: :name)
1984
- selection bind(company, :selected_coworker)
1985
- }
1986
- }
1987
- ```
1988
-
1989
- The code above includes two data-bindings:
1990
- - 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.
1991
- - Tree `selection`, which binds the single tree item selected by the user to the attribute denoted by the `bind` keyword
1992
-
1993
- Additionally, Tree `items` data-binding automatically stores each node model unto the SWT TreeItem object via `setData` method. This enables things like searchability.
1994
-
1995
- The tree widget in Glimmer is represented by a subclass of `WidgetProxy` called `TreeProxy`.
1996
- TreeProxy includes a `depth_first_search` method that takes a block to look for a tree item.
1997
-
1998
- Example:
1999
-
2000
- ```ruby
2001
- found_array = @tree.depth_first_search { |tree_item| tree_item.getData == company.owner }
2002
- ```
2003
-
2004
- 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.
2005
-
2006
- 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
2007
- 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.
2008
-
2009
- ### Observer
2010
-
2011
- 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.
2012
-
2013
- #### Observing Widgets
2014
-
2015
- Glimmer supports observing widgets with two main types of events:
2016
- 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`).
2017
- 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)
2018
-
2019
- Additionally, there are two more types of events:
2020
- - SWT `display` supports global listeners called filters that run on any widget. They are hooked via `on_swt_{swt-event-constant}`
2021
- - SWT `display` supports Mac application menu item observers (`on_about` and `on_preferences`), which you can read about under [Miscellaneous](#miscellaneous).
2022
-
2023
- 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.
2024
-
2025
- **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".
2026
-
2027
- For example, if you look at the `Button` SWT API:
2028
- https://help.eclipse.org/2019-12/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fswt%2Fbrowser%2FBrowser.html
2029
-
2030
- 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`
2031
-
2032
- 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.
2033
-
2034
- 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.
2035
-
2036
- Instead, simply add a `on_widget_selected` followed by a Ruby block containing the logic to perform. Glimmer figures out the rest.
2037
-
2038
- Let's revisit the Tic Tac Toe example shown near the beginning of the page:
2039
-
2040
- ```ruby
2041
- shell {
2042
- text "Tic-Tac-Toe"
2043
- minimum_size 150, 178
2044
- composite {
2045
- grid_layout 3, true
2046
- (1..3).each { |row|
2047
- (1..3).each { |column|
2048
- button {
2049
- layout_data :fill, :fill, true, true
2050
- text bind(@tic_tac_toe_board[row, column], :sign)
2051
- enabled bind(@tic_tac_toe_board[row, column], :empty)
2052
- on_widget_selected {
2053
- @tic_tac_toe_board.mark(row, column)
2054
- }
2055
- }
2056
- }
2057
- }
2058
- }
2059
- }
2060
- ```
2061
-
2062
- 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.
2063
-
2064
- 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.
2065
-
2066
- **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:
2067
-
2068
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
2069
-
2070
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2071
-
2072
- `SWT.Show` - hooks a listener for showing a widget (using `on_swt_show` in Glimmer)
2073
- `SWT.Hide` - hooks a listener for hiding a widget (using `on_swt_hide` in Glimmer)
2074
-
2075
- ```ruby
2076
- shell {
2077
- @button1 = button {
2078
- text "Show 2nd Button"
2079
- visible true
2080
- on_swt_show {
2081
- @button2.swt_widget.setVisible(false)
2082
- }
2083
- on_widget_selected {
2084
- @button2.swt_widget.setVisible(true)
2085
- }
2086
- }
2087
- @button2 = button {
2088
- text "Show 1st Button"
2089
- visible false
2090
- on_swt_show {
2091
- @button1.swt_widget.setVisible(false)
2092
- }
2093
- on_widget_selected {
2094
- @button1.swt_widget.setVisible(true)
2095
- }
2096
- }
2097
- }.open
2098
- ```
2099
-
2100
- **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`
2101
-
2102
- ##### Alternative Syntax
2103
-
2104
- 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.
2105
-
2106
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2107
-
2108
- ```
2109
- @shell = shell {
2110
- label {
2111
- text "Hello, World!"
2112
- }
2113
- }
2114
- @shell.on_shell_iconified {
2115
- @shell.close
2116
- }
2117
- @shell.open
2118
- ```
2119
-
2120
- 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.
2121
-
2122
- 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.
2123
-
2124
- #### Observing Models
2125
-
2126
- 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.
2127
-
2128
- ```ruby
2129
- class TicTacToe
2130
- include Glimmer
2131
-
2132
- def initialize
2133
- # ...
2134
- observe(@tic_tac_toe_board, :game_status) { |game_status|
2135
- display_win_message if game_status == Board::WIN
2136
- display_draw_message if game_status == Board::DRAW
2137
- }
2138
- end
2139
- # ...
2140
- end
2141
- ```
2142
-
2143
- 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).
2144
-
2145
- Look at [`samples/elaborate/tictactoe/tic_tac_toe.rb`](samples/tictactoe/tic_tac_toe.rb) for more details starting with the code included below.
2146
-
2147
- ```ruby
2148
- class TicTacToe
2149
- include Glimmer
2150
- include Observer
2151
-
2152
- def initialize
2153
- # ...
2154
- observe(@tic_tac_toe_board, :game_status) { |game_status|
2155
- display_win_message if game_status == Board::WIN
2156
- display_draw_message if game_status == Board::DRAW
2157
- }
2158
- end
2159
-
2160
- def display_win_message
2161
- display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
2162
- end
2163
-
2164
- def display_draw_message
2165
- display_game_over_message("Draw!")
2166
- end
2167
-
2168
- def display_game_over_message(message)
2169
- message_box(@shell) {
2170
- text 'Game Over'
2171
- message message_text
2172
- }.open
2173
- @tic_tac_toe_board.reset
2174
- end
2175
- # ...
2176
- end
2177
- ```
2178
-
2179
- ### Custom Widgets
2180
-
2181
- Glimmer supports creating custom widgets with minimal code, which automatically extends Glimmer's DSL syntax with an underscored lowercase keyword.
2182
-
2183
- 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 `__`)
2184
-
2185
- #### Simple Example
2186
-
2187
- (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2188
-
2189
- Definition:
2190
- ```ruby
2191
- class RedLabel
2192
- include Glimmer::UI::CustomWidget
2193
-
2194
- body {
2195
- label(swt_style) {
2196
- background :red
2197
- }
2198
- }
2199
- end
2200
- ```
2201
-
2202
- Usage:
2203
- ```ruby
2204
- shell {
2205
- red_label {
2206
- text 'Red Label'
2207
- }
2208
- }.open
2209
- ```
2210
-
2211
- As you can see, `RedLabel` became Glimmer DSL keyword: `red_label`
2212
-
2213
- #### Lifecycle Hook Example
2214
-
2215
- (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2216
-
2217
- Definition:
2218
- ```ruby
2219
- module Red
2220
- class Composite
2221
- include Glimmer::UI::CustomWidget
2222
-
2223
- before_body {
2224
- @color = :red
2225
- }
2226
-
2227
- body {
2228
- composite(swt_style) {
2229
- background @color
2230
- }
2231
- }
2232
- end
2233
- end
2234
- ```
2235
-
2236
- Usage:
2237
- ```ruby
2238
- shell {
2239
- red__composite {
2240
- label {
2241
- foreground :white
2242
- text 'This is showing inside a Red Composite'
2243
- }
2244
- }
2245
- }.open
2246
- ```
2247
-
2248
- 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`.
2249
-
2250
- 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.
2251
-
2252
- #### Custom Widget API
2253
-
2254
- Custom Widgets have the following attributes available to call from inside the `#body` method:
2255
- - `#parent`: Glimmer object parenting custom widget
2256
- - `#swt_style`: SWT style integer. Can be useful if you want to allow consumers to customize a widget inside the custom widget body
2257
- - `#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)
2258
- - `#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.
2259
- - `#body_root`: top-most (root) widget returned from `#body` method.
2260
- - `#swt_widget`: actual SWT widget for `body_root`
2261
-
2262
- Additionally, custom widgets can call the following class methods:
2263
- - `::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)
2264
- - `::option(option_name, default: nil)`: declares a single option taking option name and default value as arguments (also generates attribute accessors just like `::options`)
2265
-
2266
- #### Content/Options Example
2267
-
2268
- (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2269
-
2270
- Definition:
2271
- ```ruby
2272
- class Sandwich
2273
- include Glimmer::UI::CustomWidget
2274
-
2275
- options :orientation, :bg_color
2276
- option :fg_color, default: :black
2277
-
2278
- body {
2279
- composite(swt_style) { # gets custom widget style
2280
- fill_layout orientation # using orientation option
2281
- background bg_color # using container_background option
2282
- label {
2283
- text 'SANDWICH TOP'
2284
- }
2285
- content.call # this is where content block is called
2286
- label {
2287
- text 'SANDWICH BOTTOM'
2288
- }
2289
- }
2290
- }
2291
- end
2292
- ```
2293
-
2294
- Usage:
2295
- ```ruby
2296
- shell {
2297
- sandwich(:no_focus, orientation: :vertical, bg_color: :red) {
2298
- label {
2299
- background :green
2300
- text 'SANDWICH CONTENT'
2301
- }
2302
- }
2303
- }.open
2304
- ```
2305
-
2306
- 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'`
2307
-
2308
- #### Custom Widget Lifecycle Hooks
2309
-
2310
- Last but not least, these are the available lifecycle hooks:
2311
- - `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`
2312
- - `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.
2313
-
2314
- #### Gotcha
2315
-
2316
- Beware of defining a custom attribute that is a common SWT widget property name.
2317
- For example, if you define `text=` and `text` methods to accept a custom text and then later you write this body:
2318
-
2319
- ```ruby
2320
- # ...
2321
- def text
2322
- # ...
2323
- end
2324
-
2325
- def text=(value)
2326
- # ...
2327
- end
2328
-
2329
- body {
2330
- composite {
2331
- label {
2332
- text "Hello"
2333
- }
2334
- label {
2335
- text "World"
2336
- }
2337
- }
2338
- }
2339
- # ...
2340
- ```
2341
-
2342
- 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`.
2343
-
2344
- #### Final Notes
2345
-
2346
- 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:
2347
- [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)
2348
-
2349
- ### Custom Shells
2350
-
2351
- 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.
2352
-
2353
- They may also be chained in a wizard fashion.
2354
-
2355
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2356
-
2357
- ```ruby
2358
- class WizardStep
2359
- include Glimmer::UI::CustomShell
2360
-
2361
- options :number, :step_count
2362
-
2363
- before_body {
2364
- @title = "Step #{number}"
2365
- }
2366
-
2367
- body {
2368
- shell {
2369
- text "Wizard - #{@title}"
2370
- minimum_size 200, 100
2371
- fill_layout :vertical
2372
- label(:center) {
2373
- text @title
2374
- font height: 30
2375
- }
2376
- if number < step_count
2377
- button {
2378
- text "Go To Next Step"
2379
- on_widget_selected {
2380
- body_root.hide
2381
- }
2382
- }
2383
- end
2384
- }
2385
- }
2386
- end
2387
-
2388
- shell { |app_shell|
2389
- text "Wizard"
2390
- minimum_size 200, 100
2391
- @current_step_number = 1
2392
- @wizard_steps = 5.times.map { |n|
2393
- wizard_step(number: n+1, step_count: 5) {
2394
- on_swt_hide {
2395
- if @current_step_number < 5
2396
- @current_step_number += 1
2397
- app_shell.hide
2398
- @wizard_steps[@current_step_number - 1].open
2399
- end
2400
- }
2401
- }
2402
- }
2403
- button {
2404
- text "Start"
2405
- font height: 40
2406
- on_widget_selected {
2407
- app_shell.hide
2408
- @wizard_steps[@current_step_number - 1].open
2409
- }
2410
- }
2411
- }.open
2412
- ```
2413
-
2414
- ### Drag and Drop
2415
-
2416
- Glimmer offers Drag and Drop support, thanks to [SWT](https://www.eclipse.org/swt/) and Glimmer's lightweight [DSL syntax](#glimmer-dsl-syntax).
2417
-
2418
- 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)
2419
-
2420
- To get started, simply follow these steps:
2421
- 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)
2422
- 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)
2423
- 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)
2424
- 1. Read `event.data` and consume it (e.g. change widget text) inside the `on_drop` event handler block.
2425
-
2426
- Example (taken from [samples/hello/hello_drag_and_drop.rb](#hello-drag-and-drop) / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2427
-
2428
- ```ruby
2429
- class Location
2430
- attr_accessor :country
2431
-
2432
- def country_options
2433
- %w[USA Canada Mexico Columbia UK Australia Germany Italy Spain]
2434
- end
2435
- end
2436
-
2437
- @location = Location.new
2438
-
2439
- include Glimmer
2440
-
2441
- shell {
2442
- text 'Hello, Drag and Drop!'
2443
- list {
2444
- selection bind(@location, :country)
2445
- on_drag_set_data { |event|
2446
- list = event.widget.getControl
2447
- event.data = list.getSelection.first
2448
- }
2449
- }
2450
- label(:center) {
2451
- text 'Drag a country here!'
2452
- font height: 20
2453
- on_drop { |event|
2454
- event.widget.getControl.setText(event.data)
2455
- }
2456
- }
2457
- }.open
2458
- ```
2459
-
2460
- ![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
2461
-
2462
- Optional steps:
2463
- - 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.
2464
- - Specify `drag_source_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
2465
- - 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)
2466
- - Specify `drop_target_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
2467
- - 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)
2468
- - Set drag operation in `event.detail` (e.g. DND::DROP_COPY) inside `on_drag_enter`
2469
-
2470
- ### Miscellaneous
2471
-
2472
- #### Multi-DSL Support
2473
-
2474
- Glimmer is a DSL engine that supports multiple DSLs (Domain Specific Languages):
2475
- - [SWT](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
2476
- - [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
2477
- - [XML](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML) - Useful with [SWT Browser Widget](#browser-widget)
2478
- - [CSS](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets) - Useful with [SWT Browser Widget](#browser-widget)
2479
-
2480
- 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.
2481
-
2482
- ##### SWT
2483
-
2484
- 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:
2485
- - `shell`
2486
- - `display`
2487
- - `color`
2488
- - `observe`
2489
- - `async_exec`
2490
- - `sync_exec`
2491
-
2492
- ##### Opal
2493
-
2494
- Full instructions are found in the [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) DSL page.
2495
-
2496
- 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.
2497
-
2498
- ##### XML
2499
-
2500
- Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
2501
- Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
2502
-
2503
- Here are all the Glimmer XML DSL top-level keywords:
2504
- - `html`
2505
- - `tag`: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
2506
- - `name_space`: enables namespacing html tags
2507
-
2508
- 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')` )
2509
-
2510
- Example (basic HTML / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2511
-
2512
- ```ruby
2513
- @xml = html {
2514
- head {
2515
- meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
2516
- }
2517
- body {
2518
- h1 { "Hello, World!" }
2519
- }
2520
- }
2521
- puts @xml
2522
- ```
2523
-
2524
- Output:
2525
-
2526
- ```
2527
- <html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
2528
- ```
2529
-
2530
- Example (explicit XML tag / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2531
-
2532
- ```ruby
2533
- puts tag(:_name => "DOCUMENT")
2534
- ```
2535
-
2536
- Output:
2537
-
2538
- ```
2539
- <DOCUMENT/>
2540
- ```
2541
-
2542
- Example (XML namespaces using `name_space` keyword / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2543
-
2544
- ```ruby
2545
- @xml = name_space(:w3c) {
2546
- html(:id => "thesis", :class => "document") {
2547
- body(:id => "main") {
2548
- }
2549
- }
2550
- }
2551
- puts @xml
2552
- ```
2553
-
2554
- Output:
2555
-
2556
- ```
2557
- <w3c:html id="thesis" class="document"><w3c:body id="main"></w3c:body></w3c:html>
2558
- ```
2559
-
2560
- Example (XML namespaces using dot operator / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2561
-
2562
- ```ruby
2563
- @xml = tag(:_name => "DOCUMENT") {
2564
- document.body(document.id => "main") {
2565
- }
2566
- }
2567
- puts @xml
2568
- ```
2569
-
2570
- Output:
2571
-
2572
- ```
2573
- <DOCUMENT><document:body document:id="main"></document:body></DOCUMENT>
2574
- ```
2575
-
2576
- ##### CSS
2577
-
2578
- Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
2579
- Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
2580
-
2581
- `css` is the only top-level keyword in the Glimmer CSS DSL
2582
-
2583
- Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
2584
- Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
2585
-
2586
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2587
-
2588
- ```ruby
2589
- @css = css {
2590
- body {
2591
- font_size '1.1em'
2592
- pv 'background', 'white'
2593
- }
2594
-
2595
- s('body > h1') {
2596
- background_color :red
2597
- pv 'font-size', '2em'
2598
- }
2599
- }
2600
- puts @css
2601
- ```
2602
-
2603
- ##### Listing / Enabling / Disabling DSLs
2604
-
2605
- Glimmer provides a number of methods on Glimmer::DSL::Engine to configure DSL support or inquire about it:
2606
- - `Glimmer::DSL::Engine.dsls`: Lists available Glimmer DSLs
2607
- - `Glimmer::DSL::Engine.disable_dsl(dsl_name)`: Disables a specific DSL. Useful when there is no need for certain DSLs in a certain application.
2608
- - `Glimmer::DSL::Engine.disabled_dsls': Lists disabled DSLs
2609
- - `Glimmer::DSL::Engine.enable_dsl(dsl_name)`: Re-enables disabled DSL
2610
- - `Glimmer::DSL::Engine.enabled_dsls=(dsl_names)`: Disables all DSLs except the ones specified.
2611
-
2612
- #### Application Menu Items (About/Preferences)
2613
-
2614
- Mac applications always have About and Preferences menu items. Glimmer provides widget observer hooks for them on the `display`:
2615
- - `on_about`: executes code when user selects App Name -> About
2616
- - `on_preferences`: executes code when user selects App Name -> Preferences or hits 'CMD+,' on the Mac
2617
-
2618
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2619
-
2620
- ```ruby
2621
- class Example
2622
- def initialize
2623
- display {
2624
- on_about {
2625
- message_box(@shell_proxy) {
2626
- text 'About'
2627
- message 'About Application'
2628
- }.open
2629
- }
2630
- on_preferences {
2631
- preferences_dialog = dialog {
2632
- text 'Preferences'
2633
- row_layout {
2634
- type :vertical
2635
- margin_left 15
2636
- margin_top 15
2637
- margin_right 15
2638
- margin_bottom 15
2639
- }
2640
- label {
2641
- text 'Check one of these options:'
2642
- }
2643
- button(:radio) {
2644
- text 'Option 1'
2645
- }
2646
- button(:radio) {
2647
- text 'Option 2'
2648
- }
2649
- }
2650
- preferences_dialog.open
2651
- }
2652
- }
2653
- @shell_proxy = shell {
2654
- text 'Application Menu Items'
2655
- fill_layout {
2656
- margin_width 15
2657
- margin_height 15
2658
- }
2659
- label {
2660
- text 'Application Menu Items'
2661
- font height: 30
2662
- }
2663
- }
2664
- @shell_proxy.open
2665
- end
2666
- end
2667
-
2668
- Example.new
2669
- ```
2670
-
2671
- #### App Name and Version
2672
-
2673
- 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.
2674
-
2675
- 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).
2676
-
2677
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2678
-
2679
- ```ruby
2680
- Display.setAppName('Glimmer Demo')
2681
-
2682
- shell(:no_resize) {
2683
- text "Glimmer"
2684
- label {
2685
- text "Hello, World!"
2686
- }
2687
- }.open
2688
- ```
2689
-
2690
- 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.
2691
-
2692
- #### Video Widget
2693
-
2694
- [![Video Widget](images/glimmer-video-widget.png)](https://github.com/AndyObtiva/glimmer-cw-video)
2695
-
2696
- Glimmer supports a [video custom widget](https://github.com/AndyObtiva/glimmer-cw-video) not in SWT.
2697
-
2698
- You may obtain via `glimmer-cw-video` gem.
2699
-
2700
- #### Browser Widget
2701
-
2702
- ![Hello Browser](images/glimmer-hello-browser.png)
2703
-
2704
- 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).
2705
-
2706
- Example loading a URL (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2707
-
2708
- ```ruby
2709
- shell {
2710
- minimum_size 1024, 860
2711
- browser {
2712
- url 'http://brightonresort.com/about'
2713
- }
2714
- }.open
2715
- ```
2716
-
2717
- 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)):
2718
-
2719
- ```ruby
2720
- shell {
2721
- minimum_size 130, 130
2722
- @browser = browser {
2723
- text html {
2724
- head {
2725
- meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
2726
- }
2727
- body {
2728
- h1 { "Hello, World!" }
2729
- }
2730
- }
2731
- on_completed { # on load of the page execute this JavaScript
2732
- @browser.swt_widget.execute("alert('Hello, World!');")
2733
- }
2734
- }
2735
- }.open
2736
- ```
2737
-
2738
- 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).
2739
-
2740
- ## Glimmer Configuration
2741
-
2742
- Glimmer configuration may be done via the `Glimmer::Config` module.
2743
-
2744
- ### logger
2745
-
2746
- The Glimmer DSL engine supports logging via a standard `STDOUT` Ruby `Logger` configured in the `Glimmer::Config.logger` config option.
2747
- It is set to level Logger::ERROR by default.
2748
- Log level may be adjusted via `Glimmer::Config.logger.level` just like any other Ruby Logger.
2749
-
2750
- Example:
2751
-
2752
- ```ruby
2753
- Glimmer::Config.logger.level = :debug
2754
- ```
2755
- This results in more verbose debug loggging to `STDOUT`, which is very helpful in troubleshooting Glimmer DSL syntax when needed.
2756
-
2757
- Example log:
2758
- ```
2759
- D, [2017-07-21T19:23:12.587870 #35707] DEBUG -- : method: shell and args: []
2760
- D, [2017-07-21T19:23:12.594405 #35707] DEBUG -- : ShellCommandHandler will handle command: shell with arguments []
2761
- D, [2017-07-21T19:23:12.844775 #35707] DEBUG -- : method: composite and args: []
2762
- D, [2017-07-21T19:23:12.845388 #35707] DEBUG -- : parent is a widget: true
2763
- D, [2017-07-21T19:23:12.845833 #35707] DEBUG -- : on listener?: false
2764
- D, [2017-07-21T19:23:12.864395 #35707] DEBUG -- : WidgetCommandHandler will handle command: composite with arguments []
2765
- D, [2017-07-21T19:23:12.864893 #35707] DEBUG -- : widget styles are: []
2766
- D, [2017-07-21T19:23:12.874296 #35707] DEBUG -- : method: list and args: [:multi]
2767
- D, [2017-07-21T19:23:12.874969 #35707] DEBUG -- : parent is a widget: true
2768
- D, [2017-07-21T19:23:12.875452 #35707] DEBUG -- : on listener?: false
2769
- D, [2017-07-21T19:23:12.878434 #35707] DEBUG -- : WidgetCommandHandler will handle command: list with arguments [:multi]
2770
- D, [2017-07-21T19:23:12.878798 #35707] DEBUG -- : widget styles are: [:multi]
2771
- ```
2772
-
2773
- The `logger` instance may be replaced with a custom logger via `Glimmer::Config.logger = custom_logger`
2774
-
2775
- To reset `logger` to the default instance, you may call `Glimmer::Config.reset_logger!`
2776
-
2777
- 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.
2778
-
2779
- [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) enhances Glimmer default logging support via the Ruby [`logging`](https://github.com/TwP/logging) gem, enabling buffered asynchronous logging in a separate thread, thus completely unhindering normal desktop app performance.
2780
-
2781
- Other config options related to the [`logging`](https://github.com/TwP/logging) gem are mentioned below.
2782
-
2783
- #### logging_devices
2784
-
2785
- This is an array of these possible values: `:stdout` (default), `:stderr`, `:file`, `:syslog` (default), `:stringio`
2786
-
2787
- It defaults to `[:stdout, :syslog]`
2788
-
2789
- When `:file` is included, Glimmer creates a 'log' directory directly below the Glimmer app local directory.
2790
- It may also be customized further via the `logging_device_file_options` option.
2791
- This is useful on Windows as an alternative to `syslog`, which is not available on Windows by default.
2792
-
2793
- #### logging_device_file_options
2794
-
2795
- This is a hash of [`logging`](https://github.com/TwP/logging) gem options for the `:file` logging device.
2796
-
2797
- Default: `{size: 1_000_000, age: 'daily', roll_by: 'number'}`
2798
-
2799
- That ensures splitting log files at the 1MB size and daily, rolling them by unique number.
2800
-
2801
- #### logging_appender_options
2802
-
2803
- Appender options is a hash passed as options to every appender (logging device) used in the [`logging`](https://github.com/TwP/logging) gem.
2804
-
2805
- Default: `{async: true, auto_flushing: 500, write_size: 500, flush_period: 60, immediate_at: [:error, :fatal], layout: logging_layout}`
2806
-
2807
- That ensures asynchronous buffered logging that is flushed every 500 messages and 60 seconds, or immediately at error and fatal log levels
2808
-
2809
- #### logging_layout
2810
-
2811
- This is a [`logging`](https://github.com/TwP/logging) gem layout that formats the logging output.
2812
-
2813
- Default:
2814
-
2815
- ```
2816
- Logging.layouts.pattern(
2817
- pattern: '[%d] %-5l %c: %m\n',
2818
- date_pattern: '%Y-%m-%d %H:%M:%S'
2819
- )
2820
- ```
2821
-
2822
- ### import_swt_packages
2823
-
2824
- 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.
2825
-
2826
- As a result, you may call SWT Java classes from Glimmer Ruby code without mentioning Java package references explicitly.
2827
-
2828
- For example, `org.eclipse.swt.graphics.Color` can be referenced as just `Color`
2829
-
2830
- 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:
2831
- ```
2832
- org.eclipse.swt.*
2833
- org.eclipse.swt.widgets.*
2834
- org.eclipse.swt.layout.*
2835
- org.eclipse.swt.graphics.*
2836
- org.eclipse.swt.browser.*
2837
- org.eclipse.swt.custom.*
2838
- org.eclipse.swt.dnd.*
2839
- ```
2840
-
2841
- 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).
2842
-
2843
- Example:
2844
-
2845
- ```ruby
2846
- Glimmer::Config.import_swt_packages += [
2847
- 'org.eclipse.nebula.widgets.ganttchart'
2848
- ]
2849
- ```
2850
-
2851
- 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)
2852
-
2853
- Nonetheless, you can disable automatic Java package import if needed via this Glimmer configuration option:
2854
-
2855
- ```ruby
2856
- Glimmer::Config.import_swt_packages = false
2857
- ```
2858
-
2859
- Once disabled, to import SWT Java packages manually, you may simply:
2860
-
2861
- 1. `include Glimmer::SWT::Packages`: lazily imports all SWT Java packages to your class, lazy-loading SWT Java class constants on first reference.
2862
-
2863
- 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'`)
2864
-
2865
- 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.
2866
-
2867
- You can learn more about importing Java packages into Ruby code at this JRuby WIKI page:
2868
-
2869
- https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby
2870
-
2871
- ### loop_max_count
2872
-
2873
- Glimmer has infinite loop detection support.
2874
- It can detect when an infinite loop is about to occur in method_missing and stops it.
2875
- It detects potential infinite loops when the same keyword and args repeat more than 100 times, which is unusual in a GUI app.
2876
-
2877
- The max limit can be changed via the `Glimmer::Config::loop_max_count=(count)` config option.
2878
-
2879
- Infinite loop detection may be disabled altogether if needed by setting `Glimmer::Config::loop_max_count` to `-1`
2880
-
2881
- ### excluded_keyword_checkers
2882
-
2883
- Glimmer permits consumers to exclude keywords from DSL processing by its engine via the `excluded_keyword_checkers` config option.
2884
-
2885
- To do so, add a proc to it that returns a boolean indicating if a keyword is excluded or not.
2886
-
2887
- 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.
2888
-
2889
- Example of keywords excluded by [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt):
2890
-
2891
- ```ruby
2892
- Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
2893
- method = method_symbol.to_s
2894
- result = false
2895
- result ||= method.start_with?('on_swt_') && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
2896
- result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
2897
- result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
2898
- result ||= method == 'post_initialize_child'
2899
- result ||= method.end_with?('=')
2900
- result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
2901
- end
2902
- ```
2903
-
2904
- ## Glimmer Style Guide
2905
-
2906
- - Widgets are declared with underscored lowercase versions of their SWT names minus the SWT package name.
2907
- - Widget declarations may optionally have arguments and be followed by a block (to contain properties and content)
2908
- - Widget blocks are always declared with curly braces
2909
- - Widget arguments are always wrapped inside parentheses
2910
- - Widget properties are declared with underscored lowercase versions of the SWT properties
2911
- - Widget property declarations always have arguments and never take a block
2912
- - Widget property arguments are never wrapped inside parentheses
2913
- - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
2914
- - 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)
2915
- - Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
2916
- - Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
2917
- - 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.
2918
-
2919
- ## SWT Reference
2920
-
2921
- https://www.eclipse.org/swt/docs.php
2922
-
2923
- Here is the SWT API:
2924
-
2925
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
2926
-
2927
- Here is a visual list of SWT widgets:
2928
-
2929
- https://www.eclipse.org/swt/widgets/
2930
-
2931
- Here is a textual list of SWT widgets:
2932
-
2933
- https://help.eclipse.org/2019-12/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_controls.htm?cp=2_0_7_0_0
2934
-
2935
- Here is a list of SWT style bits as used in widget declaration:
2936
-
2937
- https://wiki.eclipse.org/SWT_Widget_Style_Bits
2938
-
2939
- Here is a SWT style bit constant reference:
2940
-
2941
- https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
2942
-
2943
- Here is an SWT Drag and Drop guide:
2944
-
2945
- https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
2946
-
2947
- Here is an SWT Custom Widget guide:
2948
-
2949
- https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm
2950
-
2951
- ## Samples
2952
-
2953
- 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`).
2954
-
2955
- 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:
2956
-
2957
- ```
2958
- samples/launch
2959
- ```
2960
-
2961
- ### Hello Samples
2962
-
2963
- For hello-type simple samples, check the following.
2964
-
2965
- #### Hello, World! Sample
2966
-
2967
- Code:
2968
-
2969
- [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)
2970
-
2971
- Run:
2972
-
2973
- ```
2974
- glimmer samples/hello/hello_world.rb
2975
- ```
2976
-
2977
- ![Hello World](images/glimmer-hello-world.png)
2978
-
2979
- #### Hello, Tab!
2980
-
2981
- Code:
2982
-
2983
- [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_tab.rb)
2984
-
2985
- Run:
2986
-
2987
- ```
2988
- glimmer samples/hello/hello_tab.rb
2989
- ```
2990
-
2991
- ![Hello Tab English](images/glimmer-hello-tab-english.png)
2992
- ![Hello Tab French](images/glimmer-hello-tab-french.png)
2993
-
2994
- #### Hello, Combo!
2995
-
2996
- This sample demonstrates combo data-binding.
2997
-
2998
- Code:
2999
-
3000
- [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_combo.rb)
3001
-
3002
- Run:
3003
-
3004
- ```
3005
- glimmer samples/hello/hello_combo.rb
3006
- ```
3007
-
3008
- ![Hello Combo](images/glimmer-hello-combo.png)
3009
- ![Hello Combo Expanded](images/glimmer-hello-combo-expanded.png)
3010
-
3011
- #### Hello, List Single Selection!
3012
-
3013
- This sample demonstrates list single-selection data-binding.
3014
-
3015
- Code:
3016
-
3017
- [samples/hello/hello_list_single_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_single_selection.rb)
3018
-
3019
- Run:
3020
-
3021
- ```
3022
- glimmer samples/hello/hello_list_single_selection.rb
3023
- ```
3024
-
3025
- ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
3026
-
3027
- #### Hello, List Multi Selection!
3028
-
3029
- This sample demonstrates list multi-selection data-binding.
3030
-
3031
- Code:
3032
-
3033
- [samples/hello/hello_list_multi_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_multi_selection.rb)
3034
-
3035
- Run:
3036
-
3037
- ```
3038
- glimmer samples/hello/hello_list_multi_selection.rb
3039
- ```
3040
-
3041
- ![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
3042
-
3043
- #### Hello, Computed!
3044
-
3045
- This sample demonstrates computed data-binding.
3046
-
3047
- Code:
3048
-
3049
- [samples/hello/hello_computed.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_computed.rb)
3050
-
3051
- Run:
3052
-
3053
- ```
3054
- glimmer samples/hello/hello_computed.rb
3055
- ```
3056
-
3057
- ![Hello Browser](images/glimmer-hello-computed.png)
3058
-
3059
- #### Hello, Message Box!
3060
-
3061
- This sample demonstrates a `message_box` dialog.
3062
-
3063
- Code:
3064
-
3065
- [samples/hello/hello_message_box.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_message_box.rb)
3066
-
3067
- Run:
3068
-
3069
- ```
3070
- glimmer samples/hello/hello_message_box.rb
3071
- ```
3072
-
3073
- ![Hello Message Box](images/glimmer-hello-message-box.png)
3074
- ![Hello Message Box Dialog](images/glimmer-hello-message-box-dialog.png)
3075
-
3076
- #### Hello, Browser!
3077
-
3078
- This sample demonstrates the `browser` widget.
3079
-
3080
- Code:
3081
-
3082
- [samples/hello/hello_browser.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_browser.rb)
3083
-
3084
- Run:
3085
-
3086
- ```
3087
- glimmer samples/hello/hello_browser.rb
3088
- ```
3089
-
3090
- ![Hello Browser](images/glimmer-hello-browser.png)
3091
-
3092
- #### Hello, Drag and Drop!
3093
-
3094
- This sample demonstrates drag and drop in Glimmer.
3095
-
3096
- Code:
3097
-
3098
- [samples/hello/hello_drag_and_drop.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_drag_and_drop.rb)
3099
-
3100
- Run:
3101
-
3102
- ```
3103
- glimmer samples/hello/hello_drag_and_drop.rb
3104
- ```
3105
-
3106
- ![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
3107
-
3108
- #### Hello, Menu Bar!
3109
-
3110
- This sample demonstrates menus in Glimmer.
3111
-
3112
- Code:
3113
-
3114
- [samples/hello/hello_menu_bar.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_menu_bar.rb)
3115
-
3116
- Run:
3117
-
3118
- ```
3119
- glimmer samples/hello/hello_menu_bar.rb
3120
- ```
3121
-
3122
- ![Hello Menu Bar](images/glimmer-hello-menu-bar.png)
3123
- ![Hello Menu Bar File Menu](images/glimmer-hello-menu-bar-file-menu.png)
3124
- ![Hello Menu Bar History Menu](images/glimmer-hello-menu-bar-history-menu.png)
3125
-
3126
- #### Hello, Pop Up Context Menu!
3127
-
3128
- This sample demonstrates pop up context menus in Glimmer.
3129
-
3130
- Code:
3131
-
3132
- [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)
3133
-
3134
- Run:
3135
-
3136
- ```
3137
- glimmer samples/hello/hello_pop_up_context_menu.rb
3138
- ```
3139
-
3140
- ![Hello Pop Up Context Menu](images/glimmer-hello-pop-up-context-menu.png)
3141
- ![Hello Pop Up Context Menu Popped Up](images/glimmer-hello-pop-up-context-menu-popped-up.png)
3142
-
3143
- ### Elaborate Samples
3144
-
3145
- For more elaborate samples, check the following:
3146
-
3147
- #### Login
3148
-
3149
- This sample demonstrates basic data-binding, password and text fields, and field enablement data-binding.
3150
-
3151
- Code:
3152
-
3153
- [samples/elaborate/login.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/login.rb)
3154
-
3155
- Run:
3156
-
3157
- ```
3158
- glimmer samples/elaborate/login.rb
3159
- ```
3160
-
3161
- ![Login](images/glimmer-login.png)
3162
- ![Login Filled In](images/glimmer-login-filled-in.png)
3163
- ![Login Logged In](images/glimmer-login-logged-in.png)
3164
-
3165
- #### Tic Tac Toe Sample
3166
-
3167
- 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)).
3168
-
3169
- Code:
3170
-
3171
- (Please note that on some Linux instances where the display x-axis is set to double-scale, you need to set the `shell` `minimum_size` to `300, 178` instead of `150, 178`)
3172
-
3173
- [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)
3174
-
3175
- Run:
3176
-
3177
- ```
3178
- glimmer samples/elaborate/tic_tac_toe.rb
3179
- ```
3180
-
3181
- ![Tic Tac Toe](images/glimmer-tic-tac-toe.png)
3182
- ![Tic Tac Toe In Progress](images/glimmer-tic-tac-toe-in-progress.png)
3183
- ![Tic Tac Toe Game Over](images/glimmer-tic-tac-toe-game-over.png)
3184
-
3185
- #### Contact Manager Sample
3186
-
3187
- 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)).
3188
-
3189
- Code:
3190
-
3191
- [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)
3192
-
3193
- Run:
3194
-
3195
- ```
3196
- glimmer samples/elaborate/contact_manager.rb
3197
- ```
3198
-
3199
- Contact Manager
3200
-
3201
- ![Contact Manager](images/glimmer-contact-manager.png)
3202
-
3203
- Contact Manager - Find
3204
-
3205
- ![Contact Manager](images/glimmer-contact-manager-find.png)
3206
-
3207
- Contact Manager - Edit Started
3208
-
3209
- ![Contact Manager](images/glimmer-contact-manager-edit-started.png)
3210
-
3211
- Contact Manager - Edit In Progress
3212
-
3213
- ![Contact Manager](images/glimmer-contact-manager-edit-in-progress.png)
3214
-
3215
- Contact Manager - Edit Done
3216
-
3217
- ![Contact Manager](images/glimmer-contact-manager-edit-done.png)
3218
-
3219
- ### External Samples
3220
-
3221
- #### Glimmer Calculator
3222
-
3223
- [<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 app demonstrating data-binding and TDD (test-driven-development) with Glimmer following the MVP pattern (Model-View-Presenter).
3224
-
3225
- [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-calculator)
3226
-
3227
- #### Gladiator
3228
-
3229
- [<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.
3230
- You may check it out to learn how to build a Glimmer Custom Shell gem.
3231
-
3232
- [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-gladiator.png" />](https://github.com/AndyObtiva/glimmer-cs-gladiator)
3233
-
3234
- Gladiator is a good demonstration of:
3235
- - MVP Pattern
3236
- - Tree data-binding
3237
- - List data-binding
3238
- - Text selection data-binding
3239
- - Tabs
3240
- - Context menus
3241
- - Custom Shell
3242
- - Custom widget
3243
-
3244
- #### Timer
3245
-
3246
- [<img alt="Glimmer Timer Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-timer/master/images/glimmer-timer-logo.png" height=40 /> Timer](https://github.com/AndyObtiva/glimmer-cs-timer) is a sample app demonstrating data-binding and multi-threading in a desktop application.
3247
-
3248
- [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-timer/master/glimmer-timer-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-timer)
3249
-
3250
- ## In Production
3251
-
3252
- The following production apps have been built with Glimmer.
3253
-
3254
- If you have a Glimmer app you would like referenced here, please mention in a Pull Request.
3255
-
3256
- ### Math Bowling
3257
-
3258
- [<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
3259
-
3260
- ### Are We There Yet?
3261
-
3262
- [<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
3263
-
3264
- ### Garderie Rainbow Daily Agenda
3265
-
3266
- [<img alt="Garderie Rainbow Daily Agenda Logo" src="https://github.com/AndyObtiva/garderie_rainbow_daily_agenda/raw/master/images/garderie_rainbow_daily_agenda_logo.png" width="40" />Garderie Rainbow Daily Agenda](https://github.com/AndyObtiva/garderie_rainbow_daily_agenda): A child nursery daily agenda reporting desktop app
3267
-
3268
- ## Packaging & Distribution
3269
-
3270
- Glimmer simplifies the process of native-executable packaging and distribution on Mac and Windows via a single `glimmer package` command:
3271
-
3272
- ```
3273
- glimmer package
3274
- ```
3275
-
3276
- It works out of the box for any application scaffolded by [Glimmer Scaffolding](#scaffolding), generating all available packaging types on the current platform (e.g. `DMG`, `PKG`, `APP` on the Mac) and displaying a message indicating what pre-requisite setup tools are needed if not installed already (e.g. [Wix Toolset](https://wixtoolset.org/) to generate MSI files on Windows)
3277
-
3278
- You may choose to generate a specific type of packaging instead by addionally passing in the `[type]` option. For example, this generates an MSI setup file on Windows:
3279
-
3280
- ```
3281
- glimmer package[msi]
3282
- ```
3283
-
3284
- - Available Mac packaging types are `dmg`, `pkg`, and `image` (image means a pure Mac `app` without a setup program)
3285
- - Available Windows packaging types are `msi`, `exe`, and `image` (image means a Windows application directory without a setup program). Learn more about Windows packaging are [over here](#windows-application-packaging).
3286
-
3287
- Note: if you are using Glimmer manually, to make the `glimmer package` command available, you must add the following line to your application `Rakefile` (automatically done for you if you scaffold an app or gem with `glimmer scaffold[AppName]` or `glimmer scaffold:gem:customshell[GemName]`):
3288
-
3289
- ```ruby
3290
- require 'glimmer/rake_task'
3291
- ```
3292
-
3293
- The Glimmer packaging process done in the `glimmer package` command consists of the following steps:
3294
- 1. Generate gemspec via Jeweler (`rake gemspec:generate`): Having a gemspec is required by the [`jar-dependencies`](https://github.com/mkristian/jar-dependencies) JRuby gem, used by JRuby libraries to declare JAR dependencies.
3295
- 1. Lock JAR versions (`glimmer package:lock_jars`): This locks versions of JAR dependencies leveraged by the `jar-dependencies` JRuby gem, downloading them into the `./vendor` directory so they would get inside the top-level Glimmer app/gem JAR file.
3296
- 1. Generate [Warbler](https://github.com/jruby/warbler) config (`glimmer package:config`): Generates initial Warbler config file (under `./config/warble.rb`) to use for generating JAR file.
3297
- 1. Generate JAR file using [Warbler](https://github.com/jruby/warbler) (`glimmer package:jar`): Enables bundling a Glimmer app into a JAR file under the `./dist` directory
3298
- 1. Generate native executable using [javapackager](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html) (`glimmer package:native`): Enables packaging a JAR file as a DMG/PKG/APP file on Mac, MSI/EXE/APP on Windows, and DEB/RPM/APP on Linux (Glimmer does not officially support Linux with the `glimmer package` command yet, but it generates the JAR file successfully, and you could use `javapackager` manually afterwards if needed).
3299
-
3300
- Those steps automatically ensure generating a JAR file under the `./dist` directory using [Warbler](https://github.com/jruby/warbler), which is then used to automatically generate a DMG/MSI file (and other executables) under the `./packages/bundles` directory using `javapackager`.
3301
- The JAR file name will match your application local directory name (e.g. `MathBowling.jar` for `~/code/MathBowling`)
3302
- The 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)
3303
-
3304
- The `glimmer package` command will automatically set "mac.CFBundleIdentifier" to ="org.#{project_name}.application.#{project_name}".
3305
- You may override by configuring as an extra argument for javapackger (e.g. Glimmer::Package.javapackager_extra_args = " -Bmac.CFBundleIdentifier=org.andymaleh.application.MathBowling")
3306
-
3307
- ### Packaging Defaults
3308
-
3309
- Glimmer employs smart defaults in packaging.
3310
-
3311
- 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.
3312
-
3313
- Also, the package will only include these directories: app, config, db, lib, script, bin, docs, fonts, images, sounds, videos
3314
-
3315
- 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:
3316
-
3317
- ```
3318
- glimmer package:config
3319
- ```
3320
-
3321
- This will generate `config/warble.rb`, which you may configure and then run `glimmer package` afterwards.
3322
-
3323
- ### Packaging Configuration
3324
-
3325
- - Ensure you have a Ruby script under `bin` directory that launches the application, preferably matching your project directory name (e.g. `bin/math_bowling`) :
3326
- ```ruby
3327
- require_relative '../app/my_application.rb'
3328
- ```
3329
- - 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/)
3330
- - Include Version (Optional): Create a `VERSION` file in your application and fill it your app version on one line (e.g. `1.1.0`)
3331
- - 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).
3332
- - 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).
3333
-
3334
- ### javapackager Extra Arguments
3335
-
3336
- In order to explicitly configure javapackager, Mac package attributes, or sign your Mac app to distribute on the App Store, you can follow more advanced instructions for `javapackager` here:
3337
- - https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
3338
- - https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html
3339
- - https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html#BCGICFDB
3340
- - https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html
3341
- - https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMacAppStore/index.html#//apple_ref/doc/uid/TP40010572-CH16-SW8
3342
-
3343
- The Glimmer rake task allows passing extra options to javapackager via:
3344
- - `Glimmer::Packager.javapackager_extra_args="..."` in your application Rakefile
3345
- - Environment variable: `JAVAPACKAGER_EXTRA_ARGS`
3346
-
3347
- Example (Rakefile):
3348
-
3349
- ```ruby
3350
- require 'glimmer/rake_task'
3351
-
3352
- Glimmer::Package.javapackager_extra_args = '-BlicenseType="MIT" -Bmac.category="public.app-category.business" -Bmac.signing-key-developer-id-app="Andy Maleh"'
3353
- ```
3354
-
3355
- Note that `mac.category` defaults to "public.app-category.business", but can be overridden with one of the category UTI values mentioned here:
3356
-
3357
- https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMacAppStore/index.html#//apple_ref/doc/uid/TP40010572-CH16-SW8
3358
-
3359
- Example (env var):
3360
-
3361
- ```
3362
- JAVAPACKAGER_EXTRA_ARGS='-Bmac.CFBundleName="Math Bowling Game"' glimmer package
3363
- ```
3364
-
3365
- That overrides the default application display name.
3366
-
3367
- ### Windows Application Packaging
3368
-
3369
- Windows offers two options for setup packaging:
3370
- - `msi` (recommended): simpler packaging option. Requires [WiX Toolset](https://wixtoolset.org/) and [.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework). Simply run `glimmer package[msi]` (or `glimmer package:native[msi]` if it's not your first time) and it will give you more details on the pre-requisites you need to install (e.g. [WiX Toolset](https://wixtoolset.org/) and [.NET Framework 3.5 SP1](https://dotnet.microsoft.com/download/dotnet-framework/net35-sp1)).
3371
- - `exe`: more advanced packaging option. Requires [Inno Setup](https://jrsoftware.org/isinfo.php). Simply run `glimmer package[exe]` (or `glimmer package:native[exe]` if it's not your first time) and it will tell you what you need to install.
3372
-
3373
- If you just want to test out packaging into a native Windows app that is not packaged for Windows setup, just pass `image` to generate a native Windows app only.
3374
-
3375
- ### Mac Application Distribution
3376
-
3377
- 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.
3378
-
3379
- 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
3380
-
3381
- DMG signing key argument:
3382
- ```
3383
- -Bmac.signing-key-developer-id-app="..."
3384
- ```
3385
-
3386
- PKG signing key argument:
3387
- ```
3388
- -Bmac.signing-key-developer-id-installer="..."
3389
- ```
3390
-
3391
- Mac App Store signing key arguments:
3392
- ```
3393
- -Bmac.signing-key-app="..."
3394
- -Bmac.signing-key-pkg="..."
3395
- ```
3396
-
3397
- ### Self Signed Certificate
3398
-
3399
- 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".
3400
-
3401
- 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):
3402
- - Open Keychain Access
3403
- - Choose Keychain Access > Certificate Assistant > Create Certificate ...
3404
- - Enter Name (referred to below as "CertificateName")
3405
- - Set 'Certificate Type' to 'Code Signing'
3406
- - Create (if you alternatively override defaults, make sure to enable all capabilities)
3407
- - Add the following option to javapackager: `-Bmac.signing-key-developer-id-app="CertificateName"` via `Glimmer::Package.javapackager_extra_args` or `JAVAPACKAGER_EXTRA_ARGS`
3408
-
3409
- Example:
3410
-
3411
- ```ruby
3412
- Glimmer::Package.javapackager_extra_args = '-Bmac.signing-key-developer-id-app="Andy Maleh"'
3413
- ```
3414
-
3415
- Now, when you run `glimmer package`, it builds a self-signed DMG file. When you make available online, and users download, upon launching application, they are presented with your certificate, which they have to sign if they trust you in order to use the application.
3416
-
3417
- ### Gotchas
3418
-
3419
- 1. Specifying License File
3420
-
3421
- The javapackager documentation states that a license file may be specified with "-BlicenseFile" javapackager option. However, in order for that to work, one must specify as a source file via "-srcfiles" javapackager option.
3422
- Keep that in mind if you are not going to rely on the default `LICENSE.txt` support.
3423
-
3424
- Example:
3425
-
3426
- ```ruby
3427
- Glimmer::Package.javapackager_extra_args = '-srcfiles "ACME.txt" -BlicenseFile="ACME.txt" -BlicenseType="ACME"'
3428
- ```
3429
-
3430
- 2. Mounted DMG Residue
3431
-
3432
- If you run `glimmer package` multiple times, sometimes it leaves a mounted DMG project in your finder. Unmount before you run the command again or it might fail with an error saying: "Error: Bundler "DMG Installer" (dmg) failed to produce a bundle."
3433
-
3434
- By the way, keep in mind that during normal operation, it does also indicate a false-negative while completing successfully similar to the following (please ignore):
3435
-
3436
- ```
3437
- 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
3438
- ```
3439
-
3440
- ## App Updates
3441
-
3442
- Glimmer already supports automatic (and manual) app updates via the Mac App Store for Mac apps. Simply run the `glimmer package` command with the Mac App Store keys configured as per [Mac Application Distribution](mac-application-distribution) instructions and you get automatic (and manual) app update support courtesy of the Mac App Store.
3443
-
3444
- ## Glimmer Supporting Libraries
3445
-
3446
- Here is a list of notable 3rd party gems used by Glimmer:
3447
- - [jeweler](https://github.com/technicalpickles/jeweler): generates app gems during [Glimmer Scaffolding](#scaffolding)
3448
- - [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)
3449
- - [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
3450
- - [os](https://github.com/rdp/os): provides OS detection capabilities (e.g. `OS.mac?` or `OS.windows?`) to write cross-platform code inexpensively
3451
- - [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`)
3452
- - [rake](https://github.com/ruby/rake): used to implement and execute `glimmer` commands
3453
- - [super_module](https://github.com/AndyObtiva/super_module): used to cleanly write the Glimmer::UI:CustomWidget and Glimmer::UI::CustomShell modules
3454
- - [text-table](https://github.com/aptinio/text-table): renders textual data in a textual table for the command-line interface of Glimmer
3455
- - [warbler](https://github.com/jruby/warbler): converts a Glimmer app into a Java JAR file during packaging
3456
-
3457
- ## Glimmer Process
3458
-
3459
- [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.
3460
-
3461
- Learn more by reading the [GPG](PROCESS.md) (Glimmer Process Guidelines)
3462
-
3463
- ## Resources
3464
-
3465
- * [Code Master Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
3466
- * [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
3467
- * [MountainWest RubyConf 2011 Video](https://confreaks.tv/videos/mwrc2011-whatever-happened-to-desktop-development-in-ruby)
3468
- * [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
3469
- * [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
3470
- * [DZone Tutorial](https://dzone.com/articles/an-introduction-glimmer)
3471
-
3472
- ## Help
3473
-
3474
- ### Issues
3475
-
3476
- You may submit [issues](https://github.com/AndyObtiva/glimmer/issues) on [GitHub](https://github.com/AndyObtiva/glimmer/issues).
3477
-
3478
- [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer/issues)
3479
-
3480
- ### Chat
3481
-
3482
- If you need live help, try to [![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)
3483
-
3484
- ## Feature Suggestions
3485
-
3486
- These features have been suggested. You might see them in a future version of Glimmer. You are welcome to contribute more feature suggestions.
3487
-
3488
- [glimmer-dsl-swt/TODO.md](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/TODO.md)
3489
-
3490
- Glimmer DSL Engine specific tasks are at:
3491
-
3492
- [TODO.md](TODO.md)
738
+ [TODO.md](TODO.md)
3493
739
 
3494
740
  ## Change Log
3495
741
 
@@ -3501,13 +747,14 @@ Glimmer DSL Engine specific tasks are at:
3501
747
 
3502
748
  **Contributors Wanted!**
3503
749
 
3504
- 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).
750
+ 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).
3505
751
 
3506
752
  You may apply for contributing to any of these Glimmer DSL gems whether you prefer to focus on the desktop or web:
3507
- - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
753
+ - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Library)
754
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop Development GUI Library)
3508
755
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
3509
756
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
3510
- - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
757
+ - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
3511
758
 
3512
759
  [CONTRIBUTING.md](CONTRIBUTING.md)
3513
760
 
@@ -3524,10 +771,9 @@ If your company would like to invest fulltime in further development of the Glim
3524
771
 
3525
772
  ## License
3526
773
 
3527
- [MIT](https://opensource.org/licenses/MIT)
774
+ [MIT](LICENSE.txt)
3528
775
 
3529
- Copyright (c) 2007-2020 - Andy Maleh.
3530
- See [LICENSE.txt](LICENSE.txt) for further details.
776
+ Copyright (c) 2007-2020 - Andy Maleh.
3531
777
 
3532
778
  --
3533
779