glimmer-dsl-opal 0.13.0 → 0.16.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4c1b8623cfb624fda19e969ff546ad75a41d32331c9c5c3cabd267a74c96b62
4
- data.tar.gz: 4b84ff762bbc5cecc3eefbf4e0dbd7f95501658523751e2a2936405974a662a6
3
+ metadata.gz: e2ee052273513ff79b9d9ef0f351f30bfa0c4083f589429646b894b132afa4c3
4
+ data.tar.gz: f2a9113b2a92168046d19c3068d9b62df1000fdc1dd6c9ea5f168ffb89123fb4
5
5
  SHA512:
6
- metadata.gz: da478774923d89f49c1fbc050f9234f24a728781dddc7f38a1790795efdf5f1d9228982859e6027fd4e90d3ad61d8254275df4882a9453c61f0bbfcf16c7e089
7
- data.tar.gz: f8a46956d0caf08f7ab94bb183e02131acd808f4e17d084d961e15c79ea044c9dc16cb20a436e94ea63a26446cc6ce6b5467878634c04960a8464b38ee26666d
6
+ metadata.gz: 20edcc6d97dbee21a5b8b332bc649fb0f7d77f93e41d02095cdbbed43f0686f8c1dd5bf08972bd21511cb983f77caf247f61136ddde5de859f444ac92f31dbf2
7
+ data.tar.gz: ce7347775105f4b2af132cd68b4a805332b4afe910cb3ff91c0da083bd043e93cf067c693c9e4a54b506c2417d684248d23fa78aecccc264c741c38028b35b6e
data/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.16.1
4
+
5
+ - Support GridLayout make_columns_equal_width, horizontal_spacing, and vertical_spacing attributes
6
+ - Hello, Composite! sample
7
+
8
+ ## 0.16.0
9
+
10
+ - Support label widget background_image attribute
11
+ - Have File.expand_path support expanding paths even if they did not base off of __dir__ or __FILE__
12
+ - Custom specification of gems having image paths via server-side configuration in Glimmer::Config.gems_having_image_paths
13
+
14
+ ## 0.15.1
15
+
16
+ - Auto-expose images of gems that depend on glimmer-dsl-opal as downloadable asset links providing `/glimmer/image_paths` server call to obtain them
17
+ - Update Hello, Table! to work with image background
18
+
19
+ ## 0.14.0
20
+
21
+ - Initial Net::HTTP support for get and post_form
22
+ - Added "Weather" elaborate sample (minus multi-threaded refetches)
23
+ - Updated Hello, Button!, Hello, Combo!, and Hello, Computed! from Glimmer DSL for SWT
24
+
3
25
  ## 0.13.0
4
26
 
5
27
  - Support Shine data-binding syntax in custom widgets
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Opal 0.13.0 (Pure Ruby Web GUI)
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for Opal 0.16.0 (Pure Ruby Web GUI)
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-opal.svg)](http://badge.fury.io/rb/glimmer-dsl-opal)
3
3
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
4
4
 
@@ -8,12 +8,14 @@
8
8
 
9
9
  Use in one of two ways:
10
10
  - **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.
11
- - **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 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.
11
+ - **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.
12
12
 
13
13
  Glimmer 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 `<=>` and `<=` for bidirectional [two-way] and unidirectional [one-way] data-binding respectively).
14
14
 
15
15
  #### Hello, Table! Sample
16
16
 
17
+ Code: [lib/glimmer-dsl-opal/samples/hello/hello_table.rb](lib/glimmer-dsl-opal/samples/hello/hello_table.rb)
18
+
17
19
  Glimmer GUI code from [glimmer-dsl-opal/samples/hello/hello_table.rb](lib/glimmer-dsl-opal/samples/hello/hello_table.rb):
18
20
 
19
21
  ```ruby
@@ -103,7 +105,7 @@ shell {
103
105
 
104
106
  ![Glimmer DSL for SWT Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table.png)
105
107
 
106
- **Hello, Table! (same code) running on the web via Opal on Rails (using the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem):**
108
+ **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):**
107
109
 
108
110
  ![Glimmer DSL for Opal Hello Table](images/glimmer-dsl-opal-hello-table.png)
109
111
 
@@ -141,7 +143,7 @@ Hello, Table! Game Booked
141
143
 
142
144
  NOTE: Glimmer DSL for Opal is an alpha project. 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.
143
145
 
144
- **Alpha Version** 0.13.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))
146
+ **Alpha Version** 0.16.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))
145
147
 
146
148
  Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
147
149
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
@@ -151,7 +153,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
151
153
 
152
154
  ## Table of Contents
153
155
 
154
- - [Glimmer DSL for Opal 0.13.0 (Pure Ruby Web GUI)](#-glimmer-dsl-for-opal-0130-pure-ruby-web-gui)
156
+ - [Glimmer DSL for Opal 0.16.0 (Pure Ruby Web GUI)](#-glimmer-dsl-for-opal-0160-pure-ruby-web-gui)
155
157
  - [Principles](#principles)
156
158
  - [Background](#background)
157
159
  - [Pre-requisites](#pre-requisites)
@@ -161,6 +163,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
161
163
  - [Hello Samples](#hello-samples)
162
164
  - [Hello, World!](#hello-world)
163
165
  - [Hello, Combo!](#hello-combo)
166
+ - [Hello, Composite!](#hello-composite)
164
167
  - [Hello, Computed!](#hello-computed)
165
168
  - [Hello, List Single Selection!](#hello-list-single-selection)
166
169
  - [Hello, List Multi Selection!](#hello-list-multi-selection)
@@ -184,6 +187,7 @@ Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
184
187
  - [Login](#login)
185
188
  - [Tic Tac Toe](#tic-tac-toe)
186
189
  - [Contact Manager](#contact-manager)
190
+ - [Weather](#weather)
187
191
  - [External Samples](#external-samples)
188
192
  - [Glimmer Calculator](#glimmer-calculator)
189
193
  - [Glimmer Supporting Libraries](#glimmer-supporting-libraries)
@@ -205,7 +209,6 @@ Please keep in mind this is a live list of innovative ideas, some of which have
205
209
  - **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.
206
210
  - **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 has a predictable ID and CSS class. This achieves a proper separation of responsibilities between developers and designers.
207
211
  - **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.
208
- - **Everybody In!** All JS frameworks sadly suffer from very major software design handicaps because of trying to ensure data security, so you end up with a very strict separation between server data and client data, making your head spin and worrying more about hackers and attackers than serving users. Try again! Thanks to the principle of Everybody In, Glimmer DSL for Opal apps have a very unique software architecture that revolves around the idea of multi-tenancy. Basically, every user gets their own server-side real-estate, that is a fully-secure-and-independent server-side instance that has its own user database, so hackers are not only intentionally allowed in, they are WELCOMED! As such, instead of protecting all users' data with an iron fist over a shared server/database (a terribly insecure architecture no matter how profilerate), you simply provide each user their own fully-independent server/client real-estate, thus be able to focus on serving them in the best way possible by writing code that is so simple it is just like desktop application code, but living in the Cloud on the Web. In summary, just code web as if you're on the desktop and be happy!
209
212
  - **Forget Routers!** Glimmer DSL for 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.
210
213
 
211
214
  ## Background
@@ -221,7 +224,8 @@ Alternatively, web developers may directly use [Glimmer DSL for Opal](https://ru
221
224
  ## Pre-requisites
222
225
 
223
226
  - Rails 5: [https://github.com/rails/rails/tree/5-2-stable](https://github.com/rails/rails/tree/5-2-stable)
224
- - Opal 1: [https://github.com/opal/opal-rails](https://github.com/opal/opal-rails)
227
+ - Opal 1.0.4: [https://github.com/opal/opal](https://github.com/opal/opal)
228
+ - Opal-Rails 1.1.2: [https://github.com/opal/opal-rails](https://github.com/opal/opal-rails)
225
229
  - jQuery 3: [https://code.jquery.com/](https://code.jquery.com/) (jQuery 3.5.1 is included in the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem)
226
230
  - jQuery-UI 1.12: [https://code.jquery.com/](https://jqueryui.com/) (jQuery-UI 1.12.1 is included in the [glimmer-dsl-opal](https://rubygems.org/gems/glimmer-dsl-opal) gem)
227
231
  - jQuery-UI Timepicker 0.3: [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)
@@ -249,10 +253,11 @@ rails new glimmer_app_server
249
253
  Add the following to `Gemfile`:
250
254
 
251
255
  ```
