glimmer-dsl-libui 0.4.6 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +29 -0
  3. data/README.md +601 -323
  4. data/VERSION +1 -1
  5. data/examples/button_counter.rb +2 -1
  6. data/examples/cpu_percentage.rb +36 -0
  7. data/examples/date_time_picker.rb +19 -14
  8. data/examples/date_time_picker2.rb +20 -0
  9. data/examples/meta_example.rb +17 -6
  10. data/examples/midi_player.rb +5 -6
  11. data/examples/midi_player2.rb +83 -0
  12. data/examples/midi_player3.rb +84 -0
  13. data/examples/snake.rb +19 -10
  14. data/examples/snake2.rb +97 -0
  15. data/examples/tetris.rb +15 -18
  16. data/examples/tic_tac_toe.rb +1 -0
  17. data/examples/tic_tac_toe2.rb +84 -0
  18. data/glimmer-dsl-libui.gemspec +0 -0
  19. data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +4 -0
  20. data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -2
  21. data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +17 -2
  22. data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +4 -0
  23. data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +4 -0
  24. data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -2
  25. data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +5 -1
  26. data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +4 -0
  27. data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +16 -4
  28. data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -2
  29. data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +19 -0
  30. data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -2
  31. data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -2
  32. data/lib/glimmer/libui/control_proxy.rb +2 -2
  33. data/lib/glimmer/libui/data_bindable.rb +27 -2
  34. metadata +8 -2
data/README.md CHANGED
@@ -1,9 +1,9 @@
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.6
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.10
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 library. No need to pre-install any prerequisites. Just install the gem and have platform-independent native GUI that just works!
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!
7
7
 
8
8
  Mac | Windows | Linux
9
9
  ----|---------|------
