glimmer-dsl-libui 0.2.8 → 0.2.12
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +28 -0
- data/README.md +472 -92
- data/VERSION +1 -1
- data/examples/area_gallery.rb +16 -8
- data/examples/area_gallery2.rb +26 -12
- data/examples/area_gallery3.rb +14 -6
- data/examples/area_gallery4.rb +26 -12
- data/examples/basic_draw_text2.rb +0 -1
- data/examples/basic_table_button.rb +1 -0
- data/examples/basic_table_progress_bar.rb +1 -1
- data/examples/color_button.rb +2 -0
- data/examples/editable_table.rb +2 -0
- data/examples/form.rb +12 -2
- 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/dsl/libui/string_expression.rb +9 -2
- data/lib/glimmer/libui/attributed_string.rb +12 -2
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +31 -6
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +13 -3
- data/lib/glimmer/libui.rb +1 -1
- metadata +12 -8
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.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.12
|
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
|
-
[![Maintainability](https://api.codeclimate.com/v1/badges/ce2853efdbecf6ebdc73/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-libui/maintainability)
|
5
4
|
[![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
6
5
|
|
7
6
|
[Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free Ruby desktop development GUI library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
|
@@ -10,14 +9,16 @@
|
|
10
9
|
|
11
10
|
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
11
|
|
12
|
+
**(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. Issue reporting for Windows is appreciated in the meantime.)**
|
13
|
+
|
13
14
|
[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
15
|
- Declarative DSL syntax that visually maps to the GUI control hierarchy
|
15
16
|
- Convention over configuration via smart defaults and automation of low-level details
|
16
17
|
- 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
18
|
- Custom Control support
|
19
|
-
-
|
20
|
-
-
|
19
|
+
- [Far Future Plan] Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
|
20
|
+
- [Far Future Plan] Scaffolding for new custom controls, apps, and gems
|
21
|
+
- [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
|
21
22
|
|
22
23
|
Hello, World!
|
23
24
|
|
@@ -61,7 +62,7 @@ window('Task Progress', 300, 200) {
|
|
61
62
|
|
62
63
|
on_clicked do
|
63
64
|
data.each_with_index do |row_data, row|
|
64
|
-
data[row] =
|
65
|
+
data[row][1] = 100 # automatically updates table due to implicit data-binding
|
65
66
|
end
|
66
67
|
end
|
67
68
|
}
|
@@ -84,14 +85,15 @@ window('Area Gallery', 400, 400) {
|
|
84
85
|
path { # declarative stable path
|
85
86
|
square(0, 0, 100)
|
86
87
|
square(100, 100, 400)
|
87
|
-
|
88
|
+
|
88
89
|
fill r: 102, g: 102, b: 204
|
89
90
|
}
|
90
91
|
path { # declarative stable path
|
91
92
|
rectangle(0, 100, 100, 400)
|
92
93
|
rectangle(100, 0, 400, 100)
|
93
|
-
|
94
|
-
|
94
|
+
|
95
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
96
|
+
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
97
|
}
|
96
98
|
path { # declarative stable path
|
97
99
|
figure(100, 100) {
|
@@ -117,17 +119,24 @@ window('Area Gallery', 400, 400) {
|
|
117
119
|
fill r: 202, g: 102, b: 204, a: 0.5
|
118
120
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
119
121
|
}
|
122
|
+
path { # declarative stable path
|
123
|
+
arc(400, 220, 180, 90, 90, false)
|
124
|
+
|
125
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
126
|
+
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}]
|
127
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
128
|
+
}
|
120
129
|
path { # declarative stable path
|
121
130
|
circle(200, 200, 90)
|
122
131
|
|
123
132
|
fill r: 202, g: 102, b: 204, a: 0.5
|
124
133
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
125
134
|
}
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
135
|
+
text(160, 40, 100) { # x, y, width
|
136
|
+
string('Area Gallery') {
|
137
|
+
font family: 'Times', size: 14
|
138
|
+
color :black
|
139
|
+
}
|
131
140
|
}
|
132
141
|
|
133
142
|
on_mouse_event do |area_mouse_event|
|
@@ -186,7 +195,7 @@ window('Area Gallery', 400, 400) {
|
|
186
195
|
|
187
196
|
[Check Out Many More Examples Over Here!](#examples)
|
188
197
|
|
189
|
-
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is in
|
198
|
+
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
199
|
|
191
200
|
Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
|
192
201
|
- [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
|
@@ -197,7 +206,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
197
206
|
|
198
207
|
## Table of Contents
|
199
208
|
|
200
|
-
- [Glimmer DSL for LibUI
|
209
|
+
- [Glimmer DSL for LibUI](#)
|
201
210
|
- [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
|
202
211
|
- [Usage](#usage)
|
203
212
|
- [Girb (Glimmer IRB)](#girb-glimmer-irb)
|
@@ -211,8 +220,10 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
211
220
|
- [Table API](#table-api)
|
212
221
|
- [Area API](#area-api)
|
213
222
|
- [Smart Defaults and Conventions](#smart-defaults-and-conventions)
|
223
|
+
- [Custom Keywords](#custom-keywords)
|
214
224
|
- [API Gotchas](#api-gotchas)
|
215
225
|
- [Original API](#original-api)
|
226
|
+
- [Packaging](#packaging)
|
216
227
|
- [Glimmer Style Guide](#glimmer-style-guide)
|
217
228
|
- [Examples](#examples)
|
218
229
|
- [Basic Window](#basic-window)
|
@@ -247,6 +258,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
247
258
|
- [Color The Circles](#color-the-circles)
|
248
259
|
- [Basic Draw Text](#basic-draw-text)
|
249
260
|
- [Custom Draw Text](#custom-draw-text)
|
261
|
+
- [Method-Based Custom Keyword](#method-based-custom-keyword)
|
250
262
|
- [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
|
251
263
|
- [Help](#help)
|
252
264
|
- [Issues](#issues)
|
@@ -334,7 +346,7 @@ gem install glimmer-dsl-libui
|
|
334
346
|
Or install via Bundler `Gemfile`:
|
335
347
|
|
336
348
|
```ruby
|
337
|
-
gem 'glimmer-dsl-libui', '~> 0.2.
|
349
|
+
gem 'glimmer-dsl-libui', '~> 0.2.12'
|
338
350
|
```
|
339
351
|
|
340
352
|
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 +458,7 @@ Control(Args) | Properties | Listeners
|
|
446
458
|
`msg_box_error(window = main_window as Glimmer::LibUI::WindowProxy, title as String, description as String)` | None | None
|
447
459
|
`non_wrapping_multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
|
448
460
|
`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
|
461
|
+
`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
462
|
`preferences_menu_item` | None | `on_clicked`
|
451
463
|
`progress_bar` | `value` (`Numeric`) | None
|
452
464
|
`progress_bar_column(name as String)` | None | None
|
@@ -457,7 +469,7 @@ Control(Args) | Properties | Listeners
|
|
457
469
|
`slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
|
458
470
|
`spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
|
459
471
|
`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
|
472
|
+
`string(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`, `string` (`String`) | None
|
461
473
|
`tab` | `margined` (Boolean), `num_pages` (`Integer`) | None
|
462
474
|
`tab_item(name as String)` | `index` [read-only] (`Integer`), `margined` (Boolean), `name` [read-only] (`String`) | None
|
463
475
|
`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| ...}`
|
@@ -801,19 +813,23 @@ Check [Basic Transform](#basic-transform) example for use of [X11](https://en.wi
|
|
801
813
|
|
802
814
|
Check [Histogram](#histogram) example for use of hex colors.
|
803
815
|
|
804
|
-
To draw `text` in an `area`, you simply nest a `text(x, y, width)` control directly under `area` or inside a `on_draw` listener, and then nest attributed `string {string_value}` controls underneath it returning an actual `String` (think of them as the `<span>` element in html, which contains a string of text).
|
816
|
+
To draw `text` in an `area`, you simply nest a `text(x, y, width)` control directly under `area` or inside a `on_draw` listener, and then nest attributed `string {[attributes]; string_value}` controls underneath it returning an actual `String` (think of them as the `<span>` or `<p>` element in html, which contains a string of text). Alternatively, you can nest attributed `string(string_value) {[attributes]}` if `string_value` is a short single-line string. An attributed `string` value can be changed dynamically via its `string` property.
|
805
817
|
|
806
|
-
`text`
|
818
|
+
`text` has the following properties:
|
807
819
|
- `default_font`:
|
808
820
|
- `align`: `:left` (default), `:center`, or `:right` (`align` currently seems not to work on the Mac)
|
821
|
+
- `x`: x coordinate in relation to parent `area` top-left corner
|
822
|
+
- `y`: y coordinate in relation to parent `area` top-left corner
|
823
|
+
- `width` (default: area width - x*2): width of text to display
|
809
824
|
|
810
|
-
`string`
|
825
|
+
`string` has the following properties:
|
811
826
|
- `font`: font descriptor hash consisting of `:family`, `:size`, `:weight` (`[:minimum, :thin, :ultra_light, :light, :book, :normal, :medium, :semi_bold, :bold, :ultra_bold, :heavy, :ultra_heavy, :maximum]`), `:italic` (`[:normal, :oblique, :italic]`), and `:stretch` (`[:ultra_condensed, :extra_condensed, :condensed, :semi_condensed, :normal, :semi_expanded, :expanded, :extra_expanded, :ultra_expanded]`) key values
|
812
827
|
- `color`: rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
|
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.
|
832
|
+
- `string`: string value (`String`)
|
817
833
|
|
818
834
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
819
835
|
|
@@ -834,12 +850,13 @@ window('area text drawing') {
|
|
834
850
|
open_type_tag 'l', 'i', 'g', 'a', 1
|
835
851
|
}
|
836
852
|
|
837
|
-
"This is a
|
853
|
+
"This is a demonstration\n" \
|
854
|
+
"of a very long\n" \
|
855
|
+
"attributed string\n" \
|
856
|
+
"spanning multiple lines\n\n"
|
838
857
|
}
|
839
858
|
|
840
|
-
string
|
841
|
-
'This is another test'
|
842
|
-
}
|
859
|
+
string('This is a short unattributed string')
|
843
860
|
}
|
844
861
|
}
|
845
862
|
}.show
|
@@ -876,12 +893,121 @@ window('area text drawing') {
|
|
876
893
|
- `area` paths are specified declaratively with figures underneath (e.g. `rectangle`) and `area` draw listener is automatically generated
|
877
894
|
- Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
|
878
895
|
- Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
|
896
|
+
- Observe `text` and `string` properties for changes and automatically redraw containing area accordingly
|
879
897
|
- All controls are protected from garbage collection until no longer needed (explicitly destroyed), so there is no need to worry about surprises.
|
880
898
|
- All resources are freed automatically once no longer needed or left to garbage collection.
|
881
899
|
- When nesting an `area` directly underneath `window` (without a layout control like `vertical_box`), it is automatically reparented with `vertical_box` in between the `window` and `area` since it would not show up on Linux otherwise.
|
882
900
|
- 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
901
|
- Color alpha value defaults to `1.0` when not specified.
|
884
902
|
|
903
|
+
### Custom Keywords
|
904
|
+
|
905
|
+
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.
|
906
|
+
|
907
|
+
Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
908
|
+
|
909
|
+
```ruby
|
910
|
+
require 'glimmer-dsl-libui'
|
911
|
+
require 'facets'
|
912
|
+
|
913
|
+
include Glimmer
|
914
|
+
|
915
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
916
|
+
|
917
|
+
def form_field(model, property)
|
918
|
+
property = property.to_s
|
919
|
+
entry { |e|
|
920
|
+
label property.underscore.split('_').map(&:capitalize).join(' ')
|
921
|
+
text model.send(property).to_s
|
922
|
+
|
923
|
+
on_changed do
|
924
|
+
model.send("#{property}=", e.text)
|
925
|
+
end
|
926
|
+
}
|
927
|
+
end
|
928
|
+
|
929
|
+
def address_form(address)
|
930
|
+
form {
|
931
|
+
form_field(address, :street)
|
932
|
+
form_field(address, :p_o_box)
|
933
|
+
form_field(address, :city)
|
934
|
+
form_field(address, :state)
|
935
|
+
form_field(address, :zip_code)
|
936
|
+
}
|
937
|
+
end
|
938
|
+
|
939
|
+
def label_pair(model, attribute, value)
|
940
|
+
name_label = nil
|
941
|
+
value_label = nil
|
942
|
+
horizontal_box {
|
943
|
+
name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
944
|
+
value_label = label(value.to_s)
|
945
|
+
}
|
946
|
+
Glimmer::DataBinding::Observer.proc do
|
947
|
+
value_label.text = model.send(attribute)
|
948
|
+
end.observe(model, attribute)
|
949
|
+
end
|
950
|
+
|
951
|
+
def address(address)
|
952
|
+
vertical_box {
|
953
|
+
address.each_pair do |attribute, value|
|
954
|
+
label_pair(address, attribute, value)
|
955
|
+
end
|
956
|
+
}
|
957
|
+
end
|
958
|
+
|
959
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
960
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
961
|
+
|
962
|
+
window('Method-Based Custom Keyword') {
|
963
|
+
margined true
|
964
|
+
|
965
|
+
horizontal_box {
|
966
|
+
vertical_box {
|
967
|
+
label('Address 1') {
|
968
|
+
stretchy false
|
969
|
+
}
|
970
|
+
|
971
|
+
address_form(address1)
|
972
|
+
|
973
|
+
horizontal_separator {
|
974
|
+
stretchy false
|
975
|
+
}
|
976
|
+
|
977
|
+
label('Address 1 (Saved)') {
|
978
|
+
stretchy false
|
979
|
+
}
|
980
|
+
|
981
|
+
address(address1)
|
982
|
+
}
|
983
|
+
|
984
|
+
vertical_separator {
|
985
|
+
stretchy false
|
986
|
+
}
|
987
|
+
|
988
|
+
vertical_box {
|
989
|
+
label('Address 2') {
|
990
|
+
stretchy false
|
991
|
+
}
|
992
|
+
|
993
|
+
address_form(address2)
|
994
|
+
|
995
|
+
horizontal_separator {
|
996
|
+
stretchy false
|
997
|
+
}
|
998
|
+
|
999
|
+
label('Address 2 (Saved)') {
|
1000
|
+
stretchy false
|
1001
|
+
}
|
1002
|
+
|
1003
|
+
address(address2)
|
1004
|
+
}
|
1005
|
+
}
|
1006
|
+
}.show
|
1007
|
+
```
|
1008
|
+
|
1009
|
+
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
1010
|
+
|
885
1011
|
### API Gotchas
|
886
1012
|
|
887
1013
|
- 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 +1016,20 @@ window('area text drawing') {
|
|
890
1016
|
|
891
1017
|
### Original API
|
892
1018
|
|
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
|
-
|
1019
|
+
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):
|
1020
|
+
- Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
|
1021
|
+
- Check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
|
1022
|
+
- 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.
|
1023
|
+
|
1024
|
+
## Packaging
|
1025
|
+
|
1026
|
+
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.
|
1027
|
+
|
1028
|
+
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.
|
1029
|
+
|
1030
|
+
For Mac, consider [Platybus](https://github.com/sveinbjornt/Platypus) (builds a native Mac app from a Ruby script)
|
1031
|
+
|
1032
|
+
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
1033
|
|
896
1034
|
## Glimmer Style Guide
|
897
1035
|
|
@@ -908,8 +1046,6 @@ The following examples include reimplementions of the examples in the [LibUI](ht
|
|
908
1046
|
|
909
1047
|
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
1048
|
|
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
1049
|
[examples/meta_example.rb](examples/meta_example.rb)
|
914
1050
|
|
915
1051
|
Run with this command from the root of the project if you cloned the project:
|
@@ -941,59 +1077,122 @@ require 'facets'
|
|
941
1077
|
class MetaExample
|
942
1078
|
include Glimmer
|
943
1079
|
|
1080
|
+
def initialize
|
1081
|
+
@selected_example_index = 0
|
1082
|
+
end
|
1083
|
+
|
944
1084
|
def examples
|
945
1085
|
if @examples.nil?
|
946
1086
|
example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '**', '*.rb'))
|
947
1087
|
example_file_names = example_files.map { |f| File.basename(f, '.rb') }
|
948
|
-
example_file_names = example_file_names.reject { |f| f == 'meta_example' }
|
1088
|
+
example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
|
949
1089
|
@examples = example_file_names.map { |f| f.underscore.titlecase }
|
950
1090
|
end
|
951
1091
|
@examples
|
952
1092
|
end
|
953
1093
|
|
1094
|
+
def examples_with_versions
|
1095
|
+
examples.map do |example|
|
1096
|
+
version_count_for(example) > 1 ? "#{example} (#{version_count_for(example)} versions)" : example
|
1097
|
+
end
|
1098
|
+
end
|
1099
|
+
|
954
1100
|
def file_path_for(example)
|
955
1101
|
File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
|
956
1102
|
end
|
957
1103
|
|
1104
|
+
def version_count_for(example)
|
1105
|
+
Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/\d\.rb$/)}.count + 1
|
1106
|
+
end
|
1107
|
+
|
958
1108
|
def glimmer_dsl_libui_file
|
959
1109
|
File.expand_path('../lib/glimmer-dsl-libui', __dir__)
|
960
1110
|
end
|
961
1111
|
|
1112
|
+
def selected_example
|
1113
|
+
examples[@selected_example_index]
|
1114
|
+
end
|
1115
|
+
|
1116
|
+
def run_example(example)
|
1117
|
+
command = "ruby -r #{glimmer_dsl_libui_file} #{example} 2>&1"
|
1118
|
+
result = ''
|
1119
|
+
IO.popen(command) do |f|
|
1120
|
+
f.each_line do |line|
|
1121
|
+
result << line
|
1122
|
+
puts line
|
1123
|
+
end
|
1124
|
+
end
|
1125
|
+
msg_box('Error Running Example', result) if result.downcase.include?('error')
|
1126
|
+
end
|
1127
|
+
|
962
1128
|
def launch
|
963
1129
|
window('Meta-Example', 700, 500) {
|
964
1130
|
margined true
|
965
1131
|
|
966
1132
|
horizontal_box {
|
967
1133
|
vertical_box {
|
968
|
-
|
1134
|
+
stretchy false
|
1135
|
+
|
1136
|
+
@example_radio_buttons = radio_buttons {
|
969
1137
|
stretchy false
|
970
|
-
items
|
971
|
-
selected
|
1138
|
+
items examples_with_versions
|
1139
|
+
selected @selected_example_index
|
972
1140
|
|
973
1141
|
on_selected do
|
974
|
-
@
|
1142
|
+
@selected_example_index = @example_radio_buttons.selected
|
1143
|
+
example = selected_example
|
1144
|
+
@code_entry.text = File.read(file_path_for(example))
|
1145
|
+
@version_spinbox.value = 1
|
975
1146
|
end
|
976
1147
|
}
|
977
|
-
|
1148
|
+
|
1149
|
+
horizontal_box {
|
1150
|
+
label('Version') {
|
1151
|
+
stretchy false
|
1152
|
+
}
|
1153
|
+
|
1154
|
+
@version_spinbox = spinbox(1, 100) {
|
1155
|
+
value 1
|
1156
|
+
|
1157
|
+
on_changed do
|
1158
|
+
example = selected_example
|
1159
|
+
if @version_spinbox.value > version_count_for(example)
|
1160
|
+
@version_spinbox.value -= 1
|
1161
|
+
else
|
1162
|
+
version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
|
1163
|
+
example = "#{selected_example}#{version_number}"
|
1164
|
+
@code_entry.text = File.read(file_path_for(example))
|
1165
|
+
end
|
1166
|
+
end
|
1167
|
+
}
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
horizontal_box {
|
978
1171
|
stretchy false
|
979
1172
|
|
980
|
-
|
981
|
-
|
982
|
-
|
983
|
-
|
984
|
-
|
985
|
-
|
986
|
-
|
987
|
-
|
988
|
-
|
1173
|
+
button('Launch') {
|
1174
|
+
on_clicked do
|
1175
|
+
begin
|
1176
|
+
meta_example_file = File.join(Dir.home, '.meta_example.rb')
|
1177
|
+
File.write(meta_example_file, @code_entry.text)
|
1178
|
+
run_example(meta_example_file)
|
1179
|
+
rescue => e
|
1180
|
+
puts e.full_message
|
1181
|
+
puts 'Unable to write code changes! Running original example...'
|
1182
|
+
run_example(file_path_for(selected_example))
|
1183
|
+
end
|
989
1184
|
end
|
990
|
-
|
1185
|
+
}
|
1186
|
+
button('Reset') {
|
1187
|
+
on_clicked do
|
1188
|
+
@code_entry.text = File.read(file_path_for(selected_example))
|
1189
|
+
end
|
1190
|
+
}
|
991
1191
|
}
|
992
1192
|
}
|
993
|
-
|
994
|
-
|
995
|
-
|
996
|
-
}
|
1193
|
+
|
1194
|
+
@code_entry = non_wrapping_multiline_entry {
|
1195
|
+
text File.read(file_path_for(selected_example))
|
997
1196
|
}
|
998
1197
|
}
|
999
1198
|
}.show
|
@@ -2060,6 +2259,8 @@ include Glimmer
|
|
2060
2259
|
|
2061
2260
|
window('color button', 230) {
|
2062
2261
|
color_button { |cb|
|
2262
|
+
color :blue
|
2263
|
+
|
2063
2264
|
on_changed do
|
2064
2265
|
rgba = cb.color
|
2065
2266
|
p rgba
|
@@ -2342,11 +2543,21 @@ window('Form') {
|
|
2342
2543
|
@last_name_entry = entry {
|
2343
2544
|
label 'Last Name' # label property is available when control is nested under form
|
2344
2545
|
}
|
2546
|
+
|
2547
|
+
@phone_entry = entry {
|
2548
|
+
label 'Phone' # label property is available when control is nested under form
|
2549
|
+
}
|
2550
|
+
|
2551
|
+
@email_entry = entry {
|
2552
|
+
label 'Email' # label property is available when control is nested under form
|
2553
|
+
}
|
2345
2554
|
}
|
2346
2555
|
|
2347
|
-
button('Display
|
2556
|
+
button('Display Info') {
|
2557
|
+
stretchy false
|
2558
|
+
|
2348
2559
|
on_clicked do
|
2349
|
-
msg_box('
|
2560
|
+
msg_box('Info', "#{@first_name_entry.text} #{@last_name_entry.text} has phone #{@phone_entry.text} and email #{@email_entry.text}")
|
2350
2561
|
end
|
2351
2562
|
}
|
2352
2563
|
}
|
@@ -3056,7 +3267,7 @@ window('Task Progress', 300, 200) {
|
|
3056
3267
|
|
3057
3268
|
on_clicked do
|
3058
3269
|
data.each_with_index do |row_data, row|
|
3059
|
-
data[row] =
|
3270
|
+
data[row][1] = 100 # automatically updates table due to implicit data-binding
|
3060
3271
|
end
|
3061
3272
|
end
|
3062
3273
|
}
|
@@ -3650,14 +3861,15 @@ window('Area Gallery', 400, 400) {
|
|
3650
3861
|
path { # declarative stable path
|
3651
3862
|
square(0, 0, 100)
|
3652
3863
|
square(100, 100, 400)
|
3653
|
-
|
3864
|
+
|
3654
3865
|
fill r: 102, g: 102, b: 204
|
3655
3866
|
}
|
3656
3867
|
path { # declarative stable path
|
3657
3868
|
rectangle(0, 100, 100, 400)
|
3658
3869
|
rectangle(100, 0, 400, 100)
|
3659
|
-
|
3660
|
-
|
3870
|
+
|
3871
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
3872
|
+
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}]
|
3661
3873
|
}
|
3662
3874
|
path { # declarative stable path
|
3663
3875
|
figure(100, 100) {
|
@@ -3683,17 +3895,24 @@ window('Area Gallery', 400, 400) {
|
|
3683
3895
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3684
3896
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3685
3897
|
}
|
3898
|
+
path { # declarative stable path
|
3899
|
+
arc(400, 220, 180, 90, 90, false)
|
3900
|
+
|
3901
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
3902
|
+
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}]
|
3903
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3904
|
+
}
|
3686
3905
|
path { # declarative stable path
|
3687
3906
|
circle(200, 200, 90)
|
3688
3907
|
|
3689
3908
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3690
3909
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
3691
3910
|
}
|
3692
|
-
|
3693
|
-
|
3694
|
-
|
3695
|
-
|
3696
|
-
|
3911
|
+
text(160, 40, 100) { # x, y, width
|
3912
|
+
string('Area Gallery') {
|
3913
|
+
font family: 'Times', size: 14
|
3914
|
+
color :black
|
3915
|
+
}
|
3697
3916
|
}
|
3698
3917
|
|
3699
3918
|
on_mouse_event do |area_mouse_event|
|
@@ -3784,7 +4003,8 @@ window('Area Gallery', 400, 400) {
|
|
3784
4003
|
height 100
|
3785
4004
|
}
|
3786
4005
|
|
3787
|
-
|
4006
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4007
|
+
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}]
|
3788
4008
|
}
|
3789
4009
|
path { # declarative stable path
|
3790
4010
|
figure {
|
@@ -3846,16 +4066,6 @@ window('Area Gallery', 400, 400) {
|
|
3846
4066
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3847
4067
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3848
4068
|
}
|
3849
|
-
path { # declarative stable path
|
3850
|
-
circle {
|
3851
|
-
x_center 200
|
3852
|
-
y_center 200
|
3853
|
-
radius 90
|
3854
|
-
}
|
3855
|
-
|
3856
|
-
fill r: 202, g: 102, b: 204, a: 0.5
|
3857
|
-
stroke r: 0, g: 0, b: 0, thickness: 2
|
3858
|
-
}
|
3859
4069
|
path { # declarative stable path
|
3860
4070
|
arc {
|
3861
4071
|
x_center 400
|
@@ -3866,9 +4076,32 @@ window('Area Gallery', 400, 400) {
|
|
3866
4076
|
is_negative false
|
3867
4077
|
}
|
3868
4078
|
|
3869
|
-
|
4079
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4080
|
+
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}]
|
3870
4081
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3871
4082
|
}
|
4083
|
+
path { # declarative stable path
|
4084
|
+
circle {
|
4085
|
+
x_center 200
|
4086
|
+
y_center 200
|
4087
|
+
radius 90
|
4088
|
+
}
|
4089
|
+
|
4090
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
4091
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
4092
|
+
}
|
4093
|
+
text {
|
4094
|
+
x 160
|
4095
|
+
y 40
|
4096
|
+
width 100
|
4097
|
+
|
4098
|
+
string {
|
4099
|
+
font family: 'Times', size: 14
|
4100
|
+
color :black
|
4101
|
+
|
4102
|
+
'Area Gallery'
|
4103
|
+
}
|
4104
|
+
}
|
3872
4105
|
|
3873
4106
|
on_mouse_event do |area_mouse_event|
|
3874
4107
|
p area_mouse_event
|
@@ -3941,7 +4174,8 @@ window('Area Gallery', 400, 400) {
|
|
3941
4174
|
rectangle(0, 100, 100, 400)
|
3942
4175
|
rectangle(100, 0, 400, 100)
|
3943
4176
|
|
3944
|
-
|
4177
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4178
|
+
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}]
|
3945
4179
|
}
|
3946
4180
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
3947
4181
|
figure(100, 100) {
|
@@ -3967,17 +4201,24 @@ window('Area Gallery', 400, 400) {
|
|
3967
4201
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3968
4202
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
3969
4203
|
}
|
4204
|
+
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4205
|
+
arc(400, 220, 180, 90, 90, false)
|
4206
|
+
|
4207
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4208
|
+
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}]
|
4209
|
+
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4210
|
+
}
|
3970
4211
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
3971
4212
|
circle(200, 200, 90)
|
3972
4213
|
|
3973
4214
|
fill r: 202, g: 102, b: 204, a: 0.5
|
3974
4215
|
stroke r: 0, g: 0, b: 0, thickness: 2
|
3975
4216
|
}
|
3976
|
-
|
3977
|
-
|
3978
|
-
|
3979
|
-
|
3980
|
-
|
4217
|
+
text(160, 40, 100) { # x, y, width
|
4218
|
+
string('Area Gallery') {
|
4219
|
+
font family: 'Times', size: 14
|
4220
|
+
color :black
|
4221
|
+
}
|
3981
4222
|
}
|
3982
4223
|
end
|
3983
4224
|
|
@@ -4070,7 +4311,8 @@ window('Area Gallery', 400, 400) {
|
|
4070
4311
|
height 100
|
4071
4312
|
}
|
4072
4313
|
|
4073
|
-
|
4314
|
+
# linear gradient (has x0, y0, x1, y1, and stops)
|
4315
|
+
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}]
|
4074
4316
|
}
|
4075
4317
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4076
4318
|
figure {
|
@@ -4132,16 +4374,6 @@ window('Area Gallery', 400, 400) {
|
|
4132
4374
|
fill r: 202, g: 102, b: 204, a: 0.5
|
4133
4375
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4134
4376
|
}
|
4135
|
-
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4136
|
-
circle {
|
4137
|
-
x_center 200
|
4138
|
-
y_center 200
|
4139
|
-
radius 90
|
4140
|
-
}
|
4141
|
-
|
4142
|
-
fill r: 202, g: 102, b: 204, a: 0.5
|
4143
|
-
stroke r: 0, g: 0, b: 0, thickness: 2
|
4144
|
-
}
|
4145
4377
|
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4146
4378
|
arc {
|
4147
4379
|
x_center 400
|
@@ -4152,9 +4384,32 @@ window('Area Gallery', 400, 400) {
|
|
4152
4384
|
is_negative false
|
4153
4385
|
}
|
4154
4386
|
|
4155
|
-
|
4387
|
+
# radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
|
4388
|
+
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}]
|
4156
4389
|
stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
|
4157
4390
|
}
|
4391
|
+
path { # a dynamic path is added semi-declaratively inside on_draw block
|
4392
|
+
circle {
|
4393
|
+
x_center 200
|
4394
|
+
y_center 200
|
4395
|
+
radius 90
|
4396
|
+
}
|
4397
|
+
|
4398
|
+
fill r: 202, g: 102, b: 204, a: 0.5
|
4399
|
+
stroke r: 0, g: 0, b: 0, thickness: 2
|
4400
|
+
}
|
4401
|
+
text {
|
4402
|
+
x 160
|
4403
|
+
y 40
|
4404
|
+
width 100
|
4405
|
+
|
4406
|
+
string {
|
4407
|
+
font family: 'Times', size: 14
|
4408
|
+
color :black
|
4409
|
+
|
4410
|
+
'Area Gallery'
|
4411
|
+
}
|
4412
|
+
}
|
4158
4413
|
end
|
4159
4414
|
|
4160
4415
|
on_mouse_event do |area_mouse_event|
|
@@ -5578,7 +5833,132 @@ class CustomDrawText
|
|
5578
5833
|
end
|
5579
5834
|
|
5580
5835
|
CustomDrawText.new.launch
|
5836
|
+
```
|
5837
|
+
|
5838
|
+
### Method-Based Custom Keyword
|
5839
|
+
|
5840
|
+
[examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
|
5841
|
+
|
5842
|
+
Run with this command from the root of the project if you cloned the project:
|
5843
|
+
|
5844
|
+
```
|
5845
|
+
ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_keyword.rb
|
5846
|
+
```
|
5581
5847
|
|
5848
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
5849
|
+
|
5850
|
+
```
|
5851
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_keyword'"
|
5852
|
+
```
|
5853
|
+
|
5854
|
+
Mac
|
5855
|
+
|
5856
|
+
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
5857
|
+
|
5858
|
+
Linux
|
5859
|
+
|
5860
|
+
![glimmer-dsl-libui-linux-method-based-custom-keyword.png](images/glimmer-dsl-libui-linux-method-based-custom-keyword.png)
|
5861
|
+
|
5862
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
5863
|
+
|
5864
|
+
```ruby
|
5865
|
+
require 'glimmer-dsl-libui'
|
5866
|
+
require 'facets'
|
5867
|
+
|
5868
|
+
include Glimmer
|
5869
|
+
|
5870
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
5871
|
+
|
5872
|
+
def form_field(model, property)
|
5873
|
+
property = property.to_s
|
5874
|
+
entry { |e|
|
5875
|
+
label property.underscore.split('_').map(&:capitalize).join(' ')
|
5876
|
+
text model.send(property).to_s
|
5877
|
+
|
5878
|
+
on_changed do
|
5879
|
+
model.send("#{property}=", e.text)
|
5880
|
+
end
|
5881
|
+
}
|
5882
|
+
end
|
5883
|
+
|
5884
|
+
def address_form(address)
|
5885
|
+
form {
|
5886
|
+
form_field(address, :street)
|
5887
|
+
form_field(address, :p_o_box)
|
5888
|
+
form_field(address, :city)
|
5889
|
+
form_field(address, :state)
|
5890
|
+
form_field(address, :zip_code)
|
5891
|
+
}
|
5892
|
+
end
|
5893
|
+
|
5894
|
+
def label_pair(model, attribute, value)
|
5895
|
+
name_label = nil
|
5896
|
+
value_label = nil
|
5897
|
+
horizontal_box {
|
5898
|
+
name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
5899
|
+
value_label = label(value.to_s)
|
5900
|
+
}
|
5901
|
+
Glimmer::DataBinding::Observer.proc do
|
5902
|
+
value_label.text = model.send(attribute)
|
5903
|
+
end.observe(model, attribute)
|
5904
|
+
end
|
5905
|
+
|
5906
|
+
def address(address)
|
5907
|
+
vertical_box {
|
5908
|
+
address.each_pair do |attribute, value|
|
5909
|
+
label_pair(address, attribute, value)
|
5910
|
+
end
|
5911
|
+
}
|
5912
|
+
end
|
5913
|
+
|
5914
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
5915
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
5916
|
+
|
5917
|
+
window('Method-Based Custom Keyword') {
|
5918
|
+
margined true
|
5919
|
+
|
5920
|
+
horizontal_box {
|
5921
|
+
vertical_box {
|
5922
|
+
label('Address 1') {
|
5923
|
+
stretchy false
|
5924
|
+
}
|
5925
|
+
|
5926
|
+
address_form(address1)
|
5927
|
+
|
5928
|
+
horizontal_separator {
|
5929
|
+
stretchy false
|
5930
|
+
}
|
5931
|
+
|
5932
|
+
label('Address 1 (Saved)') {
|
5933
|
+
stretchy false
|
5934
|
+
}
|
5935
|
+
|
5936
|
+
address(address1)
|
5937
|
+
}
|
5938
|
+
|
5939
|
+
vertical_separator {
|
5940
|
+
stretchy false
|
5941
|
+
}
|
5942
|
+
|
5943
|
+
vertical_box {
|
5944
|
+
label('Address 2') {
|
5945
|
+
stretchy false
|
5946
|
+
}
|
5947
|
+
|
5948
|
+
address_form(address2)
|
5949
|
+
|
5950
|
+
horizontal_separator {
|
5951
|
+
stretchy false
|
5952
|
+
}
|
5953
|
+
|
5954
|
+
label('Address 2 (Saved)') {
|
5955
|
+
stretchy false
|
5956
|
+
}
|
5957
|
+
|
5958
|
+
address(address2)
|
5959
|
+
}
|
5960
|
+
}
|
5961
|
+
}.show
|
5582
5962
|
```
|
5583
5963
|
|
5584
5964
|
## Contributing to glimmer-dsl-libui
|