glimmer-dsl-libui 0.4.6 → 0.4.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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)