252
- gem 'opal-rails', '~> 1.1.2'
256
+ gem 'opal', '1.0.4'
257
+ gem 'opal-rails', '1.1.2'
253
258
  gem 'opal-async', '~> 1.2.0'
254
259
  gem 'opal-jquery', '~> 0.4.4'
255
- gem 'glimmer-dsl-opal', '~> 0.13.0'
260
+ gem 'glimmer-dsl-opal', '~> 0.16.0'
256
261
  gem 'glimmer-dsl-xml', '~> 1.2.0', require: false
257
262
  gem 'glimmer-dsl-css', '~> 1.2.0', require: false
258
263
 
@@ -441,52 +446,54 @@ require 'glimmer-dsl-opal/samples/hello/hello_combo'
441
446
  Or add the Glimmer code directly if you prefer to play around with it:
442
447
 
443
448
  ```ruby
444
- class Person
445
- attr_accessor :country, :country_options
446
-
447
- def initialize
448
- self.country_options = ['', 'Canada', 'US', 'Mexico']
449
- reset_country
450
- end
451
-
452
- def reset_country
453
- self.country = 'Canada'
449
+ class HelloCombo
450
+ class Person
451
+ attr_accessor :country, :country_options
452
+
453
+ def initialize
454
+ self.country_options = ['', 'Canada', 'US', 'Mexico']
455
+ reset_country!
456
+ end
457
+
458
+ def reset_country!
459
+ self.country = 'Canada'
460
+ end
454
461
  end
455
- end
456
462
 
457
- class HelloCombo
458
- include Glimmer
463
+ include Glimmer::UI::CustomShell
459
464
 
460
- def launch
461
- person = Person.new
462
-
465
+ before_body {
466
+ @person = Person.new
467
+ }
468
+
469
+ body {
463
470
  shell {
464
471
  row_layout(:vertical) {
465
- pack false
472
+ fill true
466
473
  }
467
474
 
468
475
  text 'Hello, Combo!'
469
476
 
470
477
  combo(:read_only) {
471
- selection <=> [person, :country]
478
+ selection <=> [@person, :country] # also binds to country_options by convention
472
479
  }
473
480
 
474
481
  button {
475
482
  text 'Reset Selection'
476
483
 
477
484
  on_widget_selected do
478
- person.reset_country
485
+ @person.reset_country!
479
486
  end
480
487
  }
481
- }.open
482
- end
488
+ }
489
+ }
483
490
  end
484
491
 
485
- HelloCombo.new.launch
492
+ HelloCombo.launch
486
493
  ```
487
494
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
488
495
 
489
- ![Glimmer DSL for SWT Hello Combo](https://github.com/AndyObtiva/glimmer/blob/master/images/glimmer-hello-combo.png)
496
+ ![Glimmer DSL for SWT Hello Combo](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/images/glimmer-hello-combo.png)
490
497
 
491
498
  Glimmer app on the web (using `glimmer-dsl-opal` gem):
492
499
 
@@ -501,6 +508,31 @@ You should see "Hello, Combo!"
501
508
 
502
509
  ![Glimmer DSL for Opal Hello Combo](images/glimmer-dsl-opal-hello-combo.png)
503
510
 
511
+ #### Hello, Composite!
512
+
513
+ Add the following require statement to `app/assets/javascripts/application.rb`
514
+
515
+ ```ruby
516
+ require 'glimmer-dsl-opal/samples/hello/hello_composite'
517
+ ```
518
+
519
+ Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
520
+
521
+ ![Glimmer DSL for SWT Hello Composite](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/images/glimmer-hello-composite.png)
522
+
523
+ Glimmer app on the web (using `glimmer-dsl-opal` gem):
524
+
525
+ Start the Rails server:
526
+ ```
527
+ rails s
528
+ ```
529
+
530
+ Visit `http://localhost:3000`
531
+
532
+ You should see "Hello, Composite!"
533
+
534
+ ![Glimmer DSL for Opal Hello Composite](images/glimmer-dsl-opal-hello-composite.png)
535
+
504
536
  #### Hello, Computed!
505
537
 
506
538
  Add the following require statement to `app/assets/javascripts/application.rb`
@@ -534,17 +566,17 @@ class HelloComputed
534
566
  end
535
567
  end
536
568
 
537
- include Glimmer
569
+ include Glimmer::UI::CustomShell
538
570
 
539
- def initialize
571
+ before_body {
540
572
  @contact = Contact.new(
541
573
  first_name: 'Barry',
542
574
  last_name: 'McKibbin',
543
575
  year_of_birth: 1985
544
576
  )
545
- end
577
+ }
546
578
 
547
- def launch
579
+ body {
548
580
  shell {
549
581
  text 'Hello, Computed!'
550
582
 
@@ -601,11 +633,11 @@ class HelloComputed
601
633
  }
602
634
  }
603
635
  }
604
- }.open
605
- end
636
+ }
637
+ }
606
638
  end
607
639
 
608
- HelloComputed.new.launch
640
+ HelloComputed.launch
609
641
  ```
610
642
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
611
643
 
@@ -633,44 +665,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
633
665
  require 'glimmer-dsl-opal/samples/hello/hello_list_single_selection'
634
666
  ```
635
667
 
636
- Or add the Glimmer code directly if you prefer to play around with it:
637
-
638
- ```ruby
639
- class Person
640
- attr_accessor :country, :country_options
641
-
642
- def initialize
643
- self.country_options=["", "Canada", "US", "Mexico"]
644
- self.country = "Canada"
645
- end
646
-
647
- def reset_country
648
- self.country = "Canada"
649
- end
650
- end
651
-
652
- class HelloListSingleSelection
653
- include Glimmer
654
- def launch
655
- person = Person.new
656
- shell {
657
- composite {
658
- list {
659
- selection bind(person, :country)
660
- }
661
- button {
662
- text "Reset"
663
- on_widget_selected do
664
- person.reset_country
665
- end
666
- }
667
- }
668
- }.open
669
- end
670
- end
671
-
672
- HelloListSingleSelection.new.launch
673
- ```
674
668
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
675
669
 
676
670
  ![Glimmer DSL for SWT Hello List Single Selection](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-list-single-selection.png)
@@ -696,63 +690,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
696
690
  require 'glimmer-dsl-opal/samples/hello/hello_list_multi_selection'
697
691
  ```
698
692
 
699
- Or add the Glimmer code directly if you prefer to play around with it:
700
-
701
- ```ruby
702
- class HelloListMultiSelection
703
- class Person
704
- attr_accessor :provinces, :provinces_options
705
-
706
- def initialize
707
- self.provinces_options = [
708
- '',
709
- 'Alberta',
710
- 'British Columbia',
711
- 'Manitoba',
712
- 'New Brunswick',
713
- 'Newfoundland and Labrador',
714
- 'Northwest Territories',
715
- 'Nova Scotia',
716
- 'Nunavut',
717
- 'Ontario',
718
- 'Prince Edward Island',
719
- 'Quebec',
720
- 'Saskatchewan',
721
- 'Yukon'
722
- ]
723
- reset_provinces
724
- end
725
-
726
- def reset_provinces
727
- self.provinces = ['Quebec', 'Manitoba', 'Alberta']
728
- end
729
- end
730
-
731
- include Glimmer
732
-
733
- def launch
734
- person = Person.new
735
-
736
- shell {
737
- grid_layout
738
-
739
- text 'Hello, List Multi Selection!'
740
-
741
- list(:multi) {
742
- selection bind(person, :provinces)
743
- }
744
-
745
- button {
746
- text 'Reset Selections To Default Values'
747
-
748
- on_widget_selected { person.reset_provinces }
749
- }
750
- }.open
751
- end
752
- end
753
-
754
- HelloListMultiSelection.new.launch
755
- ```
756
693
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
757
694
 
758
695
  ![Glimmer DSL for SWT Hello List Multi Selection](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-list-multi-selection.png)
@@ -778,18 +715,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
778
715
  require 'glimmer-dsl-opal/samples/hello/hello_browser'
779
716
  ```
780
717
 
