glimmer-dsl-libui 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
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