glimmer-dsl-libui 0.4.19 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (116) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +22 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +452 -52
  5. data/VERSION +1 -1
  6. data/bin/girb_runner.rb +1 -1
  7. data/examples/area_based_custom_controls.rb +282 -0
  8. data/examples/snake/model/game.rb +18 -1
  9. data/examples/snake.rb +6 -2
  10. data/examples/snake2.rb +6 -2
  11. data/examples/tetris/model/block.rb +1 -1
  12. data/examples/tetris/model/game.rb +26 -24
  13. data/examples/tetris/model/past_game.rb +1 -1
  14. data/examples/tetris/model/tetromino.rb +1 -1
  15. data/examples/tetris.rb +51 -20
  16. data/examples/tic_tac_toe/board.rb +1 -1
  17. data/examples/tic_tac_toe/cell.rb +1 -1
  18. data/glimmer-dsl-libui.gemspec +0 -0
  19. data/lib/glimmer/dsl/libui/bind_expression.rb +1 -1
  20. data/lib/glimmer/dsl/libui/control_expression.rb +1 -1
  21. data/lib/glimmer/dsl/libui/data_binding_expression.rb +1 -1
  22. data/lib/glimmer/dsl/libui/dsl.rb +1 -1
  23. data/lib/glimmer/dsl/libui/file_expression.rb +1 -1
  24. data/lib/glimmer/dsl/libui/listener_expression.rb +1 -1
  25. data/lib/glimmer/dsl/libui/observe_expression.rb +1 -1
  26. data/lib/glimmer/dsl/libui/open_file_expression.rb +1 -1
  27. data/lib/glimmer/dsl/libui/operation_expression.rb +1 -1
  28. data/lib/glimmer/dsl/libui/property_expression.rb +1 -1
  29. data/lib/glimmer/dsl/libui/save_file_expression.rb +1 -1
  30. data/lib/glimmer/dsl/libui/shape_expression.rb +1 -1
  31. data/lib/glimmer/dsl/libui/shine_data_binding_expression.rb +1 -1
  32. data/lib/glimmer/dsl/libui/string_expression.rb +1 -1
  33. data/lib/glimmer/dsl/libui/tab_item_expression.rb +1 -1
  34. data/lib/glimmer/fiddle_consumer.rb +1 -1
  35. data/lib/glimmer/libui/attributed_string.rb +1 -1
  36. data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +1 -1
  37. data/lib/glimmer/libui/control_proxy/area_proxy.rb +1 -1
  38. data/lib/glimmer/libui/control_proxy/box/horizontal_box_proxy.rb +1 -1
  39. data/lib/glimmer/libui/control_proxy/box/vertical_box_proxy.rb +1 -1
  40. data/lib/glimmer/libui/control_proxy/box.rb +2 -1
  41. data/lib/glimmer/libui/control_proxy/button_proxy.rb +1 -1
  42. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +1 -1
  43. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -1
  44. data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +1 -1
  45. data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -1
  46. data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +1 -1
  47. data/lib/glimmer/libui/control_proxy/column/checkbox_text_color_column_proxy.rb +1 -1
  48. data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +1 -1
  49. data/lib/glimmer/libui/control_proxy/column/image_column_proxy.rb +1 -1
  50. data/lib/glimmer/libui/control_proxy/column/image_text_color_column_proxy.rb +1 -1
  51. data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +1 -1
  52. data/lib/glimmer/libui/control_proxy/column/progress_bar_column_proxy.rb +1 -1
  53. data/lib/glimmer/libui/control_proxy/column/text_color_column_proxy.rb +1 -1
  54. data/lib/glimmer/libui/control_proxy/column/text_column_proxy.rb +1 -1
  55. data/lib/glimmer/libui/control_proxy/column.rb +1 -1
  56. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -1
  57. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +1 -1
  58. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +1 -1
  59. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -1
  60. data/lib/glimmer/libui/control_proxy/dual_column.rb +1 -1
  61. data/lib/glimmer/libui/control_proxy/editable_column.rb +1 -1
  62. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -1
  63. data/lib/glimmer/libui/control_proxy/enableable_column.rb +1 -1
  64. data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +1 -1
  65. data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +1 -1
  66. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -1
  67. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +1 -1
  68. data/lib/glimmer/libui/control_proxy/form_proxy.rb +2 -1
  69. data/lib/glimmer/libui/control_proxy/grid_proxy.rb +1 -1
  70. data/lib/glimmer/libui/control_proxy/group_proxy.rb +1 -1
  71. data/lib/glimmer/libui/control_proxy/image_part_proxy.rb +1 -1
  72. data/lib/glimmer/libui/control_proxy/image_proxy.rb +1 -1
  73. data/lib/glimmer/libui/control_proxy/label_proxy.rb +1 -1
  74. data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +1 -1
  75. data/lib/glimmer/libui/control_proxy/menu_item_proxy/about_menu_item_proxy.rb +1 -1
  76. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -1
  77. data/lib/glimmer/libui/control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb +1 -1
  78. data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +1 -1
  79. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -1
  80. data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +1 -1
  81. data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +1 -1
  82. data/lib/glimmer/libui/control_proxy/menu_proxy.rb +1 -1
  83. data/lib/glimmer/libui/control_proxy/message_box/msg_box_error_proxy.rb +1 -1
  84. data/lib/glimmer/libui/control_proxy/message_box/msg_box_proxy.rb +1 -1
  85. data/lib/glimmer/libui/control_proxy/message_box.rb +1 -1
  86. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb +1 -1
  87. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -1
  88. data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +1 -1
  89. data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +1 -1
  90. data/lib/glimmer/libui/control_proxy/path_proxy.rb +1 -1
  91. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -1
  92. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -1
  93. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -1
  94. data/lib/glimmer/libui/control_proxy/tab_item_proxy.rb +1 -1
  95. data/lib/glimmer/libui/control_proxy/table_proxy.rb +1 -1
  96. data/lib/glimmer/libui/control_proxy/text_proxy.rb +1 -1
  97. data/lib/glimmer/libui/control_proxy/transformable.rb +1 -1
  98. data/lib/glimmer/libui/control_proxy/triple_column.rb +1 -1
  99. data/lib/glimmer/libui/control_proxy/window_proxy.rb +1 -1
  100. data/lib/glimmer/libui/control_proxy.rb +2 -1
  101. data/lib/glimmer/libui/data_bindable.rb +1 -1
  102. data/lib/glimmer/libui/parent.rb +1 -1
  103. data/lib/glimmer/libui/shape/arc.rb +1 -1
  104. data/lib/glimmer/libui/shape/bezier.rb +1 -1
  105. data/lib/glimmer/libui/shape/circle.rb +1 -1
  106. data/lib/glimmer/libui/shape/figure.rb +1 -1
  107. data/lib/glimmer/libui/shape/line.rb +1 -1
  108. data/lib/glimmer/libui/shape/polybezier.rb +1 -1
  109. data/lib/glimmer/libui/shape/polygon.rb +1 -1
  110. data/lib/glimmer/libui/shape/polyline.rb +1 -1
  111. data/lib/glimmer/libui/shape/rectangle.rb +8 -3
  112. data/lib/glimmer/libui/shape/square.rb +10 -3
  113. data/lib/glimmer/libui/shape.rb +37 -1
  114. data/lib/glimmer/libui.rb +1 -1
  115. data/lib/glimmer-dsl-libui.rb +2 -1
  116. metadata +27 -11
