glimmer-dsl-swt 4.21.2.5 → 4.22.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +212 -168
  3. data/README.md +15 -11
  4. data/VERSION +1 -1
  5. data/docs/reference/GLIMMER_COMMAND.md +20 -6
  6. data/docs/reference/GLIMMER_CONFIGURATION.md +14 -3
  7. data/docs/reference/GLIMMER_GIRB.md +1 -1
  8. data/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md +774 -132
  9. data/docs/reference/GLIMMER_SAMPLES.md +22 -8
  10. data/glimmer-dsl-swt.gemspec +0 -0
  11. data/lib/ext/glimmer/config.rb +41 -24
  12. data/lib/glimmer/data_binding/observable_widget.rb +6 -6
  13. data/lib/glimmer/data_binding/widget_binding.rb +4 -3
  14. data/lib/glimmer/dsl/swt/dsl.rb +1 -1
  15. data/lib/glimmer/dsl/swt/observe_expression.rb +2 -1
  16. data/lib/glimmer/dsl/swt/shape_expression.rb +1 -0
  17. data/lib/glimmer/dsl/swt/shine_data_binding_expression.rb +3 -8
  18. data/lib/glimmer/dsl/swt/sync_call_expression.rb +38 -0
  19. data/lib/glimmer/dsl/swt/transform_expression.rb +1 -1
  20. data/lib/glimmer/launcher.rb +16 -15
  21. data/lib/glimmer/rake_task/package.rb +5 -3
  22. data/lib/glimmer/rake_task/scaffold.rb +5 -16
  23. data/lib/glimmer/swt/color_proxy.rb +5 -5
  24. data/lib/glimmer/swt/custom/drawable.rb +8 -2
  25. data/lib/glimmer/swt/custom/shape/line.rb +0 -1
  26. data/lib/glimmer/swt/custom/shape/path.rb +2 -2
  27. data/lib/glimmer/swt/custom/shape/path_segment.rb +2 -2
  28. data/lib/glimmer/swt/custom/shape/point.rb +8 -1
  29. data/lib/glimmer/swt/custom/shape.rb +171 -69
  30. data/lib/glimmer/swt/display_proxy.rb +15 -10
  31. data/lib/glimmer/swt/image_proxy.rb +5 -5
  32. data/lib/glimmer/swt/message_box_proxy.rb +5 -5
  33. data/lib/glimmer/swt/shape_listener_proxy.rb +55 -0
  34. data/lib/glimmer/swt/shell_proxy.rb +2 -1
  35. data/lib/glimmer/swt/transform_proxy.rb +3 -3
  36. data/lib/glimmer/swt/tray_proxy.rb +4 -4
  37. data/lib/glimmer/swt/widget_proxy.rb +14 -10
  38. data/lib/glimmer/ui/custom_shape.rb +34 -10
  39. data/lib/glimmer/ui/custom_widget.rb +7 -10
  40. data/lib/glimmer-dsl-swt.rb +6 -2
  41. data/samples/elaborate/battleship/view/cell.rb +10 -2
  42. data/samples/elaborate/klondike_solitaire/model/column_pile.rb +0 -1
  43. data/samples/elaborate/klondike_solitaire/view/column_pile.rb +3 -16
  44. data/samples/elaborate/klondike_solitaire/view/dealing_pile.rb +1 -1
  45. data/samples/elaborate/klondike_solitaire/view/dealt_pile.rb +12 -5
  46. data/samples/elaborate/klondike_solitaire/view/empty_playing_card.rb +2 -1
  47. data/samples/elaborate/klondike_solitaire/view/foundation_pile.rb +2 -2
  48. data/samples/elaborate/klondike_solitaire/view/hidden_playing_card.rb +2 -2
  49. data/samples/elaborate/klondike_solitaire/view/klondike_solitaire_menu_bar.rb +60 -0
  50. data/samples/elaborate/klondike_solitaire/view/playing_card.rb +3 -2
  51. data/samples/elaborate/klondike_solitaire.rb +13 -55
  52. data/samples/elaborate/mandelbrot_fractal.rb +3 -1
  53. data/samples/elaborate/quarto/model/game.rb +124 -0
  54. data/samples/elaborate/quarto/model/piece/cube.rb +31 -0
  55. data/samples/elaborate/quarto/model/piece/cylinder.rb +31 -0
  56. data/samples/elaborate/quarto/model/piece.rb +70 -0
  57. data/samples/elaborate/quarto/view/available_pieces_area.rb +72 -0
  58. data/samples/elaborate/quarto/view/board.rb +65 -0
  59. data/samples/elaborate/quarto/view/cell.rb +85 -0
  60. data/samples/elaborate/quarto/view/cube.rb +73 -0
  61. data/samples/elaborate/quarto/view/cylinder.rb +72 -0
  62. data/samples/elaborate/quarto/view/message_box_panel.rb +114 -0
  63. data/samples/elaborate/quarto/view/piece.rb +56 -0
  64. data/samples/elaborate/quarto/view/selected_piece_area.rb +69 -0
  65. data/samples/elaborate/quarto.rb +188 -0
  66. data/samples/hello/hello_canvas_data_binding.rb +7 -7
  67. data/samples/hello/hello_custom_widget.rb +23 -5
  68. data/vendor/swt/linux/swt.jar +0 -0
  69. data/vendor/swt/linux_aarch64/swt.jar +0 -0
  70. data/vendor/swt/mac/swt.jar +0 -0
  71. data/vendor/swt/mac_aarch64/swt.jar +0 -0
  72. data/vendor/swt/windows/swt.jar +0 -0
  73. metadata +36 -40
  74. data/bin/glimmer_runner.rb +0 -4
