glimmer-dsl-libui 0.4.17 → 0.4.21

Sign up to get free protection for your applications and to get access to all the features.
Files changed (122) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +24 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +474 -75
  5. data/VERSION +1 -1
  6. data/bin/girb +0 -0
  7. data/bin/girb_runner.rb +1 -1
  8. data/examples/area_based_custom_controls.rb +282 -0
  9. data/examples/basic_table.rb +1 -1
  10. data/examples/custom_draw_text.rb +14 -7
  11. data/examples/custom_draw_text2.rb +15 -8
  12. data/examples/method_based_custom_keyword.rb +9 -9
  13. data/examples/method_based_custom_keyword2.rb +9 -9
  14. data/examples/snake/model/game.rb +18 -1
  15. data/examples/snake.rb +6 -2
  16. data/examples/snake2.rb +6 -2
  17. data/examples/tetris/model/block.rb +1 -1
  18. data/examples/tetris/model/game.rb +1 -1
  19. data/examples/tetris/model/past_game.rb +1 -1
  20. data/examples/tetris/model/tetromino.rb +1 -1
  21. data/examples/tetris.rb +1 -1
  22. data/examples/tic_tac_toe/board.rb +1 -1
  23. data/examples/tic_tac_toe/cell.rb +1 -1
  24. data/glimmer-dsl-libui.gemspec +0 -0
  25. data/lib/glimmer/dsl/libui/bind_expression.rb +1 -1
  26. data/lib/glimmer/dsl/libui/control_expression.rb +1 -1
  27. data/lib/glimmer/dsl/libui/data_binding_expression.rb +1 -1
  28. data/lib/glimmer/dsl/libui/dsl.rb +2 -1
  29. data/lib/glimmer/dsl/libui/file_expression.rb +1 -1
  30. data/lib/glimmer/dsl/libui/listener_expression.rb +1 -1
  31. data/lib/glimmer/dsl/libui/observe_expression.rb +2 -2
  32. data/lib/glimmer/dsl/libui/open_file_expression.rb +1 -1
  33. data/lib/glimmer/dsl/libui/operation_expression.rb +47 -0
  34. data/lib/glimmer/dsl/libui/property_expression.rb +3 -3
  35. data/lib/glimmer/dsl/libui/save_file_expression.rb +1 -1
  36. data/lib/glimmer/dsl/libui/shape_expression.rb +1 -1
  37. data/lib/glimmer/dsl/libui/shine_data_binding_expression.rb +1 -1
  38. data/lib/glimmer/dsl/libui/string_expression.rb +1 -1
  39. data/lib/glimmer/dsl/libui/tab_item_expression.rb +1 -1
  40. data/lib/glimmer/fiddle_consumer.rb +1 -1
  41. data/lib/glimmer/libui/attributed_string.rb +1 -1
  42. data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +1 -1
  43. data/lib/glimmer/libui/control_proxy/area_proxy.rb +1 -1
  44. data/lib/glimmer/libui/control_proxy/box/horizontal_box_proxy.rb +1 -1
  45. data/lib/glimmer/libui/control_proxy/box/vertical_box_proxy.rb +1 -1
  46. data/lib/glimmer/libui/control_proxy/box.rb +2 -1
  47. data/lib/glimmer/libui/control_proxy/button_proxy.rb +1 -1
  48. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +1 -1
  49. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -1
  50. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +1 -1
  51. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -1
  52. data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +1 -1
  53. data/lib/glimmer/libui/control_proxy/column/checkbox_text_color_column_proxy.rb +1 -1
  54. data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +1 -1
  55. data/lib/glimmer/libui/control_proxy/column/image_column_proxy.rb +1 -1
  56. data/lib/glimmer/libui/control_proxy/column/image_text_color_column_proxy.rb +1 -1
  57. data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +1 -1
  58. data/lib/glimmer/libui/control_proxy/column/progress_bar_column_proxy.rb +1 -1
  59. data/lib/glimmer/libui/control_proxy/column/text_color_column_proxy.rb +1 -1
  60. data/lib/glimmer/libui/control_proxy/column/text_column_proxy.rb +1 -1
  61. data/lib/glimmer/libui/control_proxy/column.rb +1 -1
  62. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -1
  63. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +1 -1
  64. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +1 -1
  65. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -1
  66. data/lib/glimmer/libui/control_proxy/dual_column.rb +1 -1
  67. data/lib/glimmer/libui/control_proxy/editable_column.rb +1 -1
  68. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -1
  69. data/lib/glimmer/libui/control_proxy/enableable_column.rb +1 -1
  70. data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +1 -1
  71. data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +1 -1
  72. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -1
  73. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +1 -1
  74. data/lib/glimmer/libui/control_proxy/form_proxy.rb +2 -1
  75. data/lib/glimmer/libui/control_proxy/grid_proxy.rb +1 -1
  76. data/lib/glimmer/libui/control_proxy/group_proxy.rb +1 -1
  77. data/lib/glimmer/libui/control_proxy/image_part_proxy.rb +1 -1
  78. data/lib/glimmer/libui/control_proxy/image_proxy.rb +1 -1
  79. data/lib/glimmer/libui/control_proxy/label_proxy.rb +1 -1
  80. data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +1 -1
  81. data/lib/glimmer/libui/control_proxy/menu_item_proxy/about_menu_item_proxy.rb +1 -1
  82. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -1
  83. data/lib/glimmer/libui/control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb +1 -1
  84. data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +1 -2
  85. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -1
  86. data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +1 -1
  87. data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +1 -1
  88. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +1 -1
  89. data/lib/glimmer/libui/control_proxy/message_box/msg_box_error_proxy.rb +1 -1
  90. data/lib/glimmer/libui/control_proxy/message_box/msg_box_proxy.rb +1 -1
  91. data/lib/glimmer/libui/control_proxy/message_box.rb +1 -1
  92. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb +1 -1
  93. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -1
  94. data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +1 -1
  95. data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +1 -1
  96. data/lib/glimmer/libui/control_proxy/path_proxy.rb +7 -5
  97. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -1
  98. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -1
  99. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -1
  100. data/lib/glimmer/libui/control_proxy/tab_item_proxy.rb +1 -1
  101. data/lib/glimmer/libui/control_proxy/table_proxy.rb +48 -34
  102. data/lib/glimmer/libui/control_proxy/text_proxy.rb +1 -1
  103. data/lib/glimmer/libui/control_proxy/transformable.rb +1 -1
  104. data/lib/glimmer/libui/control_proxy/triple_column.rb +1 -1
  105. data/lib/glimmer/libui/control_proxy/window_proxy.rb +1 -1
  106. data/lib/glimmer/libui/control_proxy.rb +2 -1
  107. data/lib/glimmer/libui/data_bindable.rb +2 -2
  108. data/lib/glimmer/libui/parent.rb +1 -1
  109. data/lib/glimmer/libui/shape/arc.rb +1 -1
  110. data/lib/glimmer/libui/shape/bezier.rb +21 -4
  111. data/lib/glimmer/libui/shape/circle.rb +1 -1
  112. data/lib/glimmer/libui/shape/figure.rb +1 -1
  113. data/lib/glimmer/libui/shape/line.rb +24 -4
  114. data/lib/glimmer/libui/shape/polybezier.rb +1 -1
  115. data/lib/glimmer/libui/shape/polygon.rb +1 -1
  116. data/lib/glimmer/libui/shape/polyline.rb +1 -1
  117. data/lib/glimmer/libui/shape/rectangle.rb +1 -1
  118. data/lib/glimmer/libui/shape/square.rb +1 -1
  119. data/lib/glimmer/libui/shape.rb +1 -1
  120. data/lib/glimmer/libui.rb +1 -1
  121. data/lib/glimmer-dsl-libui.rb +2 -2
  122. metadata +9 -6
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.4.17
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.4.21
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
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -22,6 +22,8 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
22
22
  - [Far Future Plan] Scaffolding for new custom controls, apps, and gems
