glimmer-dsl-swt 4.18.5.3 → 4.18.6.2

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: acf07c3a7c6f534f4082330369a0bacac2593675506da814ec68e70446ec02b9
4
- data.tar.gz: ff2983c28283b30b4f7f9e6bdf9cc0bad3768331014ef281ff5397726851b39d
3
+ metadata.gz: 6d4f89641fcc2573b43cd52e23cf4dda45aa209e55cd49cbe751f6a4b7bc85b1
4
+ data.tar.gz: 9cf53e2b5aef11f838314b50188f63b3a69936ea31f54930da77bddfd561b08e
5
5
  SHA512:
6
- metadata.gz: d89da48eb352e555e861032de5b5fee9c1e1ad3e7796c920e354b61b5fb1e2522fa1eafd89e363259ee9c3961ab4207a51ea483ef670e072c869fb1671fecccb
7
- data.tar.gz: d267615568ac7e20ef8b5778df77cfe36f3de061ab9dd5c4e227b2ef08d5323a27bc6742f07728a84b0760a11f64bdad822c4468f1cf24049ac536919816548e
6
+ metadata.gz: 355fe3aab4e45648832678ce3a2c00411129be61bbff91575f4c19b73254a82ab9fa58ce6acbac78d6dca4daf383340cd074e6ec5ea6128bcdb37df94befe125
7
+ data.tar.gz: d30171ae846dc373fb4d58426bcdc64a179579c4f86ee749d16cb0a5f3440bc542305a91a1e0d5774b796e14750a1918ae4e8d3bb33b2eaff70da4fd3a7afff1
data/CHANGELOG.md CHANGED
@@ -1,5 +1,46 @@
1
1
  # Change Log
2
2
 
3
+ ### 4.18.6.2
4
+
5
+ - Hello, Canvas Data Binding! Sample
6
+ - Update Stock Ticker sample to keep stock names visible when scrolling graphs off the screen
7
+ - `rgb` keyword tolerance of nil values (converts to 0)
8
+ - Canvas Path DSL Data-Binding
9
+ - Added `Glimmer::SWT::Custom::Shape::PathSegment` `#path` and `#root_path` API methods to enable determining what path/root-path the path segment is part of.
10
+ - Fixed issues with geometry calculation of path segments (especially line)
11
+
12
+ ### 4.18.6.1
13
+
14
+ - Fixed issues with Canvas Path DSL handling of connected vs non-connected path segments (including in geometry calculations)
15
+ - Optimized Canvas Path DSL redraw performance by removing extra redraws
16
+ - Fixed issues in the Hello, Canvas Path! sample and renamed to Stock Ticker
17
+ - Added a new simpler Hello, Canvas Path! sample
18
+
19
+ ### 4.18.6.0
20
+
21
+ - Canvas Path DSL support (Alpha) for `path` as drawn or filled (`fill: true`) to the Canvas Shape DSL, supporting `point`, `line` (first point is auto-derived from previous point if not specified)
22
+ - Hello, Canvas Path! sample showing a Stock Ticker with line curves for multiple company stocks, animated with randomly generated data, moving to the left out of screen second by second. Has multiple tabs demonstrating different types of paths for graphing/charting of different real world business applications: point, line, quad, cubic.
23
+ - Fix issue to allow invocation of `set_min_size` off of `scrolled_composite` proxy directly (not just swt_widget), thus taking advantage of implicit `auto_exec`
24
+ - Support `Shape#content {}` method just like `WidgetProxy#content` to enable reopening and adding nested shapes at runtime after initial construction
25
+ - Support a path containing a `quad` bezier curve with `point_array` property
26
+ - Support a path containing a `cubic` bezier curve with `point_array` property
27
+
28
+ ### 4.18.5.5
29
+
30
+ - Automatically recalculate default size (width/height) to accomodate nested shapes when changing x/y/width/height sticking out of parent from right or bottom.
31
+ - Support special case of centering a nested shape with default x/y within a parent with default width/height calculated from nested shape
32
+ - Consider Canvas Shape DSL support for LineAttributes `line_dash_offset` and `line_miter_limit`
33
+ - Canvas Shape DSL Polygon `include?` does an outer/inner check of edge detection only
34
+ - Ensure all Canvas Shape DSL properties are restored upon painting a shape to what they were prior to painting that shape
35
+ - Fix issue with bringing high score dialog up in Tetris caused by latest dialog changes for supporting the new `color_dialog` and `font_dialog`
36
+
37
+ ### 4.18.5.4
38
+
39
+ - Support passing width, height as :default (or nil or not passed in if they are the last args) in all shapes that include other shapes to indicate they are calculated automatically from nested shapes, text/string extent, or otherwise defaulting to 0, 0
40
+ - Support [:default, width_delta], [:default, height_delta] attributes for width and height, which add/subtract from defaults used for shape
41
+ - Switch from use of `:default` with x_delta/y_delta to passing `[:default, x_delta]` or `[:default, y_delta]` (e.g. `image(file, [:default, -30], :default)` for x = default - 30 and y = default + 0)
42
+ - Support a bounding box for all shapes, implementing `#bounds` (x, y, width, and height) and `#size` (width, height) for the ones that don't receive as parameters (like polygon)
43
+
3
44
  ### 4.18.5.3
4
45
 
