glimmer-dsl-libui 0.5.6 → 0.5.9

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: 48926d5aab22eec664c80bc9ebe5a94a841e4b6173cca343ad9876fb33924b03
4
- data.tar.gz: 890dba3f171225448aad354ec767819faaf3cb59e48764adc63b0f35bcbc0fe3
3
+ metadata.gz: 6a198df8af13246ffee6aa56434bfa89740e9211e19da28fb0dddb1705000eec
4
+ data.tar.gz: cb4c0b685980983fb17cc3f501165d8fa788a56ce4da9ced03630a78329045af
5
5
  SHA512:
6
- metadata.gz: 94650e60ccb1ed496dd682f937e4a91a3cffad37cfc3da17c462fd2db7349e06af9572c425f1b90d8a3a522c09eddd2b7eed6b4982c31a77fff97322aff6bbed
7
- data.tar.gz: fc41c1e9882b42155729547ee2d83e43a40a64ae56c2523ff880cd2f818357fb42328a72b79810ccfb75fc7356ac70910e1d4202249488cb4f8aef9d3a8034fe
6
+ metadata.gz: 3809a094cb6cd607d94c0d37c10f67213c3bab4cc3d74d7d8d3c4d49514da75e3ae2b99328d09291f4905b7d3c85aea14f6935f22d3cac249edf533eff5e575d
7
+ data.tar.gz: 3efefe3c53b4520b077165df099f9427f112c7cc1acafed0d878fe187d65657264b6317de470e6330a12c63bde1edd4694455bc675be87f8e374ee0113235ae0
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Change Log
2
2
 
3
+ ## 0.5.9
4
+
5
+ - Upgrade to glimmer v2.7.3
6
+ - Upgrade to perfect-shape v1.0.3
7
+ - Support `path`/`figure`/shape `#contain?`, `#include?`, and `#bounding_box` methods via perfect-shape algorithms applying the path winding rule (aka `draw_fill_mode`)
8
+
9
+ ## 0.5.8
10
+
11
+ - Support `code_area` class-based custom control as a code-syntax-highlighted `area` control using the `rouge` gem
12
+ - `examples/basic_code_area.rb`
13
+ - Handle hex colors that have a 3-digit shorthand
14
+ - Stop annoying false negative logs when using `Glimmer::LibUI::CustomWindow`
15
+
16
+ ## 0.5.7
17
+
18
+ - Support Custom Window keywords (aka Applications) using `Glimmer::LibUI::CustomWindow` or alias of `Glimmer::LibUI::Application`
19
+ - Refactor examples/class_based_custom_controls.rb to use `Glimmer::LibUI::Application` (alias: `Glimmer::LibUI::CustomWindow`)
20
+
3
21
  ## 0.5.6
4
22
 
5
23
  - Upgrade to glimmer 2.7.1 and document its support for keyed data-binding
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 LibUI 0.5.6
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 LibUI 0.5.9
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -322,11 +322,11 @@ Mac | Windows | Linux
322
322
 
