glimmer-dsl-libui 0.1.1 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +1215 -30
  4. data/VERSION +1 -1
  5. data/examples/area_gallery.rb +50 -0
  6. data/examples/area_gallery2.rb +111 -0
  7. data/examples/area_gallery3.rb +52 -0
  8. data/examples/area_gallery4.rb +113 -0
  9. data/examples/basic_area2.rb +1 -1
  10. data/examples/basic_table_progress_bar.rb +13 -3
  11. data/examples/basic_transform.rb +27 -0
  12. data/examples/dynamic_area.rb +1 -1
  13. data/examples/dynamic_area2.rb +97 -0
  14. data/examples/histogram.rb +119 -0
  15. data/glimmer-dsl-libui.gemspec +0 -0
  16. data/lib/glimmer/dsl/libui/control_expression.rb +1 -1
  17. data/lib/glimmer/dsl/libui/dsl.rb +1 -0
  18. data/lib/glimmer/dsl/libui/property_expression.rb +5 -1
  19. data/lib/glimmer/dsl/libui/shape_expression.rb +56 -0
  20. data/lib/glimmer/libui/{rectangle_proxy.rb → arc.rb} +11 -26
  21. data/lib/glimmer/libui/area_proxy.rb +21 -11
  22. data/lib/glimmer/libui/bezier.rb +36 -0
  23. data/lib/glimmer/libui/box.rb +1 -1
  24. data/lib/glimmer/libui/color_button_proxy.rb +67 -15
  25. data/lib/glimmer/libui/control_proxy.rb +10 -14
  26. data/lib/glimmer/libui/figure.rb +52 -0
  27. data/lib/glimmer/libui/form_proxy.rb +1 -1
  28. data/lib/glimmer/libui/grid_proxy.rb +1 -1
  29. data/lib/glimmer/libui/line.rb +36 -0
  30. data/lib/glimmer/libui/matrix_proxy.rb +145 -0
  31. data/lib/glimmer/libui/parent.rb +36 -0
  32. data/lib/glimmer/libui/path_proxy.rb +35 -18
  33. data/lib/glimmer/libui/rectangle.rb +36 -0
  34. data/lib/glimmer/libui/shape.rb +143 -0
  35. data/lib/glimmer/libui/square.rb +36 -0
  36. data/lib/glimmer/libui/transformable.rb +72 -0
  37. data/lib/glimmer/libui/window_proxy.rb +8 -1
  38. data/lib/glimmer/libui.rb +50 -0
  39. data/lib/glimmer-dsl-libui.rb +1 -0
  40. metadata +23 -5
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.1.1
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.1.4
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)
@@ -19,7 +19,7 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
19
19
  - Scaffolding for new custom controls, apps, and gems
20
20
  - Native-Executable packaging on Mac, Windows, and Linux.
21
21
 
22
- Example:
22
+ Hello, World!
23
23
 
24
24
  ```ruby
25
25
  require 'glimmer-dsl-libui'
@@ -32,6 +32,106 @@ window('hello world').show
32
32
  ![glimmer-dsl-libui-mac-basic-window.png](images/glimmer-dsl-libui-mac-basic-window.png)
33
33
  ![glimmer-dsl-libui-linux-basic-window.png](images/glimmer-dsl-libui-linux-basic-window.png)
34
34
 
35
+ Basic Table Progress Bar
36
+
37
+ ```ruby
38
+ require 'glimmer-dsl-libui'
39
+
40
+ include Glimmer
41
+
42
+ data = [
43
+ ['task 1', 0],
44
+ ['task 2', 15],
45
+ ['task 3', 100],
46
+ ['task 4', 75],
47
+ ['task 5', -1],
48
+ ]
49
+
50
+ window('Task Progress', 300, 200) {
51
+ vertical_box {
52
+ table {
53
+ text_column('Task')
54
+ progress_bar_column('Progress')
55
+
56
+ cell_rows data # implicit data-binding
57
+ }
58
+
59
+ button('Mark All As Done') {
60
+ stretchy false
61
+
62
+ on_clicked do
63
+ data.each_with_index do |row_data, row|
64
+ data[row] = [row_data[0], 100] # automatically updates table due to implicit data-binding
65
+ end
66
+ end
67
+ }
68
+ }
69
+ }.show
70
+ ```
71
+
72
+ ![glimmer-dsl-libui-mac-basic-table-progress-bar.png](images/glimmer-dsl-libui-mac-basic-table-progress-bar.png)
73
+ ![glimmer-dsl-libui-linux-basic-table-progress-bar.png](images/glimmer-dsl-libui-linux-basic-table-progress-bar.png)
74
+
75
+ Area Gallery
76
+
77
+ ```ruby
78
+ require 'glimmer-dsl-libui'
79
+
80
+ include Glimmer
81
+
82
+ window('Area Gallery', 400, 400) {
83
+ area {
84
+ path { # declarative stable path
85
+ square(0, 0, 100)
86
+ square(100, 100, 400)
87
+
88
+ fill r: 102, g: 102, b: 204
89
+ }
90
+ path { # declarative stable path
91
+ rectangle(0, 100, 100, 400)
92
+ rectangle(100, 0, 400, 100)
93
+
94
+ fill r: 204, g: 102, b: 204
95
+ }
96
+ path { # declarative stable path
97
+ figure(100, 100) {
98
+ line(100, 400)
99
+ line(400, 100)
100
+ line(400, 400)
101
+
102
+ closed true
103
+ }
104
+
105
+ fill r: 202, g: 102, b: 104, a: 0.5
106
+ stroke r: 0, g: 0, b: 0
107
+ }
108
+ path { # declarative stable path
109
+ figure(0, 0) {
110
+ bezier(200, 100, 100, 200, 400, 100)
111
+ bezier(300, 100, 100, 300, 100, 400)
112
+ bezier(100, 300, 300, 100, 400, 400)
113
+
114
+ closed true
115
+ }
116
+
117
+ fill r: 202, g: 102, b: 204, a: 0.5
118
+ stroke thickness: 2, r: 0, g: 0, b: 0
119
+ }
120
+ path { # declarative stable path
121
+ arc(200, 200, 90, 0, 360, false)
122
+
123
+ fill r: 202, g: 102, b: 204, a: 0.5
124
+ stroke thickness: 2, r: 0, g: 0, b: 0
125
+ }
126
+ }
127
+ }.show
128
+ ```
129
+
130
+ ![glimmer-dsl-libui-mac-area-gallery.png](images/glimmer-dsl-libui-mac-area-gallery.png)
131
+ ![glimmer-dsl-libui-linux-area-gallery.png](images/glimmer-dsl-libui-linux-area-gallery.png)
132
+
133
+ [Check Out Many More Examples Over Here!](#examples)
134
+
35
135
  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.
36
136
 
37
137
  Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
@@ -43,9 +143,10 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
43
143
 
44
144
  ## Table of Contents
45
145
 
46
- - [Glimmer DSL for LibUI 0.1.1](#-glimmer-dsl-for-libui-011)
146
+ - [Glimmer DSL for LibUI 0.1.4](#-glimmer-dsl-for-libui-014)
47
147
  - [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts)
48
148
  - [Usage](#usage)
149
+ - [Girb (Glimmer IRB)](#girb-glimmer-irb)
49
150
  - [API](#api)
50
151
  - [Supported Controls](#supported-controls)
51
152
  - [Common Control Properties](#common-control-properties)
@@ -58,7 +159,6 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
58
159
  - [API Gotchas](#api-gotchas)
59
160
  - [Original API](#original-api)
60
161
  - [Glimmer Style Guide](#glimmer-style-guide)
61
- - [Girb (Glimmer IRB)](#girb-glimmer-irb)
62
162
  - [Examples](#examples)
63
163
  - [Basic Window](#basic-window)
64
164
  - [Basic Button](#basic-button)
@@ -83,6 +183,9 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
83
183
  - [Form Table](#form-table)
84
184
  - [Basic Area](#basic-area)
85
185
  - [Dynamic Area](#dynamic-area)
186
+ - [Area Gallery](#area-gallery)
187
+ - [Histogram](#histogram)
188
+ - [Basic Transform](#basic-transform)
86
189
  - [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
87
190
  - [Help](#help)
88
191
  - [Issues](#issues)
@@ -170,7 +273,7 @@ gem install glimmer-dsl-libui
170
273
  Or install via Bundler `Gemfile`:
171
274
 
172
275
  ```ruby