5
46
  - Support nesting shapes within shapes, with relative positioning (meaning x, y coordinates are assumed relative to parent's x, y in nested shapes)
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 SWT 4.18.5.3
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for SWT 4.18.6.2
2
2
  ## JRuby Desktop Development GUI Framework
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-swt.svg)](http://badge.fury.io/rb/glimmer-dsl-swt)
4
4
  [![Travis CI](https://travis-ci.com/AndyObtiva/glimmer-dsl-swt.svg?branch=master)](https://travis-ci.com/github/AndyObtiva/glimmer-dsl-swt)
@@ -8,17 +8,15 @@
8
8
 
9
9
  **[Contributors Wanted! (Submit a Glimmer App Sample to Get Started)](#contributing)**
10
10
 
11
- [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer) is a native-GUI cross-platform desktop development library written in [JRuby](https://www.jruby.org/), an OS-threaded faster JVM version of [Ruby](https://www.ruby-lang.org/en/). [Glimmer](https://github.com/AndyObtiva/glimmer)'s main innovation is a declarative [Ruby DSL](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#glimmer-dsl-syntax) that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust [Eclipse SWT library](https://www.eclipse.org/swt/). [Glimmer](https://rubygems.org/gems/glimmer) additionally innovates by having built-in [data-binding](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#data-binding) support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI [widgets](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#widgets), but it also supports drawing Canvas Graphics like [Shapes](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl) and [Animations](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-animation-dsl). To get started quickly, [Glimmer](https://rubygems.org/gems/glimmer) offers [scaffolding](docs/reference/GLIMMER_COMMAND.md#scaffolding) options for [Apps](#in-production), [Gems](docs/reference/GLIMMER_COMMAND.md#custom-shell-gem), and [Custom Widgets](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#custom-widgets). [Glimmer](https://rubygems.org/gems/glimmer) also includes native-executable [packaging](docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in [Ruby](https://www.ruby-lang.org/en/) as truly native DMG/PKG/APP files on the [Mac](https://www.apple.com/ca/macos) + [App Store](https://developer.apple.com/macos/distribution/), MSI/EXE files on [Windows](https://www.microsoft.com/en-ca/windows), and [Gem Packaged Shell Scripts](docs/reference/GLIMMER_COMMAND.md#packaging) on [Linux](https://www.linux.org/).
12
-
13
- [Glimmer receives two updates per month](https://rubygems.org/gems/glimmer-dsl-swt/versions). You can trust [Glimmer](https://rubygems.org/gems/glimmer) with your Ruby desktop GUI development needs. Please make [Glimmer](https://rubygems.org/gems/glimmer) even better by providing feedback and [contributing](#contributing) when possible.
14
-
15
- Gem version numbers are in sync with the SWT library versions. The first two digits represent the SWT version number. The last two digits represent the minor and patch versions of Glimmer DSL for SWT.
16
-
17
- [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) versions 4.18.x.y come with [SWT 4.18](https://download.eclipse.org/eclipse/downloads/drops4/R-4.18-202012021800/), which was released on December 2, 2020.
11
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for SWT is a native-GUI cross-platform desktop development library written in [JRuby](https://www.jruby.org/), an OS-threaded faster JVM version of [Ruby](https://www.ruby-lang.org/en/). [Glimmer](https://github.com/AndyObtiva/glimmer)'s main innovation is a declarative [Ruby DSL](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#glimmer-dsl-syntax) that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust [Eclipse SWT library](https://www.eclipse.org/swt/). [Glimmer](https://rubygems.org/gems/glimmer) additionally innovates by having built-in [data-binding](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#data-binding) support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI [widgets](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#widgets), but it also supports drawing Canvas Graphics like [Shapes](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl) and [Animations](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-animation-dsl). To get started quickly, [Glimmer](https://rubygems.org/gems/glimmer) offers [scaffolding](docs/reference/GLIMMER_COMMAND.md#scaffolding) options for [Apps](#in-production), [Gems](docs/reference/GLIMMER_COMMAND.md#custom-shell-gem), and [Custom Widgets](docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#custom-widgets). [Glimmer](https://rubygems.org/gems/glimmer) also includes native-executable [packaging](docs/reference/GLIMMER_PACKAGING_AND_DISTRIBUTION.md) support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in [Ruby](https://www.ruby-lang.org/en/) as truly native DMG/PKG/APP files on the [Mac](https://www.apple.com/ca/macos) + [App Store](https://developer.apple.com/macos/distribution/), MSI/EXE files on [Windows](https://www.microsoft.com/en-ca/windows), and [Gem Packaged Shell Scripts](docs/reference/GLIMMER_COMMAND.md#packaging) on [Linux](https://www.linux.org/).
18
12
 
19
13
  [<img src="https://covers.oreillystatic.com/images/9780596519650/lrg.jpg" width=105 /><br />
20
14
  Featured in JRuby Cookbook](http://shop.oreilly.com/product/9780596519650.do) and [Chalmers/Gothenburg University Software Engineering Master's Lecture Material](http://www.cse.chalmers.se/~bergert/slides/guest_lecture_DSLs.pdf)
21
15
 
16
+ [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) 4.18.6.2 includes [SWT 4.18](https://download.eclipse.org/eclipse/downloads/drops4/R-4.18-202012021800/), which was released on December 2, 2020. Gem version numbers are in sync with the SWT library versions. The first two digits represent the SWT version number. The last two digits represent the minor and patch versions of Glimmer DSL for SWT.
17
+
18
+ [Glimmer DSL for SWT receives two updates per month](https://rubygems.org/gems/glimmer-dsl-swt/versions). You can trust [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) with your Ruby desktop GUI development needs! [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) brings great ideas to the table, such as declarative programming via domain specific languages, currently under-utilized in the GUI domain. That said, it may not be feature complete enough for everybody's needs, so please help make [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) even better by providing feedback and [contributing](#contributing) when possible. The project is very active, so any feature suggestions that are accepted could be implemented within weeks if not days. Also, you are welcome to [hire me](#hire-me) full-time if you want long-term development of [Glimmer DSL for SWT](https://rubygems.org/gems/glimmer-dsl-swt) for your project needs.
19
+
22
20
  Glimmer DSL gems:
23
21
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
24
22
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
@@ -342,7 +340,7 @@ jgem install glimmer-dsl-swt
342
340
 
343
341
  Or this command if you want a specific version:
344
342
  ```
345
- jgem install glimmer-dsl-swt -v 4.18.5.3
343
+ jgem install glimmer-dsl-swt -v 4.18.6.2
346
344
  ```
347
345
 
348
346
  `jgem` is JRuby's version of `gem` command.
@@ -360,7 +358,7 @@ Note: if you're using activerecord or activesupport, keep in mind that Glimmer u
360
358
 
361
359
  Add the following to `Gemfile`:
362
360
  ```
363
- gem 'glimmer-dsl-swt', '~> 4.18.5.3'
361
+ gem 'glimmer-dsl-swt', '~> 4.18.6.2'
364
362
  ```
365
363
 
366
364
  And, then run:
data/VERSION CHANGED
@@ -1 +1 @@
1
- 4.18.5.3
1
+ 4.18.6.2
@@ -36,10 +36,12 @@ This guide should help you get started with Glimmer DSL for SWT. For more advanc
36
36
  - [Layouts](#layouts)
37
37
  - [Layout Data](#layout-data)
38
38
  - [Canvas Shape DSL](#canvas-shape-dsl)
39
+ - [Shapes inside a Shape](#shapes-inside-a-shape)
39
40
  - [Shapes inside a Widget](#shapes-inside-a-widget)
40
41
  - [Shapes inside an Image](#shapes-inside-an-image)
41
42
  - [Canvas Shape API](#canvas-shape-api)
42
43
  - [Pixel Graphics](#pixel-graphics)
44
+ - [Canvas Path DSL](#canvas-path-dsl)
43
45
  - [Canvas Transform DSL](#canvas-transform-dsl)
44
46
  - [Top-Level Transform Fluent Interface](#top-level-transform-fluent-interface)
45
47
  - [Canvas Animation DSL](#canvas-animation-dsl)
@@ -1403,7 +1405,7 @@ Shape keywords and their args (including defaults) are listed below (they basica
1403
1405
  - `rectangle(x, y, width, height, arcWidth = 60, arcHeight = 60, fill: false, round: true)` round rectangle, which can be optionally filled, and takes optional extra round angle arguments
1404
1406
  - `rectangle(x, y, width, height, vertical = true, fill: true, gradient: true)` gradient rectangle, which is always filled, and takes an optional extra argument to specify true for vertical gradient (default) and false for horizontal gradient
1405
1407
  - `text(string, x, y, is_transparent = true)` text with optional is_transparent to indicate if background is transparent (default is true)
1406
- - `text(string, x, y, flags)` text with optional flags (flag format is `swt(comma_separated_flags)` where flags can be :draw_delimiter (i.e. new lines), :draw_tab, :draw_mnemonic, and :draw_transparent as explained in [GC API](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html))
1408
+ - `text(string, x, y, flags)` text with optional flags (flag format is `swt(comma_separated_flags)` where flags can be `:draw_delimiter` (i.e. new lines), `:draw_tab`, `:draw_mnemonic`, and `:draw_transparent` as explained in [GC API](https://help.eclipse.org/2020-12/topic/org.eclipse.platform.doc.isv/reference/api/org/eclipse/swt/graphics/GC.html))
1407
1409
 
1408
1410
  Shape keywords that can be filled with color can take a keyword argument `fill: true`. Defaults to false when not specified unless background is set with no foreground (or foreground is set with no background), in which case a smart default is applied.
1409
1411
  Smart defaults can be applied to automatically infer `gradient: true` (rectangle with both foreground and background) and `round: true` (rectangle with more than 4 args, the extra args are numeric) as well.
@@ -1460,7 +1462,7 @@ shell {
1460
1462
  font height: 25, style: :bold
1461
1463
  }
1462
1464
  rectangle(200, 80, 108, 36) {
1463
- foreground :black
1465
+ foreground rgb(0, 0, 0)
1464
1466
  line_width 3
1465
1467
  }
1466
1468
  image(image_object, 70, 50)
@@ -1506,7 +1508,7 @@ shell {
1506
1508
  font height: 25, style: :bold
1507
1509
  }
1508
1510
  rectangle(200, 80, 108, 36) {
1509
- foreground :black
1511
+ foreground rgb(0, 0, 0)
1510
1512
  line_width 3
1511
1513
  }
1512
1514
  image(image_object, 70, 50)
@@ -1541,7 +1543,7 @@ shell {
1541
1543
  }
1542
1544
  rectangle {
1543
1545
  x 50
1544
- x 20
1546
+ y 20
1545
1547
  width 300
1546
1548
  height 150
1547
1549
  arc_width 30
@@ -1596,23 +1598,26 @@ shell {
1596
1598
  minimum_size 320, 400
1597
1599
 
1598
1600
  canvas {
1599
- background :yellow
1601
+ background :dark_yellow
1600
1602
 
1601
1603
  on_paint_control { |paint_event|
1602
1604
  gc = paint_event.gc
1603
- gc.background = color(:red).swt_color
1604
- gc.fill_rectangle(0, 0, 220, 400)
1605
1605
 
1606
- gc.background = color(:magenta).swt_color
1607
- gc.fill_roundRectangle(50, 20, 300, 150, 30, 50)
1606
+ gc.background = color(:dark_red).swt_color
1607
+ gc.fill_rectangle(0, 0, 220, 400)
1608
1608
 
1609
- gc.background = color(:dark_magenta).swt_color
1610
- gc.fill_gradientRectangle(150, 200, 100, 70, true)
1609
+ gc.background = color(:yellow).swt_color
1610
+ gc.fill_round_rectangle(50, 20, 300, 150, 30, 50)
1611
1611
 
1612
- gc.foreground = color(:dark_blue).swt_color
1613
- gc.draw_rectangle(200, 80, 108, 36)
1612
+ gc.background = color(:dark_red).swt_color
1613
+ gc.foreground = color(:yellow).swt_color
1614
+ gc.fill_gradient_rectangle(150, 200, 100, 70, true)
1614
1615
 
1616
+ gc.font = font(height: 25, style: :bold).swt_font
1615
1617
  gc.foreground = color(:black).swt_color
1618
+ gc.draw_text('Glimmer', 208, 83, true)
1619
+
1620
+ gc.foreground = rgb(0, 0, 0).swt_color
1616
1621
  gc.line_width = 3
1617
1622
  gc.draw_rectangle(200, 80, 108, 36)
1618
1623
 
@@ -1762,6 +1767,8 @@ They are implemented with the help of the highly robust Java built-in shape geom
1762
1767
  - `Shape#contain?(x, y)` : indicates if shape contains x, y point
1763
1768
  - `Shape#include?(x, y)` : indicates if shape includes x, y point on the edge if drawn or inside if filled (include uses contain for filled shapes)
1764
1769
  - `Shape#move_by(x_delta, y_delta)` : moves shape object at x, y location
1770
+ - `Shape#size` : calculated size for shape bounding box (e.g. a polygon with an irregular shape will have its bounding box width and height calculated)
1771
+ - `Shape#bounds` : calculated bounds (x, y, width, height) for shape bounding box (e.g. a polygon with an irregular shape will have its bounding box top-left x, y, width and height calculated)
1765
1772
 
1766
1773
  Check [Hello, Canvas!](GLIMMER_SAMPLES.md#hello-canvas) for an example.
1767
1774
 
@@ -1947,6 +1954,43 @@ shell {
1947
1954
 
1948
1955
  As they say, there are many ways to skin a cat! This is in line with the Ruby way of providing more ways than one. Pick and choose the right tool for the job just like true software engineers.
1949
1956
 
1957
+ ### Canvas Path DSL
1958
+
1959
+ **(ALPHA FEATURE)**
1960
+
1961
+ Unlike common imperative GUI graphing toolkits, Glimmer enables declarative rendering of paths with the new Canvas Path DSL (Early Alpha) via the new `path { }` keyword and by nesting one of the following path segment keywords underneath:
1962
+ - `point(x1, y1)`: renders a Point (Dot) as part of a path.
1963
+ - `line(x1, y1, x2=nil, y2=nil)`: renders a Line as part of a path. If you drop x2, y2, it joins to the previous point automatically. You may repeat for a series of lines forming a curve.
1964
+ - `quad(x1, y1, x2, y2, x3=nil, y3=nil)`: renders a Quadratic Bezier Curve. If you drop x3 and y3, it joins to the previous point automatically.
1965
+ - `cubic(x1, y1, x2, y2, x3, y3, x4=nil, y4=nil)`: renders a Cubic Bezier Curve. If you drop x4 and y4, it joins to the previous point automatically.
1966
+
1967
+ Example:
1968
+
1969
+ ```ruby
1970
+ include Glimmer
1971
+
1972
+ shell {
1973
+ text 'Canvas Path Example'
1974
+ minimum_size 300, 300
1975
+
1976
+ canvas {
1977
+ path {
1978
+ foreground :black
1979
+ 250.times {|n|
1980
+ cubic(n + n%30, n+ n%50, 40, 40, 70, 70, n + 20 + n%30, n%30*-1 * n%50)
1981
+ }
1982
+ }
1983
+ }
1984
+
1985
+ }.open
1986
+ ```
1987
+
1988
+ ![Canvas Path Example](/images/glimmer-example-canvas-path.png)
1989
+
1990
+ Learn more at the [Hello, Canvas Path!](GLIMMER_SAMPLES.md#hello-canvas-path) and [Stock Ticker](GLIMMER_SAMPLES.md#stock-ticker) samples.
1991
+
1992
+ ![Stock Ticker](/images/glimmer-stock-ticker.gif)
1993
+
1950
1994
  ### Canvas Transform DSL
1951
1995
 
1952
1996
  **(ALPHA FEATURE)**
@@ -12,6 +12,8 @@ glimmer package
12
12
 
13
13
  It works out of the box for any application scaffolded by [Glimmer Scaffolding](#scaffolding), generating all available packaging types on the current platform (e.g. `DMG`, `PKG`, `APP` on the Mac) and displaying a message indicating what pre-requisite setup tools are needed if not installed already (e.g. [Wix Toolset](https://wixtoolset.org/) to generate MSI files on Windows). If you install Wix, make sure it is on the system PATH by adding for example "C:\Program Files (x86)\WiX Toolset v3.11\bin" to the Windows Environment Variables.
14
14
 
15
+ (note: if you see this error on the Mac 'Error: Bundler "DMG Installer" (dmg) failed to produce a bundle.', ignore it as it should have produced a bundle anyways. It is a harmless issue in 3rd party dependency: javapackager.)
16
+
15
17
  You may choose to generate a specific type of packaging instead by addionally passing in the `[type]` option. For example, this generates an MSI setup file on Windows:
16
18
 
17
19
  ```
@@ -33,6 +33,7 @@
33
33
  - [Hello, Canvas!](#hello-canvas)
34
34
  - [Hello, Canvas Animation!](#hello-canvas-animation)
35
35
  - [Hello, Canvas Transform!](#hello-canvas-transform)
36
+ - [Hello, Canvas Path!](#hello-canvas-path)
36
37
  - [Hello, Cursor!](#hello-cursor)
37
38
  - [Hello, Progress Bar!](#hello-progress-bar)
38
39
  - [Hello, Tree!](#hello-tree)
@@ -45,6 +46,7 @@
45
46
  - [Contact Manager](#contact-manager)
46
47
  - [Glimmer Tetris](#glimmer-tetris)
47
48
  - [Mandelbrot Fractal](#mandelbrot-fractal)
49
+ - [Stock Ticker](#stock-ticker)
48
50
  - [External Samples](#external-samples)
49
51
  - [Glimmer Calculator](#glimmer-calculator)
50
52
  - [Gladiator](#gladiator)
@@ -564,6 +566,10 @@ Hello, Canvas!
564
566
 
565
567
  ![Hello Canvas](/images/glimmer-hello-canvas.png)
566
568
 
569
+ Hello, Canvas! Moving Shapes and Nested Shapes via Drag'n'Drop
570
+
571
+ ![Hello Canvas Moving Shapes](/images/glimmer-hello-canvas-moving-shapes.gif)
572
+
567
573
  Hello, Canvas! with Moved Shapes (via Drag'n'Drop)
568
574
 
569
575
  ![Hello Canvas Moved Shapes](/images/glimmer-hello-canvas-moved-shapes.png)
@@ -580,6 +586,10 @@ Hello, Canvas! Colors Changed
580
586
 
581
587
  ![Hello Canvas Colors Changed](/images/glimmer-hello-canvas-colors-changed.png)
582
588
 
589
+ Hello, Canvas! Data-Binding (changing a `text` shape `string` via data-binding changes from another thread)
590
+
591
+ ![Hello Canvas Data Binding](/images/glimmer-hello-canvas-data-binding.gif)
592
+
583
593
  #### Hello, Canvas Animation!
584
594
 
585
595
  This sample demonstrates the use of the `canvas` widget and [Animation DSL](#canvas-animation-dsl) in Glimmer.
@@ -608,6 +618,18 @@ Hello, Canvas Transform!
608
618
 
609
619
  ![Hello Canvas Transform](/images/glimmer-hello-canvas-transform.png)
610
620
 
621
+ #### Hello, Canvas Path!
622
+
623
+ This sample demonstrates the use of the `path`, `quad`, `cubic`, `line`, and `point` keywords as part of the [Canvas Path DSL](/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-path-dsl) within the [Canvas Shape DSL](/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-shape-dsl).
624
+
625
+ Code:
626
+
627
+ [samples/hello/hello_canvas_path.rb](/samples/hello/hello_canvas_path.rb)
628
+
629
+ Hello, Canvas Path!
630
+
631
+ ![Hello Canvas Path](/images/glimmer-hello-canvas-path.png)
632
+
611
633
  #### Hello, Cursor!
612
634
 
613
635
  This sample demonstrates the use of the `cursor` property keyword to change the mouse cursor.
@@ -804,6 +826,16 @@ Code:
804
826
 
805
827
  ![Mandelbrot Fractal Help Menu](/images/glimmer-mandelbrot-menu-help.png)
806
828
 
829
+ #### Stock Ticker
830
+
831
+ This sample demonstrates a Stock Ticker that generates random stock price data for 4 different stocks and provides 4 different tab views of the graphed data using the [Canvas Path DSL](/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#canvas-path-dsl). It leverages a thread that runs in the background and ticks the stocks to generate random new stock prices before amending the graphed paths with them.
832
+
833
+ Code:
834
+
835
+ [samples/elaborate/stock_ticker.rb](/samples/elaborate/stock_ticker.rb)
836
+
837
+ ![Stock Ticker](/images/glimmer-stock-ticker.gif)
838
+
807
839
  ### External Samples
808
840
 
809
841
  #### Glimmer Calculator
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: glimmer-dsl-swt 4.18.5.3 ruby lib
5
+ # stub: glimmer-dsl-swt 4.18.6.2 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "glimmer-dsl-swt".freeze
9
- s.version = "4.18.5.3"
9
+ s.version = "4.18.6.2"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib".freeze]
13
13
  s.authors = ["AndyMaleh".freeze]
14
- s.date = "2021-02-25"
14
+ s.date = "2021-03-02"
15
15
  s.description = "Glimmer DSL for SWT (JRuby Desktop Development GUI Framework) is a native-GUI cross-platform desktop development library written in JRuby, an OS-threaded faster JVM version of Ruby. Glimmer's main innovation is a declarative Ruby DSL that enables productive and efficient authoring of desktop application user-interfaces by relying on the robust Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support, which greatly facilitates synchronizing the GUI with domain models, thus achieving true decoupling of object oriented components and enabling developers to solve business problems (test-first) without worrying about GUI concerns, or alternatively drive development GUI-first, and then write clean business models (test-first) afterwards. Not only does Glimmer provide a large set of GUI widgets, but it also supports drawing Canvas Graphics like Shapes and Animations. To get started quickly, Glimmer offers scaffolding options for Apps, Gems, and Custom Widgets. Glimmer also includes native-executable packaging support, sorely lacking in other libraries, thus enabling the delivery of desktop apps written in Ruby as truly native DMG/PKG/APP files on the Mac + App Store, MSI/EXE files on Windows, and Gem Packaged Shell Scripts on Linux.".freeze
16
16
  s.email = "andy.am@gmail.com".freeze
17
17
  s.executables = ["glimmer".freeze, "girb".freeze]
@@ -115,13 +115,17 @@ Gem::Specification.new do |s|
115
115
  "lib/glimmer/swt/custom/radio_group.rb",
116
116
  "lib/glimmer/swt/custom/shape.rb",
117
117
  "lib/glimmer/swt/custom/shape/arc.rb",
118
+ "lib/glimmer/swt/custom/shape/cubic.rb",
118
119
  "lib/glimmer/swt/custom/shape/focus.rb",
119
120
  "lib/glimmer/swt/custom/shape/image.rb",
120
121
  "lib/glimmer/swt/custom/shape/line.rb",
121
122
  "lib/glimmer/swt/custom/shape/oval.rb",
123
+ "lib/glimmer/swt/custom/shape/path.rb",
124
+ "lib/glimmer/swt/custom/shape/path_segment.rb",
122
125
  "lib/glimmer/swt/custom/shape/point.rb",
123
126
  "lib/glimmer/swt/custom/shape/polygon.rb",
124
127
  "lib/glimmer/swt/custom/shape/polyline.rb",
128
+ "lib/glimmer/swt/custom/shape/quad.rb",
125
129
  "lib/glimmer/swt/custom/shape/rectangle.rb",
126
130
  "lib/glimmer/swt/custom/shape/text.rb",
127
131
  "lib/glimmer/swt/date_time_proxy.rb",
@@ -162,6 +166,7 @@ Gem::Specification.new do |s|
162
166
  "samples/elaborate/login.rb",
163
167
  "samples/elaborate/mandelbrot_fractal.rb",
164
168
  "samples/elaborate/meta_sample.rb",
169
+ "samples/elaborate/stock_ticker.rb",
165
170
  "samples/elaborate/tetris.rb",
166
171
  "samples/elaborate/tetris/model/block.rb",
167
172
  "samples/elaborate/tetris/model/game.rb",
@@ -180,6 +185,8 @@ Gem::Specification.new do |s|
180
185
  "samples/hello/hello_button.rb",
181
186
  "samples/hello/hello_canvas.rb",
182
187
  "samples/hello/hello_canvas_animation.rb",
188
+ "samples/hello/hello_canvas_data_binding.rb",
189
+ "samples/hello/hello_canvas_path.rb",
183
190
  "samples/hello/hello_canvas_transform.rb",
184
191
  "samples/hello/hello_checkbox.rb",
185
192
  "samples/hello/hello_checkbox_group.rb",
@@ -99,7 +99,7 @@ module Glimmer
99
99
  def ensure_arg_values_within_valid_bounds
100
100
  if @args.to_a.size >= 3
101
101
  @args = @args.map do |value|
102
- [[value, 255].min, 0].max
102
+ [[value.to_f, 255].min, 0].max
103
103
  end
104
104
  end
105
105
  end
@@ -26,13 +26,40 @@ require 'glimmer/swt/color_proxy'
26
26
  require 'glimmer/swt/font_proxy'
27
27
  require 'glimmer/swt/transform_proxy'
28
28
 
29
+ class Java::OrgEclipseSwtGraphics::GC
30
+ def setLineDashOffset(value)
31
+ lineMiterLimit = getLineAttributes&.miterLimit || 999_999
32
+ setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), value, lineMiterLimit))
33
+ end
34
+ alias set_line_dash_offset setLineDashOffset
35
+ alias line_dash_offset= setLineDashOffset
36
+
37
+ def getLineDashOffset
38
+ getLineAttributes&.dashOffset
39
+ end
40
+ alias get_line_dash_offset getLineDashOffset
41
+ alias line_dash_offset getLineDashOffset
42
+
43
+ def setLineMiterLimit(value)
44
+ lineDashOffset = getLineAttributes&.dashOffset || 0
45
+ setLineAttributes(Java::OrgEclipseSwtGraphics::LineAttributes.new(getLineWidth, getLineCap, getLineJoin, getLineStyle, getLineDash.map(&:to_f).to_java(:float), lineDashOffset, value))
46
+ end
47
+ alias set_line_miter_limit setLineMiterLimit
48
+ alias line_miter_limit= setLineMiterLimit
49
+
50
+ def getLineMiterLimit
51
+ getLineAttributes&.miterLimit
52
+ end
53
+ alias get_line_miter_limit getLineMiterLimit
54
+ alias line_miter_limit getLineMiterLimit
55
+ end
56
+
29
57
  module Glimmer
30
58
  module SWT
31
59
  module Custom
32
60
  # Represents a shape (graphics) to be drawn on a control/widget/canvas/display
33
61
  # That is because Shape is drawn on a parent as graphics and doesn't have an SWT widget for itself
34
62
  class Shape
35
- include Packages
36
63
  include Properties
37
64
 
38
65
  class << self
@@ -46,7 +73,8 @@ module Glimmer
46
73
  end
47
74
 
48
75
  def valid?(parent, keyword, *args, &block)
49
- gc_instance_methods.include?(method_name(keyword, arg_options(args)))
76
+ gc_instance_methods.include?(method_name(keyword, arg_options(args))) ||
77
+ constants.include?(keyword.to_s.camelcase(:upper).to_sym)
50
78
  end
51
79
 
52
80
  def gc_instance_methods
@@ -99,7 +127,7 @@ module Glimmer
99
127
  end
100
128
 
101
129
  attr_reader :drawable, :parent, :name, :args, :options, :shapes
102
- attr_accessor :x_delta, :y_delta
130
+ attr_accessor :extent
103
131
 
104
132
  def initialize(parent, keyword, *args, &property_block)
105
133
  @parent = parent
@@ -110,8 +138,6 @@ module Glimmer
110
138
  @args = args
111
139
  @properties = {}
112
140
  @shapes = [] # nested shapes
113
- @x_delta = 0
114
- @y_delta = 0
115
141
  @options.reject {|key, value| %w[fill gradient round].include?(key.to_s)}.each do |property, property_args|
116
142
  @properties[property] = property_args
117
143
  end
@@ -121,6 +147,7 @@ module Glimmer
121
147
 
122
148
  def add_shape(shape)
123
149
  @shapes << shape
150
+ calculated_args_changed_for_defaults!
124
151
  end
125
152
 
126
153
  def draw?
@@ -141,26 +168,44 @@ module Glimmer
141
168
  @options[:round]
142
169
  end
143
170
 
144
- # subclasses (like polygon) may override to indicate if a point x,y coordinates falls inside the shape
171
+ # The bounding box top-left x, y, width, height in absolute positioning
172
+ def bounds
173
+ org.eclipse.swt.graphics.Rectangle.new(absolute_x, absolute_y, calculated_width, calculated_height)
174
+ end
175
+
176
+ # The bounding box top-left x and y
177
+ def location
178
+ org.eclipse.swt.graphics.Point.new(bounds.x, bounds.y)
179
+ end
180
+
181
+ # The bounding box width and height (as a Point object with x being width and y being height)
182
+ def size
183
+ org.eclipse.swt.graphics.Point.new(calculated_width, calculated_height)
184
+ end
185
+
186
+ def extent
187
+ @extent || size
188
+ end
189
+
190
+ # Returns if shape contains a point
191
+ # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls inside the shape
145
192
  # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
146
193
  def contain?(x, y)
147
194
  # assume a rectangular filled shape by default (works for several shapes like image, text, and focus)
148
- if respond_to?(:x) && respond_to?(:y) && respond_to?(:width) && respond_to?(:height) && self.x && self.y && width && height
149
- x.between?(self.absolute_x, self.absolute_x + width) && y.between?(self.absolute_y, self.absolute_y + height)
150
- else
151
- false # subclasses must provide implementation
152
- end
195
+ x.between?(self.absolute_x, self.absolute_x + calculated_width) && y.between?(self.absolute_y, self.absolute_y + calculated_height)
153
196
  end
154
197
 
155
- # subclasses (like polygon) may override to indicate if a point x,y coordinates falls on the edge of a drawn shape or inside a filled shape
198
+ # Returns if shape includes a point. When the shape is filled, this is the same as contain. When the shape is drawn, it only returns true if the point lies on the edge (boundary/border)
199
+ # Subclasses (like polygon) may override to indicate if a point x,y coordinates falls on the edge of a drawn shape or inside a filled shape
156
200
  # some shapes may choose to provide a fuzz factor to make usage of this method for mouse clicking more user friendly
157
201
  def include?(x, y)
158
202
  # assume a rectangular shape by default
159
- if respond_to?(:x) && respond_to?(:y) && respond_to?(:width) && respond_to?(:height)
160
- contain?(x, y)
161
- else
162
- false # subclasses must provide implementation
163
- end
203
+ contain?(x, y)
204
+ end
205
+
206
+ # Indicates if a shape's x, y, width, height differ from its bounds calculation (e.g. arc / polygon)
207
+ def irregular?
208
+ false
164
209
  end
165
210
 
166
211
  # moves by x delta and y delta. Subclasses must implement
@@ -168,18 +213,25 @@ module Glimmer
168
213
  def move_by(x_delta, y_delta)
169
214
  if respond_to?(:x) && respond_to?(:y) && respond_to?(:x=) && respond_to?(:y=)
170
215
  if default_x?
171
- self.x_delta += x_delta
216
+ self.default_x_delta += x_delta
172
217
  else
173
218
  self.x += x_delta
174
219
  end
175
220
  if default_y?
176
- self.y_delta += y_delta
221
+ self.default_y_delta += y_delta
177
222
  else
178
223
  self.y += y_delta
179
224
  end
180
225
  end
181
226
  end
182
227
 
228
+ def content(&block)
229
+ Glimmer::SWT::DisplayProxy.instance.auto_exec do
230
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::ShapeExpression.new, &block)
231
+ calculated_args_changed!(children: false)
232
+ end
233
+ end
234
+
183
235
  def has_some_background?
184
236
  @properties.keys.map(&:to_s).include?('background') || @properties.keys.map(&:to_s).include?('background_pattern')
185
237
  end
@@ -189,8 +241,8 @@ module Glimmer
189
241
  end
190
242
 
191
243
  def post_add_content
192
- unless @content_added
193
- amend_method_name_options_based_on_properties!
244
+ amend_method_name_options_based_on_properties!
245
+ if !@content_added || @method_name != @original_method_name
194
246
  @drawable.setup_shape_painting unless @drawable.is_a?(ImageProxy)
195
247
  @content_added = true
196
248
  end
@@ -199,14 +251,19 @@ module Glimmer
199
251
  def apply_property_arg_conversions(method_name, property, args)
200
252
  args = args.dup
201
253
  the_java_method = org.eclipse.swt.graphics.GC.java_class.declared_instance_methods.detect {|m| m.name == method_name}
202
- if the_java_method.parameter_types.first == Color.java_class && args.first.is_a?(RGB)
254
+ return args if the_java_method.nil?
255
+ if the_java_method.parameter_types.first == org.eclipse.swt.graphics.Color.java_class && args.first.is_a?(org.eclipse.swt.graphics.RGB)
203
256
  args[0] = [args[0].red, args[0].green, args[0].blue]
204
257
  end
205
258
  if ['setBackground', 'setForeground'].include?(method_name.to_s) && args.first.is_a?(Array)
206
259
  args[0] = ColorProxy.new(args[0])
207
260
  end
261
+ if method_name.to_s == 'setLineDash' && args.size > 1
262
+ args[0] = args.dup
263
+ args[1..-1] = []
264
+ end
208
265
  if args.first.is_a?(Symbol) || args.first.is_a?(::String)
209
- if the_java_method.parameter_types.first == Color.java_class
266
+ if the_java_method.parameter_types.first == org.eclipse.swt.graphics.Color.java_class
210
267
  args[0] = ColorProxy.new(args[0])
211
268
  end
212
269
  if the_java_method.parameter_types.first == Java::int.java_class
@@ -216,7 +273,7 @@ module Glimmer
216
273
  if args.first.is_a?(ColorProxy)
217
274
  args[0] = args[0].swt_color
218
275
  end
219
- if (args.first.is_a?(Hash) || args.first.is_a?(FontData)) && the_java_method.parameter_types.first == Font.java_class
276
+ if (args.first.is_a?(Hash) || args.first.is_a?(org.eclipse.swt.graphics.FontData)) && the_java_method.parameter_types.first == org.eclipse.swt.graphics.Font.java_class
220
277
  args[0] = FontProxy.new(args[0])
221
278
  end
222
279
  if args.first.is_a?(FontProxy)
@@ -229,7 +286,7 @@ module Glimmer
229
286
  @drawable.requires_shape_disposal = true
230
287
  args = args.first if args.first.is_a?(Array)
231
288
  args.each_with_index do |arg, i|
232
- arg = ColorProxy.new(arg.red, arg.green, arg.blue) if arg.is_a?(RGB)
289
+ arg = ColorProxy.new(arg.red, arg.green, arg.blue) if arg.is_a?(org.eclipse.swt.graphics.RGB)
233
290
  arg = ColorProxy.new(arg) if arg.is_a?(Symbol) || arg.is_a?(::String)
234
291
  arg = arg.swt_color if arg.is_a?(ColorProxy)
235
292
  args[i] = arg
@@ -278,20 +335,22 @@ module Glimmer
278
335
  end
279
336
 
280
337
  def apply_shape_arg_defaults!
338
+ self.x = :default if current_parameter_name?(:x) && x.nil?
339
+ self.y = :default if current_parameter_name?(:y) && y.nil?
340
+ self.dest_x = :default if current_parameter_name?(:dest_x) && dest_x.nil?
341
+ self.dest_y = :default if current_parameter_name?(:dest_y) && dest_y.nil?
342
+ self.width = :default if current_parameter_name?(:width) && width.nil?
343
+ self.height = :default if current_parameter_name?(:height) && height.nil?
281
344
  if @name.include?('rectangle') && round? && @args.size.between?(4, 5)
282
345
  (6 - @args.size).times {@args << 60}
283
346
  elsif @name.include?('rectangle') && gradient? && @args.size == 4
284
- @args << true # vertical is true by default
285
- elsif (@name.include?('text') || @name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && @args.size == 3
286
- @args << true # is_transparent is true by default
347
+ set_attribute('vertical', true, redraw: false)
348
+ elsif (@name.include?('text') || @name.include?('string')) && !@properties.keys.map(&:to_s).include?('background') && @args.size < 4
349
+ set_attribute('is_transparent', true, redraw: false)
287
350
  end
288
351
  if @name.include?('image')
289
352
  @drawable.requires_shape_disposal = true
290
353
  end
291
- self.x = :default if current_parameter_name?(:x) && x.nil?
292
- self.y = :default if current_parameter_name?(:y) && y.nil?
293
- self.dest_x = :default if current_parameter_name?(:dest_x) && dest_x.nil?
294
- self.dest_y = :default if current_parameter_name?(:dest_y) && dest_y.nil?
295
354
  end
296
355
 
297
356
  # Tolerates shape extra args added by user by mistake
@@ -306,6 +365,7 @@ module Glimmer
306
365
  end
307
366
 
308
367
  def amend_method_name_options_based_on_properties!
368
+ @original_method_name = @method_name
309
369
  return if @name == 'point'
310
370
  if @name != 'text' && @name != 'string' && has_some_background? && !has_some_foreground?
311
371
  @options[:fill] = true
@@ -341,11 +401,11 @@ module Glimmer
341
401
  end
342
402
 
343
403
  def current_parameter_name?(attribute_name)
344
- parameter_names.map(&:to_s).include?(ruby_attribute_getter(attribute_name))
404
+ parameter_names.include?(attribute_name.to_s.to_sym)
345
405
  end
346
406
 
347
407
  def parameter_index(attribute_name)
348
- parameter_names.map(&:to_s).index(attribute_name.to_s)
408
+ parameter_names.index(attribute_name.to_s.to_sym)
349
409
  end
350
410
 
351
411
  def set_parameter_attribute(attribute_name, *args)
@@ -357,9 +417,10 @@ module Glimmer
357
417
  parameter_name?(attribute_name) or
358
418
  (respond_to?(attribute_name, super: true) and respond_to?(ruby_attribute_setter(attribute_name), super: true))
359
419
  end
360
-
420
+
361
421
  def set_attribute(attribute_name, *args)
362
422
  options = args.last if args.last.is_a?(Hash)
423
+ args.pop if !options.nil? && !options[:redraw].nil?
363
424
  perform_redraw = @perform_redraw
364
425
  perform_redraw = options[:redraw] if perform_redraw.nil? && !options.nil?
365
426
  perform_redraw = true if perform_redraw.nil?
@@ -372,9 +433,19 @@ module Glimmer
372
433
  end
373
434
  if @content_added && perform_redraw && !drawable.is_disposed
374
435
  @calculated_paint_args = false
436
+ if is_a?(PathSegment)
437
+ root_path&.calculated_path_args = @calculated_path_args = false
438
+ calculated_args_changed!
439
+ root_path&.calculated_args_changed!
440
+ end
375
441
  attribute_name = ruby_attribute_getter(attribute_name)
376
- @calculated_absolute_args = false if (location_parameter_names.map(&:to_s) + ['x_delta', 'y_delta']).include?(attribute_name)
377
-
442
+ if location_parameter_names.map(&:to_s).include?(attribute_name)
443
+ @calculated_args = nil
444
+ parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
445
+ end
446
+ if ['width', 'height'].include?(attribute_name)
447
+ calculated_args_changed_for_defaults!
448
+ end
378
449
  # TODO consider redrawing an image proxy's gc in the future
379
450
  drawable.redraw unless drawable.is_a?(ImageProxy)
380
451
  end
@@ -433,6 +504,7 @@ module Glimmer
433
504
  end
434
505
 
435
506
  def dispose(dispose_images: true, dispose_patterns: true)
507
+ shapes.each { |shape| shape.is_a?(Shape::Path) && shape.dispose }
436
508
  if dispose_patterns
437
509
  @background_pattern&.dispose
438
510
  @background_pattern = nil
@@ -445,27 +517,46 @@ module Glimmer
445
517
  end
446
518
  @parent.shapes.delete(self)
447
519
  end
448
-
520
+
449
521
  def paint(paint_event)
522
+ # pre-paint children an extra-time first when default width/height need to be calculated for defaults
523
+ paint_children(paint_event) if default_width? || default_height?
524
+ paint_self(paint_event)
525
+ # re-paint children from scratch in the special case of pre-calculating parent width/height to re-center within new parent dimensions
526
+ shapes.each(&:calculated_args_changed!) if default_width? || default_height?
527
+ paint_children(paint_event)
528
+ rescue => e
529
+ Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
530
+ Glimmer::Config.logger.error {e.full_message}
531
+ end
532
+
533
+ def paint_self(paint_event)
534
+ @painting = true
450
535
  calculate_paint_args!
536
+ @original_properties_backup = {}
451
537
  @properties.each do |property, args|
452
538
  method_name = attribute_setter(property)
453
- # TODO consider optimization of not setting a background/foreground/font if it didn't change from last shape
539
+ @original_properties_backup[method_name] = paint_event.gc.send(method_name.sub('set', 'get')) rescue nil
454
540
  paint_event.gc.send(method_name, *args)
455
541
  if property == 'transform' && args.first.is_a?(TransformProxy)
456
542
  args.first.swt_transform.dispose
457
543
  end
458
544
  end
459
- self.extent = paint_event.gc.send("#{@name}Extent", *(([string, flags] if respond_to?(:flags)).compact)) if ['text', 'string'].include?(@name)
460
- if !@calculated_absolute_args || parent_shape_absolute_location_changed?
461
- @absolute_args = absolute_args
462
- @calculated_absolute_args = true
545
+ ensure_extent(paint_event)
546
+ if !@calculated_args || parent_shape_absolute_location_changed?
547
+ @calculated_args = calculated_args
463
548
  end
464
- paint_event.gc.send(@method_name, *@absolute_args)
465
- paint_children(paint_event)
549
+ # paint unless parent's calculated args are not calculated yet, meaning it is about to get painted and trigger a paint on this child anyways
550
+ paint_event.gc.send(@method_name, *@calculated_args) unless parent.is_a?(Shape) && !parent.calculated_args?
551
+ @original_properties_backup.each do |method_name, value|
552
+ paint_event.gc.send(method_name, value)
553
+ end
554
+ @painting = false
466
555
  rescue => e
467
- Glimmer::Config.logger.error {"Error encountered in painting shape: #{self.inspect}"}
556
+ Glimmer::Config.logger.error {"Error encountered in painting shape (#{self.inspect}) with calculated args (#{@calculated_args}) and args (#{@args})"}
468
557
  Glimmer::Config.logger.error {e.full_message}
558
+ ensure
559
+ @painting = false
469
560
  end
470
561
 
471
562
  def paint_children(paint_event)
@@ -474,6 +565,21 @@ module Glimmer
474
565
  end
475
566
  end
476
567
 
568
+ def ensure_extent(paint_event)
569
+ old_extent = @extent
570
+ if ['text', 'string'].include?(@name)
571
+ extent_args = [string]
572
+ extent_flags = SWTProxy[:draw_transparent] if current_parameter_name?(:is_transparent) && is_transparent
573
+ extent_flags = flags if current_parameter_name?(:flags)
574
+ extent_args << extent_flags unless extent_flags.nil?
575
+ self.extent = paint_event.gc.send("#{@name}Extent", *extent_args)
576
+ end
577
+ if !@extent.nil? && (old_extent&.x != @extent&.x || old_extent&.y != @extent&.y)
578
+ calculated_args_changed!
579
+ parent.calculated_args_changed_for_defaults! if parent.is_a?(Shape)
580
+ end
581
+ end
582
+
477
583
  def expanded_shapes
478
584
  if shapes.to_a.any?
479
585
  shapes.map do |shape|
@@ -487,69 +593,200 @@ module Glimmer
487
593
  def parent_shape_absolute_location_changed?
488
594
  (parent.is_a?(Shape) && (parent.absolute_x != @parent_absolute_x || parent.absolute_y != @parent_absolute_y))
489
595
  end
596
+
597
+ def calculated_args_changed!(children: true)
598
+ # TODO add a children: true option to enable setting to false to avoid recalculating children args
599
+ @calculated_args = nil
600
+ shapes.each(&:calculated_args_changed!) if children
601
+ end
602
+
603
+ def calculated_args_changed_for_defaults!
604
+ has_default_dimensions = default_width? || default_height?
605
+ parent_calculated_args_changed_for_defaults = has_default_dimensions
606
+ @calculated_args = nil if default_x? || default_y? || has_default_dimensions
607
+ if has_default_dimensions && parent.is_a?(Shape)
608
+ parent.calculated_args_changed_for_defaults!
609
+ elsif @content_added && !drawable.is_disposed
610
+ # TODO consider optimizing in the future if needed by ensuring one redraw for all parents in the hierarchy at the end instead of doing one per parent that needs it
611
+ drawable.redraw if !@painting && !drawable.is_a?(ImageProxy)
612
+ end
613
+ end
614
+
615
+ def calculated_args?
616
+ !!@calculated_args
617
+ end
490
618
 
491
619
  # args translated to absolute coordinates
492
- def absolute_args
493
- return @args if !default_x? && !default_y? && @x_delta == 0 && @y_delta == 0 && parent.is_a?(Drawable)
620
+ def calculated_args
621
+ return @args if !default_x? && !default_y? && !default_width? && !default_height? && parent.is_a?(Drawable)
622
+ # Note: Must set x and move_by because not all shapes have a real x and some must translate all their points with move_by
623
+ # TODO change that by setting a bounding box for all shapes with a calculated top-left x, y and
624
+ # a setter that does the moving inside them instead so that I could rely on absolute_x and absolute_y
625
+ # here to get the job done of calculating absolute args
494
626
  @perform_redraw = false
495
627
  original_x = nil
496
628
  original_y = nil
629
+ original_width = nil
630
+ original_height = nil
631
+ if parent.is_a?(Shape)
632
+ @parent_absolute_x = parent.absolute_x
633
+ @parent_absolute_y = parent.absolute_y
634
+ end
635
+ if default_width?
636
+ original_width = width
637
+ self.width = default_width + default_width_delta
638
+ end
639
+ if default_height?
640
+ original_height = height
641
+ self.height = default_height + default_height_delta
642
+ end
497
643
  if default_x?
498
644
  original_x = x
499
- self.x = default_x
645
+ self.x = default_x + default_x_delta
500
646
  end
501
647
  if default_y?
502
648
  original_y = y
503
- self.y = default_y
649
+ self.y = default_y + default_y_delta
504
650
  end
505
- move_by(@x_delta, @y_delta)
506
651
  if parent.is_a?(Shape)
507
- @parent_absolute_x = parent.absolute_x
508
- @parent_absolute_y = parent.absolute_y
509
- move_by(parent.absolute_x, parent.absolute_y)
510
- calculated_absolute_args = @args.clone
511
- move_by(-1*parent.absolute_x, -1*parent.absolute_y)
652
+ move_by(@parent_absolute_x, @parent_absolute_y)
653
+ result_args = @args.clone
654
+ move_by(-1*@parent_absolute_x, -1*@parent_absolute_y)
512
655
  else
513
- calculated_absolute_args = @args.clone
656
+ result_args = @args.clone
514
657
  end
515
- move_by(-1*@x_delta, -1*@y_delta)
516
658
  if original_x
517
659
  self.x = original_x
518
660
  end
519
661
  if original_y
520
662
  self.y = original_y
521
663
  end
664
+ if original_width
665
+ self.width = original_width
666
+ end
667
+ if original_height
668
+ self.height = original_height
669
+ end
522
670
  @perform_redraw = true
523
- calculated_absolute_args
671
+ result_args
524
672
  end
525
673
 
526
674
  def default_x?
527
- current_parameter_name?('x') && (x.nil? || x.to_s == 'default')
675
+ current_parameter_name?(:x) and
676
+ (x.nil? || x.to_s == 'default' || (x.is_a?(Array) && x.first.to_s == 'default'))
528
677
  end
529
678
 
530
679
  def default_y?
531
- current_parameter_name?('y') && (y.nil? || y.to_s == 'default')
680
+ current_parameter_name?(:y) and
681
+ (y.nil? || y.to_s == 'default' || (y.is_a?(Array) && y.first.to_s == 'default'))
682
+ end
683
+
684
+ def default_width?
685
+ return false unless current_parameter_name?(:width)
686
+ width = self.width
687
+ (width.nil? || width == :default || width == 'default' || (width.is_a?(Array) && (width.first.to_s == :default || width.first.to_s == 'default')))
688
+ end
689
+
690
+ def default_height?
691
+ return false unless current_parameter_name?(:height)
692
+ height = self.height
693
+ (height.nil? || height == :default || height == 'default' || (height.is_a?(Array) && (height.first.to_s == :default || height.first.to_s == 'default')))
532
694
  end
533
695
 
534
696
  def default_x
535
- if respond_to?(:width) && width && parent.respond_to?(:width) && parent.width
536
- (parent.width - width) / 2
537
- else
538
- 0
539
- end
697
+ result = ((parent.size.x - size.x) / 2)
698
+ result += parent.bounds.x - parent.absolute_x if parent.is_a?(Shape) && parent.irregular?
699
+ result
540
700
  end
541
701
 
542
702
  def default_y
543
- if respond_to?(:height) && height && parent.respond_to?(:height) && parent.height
544
- (parent.height - height) / 2
545
- else
546
- 0
703
+ result = ((parent.size.y - size.y) / 2)
704
+ result += parent.bounds.y - parent.absolute_y if parent.is_a?(Shape) && parent.irregular?
705
+ result
706
+ end
707
+
708
+ def default_width
709
+ # TODO consider caching
710
+ x_ends = shapes.map do |shape|
711
+ shape_width = shape.calculated_width.to_f
712
+ shape_x = shape.default_x? ? 0 : shape.x.to_f
713
+ shape_x + shape_width
714
+ end
715
+ x_ends.max.to_f
716
+ end
717
+
718
+ def default_height
719
+ # TODO consider caching
720
+ y_ends = shapes.map do |shape|
721
+ shape_height = shape.calculated_height.to_f
722
+ shape_y = shape.default_y? ? 0 : shape.y.to_f
723
+ shape_y + shape_height
547
724
  end
725
+ y_ends.max.to_f
726
+ end
727
+
728
+ def calculated_width
729
+ default_width? ? (default_width + default_width_delta) : width
730
+ end
731
+
732
+ def calculated_height
733
+ default_height? ? (default_height + default_height_delta) : height
734
+ end
735
+
736
+ def default_x_delta
737
+ return 0 unless default_x? && x.is_a?(Array)
738
+ x[1].to_f
739
+ end
740
+
741
+ def default_y_delta
742
+ return 0 unless default_y? && y.is_a?(Array)
743
+ y[1].to_f
744
+ end
745
+
746
+ def default_width_delta
747
+ return 0 unless default_width? && width.is_a?(Array)
748
+ width[1].to_f
749
+ end
750
+
751
+ def default_height_delta
752
+ return 0 unless default_height? && height.is_a?(Array)
753
+ height[1].to_f
754
+ end
755
+
756
+ def default_x_delta=(delta)
757
+ return unless default_x?
758
+ self.x = [:default, delta]
759
+ end
760
+
761
+ def default_y_delta=(delta)
762
+ return unless default_y?
763
+ self.y = [:default, delta]
764
+ end
765
+
766
+ def default_width_delta=(delta)
767
+ return unless default_width?
768
+ self.width = [:default, delta]
769
+ end
770
+
771
+ def default_height_delta=(delta)
772
+ return unless default_height?
773
+ self.height = [:default, delta]
774
+ end
775
+
776
+ def calculated_x
777
+ result = default_x? ? default_x : self.x
778
+ result += default_x_delta
779
+ result
780
+ end
781
+
782
+ def calculated_y
783
+ result = default_y? ? default_y : self.y
784
+ result += default_y_delta
785
+ result
548
786
  end
549
787
 
550
788
  def absolute_x
551
- x = default_x? ? default_x : self.x
552
- x += @x_delta
789
+ x = calculated_x
553
790
  if parent.is_a?(Shape)
554
791
  parent.absolute_x + x
555
792
  else
@@ -558,8 +795,7 @@ module Glimmer
558
795
  end
559
796
 
560
797
  def absolute_y
561
- y = default_y? ? default_y : self.y
562
- y += @y_delta
798
+ y = calculated_y
563
799
  if parent.is_a?(Shape)
564
800
  parent.absolute_y + y
565
801
  else
@@ -567,12 +803,19 @@ module Glimmer
567
803
  end
568
804
  end
569
805
 
806
+ # Overriding inspect to avoid printing very long shape hierarchies
807
+ def inspect
808
+ "#<#{self.class.name}:0x#{self.hash.to_s(16)} args=#{@args.inspect}, properties=#{@properties.inspect}}>"
809
+ rescue => e
810
+ "#<#{self.class.name}:0x#{self.hash.to_s(16)}"
811
+ end
812
+
570
813
  def calculate_paint_args!
571
814
  unless @calculated_paint_args
572
815
  if @name == 'pixel'
573
816
  @name = 'point'
574
817
  # optimized performance calculation for pixel points
575
- if !@properties[:foreground].is_a?(Color)
818
+ if !@properties[:foreground].is_a?(org.eclipse.swt.graphics.Color)
576
819
  if @properties[:foreground].is_a?(Array)
577
820
  @properties[:foreground] = ColorProxy.new(@properties[:foreground], ensure_bounds: false)
578
821
  end
@@ -589,7 +832,7 @@ module Glimmer
589
832
  # TODO regarding alpha, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
590
833
  @properties['alpha'] ||= [255]
591
834
  @properties['font'] = [@drawable.font] if @drawable.respond_to?(:font) && draw? && !@properties.keys.map(&:to_s).include?('font')
592
- # TODO regarding transform, make sure to reset it to parent stored alpha once we allow setting shape properties on parents directly without shapes
835
+ # TODO regarding transform, make sure to reset it to parent stored transform once we allow setting shape properties on parents directly without shapes
593
836
  # Also do that with all future-added properties
594
837
  @properties['transform'] = [nil] if @drawable.respond_to?(:transform) && !@properties.keys.map(&:to_s).include?('transform')
595
838
  @properties.each do |property, args|