23
23
  - [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
24
24
 
25
+ Note that currently, [LibUI](https://github.com/kojix2/LibUI) only includes x86_64 binaries out of the box, but there are plans to include ARM64/AARCH64 binaries too in the future.
26
+
25
27
  Hello, World!
26
28
 
27
29
  ```ruby
@@ -320,11 +322,13 @@ Mac | Windows | Linux
320
322
 
321
323
  [Check Out Many More Examples Over Here!](#examples)
322
324
 
323
- Mac | Windows | Linux
324
- ----|---------|------
325
- ![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png) | ![glimmer-dsl-libui-windows-tetris.png](images/glimmer-dsl-libui-windows-tetris.png) | ![glimmer-dsl-libui-linux-tetris.png](images/glimmer-dsl-libui-linux-tetris.png)
325
+ ![glimmer-dsl-libui-mac-snake.gif](images/glimmer-dsl-libui-mac-snake.gif)
326
+
327
+ ![glimmer-dsl-libui-mac-color-the-circles.gif](images/glimmer-dsl-libui-mac-color-the-circles.gif)
326
328
 
327
- NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is feature-complete and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
329
+ ![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)
330
+
331
+ NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is 100% feature-complete and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
328
332
 
329
333
  Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
330
334
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
@@ -409,6 +413,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
409
413
  - [Histogram](#histogram)
410
414
  - [Login](#login)
411
415
  - [Method-Based Custom Keyword](#method-based-custom-keyword)
416
+ - [Area-Based Custom Controls](#area-based-custom-controls)
412
417
  - [Midi Player](#midi-player)
413
418
  - [Snake](#snake)
414
419
  - [Tetris](#tetris)
@@ -506,7 +511,7 @@ gem install glimmer-dsl-libui
506
511
  Or install via Bundler `Gemfile`:
507
512
 
508
513
  ```ruby
509
- gem 'glimmer-dsl-libui', '~> 0.4.17'
514
+ gem 'glimmer-dsl-libui', '~> 0.4.21'
510
515
  ```
511
516
 
512
517
  Test that installation worked by running the [Meta-Example](#examples):
@@ -595,7 +600,7 @@ Keyword(Args) | Properties | Listeners
595
600
  `area` | `auto_draw_enabled` | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
596
601
  `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
597
602
  `background_color_column` | None | None
598
- `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
603
+ `bezier(x = nil as Numeric, y = nil as Numeric, c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
599
604
  `button(text as String)` | `text` (`String`) | `on_clicked`
600
605
  `button_column(name as String)` | `enabled` (Boolean) | None
601
606
  `checkbox(text as String)` | `checked` (Boolean), `text` (`String`) | `on_toggled`
@@ -622,7 +627,7 @@ Keyword(Args) | Properties | Listeners
622
627
  `image_text_column(name as String)` | None | None
623
628
  `image_text_color_column(name as String)` | None | None
624
629
  `label(text as String)` | `text` (`String`) | None
625
- `line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
630
+ `line(x as Numeric, y as Numeric, end_x = nil as Numeric, end_y = nil as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
626
631
  `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
627
632
  `menu(text as String)` | None | None
628
633
  `menu_item(text as String)` | None | `on_clicked`
@@ -958,8 +963,8 @@ Available `path` shapes (that can be nested explicitly under `path` or implicitl
958
963
  - `square(x as Numeric, y as Numeric, length as Numeric)`
959
964
  - `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
960
965
  - `circle(x_center as Numeric, y_center as Numeric, radius as Numeric)`
961
- - `line(x as Numeric, y as Numeric)`: must be placed in a figure (check `polyline`/`polygon` alternatives that do not require a `figure`)
962
- - `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)`: must be placed in a figure (check `polybezier` alternative that does not require a `figure`)
966
+ - `line(x as Numeric, y as Numeric, end_x = nil as Numeric, end_y = nil as Numeric)`: must be placed in a `figure` if only `x`/`y` are specified or have `end_x`/`end_y` otherwise if outside of `figure` (check `polyline`/`polygon` alternatives that do not require a `figure`)
967
+ - `bezier(x = nil as Numeric, y = nil as Numeric, c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)`: must be placed in a `figure` if `x`/`y` are not specified or have `x`/`y` as start point otherwise if outside of `figure` (check `polybezier` alternative that does not require a `figure`)
963
968
  - `polygon(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for a closed figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
964
969
  - `polyline(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for an open figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
965
970
  - `polybezier(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for an open figure of beziers; can receive points as [[start_x1, start_y1], [c1_x2, c1_y2, c2_x2, c2_y2, end_x2, end_y2], [c1_x3, c1_y3, c2_x3, c2_y3, end_x3, end_y3], ...] or [start_x1, start_y1, c1_x2, c1_y2, c2_x2, c2_y2, end_x2, end_y2, c1_x3, c1_y3, c2_x3, c2_y3, end_x3, end_y3, ...]
@@ -1365,8 +1370,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1365
1370
  - All boolean property readers return `true` or `false` in Ruby instead of the [libui](https://github.com/andlabs/libui) original `0` or `1` in C.
1366
1371
  - All boolean property writers accept `true`/`false` in addition to `1`/`0` in Ruby
1367
1372
  - All string property readers return a `String` object in Ruby instead of the [libui](https://github.com/andlabs/libui) Fiddle pointer object.
1368
- - Automatically allocate font descriptors upon instantiating `font_button` controls and free them when destorying `font_button` controls
1369
- - Automatically allocate color value pointers upon instantiating `color_button` controls and free them when destorying `color_button` controls
1373
+ - Automatically allocate font descriptors upon instantiating `font_button` controls and free them when destroying `font_button` controls
1374
+ - Automatically allocate color value pointers upon instantiating `color_button` controls and free them when destroying `color_button` controls
1370
1375
  - On the Mac, if no `menu` items were added, an automatic `quit_menu_item` is added to enable quitting with CTRL+Q
1371
1376
  - When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
1372
1377
  - When destroying a control nested under a `form`, it is automatically deleted from the form's children
@@ -1384,6 +1389,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1384
1389
  - `scrolling_area` `#scroll_to` 3rd and 4th arguments (`width` and `height`) default to main window width and height if not specified.
1385
1390
  - `area` paths are specified declaratively with shapes/figures underneath (e.g. `rectangle`), and `area` draw listener is automatically generated
1386
1391
  - `area` path shapes can be added directly under `area` without declaring `path` explicitly as a convenient shorthand
1392
+ - `line` and `bezier` automatically start a new figure if placed outside of `figure`
1387
1393
  - Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
1388
1394
  - Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
1389
1395
  - Observe `text` and `string` properties for changes and automatically redraw containing area accordingly
@@ -1395,7 +1401,15 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1395
1401
 
1396
1402
  ### Custom Keywords
1397
1403
 
1398
- To define custom keywords, simply define a method representing the custom control you want. To make reusable, you can define in modules and simply include the modules in the view classes that need them.
1404
+ Custom keywords can be defined to represent custom controls (components) that provide new features or act as composites of [existing controls](#supported-keywords) that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
1405
+
1406
+ For example, you can define a custom `address` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
1407
+
1408
+ To define custom keywords, simply define a method representing the custom control you want (e.g. `address`) with any arguments needed (e.g. `address(address_model)`).
1409
+
1410
+ To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
1411
+
1412
+ It is OK to use terms "custom keyword" and "custom control" synonymously though "custom keyword" is a broader term that covers things other than controls too like custom shapes (e.g. `cylinder`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
1399
1413
 
1400
1414
  Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1401
1415
 
@@ -1407,44 +1421,37 @@ include Glimmer
1407
1421
 
1408
1422
  Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
1409
1423
 
1410
- def form_field(model, property)
1411
- property = property.to_s
1424
+ def form_field(model, attribute)
1425
+ attribute = attribute.to_s
1412
1426
  entry { |e|
1413
- label property.underscore.split('_').map(&:capitalize).join(' ')
1414
- text model.send(property).to_s
1415
-
1416
- on_changed do
1417
- model.send("#{property}=", e.text)
1418
- end
1427
+ label attribute.underscore.split('_').map(&:capitalize).join(' ')
1428
+ text <=> [model, attribute]
1419
1429
  }
1420
1430
  end
1421
1431
 
1422
- def address_form(address)
1432
+ def address_form(address_model)
1423
1433
  form {
1424
- form_field(address, :street)
1425
- form_field(address, :p_o_box)
1426
- form_field(address, :city)
1427
- form_field(address, :state)
1428
- form_field(address, :zip_code)
1434
+ form_field(address_model, :street)
1435
+ form_field(address_model, :p_o_box)
1436
+ form_field(address_model, :city)
1437
+ form_field(address_model, :state)
1438
+ form_field(address_model, :zip_code)
1429
1439
  }
1430
1440
  end
1431
1441
 
1432
1442
  def label_pair(model, attribute, value)
1433
- name_label = nil
1434
- value_label = nil
1435
1443
  horizontal_box {
1436
- name_label = label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
1437
- value_label = label(value.to_s)
1444
+ label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
1445
+ label(value.to_s) {
1446
+ text <= [model, attribute]
1447
+ }
1438
1448
  }
1439
- Glimmer::DataBinding::Observer.proc do
1440
- value_label.text = model.send(attribute)
1441
- end.observe(model, attribute)
1442
1449
  end
1443
1450
 
1444
- def address(address)
1451
+ def address(address_model)
1445
1452
  vertical_box {
1446
- address.each_pair do |attribute, value|
1447
- label_pair(address, attribute, value)
1453
+ address_model.each_pair do |attribute, value|
1454
+ label_pair(address_model, attribute, value)
1448
1455
  end
1449
1456
  }
1450
1457
  end
@@ -1501,6 +1508,12 @@ window('Method-Based Custom Keyword') {
1501
1508
 
1502
1509
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1503
1510
 
1511
+ The [`area`](#area-api) control can be utilized to build non-native custom controls from scratch by leveraging vector graphics, formattable text, keyboard events, and mouse events. This is demonstrated in the [Area-Based Custom Controls](#area-based-custom-controls) example.
1512
+
1513
+ Defining custom keywords enables unlimited extension of the [Glimmer GUI DSL](#glimmer-gui-dsl). The sky is the limit on what can be done with custom keywords as a result. You can compose new visual vocabulary to build applications in any domain from higher concepts rather than [mere standard controls](#supported-keywords). For example, in a traffic signaling app, you could define `street`, `light_signal`, `traffic_sign`, and `car` as custom keywords and build your application from these concepts directly, saving enormous time and achieving much higher productivity.
1514
+
1515
+ Learn more from custom keyword usage in [Method-Based Custom Keyword](#method-based-custom-keyword), [Area-Based Custom Controls](#area-based-custom-controls), [Basic Scrolling Area](#basic-scrolling-area), [Histogram](#histogram), and [Tetris](#tetris) examples.
1516
+
1504
1517
  ### Observer Pattern
1505
1518
 
1506
1519
  The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a.k.a. Observer Pattern) is fundamental to building GUIs (Graphical User Interfaces) following the [MVC (Model View Controller) Architectural Pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) or any of its variations like [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter). In the original Smalltalk-MVC, the View observes the Model for changes and updates itself accordingly.
@@ -1772,9 +1785,9 @@ Data-bound model attribute can be:
1772
1785
  - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1773
1786
 
1774
1787
  Data-binding options include:
1775
- - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1788
+ - `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
1776
1789
  - `on_read {|value| ...}`: converts value read from Model to update the View.
1777
- - `after_read {|converted_value| ...}`: performs an operation after read from Model and updating the View.
1790
+ - `after_read {|converted_value| ...}`: performs an operation after read from Model to update View.
1778
1791
  - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1779
1792
  - `on_write {|value| ...}`: converts value read from View to update the Model.
1780
1793
  - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
@@ -1805,8 +1818,8 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1805
1818
  - `table` `checkbox_text_column` checkbox editing only works on Linux (not Mac or Windows) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
1806
1819
  - `text` `align` property seems not to work on the Mac ([libui](https://github.com/andlabs/libui) has an [issue](https://github.com/andlabs/libui/pull/407) about it)
1807
1820
  - `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
1808
- - `table` controls on Windows intentionally get an extra empty row at the end because if any row were to be deleted for the first time, double-deletion happens due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
1809
1821
  - `table` `progress_bar` column on Windows cannot be updated with a positive value if it started initially with `-1` (it ignores update to avoid crashing due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
1822
+ - `radio_buttons` on Linux has an issue where it always selects the first item even if you did not set its `selected` value or set it to `-1` (meaning unselected). It works correctly on Mac and Windows.
1810
1823
  - It seems that [libui](https://github.com/andlabs/libui) does not support nesting multiple `area` controls under a `grid` as only the first one shows up in that scenario. To workaround that limitation, use a `vertical_box` with nested `horizontal_box`s instead to include multiple `area`s in a GUI.
1811
1824
  - As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
1812
1825
  - `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
@@ -6039,7 +6052,7 @@ Glimmer::LibUI.timer(1) do
6039
6052
  cpu_percentage_value = nil
6040
6053
  if OS.windows?
6041
6054
  cpu_percentage_raw_value = `wmic cpu get loadpercentage`
6042
- cpu_percentage_value = cpu_percentage_raw_value.split("\n")[2].to_i
6055
+ cpu_percentage_value = cpu_percentage_raw_value.split("\n").map(&:strip).find {|l| l.match(/^\d+$/)}.to_i
6043
6056
  elsif OS.mac?
6044
6057
  cpu_percentage_value = `ps -A -o %cpu | awk '{s+=$1} END {print s}'`.to_i
6045
6058
  elsif OS.linux?
@@ -6110,6 +6123,7 @@ class CustomDrawText
6110
6123
  @string.font = fb.font
6111
6124
  end
6112
6125
  }
6126
+
6113
6127
  color_button { |cb|
6114
6128
  label 'Color'
6115
6129
 
@@ -6117,13 +6131,17 @@ class CustomDrawText
6117
6131
  @string.color = cb.color
6118
6132
  end
6119
6133
  }
6120
- color_button { |cb|
6121
- label 'Background'
6122
-
6123
- on_changed do
6124
- @string.background = cb.color
6125
- end
6126
- }
6134
+
6135
+ unless OS.windows?
6136
+ color_button { |cb|
6137
+ label 'Background'
6138
+
6139
+ on_changed do
6140
+ @string.background = cb.color
6141
+ end
6142
+ }
6143
+ end
6144
+
6127
6145
  combobox { |c|
6128
6146
  label 'Underline'
6129
6147
  items Glimmer::LibUI.enum_symbols(:underline).map(&:to_s).map {|word| word.split('_').map(&:capitalize).join(' ')}
@@ -6133,12 +6151,36 @@ class CustomDrawText
6133
6151
  @string.underline = c.selected_item.underscore
6134
6152
  end
6135
6153
  }
6154
+
6155
+ combobox { |c|
6156
+ label 'Underline Built-In Color'
6157
+ items Glimmer::LibUI.enum_symbols(:underline_color).map(&:to_s).map(&:capitalize)
6158
+ selected 'Custom'
6159
+
6160
+ on_selected do
6161
+ @underline_custom_color_button.enabled = c.selected_item == 'Custom'
6162
+ if c.selected_item == 'Custom'
6163
+ @string.underline_color = @underline_custom_color_button.color
6164
+ else
6165
+ @string.underline_color = c.selected_item.underscore
6166
+ @underline_custom_color_button.color = :black
6167
+ end
6168
+ end
6169
+ }
6170
+
6171
+ @underline_custom_color_button = color_button {
6172
+ label 'Underline Custom Color'
6173
+
6174
+ on_changed do
6175
+ @string.underline_color = @underline_custom_color_button.color
6176
+ end
6177
+ }
6136
6178
  }
6137
6179
 
6138
6180
  area {
6139
- text { # default arguments for x, y, and width are (0, 0, area_draw_params[:area_width])
6181
+ text { # default arguments for x, y, and width are (0, 0, area_draw_params[:area_width] - 2*x)
6140
6182
  # align :left # default alignment
6141
-
6183
+
6142
6184
  @string = string {
6143
6185
  ' At last Ygramul sensed that something was coming toward ' \
6144
6186
  'her. With the speed of lightning, she turned about, confronting ' \
@@ -6183,62 +6225,94 @@ require 'glimmer-dsl-libui'
6183
6225
  # The English version, translated by Ralph Manheim, was published in 1983.
6184
6226
  class CustomDrawText
6185
6227
  include Glimmer
6186
-
6228
+
6187
6229
  def launch
6188
6230
  window('Michael Ende (1929-1995) The Neverending Story', 600, 500) {
6189
6231
  margined true
6190
-
6232
+
6191
6233
  vertical_box {
6192
6234
  form {
6193
6235
  stretchy false
6194
-
6236
+
6195
6237
  font_button { |fb|
6196
6238
  label 'Font'
6197
-
6239
+
6198
6240
  on_changed do
6199
6241
  @font = fb.font
6200
6242
  @area.queue_redraw_all
6201
6243
  end
6202
6244
  }
6245
+
6203
6246
  color_button { |cb|
6204
6247
  label 'Color'
6205
-
6248
+
6206
6249
  on_changed do
6207
6250
  @color = cb.color
6208
6251
  @area.queue_redraw_all
6209
6252
  end
6210
6253
  }
6211
- color_button { |cb|
6212
- label 'Background'
6213
-
6214
- on_changed do
6215
- @background = cb.color
6216
- @area.queue_redraw_all
6217
- end
6218
- }
6254
+
6255
+ unless OS.windows?
6256
+ color_button { |cb|
6257
+ label 'Background'
6258
+
6259
+ on_changed do
6260
+ @background = cb.color
6261
+ @area.queue_redraw_all
6262
+ end
6263
+ }
6264
+ end
6265
+
6219
6266
  combobox { |c|
6220
6267
  label 'Underline'
6221
6268
  items Glimmer::LibUI.enum_symbols(:underline).map(&:to_s).map {|word| word.split('_').map(&:capitalize).join(' ')}
6222
6269
  selected 'None'
6223
-
6270
+
6224
6271
  on_selected do
6225
6272
  @underline = c.selected_item.underscore
6226
6273
  @area.queue_redraw_all
6227
6274
  end
6228
6275
  }
6276
+
6277
+ combobox { |c|
6278
+ label 'Underline Built-In Color'
6279
+ items Glimmer::LibUI.enum_symbols(:underline_color).map(&:to_s).map(&:capitalize)
6280
+ selected 'Custom'
6281
+
6282
+ on_selected do
6283
+ @underline_custom_color_button.enabled = c.selected_item == 'Custom'
6284
+ if c.selected_item == 'Custom'
6285
+ @underline_color = @underline_custom_color_button.color
6286
+ else
6287
+ @underline_color = c.selected_item.underscore
6288
+ @underline_custom_color_button.color = :black
6289
+ end
6290
+ @area.queue_redraw_all
6291
+ end
6292
+ }
6293
+
6294
+ @underline_custom_color_button = color_button {
6295
+ label 'Underline Custom Color'
6296
+
6297
+ on_changed do
6298
+ @underline_color = @underline_custom_color_button.color
6299
+ @area.queue_redraw_all
6300
+ end
6301
+ }
6229
6302
  }
6230
-
6303
+
6231
6304
  @area = area {
6232
6305
  on_draw do |area_draw_params|
6233
- text { # default arguments for x, y, and width are (0, 0, area_draw_params[:area_width])
6306
+ text { # default arguments for x, y, and width are (0, 0, area_draw_params[:area_width] - 2*x)
6234
6307
  # align :left # default alignment
6235
-
6308
+
6236
6309
  string {
6237
6310
  font @font
6238
6311
  color @color
6239
6312
  background @background
6240
6313
  underline @underline
6241
-
6314
+ underline_color @underline_color
6315
+
6242
6316
  ' At last Ygramul sensed that something was coming toward ' \
6243
6317
  'her. With the speed of lightning, she turned about, confronting ' \
6244
6318
  'Atreyu with an enormous steel-blue face. Her single eye had a ' \
@@ -8309,6 +8383,12 @@ window('Login') {
8309
8383
 
8310
8384
  #### Method-Based Custom Keyword
8311
8385
 
8386
+ [Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
8387
+
8388
+ This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
8389
+
8390
+ The custom keywords are defined via methods (thus are "method-based").
8391
+
8312
8392
  [examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
8313
8393
 
8314
8394
  Run with this command from the root of the project if you cloned the project:
@@ -8524,6 +8604,317 @@ window('Method-Based Custom Keyword') {
8524
8604
  }.show
8525
8605
  ```
8526
8606
 
8607
+ #### Area-Based Custom Controls
8608
+
8609
+ [Custom keywords](#custom-keywords) can be defined for graphical custom controls (components) built completely from scratch as vector-graphics on top of the [`area`](#area-api) control while leveraging keyboard and mouse listeners.
8610
+
8611
+ This example defines `text_label` and `push_button` as [`area`](#area-api)-based graphical custom controls that can have width, height, font, fill, stroke, border, and custom text location.
8612
+
8613
+ [examples/area_based_custom_controls.rb](examples/area_based_custom_controls.rb)
8614
+
8615
+ Run with this command from the root of the project if you cloned the project:
8616
+
8617
+ ```
8618
+ ruby -r './lib/glimmer-dsl-libui' examples/area_based_custom_controls.rb
8619
+ ```
8620
+
8621
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8622
+
8623
+ ```
8624
+ ruby -r glimmer-dsl-libui -e "require 'examples/area_based_custom_controls'"
8625
+ ```
8626
+
8627
+ Mac | Windows | Linux
8628
+ ----|---------|------
8629
+ ![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-push-button-clicked.png) | ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-push-button-clicked.png) | ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-push-button-clicked.png)
8630
+
8631
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8632
+
8633
+ ```ruby
8634
+ require 'glimmer-dsl-libui'
8635
+
8636
+ class AreaBasedCustomControls
8637
+ include Glimmer
8638
+
8639
+ attr_accessor :label_width, :label_height, :label_font_descriptor,
8640
+ :label_text_color, :label_background_fill, :label_border_stroke,
8641
+ :label_text_x, :label_text_y,
8642
+ :button_width, :button_height, :button_font_descriptor,
8643
+ :button_text_color, :button_background_fill, :button_border_stroke,
8644
+ :button_text_x, :button_text_y
8645
+
8646
+ def initialize
8647
+ self.label_width = 335
8648
+ self.label_height = 50
8649
+ self.label_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 16, weight: :bold, italic: :italic}
8650
+ self.label_text_color = :red
8651
+ self.label_background_fill = :yellow
8652
+ self.label_border_stroke = :limegreen
8653
+
8654
+ self.button_width = 150
8655
+ self.button_height = 50
8656
+ self.button_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 36, weight: OS.linux? ? :normal : :bold, italic: :italic}
8657
+ self.button_text_color = :green
8658
+ self.button_background_fill = :yellow
8659
+ self.button_border_stroke = :limegreen
8660
+ end
8661
+
8662
+ def rebuild_text_label
8663
+ @text_label.destroy
8664
+ @text_label_vertical_box.content { # re-open vertical box content and shove in a new label
8665
+ @text_label = text_label('This is a text label.',
8666
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
8667
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
8668
+ text_x: label_text_x, text_y: label_text_y)
8669
+ }
8670
+ end
8671
+
8672
+ def rebuild_push_button
8673
+ @push_button.destroy
8674
+ @push_button_vertical_box.content { # re-open vertical box content and shove in a new button
8675
+ @push_button = push_button('Push',
8676
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
8677
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
8678
+ text_x: button_text_x, text_y: button_text_y) {
8679
+ on_mouse_up do
8680
+ message_box('Button Pushed', 'Thank you for pushing the button!')
8681
+ end
8682
+ }
8683
+ }
8684
+ end
8685
+
8686
+ def launch
8687
+ window('Area-Based Custom Controls', 385, 385) { |w|
8688
+ margined true
8689
+
8690
+ tab {
8691
+ tab_item('Text Label') {
8692
+ @text_label_vertical_box = vertical_box {
8693
+ vertical_box {
8694
+ text_label('Text Label Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
8695
+
8696
+ horizontal_box {
8697
+ label('Width')
8698
+ spinbox(1, 1000) {
8699
+ value <=> [self, :label_width, after_write: method(:rebuild_text_label)]
8700
+ }
8701
+ }
8702
+
8703
+ horizontal_box {
8704
+ label('Height')
8705
+ spinbox(1, 1000) {
8706
+ value <=> [self, :label_height, after_write: method(:rebuild_text_label)]
8707
+ }
8708
+ }
8709
+
8710
+ horizontal_box {
8711
+ label('Font')
8712
+ font_button {
8713
+ font <=> [self, :label_font_descriptor, after_write: method(:rebuild_text_label)]
8714
+ }
8715
+ }
8716
+
8717
+ horizontal_box {
8718
+ label('Text Color')
8719
+ color_button {
8720
+ color <=> [self, :label_text_color, after_write: method(:rebuild_text_label)]
8721
+ }
8722
+ }
8723
+
8724
+ horizontal_box {
8725
+ label('Background Color')
8726
+ color_button {
8727
+ color <=> [self, :label_background_fill, after_write: method(:rebuild_text_label)]
8728
+ }
8729
+ }
8730
+
8731
+ horizontal_box {
8732
+ label('Border Color')
8733
+ color_button {
8734
+ color <=> [self, :label_border_stroke, after_write: method(:rebuild_text_label)]
8735
+ }
8736
+ }
8737
+
8738
+ horizontal_box {
8739
+ label('Text X (0=centered)')
8740
+ spinbox(0, 1000) {
8741
+ value <=> [self, :label_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_text_label)]
8742
+ }
8743
+ }
8744
+
8745
+ horizontal_box {
8746
+ label('Text Y (0=centered)')
8747
+ spinbox(0, 1000) {
8748
+ value <=> [self, :label_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_text_label)]
8749
+ }
8750
+ }
8751
+ }
8752
+
8753
+ @text_label = text_label('This is a text label.',
8754
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
8755
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
8756
+ text_x: label_text_x, text_y: label_text_y)
8757
+ }
8758
+ }
8759
+
8760
+ tab_item('Push Button') {
8761
+ @push_button_vertical_box = vertical_box {
8762
+ vertical_box {
8763
+ text_label('Push Button Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
8764
+
8765
+ horizontal_box {
8766
+ label('Width')
8767
+ spinbox(1, 1000) {
8768
+ value <=> [self, :button_width, after_write: method(:rebuild_push_button)]
8769
+ }
8770
+ }
8771
+
8772
+ horizontal_box {
8773
+ label('Height')
8774
+ spinbox(1, 1000) {
8775
+ value <=> [self, :button_height, after_write: method(:rebuild_push_button)]
8776
+ }
8777
+ }
8778
+
8779
+ horizontal_box {
8780
+ label('Font')
8781
+ font_button {
8782
+ font <=> [self, :button_font_descriptor, after_write: method(:rebuild_push_button)]
8783
+ }
8784
+ }
8785
+
8786
+ horizontal_box {
8787
+ label('Text Color')
8788
+ color_button {
8789
+ color <=> [self, :button_text_color, after_write: method(:rebuild_push_button)]
8790
+ }
8791
+ }
8792
+
8793
+ horizontal_box {
8794
+ label('Background Color')
8795
+ color_button {
8796
+ color <=> [self, :button_background_fill, after_write: method(:rebuild_push_button)]
8797
+ }
8798
+ }
8799
+
8800
+ horizontal_box {
8801
+ label('Border Color')
8802
+ color_button {
8803
+ color <=> [self, :button_border_stroke, after_write: method(:rebuild_push_button)]
8804
+ }
8805
+ }
8806
+
8807
+ horizontal_box {
8808
+ label('Text X (0=centered)')
8809
+ spinbox(0, 1000) {
8810
+ value <=> [self, :button_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_push_button)]
8811
+ }
8812
+ }
8813
+
8814
+ horizontal_box {
8815
+ label('Text Y (0=centered)')
8816
+ spinbox(0, 1000) {
8817
+ value <=> [self, :button_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_push_button)]
8818
+ }
8819
+ }
8820
+ }
8821
+
8822
+ @push_button = push_button('Push',
8823
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
8824
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
8825
+ text_x: button_text_x, text_y: button_text_y) {
8826
+ on_mouse_up do
8827
+ message_box('Button Pushed', 'Thank you for pushing the button!')
8828
+ end
8829
+ }
8830
+ }
8831
+ }
8832
+ }
8833
+ }.show
8834
+ end
8835
+
8836
+ # text label (area-based custom control) built with vector graphics on top of area.
8837
+ #
8838
+ # background_fill is transparent by default.
8839
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
8840
+ # border_stroke is transparent by default.
8841
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
8842
+ # text_x and text_y are the offset of the label text in relation to its top-left corner.
8843
+ # When text_x, text_y are left nil, the text is automatically centered in the label area.
8844
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
8845
+ # in that case, pass in text_x, and text_y manually.
8846
+ def text_label(label_text,
8847
+ width: 80, height: 30, font_descriptor: {},
8848
+ background_fill: {a: 0}, text_color: :black, border_stroke: {a: 0},
8849
+ text_x: nil, text_y: nil,
8850
+ &content)
8851
+ area { |the_area|
8852
+ rectangle(1, 1, width, height) {
8853
+ fill background_fill
8854
+ }
8855
+ rectangle(1, 1, width, height) {
8856
+ stroke border_stroke
8857
+ }
8858
+
8859
+ text_height = (font_descriptor[:size] || 12) * (OS.mac? ? 0.75 : 1.35)
8860
+ text_width = (text_height * label_text.size) * (OS.mac? ? 0.75 : 0.60)
8861
+ text_x ||= (width - text_width) / 2.0
8862
+ text_y ||= (height - 4 - text_height) / 2.0
8863
+ text(text_x, text_y, width) {
8864
+ string(label_text) {
8865
+ color text_color
8866
+ font font_descriptor
8867
+ }
8868
+ }
8869
+
8870
+ content&.call(the_area)
8871
+ }
8872
+ end
8873
+
8874
+ # push button (area-based custom control) built with vector graphics on top of area.
8875
+ #
8876
+ # background_fill is white by default.
8877
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
8878
+ # border_stroke is black by default.
8879
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
8880
+ # text_x and text_y are the offset of the button text in relation to its top-left corner.
8881
+ # When text_x, text_y are left nil, the text is automatically centered in the button area.
8882
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
8883
+ # in that case, pass in text_x, and text_y manually.
8884
+ #
8885
+ # reuses the text_label custom control
8886
+ def push_button(button_text,
8887
+ width: 80, height: 30, font_descriptor: {},
8888
+ background_fill: :white, text_color: :black, border_stroke: {r: 201, g: 201, b: 201},
8889
+ text_x: nil, text_y: nil,
8890
+ &content)
8891
+ text_label(button_text,
8892
+ width: width, height: height, font_descriptor: font_descriptor,
8893
+ background_fill: background_fill, text_color: text_color, border_stroke: border_stroke,
8894
+ text_x: text_x, text_y: text_y) { |the_area|
8895
+
8896
+ # dig into the_area content and grab elements to modify in mouse listeners below
8897
+ background_rectangle = the_area.children[0]
8898
+ button_string = the_area.children[2].children[0]
8899
+
8900
+ on_mouse_down do
8901
+ background_rectangle.fill = {x0: 0, y0: 0, x1: 0, y1: height, stops: [{pos: 0, r: 72, g: 146, b: 247}, {pos: 1, r: 12, g: 85, b: 214}]}
8902
+ button_string.color = :white
8903
+ end
8904
+
8905
+ on_mouse_up do
8906
+ background_rectangle.fill = background_fill
8907
+ button_string.color = text_color
8908
+ end
8909
+
8910
+ content&.call(the_area)
8911
+ }
8912
+ end
8913
+ end
8914
+
8915
+ AreaBasedCustomControls.new.launch
8916
+ ```
8917
+
8527
8918
  #### Midi Player
8528
8919
 
8529
8920
  To run this example, install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
@@ -8977,7 +9368,7 @@ class Snake
8977
9368
  end
8978
9369
 
8979
9370
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
8980
- unless @game.over?
9371
+ unless @game.paused? || @game.over?
8981
9372
  process_queued_keypress
8982
9373
  @game.snake.move
8983
9374
  end
@@ -9018,7 +9409,11 @@ class Snake
9018
9409
  }
9019
9410
 
9020
9411
  on_key_up do |area_key_event|
9021
- @keypress_queue << area_key_event[:ext_key]
9412
+ if area_key_event[:key] == ' '
9413
+ @game.toggle_pause
9414
+ else
9415
+ @keypress_queue << area_key_event[:ext_key]
9416
+ end
9022
9417
  end
9023
9418
  }
9024
9419
  end
@@ -9077,7 +9472,7 @@ class Snake
9077
9472
  end
9078
9473
 
9079
9474
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
9080
- unless @game.over?
9475
+ unless @game.paused? || @game.over?
9081
9476
  process_queued_keypress
9082
9477
  @game.snake.move
9083
9478
  end
@@ -9120,7 +9515,11 @@ class Snake
9120
9515
  }
9121
9516
 
9122
9517
  on_key_up do |area_key_event|
9123
- @keypress_queue << area_key_event[:ext_key]
9518
+ if area_key_event[:key] == ' '
9519
+ @game.toggle_pause
9520
+ else
9521
+ @keypress_queue << area_key_event[:ext_key]
9522
+ end
9124
9523
  end
9125
9524
  }
9126
9525
  end
@@ -9521,7 +9920,7 @@ class Tetris
9521
9920
 
9522
9921
  def show_about_dialog
9523
9922
  Glimmer::LibUI.queue_main do
9524
- msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
9923
+ msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021-2022 Andy Maleh')
9525
9924
  end
9526
9925
  end
9527
9926
  end
@@ -10046,7 +10445,7 @@ https://github.com/iraamaro/i3off-gtk-ruby
10046
10445
 
10047
10446
  ### Issues
10048
10447
 
10049
- If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com). In the meantime, you may try older gem versions of [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) till you find one that works until issues are resolved.
10448
+ If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com). In the meantime, you may try older gem versions of [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) till you find one that works.
10050
10449
 
10051
10450
  ### Chat
10052
10451
 
@@ -10090,7 +10489,7 @@ Note that the latest development sometimes takes place in the [development](http
10090
10489
 
10091
10490
  [MIT](LICENSE.txt)
10092
10491
 
10093
- Copyright (c) 2021 Andy Maleh
10492
+ Copyright (c) 2021-2022 Andy Maleh
10094
10493
 
10095
10494
  --
10096
10495