glimmer-dsl-swt 0.6.9 → 4.17.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +276 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +4017 -24
  5. data/VERSION +1 -1
  6. data/bin/girb +21 -0
  7. data/bin/girb_runner.rb +21 -0
  8. data/bin/glimmer +21 -0
  9. data/glimmer-dsl-swt.gemspec +187 -0
  10. data/lib/ext/glimmer.rb +21 -0
  11. data/lib/ext/glimmer/config.rb +21 -5
  12. data/lib/glimmer-dsl-swt.rb +27 -0
  13. data/lib/glimmer/Rakefile +21 -0
  14. data/lib/glimmer/data_binding/list_selection_binding.rb +21 -0
  15. data/lib/glimmer/data_binding/observable_widget.rb +21 -0
  16. data/lib/glimmer/data_binding/shine.rb +22 -1
  17. data/lib/glimmer/data_binding/table_items_binding.rb +21 -0
  18. data/lib/glimmer/data_binding/tree_items_binding.rb +21 -0
  19. data/lib/glimmer/data_binding/widget_binding.rb +21 -0
  20. data/lib/glimmer/dsl/swt/async_exec_expression.rb +21 -0
  21. data/lib/glimmer/dsl/swt/bind_expression.rb +21 -0
  22. data/lib/glimmer/dsl/swt/block_property_expression.rb +21 -0
  23. data/lib/glimmer/dsl/swt/color_expression.rb +21 -0
  24. data/lib/glimmer/dsl/swt/column_properties_expression.rb +21 -0
  25. data/lib/glimmer/dsl/swt/combo_selection_data_binding_expression.rb +21 -0
  26. data/lib/glimmer/dsl/swt/cursor_expression.rb +21 -0
  27. data/lib/glimmer/dsl/swt/custom_widget_expression.rb +20 -0
  28. data/lib/glimmer/dsl/swt/data_binding_expression.rb +21 -0
  29. data/lib/glimmer/dsl/swt/dialog_expression.rb +21 -0
  30. data/lib/glimmer/dsl/swt/display_expression.rb +21 -0
  31. data/lib/glimmer/dsl/swt/dnd_expression.rb +21 -0
  32. data/lib/glimmer/dsl/swt/dsl.rb +21 -0
  33. data/lib/glimmer/dsl/swt/exec_expression.rb +21 -0
  34. data/lib/glimmer/dsl/swt/font_expression.rb +21 -0
  35. data/lib/glimmer/dsl/swt/image_expression.rb +21 -0
  36. data/lib/glimmer/dsl/swt/layout_data_expression.rb +21 -0
  37. data/lib/glimmer/dsl/swt/layout_expression.rb +21 -0
  38. data/lib/glimmer/dsl/swt/list_selection_data_binding_expression.rb +21 -0
  39. data/lib/glimmer/dsl/swt/menu_bar_expression.rb +21 -0
  40. data/lib/glimmer/dsl/swt/menu_expression.rb +21 -0
  41. data/lib/glimmer/dsl/swt/message_box_expression.rb +21 -0
  42. data/lib/glimmer/dsl/swt/observe_expression.rb +21 -0
  43. data/lib/glimmer/dsl/swt/property_expression.rb +21 -0
  44. data/lib/glimmer/dsl/swt/rgb_expression.rb +21 -0
  45. data/lib/glimmer/dsl/swt/rgba_expression.rb +21 -0
  46. data/lib/glimmer/dsl/swt/shell_expression.rb +21 -0
  47. data/lib/glimmer/dsl/swt/swt_expression.rb +21 -0
  48. data/lib/glimmer/dsl/swt/sync_exec_expression.rb +21 -0
  49. data/lib/glimmer/dsl/swt/tab_item_expression.rb +21 -0
  50. data/lib/glimmer/dsl/swt/table_items_data_binding_expression.rb +21 -0
  51. data/lib/glimmer/dsl/swt/tree_items_data_binding_expression.rb +21 -0
  52. data/lib/glimmer/dsl/swt/tree_properties_expression.rb +21 -0
  53. data/lib/glimmer/dsl/swt/widget_expression.rb +21 -0
  54. data/lib/glimmer/dsl/swt/widget_listener_expression.rb +21 -0
  55. data/lib/glimmer/launcher.rb +34 -9
  56. data/lib/glimmer/rake_task.rb +66 -85
  57. data/lib/glimmer/rake_task/list.rb +25 -1
  58. data/lib/glimmer/rake_task/package.rb +128 -0
  59. data/lib/glimmer/rake_task/sample.rb +115 -0
  60. data/lib/glimmer/rake_task/scaffold.rb +661 -0
  61. data/lib/glimmer/swt/color_proxy.rb +21 -0
  62. data/lib/glimmer/swt/cursor_proxy.rb +21 -0
  63. data/lib/glimmer/swt/display_proxy.rb +21 -0
  64. data/lib/glimmer/swt/dnd_proxy.rb +21 -0
  65. data/lib/glimmer/swt/font_proxy.rb +21 -0
  66. data/lib/glimmer/swt/image_proxy.rb +21 -0
  67. data/lib/glimmer/swt/layout_data_proxy.rb +21 -0
  68. data/lib/glimmer/swt/layout_proxy.rb +21 -0
  69. data/lib/glimmer/swt/menu_proxy.rb +21 -0
  70. data/lib/glimmer/swt/message_box_proxy.rb +21 -0
  71. data/lib/glimmer/swt/packages.rb +21 -0
  72. data/lib/glimmer/swt/scrolled_composite_proxy.rb +21 -0
  73. data/lib/glimmer/swt/shell_proxy.rb +22 -0
  74. data/lib/glimmer/swt/style_constantizable.rb +21 -0
  75. data/lib/glimmer/swt/swt_proxy.rb +21 -0
  76. data/lib/glimmer/swt/tab_item_proxy.rb +21 -0
  77. data/lib/glimmer/swt/table_column_proxy.rb +21 -0
  78. data/lib/glimmer/swt/table_proxy.rb +21 -0
  79. data/lib/glimmer/swt/tree_proxy.rb +21 -0
  80. data/lib/glimmer/swt/widget_listener_proxy.rb +21 -0
  81. data/lib/glimmer/swt/widget_proxy.rb +24 -0
  82. data/lib/glimmer/ui/custom_shell.rb +21 -0
  83. data/lib/glimmer/ui/custom_widget.rb +29 -4
  84. data/lib/glimmer/util/proc_tracker.rb +23 -0
  85. data/samples/elaborate/contact_manager.rb +21 -0
  86. data/samples/elaborate/contact_manager/contact.rb +21 -0
  87. data/samples/elaborate/contact_manager/contact_manager_presenter.rb +21 -0
  88. data/samples/elaborate/contact_manager/contact_repository.rb +21 -0
  89. data/samples/elaborate/login.rb +20 -2
  90. data/samples/elaborate/tic_tac_toe.rb +21 -0
  91. data/samples/elaborate/tic_tac_toe/board.rb +21 -0
  92. data/samples/elaborate/tic_tac_toe/cell.rb +21 -0
  93. data/samples/elaborate/user_profile.rb +21 -0
  94. data/samples/hello/hello_browser.rb +22 -1
  95. data/samples/hello/hello_combo.rb +21 -0
  96. data/samples/hello/hello_computed.rb +21 -0
  97. data/samples/hello/hello_computed/contact.rb +21 -0
  98. data/samples/hello/hello_custom_shell.rb +134 -0
  99. data/samples/hello/hello_custom_widget.rb +65 -0
  100. data/samples/hello/hello_drag_and_drop.rb +21 -0
  101. data/samples/hello/hello_list_multi_selection.rb +21 -0
  102. data/samples/hello/hello_list_single_selection.rb +21 -0
  103. data/samples/hello/hello_menu_bar.rb +21 -0
  104. data/samples/hello/hello_message_box.rb +21 -0
  105. data/samples/hello/hello_pop_up_context_menu.rb +21 -0
  106. data/samples/hello/hello_tab.rb +21 -0
  107. data/samples/hello/hello_world.rb +21 -0
  108. data/vendor/swt/linux/swt.jar +0 -0
  109. data/vendor/swt/mac/swt.jar +0 -0
  110. data/vendor/swt/windows/swt.jar +0 -0
  111. metadata +52 -61
  112. data/lib/glimmer/package.rb +0 -91
  113. data/lib/glimmer/scaffold.rb +0 -631
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f8cb96b5eda19eb65af0d9fa5b8f79050a341f5f148e78c13a27b18c07cef083
4
- data.tar.gz: 6e925dab92f1ea52557c2f9078a1c7b0b0068225644da25ce81677e6d4b9fca4
3
+ metadata.gz: c16edb19554a8b68798a5b63dc573094b6381c3c7528a490d1094c5b9c28be6a
4
+ data.tar.gz: 3c5c5a19894d9dd1dfe00619fbfee22cb109c20c252530bb717898c22f520867
5
5
  SHA512:
