glimmer-dsl-libui 0.2.19 → 0.2.23

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.19
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.23
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)
@@ -143,7 +143,7 @@ window('Area Gallery', 400, 400) {
143
143
  }
144
144
  text(161, 40, 100) { # x, y, width
145
145
  string('Area Gallery') {
146
- font family: 'Arial', size: 14
146
+ font family: 'Arial', size: (OS.mac? ? 14 : 11)
147
147
  color :black
148
148
  }
149
149
  }
@@ -218,9 +218,10 @@ NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is fe
218
218
  Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interested in:
219
219
  - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)
220
220
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)
221
+ - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
222
+ - [glimmer-dsl-gtk](https://github.com/AndyObtiva/glimmer-dsl-gtk): Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)
221
223
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
222
224
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS
223
- - [glimmer-dsl-tk](https://github.com/AndyObtiva/glimmer-dsl-tk): Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)
224
225
 
225
226
  ## Table of Contents
226
227
 
@@ -278,6 +279,8 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
278
279
  - [Custom Draw Text](#custom-draw-text)
279
280
  - [Method-Based Custom Keyword](#method-based-custom-keyword)
280
281
  - [Tetris](#tetris)
282
+ - [Tic Tac Toe](#tic-tac-toe)
283
+ - [Snake](#snake)
281
284
  - [Applications](#applications)
282
285
  - [Manga2PDF](#manga2pdf)
283
286
  - [Befunge98 GUI](#befunge98-gui)
@@ -370,7 +373,7 @@ gem install glimmer-dsl-libui
370
373
  Or install via Bundler `Gemfile`:
371
374
 
372
375
  ```ruby
373
- gem 'glimmer-dsl-libui', '~> 0.2.19'
376
+ gem 'glimmer-dsl-libui', '~> 0.2.23'
374
377
  ```
375
378
 
376
379
  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.
@@ -456,6 +459,7 @@ Keyword(Args) | Properties | Listeners
456
459
  `checkbox_column(name as String)` | `editable` (Boolean) | None
457
460
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
458
461
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
462
+ `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
459
463
  `combobox` | `items` (`Array` of `String`), `selected` (`Integer`) | `on_selected`
460
464
  `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`
461
465
  `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`
@@ -478,7 +482,7 @@ Keyword(Args) | Properties | Listeners
478
482
  `line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
479
483
  `matrix(m11 = nil as Numeric, m12 = nil as Numeric, m21 = nil as Numeric, m22 = nil as Numeric, m31 = nil as Numeric, m32 = nil as Numeric)` | `m11` (`Numeric`), `m12` (`Numeric`), `m21` (`Numeric`), `m22` (`Numeric`), `m31` (`Numeric`), `m32` (`Numeric`) | None
480
484
  `menu(text as String)` | None | None
481
- `menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
485
+ `menu_item(text as String)` | None | `on_clicked`
482
486
  `message_box` (alias for `msg_box`; see for arguments) | None | None
483
487
  `message_box_error` (alias for `msg_box_error`; see for arguments) | None | None
484
488
  `multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
@@ -497,6 +501,7 @@ Keyword(Args) | Properties | Listeners
497
501
  `radio_buttons` | `selected` (`Integer`) | `on_selected`
498
502
  `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `width` (`Numeric`), `height` (`Numeric`) | None
499
503
  `search_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
504
+ `separator_menu_item` | None | None
500
505
  `slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
501
506
  `spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
502
507
  `square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
@@ -510,7 +515,7 @@ Keyword(Args) | Properties | Listeners
510
515
  `time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`) | `on_changed`
511
516
  `vertical_box` | `padded` (Boolean) | None
512
517
  `vertical_separator` | None | None
513
- `window(title as String, width as Integer, height as Integer, has_menubar as Boolean)` | `borderless` (Boolean), `content_size` (width `Numeric`, height `Numeric`), `fullscreen` (Boolean), `margined` (Boolean), `title` (`String`) | `on_closing`, `on_content_size_changed`, `on_destroy`
518
+ `window(title as String, width as Integer, height as Integer, has_menubar as Boolean)` | `borderless` (Boolean), `content_size` (width `Numeric`, height `Numeric`), `fullscreen` (Boolean), `margined` (Boolean), `title` (`String`), `resizable` (Boolean) | `on_closing`, `on_content_size_changed`, `on_destroy`
514
519
 
515
520
  ### Common Control Properties
516
521
  - `enabled` (Boolean)
@@ -543,6 +548,35 @@ All operations that could normally be called on `LibUI` can also be called on `G
543
548
  - `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible
544
549
  - `Glimmer::LibUI::timer(time_in_seconds=0.1, repeat: true, &block)`: calls block after time_in_seconds has elapsed, repeating indefinitely unless repeat is `false` or an `Integer` for finite number of repeats. Block can return `false` or `true` to override next repetition.
545
550
 
551
+ There are additional useful `Glimmer::LibUI` operations that are not found in `LibUI`, which mostly help if you would like to do advanced lower level [LibUI](https://github.com/kojix2/LibUI) programming:
552
+ - `Glimmer::LibUI::integer_to_boolean(int, allow_nil: true)`
553
+ - `Glimmer::LibUI::boolean_to_integer(int, allow_nil: true)`
554
+ - `Glimmer::LibUI::degrees_to_radians(degrees)`
555
+ - `Glimmer::LibUI::interpret_color(value)`: interprets a color in any form like `String`, `Symbol`, or hex into an rgb `Hash`
556
+ - `Glimmer::LibUI::hex_to_rgb(value)`: converts a hex color to an rgb `Hash`
557
+ - `Glimmer::LibUI::enum_names`: provides all possible enum names to use with `Glimmer::LibUI::enum_symbols(enum_name)`
558
+ - `Glimmer::LibUI::enum_symbols(enum_name)`: returns all possible values for an enum. `enum_name` can be:
559
+ - `:draw_brush_type`: `[:solid, :linear_gradient, :radial_gradient, :image]`
560
+ - `:draw_line_cap`: `[:flat, :round, :square]`
561
+ - `:draw_line_join`: `[:miter, :round, :bevel]`
562
+ - `:draw_fill_mode`: `[:winding, :alternate]`
563
+ - `:attribute_type`: attributes for attributed `string`s: `[:family, :size, weight, :italic, :stretch, :color, :background, :underline, :underline_color, :features]`
564
+ - `:text_weight`: `[:minimum, :thin, :ultra_light, :light, :book, :normal, :medium, :semi_bold, :bold, :ultra_bold, :heavy, :ultra_heavy, :maximum]`
565
+ - `:text_italic`: `[:normal, :oblique, :italic]`
566
+ - `:text_stretch`: `[:ultra_condensed, :extra_condensed, :condensed, :semi_condensed, :normal, :semi_expanded, :expanded, :extra_expanded, :ultra_expanded]`
567
+ - `:underline`: `[:none, :single, :double, :suggestion, :color_custom, :color_spelling, :color_grammar, :color_auxiliary]`
568
+ - `:underline_color`: `[:custom, :spelling, :grammar, :auxiliary]`
569
+ - `:draw_text_align`: `[:left, :center, :right]`
570
+ - `:modifier`: `[:ctrl, :alt, :shift, :super]`
571
+ - `:ext_key`: `[:escape, :insert, :delete, :home, :end, :page_up, :page_down, :up, :down, :left, :right, :f1, :f2, :f3, :f4, :f5, :f6, :f7, :f8, :f9, :f10, :f11, :f12, :n0, :n1, :n2, :n3, :n4, :n5, :n6, :n7, :n8, :n9, :n_dot, :n_enter, :n_add, :n_subtract, :n_multiply, :n_divide]`
572
+ - `:at`: for inserting `grid` controls: `[:leading, :top, :trailing, :bottom]`
573
+ - `:align`: `[:fill, :start, :center, :end]`
574
+ - `:table_value_type`: `[:string, :image, :int, :color]`
575
+ - `:table_model_column`: `[:never_editable, :always_editable]`
576
+ - `Glimmer::LibUI::enum_symbol_to_value(enum_name, enum_symbol, default_symbol: nil, default_index: 0)`
577
+ - `Glimmer::LibUI::enum_value_to_symbol(enum_name, enum_value)`
578
+ - `Glimmer::LibUI::x11_colors`: returns all [X11 colors](https://en.wikipedia.org/wiki/X11_color_names): `[:alice_blue, :antique_white, :aqua, :aquamarine, :azure, :beige, :bisque, :rebecca_purple, :becca_purple, :blanched_almond, :blue, :blue_violet, :brown, :burly_wood, :burlywood, :cadet_blue, :carnation, :cayenne, :chartreuse, :chocolate, :coral, :cornflower_blue, :cornsilk, :crimson, :cyan, :dark_blue, :dark_cyan, :dark_golden_rod, :dark_goldenrod, :dark_gray, :dark_grey, :dark_green, :dark_khaki, :dark_magenta, :dark_olive_green, :darkolive_green, :dark_orange, :dark_orchid, :dark_red, :dark_salmon, :darksalmon, :dark_sea_green, :dark_slate_blue, :dark_slate_gray, :dark_slate_grey, :dark_turquoise, :dark_violet, :darkorange, :deep_pink, :deep_sky_blue, :dim_gray, :dim_grey, :dodger_blue, :feldspar, :fire_brick, :firebrick, :floral_white, :forest_green, :fuchsia, :gainsboro, :ghost_white, :gold, :golden_rod, :goldenrod, :gray, :grey, :gray10, :grey10, :gray20, :grey20, :gray30, :grey30, :gray40, :grey40, :gray50, :grey50, :gray60, :grey60, :gray70, :grey70, :gray80, :grey80, :gray90, :grey90, :green, :green_yellow, :honey_dew, :honeydew, :hot_pink, :indian_red, :indigo, :ivory, :khaki, :lavender, :lavender_blush, :lawn_green, :lemon_chiffon, :light_blue, :light_coral, :light_cyan, :light_golden_rod_yellow, :light_goldenrod_yellow, :light_gray, :light_grey, :light_green, :light_pink, :light_salmon, :lightsalmon, :light_sea_green, :light_sky_blue, :light_slate_blue, :light_slate_gray, :light_slate_grey, :light_steel_blue, :lightsteel_blue, :light_yellow, :lime, :lime_green, :linen, :magenta, :maroon, :medium_aqua_marine, :medium_aquamarine, :medium_blue, :medium_orchid, :medium_purple, :medium_sea_green, :medium_slate_blue, :medium_spring_green, :medium_turquoise, :medium_violet_red, :midnight_blue, :mint_cream, :misty_rose, :moccasin, :navajo_white, :navy, :old_lace, :olive, :olive_drab, :olivedrab, :orange, :orange_red, :orchid, :pale_golden_rod, :pale_goldenrod, :pale_green, :pale_turquoise, :pale_violet_red, :papaya_whip, :peach_puff, :peachpuff, :peru, :pink, :plum, :powder_blue, :purple, :red, :rosy_brown, :royal_blue, :saddle_brown, :salmon, :sandy_brown, :sea_green, :sea_shell, :seashell, :sienna, :silver, :sky_blue, :slate_blue, :slate_gray, :slate_grey, :snow, :spring_green, :steel_blue, :tan, :teal, :thistle, :tomato, :turquoise, :violet, :violet_red, :wheat, :white_smoke, :yellow, :yellow_green, :metallic, :white, :black, :gray_scale, :grey_scale]`
579
+
546
580
  ### Extra Dialogs
547
581
 
548
582
  - `open_file(window as Glimmer::LibUI::WindowProxy = ControlProxy::main_window_proxy)`: returns selected file (`String`) or `nil` if cancelled
@@ -871,7 +905,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
871
905
 
872
906
  `fill` and `stroke` accept [X11](https://en.wikipedia.org/wiki/X11_color_names) color `Symbol`s/`String`s like `:skyblue` and `'sandybrown'` or 6-number hex or 3-number hex-shorthand (as `Integer` or `String` with or without `0x` prefix)
873
907
 
874
- Available [X11](https://en.wikipedia.org/wiki/X11_color_names) colors can be obtained through `Glimmer::LibUI.x11_colors` method.
908
+ Available [X11 colors](https://en.wikipedia.org/wiki/X11_color_names) can be obtained through `Glimmer::LibUI.x11_colors` method.
875
909
 
876
910
  Check [Basic Transform](#basic-transform) example for use of [X11](https://en.wikipedia.org/wiki/X11_color_names) colors.
877
911
 
@@ -1088,8 +1122,8 @@ window('Method-Based Custom Keyword') {
1088
1122
 
1089
1123
  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):
1090
1124
  - Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
1091
- - Check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
1092
- - Check out the [Go UI (Golang LibUI) documentation](https://pkg.go.dev/github.com/andlabs/ui) for an alternative well-documented [libui](https://github.com/andlabs/libui) reference.
1125
+ - Check out the [libui C Headers](https://github.com/andlabs/libui/blob/master/ui.h)
1126
+ - Check out the [Go UI (Golang LibUI) API Documentation](https://pkg.go.dev/github.com/andlabs/ui) for an alternative well-documented [libui](https://github.com/andlabs/libui) reference.
1093
1127
 
1094
1128
  ## Packaging
1095
1129
 
@@ -1189,7 +1223,7 @@ class MetaExample
1189
1223
 
1190
1224
  def run_example(example)
1191
1225
  Thread.new do
1192
- command = "ruby -r #{glimmer_dsl_libui_file} #{example} 2>&1"
1226
+ command = "#{RbConfig.ruby} -r #{glimmer_dsl_libui_file} #{example} 2>&1"
1193
1227
  result = ''
1194
1228
  IO.popen(command) do |f|
1195
1229
  sleep(0.0001) # yield to main thread
@@ -1726,11 +1760,11 @@ class TinyMidiPlayer
1726
1760
 
1727
1761
  UI.new_horizontal_box.tap do |hbox|
1728
1762
  UI.new_vertical_box.tap do |vbox|
1729
- UI.new_button('').tap do |button1|
1763
+ UI.new_button('â–¶').tap do |button1|
1730
1764
  UI.button_on_clicked(button1) { play_midi }
1731
1765
  UI.box_append(vbox, button1, 1)
1732
1766
  end
1733
- UI.new_button('').tap do |button2|
1767
+ UI.new_button('â– ').tap do |button2|
1734
1768
  UI.button_on_clicked(button2) { stop_midi }
1735
1769
  UI.box_append(vbox, button2, 1)
1736
1770
  end
@@ -1824,12 +1858,12 @@ class TinyMidiPlayer
1824
1858
  vertical_box {
1825
1859
  stretchy false
1826
1860
 
1827
- button('') {
1861
+ button('â–¶') {
1828
1862
  on_clicked do
1829
1863
  play_midi
1830
1864
  end
1831
1865
  }
1832
- button('') {
1866
+ button('â– ') {
1833
1867
  on_clicked do
1834
1868
  stop_midi
1835
1869
  end
@@ -4095,7 +4129,7 @@ window('Area Gallery', 400, 400) {
4095
4129
  }
4096
4130
  text(161, 40, 100) { # x, y, width
4097
4131
  string('Area Gallery') {
4098
- font family: 'Arial', size: 14
4132
+ font family: 'Arial', size: (OS.mac? ? 14 : 11)
4099
4133
  color :black
4100
4134
  }
4101
4135
  }
@@ -4305,7 +4339,7 @@ window('Area Gallery', 400, 400) {
4305
4339
  width 100
4306
4340
 
4307
4341
  string {
4308
- font family: 'Arial', size: 14
4342
+ font family: 'Arial', size: (OS.mac? ? 14 : 11)
4309
4343
  color :black
4310
4344
 
4311
4345
  'Area Gallery'
@@ -4418,7 +4452,7 @@ window('Area Gallery', 400, 400) {
4418
4452
  }
4419
4453
  text(161, 40, 100) { # x, y, width
4420
4454
  string('Area Gallery') {
4421
- font family: 'Arial', size: 14
4455
+ font family: 'Arial', size: (OS.mac? ? 14 : 11)
4422
4456
  color :black
4423
4457
  }
4424
4458
  }
@@ -4630,7 +4664,7 @@ window('Area Gallery', 400, 400) {
4630
4664
  width 100
4631
4665
 
4632
4666
  string {
4633
- font family: 'Arial', size: 14
4667
+ font family: 'Arial', size: (OS.mac? ? 14 : 11)
4634
4668
  color :black
4635
4669
 
4636
4670
  'Area Gallery'
@@ -6234,6 +6268,8 @@ window('Method-Based Custom Keyword') {
6234
6268
 
6235
6269
  ### Tetris
6236
6270
 
6271
+ Glimmer Tetris utilizes many small areas to represent Tetromino blocks because this ensures smaller redraws per tetromino block color change, thus achieving higher performance than redrawing one large area on every little change.
6272
+
6237
6273
  [examples/tetris.rb](examples/tetris.rb)
6238
6274
 
6239
6275
  Run with this command from the root of the project if you cloned the project:
@@ -6254,6 +6290,24 @@ Mac
6254
6290
 
6255
6291
  ![glimmer-dsl-libui-mac-tetris-game-over.png](images/glimmer-dsl-libui-mac-tetris-game-over.png)
6256
6292
 
6293
+ ![glimmer-dsl-libui-mac-tetris-high-scores.png](images/glimmer-dsl-libui-mac-tetris-high-scores.png)
6294
+
6295
+ Windows
6296
+
6297
+ ![glimmer-dsl-libui-windows-tetris.png](images/glimmer-dsl-libui-windows-tetris.png)
6298
+
6299
+ ![glimmer-dsl-libui-windows-tetris-game-over.png](images/glimmer-dsl-libui-windows-tetris-game-over.png)
6300
+
6301
+ ![glimmer-dsl-libui-windows-tetris-high-scores.png](images/glimmer-dsl-libui-windows-tetris-high-scores.png)
6302
+
6303
+ Linux
6304
+
6305
+ ![glimmer-dsl-libui-linux-tetris.png](images/glimmer-dsl-libui-linux-tetris.png)
6306
+
6307
+ ![glimmer-dsl-libui-linux-tetris-game-over.png](images/glimmer-dsl-libui-linux-tetris-game-over.png)
6308
+
6309
+ ![glimmer-dsl-libui-linux-tetris-high-scores.png](images/glimmer-dsl-libui-linux-tetris-high-scores.png)
6310
+
6257
6311
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6258
6312
 
6259
6313
  ```ruby
@@ -6268,8 +6322,6 @@ class Tetris
6268
6322
  BEVEL_CONSTANT = 20
6269
6323
  COLOR_GRAY = {r: 192, g: 192, b: 192}
6270
6324
 
6271
- attr_reader :game
6272
-
6273
6325
  def initialize
6274
6326
  @game = Model::Game.new
6275
6327
  end
@@ -6282,8 +6334,11 @@ class Tetris
6282
6334
  end
6283
6335
 
6284
6336
  def create_gui
6337
+ menu_bar
6338
+
6285
6339
  @main_window = window('Glimmer Tetris') {
6286
6340
  content_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE + 98
6341
+ resizable false
6287
6342
 
6288
6343
  vertical_box {
6289
6344
  label { # filler
@@ -6302,8 +6357,10 @@ class Tetris
6302
6357
  def register_observers
6303
6358
  Glimmer::DataBinding::Observer.proc do |game_over|
6304
6359
  if game_over
6360
+ @pause_menu_item.enabled = false
6305
6361
  show_game_over_dialog
6306
6362
  else
6363
+ @pause_menu_item.enabled = true
6307
6364
  start_moving_tetrominos_down
6308
6365
  end
6309
6366
  end.observe(@game, :game_over)
@@ -6361,6 +6418,76 @@ class Tetris
6361
6418
  end.observe(@game, :level)
6362
6419
  end
6363
6420
 
6421
+ def menu_bar
6422
+ menu('Game') {
6423
+ @pause_menu_item = check_menu_item('Pause') {
6424
+ enabled false
6425
+
6426
+ on_clicked do
6427
+ @game.paused = @pause_menu_item.checked?
6428
+ end
6429
+ }
6430
+ menu_item('Restart') {
6431
+ on_clicked do
6432
+ @game.restart!
6433
+ end
6434
+ }
6435
+ separator_menu_item
6436
+ menu_item('Exit') {
6437
+ on_clicked do
6438
+ exit(0)
6439
+ end
6440
+ }
6441
+ quit_menu_item if OS.mac?
6442
+ }
6443
+
6444
+ menu('View') {
6445
+ menu_item('Show High Scores') {
6446
+ on_clicked do
6447
+ show_high_scores
6448
+ end
6449
+ }
6450
+ menu_item('Clear High Scores') {
6451
+ on_clicked {
6452
+ @game.clear_high_scores!
6453
+ }
6454
+ }
6455
+ }
6456
+
6457
+ menu('Options') {
6458
+ radio_menu_item('Instant Down on Up Arrow') {
6459
+ on_clicked do
6460
+ @game.instant_down_on_up = true
6461
+ end
6462
+ }
6463
+ radio_menu_item('Rotate Right on Up Arrow') {
6464
+ on_clicked do
6465
+ @game.rotate_right_on_up = true
6466
+ end
6467
+ }
6468
+ radio_menu_item('Rotate Left on Up Arrow') {
6469
+ on_clicked do
6470
+ @game.rotate_left_on_up = true
6471
+ end
6472
+ }
6473
+ }
6474
+
6475
+ menu('Help') {
6476
+ if OS.mac?
6477
+ about_menu_item {
6478
+ on_clicked do
6479
+ show_about_dialog
6480
+ end
6481
+ }
6482
+ end
6483
+ menu_item('About') {
6484
+ on_clicked do
6485
+ show_about_dialog
6486
+ end
6487
+ }
6488
+ }
6489
+ end
6490
+
6364
6491
  def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
6365
6492
  blocks = []
6366
6493
  vertical_box {
@@ -6421,26 +6548,38 @@ class Tetris
6421
6548
  on_key_down do |key_event|
6422
6549
  case key_event
6423
6550
  in ext_key: :down
6424
- game.down!
6551
+ if OS.windows?
6552
+ # rate limit downs in Windows as they go too fast when key is held
6553
+ @queued_downs ||= 0
6554
+ if @queued_downs < 2
6555
+ @queued_downs += 1
6556
+ Glimmer::LibUI.timer(0.01, repeat: false) do
6557
+ @game.down! if @queued_downs < 2
6558
+ @queued_downs -= 1
6559
+ end
6560
+ end
6561
+ else
6562
+ @game.down!
6563
+ end
6425
6564
  in key: ' '
6426
- game.down!(instant: true)
6565
+ @game.down!(instant: true)
6427
6566
  in ext_key: :up
6428
- case game.up_arrow_action
6567
+ case @game.up_arrow_action
6429
6568
  when :instant_down
6430
- game.down!(instant: true)
6569
+ @game.down!(instant: true)
6431
6570
  when :rotate_right
6432
- game.rotate!(:right)
6571
+ @game.rotate!(:right)
6433
6572
  when :rotate_left
6434
- game.rotate!(:left)
6573
+ @game.rotate!(:left)
6435
6574
  end
6436
6575
  in ext_key: :left
6437
- game.left!
6576
+ @game.left!
6438
6577
  in ext_key: :right
6439
- game.right!
6578
+ @game.right!
6440
6579
  in modifier: :shift
6441
- game.rotate!(:right)
6580
+ @game.rotate!(:right)
6442
6581
  in modifier: :control
6443
- game.rotate!(:left)
6582
+ @game.rotate!(:left)
6444
6583
  else
6445
6584
  # Do Nothing
6446
6585
  end
@@ -6505,22 +6644,314 @@ class Tetris
6505
6644
  end
6506
6645
 
6507
6646
  def start_moving_tetrominos_down
6508
- Glimmer::LibUI.timer(@game.delay) do
6509
- @game.down! if !@game.game_over? && !@game.paused?
6647
+ unless @tetrominos_start_moving_down
6648
+ @tetrominos_start_moving_down = true
6649
+ Glimmer::LibUI.timer(@game.delay) do
6650
+ @game.down! if !@game.game_over? && !@game.paused?
6651
+ end
6510
6652
  end
6511
6653
  end
6512
6654
 
6513
6655
  def show_game_over_dialog
6514
6656
  Glimmer::LibUI.queue_main do
6515
- msg_box('Game Over', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
6657
+ msg_box('Game Over!', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
6516
6658
  @game.restart!
6517
6659
  end
6518
6660
  end
6661
+
6662
+ def show_high_scores
6663
+ Glimmer::LibUI.queue_main do
6664
+ game_paused = !!@game.paused
6665
+ @game.paused = true
6666
+ if @game.high_scores.empty?
6667
+ high_scores_string = "No games have been scored yet."
6668
+ else
6669
+ high_scores_string = @game.high_scores.map do |high_score|
6670
+ "#{high_score.name} | Score: #{high_score.score} | Lines: #{high_score.lines} | Level: #{high_score.level}"
6671
+ end.join("\n")
6672
+ end
6673
+ msg_box('High Scores', high_scores_string)
6674
+ @game.paused = game_paused
6675
+ end
6676
+ end
6677
+
6678
+ def show_about_dialog
6679
+ Glimmer::LibUI.queue_main do
6680
+ msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
6681
+ end
6682
+ end
6519
6683
  end
6520
6684
 
6521
6685
  Tetris.new.launch
6522
6686
  ```
6523
6687
 
6688
+ ### Tic Tac Toe
6689
+
6690
+ [examples/tic_tac_toe.rb](examples/tic_tac_toe.rb)
6691
+
6692
+ Run with this command from the root of the project if you cloned the project:
6693
+
6694
+ ```
6695
+ ruby -r './lib/glimmer-dsl-libui' examples/tic_tac_toe.rb
6696
+ ```
6697
+
6698
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
6699
+
6700
+ ```
6701
+ ruby -r glimmer-dsl-libui -e "require 'examples/tic_tac_toe'"
6702
+ ```
6703
+
6704
+ Mac
6705
+
6706
+ ![glimmer-dsl-libui-mac-tic-tac-toe.png](images/glimmer-dsl-libui-mac-tic-tac-toe.png)
6707
+
6708
+ ![glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png)
6709
+
6710
+ ![glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png)
6711
+
6712
+ ![glimmer-dsl-libui-mac-tic-tac-toe-draw.png](images/glimmer-dsl-libui-mac-tic-tac-toe-draw.png)
6713
+
6714
+ Windows
6715
+
6716
+ ![glimmer-dsl-libui-windows-tic-tac-toe.png](images/glimmer-dsl-libui-windows-tic-tac-toe.png)
6717
+
6718
+ ![glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-o-wins.png)
6719
+
6720
+ ![glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-windows-tic-tac-toe-player-x-wins.png)
6721
+
6722
+ ![glimmer-dsl-libui-windows-tic-tac-toe-draw.png](images/glimmer-dsl-libui-windows-tic-tac-toe-draw.png)
6723
+
6724
+ Linux
6725
+
6726
+ ![glimmer-dsl-libui-linux-tic-tac-toe.png](images/glimmer-dsl-libui-linux-tic-tac-toe.png)
6727
+
6728
+ ![glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-o-wins.png)
6729
+
6730
+ ![glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-linux-tic-tac-toe-player-x-wins.png)
6731
+
6732
+ ![glimmer-dsl-libui-linux-tic-tac-toe-draw.png](images/glimmer-dsl-libui-linux-tic-tac-toe-draw.png)
6733
+
6734
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6735
+
6736
+ ```ruby
6737
+ require 'glimmer-dsl-libui'
6738
+
6739
+ require_relative "tic_tac_toe/board"
6740
+
6741
+ class TicTacToe
6742
+ include Glimmer
6743
+
6744
+ def initialize
6745
+ @tic_tac_toe_board = Board.new
6746
+ end
6747
+
6748
+ def launch
6749
+ create_gui
6750
+ register_observers
6751
+ @main_window.show
6752
+ end
6753
+
6754
+ def register_observers
6755
+ Glimmer::DataBinding::Observer.proc do |game_status|
6756
+ display_win_message if game_status == Board::WIN
6757
+ display_draw_message if game_status == Board::DRAW
6758
+ end.observe(@tic_tac_toe_board, :game_status)
6759
+
6760
+ 3.times.map do |row|
6761
+ 3.times.map do |column|
6762
+ Glimmer::DataBinding::Observer.proc do |sign|
6763
+ @cells[row][column].string = sign
6764
+ end.observe(@tic_tac_toe_board[row + 1, column + 1], :sign) # board model is 1-based
6765
+ end
6766
+ end
6767
+ end
6768
+
6769
+ def create_gui
6770
+ @main_window = window('Tic-Tac-Toe', 180, 180) {
6771
+ resizable false
6772
+
6773
+ @cells = []
6774
+ vertical_box {
6775
+ padded false
6776
+
6777
+ 3.times.map do |row|
6778
+ @cells << []
6779
+ horizontal_box {
6780
+ padded false
6781
+
6782
+ 3.times.map do |column|
6783
+ area {
6784
+ path {
6785
+ square(0, 0, 60)
6786
+
6787
+ stroke :black, thickness: 2
6788
+ }
6789
+ text(23, 19) {
6790
+ @cells[row] << string('') {
6791
+ font family: 'Arial', size: 20
6792
+ }
6793
+ }
6794
+ on_mouse_up do
6795
+ @tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
6796
+ end
6797
+ }
6798
+ end
6799
+ }
6800
+ end
6801
+ }
6802
+ }
6803
+ end
6804
+
6805
+ def display_win_message
6806
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
6807
+ end
6808
+
6809
+ def display_draw_message
6810
+ display_game_over_message("Draw!")
6811
+ end
6812
+
6813
+ def display_game_over_message(message_text)
6814
+ Glimmer::LibUI.queue_main do
6815
+ msg_box('Game Over', message_text)
6816
+ @tic_tac_toe_board.reset!
6817
+ end
6818
+ end
6819
+ end
6820
+
6821
+ TicTacToe.new.launch
6822
+ ```
6823
+
6824
+ ### Snake
6825
+
6826
+ Snake provides an example of building a desktop application [test-first](/spec/examples/snake/model/game_spec.rb) following the MVP ([Model](/examples/snake/model/game.rb) / [View](/examples/snake.rb) / [Presenter](/examples/snake/presenter/grid.rb)) architectural pattern.
6827
+
6828
+ [examples/snake.rb](examples/snake.rb)
6829
+
6830
+ Run with this command from the root of the project if you cloned the project:
6831
+
6832
+ ```
6833
+ ruby -r './lib/glimmer-dsl-libui' examples/snake.rb
6834
+ ```
6835
+
6836
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
6837
+
6838
+ ```
6839
+ ruby -r glimmer-dsl-libui -e "require 'examples/snake'"
6840
+ ```
6841
+
6842
+ Mac
6843
+
6844
+ ![glimmer-dsl-libui-mac-snake.png](images/glimmer-dsl-libui-mac-snake.png)
6845
+
6846
+ ![glimmer-dsl-libui-mac-snake-game-over.png](images/glimmer-dsl-libui-mac-snake-game-over.png)
6847
+
6848
+ Windows
6849
+
6850
+ ![glimmer-dsl-libui-windows-snake.png](images/glimmer-dsl-libui-windows-snake.png)
6851
+
6852
+ ![glimmer-dsl-libui-windows-snake-game-over.png](images/glimmer-dsl-libui-windows-snake-game-over.png)
6853
+
6854
+ Linux
6855
+
6856
+ ![glimmer-dsl-libui-linux-snake.png](images/glimmer-dsl-libui-linux-snake.png)
6857
+
6858
+ ![glimmer-dsl-libui-linux-snake-game-over.png](images/glimmer-dsl-libui-linux-snake-game-over.png)
6859
+
6860
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6861
+
6862
+ ```ruby
6863
+ require 'glimmer-dsl-libui'
6864
+ require 'glimmer/data_binding/observer'
6865
+
6866
+ require_relative 'snake/presenter/grid'
6867
+
6868
+ class Snake
6869
+ CELL_SIZE = 15
6870
+ SNAKE_MOVE_DELAY = 0.1
6871
+ include Glimmer
6872
+
6873
+ def initialize
6874
+ @game = Model::Game.new
6875
+ @grid = Presenter::Grid.new(@game)
6876
+ @game.start
6877
+ create_gui
6878
+ register_observers
6879
+ end
6880
+
6881
+ def launch
6882
+ @main_window.show
6883
+ end
6884
+
6885
+ def register_observers
6886
+ @game.height.times do |row|
6887
+ @game.width.times do |column|
6888
+ Glimmer::DataBinding::Observer.proc do |new_color|
6889
+ @cell_grid[row][column].fill = new_color
6890
+ end.observe(@grid.cells[row][column], :color)
6891
+ end
6892
+ end
6893
+
6894
+ Glimmer::DataBinding::Observer.proc do |game_over|
6895
+ Glimmer::LibUI.queue_main do
6896
+ if game_over
6897
+ msg_box('Game Over!', "Score: #{@game.score}")
6898
+ @game.start
6899
+ end
6900
+ end
6901
+ end.observe(@game, :over)
6902
+
6903
+ Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
6904
+ unless @game.over?
6905
+ @game.snake.move
6906
+ @main_window.title = "Glimmer Snake (Score: #{@game.score})"
6907
+ end
6908
+ end
6909
+ end
6910
+
6911
+ def create_gui
6912
+ @cell_grid = []
6913
+ @main_window = window('Glimmer Snake', @game.width * CELL_SIZE, @game.height * CELL_SIZE) {
6914
+ resizable false
6915
+
6916
+ vertical_box {
6917
+ padded false
6918
+
6919
+ @game.height.times do |row|
6920
+ @cell_grid << []
6921
+ horizontal_box {
6922
+ padded false
6923
+
6924
+ @game.width.times do |column|
6925
+ area {
6926
+ @cell_grid.last << path {
6927
+ square(0, 0, CELL_SIZE)
6928
+
6929
+ fill Presenter::Cell::COLOR_CLEAR
6930
+ }
6931
+
6932
+ on_key_up do |area_key_event|
6933
+ orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
6934
+ case orientation_and_key
6935
+ in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
6936
+ @game.snake.turn_right
6937
+ in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
6938
+ @game.snake.turn_left
6939
+ else
6940
+ # No Op
6941
+ end
6942
+ end
6943
+ }
6944
+ end
6945
+ }
6946
+ end
6947
+ }
6948
+ }
6949
+ end
6950
+ end
6951
+
6952
+ Snake.new.launch
6953
+ ```
6954
+
6524
6955
  ## Applications
6525
6956
 
6526
6957
  Here are some applications built with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
@@ -6551,9 +6982,10 @@ https://github.com/iraamaro/i3off-gtk-ruby
6551
6982
 
6552
6983
  ## Resources
6553
6984
 
6554
- - [libui C Library](https://github.com/andlabs/libui)
6555
- - [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
6556
6985
  - [Code Master Blog](https://andymaleh.blogspot.com/search/label/LibUI)
6986
+ - [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
6987
+ - [libui C Library](https://github.com/andlabs/libui)
6988
+ - [Go UI (Golang LibUI) API Documentation](https://pkg.go.dev/github.com/andlabs/ui)
6557
6989
 
6558
6990
  ## Help
6559
6991