glimmer-dsl-libui 0.2.6 → 0.2.10

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.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.10
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)
@@ -14,10 +14,10 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
14
14
  - Declarative DSL syntax that visually maps to the GUI control hierarchy
15
15
  - Convention over configuration via smart defaults and automation of low-level details
16
16
  - 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
17
  - Custom Control support
19
- - Scaffolding for new custom controls, apps, and gems
20
- - Native-Executable packaging on Mac, Windows, and Linux.
18
+ - [Far Future Plan] Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
19
+ - [Far Future Plan] Scaffolding for new custom controls, apps, and gems
20
+ - [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
21
21
 
22
22
  Hello, World!
23
23
 
@@ -61,7 +61,7 @@ window('Task Progress', 300, 200) {
61
61
 
62
62
  on_clicked do
63
63
  data.each_with_index do |row_data, row|
64
- data[row] = [row_data[0], 100] # automatically updates table due to implicit data-binding
64
+ data[row][1] = 100 # automatically updates table due to implicit data-binding
65
65
  end
66
66
  end
67
67
  }
@@ -84,14 +84,15 @@ window('Area Gallery', 400, 400) {
84
84
  path { # declarative stable path
85
85
  square(0, 0, 100)
86
86
  square(100, 100, 400)
87
-
87
+
88
88
  fill r: 102, g: 102, b: 204
89
89
  }
90
90
  path { # declarative stable path
91
91
  rectangle(0, 100, 100, 400)
92
92
  rectangle(100, 0, 400, 100)
93
-
94
- fill r: 204, g: 102, b: 204
93
+
94
+ # linear gradient (has x0, y0, x1, y1, and stops)
95
+ 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
96
  }
96
97
  path { # declarative stable path
97
98
  figure(100, 100) {
@@ -117,17 +118,26 @@ window('Area Gallery', 400, 400) {
117
118
  fill r: 202, g: 102, b: 204, a: 0.5
118
119
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
119
120
  }
121
+ path { # declarative stable path
122
+ arc(400, 220, 180, 90, 90, false)
123
+
124
+ # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
125
+ 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}]
126
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
127
+ }
120
128
  path { # declarative stable path
121
129
  circle(200, 200, 90)
122
130
 
123
131
  fill r: 202, g: 102, b: 204, a: 0.5
124
132
  stroke r: 0, g: 0, b: 0, thickness: 2
125
133
  }
126
- path { # declarative stable path
127
- arc(400, 220, 180, 90, 90, false)
128
-
129
- fill r: 204, g: 102, b: 204, a: 0.5
130
- stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
134
+ text(160, 40, 100) { # x, y, width
135
+ string {
136
+ font family: 'Times', size: 14
137
+ color :black
138
+
139
+ 'Area Gallery'
140
+ }
131
141
  }
132
142
 
133
143
  on_mouse_event do |area_mouse_event|
@@ -186,7 +196,7 @@ window('Area Gallery', 400, 400) {
186
196
 
187
197
  [Check Out Many More Examples Over Here!](#examples)
188
198
 
189
- NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is in early alpha mode (only supports included [examples](#examples)). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. It is still an early alpha, so the more feedback and issues you report the better.
199
+ 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
200
 
191
201
  Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
192
202
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
@@ -197,7 +207,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
197
207
 
198
208
  ## Table of Contents
199
209
 
200
- - [Glimmer DSL for LibUI 0.2.6](#-glimmer-dsl-for-libui-026)
210
+ - [Glimmer DSL for LibUI 0.2.10](#-glimmer-dsl-for-libui-0210)
201
211
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
202
212
  - [Usage](#usage)
203
213
  - [Girb (Glimmer IRB)](#girb-glimmer-irb)
@@ -211,8 +221,10 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
211
221
  - [Table API](#table-api)
212
222
  - [Area API](#area-api)
213
223
  - [Smart Defaults and Conventions](#smart-defaults-and-conventions)
224
+ - [Custom Keywords](#custom-keywords)
214
225
  - [API Gotchas](#api-gotchas)
215
226
  - [Original API](#original-api)
227
+ - [Packaging](#packaging)
216
228
  - [Glimmer Style Guide](#glimmer-style-guide)
217
229
  - [Examples](#examples)
218
230
  - [Basic Window](#basic-window)
@@ -235,6 +247,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
235
247
  - [Basic Table Checkbox](#basic-table-checkbox)
236
248
  - [Basic Table Checkbox Text](#basic-table-checkbox-text)
237
249
  - [Basic Table Progress Bar](#basic-table-progress-bar)
250
+ - [Basic Table Color](#basic-table-color)
238
251
  - [Form Table](#form-table)
239
252
  - [Basic Area](#basic-area)
240
253
  - [Dynamic Area](#dynamic-area)
@@ -246,6 +259,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
246
259
  - [Color The Circles](#color-the-circles)
247
260
  - [Basic Draw Text](#basic-draw-text)
248
261
  - [Custom Draw Text](#custom-draw-text)
262
+ - [Method-Based Custom Keyword](#method-based-custom-keyword)
249
263
  - [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
250
264
  - [Help](#help)
251
265
  - [Issues](#issues)
@@ -333,7 +347,7 @@ gem install glimmer-dsl-libui
333
347
  Or install via Bundler `Gemfile`:
334
348
 
335
349
  ```ruby
336
- gem 'glimmer-dsl-libui', '~> 0.2.6'
350
+ gem 'glimmer-dsl-libui', '~> 0.2.10'
337
351
  ```
338
352
 
339
353
  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.
@@ -409,12 +423,14 @@ Control(Args) | Properties | Listeners
409
423
  `about_menu_item` | None | `on_clicked`
410
424
  `area` | None | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
411
425
  `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)` | `x_center` (`Numeric`), `y_center` (`Numeric`), `radius` (`Numeric`), `start_angle` (`Numeric`), `sweep` (`Numeric`), `is_negative` (Boolean) | None
426
+ `background_color_column(name as String)` | None | None
412
427
  `bezier(c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
413
428
  `button(text as String)` | `text` (`String`) | `on_clicked`
414
429
  `button_column(name as String)` | `enabled` (Boolean) | None
415
430
  `checkbox(text as String)` | `checked` (Boolean), `text` (`String`) | `on_toggled`
416
431
  `checkbox_column(name as String)` | `editable` (Boolean) | None
417
432
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
433
+ `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
418
434
  `combobox` | `items` (`Array` of `String`), `selected` (`Integer`) | `on_selected`
419
435
  `color_button` | `color` (Array of `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float`), `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float` | `on_changed`
420
436
  `date_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
@@ -432,6 +448,7 @@ Control(Args) | Properties | Listeners
432
448
  `image_part(pixels as String [encoded image rgba byte array], width as Numeric, height as Numeric, byte_stride as Numeric [usually width*4])` | None | None
433
449
  `image_column(name as String)` | None | None
434
450
  `image_text_column(name as String)` | None | None
451
+ `image_text_color_column(name as String)` | None | None
435
452
  `label(text as String)` | `text` (`String`) | None
436
453
  `line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
437
454
  `matrix(m11 = nil as Numeric, m12 = nil as Numeric, m21 = nil as Numeric, m22 = nil as Numeric, m31 = nil as Numeric, m32 = nil as Numeric)` | `m11` (`Numeric`), `m12` (`Numeric`), `m21` (`Numeric`), `m22` (`Numeric`), `m31` (`Numeric`), `m32` (`Numeric`) | None
@@ -442,7 +459,7 @@ Control(Args) | Properties | Listeners
442
459
  `msg_box_error(window = main_window as Glimmer::LibUI::WindowProxy, title as String, description as String)` | None | None
443
460
  `non_wrapping_multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
444
461
  `password_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
445
- `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`), `stroke` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, `:cap` as (`:round`, `:square`, `:flat`), `:join` as (`:miter`, `:round`, `:bevel`), `:thickness` as `Numeric`, `:miter_limit` as `Numeric`, `:dashes` as `Array` of `Numeric` ) | None
462
+ `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
446
463
  `preferences_menu_item` | None | `on_clicked`
447
464
  `progress_bar` | `value` (`Numeric`) | None
448
465
  `progress_bar_column(name as String)` | None | None
@@ -453,10 +470,13 @@ Control(Args) | Properties | Listeners
453
470
  `slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
454
471
  `spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
455
472
  `square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
473
+ `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
456
474
  `tab` | `margined` (Boolean), `num_pages` (`Integer`) | None
457
475
  `tab_item(name as String)` | `index` [read-only] (`Integer`), `margined` (Boolean), `name` [read-only] (`String`) | None
458
- `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 | None
476
+ `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| ...}`
477
+ `text(x = 0 as Numeric, y = 0 as Numeric, width = area_width as Numeric)` | `align`, `default_font` | None
459
478
  `text_column(name as String)` | `editable` (Boolean) | None
479
+ `text_color_column(name as String)` | `editable` (Boolean) | None
460
480
  `time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`) | `on_changed`
461
481
  `vertical_box` | `padded` (Boolean) | None
462
482
  `vertical_separator` | None | None
@@ -510,12 +530,16 @@ All operations that could normally be called on `LibUI` can also be called on `G
510
530
  ### Table API
511
531
 
512
532
  The `table` control must first declare its columns via one of these column keywords (mentioned in [Supported Controls](#supported-controls)):
533
+ - `background_color_column`: expects color cell values
513
534
  - `button_column`: expects `String` cell values
514
535
  - `checkbox_column`: expects Boolean cell values
515
536
  - `checkbox_text_column`: expects dual-element `Array` of Boolean and `String` cell values
537
+ - `checkbox_text_color_column`: expects triple-element `Array` of Boolean, `String`, and color cell values
516
538
  - `image_column`: expects `image` cell values (produced by `image` and `image_part` keywords as per [Supported Controls](#supported-controls))
517
539
  - `image_text_column`: expects dual-element `Array` of `image` and `String` cell values
540
+ - `image_text_color_column`: expects triple-element `Array` of `image`, `String`, and color cell values
518
541
  - `text_column`: expects `String` cell values
542
+ - `text_color_column`: expects dual-element `Array` of `String` and color cell values
519
543
  - `progress_bar_column`: expects `Integer` cell values
520
544
 
521
545
  Afterwards, it must declare its `cell_rows` array (`Array` of `Array`s of column cell values) and whether it is `editable` (Boolean) for all its columns.
@@ -798,11 +822,11 @@ To draw `text` in an `area`, you simply nest a `text(x, y, width)` control direc
798
822
 
799
823
  `string` can have the following properties:
800
824
  - `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
801
- - `color`: rgba, hex, or x11 color
802
- - `background`: rgba, hex, or x11 color
825
+ - `color`: rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
826
+ - `background`: rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
803
827
  - `underline`: one of `:none`, `:single`, `:double`, `:suggestion`, `:color_custom`, `:color_spelling`, `:color_grammar`, `:color_auxiliary`
804
- - `underline_color`: one of `:spelling`, `:grammar`, `:auxiliary`, rgba, hex, or x11 color
805
- - `open_type_features`: it must have a block containing `open_type_tag` occurrances, which take the a, b, c, d arguments plus a number at the end.
828
+ - `underline_color`: one of `:spelling`, `:grammar`, `:auxiliary`, rgba, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color
829
+ - `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.
806
830
 
807
831
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
808
832
 
@@ -871,16 +895,126 @@ window('area text drawing') {
871
895
  - 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)
872
896
  - Color alpha value defaults to `1.0` when not specified.
873
897
 
898
+ ### Custom Keywords
899
+
900
+ 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.
901
+
902
+ Example that defines `field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
903
+
904
+ ```ruby
905
+ require 'glimmer-dsl-libui'
906
+ require 'facets'
907
+
908
+ include Glimmer
909
+
910
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
911
+
912
+ def field(model, property)
913
+ property = property.to_s
914
+ entry { |e|
915
+ label property.underscore.split('_').map(&:capitalize).join(' ')
916
+ text model.send(property).to_s
917
+
918
+ on_changed do
919
+ model.send("#{property}=", e.text)
920
+ end
921
+ }
922
+ end
923
+
924
+ def address_form(address)
925
+ form {
926
+ field(address, :street)
927
+ field(address, :p_o_box)
928
+ field(address, :city)
929
+ field(address, :state)
930
+ field(address, :zip_code)
931
+ }
932
+ end
933
+
934
+ def label_pair(model, attribute, value)
935
+ name_label = nil
936
+ value_label = nil
937
+ horizontal_box {
938
+ name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
939
+ value_label = label(value.to_s)
940
+ }
941
+ Glimmer::DataBinding::Observer.proc do
942
+ value_label.text = model.send(attribute)
943
+ end.observe(model, attribute)
944
+ end
945
+
946
+ def address(address)
947
+ vertical_box {
948
+ address.each_pair do |attribute, value|
949
+ label_pair(address, attribute, value)
950
+ end
951
+ }
952
+ end
953
+
954
+ address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
955
+ address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
956
+
957
+ window('Method-Based Custom Keyword') {
958
+ margined true
959
+
960
+ horizontal_box {
961
+ vertical_box {
962
+ label('Address 1') {
963
+ stretchy false
964
+ }
965
+ address_form(address1)
966
+ horizontal_separator {
967
+ stretchy false
968
+ }
969
+ label('Address 1 (Saved)') {
970
+ stretchy false
971
+ }
972
+ address(address1)
973
+ }
974
+ vertical_separator {
975
+ stretchy false
976
+ }
977
+ vertical_box {
978
+ label('Address 2') {
979
+ stretchy false
980
+ }
981
+ address_form(address2)
982
+ horizontal_separator {
983
+ stretchy false
984
+ }
985
+ label('Address 2 (Saved)') {
986
+ stretchy false
987
+ }
988
+ address(address2)
989
+ }
990
+ }
991
+ }.show
992
+ ```
993
+
994
+ ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
995
+
874
996
  ### API Gotchas
875
997
 
876
998
  - 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`).
877
999
  - `table` `checkbox_column` and `checkbox_text_column` checkbox editing only works on Windows and Linux (not Mac) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
878
- - It seems that `arc` `start_angle` and `sweep` properties are ignored by [libui](https://github.com/andlabs/libui) and always set to `0` and `360` respectively, producing a full circle.
1000
+ - `text` `align` property seems not to work on the Mac ([libui](https://github.com/andlabs/libui) has an [issue](https://github.com/andlabs/libui/pull/407) about it)
879
1001
 
880
1002
  ### Original API
881
1003
 
882
- 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),
883
- check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
1004
+ 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):
1005
+ - Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
1006
+ - Check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
1007
+ - 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.
1008
+
1009
+ ## Packaging
1010
+
1011
+ 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.
1012
+
1013
+ 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.
1014
+
1015
+ For Mac, consider [Platybus](https://github.com/sveinbjornt/Platypus) (builds a native Mac app from a Ruby script)
1016
+
1017
+ 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/).
884
1018
 
885
1019
  ## Glimmer Style Guide
886
1020
 
@@ -897,8 +1031,6 @@ The following examples include reimplementions of the examples in the [LibUI](ht
897
1031
 
898
1032
  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.
899
1033
 
900
- (note that for examples that emit output to terminal/command-line via `p` or `puts`, you must run them directly to see output)
901
-
902
1034
  [examples/meta_example.rb](examples/meta_example.rb)
903
1035
 
904
1036
  Run with this command from the root of the project if you cloned the project:
@@ -924,65 +1056,118 @@ Linux
924
1056
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
925
1057
 
926
1058
  ```ruby
1059
+ # frozen_string_literal: true
1060
+
927
1061
  require 'glimmer-dsl-libui'
928
1062
  require 'facets'
929
1063
 
930
1064
  class MetaExample
931
1065
  include Glimmer
932
1066
 
1067
+ def initialize
1068
+ @selected_example_index = 0
1069
+ end
1070
+
933
1071
  def examples
934
1072
  if @examples.nil?
935
1073
  example_files = Dir.glob(File.join(File.expand_path('.', __dir__), '**', '*.rb'))
936
1074
  example_file_names = example_files.map { |f| File.basename(f, '.rb') }
937
- example_file_names = example_file_names.reject { |f| f == 'meta_example' }
1075
+ example_file_names = example_file_names.reject { |f| f == 'meta_example' || f.match(/\d$/) }
938
1076
  @examples = example_file_names.map { |f| f.underscore.titlecase }
939
1077
  end
940
1078
  @examples
941
1079
  end
942
1080
 
1081
+ def examples_with_versions
1082
+ examples.map do |example|
1083
+ version_count_for(example) > 1 ? "#{example} (#{version_count_for(example)} versions)" : example
1084
+ end
1085
+ end
1086
+
943
1087
  def file_path_for(example)
944
1088
  File.join(File.expand_path('.', __dir__), "#{example.underscore}.rb")
945
1089
  end
946
1090
 
1091
+ def version_count_for(example)
1092
+ Dir.glob(File.join(File.expand_path('.', __dir__), "#{example.underscore}*.rb")).select {|file| file.match(/\d\.rb$/)}.count + 1
1093
+ end
1094
+
947
1095
  def glimmer_dsl_libui_file
948
1096
  File.expand_path('../lib/glimmer-dsl-libui', __dir__)
949
1097
  end
950
1098
 
1099
+ def selected_example
1100
+ examples[@selected_example_index]
1101
+ end
1102
+
951
1103
  def launch
952
1104
  window('Meta-Example', 700, 500) {
953
1105
  margined true
954
1106
 
955
1107
  horizontal_box {
956
1108
  vertical_box {
957
- @rbs = radio_buttons {
1109
+ stretchy false
1110
+
1111
+ @example_radio_buttons = radio_buttons {
958
1112
  stretchy false
959
- items examples
960
- selected 0
1113
+ items examples_with_versions
1114
+ selected @selected_example_index
961
1115
 
962
1116
  on_selected do
963
- @nwme.text = File.read(file_path_for(@examples[@rbs.selected]))
1117
+ @selected_example_index = @example_radio_buttons.selected
1118
+ example = selected_example
1119
+ @code_entry.text = File.read(file_path_for(example))
1120
+ @version_spinbox.value = 1
964
1121
  end
965
1122
  }
966
- button('Launch') {
1123
+
1124
+ horizontal_box {
1125
+ label('Version') {
1126
+ stretchy false
1127
+ }
1128
+
1129
+ @version_spinbox = spinbox(1, 100) {
1130
+ value 1
1131
+
1132
+ on_changed do
1133
+ example = selected_example
1134
+ if @version_spinbox.value > version_count_for(example)
1135
+ @version_spinbox.value -= 1
1136
+ else
1137
+ version_number = @version_spinbox.value == 1 ? '' : @version_spinbox.value
1138
+ example = "#{selected_example}#{version_number}"
1139
+ @code_entry.text = File.read(file_path_for(example))
1140
+ end
1141
+ end
1142
+ }
1143
+ }
1144
+
1145
+ horizontal_box {
967
1146
  stretchy false
968
1147
 
969
- on_clicked do
970
- begin
971
- meta_example_file = File.join(Dir.home, '.meta_example.rb')
972
- File.write(meta_example_file, @nwme.text)
973
- result = `ruby -r #{glimmer_dsl_libui_file} #{meta_example_file} 2>&1`
974
- msg_box('Error Running Example', result) if result.include?('error')
975
- rescue => e
976
- puts 'Unable to write code changes! Running original example...'
977
- system "ruby -r #{glimmer_dsl_libui_file} #{file_path_for(@examples[@rbs.selected])}"
1148
+ button('Launch') {
1149
+ on_clicked do
1150
+ begin
1151
+ meta_example_file = File.join(Dir.home, '.meta_example.rb')
1152
+ File.write(meta_example_file, @code_entry.text)
1153
+ result = `ruby -r #{glimmer_dsl_libui_file} #{meta_example_file} 2>&1`
1154
+ msg_box('Error Running Example', result) if result.include?('error')
1155
+ rescue => e
1156
+ puts 'Unable to write code changes! Running original example...'
1157
+ system "ruby -r #{glimmer_dsl_libui_file} #{file_path_for(selected_example)}"
1158
+ end
978
1159
  end
979
- end
1160
+ }
1161
+ button('Reset') {
1162
+ on_clicked do
1163
+ @code_entry.text = File.read(file_path_for(selected_example))
1164
+ end
1165
+ }
980
1166
  }
981
1167
  }
982
- vertical_box {
983
- @nwme = non_wrapping_multiline_entry {
984
- text File.read(file_path_for(@examples[@rbs.selected]))
985
- }
1168
+
1169
+ @code_entry = non_wrapping_multiline_entry {
1170
+ text File.read(file_path_for(selected_example))
986
1171
  }
987
1172
  }
988
1173
  }.show
@@ -2049,6 +2234,8 @@ include Glimmer
2049
2234
 
2050
2235
  window('color button', 230) {
2051
2236
  color_button { |cb|
2237
+ color :blue
2238
+
2052
2239
  on_changed do
2053
2240
  rgba = cb.color
2054
2241
  p rgba
@@ -2513,6 +2700,14 @@ window('Editable animal sounds', 300, 200) {
2513
2700
 
2514
2701
  cell_rows data
2515
2702
  editable true
2703
+
2704
+ on_changed do |row, type, row_data| # fires on all changes (even ones happening through data array)
2705
+ puts "Row #{row} #{type}: #{row_data}"
2706
+ end
2707
+
2708
+ on_edited do |row, row_data| # only fires on direct table editing
2709
+ puts "Row #{row} edited: #{row_data}"
2710
+ end
2516
2711
  }
2517
2712
  }
2518
2713
 
@@ -2584,7 +2779,7 @@ window('Editable column animal sounds', 400, 200) {
2584
2779
 
2585
2780
  ### Basic Table Image
2586
2781
 
2587
- This example has a prerequisite of installing `chunky_png` Ruby gem:
2782
+ This example requires pre-installing `chunky_png` Ruby gem:
2588
2783
 
2589
2784
  ```
2590
2785
  gem install chunky_png -v1.4.0
@@ -2871,6 +3066,10 @@ window('Animal sounds', 300, 200) {
2871
3066
  }
2872
3067
 
2873
3068
  cell_rows data # implicit data-binding
3069
+
3070
+ on_changed do |row, type, row_data|
3071
+ puts "Row #{row} #{type}: #{row_data}"
3072
+ end
2874
3073
  }
2875
3074
  }
2876
3075
  }.show
@@ -3033,7 +3232,7 @@ window('Task Progress', 300, 200) {
3033
3232
 
3034
3233
  on_clicked do
3035
3234
  data.each_with_index do |row_data, row|
3036
- data[row] = [row_data[0], 100] # automatically updates table due to implicit data-binding
3235
+ data[row][1] = 100 # automatically updates table due to implicit data-binding
3037
3236
  end
3038
3237
  end
3039
3238
  }
@@ -3041,6 +3240,78 @@ window('Task Progress', 300, 200) {
3041
3240
  }.show
3042
3241
  ```
3043
3242
 
3243
+ ### Basic Table Color
3244
+
3245
+ This example requires pre-installing `chunky_png` Ruby gem:
3246
+
3247
+ ```
3248
+ gem install chunky_png -v1.4.0
3249
+ ```
3250
+
3251
+ [examples/basic_table_color.rb](examples/basic_table_color.rb)
3252
+
3253
+ Run with this command from the root of the project if you cloned the project:
3254
+
3255
+ ```
3256
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_table_color.rb
3257
+ ```
3258
+
3259
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
3260
+
3261
+ ```
3262
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_table_color'"
3263
+ ```
3264
+
3265
+ Mac
3266
+
3267
+ ![glimmer-dsl-libui-mac-basic-table-color.png](images/glimmer-dsl-libui-mac-basic-table-color.png)
3268
+
3269
+ Linux
3270
+
3271
+ ![glimmer-dsl-libui-linux-basic-table-color.png](images/glimmer-dsl-libui-linux-basic-table-color.png)
3272
+
3273
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3274
+
3275
+ ```ruby
3276
+ require 'glimmer-dsl-libui'
3277
+ require 'chunky_png'
3278
+
3279
+ include Glimmer
3280
+
3281
+ f = File.open(File.expand_path('../icons/glimmer.png', __dir__))
3282
+ canvas = ChunkyPNG::Canvas.from_io(f)
3283
+ f.close
3284
+ canvas.resample_nearest_neighbor!(24, 24)
3285
+ data = canvas.to_rgba_stream
3286
+ width = canvas.width
3287
+ height = canvas.height
3288
+ img = image {
3289
+ image_part(data, width, height, width * 4)
3290
+ }
3291
+
3292
+ data = [
3293
+ [['cat', :red] , ['meow', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], {r: 255, g: 120, b: 0, a: 0.5}],
3294
+ [['dog', :yellow] , ['woof', {r: 240, g: 32, b: 32}] , [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], :skyblue],
3295
+ [['chicken', :beige], ['cock-a-doodle-doo', :blue] , [false, 'mammal', :red] , [img, 'Glimmer', :beige], {r: 5, g: 120, b: 110}],
3296
+ [['horse', :purple] , ['neigh', {r: 240, g: 32, b: 32}], [true, 'mammal', :green], [img, 'Glimmer', :dark_blue], '13a1fb'],
3297
+ [['cow', :gray] , ['moo', :blue] , [true, 'mammal', :green], [img, 'Glimmer', :brown], 0x12ff02]
3298
+ ]
3299
+
3300
+ window('Animals', 500, 200) {
3301
+ horizontal_box {
3302
+ table {
3303
+ text_color_column('Animal')
3304
+ text_color_column('Sound')
3305
+ checkbox_text_color_column('Description')
3306
+ image_text_color_column('GUI')
3307
+ background_color_column('Mammal')
3308
+
3309
+ cell_rows data
3310
+ }
3311
+ }
3312
+ }.show
3313
+ ```
3314
+
3044
3315
  ### Form Table
3045
3316
 
3046
3317
  [examples/form_table.rb](examples/form_table.rb)
@@ -3156,6 +3427,10 @@ window('Contacts', 600, 600) { |w|
3156
3427
  text_column('State')
3157
3428
 
3158
3429
  cell_rows data # implicit data-binding
3430
+
3431
+ on_changed do |row, type, row_data|
3432
+ puts "Row #{row} #{type}: #{row_data}"
3433
+ end
3159
3434
  }
3160
3435
  }
3161
3436
  }.show
@@ -3551,14 +3826,15 @@ window('Area Gallery', 400, 400) {
3551
3826
  path { # declarative stable path
3552
3827
  square(0, 0, 100)
3553
3828
  square(100, 100, 400)
3554
-
3829
+
3555
3830
  fill r: 102, g: 102, b: 204
3556
3831
  }
3557
3832
  path { # declarative stable path
3558
3833
  rectangle(0, 100, 100, 400)
3559
3834
  rectangle(100, 0, 400, 100)
3560
-
3561
- fill r: 204, g: 102, b: 204
3835
+
3836
+ # linear gradient (has x0, y0, x1, y1, and stops)
3837
+ 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}]
3562
3838
  }
3563
3839
  path { # declarative stable path
3564
3840
  figure(100, 100) {
@@ -3584,17 +3860,26 @@ window('Area Gallery', 400, 400) {
3584
3860
  fill r: 202, g: 102, b: 204, a: 0.5
3585
3861
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3586
3862
  }
3863
+ path { # declarative stable path
3864
+ arc(400, 220, 180, 90, 90, false)
3865
+
3866
+ # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
3867
+ 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}]
3868
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3869
+ }
3587
3870
  path { # declarative stable path
3588
3871
  circle(200, 200, 90)
3589
3872
 
3590
3873
  fill r: 202, g: 102, b: 204, a: 0.5
3591
3874
  stroke r: 0, g: 0, b: 0, thickness: 2
3592
3875
  }
3593
- path { # declarative stable path
3594
- arc(400, 220, 180, 90, 90, false)
3595
-
3596
- fill r: 204, g: 102, b: 204, a: 0.5
3597
- stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3876
+ text(160, 40, 100) { # x, y, width
3877
+ string {
3878
+ font family: 'Times', size: 14
3879
+ color :black
3880
+
3881
+ 'Area Gallery'
3882
+ }
3598
3883
  }
3599
3884
 
3600
3885
  on_mouse_event do |area_mouse_event|
@@ -3685,7 +3970,8 @@ window('Area Gallery', 400, 400) {
3685
3970
  height 100
3686
3971
  }
3687
3972
 
3688
- fill r: 204, g: 102, b: 204
3973
+ # linear gradient (has x0, y0, x1, y1, and stops)
3974
+ 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}]
3689
3975
  }
3690
3976
  path { # declarative stable path
3691
3977
  figure {
@@ -3747,16 +4033,6 @@ window('Area Gallery', 400, 400) {
3747
4033
  fill r: 202, g: 102, b: 204, a: 0.5
3748
4034
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3749
4035
  }
3750
- path { # declarative stable path
3751
- circle {
3752
- x_center 200
3753
- y_center 200
3754
- radius 90
3755
- }
3756
-
3757
- fill r: 202, g: 102, b: 204, a: 0.5
3758
- stroke r: 0, g: 0, b: 0, thickness: 2
3759
- }
3760
4036
  path { # declarative stable path
3761
4037
  arc {
3762
4038
  x_center 400
@@ -3767,9 +4043,32 @@ window('Area Gallery', 400, 400) {
3767
4043
  is_negative false
3768
4044
  }
3769
4045
 
3770
- fill r: 204, g: 102, b: 204, a: 0.5
4046
+ # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
4047
+ 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}]
3771
4048
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3772
4049
  }
4050
+ path { # declarative stable path
4051
+ circle {
4052
+ x_center 200
4053
+ y_center 200
4054
+ radius 90
4055
+ }
4056
+
4057
+ fill r: 202, g: 102, b: 204, a: 0.5
4058
+ stroke r: 0, g: 0, b: 0, thickness: 2
4059
+ }
4060
+ text {
4061
+ x 160
4062
+ y 40
4063
+ width 100
4064
+
4065
+ string {
4066
+ font family: 'Times', size: 14
4067
+ color :black
4068
+
4069
+ 'Area Gallery'
4070
+ }
4071
+ }
3773
4072
 
3774
4073
  on_mouse_event do |area_mouse_event|
3775
4074
  p area_mouse_event
@@ -3842,7 +4141,8 @@ window('Area Gallery', 400, 400) {
3842
4141
  rectangle(0, 100, 100, 400)
3843
4142
  rectangle(100, 0, 400, 100)
3844
4143
 
3845
- fill r: 204, g: 102, b: 204
4144
+ # linear gradient (has x0, y0, x1, y1, and stops)
4145
+ 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}]
3846
4146
  }
3847
4147
  path { # a dynamic path is added semi-declaratively inside on_draw block
3848
4148
  figure(100, 100) {
@@ -3868,17 +4168,26 @@ window('Area Gallery', 400, 400) {
3868
4168
  fill r: 202, g: 102, b: 204, a: 0.5
3869
4169
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
3870
4170
  }
4171
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4172
+ arc(400, 220, 180, 90, 90, false)
4173
+
4174
+ # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
4175
+ 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}]
4176
+ stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4177
+ }
3871
4178
  path { # a dynamic path is added semi-declaratively inside on_draw block
3872
4179
  circle(200, 200, 90)
3873
4180
 
3874
4181
  fill r: 202, g: 102, b: 204, a: 0.5
3875
4182
  stroke r: 0, g: 0, b: 0, thickness: 2
3876
4183
  }
3877
- path { # a dynamic path is added semi-declaratively inside on_draw block
3878
- arc(400, 220, 180, 90, 90, false)
3879
-
3880
- fill r: 204, g: 102, b: 204, a: 0.5
3881
- stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4184
+ text(160, 40, 100) { # x, y, width
4185
+ string {
4186
+ font family: 'Times', size: 14
4187
+ color :black
4188
+
4189
+ 'Area Gallery'
4190
+ }
3882
4191
  }
3883
4192
  end
3884
4193
 
@@ -3971,7 +4280,8 @@ window('Area Gallery', 400, 400) {
3971
4280
  height 100
3972
4281
  }
3973
4282
 
3974
- fill r: 204, g: 102, b: 204
4283
+ # linear gradient (has x0, y0, x1, y1, and stops)
4284
+ 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}]
3975
4285
  }
3976
4286
  path { # a dynamic path is added semi-declaratively inside on_draw block
3977
4287
  figure {
@@ -4033,16 +4343,6 @@ window('Area Gallery', 400, 400) {
4033
4343
  fill r: 202, g: 102, b: 204, a: 0.5
4034
4344
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4035
4345
  }
4036
- path { # a dynamic path is added semi-declaratively inside on_draw block
4037
- circle {
4038
- x_center 200
4039
- y_center 200
4040
- radius 90
4041
- }
4042
-
4043
- fill r: 202, g: 102, b: 204, a: 0.5
4044
- stroke r: 0, g: 0, b: 0, thickness: 2
4045
- }
4046
4346
  path { # a dynamic path is added semi-declaratively inside on_draw block
4047
4347
  arc {
4048
4348
  x_center 400
@@ -4053,9 +4353,32 @@ window('Area Gallery', 400, 400) {
4053
4353
  is_negative false
4054
4354
  }
4055
4355
 
4056
- fill r: 204, g: 102, b: 204, a: 0.5
4356
+ # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
4357
+ 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}]
4057
4358
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4058
4359
  }
4360
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4361
+ circle {
4362
+ x_center 200
4363
+ y_center 200
4364
+ radius 90
4365
+ }
4366
+
4367
+ fill r: 202, g: 102, b: 204, a: 0.5
4368
+ stroke r: 0, g: 0, b: 0, thickness: 2
4369
+ }
4370
+ text {
4371
+ x 160
4372
+ y 40
4373
+ width 100
4374
+
4375
+ string {
4376
+ font family: 'Times', size: 14
4377
+ color :black
4378
+
4379
+ 'Area Gallery'
4380
+ }
4381
+ }
4059
4382
  end
4060
4383
 
4061
4384
  on_mouse_event do |area_mouse_event|
@@ -5441,11 +5764,11 @@ class CustomDrawText
5441
5764
  text { # default arguments for x, y, and width are (0, 0, area_draw_params[:area_width])
5442
5765
  # align :left # default alignment
5443
5766
 
5444
- @string = string {
5445
- font @font unless @font.nil?
5446
- color @color unless @color.nil?
5447
- background @background unless @background.nil?
5448
- underline @underline unless @underline.nil?
5767
+ string {
5768
+ font @font
5769
+ color @color
5770
+ background @background
5771
+ underline @underline
5449
5772
 
5450
5773
  ' At last Ygramul sensed that something was coming toward ' \
5451
5774
  'her. With the speed of lightning, she turned about, confronting ' \
@@ -5479,7 +5802,122 @@ class CustomDrawText
5479
5802
  end
5480
5803
 
5481
5804
  CustomDrawText.new.launch
5805
+ ```
5806
+
5807
+ ### Method-Based Custom Keyword
5808
+
5809
+ [examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
5810
+
5811
+ Run with this command from the root of the project if you cloned the project:
5812
+
5813
+ ```
5814
+ ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_keyword.rb
5815
+ ```
5816
+
5817
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
5818
+
5819
+ ```
5820
+ ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_keyword'"
5821
+ ```
5822
+
5823
+ Mac
5824
+
5825
+ ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
5826
+
5827
+ Linux
5828
+
5829
+ ![glimmer-dsl-libui-linux-method-based-custom-keyword.png](images/glimmer-dsl-libui-linux-method-based-custom-keyword.png)
5830
+
5831
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
5482
5832
 
5833
+ ```ruby
5834
+ require 'glimmer-dsl-libui'
5835
+ require 'facets'
5836
+
5837
+ include Glimmer
5838
+
5839
+ Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
5840
+
5841
+ def field(model, property)
5842
+ property = property.to_s
5843
+ entry { |e|
5844
+ label property.underscore.split('_').map(&:capitalize).join(' ')
5845
+ text model.send(property).to_s
5846
+
5847
+ on_changed do
5848
+ model.send("#{property}=", e.text)
5849
+ end
5850
+ }
5851
+ end
5852
+
5853
+ def address_form(address)
5854
+ form {
5855
+ field(address, :street)
5856
+ field(address, :p_o_box)
5857
+ field(address, :city)
5858
+ field(address, :state)
5859
+ field(address, :zip_code)
5860
+ }
5861
+ end
5862
+
5863
+ def label_pair(model, attribute, value)
5864
+ name_label = nil
5865
+ value_label = nil
5866
+ horizontal_box {
5867
+ name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
5868
+ value_label = label(value.to_s)
5869
+ }
5870
+ Glimmer::DataBinding::Observer.proc do
5871
+ value_label.text = model.send(attribute)
5872
+ end.observe(model, attribute)
5873
+ end
5874
+
5875
+ def address(address)
5876
+ vertical_box {
5877
+ address.each_pair do |attribute, value|
5878
+ label_pair(address, attribute, value)
5879
+ end
5880
+ }
5881
+ end
5882
+
5883
+ address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
5884
+ address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
5885
+
5886
+ window('Method-Based Custom Keyword') {
5887
+ margined true
5888
+
5889
+ horizontal_box {
5890
+ vertical_box {
5891
+ label('Address 1') {
5892
+ stretchy false
5893
+ }
5894
+ address_form(address1)
5895
+ horizontal_separator {
5896
+ stretchy false
5897
+ }
5898
+ label('Address 1 (Saved)') {
5899
+ stretchy false
5900
+ }
5901
+ address(address1)
5902
+ }
5903
+ vertical_separator {
5904
+ stretchy false
5905
+ }
5906
+ vertical_box {
5907
+ label('Address 2') {
5908
+ stretchy false
5909
+ }
5910
+ address_form(address2)
5911
+ horizontal_separator {
5912
+ stretchy false
5913
+ }
5914
+ label('Address 2 (Saved)') {
5915
+ stretchy false
5916
+ }
5917
+ address(address2)
5918
+ }
5919
+ }
5920
+ }.show
5483
5921
  ```
5484
5922
 
5485
5923
  ## Contributing to glimmer-dsl-libui