6
- metadata.gz: f23a464b36497e60e0379b90a1f17f630d84c0ff01359a89a9dd8cfe008306bbbd779c1b456c3ff17cdbaae331b795b1241597d57b653e063c3aa8af7f463ce7
7
- data.tar.gz: 02a87c762461b89acac3385dfb2ab4ea0089e6b3b4320f60e843758e5b28df2036cb4524e8b0851b792576401857de3e77e9981bf92ee98205ddfadf425cbf8a
6
+ metadata.gz: cd4a6c25743aaec88b0638c1e869fe980f3a3c96f9674fd54497859485690b23a00b128cd7a310a29c6c29182e1f9766f2c847687c218b29dc33cbf19d77936d
7
+ data.tar.gz: 35133834f642a0e90c665c5811540c3fa50e22164ee7db560edf7b0243cc5650bcf70ad1d196442a0eb6c090a35a455412131bc50ac97ead0089aba1007a04cd
@@ -0,0 +1,276 @@
1
+ # Change Log
2
+
3
+ ### 4.17.2.1
4
+
5
+ - Add `--bundler=group` option to `glimmer` command
6
+ - Add `--pd` option to `glimmer` command
7
+ - Hello, Custom Widget! sample
8
+ - Hello, Custom Shell! sample
9
+
10
+ ### 4.17.2.0
11
+
12
+ - `glimmer` command --bundler option to run with bundler/setup (instead of picking gems directly)
13
+ - Remove Gemfile dependency on Juwelier since it does not relate to GUI (delaying install of it till scaffolding)
14
+ - Remove Gemfile dependency on Warbler since it does not relate to GUI (delaying install of it till packaging)
15
+ - Move Package and Scaffold classes under Glimmer::RakeTask (Glimmer::Package.javapackager_extra_args is now Glimmer::RakeTask::Package.javapackager_extra_args)
16
+ - Fixed issue with scaffolding spec/spec_helper.rb with Juwelier (since it changed from Jeweler)
17
+
18
+ ### 4.17.1.1
19
+
20
+ - Fixed issue with showing glimmer command tasks twice
21
+
22
+ ### 4.17.1.0
23
+
24
+ - Switch to Juwelier gem (from Jeweler)
25
+ - Load samples from Glimmer gems automatically (no need for configuration)
26
+ - Empty body validation in CustomWidget (and CustomShell by inheritance)
27
+ - require 'bundler/setup' in `glimmer` command if a `Gemfile` is present (disabled with GLIMMER_BUNDLER_SETUP=false env var)
28
+ - Upgrade to rvm-tui version 0.2.2
29
+
30
+ ### 4.17.0.0
31
+
32
+ - Upgrade to SWT (Standard Widget Toolkit) 4.17 and sync version with SWT going forward
33
+ - Upgrade to Glimmer (DSL Engine) 1.0.0
34
+ - Sync version number with the SWT version number (first two numbers, leaving the last two as minor and patch)
35
+
36
+ ### 0.6.9
37
+
38
+ - Log error messages when running inside sync_exec or async_exec (since you cannot rescue their errors from outside them)
39
+ - Exclude gladiator from required libraries during sample listing/running/code-display
40
+ - Ensured creating a widget with swt_widget keyword arg doesn't retrigger initializers on its parents if already initialized
41
+ - Extract `WidgetProxy#interpret_style` to make it possible to extend with further styles with less code (e.g. CDateTimeProxy adds CDT styles by overriding method)
42
+
43
+ ### 0.6.8
44
+
45
+ - Support external configuration of `WidgetProxy::KEYWORD_ALIASES` (e.g. `radio` is an alias for `button(:radio)`)
46
+ - Support external configuration of `Glimmer::Config::SAMPLE_DIRECTORIES` for the `glimmer sample` commands from Glimmer gems
47
+
48
+ ### 0.6.7
49
+
50
+ - Fix issue with re-initializing layout for already initialized swt_widget being wrapped by WidgetProxy via swt_widget keyword args
51
+ - Change naming of scaffolded app bundle for mac to start with a capital letter (e.g. com.evernote.Evernote not com.evernote.evernote)
52
+
53
+ ### 0.6.6
54
+
55
+ - Add User Profile sample from DZone article
56
+ - Colored Ruby syntax highlighting for sample:code and sample:run tasks courtesy of tty-markdown
57
+ - Support `check` as alias to `checkbox` DSL keyword for Button widget with :check style.
58
+ - Validate scaffolded custom shell gem name to ensure it doesn't clash with a built in Ruby method
59
+ - GLIMMER_LOGGER_ASYNC env var for disabling async logging when needed for immediate troubleshooting purposes
60
+ - Fix issue with table equivalent sort edge case (that is two sorts that are equivalent causing an infinite loop of resorting since the table is not correctly identified as sorted already)
61
+
62
+ ### 0.6.5
63
+
64
+ - Added the [rake-tui](https://github.com/AndyObtiva/rake-tui) gem as a productivity tool for arrow key navigation/text-filtering/quick-triggering of rake tasks
65
+ - Use rake-tui gem in `glimmer` command by default on Mac and Linux
66
+
67
+ ### 0.6.4
68
+
69
+ - Display glimmer-dsl-swt gem version in glimmer command usage
70
+ - Include Glimmer Samples in Gem and provide access via `glimmer samples:list`, `glimmer samples:run`, and `glimmer samples:code` commands
71
+ - Fix issue with glimmer not listing commands in usage without having a Rakefile
72
+ - Fix issue with passing --log-level or --debug to the `girb` command
73
+
74
+ ### 0.6.3
75
+
76
+ **Scaffolding:**
77
+
78
+ - Add mnemonic to Preferences menu in scaffolding
79
+ - Make bin/glimmer, bin/girb, and scaffolded bin/script files call jruby instead of ruby in the shebang
80
+ - Launch scaffolded app on Linux without using packaging (via `glimmer bin/app_script`)
81
+ - Add all of Mac, Windows, and Linux icons upon scaffolding (not just for the OS we are on)
82
+
83
+ **Packaging:**
84
+
85
+ - Perform gemspec:generate first during packaging
86
+ - Ensure lock_jars step happens before package:jar to have vendor jar-dependencies packaged inside JAR file
87
+ - Change lock_jar vendor-dir to vendor/jars and add it to .gitignore
88
+ - Handle naming of -Bwin.menuGroup properly for Windows in scaffolded custom shell gems (and apps) (e.g. instead of Glimmer Cs Timer set to Timer only or namespace when available in a CustomShell)
89
+ - Support passing javapackager extra args after `glimmer package:native` command inside double-quotes (e.g. `glimmer package:native "-title 'CarMaker'"`)
90
+ - JDK14 experimental `jpackage` support as a packaging alternative to `javapackager` (Not recommended for production use and must specify JDK8 as JRE with an additional option since SWT doesn't support JDK14 yet)
91
+
92
+ **GUI:**
93
+
94
+ - Add radio and checkbox table editors
95
+ - Add `content` method to DisplayProxy
96
+ - Add `content` method to MessageBox
97
+ - WidgetProxy now supports taking a fully constructed swt_widget argument instead of init_args
98
+
99
+ **CI:**
100
+
101
+ - Add Windows to Travis-CI
102
+
103
+ **Issues:**
104
+
105
+ - Fix issue with TableProxy editor rejecting false and nil values set on items
106
+ - Fix issue with message_box getting stuck upon closing when no parent in its args
107
+ - Fix transient issue with git bash not interpretting glimmer package[msi] as a rake task (yet as packages instead as it resolves [msi] by picking s to match packages local directory)
108
+ - Fix issue with getting "Namespace is required!" when running `glimmer scaffold[app_name]` or `glimmer scaffold:gem:customshell[name,namespace]` (https://github.com/AndyObtiva/glimmer/issues/5)
109
+
110
+ ### 0.6.2
111
+
112
+ - Set default margins on layouts (FilLayout, RowLayout, GridLayout, and any layout that responds to marginWidth and marginHeight)
113
+ - Have scrolled_composite autoset min width and min height based on parent size
114
+ - Add `radio`, `toggle`, `checkbox`, and `arrow` widgets as shortcuts to `button` widget with different styles
115
+ - Add parent_proxy method to WidgetProxy
116
+ - Add post_add_content hook method to WidgetProxy
117
+ - Add `image` keyword to create an ImageProxy and be able to scale it
118
+ - Fix issue with ImageProxy not being scalable before swt_image is called
119
+
120
+ ### 0.6.1
121
+
122
+ - Lock JARs task for jar-dependencies as part of packaging
123
+ - Add 'vendor' to require_paths for custom shell gem
124
+ - Add Windows icon to scaffold
125
+ - Generate scaffolded app/custom-shell-gem gemspec as part of packaging (useful for jar-dependencies)
126
+ - Support a package:native type option for specifying native type
127
+ - Add a preferences menu for Windows (since it does not have one out of the box)
128
+ - Fix app scaffold on Windows by having it generate jeweler gem first (to have gemspec for jar-dependencies)
129
+ - Fix girb for Windows
130
+
131
+ ### 0.6.0
132
+
133
+ - Upgrade to JRuby 9.2.13.0
134
+ - Upgrade to SWT 4.16
135
+ - Support `font` keyword
136
+ - Support cursor setting via SWT style symbols directly
137
+ - Support `cursor` keyword
138
+
139
+ ### 0.5.6
140
+
141
+ - Fixed issue with excluding on_swt_* listeners from Glimmer DSL engine processing in CustomWidget
142
+ - Add shell minimum_size to Tic Tac Toe sample for Linux
143
+
144
+ ### 0.5.5
145
+
146
+ - Add 'package' directory to 'config/warble.rb' for packaging in JAR file
147
+ - Fix issue with image path conversion to imagedata on Mac vs Windows
148
+
149
+ ### 0.5.4
150
+
151
+ - Fix issue with uri:classloader paths generated by JRuby when using File.expand_path inside packaged JAR files
152
+
153
+ ### 0.5.3
154
+
155
+ - Set widget `image`/`images` property via string file path(s) just like `background_image`
156
+
157
+ ### 0.5.2
158
+
159
+ - Added :full_selection to table widget default SWT styles
160
+
161
+ ### 0.5.1
162
+
163
+ - Made packaging -BsystemWide option true on the Mac only
164
+
165
+ ### 0.5.0
166
+
167
+ - Upgrade to glimmer 0.10.1 to take advantage of the new logging library
168
+ - Make Glimmer commands support acronym, dash and no separator (default) alternatives
169
+ - Support scaffold commands for gems with `scaffold:gem:cw` pattern (`cs` as other suffix)
170
+ - Support listing commands with `list:gems:cw` pattern (`cs` as other suffix)
171
+ - Add -BinstalldirChooser=true / -Bcopyright=string / -Bvendor=string / -Bwin.menuGroup=string to Package class to support Windows packaging
172
+ - Configure 'logging' gem to generate log files on Windows/Linux/Mac and syslog where available
173
+ - Configure 'logging' gem to do async buffered logging via a thread to avoid impacting app performance with logging
174
+ - Make GLIMMER_LOGGER_LEVEL env var work with logging gem
175
+ - Update all logger calls to be lazy blocks
176
+ - Add logging formatter (called layout in logging library)
177
+ - Support legacy rake tasks for package and scaffold (the ones without gem/gems nesting)
178
+ - GLIMMER_LOGGER_LEVEL env var disables async logging in logging gem to help with immediate troubleshooting
179
+ - Create 'log' directory if :file logging device is specified
180
+ - Remember log level when reseting logger after the first time
181
+ - Dispose all tree items when removed
182
+ - Dispose all table items when removed
183
+ - Remove table model collection observers when updating
184
+ - Make message_box instantiate a shell if none passed in
185
+ - Eliminate unimportant (false negative) log messages getting reported as ERROR when running test suite
186
+ - Sort table on every change to maintain its sort according to its sorted column
187
+
188
+ ### 0.4.1
189
+
190
+ - Fixed an issue with async_exec and sync_exec keywords not working when used from a module that mixes Glimmer
191
+
192
+ ### 0.4.0
193
+
194
+ - Support SWT listener events that take multiple-args (as in custom libraries like Nebula GanttChart)
195
+ - Drop on_event_* keywords in favor of on_swt_* for SWT constant events
196
+ - Remove Table#table_editor_text_proxy in favor of Table#table_editor_widget_proxy
197
+ - Set WidgetProxy/ShellProxy/DisplayProxy data('proxy') objects
198
+ - Set CustomWidget data('custom_widget') objects
199
+ - Set CustomShell data('custom_shell') objects
200
+ - Delegate all WidgetProxy/ShellProxy/DisplayProxy/CustomWidget/CustomShell methods to wrapped SWT object on method_missing
201
+
202
+ ### 0.3.1
203
+
204
+ - Support multiple widgets for editing table items
205
+
206
+ ## 0.3.0
207
+
208
+ - Update API for table column sorting to pass models not properties to sorting blocks
209
+ - Support table multi-column sort_property
210
+ - Support table additional_sort_properties array
211
+ - Display table column sorting direction sign
212
+ - Update Scaffold MessageBox reference to message_box DSL keyword
213
+ - Fix issue with different sorting blocks not reseting each other on different table columns
214
+
215
+ ## 0.2.4
216
+
217
+ - Make table auto-sortable
218
+ - Configure custom sorters for table columns
219
+ - Support for ScrolledComposite smart default behavior (auto setting of content, h_scroll/v_scroll styles, and horizontal/vertical expand)
220
+
221
+ ## 0.2.3
222
+
223
+ - Upgraded to Glimmer 0.9.4
224
+ - Add vendor directory to warble config for glimmer package command.
225
+ - Make WidgetProxy register only the nearest ancestor property observer, calling on_modify_text and on_widget_selected for widgets that support these listeners, or otherwise the widget specific customizations
226
+ - Add glimmer package:clean command
227
+ - Make scaffolding gems fail when no namespace is specified
228
+ - Add a hello menu samples
229
+
230
+ ## 0.2.2
231
+
232
+ - Support Combo custom-text-entry data-binding
233
+ - Remove Gemfile.lock from .gitignore in scaffolding apps/gems
234
+
235
+ ## 0.2.1
236
+
237
+ - Support latest JRuby 9.2.12.0
238
+ - Support extra args (other than style) in WidgetProxy just like ShellProxy
239
+ - Specify additional Java packages to import when including Glimmer via Glimmer::Config::import_swt_packages=(packages)
240
+ - Add compatibility for ActiveSupport (automatically call ActiveSupport::Dependencies.unhook! if ActiveSupport is loaded)
241
+ - Fix bug with table items data binding ignoring bind converters
242
+
243
+ ## 0.2.0
244
+
245
+ - Simplified Drag and Drop API by being able to attach drag and drop event listener handlers directly on widgets
246
+ - Support drag and drop events implicitly on all widgets without explicit drag source and drop target declarations
247
+ - Set drag and drop transfer property to :text by default if not specified
248
+ - Automatically set `event.detail` inside `on_drag_enter` to match the first operation specified in `drop_target` (make sure it doesn't cause problems if source and target have differnet operations, denying drop gracefully)
249
+ - Support `dnd` keyword for easily setting `event.detail` (e.g. dnd(:drop_copy)) inside `on_drag_enter` (and consider supporting symbol directly as well)
250
+ - Support Drag and Drop on Custom Widgets
251
+ - Fix hello_computed.rb sample (convert camelcase to underscore case for layout data properties)
252
+
253
+ ## 0.1.3
254
+
255
+ - Added 'org.eclipse.swt.dnd' to glimmer auto-included Java packages
256
+ - Updated Tic Tac Toe sample to use new `message_box` keyword
257
+ - Add DragSource and DropTarget transfer expression that takes a symbol or symbol array representing one or more of the following: FileTransfer, HTMLTransfer, ImageTransfer, RTFTransfer, TextTransfer, URLTransfer
258
+ - Set default style DND::DROP_COPY in DragSource and DropTarget widgets
259
+ - Support Glimmer::SWT::DNDProxy for handling Drop & Drop styles
260
+ - Implemented list:* rake tasks for listing Glimmer custom widget gems, custom shell gems, and dsl gems
261
+ - Support querying for Glimmer gems (not just listing them)
262
+ - Fix bug with table edit remaining when sorting table or re-listing (in contact_manager.rb sample)
263
+ - Update icon of scaffolded apps to Glimmer logo
264
+
265
+ ## 0.1.2
266
+
267
+ - Extracted common model data-binding classes into glimmer
268
+
269
+ ## 0.1.1
270
+
271
+ - Fixed issue with packaging after generating gemspec
272
+ - Fixed issue with enabling development mode in glimmer command
273
+
274
+ ## 0.1.0
275
+
276
+ - Extracted Glimmer DSL for SWT (glimmer-dsl-swt gem) from Glimmer
@@ -1,4 +1,4 @@
1
- Copyright (c) 2020 Andy Maleh
1
+ Copyright (c) 2007-2020 Andy Maleh
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,50 +1,4023 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for SWT 0.6.9 (JRuby Desktop GUI)
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for SWT 4.17.2.1
2
+ ## JRuby Desktop Development GUI Library
2
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-swt.svg)](http://badge.fury.io/rb/glimmer-dsl-swt)
3
4
  [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer-dsl-swt.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer-dsl-swt)
4
5
  [![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer-dsl-swt/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer-dsl-swt?branch=master)
5
6
  [![Maintainability](https://api.codeclimate.com/v1/badges/aaf1cba142dd351f84bd/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-swt/maintainability)
6
7
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
7
8
 
8
- [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [SWT](https://www.eclipse.org/swt/) enables desktop development with [Glimmer](https://github.com/AndyObtiva/glimmer) in [JRuby](https://www.jruby.org/).
9
+ **[Contributors Wanted! (Submit a Glimmer App Sample to Get Started)](#contributing)**
9
10
 
10
- [Glimmer](https://github.com/AndyObtiva/glimmer) is a native-GUI cross-platform desktop development library written in Ruby. Glimmer's main innovation is a JRuby DSL that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support to greatly facilitate synchronizing the GUI with domain models. As a result, that achieves true decoupling of object oriented components, enabling developers to solve business problems without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models test-first afterwards.
11
+ (The Original Glimmer Library Since 2007. Beware of Imitators!)
11
12
 
12
- You may find full setup/usage instructions at the main [Glimmer project page](https://github.com/AndyObtiva/glimmer).
13
+ [**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).
13
14
 
14
- Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
15
+ [Glimmer receives two updates per month](https://rubygems.org/gems/glimmer-dsl-swt/versions). You can trust [Glimmer](https://rubygems.org/gems/glimmer) with your Ruby desktop GUI development needs.
16
+
17
+ NOTE: Glimmer is in beta mode. Please make better by providing feedback and [contributing](#contributing) when possible. The more feedback and issues you report the better.
18
+
19
+ [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
20
+ Featured in<br />JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do)
21
+
22
+ Glimmer DSL gems:
23
+ - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop GUI)
24
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (Ruby Desktop GUI)
15
25
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
16
26
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
17
- - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
18
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop GUI)
27
+ - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
28
+
29
+ ## Examples
30
+
31
+ ### Hello, World!
32
+
33
+ Glimmer code (from [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)):
34
+ ```ruby
35
+ include Glimmer
36
+
37
+ shell {
38
+ text "Glimmer"
39
+ label {
40
+ text "Hello, World!"
41
+ }
42
+ }.open
43
+ ```
44
+
45
+ Run:
46
+ ```
47
+ glimmer sample:run[hello_world]
48
+ ```
49
+
50
+ Glimmer app:
51
+
52
+ ![Hello World](images/glimmer-hello-world.png)
53
+
54
+ ### Tic Tac Toe
55
+
56
+ Glimmer code (from [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)):
57
+
58
+ ```ruby
59
+ # ...
60
+ @tic_tac_toe_board = Board.new
61
+
62
+ @shell = shell {
63
+ text "Tic-Tac-Toe"
64
+ minimum_size 150, 178
65
+ composite {
66
+ grid_layout 3, true
67
+ (1..3).each { |row|
68
+ (1..3).each { |column|
69
+ button {
70
+ layout_data :fill, :fill, true, true
71
+ text bind(@tic_tac_toe_board[row, column], :sign)
72
+ enabled bind(@tic_tac_toe_board[row, column], :empty)
73
+ font style: :bold, height: 20
74
+ on_widget_selected {
75
+ @tic_tac_toe_board.mark(row, column)
76
+ }
77
+ }
78
+ }
79
+ }
80
+ }
81
+ }
82
+
83
+ observe(@tic_tac_toe_board, :game_status) { |game_status|
84
+ display_win_message if game_status == Board::WIN
85
+ display_draw_message if game_status == Board::DRAW
86
+ }
87
+ # ...
88
+ ```
89
+
90
+ Run:
91
+
92
+ ```
93
+ glimmer sample:run[tic_tac_toe]
94
+ ```
95
+
96
+ Glimmer app:
97
+
98
+ ![Tic Tac Toe](images/glimmer-tic-tac-toe-in-progress.png)
99
+
100
+ ### Contact Manager
101
+
102
+ Glimmer code (from [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)):
103
+
104
+ ```ruby
105
+ # ...
106
+ shell {
107
+ text "Contact Manager"
108
+ composite {
109
+ group {
110
+ grid_layout(2, false) {
111
+ margin_width 0
112
+ margin_height 0
113
+ }
114
+ layout_data :fill, :center, true, false
115
+ text 'Lookup Contacts'
116
+ font height: 24
117
+
118
+ label {
119
+ layout_data :right, :center, false, false
120
+ text "First &Name: "
121
+ font height: 16
122
+ }
123
+ text {
124
+ layout_data :fill, :center, true, false
125
+ text bind(@contact_manager_presenter, :first_name)
126
+ on_key_pressed {|key_event|
127
+ @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
128
+ }
129
+ }
130
+
131
+ label {
132
+ layout_data :right, :center, false, false
133
+ text "&Last Name: "
134
+ font height: 16
135
+ }
136
+ text {
137
+ layout_data :fill, :center, true, false
138
+ text bind(@contact_manager_presenter, :last_name)
139
+ on_key_pressed {|key_event|
140
+ @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
141
+ }
142
+ }
143
+
144
+ label {
145
+ layout_data :right, :center, false, false
146
+ text "&Email: "
147
+ font height: 16
148
+ }
149
+ text {
150
+ layout_data :fill, :center, true, false
151
+ text bind(@contact_manager_presenter, :email)
152
+ on_key_pressed {|key_event|
153
+ @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
154
+ }
155
+ }
156
+
157
+ composite {
158
+ row_layout {
159
+ margin_width 0
160
+ margin_height 0
161
+ }
162
+ layout_data(:right, :center, false, false) {
163
+ horizontal_span 2
164
+ }
165
+
166
+ button {
167
+ text "&Find"
168
+ on_widget_selected { @contact_manager_presenter.find }
169
+ on_key_pressed {|key_event|
170
+ @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
171
+ }
172
+ }
173
+
174
+ button {
175
+ text "&List All"
176
+ on_widget_selected { @contact_manager_presenter.list }
177
+ on_key_pressed {|key_event|
178
+ @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ table(:multi) { |table_proxy|
185
+ layout_data {
186
+ horizontal_alignment :fill
187
+ vertical_alignment :fill
188
+ grab_excess_horizontal_space true
189
+ grab_excess_vertical_space true
190
+ height_hint 200
191
+ }
192
+ table_column {
193
+ text "First Name"
194
+ width 80
195
+ }
196
+ table_column {
197
+ text "Last Name"
198
+ width 80
199
+ }
200
+ table_column {
201
+ text "Email"
202
+ width 200
203
+ }
204
+ items bind(@contact_manager_presenter, :results),
205
+ column_properties(:first_name, :last_name, :email)
206
+ on_mouse_up { |event|
207
+ table_proxy.edit_table_item(event.table_item, event.column_index)
208
+ }
209
+ }
210
+ }
211
+ }.open
212
+ # ...
213
+ ```
214
+
215
+ Run:
216
+
217
+ ```
218
+ glimmer sample:run[contact_manager]
219
+ ```
220
+
221
+ Glimmer App:
222
+
223
+ ![Contact Manager](images/glimmer-contact-manager.png)
224
+
225
+ ### Desktop Apps Built with Glimmer DSL for SWT
226
+
227
+ [<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
228
+
229
+ [![Are We There Yet? App Screenshot](https://raw.githubusercontent.com/AndyObtiva/are-we-there-yet/master/are-we-there-yet-screenshot-windows.png)](https://github.com/AndyObtiva/are-we-there-yet)
230
+
231
+ [<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
232
+
233
+ [![Math Bowling App Screenshot](https://raw.githubusercontent.com/AndyObtiva/MathBowling/master/Math-Bowling-Screenshot.png)](https://github.com/AndyObtiva/MathBowling)
234
+
235
+ ## Table of contents
236
+
237
+ - [Glimmer (Ruby Desktop Development GUI Library)](#-glimmer-ruby-desktop-development-gui-library)
238
+ - [Examples](#examples)
239
+ - [Hello, World!](#hello-world)
240
+ - [Tic Tac Toe](#tic-tac-toe)
241
+ - [Contact Manager](#contact-manager)
242
+ - [Table of contents](#table-of-contents)
243
+ - [Background](#background)
244
+ - [Platform Support](#platform-support)
245
+ - [Pre-requisites](#pre-requisites)
246
+ - [Setup](#setup)
247
+ - [Option 1: Direct Install](#option-1-direct-install)
248
+ - [Option 2: Bundler](#option-2-bundler)
249
+ - [Glimmer Command](#glimmer-command)
250
+ - [Basic Usage](#basic-usage)
251
+ - [Advanced Usage](#advanced-usage)
252
+ - [Sample List/Run/Code](#sample-listruncode)
253
+ - [Sample List](#sample-list)
254
+ - [Sample Run](#sample-run)
255
+ - [Sample Code](#sample-code)
256
+ - [Scaffolding](#scaffolding)
257
+ - [App](#app)
258
+ - [Custom Shell](#custom-shell)
259
+ - [Custom Widget](#custom-widget)
260
+ - [Custom Shell Gem](#custom-shell-gem)
261
+ - [Custom Widget Gem](#custom-widget-gem)
262
+ - [Gem Listing](#gem-listing)
263
+ - [Listing Custom Shell Gems](#listing-custom-shell-gems)
264
+ - [Listing Custom Widget Gems](#listing-custom-widget-gems)
265
+ - [Listing DSL Gems](#listing-dsl-gems)
266
+ - [Packaging](#packaging)
267
+ - [Raw JRuby Command](#raw-jruby-command)
268
+ - [Mac Support](#mac-support)
269
+ - [Girb (Glimmer irb) Command](#girb-glimmer-irb-command)
270
+ - [Glimmer GUI DSL Syntax](#glimmer-gui-dsl-syntax)
271
+ - [DSL Auto-Expansion](#dsl-auto-expansion)
272
+ - [Widgets](#widgets)
273
+ - [Display](#display)
274
+ - [SWT Proxies](#swt-proxies)
275
+ - [Dialog](#dialog)
276
+ - [Menus](#menus)
277
+ - [ScrolledComposite](#scrolledcomposite)
278
+ - [Widget Styles](#widget-styles)
279
+ - [Explicit SWT Style Bit](#explicit-swt-style-bit)
280
+ - [Negative SWT Style Bits](#negative-swt-style-bits)
281
+ - [Extra SWT Styles](#extra-swt-styles)
282
+ - [Widget Properties](#widget-properties)
283
+ - [Color](#color)
284
+ - [Font](#font)
285
+ - [Cursor](#cursor)
286
+ - [Layouts](#layouts)
287
+ - [Layout Data](#layout-data)
288
+ - [Data-Binding](#data-binding)
289
+ - [General Examples](#general-examples)
290
+ - [Combo](#combo)
291
+ - [List](#list)
292
+ - [Table](#table)
293
+ - [Tree](#tree)
294
+ - [Observer](#observer)
295
+ - [Observing Widgets](#observing-widgets)
296
+ - [Observing Models](#observing-models)
297
+ - [Custom Widgets](#custom-widgets)
298
+ - [Simple Example](#simple-example)
299
+ - [Lifecycle Hook Example](#lifecycle-hook-example)
300
+ - [Custom Widget API](#custom-widget-api)
301
+ - [Content/Options Example](#contentoptions-example)
302
+ - [Custom Widget Lifecycle Hooks](#custom-widget-lifecycle-hooks)
303
+ - [Gotcha](#gotcha)
304
+ - [Final Notes](#final-notes)
305
+ - [Custom Shells](#custom-shells)
306
+ - [Drag and Drop](#drag-and-drop)
307
+ - [Miscellaneous](#miscellaneous)
308
+ - [Multi-DSL Support](#multi-dsl-support)
309
+ - [Application Menu Items (About/Preferences)](#application-menu-items-aboutpreferences)
310
+ - [App Name and Version](#app-name-and-version)
311
+ - [Video Widget](#video-widget)
312
+ - [Browser Widget](#browser-widget)
313
+ - [Glimmer Configuration](#glimmer-configuration)
314
+ - [logger](#logger)
315
+ - [logging_devices](#loggingdevices)
316
+ - [logging_device_file_options](#loggingdevicefileoptions)
317
+ - [logging_appender_options](#loggingappenderoptions)
318
+ - [logging_layout](#logginglayout)
319
+ - [import_swt_packages](#importswtpackages)
320
+ - [loop_max_count](#loopmaxcount)
321
+ - [excluded_keyword_checkers](#excludedkeywordcheckers)
322
+ - [log_excluded_keywords](#logexcludedkeywords)
323
+ - [Glimmer Style Guide](#glimmer-style-guide)
324
+ - [SWT Reference](#swt-reference)
325
+ - [Samples](#samples)
326
+ - [Hello Samples](#hello-samples)
327
+ - [Hello, World! Sample](#hello-world-sample)
328
+ - [Hello, Tab!](#hello-tab)
329
+ - [Hello, Combo!](#hello-combo)
330
+ - [Hello, List Single Selection!](#hello-list-single-selection)
331
+ - [Hello, List Multi Selection!](#hello-list-multi-selection)
332
+ - [Hello, Computed!](#hello-computed)
333
+ - [Hello, Message Box!](#hello-message-box)
334
+ - [Hello, Browser!](#hello-browser)
335
+ - [Hello, Drag and Drop!](#hello-drag-and-drop)
336
+ - [Hello, Menu Bar!](#hello-menu-bar)
337
+ - [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu)
338
+ - [Hello, Custom Widget!](#hello-custom-widget)
339
+ - [Hello, Custom Shell!](#hello-custom-shell)
340
+ - [Elaborate Samples](#elaborate-samples)
341
+ - [User Profile](#user-profile)
342
+ - [Login](#login)
343
+ - [Tic Tac Toe Sample](#tic-tac-toe-sample)
344
+ - [Contact Manager Sample](#contact-manager-sample)
345
+ - [External Samples](#external-samples)
346
+ - [Glimmer Calculator](#glimmer-calculator)
347
+ - [Gladiator](#gladiator)
348
+ - [Timer](#timer)
349
+ - [In Production](#in-production)
350
+ - [Math Bowling](#math-bowling)
351
+ - [Are We There Yet?](#are-we-there-yet)
352
+ - [Garderie Rainbow Daily Agenda](#garderie-rainbow-daily-agenda)
353
+ - [Packaging & Distribution](#packaging--distribution)
354
+ - [Packaging Defaults](#packaging-defaults)
355
+ - [Packaging Configuration](#packaging-configuration)
356
+ - [javapackager Extra Arguments](#javapackager-extra-arguments)
357
+ - [Verbose Mode](#verbose-mode)
358
+ - [Windows Application Packaging](#windows-application-packaging)
359
+ - [Mac Application Distribution](#mac-application-distribution)
360
+ - [Self Signed Certificate](#self-signed-certificate)
361
+ - [Gotchas](#gotchas)
362
+ - [App Updates](#app-updates)
363
+ - [Glimmer Supporting Libraries](#glimmer-supporting-libraries)
364
+ - [Glimmer Process](#glimmer-process)
365
+ - [Resources](#resources)
366
+ - [Help](#help)
367
+ - [Issues](#issues)
368
+ - [Chat](#chat)
369
+ - [Feature Suggestions](#feature-suggestions)
370
+ - [Change Log](#change-log)
371
+ - [Contributing](#contributing)
372
+ - [Contributors](#contributors)
373
+ - [Hire Me](#hire-me)
374
+ - [License](#license)
375
+
376
+ ## Background
377
+
378
+ 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.
379
+
380
+ ## Platform Support
381
+
382
+ Glimmer runs on the following platforms:
383
+ - Mac
384
+ - Windows
385
+ - Linux
386
+
387
+ 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:
388
+ - Win32 on Windows
389
+ - Cocoa on Mac
390
+ - GTK on Linux
391
+
392
+ More info about the SWT GUI on various platforms can be found on the Eclipse WIKI and SWT FAQ:
393
+
394
+ https://wiki.eclipse.org/SWT/Devel/Gtk/Dev_guide#Win32.2FCocoa.2FGTK
395
+ https://www.eclipse.org/swt/faq.php
396
+
397
+ ## Pre-requisites
398
+
399
+ - JDK 8u241 (1.8.0_241) (find at https://www.oracle.com/java/technologies/javase/javase8u211-later-archive-downloads.html)
400
+ - 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)
401
+ - SWT 4.17 (already included in the [glimmer-dsl-swt](https://rubygems.org/gems/glimmer-dsl-swt) gem)
402
+
403
+ To obtain JRuby through [RVM](http://rvm.io), you may run:
404
+
405
+ ```bash
406
+ rvm install jruby-9.2.13.0
407
+ ```
408
+
409
+ 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.
410
+
411
+ ## Setup
412
+
413
+ 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.
414
+
415
+ 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)).
416
+
417
+ If you intend to build a Glimmer app from scratch with [scaffolding](#scaffolding), pick Option 1 ([Direct Install](#option-1-direct-install)) as well.
418
+
419
+ Otherwise, Option 2 ([Bundler](#option-2-bundler)) can be followed in rare cases where you want to build an app without [scaffolding](#scaffolding).
420
+
421
+ Note: if you encounter any [issues](https://github.com/AndyObtiva/glimmer-dsl-swt/issues), please [report](https://github.com/AndyObtiva/glimmer-dsl-swt/issues) and then install a previous version instead from the list of [Glimmer Releases](https://rubygems.org/gems/glimmer-dsl-swt/versions).
422
+
423
+ ### Option 1: Direct Install
424
+ (Use for [Scaffolding](#scaffolding))
425
+
426
+ Run this command to install directly:
427
+ ```
428
+ jgem install glimmer-dsl-swt
429
+ ```
430
+
431
+ Or this command if you want a specific version:
432
+ ```
433
+ jgem install glimmer-dsl-swt -v 4.17.2.1
434
+ ```
435
+
436
+ Note: Gem version numbers are in sync with the SWT library versions. The first two digits represent the SWT version number. The last two digits represent the minor and patch versions of Glimmer DSL for SWT.
437
+
438
+ `jgem` is JRuby's version of `gem` command.
439
+ RVM allows running `gem install` directly as an alias.
440
+ Otherwise, you may also run `jruby -S gem install ...`
441
+
442
+ 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.
443
+
444
+ 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.
445
+
446
+ Note: if you're using activerecord or activesupport, keep in mind that Glimmer unhooks ActiveSupport::Dependencies as it does not rely on it.
447
+
448
+ ### Option 2: Bundler
449
+ (Use for Manual App Creation)
450
+
451
+ Add the following to `Gemfile`:
452
+ ```
453
+ gem 'glimmer-dsl-swt', '~> 4.17'
454
+ ```
455
+
456
+ And, then run:
457
+ ```
458
+ jruby -S bundle install
459
+ ```
460
+
461
+ Note: if you're using activerecord or activesupport, keep in mind that Glimmer unhooks ActiveSupport::Dependencies as it does not rely on it.
462
+
463
+ 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)
464
+
465
+ ## Glimmer Command
466
+
467
+ The `glimmer` command allows you to run, scaffold, package, and list Glimmer applications/gems.
468
+
469
+ You can bring up usage instructions by running the `glimmer` command without arguments:
470
+
471
+ ```
472
+ glimmer
473
+ ```
474
+
475
+ On Mac and Linux, it additionally brings up a TUI (Text-based User Interface) for interactive navigation and execution of Glimmer tasks (courtesy of [rake-tui](https://github.com/AndyObtiva/rake-tui)).
476
+
477
+ On Windows, it simply lists the available Glimmer tasks at the end (courtsey of [rake](https://github.com/ruby/rake)).
478
+
479
+ 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).
480
+
481
+ Note: If you encounter an issue running the `glimmer` command, run `bundle exec glimmer` instead.
482
+
483
+ ### Basic Usage
484
+
485
+ ```
486
+ glimmer application.rb
487
+ ```
488
+
489
+ Runs a Glimmer application using JRuby, automatically preloading
490
+ the glimmer ruby gem and SWT jar dependency.
491
+
492
+ Example:
493
+ ```
494
+ glimmer sample:run[hello_world]
495
+ ```
496
+ This runs the Glimmer "Hello, World!" sample.
497
+
498
+ If you cloned this project locally, you may run `bin/glimmer` instead.
499
+
500
+ Example:
501
+ ```
502
+ bin/glimmer sample:run[hello_world]
503
+ ```
504
+
505
+ ### Advanced Usage
506
+
507
+ Below are the full usage instructions that come up when running `glimmer` without args.
508
+
509
+ ```
510
+ Glimmer (Ruby Desktop Development GUI Library) - JRuby Gem: glimmer-dsl-swt v4.17.2.1
511
+
512
+ Usage: glimmer [--bundler] [--pd] [--quiet] [--debug] [--log-level=VALUE] [[ENV_VAR=VALUE]...] [[-jruby-option]...] (application.rb or task[task_args]) [[application2.rb]...]
513
+
514
+ Runs Glimmer applications and tasks.
515
+
516
+ When applications are specified, they are run using JRuby,
517
+ automatically preloading the glimmer Ruby gem and SWT jar dependency.
518
+
519
+ Optionally, extra Glimmer options, JRuby options, and/or environment variables may be passed in.
520
+
521
+ Glimmer options:
522
+ - "--bundler=GROUP" : Activates gems in Bundler default group in Gemfile
523
+ - "--pd=BOOLEAN" : Requires puts_debuggerer to enable pd method
524
+ - "--quiet=BOOLEAN" : Does not announce file path of Glimmer application being launched
525
+ - "--debug" : Displays extra debugging information, passes "--debug" to JRuby, and enables debug logging
526
+ - "--log-level=VALUE" : Sets Glimmer's Ruby logger level ("ERROR" / "WARN" / "INFO" / "DEBUG"; default is none)
527
+
528
+ Tasks are run via rake. Some tasks take arguments in square brackets.
529
+
530
+ Available tasks are below (if you do not see any, please add `require 'glimmer/rake_task'` to Rakefile and rerun or run rake -T):
531
+
532
+ Select a Glimmer task to run: (Press ↑/↓ arrow to move, Enter to select and letters to filter)
533
+ ‣ glimmer list:gems:customshell[query] # List Glimmer custom shell gems available at rubygems.org (query is optional) [alt: list:gems:cs]
534
+ glimmer list:gems:customwidget[query] # List Glimmer custom widget gems available at rubygems.org (query is optional) [alt: list:gems:cw]
535
+ glimmer list:gems:dsl[query] # List Glimmer DSL gems available at rubygems.org (query is optional)
536
+ glimmer package[type] # Package app for distribution (generating config, jar, and native files) (type is optional)
537
+ glimmer package:clean # Clean by removing "dist" and "packages" directories
538
+ glimmer package:config # Generate JAR config file
539
+ glimmer package:jar # Generate JAR file
540
+ glimmer package:lock_jars # Lock JARs
541
+ glimmer package:native[type] # Generate Native files
542
+ glimmer sample:code[name] # Outputs code for a Glimmer internal sample [included in gem] (name is required)
543
+ glimmer sample:list[query] # Lists Glimmer internal samples [included in gem]. Filters by query if specified (query is optional)
544
+ glimmer sample:run[name] # Runs a Glimmer internal sample [included in gem]. If no name is supplied, it runs all samples
545
+ glimmer scaffold[app_name] # Scaffold Glimmer application directory structure to build a new app
546
+ glimmer scaffold:customshell[name,namespace] # Scaffold Glimmer::UI::CustomShell subclass (full window view) under app/views (namespace is optional) [alt: scaffold:cs]
547
+ glimmer scaffold:customwidget[name,namespace] # Scaffold Glimmer::UI::CustomWidget subclass (part of a view) under app/views (namespace is optional) [alt: scaffold:cw]
548
+ 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:ge...
549
+ 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]
550
+ ```
551
+
552
+ Example (Glimmer/JRuby option specified):
553
+ ```
554
+ glimmer --debug samples/hello/hello_world.rb
555
+ ```
556
+
557
+ Runs Glimmer application with JRuby debug option to enable JRuby debugging.
558
+
559
+ Example (Multiple apps):
560
+ ```
561
+ glimmer samples/hello/hello_world.rb samples/hello/hello_tab.rb
562
+ ```
563
+
564
+ Launches samples/hello/hello_world.rb and samples/hello_tab.rb at the same time, each in a separate JRuby thread.
565
+
566
+ ### Sample List/Run/Code
567
+
568
+ #### Sample List
569
+
570
+ You can list available Glimmer samples by running:
571
+
572
+ ```
573
+ glimmer sample:list
574
+ ```
575
+
576
+ This should output the following (providing the name of each sample, description, and command to run the sample and view its code):
577
+
578
+ ```
579
+ $ glimmer sample:list
580
+
581
+ Glimmer Hello Samples:
582
+
583
+ Name Description Run
584
+
585
+ hello_browser Hello Browser glimmer sample:run[hello_browser]
586
+ hello_combo Hello Combo glimmer sample:run[hello_combo]
587
+ hello_computed Hello Computed glimmer sample:run[hello_computed]
588
+ hello_drag_and_drop Hello Drag And Drop glimmer sample:run[hello_drag_and_drop]
589
+ hello_list_multi_selection Hello List Multi Selection glimmer sample:run[hello_list_multi_selection]
590
+ hello_list_single_selection Hello List Single Selection glimmer sample:run[hello_list_single_selection]
591
+ hello_menu_bar Hello Menu Bar glimmer sample:run[hello_menu_bar]
592
+ hello_message_box Hello Message Box glimmer sample:run[hello_message_box]
593
+ hello_pop_up_context_menu Hello Pop Up Context Menu glimmer sample:run[hello_pop_up_context_menu]
594
+ hello_tab Hello Tab glimmer sample:run[hello_tab]
595
+ hello_world Hello World glimmer sample:run[hello_world]
596
+
597
+
598
+ Glimmer Elaborate Samples:
599
+
600
+ Name Description Run
601
+
602
+ contact_manager Contact Manager glimmer sample:run[contact_manager]
603
+ login Login glimmer sample:run[login]
604
+ tic_tac_toe Tic Tac Toe glimmer sample:run[tic_tac_toe]
605
+ user_profile User Profile glimmer sample:run[user_profile]
606
+ ```
607
+
608
+ #### Sample Run
609
+
610
+ A sample may be run via `glimmer sample:run[name]`. This also outputs the sample code so that you could take a look at it and compare to the GUI that launches.
611
+
612
+ If the sample name is left empty (e.g. `glimmer sample:run`), then all samples are run.
613
+
614
+ Example:
615
+
616
+ ```
617
+ glimmer sample:run[hello_tab]
618
+ ```
619
+
620
+ This will run the hello_tab sample and output its code:
621
+
622
+ ```
623
+ $ glimmer sample:run[hello_tab]
624
+
625
+ # /Users/User/.rvm/gems/jruby-9.2.13.0@glimmerapp/gems/glimmer-dsl-swt-4.17.2.1/samples/hello/hello_tab.rb
626
+
627
+ class HelloTab
628
+ include Glimmer
629
+ def launch
630
+ shell {
631
+ text "Hello, Tab!"
632
+ tab_folder {
633
+ tab_item {
634
+ text "English"
635
+ label {
636
+ text "Hello, World!"
637
+ }
638
+ }
639
+ tab_item {
640
+ text "French"
641
+ label {
642
+ text "Bonjour, Univers!"
643
+ }
644
+ }
645
+ }
646
+ }.open
647
+ end
648
+ end
649
+
650
+ HelloTab.new.launch
651
+
652
+ # # #
653
+ ```
654
+
655
+ ![Hello Tab English](images/glimmer-hello-tab-english.png)
656
+
657
+ #### Sample Code
658
+
659
+ You may output any sample code via this command: `glimmer sample:code[name]`
660
+
661
+ This is very similar to the sample run command, except the name is required.
662
+
663
+ It will not only output the main sample file, but any required supporting files as well.
664
+
665
+ Example:
666
+
667
+ ```
668
+ $ glimmer sample:code[tic_tac_toe]
669
+
670
+ # /Users/User/.rvm/gems/jruby-9.2.13.0@glimmerapp/gems/glimmer-dsl-swt-4.17.2.1/samples/elaborate/tic_tac_toe.rb
671
+
672
+ require_relative "tic_tac_toe/board"
673
+
674
+ class TicTacToe
675
+ include Glimmer
676
+
677
+ def initialize
678
+ @tic_tac_toe_board = Board.new
679
+ @shell = shell {
680
+ text "Tic-Tac-Toe"
681
+ minimum_size 150, 178
682
+ composite {
683
+ grid_layout 3, true
684
+ (1..3).each { |row|
685
+ (1..3).each { |column|
686
+ button {
687
+ layout_data :fill, :fill, true, true
688
+ text bind(@tic_tac_toe_board[row, column], :sign)
689
+ enabled bind(@tic_tac_toe_board[row, column], :empty)
690
+ font style: :bold, height: 20
691
+ on_widget_selected {
692
+ @tic_tac_toe_board.mark(row, column)
693
+ }
694
+ }
695
+ }
696
+ }
697
+ }
698
+ }
699
+ observe(@tic_tac_toe_board, :game_status) { |game_status|
700
+ display_win_message if game_status == Board::WIN
701
+ display_draw_message if game_status == Board::DRAW
702
+ }
703
+ end
704
+
705
+ def display_win_message
706
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
707
+ end
708
+
709
+ def display_draw_message
710
+ display_game_over_message("Draw!")
711
+ end
712
+
713
+ def display_game_over_message(message_text)
714
+ message_box(@shell) {
715
+ text 'Game Over'
716
+ message message_text
717
+ }.open
718
+ @tic_tac_toe_board.reset
719
+ end
720
+
721
+ def open
722
+ @shell.open
723
+ end
724
+ end
725
+
726
+ TicTacToe.new.open
727
+
728
+ # # #
729
+
730
+
731
+ # /Users/User/.rvm/gems/jruby-9.2.13.0@glimmerapp/gems/glimmer-dsl-swt-4.17.2.1/samples/elaborate/tic_tac_toe/cell.rb
732
+
733
+ class TicTacToe
734
+ class Cell
735
+ EMPTY = ""
736
+ attr_accessor :sign, :empty
737
+
738
+ def initialize
739
+ reset
740
+ end
741
+
742
+ def mark(sign)
743
+ self.sign = sign
744
+ end
745
+
746
+ def reset
747
+ self.sign = EMPTY
748
+ end
749
+
750
+ def sign=(sign_symbol)
751
+ @sign = sign_symbol
752
+ self.empty = sign == EMPTY
753
+ end
754
+
755
+ def marked
756
+ !empty
757
+ end
758
+ end
759
+ end
760
+
761
+ # # #
762
+
763
+
764
+ # /Users/User/.rvm/gems/jruby-9.2.13.0@glimmerapp/gems/glimmer-dsl-swt-4.17.2.1/samples/elaborate/tic_tac_toe/board.rb
765
+
766
+ require_relative 'cell'
767
+
768
+ class TicTacToe
769
+ class Board
770
+ DRAW = :draw
771
+ IN_PROGRESS = :in_progress
772
+ WIN = :win
773
+ attr :winning_sign
774
+ attr_accessor :game_status
775
+
776
+ def initialize
777
+ @sign_state_machine = {nil => "X", "X" => "O", "O" => "X"}
778
+ build_grid
779
+ @winning_sign = Cell::EMPTY
780
+ @game_status = IN_PROGRESS
781
+ end
782
+
783
+ #row and column numbers are 1-based
784
+ def mark(row, column)
785
+ self[row, column].mark(current_sign)
786
+ game_over? #updates winning sign
787
+ end
788
+
789
+ def current_sign
790
+ @current_sign = @sign_state_machine[@current_sign]
791
+ end
792
+
793
+ def [](row, column)
794
+ @grid[row-1][column-1]
795
+ end
796
+
797
+ def game_over?
798
+ win? or draw?
799
+ end
800
+
801
+ def win?
802
+ win = (row_win? or column_win? or diagonal_win?)
803
+ self.game_status=WIN if win
804
+ win
805
+ end
806
+
807
+ def reset
808
+ (1..3).each do |row|
809
+ (1..3).each do |column|
810
+ self[row, column].reset
811
+ end
812
+ end
813
+ @winning_sign = Cell::EMPTY
814
+ @current_sign = nil
815
+ self.game_status=IN_PROGRESS
816
+ end
817
+
818
+ private
819
+
820
+ def build_grid
821
+ @grid = []
822
+ 3.times do |row_index| #0-based
823
+ @grid << []
824
+ 3.times { @grid[row_index] << Cell.new }
825
+ end
826
+ end
827
+
828
+ def row_win?
829
+ (1..3).each do |row|
830
+ if row_has_same_sign(row)
831
+ @winning_sign = self[row, 1].sign
832
+ return true
833
+ end
834
+ end
835
+ false
836
+ end
837
+
838
+ def column_win?
839
+ (1..3).each do |column|
840
+ if column_has_same_sign(column)
841
+ @winning_sign = self[1, column].sign
842
+ return true
843
+ end
844
+ end
845
+ false
846
+ end
847
+
848
+ #needs refactoring if we ever decide to make the board size dynamic
849
+ def diagonal_win?
850
+ if (self[1, 1].sign == self[2, 2].sign) and (self[2, 2].sign == self[3, 3].sign) and self[1, 1].marked
851
+ @winning_sign = self[1, 1].sign
852
+ return true
853
+ end
854
+ if (self[3, 1].sign == self[2, 2].sign) and (self[2, 2].sign == self[1, 3].sign) and self[3, 1].marked
855
+ @winning_sign = self[3, 1].sign
856
+ return true
857
+ end
858
+ false
859
+ end
860
+
861
+ def draw?
862
+ @board_full = true
863
+ 3.times do |x|
864
+ 3.times do |y|
865
+ @board_full = false if self[x, y].empty
866
+ end
867
+ end
868
+ self.game_status = DRAW if @board_full
869
+ @board_full
870
+ end
871
+
872
+ def row_has_same_sign(number)
873
+ row_sign = self[number, 1].sign
874
+ [2, 3].each do |column|
875
+ return false unless row_sign == (self[number, column].sign)
876
+ end
877
+ true if self[number, 1].marked
878
+ end
879
+
880
+ def column_has_same_sign(number)
881
+ column_sign = self[1, number].sign
882
+ [2, 3].each do |row|
883
+ return false unless column_sign == (self[row, number].sign)
884
+ end
885
+ true if self[1, number].marked
886
+ end
887
+
888
+ end
889
+ end
890
+
891
+ # # #
892
+ ```
893
+
894
+ ### Scaffolding
895
+
896
+ Glimmer borrows from Rails the idea of Scaffolding, that is generating a structure for your app files that
897
+ helps you get started just like true buildinThis g scaffolding helps construction workers, civil engineers, and architects.
898
+
899
+ Glimmer scaffolding goes beyond just scaffolding the app files that Rails does. It also packages it and launches it,
900
+ getting you to a running and delivered state of an advanced "Hello, World!" Glimmer application right off the bat.
901
+
902
+ This should greatly facilitate building a new Glimmer app by helping you be productive and focus on app details while
903
+ letting Glimmer scaffolding take care of initial app file structure concerns, such as adding:
904
+ - Main application class that includes Glimmer (`app/{app_name}.rb`)
905
+ - Main application view that houses main window content, menu, about dialog, and preferences dialog
906
+ - View and Model directories (`app/views` and `app/models`)
907
+ - Rakefile including Glimmer tasks (`Rakefile`)
908
+ - Version (`VERSION`)
909
+ - License (`LICENSE.txt`)
910
+ - Icon (under `package/{platform}/{App Name}.{icon_extension}` for `macosx` .icns, `windows` .ico, and `linux` .png)
911
+ - Bin file for starting application (`bin/{app_name}.rb`)
912
+
913
+ NOTE: Scaffolding supports Mac and Windows packaging at the moment.
914
+
915
+ #### App
916
+
917
+ Before you start, make sure you are in a JRuby environment with Glimmer gem installed as per "Direct Install" pre-requisites.
918
+
919
+ To scaffold a Glimmer app from scratch, run the following command:
920
+
921
+ ```
922
+ glimmer scaffold[AppName]
923
+ ```
924
+
925
+ 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.
926
+
927
+ Suppose you run:
928
+
929
+ ```
930
+ glimmer scaffold[greeter]
931
+ ```
932
+
933
+ You should see output like the following:
934
+
935
+ ```
936
+ $ glimmer scaffold[greeter]
937
+ create .gitignore
938
+ create Rakefile
939
+ create Gemfile
940
+ create LICENSE.txt
941
+ create README.rdoc
942
+ create .document
943
+ create lib
944
+ create lib/greeter.rb
945
+ create spec
946
+ create spec/spec_helper.rb
947
+ create spec/greeter_spec.rb
948
+ create .rspec
949
+ Jeweler has prepared your gem in ./greeter
950
+ Created greeter/.gitignore
951
+ Created greeter/.ruby-version
952
+ Created greeter/.ruby-gemset
953
+ Created greeter/VERSION
954
+ Created greeter/LICENSE.txt
955
+ Created greeter/Gemfile
956
+ Created greeter/Rakefile
957
+ Created greeter/app/greeter.rb
958
+ Created greeter/app/views/greeter/app_view.rb
959
+ Created greeter/package/windows/Greeter.ico
960
+ Created greeter/package/macosx/Greeter.icns
961
+ Created greeter/package/linux/Greeter.png
962
+ Created greeter/bin/greeter
963
+ Created greeter/spec/spec_helper.rb
964
+ ...
965
+ ```
966
+
967
+ Eventually, it will launch an advanced "Hello, World!" app window having the title of your application ("Greeter").
968
+
969
+ ![Glimmer Scaffold App](images/glimmer-scaffolding-app.png)
970
+
971
+ It also comes with a boilerplate Preferences dialog.
972
+
973
+ ![Glimmer Scaffold App Preferences](images/glimmer-scaffolding-app-preferences.png)
974
+
975
+ Here is the Windows version of the scaffolded "Greeter" app:
976
+
977
+ ![Glimmer Scaffold App Windows](images/glimmer-scaffolding-app-windows.png)
978
+
979
+ And, here is the Windows version of the boilerplate Preferences dialog.
980
+
981
+ ![Glimmer Scaffold App Windows Preferences](images/glimmer-scaffolding-app-windows-preferences.png)
982
+
983
+ In order to run the app after making changes, you must run the `glimmer` command and pass it the generated script under the `bin` directory as an argument:
984
+
985
+ ```
986
+ glimmer bin/greeter
987
+ ```
988
+
989
+ #### Custom Shell
990
+
991
+ To scaffold a Glimmer custom shell (full window view) for an existing Glimmer app, run the following command:
992
+
993
+ ```
994
+ glimmer scaffold:customshell[name]
995
+ ```
996
+
997
+ Or the following alternative abbreviation:
998
+
999
+ ```
1000
+ glimmer scaffold:cs[name]
1001
+ ```
1002
+
1003
+ #### Custom Widget
1004
+
1005
+ To scaffold a Glimmer custom widget (part of a view) for an existing Glimmer app, run the following command:
1006
+
1007
+ ```
1008
+ glimmer scaffold:customwidget[name]
1009
+ ```
1010
+
1011
+ Or the following alternative abbreviation:
1012
+
1013
+ ```
1014
+ glimmer scaffold:cw[name]
1015
+ ```
1016
+
1017
+ #### Custom Shell Gem
1018
+
1019
+ Custom shell gems are self-contained Glimmer apps as well as reusable custom shells.
1020
+ 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.
1021
+ Unlike scaffolded Glimmer apps, custom shell gem content lives under the `lib` directory (not `app`).
1022
+ They can be packaged as both a native executable (e.g. Mac DMG/PKG/APP) and a Ruby gem.
1023
+ Of course, you can just build a Ruby gem and disregard native executable packaging if you do not need it.
1024
+
1025
+ To scaffold a Glimmer custom shell gem (full window view distributed as a Ruby gem), run the following command:
1026
+
1027
+ ```
1028
+ glimmer scaffold:gem:customshell[name,namespace]
1029
+ ```
1030
+
1031
+ Or the following alternative abbreviation:
1032
+
1033
+ ```
1034
+ glimmer scaffold:gem:cs[name,namespace]
1035
+ ```
1036
+
1037
+ It is important to specify a namespace to avoid having your gem clash with existing gems.
1038
+
1039
+ The Ruby gem name will follow the convention "glimmer-cs-customwidgetname-namespace" (the 'cs' is for Custom Shell).
1040
+
1041
+ 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)
1042
+
1043
+ Since custom shell gems are both an app and a gem, they provide two ways to run:
1044
+ - Run the `glimmer` command and pass it the generated script under the `bin` directory that matches the gem name (e.g. run `glimmer bin/glimmer-cs-calculator`)
1045
+ - Run the executable binary file that ships with the gem directly (without `glimmer`). It intentionally has a shorter name for convenience since it is meant to be used on the command line (not in a package), so you can leave out the `glimmer-cs-` prefix (e.g. run `bin/calculator` directly)
1046
+
1047
+ Examples:
1048
+
1049
+ - [glimmer-cs-gladiator](https://github.com/AndyObtiva/glimmer-cs-gladiator): Gladiator (Glimmer Editor)
1050
+ - [glimmer-cs-calculator](https://github.com/AndyObtiva/glimmer-cs-calculator): Glimmer Calculator
1051
+
1052
+ #### Custom Widget Gem
1053
+
1054
+ To scaffold a Glimmer custom widget gem (part of a view distributed as a Ruby gem), run the following command:
1055
+
1056
+ ```
1057
+ glimmer scaffold:gem:customwidget[name,namespace]
1058
+ ```
1059
+
1060
+ Or the following alternative abbreviation:
1061
+
1062
+ ```
1063
+ glimmer scaffold:gem:cw[name,namespace]
1064
+ ```
1065
+
1066
+
1067
+ It is important to specify a namespace to avoid having your gem clash with existing gems.
1068
+
1069
+ The Ruby gem name will follow the convention "glimmer-cw-customwidgetname-namespace" (the 'cw' is for Custom Widget)
1070
+
1071
+ 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)
1072
+
1073
+ Examples:
1074
+
1075
+ - [glimmer-cw-video](https://github.com/AndyObtiva/glimmer-cw-video): Video Widget
1076
+ - [glimmer-cw-cdatetime-nebula](https://github.com/AndyObtiva/glimmer-cw-cdatetime-nebula): Nebula CDateTime Widget
1077
+
1078
+ ### Gem Listing
1079
+
1080
+ 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).
1081
+
1082
+ #### Listing Custom Shell Gems
1083
+
1084
+ 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):
1085
+
1086
+ ```
1087
+ glimmer list:gems:customshell[query]
1088
+ ```
1089
+
1090
+ Or the following alternative abbreviation:
1091
+
1092
+ ```
1093
+ glimmer list:gems:cs[query]
1094
+ ```
1095
+
1096
+ Example:
1097
+
1098
+ ```
1099
+ glimmer list:gems:cs
1100
+ ```
1101
+
1102
+ Output:
1103
+
1104
+ ```
1105
+
1106
+ Glimmer Custom Shell Gems at rubygems.org:
1107
+
1108
+ Name Gem Version Author Description
1109
+
1110
+ Calculator glimmer-cs-calculator 1.0.2 Andy Maleh Calculator - Glimmer Custom Shell
1111
+ Gladiator glimmer-cs-gladiator 0.2.4 Andy Maleh Gladiator (Glimmer Editor) - Glimmer Custom Shell
1112
+ Timer glimmer-cs-timer 1.0.0 Andy Maleh Timer - Glimmer Custom Shell
1113
+
1114
+ ```
1115
+
1116
+ #### Listing Custom Widget Gems
1117
+
1118
+ 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):
1119
+
1120
+ ```
1121
+ glimmer list:gems:customwidget[query]
1122
+ ```
1123
+
1124
+ Or the following alternative abbreviation:
1125
+
1126
+ ```
1127
+ glimmer list:gems:cw[query]
1128
+ ```
1129
+
1130
+ Example:
1131
+
1132
+ Check if there is a custom video widget for Glimmer.
1133
+
1134
+ ```
1135
+ glimmer list:gems:cw[video]
1136
+ ```
1137
+
1138
+ Output:
1139
+
1140
+ ```
1141
+
1142
+ Glimmer Custom Widget Gems matching [video] at rubygems.org:
1143
+
1144
+ Name Gem Version Author Description
1145
+
1146
+ Video glimmer-cw-video 1.0.0 Andy Maleh Glimmer Custom Widget - Video
1147
+
1148
+ ```
1149
+
1150
+ Example:
1151
+
1152
+ Check all custom widgets for Glimmer.
1153
+
1154
+ ```
1155
+ glimmer list:gems:cw
1156
+ ```
1157
+
1158
+ Output:
1159
+
1160
+ ```
1161
+
1162
+ Glimmer Custom Widget Gems at rubygems.org:
1163
+
1164
+ Name Gem Version Author Description
1165
+
1166
+ Browser (Chromium) glimmer-cw-browser-chromium 1.0.0 Andy Maleh Chromium Browser - Glimmer Custom Widget
1167
+ Cdatetime (Nebula) glimmer-cw-cdatetime-nebula 1.5.0.0.1 Andy Maleh Nebula CDateTime Widget - Glimmer Custom Widget
1168
+ Video glimmer-cw-video 1.0.0 Andy Maleh Glimmer Custom Widget - Video
1169
+
1170
+ ```
1171
+
1172
+ #### Listing DSL Gems
1173
+
1174
+ 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):
1175
+
1176
+ ```
1177
+ glimmer list:gems:dsl[query]
1178
+ ```
1179
+
1180
+ Example:
1181
+
1182
+ ```
1183
+ glimmer list:gems:dsl
1184
+ ```
1185
+
1186
+ Output:
1187
+
1188
+ ```
1189
+
1190
+ Glimmer DSL Gems at rubygems.org:
1191
+
1192
+ Name Gem Version Author Description
1193
+
1194
+ Css glimmer-dsl-css 0.2.0 AndyMaleh Glimmer DSL for CSS
1195
+ Opal glimmer-dsl-opal 0.1.0 AndyMaleh Glimmer DSL for Opal
1196
+ Swt glimmer-dsl-swt 4.17.2.1 AndyMaleh Glimmer DSL for SWT
1197
+ Tk glimmer-dsl-tk 0.0.5 AndyMaleh Glimmer DSL for Tk
1198
+ Xml glimmer-dsl-xml 0.2.0 AndyMaleh Glimmer DSL for XML
1199
+ ```
1200
+
1201
+ ### Packaging
1202
+
1203
+ Glimmer packaging tasks are detailed under [Packaging & Distribution](#packaging--distribution).
1204
+
1205
+ ### Raw JRuby Command
1206
+
1207
+ If there is a need to run Glimmer directly via the `jruby` command, you
1208
+ may run the following:
1209
+
1210
+ ```
1211
+ jruby -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
1212
+ ```
1213
+
1214
+ The `-J-classpath` option specifies the `swt.jar` file path, which can be a
1215
+ 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.
1216
+
1217
+ The `-r` option preloads (requires) the `glimmer` library in Ruby.
1218
+
1219
+ The `-S` option specifies a script to run.
1220
+
1221
+ #### Mac Support
1222
+
1223
+ The Mac is well supported with the `glimmer` command. The advice below is not needed if you are using it.
1224
+
1225
+ 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).
1226
+
1227
+ Example:
1228
+ ```
1229
+ jruby -J-XstartOnFirstThread -J-classpath "path_to/swt.jar" -r glimmer -S application.rb
1230
+ ```
1231
+
1232
+ ## Girb (Glimmer irb) Command
1233
+
1234
+ 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.
1235
+
1236
+ ```
1237
+ girb
1238
+ ```
1239
+
1240
+ If you cloned [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) project locally, you may run `bin/girb` instead.
1241
+
1242
+ ```
1243
+ bin/girb
1244
+ ```
1245
+
1246
+ Watch out for hands-on examples in this README indicated by "you may copy/paste in [`girb`](#girb-glimmer-irb-command)"
1247
+
1248
+ Keep in mind that all samples live under [https://github.com/AndyObtiva/glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt)
1249
+
1250
+ ## Glimmer GUI DSL Syntax
1251
+
1252
+ Glimmer's core is a GUI DSL with a lightweight visual syntax that makes it easy to visualize the nesting of widgets in the GUI hierarchy tree.
1253
+
1254
+ It is available through mixing in the `Glimmer` module, which makes Glimmer GUI DSL keywords available to both the instance scope and class scope:
1255
+
1256
+ ```ruby
1257
+ include Glimmer
1258
+ ```
1259
+
1260
+ For example, here is the basic "Hello, World!" sample code (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1261
+
1262
+ ```ruby
1263
+ include Glimmer
1264
+
1265
+ shell {
1266
+ text "Glimmer"
1267
+ label {
1268
+ text "Hello, World!"
1269
+ }
1270
+ }.open
1271
+ ```
1272
+
1273
+ The `include Glimmer` declaration on top mixed the `Glimmer` module into the Ruby global main object making the Glimmer GUI DSL available at the top-level global scope.
1274
+
1275
+ While this works well enough for mini-samples, it is better to isolate Glimmer in a class or module during production application development to create a clean separation between view code (GUI) and model code (business domain). Here is the "Hello, World!" sample re-written in a class to illustrate how mixing in the `Glimmer` module (via `include Glimmer`) makes the Glimmer GUI DSL available in both the instance scope and class scope. That is clearly demonstrated by pre-initializing a color constant in the class scope and building the GUI in the `#open` instance method (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1276
+
1277
+ ```ruby
1278
+ class HelloWorld
1279
+ include Glimmer # makes the GUI DSL available in both the class scope and instance scope
1280
+
1281
+ COLOR_FOREGROUND_DEFAULT = rgb(255, 0, 0) # rgb is a GUI DSL keyword used in the class scope
1282
+
1283
+ def open
1284
+ # the following are GUI DSL keywords (shell, text, and label) used in the instance scope
1285
+ shell {
1286
+ text "Glimmer"
1287
+ label {
1288
+ text "Hello, World!"
1289
+ foreground COLOR_FOREGROUND_DEFAULT
1290
+ }
1291
+ }.open
1292
+ end
1293
+ end
1294
+
1295
+ HelloWorld.new.open
1296
+ ```
1297
+
1298
+ This renders "Hello, World!" with a red foreground color:
1299
+
1300
+ ![Hello World Red Foreground Color](images/glimmer-hello-world-red-foreground-color.png)
1301
+
1302
+ 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.
1303
+
1304
+ 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)
1305
+
1306
+ Glimmer DSL syntax consists mainly of:
1307
+ - keywords (e.g. `table` for a table widget)
1308
+ - style/args (e.g. :multi as in `table(:multi)` for a multi-line selection table widget)
1309
+ - 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)
1310
+
1311
+ ### DSL Auto-Expansion
1312
+
1313
+ Glimmer supports a new and radical Ruby DSL concept called DSL Auto-Expansion. It is explained by first mentioning the two types of Glimmer GUI DSL keywords: static and dynamic.
1314
+
1315
+ Static keywords are pre-identified keywords in the Glimmer DSL, such as `shell`, `display`, `message_box`, `async_exec`, `sync_exec`, and `bind`.
1316
+
1317
+ 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.
1318
+
1319
+ 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.
1320
+
1321
+ For example, if a project adds this custom Java SWT library:
1322
+
1323
+ https://www.eclipse.org/nebula/widgets/cdatetime/cdatetime.php?page=operation
1324
+
1325
+ Glimmer will automatically support using the keyword `c_date_time`
1326
+
1327
+ You will learn more about widgets next.
1328
+
1329
+ ### Widgets
1330
+
1331
+ Glimmer GUIs (user interfaces) are modeled with widgets, which are wrappers around the SWT library widgets found here:
1332
+
1333
+ https://www.eclipse.org/swt/widgets/
1334
+
1335
+ This screenshot taken from the link above should give a glimpse of how SWT widgets look and feel:
1336
+
1337
+ [![SWT Widgets](images/glimmer-swt-widgets.png)](https://www.eclipse.org/swt/widgets/)
1338
+
1339
+ 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:
1340
+
1341
+ - `shell` instantiates `org.eclipse.swt.widgets.Shell`
1342
+ - `text` instantiates `org.eclipse.swt.widgets.Text`
1343
+ - `button` instantiates `org.eclipse.swt.widgets.Button`
1344
+ - `label` instantiates `org.eclipse.swt.widgets.Label`
1345
+ - `composite` instantiates `org.eclipse.swt.widgets.Composite`
1346
+ - `tab_folder` instantiates `org.eclipse.swt.widgets.TabFolder`
1347
+ - `tab_item` instantiates `org.eclipse.swt.widgets.TabItem`
1348
+ - `table` instantiates `org.eclipse.swt.widgets.Table`
1349
+ - `table_column` instantiates `org.eclipse.swt.widgets.TableColumn`
1350
+ - `tree` instantiates `org.eclipse.swt.widgets.Tree`
1351
+ - `combo` instantiates `org.eclipse.swt.widgets.Combo`
1352
+ - `list` instantiates `org.eclipse.swt.widgets.List`
1353
+
1354
+ Every **widget** is sufficiently declared by name, but may optionally be accompanied with:
1355
+ - SWT **style**/***arguments*** wrapped by parenthesis according to [Glimmer Style Guide](#glimmer-style-guide) (see [next section](#widget-styles) for details).
1356
+ - Ruby block containing **content**, which may be **properties** (e.g. `enabled false`) or nested **widgets** (e.g. `table_column` nested inside `table`)
1357
+
1358
+ For example, if we were to revisit `samples/hello/hello_world.rb` above (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1359
+
1360
+ ```ruby
1361
+ shell {
1362
+ text "Glimmer"
1363
+ label {
1364
+ text "Hello, World!"
1365
+ }
1366
+ }.open
1367
+ ```
1368
+
1369
+ Note that `shell` instantiates the outer shell **widget**, in other words, the window that houses all of the desktop graphical user interface.
1370
+
1371
+ `shell` is then followed by a ***block*** that contains
1372
+
1373
+ ```ruby
1374
+ # ...
1375
+ text "Glimmer" # text property of shell
1376
+ label { # label widget declaration as content of shell
1377
+ text "Hello, World!" # text property of label
1378
+ }
1379
+ # ...
1380
+ ```
1381
+
1382
+ 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).
1383
+
1384
+ The second line declares the `label` **widget**, which is followed by a Ruby **content** ***block*** that contains its `text` **property** with value `"Hello, World!"`
1385
+
1386
+ 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.
1387
+
1388
+ Example:
1389
+
1390
+ ```ruby
1391
+ shell {|shell_proxy|
1392
+ #...
1393
+ }
1394
+ ```
1395
+
1396
+ Remember that The `shell` widget is always the outermost widget containing all others in a Glimmer desktop windowed application.
1397
+
1398
+ 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)
1399
+
1400
+ ```ruby
1401
+ @shell = shell {
1402
+ # properties and content
1403
+ # ...
1404
+ }
1405
+ @shell.open
1406
+ ```
1407
+
1408
+ 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)
1409
+
1410
+ Check out the [samples](samples) directory for more examples.
1411
+
1412
+ Example from [hello_tab.rb](samples/hello/hello_tab.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1413
+
1414
+ ![Hello Tab English](images/glimmer-hello-tab-english.png)
1415
+
1416
+ ![Hello Tab French](images/glimmer-hello-tab-french.png)
1417
+
1418
+ ```ruby
1419
+ shell {
1420
+ text "Hello, Tab!"
1421
+ tab_folder {
1422
+ tab_item {
1423
+ text "English"
1424
+ label {
1425
+ text "Hello, World!"
1426
+ }
1427
+ }
1428
+ tab_item {
1429
+ text "French"
1430
+ label {
1431
+ text "Bonjour Univers!"
1432
+ }
1433
+ }
1434
+ }
1435
+ }.open
1436
+ ```
1437
+
1438
+ 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.
1439
+
1440
+ #### Display
1441
+
1442
+ SWT Display is a singleton in Glimmer. It is used in SWT to represent your display device, allowing you to manage GUI globally
1443
+ and access available monitors.
1444
+ It is automatically instantiated upon first instantiation of a `shell` widget.
1445
+ Alternatively, for advanced use cases, it can be created explicitly with Glimmer `display` keyword. When a `shell` is later declared, it
1446
+ automatically uses the display created earlier without having to explicitly hook it.
1447
+
1448
+ ```ruby
1449
+ @display = display {
1450
+ cursor_location 300, 300
1451
+ on_swt_keydown {
1452
+ # ...
1453
+ }
1454
+ # ...
1455
+ }
1456
+ @shell = shell { # uses display created above
1457
+ }
1458
+ ```
1459
+ The benefit of instantiating an SWT Display explicitly is to set [Properties](#widget-properties) or [Observers](#observer).
1460
+ Although SWT Display is not technically a widget, it has similar APIs in SWT and similar DSL support in Glimmer.
1461
+
1462
+ #### SWT Proxies
1463
+
1464
+ Glimmer follows Proxy Design Pattern by having Ruby proxy wrappers for all SWT objects:
1465
+ - `Glimmer::SWT:WidgetProxy` wraps all descendants of `org.eclipse.swt.widgets.Widget` except the ones that have their own wrappers.
1466
+ - `Glimmer::SWT::ShellProxy` wraps `org.eclipse.swt.widgets.Shell`
1467
+ - `Glimmer::SWT:TabItemProxy` wraps `org.eclipse.swt.widget.TabItem` (also adds a composite to enable adding content under tab items directly in Glimmer)
1468
+ - `Glimmer::SWT:LayoutProxy` wraps all descendants of `org.eclipse.swt.widget.Layout`
1469
+ - `Glimmer::SWT:LayoutDataProxy` wraps all layout data objects
1470
+ - `Glimmer::SWT:DisplayProxy` wraps `org.eclipse.swt.widget.Display` (manages displaying GUI)
1471
+ - `Glimmer::SWT:ColorProxy` wraps `org.eclipse.swt.graphics.Color`
1472
+ - `Glimmer::SWT:FontProxy` wraps `org.eclipse.swt.graphics.Font`
1473
+ - `Glimmer::SWT::WidgetListenerProxy` wraps all widget listeners
1474
+
1475
+ These proxy objects have an API and provide some convenience methods, some of which are mentioned below.
1476
+
1477
+ ##### `#content { ... }`
1478
+
1479
+ Glimmer allows re-opening any widget and adding properties or extra content after it has been constructed already by using the `#content` method.
1480
+
1481
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1482
+
1483
+ ```ruby
1484
+ @shell = shell {
1485
+ text "Application"
1486
+ row_layout
1487
+ @label1 = label {
1488
+ text "Hello,"
1489
+ }
1490
+ }
1491
+ @shell.content {
1492
+ minimum_size 130, 130
1493
+ label {
1494
+ text "World!"
1495
+ }
1496
+ }
1497
+ @label1.content {
1498
+ foreground :red
1499
+ }
1500
+ @shell.open
1501
+ ```
1502
+
1503
+ ##### `message_box`
1504
+
1505
+ 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.
1506
+
1507
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1508
+
1509
+ ```ruby
1510
+ include Glimmer
1511
+
1512
+ @shell = shell {
1513
+ text 'Hello, Message Box!'
1514
+ button {
1515
+ text 'Please Click To Win a Surprise'
1516
+ on_widget_selected {
1517
+ message_box(@shell) {
1518
+ text 'Surprise'
1519
+ message "Congratulations!\n\nYou have won $1,000,000!"
1520
+ }.open
1521
+ }
1522
+ }
1523
+ }
1524
+ @shell.open
1525
+ ```
1526
+
1527
+ ![Hello Message Box Dialog](images/glimmer-hello-message-box-dialog.png)
1528
+
1529
+ 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):
1530
+
1531
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1532
+
1533
+ ```ruby
1534
+ include Glimmer
1535
+
1536
+ message_box {
1537
+ text 'Greeting'
1538
+ message "Hello, World!"
1539
+ }.open
1540
+ ```
1541
+
1542
+ ##### `#swt_widget`
1543
+
1544
+ 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.
1545
+
1546
+ ##### Shell widget proxy methods
1547
+
1548
+ Shell widget proxy has extra methods specific to SWT Shell:
1549
+ - `#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.
1550
+ - `#show`: Alias for `#open`
1551
+ - `#hide`: Hides a shell setting "visible" property to false
1552
+ - `#close`: Closes the shell
1553
+ - `#center`: Centers the shell within monitor it is in
1554
+ - `#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.
1555
+ - `#visible?`: Returns whether a shell is visible
1556
+ - `#opened_before?`: Returns whether a shell has been opened at least once before (additionally implying the SWT Event Loop has been started already)
1557
+ - `#visible=`: Setting to true opens/shows shell. Setting to false hides the shell.
1558
+ - `#pack`: Packs contained widgets using SWT's `Shell#pack` method
1559
+ - `#pack_same_size`: Packs contained widgets without changing shell's size when widget sizes change
1560
+
1561
+ ##### Shell Icon
1562
+
1563
+ 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)
1564
+
1565
+ Example:
1566
+
1567
+ ```ruby
1568
+ shell {
1569
+ # ...
1570
+ image 'path/to/image.png'
1571
+ # ...
1572
+ }
1573
+ ```
1574
+
1575
+ ###### Shell Icon Tip for Packaging on Windows
1576
+
1577
+ 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'`)
1578
+
1579
+ #### Dialog
1580
+
1581
+ 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.
1582
+
1583
+ 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.
1584
+
1585
+ #### Menus
1586
+
1587
+ Glimmer DSL provides support for SWT Menu and MenuItem widgets.
1588
+
1589
+ There are 2 main types of menus in SWT:
1590
+ - Menu Bar (shows up on top)
1591
+ - Pop Up Context Menu (shows up when right-clicking a widget)
1592
+
1593
+ Underneath both types, there can be a 3rd menu type called Drop Down.
1594
+
1595
+ 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.
1596
+
1597
+ The ampersand symbol indicates the keyboard shortcut key for the menu item (e.g. '&Help' can be triggered on Windows by hitting ALT+H)
1598
+
1599
+ Example of a Menu Bar (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1600
+
1601
+ ```ruby
1602
+ shell { |shell_proxy|
1603
+ text 'Hello, Menu Bar!'
1604
+ grid_layout
1605
+ label(:center) {
1606
+ font height: 16
1607
+ text 'Check Out The File Menu and History Menu in The Menu Bar Above!'
1608
+ }
1609
+ menu_bar {
1610
+ menu {
1611
+ text '&File'
1612
+ menu_item {
1613
+ text 'E&xit'
1614
+ }
1615
+ menu_item(0) {
1616
+ text '&New'
1617
+ on_widget_selected {
1618
+ message_box(shell_proxy) {
1619
+ text 'New File'
1620
+ message 'New File Contents'
1621
+ }.open
1622
+ }
1623
+ }
1624
+ menu(1) {
1625
+ text '&Options'
1626
+ menu_item(:radio) {
1627
+ text 'Option 1'
1628
+ }
1629
+ menu_item(:separator)
1630
+ menu_item(:check) {
1631
+ text 'Option 3'
1632
+ }
1633
+ }
1634
+ }
1635
+ menu {
1636
+ text '&History'
1637
+ menu {
1638
+ text '&Recent'
1639
+ menu_item {
1640
+ text 'File 1'
1641
+ on_widget_selected {
1642
+ message_box(shell_proxy) {
1643
+ text 'File 1'
1644
+ message 'File 1 Contents'
1645
+ }.open
1646
+ }
1647
+ }
1648
+ menu_item {
1649
+ text 'File 2'
1650
+ on_widget_selected {
1651
+ message_box(shell_proxy) {
1652
+ text 'File 2'
1653
+ message 'File 2 Contents'
1654
+ }.open
1655
+ }
1656
+ }
1657
+ }
1658
+ }
1659
+ }
1660
+ }.open
1661
+ ```
1662
+
1663
+ Example of a Pop Up Context Menu (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
1664
+
1665
+ ```ruby
1666
+ shell { |shell_proxy|
1667
+ text 'Hello, Pop Up Context Menu!'
1668
+ grid_layout
1669
+ label {
1670
+ font height: 16
1671
+ text 'Right-Click To Pop Up a Context Menu'
1672
+ menu {
1673
+ menu {
1674
+ text '&History'
1675
+ menu {
1676
+ text '&Recent'
1677
+ menu_item {
1678
+ text 'File 1'
1679
+ on_widget_selected {
1680
+ message_box(shell_proxy) {
1681
+ text 'File 1'
1682
+ message 'File 1 Contents'
1683
+ }.open
1684
+ }
1685
+ }
1686
+ menu_item {
1687
+ text 'File 2'
1688
+ on_widget_selected {
1689
+ message_box(shell_proxy) {
1690
+ text 'File 2'
1691
+ message 'File 2 Contents'
1692
+ }.open
1693
+ }
1694
+ }
1695
+ }
1696
+ }
1697
+ }
1698
+ }
1699
+ }.open
1700
+ ```
1701
+
1702
+ #### ScrolledComposite
1703
+
1704
+ Glimmer provides smart defaults for the `scrolled_composite` widget by:
1705
+ - Automatically setting the nested widget as its content (meaning use can just like a plain old `composite` to add scrolling)
1706
+ - 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 )
1707
+ - Automatically setting the expand horizontal and expand vertical SWT properties to `true`
1708
+
1709
+ ### Widget Styles
1710
+
1711
+ SWT widgets receive `SWT` styles in their constructor as per this guide:
1712
+
1713
+ https://wiki.eclipse.org/SWT_Widget_Style_Bits
1714
+
1715
+ 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`).
1716
+
1717
+ These styles customize widget look, feel, and behavior.
1718
+
1719
+ Example:
1720
+
1721
+ ```ruby
1722
+ # ...
1723
+ list(:multi) { # SWT styles go inside ()
1724
+ # ...
1725
+ }
1726
+ # ...
1727
+ ```
1728
+ Passing `:multi` to `list` widget enables list element multi-selection.
1729
+
1730
+ ```ruby
1731
+ # ...
1732
+ composite(:border) { # SWT styles go inside ()
1733
+ # ...
1734
+ }
1735
+ # ...
1736
+ ```
1737
+ Passing `:border` to `composite` widget ensures it has a border.
1738
+
1739
+ When you need to pass in **multiple SWT styles**, simply separate by commas.
1740
+
1741
+ Example:
1742
+
1743
+ ```ruby
1744
+ # ...
1745
+ text(:center, :border) { # Multiple SWT styles separated by comma
1746
+ # ...
1747
+ }
1748
+ # ...
1749
+ ```
1750
+
1751
+ 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):
1752
+
1753
+ - `text(:border)`
1754
+ - `table(:border, :virtual, :full_selection)`
1755
+ - `tree(:border, :virtual, :v_scroll, :h_scroll)`
1756
+ - `spinner(:border)`
1757
+ - `list(:border, :v_scroll)`
1758
+ - `button(:push)`
1759
+
1760
+ You may check out all available `SWT` styles here:
1761
+
1762
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
1763
+
1764
+ #### Explicit SWT Style Bit
1765
+
1766
+ 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`.
1767
+
1768
+ 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.
1769
+ Example:
1770
+
1771
+ ```ruby
1772
+ style = swt(:border, :v_scroll)
1773
+ ```
1774
+
1775
+ #### Negative SWT Style Bits
1776
+
1777
+ In rare occasions, you might need to apply & with a negative (not) style bit to negate it from another style bit that includes it.
1778
+ Glimmer facilitates that by declaring the negative style bit via postfixing a symbol with `!`.
1779
+
1780
+ Example:
1781
+
1782
+ ```ruby
1783
+ style = swt(:shell_trim, :max!) # creates a shell trim style without the maximize button (negated)
1784
+ ```
1785
+
1786
+ #### Extra SWT Styles
1787
+
1788
+ ##### Non-resizable Window
1789
+
1790
+ 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!)`.
1791
+
1792
+ Glimmer makes this easier by alternatively offering a `:no_resize` extra SWT style, added for convenience.
1793
+ This makes declaring a non-resizable window as easy as:
1794
+
1795
+ ```ruby
1796
+ shell(:no_resize) {
1797
+ # ...
1798
+ }
1799
+ ```
1800
+
1801
+ ### Widget Properties
1802
+
1803
+ 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:
1804
+
1805
+ https://help.eclipse.org/2019-12/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_controls.htm?cp=2_0_7_0_0
1806
+
1807
+
1808
+ Code examples:
1809
+
1810
+ ```ruby
1811
+ # ...
1812
+ label {
1813
+ text "Hello, World!" # SWT properties go inside {} block
1814
+ }
1815
+ # ...
1816
+ ```
1817
+
1818
+ In the above example, the `label` widget `text` property was set to "Hello, World!".
1819
+
1820
+ ```ruby
1821
+ # ...
1822
+ button {
1823
+ enabled bind(@tic_tac_toe_board.box(row, column), :empty)
1824
+ }
1825
+ # ...
1826
+ ```
1827
+
1828
+ 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)
1829
+
1830
+ #### Color
1831
+
1832
+ 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.
1833
+
1834
+ Example:
1835
+
1836
+ ```ruby
1837
+ # ...
1838
+ label {
1839
+ background rgb(144, 240, 244)
1840
+ foreground rgba(38, 92, 232, 255)
1841
+ }
1842
+ # ...
1843
+ ```
1844
+
1845
+ SWT also supports standard colors available as constants under the `SWT` namespace with the `COLOR_` prefix (e.g. `SWT::COLOR_BLUE`)
1846
+
1847
+ Glimmer supports constructing colors for these constants as lowercase Ruby symbols (with or without `color_` prefix) passed to `color` DSL keyword
1848
+
1849
+ Example:
1850
+
1851
+ ```ruby
1852
+ # ...
1853
+ label {
1854
+ background color(:black)
1855
+ foreground color(:yellow)
1856
+ }
1857
+ label {
1858
+ background color(:color_white)
1859
+ foreground color(:color_red)
1860
+ }
1861
+ # ...
1862
+ ```
1863
+
1864
+ You may check out all available standard colors in `SWT` over here (having `COLOR_` prefix):
1865
+
1866
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
1867
+
1868
+
1869
+ ##### `#swt_color`
1870
+
1871
+ 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.
1872
+
1873
+ Example:
1874
+
1875
+ ```ruby
1876
+ color(:black).swt_color # returns SWT Color object
1877
+ ```
1878
+
1879
+ #### Font
1880
+
1881
+ Fonts are represented in Glimmer as a hash of name, height, and style keys.
1882
+
1883
+ The style can be one (or more) of 3 values: `:normal`, `:bold`, and `:italic`
1884
+
1885
+ Example:
1886
+
1887
+ ```ruby
1888
+ # ...
1889
+ label {
1890
+ font name: 'Arial', height: 36, style: :normal
1891
+ }
1892
+ # ...
1893
+ ```
1894
+
1895
+ Keys are optional, so some of them may be left off.
1896
+ When passing multiple styles, they are included in an array.
1897
+
1898
+ Example:
1899
+
1900
+ ```ruby
1901
+ # ...
1902
+ label {
1903
+ font style: [:bold, :italic]
1904
+ }
1905
+ # ...
1906
+ ```
1907
+
1908
+ 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.
1909
+
1910
+ Example:
1911
+
1912
+ ```ruby
1913
+ @font = font(name: 'Arial', height: 36, style: :normal)
1914
+ ```
1915
+
1916
+ ### Cursor
1917
+
1918
+ SWT widget `cursor` property represents the mouse cursor you see on the screen when you hover over that widget.
1919
+
1920
+ 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)
1921
+
1922
+ 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.
1923
+
1924
+ Example:
1925
+
1926
+ ```ruby
1927
+ shell {
1928
+ minimum_size 128, 128
1929
+ cursor :appstarting
1930
+ }
1931
+ ```
1932
+
1933
+ This sets the shell `cursor` to that of `SWT::CURSOR_APPSTARTING`
1934
+
1935
+ ### Layouts
1936
+
1937
+ Glimmer lays widgets out visually using SWT layouts, which can only be set on composite widget and subclasses.
1938
+
1939
+ The most common SWT layouts are:
1940
+ - `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.
1941
+ - `RowLayout`: lays widgets out horizontally or vertically in varying proportions with advanced spacing/margin/justify options
1942
+ - `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.
1943
+
1944
+ 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`).
1945
+
1946
+ Example:
1947
+
1948
+ ```ruby
1949
+ # ...
1950
+ composite {
1951
+ row_layout {
1952
+ wrap true
1953
+ pack false
1954
+ justify true
1955
+ type :vertical
1956
+ margin_left 1
1957
+ margin_top 2
1958
+ margin_right 3
1959
+ margin_bottom 4
1960
+ spacing 5
1961
+ }
1962
+ # ... widgets follow
1963
+ }
1964
+ # ...
1965
+ ```
1966
+
1967
+ 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.
1968
+
1969
+ 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`.)
1970
+
1971
+ ```ruby
1972
+ # ...
1973
+ composite {
1974
+ row_layout :horizontal
1975
+ # ... widgets follow
1976
+ }
1977
+ # ...
1978
+ ```
1979
+
1980
+ Here is a more sophisticated example taken from [hello_computed.rb](samples/hello/hello_computed.rb) sample:
1981
+
1982
+ ![Hello Computed](images/glimmer-hello-computed.png)
1983
+
1984
+ ```ruby
1985
+ shell {
1986
+ text 'Hello, Computed!'
1987
+ composite {
1988
+ grid_layout {
1989
+ num_columns 2
1990
+ make_columns_equal_width true
1991
+ horizontal_spacing 20
1992
+ vertical_spacing 10
1993
+ }
1994
+ label {text 'First &Name: '}
1995
+ text {
1996
+ text bind(@contact, :first_name)
1997
+ layout_data {
1998
+ horizontal_alignment :fill
1999
+ grab_excess_horizontal_space true
2000
+ }
2001
+ }
2002
+ label {text '&Last Name: '}
2003
+ text {
2004
+ text bind(@contact, :last_name)
2005
+ layout_data {
2006
+ horizontal_alignment :fill
2007
+ grab_excess_horizontal_space true
2008
+ }
2009
+ }
2010
+ label {text '&Year of Birth: '}
2011
+ text {
2012
+ text bind(@contact, :year_of_birth)
2013
+ layout_data {
2014
+ horizontal_alignment :fill
2015
+ grab_excess_horizontal_space true
2016
+ }
2017
+ }
2018
+ label {text 'Name: '}
2019
+ label {
2020
+ text bind(@contact, :name, computed_by: [:first_name, :last_name])
2021
+ layout_data {
2022
+ horizontal_alignment :fill
2023
+ grab_excess_horizontal_space true
2024
+ }
2025
+ }
2026
+ label {text 'Age: '}
2027
+ label {
2028
+ text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
2029
+ layout_data {
2030
+ horizontal_alignment :fill
2031
+ grab_excess_horizontal_space true
2032
+ }
2033
+ }
2034
+ }
2035
+ }.open
2036
+ ```
2037
+
2038
+ Check out the samples directory for more advanced examples of layouts in Glimmer.
2039
+
2040
+ **Defaults**:
2041
+
2042
+ 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.
2043
+
2044
+ Glimmer shell always comes with `fill_layout` having `:horizontal` type.
2045
+
2046
+ This is a great guide for learning more about SWT layouts:
2047
+
2048
+ https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
2049
+
2050
+ Also, for a reference, check the SWT API:
2051
+
2052
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
2053
+
2054
+ ### Layout Data
2055
+
2056
+ 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.
2057
+
2058
+ By convention, SWT layouts expect widgets to set layout data with a class matching their class name with the word "Data" replacing "Layout":
2059
+ - `GridLayout` on a composite demands `GridData` on contained widgets
2060
+ - `RowLayout` on a composite demands `RowData` on contained widgets
2061
+
2062
+ Not all layouts support layout data to further customize widget layouts. For example, `FillLayout` supports no layout data.
2063
+
2064
+ 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).
2065
+
2066
+ 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)
2067
+
2068
+ Glimmer also automatically accepts symbols (e.g. `:fill`) for SWT style arguments like `SWT::FILL`.
2069
+
2070
+ Examples:
2071
+
2072
+ ```ruby
2073
+ # ...
2074
+ composite {
2075
+ row_layout :horizontal
2076
+ label {
2077
+ layout_data { # followed by properties
2078
+ width 50
2079
+ height 30
2080
+ }
2081
+ }
2082
+ # ... more widgets follow
2083
+ }
2084
+ # ...
2085
+ ```
2086
+
2087
+ ```ruby
2088
+ # ...
2089
+ composite {
2090
+ grid_layout 3, false # grid layout with 3 columns not of equal width
2091
+ label {
2092
+ # layout data followed by arguments passed to SWT GridData constructor
2093
+ layout_data :fill, :end, true, false
2094
+ }
2095
+ }
2096
+ # ...
2097
+ ```
2098
+
2099
+ ```ruby
2100
+ # ...
2101
+ composite {
2102
+ grid_layout 3, false # grid layout with 3 columns not of equal width
2103
+ label {
2104
+ # layout data set explicitly via an object (helps in rare cases that break convention)
2105
+ layout_data GridData.new(swt(:fill), swt(:end), true, false)
2106
+ }
2107
+ }
2108
+ # ...
2109
+ ```
2110
+
2111
+ 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.
2112
+
2113
+ **NOTE**: Layout data must never be reused between widgets. Always specify or clone again for every widget.
2114
+
2115
+ This is a great guide for learning more about SWT layouts:
2116
+
2117
+ https://www.eclipse.org/articles/Article-Understanding-Layouts/Understanding-Layouts.htm
2118
+
2119
+ Also, for a reference, check the SWT API:
2120
+
2121
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
2122
+
2123
+ ### Data-Binding
2124
+
2125
+ Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
2126
+
2127
+ #### General Examples
2128
+
2129
+ `text bind(contact, :first_name)`
2130
+
2131
+ This example binds the text property of a widget like `label` to the first name of a contact model.
2132
+
2133
+ `text bind(contact, 'address.street')`
2134
+
2135
+ This example binds the text property of a widget like `label` to the nested street of
2136
+ the address of a contact. This is called nested property data binding.
2137
+
2138
+ `text bind(contact, 'address.street', on_read: :upcase, on_write: :downcase)`
2139
+
2140
+ 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)
2141
+
2142
+ `text bind(contact, 'address.street', on_read: lambda { |s| s[0..10] })`
2143
+
2144
+ 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`
2145
+
2146
+ `text bind(contact, 'address.street') { |s| s[0..10] }`
2147
+
2148
+ 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.
2149
+
2150
+ `text bind(contact, 'address.street', read_only: true)
2151
+
2152
+ This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
2153
+
2154
+ `text bind(contact, 'addresses[1].street')`
2155
+
2156
+ 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.
2157
+
2158
+ `text bind(contact, :age, computed_by: :date_of_birth)`
2159
+
2160
+ This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
2161
+
2162
+ `text bind(contact, :name, computed_by: [:first_name, :last_name])`
2163
+
2164
+ This example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
2165
+
2166
+ `text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
2167
+
2168
+ 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`.
2169
+
2170
+ Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2171
+
2172
+ #### Combo
2173
+
2174
+ 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.
2175
+
2176
+ When data-binding a `combo` widget, Glimmer can automatically deduce available options from data-bound model by convention: `{attribute_name}_options` method.
2177
+
2178
+ ![Hello Combo](images/glimmer-hello-combo.png)
2179
+
2180
+ ![Hello Combo](images/glimmer-hello-combo-expanded.png)
2181
+
2182
+ ```ruby
2183
+ class Person
2184
+ attr_accessor :country, :country_options
2185
+
2186
+ def initialize
2187
+ self.country_options=["", "Canada", "US", "Mexico"]
2188
+ self.country = "Canada"
2189
+ end
2190
+
2191
+ def reset_country
2192
+ self.country = "Canada"
2193
+ end
2194
+ end
2195
+
2196
+ class HelloCombo
2197
+ include Glimmer
2198
+ def launch
2199
+ person = Person.new
2200
+ shell {
2201
+ composite {
2202
+ combo(:read_only) {
2203
+ selection bind(person, :country)
2204
+ }
2205
+ button {
2206
+ text "Reset"
2207
+ on_widget_selected do
2208
+ person.reset_country
2209
+ end
2210
+ }
2211
+ }
2212
+ }.open
2213
+ end
2214
+ end
2215
+
2216
+ HelloCombo.new.launch
2217
+ ```
2218
+
2219
+ `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.
2220
+
2221
+ #### List
2222
+
2223
+ Example from [samples/hello/hello_list_single_selection.rb](samples/hello_list_single_selection.rb) sample:
2224
+
2225
+ ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
2226
+
2227
+ ```ruby
2228
+ shell {
2229
+ composite {
2230
+ list {
2231
+ selection bind(person, :country)
2232
+ }
2233
+ button {
2234
+ text "Reset"
2235
+ on_widget_selected do
2236
+ person.reset_country
2237
+ end
2238
+ }
2239
+ }
2240
+ }.open
2241
+ ```
2242
+
2243
+ `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).
2244
+
2245
+ 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.
2246
+
2247
+ 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)):
2248
+
2249
+ ![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
2250
+
2251
+ ```ruby
2252
+ class Person
2253
+ attr_accessor :provinces, :provinces_options
2254
+
2255
+ def initialize
2256
+ self.provinces_options=[
2257
+ "",
2258
+ "Quebec",
2259
+ "Ontario",
2260
+ "Manitoba",
2261
+ "Saskatchewan",
2262
+ "Alberta",
2263
+ "British Columbia",
2264
+ "Nova Skotia",
2265
+ "Newfoundland"
2266
+ ]
2267
+ self.provinces = ["Quebec", "Manitoba", "Alberta"]
2268
+ end
2269
+
2270
+ def reset_provinces
2271
+ self.provinces = ["Quebec", "Manitoba", "Alberta"]
2272
+ end
2273
+ end
2274
+
2275
+ class HelloListMultiSelection
2276
+ include Glimmer
2277
+ def launch
2278
+ person = Person.new
2279
+ shell {
2280
+ composite {
2281
+ list(:multi) {
2282
+ selection bind(person, :provinces)
2283
+ }
2284
+ button {
2285
+ text "Reset"
2286
+ on_widget_selected do
2287
+ person.reset_provinces
2288
+ end
2289
+ }
2290
+ }
2291
+ }.open
2292
+ end
2293
+ end
2294
+
2295
+ HelloListMultiSelection.new.launch
2296
+ ```
2297
+
2298
+ 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`.
2299
+
2300
+ 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).
2301
+
2302
+ You may learn more about Glimmer's data-binding syntax by reading the code under the [samples](samples) directory.
2303
+
2304
+ #### Table
2305
+
2306
+ The SWT Tree widget renders a multi-column data table, such as a contact listing or a sales report.
2307
+
2308
+ To data-bind a Table, you need the main model, the collection property, and the text display attribute for each table column.
2309
+
2310
+ This involves using the `bind` keyword mentioned above in addition to a special `column_properties` keyword that takes the table column text attribute methods.
2311
+
2312
+ It assumes you have defined the table columns via `table_column` widget.
2313
+
2314
+ Example:
2315
+
2316
+ ```ruby
2317
+ shell {
2318
+ @table = table {
2319
+ table_column {
2320
+ text "Name"
2321
+ width 120
2322
+ }
2323
+ table_column {
2324
+ text "Age"
2325
+ width 120
2326
+ }
2327
+ table_column {
2328
+ text "Adult"
2329
+ width 120
2330
+ }
2331
+ items bind(group, :people), column_properties(:name, :age, :adult)
2332
+ selection bind(group, :selected_person)
2333
+ on_mouse_up { |event|
2334
+ @table.edit_table_item(event.table_item, event.column_index)
2335
+ }
2336
+ }
2337
+ }
2338
+ ```
2339
+
2340
+ The code above includes two data-bindings:
2341
+ - 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.
2342
+ - 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)
2343
+ - 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.)
2344
+
2345
+ Additionally, Table `items` data-binding automatically stores each node model unto the SWT TableItem object via `setData` method. This enables things like searchability.
2346
+
2347
+ The table widget in Glimmer is represented by a subclass of `WidgetProxy` called `TableProxy`.
2348
+ TableProxy includes a `search` method that takes a block to look for a table item.
2349
+
2350
+ Example:
2351
+
2352
+ ```ruby
2353
+ found_array = @table.search { |table_item| table_item.getData == company.owner }
2354
+ ```
2355
+
2356
+ 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.
2357
+
2358
+ 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)`.
2359
+ This automatically leverages the SWT TableEditor custom class behind the scenes, displaying a text widget to the user to change the selected or
2360
+ passed table item text into something else.
2361
+ It automatically persists the change to `items` data-bound model on ENTER/FOCUS-OUT or cancels on ESC/NO-CHANGE.
2362
+
2363
+ ##### Table Sorting
2364
+
2365
+ Glimmer automatically adds sorting support to the SWT `Table` widget.
2366
+
2367
+ Check out the [Contact Manager](#contact-manager) sample for an example.
2368
+ You may click on any column and it will sort by ascending order first and descending if you click again.
2369
+
2370
+ Glimmer automatic table sorting supports `String`, `Integer`, and `Float` columns out of the box as well as any column data that is comparable.
2371
+
2372
+ 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`.
2373
+
2374
+ Should you have a special data type that could not be compared automatically, Glimmer offers the following 3 alternatives for custom sorting:
2375
+ - `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`).
2376
+ - `sort_by(&block)`: this works just like Ruby `Enumerable` `sort_by`. The block receives the table column data as argument.
2377
+ - `sort(&comparator)`: this works just like Ruby `Enumerable` `sort`. The comparator block receives two objects from the table column data.
2378
+
2379
+ 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)
2380
+
2381
+ Example:
2382
+
2383
+ ```ruby
2384
+ # ...
2385
+ table {
2386
+ table_column {
2387
+ text 'Task'
2388
+ width 120
2389
+ }
2390
+ table_column {
2391
+ text 'Project'
2392
+ width 120
2393
+ }
2394
+ table_column {
2395
+ text 'Duration (hours)'
2396
+ width 120
2397
+ sort_property :duration_in_hours
2398
+ }
2399
+ table_column {
2400
+ text 'Priority'
2401
+ width 120
2402
+ sort_by { |value| ['High', 'Medium', 'Low'].index(value) }
2403
+ }
2404
+ table_column {
2405
+ text 'Start Date'
2406
+ width 120
2407
+ sort { |d1, d2| d1.to_date <=> d2.to_date }
2408
+ }
2409
+ additional_sort_properties :project_name, :duration_in_hours, :name
2410
+ items bind(Task, :list), column_properties(:name, :project_name, :duration, :priority, :start_date)
2411
+ # ...
2412
+ }
2413
+ # ...
2414
+ ```
2415
+
2416
+ Here is an explanation of the example above:
2417
+ - Task and Project table columns are data-bound to the `:name` and `:project_name` properties and sorted through them automatically
2418
+ - Task Duration table column is data-bound to the `:duration` property, but sorted via the `:duration_in_hours` property instead
2419
+ - Task Priority table column has a custom sort_by block
2420
+ - Task Start Date table column has a custom sort comparator block
2421
+ - Additional (secondary) sort properties are applied when sorting by Task, Project, or Duration in the order specified
2422
+
2423
+
2424
+ #### Tree
2425
+
2426
+ The SWT Tree widget visualizes a tree data-structure, such as an employment or composition hierarchy.
2427
+
2428
+ To data-bind a Tree, you need the root model, the children querying method, and the text display attribute on each child.
2429
+
2430
+ This involves using the `bind` keyword mentioned above in addition to a special `tree_properties` keyword that takes the children and text attribute methods.
2431
+
2432
+ Example:
2433
+
2434
+ ```ruby
2435
+ shell {
2436
+ @tree = tree {
2437
+ items bind(company, :owner), tree_properties(children: :coworkers, text: :name)
2438
+ selection bind(company, :selected_coworker)
2439
+ }
2440
+ }
2441
+ ```
2442
+
2443
+ The code above includes two data-bindings:
2444
+ - 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.
2445
+ - Tree `selection`, which binds the single tree item selected by the user to the attribute denoted by the `bind` keyword
2446
+
2447
+ Additionally, Tree `items` data-binding automatically stores each node model unto the SWT TreeItem object via `setData` method. This enables things like searchability.
2448
+
2449
+ The tree widget in Glimmer is represented by a subclass of `WidgetProxy` called `TreeProxy`.
2450
+ TreeProxy includes a `depth_first_search` method that takes a block to look for a tree item.
2451
+
2452
+ Example:
2453
+
2454
+ ```ruby
2455
+ found_array = @tree.depth_first_search { |tree_item| tree_item.getData == company.owner }
2456
+ ```
2457
+
2458
+ 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.
2459
+
2460
+ 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
2461
+ 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.
2462
+
2463
+ ### Observer
2464
+
2465
+ 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.
2466
+
2467
+ #### Observing Widgets
2468
+
2469
+ Glimmer supports observing widgets with two main types of events:
2470
+ 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`).
2471
+ 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)
2472
+
2473
+ Additionally, there are two more types of events:
2474
+ - SWT `display` supports global listeners called filters that run on any widget. They are hooked via `on_swt_{swt-event-constant}`
2475
+ - SWT `display` supports Mac application menu item observers (`on_about` and `on_preferences`), which you can read about under [Miscellaneous](#miscellaneous).
2476
+
2477
+ 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.
2478
+
2479
+ **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".
2480
+
2481
+ For example, if you look at the `Button` SWT API:
2482
+ https://help.eclipse.org/2019-12/index.jsp?topic=%2Forg.eclipse.platform.doc.isv%2Freference%2Fapi%2Forg%2Feclipse%2Fswt%2Fbrowser%2FBrowser.html
2483
+
2484
+ 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`
2485
+
2486
+ 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.
2487
+
2488
+ 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.
2489
+
2490
+ Instead, simply add a `on_widget_selected` followed by a Ruby block containing the logic to perform. Glimmer figures out the rest.
2491
+
2492
+ Let's revisit the Tic Tac Toe example shown near the beginning of the page:
2493
+
2494
+ ```ruby
2495
+ shell {
2496
+ text "Tic-Tac-Toe"
2497
+ minimum_size 150, 178
2498
+ composite {
2499
+ grid_layout 3, true
2500
+ (1..3).each { |row|
2501
+ (1..3).each { |column|
2502
+ button {
2503
+ layout_data :fill, :fill, true, true
2504
+ text bind(@tic_tac_toe_board[row, column], :sign)
2505
+ enabled bind(@tic_tac_toe_board[row, column], :empty)
2506
+ on_widget_selected {
2507
+ @tic_tac_toe_board.mark(row, column)
2508
+ }
2509
+ }
2510
+ }
2511
+ }
2512
+ }
2513
+ }
2514
+ ```
2515
+
2516
+ 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.
2517
+
2518
+ 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.
2519
+
2520
+ **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:
2521
+
2522
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
2523
+
2524
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2525
+
2526
+ `SWT.Show` - hooks a listener for showing a widget (using `on_swt_show` in Glimmer)
2527
+ `SWT.Hide` - hooks a listener for hiding a widget (using `on_swt_hide` in Glimmer)
2528
+
2529
+ ```ruby
2530
+ shell {
2531
+ @button1 = button {
2532
+ text "Show 2nd Button"
2533
+ visible true
2534
+ on_swt_show {
2535
+ @button2.swt_widget.setVisible(false)
2536
+ }
2537
+ on_widget_selected {
2538
+ @button2.swt_widget.setVisible(true)
2539
+ }
2540
+ }
2541
+ @button2 = button {
2542
+ text "Show 1st Button"
2543
+ visible false
2544
+ on_swt_show {
2545
+ @button1.swt_widget.setVisible(false)
2546
+ }
2547
+ on_widget_selected {
2548
+ @button1.swt_widget.setVisible(true)
2549
+ }
2550
+ }
2551
+ }.open
2552
+ ```
2553
+
2554
+ **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`
2555
+
2556
+ ##### Alternative Syntax
2557
+
2558
+ 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.
2559
+
2560
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2561
+
2562
+ ```
2563
+ @shell = shell {
2564
+ label {
2565
+ text "Hello, World!"
2566
+ }
2567
+ }
2568
+ @shell.on_shell_iconified {
2569
+ @shell.close
2570
+ }
2571
+ @shell.open
2572
+ ```
2573
+
2574
+ 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.
2575
+
2576
+ 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.
2577
+
2578
+ #### Observing Models
2579
+
2580
+ 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.
2581
+
2582
+ ```ruby
2583
+ class TicTacToe
2584
+ include Glimmer
2585
+
2586
+ def initialize
2587
+ # ...
2588
+ observe(@tic_tac_toe_board, :game_status) { |game_status|
2589
+ display_win_message if game_status == Board::WIN
2590
+ display_draw_message if game_status == Board::DRAW
2591
+ }
2592
+ end
2593
+ # ...
2594
+ end
2595
+ ```
2596
+
2597
+ 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).
2598
+
2599
+ Look at [`samples/elaborate/tictactoe/tic_tac_toe.rb`](samples/tictactoe/tic_tac_toe.rb) for more details starting with the code included below.
2600
+
2601
+ ```ruby
2602
+ class TicTacToe
2603
+ include Glimmer
2604
+ include Observer
2605
+
2606
+ def initialize
2607
+ # ...
2608
+ observe(@tic_tac_toe_board, :game_status) { |game_status|
2609
+ display_win_message if game_status == Board::WIN
2610
+ display_draw_message if game_status == Board::DRAW
2611
+ }
2612
+ end
2613
+
2614
+ def display_win_message
2615
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
2616
+ end
2617
+
2618
+ def display_draw_message
2619
+ display_game_over_message("Draw!")
2620
+ end
2621
+
2622
+ def display_game_over_message(message)
2623
+ message_box(@shell) {
2624
+ text 'Game Over'
2625
+ message message_text
2626
+ }.open
2627
+ @tic_tac_toe_board.reset
2628
+ end
2629
+ # ...
2630
+ end
2631
+ ```
2632
+
2633
+ ### Custom Widgets
2634
+
2635
+ Glimmer supports creating custom widgets with minimal code, which automatically extends Glimmer's DSL syntax with an underscored lowercase keyword.
2636
+
2637
+ 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 `__`)
2638
+
2639
+ #### Simple Example
2640
+
2641
+ (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2642
+
2643
+ Definition:
2644
+ ```ruby
2645
+ class RedLabel
2646
+ include Glimmer::UI::CustomWidget
2647
+
2648
+ body {
2649
+ label(swt_style) {
2650
+ background :red
2651
+ }
2652
+ }
2653
+ end
2654
+ ```
2655
+
2656
+ Usage:
2657
+ ```ruby
2658
+ shell {
2659
+ red_label {
2660
+ text 'Red Label'
2661
+ }
2662
+ }.open
2663
+ ```
2664
+
2665
+ As you can see, `RedLabel` became Glimmer DSL keyword: `red_label`
2666
+
2667
+ #### Custom Widget Lifecycle Hooks
2668
+
2669
+ You may execute code before or after evaluating the body with these lifecycle hooks:
2670
+ - `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`
2671
+ - `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.
2672
+
2673
+ #### Lifecycle Hooks Example
2674
+
2675
+ (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2676
+
2677
+ Definition:
2678
+ ```ruby
2679
+ module Red
2680
+ class Composite
2681
+ include Glimmer::UI::CustomWidget
2682
+
2683
+ before_body {
2684
+ @color = :red
2685
+ }
2686
+
2687
+ body {
2688
+ composite(swt_style) {
2689
+ background @color
2690
+ }
2691
+ }
2692
+ end
2693
+ end
2694
+ ```
2695
+
2696
+ Usage:
2697
+ ```ruby
2698
+ shell {
2699
+ red__composite {
2700
+ label {
2701
+ foreground :white
2702
+ text 'This is showing inside a Red Composite'
2703
+ }
2704
+ }
2705
+ }.open
2706
+ ```
2707
+
2708
+ 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`.
2709
+
2710
+ 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.
2711
+
2712
+ #### Custom Widget API
2713
+
2714
+ Custom Widgets have the following attributes available to call from inside the `#body` method:
2715
+ - `#parent`: Glimmer object parenting custom widget
2716
+ - `#swt_style`: SWT style integer. Can be useful if you want to allow consumers to customize a widget inside the custom widget body
2717
+ - `#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)
2718
+ - `#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.
2719
+ - `#body_root`: top-most (root) widget returned from `#body` method.
2720
+ - `#swt_widget`: actual SWT widget for `body_root`
2721
+
2722
+ Additionally, custom widgets can call the following class methods:
2723
+ - `::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)
2724
+ - `::option(option_name, default: nil)`: declares a single option taking option name and default value as arguments (also generates attribute accessors just like `::options`)
2725
+
2726
+ #### Content/Options Example
2727
+
2728
+ (you may copy/paste in [`girb`](#girb-glimmer-irb-command))
2729
+
2730
+ Definition:
2731
+ ```ruby
2732
+ class Sandwich
2733
+ include Glimmer::UI::CustomWidget
2734
+
2735
+ options :orientation, :bg_color
2736
+ option :fg_color, default: :black
2737
+
2738
+ body {
2739
+ composite(swt_style) { # gets custom widget style
2740
+ fill_layout orientation # using orientation option
2741
+ background bg_color # using container_background option
2742
+ label {
2743
+ text 'SANDWICH TOP'
2744
+ }
2745
+ content.call # this is where content block is called
2746
+ label {
2747
+ text 'SANDWICH BOTTOM'
2748
+ }
2749
+ }
2750
+ }
2751
+ end
2752
+ ```
2753
+
2754
+ Usage:
2755
+ ```ruby
2756
+ shell {
2757
+ sandwich(:no_focus, orientation: :vertical, bg_color: :red) {
2758
+ label {
2759
+ background :green
2760
+ text 'SANDWICH CONTENT'
2761
+ }
2762
+ }
2763
+ }.open
2764
+ ```
2765
+
2766
+ 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'`
2767
+
2768
+ #### Gotcha
2769
+
2770
+ Beware of defining a custom attribute that is a common SWT widget property name.
2771
+ For example, if you define `text=` and `text` methods to accept a custom text and then later you write this body:
2772
+
2773
+ ```ruby
2774
+ # ...
2775
+ def text
2776
+ # ...
2777
+ end
2778
+
2779
+ def text=(value)
2780
+ # ...
2781
+ end
2782
+
2783
+ body {
2784
+ composite {
2785
+ label {
2786
+ text "Hello"
2787
+ }
2788
+ label {
2789
+ text "World"
2790
+ }
2791
+ }
2792
+ }
2793
+ # ...
2794
+ ```
2795
+
2796
+ 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`.
2797
+
2798
+ #### Final Notes
2799
+
2800
+ 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:
2801
+ [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)
2802
+
2803
+ Also, you may check out [Hello, Custom Widget!](#hello-custom-widget) for another example.
2804
+
2805
+ ### Custom Shells
2806
+
2807
+ 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.
2808
+
2809
+ They may also be chained in a wizard fashion.
2810
+
2811
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2812
+
2813
+ ```ruby
2814
+ class WizardStep
2815
+ include Glimmer::UI::CustomShell
2816
+
2817
+ options :number, :step_count
2818
+
2819
+ before_body {
2820
+ @title = "Step #{number}"
2821
+ }
2822
+
2823
+ body {
2824
+ shell {
2825
+ text "Wizard - #{@title}"
2826
+ minimum_size 200, 100
2827
+ fill_layout :vertical
2828
+ label(:center) {
2829
+ text @title
2830
+ font height: 30
2831
+ }
2832
+ if number < step_count
2833
+ button {
2834
+ text "Go To Next Step"
2835
+ on_widget_selected {
2836
+ body_root.hide
2837
+ }
2838
+ }
2839
+ end
2840
+ }
2841
+ }
2842
+ end
2843
+
2844
+ shell { |app_shell|
2845
+ text "Wizard"
2846
+ minimum_size 200, 100
2847
+ @current_step_number = 1
2848
+ @wizard_steps = 5.times.map { |n|
2849
+ wizard_step(number: n+1, step_count: 5) {
2850
+ on_swt_hide {
2851
+ if @current_step_number < 5
2852
+ @current_step_number += 1
2853
+ app_shell.hide
2854
+ @wizard_steps[@current_step_number - 1].open
2855
+ end
2856
+ }
2857
+ }
2858
+ }
2859
+ button {
2860
+ text "Start"
2861
+ font height: 40
2862
+ on_widget_selected {
2863
+ app_shell.hide
2864
+ @wizard_steps[@current_step_number - 1].open
2865
+ }
2866
+ }
2867
+ }.open
2868
+ ```
2869
+
2870
+ You may check out [Hello, Custom Shell!](#hello-custom-shell) for another example.
2871
+
2872
+ ### Drag and Drop
2873
+
2874
+ Glimmer offers Drag and Drop support, thanks to [SWT](https://www.eclipse.org/swt/) and Glimmer's lightweight [DSL syntax](#glimmer-dsl-syntax).
2875
+
2876
+ 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)
2877
+
2878
+ To get started, simply follow these steps:
2879
+ 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)
2880
+ 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)
2881
+ 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)
2882
+ 1. Read `event.data` and consume it (e.g. change widget text) inside the `on_drop` event handler block.
2883
+
2884
+ Example (taken from [samples/hello/hello_drag_and_drop.rb](#hello-drag-and-drop) / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2885
+
2886
+ ```ruby
2887
+ class Location
2888
+ attr_accessor :country
2889
+
2890
+ def country_options
2891
+ %w[USA Canada Mexico Columbia UK Australia Germany Italy Spain]
2892
+ end
2893
+ end
2894
+
2895
+ @location = Location.new
2896
+
2897
+ include Glimmer
2898
+
2899
+ shell {
2900
+ text 'Hello, Drag and Drop!'
2901
+ list {
2902
+ selection bind(@location, :country)
2903
+ on_drag_set_data { |event|
2904
+ list = event.widget.getControl
2905
+ event.data = list.getSelection.first
2906
+ }
2907
+ }
2908
+ label(:center) {
2909
+ text 'Drag a country here!'
2910
+ font height: 20
2911
+ on_drop { |event|
2912
+ event.widget.getControl.setText(event.data)
2913
+ }
2914
+ }
2915
+ }.open
2916
+ ```
2917
+
2918
+ ![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
2919
+
2920
+ Optional steps:
2921
+ - 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.
2922
+ - Specify `drag_source_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
2923
+ - 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)
2924
+ - Specify `drop_target_style` operation (may be: :drop_copy (default), :drop_link, :drop_move, :drop_none, or an array of multiple operations)
2925
+ - 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)
2926
+ - Set drag operation in `event.detail` (e.g. DND::DROP_COPY) inside `on_drag_enter`
2927
+
2928
+ ### Miscellaneous
2929
+
2930
+ #### Multi-DSL Support
2931
+
2932
+ Glimmer is a DSL engine that supports multiple DSLs (Domain Specific Languages):
2933
+ - [SWT](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
2934
+ - [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
2935
+ - [XML](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML) - Useful with [SWT Browser Widget](#browser-widget)
2936
+ - [CSS](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets) - Useful with [SWT Browser Widget](#browser-widget)
2937
+
2938
+ 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.
2939
+
2940
+ ##### SWT
2941
+
2942
+ 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:
2943
+ - `shell`
2944
+ - `display`
2945
+ - `color`
2946
+ - `observe`
2947
+ - `async_exec`
2948
+ - `sync_exec`
2949
+
2950
+ ##### Opal
2951
+
2952
+ Full instructions are found in the [Opal](https://github.com/AndyObtiva/glimmer-dsl-opal) DSL page.
2953
+
2954
+ 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.
2955
+
2956
+ ##### XML
2957
+
2958
+ Simply start with `html` keyword and add HTML inside its block using Glimmer DSL syntax.
2959
+ Once done, you may call `to_s`, `to_xml`, or `to_html` to get the formatted HTML output.
2960
+
2961
+ Here are all the Glimmer XML DSL top-level keywords:
2962
+ - `html`
2963
+ - `tag`: enables custom tag creation for exceptional cases by passing tag name as '_name' attribute
2964
+ - `name_space`: enables namespacing html tags
2965
+
2966
+ 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')` )
2967
+
2968
+ Example (basic HTML / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2969
+
2970
+ ```ruby
2971
+ @xml = html {
2972
+ head {
2973
+ meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
2974
+ }
2975
+ body {
2976
+ h1 { "Hello, World!" }
2977
+ }
2978
+ }
2979
+ puts @xml
2980
+ ```
2981
+
2982
+ Output:
2983
+
2984
+ ```
2985
+ <html><head><meta name="viewport" content="width=device-width, initial-scale=2.0" /></head><body><h1>Hello, World!</h1></body></html>
2986
+ ```
2987
+
2988
+ Example (explicit XML tag / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
2989
+
2990
+ ```ruby
2991
+ puts tag(:_name => "DOCUMENT")
2992
+ ```
2993
+
2994
+ Output:
2995
+
2996
+ ```
2997
+ <DOCUMENT/>
2998
+ ```
2999
+
3000
+ Example (XML namespaces using `name_space` keyword / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3001
+
3002
+ ```ruby
3003
+ @xml = name_space(:w3c) {
3004
+ html(:id => "thesis", :class => "document") {
3005
+ body(:id => "main") {
3006
+ }
3007
+ }
3008
+ }
3009
+ puts @xml
3010
+ ```
3011
+
3012
+ Output:
3013
+
3014
+ ```
3015
+ <w3c:html id="thesis" class="document"><w3c:body id="main"></w3c:body></w3c:html>
3016
+ ```
3017
+
3018
+ Example (XML namespaces using dot operator / you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3019
+
3020
+ ```ruby
3021
+ @xml = tag(:_name => "DOCUMENT") {
3022
+ document.body(document.id => "main") {
3023
+ }
3024
+ }
3025
+ puts @xml
3026
+ ```
3027
+
3028
+ Output:
3029
+
3030
+ ```
3031
+ <DOCUMENT><document:body document:id="main"></document:body></DOCUMENT>
3032
+ ```
3033
+
3034
+ ##### CSS
3035
+
3036
+ Simply start with `css` keyword and add stylesheet rule sets inside its block using Glimmer DSL syntax.
3037
+ Once done, you may call `to_s` or `to_css` to get the formatted CSS output.
3038
+
3039
+ `css` is the only top-level keyword in the Glimmer CSS DSL
3040
+
3041
+ Selectors may be specified by `s` keyword or HTML element keyword directly (e.g. `body`)
3042
+ Rule property values may be specified by `pv` keyword or underscored property name directly (e.g. `font_size`)
3043
+
3044
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3045
+
3046
+ ```ruby
3047
+ @css = css {
3048
+ body {
3049
+ font_size '1.1em'
3050
+ pv 'background', 'white'
3051
+ }
3052
+
3053
+ s('body > h1') {
3054
+ background_color :red
3055
+ pv 'font-size', '2em'
3056
+ }
3057
+ }
3058
+ puts @css
3059
+ ```
3060
+
3061
+ ##### Listing / Enabling / Disabling DSLs
3062
+
3063
+ Glimmer provides a number of methods on Glimmer::DSL::Engine to configure DSL support or inquire about it:
3064
+ - `Glimmer::DSL::Engine.dsls`: Lists available Glimmer DSLs
3065
+ - `Glimmer::DSL::Engine.disable_dsl(dsl_name)`: Disables a specific DSL. Useful when there is no need for certain DSLs in a certain application.
3066
+ - `Glimmer::DSL::Engine.disabled_dsls': Lists disabled DSLs
3067
+ - `Glimmer::DSL::Engine.enable_dsl(dsl_name)`: Re-enables disabled DSL
3068
+ - `Glimmer::DSL::Engine.enabled_dsls=(dsl_names)`: Disables all DSLs except the ones specified.
3069
+
3070
+ #### Application Menu Items (About/Preferences)
3071
+
3072
+ Mac applications always have About and Preferences menu items. Glimmer provides widget observer hooks for them on the `display`:
3073
+ - `on_about`: executes code when user selects App Name -> About
3074
+ - `on_preferences`: executes code when user selects App Name -> Preferences or hits 'CMD+,' on the Mac
3075
+
3076
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3077
+
3078
+ ```ruby
3079
+ class Example
3080
+ def initialize
3081
+ display {
3082
+ on_about {
3083
+ message_box(@shell_proxy) {
3084
+ text 'About'
3085
+ message 'About Application'
3086
+ }.open
3087
+ }
3088
+ on_preferences {
3089
+ preferences_dialog = dialog {
3090
+ text 'Preferences'
3091
+ row_layout {
3092
+ type :vertical
3093
+ margin_left 15
3094
+ margin_top 15
3095
+ margin_right 15
3096
+ margin_bottom 15
3097
+ }
3098
+ label {
3099
+ text 'Check one of these options:'
3100
+ }
3101
+ button(:radio) {
3102
+ text 'Option 1'
3103
+ }
3104
+ button(:radio) {
3105
+ text 'Option 2'
3106
+ }
3107
+ }
3108
+ preferences_dialog.open
3109
+ }
3110
+ }
3111
+ @shell_proxy = shell {
3112
+ text 'Application Menu Items'
3113
+ fill_layout {
3114
+ margin_width 15
3115
+ margin_height 15
3116
+ }
3117
+ label {
3118
+ text 'Application Menu Items'
3119
+ font height: 30
3120
+ }
3121
+ }
3122
+ @shell_proxy.open
3123
+ end
3124
+ end
3125
+
3126
+ Example.new
3127
+ ```
3128
+
3129
+ #### App Name and Version
3130
+
3131
+ 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.
3132
+
3133
+ 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).
3134
+
3135
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3136
+
3137
+ ```ruby
3138
+ Display.setAppName('Glimmer Demo')
3139
+
3140
+ shell(:no_resize) {
3141
+ text "Glimmer"
3142
+ label {
3143
+ text "Hello, World!"
3144
+ }
3145
+ }.open
3146
+ ```
3147
+
3148
+ 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.
3149
+
3150
+ #### Video Widget
3151
+
3152
+ [![Video Widget](images/glimmer-video-widget.png)](https://github.com/AndyObtiva/glimmer-cw-video)
3153
+
3154
+ Glimmer supports a [video custom widget](https://github.com/AndyObtiva/glimmer-cw-video) not in SWT.
3155
+
3156
+ You may obtain via `glimmer-cw-video` gem.
3157
+
3158
+ #### Browser Widget
3159
+
3160
+ ![Hello Browser](images/glimmer-hello-browser.png)
3161
+
3162
+ 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).
3163
+
3164
+ Example loading a URL (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3165
+
3166
+ ```ruby
3167
+ shell {
3168
+ minimum_size 1024, 860
3169
+ browser {
3170
+ url 'http://brightonresort.com/about'
3171
+ }
3172
+ }.open
3173
+ ```
3174
+
3175
+ 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)):
3176
+
3177
+ ```ruby
3178
+ shell {
3179
+ minimum_size 130, 130
3180
+ @browser = browser {
3181
+ text html {
3182
+ head {
3183
+ meta(name: "viewport", content: "width=device-width, initial-scale=2.0")
3184
+ }
3185
+ body {
3186
+ h1 { "Hello, World!" }
3187
+ }
3188
+ }
3189
+ on_completed { # on load of the page execute this JavaScript
3190
+ @browser.swt_widget.execute("alert('Hello, World!');")
3191
+ }
3192
+ }
3193
+ }.open
3194
+ ```
3195
+
3196
+ ##### SWT Browser Style Options
3197
+
3198
+ The `browser` widget can use a particular desktop browser by setting the SWT Style to:
3199
+ - `:webkit`: use the Webkit browser engine
3200
+ - `:chromium`: use the Chromium browser engine
3201
+
3202
+ Example using the Chromium browser (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
3203
+
3204
+ ```ruby
3205
+ shell {
3206
+ minimum_size 1024, 860
3207
+ browser(:chromium) {
3208
+ url 'http://brightonresort.com/about'
3209
+ }
3210
+ }.open
3211
+ ```
3212
+
3213
+ 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).
3214
+
3215
+ ## Glimmer Configuration
3216
+
3217
+ Glimmer configuration may be done via the `Glimmer::Config` module.
3218
+
3219
+ ### logger
3220
+
3221
+ The Glimmer DSL engine supports logging via a standard `STDOUT` Ruby `Logger` configured in the `Glimmer::Config.logger` config option.
3222
+ It is set to level Logger::ERROR by default.
3223
+ Log level may be adjusted via `Glimmer::Config.logger.level` just like any other Ruby Logger.
3224
+
3225
+ Example:
3226
+
3227
+ ```ruby
3228
+ Glimmer::Config.logger.level = :debug
3229
+ ```
3230
+ This results in more verbose debug loggging to `STDOUT`, which is very helpful in troubleshooting Glimmer DSL syntax when needed.
3231
+
3232
+ Example log:
3233
+ ```
3234
+ D, [2017-07-21T19:23:12.587870 #35707] DEBUG -- : method: shell and args: []
3235
+ D, [2017-07-21T19:23:12.594405 #35707] DEBUG -- : ShellCommandHandler will handle command: shell with arguments []
3236
+ D, [2017-07-21T19:23:12.844775 #35707] DEBUG -- : method: composite and args: []
3237
+ D, [2017-07-21T19:23:12.845388 #35707] DEBUG -- : parent is a widget: true
3238
+ D, [2017-07-21T19:23:12.845833 #35707] DEBUG -- : on listener?: false
3239
+ D, [2017-07-21T19:23:12.864395 #35707] DEBUG -- : WidgetCommandHandler will handle command: composite with arguments []
3240
+ D, [2017-07-21T19:23:12.864893 #35707] DEBUG -- : widget styles are: []
3241
+ D, [2017-07-21T19:23:12.874296 #35707] DEBUG -- : method: list and args: [:multi]
3242
+ D, [2017-07-21T19:23:12.874969 #35707] DEBUG -- : parent is a widget: true
3243
+ D, [2017-07-21T19:23:12.875452 #35707] DEBUG -- : on listener?: false
3244
+ D, [2017-07-21T19:23:12.878434 #35707] DEBUG -- : WidgetCommandHandler will handle command: list with arguments [:multi]
3245
+ D, [2017-07-21T19:23:12.878798 #35707] DEBUG -- : widget styles are: [:multi]
3246
+ ```
3247
+
3248
+ The `logger` instance may be replaced with a custom logger via `Glimmer::Config.logger = custom_logger`
3249
+
3250
+ To reset `logger` to the default instance, you may call `Glimmer::Config.reset_logger!`
3251
+
3252
+ 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.
3253
+
3254
+ [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.
3255
+
3256
+ Other config options related to the [`logging`](https://github.com/TwP/logging) gem are mentioned below.
3257
+
3258
+ #### logging_devices
3259
+
3260
+ This is an array of these possible values: `:stdout` (default), `:stderr`, `:file`, `:syslog` (default), `:stringio`
3261
+
3262
+ It defaults to `[:stdout, :syslog]`
3263
+
3264
+ When `:file` is included, Glimmer creates a 'log' directory directly below the Glimmer app local directory.
3265
+ It may also be customized further via the `logging_device_file_options` option.
3266
+ This is useful on Windows as an alternative to `syslog`, which is not available on Windows by default.
3267
+
3268
+ #### logging_device_file_options
3269
+
3270
+ This is a hash of [`logging`](https://github.com/TwP/logging) gem options for the `:file` logging device.
3271
+
3272
+ Default: `{size: 1_000_000, age: 'daily', roll_by: 'number'}`
3273
+
3274
+ That ensures splitting log files at the 1MB size and daily, rolling them by unique number.
3275
+
3276
+ #### logging_appender_options
3277
+
3278
+ Appender options is a hash passed as options to every appender (logging device) used in the [`logging`](https://github.com/TwP/logging) gem.
3279
+
3280
+ Default: `{async: true, auto_flushing: 500, write_size: 500, flush_period: 60, immediate_at: [:error, :fatal], layout: logging_layout}`
3281
+
3282
+ That ensures asynchronous buffered logging that is flushed every 500 messages and 60 seconds, or immediately at error and fatal log levels
3283
+
3284
+ #### logging_layout
3285
+
3286
+ This is a [`logging`](https://github.com/TwP/logging) gem layout that formats the logging output.
3287
+
3288
+ Default:
3289
+
3290
+ ```
3291
+ Logging.layouts.pattern(
3292
+ pattern: '[%d] %-5l %c: %m\n',
3293
+ date_pattern: '%Y-%m-%d %H:%M:%S'
3294
+ )
3295
+ ```
3296
+
3297
+ ### import_swt_packages
3298
+
3299
+ 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.
3300
+
3301
+ As a result, you may call SWT Java classes from Glimmer Ruby code without mentioning Java package references explicitly.
3302
+
3303
+ For example, `org.eclipse.swt.graphics.Color` can be referenced as just `Color`
3304
+
3305
+ 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:
3306
+ ```
3307
+ org.eclipse.swt.*
3308
+ org.eclipse.swt.widgets.*
3309
+ org.eclipse.swt.layout.*
3310
+ org.eclipse.swt.graphics.*
3311
+ org.eclipse.swt.browser.*
3312
+ org.eclipse.swt.custom.*
3313
+ org.eclipse.swt.dnd.*
3314
+ ```
3315
+
3316
+ 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).
3317
+
3318
+ Example:
3319
+
3320
+ ```ruby
3321
+ Glimmer::Config.import_swt_packages += [
3322
+ 'org.eclipse.nebula.widgets.ganttchart'
3323
+ ]
3324
+ ```
3325
+
3326
+ 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)
3327
+
3328
+ Nonetheless, you can disable automatic Java package import if needed via this Glimmer configuration option:
3329
+
3330
+ ```ruby
3331
+ Glimmer::Config.import_swt_packages = false
3332
+ ```
3333
+
3334
+ Once disabled, to import SWT Java packages manually, you may simply:
3335
+
3336
+ 1. `include Glimmer::SWT::Packages`: lazily imports all SWT Java packages to your class, lazy-loading SWT Java class constants on first reference.
3337
+
3338
+ 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'`)
3339
+
3340
+ 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.
3341
+
3342
+ You can learn more about importing Java packages into Ruby code at this JRuby WIKI page:
3343
+
3344
+ https://github.com/jruby/jruby/wiki/CallingJavaFromJRuby
3345
+
3346
+ ### loop_max_count
3347
+
3348
+ Glimmer has infinite loop detection support.
3349
+ It can detect when an infinite loop is about to occur in method_missing and stops it.
3350
+ It detects potential infinite loops when the same keyword and args repeat more than 100 times, which is unusual in a GUI app.
3351
+
3352
+ The max limit can be changed via the `Glimmer::Config::loop_max_count=(count)` config option.
3353
+
3354
+ Infinite loop detection may be disabled altogether if needed by setting `Glimmer::Config::loop_max_count` to `-1`
3355
+
3356
+ ### excluded_keyword_checkers
3357
+
3358
+ Glimmer permits consumers to exclude keywords from DSL processing by its engine via the `excluded_keyword_checkers` config option.
3359
+
3360
+ To do so, add a proc to it that returns a boolean indicating if a keyword is excluded or not.
3361
+
3362
+ 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.
3363
+
3364
+ Example of keywords excluded by [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt):
3365
+
3366
+ ```ruby
3367
+ Glimmer::Config.excluded_keyword_checkers << lambda do |method_symbol, *args|
3368
+ method = method_symbol.to_s
3369
+ result = false
3370
+ result ||= method.start_with?('on_swt_') && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
3371
+ result ||= method == 'dispose' && is_a?(Glimmer::UI::CustomWidget) && respond_to?(method)
3372
+ result ||= ['drag_source_proxy', 'drop_target_proxy'].include?(method) && is_a?(Glimmer::UI::CustomWidget)
3373
+ result ||= method == 'post_initialize_child'
3374
+ result ||= method.end_with?('=')
3375
+ result ||= ['finish_edit!', 'search', 'all_tree_items', 'depth_first_search'].include?(method) && is_a?(Glimmer::UI::CustomWidget) && body_root.respond_to?(method)
3376
+ end
3377
+ ```
3378
+
3379
+ ### log_excluded_keywords
3380
+
3381
+ This just tells Glimmer whether to log excluded keywords or not (at the debug level). It is off by default.
3382
+
3383
+ ## Glimmer Style Guide
3384
+
3385
+ - Widgets are declared with underscored lowercase versions of their SWT names minus the SWT package name.
3386
+ - Widget declarations may optionally have arguments and be followed by a block (to contain properties and content)
3387
+ - Widget blocks are always declared with curly braces
3388
+ - Widget arguments are always wrapped inside parentheses
3389
+ - Widget properties are declared with underscored lowercase versions of the SWT properties
3390
+ - Widget property declarations always have arguments and never take a block
3391
+ - Widget property arguments are never wrapped inside parentheses
3392
+ - Widget listeners are always declared starting with `on_` prefix and affixing listener event method name afterwards in underscored lowercase form
3393
+ - 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)
3394
+ - Data-binding is done via `bind` keyword, which always takes arguments wrapped in parentheses
3395
+ - Custom widget body, before_body, and after_body blocks open their blocks and close them with curly braces.
3396
+ - 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.
3397
+
3398
+ ## SWT Reference
3399
+
3400
+ https://www.eclipse.org/swt/docs.php
3401
+
3402
+ Here is the SWT API:
3403
+
3404
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/index.html
3405
+
3406
+ Here is a visual list of SWT widgets:
3407
+
3408
+ https://www.eclipse.org/swt/widgets/
3409
+
3410
+ Here is a textual list of SWT widgets:
3411
+
3412
+ https://help.eclipse.org/2019-12/topic/org.eclipse.platform.doc.isv/guide/swt_widgets_controls.htm?cp=2_0_7_0_0
3413
+
3414
+ Here is a list of SWT style bits as used in widget declaration:
3415
+
3416
+ https://wiki.eclipse.org/SWT_Widget_Style_Bits
3417
+
3418
+ Here is a SWT style bit constant reference:
3419
+
3420
+ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/SWT.html
3421
+
3422
+ Here is an SWT Drag and Drop guide:
3423
+
3424
+ https://www.eclipse.org/articles/Article-SWT-DND/DND-in-SWT.html
3425
+
3426
+ Here is an SWT Custom Widget guide:
3427
+
3428
+ https://www.eclipse.org/articles/Article-Writing%20Your%20Own%20Widget/Writing%20Your%20Own%20Widget.htm
3429
+
3430
+ ## Samples
3431
+
3432
+ 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`).
3433
+
3434
+ 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:
3435
+
3436
+ ```
3437
+ samples/launch
3438
+ ```
3439
+
3440
+ ### Hello Samples
3441
+
3442
+ For hello-type simple samples, check the following.
3443
+
3444
+ #### Hello, World! Sample
3445
+
3446
+ Code:
3447
+
3448
+ [samples/hello/hello_world.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_world.rb)
3449
+
3450
+ Run:
3451
+
3452
+ ```
3453
+ glimmer sample:run[hello_world]
3454
+ ```
3455
+
3456
+ ![Hello World](images/glimmer-hello-world.png)
3457
+
3458
+ #### Hello, Tab!
3459
+
3460
+ Code:
3461
+
3462
+ [samples/hello/hello_tab.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_tab.rb)
3463
+
3464
+ Run:
3465
+
3466
+ ```
3467
+ glimmer sample:run[hello_tab]
3468
+ ```
3469
+
3470
+ ![Hello Tab English](images/glimmer-hello-tab-english.png)
3471
+ ![Hello Tab French](images/glimmer-hello-tab-french.png)
3472
+
3473
+ #### Hello, Combo!
3474
+
3475
+ This sample demonstrates combo data-binding.
3476
+
3477
+ Code:
3478
+
3479
+ [samples/hello/hello_combo.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_combo.rb)
3480
+
3481
+ Run:
3482
+
3483
+ ```
3484
+ glimmer sample:run[hello_combo]
3485
+ ```
3486
+
3487
+ ![Hello Combo](images/glimmer-hello-combo.png)
3488
+ ![Hello Combo Expanded](images/glimmer-hello-combo-expanded.png)
3489
+
3490
+ #### Hello, List Single Selection!
3491
+
3492
+ This sample demonstrates list single-selection data-binding.
3493
+
3494
+ Code:
3495
+
3496
+ [samples/hello/hello_list_single_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_single_selection.rb)
3497
+
3498
+ Run:
3499
+
3500
+ ```
3501
+ glimmer sample:run[hello_list_single_selection]
3502
+ ```
3503
+
3504
+ ![Hello List Single Selection](images/glimmer-hello-list-single-selection.png)
3505
+
3506
+ #### Hello, List Multi Selection!
3507
+
3508
+ This sample demonstrates list multi-selection data-binding.
3509
+
3510
+ Code:
3511
+
3512
+ [samples/hello/hello_list_multi_selection.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_list_multi_selection.rb)
3513
+
3514
+ Run:
3515
+
3516
+ ```
3517
+ glimmer sample:run[hello_list_multi_selection]
3518
+ ```
3519
+
3520
+ ![Hello List Multi Selection](images/glimmer-hello-list-multi-selection.png)
3521
+
3522
+ #### Hello, Computed!
3523
+
3524
+ This sample demonstrates computed data-binding.
3525
+
3526
+ Code:
3527
+
3528
+ [samples/hello/hello_computed.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_computed.rb)
3529
+
3530
+ Run:
3531
+
3532
+ ```
3533
+ glimmer sample:run[hello_computed]
3534
+ ```
3535
+
3536
+ ![Hello Browser](images/glimmer-hello-computed.png)
3537
+
3538
+ #### Hello, Message Box!
3539
+
3540
+ This sample demonstrates a `message_box` dialog.
3541
+
3542
+ Code:
3543
+
3544
+ [samples/hello/hello_message_box.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_message_box.rb)
3545
+
3546
+ Run:
3547
+
3548
+ ```
3549
+ glimmer sample:run[hello_message_box]
3550
+ ```
3551
+
3552
+ ![Hello Message Box](images/glimmer-hello-message-box.png)
3553
+ ![Hello Message Box Dialog](images/glimmer-hello-message-box-dialog.png)
3554
+
3555
+ #### Hello, Browser!
3556
+
3557
+ This sample demonstrates the `browser` widget.
3558
+
3559
+ Code:
3560
+
3561
+ [samples/hello/hello_browser.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_browser.rb)
3562
+
3563
+ Run:
3564
+
3565
+ ```
3566
+ glimmer sample:run[hello_browser]
3567
+ ```
3568
+
3569
+ ![Hello Browser](images/glimmer-hello-browser.png)
3570
+
3571
+ #### Hello, Drag and Drop!
3572
+
3573
+ This sample demonstrates drag and drop in Glimmer.
3574
+
3575
+ Code:
3576
+
3577
+ [samples/hello/hello_drag_and_drop.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_drag_and_drop.rb)
3578
+
3579
+ Run:
3580
+
3581
+ ```
3582
+ glimmer sample:run[hello_drag_and_drop]
3583
+ ```
3584
+
3585
+ ![Hello Drag and Drop](images/glimmer-hello-drag-and-drop.gif)
3586
+
3587
+ #### Hello, Menu Bar!
3588
+
3589
+ This sample demonstrates menus in Glimmer.
3590
+
3591
+ Code:
3592
+
3593
+ [samples/hello/hello_menu_bar.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_menu_bar.rb)
3594
+
3595
+ Run:
3596
+
3597
+ ```
3598
+ glimmer sample:run[hello_menu_bar]
3599
+ ```
3600
+
3601
+ ![Hello Menu Bar](images/glimmer-hello-menu-bar.png)
3602
+ ![Hello Menu Bar File Menu](images/glimmer-hello-menu-bar-file-menu.png)
3603
+ ![Hello Menu Bar History Menu](images/glimmer-hello-menu-bar-history-menu.png)
3604
+
3605
+ #### Hello, Pop Up Context Menu!
3606
+
3607
+ This sample demonstrates pop up context menus in Glimmer.
3608
+
3609
+ Code:
3610
+
3611
+ [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)
3612
+
3613
+ Run:
3614
+
3615
+ ```
3616
+ glimmer sample:run[hello_pop_up_context_menu]
3617
+ ```
3618
+
3619
+ ![Hello Pop Up Context Menu](images/glimmer-hello-pop-up-context-menu.png)
3620
+ ![Hello Pop Up Context Menu Popped Up](images/glimmer-hello-pop-up-context-menu-popped-up.png)
3621
+
3622
+ #### Hello, Custom Widget!
3623
+
3624
+ This sample demonstrates the use of a custom widget in Glimmer.
3625
+
3626
+ Code:
3627
+
3628
+ [samples/hello/hello_custom_widget.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_custom_widget.rb)
3629
+
3630
+ Run:
3631
+
3632
+ ```
3633
+ glimmer sample:run[hello_custom_widget]
3634
+ ```
3635
+
3636
+ ![Hello Custom Widget](images/glimmer-hello-custom-widget.gif)
3637
+
3638
+ #### Hello, Custom Shell!
3639
+
3640
+ This sample demonstrates the use of a custom shell (aka custom window) in Glimmer.
3641
+
3642
+ Code:
3643
+
3644
+ [samples/hello/hello_custom_shell.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/hello/hello_custom_shell.rb)
3645
+
3646
+ Run:
3647
+
3648
+ ```
3649
+ glimmer sample:run[hello_custom_shell]
3650
+ ```
3651
+
3652
+ ![Hello Custom Shell](images/glimmer-hello-custom-shell.png)
3653
+ ![Hello Custom Shell Email1](images/glimmer-hello-custom-shell-email1.png)
3654
+ ![Hello Custom Shell Email2](images/glimmer-hello-custom-shell-email2.png)
3655
+ ![Hello Custom Shell Email3](images/glimmer-hello-custom-shell-email3.png)
3656
+
3657
+ ### Elaborate Samples
3658
+
3659
+ For more elaborate samples, check the following:
3660
+
3661
+ #### User Profile
3662
+
3663
+ This sample was used in the [DZone Article about Glimmer](https://dzone.com/articles/an-introduction-glimmer), demonstrating Glimmer widgets in general.
3664
+
3665
+ Please note that the code has changed since that article was written (the GUI DSL has been improved/simplified), so use the code sample mentioned here instead as the correct version.
3666
+
3667
+ Code:
3668
+
3669
+ [samples/elaborate/user_profile.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/user_profile.rb)
3670
+
3671
+ Run:
3672
+
3673
+ ```
3674
+ glimmer sample:run[user_profile]
3675
+ ```
3676
+
3677
+ ![User Profile](images/glimmer-user-profile.png)
3678
+
3679
+ #### Login
3680
+
3681
+ This sample demonstrates basic data-binding, password and text fields, and field enablement data-binding.
3682
+
3683
+ Code:
3684
+
3685
+ [samples/elaborate/login.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/login.rb)
3686
+
3687
+ Run:
3688
+
3689
+ ```
3690
+ glimmer sample:run[login]
3691
+ ```
3692
+
3693
+ ![Login](images/glimmer-login.png)
3694
+ ![Login Filled In](images/glimmer-login-filled-in.png)
3695
+ ![Login Logged In](images/glimmer-login-logged-in.png)
3696
+
3697
+ #### Tic Tac Toe Sample
3698
+
3699
+ 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)).
3700
+
3701
+ Code:
3702
+
3703
+ (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`)
3704
+
3705
+ [samples/elaborate/tic_tac_toe.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/tic_tac_toe.rb)
3706
+
3707
+ Run:
3708
+
3709
+ ```
3710
+ glimmer sample:run[tic_tac_toe]
3711
+ ```
3712
+
3713
+ ![Tic Tac Toe](images/glimmer-tic-tac-toe.png)
3714
+ ![Tic Tac Toe In Progress](images/glimmer-tic-tac-toe-in-progress.png)
3715
+ ![Tic Tac Toe Game Over](images/glimmer-tic-tac-toe-game-over.png)
3716
+
3717
+ #### Contact Manager Sample
3718
+
3719
+ 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)).
3720
+
3721
+ Code:
3722
+
3723
+ [samples/elaborate/contact_manager.rb](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/samples/elaborate/contact_manager.rb)
3724
+
3725
+ Run:
3726
+
3727
+ ```
3728
+ glimmer sample:run[contact_manager]
3729
+ ```
3730
+
3731
+ Contact Manager
3732
+
3733
+ ![Contact Manager](images/glimmer-contact-manager.png)
3734
+
3735
+ Contact Manager - Find
3736
+
3737
+ ![Contact Manager](images/glimmer-contact-manager-find.png)
3738
+
3739
+ Contact Manager - Edit Started
19
3740
 
20
- ## Example
3741
+ ![Contact Manager](images/glimmer-contact-manager-edit-started.png)
3742
+
3743
+ Contact Manager - Edit In Progress
3744
+
3745
+ ![Contact Manager](images/glimmer-contact-manager-edit-in-progress.png)
3746
+
3747
+ Contact Manager - Edit Done
3748
+
3749
+ ![Contact Manager](images/glimmer-contact-manager-edit-done.png)
3750
+
3751
+ ### External Samples
3752
+
3753
+ #### Glimmer Calculator
3754
+
3755
+ [<img alt="Glimmer Calculator Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-icon.png" height=40 /> Glimmer Calculator](https://github.com/AndyObtiva/glimmer-cs-calculator) is a basic calculator sample app demonstrating data-binding and TDD (test-driven-development) with Glimmer following the MVP pattern (Model-View-Presenter).
3756
+
3757
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-calculator/master/glimmer-cs-calculator-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-calculator)
3758
+
3759
+ #### Gladiator
3760
+
3761
+ [<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.
3762
+ You may check it out to learn how to build a Glimmer Custom Shell gem.
3763
+
3764
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-gladiator/master/images/glimmer-gladiator.png" />](https://github.com/AndyObtiva/glimmer-cs-gladiator)
3765
+
3766
+ Gladiator is a good demonstration of:
3767
+ - MVP Pattern
3768
+ - Tree data-binding
3769
+ - List data-binding
3770
+ - Text selection data-binding
3771
+ - Tabs
3772
+ - Context menus
3773
+ - Custom Shell
3774
+ - Custom widget
3775
+
3776
+ #### Timer
3777
+
3778
+ [<img alt="Glimmer Timer Icon" src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-timer/master/images/glimmer-timer-logo.png" height=40 /> Timer](https://github.com/AndyObtiva/glimmer-cs-timer) is a sample app demonstrating data-binding and multi-threading in a desktop application.
3779
+
3780
+ [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer-cs-timer/master/glimmer-timer-screenshot.png" />](https://github.com/AndyObtiva/glimmer-cs-timer)
3781
+
3782
+ ## In Production
3783
+
3784
+ The following production apps have been built with Glimmer.
3785
+
3786
+ If you have a Glimmer app you would like referenced here, please mention in a Pull Request.
3787
+
3788
+ ### Math Bowling
3789
+
3790
+ [<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
3791
+
3792
+ ### Are We There Yet?
3793
+
3794
+ [<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
3795
+
3796
+ ### Garderie Rainbow Daily Agenda
3797
+
3798
+ [<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
3799
+
3800
+ ## Packaging & Distribution
3801
+
3802
+ Glimmer simplifies the process of native-executable packaging and distribution on Mac and Windows via a single `glimmer package` command:
3803
+
3804
+ ```
3805
+ glimmer package
3806
+ ```
3807
+
3808
+ It works out of the box for any application scaffolded by [Glimmer Scaffolding](#scaffolding), generating all available packaging types on the current platform (e.g. `DMG`, `PKG`, `APP` on the Mac) and displaying a message indicating what pre-requisite setup tools are needed if not installed already (e.g. [Wix Toolset](https://wixtoolset.org/) to generate MSI files on Windows)
3809
+
3810
+ You may choose to generate a specific type of packaging instead by addionally passing in the `[type]` option. For example, this generates an MSI setup file on Windows:
3811
+
3812
+ ```
3813
+ glimmer package[msi]
3814
+ ```
3815
+
3816
+ - Available Mac packaging types are `dmg`, `pkg`, and `image` (image means a pure Mac `app` without a setup program)
3817
+ - Available Windows packaging types are `msi`, `exe`, and `image` (image means a Windows application directory without a setup program). Learn more about Windows packaging are [over here](#windows-application-packaging).
3818
+
3819
+ Note: if you are using Glimmer manually, to make the `glimmer package` command available, you must add the following line to your application `Rakefile` (automatically done for you if you scaffold an app or gem with `glimmer scaffold[AppName]` or `glimmer scaffold:gem:customshell[GemName]`):
21
3820
 
22
3821
  ```ruby
23
- include Glimmer
24
-
25
- shell {
26
- text 'Glimmer'
27
- label {
28
- text 'Hello, World!'
29
- }
30
- }.open
3822
+ require 'glimmer/rake_task'
3823
+ ```
3824
+
3825
+ The Glimmer packaging process done in the `glimmer package` command consists of the following steps:
3826
+ 1. Generate gemspec via Jeweler (`rake gemspec:generate`): Having a gemspec is required by the [`jar-dependencies`](https://github.com/mkristian/jar-dependencies) JRuby gem, used by JRuby libraries to declare JAR dependencies.
3827
+ 1. Lock JAR versions (`glimmer package:lock_jars`): This locks versions of JAR dependencies leveraged by the `jar-dependencies` JRuby gem, downloading them into the `./vendor` directory so they would get inside the top-level Glimmer app/gem JAR file.
3828
+ 1. Generate [Warbler](https://github.com/jruby/warbler) config (`glimmer package:config`): Generates initial Warbler config file (under `./config/warble.rb`) to use for generating JAR file.
3829
+ 1. Generate JAR file using [Warbler](https://github.com/jruby/warbler) (`glimmer package:jar`): Enables bundling a Glimmer app into a JAR file under the `./dist` directory
3830
+ 1. Generate native executable using [javapackager](https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html) (`glimmer package:native`): Enables packaging a JAR file as a DMG/PKG/APP file on Mac, MSI/EXE/APP on Windows, and DEB/RPM/APP on Linux (Glimmer does not officially support Linux with the `glimmer package` command yet, but it generates the JAR file successfully, and you could use `javapackager` manually afterwards if needed).
3831
+
3832
+ Those steps automatically ensure generating a JAR file under the `./dist` directory using [Warbler](https://github.com/jruby/warbler), which is then used to automatically generate a DMG/MSI file (and other executables) under the `./packages/bundles` directory using `javapackager`.
3833
+ The JAR file name will match your application local directory name (e.g. `MathBowling.jar` for `~/code/MathBowling`)
3834
+ The DMG file name will match the humanized local directory name + dash + application version (e.g. `Math Bowling-1.0.dmg` for `~/code/MathBowling` with version 1.0 or unspecified)
3835
+
3836
+ The `glimmer package` command will automatically set "mac.CFBundleIdentifier" to ="org.#{project_name}.application.#{project_name}".
3837
+ You may override by configuring as an extra argument for javapackger (e.g. Glimmer::RakeTask::Package.javapackager_extra_args = " -Bmac.CFBundleIdentifier=org.andymaleh.application.MathBowling")
3838
+
3839
+ ### Packaging Defaults
3840
+
3841
+ Glimmer employs smart defaults in packaging.
3842
+
3843
+ 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.
3844
+
3845
+ Also, the package will only include these directories: app, config, db, lib, script, bin, docs, fonts, images, sounds, videos
3846
+
3847
+ 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:
3848
+
3849
+ ```
3850
+ glimmer package:config
3851
+ ```
3852
+
3853
+ This will generate `config/warble.rb`, which you may configure and then run `glimmer package` afterwards.
3854
+
3855
+ ### Packaging Configuration
3856
+
3857
+ - Ensure you have a Ruby script under `bin` directory that launches the application, preferably matching your project directory name (e.g. `bin/math_bowling`) :
3858
+ ```ruby
3859
+ require_relative '../app/my_application.rb'
3860
+ ```
3861
+ - 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/)
3862
+ - Include DMG Background Icon (Optional): Simply place a .png file under `package/macosx/{HumanAppName}-background.png`
3863
+ - Include Version (Optional): Create a `VERSION` file in your application and fill it your app version on one line (e.g. `1.1.0`)
3864
+ - 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).
3865
+ - Extra args (Optional): You may optionally add the following to `Rakefile` to configure extra arguments for javapackager: `Glimmer::RakeTask::Package.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).
3866
+
3867
+ ### javapackager Extra Arguments
3868
+
3869
+ 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:
3870
+ - https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
3871
+ - https://docs.oracle.com/javase/8/docs/technotes/tools/unix/javapackager.html
3872
+ - https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html#BCGICFDB
3873
+ - https://docs.oracle.com/javase/8/docs/technotes/guides/deploy/self-contained-packaging.html
3874
+ - https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMacAppStore/index.html#//apple_ref/doc/uid/TP40010572-CH16-SW8
3875
+
3876
+ The Glimmer rake task allows passing extra options to javapackager via:
3877
+ - `Glimmer::RakeTask::Package.javapackager_extra_args="..."` in your application Rakefile
3878
+ - Environment variable: `JAVAPACKAGER_EXTRA_ARGS`
3879
+
3880
+ Example (Rakefile):
3881
+
3882
+ ```ruby
3883
+ require 'glimmer/rake_task'
3884
+
3885
+ Glimmer::RakeTask::Package.javapackager_extra_args = '-BlicenseType="MIT" -Bmac.category="public.app-category.business" -Bmac.signing-key-developer-id-app="Andy Maleh"'
3886
+ ```
3887
+
3888
+ Note that `mac.category` defaults to "public.app-category.business", but can be overridden with one of the category UTI values mentioned here:
3889
+
3890
+ https://developer.apple.com/library/archive/releasenotes/General/SubmittingToMacAppStore/index.html#//apple_ref/doc/uid/TP40010572-CH16-SW8
3891
+
3892
+ Example (env var):
3893
+
3894
+ ```
3895
+ JAVAPACKAGER_EXTRA_ARGS='-Bmac.CFBundleName="Math Bowling Game"' glimmer package
3896
+ ```
3897
+
3898
+ That overrides the default application display name.
3899
+
3900
+ ### Verbose Mode
3901
+
3902
+ Pass `-v` to javapackager in `Glimmer::RakeTask::Package.javapackager_extra_args` or by running `glimmer package:native[type] -v` to learn more about further available customizations for the installer you are requesting to generate.
3903
+
3904
+ ### Windows Application Packaging
3905
+
3906
+ Windows offers two options for setup packaging:
3907
+ - `msi` (recommended): simpler packaging option. Requires [WiX Toolset](https://wixtoolset.org/) and [.NET Framework](https://dotnet.microsoft.com/download/dotnet-framework). Simply run `glimmer package[msi]` (or `glimmer package:native[msi]` if it's not your first time) and it will give you more details on the pre-requisites you need to install (e.g. [WiX Toolset](https://wixtoolset.org/) and [.NET Framework 3.5 SP1](https://dotnet.microsoft.com/download/dotnet-framework/net35-sp1)).
3908
+ - `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.
3909
+
3910
+ 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.
3911
+
3912
+ ### Mac Application Distribution
3913
+
3914
+ 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.
3915
+
3916
+ Afterwards, you may add developer-id/signing-key arguments to `javapackager` via `Glimmer::RakeTask::Package.javapackager_extra_args` or `JAVAPACKAGER_EXTRA_ARGS` according to this webpage: https://docs.oracle.com/javase/9/tools/javapackager.htm#JSWOR719
3917
+
3918
+ DMG signing key argument:
3919
+ ```
3920
+ -Bmac.signing-key-developer-id-app="..."
3921
+ ```
3922
+
3923
+ PKG signing key argument:
3924
+ ```
3925
+ -Bmac.signing-key-developer-id-installer="..."
3926
+ ```
3927
+
3928
+ Mac App Store signing key arguments:
3929
+ ```
3930
+ -Bmac.signing-key-app="..."
3931
+ -Bmac.signing-key-pkg="..."
3932
+ ```
3933
+
3934
+ ### Self Signed Certificate
3935
+
3936
+ 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".
3937
+
3938
+ 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):
3939
+ - Open Keychain Access
3940
+ - Choose Keychain Access > Certificate Assistant > Create Certificate ...
3941
+ - Enter Name (referred to below as "CertificateName")
3942
+ - Set 'Certificate Type' to 'Code Signing'
3943
+ - Create (if you alternatively override defaults, make sure to enable all capabilities)
3944
+ - Add the following option to javapackager: `-Bmac.signing-key-developer-id-app="CertificateName"` via `Glimmer::RakeTask::Package.javapackager_extra_args` or `JAVAPACKAGER_EXTRA_ARGS`
3945
+
3946
+ Example:
3947
+
3948
+ ```ruby
3949
+ Glimmer::RakeTask::Package.javapackager_extra_args = '-Bmac.signing-key-developer-id-app="Andy Maleh"'
31
3950
  ```
32
3951
 
33
- ![Glimmer DSL for SWT Hello World](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-world.png)
3952
+ 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.
34
3953
 
35
- Learn more at the main [Glimmer project page](https://github.com/AndyObtiva/glimmer).
3954
+ ### Gotchas
3955
+
3956
+ 1. Specifying License File
3957
+
3958
+ 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.
3959
+ Keep that in mind if you are not going to rely on the default `LICENSE.txt` support.
3960
+
3961
+ Example:
3962
+
3963
+ ```ruby
3964
+ Glimmer::RakeTask::Package.javapackager_extra_args = '-srcfiles "ACME.txt" -BlicenseFile="ACME.txt" -BlicenseType="ACME"'
3965
+ ```
3966
+
3967
+ 2. Mounted DMG Residue
3968
+
3969
+ 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."
3970
+
3971
+ 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):
3972
+
3973
+ ```
3974
+ 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
3975
+ ```
3976
+
3977
+ ## App Updates
3978
+
3979
+ 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.
3980
+
3981
+ ## Glimmer Supporting Libraries
3982
+
3983
+ Here is a list of notable 3rd party gems used by Glimmer:
3984
+ - [jeweler](https://github.com/technicalpickles/jeweler): generates app gems during [Glimmer Scaffolding](#scaffolding)
3985
+ - [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)
3986
+ - [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
3987
+ - [os](https://github.com/rdp/os): provides OS detection capabilities (e.g. `OS.mac?` or `OS.windows?`) to write cross-platform code inexpensively
3988
+ - [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`)
3989
+ - [rake](https://github.com/ruby/rake): used to implement and execute `glimmer` commands
3990
+ - [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.
3991
+ - [super_module](https://github.com/AndyObtiva/super_module): used to cleanly write the Glimmer::UI:CustomWidget and Glimmer::UI::CustomShell modules
3992
+ - [text-table](https://github.com/aptinio/text-table): renders textual data in a textual table for the command-line interface of Glimmer
3993
+ - [warbler](https://github.com/jruby/warbler): converts a Glimmer app into a Java JAR file during packaging
3994
+
3995
+ ## Glimmer Process
3996
+
3997
+ [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/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.
3998
+
3999
+ Learn more by reading the [GPG](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md) (Glimmer Process Guidelines)
4000
+
4001
+ ## Resources
4002
+
4003
+ * [Code Master Blog](http://andymaleh.blogspot.com/search/label/Glimmer)
4004
+ * [JRuby Cookbook by Justin Edelson & Henry Liu](http://shop.oreilly.com/product/9780596519650.do)
4005
+ * [InfoQ Article](http://www.infoq.com/news/2008/02/glimmer-jruby-swt)
4006
+ * [DZone Tutorial](https://dzone.com/articles/an-introduction-glimmer)
4007
+ * [MountainWest RubyConf 2011 Video](https://confreaks.tv/videos/mwrc2011-whatever-happened-to-desktop-development-in-ruby)
4008
+ * [RubyConf 2008 Video](https://confreaks.tv/videos/rubyconf2008-desktop-development-with-glimmer)
36
4009
 
37
4010
  ## Help
38
4011
 
39
4012
  ### Issues
40
4013
 
41
- You may submit [issues](https://github.com/AndyObtiva/glimmer/issues) on [GitHub](https://github.com/AndyObtiva/glimmer/issues).
4014
+ You may submit [issues](https://github.com/AndyObtiva/glimmer-dsl-swt/issues) on [GitHub](https://github.com/AndyObtiva/glimmer-dsl-swt/issues).
42
4015
 
43
- [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer/issues)
4016
+ [Click here to submit an issue.](https://github.com/AndyObtiva/glimmer-dsl-swt/issues)
44
4017
 
45
4018
  ### Chat
46
4019
 
47
- If you need live help, try to [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4020
+ If you need live help, try to [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
48
4021
 
49
4022
  ## Feature Suggestions
50
4023
 
@@ -52,26 +4025,46 @@ These features have been suggested. You might see them in a future version of Gl
52
4025
 
53
4026
  [TODO.md](TODO.md)
54
4027
 
4028
+ Glimmer DSL Engine specific tasks are at:
4029
+
4030
+ [glimmer/TODO.md](https://github.com/AndyObtiva/glimmer/blob/master/TODO.md)
4031
+
55
4032
  ## Change Log
56
4033
 
57
4034
  [CHANGELOG.md](CHANGELOG.md)
58
4035
 
4036
+ Also: [glimmer/CHANGELOG.md](https://github.com/AndyObtiva/glimmer/blob/master/CHANGELOG.md)
4037
+
59
4038
  ## Contributing
60
4039
 
4040
+ **Contributors Wanted!**
4041
+
4042
+ 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).
4043
+
4044
+ You may apply for contributing to any of these Glimmer DSL gems whether you prefer to focus on the desktop or web:
4045
+ - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI)
4046
+ - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
4047
+ - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
4048
+ - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
4049
+
61
4050
  [CONTRIBUTING.md](CONTRIBUTING.md)
62
4051
 
63
4052
  ## Contributors
64
4053
 
65
4054
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)
4055
+ * [Dennis Theisen](https://github.com/Soleone) (Contributor, originally in [Glimmer](https://github.com/AndyObtiva/glimmer/graphs/contributors) before splitting glimmer-dsl-swt)
66
4056
 
67
4057
  [Click here to view contributor commits.](https://github.com/AndyObtiva/glimmer-dsl-swt/graphs/contributors)
68
4058
 
4059
+ ## Hire Me
4060
+
4061
+ If your company would like to invest fulltime in further development of the Glimmer open-source project, [hire me](https://www.linkedin.com/in/andymaleh/).
4062
+
69
4063
  ## License
70
4064
 
71
- [MIT](https://opensource.org/licenses/MIT)
4065
+ [MIT](LICENSE.txt)
72
4066
 
73
- Copyright (c) 2020 - Andy Maleh.
74
- See [LICENSE.txt](LICENSE.txt) for further details.
4067
+ Copyright (c) 2007-2020 - Andy Maleh.
75
4068
 
76
4069
  --
77
4070