781
- Or add the Glimmer code directly if you prefer to play around with it:
782
-
783
- ```ruby
784
- include Glimmer
785
-
786
- shell {
787
- minimum_size 1024, 860
788
- browser {
789
- url 'http://brightonresort.com/about'
790
- }
791
- }.open
792
- ```
793
718
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
794
719
 
795
720
  ![Glimmer DSL for SWT Hello Browser](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-browser.png)
@@ -894,7 +819,7 @@ class GreetingLabel
894
819
  after_body {
895
820
  return if colors.nil?
896
821
 
897
- Thread.new {
822
+ Thread.new { # imported from Glimmer DSL for SWT. In Opal, avoid Threads and sleep to avoid blocking GUI.
898
823
  colors.cycle { |color|
899
824
  async_exec {
900
825
  self.color = color
@@ -909,7 +834,7 @@ class GreetingLabel
909
834
  label(swt_style) {
910
835
  text "#{greeting}, #{name}!"
911
836
  font @font
912
- foreground bind(self, :color)
837
+ foreground <=> [self, :color]
913
838
  }
914
839
  }
915
840
 
@@ -1131,103 +1056,14 @@ You should see "Hello, Custom Widget!"
1131
1056
 
1132
1057
  #### Hello, Radio!
1133
1058
 
1059
+ This is the low level way of using `radio`
1060
+
1134
1061
  Add the following require statement to `app/assets/javascripts/application.rb`
1135
1062
 
1136
1063
  ```ruby
1137
1064
  require 'glimmer-dsl-opal/samples/hello/hello_radio'
1138
1065
  ```
1139
1066
 
1140
- Or add the Glimmer code directly if you prefer to play around with it:
1141
-
1142
- ```ruby
1143
- class HelloRadio
1144
- class Person
1145
- attr_accessor :male, :female, :child, :teen, :adult, :senior
1146
-
1147
- def initialize
1148
- reset
1149
- end
1150
-
1151
- def reset
1152
- self.male = nil
1153
- self.female = nil
1154
- self.child = nil
1155
- self.teen = nil
1156
- self.adult = true
1157
- self.senior = nil
1158
- end
1159
- end
1160
-
1161
- include Glimmer
1162
-
1163
- def launch
1164
- person = Person.new
1165
-
1166
- shell {
1167
- text 'Hello, Radio!'
1168
- row_layout :vertical
1169
-
1170
- label {
1171
- text 'Gender:'
1172
- font style: :bold
1173
- }
1174
-
1175
- composite {
1176
- row_layout
1177
-
1178
- radio {
1179
- text 'Male'
1180
- selection bind(person, :male)
1181
- }
1182
-
1183
- radio {
1184
- text 'Female'
1185
- selection bind(person, :female)
1186
- }
1187
- }
1188
-
1189
- label {
1190
- text 'Age Group:'
1191
- font style: :bold
1192
- }
1193
-
1194
- composite {
1195
- row_layout
1196
-
1197
- radio {
1198
- text 'Child'
1199
- selection bind(person, :child)
1200
- }
1201
-
1202
- radio {
1203
- text 'Teen'
1204
- selection bind(person, :teen)
1205
- }
1206
-
1207
- radio {
1208
- text 'Adult'
1209
- selection bind(person, :adult)
1210
- }
1211
-
1212
- radio {
1213
- text 'Senior'
1214
- selection bind(person, :senior)
1215
- }
1216
- }
1217
-
1218
- button {
1219
- text 'Reset'
1220
-
1221
- on_widget_selected do
1222
- person.reset
1223
- end
1224
- }
1225
- }.open
1226
- end
1227
- end
1228
-
1229
- HelloRadio.new.launch
1230
- ```
1231
1067
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1232
1068
 
1233
1069
  ![Glimmer DSL for SWT Hello Radio](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-radio.png)
@@ -1247,6 +1083,8 @@ You should see "Hello, Radio!"
1247
1083
 
1248
1084
  #### Hello, Radio Group!
1249
1085
 
1086
+ `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.
1087
+
1250
1088
  Add the following require statement to `app/assets/javascripts/application.rb`
1251
1089
 
1252
1090
  ```ruby
@@ -1261,7 +1099,7 @@ class HelloRadioGroup
1261
1099
  attr_accessor :gender, :age_group
1262
1100
 
1263
1101
  def initialize
1264
- reset
1102
+ reset!
1265
1103
  end
1266
1104
 
1267
1105
  def gender_options
@@ -1272,17 +1110,19 @@ class HelloRadioGroup
1272
1110
  ['Child', 'Teen', 'Adult', 'Senior']
1273
1111
  end
1274
1112
 
1275
- def reset
1113
+ def reset!
1276
1114
  self.gender = nil
1277
1115
  self.age_group = 'Adult'
1278
1116
  end
1279
1117
  end
1280
1118
 
1281
- include Glimmer
1119
+ include Glimmer::UI::CustomShell
1282
1120
 
1283
- def launch
1284
- person = Person.new
1285
-
1121
+ before_body {
1122
+ @person = Person.new
1123
+ }
1124
+
1125
+ body {
1286
1126
  shell {
1287
1127
  text 'Hello, Radio Group!'
1288
1128
  row_layout :vertical
@@ -1294,7 +1134,7 @@ class HelloRadioGroup
1294
1134
 
1295
1135
  radio_group {
1296
1136
  row_layout :horizontal
1297
- selection <=> [person, :gender]
1137
+ selection <=> [@person, :gender]
1298
1138
  }
1299
1139
 
1300
1140
  label {
@@ -1304,21 +1144,21 @@ class HelloRadioGroup
1304
1144
 
1305
1145
  radio_group {
1306
1146
  row_layout :horizontal
1307
- selection <=> [person, :age_group]
1147
+ selection <=> [@person, :age_group]
1308
1148
  }
1309
1149
 
1310
1150
  button {
1311
1151
  text 'Reset'
1312
1152
 
1313
1153
  on_widget_selected do
1314
- person.reset
1154
+ @person.reset!
1315
1155
  end
1316
1156
  }
1317
- }.open
1318
- end
1157
+ }
1158
+ }
1319
1159
  end
1320
1160
 
1321
- HelloRadioGroup.new.launch
1161
+ HelloRadioGroup.launch
1322
1162
  ```
1323
1163
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1324
1164
 
@@ -1339,99 +1179,14 @@ You should see "Hello, Radio Group!"
1339
1179
 
1340
1180
  #### Hello, Group!
1341
1181
 
1182
+ Not to be confused with `radio_group` or `checkbox_group`, `group` simply groups arbitrary widgets together and adds a title header above them.
1183
+
1342
1184
  Add the following require statement to `app/assets/javascripts/application.rb`
1343
1185
 
1344
1186
  ```ruby
1345
1187
  require 'glimmer-dsl-opal/samples/hello/hello_group'
1346
1188
  ```
1347
1189
 
1348
- Or add the Glimmer code directly if you prefer to play around with it:
1349
-
1350
- ```ruby
1351
- class HelloGroup
1352
- class Person
1353
- attr_accessor :male, :female, :child, :teen, :adult, :senior
1354
-
1355
- def initialize
1356
- reset
1357
- end
1358
-
1359
- def reset
1360
- self.male = nil
1361
- self.female = nil
1362
- self.child = nil
1363
- self.teen = nil
1364
- self.adult = true
1365
- self.senior = nil
1366
- end
1367
- end
1368
-
1369
- include Glimmer
1370
-
1371
- def launch
1372
- person = Person.new
1373
-
1374
- shell {
1375
- text 'Hello, Group!'
1376
- row_layout :vertical
1377
-
1378
- group {
1379
- row_layout
1380
-
1381
- text 'Gender'
1382
- font style: :bold
1383
-
1384
- radio {
1385
- text 'Male'
1386
- selection <=> [person, :male]
1387
- }
1388
-
1389
- radio {
1390
- text 'Female'
1391
- selection <=> [person, :female]
1392
- }
1393
- }
1394
-
1395
- group {
1396
- row_layout
1397
-
1398
- text 'Age Group'
1399
- font style: :bold
1400
-
1401
- radio {
1402
- text 'Child'
1403
- selection bind(person, :child)
1404
- }
1405
-
1406
- radio {
1407
- text 'Teen'
1408
- selection bind(person, :teen)
1409
- }
1410
-
1411
- radio {
1412
- text 'Adult'
1413
- selection bind(person, :adult)
1414
- }
1415
-
1416
- radio {
1417
- text 'Senior'
1418
- selection bind(person, :senior)
1419
- }
1420
- }
1421
-
1422
- button {
1423
- text 'Reset'
1424
-
1425
- on_widget_selected do
1426
- person.reset
1427
- end
1428
- }
1429
- }.open
1430
- end
1431
- end
1432
-
1433
- HelloGroup.new.launch
1434
- ```
1435
1190
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1436
1191
 
1437
1192
  ![Glimmer DSL for SWT Hello Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-group.png)
@@ -1451,81 +1206,15 @@ You should see "Hello, Group!"
1451
1206
 
1452
1207
  #### Hello, Checkbox!
1453
1208
 
1209
+ This is the low level way of using `checkbox`
1210
+
1454
1211
  Add the following require statement to `app/assets/javascripts/application.rb`
1455
1212
 
1456
1213
  ```ruby
1457
1214
  require 'glimmer-dsl-opal/samples/hello/hello_checkbox'
1458
1215
  ```
1459
1216
 
1460
- Or add the Glimmer code directly if you prefer to play around with it:
1461
-
1462
- ```ruby
1463
- class HelloCheckbox
1464
- class Person
1465
- attr_accessor :skiing, :snowboarding, :snowmobiling, :snowshoeing
1466
-
1467
- def initialize
1468
- reset_activities
1469
- end
1470
-
1471
- def reset_activities
1472
- self.skiing = false
1473
- self.snowboarding = true
1474
- self.snowmobiling = false
1475
- self.snowshoeing = false
1476
- end
1477
- end
1478
-
1479
- include Glimmer
1480
-
1481
- def launch
1482
- person = Person.new
1483
-
1484
- shell {
1485
- text 'Hello, Checkbox!'
1486
- row_layout :vertical
1487
-
1488
- label {
1489
- text 'Check all snow activities you are interested in:'
1490
- font style: :bold
1491
- }
1492
-
1493
- composite {
1494
- checkbox {
1495
- text 'Skiing'
1496
- selection bind(person, :skiing)
1497
- }
1498
-
1499
- checkbox {
1500
- text 'Snowboarding'
1501
- selection bind(person, :snowboarding)
1502
- }
1503
-
1504
- checkbox {
1505
- text 'Snowmobiling'
1506
- selection bind(person, :snowmobiling)
1507
- }
1508
-
1509
- checkbox {
1510
- text 'Snowshoeing'
1511
- selection bind(person, :snowshoeing)
1512
- }
1513
- }
1514
-
1515
- button {
1516
- text 'Reset Activities'
1517
-
1518
- on_widget_selected do
1519
- person.reset_activities
1520
- end
1521
- }
1522
- }.open
1523
- end
1524
- end
1525
-
1526
- HelloCheckbox.new.launch
1527
- ```
1528
- Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1217
+ Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1529
1218
 
1530
1219
  ![Glimmer DSL for SWT Hello Checkbox](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-checkbox.png)
1531
1220
 
@@ -1544,6 +1233,8 @@ You should see "Hello, Checkbox!"
1544
1233
 
1545
1234
  #### Hello, Checkbox Group!
1546
1235
 
1236
+ `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.
1237
+
1547
1238
  Add the following require statement to `app/assets/javascripts/application.rb`
1548
1239
 
1549
1240
  ```ruby
@@ -1570,11 +1261,13 @@ class HelloCheckboxGroup
1570
1261
  end
1571
1262
  end
1572
1263
 
1573
- include Glimmer
1264
+ include Glimmer::UI::CustomShell
1574
1265
 
1575
- def launch
1576
- person = Person.new
1577
-
1266
+ before_body {
1267
+ @person = Person.new
1268
+ }
1269
+
1270
+ body {
1578
1271
  shell {
1579
1272
  text 'Hello, Checkbox Group!'
1580
1273
  row_layout :vertical
@@ -1585,21 +1278,21 @@ class HelloCheckboxGroup
1585
1278
  }
1586
1279
 
1587
1280
  checkbox_group {
1588
- selection <=> [person, :activities]
1281
+ selection <=> [@person, :activities]
1589
1282
  }
1590
1283
 
1591
1284
  button {
1592
1285
  text 'Reset Activities'
1593
1286
 
1594
1287
  on_widget_selected do
1595
- person.reset_activities
1288
+ @person.reset_activities
1596
1289
  end
1597
1290
  }
1598
- }.open
1599
- end
1291
+ }
1292
+ }
1600
1293
  end
1601
1294
 
1602
- HelloCheckboxGroup.new.launch
1295
+ HelloCheckboxGroup.launch
1603
1296
  ```
1604
1297
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1605
1298
 
@@ -1626,53 +1319,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
1626
1319
  require 'glimmer-dsl-opal/samples/hello/hello_date_time'
1627
1320
  ```
1628
1321
 
1629
- Or add the Glimmer code directly if you prefer to play around with it:
1630
-
1631
- ```ruby
1632
- class HelloDateTime
1633
- class Person
1634
- attr_accessor :date_of_birth
1635
- end
1636
-
1637
- include Glimmer
1638
-
1639
- def launch
1640
- person = Person.new
1641
- person.date_of_birth = DateTime.new(2013, 7, 12, 18, 37, 23)
1642
-
1643
- shell {
1644
- row_layout :vertical
1645
-
1646
- text 'Hello, Date Time!'
1647
- minimum_size 180, 180
1648
-
1649
- label {
1650
- text 'Date of Birth'
1651
- font height: 16, style: :bold
1652
- }
1653
-
1654
- date { # alias for date_time(:date)
1655
- date_time bind(person, :date_of_birth)
1656
- }
1657
-
1658
- date_drop_down { # alias for date_time(:date, :drop_down)
1659
- date_time bind(person, :date_of_birth)
1660
- }
1661
-
1662
- time { # alias for date_time(:time)
1663
- date_time bind(person, :date_of_birth)
1664
- }
1665
-
1666
- calendar { # alias for date_time(:calendar)
1667
- date_time bind(person, :date_of_birth)
1668
- }
1669
- }.open
1670
- end
1671
- end
1672
-
1673
- HelloDateTime.new.launch
1674
- ```
1675
-
1676
1322
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1677
1323
 
1678
1324
  ![Glimmer DSL for SWT Hello Checkbox Group](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-date-time.png)
@@ -1700,263 +1346,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
1700
1346
  require 'glimmer-dsl-opal/samples/hello/hello_table'
1701
1347
  ```
1702
1348
 
1703
- Or add the Glimmer code directly if you prefer to play around with it:
1704
-
1705
- ```ruby
1706
- class HelloTable
1707
- class BaseballGame
1708
- class << self
1709
- attr_accessor :selected_game
1710
-
1711
- def all_playoff_games
1712
- @all_playoff_games ||= {
1713
- 'NLDS' => [
1714
- new(Time.new(2037, 10, 6, 12, 0), 'Chicago Cubs', 'Milwaukee Brewers', 'Free Bobblehead'),
1715
- new(Time.new(2037, 10, 7, 12, 0), 'Chicago Cubs', 'Milwaukee Brewers'),
1716
- new(Time.new(2037, 10, 8, 12, 0), 'Milwaukee Brewers', 'Chicago Cubs'),
1717
- new(Time.new(2037, 10, 9, 12, 0), 'Milwaukee Brewers', 'Chicago Cubs'),
1718
- new(Time.new(2037, 10, 10, 12, 0), 'Milwaukee Brewers', 'Chicago Cubs', 'Free Umbrella'),
1719
- new(Time.new(2037, 10, 6, 18, 0), 'Cincinnati Reds', 'St Louis Cardinals', 'Free Bobblehead'),
1720
- new(Time.new(2037, 10, 7, 18, 0), 'Cincinnati Reds', 'St Louis Cardinals'),
1721
- new(Time.new(2037, 10, 8, 18, 0), 'St Louis Cardinals', 'Cincinnati Reds'),
1722
- new(Time.new(2037, 10, 9, 18, 0), 'St Louis Cardinals', 'Cincinnati Reds'),
1723
- new(Time.new(2037, 10, 10, 18, 0), 'St Louis Cardinals', 'Cincinnati Reds', 'Free Umbrella'),
1724
- ],
1725
- 'ALDS' => [
1726
- new(Time.new(2037, 10, 6, 12, 0), 'New York Yankees', 'Boston Red Sox', 'Free Bobblehead'),
1727
- new(Time.new(2037, 10, 7, 12, 0), 'New York Yankees', 'Boston Red Sox'),
1728
- new(Time.new(2037, 10, 8, 12, 0), 'Boston Red Sox', 'New York Yankees'),
1729
- new(Time.new(2037, 10, 9, 12, 0), 'Boston Red Sox', 'New York Yankees'),
1730
- new(Time.new(2037, 10, 10, 12, 0), 'Boston Red Sox', 'New York Yankees', 'Free Umbrella'),
1731
- new(Time.new(2037, 10, 6, 18, 0), 'Houston Astros', 'Cleveland Indians', 'Free Bobblehead'),
1732
- new(Time.new(2037, 10, 7, 18, 0), 'Houston Astros', 'Cleveland Indians'),
1733
- new(Time.new(2037, 10, 8, 18, 0), 'Cleveland Indians', 'Houston Astros'),
1734
- new(Time.new(2037, 10, 9, 18, 0), 'Cleveland Indians', 'Houston Astros'),
1735
- new(Time.new(2037, 10, 10, 18, 0), 'Cleveland Indians', 'Houston Astros', 'Free Umbrella'),
1736
- ],
1737
- 'NLCS' => [
1738
- new(Time.new(2037, 10, 12, 12, 0), 'Chicago Cubs', 'Cincinnati Reds', 'Free Towel'),
1739
- new(Time.new(2037, 10, 13, 12, 0), 'Chicago Cubs', 'Cincinnati Reds'),
1740
- new(Time.new(2037, 10, 14, 12, 0), 'Cincinnati Reds', 'Chicago Cubs'),
1741
- new(Time.new(2037, 10, 15, 18, 0), 'Cincinnati Reds', 'Chicago Cubs'),
1742
- new(Time.new(2037, 10, 16, 18, 0), 'Cincinnati Reds', 'Chicago Cubs'),
1743
- new(Time.new(2037, 10, 17, 18, 0), 'Chicago Cubs', 'Cincinnati Reds'),
1744
- new(Time.new(2037, 10, 18, 12, 0), 'Chicago Cubs', 'Cincinnati Reds', 'Free Poncho'),
1745
- ],
1746
- 'ALCS' => [
1747
- new(Time.new(2037, 10, 12, 12, 0), 'Houston Astros', 'Boston Red Sox', 'Free Towel'),
1748
- new(Time.new(2037, 10, 13, 12, 0), 'Houston Astros', 'Boston Red Sox'),
1749
- new(Time.new(2037, 10, 14, 12, 0), 'Boston Red Sox', 'Houston Astros'),
1750
- new(Time.new(2037, 10, 15, 18, 0), 'Boston Red Sox', 'Houston Astros'),
1751
- new(Time.new(2037, 10, 16, 18, 0), 'Boston Red Sox', 'Houston Astros'),
1752
- new(Time.new(2037, 10, 17, 18, 0), 'Houston Astros', 'Boston Red Sox'),
1753
- new(Time.new(2037, 10, 18, 12, 0), 'Houston Astros', 'Boston Red Sox', 'Free Poncho'),
1754
- ],
1755
- 'World Series' => [
1756
- new(Time.new(2037, 10, 20, 18, 0), 'Chicago Cubs', 'Boston Red Sox', 'Free Baseball Cap'),
1757
- new(Time.new(2037, 10, 21, 18, 0), 'Chicago Cubs', 'Boston Red Sox'),
1758
- new(Time.new(2037, 10, 22, 18, 0), 'Boston Red Sox', 'Chicago Cubs'),
1759
- new(Time.new(2037, 10, 23, 18, 0), 'Boston Red Sox', 'Chicago Cubs'),
1760
- new(Time.new(2037, 10, 24, 18, 0), 'Boston Red Sox', 'Chicago Cubs'),
1761
- new(Time.new(2037, 10, 25, 18, 0), 'Chicago Cubs', 'Boston Red Sox'),
1762
- new(Time.new(2037, 10, 26, 18, 0), 'Chicago Cubs', 'Boston Red Sox', 'Free World Series Polo'),
1763
- ]
1764
- }
1765
- end
1766
-
1767
- def playoff_type
1768
- @playoff_type ||= 'World Series'
1769
- end
1770
-
1771
- def playoff_type=(new_playoff_type)
1772
- @playoff_type = new_playoff_type
1773
- self.schedule=(all_playoff_games[@playoff_type])
1774
- end
1775
-
1776
- def playoff_type_options
1777
- all_playoff_games.keys
1778
- end
1779
-
1780
- def schedule
1781
- @schedule ||= all_playoff_games[playoff_type]
1782
- end
1783
-
1784
- def schedule=(new_schedule)
1785
- @schedule = new_schedule
1786
- end
1787
- end
1788
-
1789
- include Glimmer
1790
- include Glimmer::DataBinding::ObservableModel
1791
-
1792
- TEAM_BALLPARKS = {
1793
- 'Boston Red Sox' => 'Fenway Park',
1794
- 'Chicago Cubs' => 'Wrigley Field',
1795
- 'Cincinnati Reds' => 'Great American Ball Park',
1796
- 'Cleveland Indians' => 'Progressive Field',
1797
- 'Houston Astros' => 'Minute Maid Park',
1798
- 'Milwaukee Brewers' => 'Miller Park',
1799
- 'New York Yankees' => 'Yankee Stadium',
1800
- 'St Louis Cardinals' => 'Busch Stadium',
1801
- }
1802
-
1803
- attr_accessor :date_time, :home_team, :away_team, :ballpark, :promotion
1804
-
1805
- def initialize(date_time, home_team, away_team, promotion = 'N/A')
1806
- self.date_time = date_time
1807
- self.home_team = home_team
1808
- self.away_team = away_team
1809
- self.promotion = promotion
1810
- observe(self, :date_time) do |new_value|
1811
- notify_observers(:game_date)
1812
- notify_observers(:game_time)
1813
- end
1814
- end
1815
-
1816
- def home_team=(home_team_value)
1817
- if home_team_value != away_team
1818
- @home_team = home_team_value
1819
- self.ballpark = TEAM_BALLPARKS[@home_team]
1820
- end
1821
- end
1822
-
1823
- def away_team=(away_team_value)
1824
- if away_team_value != home_team
1825
- @away_team = away_team_value
1826
- end
1827
- end
1828
-
1829
- def date
1830
- Date.new(date_time.year, date_time.month, date_time.day)
1831
- end
1832
-
1833
- def time
1834
- Time.new(0, 1, 1, date_time.hour, date_time.min, date_time.sec, '+00:00')
1835
- end
1836
-
1837
- def game_date
1838
- date_time.strftime("%m/%d/%Y")
1839
- end
1840
-
1841
- def game_time
1842
- date_time.strftime("%I:%M %p")
1843
- end
1844
-
1845
- def home_team_options
1846
- TEAM_BALLPARKS.keys
1847
- end
1848
-
1849
- def away_team_options
1850
- TEAM_BALLPARKS.keys
1851
- end
1852
-
1853
- def ballpark_options
1854
- [TEAM_BALLPARKS[@home_team], TEAM_BALLPARKS[@away_team]]
1855
- end
1856
-
1857
- def to_s
1858
- "#{home_team} vs #{away_team} at #{ballpark} on #{game_date} #{game_time}"
1859
- end
1860
-
1861
- def book!
1862
- "Thank you for booking #{to_s}"
1863
- end
1864
- end
1865
-
1866
- include Glimmer
1867
-
1868
- def launch
1869
- shell {
1870
- grid_layout
1871
-
1872
- text 'Hello, Table!'
1873
-
1874
- label {
1875
- layout_data :center, :center, true, false
1876
-
1877
- text 'Baseball Playoff Schedule'
1878
- font height: 30, style: :bold
1879
- }
1880
-
1881
- combo(:read_only) {
1882
- layout_data :center, :center, true, false
1883
- selection bind(BaseballGame, :playoff_type)
1884
- font height: 16
1885
- }
1886
-
1887
- table(:editable) { |table_proxy|
1888
- layout_data :fill, :fill, true, true
1889
-
1890
- table_column {
1891
- text 'Game Date'
1892
- width 150
1893
- sort_property :date # ensure sorting by real date value (not `game_date` string specified in items below)
1894
- editor :date_drop_down, property: :date_time
1895
- }
1896
- table_column {
1897
- text 'Game Time'
1898
- width 150
1899
- sort_property :time # ensure sorting by real time value (not `game_time` string specified in items below)
1900
- editor :time, property: :date_time
1901
- }
1902
- table_column {
1903
- text 'Ballpark'
1904
- width 180
1905
- editor :none
1906
- }
1907
- table_column {
1908
- text 'Home Team'
1909
- width 150
1910
- editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
1911
- }
1912
- table_column {
1913
- text 'Away Team'
1914
- width 150
1915
- editor :combo, :read_only # read_only is simply an SWT style passed to combo widget
1916
- }
1917
- table_column {
1918
- text 'Promotion'
1919
- width 150
1920
- # default text editor is used here
1921
- }
1922
-
1923
- # Data-bind table items (rows) to a model collection property, specifying column properties ordering per nested model
1924
- items bind(BaseballGame, :schedule), column_properties(:game_date, :game_time, :ballpark, :home_team, :away_team, :promotion)
1925
-
1926
- # Data-bind table selection
1927
- selection bind(BaseballGame, :selected_game)
1928
-
1929
- # Default initial sort property
1930
- sort_property :date
1931
-
1932
- # Sort by these additional properties after handling sort by the column the user clicked
1933
- additional_sort_properties :date, :time, :home_team, :away_team, :ballpark, :promotion
1934
- }
1935
-
1936
- button {
1937
- text 'Book Selected Game'
1938
- layout_data :center, :center, true, false
1939
- font height: 16
1940
- enabled bind(BaseballGame, :selected_game)
1941
-
1942
- on_widget_selected {
1943
- book_selected_game
1944
- }
1945
- }
1946
- }.open
1947
- end
1948
-
1949
- def book_selected_game
1950
- message_box {
1951
- text 'Baseball Game Booked!'
1952
- message BaseballGame.selected_game.book!
1953
- }.open
1954
- end
1955
- end
1956
-
1957
- HelloTable.new.launch
1958
- ```
1959
-
1960
1349
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
1961
1350
 
1962
1351
  ![Glimmer DSL for SWT Hello Table](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-hello-table.png)
@@ -2051,30 +1440,30 @@ Or add the Glimmer code directly if you prefer to play around with it:
2051
1440
 
2052
1441
  ```ruby
2053
1442
  class HelloButton
2054
- include Glimmer
1443
+ include Glimmer::UI::CustomShell
2055
1444
 
2056
1445
  attr_accessor :count
2057
1446
 
2058
- def initialize
1447
+ before_body {
2059
1448
  @count = 0
2060
- end
1449
+ }
2061
1450
 
2062
- def launch
1451
+ body {
2063
1452
  shell {
2064
1453
  text 'Hello, Button!'
2065
1454
 
2066
1455
  button {
2067
- text bind(self, :count) {|value| "Click To Increment: #{value} "}
1456
+ text <= [self, :count, on_read: ->(value) { "Click To Increment: #{value} " }]
2068
1457
 
2069
1458
  on_widget_selected {
2070
1459
  self.count += 1
2071
1460
  }
2072
1461
  }
2073
- }.open
2074
- end
1462
+ }
1463
+ }
2075
1464
  end
2076
1465
 
2077
- HelloButton.new.launch
1466
+ HelloButton.launch
2078
1467
  ```
2079
1468
 
2080
1469
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
@@ -2694,19 +2083,24 @@ class LoginPresenter
2694
2083
  end
2695
2084
 
2696
2085
  class Login
2697
- include Glimmer
2086
+ include Glimmer::UI::CustomShell
2087
+
2088
+ before_body {
2089
+ @presenter = LoginPresenter.new
2090
+ }
2698
2091
 
2699
- def launch
2700
- presenter = LoginPresenter.new
2701
- @shell = shell {
2092
+ body {
2093
+ shell {
2702
2094
  text "Login"
2095
+
2703
2096
  composite {
2704
2097
  grid_layout 2, false #two columns with differing widths
2705
2098
 
2706
2099
  label { text "Username:" } # goes in column 1
2707
2100
  @user_name_text = text { # goes in column 2
2708
- text bind(presenter, :user_name)
2709
- enabled bind(presenter, :logged_out)
2101
+ text <=> [@presenter, :user_name]
2102
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
2103
+
2710
2104
  on_key_pressed { |event|
2711
2105
  @password_text.set_focus if event.keyCode == swt(:cr)
2712
2106
  }
@@ -2714,43 +2108,47 @@ class Login
2714
2108
 
2715
2109
  label { text "Password:" }
2716
2110
  @password_text = text(:password, :border) {
2717
- text bind(presenter, :password)
2718
- enabled bind(presenter, :logged_out)
2111
+ text <=> [@presenter, :password]
2112
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
2113
+
2719
2114
  on_key_pressed { |event|
2720
- presenter.login if event.keyCode == swt(:cr)
2115
+ @presenter.login! if event.keyCode == swt(:cr)
2721
2116
  }
2722
2117
  }
2723
2118
 
2724
2119
  label { text "Status:" }
2725
- label { text bind(presenter, :status) }
2120
+ label { text <= [@presenter, :status] }
2726
2121
 
2727
2122
  button {
2728
2123
  text "Login"
2729
- enabled bind(presenter, :logged_out)
2730
- on_widget_selected { presenter.login }
2124
+ enabled <= [@presenter, :logged_out?, computed_by: :status]
2125
+
2126
+ on_widget_selected { @presenter.login! }
2731
2127
  on_key_pressed { |event|
2732
- presenter.login if event.keyCode == swt(:cr)
2128
+ if event.keyCode == swt(:cr)
2129
+ @presenter.login!
2130
+ end
2733
2131
  }
2734
2132
  }
2735
2133
 
2736
2134
  button {
2737
2135
  text "Logout"
2738
- enabled bind(presenter, :logged_in)
2739
- on_widget_selected { presenter.logout }
2136
+ enabled <= [@presenter, :logged_in?, computed_by: :status]
2137
+
2138
+ on_widget_selected { @presenter.logout! }
2740
2139
  on_key_pressed { |event|
2741
2140
  if event.keyCode == swt(:cr)
2742
- presenter.logout
2141
+ @presenter.logout!
2743
2142
  @user_name_text.set_focus
2744
2143
  end
2745
2144
  }
2746
2145
  }
2747
2146
  }
2748
2147
  }
2749
- @shell.open
2750
- end
2148
+ }
2751
2149
  end
2752
2150
 
2753
- Login.new.launch
2151
+ Login.launch
2754
2152
  ```
2755
2153
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
2756
2154
 
@@ -2781,214 +2179,7 @@ Add the following require statement to `app/assets/javascripts/application.rb`
2781
2179
  require 'glimmer-dsl-opal/samples/elaborate/tic_tac_toe'
2782
2180
  ```
2783
2181
 
2784
- Or add the Glimmer code directly if you prefer to play around with it:
2785
-
2786
2182
  ```ruby
2787
- class TicTacToe
2788
- class Cell
2789
- EMPTY = ""
2790
- attr_accessor :sign, :empty
2791
-
2792
- def initialize
2793
- reset
2794
- end
2795
-
2796
- def mark(sign)
2797
- self.sign = sign
2798
- end
2799
-
2800
- def reset
2801
- self.sign = EMPTY
2802
- end
2803
-
2804
- def sign=(sign_symbol)
2805
- @sign = sign_symbol
2806
- self.empty = sign == EMPTY
2807
- end
2808
-
2809
- def marked
2810
- !empty
2811
- end
2812
- end
2813
- end
2814
-
2815
- class TicTacToe
2816
- class Board
2817
- DRAW = :draw
2818
- IN_PROGRESS = :in_progress
2819
- WIN = :win
2820
- attr :winning_sign
2821
- attr_accessor :game_status
2822
-
2823
- def initialize
2824
- @sign_state_machine = {nil => "X", "X" => "O", "O" => "X"}
2825
- build_grid
2826
- @winning_sign = Cell::EMPTY
2827
- @game_status = IN_PROGRESS
2828
- end
2829
-
2830
- #row and column numbers are 1-based
2831
- def mark(row, column)
2832
- self[row, column].mark(current_sign)
2833
- game_over? #updates winning sign
2834
- end
2835
-
2836
- def current_sign
2837
- @current_sign = @sign_state_machine[@current_sign]
2838
- end
2839
-
2840
- def [](row, column)
2841
- @grid[row-1][column-1]
2842
- end
2843
-
2844
- def game_over?
2845
- win? or draw?
2846
- end
2847
-
2848
- def win?
2849
- win = (row_win? or column_win? or diagonal_win?)
2850
- self.game_status=WIN if win
2851
- win
2852
- end
2853
-
2854
- def reset
2855
- (1..3).each do |row|
2856
- (1..3).each do |column|
2857
- self[row, column].reset
2858
- end
2859
- end
2860
- @winning_sign = Cell::EMPTY
2861
- @current_sign = nil
2862
- self.game_status=IN_PROGRESS
2863
- end
2864
-
2865
- private
2866
-
2867
- def build_grid
2868
- @grid = []
2869
- 3.times do |row_index| #0-based
2870
- @grid << []
2871
- 3.times { @grid[row_index] << Cell.new }
2872
- end
2873
- end
2874
-
2875
- def row_win?
2876
- (1..3).each do |row|
2877
- if row_has_same_sign(row)
2878
- @winning_sign = self[row, 1].sign
2879
- return true
2880
- end
2881
- end
2882
- false
2883
- end
2884
-
2885
- def column_win?
2886
- (1..3).each do |column|
2887
- if column_has_same_sign(column)
2888
- @winning_sign = self[1, column].sign
2889
- return true
2890
- end
2891
- end
2892
- false
2893
- end
2894
-
2895
- #needs refactoring if we ever decide to make the board size dynamic
2896
- def diagonal_win?
2897
- if (self[1, 1].sign == self[2, 2].sign) and (self[2, 2].sign == self[3, 3].sign) and self[1, 1].marked
2898
- @winning_sign = self[1, 1].sign
2899
- return true
2900
- end
2901
- if (self[3, 1].sign == self[2, 2].sign) and (self[2, 2].sign == self[1, 3].sign) and self[3, 1].marked
2902
- @winning_sign = self[3, 1].sign
2903
- return true
2904
- end
2905
- false
2906
- end
2907
-
2908
- def draw?
2909
- @board_full = true
2910
- 3.times do |x|
2911
- 3.times do |y|
2912
- @board_full = false if self[x, y].empty
2913
- end
2914
- end
2915
- self.game_status = DRAW if @board_full
2916
- @board_full
2917
- end
2918
-
2919
- def row_has_same_sign(number)
2920
- row_sign = self[number, 1].sign
2921
- [2, 3].each do |column|
2922
- return false unless row_sign == (self[number, column].sign)
2923
- end
2924
- true if self[number, 1].marked
2925
- end
2926
-
2927
- def column_has_same_sign(number)
2928
- column_sign = self[1, number].sign
2929
- [2, 3].each do |row|
2930
- return false unless column_sign == (self[row, number].sign)
2931
- end
2932
- true if self[1, number].marked
2933
- end
2934
-
2935
- end
2936
- end
2937
-
2938
- class TicTacToe
2939
- include Glimmer
2940
-
2941
- def initialize
2942
- @tic_tac_toe_board = Board.new
2943
- @shell = shell {
2944
- text "Tic-Tac-Toe"
2945
- minimum_size 150, 178
2946
- composite {
2947
- grid_layout 3, true
2948
- (1..3).each { |row|
2949
- (1..3).each { |column|
2950
- button {
2951
- layout_data :fill, :fill, true, true
2952
- text bind(@tic_tac_toe_board[row, column], :sign)
2953
- enabled bind(@tic_tac_toe_board[row, column], :empty)
2954
- font style: :bold, height: 20
2955
- on_widget_selected {
2956
- @tic_tac_toe_board.mark(row, column)
2957
- }
2958
- }
2959
- }
2960
- }
2961
- }
2962
- }
2963
- observe(@tic_tac_toe_board, :game_status) { |game_status|
2964
- display_win_message if game_status == Board::WIN
2965
- display_draw_message if game_status == Board::DRAW
2966
- }
2967
- end
2968
-
2969
- def display_win_message
2970
- display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
2971
- end
2972
-
2973
- def display_draw_message
2974
- display_game_over_message("Draw!")
2975
- end
2976
-
2977
- def display_game_over_message(message_text)
2978
- message_box(@shell) {
2979
- text 'Game Over'
2980
- message message_text
2981
- }.open
2982
- @tic_tac_toe_board.reset
2983
- end
2984
-
2985
- def open
2986
- @shell.open
2987
- end
2988
- end
2989
-
2990
- TicTacToe.new.open
2991
- ```
2992
2183
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
2993
2184
 
2994
2185
  ![Glimmer DSL for SWT Tic Tac Toe](https://github.com/AndyObtiva/glimmer-dsl-swt/raw/master/images/glimmer-tic-tac-toe.png)
@@ -3018,321 +2209,6 @@ Add the following require statement to `app/assets/javascripts/application.rb`
3018
2209
  require 'glimmer-dsl-opal/samples/elaborate/contact_manager'
3019
2210
  ```
3020
2211
 
3021
- Or add the Glimmer code directly if you prefer to play around with it:
3022
-
3023
- ```ruby
3024
- class ContactManager
3025
- class Contact
3026
- attr_accessor :first_name, :last_name, :email
3027
-
3028
- def initialize(attribute_map)
3029
- @first_name = attribute_map[:first_name]
3030
- @last_name = attribute_map[:last_name]
3031
- @email = attribute_map[:email]
3032
- end
3033
- end
3034
- end
3035
-
3036
- class ContactManager
3037
- class ContactRepository
3038
- NAMES_FIRST = %w[
3039
- Liam
3040
- Noah
3041
- William
3042
- James
3043
- Oliver
3044
- Benjamin
3045
- Elijah
3046
- Lucas
3047
- Mason
3048
- Logan
3049
- Alexander
3050
- Ethan
3051
- Jacob
3052
- Michael
3053
- Daniel
3054
- Henry
3055
- Jackson
3056
- Sebastian
3057
- Aiden
3058
- Matthew
3059
- Samuel
3060
- David
3061
- Joseph
3062
- Carter
3063
- Owen
3064
- Wyatt
3065
- John
3066
- Jack
3067
- Luke
3068
- Jayden
3069
- Dylan
3070
- Grayson
3071
- Levi
3072
- Isaac
3073
- Gabriel
3074
- Julian
3075
- Mateo
3076
- Anthony
3077
- Jaxon
3078
- Lincoln
3079
- Joshua
3080
- Christopher
3081
- Andrew
3082
- Theodore
3083
- Caleb
3084
- Ryan
3085
- Asher
3086
- Nathan
3087
- Thomas
3088
- Leo
3089
- Isaiah
3090
- Charles
3091
- Josiah
3092
- Hudson
3093
- Christian
3094
- Hunter
3095
- Connor
3096
- Eli
3097
- Ezra
3098
- Aaron
3099
- Landon
3100
- Adrian
3101
- Jonathan
3102
- Nolan
3103
- Jeremiah
3104
- Easton
3105
- Elias
3106
- Colton
3107
- Cameron
3108
- Carson
3109
- Robert
3110
- Angel
3111
- Maverick
3112
- Nicholas
3113
- Dominic
3114
- Jaxson
3115
- Greyson
3116
- Adam
3117
- Ian
3118
- Austin
3119
- Santiago
3120
- Jordan
3121
- Cooper
3122
- Brayden
3123
- Roman
3124
- Evan
3125
- Ezekiel
3126
- Xaviar
3127
- Jose
3128
- Jace
3129
- Jameson
3130
- Leonardo
3131
- Axel
3132
- Everett
3133
- Kayden
3134
- Miles
3135
- Sawyer
3136
- Jason
3137
- Emma
3138
- Olivia
3139
- ]
3140
- NAMES_LAST = %w[
3141
- Smith
3142
- Johnson
3143
- Williams
3144
- Brown
3145
- Jones
3146
- Miller
3147
- Davis
3148
- Wilson
3149
- Anderson
3150
- Taylor
3151
- ]
3152
- def initialize(contacts = nil)
3153
- @contacts = contacts || 100.times.map do |n|
3154
- random_first_name_index = (rand*NAMES_FIRST.size).to_i
3155
- random_last_name_index = (rand*NAMES_LAST.size).to_i
3156
- first_name = NAMES_FIRST[random_first_name_index]
3157
- last_name = NAMES_LAST[random_last_name_index]
3158
- email = "#{first_name}@#{last_name}.com".downcase
3159
- Contact.new(
3160
- first_name: first_name,
3161
- last_name: last_name,
3162
- email: email
3163
- )
3164
- end
3165
- end
3166
-
3167
- def find(attribute_filter_map)
3168
- @contacts.find_all do |contact|
3169
- match = true
3170
- attribute_filter_map.keys.each do |attribute_name|
3171
- contact_value = contact.send(attribute_name).downcase
3172
- filter_value = attribute_filter_map[attribute_name].downcase
3173
- match = false unless contact_value.match(filter_value)
3174
- end
3175
- match
3176
- end
3177
- end
3178
- end
3179
- end
3180
-
3181
- class ContactManager
3182
- class ContactManagerPresenter
3183
- attr_accessor :results
3184
- @@contact_attributes = [:first_name, :last_name, :email]
3185
- @@contact_attributes.each {|attribute_name| attr_accessor attribute_name}
3186
-
3187
- def initialize(contact_repository = nil)
3188
- @contact_repository = contact_repository || ContactRepository.new
3189
- @results = []
3190
- end
3191
-
3192
- def list
3193
- self.results = @contact_repository.find({})
3194
- end
3195
-
3196
- def find
3197
- filter_map = {}
3198
- @@contact_attributes.each do |attribute_name|
3199
- filter_map[attribute_name] = self.send(attribute_name) if self.send(attribute_name)
3200
- end
3201
- self.results = @contact_repository.find(filter_map)
3202
- @sort_attribute_name = nil
3203
- @sort_direction_ascending = nil
3204
- end
3205
-
3206
- def toggle_sort(attribute_name)
3207
- @sort_attribute_name = attribute_name
3208
- @sort_direction_ascending = !@sort_direction_ascending
3209
- sorted_results = self.results.sort_by {|contact| contact.send(attribute_name).downcase}
3210
- sorted_results = sorted_results.reverse unless @sort_direction_ascending
3211
- self.results = sorted_results
3212
- end
3213
- end
3214
- end
3215
-
3216
- class ContactManager
3217
- include Glimmer
3218
-
3219
- def initialize
3220
- @contact_manager_presenter = ContactManagerPresenter.new
3221
- @contact_manager_presenter.list
3222
- end
3223
-
3224
- def launch
3225
- shell {
3226
- text "Contact Manager"
3227
- composite {
3228
- group {
3229
- grid_layout(2, false) {
3230
- margin_width 0
3231
- margin_height 0
3232
- }
3233
- layout_data :fill, :center, true, false
3234
- text 'Lookup Contacts'
3235
- font height: 24
3236
-
3237
- label {
3238
- layout_data :right, :center, false, false
3239
- text "First &Name: "
3240
- font height: 16
3241
- }
3242
- text {
3243
- layout_data :fill, :center, true, false
3244
- text bind(@contact_manager_presenter, :first_name)
3245
- on_key_pressed {|key_event|
3246
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
3247
- }
3248
- }
3249
-
3250
- label {
3251
- layout_data :right, :center, false, false
3252
- text "&Last Name: "
3253
- font height: 16
3254
- }
3255
- text {
3256
- layout_data :fill, :center, true, false
3257
- text bind(@contact_manager_presenter, :last_name)
3258
- on_key_pressed {|key_event|
3259
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
3260
- }
3261
- }
3262
-
3263
- label {
3264
- layout_data :right, :center, false, false
3265
- text "&Email: "
3266
- font height: 16
3267
- }
3268
- text {
3269
- layout_data :fill, :center, true, false
3270
- text bind(@contact_manager_presenter, :email)
3271
- on_key_pressed {|key_event|
3272
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
3273
- }
3274
- }
3275
-
3276
- composite {
3277
- row_layout {
3278
- margin_width 0
3279
- margin_height 0
3280
- }
3281
- layout_data(:right, :center, false, false) {
3282
- horizontal_span 2
3283
- }
3284
-
3285
- button {
3286
- text "&Find"
3287
- on_widget_selected { @contact_manager_presenter.find }
3288
- on_key_pressed {|key_event|
3289
- @contact_manager_presenter.find if key_event.keyCode == swt(:cr)
3290
- }
3291
- }
3292
-
3293
- button {
3294
- text "&List All"
3295
- on_widget_selected { @contact_manager_presenter.list }
3296
- on_key_pressed {|key_event|
3297
- @contact_manager_presenter.list if key_event.keyCode == swt(:cr)
3298
- }
3299
- }
3300
- }
3301
- }
3302
-
3303
- table(:multi) { |table_proxy|
3304
- layout_data {
3305
- horizontal_alignment :fill
3306
- vertical_alignment :fill
3307
- grab_excess_horizontal_space true
3308
- grab_excess_vertical_space true
3309
- height_hint 200
3310
- }
3311
- table_column {
3312
- text "First Name"
3313
- width 80
3314
- }
3315
- table_column {
3316
- text "Last Name"
3317
- width 80
3318
- }
3319
- table_column {
3320
- text "Email"
3321
- width 200
3322
- }
3323
- items bind(@contact_manager_presenter, :results),
3324
- column_properties(:first_name, :last_name, :email)
3325
- on_mouse_up { |event|
3326
- table_proxy.edit_table_item(event.table_item, event.column_index)
3327
- }
3328
- }
3329
- }
3330
- }.open
3331
- end
3332
- end
3333
-
3334
- ContactManager.new.launch
3335
- ```
3336
2212
  Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
3337
2213
 
3338
2214
  Glimmer DSL for SWT Contact Manager
@@ -3386,6 +2262,41 @@ Glimmer DSL for Opal Contact Manager Edit Done
3386
2262
 
3387
2263
  ![Glimmer DSL for Opal Contact Manager Edit Done](images/glimmer-dsl-opal-contact-manager-edit-done.png)
3388
2264
 
2265
+ #### Weather
2266
+
2267
+ Code: [lib/glimmer-dsl-opal/samples/elaborate/weather](lib/glimmer-dsl-opal/samples/elaborate/weather.rb)
2268
+
2269
+ Add the following require statement to `app/assets/javascripts/application.rb`
2270
+
2271
+ ```ruby
2272
+ require 'glimmer-dsl-opal/samples/elaborate/weather'
2273
+ ```
2274
+
2275
+ Glimmer app on the desktop (using [`glimmer-dsl-swt`](https://github.com/AndyObtiva/glimmer-dsl-swt) gem):
2276
+
2277
+ ![Weather Montreal C](https://github.com/AMaleh/glimmer-dsl-swt/raw/master/images/glimmer-weather-montreal-celsius.png)
2278
+
2279
+ ![Weather Montreal F](https://github.com/AMaleh/glimmer-dsl-swt/raw/master/images/glimmer-weather-montreal-fahrenheit.png)
2280
+
2281
+ ![Weather Atlanta F](https://github.com/AMaleh/glimmer-dsl-swt/raw/master/images/glimmer-weather-atlanta-fahrenheit.png)
2282
+
2283
+ Glimmer app on the web (using `glimmer-dsl-opal` gem):
2284
+
2285
+ Start the Rails server:
2286
+ ```
2287
+ rails s
2288
+ ```
2289
+
2290
+ Visit `http://localhost:3000`
2291
+
2292
+ You should see "Weather"
2293
+
2294
+ ![Opal Weather Montreal C](/images/glimmer-dsl-opal-weather-montreal-celsius.png)
2295
+
2296
+ ![Opal Weather Montreal F](/images/glimmer-dsl-opal-weather-montreal-fahrenheit.png)
2297
+
2298
+ ![Opal Weather Atlanta F](/images/glimmer-dsl-opal-weather-atlanta-fahrenheit.png)
2299
+
3389
2300
  ### External Samples
3390
2301
 
3391
2302
  #### Glimmer Calculator