{"id":13544151,"url":"https://github.com/AndyObtiva/glimmer-dsl-opal","last_synced_at":"2025-04-02T14:30:33.317Z","repository":{"id":40008975,"uuid":"271674484","full_name":"AndyObtiva/glimmer-dsl-opal","owner":"AndyObtiva","description":"Glimmer DSL for Opal (Pure-Ruby Web GUI and Auto-Webifier of Desktop Apps)","archived":true,"fork":false,"pushed_at":"2023-12-29T02:11:43.000Z","size":13852,"stargazers_count":26,"open_issues_count":0,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-11-03T11:32:46.496Z","etag":null,"topics":["dom","dsl","dsl-syntax","glimmer","gui","opal","opal-wrapper","ruby","ruby-gem","ruby-library","rubygem","web"],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/AndyObtiva.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2020-06-12T00:39:41.000Z","updated_at":"2024-09-06T22:54:10.000Z","dependencies_parsed_at":"2023-12-29T03:26:53.006Z","dependency_job_id":"0a5915d8-8899-4746-b93b-6713913c6480","html_url":"https://github.com/AndyObtiva/glimmer-dsl-opal","commit_stats":{"total_commits":448,"total_committers":3,"mean_commits":"149.33333333333334","dds":0.3102678571428571,"last_synced_commit":"5564af2473e4709a32736368b15c47ac5ace7375"},"previous_names":[],"tags_count":67,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fglimmer-dsl-opal","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fglimmer-dsl-opal/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fglimmer-dsl-opal/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/AndyObtiva%2Fglimmer-dsl-opal/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/AndyObtiva","download_url":"https://codeload.github.com/AndyObtiva/glimmer-dsl-opal/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246832075,"owners_count":20841106,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dom","dsl","dsl-syntax","glimmer","gui","opal","opal-wrapper","ruby","ruby-gem","ruby-library","rubygem","web"],"created_at":"2024-08-01T11:00:42.889Z","updated_at":"2025-04-02T14:30:30.628Z","avatar_url":"https://github.com/AndyObtiva.png","language":"Ruby","funding_links":[],"categories":["Uncategorized"],"sub_categories":["Uncategorized"],"readme":"# [\u003cimg src=\"https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png\" height=85 /\u003e](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Opal [Alpha]\n## Pure Ruby Web GUI and Auto-Webifier of Desktop Apps\n[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-opal.svg)](http://badge.fury.io/rb/glimmer-dsl-opal)\n[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n## **Note: This project has been superseded by [Glimmer DSL for Web](https://github.com/AndyObtiva/glimmer-dsl-web), which you should check out for more serious usage of Glimmer on the Web going forward! Glimmer DSL for Opal was a great experiment that proved a Glimmer GUI DSL can work on the Web by rendering HTML in web browsers using Ruby as the frontend language of Rails applications instead of JavaScript, allowing both declarative programming of user interface structure and imperative programming of user interface logic to happen more productively in one language instead of the awkward and unproductive mixing of multiple languages (HTML, JS, JSX, etc...) on the Web. [Glimmer DSL for Web](https://github.com/AndyObtiva/glimmer-dsl-web) offers a more web-like approach to the Glimmer GUI DSL that better leverages existing HTML/CSS/JS skills, and will be maintained in place of this project going forward.**\n\n### You can finally live in pure Rubyland on the web!\n\n[Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [Opal](https://opalrb.com/) is an **alpha** [gem](https://rubygems.org/gems/glimmer-dsl-opal) that enables building web GUI in pure Ruby via [Opal](https://opalrb.com/) on [Rails](https://rubyonrails.org/) **(now comes with the new Shine data-binding syntax)**.\n\nUse in one of two ways:\n- **Direct:** build the GUI of web apps with the same friendly desktop GUI Ruby syntax as [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt), thus requiring a lot less code than web technologies that is in pure Ruby and avoiding opaque web concepts like 'render' and 'reactive'. No HTML/JS/CSS skills are even required. Web designers may be involved with CSS styling only if needed.\n- **Adapter:** auto-webify [Glimmer](https://github.com/AndyObtiva/glimmer) desktop apps (i.e. apps built with [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt)) via [Opal](https://opalrb.com/) on [Rails](https://rubyonrails.org/) without changing a line of GUI code. Just insert them as a single require statement in a Rails app, and BOOM! They're running on the web! Apps may then optionally be custom-styled for the web by web designers with standard CSS if needed.\n\nGlimmer DSL for Opal successfully reuses the entire [Glimmer](https://github.com/AndyObtiva/glimmer) core DSL engine in [Opal Ruby](https://opalrb.com/) inside a web browser, and as such inherits the full range of Glimmer desktop [data-binding](https://github.com/AndyObtiva/glimmer#data-binding) capabilities for the web (including Shine syntax using `\u003c=\u003e` and `\u003c=` for bidirectional [two-way] and unidirectional [one-way] data-binding respectively).\n\n(note that auto-webification of desktop apps that involve multiple threads might involve extra changes to the code to utilize web async calls due to the async nature of transpiled JavaScript code)\n\n#### Hello, Table! Sample\n\nCode: [lib/glimmer-dsl-opal/samples/hello/hello_table.rb](lib/glimmer-dsl-opal/samples/hello/hello_table.rb)\n\nGlimmer GUI code from [glimmer-dsl-opal/samples/hello/hello_table.rb](lib/glimmer-dsl-opal/samples/hello/hello_table.rb):\n\n```ruby\n# ...\nshell {\n  grid_layout\n\n  text 'Hello, Table!'\n  background_image File.expand_path('hello_table/baseball_park.png', __dir__)\n  image File.expand_path('hello_table/baseball_park.png', __dir__)\n\n  label {\n    layout_data :center, :center, true, false\n\n    text 'BASEBALL PLAYOFF SCHEDULE'\n    background :transparent if OS.windows?\n    foreground rgb(94, 107, 103)\n    font name: 'Optima', height: 38, style: :bold\n  }\n\n  combo(:read_only) {\n    layout_data :center, :center, true, false\n    selection \u003c=\u003e [BaseballGame, :playoff_type]\n    font height: 14\n  }\n\n  table(:editable) { |table_proxy|\n    layout_data :fill, :fill, true, true\n\n    table_column {\n      text 'Game Date'\n      width 150\n      sort_property :date # ensure sorting by real date value (not `game_date` string specified in items below)\n      editor :date_drop_down, property: :date_time\n    }\n    table_column {\n      text 'Game Time'\n      width 150\n      sort_property :time # ensure sorting by real time value (not `game_time` string specified in items below)\n      editor :time, property: :date_time\n    }\n    table_column {\n      text 'Ballpark'\n      width 180\n      editor :none\n    }\n    table_column {\n      text 'Home Team'\n      width 150\n      editor :combo, :read_only # read_only is simply an SWT style passed to combo widget\n    }\n    table_column {\n      text 'Away Team'\n      width 150\n      editor :combo, :read_only # read_only is simply an SWT style passed to combo widget\n    }\n    table_column {\n      text 'Promotion'\n      width 150\n      # default text editor is used here\n    }\n\n    # Data-bind table items (rows) to a model collection property, specifying column properties ordering per nested model\n    items \u003c=\u003e [BaseballGame, :schedule, column_attributes: [:game_date, :game_time, :ballpark, :home_team, :away_team, :promotion]]\n\n    # Data-bind table selection\n    selection \u003c=\u003e [BaseballGame, :selected_game]\n\n    # Default initial sort property\n    sort_property :date\n\n    # Sort by these additional properties after handling sort by the column the user clicked\n    additional_sort_properties :date, :time, :home_team, :away_team, :ballpark, :promotion\n\n    menu {\n      menu_item {\n        text 'Book'\n\n        on_widget_selected {\n          book_selected_game\n        }\n      }\n    }\n  }\n\n  button {\n    text 'Book Selected Game'\n    layout_data :center, :center, true, false\n    font height: 14\n    enabled \u003c= [BaseballGame, :selected_game]\n\n    on_widget_selected {\n      book_selected_game\n    }\n  }\n}\n# ...\n```\n\n**Hello, Table! originally running on the desktop (using the [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):**\n\n![Glimmer DSL for SWT Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table.png)\n\n**Hello, Table! (same GUI code) running on the web via Opal on Rails (using the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem):**\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table.png)\n\nHello, Table! Editing Game Date\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-game-date.png)\n\nHello, Table! Editing Game Time\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-game-time.png)\n\nHello, Table! Editing Home Team\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-home-team.png)\n\nHello, Table! Sorted Game Date Ascending\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-sorted-game-date-ascending.png)\n\nHello, Table! Sorted Game Date Descending\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-sorted-game-date-descending.png)\n\nHello, Table! Playoff Type Combo\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-playoff-type-combo.png)\n\nHello, Table! Playoff Type Changed\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-playoff-type-changed.png)\n\nHello, Table! Game Booked\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-game-booked.png)\n\nNOTE: Glimmer DSL for Opal is an alpha project (only about 76% complete). If you want it developed faster, then [open an issue report](https://github.com/AndyObtiva/glimmer-dsl-opal/issues/new). I have completed some GitHub project features much faster before due to [issue reports](https://github.com/AndyObtiva/glimmer-dsl-opal/issues) and [pull requests](https://github.com/AndyObtiva/glimmer-dsl-opal/pulls). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.\n\n**Alpha Version** 0.29.0 only supports bare-minimum capabilities for the included [samples](https://github.com/AndyObtiva/glimmer-dsl-opal#samples) (originally written for [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt)).\n\nLearn more about the differences between various [Glimmer](https://github.com/AndyObtiva/glimmer) DSLs by looking at the **[Glimmer DSL Comparison Table](https://github.com/AndyObtiva/glimmer#glimmer-dsl-comparison-table)**.\n\n## Table of Contents\n\n- [Glimmer DSL for Opal [Alpha] (Pure Ruby Web GUI)](#-glimmer-dsl-for-opal-alpha-pure-ruby-web-gui)\n  - [Principles](#principles)\n  - [Background](#background)\n  - [Pre-requisites](#pre-requisites)\n  - [Setup](#setup)\n  - [Supported Glimmer DSL Keywords](#supported-glimmer-dsl-keywords)\n  - [Samples](#samples)\n    - [Hello Samples](#hello-samples)\n      - [Hello, World!](#hello-world)\n      - [Hello, Combo!](#hello-combo)\n      - [Hello, Composite!](#hello-composite)\n      - [Hello, Computed!](#hello-computed)\n      - [Hello, Cursor!](#hello-cursor)\n      - [Hello, Label!](#hello-label)\n      - [Hello, Layout!](#hello-layout)\n      - [Hello, List Single Selection!](#hello-list-single-selection)\n      - [Hello, List Multi Selection!](#hello-list-multi-selection)\n      - [Hello, Browser!](#hello-browser)\n      - [Hello, Tab!](#hello-tab)\n      - [Hello, Custom Widget!](#hello-custom-widget)\n      - [Hello, Custom Shell!](#hello-custom-shell)\n      - [Hello, Radio!](#hello-radio)\n      - [Hello, Radio Group!](#hello-radio-group)\n      - [Hello, Group!](#hello-group)\n      - [Hello, Canvas!](#hello-canvas)\n      - [Hello, C Combo!](#hello-c-combo)\n      - [Hello, C Tab!](#hello-c-tab)\n      - [Hello, Checkbox!](#hello-checkbox)\n      - [Hello, Checkbox Group!](#hello-checkbox-group)\n      - [Hello, Date Time!](#hello-date-time)\n      - [Hello, Table!](#hello-table)\n      - [Hello, Text!](#hello-text)\n      - [Hello, Button!](#hello-button)\n      - [Hello, Arrow!](#hello-arrow)\n      - [Hello, Message Box!](#hello-message-box)\n      - [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu)\n      - [Hello, Print!](#hello-print)\n      - [Hello, Progress Bar!](#hello-progress-bar)\n      - [Hello, Menu Bar!](#hello-menu-bar)\n      - [Hello, Dialog!](#hello-dialog)\n    - [Elaborate Samples](#elaborate-samples)\n      - [Login](#login)\n      - [Contact Manager](#contact-manager)\n      - [Tetris](#tetris)\n      - [Tic Tac Toe](#tic-tac-toe)\n      - [User Profile](#user-profile)\n      - [Weather](#weather)\n    - [External Samples](#external-samples)\n      - [Glimmer Calculator](#glimmer-calculator)\n  - [Glimmer Supporting Libraries](#glimmer-supporting-libraries)\n  - [Glimmer Process](#glimmer-process)\n  - [Help](#help)\n    - [Issues](#issues)\n    - [Chat](#chat)\n  - [Feature Suggestions](#feature-suggestions)\n  - [Change Log](#change-log)\n  - [Contributing](#contributing)\n  - [Contributors](#contributors)\n  - [License](#license)\n\n## Principles\n\n- **Live purely in Rubyland via the Glimmer GUI DSL**, completely oblivious to web browser technologies, thanks to [Opal](https://opalrb.com/).\n- **HTML is for creating documents not interactive applications**. As such, software engineers can avoid it and focus on creating web applications more productively with Glimmer DSL for Opal in pure Ruby instead (just like they do in desktop development) while content creators and web designers can be the ones responsible for creating HTML documents for web content purposes only as HTML was originally intended. That way, Glimmer web GUI is used and embedded in web pages when providing users with applications while the rest of the web pages are maintained by non-engineers as pure HTML. This achieves a correct separation of responsibilities and better productivity and maintainability.\n- **Approximate styles by developers via the Glimmer GUI DSL. Perfect styles by designers via pure CSS**. Developers can simply build GUI with approximate styling similar to desktop GUI and mockups without worrying about pixel-perfect aesthetics. Web designers can take styling further with pure CSS since every HTML element auto-generated by [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) (using [Glimmer DSL for XML \u0026 HTML](https://rubygems.org/gems/glimmer-dsl-xml)) has a predictable ID and CSS class. This achieves a proper separation of responsibilities between developers and designers.\n- **Web servers are used just like servers in traditional client/server architecture**, meaning they simply provide RMI services to enable centralizing some of the application logic and data in the cloud to make available everywhere and enable data-sharing with others.\n- **Forget Routers!** [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) supports auto-routing of custom shells (windows), which are opened as separate tabs in a web browser with automatically generated routes and bookmarkable URLs.\n- **Images Are Local** Desktop apps typically display local images included in app files. When running a desktop app on the web, Glimmer DSL for Opal automatically copies application images to the assets directory and exposes them as download links. Desktop image Ruby code that uses `File.expand_path` or `File.join` automatically detects available image web URLs and matches them to corresponding desktop image paths behind the scenes, so desktop app images show up on the web without any extra effort! ([Hello, Table!](#hello-table-sample) and [Hello, Label!](#hello-label-sample) are good examples of that) (note that this works in Rails 5 on Heroku, but does not work in Rails 6-7 on Heroku though it works outside of it in Rails 6-7)\n\n## Background\n\nThe original idea behind Glimmer DSL for Opal (which [later evolved](#principles)) was that you start by having a [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) desktop app that communicates with a Rails API for any web/cloud concerns. The pure Ruby [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) is very simple, so it is more productive to build GUI in it since it does not go through a server/client request/response cycle and can be iterated on locally with a much shorter feedback cycle. Once the GUI and the rest of the app is built. You simply embed it in a Rails app as a one line require statement, and BOOM, it just works on the web inside a web browser with the same server/client communication you had in the desktop app (I am working on adding minimal support for net/http in Opal so that desktop apps that use it continue to work in a web browser. Until then, just use [Opal-jQuery](https://github.com/opal/opal-jquery) http support). That way, you get two apps for one: desktop and web.\n\nPart of the idea is that web browsers just render GUI widgets similar to those of a desktop app (after all a web browser is a desktop app), so whether you run your GUI on the desktop or on the web should just be a low-level concern, hopefully automated completely with Glimmer DSL for Opal.\n\nLast but not least, you would likely want some special branding on the web, so you can push that off to a web designer who would be more than happy to do the web graphic design and customize the look and feel with pure CSS (no need for programming with Ruby or JavaScript). This enables a clean separation of concerns and distribution of tasks among developers and designers, let alone saving effort on the web GUI by reusing the desktop GUI as a base right off the bat.\n\nAlternatively, web developers may directly use [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) to build the GUI of web apps since it is as simple as desktop development, thus requiring a lot less code that is in pure Ruby only (as demonstrated in examples below) and avoiding opaque web concepts like 'render' and 'reactive' due to treating GUI as persistent just like desktop apps do. No HTML/JS/CSS skills are even required. Still, web designers may be involved with CSS only if needed, thanks to the clean semantic markup [Glimmer DSL for Opal](https://rubygems.org/gems/glimmer-dsl-opal) automatically produces.\n\n## Pre-requisites\n\n- Rails 5-7: [https://github.com/rails/rails](https://github.com/rails/rails)\n- Opal 1.4.1 for Rails 6-7 or Opal 1.0.5 for Rails 5: [https://github.com/opal/opal](https://github.com/opal/opal)\n- Opal-Rails 2.0.2 for Rails 6-7 or Opal-Rails 1.1.2 for Rails 5: [https://github.com/opal/opal-rails](https://github.com/opal/opal-rails)\n- jQuery 3 (included): [https://code.jquery.com/](https://code.jquery.com/) (jQuery 3.6.0 is included in the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem)\n- jQuery-UI 1 (included): [https://code.jquery.com/](https://jqueryui.com/) (jQuery-UI 1.13.1 is included in the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem)\n- jQuery-UI Timepicker 0.3 (included): [https://code.jquery.com/](https://fgelinas.com/code/timepicker/) (jQuery-UI Timepicker 0.3.3 is included in the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem)\n\n## Setup\n\n**Note: This project has been superseded by [Glimmer DSL for Web](https://github.com/AndyObtiva/glimmer-dsl-web), which you should check out for more serious usage of Glimmer on the Web going forward! Glimmer DSL for Opal was a great experiment that proved a Glimmer GUI DSL can work on the Web by rendering HTML in web browsers using Ruby as the frontend language of Rails applications instead of JavaScript, allowing both declarative programming of user interface structure and imperative programming of user interface logic to happen more productively in one language instead of the awkward and unproductive mixing of multiple languages (HTML, JS, JSX, etc...) on the Web. [Glimmer DSL for Web](https://github.com/AndyObtiva/glimmer-dsl-web) offers a more web-like approach to the Glimmer GUI DSL that better leverages existing HTML/CSS/JS skills, and will be maintained in place of this project going forward.**\n\n(NOTE: Keep in mind this is a very early experimental and incomplete **alpha**. If you run into issues, try to go back to a [previous revision](https://rubygems.org/gems/glimmer-dsl-opal/versions). Also, there is a slight chance any issues you encounter are fixed in master or some other branch that you could check out instead)\n\nThe [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem is a [Rails Engine](https://guides.rubyonrails.org/engines.html) gem that includes assets.\n\n### Rails 7\n\nPlease follow the following steps to setup.\n\nInstall a Rails 7 gem:\n\n```\ngem install rails -v7.0.1\n```\n\nStart a new Rails 7 app:\n\n```\nrails new glimmer_app_server\n```\n\nAdd the following to `Gemfile`:\n\n```\ngem 'opal', '1.4.1'\ngem 'opal-rails', '2.0.2'\ngem 'opal-async', '~\u003e 1.4.0'\ngem 'opal-jquery', '~\u003e 0.4.6'\ngem 'glimmer-dsl-opal', '~\u003e 0.29.0'\ngem 'glimmer-dsl-xml', '~\u003e 1.3.1', require: false\ngem 'glimmer-dsl-css', '~\u003e 1.2.1', require: false\n```\n\nRun:\n\n```\nbundle\n```\n\nFollow [opal-rails](https://github.com/opal/opal-rails) instructions, basically running:\n\n```\nbin/rails g opal:install\n```\n\nEdit `config/initializers/assets.rb` and add the following at the bottom:\n```\nOpal.use_gem 'glimmer-dsl-opal'\n```\n\nRun:\n\n```\nrails g scaffold welcome\n```\n\nRun:\n\n```\nrails db:migrate\n```\n\nAdd the following to `config/routes.rb` inside the `Rails.application.routes.draw` block:\n\n```ruby\nmount Glimmer::Engine =\u003e \"/glimmer\" # add on top\nroot to: 'welcomes#index'\n```\n\nEdit `app/views/layouts/application.html.erb` and add the following below other `stylesheet_link_tag` declarations:\n\n```erb\n\u003c%= stylesheet_link_tag    'glimmer/glimmer', media: 'all', 'data-turbolinks-track': 'reload' %\u003e\n```\n\nClear the file `app/views/welcomes/index.html.erb` completely from all content.\n\nDelete `app/javascript/application.js`\n\nEdit and replace `app/assets/javascript/application.js.rb` content with code below (optionally including a require statement for one of the [samples](#samples) below):\n\n```ruby\nrequire 'glimmer-dsl-opal' # brings opal and other dependencies automatically\n\n# Add more require-statements or Glimmer GUI DSL code\n```\n\nExample to confirm setup is working:\n\n```ruby\nrequire 'glimmer-dsl-opal'\n\ninclude Glimmer\n\nshell {\n  fill_layout\n  text 'Example to confirm setup is working'\n  \n  label {\n    text \"Welcome to Glimmer DSL for Opal!\"\n    foreground :red\n    font height: 24\n  }\n}.open\n```\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see:\n\n![setup is working](/images/glimmer-dsl-opal-setup-example-working.png)\n\nIf you run into any issues in setup, refer to the [Sample Glimmer DSL for Opal Rails 7 App](https://github.com/AndyObtiva/sample-glimmer-dsl-opal-rails7-app) project (in case I forgot to include some setup steps by mistake). It is also hosted online (proving that it works): https://sample-glimmer-dsl-opal-rails7.herokuapp.com/\n\nOtherwise, if you still cannot setup successfully (even with the help of the sample project, or if the sample project stops working), please do not hesitate to report an [Issue request](https://github.com/AndyObtiva/glimmer-dsl-opal/issues) or fix and submit a [Pull Request](https://github.com/AndyObtiva/glimmer-dsl-opal/pulls).\n\n### Rails 6\n\nPlease follow the following steps to setup.\n\nInstall a Rails 6 gem:\n\n```\ngem install rails -v6.1.4.6\n```\n\nStart a new Rails 6 app (skipping webpack):\n\n```\nrails new glimmer_app_server --skip-webpack-install\n```\n\nDisable the `webpacker` gem line in `Gemfile`:\n\n```ruby\n# gem 'webpacker', '~\u003e 5.0'\n```\n\nAdd the following to `Gemfile`:\n\n```ruby\ngem 'opal', '1.4.1'\ngem 'opal-rails', '2.0.2'\ngem 'opal-async', '~\u003e 1.4.0'\ngem 'opal-jquery', '~\u003e 0.4.6'\ngem 'glimmer-dsl-opal', '~\u003e 0.29.0'\ngem 'glimmer-dsl-xml', '~\u003e 1.3.1', require: false\ngem 'glimmer-dsl-css', '~\u003e 1.2.1', require: false\n```\n\nRun:\n\n```\nbundle\n```\n\nFollow [opal-rails](https://github.com/opal/opal-rails) instructions, basically running:\n\n```\nbin/rails g opal:install\n```\n\nEdit `config/initializers/assets.rb` and add the following at the bottom:\n```\nOpal.use_gem 'glimmer-dsl-opal'\n```\n\nRun:\n\n```\nrails g scaffold welcome\n```\n\nRun:\n\n```\nrails db:migrate\n```\n\nAdd the following to `config/routes.rb` inside the `Rails.application.routes.draw` block:\n\n```ruby\nmount Glimmer::Engine =\u003e \"/glimmer\" # add on top\nroot to: 'welcomes#index'\n```\n\nEdit `app/views/layouts/application.html.erb` and add the following below other `stylesheet_link_tag` declarations:\n\n```erb\n\u003c%= stylesheet_link_tag 'glimmer/glimmer', media: 'all', 'data-turbolinks-track': 'reload' %\u003e\n```\n\nAlso, delete the following line:\n\n```erb\n\u003c%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %\u003e\n```\n\nClear the file `app/views/welcomes/index.html.erb` completely from all content.\n\nEdit and replace `app/assets/javascript/application.js.rb` content with code below (optionally including a require statement for one of the [samples](#samples) below):\n\n```ruby\nrequire 'glimmer-dsl-opal' # brings opal and other dependencies automatically\n\n# Add more require-statements or Glimmer GUI DSL code\n```\n\nExample to confirm setup is working:\n\n```ruby\nrequire 'glimmer-dsl-opal'\n\ninclude Glimmer\n\nshell {\n  fill_layout\n  text 'Example to confirm setup is working'\n  \n  label {\n    text \"Welcome to Glimmer DSL for Opal!\"\n    foreground :red\n    font height: 24\n  }\n}.open\n```\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see:\n\n![setup is working](/images/glimmer-dsl-opal-setup-example-working.png)\n\nIf you run into any issues in setup, refer to the [Sample Glimmer DSL for Opal Rails 6 App](https://github.com/AndyObtiva/sample-glimmer-dsl-opal-rails6-app) project (in case I forgot to include some setup steps by mistake). It is also hosted online (proving that it works): https://sample-glimmer-dsl-opal-rails6.herokuapp.com/\n\nOtherwise, if you still cannot setup successfully (even with the help of the sample project, or if the sample project stops working), please do not hesitate to report an [Issue request](https://github.com/AndyObtiva/glimmer-dsl-opal/issues) or fix and submit a [Pull Request](https://github.com/AndyObtiva/glimmer-dsl-opal/pulls).\n\n### Rails 5\n\nPlease follow the following steps to setup.\n\nInstall a Rails 5 gem:\n\n```\ngem install rails -v5.2.6\n```\n\nStart a new Rails 5 app:\n\n```\nrails new glimmer_app_server\n```\n\nAdd the following to `Gemfile`:\n\n```\ngem 'opal', '1.0.5'\ngem 'opal-rails', '1.1.2'\ngem 'opal-async', '~\u003e 1.4.0'\ngem 'opal-jquery', '~\u003e 0.4.4'\ngem 'glimmer-dsl-opal', '~\u003e 0.29.0'\ngem 'glimmer-dsl-xml', '~\u003e 1.2.0', require: false\ngem 'glimmer-dsl-css', '~\u003e 1.2.0', require: false\n```\n\nRun:\n\n```\nbundle\n```\n\nFollow [opal-rails](https://github.com/opal/opal-rails) instructions, basically the configuration of: `config/initializers/assets.rb`\n\nEdit `config/initializers/assets.rb` and add the following at the bottom:\n```\nOpal.use_gem 'glimmer-dsl-opal'\n```\n\nRun:\n\n```\nrails g scaffold welcome\n```\n\nRun:\n\n```\nrails db:migrate\n```\n\nAdd the following to `config/routes.rb` inside the `Rails.application.routes.draw` block:\n\n```ruby\nmount Glimmer::Engine =\u003e \"/glimmer\" # add on top\nroot to: 'welcomes#index'\n```\n\nEdit `app/views/layouts/application.html.erb` and add the following below other `stylesheet_link_tag` declarations:\n\n```erb\n\u003c%= stylesheet_link_tag    'glimmer/glimmer', media: 'all', 'data-turbolinks-track': 'reload' %\u003e\n```\n\nClear the file `app/views/welcomes/index.html.erb` completely from all content.\n\nDelete `app/assets/javascripts/application.js`\n\nCreate an empty `app/assets/javascripts/application.rb`, and add Glimmer GUI DSL code or a require statement for one of the [samples](#samples) below.\n\n```ruby\nrequire 'glimmer-dsl-opal' # brings opal and other dependencies automatically\n\n# Add more require-statements or Glimmer GUI DSL code\n```\n\nExample to confirm setup is working:\n\n```ruby\nrequire 'glimmer-dsl-opal'\n\ninclude Glimmer\n\nshell {\n  fill_layout\n  text 'Example to confirm setup is working'\n  \n  label {\n    text \"Welcome to Glimmer DSL for Opal!\"\n    foreground :red\n    font height: 24\n  }\n}.open\n```\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see:\n\n![setup is working](/images/glimmer-dsl-opal-setup-example-working.png)\n\nIf you run into any issues in setup, refer to the [Sample Glimmer DSL for Opal Rails App](https://github.com/AndyObtiva/sample-glimmer-dsl-opal-rails-app) project (in case I forgot to include some setup steps by mistake). It is also hosted online (proving that it works): https://sample-glimmer-dsl-opal-app.herokuapp.com/\n\nOtherwise, if you still cannot setup successfully (even with the help of the sample project, or if the sample project stops working), please do not hesitate to report an [Issue request](https://github.com/AndyObtiva/glimmer-dsl-opal/issues) or fix and submit a [Pull Request](https://github.com/AndyObtiva/glimmer-dsl-opal/pulls).\n\n## Supported Glimmer DSL Keywords\n\nThe following keywords from [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) have fully functional partial support in Opal:\n\nWidgets:\n- `arrow`: featured in [Hello, Arrow!](#hello-arrow)\n- `button`: featured in [Hello, Checkbox!](#hello-checkbox) / [Hello, Button!](#hello-button) / [Hello, Table!](#hello-table) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, Message Box!](#hello-message-box) / [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Hello, Group!](#hello-group) / [Hello, Combo!](#hello-combo) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Contact Manager](#contact-manager) / [Tic Tac Toe](#tic-tac-toe) / [Login](#login)\n- `browser`: featured in [Hello, Browser!](#hello-browser)\n- `calendar`: featured in [Hello, Date Time!](#hello-date-time)\n- `checkbox`: featured in [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox)\n- `checkbox_group`: featured in [Hello, Checkbox Group!](#hello-checkbox-group)\n- `combo`: featured in [Hello, Table!](#hello-table) / [Hello, Combo!](#hello-combo)\n- `composite`: featured in [Hello, Radio!](#hello-radio) / [Hello, Computed!](#hello-computed) / [Hello, Checkbox!](#hello-checkbox) / [Tic Tac Toe](#tic-tac-toe) / [Login](#login) / [Contact Manager](#contact-manager)\n- `date`: featured in [Hello, Table!](#hello-table) / [Hello, Date Time!](#hello-date-time) / [Hello, Custom Shell!](#hello-custom-shell) / [Tic Tac Toe](#tic-tac-toe)\n- `date_drop_down`: featured in [Hello, Table!](#hello-table) / [Hello, Date Time!](#hello-date-time)\n- `dialog`: featured in [Hello, Dialog!](#hello-dialog)\n- `group`: featured in [Hello, Group!](#hello-group) / [Contact Manager](#contact-manager)\n- `label`: featured in [Hello, Computed!](#hello-computed) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox) / [Hello, World!](#hello-world) / [Hello, Table!](#hello-table) / [Hello, Tab!](#hello-tab) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Menu Bar!](#hello-menu-bar) / [Hello, Date Time!](#hello-date-time) / [Hello, Custom Widget!](#hello-custom-widget) / [Hello, Custom Shell!](#hello-custom-shell) / [Contact Manager](#contact-manager) / [Login](#login)\n- `list` (w/ optional `:multi` SWT style): featured in [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Contact Manager](#contact-manager)\n- `menu`: featured in [Hello, Menu Bar!](#hello-menu-bar) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Table!](#hello-table)\n- `menu_bar`: featured in [Hello, Menu Bar!](#hello-menu-bar)\n- `menu_item`: featured in [Hello, Table!](#hello-table) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Menu Bar!](#hello-menu-bar)\n- `message_box`: featured in [Hello, Table!](#hello-table) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Message Box!](#hello-message-box) / [Hello, Menu Bar!](#hello-menu-bar)\n- `radio`: featured in [Hello, Radio!](#hello-radio) / [Hello, Group!](#hello-group)\n- `radio_group`: featured in [Hello, Radio Group!](#hello-radio-group)\n- `scrolled_composite`\n- `shell`: featured in [Hello, Checkbox!](#hello-checkbox) / [Hello, Button!](#hello-button) / [Hello, Table!](#hello-table) / [Hello, Tab!](#hello-tab) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Hello, Group!](#hello-group) / [Hello, Date Time!](#hello-date-time) / [Hello, Custom Shell!](#hello-custom-shell) / [Hello, Computed!](#hello-computed) / [Hello, Combo!](#hello-combo) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Contact Manager](#contact-manager) / [Tic Tac Toe](#tic-tac-toe) / [Login](#login)\n- `tab_folder`: featured in [Hello, Tab!](#hello-tab)\n- `tab_item`: featured in [Hello, Tab!](#hello-tab)\n- `c_tab_folder`: featured in [Hello, C Tab!](#hello-c-tab)\n- `c_tab_item`: featured in [Hello, C Tab!](#hello-c-tab)\n- `table`: featured in [Hello, Custom Shell!](#hello-custom-shell) / [Hello, Table!](#hello-table) / [Contact Manager](#contact-manager)\n- `table_column`: featured in [Hello, Table!](#hello-table) / [Hello, Custom Shell!](#hello-custom-shell) / [Contact Manager](#contact-manager)\n- `text`: featured in [Hello, Computed!](#hello-computed) / [Login](#login) / [Contact Manager](#contact-manager)\n- `time`: featured in [Hello, Table!](#hello-table) / [Hello, Date Time!](#hello-date-time)\n- Glimmer::UI::CustomWidget: ability to define any keyword as a custom widget - featured in [Hello, Custom Widget!](#hello-custom-widget)\n- Glimmer::UI::CustomShell: ability to define any keyword as a custom shell (aka custom window) that opens in a new browser window (tab) automatically unless there is no shell open in the current browser window (tab) - featured in [Hello, Custom Shell!](#hello-custom-shell)\n\nLayouts:\n- `grid_layout`: featured in [Hello, Custom Shell!](#hello-custom-shell) / [Hello, Computed!](#hello-computed) / [Hello, Table!](#hello-table) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Menu Bar!](#hello-menu-bar) / [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Contact Manager](#contact-manager) / [Login](#login) / [Tic Tac Toe](#tic-tac-toe)\n- `row_layout`: featured in [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, Group!](#hello-group) / [Hello, Date Time!](#hello-date-time) / [Hello, Combo!](#hello-combo) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox) / [Contact Manager](#contact-manager)\n- `fill_layout`: featured in [Hello, Custom Widget!](#hello-custom-widget)\n- `layout_data`: featured in [Hello, Table!](#hello-table) / [Hello, Custom Shell!](#hello-custom-shell) / [Hello, Computed!](#hello-computed) / [Tic Tac Toe](#tic-tac-toe) / [Contact Manager](#contact-manager)\n\nGraphics/Style:\n- `color`: featured in [Hello, Custom Widget!](#hello-custom-widget) / [Hello, Menu Bar!](#hello-menu-bar)\n- `font`: featured in [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox) / [Hello, Table!](#hello-table) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Menu Bar!](#hello-menu-bar) / [Hello, Group!](#hello-group) / [Hello, Date Time!](#hello-date-time) / [Hello, Custom Widget!](#hello-custom-widget) / [Hello, Custom Shell!](#hello-custom-shell) / [Contact Manager](#contact-manager) / [Tic Tac Toe](#tic-tac-toe)\n- `Point` class used in setting location on widgets\n- `swt` and `SWT` class to set SWT styles on widgets - featured in [Hello, Custom Shell!](#hello-custom-shell) / [Login](#login) / [Contact Manager](#contact-manager)\n\nData-Binding/Observers:\n- `bind`: featured in [Hello, Computed!](#hello-computed) / [Hello, Combo!](#hello-combo) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox) / [Hello, Button!](#hello-button) / [Hello, Table!](#hello-table) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Hello, Group!](#hello-group) / [Hello, Date Time!](#hello-date-time) / [Hello, Custom Widget!](#hello-custom-widget) / [Hello, Custom Shell!](#hello-custom-shell) / [Login](#login) / [Contact Manager](#contact-manager) / [Tic Tac Toe](#tic-tac-toe)\n- `observe`: featured in [Hello, Table!](#hello-table) / [Tic Tac Toe](#tic-tac-toe)\n- `on_widget_selected`: featured in [Hello, Combo!](#hello-combo) / [Hello, Checkbox Group!](#hello-checkbox-group) / [Hello, Checkbox!](#hello-checkbox) / [Hello, Button!](#hello-button) / [Hello, Table!](#hello-table) / [Hello, Radio Group!](#hello-radio-group) / [Hello, Radio!](#hello-radio) / [Hello, Pop Up Context Menu!](#hello-pop-up-context-menu) / [Hello, Message Box!](#hello-message-box) / [Hello, Menu Bar!](#hello-menu-bar) / [Hello, List Single Selection!](#hello-list-single-selection) / [Hello, List Multi Selection!](#hello-list-multi-selection) / [Hello, Group!](#hello-group) / [Contact Manager](#contact-manager) / [Login](#login) / [Tic Tac Toe](#tic-tac-toe)\n- `on_modify_text`\n- `on_key_pressed` (and SWT alias `on_swt_keydown`) - featured in [Login](#login) / [Contact Manager](#contact-manager)\n- `on_key_released` (and SWT alias `on_swt_keyup`)\n- `on_mouse_down` (and SWT alias `on_swt_mousedown`)\n- `on_mouse_up` (and SWT alias `on_swt_mouseup`) - featured in [Hello, Custom Shell!](#hello-custom-shell) / [Contact Manager](#contact-manager)\n\nEvent loop:\n- `display`: featured in [Tic Tac Toe](#tic-tac-toe)\n- `async_exec`: featured in [Hello, Custom Widget!](#hello-custom-widget) / [Hello, Custom Shell!](#hello-custom-shell)\n\nCanvas Shape DSL:\n- `line`\n- `point`\n- `oval`\n- `polygon`\n- `polyline`\n- `rectangle`\n- `string`\n- `text`\n\n## Samples\n\nFollow the instructions below to try out [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) samples webified via [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal)\n\nThe [Hello samples](#hello-samples) demonstrate tiny building blocks (widgets) for building full fledged applications.\n\nThe [Elaborate samples](#elaborate-samples) demonstrate more advanced sample applications that assemble multiple building blocks.\n\nThis external sample app contains all the samples mentioned below configured inside a Rails [Opal](https://opalrb.com/) app with all the pre-requisites ready to go for convenience:\n\nhttps://github.com/AndyObtiva/sample-glimmer-dsl-opal-rails7-app\n\nhttps://github.com/AndyObtiva/sample-glimmer-dsl-opal-rails-app\n\nYou may visit a Heroku hosted version at:\n\nhttps://sample-glimmer-dsl-opal-rails7.herokuapp.com/\n\nhttps://sample-glimmer-dsl-opal-app.herokuapp.com/\n\nNote: Some of the screenshots might be out of date with updates done to samples in both [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt) and [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal).\n\n### Hello Samples\n\n#### Hello, World!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_world'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\ninclude Glimmer\n   \nshell {\n  text 'Glimmer'\n  label {\n    text 'Hello, World!'\n  }\n}.open\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello World](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-world.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, World!\"\n\n![Glimmer DSL for Opal Hello World](images/glimmer-dsl-opal-hello-world.png)\n\n#### Hello, Combo!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_combo'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloCombo\n  class Person\n    attr_accessor :country, :country_options\n  \n    def initialize\n      self.country_options = ['', 'Canada', 'US', 'Mexico']\n      reset_country!\n    end\n  \n    def reset_country!\n      self.country = 'Canada'\n    end\n  end\n\n  include Glimmer::UI::CustomShell\n  \n  before_body do\n    @person = Person.new\n  end\n  \n  body {\n    shell {\n      row_layout(:vertical) {\n        fill true\n      }\n      \n      text 'Hello, Combo!'\n      \n      combo(:read_only) {\n        selection \u003c=\u003e [@person, :country] # also binds to country_options by convention\n      }\n      \n      button {\n        text 'Reset Selection'\n        \n        on_widget_selected do\n          @person.reset_country!\n        end\n      }\n    }\n  }\nend\n\nHelloCombo.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Combo](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/images/glimmer-hello-combo.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Combo!\"\n\n![Glimmer DSL for Opal Hello Combo](images/glimmer-dsl-opal-hello-combo.png)\n\n#### Hello, Composite!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_composite'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Composite](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/images/glimmer-hello-composite.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Composite!\"\n\n![Glimmer DSL for Opal Hello Composite](images/glimmer-dsl-opal-hello-composite.png)\n\n#### Hello, Computed!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_computed'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloComputed\n  class Contact\n    attr_accessor :first_name, :last_name, :year_of_birth\n  \n    def initialize(attribute_map)\n      @first_name = attribute_map[:first_name]\n      @last_name = attribute_map[:last_name]\n      @year_of_birth = attribute_map[:year_of_birth]\n    end\n  \n    def name\n      \"#{last_name}, #{first_name}\"\n    end\n  \n    def age\n      Time.now.year - year_of_birth.to_i\n    rescue\n      0\n    end\n  end\n\n  include Glimmer::UI::CustomShell\n\n  before_body do\n    @contact = Contact.new(\n      first_name: 'Barry',\n      last_name: 'McKibbin',\n      year_of_birth: 1985\n    )\n  end\n\n  body {\n    shell {\n      text 'Hello, Computed!'\n      \n      composite {\n        grid_layout {\n          num_columns 2\n          make_columns_equal_width true\n          horizontal_spacing 20\n          vertical_spacing 10\n        }\n        \n        label {text 'First \u0026Name: '}\n        text {\n          fill_horizontally_layout_data\n          text \u003c=\u003e [@contact, :first_name]\n        }\n        \n        label {text '\u0026Last Name: '}\n        text {\n          fill_horizontally_layout_data\n          text \u003c=\u003e [@contact, :last_name]\n        }\n        \n        label {text '\u0026Year of Birth: '}\n        text {\n          fill_horizontally_layout_data\n          text \u003c=\u003e [@contact, :year_of_birth]\n        }\n        \n        label {text 'Name: '}\n        label {\n          fill_horizontally_layout_data\n          text \u003c= [@contact, :name, computed_by: [:first_name, :last_name]]\n        }\n        \n        label {text 'Age: '}\n        label {\n          fill_horizontally_layout_data\n          text \u003c= [@contact, :age, on_write: :to_i, computed_by: [:year_of_birth]]\n        }\n      }\n    }\n  }\n  \n  def fill_horizontally_layout_data\n    layout_data {\n      horizontal_alignment :fill\n      grab_excess_horizontal_space true\n    }\n  end\nend\n\nHelloComputed.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Computed](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-computed.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Computed!\"\n\n![Glimmer DSL for Opal Hello Computed](images/glimmer-dsl-opal-hello-computed.png)\n\n#### Hello, Cursor!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_cursor'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Cursor](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-cursor.gif)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Cursor!\"\n\n![Glimmer DSL for Opal Hello Cursor](images/glimmer-dsl-opal-hello-cursor.gif)\n\n#### Hello, Label!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_label'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-left-aligned.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-center-aligned.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-right-aligned.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-images.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-background_images.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-horizontal-separator.png)\n\n![Glimmer DSL for SWT Hello Label](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-label-vertical-separator.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Label!\"\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-left-aligned.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-center-aligned.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-right-aligned.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-images.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-background_images.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-horizontal-separator.png)\n\n![Glimmer DSL for Opal Hello Label](images/glimmer-dsl-opal-hello-label-vertical-separator.png)\n\n#### Hello, Layout!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_layout'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab1.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab2.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab3.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab4.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab5.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab6.png)\n\n![Glimmer DSL for SWT Hello Layout](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-layout-tab7.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Layout!\"\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab1.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab2.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab3.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab4.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab4-shrunk.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab5.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab6.png)\n\n![Glimmer DSL for Opal Hello Layout](images/glimmer-dsl-opal-hello-layout-tab7.png)\n\n#### Hello, List Single Selection!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_list_single_selection'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello List Single Selection](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-list-single-selection.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, List Single Selection!\"\n\n![Glimmer DSL for Opal Hello List Single Selection](images/glimmer-dsl-opal-hello-list-single-selection.png)\n\n#### Hello, List Multi Selection!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_list_multi_selection'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello List Multi Selection](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-list-multi-selection.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, List Multi Selection!\"\n\n![Glimmer DSL for Opal Hello List Multi Selection](images/glimmer-dsl-opal-hello-list-multi-selection.png)\n\n#### Hello, Arrow!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_arrow'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Arrow](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-arrow.png)\n\n![Glimmer DSL for SWT Hello Arrow](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-arrow-menu.png)\n\n![Glimmer DSL for SWT Hello Arrow](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-arrow-item-selected.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Arrow!\"\n\n![Glimmer DSL for Opal Hello Arrow](images/glimmer-dsl-opal-hello-arrow.png)\n\n![Glimmer DSL for Opal Hello Arrow](images/glimmer-dsl-opal-hello-arrow-menu.png)\n\n![Glimmer DSL for Opal Hello Arrow](images/glimmer-dsl-opal-hello-arrow-item-selected.png)\n\n#### Hello, Scale!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_scale'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Scale](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-scale.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Scale!\"\n\n![Glimmer DSL for Opal Hello Scale](images/glimmer-dsl-opal-hello-scale.png)\n\n#### Hello, Slider!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_slider'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Slider](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-slider.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Slider!\"\n\n![Glimmer DSL for Opal Hello Slider](images/glimmer-dsl-opal-hello-slider.png)\n\n#### Hello, Spinner!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_spinner'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Spinner](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-spinner.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Spinner!\"\n\n![Glimmer DSL for Opal Hello Spinner](images/glimmer-dsl-opal-hello-spinner.png)\n\n#### Hello, Browser!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_browser'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Browser](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-browser.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Browser!\"\n\n![Glimmer DSL for Opal Hello Browser](images/glimmer-dsl-opal-hello-browser.png)\n\n#### Hello, Tab!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_tab'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloTab\n  include Glimmer\n  def launch\n    shell {\n      text \"Hello, Tab!\"\n      tab_folder {\n        tab_item {\n          text \"English\"\n          label {\n            text \"Hello, World!\"\n          }\n        }\n        tab_item {\n          text \"French\"\n          label {\n            text \"Bonjour, Univers!\"\n          }\n        }\n      }\n    }.open\n  end\nend\n\nHelloTab.new.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Tab English](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-tab-english.png)\n![Glimmer DSL for SWT Hello Tab French](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-tab-french.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Tab!\"\n\n![Glimmer DSL for Opal Hello Tab English](images/glimmer-dsl-opal-hello-tab-english.png)\n![Glimmer DSL for Opal Hello Tab French](images/glimmer-dsl-opal-hello-tab-french.png)\n\n#### Hello, Custom Widget!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_custom_widget'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it (note that the Opal version needs `Array#async_cycle` from the opal-async gem instead of `Array#cycle` due to the async nature of JavaScript):\n\n```ruby\n# This class declares a `greeting_label` custom widget (by convention)\nclass GreetingLabel\n  include Glimmer::UI::CustomWidget\n  \n  # multiple options without default values\n  options :name, :colors\n  \n  # single option with default value\n  option :greeting, default: 'Hello'\n  \n  # internal attribute (not a custom widget option)\n  attr_accessor :color\n  \n  before_body {\n    @font = {height: 24, style: :bold}\n    @color = :black\n  }\n  \n  after_body {\n    return if colors.nil?\n    \n    Thread.new { # imported from Glimmer DSL for SWT. In Opal, avoid Threads and sleep to avoid blocking GUI.\n      colors.async_cycle { |color|\n        async_exec {\n          self.color = color\n        }\n        sleep(1)\n      }\n    }\n  }\n  \n  body {\n    # pass received swt_style through to label to customize (e.g. :center to center text)\n    label(swt_style) {\n      text \"#{greeting}, #{name}!\"\n      font @font\n      foreground \u003c=\u003e [self, :color]\n    }\n  }\n  \nend\n\n# including Glimmer enables the Glimmer DSL syntax, including auto-discovery of the `greeting_label` custom widget\ninclude Glimmer\n\nshell {\n  fill_layout :vertical\n  \n  minimum_size 215, 215\n  text 'Hello, Custom Widget!'\n  \n  # custom widget options are passed in a hash\n  greeting_label(name: 'Sean')\n  \n  # pass :center SWT style followed by custom widget options hash\n  greeting_label(:center, name: 'Laura', greeting: 'Aloha') #\n  \n  greeting_label(:right, name: 'Rick') {\n    # you can nest attributes under custom widgets just like any standard widget\n    foreground :red\n  }\n  \n  # the colors option cycles between colors for the label foreground every second\n  greeting_label(:center, name: 'Mary', greeting: 'Aloha', colors: [:red, :dark_green, :blue])\n}.open\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Custom Widget](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-custom-widget.gif)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Custom Widget!\"\n\n![Glimmer DSL for Opal Hello Custom Widget](images/glimmer-dsl-opal-hello-custom-widget.gif)\n\n#### Hello, Custom Shell!\n\nThis sample demonstrates Glimmer DSL for Opal's ability to open multiple shells (windows) as web browser tabs.\n\nIt automatically handles routing so that tab URLs are bookmarkable. Web developers do not have to do any routing configuration manually.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_custom_shell'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nrequire 'date'\n\n# This class declares an `email_shell` custom shell, aka custom window (by convention)\n# Used to view an email message\nclass EmailShell\n  include Glimmer::UI::CustomShell\n  \n  # multiple options without default values\n  options :date, :subject, :from, :message\n  \n  # single option with default value\n  option :to, default: '\"John Irwin\" \u003cjohn.irwin@example.com\u003e'\n  \n  before_body {\n    @swt_style |= swt(:shell_trim, :modeless)\n  }\n  \n  body {\n    # pass received swt_style through to shell to customize it (e.g. :dialog_trim for a blocking shell)\n    shell(swt_style) {\n      grid_layout(2, false)\n      \n      text subject\n\n      label {\n        text 'Date:'\n      }\n      label {\n        text date\n      }\n\n      label {\n        text 'From:'\n      }\n      label {\n        text from\n      }\n\n      label {\n        text 'To:'\n      }\n      label {\n        text to\n      }\n\n      label {\n        text 'Subject:'\n      }\n      label {\n        text subject\n      }\n\n      label {\n        layout_data(:fill, :fill, true, true) {\n          horizontal_span 2 #TODO implement\n          vertical_indent 10\n        }\n        \n        background :white\n        text message\n      }\n    }\n  }\n  \nend\n\nclass HelloCustomShell\n  # including Glimmer enables the Glimmer DSL syntax, including auto-discovery of the `email_shell` custom widget\n  include Glimmer\n  \n  Email = Struct.new(:date, :subject, :from, :message, keyword_init: true)\n  EmailSystem = Struct.new(:emails, keyword_init: true)\n  \n  def initialize\n    @email_system = EmailSystem.new(\n      emails: [\n        Email.new(date: DateTime.new(2029, 10, 22, 11, 3, 0).strftime('%F %I:%M %p'), subject: '3rd Week Report', from: '\"Dianne Tux\" \u003cdianne.tux@example.com\u003e', message: \"Hello,\\n\\nI was wondering if you'd like to go over the weekly report sometime this afternoon.\\n\\nDianne\"),\n        Email.new(date: DateTime.new(2029, 10, 21, 8, 1, 0).strftime('%F %I:%M %p'), subject: 'Glimmer Upgrade v100.0', from: '\"Robert McGabbins\" \u003crobert.mcgabbins@example.com\u003e', message: \"Team,\\n\\nWe are upgrading to Glimmer version 100.0.\\n\\nEveryone pull the latest code!\\n\\nRegards,\\n\\nRobert McGabbins\"),\n        Email.new(date: DateTime.new(2029, 10, 19, 16, 58, 0).strftime('%F %I:%M %p'), subject: 'Christmas Party', from: '\"Lisa Ferreira\" \u003clisa.ferreira@example.com\u003e', message: \"Merry Christmas,\\n\\nAll office Christmas Party arrangements have been set\\n\\nMake sure to bring a Secret Santa gift\\n\\nBest regards,\\n\\nLisa Ferreira\"),\n        Email.new(date: DateTime.new(2029, 10, 16, 9, 43, 0).strftime('%F %I:%M %p'), subject: 'Glimmer Upgrade v99.0', from: '\"Robert McGabbins\" \u003crobert.mcgabbins@example.com\u003e', message: \"Team,\\n\\nWe are upgrading to Glimmer version 99.0.\\n\\nEveryone pull the latest code!\\n\\nRegards,\\n\\nRobert McGabbins\"),\n        Email.new(date: DateTime.new(2029, 10, 15, 11, 2, 0).strftime('%F %I:%M %p'), subject: '2nd Week Report', from: '\"Dianne Tux\" \u003cdianne.tux@example.com\u003e', message: \"Hello,\\n\\nI was wondering if you'd like to go over the weekly report sometime this afternoon.\\n\\nDianne\"),\n        Email.new(date: DateTime.new(2029, 10, 2, 10, 34, 0).strftime('%F %I:%M %p'), subject: 'Glimmer Upgrade v98.0', from: '\"Robert McGabbins\" \u003crobert.mcgabbins@example.com\u003e', message: \"Team,\\n\\nWe are upgrading to Glimmer version 98.0.\\n\\nEveryone pull the latest code!\\n\\nRegards,\\n\\nRobert McGabbins\"),\n      ]\n    )\n  end\n  \n  def launch\n    shell {\n      grid_layout\n      \n      text 'Hello, Custom Shell!'\n      \n      label {\n        font height: 24, style: :bold\n        text 'Emails:'\n      }\n      \n      label {\n        font height: 18\n        text 'Click an email to view its message'\n      }\n      \n      table {\n        layout_data :fill, :fill, true, true\n      \n        table_column {\n          text 'Date:'\n          width 180\n        }\n        table_column {\n          text 'Subject:'\n          width 180\n        }\n        table_column {\n          text 'From:'\n          width 360\n        }\n        \n        items bind(@email_system, :emails), column_properties(:date, :subject, :from)\n        \n        on_mouse_up { |event|\n          email = event.table_item.get_data\n          Thread.new do\n            async_exec {\n              email_shell(date: email.date, subject: email.subject, from: email.from, message: email.message).open\n            }\n          end\n        }\n      }\n    }.open\n  end\nend\n\nHelloCustomShell.new.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Custom Shell](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-custom-shell.png)\n![Glimmer DSL for SWT Hello Custom Shell Email1](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-custom-shell-email1.png)\n![Glimmer DSL for SWT Hello Custom Shell Email2](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-custom-shell-email2.png)\n![Glimmer DSL for SWT Hello Custom Shell Email3](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-custom-shell-email3.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Custom Widget!\"\n\n![Glimmer DSL for Opal Hello Custom Shell](images/glimmer-dsl-opal-hello-custom-shell.png)\n![Glimmer DSL for Opal Hello Custom Shell Email1](images/glimmer-dsl-opal-hello-custom-shell-email1.png)\n![Glimmer DSL for Opal Hello Custom Shell Email2](images/glimmer-dsl-opal-hello-custom-shell-email2.png)\n![Glimmer DSL for Opal Hello Custom Shell Email3](images/glimmer-dsl-opal-hello-custom-shell-email3.png)\n\n#### Hello, Radio!\n\nThis is the low level way of using `radio`\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_radio'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Radio](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-radio.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Radio!\"\n\n![Glimmer DSL for Opal Hello Radio](images/glimmer-dsl-opal-hello-radio.png)\n\n#### Hello, Radio Group!\n\n`radio_group` is a level higher than `radio` in abstraction. It generates a group of radio widgets based on available options in model `attribute_options` methods.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_radio_group'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloRadioGroup\n  class Person\n    attr_accessor :gender, :age_group\n    \n    def initialize\n      reset!\n    end\n    \n    def gender_options\n      ['Male', 'Female']\n    end\n    \n    def age_group_options\n      ['Child', 'Teen', 'Adult', 'Senior']\n    end\n    \n    def reset!\n      self.gender = nil\n      self.age_group = 'Adult'\n    end\n  end\n\n  include Glimmer::UI::CustomShell\n  \n  before_body {\n    @person = Person.new\n  }\n  \n  body {\n    shell {\n      text 'Hello, Radio Group!'\n      row_layout :vertical\n      \n      label {\n        text 'Gender:'\n        font style: :bold\n      }\n      \n      radio_group {\n        row_layout :horizontal\n        selection \u003c=\u003e [@person, :gender]\n      }\n            \n      label {\n        text 'Age Group:'\n        font style: :bold\n      }\n      \n      radio_group {\n        row_layout :horizontal\n        selection \u003c=\u003e [@person, :age_group]\n      }\n      \n      button {\n        text 'Reset'\n        \n        on_widget_selected do\n          @person.reset!\n        end\n      }\n    }\n  }\nend\n\nHelloRadioGroup.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Radio Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-radio-group.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Radio Group!\"\n\n![Glimmer DSL for Opal Hello Radio Group](images/glimmer-dsl-opal-hello-radio-group.png)\n\n#### Hello, Group!\n\nNot to be confused with `radio_group` or `checkbox_group`, `group` simply groups arbitrary widgets together and adds a title header above them.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_group'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-group.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Group!\"\n\n![Glimmer DSL for Opal Hello Group](images/glimmer-dsl-opal-hello-group.png)\n\n#### Hello, Canvas!\n\nThis is a minimal initial version of the [Hello, Canvas! sample included in Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#hello-canvas).\n\nIt [supports](#supported-glimmer-dsl-keywords) all shapes and attribute data-binding, but no shape nesting or gradient support yet.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_canvas'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Canvas](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-canvas.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Canvas!\"\n\n![Glimmer DSL for Opal Hello Canvas](images/glimmer-dsl-opal-hello-canvas.png)\n\n#### Hello, C Combo!\n\nThis is the low level way of using `c_combo`\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_c_combo'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello C Combo](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-c-combo.png)\n\n![Glimmer DSL for SWT Hello C Combo Expanded](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-c-combo-expanded.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, C Combo!\"\n\n![Glimmer DSL for Opal Hello C Combo](images/glimmer-dsl-opal-hello-c-combo.png)\n\n![Glimmer DSL for Opal Hello C Combo Expanded](images/glimmer-dsl-opal-hello-c-combo-expanded.png)\n\n#### Hello, C Tab!\n\nThis is the low level way of using `c_tab_folder`/`c_tab_item`\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_c_tab'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello C Tab](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-c-tab.png)\n\n![Glimmer DSL for SWT Hello C Tab Other Tab](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-c-tab-other-tab.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, C Tab!\"\n\n![Glimmer DSL for Opal Hello C Tab](images/glimmer-dsl-opal-hello-c-tab.png)\n\n![Glimmer DSL for Opal Hello C Tab Other Tab](images/glimmer-dsl-opal-hello-c-tab-other-tab.png)\n\n#### Hello, Checkbox!\n\nThis is the low level way of using `checkbox`\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_checkbox'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Checkbox](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-checkbox.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Checkbox!\"\n\n![Glimmer DSL for Opal Hello Checkbox](images/glimmer-dsl-opal-hello-checkbox.png)\n\n#### Hello, Checkbox Group!\n\n`checkbox_group` is a level higher than `checkbox` in abstraction. It generates a group of checkbox widgets based on available options in model `attribute_options` methods.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_checkbox_group'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloCheckboxGroup\n  class Person\n    attr_accessor :activities\n    \n    def initialize\n      reset_activities\n    end\n    \n    def activities_options\n      ['Skiing', 'Snowboarding', 'Snowmobiling', 'Snowshoeing']\n    end\n    \n    def reset_activities\n      self.activities = ['Snowboarding']\n    end\n  end\n  \n  include Glimmer::UI::CustomShell\n  \n  before_body {\n    @person = Person.new\n  }\n  \n  body {\n    shell {\n      text 'Hello, Checkbox Group!'\n      row_layout :vertical\n      \n      label {\n        text 'Check all snow activities you are interested in:'\n        font style: :bold\n      }\n      \n      checkbox_group {\n        selection \u003c=\u003e [@person, :activities]\n      }\n    \n      button {\n        text 'Reset Activities'\n        \n        on_widget_selected do\n          @person.reset_activities\n        end\n      }\n    }\n  }\nend\n\nHelloCheckboxGroup.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Checkbox Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-checkbox-group.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Checkbox Group!\"\n\n![Glimmer DSL for Opal Hello Checkbox Group](images/glimmer-dsl-opal-hello-checkbox-group.png)\n\n#### Hello, Date Time!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_date_time'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Checkbox Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-date-time.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Date Time!\"\n\n![Glimmer DSL for Opal Hello Date Time](images/glimmer-dsl-opal-hello-date-time.png)\n\n#### Hello, Table!\n\nNote: This [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) sample has near-complete support, but is missing table context menus at the moment.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_table'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table.png)\n\nHello, Table! Editing Game Date\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-editing-game-date.png)\n\nHello, Table! Editing Game Time\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-editing-game-time.png)\n\nHello, Table! Editing Home Team\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-editing-home-team.png)\n\nHello, Table! Sorted Game Date Ascending\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-sorted-game-date-ascending.png)\n\nHello, Table! Sorted Game Date Descending\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-sorted-game-date-descending.png)\n\nHello, Table! Playoff Type Combo\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-playoff-type-combo.png)\n\nHello, Table! Playoff Type Changed\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-playoff-type-changed.png)\n\nHello, Table! Game Booked\n\n![Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table-game-booked.png)\n\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Date Time!\"\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table.png)\n\nHello, Table! Editing Game Date\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-game-date.png)\n\nHello, Table! Editing Game Time\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-game-time.png)\n\nHello, Table! Editing Home Team\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-editing-home-team.png)\n\nHello, Table! Sorted Game Date Ascending\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-sorted-game-date-ascending.png)\n\nHello, Table! Sorted Game Date Descending\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-sorted-game-date-descending.png)\n\nHello, Table! Playoff Type Combo\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-playoff-type-combo.png)\n\nHello, Table! Playoff Type Changed\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-playoff-type-changed.png)\n\nHello, Table! Game Booked\n\n![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table-game-booked.png)\n\n#### Hello, Text!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_text'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Text](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-text.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Text!\"\n\n![Glimmer DSL for Opal Hello Text](images/glimmer-dsl-opal-hello-text.png)\n\n#### Hello, Button!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_button'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nclass HelloButton\n  include Glimmer::UI::CustomShell\n  \n  attr_accessor :count\n  \n  before_body {\n    @count = 0\n  }\n  \n  body {\n    shell {\n      text 'Hello, Button!'\n      \n      button {\n        text \u003c= [self, :count, on_read: -\u003e(value) { \"Click To Increment: #{value}  \" }]\n        \n        on_widget_selected {\n          self.count += 1\n        }\n      }\n    }\n  }\nend\n\nHelloButton.launch\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Button](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-button.png)\n![Glimmer DSL for SWT Hello Button](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-button-incremented.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Button!\"\n\n![Glimmer DSL for Opal Hello Button](images/glimmer-dsl-opal-hello-button.png)\n![Glimmer DSL for Opal Hello Button](images/glimmer-dsl-opal-hello-button-incremented.png)\n\n#### Hello, Message Box!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_message_box'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\ninclude Glimmer\n\nshell {\n  text 'Hello, Message Box!'\n  \n  button {\n    text 'Please Click To Win a Surprise'\n    \n    on_widget_selected {\n      message_box {\n        text 'Surprise'\n        message \"Congratulations!\\n\\nYou won $1,000,000!\"\n      }.open\n    }\n  }\n}.open\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Message Box](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-message-box.png)\n![Glimmer DSL for SWT Message Box Dialog](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-message-box-dialog.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Message Box!\"\n\n![Glimmer DSL for Opal Hello Message Box](images/glimmer-dsl-opal-hello-message-box.png)\n![Glimmer DSL for Opal Hello Message Box Dialog](images/glimmer-dsl-opal-hello-message-box-dialog.png)\n\n#### Hello, Print!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_print'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Print](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-print.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Print!\"\n\n#### Hello, Progress Bar!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_progress_bar'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Progress Bar](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-progress-bar.gif)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Progress Bar!\"\n\n![Glimmer DSL for Opal Hello Progress Bar](https://github.com/AndyObtiva/glimmer-dsl-opal/raw/master/images/glimmer-dsl-opal-hello-progress-bar.gif)\n\n#### Hello, Pop Up Context Menu!\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_pop_up_context_menu'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\ninclude Glimmer\n\nshell {\n  grid_layout {\n    margin_width 0\n    margin_height 0\n  }\n  \n  text 'Hello, Pop Up Context Menu!'\n  \n  label {\n    text \"Right-Click on the Text to\\nPop Up a Context Menu\"\n    font height: 50\n    \n    menu {\n      menu {\n        text '\u0026History'\n        menu {\n          text '\u0026Recent'\n          menu_item {\n            text 'File 1'\n            on_widget_selected {\n              message_box {\n                text 'File 1'\n                message 'File 1 Contents'\n              }.open\n            }\n          }\n          menu_item {\n            text 'File 2'\n            on_widget_selected {\n              message_box {\n                text 'File 2'\n                message 'File 2 Contents'\n              }.open\n            }\n          }\n        }\n        menu {\n          text '\u0026Archived'\n          menu_item {\n            text 'File 3'\n            on_widget_selected {\n              message_box {\n                text 'File 3'\n                message 'File 3 Contents'\n              }.open\n            }\n          }\n          menu_item {\n            text 'File 4'\n            on_widget_selected {\n              message_box {\n                text 'File 4'\n                message 'File 4 Contents'\n              }.open\n            }\n          }\n        }\n      }\n    }\n  }\n}.open\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Hello Pop Up Context Menu Popped Up](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-pop-up-context-menu.png)\n![Glimmer DSL for SWT Hello Pop Up Context Menu](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-pop-up-context-menu-popped-up.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Pop Up Context Menu!\"\n\n![Glimmer DSL for Opal Hello Pop Up Context Menu](images/glimmer-dsl-opal-hello-pop-up-context-menu.png)\n![Glimmer DSL for Opal Hello Pop Up Context Menu Popped Up](images/glimmer-dsl-opal-hello-pop-up-context-menu-popped-up.png)\n\n#### Hello, Menu Bar!\n\nThis sample demonstrates a menu bar similar to the File menu bar you see at the top of desktop applications.\n\nIn web applications, it is typically used to provide website information architecture by denoting things like Products, News, Careers, and About.\n\nIn web applications, it is also typically styled by CSS with margin/padding around every menu, distancing it from the top.\n\nWhen auto-webifying a pre-existing desktop application, the menu bar can be hidden with CSS if not needed, or simply shown on hover only. Web designers could decide these things to their heart's content with pure CSS independently of the developers' code.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_menu_bar'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\ninclude Glimmer\n\nCOLORS = [:white, :red, :yellow, :green, :blue, :magenta, :gray, :black]\n\nshell {\n  grid_layout {\n    margin_width 0\n    margin_height 0\n  }\n  \n  text 'Hello, Menu Bar!'\n  \n  @label = label(:center) {\n    font height: 50\n    text 'Check Out The Menu Bar Above!'\n  }\n  \n  menu_bar {\n    menu {\n      text '\u0026File'\n      menu_item {\n        text '\u0026New'\n        accelerator :command, :N\n\n        on_widget_selected {\n          message_box {\n            text 'New'\n            message 'New file created.'\n          }.open\n        }\n      }\n      menu_item {\n        text '\u0026Open...'\n        accelerator :command, :O\n\n        on_widget_selected {\n          message_box {\n            text 'Open'\n            message 'Opening File...'\n          }.open\n        }\n      }\n      menu {\n        text 'Open \u0026Recent'\n        menu_item {\n          text 'File 1'\n          on_widget_selected {\n            message_box {\n              text 'File 1'\n              message 'File 1 Contents'\n            }.open\n          }\n        }\n        menu_item {\n          text 'File 2'\n          on_widget_selected {\n            message_box {\n              text 'File 2'\n              message 'File 2 Contents'\n            }.open\n          }\n        }\n      }\n      menu_item(:separator)\n      menu_item {\n        text 'E\u0026xit'\n\n        on_widget_selected {\n          exit(0)\n        }\n      }\n    }\n    menu {\n      text '\u0026Edit'\n      menu_item {\n        text 'Cut'\n        accelerator :command, :X\n      }\n      menu_item {\n        text 'Copy'\n        accelerator :command, :C\n      }\n      menu_item {\n        text 'Paste'\n        accelerator :command, :V\n      }\n    }\n    menu {\n      text '\u0026Options'\n\n      menu_item(:radio) {\n        text '\u0026Enabled'\n\n        on_widget_selected {\n          @select_one_menu.enabled = true\n          @select_multiple_menu.enabled = true\n        }\n      }\n      @select_one_menu = menu {\n        text '\u0026Select One'\n        enabled false\n\n        menu_item(:radio) {\n          text 'Option 1'\n        }\n        menu_item(:radio) {\n          text 'Option 2'\n        }\n        menu_item(:radio) {\n          text 'Option 3'\n        }\n      }\n      @select_multiple_menu = menu {\n        text '\u0026Select Multiple'\n        enabled false\n\n        menu_item(:check) {\n          text 'Option 4'\n        }\n        menu_item(:check) {\n          text 'Option 5'\n        }\n        menu_item(:check) {\n          text 'Option 6'\n        }\n      }\n    }\n    menu {\n      text '\u0026Format'\n      menu {\n        text '\u0026Background Color'\n        COLORS.each { |color_style|\n          menu_item(:radio) {\n            text color_style.to_s.split('_').map(\u0026:capitalize).join(' ')\n\n            on_widget_selected {\n              @label.background = color_style\n            }\n          }\n        }\n      }\n      menu {\n        text 'Foreground \u0026Color'\n        COLORS.each { |color_style|\n          menu_item(:radio) {\n            text color_style.to_s.split('_').map(\u0026:capitalize).join(' ')\n\n            on_widget_selected {\n              @label.foreground = color_style\n            }\n          }\n        }\n      }\n    }\n    menu {\n      text '\u0026View'\n      menu_item(:radio) {\n        text 'Small'\n\n        on_widget_selected {\n          @label.font = {height: 25}\n          @label.parent.pack\n        }\n      }\n      menu_item(:radio) {\n        text 'Medium'\n        selection true\n\n        on_widget_selected {\n          @label.font = {height: 50}\n          @label.parent.pack\n        }\n      }\n      menu_item(:radio) {\n        text 'Large'\n\n        on_widget_selected {\n          @label.font = {height: 75}\n          @label.parent.pack\n        }\n      }\n    }\n    menu {\n      text '\u0026Help'\n      menu_item {\n        text '\u0026Manual'\n        accelerator :command, :shift, :M\n\n        on_widget_selected {\n          message_box {\n            text 'Manual'\n            message 'Manual Contents'\n          }.open\n        }\n      }\n      menu_item {\n        text '\u0026Tutorial'\n        accelerator :command, :shift, :T\n\n        on_widget_selected {\n          message_box {\n            text 'Tutorial'\n            message 'Tutorial Contents'\n          }.open\n        }\n      }\n      menu_item(:separator)\n      menu_item {\n        text '\u0026Report an Issue...'\n\n        on_widget_selected {\n          message_box {\n            text 'Report an Issue'\n            message 'Reporting an issue...'\n          }.open\n        }\n      }\n    }\n  }\n}.open\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Hello Menu Bar](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar.png)\n\n![Hello Menu Bar File Menu](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-file-menu.png)\n\n![Hello Menu Bar Edit Menu](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-edit-menu.png)\n\n![Hello Menu Bar Options Menu Disabled](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-options-menu-disabled.png)\n\n![Hello Menu Bar Options Menu Select One](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-options-menu-select-one.png)\n\n![Hello Menu Bar Options Menu Select Multiple](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-options-menu-select-multiple.png)\n\n![Hello Menu Bar Format Menu Background Color](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-format-menu-background-color.png)\n\n![Hello Menu Bar Format Menu Foreground Color](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-format-menu-foreground-color.png)\n\n![Hello Menu Bar View Menu](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-view-menu.png)\n\n![Hello Menu Bar View Small](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-view-small.png)\n\n![Hello Menu Bar View Large](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-view-large.png)\n\n![Hello Menu Bar Help Menu](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-menu-bar-help-menu.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Menu Bar!\"\n\n![Hello Menu Bar](images/glimmer-dsl-opal-hello-menu-bar.png)\n\n![Hello Menu Bar File Menu](images/glimmer-dsl-opal-hello-menu-bar-file-menu.png)\n\n![Hello Menu Bar Edit Menu](images/glimmer-dsl-opal-hello-menu-bar-edit-menu.png)\n\n![Hello Menu Bar Options Menu Disabled](images/glimmer-dsl-opal-hello-menu-bar-options-menu-disabled.png)\n\n![Hello Menu Bar Options Menu Select One](images/glimmer-dsl-opal-hello-menu-bar-options-menu-select-one.png)\n\n![Hello Menu Bar Options Menu Select Multiple](images/glimmer-dsl-opal-hello-menu-bar-options-menu-select-multiple.png)\n\n![Hello Menu Bar Format Menu Background Color](images/glimmer-dsl-opal-hello-menu-bar-format-menu-background-color.png)\n\n![Hello Menu Bar Format Menu Foreground Color](images/glimmer-dsl-opal-hello-menu-bar-format-menu-foreground-color.png)\n\n![Hello Menu Bar View Menu](images/glimmer-dsl-opal-hello-menu-bar-view-menu.png)\n\n![Hello Menu Bar View Small](images/glimmer-dsl-opal-hello-menu-bar-view-small.png)\n\n![Hello Menu Bar View Large](images/glimmer-dsl-opal-hello-menu-bar-view-large.png)\n\n![Hello Menu Bar Help Menu](images/glimmer-dsl-opal-hello-menu-bar-help-menu.png)\n\n#### Hello, Dialog!\n\nThis sample demonstrates a modal dialog similar to message_box, but allows adding arbitrary widgets, not just a message.\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/hello/hello_dialog'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\ninclude Glimmer\n\nshell {\n  row_layout :vertical\n  \n  text 'Hello, Dialog!'\n  \n  7.times { |n|\n    dialog_number = n + 1\n    \n    button {\n      layout_data {\n        width 200\n        height 50\n      }\n      text \"Dialog #{dialog_number}\"\n      \n      on_widget_selected {\n        dialog { |dialog_proxy|\n          row_layout(:vertical) {\n            center true\n          }\n          \n          text \"Dialog #{dialog_number}\"\n          \n          label {\n            text \"Given `dialog` is modal, you cannot interact with the main window till the dialog is closed.\"\n          }\n          composite {\n            row_layout {\n              margin_height 0\n              margin_top 0\n              margin_bottom 0\n            }\n\n            label {\n              text \"Unlike `message_box`, `dialog` can contain arbitrary widgets:\"\n            }\n            radio {\n              text 'Radio'\n            }\n            checkbox {\n              text 'Checkbox'\n            }\n          }\n          button {\n            text 'Close'\n            \n            on_widget_selected {\n              dialog_proxy.close\n            }\n          }\n        }.open\n      }\n    }\n  }\n}.open\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Hello Dialog](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-dialog.png)\n\n![Hello Dialog Open Dialog](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-dialog-open-dialog.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Hello, Dialog!\"\n\n![Hello Dialog](images/glimmer-dsl-opal-hello-dialog.png)\n\n![Hello Dialog Open Dialog](images/glimmer-dsl-opal-hello-dialog-open-dialog.png)\n\n### Elaborate Samples\n\n#### Login\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/elaborate/login'\n```\n\nOr add the Glimmer code directly if you prefer to play around with it:\n\n```ruby\nrequire \"observer\"\n\nclass LoginPresenter\n\n  attr_accessor :user_name\n  attr_accessor :password\n  attr_accessor :status\n\n  def initialize\n    @user_name = \"\"\n    @password = \"\"\n    @status = \"Logged Out\"\n  end\n\n  def status=(status)\n    @status = status\n\n    notify_observers(\"logged_in\")\n    notify_observers(\"logged_out\")\n  end\n  \n  def valid?\n    !@user_name.to_s.strip.empty? \u0026\u0026 !@password.to_s.strip.empty?\n  end\n\n  def logged_in\n    self.status == \"Logged In\"\n  end\n\n  def logged_out\n    !self.logged_in\n  end\n\n  def login\n    return unless valid?\n    self.status = \"Logged In\"\n  end\n\n  def logout\n    self.user_name = \"\"\n    self.password = \"\"\n    self.status = \"Logged Out\"\n  end\n\nend\n\nclass Login\n  include Glimmer::UI::CustomShell\n  \n  before_body {\n    @presenter = LoginPresenter.new\n  }\n\n  body {\n    shell {\n      text \"Login\"\n      \n      composite {\n        grid_layout 2, false #two columns with differing widths\n\n        label { text \"Username:\" } # goes in column 1\n        @user_name_text = text {   # goes in column 2\n          text \u003c=\u003e [@presenter, :user_name]\n          enabled \u003c= [@presenter, :logged_out?, computed_by: :status]\n          \n          on_key_pressed { |event|\n            @password_text.set_focus if event.keyCode == swt(:cr)\n          }\n        }\n\n        label { text \"Password:\" }\n        @password_text = text(:password, :border) {\n          text \u003c=\u003e [@presenter, :password]\n          enabled \u003c= [@presenter, :logged_out?, computed_by: :status]\n          \n          on_key_pressed { |event|\n            @presenter.login! if event.keyCode == swt(:cr)\n          }\n        }\n\n        label { text \"Status:\" }\n        label { text \u003c= [@presenter, :status] }\n\n        button {\n          text \"Login\"\n          enabled \u003c= [@presenter, :logged_out?, computed_by: :status]\n          \n          on_widget_selected { @presenter.login! }\n          on_key_pressed { |event|\n            if event.keyCode == swt(:cr)\n              @presenter.login!\n            end\n          }\n        }\n\n        button {\n          text \"Logout\"\n          enabled \u003c= [@presenter, :logged_in?, computed_by: :status]\n          \n          on_widget_selected { @presenter.logout! }\n          on_key_pressed { |event|\n            if event.keyCode == swt(:cr)\n              @presenter.logout!\n              @user_name_text.set_focus\n            end\n          }\n        }\n      }\n    }\n  }\nend\n\nLogin.launch\n```\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\n![Glimmer DSL for SWT Login](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-login.png)\n![Glimmer DSL for SWT Login Filled In](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-login-filled-in.png)\n![Glimmer DSL for SWT Login Logged In](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-login-logged-in.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Login\" dialog\n\n![Glimmer DSL for Opal Login](images/glimmer-dsl-opal-login.png)\n![Glimmer DSL for Opal Login Filled In](images/glimmer-dsl-opal-login-filled-in.png)\n![Glimmer DSL for Opal Login Logged In](images/glimmer-dsl-opal-login-logged-in.png)\n\n#### Contact Manager\n\nAdd the following require statement to `app/assets/javascripts/application.rb`\n\n```ruby\nrequire 'glimmer-dsl-opal/samples/elaborate/contact_manager'\n```\n\nGlimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):\n\nGlimmer DSL for SWT Contact Manager\n\n![Glimmer DSL for SWT Contact Manager](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-contact-manager.png)\n\nGlimmer DSL for SWT Contact Manager Find\n\n![Glimmer DSL for SWT Contact Manager Find](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-contact-manager-find.png)\n\nGlimmer DSL for SWT Contact Manager Edit Started\n\n![Glimmer DSL for SWT Contact Manager Edit Started](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-contact-manager-edit-started.png)\n\nGlimmer DSL for SWT Contact Manager Edit In Progress\n\n![Glimmer DSL for SWT Contact Manager Edit In Progress](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-contact-manager-edit-in-progress.png)\n\nGlimmer DSL for SWT Contact Manager Edit Done\n\n![Glimmer DSL for SWT Contact Manager Edit Done](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-contact-manager-edit-done.png)\n\nGlimmer app on the web (using `glimmer-dsl-opal` gem):\n\nStart the Rails server:\n```\nrails s\n```\n\nVisit `http://localhost:3000`\n\nYou should see \"Contact Manager\"\n\nGlimmer DSL for Opal Contact Manager\n\n![Glimmer DSL for Opal Contact Manager](images/glimmer-dsl-opal-contact-manager.png)\n\nGlimmer DSL for Opal Contact Manager Find\n\n![Glimmer DSL for Opal Contact Manager Find](images/glimmer-dsl-opal-contact-manager-find.png)\n\nGlimmer DSL for Opal Contact Manager Edit Started\n\n![Glimmer DSL for Opal Contact Manager Edit Started](images/glimmer-dsl-opal-contact-manager-edit-started.png)\n\nGlimmer DSL for Opal Contact Manager Edit In Progress\n\n![Glimmer DSL for Opal Contact Manager Edit In Progress](images/glimmer-dsl-opal-contact-manager-edit-in-progress.png)\n\nGlimmer DSL for Opal Contact Manager Edit Done\n\n![Glimmer DSL for Opal Contact Manager Edit Done](images/glimmer-dsl-opal-contact-manager-edit-done.png)\n\n#### Tetris\n\nThis is a slightly minimal version of the [Tetris seen in Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_SAMPLES.md#glimmer-tetris).\n\nNote that while the Glimmer GUI DSL code is mostly the same, some of the behavioral code around threads is changed\ninto async Opal code via the [opal-async](https://github.com/AndyObtiva/opal-async) gem due to the ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyObtiva%2Fglimmer-dsl-opal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FAndyObtiva%2Fglimmer-dsl-opal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FAndyObtiva%2Fglimmer-dsl-opal/lists"}