@@ -69,6 +69,7 @@ This guide should help you get started with Glimmer DSL for SWT. For more advanc
69
69
  - [Class-Based Custom Widget Example](#class-based-custom-widget-example)
70
70
  - [Custom Widget Lifecycle Hooks](#custom-widget-lifecycle-hooks)
71
71
  - [Lifecycle Hooks Example](#lifecycle-hooks-example)
72
+ - [Custom Widget Listeners](#custom-widget-listeners)
72
73
  - [Custom Widget API](#custom-widget-api)
73
74
  - [Content/Options Example](#contentoptions-example)
74
75
  - [Custom Widget Gotchas](#custom-widget-gotchas)
@@ -572,6 +573,10 @@ end
572
573
 
573
574
  `sync_exec {}` is required by SWT when running GUI update from a thread other than the GUI thread. In Glimmer, it is automatically invoked for you so that you wouldn't have to worry about it. It works just like `async_exec` except it executes the block synchronously at the earliest opportunity possible, waiting for the block to be finished.
574
575
 
576
+ ##### sync_call
577
+
578
+ `sync_exec {}` is required by SWT when running GUI update from a thread other than the GUI thread. In Glimmer, it is automatically invoked for you so that you wouldn't have to worry about it. It works just like `async_exec` except it executes the block synchronously at the earliest opportunity possible, waiting for the block to be finished.
579
+
575
580
  ##### auto_exec
576
581
 
577
582
  `auto_exec(override_sync_exec:, override_async_exec) {}` only executes code block with `sync_exec` when necessary (running from a thread other than the GUI thread). It is used automatically all over the Glimmer DSL for SWT codebase, so you wouldn't need it unless you grab a direct handle on `swt_widget` from a widget proxy.
@@ -1103,6 +1108,8 @@ shell {
1103
1108
  }.open
1104
1109
  ```
1105
1110
 
1111
+ This relies on Glimmer's [Multi-DSL Support](#multi-dsl-support) for building the HTML text using [Glimmer XML DSL](https://github.com/AndyObtiva/glimmer-dsl-xml).
1112
+
1106
1113
  Learn more at the [SWT Browser widget](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/browser/Browser.html) API.
1107
1114
 
1108
1115
  ### Widget Styles
@@ -1586,7 +1593,15 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
1586
1593
 
1587
1594
  **(BETA FEATURE)**
1588
1595
 
1589
- While other GUI toolkits only offer a way to draw graphics imperatively (e.g. fill_rectangle, draw_point, etc...), Glimmer DSL for SWT breaks away from the mold by enabling software engineers to draw graphics declaratively. Simply declare all the shapes you want to see with their attributes, like background/foreground colors, and Glimmer DSL for SWT takes care of the rest, painting graphics on a blank `canvas` widget or amending/decorating an existing widget. This is accomplished through the Canvas Shape DSL, a sub-DSL of the Glimmer GUI DSL, which makes it possible to draw graphics declaratively with very understandable and maintainable syntax. Still, for the rare cases where imperative logic is needed, Glimmer DSL for SWT supports imperative painting of graphics through direct usage of SWT.
1596
+ While other GUI toolkits only offer a way to draw graphics imperatively (e.g. draw_arc, draw_rectangle, move_to, line_to, etc...), Glimmer DSL for SWT breaks away from the mold by enabling software engineers to draw graphics declaratively. Simply declare all the shapes you want to see with their attributes, like background/foreground colors, and Glimmer DSL for SWT takes care of the rest, painting graphics on a blank `canvas` widget or amending/decorating an existing widget. This is accomplished through the Canvas Shape DSL, a sub-DSL of the Glimmer GUI DSL, which makes it possible to draw graphics declaratively with very understandable and maintainable syntax. Still, for the rare cases where imperative logic is needed, Glimmer DSL for SWT supports imperative painting of graphics through direct usage of SWT.
1597
+
1598
+ ![Canvas Shape DSL Line](/images/glimmer-canvas-shape-dsl-line.png)
1599
+ ![Canvas Shape DSL Rectangle](/images/glimmer-canvas-shape-dsl-rectangle.png)
1600
+ ![Canvas Shape DSL Oval](/images/glimmer-canvas-shape-dsl-oval.png)
1601
+ ![Canvas Shape DSL Arc](/images/glimmer-canvas-shape-dsl-arc.png)
1602
+ ![Canvas Shape DSL Polyline](/images/glimmer-canvas-shape-dsl-polyline.png)
1603
+ ![Canvas Shape DSL Polygon](/images/glimmer-canvas-shape-dsl-polygon.png)
1604
+ ![Canvas Shape DSL Text](/images/glimmer-canvas-shape-dsl-text.png)
1590
1605
 
1591
1606
  `canvas` has the `:double_buffered` SWT style by default on platforms that need it (Windows & Linux) to ensure flicker-free rendering. If you need to disable it for whatever reason, just pass the `:none` SWT style instead (e.g. `canvas(:none)`)
1592
1607
 
@@ -1623,171 +1638,713 @@ Here is a list of supported attributes nestable within a block under shapes:
1623
1638
  - `foreground` sets draw color for drawable shapes (standard color symbol (e.g. `:red`), `rgb(red_integer, green_integer, blue_integer)` color, or Color/ColorProxy object directly)
1624
1639
  - `foreground_pattern` sets foreground gradient/image pattern for drawable shape lines (takes the same arguments as the SWT [Pattern](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/Pattern.html) class [e.g. `foreground_pattern 2.3, 4.2, 5.4, 7.2, :red, :blue`] / note: this feature isn't extensively tested yet)
1625
1640
  - `interpolation` sets the interpolation value (SWT style value of `:default`, `:none`, `:low`, `:high`)
1626
- - `line_cap` sets line cap (SWT style value of `:cap_flat`, `:cap_round`, or `:cap_square`)
1641
+ - `line_cap` sets line cap (SWT style value of `:flat`, `:round`, or `:square`, with aliases `:cap_flat`, `:cap_round`, and `:cap_square`)
1627
1642
  - `line_dash` line dash float values (automatically sets `line_style` to SWT style value of `:line_custom`)
1628
- - `line_join` line join style (SWT style value of `:join_miter`, `:join_round`, or `:join_bevel`)
1643
+ - `line_join` line join style (SWT style value of `:miter`, `:round`, and `:bevel`, with aliases `:join_miter`, `:join_round`, or `:join_bevel`)
1629
1644
  - `line_style` line join style (SWT style value of `:solid`, `:dash`, `:dot`, `:dashdot`, `:dashdotdot`, or `:custom` while requiring `line_dash` attribute (or alternatively with `line_` prefix as per official SWT docs like `:line_solid` for `:solid`)
1630
1645
  - `line_width` line width in integer (used in draw operations)
1631
1646
  - `text_anti_alias` enables text antialiasing (SWT style value of `:default`, `:off`, `:on` whereby `:default` applies OS default, which varies per OS)
1632
1647
  - `transform` sets transform object using [Canvas Transform DSL](#canvas-transform-dsl) syntax
1633
1648
 
1649
+ If you specify the x and y coordinates as `:default`, `nil`, or leave them out, they get calculated automatically by centering the shape within its parent `canvas`.
1650
+
1651
+ If you specify the `width` and `height` parameters as `:max`, they get calculated from the parent's size, filling the parent maximally (and they are auto-calculated on parent resize).
1652
+
1653
+ Note that you could shift a shape off its centered position within its parent `canvas` by setting `x` to `[:default, x_delta]` and `y` to `[:default, y_delta]`
1654
+
1634
1655
  Keep in mind that ordering of shapes matters as it is followed in painting. For example, it is recommended you paint filled shapes first and then drawn ones.
1635
1656
 
1636
- Example (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1657
+ Example of `line` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1637
1658
 
1638
1659
  ```ruby
1639
- include Glimmer
1660
+ require 'glimmer-dsl-swt'
1640
1661
 
1641
- # image object has to be declared outside the canvas and shell to avoid confusing with canvas image property
1642
- image_object = image(File.expand_path('./icons/scaffold_app.png'), width: 100)
1662
+ include Glimmer
1643
1663
 
1644
1664
  shell {
1645
- text 'Canvas Example'
1646
- minimum_size 320, 400
1647
-
1665
+ text 'Canvas Shape DSL'
1666
+ minimum_size 200, 220
1667
+
1648
1668
  canvas {
1649
- background :dark_yellow
1650
- rectangle(0, 0, 220, 400) {
1651
- background :dark_red
1669
+ background :white
1670
+
1671
+ line(30, 30, 170, 170) {
1672
+ foreground :red
1673
+ line_width 3
1652
1674
  }
1653
- rectangle(50, 20, 300, 150, 30, 50, round: true) {
1675
+ }
1676
+ }.open
1677
+ ```
1678
+
1679
+ ![Canvas Shape DSL Line](/images/glimmer-canvas-shape-dsl-line.png)
1680
+
1681
+ Example of `rectangle` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1682
+
1683
+ ```ruby
1684
+ require 'glimmer-dsl-swt'
1685
+
1686
+ include Glimmer
1687
+
1688
+ shell {
1689
+ text 'Canvas Shape DSL'
1690
+ minimum_size 200, 220
1691
+
1692
+ canvas {
1693
+ background :white
1694
+
1695
+ rectangle(30, 50, 140, 100) {
1654
1696
  background :yellow
1655
1697
  }
1656
- rectangle(150, 200, 100, 70, true, gradient: true) {
1657
- background :dark_red
1658
- foreground :yellow
1698
+
1699
+ rectangle(30, 50, 140, 100) {
1700
+ foreground :red
1701
+ line_width 3
1659
1702
  }
1660
- text('Glimmer', 208, 83) {
1661
- font height: 25, style: :bold
1703
+ }
1704
+ }.open
1705
+ ```
1706
+
1707
+ ![Canvas Shape DSL Rectangle](/images/glimmer-canvas-shape-dsl-rectangle.png)
1708
+
1709
+ Example of `rectangle` with round corners having 60 degree angles by default (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1710
+
1711
+ ```ruby
1712
+ require 'glimmer-dsl-swt'
1713
+
1714
+ include Glimmer
1715
+
1716
+ shell {
1717
+ text 'Canvas Shape DSL'
1718
+ minimum_size 200, 220
1719
+
1720
+ canvas {
1721
+ background :white
1722
+
1723
+ rectangle(30, 50, 140, 100, round: true) {
1724
+ background :yellow
1662
1725
  }
1663
- rectangle(200, 80, 108, 36) {
1664
- foreground rgb(0, 0, 0)
1726
+
1727
+ rectangle(30, 50, 140, 100, round: true) {
1728
+ foreground :red
1665
1729
  line_width 3
1666
1730
  }
1667
- image(image_object, 70, 50)
1668
1731
  }
1669
1732
  }.open
1670
1733
  ```
1671
1734
 
1672
- Screenshot:
1735
+ ![Canvas Shape DSL Rectangle Round](/images/glimmer-canvas-shape-dsl-rectangle-round.png)
1673
1736
 
1674
- ![Canvas Animation Example](/images/glimmer-example-canvas.png)
1737
+ Example of `rectangle` with round corners having different horizontal and vertical angles (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1675
1738
 
1676
- If you specify the x and y coordinates as `:default`, `nil`, or leave them out, they get calculated automatically by centering the shape within its parent `canvas`.
1739
+ ```ruby
1740
+ require 'glimmer-dsl-swt'
1677
1741
 
1678
- If you specify the `width` and `height` parameters as `:max`, they get calculated from the parent's size, filling the parent maximally (and they are auto-calculated on parent resize).
1742
+ include Glimmer
1679
1743
 
1680
- Note that you could shift a shape off its centered position within its parent `canvas` by setting `x` to `[:default, x_delta]` and `y` to `[:default, y_delta]`
1744
+ shell {
1745
+ text 'Canvas Shape DSL'
1746
+ minimum_size 200, 220
1747
+
1748
+ canvas {
1749
+ background :white
1750
+
1751
+ rectangle(30, 50, 140, 100, 40, 80) {
1752
+ background :yellow
1753
+ }
1754
+
1755
+ rectangle(30, 50, 140, 100, 40, 80) {
1756
+ foreground :red
1757
+ line_width 3
1758
+ }
1759
+ }
1760
+ }.open
1761
+ ```
1681
1762
 
1682
- The round and gradient options could be dropped since Glimmer DSL for SWT supports auto-inference of them based on shape parameters.
1763
+ ![Canvas Shape DSL Rectangle Round Angles](/images/glimmer-canvas-shape-dsl-rectangle-round-angles.png)
1683
1764
 
1684
- Example (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1765
+ Example of `oval` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1685
1766
 
1686
1767
  ```ruby
1687
- include Glimmer
1768
+ require 'glimmer-dsl-swt'
1688
1769
 
1689
- # image object has to be declared outside the canvas and shell to avoid confusing with canvas image property
1690
- image_object = image(File.expand_path('./icons/scaffold_app.png'), width: 100)
1770
+ include Glimmer
1691
1771
 
1692
1772
  shell {
1693
- text 'Canvas Example'
1694
- minimum_size 320, 400
1695
-
1773
+ text 'Canvas Shape DSL'
1774
+ minimum_size 200, 220
1775
+
1696
1776
  canvas {
1697
- background :dark_yellow
1698
- rectangle(0, 0, 220, 400) {
1699
- background :dark_red
1777
+ background :white
1778
+
1779
+ oval(30, 50, 140, 100) {
1780
+ background :yellow
1781
+ }
1782
+
1783
+ oval(30, 50, 140, 100) {
1784
+ foreground :red
1785
+ line_width 3
1700
1786
  }
1701
- rectangle(50, 20, 300, 150, 30, 50) {
1787
+ }
1788
+ }.open
1789
+ ```
1790
+
1791
+ ![Canvas Shape DSL Oval](/images/glimmer-canvas-shape-dsl-oval.png)
1792
+
1793
+ Example of `arc` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1794
+
1795
+ ```ruby
1796
+ require 'glimmer-dsl-swt'
1797
+
1798
+ include Glimmer
1799
+
1800
+ shell {
1801
+ text 'Canvas Shape DSL'
1802
+ minimum_size 200, 220
1803
+
1804
+ canvas {
1805
+ background :white
1806
+
1807
+ arc(30, 30, 140, 140, 0, 270) {
1702
1808
  background :yellow
1703
1809
  }
1704
- rectangle(150, 200, 100, 70, true) {
1705
- background :dark_red
1706
- foreground :yellow
1810
+
1811
+ arc(30, 30, 140, 140, 0, 270) {
1812
+ foreground :red
1813
+ line_width 3
1707
1814
  }
1708
- text('Glimmer', 208, 83) {
1709
- font height: 25, style: :bold
1815
+ }
1816
+ }.open
1817
+ ```
1818
+
1819
+ ![Canvas Shape DSL Arc](/images/glimmer-canvas-shape-dsl-arc.png)
1820
+
1821
+ Example of `polyline` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1822
+
1823
+ ```ruby
1824
+ require 'glimmer-dsl-swt'
1825
+
1826
+ include Glimmer
1827
+
1828
+ shell {
1829
+ text 'Canvas Shape DSL'
1830
+ minimum_size 200, 220
1831
+
1832
+ canvas {
1833
+ background :white
1834
+
1835
+ polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
1836
+ foreground :red
1837
+ line_width 3
1838
+ }
1839
+ }
1840
+ }.open
1841
+ ```
1842
+
1843
+ ![Canvas Shape DSL Polyline](/images/glimmer-canvas-shape-dsl-polyline.png)
1844
+
1845
+ Example of `polygon` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1846
+
1847
+ ```ruby
1848
+ require 'glimmer-dsl-swt'
1849
+
1850
+ include Glimmer
1851
+
1852
+ shell {
1853
+ text 'Canvas Shape DSL'
1854
+ minimum_size 200, 220
1855
+
1856
+ canvas {
1857
+ background :white
1858
+
1859
+ polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) {
1860
+ background :yellow
1710
1861
  }
1711
- rectangle(200, 80, 108, 36) {
1712
- foreground rgb(0, 0, 0)
1862
+
1863
+ polygon(30, 90, 80, 20, 130, 40, 170, 90, 130, 140, 80, 170, 40, 160) {
1864
+ foreground :red
1713
1865
  line_width 3
1714
1866
  }
1715
- image(image_object, 70, 50)
1716
1867
  }
1717
1868
  }.open
1718
1869
  ```
1719
1870
 
1720
- Notice how the shape declaration parameters perfectly match the method parameters in the [SWT org.eclipse.swt.graphics.GC API](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html). This is useful for developers coming to Glimmer DSL for SWT from SWT.
1871
+ ![Canvas Shape DSL Polygon](/images/glimmer-canvas-shape-dsl-polygon.png)
1721
1872
 
1722
- Of course, Glimmer DSL for SWT still supports an alternative syntax that is more declarative and consistent with the rest of the Glimmer GUI DSL syntax. This syntax in fact offers the extra-benefit of data-binding for shape parameter values (meaning you could use `bind(...)` syntax with them instead of setting values directly)
1873
+ Example of `text` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1723
1874
 
1724
- Example (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1875
+ ```ruby
1876
+ require 'glimmer-dsl-swt'
1877
+
1878
+ include Glimmer
1879
+
1880
+ shell {
1881
+ text 'Canvas Shape DSL'
1882
+ minimum_size 200, 220
1883
+
1884
+ canvas {
1885
+ background :white
1886
+
1887
+ text(" This is \n rendered text ", 30, 50) {
1888
+ background :yellow
1889
+ foreground :red
1890
+ font height: 25, style: :italic
1891
+
1892
+ rectangle { # automatically scales to match text extent
1893
+ foreground :red
1894
+ line_width 3
1895
+ }
1896
+ }
1897
+ }
1898
+ }.open
1899
+ ```
1900
+
1901
+ ![Canvas Shape DSL Text](/images/glimmer-canvas-shape-dsl-text.png)
1902
+
1903
+ Example of `image` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1725
1904
 
1726
1905
  ```ruby
1906
+ require 'glimmer-dsl-swt'
1907
+
1727
1908
  include Glimmer
1728
1909
 
1729
- # image object has to be declared outside the canvas and shell to avoid confusing with canvas image property
1730
- image_object = image(File.expand_path('./icons/scaffold_app.png'), width: 100)
1910
+ shell {
1911
+ text 'Canvas Shape DSL'
1912
+ minimum_size 512, 542
1913
+
1914
+ canvas {
1915
+ background :white
1916
+
1917
+ image(File.expand_path('icons/scaffold_app.png', __dir__), 0, 5)
1918
+ }
1919
+ }.open
1920
+ ```
1921
+
1922
+ ![Canvas Shape DSL Image](/images/glimmer-canvas-shape-dsl-image.png)
1923
+
1924
+ Example of `image` pre-built with a smaller height (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1925
+
1926
+ ```ruby
1927
+ require 'glimmer-dsl-swt'
1928
+
1929
+ include Glimmer
1930
+
1931
+ @image_object = image(File.expand_path('icons/scaffold_app.png', __dir__), height: 200)
1731
1932
 
1732
1933
  shell {
1733
- text 'Canvas Example'
1734
- minimum_size 320, 400
1934
+ text 'Canvas Shape DSL'
1935
+ minimum_size 200, 230
1936
+
1937
+ canvas {
1938
+ background :white
1939
+
1940
+ image(@image_object, 0, 5)
1941
+ }
1942
+ }.open
1943
+ ```
1944
+
1945
+ ![Canvas Shape DSL Image](/images/glimmer-canvas-shape-dsl-image-shrunk.png)
1946
+
1947
+ Example of setting `background_pattern` attribute to a horizontal gradient (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1948
+
1949
+ ```ruby
1950
+ require 'glimmer-dsl-swt'
1951
+
1952
+ include Glimmer
1735
1953
 
1954
+ shell {
1955
+ text 'Canvas Shape DSL'
1956
+ minimum_size 200, 220
1957
+
1736
1958
  canvas {
1737
- background :dark_yellow
1738
- rectangle {
1739
- x 0
1740
- y 0
1741
- width 220
1742
- height 400
1743
- background :dark_red
1959
+ background :white
1960
+
1961
+ oval(30, 30, 140, 140) {
1962
+ background_pattern 0, 0, 200, 0, rgb(255, 255, 0), rgb(255, 0, 0)
1744
1963
  }
1745
- rectangle {
1746
- x 50
1747
- y 20
1748
- width 300
1749
- height 150
1750
- arc_width 30
1751
- arc_height 50
1964
+ }
1965
+ }.open
1966
+ ```
1967
+
1968
+ ![Canvas Shape DSL Oval Background Pattern Gradient](/images/glimmer-canvas-shape-dsl-oval-background-pattern-gradient.png)
1969
+
1970
+ Example of setting `foreground_pattern` attribute to a vertical gradient (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1971
+
1972
+ ```ruby
1973
+ require 'glimmer-dsl-swt'
1974
+
1975
+ include Glimmer
1976
+
1977
+ shell {
1978
+ text 'Canvas Shape DSL'
1979
+ minimum_size 200, 220
1980
+
1981
+ canvas {
1982
+ background :white
1983
+
1984
+ oval(30, 30, 140, 140) {
1985
+ foreground_pattern 0, 0, 0, 200, :blue, :green
1986
+ line_width 10
1987
+ }
1988
+ }
1989
+ }.open
1990
+ ```
1991
+
1992
+ ![Canvas Shape DSL Oval Foreground Pattern Gradient](/images/glimmer-canvas-shape-dsl-oval-foreground-pattern-gradient.png)
1993
+
1994
+ Example of setting `line_style` attribute to `:dashdot` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1995
+
1996
+ ```ruby
1997
+ require 'glimmer-dsl-swt'
1998
+
1999
+ include Glimmer
2000
+
2001
+ shell {
2002
+ text 'Canvas Shape DSL'
2003
+ minimum_size 200, 220
2004
+
2005
+ canvas {
2006
+ background :white
2007
+
2008
+ oval(30, 50, 140, 100) {
1752
2009
  background :yellow
1753
2010
  }
1754
- rectangle {
1755
- x 150
1756
- y 200
1757
- width 100
1758
- height 70
1759
- vertical true
1760
- background :dark_red
1761
- foreground :yellow
2011
+
2012
+ oval(30, 50, 140, 100) {
2013
+ foreground :red
2014
+ line_width 3
2015
+ line_style :dashdot
1762
2016
  }
1763
- text {
1764
- string 'Glimmer'
1765
- x 208
1766
- y 83
1767
- font height: 25, style: :bold
2017
+ }
2018
+ }.open
2019
+ ```
2020
+
2021
+ ![Canvas Shape DSL Oval](/images/glimmer-canvas-shape-dsl-oval-line-style-dashdot.png)
2022
+
2023
+ Example of setting `line_width` attribute to `10`, `line_join` attribute to `:miter` (default) and `line_cap` attribute to `:flat` (default) (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2024
+
2025
+ ```ruby
2026
+ require 'glimmer-dsl-swt'
2027
+
2028
+ include Glimmer
2029
+
2030
+ shell {
2031
+ text 'Canvas Shape DSL'
2032
+ minimum_size 200, 220
2033
+
2034
+ canvas {
2035
+ background :white
2036
+
2037
+ polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
2038
+ foreground :red
2039
+ line_width 10
2040
+ line_join :miter
2041
+ line_cap :flat
1768
2042
  }
2043
+ }
2044
+ }.open
2045
+ ```
2046
+
2047
+ ![Canvas Shape DSL Polyline Line Join Miter Line Cap Flat](/images/glimmer-canvas-shape-dsl-polyline-line-join-miter-line-cap-flat.png)
2048
+
2049
+ Example of setting `line_width` attribute to `10`, `line_join` attribute to `:round` and `line_cap` attribute to `:round` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2050
+
2051
+ ```ruby
2052
+ require 'glimmer-dsl-swt'
2053
+
2054
+ include Glimmer
2055
+
2056
+ shell {
2057
+ text 'Canvas Shape DSL'
2058
+ minimum_size 200, 220
2059
+
2060
+ canvas {
2061
+ background :white
2062
+
2063
+ polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
2064
+ foreground :red
2065
+ line_width 10
2066
+ line_join :round
2067
+ line_cap :round
2068
+ }
2069
+ }
2070
+ }.open
2071
+ ```
2072
+
2073
+ ![Canvas Shape DSL Polyline Line Join Round Line Cap Round](/images/glimmer-canvas-shape-dsl-polyline-line-join-round-line-cap-round.png)
2074
+
2075
+ Example of setting `line_width` attribute to `10`, `line_join` attribute to `:bevel` and `line_cap` attribute to `:square` (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2076
+
2077
+ ```ruby
2078
+ require 'glimmer-dsl-swt'
2079
+
2080
+ include Glimmer
2081
+
2082
+ shell {
2083
+ text 'Canvas Shape DSL'
2084
+ minimum_size 200, 220
2085
+
2086
+ canvas {
2087
+ background :white
2088
+
2089
+ polyline(30, 50, 50, 170, 70, 120, 90, 150, 110, 30, 130, 100, 150, 50, 170, 135) {
2090
+ foreground :red
2091
+ line_width 10
2092
+ line_join :bevel
2093
+ line_cap :square
2094
+ }
2095
+ }
2096
+ }.open
2097
+ ```
2098
+
2099
+ ![Canvas Shape DSL Polyline Line Join Miter Line Cap Flat](/images/glimmer-canvas-shape-dsl-polyline-line-join-bevel-line-cap-square.png)
2100
+
2101
+ Shape declaration parameters perfectly match the method parameters in the [SWT org.eclipse.swt.graphics.GC API](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html). This is useful for developers coming to Glimmer DSL for SWT from SWT.
2102
+
2103
+ Glimmer DSL for SWT also supports an alternative syntax for specifying shape parameters by nesting underneath the shape instead of passing as args. This syntax in fact offers the extra-benefit of [data-binding](#data-binding) for shape parameter values (meaning you could use `<=` syntax with them instead of setting values directly)
2104
+
2105
+ Example of alternative syntax for specifying shape parameters (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2106
+
2107
+ ```ruby
2108
+ require 'glimmer-dsl-swt'
2109
+
2110
+ include Glimmer
2111
+
2112
+ shell {
2113
+ text 'Canvas Shape DSL'
2114
+ minimum_size 200, 220
2115
+
2116
+ canvas {
2117
+ background :white
2118
+
1769
2119
  rectangle {
1770
- x 200
1771
- y 80
1772
- width 108
1773
- height 36
1774
- foreground :black
1775
- line_width 3
2120
+ x 30
2121
+ y 50
2122
+ width 140
2123
+ height 100
2124
+ arc_width 40
2125
+ arc_height 80
2126
+ background :yellow
1776
2127
  }
1777
- image {
1778
- image image_object
1779
- x 70
2128
+
2129
+ rectangle {
2130
+ x 30
1780
2131
  y 50
2132
+ width 140
2133
+ height 100
2134
+ arc_width 40
2135
+ arc_height 80
2136
+ foreground :red
2137
+ line_width 3
1781
2138
  }
1782
2139
  }
1783
2140
  }.open
1784
2141
  ```
1785
2142
 
1786
- Learn more at the [Hello, Canvas! Sample](/docs/reference/GLIMMER_SAMPLES.md#hello-canvas).
2143
+ ![Canvas Shape DSL Rectangle Round Angles](/images/glimmer-canvas-shape-dsl-rectangle-round-angles.png)
1787
2144
 
1788
- If you ever have special needs or optimizations, you could always default to direct SWT painting via [org.eclipse.swt.graphics.GC](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html) instead. Learn more at the [SWT Graphics Guide](https://www.eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html) and [SWT Image Guide](https://www.eclipse.org/articles/Article-SWT-images/graphics-resources.html#Saving%20Images).
2145
+ Example of canvas shape parameter data-binding (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1789
2146
 
1790
- Example of manually doing the same things as in the previous example without relying on the declarative Glimmer Shape DSL (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2147
+ ```ruby
2148
+ require 'glimmer-dsl-swt'
2149
+
2150
+ class HelloCanvasDataBinding
2151
+ include Glimmer::GUI::CustomWindow # alias for Glimmer::UI::CustomShell
2152
+
2153
+ CANVAS_WIDTH = 300
2154
+ CANVAS_HEIGHT = 300
2155
+
2156
+ attr_accessor :x1_value, :y1_value, :x2_value, :y2_value, :foreground_red, :foreground_green, :foreground_blue, :line_width_value, :line_style_value
2157
+
2158
+ def foreground_value
2159
+ rgb(foreground_red, foreground_green, foreground_blue)
2160
+ end
2161
+
2162
+ def line_style_value_options
2163
+ [:solid, :dash, :dot, :dashdot, :dashdotdot]
2164
+ end
2165
+
2166
+ before_body do
2167
+ self.x1_value = 0
2168
+ self.y1_value = 0
2169
+ self.x2_value = CANVAS_WIDTH
2170
+ self.y2_value = CANVAS_HEIGHT
2171
+ self.foreground_red = 28
2172
+ self.foreground_green = 128
2173
+ self.foreground_blue = 228
2174
+ self.line_width_value = 3
2175
+ self.line_style_value = :dot
2176
+ end
2177
+
2178
+ body {
2179
+ shell {
2180
+ text 'Hello, Canvas Data-Binding!'
2181
+
2182
+ tab_folder {
2183
+ tab_item {
2184
+ grid_layout(6, true) {
2185
+ margin_width 0
2186
+ margin_height 0
2187
+ horizontal_spacing 0
2188
+ vertical_spacing 0
2189
+ }
2190
+ text 'line'
2191
+
2192
+ label {
2193
+ layout_data(:fill, :center, false, false) {
2194
+ horizontal_span 3
2195
+ }
2196
+ text 'x1'
2197
+ }
2198
+ label {
2199
+ layout_data(:fill, :center, false, false) {
2200
+ horizontal_span 3
2201
+ }
2202
+ text 'y1'
2203
+ }
2204
+ spinner {
2205
+ layout_data(:fill, :center, false, false) {
2206
+ horizontal_span 3
2207
+ }
2208
+ maximum CANVAS_WIDTH
2209
+ increment 3
2210
+ selection <=> [self, :x1_value]
2211
+ }
2212
+ spinner {
2213
+ layout_data(:fill, :center, false, false) {
2214
+ horizontal_span 3
2215
+ }
2216
+ maximum CANVAS_HEIGHT
2217
+ increment 3
2218
+ selection <=> [self, :y1_value]
2219
+ }
2220
+ label {
2221
+ layout_data(:fill, :center, false, false) {
2222
+ horizontal_span 3
2223
+ }
2224
+ text 'x2'
2225
+ }
2226
+ label {
2227
+ layout_data(:fill, :center, false, false) {
2228
+ horizontal_span 3
2229
+ }
2230
+ text 'y2'
2231
+ }
2232
+ spinner {
2233
+ layout_data(:fill, :center, false, false) {
2234
+ horizontal_span 3
2235
+ }
2236
+ maximum CANVAS_WIDTH
2237
+ increment 3
2238
+ selection <=> [self, :x2_value]
2239
+ }
2240
+ spinner {
2241
+ layout_data(:fill, :center, false, false) {
2242
+ horizontal_span 3
2243
+ }
2244
+ maximum CANVAS_HEIGHT
2245
+ increment 3
2246
+ selection <=> [self, :y2_value]
2247
+ }
2248
+ label {
2249
+ layout_data(:fill, :center, false, false) {
2250
+ horizontal_span 2
2251
+ }
2252
+ text 'foreground red'
2253
+ }
2254
+ label {
2255
+ layout_data(:fill, :center, false, false) {
2256
+ horizontal_span 2
2257
+ }
2258
+ text 'foreground green'
2259
+ }
2260
+ label {
2261
+ layout_data(:fill, :center, false, false) {
2262
+ horizontal_span 2
2263
+ }
2264
+ text 'foreground blue'
2265
+ }
2266
+ spinner {
2267
+ layout_data(:fill, :center, false, false) {
2268
+ horizontal_span 2
2269
+ }
2270
+ maximum 255
2271
+ increment 10
2272
+ selection <=> [self, :foreground_red]
2273
+ }
2274
+ spinner {
2275
+ layout_data(:fill, :center, false, false) {
2276
+ horizontal_span 2
2277
+ }
2278
+ maximum 255
2279
+ increment 10
2280
+ selection <=> [self, :foreground_green]
2281
+ }
2282
+ spinner {
2283
+ layout_data(:fill, :center, false, false) {
2284
+ horizontal_span 2
2285
+ }
2286
+ maximum 255
2287
+ increment 10
2288
+ selection <=> [self, :foreground_blue]
2289
+ }
2290
+ label {
2291
+ layout_data(:fill, :center, false, false) {
2292
+ horizontal_span 3
2293
+ }
2294
+ text 'line width'
2295
+ }
2296
+ label {
2297
+ layout_data(:fill, :center, false, false) {
2298
+ horizontal_span 3
2299
+ }
2300
+ text 'line style'
2301
+ }
2302
+ spinner {
2303
+ layout_data(:fill, :center, false, false) {
2304
+ horizontal_span 3
2305
+ }
2306
+ maximum 255
2307
+ selection <=> [self, :line_width_value]
2308
+ }
2309
+ combo(:read_only) {
2310
+ layout_data(:fill, :center, false, false) {
2311
+ horizontal_span 3
2312
+ }
2313
+ selection <=> [self, :line_style_value]
2314
+ }
2315
+ canvas {
2316
+ layout_data(:center, :center, false, false) {
2317
+ horizontal_span 6
2318
+ width_hint CANVAS_WIDTH
2319
+ height_hint CANVAS_WIDTH
2320
+ }
2321
+ background :white
2322
+
2323
+ line {
2324
+ x1 <= [self, :x1_value]
2325
+ y1 <= [self, :y1_value]
2326
+ x2 <= [self, :x2_value]
2327
+ y2 <= [self, :y2_value]
2328
+ foreground <= [self, :foreground_value, computed_by: [:foreground_red, :foreground_green, :foreground_blue]]
2329
+ line_width <= [self, :line_width_value]
2330
+ line_style <= [self, :line_style_value]
2331
+ }
2332
+ }
2333
+ }
2334
+ }
2335
+ }
2336
+ }
2337
+
2338
+ end
2339
+
2340
+ HelloCanvasDataBinding.launch
2341
+ ```
2342
+
2343
+ ![Glimmer Example Canvas Data-Binding Line Changed](/images/glimmer-hello-canvas-data-binding-line-changed.png)
2344
+
2345
+ If you ever have special needs for optimization, you could always default to direct SWT painting via [org.eclipse.swt.graphics.GC](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html) instead. Learn more at the [SWT Graphics Guide](https://www.eclipse.org/articles/Article-SWT-graphics/SWT_graphics.html) and [SWT Image Guide](https://www.eclipse.org/articles/Article-SWT-images/graphics-resources.html#Saving%20Images).
2346
+
2347
+ Example of manual drawing without relying on the declarative Glimmer Shape DSL (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
1791
2348
 
1792
2349
  ```ruby
1793
2350
  include Glimmer
@@ -1828,6 +2385,8 @@ shell {
1828
2385
  }.open
1829
2386
  ```
1830
2387
 
2388
+ ![Glimmer Example Canvas](/images/glimmer-example-canvas.png)
2389
+
1831
2390
  #### Shapes inside a Shape
1832
2391
 
1833
2392
  Shapes can be nested within each other. If you nest a shape within another, its coordinates are assumed to be relative to its parent.
@@ -2408,59 +2967,67 @@ The [Glimmer Tetris](/docs/reference/GLIMMER_SAMPLES.md#tetris) sample provides
2408
2967
 
2409
2968
  ### Data-Binding
2410
2969
 
2411
- Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
2970
+ Data-binding is done with either the [Shine](#shine) syntax `<=>` (bidirectional data-binding) & `<=` (unidirectional data-binding) or via the `bind` keyword, following widget property to bind and taking model and bindable attribute as arguments.
2412
2971
 
2413
2972
  #### General Examples
2414
2973
 
2974
+ `text <=> [contact, :first_name]`
2975
+
2976
+ This example binds the text property of a widget like `label` to the first name of a contact model using Shine data-binding syntax (recommended).
2977
+
2415
2978
  `text bind(contact, :first_name)`
2416
2979
 
2417
- This example binds the text property of a widget like `label` to the first name of a contact model.
2980
+ This example binds the text property of a widget like `label` to the first name of a contact model (older style of data-binding, not recommended).
2418
2981
 
2419
- `text bind(contact, 'address.street')`
2982
+ `text <=> [contact, 'address.street']`
2420
2983
 
2421
2984
  This example binds the text property of a widget like `label` to the nested street of
2422
2985
  the address of a contact. This is called nested property data binding.
2423
2986
 
2424
- `text bind(contact, 'address.street', on_read: :upcase, on_write: :downcase)`
2987
+ `text <=> [contact, 'address.street', on_read: :upcase, on_write: :downcase]`
2425
2988
 
2426
2989
  This example adds on the one above it by specifying converters on read and write of the model property, like in the case of a `text` widget. The text widget will then displays the street upper case and the model will store it lower case. When specifying converters, read and write operations must be symmetric (to avoid an infinite update loop between the widget and the model since the widget checks first if value changed before updating)
2427
2990
 
2428
- `text bind(contact, 'address.street', sync_exec: true)`
2429
-
2430
- This example forces GUI updates via [sync_exec](#sync_exec) assuming they are coming from another thread (different from the GUI thread)
2431
-
2432
- `text bind(contact, 'address.street', async_exec: true)`
2433
-
2434
- This example forces GUI updates via [async_exec](#async_exec) assuming they are coming from another thread (different from the GUI thread)
2435
-
2436
- `text bind(contact, 'address.street', on_read: lambda { |s| s[0..10] })`
2991
+ `text <=> [contact, 'address.street', on_read: ->(s) { s[0..10] }]`
2437
2992
 
2438
2993
  This example also specifies a converter on read of the model property, but via a lambda, which truncates the street to 10 characters only. Note that the read and write operations are assymetric. This is fine in the case of formatting data for a read-only widget like `label`
2439
2994
 
2440
2995
  `text bind(contact, 'address.street') { |s| s[0..10] }`
2441
2996
 
2442
- This is a block shortcut version of the syntax above it. It facilitates formatting model data for read-only widgets since it's a very common view concern. It also saves the developer from having to create a separate formatter/presenter for the model when the view can be an active view that handles common simple formatting operations directly.
2997
+ This is a block shortcut version of the syntax above it. It facilitates formatting model data for read-only widgets since it's a very common view concern. It also saves the developer from having to create a separate formatter/presenter for the model when the view can be an active view that handles common simple formatting operations directly (older style of data-binding, not recommended).
2443
2998
 
2444
- `text bind(contact, 'address.street', read_only: true)`
2999
+ `text <= [contact, 'address.street']`
2445
3000
 
2446
3001
  This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed.
2447
3002
 
2448
- `text bind(contact, 'addresses[1].street')`
3003
+ `text bind(contact, 'address.street', read_only: true)`
3004
+
3005
+ This is read-ohly data-binding. It doesn't update contact.address.street when widget text property is changed (older style of data-binding, not recommended).
3006
+
3007
+ `text <=> [contact, 'addresses[1].street']`
2449
3008
 
2450
3009
  This example binds the text property of a widget like `label` to the nested indexed address street of a contact. This is called nested indexed property data binding.
2451
3010
 
2452
- `text bind(contact, :age, computed_by: :date_of_birth)`
3011
+ `text <=> [contact, :age, computed_by: :date_of_birth]`
2453
3012
 
2454
3013
  This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
2455
3014
 
2456
- `text bind(contact, :name, computed_by: [:first_name, :last_name])`
3015
+ `text <=> [contact, :name, computed_by: [:first_name, :last_name]]`
2457
3016
 
2458
3017
  This example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
2459
3018
 
2460
- `text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
3019
+ `text <=> [contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name']]`
2461
3020
 
2462
3021
  This example demonstrates nested indexed computed value data binding whereby the value of `profiles[0].name` depends on changes to both nested `profiles[0].first_name` and `profiles[0].last_name`.
2463
3022
 
3023
+ `text <=> [contact, 'address.street', sync_exec: true]`
3024
+
3025
+ This example forces GUI updates via [sync_exec](#sync_exec) assuming they are coming from another thread (different from the GUI thread)
3026
+
3027
+ `text <=> [contact, 'address.street', async_exec: true]`
3028
+
3029
+ This example forces GUI updates via [async_exec](#async_exec) assuming they are coming from another thread (different from the GUI thread)
3030
+
2464
3031
  Example from [samples/hello/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
2465
3032
 
2466
3033
  #### Shine
@@ -3107,7 +3674,7 @@ Approach #1 is a casual Ruby-based approach. Approach #2 is the official Glimmer
3107
3674
 
3108
3675
  A developer might start with approach #1 to eliminate duplication in a view and later upgrade it to approach #2 when needing to export a custom widget to make it available in many views.
3109
3676
 
3110
- Class-based Custom Widgets a number of benefits over method-based custom widgets, such as built-in support for passing SWT style, nested block of extra widgets and properties, and `before_body`/`after_body` hooks.
3677
+ Class-based Custom Widgets offer a number of benefits over method-based custom widgets, such as built-in support for passing SWT style, nested block of extra widgets and properties, and `before_body`/`after_body` hooks.
3111
3678
 
3112
3679
  #### Simple Example
3113
3680
 
@@ -3205,6 +3772,100 @@ Notice how `Red::Composite` became `red__composite` with double-underscore, whic
3205
3772
 
3206
3773
  Keep in mind that namespaces are not needed to be specified if the Custom Widget class has a unique name, not clashing with a basic SWT widget or another custom widget name.
3207
3774
 
3775
+ #### Custom Widget Listeners
3776
+
3777
+ If you need to declare a custom listener on a custom widget, you must override these methods:
3778
+ - `can_handle_observation_request?(event, &block)`: returns if an event is supported or delegates to super otherwise (to ensure continued support for built-in events)
3779
+ - `handle_observation_request(event, &block)`: handles event by storing the block in a list of block handlers to invoke at the right time in the custom widget code
3780
+
3781
+ Example (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
3782
+
3783
+ ```ruby
3784
+ require 'glimmer-dsl-swt'
3785
+
3786
+ # This class declares a `greeting_label` custom widget (by convention)
3787
+ class GreetingLabel
3788
+ include Glimmer::UI::CustomWidget
3789
+
3790
+ # multiple options without default values
3791
+ options :name, :colors
3792
+
3793
+ # single option with default value
3794
+ option :greeting, default: 'Hello'
3795
+
3796
+ # internal attribute (not a custom widget option)
3797
+ attr_accessor :label_color
3798
+
3799
+ def can_handle_observation_request?(event, &block)
3800
+ event.to_s == 'on_color_changed' || super
3801
+ end
3802
+
3803
+ def handle_observation_request(event, &block)
3804
+ if event.to_s == 'on_color_changed'
3805
+ @color_changed_handlers ||= []
3806
+ @color_changed_handlers << block
3807
+ else
3808
+ super
3809
+ end
3810
+ end
3811
+
3812
+ before_body do
3813
+ @font = {height: 24, style: :bold}
3814
+ @label_color = :black
3815
+ end
3816
+
3817
+ after_body do
3818
+ return if colors.nil?
3819
+
3820
+ Thread.new {
3821
+ colors.cycle { |color|
3822
+ self.label_color = color
3823
+ @color_changed_handlers&.each {|handler| handler.call(color)}
3824
+ sleep(1)
3825
+ }
3826
+ }
3827
+ end
3828
+
3829
+ body {
3830
+ # pass received swt_style through to label to customize (e.g. :center to center text)
3831
+ label(swt_style) {
3832
+ text "#{greeting}, #{name}!"
3833
+ font @font
3834
+ foreground <=> [self, :label_color]
3835
+ }
3836
+ }
3837
+
3838
+ end
3839
+
3840
+ # including Glimmer enables the Glimmer DSL syntax, including auto-discovery of the `greeting_label` custom widget
3841
+ include Glimmer
3842
+
3843
+ shell {
3844
+ fill_layout :vertical
3845
+
3846
+ minimum_size 215, 215
3847
+ text 'Hello, Custom Widget!'
3848
+
3849
+ # custom widget options are passed in a hash
3850
+ greeting_label(name: 'Sean')
3851
+
3852
+ # pass :center SWT style followed by custom widget options hash
3853
+ greeting_label(:center, name: 'Laura', greeting: 'Aloha') #
3854
+
3855
+ greeting_label(:right, name: 'Rick') {
3856
+ # you can nest attributes under custom widgets just like any standard widget
3857
+ foreground :red
3858
+ }
3859
+
3860
+ # the colors option cycles between colors for the label foreground every second
3861
+ greeting_label(:center, name: 'Mary', greeting: 'Aloha', colors: [:red, :dark_green, :blue]) {
3862
+ on_color_changed do |color|
3863
+ puts "Label color changed: #{color}"
3864
+ end
3865
+ }
3866
+ }.open
3867
+ ```
3868
+
3208
3869
  #### Custom Widget API
3209
3870
 
3210
3871
  Custom Widgets have the following attributes available to call from inside the `#body` method:
@@ -4029,25 +4690,6 @@ You may run `glimmer` with the `--profile.graph` instead for a more detailed out
4029
4690
 
4030
4691
  Learn more at the [JRuby Performance Profile WIKI page](https://github.com/jruby/jruby/wiki/Profiling-JRuby).
4031
4692
 
4032
- ##### SWT Browser Style Options
4033
-
4034
- The `browser` widget can use a particular desktop browser by setting the SWT Style to:
4035
- - `:webkit`: use the Webkit browser engine
4036
- - `:chromium`: use the Chromium browser engine
4037
-
4038
- Example using the Chromium browser (you may copy/paste in [`girb`](GLIMMER_GIRB.md)):
4039
-
4040
- ```ruby
4041
- shell {
4042
- minimum_size 1024, 860
4043
- browser(:chromium) {
4044
- url 'http://brightonresort.com/about'
4045
- }
4046
- }.open
4047
- ```
4048
-
4049
- This relies on Glimmer's [Multi-DSL Support](#multi-dsl-support) for building the HTML text using [Glimmer XML DSL](https://github.com/AndyObtiva/glimmer-dsl-xml).
4050
-
4051
4693
  ## License
4052
4694
 
4053
4695
  [MIT](LICENSE.txt)