glimmer-dsl-libui 0.9.6 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
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.9.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.10.0
2
2
  ## Prerequisite-Free Ruby Desktop Development Cross-Platform Native GUI Library ([Fukuoka Award Winning](http://www.digitalfukuoka.jp/topics/187?locale=ja))
3
3
  ### The Quickest Way From Zero To GUI
4
4
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
@@ -351,10 +351,12 @@ Learn more about the differences between various [Glimmer](https://github.com/An
351
351
  - [Run Application](#run-application)
352
352
  - [Run Examples](#run-examples)
353
353
  - [Scaffold Application](#scaffold-application)
354
- - [Scaffold Custom Window](#scaffold-custom-window)
355
354
  - [Scaffold Custom Control](#scaffold-custom-control)
355
+ - [Scaffold Custom Window](#scaffold-custom-window)
356
+ - [Scaffold Custom Control Gem](#scaffold-custom-control-gem)
356
357
  - [Scaffold Custom Window Gem](#scaffold-custom-window-gem)
357
358
  - [List Custom Window Gems](#list-custom-window-gems)
359
+ - [List Custom Control Gems](#list-custom-control-gems)
358
360
  - [List Glimmer DSLs](#list-glimmer-dsls)
359
361
  - [Girb (Glimmer IRB)](#girb-glimmer-irb)
360
362
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
@@ -379,9 +381,9 @@ Learn more about the differences between various [Glimmer](https://github.com/An
379
381
  - [Area Composite Shape](#area-composite-shape)
380
382
  - [Area Animation](#area-animation)
381
383
  - [Smart Defaults and Conventions](#smart-defaults-and-conventions)
382
- - [Custom Controls](#custom-controls)
383
- - [Method-Based Custom Controls](#method-based-custom-controls)
384
- - [Class-Based Custom Controls](#class-based-custom-controls)
384
+ - [Custom Components](#custom-components)
385
+ - [Method-Based Custom Components](#method-based-custom-components)
386
+ - [Class-Based Custom Components](#class-based-custom-components)
385
387
  - [Observer Pattern](#observer-pattern)
386
388
  - [Data-Binding](#data-binding)
387
389
  - [Bidirectional (Two-Way) Data-Binding](#bidirectional-two-way-data-binding)
@@ -426,7 +428,7 @@ gem install glimmer-dsl-libui
426
428
  Or install via Bundler `Gemfile`:
427
429
 
428
430
  ```ruby
429
- gem 'glimmer-dsl-libui', '~> 0.9.6'
431
+ gem 'glimmer-dsl-libui', '~> 0.10.0'
430
432
  ```
431
433
 
432
434
  Test that installation worked by running the [Glimmer Meta-Example](#examples):
@@ -622,7 +624,7 @@ Mac | Windows | Linux
622
624
 
623
625
  ### Scaffold Application
624
626
 
625
- Application scaffolding enables automatically generating the directories/files of a new desktop GUI application that follows the MVC architecture and can be packaged as a Ruby gem that includes a binary script for running the app conveniently.
627
+ Application scaffolding enables automatically generating the directories/files of a new desktop GUI application that follows the MVC architecture and can be packaged as a Ruby gem that includes an executable script for running the app conveniently. It also ensures that software engineers follow the recommended Glimmer DSL for LibUI conventions and best practices. Application Scaffolding greatly improves software engineering productivity when building desktop applications with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui).
626
628
 
627
629
  Scaffold Glimmer DSL for LibUI application with this command:
628
630
 
@@ -720,7 +722,7 @@ Or by using the raw rake command:
720
722
  rake gemspec:generate
721
723
  ```
722
724
 
723
- Once you install the gem (e.g. `gem install hello_world`), you can simply run the app with its binary script:
725
+ Once you install the gem (e.g. `gem install hello_world`), you can simply run the app with its executable script:
724
726
 
725
727
  ```
726
728
  app_name
@@ -734,8 +736,198 @@ hello_world
734
736
 
735
737
  ![glimmer-dsl-libui-mac-scaffold-app-initial-screen.png](images/glimmer-dsl-libui-mac-scaffold-app-initial-screen.png)
736
738
 
739
+ ### Scaffold Custom Control
740
+
741
+ When you are in a scaffolded application, you can scaffold a new [custom control](#custom-components) (a control that you can put anything in to represent a view concept in your application) by running this command:
742
+
743
+ ```
744
+ glimmer scaffold:customcontrol[name,namespace]
745
+ ```
746
+
747
+ The `name` represents the [custom control](#custom-components) view class name (it can be underscored, and Glimmer will automatically classify it).
748
+
749
+ The `namespace` is optional and represents the module that the [custom control](#custom-components) view class will live under. If left off, the main application class namespace is used (e.g. the top-level `HelloWorld` class namespace for a `hello_world` application).
750
+
751
+ You can also use the shorter `cc` alias for `customcontrol`:
752
+
753
+ ```
754
+ glimmer scaffold:cc[name,namespace]
755
+ ```
756
+
757
+ For example by running this command under a `hello_world` application:
758
+
759
+ ```
760
+ glimmer scaffold:cw[model_form]
761
+ ```
762
+
763
+ That will generate this class under `app/hello_world/view/model_form`:
764
+
765
+ ```ruby
766
+ class HelloWorld
767
+ module View
768
+ class ModelForm
769
+ include Glimmer::LibUI::CustomControl
770
+
771
+ ## Add options like the following to configure CustomControl by outside consumers
772
+ #
773
+ # options :custom_text, :background_color
774
+ # option :foreground_color, default: :red
775
+
776
+ # Replace example options with your own options
777
+ option :model
778
+ option :attributes
779
+
780
+ ## Use before_body block to pre-initialize variables to use in body
781
+ #
782
+ #
783
+ before_body do
784
+ # Replace example code with your own before_body code
785
+ default_model_attributes = [:first_name, :last_name, :email]
786
+ default_model_class = Struct.new(*default_model_attributes)
787
+ self.model ||= default_model_class.new
788
+ self.attributes ||= default_model_attributes
789
+ end
790
+
791
+ ## Use after_body block to setup observers for controls in body
792
+ #
793
+ # after_body do
794
+ #
795
+ # end
796
+
797
+ ## Add control content under custom control body
798
+ ##
799
+ ## If you want to add a window as the top-most control,
800
+ ## consider creating a custom window instead
801
+ ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
802
+ #
803
+ body {
804
+ # Replace example content (model_form custom control) with your own custom control content.
805
+ form {
806
+ attributes.each do |attribute|
807
+ entry { |e|
808
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
809
+ text <=> [model, attribute]
810
+ }
811
+ end
812
+ }
813
+ }
814
+
815
+ end
816
+ end
817
+ end
818
+ ```
819
+
820
+ When the generated file is required in another view (e.g. `require 'hello_world/view/model_form'`), the [custom control](#custom-components) keyword `model_form` become available and reusable, like by calling:
821
+
822
+ ```ruby
823
+ window {
824
+ vertical_box {
825
+ label('Form:')
826
+ model_form(model: some_model, attributes: array_of_attributes)
827
+ }
828
+ }
829
+ ```
830
+
831
+ Here is an example that generates a [custom control](#custom-components) with a namespace:
832
+
833
+ ```
834
+ glimmer scaffold:cw[model_form,common]
835
+ ```
836
+
837
+ That will generate this class under `app/common/view/model_form`:
838
+
839
+ ```ruby
840
+ module Common
841
+ module View
842
+ class ModelForm
843
+ include Glimmer::LibUI::CustomControl
844
+
845
+ ## Add options like the following to configure CustomControl by outside consumers
846
+ #
847
+ # options :custom_text, :background_color
848
+ # option :foreground_color, default: :red
849
+
850
+ # Replace example options with your own options
851
+ option :model
852
+ option :attributes
853
+
854
+ ## Use before_body block to pre-initialize variables to use in body
855
+ #
856
+ #
857
+ before_body do
858
+ # Replace example code with your own before_body code
859
+ default_model_attributes = [:first_name, :last_name, :email]
860
+ default_model_class = Struct.new(*default_model_attributes)
861
+ self.model ||= default_model_class.new
862
+ self.attributes ||= default_model_attributes
863
+ end
864
+
865
+ ## Use after_body block to setup observers for controls in body
866
+ #
867
+ # after_body do
868
+ #
869
+ # end
870
+
871
+ ## Add control content under custom control body
872
+ ##
873
+ ## If you want to add a window as the top-most control,
874
+ ## consider creating a custom window instead
875
+ ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
876
+ #
877
+ body {
878
+ # Replace example content (model_form custom control) with your own custom control content.
879
+ form {
880
+ attributes.each do |attribute|
881
+ entry { |e|
882
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
883
+ text <=> [model, attribute]
884
+ }
885
+ end
886
+ }
887
+ }
888
+
889
+ end
890
+ end
891
+ end
892
+ ```
893
+
894
+ When that file is required in another view (e.g. `require 'common/view/model_form'`), the `model_form` keyword becomes available:
895
+
896
+ ```ruby
897
+ window {
898
+ vertical_box {
899
+ label('Form:')
900
+ model_form(model: some_model, attributes: array_of_attributes)
901
+ }
902
+ }
903
+ ```
904
+
905
+ If for whatever reason, you end up with 2 [custom control](#custom-components) views having the same name with different namespaces, then you can invoke the specific [custom control](#custom-components) you want by including the Ruby namespace in underscored format separated by double-underscores:
906
+
907
+ ```ruby
908
+ window {
909
+ vertical_box {
910
+ label('Form:')
911
+ common__view__model_form(model: some_model, attributes: array_of_attributes)
912
+ }
913
+ }
914
+ ```
915
+
916
+ Or another `model_form` [custom control](#custom-components) view:
917
+
918
+ ```ruby
919
+ window {
920
+ vertical_box {
921
+ label('Form:')
922
+ hello_world__view__model_form(model: some_model, attributes: array_of_attributes)
923
+ }
924
+ }
925
+ ```
926
+
737
927
  ### Scaffold Custom Window
738
928
 
929
+ A custom window is a specialization of a custom control that has a `window` as its `body` root.
930
+
739
931
  When you are in a scaffolded application, you can scaffold a new custom window (a window that you can put anything in to represent a view concept in your application) by running this command:
740
932
 
741
933
  ```
@@ -886,197 +1078,75 @@ Or another `train` custom window view:
886
1078
  hello_world__view__train.show
887
1079
  ```
888
1080
 
889
- ### Scaffold Custom Control
1081
+ ### Scaffold Custom Control Gem
890
1082
 
891
- When you are in a scaffolded application, you can scaffold a new custom control (a control that you can put anything in to represent a view concept in your application) by running this command:
1083
+ You can scaffold a Ruby gem around a reusable [custom control](#custom-components) to expose publicly and make available for multiple projects by running this command:
892
1084
 
893
1085
  ```
894
- glimmer scaffold:customcontrol[name,namespace]
1086
+ glimmer scaffold:gem:customcontrol[name,namespace]
895
1087
  ```
896
1088
 
897
- The `name` represents the custom control view class name (it can be underscored, and Glimmer will automatically classify it).
1089
+ That will generate a [custom control](#custom-components) gem project under the naming convention: `glimmer-libui-cc-name-namespace`
898
1090
 
899
- The `namespace` is optional and represents the module that the custom control view class will live under. If left off, the main application class namespace is used (e.g. the top-level `HelloWorld` class namespace for a `hello_world` application).
1091
+ The naming convention helps with discoverability of Ruby gems using the command `glimmer list:gems:customcontrol[query]` (or alias: `glimmer list:gems:cc[query]`) where filtering `query` is optional.
900
1092
 
901
- You can also use the shorter `cc` alias for `customcontrol`:
1093
+ The `name` is the [custom control](#custom-components) class name, which must not contain dashes by convention (multiple words can be concatenated or can use underscores between them).
902
1094
 
903
- ```
904
- glimmer scaffold:cc[name,namespace]
905
- ```
1095
+ The `namespace` is needed to avoid clashing with other [custom control](#custom-components) gems that other software engineers might have thought of. It is recommended not to include dashes between words in it by convention yet concatenated words or underscores between them.
906
1096
 
907
- For example by running this command under a `hello_world` application:
1097
+ Here is a shorter alias for the [custom control](#custom-components) gem scaffolding command:
908
1098
 
909
1099
  ```
910
- glimmer scaffold:cw[model_form]
1100
+ glimmer scaffold:gem:cc[name,namespace]
911
1101
  ```
912
1102
 
913
- That will generate this class under `app/hello_world/view/model_form`:
1103
+ You can package the newly scaffolded project as a Ruby gem by running this command:
914
1104
 
915
- ```ruby
916
- class HelloWorld
917
- module View
918
- class ModelForm
919
- include Glimmer::LibUI::CustomControl
920
-
921
- ## Add options like the following to configure CustomControl by outside consumers
922
- #
923
- # options :custom_text, :background_color
924
- # option :foreground_color, default: :red
925
-
926
- # Replace example options with your own options
927
- option :model
928
- option :attributes
929
-
930
- ## Use before_body block to pre-initialize variables to use in body
931
- #
932
- #
933
- before_body do
934
- # Replace example code with your own before_body code
935
- default_model_attributes = [:first_name, :last_name, :email]
936
- default_model_class = Struct.new(*default_model_attributes)
937
- self.model ||= default_model_class.new
938
- self.attributes ||= default_model_attributes
939
- end
940
-
941
- ## Use after_body block to setup observers for controls in body
942
- #
943
- # after_body do
944
- #
945
- # end
946
-
947
- ## Add control content under custom control body
948
- ##
949
- ## If you want to add a window as the top-most control,
950
- ## consider creating a custom window instead
951
- ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
952
- #
953
- body {
954
- # Replace example content (model_form custom control) with your own custom control content.
955
- form {
956
- attributes.each do |attribute|
957
- entry { |e|
958
- label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
959
- text <=> [model, attribute]
960
- }
961
- end
962
- }
963
- }
964
-
965
- end
966
- end
967
- end
968
1105
  ```
969
-
970
- When the generated file is required in another view (e.g. `require 'hello_world/view/model_form'`), the custom control keyword `model_form` become available and reusable, like by calling:
971
-
972
- ```ruby
973
- window {
974
- vertical_box {
975
- label('Form:')
976
- model_form(model: some_model, attributes: array_of_attributes)
977
- }
978
- }
1106
+ glimmer package:gem
979
1107
  ```
980
1108
 
981
- Here is an example that generates a custom control with a namespace:
1109
+ Or by using the raw rake command:
982
1110
 
983
1111
  ```
984
- glimmer scaffold:cw[model_form,common]
1112
+ rake build
985
1113
  ```
986
1114
 
987
- That will generate this class under `app/common/view/model_form`:
1115
+ You can generate the application gemspec explicitly if needed with this command (though it is not needed to build the gem):
988
1116
 
989
- ```ruby
990
- module Common
991
- module View
992
- class ModelForm
993
- include Glimmer::LibUI::CustomControl
994
-
995
- ## Add options like the following to configure CustomControl by outside consumers
996
- #
997
- # options :custom_text, :background_color
998
- # option :foreground_color, default: :red
999
-
1000
- # Replace example options with your own options
1001
- option :model
1002
- option :attributes
1003
-
1004
- ## Use before_body block to pre-initialize variables to use in body
1005
- #
1006
- #
1007
- before_body do
1008
- # Replace example code with your own before_body code
1009
- default_model_attributes = [:first_name, :last_name, :email]
1010
- default_model_class = Struct.new(*default_model_attributes)
1011
- self.model ||= default_model_class.new
1012
- self.attributes ||= default_model_attributes
1013
- end
1014
-
1015
- ## Use after_body block to setup observers for controls in body
1016
- #
1017
- # after_body do
1018
- #
1019
- # end
1020
-
1021
- ## Add control content under custom control body
1022
- ##
1023
- ## If you want to add a window as the top-most control,
1024
- ## consider creating a custom window instead
1025
- ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
1026
- #
1027
- body {
1028
- # Replace example content (model_form custom control) with your own custom control content.
1029
- form {
1030
- attributes.each do |attribute|
1031
- entry { |e|
1032
- label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
1033
- text <=> [model, attribute]
1034
- }
1035
- end
1036
- }
1037
- }
1038
-
1039
- end
1040
- end
1041
- end
1042
1117
  ```
1043
-
1044
- When that file is required in another view (e.g. `require 'common/view/model_form'`), the `model_form` keyword becomes available:
1045
-
1046
- ```ruby
1047
- window {
1048
- vertical_box {
1049
- label('Form:')
1050
- model_form(model: some_model, attributes: array_of_attributes)
1051
- }
1052
- }
1118
+ glimmer package:gemspec
1053
1119
  ```
1054
1120
 
1055
- If for whatever reason, you end up with 2 custom control views having the same name with different namespaces, then you can invoke the specific custom control you want by including the Ruby namespace in underscored format separated by double-underscores:
1121
+ Or by using the raw rake command:
1056
1122
 
1057
- ```ruby
1058
- window {
1059
- vertical_box {
1060
- label('Form:')
1061
- common__view__model_form(model: some_model, attributes: array_of_attributes)
1062
- }
1063
- }
1123
+ ```
1124
+ rake gemspec:generate
1064
1125
  ```
1065
1126
 
1066
- Or another `model_form` custom control view:
1127
+ Typically, consumers of the gem would include it in their own project, which makes the gem keyword available in the Glimmer GUI DSL anywhere `Glimmer`. `Glimmer::LibUI::Application`, `Glimmer::LibUI::CustomWindow`, or `Glimmer::LibUI::CustomControl` is mixed.
1128
+
1129
+ For example:
1067
1130
 
1068
1131
  ```ruby
1132
+ require 'glimmer-libui-cc-model_form-acme'
1133
+
1134
+ ...
1069
1135
  window {
1070
1136
  vertical_box {
1071
1137
  label('Form:')
1072
- hello_world__view__model_form(model: some_model, attributes: array_of_attributes)
1138
+
1139
+ model_form(model: some_model, attributes: some_attributes)
1073
1140
  }
1074
1141
  }
1142
+ ...
1075
1143
  ```
1076
1144
 
1077
1145
  ### Scaffold Custom Window Gem
1078
1146
 
1079
- You can scaffold a Ruby gem around a reusable Custom Window by running this command:
1147
+ A custom window is a specialization of a custom control that has a `window` as its `body` root.
1148
+
1149
+ You can scaffold a Ruby gem around a reusable custom window to expose publicly and make available for multiple projects by running this command:
1080
1150
 
1081
1151
  ```
1082
1152
  glimmer scaffold:gem:customwindow[name,namespace]
@@ -1120,9 +1190,9 @@ Or by using the raw rake command:
1120
1190
  rake gemspec:generate
1121
1191
  ```
1122
1192
 
1123
- The project optionally allows you to run the custom window as its own separate app with a binary script (`bin/gem_name`) to see it, which helps with prototyping it.
1193
+ The project optionally allows you to run the custom window as its own separate app with a executable script (`bin/gem_name`) to see it, which helps with prototyping it.
1124
1194
 
1125
- But, typically consumers of the gem would include it in their own project, which makes the gem keyword available in the Glimmer GUI DSL where `Glimmer`. `Glimmer::LibUI::Application`, `Glimmer::LibUI::CustomWindow`, or `Glimmer::LibUI::CustomControl` is mixed.
1195
+ But, typically consumers of the gem would include it in their own project, which makes the gem keyword available in the Glimmer GUI DSL anywhere `Glimmer`. `Glimmer::LibUI::Application`, `Glimmer::LibUI::CustomWindow`, or `Glimmer::LibUI::CustomControl` is mixed.
1126
1196
 
1127
1197
  For example:
1128
1198
 
@@ -1152,6 +1222,25 @@ glimmer list:gems:cw[query]
1152
1222
 
1153
1223
  The filtering `query` is optional.
1154
1224
 
1225
+
1226
+ ### List Custom Control Gems
1227
+
1228
+ Custom control gems are scaffolded to follow the naming convention: `glimmer-libui-cw-name-namespace`
1229
+
1230
+ The naming convention helps with discoverability of Ruby gems using the command:
1231
+
1232
+ ```
1233
+ glimmer list:gems:customcontrol[query]
1234
+ ```
1235
+
1236
+ Or by using the shorter alias:
1237
+
1238
+ ```
1239
+ glimmer list:gems:cc[query]
1240
+ ```
1241
+
1242
+ The filtering `query` is optional.
1243
+
1155
1244
  ### List Glimmer DSLs
1156
1245
 
1157
1246
  Glimmer DSLs can be listed with this command:
@@ -1640,7 +1729,7 @@ Learn more by checking out [examples](#examples).
1640
1729
 
1641
1730
  [EARLY ALPHA FEATURE]
1642
1731
 
1643
- `refined_table` is a custom control provided exclusively by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
1732
+ `refined_table` is a [custom control](#custom-components) provided exclusively by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
1644
1733
  that includes filtering and pagination support out of the box and can handle very large amounts of data (e.g. 50,000 rows).
1645
1734
 
1646
1735
  It is currently an early alpha feature, so please test-drive and report issues if you encounter any.
@@ -1662,7 +1751,7 @@ API:
1662
1751
 
1663
1752
  - `refined_model_array` (`Array`): `model_array` with filtering and pagination applied (useful to grab a table row model by index).
1664
1753
  - `filtered_model_array` (`Array`): `model_array` with filtering applied, but without pagination
1665
- - `table_proxy`: control proxy object for the `table` contained in the `refined_table` custom control
1754
+ - `table_proxy`: control proxy object for the `table` contained in the `refined_table` [custom control](#custom-components)
1666
1755
 
1667
1756
  If the initial `model_array` has no more than a single page of data, then pagination buttons are hidden (but, the filter field remains).
1668
1757
 
@@ -1905,7 +1994,7 @@ Mac | Windows | Linux
1905
1994
  **(ALPHA FEATURE)**
1906
1995
 
1907
1996
  [libui-ng](https://github.com/libui-ng/libui-ng) does not support `image` rendering outside of `table` yet.
1908
- However, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) adds a special `image(file as String path or web URL, width as Numeric, height as Numeric)` custom control that renders an image unto an `area` pixel by pixel (and when possible to optimize, line by line).
1997
+ However, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) adds a special `image(file as String path or web URL, width as Numeric, height as Numeric)` [custom control](#custom-components) that renders an image unto an `area` pixel by pixel (and when possible to optimize, line by line).
1909
1998
 
1910
1999
  Given that it is very new and is not a [libui-ng](https://github.com/libui-ng/libui-ng)-native control, please keep these notes in mind:
1911
2000
  - It only supports the `.png` file format.
@@ -2078,7 +2167,7 @@ One final note is that in Linux, table images grow and shrink with the image siz
2078
2167
 
2079
2168
  ![linux table image](images/glimmer-dsl-libui-linux-basic-table-image.png)
2080
2169
 
2081
- Check out [examples/basic_image.rb](/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-image) (all versions) for examples of using `image` Glimmer custom control.
2170
+ Check out [examples/basic_image.rb](/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-image) (all versions) for examples of using `image` Glimmer [custom control](#custom-components).
2082
2171
 
2083
2172
  #### Colors
2084
2173
 
@@ -2391,7 +2480,7 @@ BasicCompositeShape.launch
2391
2480
 
2392
2481
  If you need to animate `area` vector graphics, you just have to use the [`Glimmer::LibUI::timer`](#libui-operations) method along with making changes to shape attributes.
2393
2482
 
2394
- Spinner example that has a fully customizable method-based custom control called `spinner`, which is destroyed if you click on it (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2483
+ Spinner example that has a fully customizable method-based [custom control](#custom-components) called `spinner`, which is destroyed if you click on it (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2395
2484
 
2396
2485
  ```ruby
2397
2486
  require 'glimmer-dsl-libui'
@@ -2520,21 +2609,21 @@ SpinnerExample.new.launch
2520
2609
  - Colors may be passed in as a hash of `:r`, `:g`, `:b`, `:a`, or `:red`, `:green`, `:blue`, `:alpha`, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color like `:skyblue`, or 6-char hex or 3-char hex (as `Integer` or `String` with or without `0x` prefix)
2521
2610
  - Color alpha value defaults to `1.0` when not specified.
2522
2611
 
2523
- ### Custom Controls
2612
+ ### Custom Components
2524
2613
 
2525
- Custom controls can be defined to represent custom controls (components) that provide new features or act as composites of [existing controls](#supported-keywords) that need to be reused multiple times in an application or across multiple applications. Custom controls save a lot of development time, improving productivity and maintainability immensely.
2614
+ Custom components like custom controls, custom windows, and custom shapes can be defined to provide new features or act as composites of [existing controls](#supported-keywords) that need to be reused multiple times in an application or across multiple applications. Custom controls save a lot of development time, improving productivity and maintainability immensely.
2526
2615
 
2527
2616
  For example, you can define a custom `address_view` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
2528
2617
 
2529
- There are two ways to define custom controls:
2530
- - Method-Based: simply define a method representing the custom control you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
2531
- - Class-Based: define a class matching the camelcased name of the custom control by convention (e.g. the `address_view` custom control keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`. Classes add the benefit of being able to distribute the custom controls into separate files and reuse externally from multiple places or share via Ruby gems.
2618
+ There are two ways to define [custom components](#custom-components):
2619
+ - Method-Based: simply define a method representing the [custom component](#custom-components) you want (e.g. `address_view`) with any options needed (e.g. `address(address_model: some_model)`).
2620
+ - Class-Based: define a class matching the camelcased name of the [custom component](#custom-components) by convention (e.g. the `address_view` [custom component](#custom-components) keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`, `include Glimmer::LibUI::CustomWindow`, `include Glimmer::LibUI::CustomShape` depending on if the component represents a standard control, a whole window, or an area canvas graphics shape. Classes add the benefit of being able to distribute the [custom component](#custom-components)s into a separate file for external reuse from multiple views or for sharing as a Ruby gem.
2532
2621
 
2533
- It is OK to use the terms "custom control" and "custom keyword" synonymously though "custom keyword" is a broader term that covers things other than controls too like custom shapes (e.g. `cylinder`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
2622
+ It is OK to use the terms "custom control", "custom component", and "custom keyword" synonymously though "custom component" is a broader term that covers things other than controls too like custom shapes (e.g. `cube`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
2534
2623
 
2535
2624
  #### Method-Based Custom Controls
2536
2625
 
2537
- Simply define a method representing the custom control you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
2626
+ Simply define a method representing the [custom component](#custom-components) you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
2538
2627
 
2539
2628
  Example that defines `form_field`, `address_form`, `label_pair`, and `address_view` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2540
2629
 
@@ -2633,9 +2722,9 @@ window('Method-Based Custom Keyword') {
2633
2722
 
2634
2723
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
2635
2724
 
2636
- #### Class-Based Custom Controls
2725
+ #### Class-Based Custom Components
2637
2726
 
2638
- Define a class matching the camelcased name of the custom control by convention (e.g. the `address_view` custom control keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`. Classes add the benefit of being able to distribute the custom controls into separate files and reuse externally from multiple places or share via Ruby gems.
2727
+ Define a class matching the camelcased name of the [custom control](#custom-components) by convention (e.g. the `address_view` [custom control](#custom-components) keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`. Classes add the benefit of being able to distribute the [custom control](#custom-components)s into separate files and reuse externally from multiple places or share via Ruby gems.
2639
2728
 
2640
2729
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2641
2730
 
@@ -2765,13 +2854,185 @@ ClassBasedCustomControls.launch
2765
2854
 
2766
2855
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
2767
2856
 
2768
- 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.
2857
+ Example of a `cube` custom shape (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2858
+
2859
+ ```ruby
2860
+ require 'glimmer-dsl-libui'
2861
+
2862
+ # class-based custom shape using Glimmer::LibUI::CustomShape mixin, which automatically
2863
+ # augments the Glimmer GUI DSL with the underscored version of the class name: `cube`
2864
+ # while accepting hash options matching the options declared on the class.
2865
+ # (e.g. `cube(location_x: 50, location_y: 100)` )
2866
+ class Cube
2867
+ include Glimmer::LibUI::CustomShape
2868
+
2869
+ DEFAULT_SIZE = 28
2870
+
2871
+ option :location_x, default: 0
2872
+ option :location_y, default: 0
2873
+ option :rectangle_width, default: nil
2874
+ option :rectangle_height, default: nil
2875
+ option :cube_height, default: 75
2876
+ option :background_color, default: :brown
2877
+ option :foreground_color
2878
+ option :line_thickness, default: 1
2879
+
2880
+ # The before_body block executes before building the body
2881
+ before_body do
2882
+ self.rectangle_width ||= rectangle_height || cube_height || DEFAULT_SIZE
2883
+ self.rectangle_height ||= rectangle_width || cube_height || DEFAULT_SIZE
2884
+ self.cube_height ||= rectangle_width || rectangle_height || DEFAULT_SIZE
2885
+ if foreground_color
2886
+ self.foreground_color = Glimmer::LibUI.interpret_color(foreground_color)
2887
+ self.foreground_color[:thickness] ||= line_thickness
2888
+ else
2889
+ self.foreground_color = [0, 0, 0, thickness: line_thickness]
2890
+ end
2891
+ end
2892
+
2893
+ # Optionally, after_body could be defined to perform operations after building the body
2894
+ # like setting up observers.
2895
+ #
2896
+ # after_body do
2897
+ # end
2898
+
2899
+ body {
2900
+ # the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
2901
+ # like a cube containing polygons, a polyline, a rectangle, and a line
2902
+ # with the fill and stroke colors getting inherited by all children that do not specify them
2903
+ shape(location_x, location_y) {
2904
+ fill background_color
2905
+ stroke foreground_color
2906
+
2907
+ bottom = polygon(0, cube_height + rectangle_height / 2.0,
2908
+ rectangle_width / 2.0, cube_height,
2909
+ rectangle_width, cube_height + rectangle_height / 2.0,
2910
+ rectangle_width / 2.0, cube_height + rectangle_height) {
2911
+ # inherits fill property from parent shape if not set
2912
+ # inherits stroke property from parent shape if not set
2913
+ }
2914
+ body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
2915
+ # inherits fill property from parent shape if not set
2916
+ # stroke is overridden to ensure a different value from parent
2917
+ stroke thickness: 0
2918
+ }
2919
+ polyline(0, rectangle_height / 2.0 + cube_height,
2920
+ 0, rectangle_height / 2.0,
2921
+ rectangle_width, rectangle_height / 2.0,
2922
+ rectangle_width, rectangle_height / 2.0 + cube_height) {
2923
+ # inherits stroke property from parent shape if not set
2924
+ }
2925
+ top = polygon(0, rectangle_height / 2.0,
2926
+ rectangle_width / 2.0, 0,
2927
+ rectangle_width, rectangle_height / 2.0,
2928
+ rectangle_width / 2.0, rectangle_height) {
2929
+ # inherits fill property from parent shape if not set
2930
+ # inherits stroke property from parent shape if not set
2931
+ }
2932
+ line(rectangle_width / 2.0, cube_height + rectangle_height,
2933
+ rectangle_width / 2.0, rectangle_height) {
2934
+ # inherits stroke property from parent shape if not set
2935
+ }
2936
+ }
2937
+ }
2938
+ end
2939
+
2940
+ class BasicCustomShape
2941
+ include Glimmer::LibUI::Application
2942
+
2943
+ body {
2944
+ window {
2945
+ title 'Basic Custom Shape'
2946
+ content_size 200, 225
2947
+
2948
+ @area = area {
2949
+ rectangle(0, 0, 200, 225) {
2950
+ fill :white
2951
+ }
2952
+
2953
+ 7.times do |n|
2954
+ x_location = (rand*125).to_i%200 + (rand*15).to_i
2955
+ y_location = (rand*125).to_i%200 + (rand*15).to_i
2956
+ shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
2957
+ shape_size = 20+n
2958
+
2959
+ cube(
2960
+ location_x: x_location,
2961
+ location_y: y_location,
2962
+ rectangle_width: shape_size*2,
2963
+ rectangle_height: shape_size,
2964
+ cube_height: shape_size*2,
2965
+ background_color: shape_color,
2966
+ line_thickness: 2
2967
+ ) { |the_shape|
2968
+ on_mouse_up do |area_mouse_event|
2969
+ # Change color on mouse up without dragging
2970
+ if @drag_shape.nil?
2971
+ background_color = [rand(255), rand(255), rand(255)]
2972
+ the_shape.fill = background_color
2973
+ end
2974
+ end
2975
+
2976
+ on_mouse_drag_start do |area_mouse_event|
2977
+ @drag_shape = the_shape
2978
+ @drag_x = area_mouse_event[:x]
2979
+ @drag_y = area_mouse_event[:y]
2980
+ end
2981
+
2982
+ on_mouse_drag do |area_mouse_event|
2983
+ if @drag_shape && @drag_x && @drag_y
2984
+ drag_distance_width = area_mouse_event[:x] - @drag_x
2985
+ drag_distance_height = area_mouse_event[:y] - @drag_y
2986
+ @drag_shape.x += drag_distance_width
2987
+ @drag_shape.y += drag_distance_height
2988
+ @drag_x = area_mouse_event[:x]
2989
+ @drag_y = area_mouse_event[:y]
2990
+ end
2991
+ end
2992
+
2993
+ on_mouse_drop do |area_mouse_event|
2994
+ @drag_shape = nil
2995
+ @drag_x = nil
2996
+ @drag_y = nil
2997
+ end
2998
+ }
2999
+ end
3000
+
3001
+ # this general area on_mouse_drag listener is needed to ensure that dragging a shape
3002
+ # outside of its boundaries would still move the dragged shape
3003
+ on_mouse_drag do |area_mouse_event|
3004
+ if @drag_shape && @drag_x && @drag_y
3005
+ drag_distance_width = area_mouse_event[:x] - @drag_x
3006
+ drag_distance_height = area_mouse_event[:y] - @drag_y
3007
+ @drag_shape.x += drag_distance_width
3008
+ @drag_shape.y += drag_distance_height
3009
+ @drag_x = area_mouse_event[:x]
3010
+ @drag_y = area_mouse_event[:y]
3011
+ end
3012
+ end
3013
+
3014
+ on_mouse_drop do |area_mouse_event|
3015
+ @drag_shape = nil
3016
+ @drag_x = nil
3017
+ @drag_y = nil
3018
+ end
3019
+ }
3020
+ }
3021
+ }
3022
+ end
3023
+
3024
+ BasicCustomShape.launch
3025
+ ```
3026
+
3027
+ ![glimmer-dsl-libui-mac-basic-custom-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
3028
+
3029
+ You can also define Custom Window keywords, that is [custom control](#custom-components)s 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.
2769
3030
 
2770
3031
  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](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#area-based-custom-controls) example.
2771
3032
 
2772
3033
  Defining custom controls enables unlimited extension of the [Glimmer GUI DSL](#glimmer-gui-dsl). The sky is the limit on what can be done with custom controls 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.
2773
3034
 
2774
- Learn more from custom control usage in [Method-Based Custom Keyword](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#method-based-custom-keyword), [Area-Based Custom Controls](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#area-based-custom-controls), [Basic Scrolling Area](/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-scrolling-area), [Histogram](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#histogram), and [Tetris](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#tetris) examples.
3035
+ Learn more from custom control usage in [Method-Based Custom Controls](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#method--based-custom-controls), [Class-Based Custom Controls](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#class-based-custom-controls), [Area-Based Custom Controls](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#area-based-custom-controls), [Basic Composite Shape](), [Basic Custom Shape](), [Basic Scrolling Area](/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md#basic-scrolling-area), [Histogram](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#histogram), and [Tetris](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#tetris) examples.
2775
3036
 
2776
3037
  ### Observer Pattern
2777
3038