glimmer 0.10.2 → 1.0.2

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