glimmer-dsl-libui 0.2.17 → 0.2.21

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.17
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.2.21
2
2
  ## Prerequisite-Free Ruby Desktop Development GUI Library
3
3
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](http://badge.fury.io/rb/glimmer-dsl-libui)
4
4
  [![Join the chat at https://gitter.im/AndyObtiva/glimmer](https://badges.gitter.im/AndyObtiva/glimmer.svg)](https://gitter.im/AndyObtiva/glimmer?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
@@ -112,31 +112,24 @@ window('Area Gallery', 400, 400) {
112
112
  fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
113
113
  }
114
114
  path { # declarative stable path
115
- figure(100, 100) {
116
- line(100, 400)
117
- line(400, 100)
118
- line(400, 400)
119
-
120
- closed true
121
- }
122
-
115
+ polygon(100, 100, 100, 400, 400, 100, 400, 400)
116
+
123
117
  fill r: 202, g: 102, b: 104, a: 0.5
124
118
  stroke r: 0, g: 0, b: 0
125
119
  }
126
120
  path { # declarative stable path
127
- figure(0, 0) {
128
- bezier(200, 100, 100, 200, 400, 100)
129
- bezier(300, 100, 100, 300, 100, 400)
130
- bezier(100, 300, 300, 100, 400, 400)
131
-
132
- closed true
133
- }
121
+ polybezier(0, 0, 200, 100, 100, 200, 400, 100, 300, 100, 100, 300, 100, 400, 100, 300, 300, 100, 400, 400)
134
122
 
135
123
  fill r: 202, g: 102, b: 204, a: 0.5
136
124
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
137
125
  }
138
126
  path { # declarative stable path
139
- arc(400, 220, 180, 90, 90, false)
127
+ polyline(100, 100, 400, 100, 100, 400, 400, 400, 0, 0)
128
+
129
+ stroke r: 0, g: 0, b: 0, thickness: 2
130
+ }
131
+ path { # declarative stable path
132
+ arc(404, 216, 190, 90, 90, false)
140
133
 
141
134
  # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
142
135
  fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
@@ -148,9 +141,9 @@ window('Area Gallery', 400, 400) {
148
141
  fill r: 202, g: 102, b: 204, a: 0.5
149
142
  stroke r: 0, g: 0, b: 0, thickness: 2
150
143
  }
151
- text(160, 40, 100) { # x, y, width
144
+ text(161, 40, 100) { # x, y, width
152
145
  string('Area Gallery') {
153
- font family: 'Times', size: 14
146
+ font family: 'Arial', size: 14
154
147
  color :black
155
148
  }
156
149
  }
@@ -285,6 +278,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
285
278
  - [Custom Draw Text](#custom-draw-text)
286
279
  - [Method-Based Custom Keyword](#method-based-custom-keyword)
287
280
  - [Tetris](#tetris)
281
+ - [Tic Tac Toe](#tic-tac-toe)
288
282
  - [Applications](#applications)
289
283
  - [Manga2PDF](#manga2pdf)
290
284
  - [Befunge98 GUI](#befunge98-gui)
@@ -377,7 +371,7 @@ gem install glimmer-dsl-libui
377
371
  Or install via Bundler `Gemfile`:
378
372
 
379
373
  ```ruby
380
- gem 'glimmer-dsl-libui', '~> 0.2.17'
374
+ gem 'glimmer-dsl-libui', '~> 0.2.21'
381
375
  ```
382
376
 
383
377
  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.
@@ -453,7 +447,7 @@ These are all the supported keywords. Note that some keywords do not represent c
453
447
  Keyword(Args) | Properties | Listeners
454
448
  ------------- | ---------- | ---------
455
449
  `about_menu_item` | None | `on_clicked`
456
- `area` | None | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
450
+ `area` | `auto_draw_enabled` | `on_draw(area_draw_params)`, `on_mouse_event(area_mouse_event)`, `on_mouse_down(area_mouse_event)`, `on_mouse_up(area_mouse_event)`, `on_mouse_drag_started(area_mouse_event)`, `on_mouse_dragged(area_mouse_event)`, `on_mouse_dropped(area_mouse_event)`, `on_mouse_entered`, `on_mouse_exited`, `on_key_event(area_key_event)`, `on_key_down(area_key_event)`, `on_key_up(area_key_event)`
457
451
  `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)` | `x_center` (`Numeric`), `y_center` (`Numeric`), `radius` (`Numeric`), `start_angle` (`Numeric`), `sweep` (`Numeric`), `is_negative` (Boolean) | None
458
452
  `background_color_column(name as String)` | None | None
459
453
  `bezier(c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
@@ -463,6 +457,7 @@ Keyword(Args) | Properties | Listeners
463
457
  `checkbox_column(name as String)` | `editable` (Boolean) | None
464
458
  `checkbox_text_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
465
459
  `checkbox_text_color_column(name as String)` | `editable` (Boolean), `editable_checkbox` (Boolean), `editable_text` (Boolean) | None
460
+ `check_menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
466
461
  `combobox` | `items` (`Array` of `String`), `selected` (`Integer`) | `on_selected`
467
462
  `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`
468
463
  `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`
@@ -485,7 +480,7 @@ Keyword(Args) | Properties | Listeners
485
480
  `line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
486
481
  `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
487
482
  `menu(text as String)` | None | None
488
- `menu_item(text as String)` | `checked` (Boolean) | `on_clicked`
483
+ `menu_item(text as String)` | None | `on_clicked`
489
484
  `message_box` (alias for `msg_box`; see for arguments) | None | None
490
485
  `message_box_error` (alias for `msg_box_error`; see for arguments) | None | None
491
486
  `multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
@@ -494,6 +489,9 @@ Keyword(Args) | Properties | Listeners
494
489
  `non_wrapping_multiline_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
495
490
  `password_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
496
491
  `path(draw_fill_mode = :winding)` | `fill` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `stroke` (`Hash` of `:r` as `0`-`255`, `:g` as `0`-`255`, `:b` as `0`-`255`, `:a` as `0.0`-`1.0`, hex, or [X11](https://en.wikipedia.org/wiki/X11_color_names) color), `:cap` as (`:round`, `:square`, `:flat`), `:join` as (`:miter`, `:round`, `:bevel`), `:thickness` as `Numeric`, `:miter_limit` as `Numeric`, `:dashes` as `Array` of `Numeric` ) | None
492
+ `polygon(point_array as Array of Arrays of Numeric or Array of Numeric)` | `point_array` (`Array of Arrays of Numeric or Array of Numeric`) | None
493
+ `polyline(point_array as Array of Arrays of Numeric or Array of Numeric)` | `point_array` (`Array of Arrays of Numeric or Array of Numeric`) | None
494
+ `polybezier(point_array as Array of Arrays of Numeric or Array of Numeric)` | `point_array` (`Array of Arrays of Numeric or Array of Numeric`) | None
497
495
  `preferences_menu_item` | None | `on_clicked`
498
496
  `progress_bar` | `value` (`Numeric`) | None
499
497
  `progress_bar_column(name as String)` | None | None
@@ -501,6 +499,7 @@ Keyword(Args) | Properties | Listeners
501
499
  `radio_buttons` | `selected` (`Integer`) | `on_selected`
502
500
  `rectangle(x as Numeric, y as Numeric, width as Numeric, height as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `width` (`Numeric`), `height` (`Numeric`) | None
503
501
  `search_entry` | `read_only` (Boolean), `text` (`String`) | `on_changed`
502
+ `separator_menu_item` | None | None
504
503
  `slider(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
505
504
  `spinbox(min as Numeric, max as Numeric)` | `value` (`Numeric`) | `on_changed`
506
505
  `square(x as Numeric, y as Numeric, length as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `length` (`Numeric`) | None
@@ -514,7 +513,7 @@ Keyword(Args) | Properties | Listeners
514
513
  `time_picker` | `time` (`Hash` of keys: `sec` as `Integer`, `min` as `Integer`, `hour` as `Integer`) | `on_changed`
515
514
  `vertical_box` | `padded` (Boolean) | None
516
515
  `vertical_separator` | None | None
517
- `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`
516
+ `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`
518
517
 
519
518
  ### Common Control Properties
520
519
  - `enabled` (Boolean)
@@ -547,6 +546,35 @@ All operations that could normally be called on `LibUI` can also be called on `G
547
546
  - `Glimmer::LibUI::queue_main(&block)`: queues an operation to be run on the main event loop at the earliest opportunity possible
548
547
  - `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.
549
548
 
549
+ 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:
550
+ - `Glimmer::LibUI::integer_to_boolean(int, allow_nil: true)`
551
+ - `Glimmer::LibUI::boolean_to_integer(int, allow_nil: true)`
552
+ - `Glimmer::LibUI::degrees_to_radians(degrees)`
553
+ - `Glimmer::LibUI::interpret_color(value)`: interprets a color in any form like `String`, `Symbol`, or hex into an rgb `Hash`
554
+ - `Glimmer::LibUI::hex_to_rgb(value)`: converts a hex color to an rgb `Hash`
555
+ - `Glimmer::LibUI::enum_names`: provides all possible enum names to use with `Glimmer::LibUI::enum_symbols(enum_name)`
556
+ - `Glimmer::LibUI::enum_symbols(enum_name)`: returns all possible values for an enum. `enum_name` can be:
557
+ - `:draw_brush_type`: `[:solid, :linear_gradient, :radial_gradient, :image]`
558
+ - `:draw_line_cap`: `[:flat, :round, :square]`
559
+ - `:draw_line_join`: `[:miter, :round, :bevel]`
560
+ - `:draw_fill_mode`: `[:winding, :alternate]`
561
+ - `:attribute_type`: attributes for attributed `string`s: `[:family, :size, weight, :italic, :stretch, :color, :background, :underline, :underline_color, :features]`
562
+ - `:text_weight`: `[:minimum, :thin, :ultra_light, :light, :book, :normal, :medium, :semi_bold, :bold, :ultra_bold, :heavy, :ultra_heavy, :maximum]`
563
+ - `:text_italic`: `[:normal, :oblique, :italic]`
564
+ - `:text_stretch`: `[:ultra_condensed, :extra_condensed, :condensed, :semi_condensed, :normal, :semi_expanded, :expanded, :extra_expanded, :ultra_expanded]`
565
+ - `:underline`: `[:none, :single, :double, :suggestion, :color_custom, :color_spelling, :color_grammar, :color_auxiliary]`
566
+ - `:underline_color`: `[:custom, :spelling, :grammar, :auxiliary]`
567
+ - `:draw_text_align`: `[:left, :center, :right]`
568
+ - `:modifier`: `[:ctrl, :alt, :shift, :super]`
569
+ - `: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]`
570
+ - `:at`: for inserting `grid` controls: `[:leading, :top, :trailing, :bottom]`
571
+ - `:align`: `[:fill, :start, :center, :end]`
572
+ - `:table_value_type`: `[:string, :image, :int, :color]`
573
+ - `:table_model_column`: `[:never_editable, :always_editable]`
574
+ - `Glimmer::LibUI::enum_symbol_to_value(enum_name, enum_symbol, default_symbol: nil, default_index: 0)`
575
+ - `Glimmer::LibUI::enum_value_to_symbol(enum_name, enum_value)`
576
+ - `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]`
577
+
550
578
  ### Extra Dialogs
551
579
 
552
580
  - `open_file(window as Glimmer::LibUI::WindowProxy = ControlProxy::main_window_proxy)`: returns selected file (`String`) or `nil` if cancelled
@@ -745,6 +773,9 @@ Available nested `path` shapes:
745
773
  - `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
746
774
  - `line(x as Numeric, y as Numeric)`
747
775
  - `bezier(c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)`
776
+ - `polygon(point_array as Array of Arrays of Numeric or Array of Numeric)`: closed figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
777
+ - `polyline(point_array as Array of Arrays of Numeric or Array of Numeric)`: open figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
778
+ - `polybezier(point_array as Array of Arrays of Numeric or Array of Numeric)`: open figure of beziers; can receive points as [[start_x1, start_y1], [c1_x2, c1_y2, c2_x2, c2_y2, end_x2, end_y2], [c1_x3, c1_y3, c2_x3, c2_y3, end_x3, end_y3], ...] or [start_x1, start_y1, c1_x2, c1_y2, c2_x2, c2_y2, end_x2, end_y2, c1_x3, c1_y3, c2_x3, c2_y3, end_x3, end_y3, ...]
748
779
  - `figure(x=nil as Numeric, y=nil as Numeric)` (composite that can contain other shapes) (can set `closed true` to connect last point to first point automatically)
749
780
 
750
781
  Check [examples/area_gallery.rb](#area-gallery) for an overiew of all `path` shapes.
@@ -799,6 +830,12 @@ Note that when nesting an `area` directly underneath `window` (without a layout
799
830
 
800
831
  To redraw an `area`, you may call the `#queue_redraw_all` method, or simply `#redraw`.
801
832
 
833
+ `area` has the following Glimmer-added API methods/attributes:
834
+ - `request_auto_redraw`: requests auto redraw upon changes to nested stable `path` or shapes
835
+ - `pause_auto_redraw`: pause auto redraw upon changes to nested stable `path` or shapes (useful to avoid too many micro-change redraws, to group all redraws as one after many micro-changes)
836
+ - `resume_auto_redraw`: resume auto redraw upon changes to nested stable `path` or shapes
837
+ - `auto_redraw_enabled`/`auto_redraw_enabled?`/`auto_redraw_enabled=`: an attribute to disable/enable auto redraw on an `area` upon changes to nested stable `path` or shapes
838
+
802
839
  A transform `matrix` can be set on a path by building a `matrix(m11 = nil, m12 = nil, m21 = nil, m22 = nil, m31 = nil, m32 = nil) {operations}` proxy object and then setting via `transform` property, or alternatively by building and setting the matrix in one call to `transform(m11 = nil, m12 = nil, m21 = nil, m22 = nil, m31 = nil, m32 = nil) {operations}` passing it the matrix arguments and/or content operations.
803
840
 
804
841
  When instantiating a `matrix` object, it always starts with identity matrix.
@@ -866,7 +903,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
866
903
 
867
904
  `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)
868
905
 
869
- Available [X11](https://en.wikipedia.org/wiki/X11_color_names) colors can be obtained through `Glimmer::LibUI.x11_colors` method.
906
+ Available [X11 colors](https://en.wikipedia.org/wiki/X11_color_names) can be obtained through `Glimmer::LibUI.x11_colors` method.
870
907
 
871
908
  Check [Basic Transform](#basic-transform) example for use of [X11](https://en.wikipedia.org/wiki/X11_color_names) colors.
872
909
 
@@ -1083,8 +1120,8 @@ window('Method-Based Custom Keyword') {
1083
1120
 
1084
1121
  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):
1085
1122
  - Check out [LibUI ffi.rb](https://github.com/kojix2/LibUI/blob/main/lib/libui/ffi.rb)
1086
- - Check out the [libui C headers](https://github.com/andlabs/libui/blob/master/ui.h)
1087
- - 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.
1123
+ - Check out the [libui C Headers](https://github.com/andlabs/libui/blob/master/ui.h)
1124
+ - 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.
1088
1125
 
1089
1126
  ## Packaging
1090
1127
 
@@ -4059,31 +4096,24 @@ window('Area Gallery', 400, 400) {
4059
4096
  fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
4060
4097
  }
4061
4098
  path { # declarative stable path
4062
- figure(100, 100) {
4063
- line(100, 400)
4064
- line(400, 100)
4065
- line(400, 400)
4066
-
4067
- closed true
4068
- }
4069
-
4099
+ polygon(100, 100, 100, 400, 400, 100, 400, 400)
4100
+
4070
4101
  fill r: 202, g: 102, b: 104, a: 0.5
4071
4102
  stroke r: 0, g: 0, b: 0
4072
4103
  }
4073
4104
  path { # declarative stable path
4074
- figure(0, 0) {
4075
- bezier(200, 100, 100, 200, 400, 100)
4076
- bezier(300, 100, 100, 300, 100, 400)
4077
- bezier(100, 300, 300, 100, 400, 400)
4078
-
4079
- closed true
4080
- }
4105
+ polybezier(0, 0, 200, 100, 100, 200, 400, 100, 300, 100, 100, 300, 100, 400, 100, 300, 300, 100, 400, 400)
4081
4106
 
4082
4107
  fill r: 202, g: 102, b: 204, a: 0.5
4083
4108
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4084
4109
  }
4085
4110
  path { # declarative stable path
4086
- arc(400, 220, 180, 90, 90, false)
4111
+ polyline(100, 100, 400, 100, 100, 400, 400, 400, 0, 0)
4112
+
4113
+ stroke r: 0, g: 0, b: 0, thickness: 2
4114
+ }
4115
+ path { # declarative stable path
4116
+ arc(404, 216, 190, 90, 90, false)
4087
4117
 
4088
4118
  # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
4089
4119
  fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
@@ -4095,9 +4125,9 @@ window('Area Gallery', 400, 400) {
4095
4125
  fill r: 202, g: 102, b: 204, a: 0.5
4096
4126
  stroke r: 0, g: 0, b: 0, thickness: 2
4097
4127
  }
4098
- text(160, 40, 100) { # x, y, width
4128
+ text(161, 40, 100) { # x, y, width
4099
4129
  string('Area Gallery') {
4100
- font family: 'Times', size: 14
4130
+ font family: 'Arial', size: 14
4101
4131
  color :black
4102
4132
  }
4103
4133
  }
@@ -4246,18 +4276,42 @@ window('Area Gallery', 400, 400) {
4246
4276
  end_x 400
4247
4277
  end_y 400
4248
4278
  }
4249
-
4250
- closed true
4251
4279
  }
4252
4280
 
4253
4281
  fill r: 202, g: 102, b: 204, a: 0.5
4254
4282
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4255
4283
  }
4284
+ path { # declarative stable path
4285
+ polyline(100, 100, 400, 100, 100, 400, 400, 400, 0, 0)
4286
+ figure {
4287
+ x 100
4288
+ y 100
4289
+
4290
+ line {
4291
+ x 400
4292
+ y 100
4293
+ }
4294
+ line {
4295
+ x 100
4296
+ y 400
4297
+ }
4298
+ line {
4299
+ x 400
4300
+ y 400
4301
+ }
4302
+ line {
4303
+ x 0
4304
+ y 0
4305
+ }
4306
+ }
4307
+
4308
+ stroke r: 0, g: 0, b: 0, thickness: 2
4309
+ }
4256
4310
  path { # declarative stable path
4257
4311
  arc {
4258
- x_center 400
4259
- y_center 220
4260
- radius 180
4312
+ x_center 404
4313
+ y_center 216
4314
+ radius 190
4261
4315
  start_angle 90
4262
4316
  sweep 90
4263
4317
  is_negative false
@@ -4278,12 +4332,12 @@ window('Area Gallery', 400, 400) {
4278
4332
  stroke r: 0, g: 0, b: 0, thickness: 2
4279
4333
  }
4280
4334
  text {
4281
- x 160
4335
+ x 161
4282
4336
  y 40
4283
4337
  width 100
4284
4338
 
4285
4339
  string {
4286
- font family: 'Times', size: 14
4340
+ font family: 'Arial', size: 14
4287
4341
  color :black
4288
4342
 
4289
4343
  'Area Gallery'
@@ -4354,42 +4408,35 @@ window('Area Gallery', 400, 400) {
4354
4408
  path { # a dynamic path is added semi-declaratively inside on_draw block
4355
4409
  square(0, 0, 100)
4356
4410
  square(100, 100, 400)
4357
-
4411
+
4358
4412
  fill r: 102, g: 102, b: 204
4359
4413
  }
4360
4414
  path { # a dynamic path is added semi-declaratively inside on_draw block
4361
4415
  rectangle(0, 100, 100, 400)
4362
4416
  rectangle(100, 0, 400, 100)
4363
-
4417
+
4364
4418
  # linear gradient (has x0, y0, x1, y1, and stops)
4365
4419
  fill x0: 10, y0: 10, x1: 350, y1: 350, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 0.75, r: 102, g: 102, b: 204}]
4366
4420
  }
4367
4421
  path { # a dynamic path is added semi-declaratively inside on_draw block
4368
- figure(100, 100) {
4369
- line(100, 400)
4370
- line(400, 100)
4371
- line(400, 400)
4372
-
4373
- closed true
4374
- }
4375
-
4422
+ polygon(100, 100, 100, 400, 400, 100, 400, 400)
4423
+
4376
4424
  fill r: 202, g: 102, b: 104, a: 0.5
4377
4425
  stroke r: 0, g: 0, b: 0
4378
4426
  }
4379
4427
  path { # a dynamic path is added semi-declaratively inside on_draw block
4380
- figure(0, 0) {
4381
- bezier(200, 100, 100, 200, 400, 100)
4382
- bezier(300, 100, 100, 300, 100, 400)
4383
- bezier(100, 300, 300, 100, 400, 400)
4384
-
4385
- closed true
4386
- }
4387
-
4428
+ polybezier(0, 0, 200, 100, 100, 200, 400, 100, 300, 100, 100, 300, 100, 400, 100, 300, 300, 100, 400, 400)
4429
+
4388
4430
  fill r: 202, g: 102, b: 204, a: 0.5
4389
4431
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4390
4432
  }
4391
4433
  path { # a dynamic path is added semi-declaratively inside on_draw block
4392
- arc(400, 220, 180, 90, 90, false)
4434
+ polyline(100, 100, 400, 100, 100, 400, 400, 400, 0, 0)
4435
+
4436
+ stroke r: 0, g: 0, b: 0, thickness: 2
4437
+ }
4438
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4439
+ arc(404, 216, 190, 90, 90, false)
4393
4440
 
4394
4441
  # radial gradient (has an outer_radius in addition to x0, y0, x1, y1, and stops)
4395
4442
  fill outer_radius: 90, x0: 0, y0: 0, x1: 500, y1: 500, stops: [{pos: 0.25, r: 102, g: 102, b: 204, a: 0.5}, {pos: 0.75, r: 204, g: 102, b: 204}]
@@ -4401,9 +4448,9 @@ window('Area Gallery', 400, 400) {
4401
4448
  fill r: 202, g: 102, b: 204, a: 0.5
4402
4449
  stroke r: 0, g: 0, b: 0, thickness: 2
4403
4450
  }
4404
- text(160, 40, 100) { # x, y, width
4451
+ text(161, 40, 100) { # x, y, width
4405
4452
  string('Area Gallery') {
4406
- font family: 'Times', size: 14
4453
+ font family: 'Arial', size: 14
4407
4454
  color :black
4408
4455
  }
4409
4456
  }
@@ -4518,10 +4565,10 @@ window('Area Gallery', 400, 400) {
4518
4565
  x 400
4519
4566
  y 400
4520
4567
  }
4521
-
4568
+
4522
4569
  closed true
4523
4570
  }
4524
-
4571
+
4525
4572
  fill r: 202, g: 102, b: 104, a: 0.5
4526
4573
  stroke r: 0, g: 0, b: 0
4527
4574
  }
@@ -4554,18 +4601,42 @@ window('Area Gallery', 400, 400) {
4554
4601
  end_x 400
4555
4602
  end_y 400
4556
4603
  }
4557
-
4558
- closed true
4559
4604
  }
4560
-
4605
+
4561
4606
  fill r: 202, g: 102, b: 204, a: 0.5
4562
4607
  stroke r: 0, g: 0, b: 0, thickness: 2, dashes: [50, 10, 10, 10], dash_phase: -50.0
4563
4608
  }
4609
+ path { # a dynamic path is added semi-declaratively inside on_draw block
4610
+ polyline(100, 100, 400, 100, 100, 400, 400, 400, 0, 0)
4611
+ figure {
4612
+ x 100
4613
+ y 100
4614
+
4615
+ line {
4616
+ x 400
4617
+ y 100
4618
+ }
4619
+ line {
4620
+ x 100
4621
+ y 400
4622
+ }
4623
+ line {
4624
+ x 400
4625
+ y 400
4626
+ }
4627
+ line {
4628
+ x 0
4629
+ y 0
4630
+ }
4631
+ }
4632
+
4633
+ stroke r: 0, g: 0, b: 0, thickness: 2
4634
+ }
4564
4635
  path { # a dynamic path is added semi-declaratively inside on_draw block
4565
4636
  arc {
4566
- x_center 400
4567
- y_center 220
4568
- radius 180
4637
+ x_center 404
4638
+ y_center 216
4639
+ radius 190
4569
4640
  start_angle 90
4570
4641
  sweep 90
4571
4642
  is_negative false
@@ -4586,12 +4657,12 @@ window('Area Gallery', 400, 400) {
4586
4657
  stroke r: 0, g: 0, b: 0, thickness: 2
4587
4658
  }
4588
4659
  text {
4589
- x 160
4660
+ x 161
4590
4661
  y 40
4591
4662
  width 100
4592
4663
 
4593
4664
  string {
4594
- font family: 'Times', size: 14
4665
+ font family: 'Arial', size: 14
4595
4666
  color :black
4596
4667
 
4597
4668
  'Area Gallery'
@@ -4908,20 +4979,13 @@ end
4908
4979
 
4909
4980
  # method-based custom control representing a graph path
4910
4981
  def graph_path(width, height, should_extend, &block)
4911
- locations = point_locations(width, height)
4982
+ locations = point_locations(width, height).flatten
4912
4983
  path {
4913
- first_location = locations[0] # x and y
4914
- figure(first_location[0], first_location[1]) {
4915
- locations.each do |loc|
4916
- line(loc[0], loc[1])
4917
- end
4918
- if should_extend
4919
- line(width, height)
4920
- line(0, height)
4921
-
4922
- closed true
4923
- end
4924
- }
4984
+ if should_extend
4985
+ polygon(locations + [width, height, 0, height])
4986
+ else
4987
+ polyline(locations)
4988
+ end
4925
4989
 
4926
4990
  # apply a transform to the coordinate space for this path so (0, 0) is the top-left corner of the graph
4927
4991
  transform {
@@ -6202,6 +6266,8 @@ window('Method-Based Custom Keyword') {
6202
6266
 
6203
6267
  ### Tetris
6204
6268
 
6269
+ 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.
6270
+
6205
6271
  [examples/tetris.rb](examples/tetris.rb)
6206
6272
 
6207
6273
  Run with this command from the root of the project if you cloned the project:
@@ -6220,6 +6286,10 @@ Mac
6220
6286
 
6221
6287
  ![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png)
6222
6288
 
6289
+ ![glimmer-dsl-libui-mac-tetris-game-over.png](images/glimmer-dsl-libui-mac-tetris-game-over.png)
6290
+
6291
+ ![glimmer-dsl-libui-mac-tetris-high-scores.png](images/glimmer-dsl-libui-mac-tetris-high-scores.png)
6292
+
6223
6293
  New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6224
6294
 
6225
6295
  ```ruby
@@ -6232,85 +6302,314 @@ class Tetris
6232
6302
 
6233
6303
  BLOCK_SIZE = 25
6234
6304
  BEVEL_CONSTANT = 20
6305
+ COLOR_GRAY = {r: 192, g: 192, b: 192}
6235
6306
 
6236
- attr_reader :game
6237
-
6238
6307
  def initialize
6239
6308
  @game = Model::Game.new
6240
- create_gui
6241
- register_observers
6242
6309
  end
6243
6310
 
6244
6311
  def launch
6312
+ create_gui
6313
+ register_observers
6245
6314
  @game.start!
6246
6315
  @main_window.show
6247
6316
  end
6248
6317
 
6249
6318
  def create_gui
6250
- @main_window = window('Glimmer Tetris', Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE) {
6251
- playfield(playfield_width: Model::Game::PLAYFIELD_WIDTH, playfield_height: Model::Game::PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
6319
+ menu_bar
6320
+
6321
+ @main_window = window('Glimmer Tetris') {
6322
+ content_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE + 98
6323
+ resizable false
6324
+
6325
+ vertical_box {
6326
+ label { # filler
6327
+ stretchy false
6328
+ }
6329
+
6330
+ score_board(block_size: BLOCK_SIZE) {
6331
+ stretchy false
6332
+ }
6333
+
6334
+ @playfield_blocks = playfield(playfield_width: Model::Game::PLAYFIELD_WIDTH, playfield_height: Model::Game::PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
6335
+ }
6252
6336
  }
6253
6337
  end
6254
6338
 
6255
6339
  def register_observers
6256
6340
  Glimmer::DataBinding::Observer.proc do |game_over|
6257
6341
  if game_over
6342
+ @pause_menu_item.enabled = false
6258
6343
  show_game_over_dialog
6259
6344
  else
6345
+ @pause_menu_item.enabled = true
6260
6346
  start_moving_tetrominos_down
6261
6347
  end
6262
6348
  end.observe(@game, :game_over)
6263
6349
 
6264
6350
  Model::Game::PLAYFIELD_HEIGHT.times do |row|
6265
- Model::Game::PLAYFIELD_HEIGHT.times do |column|
6351
+ Model::Game::PLAYFIELD_WIDTH.times do |column|
6266
6352
  Glimmer::DataBinding::Observer.proc do |new_color|
6267
- @blocks[row][column].fill = new_color
6353
+ Glimmer::LibUI.queue_main do
6354
+ color = Glimmer::LibUI.interpret_color(new_color)
6355
+ block = @playfield_blocks[row][column]
6356
+ block[:background_square].fill = color
6357
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
6358
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6359
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6360
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6361
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
6362
+ end
6268
6363
  end.observe(@game.playfield[row][column], :color)
6269
6364
  end
6270
6365
  end
6366
+
6367
+ Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
6368
+ Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
6369
+ Glimmer::DataBinding::Observer.proc do |new_color|
6370
+ Glimmer::LibUI.queue_main do
6371
+ color = Glimmer::LibUI.interpret_color(new_color)
6372
+ block = @preview_playfield_blocks[row][column]
6373
+ block[:background_square].fill = color
6374
+ block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
6375
+ block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6376
+ block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6377
+ block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
6378
+ block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
6379
+ end
6380
+ end.observe(@game.preview_playfield[row][column], :color)
6381
+ end
6382
+ end
6383
+
6384
+ Glimmer::DataBinding::Observer.proc do |new_score|
6385
+ Glimmer::LibUI.queue_main do
6386
+ @score_label.text = new_score.to_s
6387
+ end
6388
+ end.observe(@game, :score)
6389
+
6390
+ Glimmer::DataBinding::Observer.proc do |new_lines|
6391
+ Glimmer::LibUI.queue_main do
6392
+ @lines_label.text = new_lines.to_s
6393
+ end
6394
+ end.observe(@game, :lines)
6395
+
6396
+ Glimmer::DataBinding::Observer.proc do |new_level|
6397
+ Glimmer::LibUI.queue_main do
6398
+ @level_label.text = new_level.to_s
6399
+ end
6400
+ end.observe(@game, :level)
6271
6401
  end
6272
6402
 
6273
- def playfield(playfield_width: , playfield_height: , block_size: )
6274
- area {
6275
- @blocks = playfield_height.times.map do |row|
6276
- playfield_width.times.map do |column|
6277
- block(row: row, column: column, block_size: block_size)
6403
+ def menu_bar
6404
+ menu('Game') {
6405
+ @pause_menu_item = check_menu_item('Pause') {
6406
+ enabled false
6407
+
6408
+ on_clicked do
6409
+ @game.paused = @pause_menu_item.checked?
6410
+ end
6411
+ }
6412
+ menu_item('Restart') {
6413
+ on_clicked do
6414
+ @game.restart!
6415
+ end
6416
+ }
6417
+ separator_menu_item
6418
+ menu_item('Exit') {
6419
+ on_clicked do
6420
+ exit(0)
6421
+ end
6422
+ }
6423
+ quit_menu_item if OS.mac?
6424
+ }
6425
+
6426
+ menu('View') {
6427
+ menu_item('Show High Scores') {
6428
+ on_clicked do
6429
+ show_high_scores
6430
+ end
6431
+ }
6432
+ menu_item('Clear High Scores') {
6433
+ on_clicked {
6434
+ @game.clear_high_scores!
6435
+ }
6436
+ }
6437
+ }
6438
+
6439
+ menu('Options') {
6440
+ radio_menu_item('Instant Down on Up Arrow') {
6441
+ on_clicked do
6442
+ @game.instant_down_on_up = true
6443
+ end
6444
+ }
6445
+ radio_menu_item('Rotate Right on Up Arrow') {
6446
+ on_clicked do
6447
+ @game.rotate_right_on_up = true
6448
+ end
6449
+ }
6450
+ radio_menu_item('Rotate Left on Up Arrow') {
6451
+ on_clicked do
6452
+ @game.rotate_left_on_up = true
6453
+ end
6454
+ }
6455
+ }
6456
+
6457
+ menu('Help') {
6458
+ if OS.mac?
6459
+ about_menu_item {
6460
+ on_clicked do
6461
+ show_about_dialog
6462
+ end
6463
+ }
6464
+ end
6465
+ menu_item('About') {
6466
+ on_clicked do
6467
+ show_about_dialog
6278
6468
  end
6469
+ }
6470
+ }
6471
+ end
6472
+
6473
+ def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
6474
+ blocks = []
6475
+ vertical_box {
6476
+ padded false
6477
+
6478
+ playfield_height.times.map do |row|
6479
+ blocks << []
6480
+ horizontal_box {
6481
+ padded false
6482
+
6483
+ playfield_width.times.map do |column|
6484
+ blocks.last << block(row: row, column: column, block_size: block_size)
6485
+ end
6486
+ }
6279
6487
  end
6280
6488
 
6489
+ extra_content&.call
6490
+ }
6491
+ blocks
6492
+ end
6493
+
6494
+ def block(row: , column: , block_size: , &extra_content)
6495
+ block = {}
6496
+ bevel_pixel_size = 0.16 * block_size.to_f
6497
+ color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
6498
+ area {
6499
+ block[:background_square] = path {
6500
+ square(0, 0, block_size)
6501
+
6502
+ fill color
6503
+ }
6504
+ block[:top_bevel_edge] = path {
6505
+ polygon(0, 0, block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size, bevel_pixel_size)
6506
+
6507
+ fill r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT
6508
+ }
6509
+ block[:right_bevel_edge] = path {
6510
+ polygon(block_size, 0, block_size - bevel_pixel_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size, block_size)
6511
+
6512
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
6513
+ }
6514
+ block[:bottom_bevel_edge] = path {
6515
+ polygon(block_size, block_size, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size, block_size - bevel_pixel_size)
6516
+
6517
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
6518
+ }
6519
+ block[:left_bevel_edge] = path {
6520
+ polygon(0, 0, 0, block_size, bevel_pixel_size, block_size - bevel_pixel_size, bevel_pixel_size, bevel_pixel_size)
6521
+
6522
+ fill r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT
6523
+ }
6524
+ block[:border_square] = path {
6525
+ square(0, 0, block_size)
6526
+
6527
+ stroke COLOR_GRAY
6528
+ }
6529
+
6281
6530
  on_key_down do |key_event|
6282
6531
  case key_event
6283
6532
  in ext_key: :down
6284
- game.down!
6533
+ @game.down!
6534
+ in key: ' '
6535
+ @game.down!(instant: true)
6285
6536
  in ext_key: :up
6286
- case game.up_arrow_action
6537
+ case @game.up_arrow_action
6287
6538
  when :instant_down
6288
- game.down!(instant: true)
6539
+ @game.down!(instant: true)
6289
6540
  when :rotate_right
6290
- game.rotate!(:right)
6541
+ @game.rotate!(:right)
6291
6542
  when :rotate_left
6292
- game.rotate!(:left)
6543
+ @game.rotate!(:left)
6293
6544
  end
6294
6545
  in ext_key: :left
6295
- game.left!
6546
+ @game.left!
6296
6547
  in ext_key: :right
6297
- game.right!
6548
+ @game.right!
6298
6549
  in modifier: :shift
6299
- game.rotate!(:right)
6550
+ @game.rotate!(:right)
6300
6551
  in modifier: :control
6301
- game.rotate!(:left)
6552
+ @game.rotate!(:left)
6302
6553
  else
6303
6554
  # Do Nothing
6304
6555
  end
6305
6556
  end
6557
+
6558
+ extra_content&.call
6306
6559
  }
6560
+ block
6307
6561
  end
6308
6562
 
6309
- def block(row: , column: , block_size: )
6310
- path {
6311
- square(column * block_size, row * block_size, block_size)
6312
-
6313
- fill Model::Block::COLOR_CLEAR
6563
+ def score_board(block_size: , &extra_content)
6564
+ vertical_box {
6565
+ horizontal_box {
6566
+ label # filler
6567
+ @preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
6568
+ label # filler
6569
+ }
6570
+
6571
+ horizontal_box {
6572
+ label # filler
6573
+ grid {
6574
+ stretchy false
6575
+
6576
+ label('Score') {
6577
+ left 0
6578
+ top 0
6579
+ halign :fill
6580
+ }
6581
+ @score_label = label {
6582
+ left 0
6583
+ top 1
6584
+ halign :center
6585
+ }
6586
+
6587
+ label('Lines') {
6588
+ left 1
6589
+ top 0
6590
+ halign :fill
6591
+ }
6592
+ @lines_label = label {
6593
+ left 1
6594
+ top 1
6595
+ halign :center
6596
+ }
6597
+
6598
+ label('Level') {
6599
+ left 2
6600
+ top 0
6601
+ halign :fill
6602
+ }
6603
+ @level_label = label {
6604
+ left 2
6605
+ top 1
6606
+ halign :center
6607
+ }
6608
+ }
6609
+ label # filler
6610
+ }
6611
+
6612
+ extra_content&.call
6314
6613
  }
6315
6614
  end
6316
6615
 
@@ -6321,13 +6620,151 @@ class Tetris
6321
6620
  end
6322
6621
 
6323
6622
  def show_game_over_dialog
6324
- msg_box('Game Over', "Score: #{@game.high_scores.first.score}")
6623
+ Glimmer::LibUI.queue_main do
6624
+ msg_box('Game Over!', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
6625
+ @game.restart!
6626
+ end
6627
+ end
6628
+
6629
+ def show_high_scores
6630
+ Glimmer::LibUI.queue_main do
6631
+ if @game.high_scores.empty?
6632
+ high_scores_string = "No games have been scored yet."
6633
+ else
6634
+ high_scores_string = @game.high_scores.map do |high_score|
6635
+ "#{high_score.name} | Score: #{high_score.score} | Lines: #{high_score.lines} | Level: #{high_score.level}"
6636
+ end.join("\n")
6637
+ end
6638
+ msg_box('High Scores', high_scores_string)
6639
+ end
6640
+ end
6641
+
6642
+ def show_about_dialog
6643
+ Glimmer::LibUI.queue_main do
6644
+ msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
6645
+ end
6325
6646
  end
6326
6647
  end
6327
6648
 
6328
6649
  Tetris.new.launch
6329
6650
  ```
6330
6651
 
6652
+ ### Tic Tac Toe
6653
+
6654
+ [examples/tic_tac_toe.rb](examples/tic_tac_toe.rb)
6655
+
6656
+ Run with this command from the root of the project if you cloned the project:
6657
+
6658
+ ```
6659
+ ruby -r './lib/glimmer-dsl-libui' examples/tic_tac_toe.rb
6660
+ ```
6661
+
6662
+ Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
6663
+
6664
+ ```
6665
+ ruby -r glimmer-dsl-libui -e "require 'examples/tic_tac_toe'"
6666
+ ```
6667
+
6668
+ Mac
6669
+
6670
+ ![glimmer-dsl-libui-mac-tic-tac-toe.png](images/glimmer-dsl-libui-mac-tic-tac-toe.png)
6671
+
6672
+ ![glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png)
6673
+
6674
+ ![glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png)
6675
+
6676
+ ![glimmer-dsl-libui-mac-tic-tac-toe-draw.png](images/glimmer-dsl-libui-mac-tic-tac-toe-draw.png)
6677
+
6678
+ New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
6679
+
6680
+ ```ruby
6681
+ require 'glimmer-dsl-libui'
6682
+
6683
+ require_relative "tic_tac_toe/board"
6684
+
6685
+ class TicTacToe
6686
+ include Glimmer
6687
+
6688
+ def initialize
6689
+ @tic_tac_toe_board = Board.new
6690
+ end
6691
+
6692
+ def launch
6693
+ create_gui
6694
+ register_observers
6695
+ @main_window.show
6696
+ end
6697
+
6698
+ def register_observers
6699
+ Glimmer::DataBinding::Observer.proc do |game_status|
6700
+ display_win_message if game_status == Board::WIN
6701
+ display_draw_message if game_status == Board::DRAW
6702
+ end.observe(@tic_tac_toe_board, :game_status)
6703
+
6704
+ 3.times.map do |row|
6705
+ 3.times.map do |column|
6706
+ Glimmer::DataBinding::Observer.proc do |sign|
6707
+ @cells[row][column].string = sign
6708
+ end.observe(@tic_tac_toe_board[row + 1, column + 1], :sign) # board model is 1-based
6709
+ end
6710
+ end
6711
+ end
6712
+
6713
+ def create_gui
6714
+ @main_window = window('Tic-Tac-Toe', 180, 180) {
6715
+ resizable false
6716
+
6717
+ @cells = []
6718
+ vertical_box {
6719
+ padded false
6720
+
6721
+ 3.times.map do |row|
6722
+ @cells << []
6723
+ horizontal_box {
6724
+ padded false
6725
+
6726
+ 3.times.map do |column|
6727
+ area {
6728
+ path {
6729
+ square(0, 0, 60)
6730
+
6731
+ stroke :black, thickness: 2
6732
+ }
6733
+ text(23, 19) {
6734
+ @cells[row] << string('') {
6735
+ font family: 'Arial', size: 20
6736
+ }
6737
+ }
6738
+ on_mouse_up do
6739
+ @tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
6740
+ end
6741
+ }
6742
+ end
6743
+ }
6744
+ end
6745
+ }
6746
+ }
6747
+ end
6748
+
6749
+ def display_win_message
6750
+ display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
6751
+ end
6752
+
6753
+ def display_draw_message
6754
+ display_game_over_message("Draw!")
6755
+ end
6756
+
6757
+ def display_game_over_message(message_text)
6758
+ Glimmer::LibUI.queue_main do
6759
+ msg_box('Game Over', message_text)
6760
+ @tic_tac_toe_board.reset!
6761
+ end
6762
+ end
6763
+ end
6764
+
6765
+ TicTacToe.new.launch
6766
+ ```
6767
+
6331
6768
  ## Applications
6332
6769
 
6333
6770
  Here are some applications built with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
@@ -6358,9 +6795,10 @@ https://github.com/iraamaro/i3off-gtk-ruby
6358
6795
 
6359
6796
  ## Resources
6360
6797
 
6361
- - [libui C Library](https://github.com/andlabs/libui)
6362
- - [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
6363
6798
  - [Code Master Blog](https://andymaleh.blogspot.com/search/label/LibUI)
6799
+ - [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
6800
+ - [libui C Library](https://github.com/andlabs/libui)
6801
+ - [Go UI (Golang LibUI) API Documentation](https://pkg.go.dev/github.com/andlabs/ui)
6364
6802
 
6365
6803
  ## Help
6366
6804