323
323
  [Check Out Many More Examples Over Here!](#examples)
324
324
 
325
- ![glimmer-dsl-libui-mac-snake.gif](images/glimmer-dsl-libui-mac-snake.gif)
325
+ [![glimmer-dsl-libui-mac-snake.gif](images/glimmer-dsl-libui-mac-snake.gif)](https://github.com/AndyObtiva/glimmer-dsl-libui#snake)
326
326
 
327
- ![glimmer-dsl-libui-mac-color-the-circles.gif](images/glimmer-dsl-libui-mac-color-the-circles.gif)
327
+ [![glimmer-dsl-libui-mac-color-the-circles.gif](images/glimmer-dsl-libui-mac-color-the-circles.gif)](https://github.com/AndyObtiva/glimmer-dsl-libui#color-the-circles)
328
328
 
329
- ![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)
329
+ [![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)](https://github.com/AndyObtiva/glimmer-dsl-libui#tetris)
330
330
 
331
331
  NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is 100% feature-complete with regards to covering the C [libui](https://github.com/andlabs/libui) library API and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha, which is why [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) cannot be declared v1.0.0 yet). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
332
332
 
@@ -405,6 +405,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
405
405
  - [Basic Image](#basic-image)
406
406
  - [Basic Transform](#basic-transform)
407
407
  - [Basic Draw Text](#basic-draw-text)
408
+ - [Basic Code Area](#basic-code-area)
408
409
  - [Advanced Examples](#advanced-examples)
409
410
  - [Area Gallery](#area-gallery)
410
411
  - [Button Counter](#button-counter)
@@ -577,7 +578,7 @@ gem install glimmer-dsl-libui
577
578
  Or install via Bundler `Gemfile`:
578
579
 
579
580
  ```ruby
580
- gem 'glimmer-dsl-libui', '~> 0.5.6'
581
+ gem 'glimmer-dsl-libui', '~> 0.5.9'
581
582
  ```
582
583
 
583
584
  Test that installation worked by running the [Meta-Example](#examples):
@@ -590,14 +591,40 @@ Mac | Windows | Linux
590
591
  ----|---------|------
591
592
  ![glimmer-dsl-libui-mac-meta-example.png](images/glimmer-dsl-libui-mac-meta-example.png) | ![glimmer-dsl-libui-windows-meta-example.png](images/glimmer-dsl-libui-windows-meta-example.png) | ![glimmer-dsl-libui-linux-meta-example.png](images/glimmer-dsl-libui-linux-meta-example.png)
592
593
 
593
- Now to use [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui), add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
594
+ Now to use [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui), add `require 'glimmer-dsl-libui'` at the top.
594
595
 
595
- Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
596
+ Afterwards, `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
597
+
598
+ Alternatively, `include Glimmer::LibUI::Application` to conveniently declare the GUI `body` and run via the `::launch` method (`Glimmer::LibUI::Application` is an alias for `Glimmer::LibUI::CustomWindow` since that is what it represents).
599
+
600
+ Example including `Glimmer::LibUI::Application` (you may copy/paste in [`girb`](#girb-glimmer-irb)):
601
+
602
+ ```ruby
603
+ require 'glimmer-dsl-libui'
604
+
605
+ class SomeGlimmerApp
606
+ include Glimmer::LibUI::Application
607
+
608
+ body {
609
+ window('hello world', 300, 200) {
610
+ button('Button') {
611
+ on_clicked do
612
+ puts 'Button Clicked'
613
+ end
614
+ }
615
+ }
616
+ }
617
+ end
618
+
619
+ SomeGlimmerApp.launch
620
+ ```
621
+
622
+ Example including `Glimmer` and manually implementing the `#launch` method (you may copy/paste in [`girb`](#girb-glimmer-irb)):
596
623
 
597
624
  ```ruby
598
625
  require 'glimmer-dsl-libui'
599
626
 
600
- class Application
627
+ class SomeGlimmerApp
601
628
  include Glimmer
602
629
 
603
630
  def launch
@@ -611,7 +638,23 @@ class Application
611
638
  end
612
639
  end
613
640
 
614
- Application.new.launch
641
+ SomeGlimmerApp.new.launch
642
+ ```
643
+
644
+ Example including `Glimmer` at the top-level scope just for some prototyping/demoing/testing (you may copy/paste in [`girb`](#girb-glimmer-irb)):
645
+
646
+ ```ruby
647
+ require 'glimmer-dsl-libui'
648
+
649
+ include Glimmer
650
+
651
+ window('hello world', 300, 200) {
652
+ button('Button') {
653
+ on_clicked do
654
+ puts 'Button Clicked'
655
+ end
656
+ }
657
+ }.show
615
658
  ```
616
659
 
617
660
  If you are new to [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), check out [Girb](#girb-glimmer-irb) and [Examples](#examples) to quickly learn through copy/paste. You may refer to the [API](#api) later on once you have gotten your feet wet with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) and need more detailed reference information.
@@ -675,6 +718,7 @@ Keyword(Args) | Properties | Listeners
675
718
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
676
719
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
677
720
  `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
721
+ `code_area` | `language` (String) (default: `'ruby'`), `theme` (String) (default: `'glimmer'`), `code` (String) | None
678
722
  `combobox` | `items` (`Array` of `String`), `selected` (`Integer`), `selected_item` (`String`) | `on_selected`
679
723
  `color_button` | `color` (Array of `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float`), `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float` | `on_changed`
680
724
  `date_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
@@ -1704,6 +1748,136 @@ window('Method-Based Custom Keyword') {
1704
1748
 
1705
1749
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1706
1750
 
1751
+ You can also define Custom Window keywords, that is custom controls with `window` being the body root. These are also known as Applications. To define a Custom Window, you `include Glimmer::LibUI::CustomWindow` or `include Glimmer:LibUI::Application` and then you can invoke the `::launch` method on the class.
1752
+
1753
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1754
+
1755
+ ```ruby
1756
+ require 'glimmer-dsl-libui'
1757
+ require 'facets'
1758
+
1759
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
1760
+
1761
+ class FormField
1762
+ include Glimmer::LibUI::CustomControl
1763
+
1764
+ options :model, :attribute
1765
+
1766
+ body {
1767
+ entry { |e|
1768
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
1769
+ text <=> [model, attribute]
1770
+ }
1771
+ }
1772
+ end
1773
+
1774
+ class AddressForm
1775
+ include Glimmer::LibUI::CustomControl
1776
+
1777
+ options :address
1778
+
1779
+ body {
1780
+ form {
1781
+ form_field(model: address, attribute: :street)
1782
+ form_field(model: address, attribute: :p_o_box)
1783
+ form_field(model: address, attribute: :city)
1784
+ form_field(model: address, attribute: :state)
1785
+ form_field(model: address, attribute: :zip_code)
1786
+ }
1787
+ }
1788
+ end
1789
+
1790
+ class LabelPair
1791
+ include Glimmer::LibUI::CustomControl
1792
+
1793
+ options :model, :attribute, :value
1794
+
1795
+ body {
1796
+ horizontal_box {
1797
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
1798
+ label(value.to_s) {
1799
+ text <= [model, attribute]
1800
+ }
1801
+ }
1802
+ }
1803
+ end
1804
+
1805
+ class AddressView
1806
+ include Glimmer::LibUI::CustomControl
1807
+
1808
+ options :address
1809
+
1810
+ body {
1811
+ vertical_box {
1812
+ address.each_pair do |attribute, value|
1813
+ label_pair(model: address, attribute: attribute, value: value)
1814
+ end
1815
+ }
1816
+ }
1817
+ end
1818
+
1819
+ class ClassBasedCustomControls
1820
+ include Glimmer::LibUI::Application # alias: Glimmer::LibUI::CustomWindow
1821
+
1822
+ before_body do
1823
+ @address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
1824
+ @address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
1825
+ end
1826
+
1827
+ body {
1828
+ window('Class-Based Custom Keyword') {
1829
+ margined true
1830
+
1831
+ horizontal_box {
1832
+ vertical_box {
1833
+ label('Address 1') {
1834
+ stretchy false
1835
+ }
1836
+
1837
+ address_form(address: @address1)
1838
+
1839
+ horizontal_separator {
1840
+ stretchy false
1841
+ }
1842
+
1843
+ label('Address 1 (Saved)') {
1844
+ stretchy false
1845
+ }
1846
+
1847
+ address_view(address: @address1)
1848
+ }
1849
+
1850
+ vertical_separator {
1851
+ stretchy false
1852
+ }
1853
+
1854
+ vertical_box {
1855
+ label('Address 2') {
1856
+ stretchy false
1857
+ }
1858
+
1859
+ address_form(address: @address2)
1860
+
1861
+ horizontal_separator {
1862
+ stretchy false
1863
+ }
1864
+
1865
+ label('Address 2 (Saved)') {
1866
+ stretchy false
1867
+ }
1868
+
1869
+ address_view(address: @address2)
1870
+ }
1871
+ }
1872
+ }
1873
+ }
1874
+ end
1875
+
1876
+ ClassBasedCustomControls.launch
1877
+ ```
1878
+
1879
+ ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1880
+
1707
1881
  The [`area`](#area-api) control can be utilized to build non-native custom controls from scratch by leveraging vector graphics, formattable text, keyboard events, and mouse events. This is demonstrated in the [Area-Based Custom Controls](#area-based-custom-controls) example.
1708
1882
 
1709
1883
  Defining custom keywords enables unlimited extension of the [Glimmer GUI DSL](#glimmer-gui-dsl). The sky is the limit on what can be done with custom keywords as a result. You can compose new visual vocabulary to build applications in any domain from higher concepts rather than [mere standard controls](#supported-keywords). For example, in a traffic signaling app, you could define `street`, `light_signal`, `traffic_sign`, and `car` as custom keywords and build your application from these concepts directly, saving enormous time and achieving much higher productivity.
@@ -4857,6 +5031,62 @@ end
4857
5031
  BasicDrawText.new.launch
4858
5032
  ```
4859
5033
 
5034
+ #### Basic Code Area
5035
+
5036
+ [examples/basic_code_area.rb](examples/basic_code_area.rb)
5037
+
5038
+ Run with this command from the root of the project if you cloned the project:
5039
+
5040
+ ```
5041
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_code_area.rb
5042
+ ```
5043
+
5044
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
5045
+
5046
+ ```
5047
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_code_area'"
5048
+ ```
5049
+
5050
+ Mac | Windows | Linux
5051
+ ----|---------|------
5052
+ ![glimmer-dsl-libui-mac-basic-code-area.png](images/glimmer-dsl-libui-mac-basic-code-area.png) | ![glimmer-dsl-libui-windows-basic-code-area.png](images/glimmer-dsl-libui-windows-basic-code-area.png) | ![glimmer-dsl-libui-linux-basic-code-area.png](images/glimmer-dsl-libui-linux-basic-code-area.png)
5053
+
5054
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
5055
+
5056
+ ```ruby
5057
+ require 'glimmer-dsl-libui'
5058
+
5059
+ class BasicCodeArea
5060
+ include Glimmer::LibUI::Application
5061
+
5062
+ before_body do
5063
+ @code = <<~CODE
5064
+ # Greets target with greeting
5065
+ def greet(greeting: 'Hello', target: 'World')
5066
+
5067
+ puts "\#{greeting}, \#{target}!"
5068
+ end
5069
+
5070
+ greet
5071
+ greet(target: 'Robert')
5072
+ greet(greeting: 'Aloha')
5073
+ greet(greeting: 'Aloha', target: 'Nancy')
5074
+ greet(greeting: 'Howdy', target: 'Doodle')
5075
+ CODE
5076
+ end
5077
+
5078
+ body {
5079
+ window('Basic Code Area', 400, 300) {
5080
+ margined true
5081
+
5082
+ code_area(language: 'ruby', code: @code)
5083
+ }
5084
+ }
5085
+ end
5086
+
5087
+ BasicCodeArea.launch
5088
+ ```
5089
+
4860
5090
  ### Advanced Examples
4861
5091
 
4862
5092
  #### Area Gallery
@@ -8837,8 +9067,6 @@ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version
8837
9067
  require 'glimmer-dsl-libui'
8838
9068
  require 'facets'
8839
9069
 
8840
- include Glimmer
8841
-
8842
9070
  Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
8843
9071
 
8844
9072
  class FormField
@@ -8899,54 +9127,64 @@ class AddressView
8899
9127
  }
8900
9128
  end
8901
9129
 
8902
- address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
8903
- address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
8904
-
8905
- window('Class-Based Custom Keyword') {
8906
- margined true
9130
+ class ClassBasedCustomControls
9131
+ include Glimmer::LibUI::Application # alias: Glimmer::LibUI::CustomWindow
8907
9132
 
8908
- horizontal_box {
8909
- vertical_box {
8910
- label('Address 1') {
8911
- stretchy false
8912
- }
8913
-
8914
- address_form(address: address1)
8915
-
8916
- horizontal_separator {
8917
- stretchy false
8918
- }
8919
-
8920
- label('Address 1 (Saved)') {
8921
- stretchy false
8922
- }
8923
-
8924
- address_view(address: address1)
8925
- }
8926
-
8927
- vertical_separator {
8928
- stretchy false
8929
- }
8930
-
8931
- vertical_box {
8932
- label('Address 2') {
8933
- stretchy false
8934
- }
8935
-
8936
- address_form(address: address2)
8937
-
8938
- horizontal_separator {
8939
- stretchy false
8940
- }
9133
+ before_body do
9134
+ @address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
9135
+ @address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
9136
+ end
9137
+
9138
+ body {
9139
+ window('Class-Based Custom Keyword') {
9140
+ margined true
8941
9141
 
8942
- label('Address 2 (Saved)') {
8943
- stretchy false
9142
+ horizontal_box {
9143
+ vertical_box {
9144
+ label('Address 1') {
9145
+ stretchy false
9146
+ }
9147
+
9148
+ address_form(address: @address1)
9149
+
9150
+ horizontal_separator {
9151
+ stretchy false
9152
+ }
9153
+
9154
+ label('Address 1 (Saved)') {
9155
+ stretchy false
9156
+ }
9157
+
9158
+ address_view(address: @address1)
9159
+ }
9160
+
9161
+ vertical_separator {
9162
+ stretchy false
9163
+ }
9164
+
9165
+ vertical_box {
9166
+ label('Address 2') {
9167
+ stretchy false
9168
+ }
9169
+
9170
+ address_form(address: @address2)
9171
+
9172
+ horizontal_separator {
9173
+ stretchy false
9174
+ }
9175
+
9176
+ label('Address 2 (Saved)') {
9177
+ stretchy false
9178
+ }
9179
+
9180
+ address_view(address: @address2)
9181
+ }
8944
9182
  }
8945
-
8946
- address_view(address: address2)
8947
9183
  }
8948
9184
  }
8949
- }.show
9185
+ end
9186
+
9187
+ ClassBasedCustomControls.launch
8950
9188
  ```
8951
9189
 
8952
9190
  #### Area-Based Custom Controls
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.6
1
+ 0.5.9
@@ -0,0 +1,31 @@
1
+ require 'glimmer-dsl-libui'
2
+
3
+ class BasicCodeArea
4
+ include Glimmer::LibUI::Application
5
+
6
+ before_body do
7
+ @code = <<~CODE
8
+ # Greets target with greeting
9
+ def greet(greeting: 'Hello', target: 'World')
10
+
11
+ puts "\#{greeting}, \#{target}!"
12
+ end
13
+
14
+ greet
15
+ greet(target: 'Robert')
16
+ greet(greeting: 'Aloha')
17
+ greet(greeting: 'Aloha', target: 'Nancy')
18
+ greet(greeting: 'Howdy', target: 'Doodle')
19
+ CODE
20
+ end
21
+
22
+ body {
23
+ window('Basic Code Area', 400, 300) {
24
+ margined true
25
+
26
+ code_area(language: 'ruby', code: @code)
27
+ }
28
+ }
29
+ end
30
+
31
+ BasicCodeArea.launch
@@ -1,8 +1,6 @@
1
1
  require 'glimmer-dsl-libui'
2
2
  require 'facets'
3
3
 
4
- include Glimmer
5
-
6
4
  Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
7
5
 
8
6
  class FormField
@@ -63,51 +61,61 @@ class AddressView
63
61
  }
64
62
  end
65
63
 
66
- address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
67
- address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
68
-
69
- window('Class-Based Custom Keyword') {
70
- margined true
64
+ class ClassBasedCustomControls
65
+ include Glimmer::LibUI::Application # alias: Glimmer::LibUI::CustomWindow
71
66
 
72
- horizontal_box {
73
- vertical_box {
74
- label('Address 1') {
75
- stretchy false
76
- }
77
-
78
- address_form(address: address1)
79
-
80
- horizontal_separator {
81
- stretchy false
82
- }
83
-
84
- label('Address 1 (Saved)') {
85
- stretchy false
86
- }
87
-
88
- address_view(address: address1)
89
- }
90
-
91
- vertical_separator {
92
- stretchy false
93
- }
94
-
95
- vertical_box {
96
- label('Address 2') {
97
- stretchy false
98
- }
99
-
100
- address_form(address: address2)
101
-
102
- horizontal_separator {
103
- stretchy false
104
- }
67
+ before_body do
68
+ @address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
69
+ @address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
70
+ end
71
+
72
+ body {
73
+ window('Class-Based Custom Keyword') {
74
+ margined true
105
75
 
106
- label('Address 2 (Saved)') {
107
- stretchy false
76
+ horizontal_box {
77
+ vertical_box {
78
+ label('Address 1') {
79
+ stretchy false
80
+ }
81
+
82
+ address_form(address: @address1)
83
+
84
+ horizontal_separator {
85
+ stretchy false
86
+ }
87
+
88
+ label('Address 1 (Saved)') {
89
+ stretchy false
90
+ }
91
+
92
+ address_view(address: @address1)
93
+ }
94
+
95
+ vertical_separator {
96
+ stretchy false
97
+ }
98
+
99
+ vertical_box {
100
+ label('Address 2') {
101
+ stretchy false
102
+ }
103
+
104
+ address_form(address: @address2)
105
+
106
+ horizontal_separator {
107
+ stretchy false
108
+ }
109
+
110
+ label('Address 2 (Saved)') {
111
+ stretchy false
112
+ }
113
+
114
+ address_view(address: @address2)
115
+ }
108
116
  }
109
-
110
- address_view(address: address2)
111
117
  }
112
118
  }
113
- }.show
119
+ end
120
+
121
+ ClassBasedCustomControls.launch
Binary file
@@ -24,6 +24,7 @@ require 'glimmer/dsl/expression'
24
24
  require 'glimmer/dsl/parent_expression'
25
25
  require 'glimmer/dsl/top_level_expression'
26
26
  require 'glimmer/libui/custom_control'
27
+ require 'glimmer/libui/custom_window'
27
28
 
28
29
  module Glimmer
29
30
  module DSL
@@ -85,7 +85,7 @@ module Glimmer
85
85
  end
86
86
 
87
87
  def post_add_content
88
- unless parent_proxy.is_a?(Box)
88
+ if OS.linux? && parent_proxy.is_a?(WindowProxy)
89
89
  unless @content_added
90
90
  original_parent_proxy = @parent_proxy
91
91
  @vertical_box_parent_proxy = ControlProxy.create('vertical_box', parent_proxy, []) {} # block prevents calling post add content
@@ -23,6 +23,7 @@ require 'glimmer/libui/control_proxy'
23
23
  require 'glimmer/libui/control_proxy/area_proxy'
24
24
  require 'glimmer/libui/parent'
25
25
  require 'glimmer/libui/control_proxy/transformable'
26
+ require 'glimmer/libui/perfect_shaped'
26
27
 
27
28
  module Glimmer
28
29
  module LibUI
@@ -32,6 +33,7 @@ module Glimmer
32
33
  # Follows the Proxy Design Pattern
33
34
  class PathProxy < ControlProxy
34
35
  include Parent
36
+ include PerfectShaped
35
37
  prepend Transformable
36
38
 
37
39
  def initialize(keyword, parent, args, &block)
@@ -160,6 +162,30 @@ module Glimmer
160
162
  @parent_proxy&.request_auto_redraw
161
163
  end
162
164
 
165
+ def perfect_shape
166
+ perfect_shape_dependencies = [draw_fill_mode, children]
167
+ if perfect_shape_dependencies != @perfect_shape_dependencies
168
+ draw_fill_mode, children = @perfect_shape_dependencies = perfect_shape_dependencies
169
+ shapes = children.map(&:perfect_shape)
170
+ new_shapes = []
171
+ shapes.each do |shape|
172
+ if shape.is_a?(PerfectShape::Path)
173
+ new_shapes += shape.basic_shapes
174
+ else
175
+ new_shapes << shape
176
+ end
177
+ end
178
+ shapes = new_shapes
179
+ winding_rule = draw_fill_mode == :winding ? :wind_non_zero : :wind_even_odd
180
+ @perfect_shape = PerfectShape::Path.new(
181
+ shapes: shapes,
182
+ winding_rule: winding_rule,
183
+ line_to_complex_shapes: true
184
+ )
185
+ end
186
+ @perfect_shape
187
+ end
188
+
163
189
  private
164
190
 
165
191
  def build_control
@@ -401,4 +401,4 @@ module Glimmer
401
401
  end
402
402
  end
403
403
 
404
- Dir[File.expand_path('./control_proxy/*.rb', __dir__)].each {|f| require f}
404
+ Dir[File.expand_path("./#{File.basename(__FILE__, '.rb')}/*.rb", __dir__)].each {|f| require f}
@@ -0,0 +1,65 @@
1
+ class CodeArea
2
+ class << self
3
+ def languages
4
+ require 'rouge'
5
+ Rouge::Lexer.all.map {|lexer| lexer.tag}.sort
6
+ end
7
+
8
+ def lexers
9
+ require 'rouge'
10
+ Rouge::Lexer.all.sort_by(&:title)
11
+ end
12
+ end
13
+
14
+ include Glimmer::LibUI::CustomControl
15
+
16
+ REGEX_COLOR_HEX6 = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/
17
+
18
+ option :language, default: 'ruby'
19
+ option :theme, default: 'glimmer'
20
+ option :code
21
+
22
+ body {
23
+ area {
24
+ rectangle(0, 0, 8000, 8000) {
25
+ fill :white
26
+ }
27
+ text {
28
+ default_font family: OS.mac? ? 'Consolas' : 'Courier', size: 13, weight: :medium, italic: :normal, stretch: :normal
29
+
30
+ syntax_highlighting(code).each do |token|
31
+ style_data = Rouge::Theme.find(theme).new.style_for(token[:token_type])
32
+
33
+ string(token[:token_text]) {
34
+ color style_data[:fg] || :black
35
+ background style_data[:bg] || :white
36
+ }
37
+ end
38
+ }
39
+ }
40
+ }
41
+
42
+ def lexer
43
+ require 'rouge'
44
+ require 'glimmer-dsl-libui/ext/rouge/theme/glimmer'
45
+ # TODO Try to use Rouge::Lexer.find_fancy('guess', code) in the future to guess the language or otherwise detect it from file extension
46
+ @lexer ||= Rouge::Lexer.find_fancy(language)
47
+ @lexer ||= Rouge::Lexer.find_fancy('ruby') # default to Ruby if no lexer is found
48
+ end
49
+
50
+ def syntax_highlighting(text)
51
+ return [] if text.to_s.strip.empty?
52
+ @syntax_highlighting ||= {}
53
+ unless @syntax_highlighting.keys.include?(text)
54
+ lex = lexer.lex(text).to_a
55
+ text_size = 0
56
+ @syntax_highlighting[text] = lex.map do |pair|
57
+ {token_type: pair.first, token_text: pair.last}
58
+ end.each do |hash|
59
+ hash[:token_index] = text_size
60
+ text_size += hash[:token_text].size
61
+ end
62
+ end
63
+ @syntax_highlighting[text]
64
+ end
65
+ end
@@ -21,6 +21,7 @@
21
21
 
22
22
  require 'super_module'
23
23
  require 'glimmer'
24
+ require 'glimmer/error'
24
25
  require 'glimmer/proc_tracker'
25
26
  require 'glimmer/data_binding/observer'
26
27
  require 'glimmer/data_binding/observable_model'
@@ -33,8 +34,10 @@ module Glimmer
33
34
 
34
35
  super_module_included do |klass|
35
36
  # TODO clear memoization of WidgetProxy.libui_class_for for a keyword if a custom control was defined with that keyword
36
- klass.include(Glimmer)
37
- Glimmer::LibUI::CustomControl.add_custom_control_namespaces_for(klass)
37
+ unless klass.name.include?('Glimmer::LibUI::CustomWindow')
38
+ klass.include(Glimmer)
39
+ Glimmer::LibUI::CustomControl.add_custom_control_namespaces_for(klass)
40
+ end
38
41
  end
39
42
 
40
43
  class << self
@@ -229,9 +232,10 @@ module Glimmer
229
232
  end
230
233
 
231
234
  def respond_to?(method_name, *args, &block)
232
- super or
233
- can_handle_listener?(method_name) or
234
- @body_root.respond_to?(method_name, *args, &block)
235
+ result = false
236
+ result ||= super
237
+ result ||= can_handle_listener?(method_name)
238
+ result ||= @body_root.respond_to?(method_name, *args, &block)
235
239
  end
236
240
 
237
241
  private
@@ -247,3 +251,5 @@ module Glimmer
247
251
  end
248
252
  end
249
253
  end
254
+
255
+ Dir[File.expand_path("./#{File.basename(__FILE__, '.rb')}/*.rb", __dir__)].each {|f| require f}
@@ -0,0 +1,65 @@
1
+ # Copyright (c) 2021-2022 Andy Maleh
2
+ #
3
+ # Permission is hereby granted, free of charge, to any person obtaining
4
+ # a copy of this software and associated documentation files (the
5
+ # "Software"), to deal in the Software without restriction, including
6
+ # without limitation the rights to use, copy, modify, merge, publish,
7
+ # distribute, sublicense, and/or sell copies of the Software, and to
8
+ # permit persons to whom the Software is furnished to do so, subject to
9
+ # the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be
12
+ # included in all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21
+
22
+ require 'super_module'
23
+ require 'glimmer/libui/custom_control'
24
+ require 'glimmer/error'
25
+
26
+ module Glimmer
27
+ module LibUI
28
+ module CustomWindow
29
+ include SuperModule
30
+ include Glimmer::LibUI::CustomControl
31
+
32
+ class << self
33
+ def launch(*args, &content)
34
+ launched_custom_shell = send(keyword, *args, &content)
35
+ launched_custom_shell.show
36
+ end
37
+ end
38
+
39
+ def initialize(parent, *swt_constants, options, &content)
40
+ original_logger = Glimmer::Config.logger
41
+ require 'stringio'
42
+ Glimmer::Config.logger = Logger.new(StringIO.new)
43
+ super
44
+ Glimmer::Config.logger = original_logger
45
+ raise Glimmer::Error, 'Invalid custom window body root! Must be a window, another custom window, or a custom control with window as its body root!' unless body_root.is_a?(Glimmer::LibUI::ControlProxy::WindowProxy) || body_root.is_a?(Glimmer::LibUI::CustomWindow) || (body_root.is_a?(Glimmer::LibUI::CustomControl) && body_root.body_root.is_a?(Glimmer::LibUI::ControlProxy::WindowProxy))
46
+ end
47
+
48
+ # Classes may override
49
+ def show
50
+ body_root.show
51
+ end
52
+
53
+ # TODO consider using Forwardable instead
54
+ def destroy
55
+ body_root.destroy
56
+ end
57
+
58
+ def destroying?
59
+ body_root.destroying?
60
+ end
61
+ end
62
+
63
+ Application = CustomWindow
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module Glimmer
2
+ module LibUI
3
+ # GUI View objects that can be represented by PerfectShape objects
4
+ module PerfectShaped
5
+ # Returns if shape contains point on the inside when outline is false (default)
6
+ # or if point is on the outline when outline is true
7
+ # distance_tolerance is used when outline is true to enable a fuzz factor in
8
+ # determining if a point lies on the outline (e.g. makes it easier to select
9
+ # a shape by mouse)
10
+ def contain?(*point, outline: false, distance_tolerance: 0)
11
+ perfect_shape&.contain?(*point, outline: outline, distance_tolerance: distance_tolerance)
12
+ end
13
+
14
+ # Returns if shape includes point on the inside when filled
15
+ # or if shape includes point on the outline when stroked
16
+ def include?(*point)
17
+ if fill.empty?
18
+ contain?(*point, outline: true, distance_tolerance: ((stroke[:thickness] || 1) - 1))
19
+ else
20
+ contain?(*point)
21
+ end
22
+ end
23
+
24
+ # Returns bounding box Array consisting of
25
+ # [minx, miny, width, height]
26
+ def bounding_box
27
+ perfect_shape_bounding_box = perfect_shape&.bounding_box
28
+ return if perfect_shape_bounding_box.nil?
29
+ [
30
+ perfect_shape_bounding_box.x,
31
+ perfect_shape_bounding_box.y,
32
+ perfect_shape_bounding_box.width,
33
+ perfect_shape_bounding_box.height,
34
+ ]
35
+ end
36
+
37
+ # Returns PerfectShape object matching this shape to enable
38
+ # executing computational geometry algorithms
39
+ #
40
+ # Including classes must implement
41
+ def perfect_shape
42
+ # No Op
43
+ end
44
+ end
45
+ end
46
+ end
@@ -29,7 +29,7 @@ module Glimmer
29
29
  class Figure < Shape
30
30
  parameters :x, :y
31
31
  parameter_defaults nil, nil
32
-
32
+
33
33
  def draw(area_draw_params)
34
34
  ::LibUI.draw_path_new_figure(path_proxy.libui, *@args) unless @args.compact.empty? # TODO if args empty then wait till there is an arc child and it starts the figure
35
35
  children.dup.each {|child| child.draw(area_draw_params)}
@@ -58,7 +58,12 @@ module Glimmer
58
58
  path_shapes = [[x, y]]
59
59
  path_shapes += children.map(&:perfect_shape)
60
60
  winding_rule = draw_fill_mode == :winding ? :wind_non_zero : :wind_even_odd
61
- @perfect_shape = PerfectShape::Path.new(closed: closed, winding_rule: winding_rule, shapes: path_shapes)
61
+ @perfect_shape = PerfectShape::Path.new(
62
+ closed: closed,
63
+ winding_rule: winding_rule,
64
+ shapes: path_shapes,
65
+ line_to_complex_shapes: true
66
+ )
62
67
  end
63
68
  @perfect_shape
64
69
  end
@@ -23,6 +23,7 @@ require 'glimmer/libui/parent'
23
23
  require 'glimmer/libui/control_proxy/area_proxy'
24
24
  require 'glimmer/libui/control_proxy/path_proxy'
25
25
  require 'glimmer/libui/data_bindable'
26
+ require 'glimmer/libui/perfect_shaped'
26
27
 
27
28
  module Glimmer
28
29
  module LibUI
@@ -65,6 +66,7 @@ module Glimmer
65
66
  end
66
67
 
67
68
  include Parent
69
+ include PerfectShaped
68
70
  include DataBindable
69
71
 
70
72
  attr_reader :parent, :args, :keyword, :block, :content_added
@@ -145,47 +147,7 @@ module Glimmer
145
147
  end
146
148
  alias transform= transform
147
149
  alias set_transform transform
148
-
149
- # Returns if shape contains point on the inside when outline is false (default)
150
- # or if point is on the outline when outline is true
151
- # distance_tolerance is used when outline is true to enable a fuzz factor in
152
- # determining if a point lies on the outline (e.g. makes it easier to select
153
- # a shape by mouse)
154
- def contain?(*point, outline: false, distance_tolerance: 0)
155
- perfect_shape&.contain?(*point, outline: outline, distance_tolerance: distance_tolerance)
156
- end
157
-
158
- # Returns if shape includes point on the inside when filled
159
- # or if shape includes point on the outline when stroked
160
- def include?(*point)
161
- if fill.empty?
162
- contain?(*point, outline: true, distance_tolerance: ((stroke[:thickness] || 1) - 1))
163
- else
164
- contain?(*point)
165
- end
166
- end
167
-
168
- # Returns bounding box Array consisting of
169
- # [minx, miny, width, height]
170
- def bounding_box
171
- perfect_shape_bounding_box = perfect_shape&.bounding_box
172
- return if perfect_shape_bounding_box.nil?
173
- [
174
- perfect_shape_bounding_box.x,
175
- perfect_shape_bounding_box.y,
176
- perfect_shape_bounding_box.width,
177
- perfect_shape_bounding_box.height,
178
- ]
179
- end
180
-
181
- # Returns PerfectShape object matching this shape to enable
182
- # executing computational geometry algorithms
183
- #
184
- # Subclasses must implement
185
- def perfect_shape
186
- # No Op
187
- end
188
-
150
+
189
151
  def respond_to?(method_name, *args, &block)
190
152
  self.class.parameters.include?(method_name.to_s.sub(/=$/, '').sub(/^set_/, '').to_sym) or
191
153
  super(method_name, true)
data/lib/glimmer/libui.rb CHANGED
@@ -80,11 +80,9 @@ module Glimmer
80
80
 
81
81
  def hex_to_rgb(value)
82
82
  if value.is_a?(String)
83
- value = "0x#{value[1..-1]}" if value.start_with?('#')
84
- if !value.start_with?('0x')
85
- value = value.chars.map {|char| [char, char]}.flatten.join if value.length == 3
86
- value = "0x#{value}"
87
- end
83
+ value = value[2..-1] if value.start_with?('0x')
84
+ value = value[1..-1] if value.start_with?('#')
85
+ value = value.chars.map {|char| [char, char]}.flatten.join if value.length == 3
88
86
  value = value.to_i(16)
89
87
  end
90
88
  if value.is_a?(Integer)
@@ -0,0 +1,7 @@
1
+ module Glimmer
2
+ class << self
3
+ def included(klass)
4
+ klass.extend(Glimmer)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,29 @@
1
+ module Rouge
2
+ module Themes
3
+ # A port of the pastie style from Pygments.
4
+ # See https://bitbucket.org/birkenfeld/pygments-main/src/default/pygments/styles/pastie.py
5
+ class Glimmer < Github
6
+ name 'glimmer'
7
+ style Comment::Single, fg: [106,115,125], italic: true # Also, Comments
8
+ style Keyword::Pseudo, fg: [:dark_red]
9
+ style Keyword, fg: [:blue]
10
+ style Literal::String::Single, fg: [106,115,125] # Also, Comments
11
+ style Literal::String::Double, fg: [0,92,197]
12
+ style Literal::String::Escape, fg: [:red]
13
+ style Literal::Number::Integer, fg: [:blue]
14
+ style Literal::String::Interpol, fg: [:blue]
15
+ style Literal::String::Symbol, fg: [:dark_green]
16
+ style Literal::String, fg: [:dark_blue]
17
+ style Name::Builtin, fg: [215,58,73]
18
+ style Name::Class, fg: [3,47,98]
19
+ style Name::Namespace, fg: [3,47,98]
20
+ style Name::Constant, fg: [0,92,197]
21
+ style Name::Function, fg: [:blue]
22
+ style Name::Variable::Instance, fg: [227,98,9]
23
+ style Name, fg: [111,66,193] #purple
24
+ style Operator, fg: [:red]
25
+ style Punctuation, fg: [:blue]
26
+ style Text, fg: [75, 75, 75]
27
+ end
28
+ end
29
+ end
@@ -37,7 +37,7 @@ require 'libui'
37
37
 
38
38
  # Internal requires
39
39
  # require 'ext/glimmer/config'
40
- # require 'ext/glimmer'
40
+ require 'glimmer-dsl-libui/ext/glimmer'
41
41
  require 'glimmer/dsl/libui/dsl'
42
42
  require 'glimmer/libui'
43
43
  Glimmer::Config.loop_max_count = -1
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-libui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.6
4
+ version: 0.5.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Maleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-03-22 00:00:00.000000000 Z
11
+ date: 2022-04-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 2.7.1
19
+ version: 2.7.3
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 2.7.1
26
+ version: 2.7.3
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: perfect-shape
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.0.1
33
+ version: 1.0.3
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.0.1
40
+ version: 1.0.3
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: super_module
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -128,6 +128,26 @@ dependencies:
128
128
  - - '='
129
129
  - !ruby/object:Gem::Version
130
130
  version: 0.0.11
131
+ - !ruby/object:Gem::Dependency
132
+ name: rouge
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 3.26.0
138
+ - - "<"
139
+ - !ruby/object:Gem::Version
140
+ version: 4.0.0
141
+ type: :runtime
142
+ prerelease: false
143
+ version_requirements: !ruby/object:Gem::Requirement
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ version: 3.26.0
148
+ - - "<"
149
+ - !ruby/object:Gem::Version
150
+ version: 4.0.0
131
151
  - !ruby/object:Gem::Dependency
132
152
  name: juwelier
133
153
  requirement: !ruby/object:Gem::Requirement
@@ -265,6 +285,7 @@ files:
265
285
  - examples/basic_area3.rb
266
286
  - examples/basic_area4.rb
267
287
  - examples/basic_button.rb
288
+ - examples/basic_code_area.rb
268
289
  - examples/basic_draw_text.rb
269
290
  - examples/basic_draw_text2.rb
270
291
  - examples/basic_entry.rb
@@ -358,6 +379,8 @@ files:
358
379
  - icons/blank.png
359
380
  - icons/glimmer.png
360
381
  - lib/glimmer-dsl-libui.rb
382
+ - lib/glimmer-dsl-libui/ext/glimmer.rb
383
+ - lib/glimmer-dsl-libui/ext/rouge/theme/glimmer.rb
361
384
  - lib/glimmer/dsl/libui/bind_expression.rb
362
385
  - lib/glimmer/dsl/libui/control_expression.rb
363
386
  - lib/glimmer/dsl/libui/custom_control_expression.rb
@@ -443,9 +466,12 @@ files:
443
466
  - lib/glimmer/libui/control_proxy/triple_column.rb
444
467
  - lib/glimmer/libui/control_proxy/window_proxy.rb
445
468
  - lib/glimmer/libui/custom_control.rb
469
+ - lib/glimmer/libui/custom_control/code_area.rb
470
+ - lib/glimmer/libui/custom_window.rb
446
471
  - lib/glimmer/libui/data_bindable.rb
447
472
  - lib/glimmer/libui/image_path_renderer.rb
448
473
  - lib/glimmer/libui/parent.rb
474
+ - lib/glimmer/libui/perfect_shaped.rb
449
475
  - lib/glimmer/libui/shape.rb
450
476
  - lib/glimmer/libui/shape/arc.rb
451
477
  - lib/glimmer/libui/shape/bezier.rb