glimmer-dsl-libui 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.md CHANGED
@@ -80,50 +80,48 @@ require 'glimmer-dsl-libui'
80
80
  include Glimmer
81
81
 
82
82
  window('Area Gallery', 400, 400) {
83
- vertical_box {
84
- area {
85
- path { # declarative stable path
86
- square(0, 0, 100)
87
- square(100, 100, 400)
88
-
89
- fill r: 102, g: 102, b: 204
90
- }
91
- path { # declarative stable path
92
- rectangle(0, 100, 100, 400)
93
- rectangle(100, 0, 400, 100)
94
-
95
- fill r: 204, g: 102, b: 204
96
- }
97
- path { # declarative stable path
98
- figure(100, 100) {
99
- line(100, 400)
100
- line(400, 100)
101
- line(400, 400)
102
-
103
- closed true
104
- }
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)
105
101
 
106
- fill r: 202, g: 102, b: 104, a: 0.5
107
- stroke r: 0, g: 0, b: 0
102
+ closed true
108
103
  }
109
- path { # declarative stable path
110
- figure(0, 0) {
111
- bezier(200, 100, 100, 200, 400, 100)
112
- bezier(300, 100, 100, 300, 100, 400)
113
- bezier(100, 300, 300, 100, 400, 400)
114
104
 
115
- closed true
116
- }
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)
117
113
 
118
- fill r: 202, g: 102, b: 204, a: 0.5
119
- stroke thickness: 2, r: 0, g: 0, b: 0
114
+ closed true
120
115
  }
121
- path { # declarative stable path
122
- arc(200, 200, 90, 0, 360, false)
123
116
 
124
- fill r: 202, g: 102, b: 204, a: 0.5
125
- stroke thickness: 2, r: 0, g: 0, b: 0
126
- }
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
127
125
  }
128
126
  }
129
127
  }.show
