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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +439 -178
- data/VERSION +1 -1
- data/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md +5 -5
- data/docs/examples/GLIMMER-DSL-LIBUI-BASIC-EXAMPLES.md +23 -0
- data/examples/basic_composite_shape.rb +2 -0
- data/examples/basic_custom_shape.rb +168 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/custom_shape_expression.rb +58 -0
- data/lib/glimmer/dsl/libui/dsl.rb +1 -0
- data/lib/glimmer/dsl/libui/listener_expression.rb +6 -1
- data/lib/glimmer/launcher.rb +2 -5
- data/lib/glimmer/libui/custom_control.rb +25 -19
- data/lib/glimmer/libui/custom_shape.rb +258 -0
- data/lib/glimmer/rake_task/scaffold.rb +19 -18
- data/lib/glimmer/rake_task.rb +13 -13
- data/lib/glimmer-dsl-libui.rb +1 -1
- metadata +8 -4
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.
|
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
|
383
|
-
- [Method-Based Custom
|
384
|
-
- [Class-Based Custom
|
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.
|
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
|
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
|
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
|
-
|
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
|
-
|
1089
|
+
That will generate a [custom control](#custom-components) gem project under the naming convention: `glimmer-libui-cc-name-namespace`
|
898
1090
|
|
899
|
-
The
|
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
|
-
|
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
|
-
|
1097
|
+
Here is a shorter alias for the [custom control](#custom-components) gem scaffolding command:
|
908
1098
|
|
909
1099
|
```
|
910
|
-
glimmer scaffold:
|
1100
|
+
glimmer scaffold:gem:cc[name,namespace]
|
911
1101
|
```
|
912
1102
|
|
913
|
-
|
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
|
-
|
1109
|
+
Or by using the raw rake command:
|
982
1110
|
|
983
1111
|
```
|
984
|
-
|
1112
|
+
rake build
|
985
1113
|
```
|
986
1114
|
|
987
|
-
|
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
|
-
|
1121
|
+
Or by using the raw rake command:
|
1056
1122
|
|
1057
|
-
```
|
1058
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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
|
2612
|
+
### Custom Components
|
2524
2613
|
|
2525
|
-
Custom
|
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
|
2530
|
-
- Method-Based: simply define a method representing the custom
|
2531
|
-
- Class-Based: define a class matching the camelcased name of the custom
|
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
|
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
|
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
|
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
|
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
|
-
|
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
|
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
|
|