@@ -267,6 +267,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
267
267
  - [Button Counter](#button-counter)
268
268
  - [Color The Circles](#color-the-circles)
269
269
  - [Control Gallery](#control-gallery)
270
+ - [CPU Percentage](#cpu-percentage)
270
271
  - [Custom Draw Text](#custom-draw-text)
271
272
  - [Dynamic Area](#dynamic-area)
272
273
  - [Editable Column Table](#editable-column-table)
@@ -373,7 +374,7 @@ gem install glimmer-dsl-libui
373
374
  Or install via Bundler `Gemfile`:
374
375
 
375
376
  ```ruby
376
- gem 'glimmer-dsl-libui', '~> 0.4.6'
377
+ gem 'glimmer-dsl-libui', '~> 0.4.10'
377
378
  ```
378
379
 
379
380
  Add `require 'glimmer-dsl-libui'` at the top, and then `include Glimmer` into the top-level main object for testing or into an actual class for serious usage.
@@ -460,7 +461,7 @@ Keyword(Args) | Properties | Listeners
460
461
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
461
462
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
462
463
  `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
463
- `combobox` | `items` (`Array` of `String`), `selected` (`Integer`) | `on_selected`
464
+ `combobox` | `items` (`Array` of `String`), `selected` (`Integer`), `selected_item` (`String`) | `on_selected`
464
465
  `color_button` | `color` (Array of `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float`), `red` as `Float`, `green` as `Float`, `blue` as `Float`, `alpha` as `Float` | `on_changed`
465
466
  `date_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
466
467
  `date_time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`, `mday` as `Integer`, `mon` as `Integer`, `year` as `Integer`, `wday` as `Integer`, `yday` as `Integer`, `dst` as Boolean) | `on_changed`
@@ -627,11 +628,11 @@ class FormTable
627
628
 
628
629
  def initialize
629
630
  @data = [
630
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
631
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
632
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
633
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
634
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
631
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
632
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
633
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
634
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
635
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
635
636
  ]
636
637
  end
637
638
 
@@ -1382,14 +1383,24 @@ Data-binding supports utilizing the [MVP (Model View Presenter)](https://en.wiki
1382
1383
  ![MVP](https://www.researchgate.net/profile/Gilles-Perrouin/publication/320249584/figure/fig8/AS:668260987068418@1536337243385/Model-view-presenter-architecture.png)
1383
1384
 
1384
1385
  [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) supports bidirectional (two-way) data-binding of the following controls/properties via the `<=>` operator (indicating data is moving in both directions between View and Model):
1386
+ - `checkbox`: `checked`
1387
+ - `check_menu_item`: `checked`
1385
1388
  - `color_button`: `color`
1389
+ - `combobox`: `selected`, `selected_item`
1390
+ - `date_picker`: `time`
1391
+ - `date_time_picker`: `time`
1392
+ - `editable_combobox`: `text`
1386
1393
  - `entry`: `text`
1387
1394
  - `font_button`: `font`
1388
1395
  - `multiline_entry`: `text`
1389
1396
  - `non_wrapping_multiline_entry`: `text`
1397
+ - `radio_buttons`: `selected`
1398
+ - `radio_menu_item`: `checked`
1390
1399
  - `search_entry`: `text`
1391
1400
  - `slider`: `value`
1392
1401
  - `spinbox`: `value`
1402
+ - `table`: `cell_rows` (explicit data-binding by using `<=>` and [implicit data-binding](#table-api) by assigning value directly)
1403
+ - `time_picker`: `time`
1393
1404
 
1394
1405
  Example of bidirectional data-binding:
1395
1406
 
@@ -1439,6 +1450,11 @@ To summarize the data-binding API:
1439
1450
 
1440
1451
  This is also known as the [Glimmer Shine](https://github.com/AndyObtiva/glimmer-dsl-swt/blob/master/docs/reference/GLIMMER_GUI_DSL_SYNTAX.md#shine) syntax for data-binding, a [Glimmer](https://github.com/AndyObtiva/glimmer)-only unique innovation that takes advantage of [Ruby](https://www.ruby-lang.org/en/)'s highly expressive syntax and malleable DSL support.
1441
1452
 
1453
+ Data-bound model attribute can be:
1454
+ - **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
1455
+ - **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
1456
+ - **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
1457
+
1442
1458
  Data-binding options include:
1443
1459
  - `before_read {|value| ...}`: performs an operation before reading data from Model to update the View.
1444
1460
  - `on_read {|value| ...}`: converts value read from Model to update the View.
@@ -1446,7 +1462,7 @@ Data-binding options include:
1446
1462
  - `before_write {|value| ...}`: performs an operation before writing data to Model from View.
1447
1463
  - `on_write {|value| ...}`: converts value read from View to update the Model.
1448
1464
  - `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
1449
- - `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb))
1465
+ - `computed_by attribute` or `computed_by [attribute1, attribute2, ...]`: indicates model attribute is computed from specified attribute(s), thus updated when they are updated (see in [Login example version 2](/examples/login2.rb)). That is known as "computed data-binding".
1450
1466
 
1451
1467
  Note that with both `on_read` and `on_write` converters, you could pass a `Symbol` representing the name of a method on the value object to invoke.
1452
1468
 
@@ -1479,268 +1495,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
1479
1495
  ### Original API
1480
1496
 
1481
1497
  Here are all the lower-level [LibUI](https://github.com/kojix2/LibUI) API methods utilized by [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
1482
- - `alloc_control`
1483
- - `area_begin_user_window_move`
1484
- - `area_begin_user_window_resize`
1485
- - `area_queue_redraw_all`
1486
- - `area_scroll_to`
1487
- - `area_set_size`
1488
- - `attribute_color`
1489
- - `attribute_family`
1490
- - `attribute_features`
1491
- - `attribute_get_type`
1492
- - `attribute_italic`
1493
- - `attribute_size`
1494
- - `attribute_stretch`
1495
- - `attribute_underline`
1496
- - `attribute_underline_color`
1497
- - `attribute_weight`
1498
- - `attributed_string_append_unattributed`
1499
- - `attributed_string_byte_index_to_grapheme`
1500
- - `attributed_string_delete`
1501
- - `attributed_string_for_each_attribute`
1502
- - `attributed_string_grapheme_to_byte_index`
1503
- - `attributed_string_insert_at_unattributed`
1504
- - `attributed_string_len`
1505
- - `attributed_string_num_graphemes`
1506
- - `attributed_string_set_attribute`
1507
- - `attributed_string_string`
1508
- - `box_append`
1509
- - `box_delete`
1510
- - `box_padded`
1511
- - `box_set_padded`
1512
- - `button_on_clicked`
1513
- - `button_set_text`
1514
- - `button_text`
1515
- - `checkbox_checked`
1516
- - `checkbox_on_toggled`
1517
- - `checkbox_set_checked`
1518
- - `checkbox_set_text`
1519
- - `checkbox_text`
1520
- - `color_button_color`
1521
- - `color_button_on_changed`
1522
- - `color_button_set_color`
1523
- - `combobox_append`
1524
- - `combobox_on_selected`
1525
- - `combobox_selected`
1526
- - `combobox_set_selected`
1527
- - `control_destroy`
1528
- - `control_disable`
1529
- - `control_enable`
1530
- - `control_enabled`
1531
- - `control_enabled_to_user`
1532
- - `control_handle`
1533
- - `control_hide`
1534
- - `control_parent`
1535
- - `control_set_parent`
1536
- - `control_show`
1537
- - `control_toplevel`
1538
- - `control_verify_set_parent`
1539
- - `control_visible`
1540
- - `date_time_picker_on_changed`
1541
- - `date_time_picker_set_time`
1542
- - `date_time_picker_time`
1543
- - `draw_clip`
1544
- - `draw_fill`
1545
- - `draw_free_path`
1546
- - `draw_free_text_layout`
1547
- - `draw_matrix_invert`
1548
- - `draw_matrix_invertible`
1549
- - `draw_matrix_multiply`
1550
- - `draw_matrix_rotate`
1551
- - `draw_matrix_scale`
1552
- - `draw_matrix_set_identity`
1553
- - `draw_matrix_skew`
1554
- - `draw_matrix_transform_point`
1555
- - `draw_matrix_transform_size`
1556
- - `draw_matrix_translate`
1557
- - `draw_new_path`
1558
- - `draw_new_text_layout`
1559
- - `draw_path_add_rectangle`
1560
- - `draw_path_arc_to`
1561
- - `draw_path_bezier_to`
1562
- - `draw_path_close_figure`
1563
- - `draw_path_end`
1564
- - `draw_path_line_to`
1565
- - `draw_path_new_figure`
1566
- - `draw_path_new_figure_with_arc`
1567
- - `draw_restore`
1568
- - `draw_save`
1569
- - `draw_stroke`
1570
- - `draw_text`
1571
- - `draw_text_layout_extents`
1572
- - `draw_transform`
1573
- - `editable_combobox_append`
1574
- - `editable_combobox_on_changed`
1575
- - `editable_combobox_set_text`
1576
- - `editable_combobox_text`
1577
- - `entry_on_changed`
1578
- - `entry_read_only`
1579
- - `entry_set_read_only`
1580
- - `entry_set_text`
1581
- - `entry_text`
1582
- - `ffi_lib`
1583
- - `ffi_lib=`
1584
- - `font_button_font`
1585
- - `font_button_on_changed`
1586
- - `form_append`
1587
- - `form_delete`
1588
- - `form_padded`
1589
- - `form_set_padded`
1590
- - `free_attribute`
1591
- - `free_attributed_string`
1592
- - `free_control`
1593
- - `free_font_button_font`
1594
- - `free_image`
1595
- - `free_init_error`
1596
- - `free_open_type_features`
1597
- - `free_table_model`
1598
- - `free_table_value`
1599
- - `free_text`
1600
- - `grid_append`
1601
- - `grid_insert_at`
1602
- - `grid_padded`
1603
- - `grid_set_padded`
1604
- - `group_margined`
1605
- - `group_set_child`
1606
- - `group_set_margined`
1607
- - `group_set_title`
1608
- - `group_title`
1609
- - `image_append`
1610
- - `init`
1611
- - `label_set_text`
1612
- - `label_text`
1613
- - `main`
1614
- - `main_step`
1615
- - `main_steps`
1616
- - `menu_append_about_item`
1617
- - `menu_append_check_item`
1618
- - `menu_append_item`
1619
- - `menu_append_preferences_item`
1620
- - `menu_append_quit_item`
1621
- - `menu_append_separator`
1622
- - `menu_item_checked`
1623
- - `menu_item_disable`
1624
- - `menu_item_enable`
1625
- - `menu_item_on_clicked`
1626
- - `menu_item_set_checked`
1627
- - `msg_box`
1628
- - `msg_box_error`
1629
- - `multiline_entry_append`
1630
- - `multiline_entry_on_changed`
1631
- - `multiline_entry_read_only`
1632
- - `multiline_entry_set_read_only`
1633
- - `multiline_entry_set_text`
1634
- - `multiline_entry_text`
1635
- - `new_area`
1636
- - `new_attributed_string`
1637
- - `new_background_attribute`
1638
- - `new_button`
1639
- - `new_checkbox`
1640
- - `new_color_attribute`
1641
- - `new_color_button`
1642
- - `new_combobox`
1643
- - `new_date_picker`
1644
- - `new_date_time_picker`
1645
- - `new_editable_combobox`
1646
- - `new_entry`
1647
- - `new_family_attribute`
1648
- - `new_features_attribute`
1649
- - `new_font_button`
1650
- - `new_form`
1651
- - `new_grid`
1652
- - `new_group`
1653
- - `new_horizontal_box`
1654
- - `new_horizontal_separator`
1655
- - `new_image`
1656
- - `new_italic_attribute`
1657
- - `new_label`
1658
- - `new_menu`
1659
- - `new_multiline_entry`
1660
- - `new_non_wrapping_multiline_entry`
1661
- - `new_open_type_features`
1662
- - `new_password_entry`
1663
- - `new_progress_bar`
1664
- - `new_radio_buttons`
1665
- - `new_scrolling_area`
1666
- - `new_search_entry`
1667
- - `new_size_attribute`
1668
- - `new_slider`
1669
- - `new_spinbox`
1670
- - `new_stretch_attribute`
1671
- - `new_tab`
1672
- - `new_table`
1673
- - `new_table_model`
1674
- - `new_table_value_color`
1675
- - `new_table_value_image`
1676
- - `new_table_value_int`
1677
- - `new_table_value_string`
1678
- - `new_time_picker`
1679
- - `new_underline_attribute`
1680
- - `new_underline_color_attribute`
1681
- - `new_vertical_box`
1682
- - `new_vertical_separator`
1683
- - `new_weight_attribute`
1684
- - `new_window`
1685
- - `on_should_quit`
1686
- - `open_file`
1687
- - `open_type_features_add`
1688
- - `open_type_features_clone`
1689
- - `open_type_features_for_each`
1690
- - `open_type_features_get`
1691
- - `open_type_features_remove`
1692
- - `progress_bar_set_value`
1693
- - `progress_bar_value`
1694
- - `queue_main`
1695
- - `quit`
1696
- - `radio_buttons_append`
1697
- - `radio_buttons_on_selected`
1698
- - `radio_buttons_selected`
1699
- - `radio_buttons_set_selected`
1700
- - `save_file`
1701
- - `slider_on_changed`
1702
- - `slider_set_value`
1703
- - `slider_value`
1704
- - `spinbox_on_changed`
1705
- - `spinbox_set_value`
1706
- - `spinbox_value`
1707
- - `tab_append`
1708
- - `tab_delete`
1709
- - `tab_insert_at`
1710
- - `tab_margined`
1711
- - `tab_num_pages`
1712
- - `tab_set_margined`
1713
- - `table_append_button_column`
1714
- - `table_append_checkbox_column`
1715
- - `table_append_checkbox_text_column`
1716
- - `table_append_image_column`
1717
- - `table_append_image_text_column`
1718
- - `table_append_progress_bar_column`
1719
- - `table_append_text_column`
1720
- - `table_model_row_changed`
1721
- - `table_model_row_deleted`
1722
- - `table_model_row_inserted`
1723
- - `table_value_color`
1724
- - `table_value_get_type`
1725
- - `table_value_image`
1726
- - `table_value_int`
1727
- - `table_value_string`
1728
- - `timer`
1729
- - `uninit`
1730
- - `user_bug_cannot_set_parent_on_toplevel`
1731
- - `window_borderless`
1732
- - `window_content_size`
1733
- - `window_fullscreen`
1734
- - `window_margined`
1735
- - `window_on_closing`
1736
- - `window_on_content_size_changed`
1737
- - `window_set_borderless`
1738
- - `window_set_child`
1739
- - `window_set_content_size`
1740
- - `window_set_fullscreen`
1741
- - `window_set_margined`
1742
- - `window_set_title`
1743
- - `window_title`
1498
+ `alloc_control`, `append_features`, `area_begin_user_window_move`, `area_begin_user_window_resize`, `area_queue_redraw_all`, `area_scroll_to`, `area_set_size`, `attribute_color`, `attribute_family`, `attribute_features`, `attribute_get_type`, `attribute_italic`, `attribute_size`, `attribute_stretch`, `attribute_underline`, `attribute_underline_color`, `attribute_weight`, `attributed_string_append_unattributed`, `attributed_string_byte_index_to_grapheme`, `attributed_string_delete`, `attributed_string_for_each_attribute`, `attributed_string_grapheme_to_byte_index`, `attributed_string_insert_at_unattributed`, `attributed_string_len`, `attributed_string_num_graphemes`, `attributed_string_set_attribute`, `attributed_string_string`, `box_append`, `box_delete`, `box_padded`, `box_set_padded`, `button_on_clicked`, `button_set_text`, `button_text`, `checkbox_checked`, `checkbox_on_toggled`, `checkbox_set_checked`, `checkbox_set_text`, `checkbox_text`, `color_button_color`, `color_button_on_changed`, `color_button_set_color`, `combobox_append`, `combobox_on_selected`, `combobox_selected`, `combobox_set_selected`, `control_destroy`, `control_disable`, `control_enable`, `control_enabled`, `control_enabled_to_user`, `control_handle`, `control_hide`, `control_parent`, `control_set_parent`, `control_show`, `control_toplevel`, `control_verify_set_parent`, `control_visible`, `date_time_picker_on_changed`, `date_time_picker_set_time`, `date_time_picker_time`, `draw_clip`, `draw_fill`, `draw_free_path`, `draw_free_text_layout`, `draw_matrix_invert`, `draw_matrix_invertible`, `draw_matrix_multiply`, `draw_matrix_rotate`, `draw_matrix_scale`, `draw_matrix_set_identity`, `draw_matrix_skew`, `draw_matrix_transform_point`, `draw_matrix_transform_size`, `draw_matrix_translate`, `draw_new_path`, `draw_new_text_layout`, `draw_path_add_rectangle`, `draw_path_arc_to`, `draw_path_bezier_to`, `draw_path_close_figure`, `draw_path_end`, `draw_path_line_to`, `draw_path_new_figure`, `draw_path_new_figure_with_arc`, `draw_restore`, `draw_save`, `draw_stroke`, `draw_text`, `draw_text_layout_extents`, `draw_transform`, `editable_combobox_append`, `editable_combobox_on_changed`, `editable_combobox_set_text`, `editable_combobox_text`, `entry_on_changed`, `entry_read_only`, `entry_set_read_only`, `entry_set_text`, `entry_text`, `ffi_lib`, `ffi_lib=`, `font_button_font`, `font_button_on_changed`, `form_append`, `form_delete`, `form_padded`, `form_set_padded`, `free_attribute`, `free_attributed_string`, `free_control`, `free_font_button_font`, `free_image`, `free_init_error`, `free_open_type_features`, `free_table_model`, `free_table_value`, `free_text`, `grid_append`, `grid_insert_at`, `grid_padded`, `grid_set_padded`, `group_margined`, `group_set_child`, `group_set_margined`, `group_set_title`, `group_title`, `image_append`, `init`, `label_set_text`, `label_text`, `main`, `main_step`, `main_steps`, `menu_append_about_item`, `menu_append_check_item`, `menu_append_item`, `menu_append_preferences_item`, `menu_append_quit_item`, `menu_append_separator`, `menu_item_checked`, `menu_item_disable`, `menu_item_enable`, `menu_item_on_clicked`, `menu_item_set_checked`, `msg_box`, `msg_box_error`, `multiline_entry_append`, `multiline_entry_on_changed`, `multiline_entry_read_only`, `multiline_entry_set_read_only`, `multiline_entry_set_text`, `multiline_entry_text`, `new_area`, `new_attributed_string`, `new_background_attribute`, `new_button`, `new_checkbox`, `new_color_attribute`, `new_color_button`, `new_combobox`, `new_date_picker`, `new_date_time_picker`, `new_editable_combobox`, `new_entry`, `new_family_attribute`, `new_features_attribute`, `new_font_button`, `new_form`, `new_grid`, `new_group`, `new_horizontal_box`, `new_horizontal_separator`, `new_image`, `new_italic_attribute`, `new_label`, `new_menu`, `new_multiline_entry`, `new_non_wrapping_multiline_entry`, `new_open_type_features`, `new_password_entry`, `new_progress_bar`, `new_radio_buttons`, `new_scrolling_area`, `new_search_entry`, `new_size_attribute`, `new_slider`, `new_spinbox`, `new_stretch_attribute`, `new_tab`, `new_table`, `new_table_model`, `new_table_value_color`, `new_table_value_image`, `new_table_value_int`, `new_table_value_string`, `new_time_picker`, `new_underline_attribute`, `new_underline_color_attribute`, `new_vertical_box`, `new_vertical_separator`, `new_weight_attribute`, `new_window`, `on_should_quit`, `open_file`, `open_type_features_add`, `open_type_features_clone`, `open_type_features_for_each`, `open_type_features_get`, `open_type_features_remove`, `progress_bar_set_value`, `progress_bar_value`, `queue_main`, `quit`, `radio_buttons_append`, `radio_buttons_on_selected`, `radio_buttons_selected`, `radio_buttons_set_selected`, `save_file`, `slider_on_changed`, `slider_set_value`, `slider_value`, `spinbox_on_changed`, `spinbox_set_value`, `spinbox_value`, `tab_append`, `tab_delete`, `tab_insert_at`, `tab_margined`, `tab_num_pages`, `tab_set_margined`, `table_append_button_column`, `table_append_checkbox_column`, `table_append_checkbox_text_column`, `table_append_image_column`, `table_append_image_text_column`, `table_append_progress_bar_column`, `table_append_text_column`, `table_model_row_changed`, `table_model_row_deleted`, `table_model_row_inserted`, `table_value_color`, `table_value_get_type`, `table_value_image`, `table_value_int`, `table_value_string`, `timer`, `uninit`, `user_bug_cannot_set_parent_on_toplevel`, `window_borderless`, `window_content_size`, `window_fullscreen`, `window_margined`, `window_on_closing`, `window_on_content_size_changed`, `window_set_borderless`, `window_set_child`, `window_set_content_size`, `window_set_fullscreen`, `window_set_margined`, `window_set_title`, `window_title`
1744
1499
 
1745
1500
  To learn more about the [LibUI](https://github.com/kojix2/LibUI) API exposed through [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui):
1746
1501
  - Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
@@ -1878,7 +1633,7 @@ Example:
1878
1633
 
1879
1634
  ## Examples
1880
1635
 
1881
- The following examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) (with and without data-binding) as well as brand new examples.
1636
+ The following examples include reimplementions of the examples in the [LibUI](https://github.com/kojix2/LibUI) project utilizing the [Glimmer GUI DSL](#glimmer-gui-dsl-concepts) (with and without [data-binding](#data-binding)) as well as brand new examples.
1882
1637
 
1883
1638
  To browse all examples, simply launch the [Meta-Example](examples/meta_example.rb), which lists all examples and displays each example's code when selected. It also enables code editing to facilitate experimentation and learning.
1884
1639
 
@@ -2668,7 +2423,35 @@ UI.main
2668
2423
  UI.quit
2669
2424
  ```
2670
2425
 
2671
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
2426
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
2427
+
2428
+ ```ruby
2429
+ require 'glimmer-dsl-libui'
2430
+
2431
+ class DateTimePicker
2432
+ include Glimmer
2433
+
2434
+ attr_accessor :picked_time
2435
+
2436
+ def launch
2437
+ window('Date Time Pickers', 300, 200) {
2438
+ vertical_box {
2439
+ date_time_picker {
2440
+ time <=> [self, :picked_time, after_write: ->(time) { p time }]
2441
+ }
2442
+ }
2443
+
2444
+ on_closing do
2445
+ puts 'Bye Bye'
2446
+ end
2447
+ }.show
2448
+ end
2449
+ end
2450
+
2451
+ DateTimePicker.new.launch
2452
+ ```
2453
+
2454
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
2672
2455
 
2673
2456
  ```ruby
2674
2457
  require 'glimmer-dsl-libui'
@@ -3226,7 +3009,62 @@ Mac | Windows | Linux
3226
3009
  ----|---------|------
3227
3010
  ![glimmer-dsl-libui-mac-basic-table-button.png](images/glimmer-dsl-libui-mac-basic-table-button.png) ![glimmer-dsl-libui-mac-basic-table-button-deleted.png](images/glimmer-dsl-libui-mac-basic-table-button-deleted.png) | ![glimmer-dsl-libui-windows-basic-table-button.png](images/glimmer-dsl-libui-windows-basic-table-button.png) ![glimmer-dsl-libui-windows-basic-table-button-deleted.png](images/glimmer-dsl-libui-windows-basic-table-button-deleted.png) | ![glimmer-dsl-libui-linux-basic-table-button.png](images/glimmer-dsl-libui-linux-basic-table-button.png) ![glimmer-dsl-libui-linux-basic-table-button-deleted.png](images/glimmer-dsl-libui-linux-basic-table-button-deleted.png)
3228
3011
 
3229
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
3012
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with explicit [data-binding](#data-binding)):
3013
+
3014
+ ```ruby
3015
+ # frozen_string_literal: true
3016
+
3017
+ require 'glimmer-dsl-libui'
3018
+
3019
+ class BasicTableButton
3020
+ include Glimmer
3021
+
3022
+ attr_accessor :data
3023
+
3024
+ def initialize
3025
+ @data = [
3026
+ %w[cat meow delete],
3027
+ %w[dog woof delete],
3028
+ %w[chicken cock-a-doodle-doo delete],
3029
+ %w[horse neigh delete],
3030
+ %w[cow moo delete]
3031
+ ]
3032
+ end
3033
+
3034
+ def launch
3035
+ window('Animal sounds', 400, 200) {
3036
+ horizontal_box {
3037
+ table {
3038
+ text_column('Animal')
3039
+ text_column('Description')
3040
+ button_column('Action') {
3041
+ on_clicked do |row|
3042
+ # Option 1: direct data deletion is the simpler solution
3043
+ # @data.delete_at(row) # automatically deletes actual table row due to explicit data-binding
3044
+
3045
+ # Option 2: cloning only to demonstrate table row deletion upon explicit setting of data attribute (cloning is not recommended beyond demonstrating this point)
3046
+ new_data = @data.clone
3047
+ new_data.delete_at(row)
3048
+ self.data = new_data # automatically loses deleted table row due to explicit data-binding
3049
+ end
3050
+ }
3051
+
3052
+ cell_rows <=> [self, :data] # explicit data-binding of table cell_rows to self.data
3053
+
3054
+ on_changed do |row, type, row_data|
3055
+ puts "Row #{row} #{type}: #{row_data}"
3056
+ $stdout.flush
3057
+ end
3058
+ }
3059
+ }
3060
+ }.show
3061
+ end
3062
+ end
3063
+
3064
+ BasicTableButton.new.launch
3065
+ ```
3066
+
3067
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with implicit [data-binding](#data-binding)):
3230
3068
 
3231
3069
  ```ruby
3232
3070
  require 'glimmer-dsl-libui'
@@ -5005,7 +4843,8 @@ class ButtonCounter
5005
4843
  def launch
5006
4844
  window('Hello, Button!') {
5007
4845
  button {
5008
- text <= [self, :count, on_read: ->(count) {"Count: #{count}"}] # data-bind button text to self count, converting to string on read.
4846
+ # data-bind button text to self count, converting to string on read.
4847
+ text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
5009
4848
 
5010
4849
  on_clicked do
5011
4850
  self.count += 1
@@ -5649,6 +5488,71 @@ MAIN_WINDOW = window('Control Gallery', 600, 500) {
5649
5488
  MAIN_WINDOW.show
5650
5489
  ```
5651
5490
 
5491
+ #### CPU Percentage
5492
+
5493
+ This example shows CPU usage percentage second by second.
5494
+
5495
+ Note that it is highly dependent on low-level OS terminal commands, so if anything changes in their output formatting, the code could break. Please report any issues you might encounter.
5496
+
5497
+ [examples/cpu_percentage.rb](examples/cpu_percentage.rb)
5498
+
5499
+ Run with this command from the root of the project if you cloned the project:
5500
+
5501
+ ```
5502
+ ruby -r './lib/glimmer-dsl-libui' examples/cpu_percentage.rb
5503
+ ```
5504
+
5505
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
5506
+
5507
+ ```
5508
+ ruby -r glimmer-dsl-libui -e "require 'examples/cpu_percentage'"
5509
+ ```
5510
+
5511
+ Mac | Windows | Linux
5512
+ ----|---------|------
5513
+ ![glimmer-dsl-libui-mac-cpu-percentage.png](images/glimmer-dsl-libui-mac-cpu-percentage.png) | ![glimmer-dsl-libui-windows-cpu-percentage.png](images/glimmer-dsl-libui-windows-cpu-percentage.png) | ![glimmer-dsl-libui-linux-cpu-percentage.png](images/glimmer-dsl-libui-linux-cpu-percentage.png)
5514
+
5515
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
5516
+
5517
+ ```ruby
5518
+ require 'glimmer-dsl-libui'
5519
+ require 'bigdecimal'
5520
+
5521
+ include Glimmer
5522
+
5523
+ data = [
5524
+ ['CPU', '0%', 0],
5525
+ ]
5526
+
5527
+ Glimmer::LibUI.timer(1) do
5528
+ cpu_percentage_value = nil
5529
+ if OS.windows?
5530
+ cpu_percentage_raw_value = `wmic cpu get loadpercentage`
5531
+ cpu_percentage_value = cpu_percentage_raw_value.split("\n")[2].to_i
5532
+ elsif OS.mac?
5533
+ cpu_percentage_value = `ps -A -o %cpu | awk '{s+=$1} END {print s}'`.to_i
5534
+ elsif OS.linux?
5535
+ stats = `top -n 1`
5536
+ idle_percentage = stats.split("\n")[2].match(/ni,.* (.*) .*id/)[1]
5537
+ cpu_percentage_value = (BigDecimal(100) - BigDecimal(idle_percentage)).to_i
5538
+ end
5539
+ data[0][1] = "#{cpu_percentage_value}%"
5540
+ data[0][2] = cpu_percentage_value
5541
+ end
5542
+
5543
+ window('CPU Percentage', 400, 200) {
5544
+ vertical_box {
5545
+ table {
5546
+ text_column('Name')
5547
+ text_column('Value')
5548
+ progress_bar_column('Percentage')
5549
+
5550
+ cell_rows data # implicit data-binding
5551
+ }
5552
+ }
5553
+ }.show
5554
+ ```
5555
+
5652
5556
  #### Custom Draw Text
5653
5557
 
5654
5558
  [examples/custom_draw_text.rb](examples/custom_draw_text.rb)
@@ -6406,15 +6310,15 @@ require 'glimmer-dsl-libui'
6406
6310
  class FormTable
6407
6311
  include Glimmer
6408
6312
 
6409
- attr_accessor :name, :email, :phone, :city, :state, :filter_value
6313
+ attr_accessor :data, :name, :email, :phone, :city, :state, :filter_value
6410
6314
 
6411
6315
  def initialize
6412
6316
  @data = [
6413
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
6414
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
6415
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
6416
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
6417
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
6317
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
6318
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
6319
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
6320
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
6321
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
6418
6322
  ]
6419
6323
  end
6420
6324
 
@@ -6477,10 +6381,10 @@ class FormTable
6477
6381
  after_write: ->(filter_value) { # execute after write to self.filter_value
6478
6382
  @unfiltered_data ||= @data.dup
6479
6383
  # Unfilter first to remove any previous filters
6480
- @data.replace(@unfiltered_data) # affects table indirectly through implicit data-binding
6384
+ self.data = @unfiltered_data # affects table indirectly through explicit data-binding
6481
6385
  # Now, apply filter if entered
6482
6386
  unless filter_value.empty?
6483
- @data.filter! do |row_data| # affects table indirectly through implicit data-binding
6387
+ self.data = @data.filter do |row_data| # affects table indirectly through explicit data-binding
6484
6388
  row_data.any? do |cell|
6485
6389
  cell.to_s.downcase.include?(filter_value.downcase)
6486
6390
  end
@@ -6497,7 +6401,7 @@ class FormTable
6497
6401
  text_column('City')
6498
6402
  text_column('State')
6499
6403
 
6500
- cell_rows @data # implicit data-binding
6404
+ cell_rows <=> [self, :data] # explicit data-binding
6501
6405
 
6502
6406
  on_changed do |row, type, row_data|
6503
6407
  puts "Row #{row} #{type}: #{row_data}"
@@ -6519,11 +6423,11 @@ require 'glimmer-dsl-libui'
6519
6423
  include Glimmer
6520
6424
 
6521
6425
  data = [
6522
- ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO', '80014'],
6523
- ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA', '02101'],
6524
- ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL', '60007'],
6525
- ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA', '98101'],
6526
- ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA', '90001'],
6426
+ ['Lisa Sky', 'lisa@sky.com', '720-523-4329', 'Denver', 'CO'],
6427
+ ['Jordan Biggins', 'jordan@biggins.com', '617-528-5399', 'Boston', 'MA'],
6428
+ ['Mary Glass', 'mary@glass.com', '847-589-8788', 'Elk Grove Village', 'IL'],
6429
+ ['Darren McGrath', 'darren@mcgrath.com', '206-539-9283', 'Seattle', 'WA'],
6430
+ ['Melody Hanheimer', 'melody@hanheimer.com', '213-493-8274', 'Los Angeles', 'CA'],
6527
6431
  ]
6528
6432
 
6529
6433
  window('Contacts', 600, 600) { |w|
@@ -7852,7 +7756,181 @@ end
7852
7756
  TinyMidiPlayer.new
7853
7757
  ```
7854
7758
 
7855
- [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
7759
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7760
+
7761
+ ```ruby
7762
+ # frozen_string_literal: true
7763
+
7764
+ require 'glimmer-dsl-libui'
7765
+
7766
+ class TinyMidiPlayer
7767
+ include Glimmer
7768
+
7769
+ VERSION = '0.0.1'
7770
+
7771
+ attr_accessor :selected_file
7772
+
7773
+ def initialize
7774
+ @pid = nil
7775
+ @music_directory = File.expand_path('../sounds', __dir__)
7776
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
7777
+ .sort_by { |path| File.basename(path) }
7778
+ at_exit { stop_midi }
7779
+ create_gui
7780
+ end
7781
+
7782
+ def stop_midi
7783
+ if @pid
7784
+ Process.kill(:SIGKILL, @pid) if @th.alive?
7785
+ @pid = nil
7786
+ end
7787
+ end
7788
+
7789
+ def play_midi
7790
+ stop_midi
7791
+ if @pid.nil? && @selected_file
7792
+ begin
7793
+ @pid = spawn "timidity #{@selected_file}"
7794
+ @th = Process.detach @pid
7795
+ rescue Errno::ENOENT
7796
+ warn 'Timidty++ not found. Please install Timidity++.'
7797
+ warn 'https://sourceforge.net/projects/timidity/'
7798
+ end
7799
+ end
7800
+ end
7801
+
7802
+ def show_version
7803
+ msg_box('Tiny Midi Player',
7804
+ "Written in Ruby\n" \
7805
+ "https://github.com/kojix2/libui\n" \
7806
+ "Version #{VERSION}")
7807
+ end
7808
+
7809
+ def create_gui
7810
+ menu('Help') {
7811
+ menu_item('Version') {
7812
+ on_clicked do
7813
+ show_version
7814
+ end
7815
+ }
7816
+ }
7817
+ window('Tiny Midi Player', 200, 50) {
7818
+ horizontal_box {
7819
+ vertical_box {
7820
+ stretchy false
7821
+
7822
+ button('▶') {
7823
+ on_clicked do
7824
+ play_midi
7825
+ end
7826
+ }
7827
+ button('■') {
7828
+ on_clicked do
7829
+ stop_midi
7830
+ end
7831
+ }
7832
+ }
7833
+
7834
+ combobox {
7835
+ items @midi_files.map { |path| File.basename(path) }
7836
+ # data-bind selected item (String) to self.selected_file with on-read/on-write converters and after_write operation
7837
+ selected_item <=> [self, :selected_file, on_read: ->(f) {File.basename(f.to_s)}, on_write: ->(f) {File.join(@music_directory, f)}, after_write: -> { play_midi if @th&.alive? }]
7838
+ }
7839
+ }
7840
+ }.show
7841
+ end
7842
+ end
7843
+
7844
+ TinyMidiPlayer.new
7845
+ ```
7846
+
7847
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (with [data-binding](#data-binding)):
7848
+
7849
+ ```ruby
7850
+ require 'glimmer-dsl-libui'
7851
+
7852
+ class TinyMidiPlayer
7853
+ include Glimmer
7854
+
7855
+ VERSION = '0.0.1'
7856
+
7857
+ attr_accessor :selected_file
7858
+
7859
+ def initialize
7860
+ @pid = nil
7861
+ @music_directory = File.expand_path('../sounds', __dir__)
7862
+ @midi_files = Dir.glob(File.join(@music_directory, '**/*.mid'))
7863
+ .sort_by { |path| File.basename(path) }
7864
+ at_exit { stop_midi }
7865
+ create_gui
7866
+ end
7867
+
7868
+ def stop_midi
7869
+ if @pid
7870
+ Process.kill(:SIGKILL, @pid) if @th.alive?
7871
+ @pid = nil
7872
+ end
7873
+ end
7874
+
7875
+ def play_midi
7876
+ stop_midi
7877
+ if @pid.nil? && @selected_file
7878
+ begin
7879
+ @pid = spawn "timidity #{@selected_file}"
7880
+ @th = Process.detach @pid
7881
+ rescue Errno::ENOENT
7882
+ warn 'Timidty++ not found. Please install Timidity++.'
7883
+ warn 'https://sourceforge.net/projects/timidity/'
7884
+ end
7885
+ end
7886
+ end
7887
+
7888
+ def show_version
7889
+ msg_box('Tiny Midi Player',
7890
+ "Written in Ruby\n" \
7891
+ "https://github.com/kojix2/libui\n" \
7892
+ "Version #{VERSION}")
7893
+ end
7894
+
7895
+ def create_gui
7896
+ menu('Help') {
7897
+ menu_item('Version') {
7898
+ on_clicked do
7899
+ show_version
7900
+ end
7901
+ }
7902
+ }
7903
+ window('Tiny Midi Player', 200, 50) {
7904
+ horizontal_box {
7905
+ vertical_box {
7906
+ stretchy false
7907
+
7908
+ button('▶') {
7909
+ on_clicked do
7910
+ play_midi
7911
+ end
7912
+ }
7913
+ button('■') {
7914
+ on_clicked do
7915
+ stop_midi
7916
+ end
7917
+ }
7918
+ }
7919
+
7920
+ combobox {
7921
+ items @midi_files.map { |path| File.basename(path) }
7922
+ # data-bind selected index (Integer) to self.selected_file with on-read/on-write converters and after_write operation
7923
+ selected <=> [self, :selected_file, on_read: ->(f) {@midi_files.index(f)}, on_write: ->(i) {@midi_files[i]}, after_write: -> { play_midi if @th&.alive? }]
7924
+ }
7925
+ }
7926
+ }.show
7927
+ end
7928
+ end
7929
+
7930
+ TinyMidiPlayer.new
7931
+ ```
7932
+
7933
+ [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 3 (without [data-binding](#data-binding)):
7856
7934
 
7857
7935
  ```ruby
7858
7936
  require 'glimmer-dsl-libui'
@@ -7961,7 +8039,7 @@ Mac | Windows | Linux
7961
8039
  ----|---------|------
7962
8040
  ![glimmer-dsl-libui-mac-snake.png](images/glimmer-dsl-libui-mac-snake.png) ![glimmer-dsl-libui-mac-snake-game-over.png](images/glimmer-dsl-libui-mac-snake-game-over.png) | ![glimmer-dsl-libui-windows-snake.png](images/glimmer-dsl-libui-windows-snake.png) ![glimmer-dsl-libui-windows-snake-game-over.png](images/glimmer-dsl-libui-windows-snake-game-over.png) | ![glimmer-dsl-libui-linux-snake.png](images/glimmer-dsl-libui-linux-snake.png) ![glimmer-dsl-libui-linux-snake-game-over.png](images/glimmer-dsl-libui-linux-snake-game-over.png)
7963
8041
 
7964
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8042
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
7965
8043
 
7966
8044
  ```ruby
7967
8045
  require 'glimmer-dsl-libui'
@@ -7978,6 +8056,7 @@ class Snake
7978
8056
  @game = Model::Game.new
7979
8057
  @grid = Presenter::Grid.new(@game)
7980
8058
  @game.start
8059
+ @keypress_queue = []
7981
8060
  create_gui
7982
8061
  register_observers
7983
8062
  end
@@ -7997,14 +8076,30 @@ class Snake
7997
8076
  end
7998
8077
 
7999
8078
  Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
8000
- @game.snake.move unless @game.over?
8079
+ unless @game.over?
8080
+ process_queued_keypress
8081
+ @game.snake.move
8082
+ end
8083
+ end
8084
+ end
8085
+
8086
+ def process_queued_keypress
8087
+ # key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
8088
+ key = @keypress_queue.shift
8089
+ case [@game.snake.head.orientation, key]
8090
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
8091
+ @game.snake.turn_right
8092
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
8093
+ @game.snake.turn_left
8094
+ else
8095
+ # No Op
8001
8096
  end
8002
8097
  end
8003
8098
 
8004
8099
  def create_gui
8005
8100
  @main_window = window {
8006
8101
  # data-bind window title to game score, converting it to a title string on read from the model
8007
- title <= [@game, :score, on_read: -> (score) {"Glimmer Snake (Score: #{@game.score})"}]
8102
+ title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
8008
8103
  content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
8009
8104
  resizable false
8010
8105
 
@@ -8022,15 +8117,109 @@ class Snake
8022
8117
  }
8023
8118
 
8024
8119
  on_key_up do |area_key_event|
8025
- orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
8026
- case orientation_and_key
8027
- in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
8028
- @game.snake.turn_right
8029
- in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
8030
- @game.snake.turn_left
8031
- else
8032
- # No Op
8033
- end
8120
+ @keypress_queue << area_key_event[:ext_key]
8121
+ end
8122
+ }
8123
+ end
8124
+ }
8125
+ end
8126
+ }
8127
+ }
8128
+ end
8129
+ end
8130
+
8131
+ Snake.new.launch
8132
+ ```
8133
+
8134
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
8135
+
8136
+ ```ruby
8137
+ require 'glimmer-dsl-libui'
8138
+
8139
+ require_relative 'snake/presenter/grid'
8140
+
8141
+ class Snake
8142
+ include Glimmer
8143
+
8144
+ CELL_SIZE = 15
8145
+ SNAKE_MOVE_DELAY = 0.1
8146
+
8147
+ def initialize
8148
+ @game = Model::Game.new
8149
+ @grid = Presenter::Grid.new(@game)
8150
+ @game.start
8151
+ @keypress_queue = []
8152
+ create_gui
8153
+ register_observers
8154
+ end
8155
+
8156
+ def launch
8157
+ @main_window.show
8158
+ end
8159
+
8160
+ def register_observers
8161
+ @game.height.times do |row|
8162
+ @game.width.times do |column|
8163
+ observe(@grid.cells[row][column], :color) do |new_color|
8164
+ @cell_grid[row][column].fill = new_color
8165
+ end
8166
+ end
8167
+ end
8168
+
8169
+ observe(@game, :over) do |game_over|
8170
+ Glimmer::LibUI.queue_main do
8171
+ if game_over
8172
+ msg_box('Game Over!', "Score: #{@game.score} | High Score: #{@game.high_score}")
8173
+ @game.start
8174
+ end
8175
+ end
8176
+ end
8177
+
8178
+ Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
8179
+ unless @game.over?
8180
+ process_queued_keypress
8181
+ @game.snake.move
8182
+ end
8183
+ end
8184
+ end
8185
+
8186
+ def process_queued_keypress
8187
+ # key press queue ensures one turn per snake move to avoid a double-turn resulting in instant death (due to snake illogically going back against itself)
8188
+ key = @keypress_queue.shift
8189
+ case [@game.snake.head.orientation, key]
8190
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
8191
+ @game.snake.turn_right
8192
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
8193
+ @game.snake.turn_left
8194
+ else
8195
+ # No Op
8196
+ end
8197
+ end
8198
+
8199
+ def create_gui
8200
+ @cell_grid = []
8201
+ @main_window = window {
8202
+ # data-bind window title to game score, converting it to a title string on read from the model
8203
+ title <= [@game, :score, on_read: -> (score) {"Snake (Score: #{@game.score})"}]
8204
+ content_size @game.width * CELL_SIZE, @game.height * CELL_SIZE
8205
+ resizable false
8206
+
8207
+ vertical_box {
8208
+ padded false
8209
+
8210
+ @game.height.times do |row|
8211
+ @cell_grid << []
8212
+ horizontal_box {
8213
+ padded false
8214
+
8215
+ @game.width.times do |column|
8216
+ area {
8217
+ @cell_grid.last << square(0, 0, CELL_SIZE) {
8218
+ fill Presenter::Cell::COLOR_CLEAR
8219
+ }
8220
+
8221
+ on_key_up do |area_key_event|
8222
+ @keypress_queue << area_key_event[:ext_key]
8034
8223
  end
8035
8224
  }
8036
8225
  end
@@ -8180,22 +8369,23 @@ class Tetris
8180
8369
  menu('Game') {
8181
8370
  @pause_menu_item = check_menu_item('Pause') {
8182
8371
  enabled false
8183
-
8184
- on_clicked do
8185
- @game.paused = @pause_menu_item.checked?
8186
- end
8372
+ checked <=> [@game, :paused]
8187
8373
  }
8374
+
8188
8375
  menu_item('Restart') {
8189
8376
  on_clicked do
8190
8377
  @game.restart!
8191
8378
  end
8192
8379
  }
8380
+
8193
8381
  separator_menu_item
8382
+
8194
8383
  menu_item('Exit') {
8195
8384
  on_clicked do
8196
8385
  exit(0)
8197
8386
  end
8198
8387
  }
8388
+
8199
8389
  quit_menu_item if OS.mac?
8200
8390
  }
8201
8391
 
@@ -8205,6 +8395,7 @@ class Tetris
8205
8395
  show_high_scores
8206
8396
  end
8207
8397
  }
8398
+
8208
8399
  menu_item('Clear High Scores') {
8209
8400
  on_clicked {
8210
8401
  @game.clear_high_scores!
@@ -8213,22 +8404,16 @@ class Tetris
8213
8404
  }
8214
8405
 
8215
8406
  menu('Options') {
8216
- radio_menu_item('Instant Down on Up Arrow') {
8217
- on_clicked do
8218
- @game.instant_down_on_up = true
8219
- end
8407
+ radio_menu_item('Instant Down on Up Arrow') { |r|
8408
+ checked <=> [@game, :instant_down_on_up]
8220
8409
  }
8221
- radio_menu_item('Rotate Right on Up Arrow') {
8222
- on_clicked do
8223
- @game.rotate_right_on_up = true
8224
- end
8410
+
8411
+ radio_menu_item('Rotate Right on Up Arrow') { |r|
8412
+ checked <=> [@game, :rotate_right_on_up]
8225
8413
  }
8226
- radio_menu_item('Rotate Left on Up Arrow') {
8227
- checked true
8228
-
8229
- on_clicked do
8230
- @game.rotate_left_on_up = true
8231
- end
8414
+
8415
+ radio_menu_item('Rotate Left on Up Arrow') { |r|
8416
+ checked <=> [@game, :rotate_left_on_up]
8232
8417
  }
8233
8418
  }
8234
8419
 
@@ -8240,6 +8425,7 @@ class Tetris
8240
8425
  end
8241
8426
  }
8242
8427
  end
8428
+
8243
8429
  menu_item('About') {
8244
8430
  on_clicked do
8245
8431
  show_about_dialog
@@ -8462,7 +8648,7 @@ Mac | Windows | Linux
8462
8648
  ----|---------|------
8463
8649
  ![glimmer-dsl-libui-mac-tic-tac-toe.png](images/glimmer-dsl-libui-mac-tic-tac-toe.png) ![glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-mac-tic-tac-toe-draw.png](images/glimmer-dsl-libui-mac-tic-tac-toe-draw.png) | ![glimmer-dsl-libui-windows-tic-tac-toe.png](images/glimmer-dsl-libui-windows-tic-tac-toe.png) ![glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-windows-tic-tac-toe-draw.png](images/glimmer-dsl-libui-windows-tic-tac-toe-draw.png) | ![glimmer-dsl-libui-linux-tic-tac-toe.png](images/glimmer-dsl-libui-linux-tic-tac-toe.png) ![glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png) ![glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png) ![glimmer-dsl-libui-linux-tic-tac-toe-draw.png](images/glimmer-dsl-libui-linux-tic-tac-toe-draw.png)
8464
8650
 
8465
- New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
8651
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
8466
8652
 
8467
8653
  ```ruby
8468
8654
  require 'glimmer-dsl-libui'
@@ -8508,6 +8694,7 @@ class TicTacToe
8508
8694
  text(23, 19) {
8509
8695
  string {
8510
8696
  font family: 'Arial', size: OS.mac? ? 20 : 16
8697
+ # data-bind string property of area text attributed string to tic tac toe board cell sign
8511
8698
  string <= [@tic_tac_toe_board[row + 1, column + 1], :sign] # board model is 1-based
8512
8699
  }
8513
8700
  }
@@ -8541,6 +8728,95 @@ end
8541
8728
  TicTacToe.new.launch
8542
8729
  ```
8543
8730
 
8731
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version 2 (without [data-binding](#data-binding)):
8732
+
8733
+ ```ruby
8734
+
8735
+ require 'glimmer-dsl-libui'
8736
+
8737
+ require_relative "tic_tac_toe/board"
8738
+
8739
+ class TicTacToe
8740
+ include Glimmer
8741
+
8742
+ def initialize
8743
+ @tic_tac_toe_board = Board.new
8744
+ end
8745
+
8746
+ def launch
8747
+ create_gui
8748
+ register_observers
8749
+ @main_window.show
8750
+ end
8751
+
8752
+ def register_observers
8753
+ observe(@tic_tac_toe_board, :game_status) do |game_status|
8754
+ display_win_message if game_status == Board::WIN
8755
+ display_draw_message if game_status == Board::DRAW
8756
+ end
8757
+
8758
+ 3.times.map do |row|
8759
+ 3.times.map do |column|
8760
+ observe(@tic_tac_toe_board[row + 1, column + 1], :sign) do |sign| # board model is 1-based
8761
+ @cells[row][column].string = sign
8762
+ end
8763
+ end
8764
+ end
8765
+ end
8766
+
8767
+ def create_gui
8768
+ @main_window = window('Tic-Tac-Toe', 180, 180) {
8769
+ resizable false
8770
+
8771
+ @cells = []
8772
+ vertical_box {
8773
+ padded false
8774
+
8775
+ 3.times.map do |row|
8776
+ @cells << []
8777
+ horizontal_box {
8778
+ padded false
8779
+
8780
+ 3.times.map do |column|
8781
+ area {
8782
+ square(0, 0, 60) {
8783
+ stroke :black, thickness: 2
8784
+ }
8785
+ text(23, 19) {
8786
+ @cells[row] << string('') {
8787
+ font family: 'Arial', size: OS.mac? ? 20 : 16
8788
+ }
8789
+ }
8790
+ on_mouse_up do
8791
+ @tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
8792
+ end
8793
+ }
8794
+ end
8795
+ }
8796
+ end
8797
+ }
8798
+ }
8799
+ end
8800
+
8801
+ def display_win_message
8802
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
8803
+ end
8804
+
8805
+ def display_draw_message
8806
+ display_game_over_message("Draw!")
8807
+ end
8808
+
8809
+ def display_game_over_message(message_text)
8810
+ Glimmer::LibUI.queue_main do
8811
+ msg_box('Game Over', message_text)
8812
+ @tic_tac_toe_board.reset!
8813
+ end
8814
+ end
8815
+ end
8816
+
8817
+ TicTacToe.new.launch
8818
+ ```
8819
+
8544
8820
  #### Timer
8545
8821
 
8546
8822
  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).
@@ -8869,7 +9145,7 @@ https://github.com/iraamaro/i3off-gtk-ruby
8869
9145
 
8870
9146
  ### Issues
8871
9147
 
8872
- 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).
9148
+ 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.
8873
9149
 
8874
9150
  ### Chat
8875
9151
 
@@ -8901,6 +9177,8 @@ These features have been planned or suggested. You might see them in a future ve
8901
9177
  is fine, but please isolate to its own commit so I can cherry-pick
8902
9178
  around it.
8903
9179
 
9180
+ Note that the latest development sometimes takes place in the [development](https://github.com/AndyObtiva/glimmer-dsl-libui/tree/development) branch (usually deleted once merged back to [master](https://github.com/AndyObtiva/glimmer-dsl-libui)).
9181
+
8904
9182
  ## Contributors
8905
9183
 
8906
9184
  * [Andy Maleh](https://github.com/AndyObtiva) (Founder)