glimmer-dsl-libui 0.2.7 → 0.2.11
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +27 -0
- data/README.md +482 -85
- data/VERSION +1 -1
- data/examples/area_gallery.rb +18 -8
- data/examples/area_gallery2.rb +26 -12
- data/examples/area_gallery3.rb +16 -6
- data/examples/area_gallery4.rb +26 -12
- data/examples/basic_table_button.rb +5 -0
- data/examples/basic_table_progress_bar.rb +1 -1
- data/examples/color_button.rb +2 -0
- data/examples/custom_draw_text.rb +1 -1
- data/examples/custom_draw_text2.rb +1 -1
- data/examples/editable_table.rb +10 -0
- data/examples/form.rb +12 -2
- data/examples/form_table.rb +4 -0
- data/examples/meta_example.rb +33 -18
- data/examples/method_based_custom_keyword.rb +97 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +31 -6
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +22 -3
- data/lib/glimmer/libui/control_proxy/text_proxy.rb +1 -1
- data/lib/glimmer/libui.rb +1 -1
- metadata +5 -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.2.
|
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.2.11
|
2
2
|
## Prerequisite-Free Ruby Desktop Development GUI Library
|
3
3
|
[![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
|
4
4
|
[![Maintainability](https://api.codeclimate.com/v1/badges/ce2853efdbecf6ebdc73/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-libui/maintainability)
|
@@ -10,14 +10,16 @@
|
|
10
10
|
|
11
11
|
The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) as opposed to [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) or [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk) is the fact that [SWT](https://www.eclipse.org/swt/) and [Tk](https://www.tcl.tk/) are more mature than mid-alpha [libui](https://github.com/andlabs/libui) as GUI toolkits. Still, if there is only a need to build a small simple application, [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) could be a good convenient choice due to having zero prerequisites beyond the dependencies included in the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui). Also, just like [Glimmer DSL for Tk](https://github.com/AndyObtiva/glimmer-dsl-tk), its apps start instantly and have a small memory footprint. [LibUI](https://github.com/kojix2/LibUI) is a promising new GUI toolkit that might prove quite worthy in the future.
|
12
12
|
|
13
|
+
**(Note: although LibUI works on Windows, this project has not been tested on Windows yet. It has only been verified on Mac x64 and Linux x64)**
|
14
|
+
|
13
15
|
[Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) aims to provide a DSL similar to the [Glimmer DSL for SWT](https://github.com/AndyObtiva/glimmer-dsl-swt) to enable more productive desktop development in Ruby with:
|
14
16
|
- Declarative DSL syntax that visually maps to the GUI control hierarchy
|
15
17
|
- Convention over configuration via smart defaults and automation of low-level details
|
16
18
|
- Requiring the least amount of syntax possible to build GUI
|
17
|
-
- Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
18
19
|
- Custom Control support
|
19
|
-
-
|
20
|
-
-
|
20
|
+
- [Far Future Plan] Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
21
|
+
- [Far Future Plan] Scaffolding for new custom controls, apps, and gems
|
22
|
+
- [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
|
21
23
|
|
22
24
|
Hello, World!
|
23
25
|
|
@@ -61,7 +63,7 @@ window('Task Progress', 300, 200) {
|
|
61
63
|
|
62
64
|
on_clicked do
|
63
65
|
data.each_with_index do |row_data, row|
|
64
|
-
data[row] =
|
66
|
+
data[row][1] = 100 # automatically updates table due to implicit data-binding
|
65
67
|
end
|
66
68
|
end
|
67
69
|
}
|
@@ -84,14 +86,15 @@ window('Area Gallery', 400, 400) {
|
|
84
86
|
path { # declarative stable path
|
85
87
|
square(0, 0, 100)
|
86
88
|
square(100, 100, 400)
|
87
|
-
|
89
|
+
|
88
90
|
fill r: 102, g: 102, b: 204
|
89
91
|
}
|
90
92
|
path { # declarative stable path
|
91
93
|
rectangle(0, 100, 100, 400)
|
92
94
|
rectangle(100, 0, 400, 100)
|
93
|
-
|
94
|
-
|
95
|
+
|
96
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
97
|
+
fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
|
95
98
|
}
|
96
99
|
path { # declarative stable path
|
97
100
|
figure(100, 100) {
|
@@ -117,17 +120,26 @@ window('Area Gallery', 400, 400) {
|
|
117
120
|
fill r: 202, g: 102, b: 204, a: 0.5
|
118
121
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
119
122
|
}
|
123
|
+
path { # declarative stable path
|
124
|
+
arc(400, 220, 180, 90, 90, false)
|
125
|
+
|
126
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
127
|
+
fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
|
128
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
129
|
+
}
|
120
130
|
path { # declarative stable path
|
121
131
|
circle(200, 200, 90)
|
122
132
|
|
123
133
|
fill r: 202, g: 102, b: 204, a: 0.5
|
124
134
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
125
135
|
}
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
136
|
+
text(160, 40, 100) { # x, y, width
|
137
|
+
string {
|
138
|
+
font family: 'Times', size: 14
|
139
|
+
color :black
|
140
|
+
|
141
|
+
'Area Gallery'
|
142
|
+
}
|
131
143
|
}
|
132
144
|
|
133
145
|
on_mouse_event do |area_mouse_event|
|
@@ -186,7 +198,7 @@ window('Area Gallery', 400, 400) {
|
|
186
198
|
|
187
199
|
[Check Out Many More Examples Over Here!](#examples)
|
188
200
|
|
189
|
-
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is in
|
201
|
+
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is feature-complete and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
|
190
202
|
|
191
203
|
Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
|
192
204
|
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
|
@@ -197,7 +209,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
197
209
|
|
198
210
|
## Table of Contents
|
199
211
|
|
200
|
-
- [Glimmer DSL for LibUI
|
212
|
+
- [Glimmer DSL for LibUI](#)
|
201
213
|
- [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
|
202
214
|
- [Usage](#usage)
|
203
215
|
- [Girb (Glimmer IRB)](#girb-glimmer-irb)
|
@@ -211,8 +223,10 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
211
223
|
- [Table API](#table-api)
|
212
224
|
- [Area API](#area-api)
|
213
225
|
- [Smart Defaults and Conventions](#smart-defaults-and-conventions)
|
226
|
+
- [Custom Keywords](#custom-keywords)
|
214
227
|
- [API Gotchas](#api-gotchas)
|
215
228
|
- [Original API](#original-api)
|
229
|
+
- [Packaging](#packaging)
|
216
230
|
- [Glimmer Style Guide](#glimmer-style-guide)
|
217
231
|
- [Examples](#examples)
|
218
232
|
- [Basic Window](#basic-window)
|
@@ -247,6 +261,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
247
261
|
- [Color The Circles](#color-the-circles)
|
248
262
|
- [Basic Draw Text](#basic-draw-text)
|
249
263
|
- [Custom Draw Text](#custom-draw-text)
|
264
|
+
- [Method-Based Custom Keyword](#method-based-custom-keyword)
|
250
265
|
- [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
|
251
266
|
- [Help](#help)
|
252
267
|
- [Issues](#issues)
|
@@ -334,7 +349,7 @@ gem install glimmer-dsl-libui
|
|
334
349
|
Or install via Bundler `Gemfile`:
|
335
350
|
|
336
351
|
```ruby
|
337
|
-
gem 'glimmer-dsl-libui', '~> 0.2.
|
352
|
+
gem 'glimmer-dsl-libui', '~> 0.2.11'
|
338
353
|
```
|
339
354
|
|
340
355
|
Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
|
@@ -446,7 +461,7 @@ Control(Args) | Properties | Listeners
|
|
446
461
|
`msg_box_error(window = main_window as Glimmer::LibUI::WindowProxy, title as String, description as String)` | None | None
|
447
462
|
`non_wrapping_multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
|
448
463
|
`password_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
|
449
|
-
`path(draw_fill_mode = :winding)` | `fill` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0
|
464
|
+
`path(draw_fill_mode = :winding)` | `fill` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `stroke` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `:cap` as (`:round`, `:square`, `:flat`), `:join` as (`:miter`, `:round`, `:bevel`), `:thickness` as `Numeric`, `:miter_limit` as `Numeric`, `:dashes` as `Array` of `Numeric` ) | None
|
450
465
|
`preferences_menu_item` | None | `on_clicked`
|
451
466
|
`progress_bar` | `value` (`Numeric`) | None
|
452
467
|
`progress_bar_column(name as String)` | None | None
|
@@ -457,10 +472,10 @@ Control(Args) | Properties | Listeners
|
|
457
472
|
`slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
|
458
473
|
`spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
|
459
474
|
`square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
|
460
|
-
`string` | `font`, `color`, `background`, `underline`, `underline_color`, `open_type_features` | None
|
475
|
+
`string` | `font`, `color` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `background` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `underline`, `underline_color` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `open_type_features` | None
|
461
476
|
`tab` | `margined` (Boolean), `num_pages` (`Integer`) | None
|
462
477
|
`tab_item(name as String)` | `index` [read-only] (`Integer`), `margined` (Boolean), `name` [read-only] (`String`) | None
|
463
|
-
`table` | `cell_rows` (`Array` (rows) of `Arrays` (row columns) of cell values (e.g. `String` values for `text_column` cells or `Array` of `image`/`String` for `image_text_column`)), `editable` as Boolean |
|
478
|
+
`table` | `cell_rows` (`Array` (rows) of `Arrays` (row columns) of cell values (e.g. `String` values for `text_column` cells or `Array` of `image`/`String` for `image_text_column`)), `editable` as Boolean | `on_changed {|row, type, row_data| ...}`, `on_edited {|row, row_data| ...}`
|
464
479
|
`text(x = 0 as Numeric, y = 0 as Numeric, width = area_width as Numeric)` | `align`, `default_font` | None
|
465
480
|
`text_column(name as String)` | `editable` (Boolean) | None
|
466
481
|
`text_color_column(name as String)` | `editable` (Boolean) | None
|
@@ -813,7 +828,7 @@ To draw `text` in an `area`, you simply nest a `text(x, y, width)` control direc
|
|
813
828
|
- `background`: rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
|
814
829
|
- `underline`: one of `:none`, `:single`, `:double`, `:suggestion`, `:color_custom`, `:color_spelling`, `:color_grammar`, `:color_auxiliary`
|
815
830
|
- `underline_color`: one of `:spelling`, `:grammar`, `:auxiliary`, rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
|
816
|
-
- `open_type_features`:
|
831
|
+
- `open_type_features`: Open Type Features (https://www.microsoft.com/typography/otspec/featuretags.htm) consist of `open_type_tag`s nested in content block, which accept (`a`, `b`, `c`, `d`, `Integer`) arguments.
|
817
832
|
|
818
833
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
819
834
|
|
@@ -882,6 +897,114 @@ window('area text drawing') {
|
|
882
897
|
- 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-number hex or 3-number hex (as `Integer` or `String` with or without `0x` prefix)
|
883
898
|
- Color alpha value defaults to `1.0` when not specified.
|
884
899
|
|
900
|
+
### Custom Keywords
|
901
|
+
|
902
|
+
To define custom keywords, simply define a method representing the custom control you want. To make reusable, you can define in modules and simply include the modules in the view classes that need them.
|
903
|
+
|
904
|
+
Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
905
|
+
|
906
|
+
```ruby
|
907
|
+
require 'glimmer-dsl-libui'
|
908
|
+
require 'facets'
|
909
|
+
|
910
|
+
include Glimmer
|
911
|
+
|
912
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
913
|
+
|
914
|
+
def form_field(model, property)
|
915
|
+
property = property.to_s
|
916
|
+
entry { |e|
|
917
|
+
label property.underscore.split('_').map(&:capitalize).join(' ')
|
918
|
+
text model.send(property).to_s
|
919
|
+
|
920
|
+
on_changed do
|
921
|
+
model.send("#{property}=", e.text)
|
922
|
+
end
|
923
|
+
}
|
924
|
+
end
|
925
|
+
|
926
|
+
def address_form(address)
|
927
|
+
form {
|
928
|
+
form_field(address, :street)
|
929
|
+
form_field(address, :p_o_box)
|
930
|
+
form_field(address, :city)
|
931
|
+
form_field(address, :state)
|
932
|
+
form_field(address, :zip_code)
|
933
|
+
}
|
934
|
+
end
|
935
|
+
|
936
|
+
def label_pair(model, attribute, value)
|
937
|
+
name_label = nil
|
938
|
+
value_label = nil
|
939
|
+
horizontal_box {
|
940
|
+
name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
941
|
+
value_label = label(value.to_s)
|
942
|
+
}
|
943
|
+
Glimmer::DataBinding::Observer.proc do
|
944
|
+
value_label.text = model.send(attribute)
|
945
|
+
end.observe(model, attribute)
|
946
|
+
end
|
947
|
+
|
948
|
+
def address(address)
|
949
|
+
vertical_box {
|
950
|
+
address.each_pair do |attribute, value|
|
951
|
+
label_pair(address, attribute, value)
|
952
|
+
end
|
953
|
+
}
|
954
|
+
end
|
955
|
+
|
956
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
957
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
958
|
+
|
959
|
+
window('Method-Based Custom Keyword') {
|
960
|
+
margined true
|
961
|
+
|
962
|
+
horizontal_box {
|
963
|
+
vertical_box {
|
964
|
+
label('Address 1') {
|
965
|
+
stretchy false
|
966
|
+
}
|
967
|
+
|
968
|
+
address_form(address1)
|
969
|
+
|
970
|
+
horizontal_separator {
|
971
|
+
stretchy false
|
972
|
+
}
|
973
|
+
|
974
|
+
label('Address 1 (Saved)') {
|
975
|
+
stretchy false
|
976
|
+
}
|
977
|
+
|
978
|
+
address(address1)
|
979
|
+
}
|
980
|
+
|
981
|
+
vertical_separator {
|
982
|
+
stretchy false
|
983
|
+
}
|
984
|
+
|
985
|
+
vertical_box {
|
986
|
+
label('Address 2') {
|
987
|
+
stretchy false
|
988
|
+
}
|
989
|
+
|
990
|
+
address_form(address2)
|
991
|
+
|
992
|
+
horizontal_separator {
|
993
|
+
stretchy false
|
994
|
+
}
|
995
|
+
|
996
|
+
label('Address 2 (Saved)') {
|
997
|
+
stretchy false
|
998
|
+
}
|
999
|
+
|
1000
|
+
address(address2)
|
1001
|
+
}
|
1002
|
+
}
|
1003
|
+
}.show
|
1004
|
+
```
|
1005
|
+
|
1006
|
+
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
1007
|
+
|
885
1008
|
### API Gotchas
|
886
1009
|
|
887
1010
|
- There is no proper way to destroy `grid` children due to [libui](https://github.com/andlabs/libui) not offering any API for deleting them from `grid` (no `grid_delete` similar to `box_delete` for `horizontal_box` and `vertical_box`).
|
@@ -890,8 +1013,20 @@ window('area text drawing') {
|
|
890
1013
|
|
891
1014
|
### Original API
|
892
1015
|
|
893
|
-
To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
|
894
|
-
|
1016
|
+
To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
|
1017
|
+
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
1018
|
+
- Check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
|
1019
|
+
- Check out the [Go UI (Golang LibUI) documentation](https://pkg.go.dev/github.com/andlabs/ui) for an alternative well-documented [libui](https://github.com/andlabs/libui) reference.
|
1020
|
+
|
1021
|
+
## Packaging
|
1022
|
+
|
1023
|
+
I am documenting options for packaging, which I have not tried myself, but figured they would still be useful to add to the README.md until I can expand further effort into supporting packaging.
|
1024
|
+
|
1025
|
+
For Windows, the [LibUI](https://github.com/kojix2/LibUI) project recommends [OCRA](https://github.com/larsch/ocra) (One-Click Ruby Application), which builds Windows executables from Ruby source.
|
1026
|
+
|
1027
|
+
For Mac, consider [Platybus](https://github.com/sveinbjornt/Platypus) (builds a native Mac app from a Ruby script)
|
1028
|
+
|
1029
|
+
For Linux, simply package your app as a [Ruby Gem](https://guides.rubygems.org/what-is-a-gem/) and [build rpm package from Ruby Gem](https://www.redpill-linpro.com/sysadvent/2015/12/07/building-rpms-from-gems.html) or [build deb package from Ruby Gem](https://openpreservation.org/blogs/building-debian-package-ruby-program/).
|
895
1030
|
|
896
1031
|
## Glimmer Style Guide
|
897
1032
|
|
@@ -908,8 +1043,6 @@ The following examples include reimplementions of the examples in the [LibUI](ht
|
|
908
1043
|
|
909
1044
|
To browse all examples, simply launch the [Meta-Example](examples/meta_example.rb), which lists all examples and displays each example's code when selected. It also enables code editing to facilitate experimentation and learning.
|
910
1045
|
|
911
|
-
(note that for examples that emit output to terminal/command-line via `p` or `puts`, you must run them directly to see output)
|
912
|
-
|
913
1046
|
[examples/meta_example.rb](examples/meta_example.rb)
|
914
1047
|
|
915
1048
|
Run with this command from the root of the project if you cloned the project:
|
@@ -941,59 +1074,122 @@ require 'facets'
|
|
941
1074
|
class MetaExample
|
942
1075
|
include Glimmer
|
943
1076
|
|
1077
|
+
def initialize
|
1078
|
+
@selected_example_index = 0
|
1079
|
+
end
|
1080
|
+
|
944
1081
|
def examples
|
945
1082
|
if @examples.nil?
|
946
1083
|
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '**', '*.rb'))
|
947
1084
|
example_file_names = example_files.map { |f| File.basename(f, '.rb') }
|
948
|
-
example_file_names = example_file_names.reject { |f| f == 'meta_example' }
|
1085
|
+
example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
|
949
1086
|
@examples = example_file_names.map { |f| f.underscore.titlecase }
|
950
1087
|
end
|
951
1088
|
@examples
|
952
1089
|
end
|
953
1090
|
|
1091
|
+
def examples_with_versions
|
1092
|
+
examples.map do |example|
|
1093
|
+
version_count_for(example) > 1 ? "#{example} (#{version_count_for(example)} versions)" : example
|
1094
|
+
end
|
1095
|
+
end
|
1096
|
+
|
954
1097
|
def file_path_for(example)
|
955
1098
|
File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
|
956
1099
|
end
|
957
1100
|
|
1101
|
+
def version_count_for(example)
|
1102
|
+
Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/\d\.rb$/)}.count + 1
|
1103
|
+
end
|
1104
|
+
|
958
1105
|
def glimmer_dsl_libui_file
|
959
1106
|
File.expand_path('../lib/glimmer-dsl-libui', __dir__)
|
960
1107
|
end
|
961
1108
|
|
1109
|
+
def selected_example
|
1110
|
+
examples[@selected_example_index]
|
1111
|
+
end
|
1112
|
+
|
1113
|
+
def run_example(example)
|
1114
|
+
command = "ruby -r #{glimmer_dsl_libui_file} #{example} 2>&1"
|
1115
|
+
result = ''
|
1116
|
+
IO.popen(command) do |f|
|
1117
|
+
f.each_line do |line|
|
1118
|
+
result << line
|
1119
|
+
puts line
|
1120
|
+
end
|
1121
|
+
end
|
1122
|
+
msg_box('Error Running Example', result) if result.downcase.include?('error')
|
1123
|
+
end
|
1124
|
+
|
962
1125
|
def launch
|
963
1126
|
window('Meta-Example', 700, 500) {
|
964
1127
|
margined true
|
965
1128
|
|
966
1129
|
horizontal_box {
|
967
1130
|
vertical_box {
|
968
|
-
|
1131
|
+
stretchy false
|
1132
|
+
|
1133
|
+
@example_radio_buttons = radio_buttons {
|
969
1134
|
stretchy false
|
970
|
-
items
|
971
|
-
selected
|
1135
|
+
items examples_with_versions
|
1136
|
+
selected @selected_example_index
|
972
1137
|
|
973
1138
|
on_selected do
|
974
|
-
@
|
1139
|
+
@selected_example_index = @example_radio_buttons.selected
|
1140
|
+
example = selected_example
|
1141
|
+
@code_entry.text = File.read(file_path_for(example))
|
1142
|
+
@version_spinbox.value = 1
|
975
1143
|
end
|
976
1144
|
}
|
977
|
-
|
1145
|
+
|
1146
|
+
horizontal_box {
|
1147
|
+
label('Version') {
|
1148
|
+
stretchy false
|
1149
|
+
}
|
1150
|
+
|
1151
|
+
@version_spinbox = spinbox(1, 100) {
|
1152
|
+
value 1
|
1153
|
+
|
1154
|
+
on_changed do
|
1155
|
+
example = selected_example
|
1156
|
+
if @version_spinbox.value > version_count_for(example)
|
1157
|
+
@version_spinbox.value -= 1
|
1158
|
+
else
|
1159
|
+
version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
|
1160
|
+
example = "#{selected_example}#{version_number}"
|
1161
|
+
@code_entry.text = File.read(file_path_for(example))
|
1162
|
+
end
|
1163
|
+
end
|
1164
|
+
}
|
1165
|
+
}
|
1166
|
+
|
1167
|
+
horizontal_box {
|
978
1168
|
stretchy false
|
979
1169
|
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
1170
|
+
button('Launch') {
|
1171
|
+
on_clicked do
|
1172
|
+
begin
|
1173
|
+
meta_example_file = File.join(Dir.home, '.meta_example.rb')
|
1174
|
+
File.write(meta_example_file, @code_entry.text)
|
1175
|
+
run_example(meta_example_file)
|
1176
|
+
rescue => e
|
1177
|
+
puts e.full_message
|
1178
|
+
puts 'Unable to write code changes! Running original example...'
|
1179
|
+
run_example(file_path_for(selected_example))
|
1180
|
+
end
|
989
1181
|
end
|
990
|
-
|
1182
|
+
}
|
1183
|
+
button('Reset') {
|
1184
|
+
on_clicked do
|
1185
|
+
@code_entry.text = File.read(file_path_for(selected_example))
|
1186
|
+
end
|
1187
|
+
}
|
991
1188
|
}
|
992
1189
|
}
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
}
|
1190
|
+
|
1191
|
+
@code_entry = non_wrapping_multiline_entry {
|
1192
|
+
text File.read(file_path_for(selected_example))
|
997
1193
|
}
|
998
1194
|
}
|
999
1195
|
}.show
|
@@ -2060,6 +2256,8 @@ include Glimmer
|
|
2060
2256
|
|
2061
2257
|
window('color button', 230) {
|
2062
2258
|
color_button { |cb|
|
2259
|
+
color :blue
|
2260
|
+
|
2063
2261
|
on_changed do
|
2064
2262
|
rgba = cb.color
|
2065
2263
|
p rgba
|
@@ -2342,11 +2540,21 @@ window('Form') {
|
|
2342
2540
|
@last_name_entry = entry {
|
2343
2541
|
label 'Last Name' # label property is available when control is nested under form
|
2344
2542
|
}
|
2543
|
+
|
2544
|
+
@phone_entry = entry {
|
2545
|
+
label 'Phone' # label property is available when control is nested under form
|
2546
|
+
}
|
2547
|
+
|
2548
|
+
@email_entry = entry {
|
2549
|
+
label 'Email' # label property is available when control is nested under form
|
2550
|
+
}
|
2345
2551
|
}
|
2346
2552
|
|
2347
|
-
button('Display
|
2553
|
+
button('Display Info') {
|
2554
|
+
stretchy false
|
2555
|
+
|
2348
2556
|
on_clicked do
|
2349
|
-
msg_box('
|
2557
|
+
msg_box('Info', "#{@first_name_entry.text} #{@last_name_entry.text} has phone #{@phone_entry.text} and email #{@email_entry.text}")
|
2350
2558
|
end
|
2351
2559
|
}
|
2352
2560
|
}
|
@@ -2524,6 +2732,14 @@ window('Editable animal sounds', 300, 200) {
|
|
2524
2732
|
|
2525
2733
|
cell_rows data
|
2526
2734
|
editable true
|
2735
|
+
|
2736
|
+
on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
|
2737
|
+
puts "Row #{row} #{type}: #{row_data}"
|
2738
|
+
end
|
2739
|
+
|
2740
|
+
on_edited do |row, row_data| # only fires on direct table editing
|
2741
|
+
puts "Row #{row} edited: #{row_data}"
|
2742
|
+
end
|
2527
2743
|
}
|
2528
2744
|
}
|
2529
2745
|
|
@@ -2882,6 +3098,10 @@ window('Animal sounds', 300, 200) {
|
|
2882
3098
|
}
|
2883
3099
|
|
2884
3100
|
cell_rows data # implicit data-binding
|
3101
|
+
|
3102
|
+
on_changed do |row, type, row_data|
|
3103
|
+
puts "Row #{row} #{type}: #{row_data}"
|
3104
|
+
end
|
2885
3105
|
}
|
2886
3106
|
}
|
2887
3107
|
}.show
|
@@ -3044,7 +3264,7 @@ window('Task Progress', 300, 200) {
|
|
3044
3264
|
|
3045
3265
|
on_clicked do
|
3046
3266
|
data.each_with_index do |row_data, row|
|
3047
|
-
data[row] =
|
3267
|
+
data[row][1] = 100 # automatically updates table due to implicit data-binding
|
3048
3268
|
end
|
3049
3269
|
end
|
3050
3270
|
}
|
@@ -3239,6 +3459,10 @@ window('Contacts', 600, 600) { |w|
|
|
3239
3459
|
text_column('State')
|
3240
3460
|
|
3241
3461
|
cell_rows data # implicit data-binding
|
3462
|
+
|
3463
|
+
on_changed do |row, type, row_data|
|
3464
|
+
puts "Row #{row} #{type}: #{row_data}"
|
3465
|
+
end
|
3242
3466
|
}
|
3243
3467
|
}
|
3244
3468
|
}.show
|
@@ -3634,14 +3858,15 @@ window('Area Gallery', 400, 400) {
|
|
3634
3858
|
path { # declarative stable path
|
3635
3859
|
square(0, 0, 100)
|
3636
3860
|
square(100, 100, 400)
|
3637
|
-
|
3861
|
+
|
3638
3862
|
fill r: 102, g: 102, b: 204
|
3639
3863
|
}
|
3640
3864
|
path { # declarative stable path
|
3641
3865
|
rectangle(0, 100, 100, 400)
|
3642
3866
|
rectangle(100, 0, 400, 100)
|
3643
|
-
|
3644
|
-
|
3867
|
+
|
3868
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
3869
|
+
fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
|
3645
3870
|
}
|
3646
3871
|
path { # declarative stable path
|
3647
3872
|
figure(100, 100) {
|
@@ -3667,17 +3892,26 @@ window('Area Gallery', 400, 400) {
|
|
3667
3892
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3668
3893
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3669
3894
|
}
|
3895
|
+
path { # declarative stable path
|
3896
|
+
arc(400, 220, 180, 90, 90, false)
|
3897
|
+
|
3898
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
3899
|
+
fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
|
3900
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3901
|
+
}
|
3670
3902
|
path { # declarative stable path
|
3671
3903
|
circle(200, 200, 90)
|
3672
3904
|
|
3673
3905
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3674
3906
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
3675
3907
|
}
|
3676
|
-
|
3677
|
-
|
3678
|
-
|
3679
|
-
|
3680
|
-
|
3908
|
+
text(160, 40, 100) { # x, y, width
|
3909
|
+
string {
|
3910
|
+
font family: 'Times', size: 14
|
3911
|
+
color :black
|
3912
|
+
|
3913
|
+
'Area Gallery'
|
3914
|
+
}
|
3681
3915
|
}
|
3682
3916
|
|
3683
3917
|
on_mouse_event do |area_mouse_event|
|
@@ -3768,7 +4002,8 @@ window('Area Gallery', 400, 400) {
|
|
3768
4002
|
height 100
|
3769
4003
|
}
|
3770
4004
|
|
3771
|
-
|
4005
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4006
|
+
fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
|
3772
4007
|
}
|
3773
4008
|
path { # declarative stable path
|
3774
4009
|
figure {
|
@@ -3830,16 +4065,6 @@ window('Area Gallery', 400, 400) {
|
|
3830
4065
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3831
4066
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3832
4067
|
}
|
3833
|
-
path { # declarative stable path
|
3834
|
-
circle {
|
3835
|
-
x_center 200
|
3836
|
-
y_center 200
|
3837
|
-
radius 90
|
3838
|
-
}
|
3839
|
-
|
3840
|
-
fill r: 202, g: 102, b: 204, a: 0.5
|
3841
|
-
stroke r: 0, g: 0, b: 0, thickness: 2
|
3842
|
-
}
|
3843
4068
|
path { # declarative stable path
|
3844
4069
|
arc {
|
3845
4070
|
x_center 400
|
@@ -3850,9 +4075,32 @@ window('Area Gallery', 400, 400) {
|
|
3850
4075
|
is_negative false
|
3851
4076
|
}
|
3852
4077
|
|
3853
|
-
|
4078
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4079
|
+
fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
|
3854
4080
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3855
4081
|
}
|
4082
|
+
path { # declarative stable path
|
4083
|
+
circle {
|
4084
|
+
x_center 200
|
4085
|
+
y_center 200
|
4086
|
+
radius 90
|
4087
|
+
}
|
4088
|
+
|
4089
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
4090
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
4091
|
+
}
|
4092
|
+
text {
|
4093
|
+
x 160
|
4094
|
+
y 40
|
4095
|
+
width 100
|
4096
|
+
|
4097
|
+
string {
|
4098
|
+
font family: 'Times', size: 14
|
4099
|
+
color :black
|
4100
|
+
|
4101
|
+
'Area Gallery'
|
4102
|
+
}
|
4103
|
+
}
|
3856
4104
|
|
3857
4105
|
on_mouse_event do |area_mouse_event|
|
3858
4106
|
p area_mouse_event
|
@@ -3925,7 +4173,8 @@ window('Area Gallery', 400, 400) {
|
|
3925
4173
|
rectangle(0, 100, 100, 400)
|
3926
4174
|
rectangle(100, 0, 400, 100)
|
3927
4175
|
|
3928
|
-
|
4176
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4177
|
+
fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
|
3929
4178
|
}
|
3930
4179
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
3931
4180
|
figure(100, 100) {
|
@@ -3951,17 +4200,26 @@ window('Area Gallery', 400, 400) {
|
|
3951
4200
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3952
4201
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3953
4202
|
}
|
4203
|
+
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4204
|
+
arc(400, 220, 180, 90, 90, false)
|
4205
|
+
|
4206
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4207
|
+
fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
|
4208
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4209
|
+
}
|
3954
4210
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
3955
4211
|
circle(200, 200, 90)
|
3956
4212
|
|
3957
4213
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3958
4214
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
3959
4215
|
}
|
3960
|
-
|
3961
|
-
|
3962
|
-
|
3963
|
-
|
3964
|
-
|
4216
|
+
text(160, 40, 100) { # x, y, width
|
4217
|
+
string {
|
4218
|
+
font family: 'Times', size: 14
|
4219
|
+
color :black
|
4220
|
+
|
4221
|
+
'Area Gallery'
|
4222
|
+
}
|
3965
4223
|
}
|
3966
4224
|
end
|
3967
4225
|
|
@@ -4054,7 +4312,8 @@ window('Area Gallery', 400, 400) {
|
|
4054
4312
|
height 100
|
4055
4313
|
}
|
4056
4314
|
|
4057
|
-
|
4315
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4316
|
+
fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
|
4058
4317
|
}
|
4059
4318
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4060
4319
|
figure {
|
@@ -4116,16 +4375,6 @@ window('Area Gallery', 400, 400) {
|
|
4116
4375
|
fill r: 202, g: 102, b: 204, a: 0.5
|
4117
4376
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4118
4377
|
}
|
4119
|
-
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4120
|
-
circle {
|
4121
|
-
x_center 200
|
4122
|
-
y_center 200
|
4123
|
-
radius 90
|
4124
|
-
}
|
4125
|
-
|
4126
|
-
fill r: 202, g: 102, b: 204, a: 0.5
|
4127
|
-
stroke r: 0, g: 0, b: 0, thickness: 2
|
4128
|
-
}
|
4129
4378
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4130
4379
|
arc {
|
4131
4380
|
x_center 400
|
@@ -4136,9 +4385,32 @@ window('Area Gallery', 400, 400) {
|
|
4136
4385
|
is_negative false
|
4137
4386
|
}
|
4138
4387
|
|
4139
|
-
|
4388
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4389
|
+
fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
|
4140
4390
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4141
4391
|
}
|
4392
|
+
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4393
|
+
circle {
|
4394
|
+
x_center 200
|
4395
|
+
y_center 200
|
4396
|
+
radius 90
|
4397
|
+
}
|
4398
|
+
|
4399
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
4400
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
4401
|
+
}
|
4402
|
+
text {
|
4403
|
+
x 160
|
4404
|
+
y 40
|
4405
|
+
width 100
|
4406
|
+
|
4407
|
+
string {
|
4408
|
+
font family: 'Times', size: 14
|
4409
|
+
color :black
|
4410
|
+
|
4411
|
+
'Area Gallery'
|
4412
|
+
}
|
4413
|
+
}
|
4142
4414
|
end
|
4143
4415
|
|
4144
4416
|
on_mouse_event do |area_mouse_event|
|
@@ -5562,7 +5834,132 @@ class CustomDrawText
|
|
5562
5834
|
end
|
5563
5835
|
|
5564
5836
|
CustomDrawText.new.launch
|
5837
|
+
```
|
5838
|
+
|
5839
|
+
### Method-Based Custom Keyword
|
5840
|
+
|
5841
|
+
[examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
|
5842
|
+
|
5843
|
+
Run with this command from the root of the project if you cloned the project:
|
5565
5844
|
|
5845
|
+
```
|
5846
|
+
ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_keyword.rb
|
5847
|
+
```
|
5848
|
+
|
5849
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
5850
|
+
|
5851
|
+
```
|
5852
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_keyword'"
|
5853
|
+
```
|
5854
|
+
|
5855
|
+
Mac
|
5856
|
+
|
5857
|
+
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
5858
|
+
|
5859
|
+
Linux
|
5860
|
+
|
5861
|
+
![glimmer-dsl-libui-linux-method-based-custom-keyword.png](images/glimmer-dsl-libui-linux-method-based-custom-keyword.png)
|
5862
|
+
|
5863
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5864
|
+
|
5865
|
+
```ruby
|
5866
|
+
require 'glimmer-dsl-libui'
|
5867
|
+
require 'facets'
|
5868
|
+
|
5869
|
+
include Glimmer
|
5870
|
+
|
5871
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
5872
|
+
|
5873
|
+
def form_field(model, property)
|
5874
|
+
property = property.to_s
|
5875
|
+
entry { |e|
|
5876
|
+
label property.underscore.split('_').map(&:capitalize).join(' ')
|
5877
|
+
text model.send(property).to_s
|
5878
|
+
|
5879
|
+
on_changed do
|
5880
|
+
model.send("#{property}=", e.text)
|
5881
|
+
end
|
5882
|
+
}
|
5883
|
+
end
|
5884
|
+
|
5885
|
+
def address_form(address)
|
5886
|
+
form {
|
5887
|
+
form_field(address, :street)
|
5888
|
+
form_field(address, :p_o_box)
|
5889
|
+
form_field(address, :city)
|
5890
|
+
form_field(address, :state)
|
5891
|
+
form_field(address, :zip_code)
|
5892
|
+
}
|
5893
|
+
end
|
5894
|
+
|
5895
|
+
def label_pair(model, attribute, value)
|
5896
|
+
name_label = nil
|
5897
|
+
value_label = nil
|
5898
|
+
horizontal_box {
|
5899
|
+
name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
5900
|
+
value_label = label(value.to_s)
|
5901
|
+
}
|
5902
|
+
Glimmer::DataBinding::Observer.proc do
|
5903
|
+
value_label.text = model.send(attribute)
|
5904
|
+
end.observe(model, attribute)
|
5905
|
+
end
|
5906
|
+
|
5907
|
+
def address(address)
|
5908
|
+
vertical_box {
|
5909
|
+
address.each_pair do |attribute, value|
|
5910
|
+
label_pair(address, attribute, value)
|
5911
|
+
end
|
5912
|
+
}
|
5913
|
+
end
|
5914
|
+
|
5915
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
5916
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
5917
|
+
|
5918
|
+
window('Method-Based Custom Keyword') {
|
5919
|
+
margined true
|
5920
|
+
|
5921
|
+
horizontal_box {
|
5922
|
+
vertical_box {
|
5923
|
+
label('Address 1') {
|
5924
|
+
stretchy false
|
5925
|
+
}
|
5926
|
+
|
5927
|
+
address_form(address1)
|
5928
|
+
|
5929
|
+
horizontal_separator {
|
5930
|
+
stretchy false
|
5931
|
+
}
|
5932
|
+
|
5933
|
+
label('Address 1 (Saved)') {
|
5934
|
+
stretchy false
|
5935
|
+
}
|
5936
|
+
|
5937
|
+
address(address1)
|
5938
|
+
}
|
5939
|
+
|
5940
|
+
vertical_separator {
|
5941
|
+
stretchy false
|
5942
|
+
}
|
5943
|
+
|
5944
|
+
vertical_box {
|
5945
|
+
label('Address 2') {
|
5946
|
+
stretchy false
|
5947
|
+
}
|
5948
|
+
|
5949
|
+
address_form(address2)
|
5950
|
+
|
5951
|
+
horizontal_separator {
|
5952
|
+
stretchy false
|
5953
|
+
}
|
5954
|
+
|
5955
|
+
label('Address 2 (Saved)') {
|
5956
|
+
stretchy false
|
5957
|
+
}
|
5958
|
+
|
5959
|
+
address(address2)
|
5960
|
+
}
|
5961
|
+
}
|
5962
|
+
}.show
|
5566
5963
|
```
|
5567
5964
|
|
5568
5965
|
## Contributing to glimmer-dsl-libui
|