glimmer-dsl-libui 0.9.7 → 0.10.1

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.7
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.1
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)
@@ -26,7 +26,7 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
26
26
  - [Declarative DSL syntax](#glimmer-gui-dsl-concepts) that visually maps to the GUI control hierarchy
27
27
  - [Convention over configuration](#smart-defaults-and-conventions) via smart defaults and automation of low-level details
28
28
  - Requiring the [least amount of syntax](#glimmer-gui-dsl-concepts) possible to build GUI
29
- - [Custom Control](#custom-keywords) support
29
+ - [Custom Component](#custom-components) support (Custom Controls, Custom Windows, and Custom Shapes)
30
30
  - [Bidirectional/Unidirectional Data-Binding](#data-binding) to declaratively wire and automatically synchronize GUI Views with Models
31
31
  - [Scaffolding](#scaffold-application) for new custom windows/controls, apps, and gems
32
32
  - [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
@@ -351,12 +351,15 @@ 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)
356
- - [Scaffold Custom Window Gem](#scaffold-custom-window-gem)
355
+ - [Scaffold Custom Window](#scaffold-custom-window)
356
+ - [Scaffold Custom Shape](#scaffold-custom-shape)
357
357
  - [Scaffold Custom Control Gem](#scaffold-custom-control-gem)
358
- - [List Custom Window Gems](#list-custom-window-gems)
358
+ - [Scaffold Custom Window Gem](#scaffold-custom-window-gem)
359
+ - [Scaffold Custom Shape Gem](#scaffold-custom-shape-gem)
359
360
  - [List Custom Control Gems](#list-custom-control-gems)
361
+ - [List Custom Window Gems](#list-custom-window-gems)
362
+ - [List Custom Shape Gems](#list-custom-shape-gems)
360
363
  - [List Glimmer DSLs](#list-glimmer-dsls)
361
364
  - [Girb (Glimmer IRB)](#girb-glimmer-irb)
362
365
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
@@ -381,9 +384,9 @@ Learn more about the differences between various [Glimmer](https://github.com/An
381
384
  - [Area Composite Shape](#area-composite-shape)
382
385
  - [Area Animation](#area-animation)
383
386
  - [Smart Defaults and Conventions](#smart-defaults-and-conventions)
384
- - [Custom Controls](#custom-controls)
385
- - [Method-Based Custom Controls](#method-based-custom-controls)
386
- - [Class-Based Custom Controls](#class-based-custom-controls)
387
+ - [Custom Components](#custom-components)
388
+ - [Method-Based Custom Components](#method-based-custom-components)
389
+ - [Class-Based Custom Components](#class-based-custom-components)
387
390
  - [Observer Pattern](#observer-pattern)
388
391
  - [Data-Binding](#data-binding)
389
392
  - [Bidirectional (Two-Way) Data-Binding](#bidirectional-two-way-data-binding)
@@ -428,7 +431,7 @@ gem install glimmer-dsl-libui
428
431
  Or install via Bundler `Gemfile`:
429
432
 
430
433
  ```ruby
431
- gem 'glimmer-dsl-libui', '~> 0.9.7'
434
+ gem 'glimmer-dsl-libui', '~> 0.10.1'
432
435
  ```
433
436
 
434
437
  Test that installation worked by running the [Glimmer Meta-Example](#examples):
@@ -624,7 +627,7 @@ Mac | Windows | Linux
624
627
 
625
628
  ### Scaffold Application
626
629
 
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 a binary script for running the app conveniently.
630
+ 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).
628
631
 
629
632
  Scaffold Glimmer DSL for LibUI application with this command:
630
633
 
@@ -722,7 +725,7 @@ Or by using the raw rake command:
722
725
  rake gemspec:generate
723
726
  ```
724
727
 
725
- Once you install the gem (e.g. `gem install hello_world`), you can simply run the app with its binary script:
728
+ Once you install the gem (e.g. `gem install hello_world`), you can simply run the app with its executable script:
726
729
 
727
730
  ```
728
731
  app_name
@@ -736,8 +739,198 @@ hello_world
736
739
 
737
740
  ![glimmer-dsl-libui-mac-scaffold-app-initial-screen.png](images/glimmer-dsl-libui-mac-scaffold-app-initial-screen.png)
738
741
 
742
+ ### Scaffold Custom Control
743
+
744
+ 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:
745
+
746
+ ```
747
+ glimmer scaffold:customcontrol[name,namespace]
748
+ ```
749
+
750
+ The `name` represents the [custom control](#custom-components) view class name (it can be underscored, and Glimmer will automatically classify it).
751
+
752
+ 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).
753
+
754
+ You can also use the shorter `cc` alias for `customcontrol`:
755
+
756
+ ```
757
+ glimmer scaffold:cc[name,namespace]
758
+ ```
759
+
760
+ For example by running this command under a `hello_world` application:
761
+
762
+ ```
763
+ glimmer scaffold:cc[model_form]
764
+ ```
765
+
766
+ That will generate this class under `app/hello_world/view/model_form`:
767
+
768
+ ```ruby
769
+ class HelloWorld
770
+ module View
771
+ class ModelForm
772
+ include Glimmer::LibUI::CustomControl
773
+
774
+ ## Add options like the following to configure CustomControl by outside consumers
775
+ #
776
+ # options :custom_text, :background_color
777
+ # option :foreground_color, default: :red
778
+
779
+ # Replace example options with your own options
780
+ option :model
781
+ option :attributes
782
+
783
+ ## Use before_body block to pre-initialize variables to use in body
784
+ #
785
+ #
786
+ before_body do
787
+ # Replace example code with your own before_body code
788
+ default_model_attributes = [:first_name, :last_name, :email]
789
+ default_model_class = Struct.new(*default_model_attributes)
790
+ self.model ||= default_model_class.new
791
+ self.attributes ||= default_model_attributes
792
+ end
793
+
794
+ ## Use after_body block to setup observers for controls in body
795
+ #
796
+ # after_body do
797
+ #
798
+ # end
799
+
800
+ ## Add control content under custom control body
801
+ ##
802
+ ## If you want to add a window as the top-most control,
803
+ ## consider creating a custom window instead
804
+ ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
805
+ #
806
+ body {
807
+ # Replace example content (model_form custom control) with your own custom control content.
808
+ form {
809
+ attributes.each do |attribute|
810
+ entry { |e|
811
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
812
+ text <=> [model, attribute]
813
+ }
814
+ end
815
+ }
816
+ }
817
+
818
+ end
819
+ end
820
+ end
821
+ ```
822
+
823
+ 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:
824
+
825
+ ```ruby
826
+ window {
827
+ vertical_box {
828
+ label('Form:')
829
+ model_form(model: some_model, attributes: array_of_attributes)
830
+ }
831
+ }
832
+ ```
833
+
834
+ Here is an example that generates a [custom control](#custom-components) with a namespace:
835
+
836
+ ```
837
+ glimmer scaffold:cc[model_form,common]
838
+ ```
839
+
840
+ That will generate this class under `app/common/view/model_form`:
841
+
842
+ ```ruby
843
+ module Common
844
+ module View
845
+ class ModelForm
846
+ include Glimmer::LibUI::CustomControl
847
+
848
+ ## Add options like the following to configure CustomControl by outside consumers
849
+ #
850
+ # options :custom_text, :background_color
851
+ # option :foreground_color, default: :red
852
+
853
+ # Replace example options with your own options
854
+ option :model
855
+ option :attributes
856
+
857
+ ## Use before_body block to pre-initialize variables to use in body
858
+ #
859
+ #
860
+ before_body do
861
+ # Replace example code with your own before_body code
862
+ default_model_attributes = [:first_name, :last_name, :email]
863
+ default_model_class = Struct.new(*default_model_attributes)
864
+ self.model ||= default_model_class.new
865
+ self.attributes ||= default_model_attributes
866
+ end
867
+
868
+ ## Use after_body block to setup observers for controls in body
869
+ #
870
+ # after_body do
871
+ #
872
+ # end
873
+
874
+ ## Add control content under custom control body
875
+ ##
876
+ ## If you want to add a window as the top-most control,
877
+ ## consider creating a custom window instead
878
+ ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
879
+ #
880
+ body {
881
+ # Replace example content (model_form custom control) with your own custom control content.
882
+ form {
883
+ attributes.each do |attribute|
884
+ entry { |e|
885
+ label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
886
+ text <=> [model, attribute]
887
+ }
888
+ end
889
+ }
890
+ }
891
+
892
+ end
893
+ end
894
+ end
895
+ ```
896
+
897
+ When that file is required in another view (e.g. `require 'common/view/model_form'`), the `model_form` keyword becomes available:
898
+
899
+ ```ruby
900
+ window {
901
+ vertical_box {
902
+ label('Form:')
903
+ model_form(model: some_model, attributes: array_of_attributes)
904
+ }
905
+ }
906
+ ```
907
+
908
+ 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:
909
+
910
+ ```ruby
911
+ window {
912
+ vertical_box {
913
+ label('Form:')
914
+ common__view__model_form(model: some_model, attributes: array_of_attributes)
915
+ }
916
+ }
917
+ ```
918
+
919
+ Or another `model_form` [custom control](#custom-components) view:
920
+
921
+ ```ruby
922
+ window {
923
+ vertical_box {
924
+ label('Form:')
925
+ hello_world__view__model_form(model: some_model, attributes: array_of_attributes)
926
+ }
927
+ }
928
+ ```
929
+
739
930
  ### Scaffold Custom Window
740
931
 
932
+ A custom window is a specialization of a custom control that has a `window` as its `body` root.
933
+
741
934
  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:
742
935
 
743
936
  ```
@@ -888,79 +1081,81 @@ Or another `train` custom window view:
888
1081
  hello_world__view__train.show
889
1082
  ```
890
1083
 
891
- ### Scaffold Custom Control
1084
+ ### Scaffold Custom Shape
892
1085
 
893
- 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:
1086
+ When you are in a scaffolded application, you can scaffold a new [custom shape](#custom-components) (a shape that you can put anything in to represent a view concept in your application) by running this command:
894
1087
 
895
1088
  ```
896
- glimmer scaffold:customcontrol[name,namespace]
1089
+ glimmer scaffold:customshape[name,namespace]
897
1090
  ```
898
1091
 
899
- The `name` represents the custom control view class name (it can be underscored, and Glimmer will automatically classify it).
1092
+ The `name` represents the [custom shape](#custom-components) view class name (it can be underscored, and Glimmer will automatically classify it).
900
1093
 
901
- 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).
1094
+ The `namespace` is optional and represents the module that the [custom shape](#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).
902
1095
 
903
- You can also use the shorter `cc` alias for `customcontrol`:
1096
+ You can also use the shorter `cs` alias for `customshape`:
904
1097
 
905
1098
  ```
906
- glimmer scaffold:cc[name,namespace]
1099
+ glimmer scaffold:cs[name,namespace]
907
1100
  ```
908
1101
 
909
1102
  For example by running this command under a `hello_world` application:
910
1103
 
911
1104
  ```
912
- glimmer scaffold:cw[model_form]
1105
+ glimmer scaffold:cs[heart]
913
1106
  ```
914
1107
 
915
- That will generate this class under `app/hello_world/view/model_form`:
1108
+ That will generate this class under `app/hello_world/view/heart`:
916
1109
 
917
1110
  ```ruby
918
1111
  class HelloWorld
919
1112
  module View
920
- class ModelForm
921
- include Glimmer::LibUI::CustomControl
1113
+ class Heart
1114
+ include Glimmer::LibUI::CustomShape
922
1115
 
923
- ## Add options like the following to configure CustomControl by outside consumers
1116
+ ## Add options like the following to configure CustomShape by outside consumers
924
1117
  #
925
- # options :custom_text, :background_color
926
- # option :foreground_color, default: :red
927
-
928
- # Replace example options with your own options
929
- option :model
930
- option :attributes
1118
+ # options :option1, option2, option3
1119
+ option :background_color, default: :red
1120
+ option :size_width, default: 100
1121
+ option :size_height, default: 100
1122
+ option :location_x, default: 0
1123
+ option :location_y, default: 0
931
1124
 
932
1125
  ## Use before_body block to pre-initialize variables to use in body
933
1126
  #
934
1127
  #
935
- before_body do
936
- # Replace example code with your own before_body code
937
- default_model_attributes = [:first_name, :last_name, :email]
938
- default_model_class = Struct.new(*default_model_attributes)
939
- self.model ||= default_model_class.new
940
- self.attributes ||= default_model_attributes
941
- end
1128
+ # before_body do
1129
+ #
1130
+ # end
942
1131
 
943
- ## Use after_body block to setup observers for controls in body
1132
+ ## Use after_body block to setup observers for shapes in body
944
1133
  #
945
1134
  # after_body do
946
1135
  #
947
1136
  # end
948
1137
 
949
- ## Add control content under custom control body
950
- ##
951
- ## If you want to add a window as the top-most control,
952
- ## consider creating a custom window instead
953
- ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
1138
+ ## Add shape content under custom shape body
954
1139
  #
955
1140
  body {
956
- # Replace example content (model_form custom control) with your own custom control content.
957
- form {
958
- attributes.each do |attribute|
959
- entry { |e|
960
- label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
961
- text <=> [model, attribute]
962
- }
963
- end
1141
+ # Replace example content below (heart shape) with custom shape content
1142
+ shape(location_x, location_y) {
1143
+ # This fill color is shared under all direct children of `shape`
1144
+ fill background_color
1145
+
1146
+ bezier(
1147
+ size_width - size_width*0.66, size_height/2 - size_height*0.33,
1148
+ size_width*0.65 - size_width*0.66, 0 - size_height*0.33,
1149
+ size_width/2 - size_width*0.66, size_height*0.75 - size_height*0.33,
1150
+ size_width - size_width*0.66, size_height - size_height*0.33
1151
+ )
1152
+
1153
+ bezier(
1154
+ size_width - size_width*0.66, size_height/2 - size_height*0.33,
1155
+ size_width*1.35 - size_width*0.66, 0 - size_height*0.33,
1156
+ size_width*1.5 - size_width*0.66, size_height*0.75 - size_height*0.33,
1157
+ size_width - size_width*0.66, size_height - size_height*0.33
1158
+ )
964
1159
  }
965
1160
  }
966
1161
 
@@ -969,72 +1164,83 @@ class HelloWorld
969
1164
  end
970
1165
  ```
971
1166
 
972
- 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:
1167
+ When the generated file is required in another view (e.g. `require 'hello_world/view/heart'`), the [custom shape](#custom-components) keyword `heart` become available and reusable, like by calling:
973
1168
 
974
1169
  ```ruby
975
1170
  window {
976
- vertical_box {
977
- label('Form:')
978
- model_form(model: some_model, attributes: array_of_attributes)
1171
+ area {
1172
+ heart
1173
+ }
1174
+ }
1175
+ ```
1176
+
1177
+ You can pass `heart` options (as defined with `option` near the top of the class):
1178
+
1179
+ ```ruby
1180
+ window {
1181
+ area {
1182
+ heart(location_x: 25, location_y: 50)
979
1183
  }
980
1184
  }
981
1185
  ```
982
1186
 
983
- Here is an example that generates a custom control with a namespace:
1187
+ Here is an example that generates a [custom shape](#custom-components) with a namespace:
984
1188
 
985
1189
  ```
986
- glimmer scaffold:cw[model_form,common]
1190
+ glimmer scaffold:cs[heart,acme]
987
1191
  ```
988
1192
 
989
- That will generate this class under `app/common/view/model_form`:
1193
+ That will generate this class under `app/acme/view/heart`:
990
1194
 
991
1195
  ```ruby
992
- module Common
1196
+ module Acme
993
1197
  module View
994
- class ModelForm
995
- include Glimmer::LibUI::CustomControl
1198
+ class Heart
1199
+ include Glimmer::LibUI::CustomShape
996
1200
 
997
- ## Add options like the following to configure CustomControl by outside consumers
1201
+ ## Add options like the following to configure CustomShape by outside consumers
998
1202
  #
999
- # options :custom_text, :background_color
1000
- # option :foreground_color, default: :red
1001
-
1002
- # Replace example options with your own options
1003
- option :model
1004
- option :attributes
1203
+ # options :option1, option2, option3
1204
+ option :background_color, default: :red
1205
+ option :size_width, default: 100
1206
+ option :size_height, default: 100
1207
+ option :location_x, default: 0
1208
+ option :location_y, default: 0
1005
1209
 
1006
1210
  ## Use before_body block to pre-initialize variables to use in body
1007
1211
  #
1008
1212
  #
1009
- before_body do
1010
- # Replace example code with your own before_body code
1011
- default_model_attributes = [:first_name, :last_name, :email]
1012
- default_model_class = Struct.new(*default_model_attributes)
1013
- self.model ||= default_model_class.new
1014
- self.attributes ||= default_model_attributes
1015
- end
1213
+ # before_body do
1214
+ #
1215
+ # end
1016
1216
 
1017
- ## Use after_body block to setup observers for controls in body
1217
+ ## Use after_body block to setup observers for shapes in body
1018
1218
  #
1019
1219
  # after_body do
1020
1220
  #
1021
1221
  # end
1022
1222
 
1023
- ## Add control content under custom control body
1024
- ##
1025
- ## If you want to add a window as the top-most control,
1026
- ## consider creating a custom window instead
1027
- ## (Glimmer::LibUI::CustomWindow offers window convenience methods, like show and hide)
1223
+ ## Add shape content under custom shape body
1028
1224
  #
1029
1225
  body {
1030
- # Replace example content (model_form custom control) with your own custom control content.
1031
- form {
1032
- attributes.each do |attribute|
1033
- entry { |e|
1034
- label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
1035
- text <=> [model, attribute]
1036
- }
1037
- end
1226
+ # Replace example content below (heart shape) with your own custom shape content
1227
+ shape(location_x, location_y) {
1228
+ # This fill color is shared under all direct children of `shape`
1229
+ fill background_color
1230
+
1231
+ bezier(
1232
+ size_width - size_width*0.66, size_height/2 - size_height*0.33,
1233
+ size_width*0.65 - size_width*0.66, 0 - size_height*0.33,
1234
+ size_width/2 - size_width*0.66, size_height*0.75 - size_height*0.33,
1235
+ size_width - size_width*0.66, size_height - size_height*0.33
1236
+ )
1237
+
1238
+ bezier(
1239
+ size_width - size_width*0.66, size_height/2 - size_height*0.33,
1240
+ size_width*1.35 - size_width*0.66, 0 - size_height*0.33,
1241
+ size_width*1.5 - size_width*0.66, size_height*0.75 - size_height*0.33,
1242
+ size_width - size_width*0.66, size_height - size_height*0.33
1243
+ )
1038
1244
  }
1039
1245
  }
1040
1246
 
@@ -1043,42 +1249,105 @@ module Common
1043
1249
  end
1044
1250
  ```
1045
1251
 
1046
- When that file is required in another view (e.g. `require 'common/view/model_form'`), the `model_form` keyword becomes available:
1252
+ When that file is required in another view (e.g. `require 'acme/view/heart'`), the `heart` keyword becomes available:
1047
1253
 
1048
1254
  ```ruby
1049
1255
  window {
1050
- vertical_box {
1051
- label('Form:')
1052
- model_form(model: some_model, attributes: array_of_attributes)
1256
+ area {
1257
+ heart
1053
1258
  }
1054
1259
  }
1055
1260
  ```
1056
1261
 
1057
- 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:
1262
+ If for whatever reason, you end up with 2 [custom shape](#custom-components) views having the same name with different namespaces, then you can invoke the specific [custom shape](#custom-components) you want by including the Ruby namespace in underscored format separated by double-underscores:
1058
1263
 
1059
1264
  ```ruby
1060
1265
  window {
1061
- vertical_box {
1062
- label('Form:')
1063
- common__view__model_form(model: some_model, attributes: array_of_attributes)
1266
+ area {
1267
+ acme__view__heart
1268
+ }
1269
+ }
1270
+ ```
1271
+
1272
+ Or another `heart` [custom shape](#custom-components) view:
1273
+
1274
+ ```ruby
1275
+ window {
1276
+ area {
1277
+ hello_world__view__heart
1064
1278
  }
1065
1279
  }
1066
1280
  ```
1067
1281
 
1068
- Or another `model_form` custom control view:
1282
+ ### Scaffold Custom Control Gem
1283
+
1284
+ 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:
1285
+
1286
+ ```
1287
+ glimmer scaffold:gem:customcontrol[name,namespace]
1288
+ ```
1289
+
1290
+ That will generate a [custom control](#custom-components) gem project under the naming convention: `glimmer-libui-cc-name-namespace`
1291
+
1292
+ 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.
1293
+
1294
+ 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).
1295
+
1296
+ 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.
1297
+
1298
+ Here is a shorter alias for the [custom control](#custom-components) gem scaffolding command:
1299
+
1300
+ ```
1301
+ glimmer scaffold:gem:cc[name,namespace]
1302
+ ```
1303
+
1304
+ You can package the newly scaffolded project as a Ruby gem by running this command:
1305
+
1306
+ ```
1307
+ glimmer package:gem
1308
+ ```
1309
+
1310
+ Or by using the raw rake command:
1311
+
1312
+ ```
1313
+ rake build
1314
+ ```
1315
+
1316
+ You can generate the application gemspec explicitly if needed with this command (though it is not needed to build the gem):
1317
+
1318
+ ```
1319
+ glimmer package:gemspec
1320
+ ```
1321
+
1322
+ Or by using the raw rake command:
1323
+
1324
+ ```
1325
+ rake gemspec:generate
1326
+ ```
1327
+
1328
+ 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`, `Glimmer::LibUI::CustomControl`, or `Glimmer::LibUI::CustomShape` is mixed.
1329
+
1330
+ For example:
1069
1331
 
1070
1332
  ```ruby
1333
+ require 'glimmer-libui-cc-model_form-acme'
1334
+
1335
+ ...
1071
1336
  window {
1072
1337
  vertical_box {
1073
1338
  label('Form:')
1074
- hello_world__view__model_form(model: some_model, attributes: array_of_attributes)
1339
+
1340
+ model_form(model: some_model, attributes: some_attributes)
1075
1341
  }
1076
1342
  }
1343
+ ...
1077
1344
  ```
1078
1345
 
1079
1346
  ### Scaffold Custom Window Gem
1080
1347
 
1081
- You can scaffold a Ruby gem around a reusable custom window by running this command:
1348
+ A custom window is a specialization of a custom control that has a `window` as its `body` root.
1349
+
1350
+ You can scaffold a Ruby gem around a reusable custom window to expose publicly and make available for multiple projects by running this command:
1082
1351
 
1083
1352
  ```
1084
1353
  glimmer scaffold:gem:customwindow[name,namespace]
@@ -1122,9 +1391,9 @@ Or by using the raw rake command:
1122
1391
  rake gemspec:generate
1123
1392
  ```
1124
1393
 
1125
- 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.
1394
+ 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.
1126
1395
 
1127
- 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.
1396
+ 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`, `Glimmer::LibUI::CustomControl`, or `Glimmer::LibUI::CustomShape` is mixed.
1128
1397
 
1129
1398
  For example:
1130
1399
 
@@ -1136,26 +1405,26 @@ greeter.show
1136
1405
  ...
1137
1406
  ```
1138
1407
 
1139
- ### Scaffold Custom Control Gem
1408
+ ### Scaffold Custom Shape Gem
1140
1409
 
1141
- You can scaffold a Ruby gem around a reusable custom control by running this command:
1410
+ You can scaffold a Ruby gem around a reusable [custom shape](#custom-components) to expose publicly and make available for multiple projects by running this command:
1142
1411
 
1143
1412
  ```
1144
- glimmer scaffold:gem:customcontrol[name,namespace]
1413
+ glimmer scaffold:gem:customshape[name,namespace]
1145
1414
  ```
1146
1415
 
1147
- That will generate a custom control gem project under the naming convention: `glimmer-libui-cc-name-namespace`
1416
+ That will generate a [custom shape](#custom-components) gem project under the naming convention: `glimmer-libui-cc-name-namespace`
1148
1417
 
1149
- 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.
1418
+ The naming convention helps with discoverability of Ruby gems using the command `glimmer list:gems:customshape[query]` (or alias: `glimmer list:gems:cs[query]`) where filtering `query` is optional.
1150
1419
 
1151
- The `name` is the custom control class name, which must not contain dashes by convention (multiple words can be concatenated or can use underscores between them).
1420
+ The `name` is the [custom shape](#custom-components) class name, which must not contain dashes by convention (multiple words can be concatenated or can use underscores between them).
1152
1421
 
1153
- The `namespace` is needed to avoid clashing with other custom control 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.
1422
+ The `namespace` is needed to avoid clashing with other [custom shape](#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.
1154
1423
 
1155
- Here is a shorter alias for the custom control gem scaffolding command:
1424
+ Here is a shorter alias for the [custom shape](#custom-components) gem scaffolding command:
1156
1425
 
1157
1426
  ```
1158
- glimmer scaffold:gem:cc[name,namespace]
1427
+ glimmer scaffold:gem:cs[name,namespace]
1159
1428
  ```
1160
1429
 
1161
1430
  You can package the newly scaffolded project as a Ruby gem by running this command:
@@ -1182,24 +1451,40 @@ Or by using the raw rake command:
1182
1451
  rake gemspec:generate
1183
1452
  ```
1184
1453
 
1185
- 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.
1454
+ 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`, `Glimmer::LibUI::CustomControl`, or `Glimmer::LibUI::CustomShape` is mixed.
1186
1455
 
1187
1456
  For example:
1188
1457
 
1189
1458
  ```ruby
1190
- require 'glimmer-libui-cc-model_form-acme'
1459
+ require 'glimmer-libui-cs-heart-acme'
1191
1460
 
1192
1461
  ...
1193
1462
  window {
1194
- vertical_box {
1195
- label('Form:')
1196
-
1197
- model_form(model: some_model, attributes: some_attributes)
1463
+ area {
1464
+ heart
1198
1465
  }
1199
1466
  }
1200
1467
  ...
1201
1468
  ```
1202
1469
 
1470
+ ### List Custom Control Gems
1471
+
1472
+ Custom control gems are scaffolded to follow the naming convention: `glimmer-libui-cc-name-namespace`
1473
+
1474
+ The naming convention helps with discoverability of Ruby gems using the command:
1475
+
1476
+ ```
1477
+ glimmer list:gems:customcontrol[query]
1478
+ ```
1479
+
1480
+ Or by using the shorter alias:
1481
+
1482
+ ```
1483
+ glimmer list:gems:cc[query]
1484
+ ```
1485
+
1486
+ The filtering `query` is optional.
1487
+
1203
1488
  ### List Custom Window Gems
1204
1489
 
1205
1490
  Custom window gems are scaffolded to follow the naming convention: `glimmer-libui-cw-name-namespace`
@@ -1218,21 +1503,20 @@ glimmer list:gems:cw[query]
1218
1503
 
1219
1504
  The filtering `query` is optional.
1220
1505
 
1506
+ ### List Custom Shape Gems
1221
1507
 
1222
- ### List Custom Control Gems
1223
-
1224
- Custom control gems are scaffolded to follow the naming convention: `glimmer-libui-cw-name-namespace`
1508
+ Custom shape gems are scaffolded to follow the naming convention: `glimmer-libui-cs-name-namespace`
1225
1509
 
1226
1510
  The naming convention helps with discoverability of Ruby gems using the command:
1227
1511
 
1228
1512
  ```
1229
- glimmer list:gems:customcontrol[query]
1513
+ glimmer list:gems:customshape[query]
1230
1514
  ```
1231
1515
 
1232
1516
  Or by using the shorter alias:
1233
1517
 
1234
1518
  ```
1235
- glimmer list:gems:cc[query]
1519
+ glimmer list:gems:cs[query]
1236
1520
  ```
1237
1521
 
1238
1522
  The filtering `query` is optional.
@@ -1725,7 +2009,7 @@ Learn more by checking out [examples](#examples).
1725
2009
 
1726
2010
  [EARLY ALPHA FEATURE]
1727
2011
 
1728
- `refined_table` is a custom control provided exclusively by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
2012
+ `refined_table` is a [custom control](#custom-components) provided exclusively by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
1729
2013
  that includes filtering and pagination support out of the box and can handle very large amounts of data (e.g. 50,000 rows).
1730
2014
 
1731
2015
  It is currently an early alpha feature, so please test-drive and report issues if you encounter any.
@@ -1747,7 +2031,7 @@ API:
1747
2031
 
1748
2032
  - `refined_model_array` (`Array`): `model_array` with filtering and pagination applied (useful to grab a table row model by index).
1749
2033
  - `filtered_model_array` (`Array`): `model_array` with filtering applied, but without pagination
1750
- - `table_proxy`: control proxy object for the `table` contained in the `refined_table` custom control
2034
+ - `table_proxy`: control proxy object for the `table` contained in the `refined_table` [custom control](#custom-components)
1751
2035
 
1752
2036
  If the initial `model_array` has no more than a single page of data, then pagination buttons are hidden (but, the filter field remains).
1753
2037
 
@@ -1990,7 +2274,7 @@ Mac | Windows | Linux
1990
2274
  **(ALPHA FEATURE)**
1991
2275
 
1992
2276
  [libui-ng](https://github.com/libui-ng/libui-ng) does not support `image` rendering outside of `table` yet.
1993
- 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).
2277
+ 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).
1994
2278
 
1995
2279
  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:
1996
2280
  - It only supports the `.png` file format.
@@ -2163,7 +2447,7 @@ One final note is that in Linux, table images grow and shrink with the image siz
2163
2447
 
2164
2448
  ![linux table image](images/glimmer-dsl-libui-linux-basic-table-image.png)
2165
2449
 
2166
- 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.
2450
+ 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).
2167
2451
 
2168
2452
  #### Colors
2169
2453
 
@@ -2476,7 +2760,7 @@ BasicCompositeShape.launch
2476
2760
 
2477
2761
  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.
2478
2762
 
2479
- 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)):
2763
+ 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)):
2480
2764
 
2481
2765
  ```ruby
2482
2766
  require 'glimmer-dsl-libui'
@@ -2605,21 +2889,21 @@ SpinnerExample.new.launch
2605
2889
  - 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)
2606
2890
  - Color alpha value defaults to `1.0` when not specified.
2607
2891
 
2608
- ### Custom Controls
2892
+ ### Custom Components
2609
2893
 
2610
- 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.
2894
+ Custom components like custom controls, custom windows, and custom shapes can be defined to provide new features or act as composites of existing controls/shapes that need to be reused multiple times in an application or across multiple applications. Custom components save a lot of development time through reuse, improving productivity and maintainability immensely.
2611
2895
 
2612
2896
  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.
2613
2897
 
2614
- There are two ways to define custom controls:
2615
- - 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)`).
2616
- - 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.
2898
+ There are two ways to define custom components:
2899
+ - Method-Based: simply define a method representing the custom component you want (e.g. `address_view`) with any options needed (e.g. `address(address_model: some_model)`).
2900
+ - Class-Based: define a class matching the camelcased name of the custom component by convention (e.g. the `address_view` custom component keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`, `include Glimmer::LibUI::CustomWindow`, or `include Glimmer::LibUI::CustomShape` depending on if the component represents a standard control, a whole window, or an [area canvas graphics shape](#area-path-shapes). Classes add the benefit of being able to distribute the custom components into a separate file for external reuse from multiple views or for sharing as a Ruby gem.
2617
2901
 
2618
- 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`).
2902
+ 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`).
2619
2903
 
2620
2904
  #### Method-Based Custom Controls
2621
2905
 
2622
- Simply define a method representing the custom control you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
2906
+ Simply define a method representing the custom component you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
2623
2907
 
2624
2908
  Example that defines `form_field`, `address_form`, `label_pair`, and `address_view` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2625
2909
 
@@ -2718,9 +3002,9 @@ window('Method-Based Custom Keyword') {
2718
3002
 
2719
3003
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
2720
3004
 
2721
- #### Class-Based Custom Controls
3005
+ #### Class-Based Custom Components
2722
3006
 
2723
- 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.
3007
+ 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.
2724
3008
 
2725
3009
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
2726
3010
 
@@ -2850,13 +3134,185 @@ ClassBasedCustomControls.launch
2850
3134
 
2851
3135
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
2852
3136
 
2853
- 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.
3137
+ Example of a `cube` custom shape (you may copy/paste in [`girb`](#girb-glimmer-irb)):
3138
+
3139
+ ```ruby
3140
+ require 'glimmer-dsl-libui'
3141
+
3142
+ # class-based custom shape using Glimmer::LibUI::CustomShape mixin, which automatically
3143
+ # augments the Glimmer GUI DSL with the underscored version of the class name: `cube`
3144
+ # while accepting hash options matching the options declared on the class.
3145
+ # (e.g. `cube(location_x: 50, location_y: 100)` )
3146
+ class Cube
3147
+ include Glimmer::LibUI::CustomShape
3148
+
3149
+ DEFAULT_SIZE = 28
3150
+
3151
+ option :location_x, default: 0
3152
+ option :location_y, default: 0
3153
+ option :rectangle_width, default: nil
3154
+ option :rectangle_height, default: nil
3155
+ option :cube_height, default: 75
3156
+ option :background_color, default: :brown
3157
+ option :foreground_color
3158
+ option :line_thickness, default: 1
3159
+
3160
+ # The before_body block executes before building the body
3161
+ before_body do
3162
+ self.rectangle_width ||= rectangle_height || cube_height || DEFAULT_SIZE
3163
+ self.rectangle_height ||= rectangle_width || cube_height || DEFAULT_SIZE
3164
+ self.cube_height ||= rectangle_width || rectangle_height || DEFAULT_SIZE
3165
+ if foreground_color
3166
+ self.foreground_color = Glimmer::LibUI.interpret_color(foreground_color)
3167
+ self.foreground_color[:thickness] ||= line_thickness
3168
+ else
3169
+ self.foreground_color = [0, 0, 0, thickness: line_thickness]
3170
+ end
3171
+ end
3172
+
3173
+ # Optionally, after_body could be defined to perform operations after building the body
3174
+ # like setting up observers.
3175
+ #
3176
+ # after_body do
3177
+ # end
3178
+
3179
+ body {
3180
+ # the shape keyword (alias for composite_shape) enables building a composite shape that is treated as one shape
3181
+ # like a cube containing polygons, a polyline, a rectangle, and a line
3182
+ # with the fill and stroke colors getting inherited by all children that do not specify them
3183
+ shape(location_x, location_y) {
3184
+ fill background_color
3185
+ stroke foreground_color
3186
+
3187
+ bottom = polygon(0, cube_height + rectangle_height / 2.0,
3188
+ rectangle_width / 2.0, cube_height,
3189
+ rectangle_width, cube_height + rectangle_height / 2.0,
3190
+ rectangle_width / 2.0, cube_height + rectangle_height) {
3191
+ # inherits fill property from parent shape if not set
3192
+ # inherits stroke property from parent shape if not set
3193
+ }
3194
+ body = rectangle(0, rectangle_height / 2.0, rectangle_width, cube_height) {
3195
+ # inherits fill property from parent shape if not set
3196
+ # stroke is overridden to ensure a different value from parent
3197
+ stroke thickness: 0
3198
+ }
3199
+ polyline(0, rectangle_height / 2.0 + cube_height,
3200
+ 0, rectangle_height / 2.0,
3201
+ rectangle_width, rectangle_height / 2.0,
3202
+ rectangle_width, rectangle_height / 2.0 + cube_height) {
3203
+ # inherits stroke property from parent shape if not set
3204
+ }
3205
+ top = polygon(0, rectangle_height / 2.0,
3206
+ rectangle_width / 2.0, 0,
3207
+ rectangle_width, rectangle_height / 2.0,
3208
+ rectangle_width / 2.0, rectangle_height) {
3209
+ # inherits fill property from parent shape if not set
3210
+ # inherits stroke property from parent shape if not set
3211
+ }
3212
+ line(rectangle_width / 2.0, cube_height + rectangle_height,
3213
+ rectangle_width / 2.0, rectangle_height) {
3214
+ # inherits stroke property from parent shape if not set
3215
+ }
3216
+ }
3217
+ }
3218
+ end
3219
+
3220
+ class BasicCustomShape
3221
+ include Glimmer::LibUI::Application
3222
+
3223
+ body {
3224
+ window {
3225
+ title 'Basic Custom Shape'
3226
+ content_size 200, 225
3227
+
3228
+ @area = area {
3229
+ rectangle(0, 0, 200, 225) {
3230
+ fill :white
3231
+ }
3232
+
3233
+ 7.times do |n|
3234
+ x_location = (rand*125).to_i%200 + (rand*15).to_i
3235
+ y_location = (rand*125).to_i%200 + (rand*15).to_i
3236
+ shape_color = [rand*125 + 130, rand*125 + 130, rand*125 + 130]
3237
+ shape_size = 20+n
3238
+
3239
+ cube(
3240
+ location_x: x_location,
3241
+ location_y: y_location,
3242
+ rectangle_width: shape_size*2,
3243
+ rectangle_height: shape_size,
3244
+ cube_height: shape_size*2,
3245
+ background_color: shape_color,
3246
+ line_thickness: 2
3247
+ ) { |the_shape|
3248
+ on_mouse_up do |area_mouse_event|
3249
+ # Change color on mouse up without dragging
3250
+ if @drag_shape.nil?
3251
+ background_color = [rand(255), rand(255), rand(255)]
3252
+ the_shape.fill = background_color
3253
+ end
3254
+ end
3255
+
3256
+ on_mouse_drag_start do |area_mouse_event|
3257
+ @drag_shape = the_shape
3258
+ @drag_x = area_mouse_event[:x]
3259
+ @drag_y = area_mouse_event[:y]
3260
+ end
3261
+
3262
+ on_mouse_drag do |area_mouse_event|
3263
+ if @drag_shape && @drag_x && @drag_y
3264
+ drag_distance_width = area_mouse_event[:x] - @drag_x
3265
+ drag_distance_height = area_mouse_event[:y] - @drag_y
3266
+ @drag_shape.x += drag_distance_width
3267
+ @drag_shape.y += drag_distance_height
3268
+ @drag_x = area_mouse_event[:x]
3269
+ @drag_y = area_mouse_event[:y]
3270
+ end
3271
+ end
3272
+
3273
+ on_mouse_drop do |area_mouse_event|
3274
+ @drag_shape = nil
3275
+ @drag_x = nil
3276
+ @drag_y = nil
3277
+ end
3278
+ }
3279
+ end
3280
+
3281
+ # this general area on_mouse_drag listener is needed to ensure that dragging a shape
3282
+ # outside of its boundaries would still move the dragged shape
3283
+ on_mouse_drag do |area_mouse_event|
3284
+ if @drag_shape && @drag_x && @drag_y
3285
+ drag_distance_width = area_mouse_event[:x] - @drag_x
3286
+ drag_distance_height = area_mouse_event[:y] - @drag_y
3287
+ @drag_shape.x += drag_distance_width
3288
+ @drag_shape.y += drag_distance_height
3289
+ @drag_x = area_mouse_event[:x]
3290
+ @drag_y = area_mouse_event[:y]
3291
+ end
3292
+ end
3293
+
3294
+ on_mouse_drop do |area_mouse_event|
3295
+ @drag_shape = nil
3296
+ @drag_x = nil
3297
+ @drag_y = nil
3298
+ end
3299
+ }
3300
+ }
3301
+ }
3302
+ end
3303
+
3304
+ BasicCustomShape.launch
3305
+ ```
3306
+
3307
+ ![glimmer-dsl-libui-mac-basic-custom-shape.gif](/images/glimmer-dsl-libui-mac-basic-composite-shape.gif)
3308
+
3309
+ 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.
2854
3310
 
2855
3311
  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.
2856
3312
 
2857
3313
  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.
2858
3314
 
2859
- 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.
3315
+ 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.
2860
3316
 
2861
3317
  ### Observer Pattern
2862
3318