data/README.md CHANGED
@@ -1,9 +1,11 @@
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.19
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.5.0
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)
5
5
 
6
- [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free Ruby desktop development GUI (Graphical User Interface) library. No need to pre-install any prerequisites. Just install the [gem](https://rubygems.org/gems/glimmer-dsl-libui) and have platform-independent native GUI that just works!
6
+ [**(Fukuoka Ruby Award Competition 2022 Special Award Winner)**](http://www.digitalfukuoka.jp/topics/187?locale=ja)
7
+
8
+ [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free [MRI Ruby](https://www.ruby-lang.org) desktop development GUI (Graphical User Interface) library. No need to pre-install any prerequisites. Just install the [gem](https://rubygems.org/gems/glimmer-dsl-libui) and have platform-independent native GUI that just works!
7
9
 
8
10
  Mac | Windows | Linux
9
11
  ----|---------|------
@@ -22,6 +24,8 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
22
24
  - [Far Future Plan] Scaffolding for new custom controls, apps, and gems
23
25
  - [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
24
26
 
27
+ 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.
28
+
25
29
  Hello, World!
26
30
 
27
31
  ```ruby
@@ -320,19 +324,27 @@ Mac | Windows | Linux
320
324
 
321
325
  [Check Out Many More Examples Over Here!](#examples)
322
326
 
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)
327
+ ![glimmer-dsl-libui-mac-snake.gif](images/glimmer-dsl-libui-mac-snake.gif)
328
+
329
+ ![glimmer-dsl-libui-mac-color-the-circles.gif](images/glimmer-dsl-libui-mac-color-the-circles.gif)
326
330
 
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.
331
+ ![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)
328
332
 
329
- Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
330
- - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
331
- - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
332
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
333
- - [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)
334
- - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
335
- - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
333
+ NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is 100% feature-complete with regards to covering the C [libui](https://github.com/andlabs/libui) library API and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha, which is why [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) cannot be declared v1.0.0 yet). 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.
334
+
335
+ **[Glimmer](https://rubygems.org/gems/glimmer) DSL Comparison Table:**
336
+ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
337
+ ----|-----------|---------|------------------|------|------|--------
338
+ [Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](https://github.com/AndyObtiva/glimmer-dsl-swt) | Mac / Windows / Linux | Yes | Yes (Canvas Shape DSL) | Very Mature / Scaffolding / Native Executable Packaging / Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint | Java / JRuby
339
+ [Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)](https://github.com/AndyObtiva/glimmer-dsl-opal) | All Web Browsers | No | Yes (Canvas Shape DSL) | Simpler than All JavaScript Technologies / Auto-Webify Desktop Apps | Setup Process / Only Rails 5 Support for Now | Rails
340
+ [Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-libui) | Mac / Windows / Linux | Yes | Yes (Area API) | Fast Startup Time / Light Memory Footprint | LibUI is an Incomplete Mid-Alpha Only | None Other Than MRI Ruby
341
+ [Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-tk) | Mac / Windows / Linux | Some Native-Themed Widgets (Not Truly Native) | Yes (Canvas) | Fast Startup Time / Light Memory Footprint | Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
342
+ [Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-gtk) | Mac / Windows / Linux | Only on Linux | Yes (Cairo) | Complete Access to GNOME Features on Linux (Forte) | Not Native on Mac and Windows | None Other Than MRI Ruby on Linux / Brew Packages on Mac / MSYS & MING Toolchains on Windows / MRI Ruby
343
+ [Glimmer DSL for FX (FOX Toolkit Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-fx) | Mac (requires XQuartz) / Windows / Linux | No | Yes (Canvas) | No Prerequisites on Windows (Forte Since Binaries Are Included Out of The Box) | Widgets Do Not Look Native / Mac Usage Obtrusively Starts XQuartz | None Other Than MRI Ruby on Windows / XQuarts on Mac / MRI Ruby
344
+ [Glimmer DSL for JFX (JRuby JavaFX Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-jfx) | Mac / Windows / Linux | No | Yes (javafx.scene.shape and javafx.scene.canvas) | Rich in Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby / JavaFX SDK
345
+ [Glimmer DSL for Swing (JRuby Swing Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-swing) | Mac / Windows / Linux | No | Yes (Java2D) | Very Mature | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby
346
+ [Glimmer DSL for XML (& HTML)](https://github.com/AndyObtiva/glimmer-dsl-xml) | All Web Browsers | No | Yes (SVG) | Programmable / Lighter-weight Than Actual XML | XML Elements Are Sometimes Not Well-Named (Many Types of Input) | None
347
+ [Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) | All Web Browsers | No | Yes | Programmable | CSS Is Over-Engineered / Too Many Features To Learn | None
336
348
 
337
349
  ## Table of Contents
338
350
 
@@ -409,6 +421,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
409
421
  - [Histogram](#histogram)
410
422
  - [Login](#login)
411
423
  - [Method-Based Custom Keyword](#method-based-custom-keyword)
424
+ - [Area-Based Custom Controls](#area-based-custom-controls)
412
425
  - [Midi Player](#midi-player)
413
426
  - [Snake](#snake)
414
427
  - [Tetris](#tetris)
@@ -506,7 +519,7 @@ gem install glimmer-dsl-libui
506
519
  Or install via Bundler `Gemfile`:
507
520
 
508
521
  ```ruby
509
- gem 'glimmer-dsl-libui', '~> 0.4.19'
522
+ gem 'glimmer-dsl-libui', '~> 0.5.0'
510
523
  ```
511
524
 
512
525
  Test that installation worked by running the [Meta-Example](#examples):
@@ -576,13 +589,14 @@ w.set_title 'aloha'
576
589
  puts w.title # => aloha
577
590
  ```
578
591
 
579
- Controls are wrapped as Ruby proxy objects, having a `#libui` method to obtain the wrapped [LibUI](https://github.com/kojix2/LibUI) Fiddle pointer object. Ruby proxy objects rely on composition (via [Proxy Design Pattern](https://en.wikipedia.org/wiki/Proxy_pattern)) instead of inheritance to shield consumers from having to deal with lower-level details unless absolutely needed.
592
+ Controls are wrapped as Ruby proxy objects, having a `#libui` method to obtain the wrapped [LibUI](https://github.com/kojix2/LibUI) Fiddle pointer object. Ruby proxy objects rely on composition (via [Proxy Design Pattern](https://en.wikipedia.org/wiki/Proxy_pattern)) instead of inheritance to shield consumers from having to deal with lower-level details unless absolutely needed. That said, you can invoke any [LibUI operation](#libui-operations) on the Glimmer proxy object directly and it gets proxied automatically to the wrapped Fiddle pointer object (e.g. `window_proxy.title` gets proxied to `LibUI.window_title(window_proxy.libui).to_s` automatically), so you rarely have to refer to the wrapped `#libui` Fiddle pointer object directly.
580
593
 
581
594
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
582
595
 
583
596
  ```ruby
584
597
  w = window('hello world') # => #<Glimmer::LibUI::WindowProxy:0x00007fde4ea39fb0
585
598
  w.libui # => #<Fiddle::Pointer:0x00007fde53997980 ptr=0x00007fde51352a60 size=0 free=0x0000000000000000>
599
+ w.title == LibUI.window_title(w.libui).to_s # => true
586
600
  ```
587
601
 
588
602
  ### Supported Keywords
@@ -994,15 +1008,49 @@ To draw `text` in an `area`, you simply nest a `text(x, y, width)` control direc
994
1008
  Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
995
1009
 
996
1010
  ```ruby
1011
+ require 'glimmer-dsl-libui'
1012
+
1013
+ include Glimmer
1014
+
997
1015
  window('area text drawing') {
998
1016
  area {
999
1017
  text {
1000
1018
  default_font family: 'Helvetica', size: 12, weight: :normal, italic: :normal, stretch: :normal
1001
1019
 
1020
+ string('This ') {
1021
+ font size: 20, weight: :bold, italic: :normal, stretch: :normal
1022
+ color r: 128, g: 0, b: 0, a: 1
1023
+ }
1024
+
1025
+ string('is ') {
1026
+ font size: 20, weight: :bold, italic: :normal, stretch: :normal
1027
+ color r: 0, g: 128, b: 0, a: 1
1028
+ }
1029
+
1030
+ string('a ') {
1031
+ font size: 20, weight: :bold, italic: :normal, stretch: :normal
1032
+ color r: 0, g: 0, b: 128, a: 1
1033
+ }
1034
+
1035
+ string('short ') {
1036
+ font size: 20, weight: :bold, italic: :italic, stretch: :normal
1037
+ color r: 128, g: 128, b: 0, a: 1
1038
+ }
1039
+
1040
+ string('attributed ') {
1041
+ font size: 20, weight: :bold, italic: :normal, stretch: :normal
1042
+ color r: 0, g: 128, b: 128, a: 1
1043
+ }
1044
+
1045
+ string("string \n\n") {
1046
+ font size: 20, weight: :bold, italic: :normal, stretch: :normal
1047
+ color r: 128, g: 0, b: 128, a: 1
1048
+ }
1049
+
1002
1050
  string {
1003
1051
  font family: 'Georgia', size: 13, weight: :medium, italic: :normal, stretch: :normal
1004
- color r: 230, g: 100, b: 50, a: 0.5
1005
- background r: 230, g: 200, b: 250, a: 0.8
1052
+ color r: 0, g: 128, b: 255, a: 1
1053
+ background r: 255, g: 255, b: 0, a: 0.5
1006
1054
  underline :single
1007
1055
  underline_color :spelling
1008
1056
  open_type_features {
@@ -1015,13 +1063,14 @@ window('area text drawing') {
1015
1063
  "attributed string\n" \
1016
1064
  "spanning multiple lines\n\n"
1017
1065
  }
1018
-
1019
- string('This is a short unattributed string')
1020
1066
  }
1021
1067
  }
1022
1068
  }.show
1023
1069
  ```
1024
1070
 
1071
+ ![glimmer-dsl-libui-mac-area-text-drawing.png](/images/glimmer-dsl-libui-mac-area-text-drawing.png)
1072
+
1073
+
1025
1074
  You may checkout [examples/basic_draw_text.rb](#basic-draw-text) and [examples/custom_draw_text.rb](#custom-draw-text) for examples of using `text` inside `area`.
1026
1075
 
1027
1076
  Mac | Windows | Linux
@@ -1365,8 +1414,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1365
1414
  - 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
1415
  - All boolean property writers accept `true`/`false` in addition to `1`/`0` in Ruby
1367
1416
  - 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
1417
+ - Automatically allocate font descriptors upon instantiating `font_button` controls and free them when destroying `font_button` controls
1418
+ - Automatically allocate color value pointers upon instantiating `color_button` controls and free them when destroying `color_button` controls
1370
1419
  - On the Mac, if no `menu` items were added, an automatic `quit_menu_item` is added to enable quitting with CTRL+Q
1371
1420
  - When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
1372
1421
  - When destroying a control nested under a `form`, it is automatically deleted from the form's children
@@ -1396,14 +1445,16 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
1396
1445
 
1397
1446
  ### Custom Keywords
1398
1447
 
1399
- 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 maintainbility.
1400
-
1448
+ 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.
1449
+
1401
1450
  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.
1402
1451
 
1403
1452
  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)`).
1404
1453
 
1405
1454
  To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
1406
1455
 
1456
+ 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`).
1457
+
1407
1458
  Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
1408
1459
 
1409
1460
  ```ruby
@@ -1501,11 +1552,11 @@ window('Method-Based Custom Keyword') {
1501
1552
 
1502
1553
  ![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
1503
1554
 
1504
- 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.
1555
+ 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.
1505
1556
 
1506
1557
  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.
1507
1558
 
1508
- Learn more from custom keyword usage in [Method-Based Custom Keyword](#method-based-custom-keyword), [Histogram](#histogram), and [Tetris](#tetris) examples.
1559
+ 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.
1509
1560
 
1510
1561
  ### Observer Pattern
1511
1562
 
@@ -1778,9 +1829,9 @@ Data-bound model attribute can be:
1778
1829
  - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1779
1830
 
1780
1831
  Data-binding options include:
1781
- - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1832
+ - `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
1782
1833
  - `on_read {|value| ...}`: converts value read from Model to update the View.
1783
- - `after_read {|converted_value| ...}`: performs an operation after read from Model and updating the View.
1834
+ - `after_read {|converted_value| ...}`: performs an operation after read from Model to update View.
1784
1835
  - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1785
1836
  - `on_write {|value| ...}`: converts value read from View to update the Model.
1786
1837
  - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
@@ -1812,6 +1863,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1812
1863
  - `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)
1813
1864
  - `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
1814
1865
  - `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.
1866
+ - `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.
1815
1867
  - 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.
1816
1868
  - As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
1817
1869
  - `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
@@ -1836,6 +1888,8 @@ For Mac, consider [Platypus](https://github.com/sveinbjornt/Platypus) (builds a
1836
1888
 
1837
1889
  For Linux, simply package your app as a [Ruby Gem](https://guides.rubygems.org/what-is-a-gem/) and [build rpm package from Ruby Gem](https://www.redpill-linpro.com/sysadvent/2015/12/07/building-rpms-from-gems.html) or [build deb package from Ruby Gem](https://openpreservation.org/blogs/building-debian-package-ruby-program/).
1838
1890
 
1891
+ Also, there is a promising project called [ruby-packer](https://github.com/pmq20/ruby-packer) that supports all platforms.
1892
+
1839
1893
  ## Glimmer Style Guide
1840
1894
 
1841
1895
  **1 - Control arguments are always wrapped by parentheses.**
@@ -8375,7 +8429,7 @@ window('Login') {
8375
8429
 
8376
8430
  #### Method-Based Custom Keyword
8377
8431
 
8378
- [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 maintainbility.
8432
+ [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.
8379
8433
 
8380
8434
  This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
8381
8435
 
@@ -8596,6 +8650,317 @@ window('Method-Based Custom Keyword') {
8596
8650
  }.show
8597
8651
  ```
8598
8652
 
8653
+ #### Area-Based Custom Controls
8654
+
8655
+ [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.
8656
+
8657
+ 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.
8658
+
8659
+ [examples/area_based_custom_controls.rb](examples/area_based_custom_controls.rb)
8660
+
8661
+ Run with this command from the root of the project if you cloned the project:
8662
+
8663
+ ```
8664
+ ruby -r './lib/glimmer-dsl-libui' examples/area_based_custom_controls.rb
8665
+ ```
8666
+
8667
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
8668
+
8669
+ ```
8670
+ ruby -r glimmer-dsl-libui -e "require 'examples/area_based_custom_controls'"
8671
+ ```
8672
+
8673
+ Mac | Windows | Linux
8674
+ ----|---------|------
8675
+ ![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)
8676
+
8677
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8678
+
8679
+ ```ruby
8680
+ require 'glimmer-dsl-libui'
8681
+
8682
+ class AreaBasedCustomControls
8683
+ include Glimmer
8684
+
8685
+ attr_accessor :label_width, :label_height, :label_font_descriptor,
8686
+ :label_text_color, :label_background_fill, :label_border_stroke,
8687
+ :label_text_x, :label_text_y,
8688
+ :button_width, :button_height, :button_font_descriptor,
8689
+ :button_text_color, :button_background_fill, :button_border_stroke,
8690
+ :button_text_x, :button_text_y
8691
+
8692
+ def initialize
8693
+ self.label_width = 335
8694
+ self.label_height = 50
8695
+ self.label_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 16, weight: :bold, italic: :italic}
8696
+ self.label_text_color = :red
8697
+ self.label_background_fill = :yellow
8698
+ self.label_border_stroke = :limegreen
8699
+
8700
+ self.button_width = 150
8701
+ self.button_height = 50
8702
+ self.button_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 36, weight: OS.linux? ? :normal : :bold, italic: :italic}
8703
+ self.button_text_color = :green
8704
+ self.button_background_fill = :yellow
8705
+ self.button_border_stroke = :limegreen
8706
+ end
8707
+
8708
+ def rebuild_text_label
8709
+ @text_label.destroy
8710
+ @text_label_vertical_box.content { # re-open vertical box content and shove in a new label
8711
+ @text_label = text_label('This is a text label.',
8712
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
8713
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
8714
+ text_x: label_text_x, text_y: label_text_y)
8715
+ }
8716
+ end
8717
+
8718
+ def rebuild_push_button
8719
+ @push_button.destroy
8720
+ @push_button_vertical_box.content { # re-open vertical box content and shove in a new button
8721
+ @push_button = push_button('Push',
8722
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
8723
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
8724
+ text_x: button_text_x, text_y: button_text_y) {
8725
+ on_mouse_up do
8726
+ message_box('Button Pushed', 'Thank you for pushing the button!')
8727
+ end
8728
+ }
8729
+ }
8730
+ end
8731
+
8732
+ def launch
8733
+ window('Area-Based Custom Controls', 385, 385) { |w|
8734
+ margined true
8735
+
8736
+ tab {
8737
+ tab_item('Text Label') {
8738
+ @text_label_vertical_box = vertical_box {
8739
+ vertical_box {
8740
+ 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)
8741
+
8742
+ horizontal_box {
8743
+ label('Width')
8744
+ spinbox(1, 1000) {
8745
+ value <=> [self, :label_width, after_write: method(:rebuild_text_label)]
8746
+ }
8747
+ }
8748
+
8749
+ horizontal_box {
8750
+ label('Height')
8751
+ spinbox(1, 1000) {
8752
+ value <=> [self, :label_height, after_write: method(:rebuild_text_label)]
8753
+ }
8754
+ }
8755
+
8756
+ horizontal_box {
8757
+ label('Font')
8758
+ font_button {
8759
+ font <=> [self, :label_font_descriptor, after_write: method(:rebuild_text_label)]
8760
+ }
8761
+ }
8762
+
8763
+ horizontal_box {
8764
+ label('Text Color')
8765
+ color_button {
8766
+ color <=> [self, :label_text_color, after_write: method(:rebuild_text_label)]
8767
+ }
8768
+ }
8769
+
8770
+ horizontal_box {
8771
+ label('Background Color')
8772
+ color_button {
8773
+ color <=> [self, :label_background_fill, after_write: method(:rebuild_text_label)]
8774
+ }
8775
+ }
8776
+
8777
+ horizontal_box {
8778
+ label('Border Color')
8779
+ color_button {
8780
+ color <=> [self, :label_border_stroke, after_write: method(:rebuild_text_label)]
8781
+ }
8782
+ }
8783
+
8784
+ horizontal_box {
8785
+ label('Text X (0=centered)')
8786
+ spinbox(0, 1000) {
8787
+ 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)]
8788
+ }
8789
+ }
8790
+
8791
+ horizontal_box {
8792
+ label('Text Y (0=centered)')
8793
+ spinbox(0, 1000) {
8794
+ 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)]
8795
+ }
8796
+ }
8797
+ }
8798
+
8799
+ @text_label = text_label('This is a text label.',
8800
+ width: label_width, height: label_height, font_descriptor: label_font_descriptor,
8801
+ background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
8802
+ text_x: label_text_x, text_y: label_text_y)
8803
+ }
8804
+ }
8805
+
8806
+ tab_item('Push Button') {
8807
+ @push_button_vertical_box = vertical_box {
8808
+ vertical_box {
8809
+ 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)
8810
+
8811
+ horizontal_box {
8812
+ label('Width')
8813
+ spinbox(1, 1000) {
8814
+ value <=> [self, :button_width, after_write: method(:rebuild_push_button)]
8815
+ }
8816
+ }
8817
+
8818
+ horizontal_box {
8819
+ label('Height')
8820
+ spinbox(1, 1000) {
8821
+ value <=> [self, :button_height, after_write: method(:rebuild_push_button)]
8822
+ }
8823
+ }
8824
+
8825
+ horizontal_box {
8826
+ label('Font')
8827
+ font_button {
8828
+ font <=> [self, :button_font_descriptor, after_write: method(:rebuild_push_button)]
8829
+ }
8830
+ }
8831
+
8832
+ horizontal_box {
8833
+ label('Text Color')
8834
+ color_button {
8835
+ color <=> [self, :button_text_color, after_write: method(:rebuild_push_button)]
8836
+ }
8837
+ }
8838
+
8839
+ horizontal_box {
8840
+ label('Background Color')
8841
+ color_button {
8842
+ color <=> [self, :button_background_fill, after_write: method(:rebuild_push_button)]
8843
+ }
8844
+ }
8845
+
8846
+ horizontal_box {
8847
+ label('Border Color')
8848
+ color_button {
8849
+ color <=> [self, :button_border_stroke, after_write: method(:rebuild_push_button)]
8850
+ }
8851
+ }
8852
+
8853
+ horizontal_box {
8854
+ label('Text X (0=centered)')
8855
+ spinbox(0, 1000) {
8856
+ 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)]
8857
+ }
8858
+ }
8859
+
8860
+ horizontal_box {
8861
+ label('Text Y (0=centered)')
8862
+ spinbox(0, 1000) {
8863
+ 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)]
8864
+ }
8865
+ }
8866
+ }
8867
+
8868
+ @push_button = push_button('Push',
8869
+ width: button_width, height: button_height, font_descriptor: button_font_descriptor,
8870
+ background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
8871
+ text_x: button_text_x, text_y: button_text_y) {
8872
+ on_mouse_up do
8873
+ message_box('Button Pushed', 'Thank you for pushing the button!')
8874
+ end
8875
+ }
8876
+ }
8877
+ }
8878
+ }
8879
+ }.show
8880
+ end
8881
+
8882
+ # text label (area-based custom control) built with vector graphics on top of area.
8883
+ #
8884
+ # background_fill is transparent by default.
8885
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
8886
+ # border_stroke is transparent by default.
8887
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
8888
+ # text_x and text_y are the offset of the label text in relation to its top-left corner.
8889
+ # When text_x, text_y are left nil, the text is automatically centered in the label area.
8890
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
8891
+ # in that case, pass in text_x, and text_y manually.
8892
+ def text_label(label_text,
8893
+ width: 80, height: 30, font_descriptor: {},
8894
+ background_fill: {a: 0}, text_color: :black, border_stroke: {a: 0},
8895
+ text_x: nil, text_y: nil,
8896
+ &content)
8897
+ area { |the_area|
8898
+ rectangle(1, 1, width, height) {
8899
+ fill background_fill
8900
+ }
8901
+ rectangle(1, 1, width, height) {
8902
+ stroke border_stroke
8903
+ }
8904
+
8905
+ text_height = (font_descriptor[:size] || 12) * (OS.mac? ? 0.75 : 1.35)
8906
+ text_width = (text_height * label_text.size) * (OS.mac? ? 0.75 : 0.60)
8907
+ text_x ||= (width - text_width) / 2.0
8908
+ text_y ||= (height - 4 - text_height) / 2.0
8909
+ text(text_x, text_y, width) {
8910
+ string(label_text) {
8911
+ color text_color
8912
+ font font_descriptor
8913
+ }
8914
+ }
8915
+
8916
+ content&.call(the_area)
8917
+ }
8918
+ end
8919
+
8920
+ # push button (area-based custom control) built with vector graphics on top of area.
8921
+ #
8922
+ # background_fill is white by default.
8923
+ # background_fill can accept a single color or gradient stops just as per `fill` property in README.
8924
+ # border_stroke is black by default.
8925
+ # border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
8926
+ # text_x and text_y are the offset of the button text in relation to its top-left corner.
8927
+ # When text_x, text_y are left nil, the text is automatically centered in the button area.
8928
+ # Sometimes, the centering calculation is not perfect due to using a custom font, so
8929
+ # in that case, pass in text_x, and text_y manually.
8930
+ #
8931
+ # reuses the text_label custom control
8932
+ def push_button(button_text,
8933
+ width: 80, height: 30, font_descriptor: {},
8934
+ background_fill: :white, text_color: :black, border_stroke: {r: 201, g: 201, b: 201},
8935
+ text_x: nil, text_y: nil,
8936
+ &content)
8937
+ text_label(button_text,
8938
+ width: width, height: height, font_descriptor: font_descriptor,
8939
+ background_fill: background_fill, text_color: text_color, border_stroke: border_stroke,
8940
+ text_x: text_x, text_y: text_y) { |the_area|
8941
+
8942
+ # dig into the_area content and grab elements to modify in mouse listeners below
8943
+ background_rectangle = the_area.children[0]
8944
+ button_string = the_area.children[2].children[0]
8945
+
8946
+ on_mouse_down do
8947
+ 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}]}
8948
+ button_string.color = :white
8949
+ end
8950
+
8951
+ on_mouse_up do
8952
+ background_rectangle.fill = background_fill
8953
+ button_string.color = text_color
8954
+ end
8955
+
8956
+ content&.call(the_area)
8957
+ }
8958
+ end
8959
+ end
8960
+
8961
+ AreaBasedCustomControls.new.launch
8962
+ ```
8963
+
8599
8964
  #### Midi Player
8600
8965
 
8601
8966
  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).
@@ -8994,6 +9359,8 @@ TinyMidiPlayer.new
8994
9359
 
8995
9360
  Snake provides an example of building a desktop application [test-first](/spec/examples/snake/model/game_spec.rb) following the MVP ([Model](/examples/snake/model/game.rb) / [View](/examples/snake.rb) / [Presenter](/examples/snake/presenter/grid.rb)) architectural pattern.
8996
9361
 
9362
+ Use arrows to move and spacebar to pause/resume.
9363
+
8997
9364
  [examples/snake.rb](examples/snake.rb)
8998
9365
 
8999
9366
  Run with this command from the root of the project if you cloned the project:
@@ -9049,7 +9416,7 @@ class Snake
9049
9416
  end
9050
9417
 
9051
9418
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
9052
- unless @game.over?
9419
+ unless @game.paused? || @game.over?
9053
9420
  process_queued_keypress
9054
9421
  @game.snake.move
9055
9422
  end
@@ -9090,7 +9457,11 @@ class Snake
9090
9457
  }
9091
9458
 
9092
9459
  on_key_up do |area_key_event|
9093
- @keypress_queue << area_key_event[:ext_key]
9460
+ if area_key_event[:key] == ' '
9461
+ @game.toggle_pause
9462
+ else
9463
+ @keypress_queue << area_key_event[:ext_key]
9464
+ end
9094
9465
  end
9095
9466
  }
9096
9467
  end
@@ -9149,7 +9520,7 @@ class Snake
9149
9520
  end
9150
9521
 
9151
9522
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
9152
- unless @game.over?
9523
+ unless @game.paused? || @game.over?
9153
9524
  process_queued_keypress
9154
9525
  @game.snake.move
9155
9526
  end
@@ -9192,7 +9563,11 @@ class Snake
9192
9563
  }
9193
9564
 
9194
9565
  on_key_up do |area_key_event|
9195
- @keypress_queue << area_key_event[:ext_key]
9566
+ if area_key_event[:key] == ' '
9567
+ @game.toggle_pause
9568
+ else
9569
+ @keypress_queue << area_key_event[:ext_key]
9570
+ end
9196
9571
  end
9197
9572
  }
9198
9573
  end
@@ -9226,7 +9601,7 @@ ruby -r glimmer-dsl-libui -e "require 'examples/tetris'"
9226
9601
 
9227
9602
  Mac | Windows | Linux
9228
9603
  ----|---------|------
9229
- ![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png) ![glimmer-dsl-libui-mac-tetris-game-over.png](images/glimmer-dsl-libui-mac-tetris-game-over.png) ![glimmer-dsl-libui-mac-tetris-high-scores.png](images/glimmer-dsl-libui-mac-tetris-high-scores.png) | ![glimmer-dsl-libui-windows-tetris.png](images/glimmer-dsl-libui-windows-tetris.png) ![glimmer-dsl-libui-windows-tetris-game-over.png](images/glimmer-dsl-libui-windows-tetris-game-over.png) ![glimmer-dsl-libui-windows-tetris-high-scores.png](images/glimmer-dsl-libui-windows-tetris-high-scores.png) | ![glimmer-dsl-libui-linux-tetris.png](images/glimmer-dsl-libui-linux-tetris.png) ![glimmer-dsl-libui-linux-tetris-game-over.png](images/glimmer-dsl-libui-linux-tetris-game-over.png) ![glimmer-dsl-libui-linux-tetris-high-scores.png](images/glimmer-dsl-libui-linux-tetris-high-scores.png)
9604
+ ![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)
9230
9605
 
9231
9606
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
9232
9607
 
@@ -9304,18 +9679,31 @@ class Tetris
9304
9679
 
9305
9680
  Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
9306
9681
  Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
9307
- observe(@game.preview_playfield[row][column], :color) do |new_color|
9682
+ preview_updater = proc do
9308
9683
  Glimmer::LibUI.queue_main do
9684
+ new_color = @game.preview_playfield[row][column].color
9309
9685
  color = Glimmer::LibUI.interpret_color(new_color)
9310
9686
  block = @preview_playfield_blocks[row][column]
9311
- block[:background_square].fill = color
9312
- block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
9313
- block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9314
- block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9315
- block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9316
- block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
9687
+ if @game.show_preview_tetromino?
9688
+ block[:background_square].fill = color
9689
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
9690
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9691
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9692
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
9693
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
9694
+ else
9695
+ transparent_color = {r: 255, g: 255, b: 255, a: 0}
9696
+ block[:background_square].fill = transparent_color
9697
+ block[:top_bevel_edge].fill = transparent_color
9698
+ block[:right_bevel_edge].fill = transparent_color
9699
+ block[:bottom_bevel_edge].fill = transparent_color
9700
+ block[:left_bevel_edge].fill = transparent_color
9701
+ block[:border_square].stroke = transparent_color
9702
+ end
9317
9703
  end
9318
9704
  end
9705
+ observe(@game.preview_playfield[row][column], :color, &preview_updater)
9706
+ observe(@game, :show_preview_tetromino, &preview_updater)
9319
9707
  end
9320
9708
  end
9321
9709
 
@@ -9363,6 +9751,12 @@ class Tetris
9363
9751
  }
9364
9752
 
9365
9753
  menu('View') {
9754
+ check_menu_item('Show Next Block Preview') {
9755
+ checked <=> [@game, :show_preview_tetromino]
9756
+ }
9757
+
9758
+ separator_menu_item
9759
+
9366
9760
  menu_item('Show High Scores') {
9367
9761
  on_clicked do
9368
9762
  show_high_scores
@@ -9374,18 +9768,20 @@ class Tetris
9374
9768
  @game.clear_high_scores!
9375
9769
  }
9376
9770
  }
9771
+
9772
+ separator_menu_item
9377
9773
  }
9378
9774
 
9379
9775
  menu('Options') {
9380
- radio_menu_item('Instant Down on Up Arrow') { |r|
9776
+ radio_menu_item('Instant Down on Up Arrow') {
9381
9777
  checked <=> [@game, :instant_down_on_up]
9382
9778
  }
9383
9779
 
9384
- radio_menu_item('Rotate Right on Up Arrow') { |r|
9780
+ radio_menu_item('Rotate Right on Up Arrow') {
9385
9781
  checked <=> [@game, :rotate_right_on_up]
9386
9782
  }
9387
9783
 
9388
- radio_menu_item('Rotate Left on Up Arrow') { |r|
9784
+ radio_menu_item('Rotate Left on Up Arrow') {
9389
9785
  checked <=> [@game, :rotate_left_on_up]
9390
9786
  }
9391
9787
  }
@@ -9432,7 +9828,7 @@ class Tetris
9432
9828
  block = {}
9433
9829
  bevel_pixel_size = 0.16 * block_size.to_f
9434
9830
  color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
9435
- area {
9831
+ block[:area] = area {
9436
9832
  block[:background_square] = square(0, 0, block_size) {
9437
9833
  fill color
9438
9834
  }
@@ -9508,12 +9904,6 @@ class Tetris
9508
9904
 
9509
9905
  def score_board(block_size: , &extra_content)
9510
9906
  vertical_box {
9511
- horizontal_box {
9512
- label # filler
9513
- @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
9514
- label # filler
9515
- }
9516
-
9517
9907
  horizontal_box {
9518
9908
  label # filler
9519
9909
  grid {
@@ -9554,6 +9944,12 @@ class Tetris
9554
9944
  }
9555
9945
  label # filler
9556
9946
  }
9947
+
9948
+ horizontal_box {
9949
+ label # filler
9950
+ @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
9951
+ label # filler
9952
+ }
9557
9953
 
9558
9954
  extra_content&.call
9559
9955
  }
@@ -9593,7 +9989,7 @@ class Tetris
9593
9989
 
9594
9990
  def show_about_dialog
9595
9991
  Glimmer::LibUI.queue_main do
9596
- msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
9992
+ msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021-2022 Andy Maleh')
9597
9993
  end
9598
9994
  end
9599
9995
  end
@@ -10103,6 +10499,10 @@ https://github.com/AndyObtiva/befunge98/tree/gui
10103
10499
 
10104
10500
  https://github.com/iraamaro/i3off-gtk-ruby
10105
10501
 
10502
+ ### Chess
10503
+
10504
+ https://github.com/mikeweber/chess
10505
+
10106
10506
  ## Process
10107
10507
 
10108
10508
  [Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
@@ -10162,7 +10562,7 @@ Note that the latest development sometimes takes place in the [development](http
10162
10562
 
10163
10563
  [MIT](LICENSE.txt)
10164
10564
 
10165
- Copyright (c) 2021 Andy Maleh
10565
+ Copyright (c) 2021-2022 Andy Maleh
10166
10566
 
10167
10567
  --
10168
10568