@@ -186,6 +184,8 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
186
184
  - [Basic Area](#basic-area)
187
185
  - [Dynamic Area](#dynamic-area)
188
186
  - [Area Gallery](#area-gallery)
187
+ - [Histogram](#histogram)
188
+ - [Basic Transform](#basic-transform)
189
189
  - [Contributing to glimmer-dsl-libui](#contributing-to-glimmer-dsl-libui)
190
190
  - [Help](#help)
191
191
  - [Issues](#issues)
@@ -374,6 +374,7 @@ Control(Args) | Properties | Listeners
374
374
  `image_text_column(name as String)` | None | None
375
375
  `label(text as String)` | `text` (`String`) | None
376
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
377
378
  `menu(text as String)` | None | None
378
379
  `menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
379
380
  `multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
@@ -530,9 +531,9 @@ Learn more by checking out [examples](#examples).
530
531
 
531
532
  ### Area API
532
533
 
533
- The `area` control can be used in one of two ways:
534
- - 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. Paths proxy objects are preserved across redraws assuming there would be few stable paths (mostly for decorative reasons).
535
- - 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. Path proxy objects are destroyed (thrown-away) at the end of drawing, thus having less memory overhead for drawing thousands of dynamic paths.
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.
536
537
 
537
538
  Here is an example of a declarative `area` with a stable path (you may copy/paste in [`girb`](#girb-glimmer-irb)):
538
539
 
@@ -558,7 +559,7 @@ window('Basic Area', 400, 400) {
558
559
 
559
560
  ![glimmer-dsl-libui-mac-basic-area.png](images/glimmer-dsl-libui-mac-basic-area.png)
560
561
 
561
- 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)):
562
563
 
563
564
  ```ruby
564
565
  require 'glimmer-dsl-libui'
@@ -571,7 +572,7 @@ window('Basic Area', 400, 400) {
571
572
  vertical_box {
572
573
  area {
573
574
  on_draw do |area_draw_params|
574
- 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
575
576
  rectangle(0, 0, 400, 400)
576
577
 
577
578
  fill r: 102, g: 102, b: 204, a: 1.0
@@ -592,9 +593,85 @@ Available nested `path` shapes:
592
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)`
593
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)
594
595
 
595
- 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 paths.
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.
596
612
 
597
- To redraw an `area`, you may call `#queue_redraw_all` method.
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.
598
675
 
599
676
  ### Smart Defaults and Conventions
600
677
 
@@ -627,6 +704,8 @@ To redraw an `area`, you may call `#queue_redraw_all` method.
627
704
  - `area` paths are specified declaratively with figures underneath (e.g. `rectangle`) and `area` draw listener is automatically generated
628
705
  - Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
629
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.
630
709
 
631
710
  ### API Gotchas
632
711
 
@@ -810,7 +889,7 @@ window('hello world', 300, 200, true) {
810
889
  }.show
811
890
  ```
812
891
 
813
- [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):
814
893
 
815
894
  ```ruby
816
895
  require 'glimmer-dsl-libui'
@@ -2956,7 +3035,7 @@ window('Basic Area', 400, 400) {
2956
3035
  }.show
2957
3036
  ```
2958
3037
 
2959
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2:
3038
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (semi-declarative `on_draw` dynamic `path` approach):
2960
3039
 
2961
3040
  ```ruby
2962
3041
  require 'glimmer-dsl-libui'
@@ -2969,7 +3048,7 @@ window('Basic Area', 400, 400) {
2969
3048
  vertical_box {
2970
3049
  area {
2971
3050
  on_draw do |area_draw_params|
2972
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3051
+ path { # a dynamic path is added semi-declaratively inside on_draw block
2973
3052
  rectangle(0, 0, 400, 400)
2974
3053
 
2975
3054
  fill r: 102, g: 102, b: 204, a: 1.0
@@ -3099,7 +3178,7 @@ window('Dynamic Area', 240, 500) {
3099
3178
 
3100
3179
  @area = area {
3101
3180
  on_draw do |area_draw_params|
3102
- 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
3103
3182
  rectangle(@x_spinbox.value, @y_spinbox.value, @width_spinbox.value, @height_spinbox.value)
3104
3183
 
3105
3184
  fill r: @red_spinbox.value, g: @green_spinbox.value, b: @blue_spinbox.value, a: @alpha_spinbox.value / 100.0
@@ -3110,7 +3189,7 @@ window('Dynamic Area', 240, 500) {
3110
3189
  }.show
3111
3190
  ```
3112
3191
 
3113
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2:
3192
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (declarative stable `path` approach):
3114
3193
 
3115
3194
  ```ruby
3116
3195
  require 'glimmer-dsl-libui'
@@ -3244,21 +3323,192 @@ require 'glimmer-dsl-libui'
3244
3323
  include Glimmer
3245
3324
 
3246
3325
  window('Area Gallery', 400, 400) {
3247
- vertical_box {
3248
- area {
3249
- path { # declarative stable path
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
3250
3500
  square(0, 0, 100)
3251
3501
  square(100, 100, 400)
3252
3502
 
3253
3503
  fill r: 102, g: 102, b: 204
3254
3504
  }
3255
- path { # declarative stable path
3505
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3256
3506
  rectangle(0, 100, 100, 400)
3257
3507
  rectangle(100, 0, 400, 100)
3258
-
3508
+
3259
3509
  fill r: 204, g: 102, b: 204
3260
3510
  }
3261
- path { # declarative stable path
3511
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3262
3512
  figure(100, 100) {
3263
3513
  line(100, 400)
3264
3514
  line(400, 100)
@@ -3270,7 +3520,7 @@ window('Area Gallery', 400, 400) {
3270
3520
  fill r: 202, g: 102, b: 104, a: 0.5
3271
3521
  stroke r: 0, g: 0, b: 0
3272
3522
  }
3273
- path { # declarative stable path
3523
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3274
3524
  figure(0, 0) {
3275
3525
  bezier(200, 100, 100, 200, 400, 100)
3276
3526
  bezier(300, 100, 100, 300, 100, 400)
@@ -3282,18 +3532,18 @@ window('Area Gallery', 400, 400) {
3282
3532
  fill r: 202, g: 102, b: 204, a: 0.5
3283
3533
  stroke thickness: 2, r: 0, g: 0, b: 0
3284
3534
  }
3285
- path { # declarative stable path
3535
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3286
3536
  arc(200, 200, 90, 0, 360, false)
3287
3537
 
3288
3538
  fill r: 202, g: 102, b: 204, a: 0.5
3289
3539
  stroke thickness: 2, r: 0, g: 0, b: 0
3290
3540
  }
3291
- }
3541
+ end
3292
3542
  }
3293
3543
  }.show
3294
3544
  ```
3295
3545
 
3296
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2:
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):
3297
3547
 
3298
3548
  ```ruby
3299
3549
  require 'glimmer-dsl-libui'
@@ -3301,9 +3551,9 @@ require 'glimmer-dsl-libui'
3301
3551
  include Glimmer
3302
3552
 
3303
3553
  window('Area Gallery', 400, 400) {
3304
- vertical_box {
3305
- area {
3306
- path { # declarative stable path
3554
+ area {
3555
+ on_draw do |area_draw_params|
3556
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3307
3557
  square {
3308
3558
  x 0
3309
3559
  y 0
@@ -3317,7 +3567,7 @@ window('Area Gallery', 400, 400) {
3317
3567
 
3318
3568
  fill r: 102, g: 102, b: 204
3319
3569
  }
3320
- path { # declarative stable path
3570
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3321
3571
  rectangle {
3322
3572
  x 0
3323
3573
  y 100
@@ -3333,7 +3583,7 @@ window('Area Gallery', 400, 400) {
3333
3583
 
3334
3584
  fill r: 204, g: 102, b: 204
3335
3585
  }
3336
- path { # declarative stable path
3586
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3337
3587
  figure {
3338
3588
  x 100
3339
3589
  y 100
@@ -3357,7 +3607,7 @@ window('Area Gallery', 400, 400) {
3357
3607
  fill r: 202, g: 102, b: 104, a: 0.5
3358
3608
  stroke r: 0, g: 0, b: 0
3359
3609
  }
3360
- path { # declarative stable path
3610
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3361
3611
  figure {
3362
3612
  x 0
3363
3613
  y 0
@@ -3393,7 +3643,7 @@ window('Area Gallery', 400, 400) {
3393
3643
  fill r: 202, g: 102, b: 204, a: 0.5
3394
3644
  stroke thickness: 2, r: 0, g: 0, b: 0
3395
3645
  }
3396
- path { # declarative stable path
3646
+ path { # a dynamic path is added semi-declaratively inside on_draw block
3397
3647
  arc {
3398
3648
  x_center 200
3399
3649
  y_center 200
@@ -3406,183 +3656,346 @@ window('Area Gallery', 400, 400) {
3406
3656
  fill r: 202, g: 102, b: 204, a: 0.5
3407
3657
  stroke thickness: 2, r: 0, g: 0, b: 0
3408
3658
  }
3409
- }
3659
+ end
3410
3660
  }
3411
3661
  }.show
3412
3662
  ```
3413
3663
 
3414
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3:
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:
3415
3689
 
3416
3690
  ```ruby
3417
- require 'glimmer-dsl-libui'
3691
+ # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
3418
3692
 
3419
- include Glimmer
3693
+ require 'libui'
3420
3694
 
3421
- window('Area Gallery', 400, 400) {
3422
- vertical_box {
3423
- area {
3424
- on_draw do |area_draw_params|
3425
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3426
- square(0, 0, 100)
3427
- square(100, 100, 400)
3428
-
3429
- fill r: 102, g: 102, b: 204
3430
- }
3431
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3432
- rectangle(0, 100, 100, 400)
3433
- rectangle(100, 0, 400, 100)
3695
+ UI = LibUI
3434
3696
 
3435
- fill r: 204, g: 102, b: 204
3436
- }
3437
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3438
- figure(100, 100) {
3439
- line(100, 400)
3440
- line(400, 100)
3441
- line(400, 400)
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
3442
3716
 
3443
- closed true
3444
- }
3717
+ matrix = UI::FFI::DrawMatrix.malloc
3445
3718
 
3446
- fill r: 202, g: 102, b: 104, a: 0.5
3447
- stroke r: 0, g: 0, b: 0
3448
- }
3449
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3450
- figure(0, 0) {
3451
- bezier(200, 100, 100, 200, 400, 100)
3452
- bezier(300, 100, 100, 300, 100, 400)
3453
- bezier(100, 300, 300, 100, 400, 400)
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
3454
3722
 
3455
- closed true
3456
- }
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
3457
3729
 
3458
- fill r: 202, g: 102, b: 204, a: 0.5
3459
- stroke thickness: 2, r: 0, g: 0, b: 0
3460
- }
3461
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3462
- arc(200, 200, 90, 0, 360, false)
3730
+ data
3731
+ end
3463
3732
 
3464
- fill r: 202, g: 102, b: 204, a: 0.5
3465
- stroke thickness: 2, r: 0, g: 0, b: 0
3466
- }
3467
- end
3468
- }
3469
- }
3470
- }.show
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
3471
3880
  ```
3472
3881
 
3473
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 4:
3882
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3474
3883
 
3475
3884
  ```ruby
3885
+ # https://github.com/jamescook/libui-ruby/blob/master/example/histogram.rb
3886
+
3476
3887
  require 'glimmer-dsl-libui'
3477
3888
 
3478
3889
  include Glimmer
3479
3890
 
3480
- window('Area Gallery', 400, 400) {
3481
- vertical_box {
3482
- area {
3483
- on_draw do |area_draw_params|
3484
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3485
- square {
3486
- x 0
3487
- y 0
3488
- length 100
3489
- }
3490
- square {
3491
- x 100
3492
- y 100
3493
- length 400
3494
- }
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)
3495
3955
 
3496
- fill r: 102, g: 102, b: 204
3956
+ on_changed do
3957
+ @area.queue_redraw_all
3958
+ end
3497
3959
  }
3498
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3499
- rectangle {
3500
- x 0
3501
- y 100
3502
- width 100
3503
- height 400
3504
- }
3505
- rectangle {
3506
- x 100
3507
- y 0
3508
- width 400
3509
- height 100
3510
- }
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])
3511
3976
 
3512
- fill r: 204, g: 102, b: 204
3977
+ fill color: 0xFFFFFF
3513
3978
  }
3514
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3515
- figure {
3516
- x 100
3517
- y 100
3518
-
3519
- line {
3520
- x 100
3521
- y 400
3522
- }
3523
- line {
3524
- x 400
3525
- y 100
3526
- }
3527
- line {
3528
- x 400
3529
- y 400
3530
- }
3531
-
3532
- closed true
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)
3533
3986
  }
3534
-
3535
- fill r: 202, g: 102, b: 104, a: 0.5
3536
- stroke r: 0, g: 0, b: 0
3987
+
3988
+ stroke color: 0x000000, thickness: 2, miter_limit: 10
3537
3989
  }
3538
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3539
- figure {
3540
- x 0
3541
- y 0
3542
-
3543
- bezier {
3544
- c1_x 200
3545
- c1_y 100
3546
- c2_x 100
3547
- c2_y 200
3548
- end_x 400
3549
- end_y 100
3550
- }
3551
- bezier {
3552
- c1_x 300
3553
- c1_y 100
3554
- c2_x 100
3555
- c2_y 300
3556
- end_x 100
3557
- end_y 400
3558
- }
3559
- bezier {
3560
- c1_x 100
3561
- c1_y 300
3562
- c2_x 300
3563
- c2_y 100
3564
- end_x 400
3565
- end_y 400
3566
- }
3567
-
3568
- closed true
3569
- }
3570
-
3571
- fill r: 202, g: 102, b: 204, a: 0.5
3572
- stroke thickness: 2, r: 0, g: 0, b: 0
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)
3573
3994
  }
3574
- path(area_draw_params) { # a dynamic path is added semi-declaratively inside on_draw block
3575
- arc {
3576
- x_center 200
3577
- y_center 200
3578
- radius 90
3579
- start_angle 0
3580
- sweep 360
3581
- is_negative false
3582
- }
3583
-
3584
- fill r: 202, g: 102, b: 204, a: 0.5
3585
- stroke thickness: 2, r: 0, g: 0, b: 0
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)
3586
3999
  }
3587
4000
  end
3588
4001
  }
@@ -3590,6 +4003,62 @@ window('Area Gallery', 400, 400) {
3590
4003
  }.show
3591
4004
  ```
3592
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
+
3593
4062
  ## Contributing to glimmer-dsl-libui
3594
4063
 
3595
4064
  - Check out the latest master to make sure the feature hasn't been