173
- gem 'glimmer-dsl-libui', '~> 0.1.1'
276
+ gem 'glimmer-dsl-libui', '~> 0.1.4'
174
277
  ```
175
278
 
176
279
  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.
@@ -197,6 +300,24 @@ end
197
300
  Application.new.launch
198
301
  ```
199
302
 
303
+ If you are new to [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), check out [Girb](#girb-glimmer-irb) and [Examples](#examples) to quickly learn through copy/paste. You may refer to the [API](#api) later on once you have gotten your feet wet with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) and need more detailed reference information.
304
+
305
+ ## Girb (Glimmer IRB)
306
+
307
+ You can run the `girb` command (`bin/girb` if you cloned the project locally) to do some quick and dirty experimentation and learning:
308
+
309
+ ```
310
+ girb
311
+ ```
312
+
313
+ This gives you `irb` with the `glimmer-dsl-libui` gem loaded and the `Glimmer` module mixed into the main object for easy experimentation with GUI.
314
+
315
+ ![glimmer-dsl-libui-girb.png](images/glimmer-dsl-libui-girb.png)
316
+
317
+ For a more advanced code editing tool, check out the [Meta-Example (The Example of Examples)](#examples).
318
+
319
+ Gotcha: On the Mac, when you close a window opened in `girb`, it remains open until you enter `exit` or open another GUI window.
320
+
200
321
  ## API
201
322
 
202
323
  Any control returned by a [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) keyword declaration can be introspected for its properties and updated via object-oriented attributes (standard Ruby `attr`/`attr=` or `set_attr`).
@@ -227,6 +348,8 @@ Control(Args) | Properties | Listeners
227
348
  ------------- | ---------- | ---------
228
349
  `about_menu_item` | None | `on_clicked`
229
350
  `area` | None | `on_draw`
351
+ `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
352
+ `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
230
353
  `button(text as String)` | `text` (`String`) | `on_clicked`
231
354
  `button_column(name as String)` | `enabled` (Boolean) | None
232
355
  `checkbox(text as String)` | `checked` (Boolean), `text` (`String`) | `on_toggled`
@@ -238,6 +361,7 @@ Control(Args) | Properties | Listeners
238
361
  `date_time_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`
239
362
  `editable_combobox` | `items` (`Array` of `String`), `text` (`String`) | `on_changed`
240
363
  `entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
364
+ `figure(x=nil as Numeric, y=nil as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `closed` (Boolean) | None
241
365
  `font_button` | `font` [read-only] (`Hash` of keys: `:family`, `:size`, `:weight`, `:italic`, `:stretch`), `family` as `String`, `size` as `Float`, `weight` as `Integer`, `italic` as `Integer`, `stretch` as `Integer` | `on_changed`
242
366
  `form` | `padded` (Boolean) | None
243
367
  `grid` | `padded` (Boolean) | None
@@ -249,6 +373,8 @@ Control(Args) | Properties | Listeners
249
373
  `image_column(name as String)` | None | None
250
374
  `image_text_column(name as String)` | None | None
251
375
  `label(text as String)` | `text` (`String`) | None
376
+ `line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
377
+ `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
252
378
  `menu(text as String)` | None | None
253
379
  `menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
254
380
  `multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
@@ -261,9 +387,10 @@ Control(Args) | Properties | Listeners
261
387
  `progress_bar_column(name as String)` | None | None
262
388
  `quit_menu_item` | None | `on_clicked`
263
389
  `radio_buttons` | `selected` (`Integer`) | `on_selected`
264
- `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | None | None
390
+ `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `width` (`Numeric`), `height` (`Numeric`) | None
265
391
  `slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
266
392
  `spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
393
+ `square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
267
394
  `tab` | `margined` (Boolean), `num_pages` (`Integer`) | None
268
395
  `tab_item(name as String)` | `index` [read-only] (`Integer`), `margined` (Boolean), `name` [read-only] (`String`) | None
269
396
  `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
@@ -308,6 +435,7 @@ Control(Args) | Properties | Listeners
308
435
  - `ControlProxy::image_proxies`: returns all instantiated `image` proxies in the application
309
436
  - `ControlProxy::main_window_proxy`: returns the first window proxy instantiated in the application
310
437
  - `ControlProxy#window_proxy`: returns the window proxy parent for a control
438
+ - `ControlProxy#content {...}`: re-opens control's content to add more nested controls or properties
311
439
 
312
440
  ### Table API
313
441
 
@@ -327,11 +455,85 @@ Note that the `cell_rows` property declaration results in "implicit data-binding
327
455
  - Inserting cell rows: Calling `Array#<<`, `Array#push`, `Array#prepend`, or any insertion/addition `Array` method automatically inserts rows in actual `table` control
328
456
  - Changing cell rows: Calling `Array#[]=`, `Array#map!`, or any update `Array` method automatically updates rows in actual `table` control
329
457
 
458
+ Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
459
+
460
+ ```ruby
461
+ require 'glimmer-dsl-libui'
462
+
463
+ include Glimmer
464
+
465
+ data = [
466
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
467
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
468
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
469
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
470
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
471
+ ]
472
+
473
+ window('Contacts', 600, 600) { |w|
474
+ margined true
475
+
476
+ vertical_box {
477
+ form {
478
+ stretchy false
479
+
480
+ @name_entry = entry {
481
+ label 'Name'
482
+ }
483
+ @email_entry = entry {
484
+ label 'Email'
485
+ }
486
+ @phone_entry = entry {
487
+ label 'Phone'
488
+ }
489
+ @city_entry = entry {
490
+ label 'City'
491
+ }
492
+ @state_entry = entry {
493
+ label 'State'
494
+ }
495
+ }
496
+
497
+ button('Save Contact') {
498
+ stretchy false
499
+
500
+ on_clicked do
501
+ new_row = [@name_entry.text, @email_entry.text, @phone_entry.text, @city_entry.text, @state_entry.text]
502
+ if new_row.include?('')
503
+ msg_box_error(w, 'Validation Error!', 'All fields are required! Please make sure to enter a value for all fields.')
504
+ else
505
+ data << new_row # automatically inserts a row into the table due to implicit data-binding
506
+ @name_entry.text = ''
507
+ @email_entry.text = ''
508
+ @phone_entry.text = ''
509
+ @city_entry.text = ''
510
+ @state_entry.text = ''
511
+ end
512
+ end
513
+ }
514
+
515
+ table {
516
+ text_column('Name')
517
+ text_column('Email')
518
+ text_column('Phone')
519
+ text_column('City')
520
+ text_column('State')
521
+
522
+ cell_rows data # implicit data-binding
523
+ }
524
+ }
525
+ }.show
526
+ ```
527
+
528
+ ![glimmer-dsl-libui-linux-form-table.png](images/glimmer-dsl-libui-linux-form-table.png)
529
+
530
+ Learn more by checking out [examples](#examples).
531
+
330
532
  ### Area API
331
533
 
332
- The `area` control can be used in one of two ways:
333
- - Declaratively via stable paths: useful for stable paths that will not change later on. Simply nest `path` and figures like `rectangle` and all drawing logic is generated automatically.
334
- - Semi-declaratively via on_draw listener dynamic paths: useful for more dynamic paths that will definitely change. Open an `on_draw` listener block and nest `path(area_draw_params)` and figures like `rectangle` and all drawing logic is generated automatically.
534
+ The `area` control is a canvas-like control for drawing paths that can be used in one of two ways:
535
+ - Declaratively via stable paths: useful for stable paths that will not change later on. Simply nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are preserved across redraws assuming there would be few stable paths (mostly for decorative reasons).
536
+ - Semi-declaratively via on_draw listener dynamic paths: useful for more dynamic paths that will definitely change. Open an `on_draw` listener block that receives a `area_draw_params` argument and nest `path` and figures like `rectangle` and all drawing logic is generated automatically. Path proxy objects are destroyed (thrown-away) at the end of drawing, thus having less memory overhead for drawing thousands of dynamic paths.
335
537
 
336
538
  Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
337
539
 
@@ -357,7 +559,7 @@ window('Basic Area', 400, 400) {
357
559
 
358
560
  ![glimmer-dsl-libui-mac-basic-area.png](images/glimmer-dsl-libui-mac-basic-area.png)
359
561
 
360
- Here is the same example using a semi-declarative `area` with `on_draw` listener and a dynamic path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
562
+ Here is the same example using a semi-declarative `area` with `on_draw` listener that receives a `area_draw_params` argument and a dynamic path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
361
563
 
362
564
  ```ruby
363
565
  require 'glimmer-dsl-libui'
@@ -370,7 +572,7 @@ window('Basic Area', 400, 400) {
370
572
  vertical_box {
371
573
  area {
372
574
  on_draw do |area_draw_params|
373
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
575
+ path { # a dynamic path is added semi-declaratively inside on_draw block
374
576
  rectangle(0, 0, 400, 400)
375
577
 
376
578
  fill r: 102, g: 102, b: 204, a: 1.0
@@ -383,7 +585,93 @@ window('Basic Area', 400, 400) {
383
585
 
384
586
  Check [examples/dynamic_area.rb](#dynamic-area) for a more detailed semi-declarative example.
385
587
 
386
- To redraw an `area`, you may call `#queue_redraw_all` method.
588
+ Available nested `path` shapes:
589
+ - `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)`
590
+ - `square(x as Numeric, y as Numeric, length as Numeric)`
591
+ - `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
592
+ - `line(x as Numeric, y as Numeric)`
593
+ - `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)`
594
+ - `figure(x=nil as Numeric, y=nil as Numeric)` (composite that can contain other shapes) (can set `closed true` to connect last point to first point automatically)
595
+
596
+ Check [examples/area_gallery.rb](#area-gallery) for an overiew of all `path` shapes.
597
+
598
+ The `area_draw_params` argument for `on_draw` block is a hash consisting of the following keys:
599
+ - `:context`: the drawing context object
600
+ - `:area_width`: area width
601
+ - `:area_height`: area height
602
+ - `:clip_x`: clip region top-left x coordinate
603
+ - `:clip_y`: clip region top-left y coordinate
604
+ - `:clip_width`: clip region width
605
+ - `:clip_height`: clip region height
606
+
607
+ In general, it is recommended to use declarative stable paths whenever feasible since they require less code and simpler maintenance. But, in more advanced cases, semi-declarative dynamic paths could be used instead, especially if there are thousands of dynamic paths that need maximum performance and low memory footprint.
608
+
609
+ To redraw an `area`, you may call the `#queue_redraw_all` method, or simply `#redraw`.
610
+
611
+ A transform `matrix` can be set on a path by building a `matrix(m11 = nil, m12 = nil, m21 = nil, m22 = nil, m31 = nil, m32 = nil) {operations}` proxy object and then setting via `transform` property, or alternatively by building and setting the matrix in one call to `transform(m11 = nil, m12 = nil, m21 = nil, m22 = nil, m31 = nil, m32 = nil) {operations}` passing it the matrix arguments and/or content operations.
612
+
613
+ When instantiating a `matrix` object, it always starts with identity matrix.
614
+
615
+ Here are the following operations that can be performed in a `matrix` body:
616
+ - `identity` [alias: `set_identity`]: resets matrix to identity matrix
617
+ - `translate(x as Numeric, y as Numeric)`
618
+ - `scale(x_center = 0 as Numeric, y_center = 0 as Numeric, x as Numeric, y as Numeric)`
619
+ - `skew(x = 0 as Numeric, y = 0 as Numeric, x_amount as Numeric, y_amount as Numeric)`
620
+ - `rotate(x = 0 as Numeric, y = 0 as Numeric, degrees as Numeric)`
621
+
622
+ Example of using transform matrix (you may copy/paste in [`girb`](#girb-glimmer-irb)):
623
+
624
+ ```ruby
625
+ require 'glimmer-dsl-libui'
626
+
627
+ include Glimmer
628
+
629
+ window('Basic Transform', 350, 350) {
630
+ area {
631
+ path {
632
+ square(0, 0, 350)
633
+
634
+ fill r: 255, g: 255, b: 0
635
+ }
636
+ 40.times do |n|
637
+ path {
638
+ square(0, 0, 100)
639
+
640
+ fill r: [255 - n*5, 0].max, g: [n*5, 255].min, b: 0, a: 0.5
641
+ stroke color: 0, thickness: 2
642
+ transform {
643
+ skew 0.15, 0.15
644
+ translate 50, 50
645
+ rotate 100, 100, -9 * n
646
+ scale 1.1, 1.1
647
+ }
648
+ }
649
+ end
650
+ }
651
+ }.show
652
+ ```
653
+
654
+ Keep in mind that this part could be written differently when there is a need to reuse the matrix:
655
+
656
+ ```ruby
657
+ transform {
658
+ translate 100, 100
659
+ rotate 100, 100, -9 * n
660
+ }
661
+ ```
662
+
663
+ Alternatively:
664
+
665
+ ```ruby
666
+ m1 = matrix {
667
+ translate 100, 100
668
+ rotate 100, 100, -9 * n
669
+ }
670
+ transform m1
671
+ # and then reuse m1 elsewhere too
672
+ ```
673
+
674
+ Note that `area`, `path`, and nested shapes are all truly declarative, meaning they do not care about the ordering of calls to `fill`, `stroke`, and `transform`. Furthermore, any transform that is applied is reversed at the end of the block, so you never have to worry about the ordering of `transform` calls among different paths. You simply set a transform on the `path`s that need it and it is guaranteed to be called before all its content is drawn, and then undone afterwards to avoid affecting later paths. Matrix `transform` can be set on an entire `area` too, applying to all nested `path`s.
387
675
 
388
676
  ### Smart Defaults and Conventions
389
677
 
@@ -414,11 +702,16 @@ To redraw an `area`, you may call `#queue_redraw_all` method.
414
702
  - `image` instances are automatically freed from memory after `window` is destroyed.
415
703
  - `image` `width` and `height` can be left off if it has one `image_part` only as they default to the same `width` and `height` of the `image_part`
416
704
  - `area` paths are specified declaratively with figures underneath (e.g. `rectangle`) and `area` draw listener is automatically generated
705
+ - Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
706
+ - Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
707
+ - All controls are protected from garbage collection until no longer needed (explicitly destroyed), so there is no need to worry about surprises.
708
+ - All resources are freed automatically once no longer needed or left to garbage collection.
417
709
 
418
710
  ### API Gotchas
419
711
 
420
712
  - 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`).
421
713
  - `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).
714
+ - 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.
422
715
 
423
716
  ### Original API
424
717
 
@@ -434,21 +727,9 @@ check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.
434
727
  - Control listeners are always declared starting with on_ prefix and affixing listener event method name afterwards in underscored lowercase form. Their multi-line blocks have a `do; end` style.
435
728
  - Pure logic multi-line blocks that do not constitute GUI DSL view elements have `do; end` style to clearly separate logic code from view code.
436
729
 
437
- ## Girb (Glimmer IRB)
438
-
439
- You can run the `girb` command (`bin/girb` if you cloned the project locally):
440
-
441
- ```
442
- girb
443
- ```
444
-
445
- This gives you `irb` with the `glimmer-dsl-libui` gem loaded and the `Glimmer` module mixed into the main object for easy experimentation with GUI.
446
-
447
- Gotcha: On the Mac, when you close a window opened in `girb`, it remains open until you enter `exit` or open another GUI window.
448
-
449
730
  ## Examples
450
731
 
451
- These examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) as well as brand new examples.
732
+ The following examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) as well as brand new examples.
452
733
 
453
734
  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.
454
735
 
@@ -608,7 +889,7 @@ window('hello world', 300, 200, true) {
608
889
  }.show
609
890
  ```
610
891
 
611
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2:
892
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (setting `window` properties instead of arguments):
612
893
 
613
894
  ```ruby
614
895
  require 'glimmer-dsl-libui'
@@ -2538,13 +2819,23 @@ data = [
2538
2819
  ['task 5', -1],
2539
2820
  ]
2540
2821
 
2541
- window('Task progress', 300, 200) {
2542
- horizontal_box {
2822
+ window('Task Progress', 300, 200) {
2823
+ vertical_box {
2543
2824
  table {
2544
2825
  text_column('Task')
2545
2826
  progress_bar_column('Progress')
2546
2827
 
2547
- cell_rows data
2828
+ cell_rows data # implicit data-binding
2829
+ }
2830
+
2831
+ button('Mark All As Done') {
2832
+ stretchy false
2833
+
2834
+ on_clicked do
2835
+ data.each_with_index do |row_data, row|
2836
+ data[row] = [row_data[0], 100] # automatically updates table due to implicit data-binding
2837
+ end
2838
+ end
2548
2839
  }
2549
2840
  }
2550
2841
  }.show
@@ -2744,6 +3035,30 @@ window('Basic Area', 400, 400) {
2744
3035
  }.show
2745
3036
  ```
2746
3037
 
3038
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (semi-declarative `on_draw` dynamic `path` approach):
3039
+
3040
+ ```ruby
3041
+ require 'glimmer-dsl-libui'
3042
+
3043
+ include Glimmer
3044
+
3045
+ window('Basic Area', 400, 400) {
3046
+ margined true
3047
+
3048
+ vertical_box {
3049
+ area {
3050
+ on_draw do |area_draw_params|
3051
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3052
+ rectangle(0, 0, 400, 400)
3053
+
3054
+ fill r: 102, g: 102, b: 204, a: 1.0
3055
+ }
3056
+ end
3057
+ }
3058
+ }
3059
+ }.show
3060
+ ```
3061
+
2747
3062
  ### Dynamic Area
2748
3063
 
2749
3064
  [examples/dynamic_area.rb](examples/dynamic_area.rb)
@@ -2863,7 +3178,7 @@ window('Dynamic Area', 240, 500) {
2863
3178
 
2864
3179
  @area = area {
2865
3180
  on_draw do |area_draw_params|
2866
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3181
+ path { # a dynamic path is added semi-declaratively inside on_draw block
2867
3182
  rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value)
2868
3183
 
2869
3184
  fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
@@ -2874,6 +3189,876 @@ window('Dynamic Area', 240, 500) {
2874
3189
  }.show
2875
3190
  ```
2876
3191
 
3192
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (declarative stable `path` approach):
3193
+
3194
+ ```ruby
3195
+ require 'glimmer-dsl-libui'
3196
+
3197
+ include Glimmer
3198
+
3199
+ window('Dynamic Area', 240, 600) {
3200
+ margined true
3201
+
3202
+ vertical_box {
3203
+ label('Rectangle Properties') {
3204
+ stretchy false
3205
+ }
3206
+
3207
+ form {
3208
+ stretchy false
3209
+
3210
+ @x_spinbox = spinbox(0, 1000) {
3211
+ label 'x'
3212
+ value 25
3213
+
3214
+ on_changed do
3215
+ @rectangle.x = @x_spinbox.value # updating properties automatically triggers area.queue_redraw_all
3216
+ end
3217
+ }
3218
+
3219
+ @y_spinbox = spinbox(0, 1000) {
3220
+ label 'y'
3221
+ value 25
3222
+
3223
+ on_changed do
3224
+ @rectangle.y = @y_spinbox.value # updating properties automatically triggers area.queue_redraw_all
3225
+ end
3226
+ }
3227
+
3228
+ @width_spinbox = spinbox(0, 1000) {
3229
+ label 'width'
3230
+ value 150
3231
+
3232
+ on_changed do
3233
+ @rectangle.width = @width_spinbox.value # updating properties automatically triggers area.queue_redraw_all
3234
+ end
3235
+ }
3236
+
3237
+ @height_spinbox = spinbox(0, 1000) {
3238
+ label 'height'
3239
+ value 150
3240
+
3241
+ on_changed do
3242
+ @rectangle.height = @height_spinbox.value # updating properties automatically triggers area.queue_redraw_all
3243
+ end
3244
+ }
3245
+
3246
+ @red_spinbox = spinbox(0, 255) {
3247
+ label 'red'
3248
+ value 102
3249
+
3250
+ on_changed do
3251
+ @path.fill[:r] = @red_spinbox.value # updating hash properties automatically triggers area.queue_redraw_all
3252
+ end
3253
+ }
3254
+
3255
+ @green_spinbox = spinbox(0, 255) {
3256
+ label 'green'
3257
+ value 102
3258
+
3259
+ on_changed do
3260
+ @path.fill[:g] = @green_spinbox.value # updating hash properties automatically triggers area.queue_redraw_all
3261
+ end
3262
+ }
3263
+
3264
+ @blue_spinbox = spinbox(0, 255) {
3265
+ label 'blue'
3266
+ value 204
3267
+
3268
+ on_changed do
3269
+ @path.fill[:b] = @blue_spinbox.value # updating hash properties automatically triggers area.queue_redraw_all
3270
+ end
3271
+ }
3272
+
3273
+ @alpha_spinbox = spinbox(0, 100) {
3274
+ label 'alpha'
3275
+ value 100
3276
+
3277
+ on_changed do
3278
+ @path.fill[:a] = @alpha_spinbox.value / 100.0 # updating hash properties automatically triggers area.queue_redraw_all
3279
+ end
3280
+ }
3281
+ }
3282
+
3283
+ area {
3284
+ @path = path { # stable path
3285
+ @rectangle = rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value)
3286
+
3287
+ fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
3288
+ }
3289
+ }
3290
+ }
3291
+ }.show
3292
+ ```
3293
+
3294
+ ### Area Gallery
3295
+
3296
+ [examples/area_gallery.rb](examples/area_gallery.rb)
3297
+
3298
+ Run with this command from the root of the project if you cloned the project:
3299
+
3300
+ ```
3301
+ ruby -r './lib/glimmer-dsl-libui' examples/area_gallery.rb
3302
+ ```
3303
+
3304
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
3305
+
3306
+ ```
3307
+ ruby -r glimmer-dsl-libui -e "require 'examples/area_gallery'"
3308
+ ```
3309
+
3310
+ Mac
3311
+
3312
+ ![glimmer-dsl-libui-mac-area-gallery.png](images/glimmer-dsl-libui-mac-area-gallery.png)
3313
+
3314
+ Linux
3315
+
3316
+ ![glimmer-dsl-libui-linux-area-gallery.png](images/glimmer-dsl-libui-linux-area-gallery.png)
3317
+
3318
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3319
+
3320
+ ```ruby
3321
+ require 'glimmer-dsl-libui'
3322
+
3323
+ include Glimmer
3324
+
3325
+ window('Area Gallery', 400, 400) {
3326
+ area {
3327
+ path { # declarative stable path
3328
+ square(0, 0, 100)
3329
+ square(100, 100, 400)
3330
+
3331
+ fill r: 102, g: 102, b: 204
3332
+ }
3333
+ path { # declarative stable path
3334
+ rectangle(0, 100, 100, 400)
3335
+ rectangle(100, 0, 400, 100)
3336
+
3337
+ fill r: 204, g: 102, b: 204
3338
+ }
3339
+ path { # declarative stable path
3340
+ figure(100, 100) {
3341
+ line(100, 400)
3342
+ line(400, 100)
3343
+ line(400, 400)
3344
+
3345
+ closed true
3346
+ }
3347
+
3348
+ fill r: 202, g: 102, b: 104, a: 0.5
3349
+ stroke r: 0, g: 0, b: 0
3350
+ }
3351
+ path { # declarative stable path
3352
+ figure(0, 0) {
3353
+ bezier(200, 100, 100, 200, 400, 100)
3354
+ bezier(300, 100, 100, 300, 100, 400)
3355
+ bezier(100, 300, 300, 100, 400, 400)
3356
+
3357
+ closed true
3358
+ }
3359
+
3360
+ fill r: 202, g: 102, b: 204, a: 0.5
3361
+ stroke thickness: 2, r: 0, g: 0, b: 0
3362
+ }
3363
+ path { # declarative stable path
3364
+ arc(200, 200, 90, 0, 360, false)
3365
+
3366
+ fill r: 202, g: 102, b: 204, a: 0.5
3367
+ stroke thickness: 2, r: 0, g: 0, b: 0
3368
+ }
3369
+ }
3370
+ }.show
3371
+ ```
3372
+
3373
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (setting shape properties instead of arguments):
3374
+
3375
+ ```ruby
3376
+ require 'glimmer-dsl-libui'
3377
+
3378
+ include Glimmer
3379
+
3380
+ window('Area Gallery', 400, 400) {
3381
+ area {
3382
+ path { # declarative stable path
3383
+ square {
3384
+ x 0
3385
+ y 0
3386
+ length 100
3387
+ }
3388
+ square {
3389
+ x 100
3390
+ y 100
3391
+ length 400
3392
+ }
3393
+
3394
+ fill r: 102, g: 102, b: 204
3395
+ }
3396
+ path { # declarative stable path
3397
+ rectangle {
3398
+ x 0
3399
+ y 100
3400
+ width 100
3401
+ height 400
3402
+ }
3403
+ rectangle {
3404
+ x 100
3405
+ y 0
3406
+ width 400
3407
+ height 100
3408
+ }
3409
+
3410
+ fill r: 204, g: 102, b: 204
3411
+ }
3412
+ path { # declarative stable path
3413
+ figure {
3414
+ x 100
3415
+ y 100
3416
+
3417
+ line {
3418
+ x 100
3419
+ y 400
3420
+ }
3421
+ line {
3422
+ x 400
3423
+ y 100
3424
+ }
3425
+ line {
3426
+ x 400
3427
+ y 400
3428
+ }
3429
+
3430
+ closed true
3431
+ }
3432
+
3433
+ fill r: 202, g: 102, b: 104, a: 0.5
3434
+ stroke r: 0, g: 0, b: 0
3435
+ }
3436
+ path { # declarative stable path
3437
+ figure {
3438
+ x 0
3439
+ y 0
3440
+
3441
+ bezier {
3442
+ c1_x 200
3443
+ c1_y 100
3444
+ c2_x 100
3445
+ c2_y 200
3446
+ end_x 400
3447
+ end_y 100
3448
+ }
3449
+ bezier {
3450
+ c1_x 300
3451
+ c1_y 100
3452
+ c2_x 100
3453
+ c2_y 300
3454
+ end_x 100
3455
+ end_y 400
3456
+ }
3457
+ bezier {
3458
+ c1_x 100
3459
+ c1_y 300
3460
+ c2_x 300
3461
+ c2_y 100
3462
+ end_x 400
3463
+ end_y 400
3464
+ }
3465
+
3466
+ closed true
3467
+ }
3468
+
3469
+ fill r: 202, g: 102, b: 204, a: 0.5
3470
+ stroke thickness: 2, r: 0, g: 0, b: 0
3471
+ }
3472
+ path { # declarative stable path
3473
+ arc {
3474
+ x_center 200
3475
+ y_center 200
3476
+ radius 90
3477
+ start_angle 0
3478
+ sweep 360
3479
+ is_negative false
3480
+ }
3481
+
3482
+ fill r: 202, g: 102, b: 204, a: 0.5
3483
+ stroke thickness: 2, r: 0, g: 0, b: 0
3484
+ }
3485
+ }
3486
+ }.show
3487
+ ```
3488
+
3489
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (semi-declarative `on_draw` dynamic `path` approach):
3490
+
3491
+ ```ruby
3492
+ require 'glimmer-dsl-libui'
3493
+
3494
+ include Glimmer
3495
+
3496
+ window('Area Gallery', 400, 400) {
3497
+ area {
3498
+ on_draw do |area_draw_params|
3499
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3500
+ square(0, 0, 100)
3501
+ square(100, 100, 400)
3502
+
3503
+ fill r: 102, g: 102, b: 204
3504
+ }
3505
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3506
+ rectangle(0, 100, 100, 400)
3507
+ rectangle(100, 0, 400, 100)
3508
+
3509
+ fill r: 204, g: 102, b: 204
3510
+ }
3511
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3512
+ figure(100, 100) {
3513
+ line(100, 400)
3514
+ line(400, 100)
3515
+ line(400, 400)
3516
+
3517
+ closed true
3518
+ }
3519
+
3520
+ fill r: 202, g: 102, b: 104, a: 0.5
3521
+ stroke r: 0, g: 0, b: 0
3522
+ }
3523
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3524
+ figure(0, 0) {
3525
+ bezier(200, 100, 100, 200, 400, 100)
3526
+ bezier(300, 100, 100, 300, 100, 400)
3527
+ bezier(100, 300, 300, 100, 400, 400)
3528
+
3529
+ closed true
3530
+ }
3531
+
3532
+ fill r: 202, g: 102, b: 204, a: 0.5
3533
+ stroke thickness: 2, r: 0, g: 0, b: 0
3534
+ }
3535
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3536
+ arc(200, 200, 90, 0, 360, false)
3537
+
3538
+ fill r: 202, g: 102, b: 204, a: 0.5
3539
+ stroke thickness: 2, r: 0, g: 0, b: 0
3540
+ }
3541
+ end
3542
+ }
3543
+ }.show
3544
+ ```
3545
+
3546
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4 (setting shape properties instead of arguments with semi-declarative `on_draw` dynamic `path` approach):
3547
+
3548
+ ```ruby
3549
+ require 'glimmer-dsl-libui'
3550
+
3551
+ include Glimmer
3552
+
3553
+ window('Area Gallery', 400, 400) {
3554
+ area {
3555
+ on_draw do |area_draw_params|
3556
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3557
+ square {
3558
+ x 0
3559
+ y 0
3560
+ length 100
3561
+ }
3562
+ square {
3563
+ x 100
3564
+ y 100
3565
+ length 400
3566
+ }
3567
+
3568
+ fill r: 102, g: 102, b: 204
3569
+ }
3570
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3571
+ rectangle {
3572
+ x 0
3573
+ y 100
3574
+ width 100
3575
+ height 400
3576
+ }
3577
+ rectangle {
3578
+ x 100
3579
+ y 0
3580
+ width 400
3581
+ height 100
3582
+ }
3583
+
3584
+ fill r: 204, g: 102, b: 204
3585
+ }
3586
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3587
+ figure {
3588
+ x 100
3589
+ y 100
3590
+
3591
+ line {
3592
+ x 100
3593
+ y 400
3594
+ }
3595
+ line {
3596
+ x 400
3597
+ y 100
3598
+ }
3599
+ line {
3600
+ x 400
3601
+ y 400
3602
+ }
3603
+
3604
+ closed true
3605
+ }
3606
+
3607
+ fill r: 202, g: 102, b: 104, a: 0.5
3608
+ stroke r: 0, g: 0, b: 0
3609
+ }
3610
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3611
+ figure {
3612
+ x 0
3613
+ y 0
3614
+
3615
+ bezier {
3616
+ c1_x 200
3617
+ c1_y 100
3618
+ c2_x 100
3619
+ c2_y 200
3620
+ end_x 400
3621
+ end_y 100
3622
+ }
3623
+ bezier {
3624
+ c1_x 300
3625
+ c1_y 100
3626
+ c2_x 100
3627
+ c2_y 300
3628
+ end_x 100
3629
+ end_y 400
3630
+ }
3631
+ bezier {
3632
+ c1_x 100
3633
+ c1_y 300
3634
+ c2_x 300
3635
+ c2_y 100
3636
+ end_x 400
3637
+ end_y 400
3638
+ }
3639
+
3640
+ closed true
3641
+ }
3642
+
3643
+ fill r: 202, g: 102, b: 204, a: 0.5
3644
+ stroke thickness: 2, r: 0, g: 0, b: 0
3645
+ }
3646
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3647
+ arc {
3648
+ x_center 200
3649
+ y_center 200
3650
+ radius 90
3651
+ start_angle 0
3652
+ sweep 360
3653
+ is_negative false
3654
+ }
3655
+
3656
+ fill r: 202, g: 102, b: 204, a: 0.5
3657
+ stroke thickness: 2, r: 0, g: 0, b: 0
3658
+ }
3659
+ end
3660
+ }
3661
+ }.show
3662
+ ```
3663
+
3664
+ ### Histogram
3665
+
3666
+ [examples/histogram.rb](examples/histogram.rb)
3667
+
3668
+ Run with this command from the root of the project if you cloned the project:
3669
+
3670
+ ```
3671
+ ruby -r './lib/glimmer-dsl-libui' examples/histogram.rb
3672
+ ```
3673
+
3674
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
3675
+
3676
+ ```
3677
+ ruby -r glimmer-dsl-libui -e "require 'examples/histogram'"
3678
+ ```
3679
+
3680
+ Mac
3681
+
3682
+ ![glimmer-dsl-libui-mac-histogram.png](images/glimmer-dsl-libui-mac-histogram.png)
3683
+
3684
+ Linux
3685
+
3686
+ ![glimmer-dsl-libui-linux-histogram.png](images/glimmer-dsl-libui-linux-histogram.png)
3687
+
3688
+ [LibUI](https://github.com/kojix2/LibUI) Original Version:
3689
+
3690
+ ```ruby
3691
+ # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
3692
+
3693
+ require 'libui'
3694
+
3695
+ UI = LibUI
3696
+
3697
+ X_OFF_LEFT = 20
3698
+ Y_OFF_TOP = 20
3699
+ X_OFF_RIGHT = 20
3700
+ Y_OFF_BOTTOM = 20
3701
+ POINT_RADIUS = 5
3702
+
3703
+ init = UI.init
3704
+ handler = UI::FFI::AreaHandler.malloc
3705
+ histogram = UI.new_area(handler)
3706
+ brush = UI::FFI::DrawBrush.malloc
3707
+ color_button = UI.new_color_button
3708
+ blue = 0x1E90FF
3709
+ datapoints = []
3710
+
3711
+ def graph_size(area_width, area_height)
3712
+ graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
3713
+ graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
3714
+ [graph_width, graph_height]
3715
+ end
3716
+
3717
+ matrix = UI::FFI::DrawMatrix.malloc
3718
+
3719
+ def point_locations(datapoints, width, height)
3720
+ xincr = width / 9.0 # 10 - 1 to make the last point be at the end
3721
+ yincr = height / 100.0
3722
+
3723
+ data = []
3724
+ datapoints.each_with_index do |dp, i|
3725
+ val = 100 - UI.spinbox_value(dp)
3726
+ data << [xincr * i, yincr * val]
3727
+ i += 1
3728
+ end
3729
+
3730
+ data
3731
+ end
3732
+
3733
+ def construct_graph(datapoints, width, height, should_extend)
3734
+ locations = point_locations(datapoints, width, height)
3735
+ path = UI.draw_new_path(0) # winding
3736
+ first_location = locations[0] # x and y
3737
+ UI.draw_path_new_figure(path, first_location[0], first_location[1])
3738
+ locations.each do |loc|
3739
+ UI.draw_path_line_to(path, loc[0], loc[1])
3740
+ end
3741
+
3742
+ if should_extend
3743
+ UI.draw_path_line_to(path, width, height)
3744
+ UI.draw_path_line_to(path, 0, height)
3745
+ UI.draw_path_close_figure(path)
3746
+ end
3747
+
3748
+ UI.draw_path_end(path)
3749
+
3750
+ path
3751
+ end
3752
+
3753
+ handler_draw_event = Fiddle::Closure::BlockCaller.new(
3754
+ 0, [1, 1, 1]
3755
+ ) do |_area_handler, _area, area_draw_params|
3756
+ area_draw_params = UI::FFI::AreaDrawParams.new(area_draw_params)
3757
+ path = UI.draw_new_path(0) # winding
3758
+ UI.draw_path_add_rectangle(path, 0, 0, area_draw_params.AreaWidth, area_draw_params.AreaHeight)
3759
+ UI.draw_path_end(path)
3760
+ set_solid_brush(brush, 0xFFFFFF, 1.0) # white
3761
+ UI.draw_fill(area_draw_params.Context, path, brush.to_ptr)
3762
+ UI.draw_free_path(path)
3763
+ dsp = UI::FFI::DrawStrokeParams.malloc
3764
+ dsp.Cap = 0 # flat
3765
+ dsp.Join = 0 # miter
3766
+ dsp.Thickness = 2
3767
+ dsp.MiterLimit = 10 # DEFAULT_MITER_LIMIT
3768
+ dashes = Fiddle::Pointer.malloc(8)
3769
+ dsp.Dashes = dashes
3770
+ dsp.NumDashes = 0
3771
+ dsp.DashPhase = 0
3772
+
3773
+ # draw axes
3774
+ set_solid_brush(brush, 0x000000, 1.0) # black
3775
+ graph_width, graph_height = *graph_size(area_draw_params.AreaWidth, area_draw_params.AreaHeight)
3776
+
3777
+ path = UI.draw_new_path(0) # winding
3778
+ UI.draw_path_new_figure(path, X_OFF_LEFT, Y_OFF_TOP)
3779
+ UI.draw_path_line_to(path, X_OFF_LEFT, Y_OFF_TOP + graph_height)
3780
+ UI.draw_path_line_to(path, X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
3781
+ UI.draw_path_end(path)
3782
+ UI.draw_stroke(area_draw_params.Context, path, brush, dsp)
3783
+ UI.draw_free_path(path)
3784
+
3785
+ # now transform the coordinate space so (0, 0) is the top-left corner of the graph
3786
+ UI.draw_matrix_set_identity(matrix)
3787
+ UI.draw_matrix_translate(matrix, X_OFF_LEFT, Y_OFF_TOP)
3788
+ UI.draw_transform(area_draw_params.Context, matrix)
3789
+
3790
+ # now get the color for the graph itself and set up the brush
3791
+ # uiColorButtonColor(colorButton, &graphR, &graphG, &graphB, &graphA)
3792
+ graph_r = Fiddle::Pointer.malloc(8) # double
3793
+ graph_g = Fiddle::Pointer.malloc(8) # double
3794
+ graph_b = Fiddle::Pointer.malloc(8) # double
3795
+ graph_a = Fiddle::Pointer.malloc(8) # double
3796
+
3797
+ UI.color_button_color(color_button, graph_r, graph_g, graph_b, graph_a)
3798
+ brush.Type = 0 # solid
3799
+ brush.R = graph_r[0, 8].unpack1('d')
3800
+ brush.G = graph_g[0, 8].unpack1('d')
3801
+ brush.B = graph_b[0, 8].unpack1('d')
3802
+
3803
+ # now create the fill for the graph below the graph line
3804
+ path = construct_graph(datapoints, graph_width, graph_height, true)
3805
+ brush.A = graph_a[0, 8].unpack1('d') / 2.0
3806
+ UI.draw_fill(area_draw_params.Context, path, brush)
3807
+ UI.draw_free_path(path)
3808
+
3809
+ # now draw the histogram line
3810
+ path = construct_graph(datapoints, graph_width, graph_height, false)
3811
+ brush.A = graph_a[0, 8].unpack1('d')
3812
+ UI.draw_stroke(area_draw_params.Context, path, brush, dsp)
3813
+ UI.draw_free_path(path)
3814
+ end
3815
+
3816
+ handler.Draw = handler_draw_event
3817
+
3818
+ # Assigning to local variables
3819
+ # This is intended to protect Fiddle::Closure from garbage collection.
3820
+ # See https://github.com/kojix2/LibUI/issues/8
3821
+ handler.MouseEvent = (c1 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
3822
+ handler.MouseCrossed = (c2 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
3823
+ handler.DragBroken = (c3 = Fiddle::Closure::BlockCaller.new(0, [0]) {})
3824
+ handler.KeyEvent = (c4 = Fiddle::Closure::BlockCaller.new(1, [0]) { 0 })
3825
+
3826
+ UI.freeInitError(init) unless init.nil?
3827
+
3828
+ hbox = UI.new_horizontal_box
3829
+ UI.box_set_padded(hbox, 1)
3830
+
3831
+ vbox = UI.new_vertical_box
3832
+ UI.box_set_padded(vbox, 1)
3833
+ UI.box_append(hbox, vbox, 0)
3834
+ UI.box_append(hbox, histogram, 1)
3835
+
3836
+ datapoints = Array.new(10) do
3837
+ UI.new_spinbox(0, 100).tap do |datapoint|
3838
+ UI.spinbox_set_value(datapoint, Random.new.rand(90))
3839
+ UI.spinbox_on_changed(datapoint) do
3840
+ UI.area_queue_redraw_all(histogram)
3841
+ end
3842
+ UI.box_append(vbox, datapoint, 0)
3843
+ end
3844
+ end
3845
+
3846
+ def set_solid_brush(brush, color, alpha)
3847
+ brush.Type = 0 # solid
3848
+ brush.R = ((color >> 16) & 0xFF) / 255.0
3849
+ brush.G = ((color >> 8) & 0xFF) / 255.0
3850
+ brush.B = (color & 0xFF) / 255.0
3851
+ brush.A = alpha
3852
+ brush
3853
+ end
3854
+
3855
+ set_solid_brush(brush, blue, 1.0)
3856
+ UI.color_button_set_color(color_button, brush.R, brush.G, brush.B, brush.A)
3857
+
3858
+ UI.color_button_on_changed(color_button) do
3859
+ UI.area_queue_redraw_all(histogram)
3860
+ end
3861
+
3862
+ UI.box_append(vbox, color_button, 0)
3863
+
3864
+ MAIN_WINDOW = UI.new_window('histogram example', 640, 480, 1)
3865
+ UI.window_set_margined(MAIN_WINDOW, 1)
3866
+ UI.window_set_child(MAIN_WINDOW, hbox)
3867
+
3868
+ should_quit = proc do |_ptr|
3869
+ UI.control_destroy(MAIN_WINDOW)
3870
+ UI.quit
3871
+ 0
3872
+ end
3873
+
3874
+ UI.window_on_closing(MAIN_WINDOW, should_quit)
3875
+ UI.on_should_quit(should_quit)
3876
+ UI.control_show(MAIN_WINDOW)
3877
+
3878
+ UI.main
3879
+ UI.quit
3880
+ ```
3881
+
3882
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3883
+
3884
+ ```ruby
3885
+ # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
3886
+
3887
+ require 'glimmer-dsl-libui'
3888
+
3889
+ include Glimmer
3890
+
3891
+ X_OFF_LEFT = 20
3892
+ Y_OFF_TOP = 20
3893
+ X_OFF_RIGHT = 20
3894
+ Y_OFF_BOTTOM = 20
3895
+ POINT_RADIUS = 5
3896
+
3897
+ COLOR_BLUE = 0x1E90FF
3898
+
3899
+ def graph_size(area_width, area_height)
3900
+ graph_width = area_width - X_OFF_LEFT - X_OFF_RIGHT
3901
+ graph_height = area_height - Y_OFF_TOP - Y_OFF_BOTTOM
3902
+ [graph_width, graph_height]
3903
+ end
3904
+
3905
+ def point_locations(datapoints, width, height)
3906
+ xincr = width / 9.0 # 10 - 1 to make the last point be at the end
3907
+ yincr = height / 100.0
3908
+
3909
+ data = []
3910
+ datapoints.each_with_index do |dp, i|
3911
+ val = 100 - dp.value
3912
+ data << [xincr * i, yincr * val]
3913
+ i += 1
3914
+ end
3915
+
3916
+ data
3917
+ end
3918
+
3919
+ def graph_path(datapoints, width, height, should_extend, &block)
3920
+ locations = point_locations(datapoints, width, height)
3921
+ path {
3922
+ first_location = locations[0] # x and y
3923
+ figure(first_location[0], first_location[1]) {
3924
+ locations.each do |loc|
3925
+ line(loc[0], loc[1])
3926
+ end
3927
+ if should_extend
3928
+ line(width, height)
3929
+ line(0, height)
3930
+
3931
+ closed true
3932
+ end
3933
+ }
3934
+
3935
+ # now transform the coordinate space so (0, 0) is the top-left corner of the graph
3936
+ transform {
3937
+ translate X_OFF_LEFT, Y_OFF_TOP
3938
+ }
3939
+
3940
+ block.call
3941
+ }
3942
+ end
3943
+
3944
+ window('histogram example', 640, 480) {
3945
+ margined true
3946
+
3947
+ horizontal_box {
3948
+ vertical_box {
3949
+ stretchy false
3950
+
3951
+ @datapoints = 10.times.map do
3952
+ spinbox(0, 100) { |datapoint|
3953
+ stretchy false
3954
+ value Random.new.rand(90)
3955
+
3956
+ on_changed do
3957
+ @area.queue_redraw_all
3958
+ end
3959
+ }
3960
+ end
3961
+
3962
+ @color_button = color_button {
3963
+ stretchy false
3964
+ color COLOR_BLUE
3965
+
3966
+ on_changed do
3967
+ @area.queue_redraw_all
3968
+ end
3969
+ }
3970
+ }
3971
+
3972
+ @area = area {
3973
+ on_draw do |area_draw_params|
3974
+ path {
3975
+ rectangle(0, 0, area_draw_params[:area_width], area_draw_params[:area_height])
3976
+
3977
+ fill color: 0xFFFFFF
3978
+ }
3979
+
3980
+ graph_width, graph_height = *graph_size(area_draw_params[:area_width], area_draw_params[:area_height])
3981
+
3982
+ path {
3983
+ figure(X_OFF_LEFT, Y_OFF_TOP) {
3984
+ line(X_OFF_LEFT, Y_OFF_TOP + graph_height)
3985
+ line(X_OFF_LEFT + graph_width, Y_OFF_TOP + graph_height)
3986
+ }
3987
+
3988
+ stroke color: 0x000000, thickness: 2, miter_limit: 10
3989
+ }
3990
+
3991
+ # now create the fill for the graph below the graph line
3992
+ graph_path(@datapoints, graph_width, graph_height, true) {
3993
+ fill @color_button.color.merge(a: 0.5)
3994
+ }
3995
+
3996
+ # now draw the histogram line
3997
+ graph_path(@datapoints, graph_width, graph_height, false) {
3998
+ stroke @color_button.color.merge(thickness: 2, miter_limit: 10)
3999
+ }
4000
+ end
4001
+ }
4002
+ }
4003
+ }.show
4004
+ ```
4005
+
4006
+ ### Basic Transform
4007
+
4008
+ [examples/basic_transform.rb](examples/basic_transform.rb)
4009
+
4010
+ Run with this command from the root of the project if you cloned the project:
4011
+
4012
+ ```
4013
+ ruby -r './lib/glimmer-dsl-libui' examples/basic_transform.rb
4014
+ ```
4015
+
4016
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
4017
+
4018
+ ```
4019
+ ruby -r glimmer-dsl-libui -e "require 'examples/basic_transform'"
4020
+ ```
4021
+
4022
+ Mac
4023
+
4024
+ ![glimmer-dsl-libui-mac-basic-transform.png](images/glimmer-dsl-libui-mac-basic-transform.png)
4025
+
4026
+ Linux
4027
+
4028
+ ![glimmer-dsl-libui-linux-basic-transform.png](images/glimmer-dsl-libui-linux-basic-transform.png)
4029
+
4030
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
4031
+
4032
+ ```ruby
4033
+ require 'glimmer-dsl-libui'
4034
+
4035
+ include Glimmer
4036
+
4037
+ window('Basic Transform', 350, 350) {
4038
+ area {
4039
+ path {
4040
+ square(0, 0, 350)
4041
+
4042
+ fill r: 255, g: 255, b: 0
4043
+ }
4044
+ 40.times do |n|
4045
+ path {
4046
+ square(0, 0, 100)
4047
+
4048
+ fill r: [255 - n*5, 0].max, g: [n*5, 255].min, b: 0, a: 0.5
4049
+ stroke color: 0, thickness: 2
4050
+ transform {
4051
+ skew 0.15, 0.15
4052
+ translate 50, 50
4053
+ rotate 100, 100, -9 * n
4054
+ scale 1.1, 1.1
4055
+ }
4056
+ }
4057
+ end
4058
+ }
4059
+ }.show
4060
+ ```
4061
+
2877
4062
  ## Contributing to glimmer-dsl-libui
2878
4063
 
2879
4064
  - Check out the latest master to make sure the feature hasn't been