glimmer-dsl-libui 0.2.18 → 0.2.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +25 -0
- data/README.md +510 -35
- data/VERSION +1 -1
- data/examples/meta_example.rb +1 -1
- data/examples/snake/model/apple.rb +33 -0
- data/examples/snake/model/game.rb +52 -0
- data/examples/snake/model/snake.rb +95 -0
- data/examples/snake/model/vertebra.rb +22 -0
- data/examples/snake/presenter/cell.rb +27 -0
- data/examples/snake/presenter/grid.rb +47 -0
- data/examples/snake.rb +90 -0
- data/examples/tetris/model/game.rb +3 -1
- data/examples/tetris.rb +222 -23
- data/examples/tic_tac_toe/board.rb +145 -0
- data/examples/tic_tac_toe/cell.rb +48 -0
- data/examples/tic_tac_toe.rb +85 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/libui/control_proxy/window_proxy.rb +40 -1
- data/lib/glimmer/libui.rb +29 -1
- metadata +12 -2
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.
|
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.22
|
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)
|
@@ -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): 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.
|
376
|
+
gem 'glimmer-dsl-libui', '~> 0.2.22'
|
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.
|
@@ -446,7 +449,7 @@ These are all the supported keywords. Note that some keywords do not represent c
|
|
446
449
|
Keyword(Args) | Properties | Listeners
|
447
450
|
------------- | ---------- | ---------
|
448
451
|
`about_menu_item` | None | `on_clicked`
|
449
|
-
`area` |
|
452
|
+
`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)`
|
450
453
|
`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
|
451
454
|
`background_color_column(name as String)` | None | None
|
452
455
|
`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
|
@@ -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)` |
|
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)
|
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
|
1092
|
-
- Check out the [Go UI (Golang LibUI)
|
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
|
@@ -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:
|
@@ -6252,6 +6288,10 @@ Mac
|
|
6252
6288
|
|
6253
6289
|
![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png)
|
6254
6290
|
|
6291
|
+
![glimmer-dsl-libui-mac-tetris-game-over.png](images/glimmer-dsl-libui-mac-tetris-game-over.png)
|
6292
|
+
|
6293
|
+
![glimmer-dsl-libui-mac-tetris-high-scores.png](images/glimmer-dsl-libui-mac-tetris-high-scores.png)
|
6294
|
+
|
6255
6295
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6256
6296
|
|
6257
6297
|
```ruby
|
@@ -6266,40 +6306,55 @@ class Tetris
|
|
6266
6306
|
BEVEL_CONSTANT = 20
|
6267
6307
|
COLOR_GRAY = {r: 192, g: 192, b: 192}
|
6268
6308
|
|
6269
|
-
attr_reader :game
|
6270
|
-
|
6271
6309
|
def initialize
|
6272
6310
|
@game = Model::Game.new
|
6273
|
-
create_gui
|
6274
|
-
register_observers
|
6275
6311
|
end
|
6276
6312
|
|
6277
6313
|
def launch
|
6314
|
+
create_gui
|
6315
|
+
register_observers
|
6278
6316
|
@game.start!
|
6279
6317
|
@main_window.show
|
6280
6318
|
end
|
6281
6319
|
|
6282
6320
|
def create_gui
|
6283
|
-
|
6284
|
-
|
6321
|
+
menu_bar
|
6322
|
+
|
6323
|
+
@main_window = window('Glimmer Tetris') {
|
6324
|
+
content_size Model::Game::PLAYFIELD_WIDTH * BLOCK_SIZE, Model::Game::PLAYFIELD_HEIGHT * BLOCK_SIZE + 98
|
6325
|
+
resizable false
|
6326
|
+
|
6327
|
+
vertical_box {
|
6328
|
+
label { # filler
|
6329
|
+
stretchy false
|
6330
|
+
}
|
6331
|
+
|
6332
|
+
score_board(block_size: BLOCK_SIZE) {
|
6333
|
+
stretchy false
|
6334
|
+
}
|
6335
|
+
|
6336
|
+
@playfield_blocks = playfield(playfield_width: Model::Game::PLAYFIELD_WIDTH, playfield_height: Model::Game::PLAYFIELD_HEIGHT, block_size: BLOCK_SIZE)
|
6337
|
+
}
|
6285
6338
|
}
|
6286
6339
|
end
|
6287
6340
|
|
6288
6341
|
def register_observers
|
6289
6342
|
Glimmer::DataBinding::Observer.proc do |game_over|
|
6290
6343
|
if game_over
|
6344
|
+
@pause_menu_item.enabled = false
|
6291
6345
|
show_game_over_dialog
|
6292
6346
|
else
|
6347
|
+
@pause_menu_item.enabled = true
|
6293
6348
|
start_moving_tetrominos_down
|
6294
6349
|
end
|
6295
6350
|
end.observe(@game, :game_over)
|
6296
6351
|
|
6297
6352
|
Model::Game::PLAYFIELD_HEIGHT.times do |row|
|
6298
|
-
Model::Game::
|
6353
|
+
Model::Game::PLAYFIELD_WIDTH.times do |column|
|
6299
6354
|
Glimmer::DataBinding::Observer.proc do |new_color|
|
6300
6355
|
Glimmer::LibUI.queue_main do
|
6301
6356
|
color = Glimmer::LibUI.interpret_color(new_color)
|
6302
|
-
block = @
|
6357
|
+
block = @playfield_blocks[row][column]
|
6303
6358
|
block[:background_square].fill = color
|
6304
6359
|
block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
|
6305
6360
|
block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
@@ -6310,27 +6365,135 @@ class Tetris
|
|
6310
6365
|
end.observe(@game.playfield[row][column], :color)
|
6311
6366
|
end
|
6312
6367
|
end
|
6368
|
+
|
6369
|
+
Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
|
6370
|
+
Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
|
6371
|
+
Glimmer::DataBinding::Observer.proc do |new_color|
|
6372
|
+
Glimmer::LibUI.queue_main do
|
6373
|
+
color = Glimmer::LibUI.interpret_color(new_color)
|
6374
|
+
block = @preview_playfield_blocks[row][column]
|
6375
|
+
block[:background_square].fill = color
|
6376
|
+
block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
|
6377
|
+
block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
6378
|
+
block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
6379
|
+
block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
6380
|
+
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
6381
|
+
end
|
6382
|
+
end.observe(@game.preview_playfield[row][column], :color)
|
6383
|
+
end
|
6384
|
+
end
|
6385
|
+
|
6386
|
+
Glimmer::DataBinding::Observer.proc do |new_score|
|
6387
|
+
Glimmer::LibUI.queue_main do
|
6388
|
+
@score_label.text = new_score.to_s
|
6389
|
+
end
|
6390
|
+
end.observe(@game, :score)
|
6391
|
+
|
6392
|
+
Glimmer::DataBinding::Observer.proc do |new_lines|
|
6393
|
+
Glimmer::LibUI.queue_main do
|
6394
|
+
@lines_label.text = new_lines.to_s
|
6395
|
+
end
|
6396
|
+
end.observe(@game, :lines)
|
6397
|
+
|
6398
|
+
Glimmer::DataBinding::Observer.proc do |new_level|
|
6399
|
+
Glimmer::LibUI.queue_main do
|
6400
|
+
@level_label.text = new_level.to_s
|
6401
|
+
end
|
6402
|
+
end.observe(@game, :level)
|
6313
6403
|
end
|
6314
6404
|
|
6315
|
-
def
|
6316
|
-
|
6405
|
+
def menu_bar
|
6406
|
+
menu('Game') {
|
6407
|
+
@pause_menu_item = check_menu_item('Pause') {
|
6408
|
+
enabled false
|
6409
|
+
|
6410
|
+
on_clicked do
|
6411
|
+
@game.paused = @pause_menu_item.checked?
|
6412
|
+
end
|
6413
|
+
}
|
6414
|
+
menu_item('Restart') {
|
6415
|
+
on_clicked do
|
6416
|
+
@game.restart!
|
6417
|
+
end
|
6418
|
+
}
|
6419
|
+
separator_menu_item
|
6420
|
+
menu_item('Exit') {
|
6421
|
+
on_clicked do
|
6422
|
+
exit(0)
|
6423
|
+
end
|
6424
|
+
}
|
6425
|
+
quit_menu_item if OS.mac?
|
6426
|
+
}
|
6427
|
+
|
6428
|
+
menu('View') {
|
6429
|
+
menu_item('Show High Scores') {
|
6430
|
+
on_clicked do
|
6431
|
+
show_high_scores
|
6432
|
+
end
|
6433
|
+
}
|
6434
|
+
menu_item('Clear High Scores') {
|
6435
|
+
on_clicked {
|
6436
|
+
@game.clear_high_scores!
|
6437
|
+
}
|
6438
|
+
}
|
6439
|
+
}
|
6440
|
+
|
6441
|
+
menu('Options') {
|
6442
|
+
radio_menu_item('Instant Down on Up Arrow') {
|
6443
|
+
on_clicked do
|
6444
|
+
@game.instant_down_on_up = true
|
6445
|
+
end
|
6446
|
+
}
|
6447
|
+
radio_menu_item('Rotate Right on Up Arrow') {
|
6448
|
+
on_clicked do
|
6449
|
+
@game.rotate_right_on_up = true
|
6450
|
+
end
|
6451
|
+
}
|
6452
|
+
radio_menu_item('Rotate Left on Up Arrow') {
|
6453
|
+
on_clicked do
|
6454
|
+
@game.rotate_left_on_up = true
|
6455
|
+
end
|
6456
|
+
}
|
6457
|
+
}
|
6458
|
+
|
6459
|
+
menu('Help') {
|
6460
|
+
if OS.mac?
|
6461
|
+
about_menu_item {
|
6462
|
+
on_clicked do
|
6463
|
+
show_about_dialog
|
6464
|
+
end
|
6465
|
+
}
|
6466
|
+
end
|
6467
|
+
menu_item('About') {
|
6468
|
+
on_clicked do
|
6469
|
+
show_about_dialog
|
6470
|
+
end
|
6471
|
+
}
|
6472
|
+
}
|
6473
|
+
end
|
6474
|
+
|
6475
|
+
def playfield(playfield_width: , playfield_height: , block_size: , &extra_content)
|
6476
|
+
blocks = []
|
6317
6477
|
vertical_box {
|
6318
6478
|
padded false
|
6319
6479
|
|
6320
6480
|
playfield_height.times.map do |row|
|
6321
|
-
|
6481
|
+
blocks << []
|
6322
6482
|
horizontal_box {
|
6323
6483
|
padded false
|
6324
6484
|
|
6325
6485
|
playfield_width.times.map do |column|
|
6326
|
-
|
6486
|
+
blocks.last << block(row: row, column: column, block_size: block_size)
|
6327
6487
|
end
|
6328
6488
|
}
|
6329
6489
|
end
|
6490
|
+
|
6491
|
+
extra_content&.call
|
6330
6492
|
}
|
6493
|
+
blocks
|
6331
6494
|
end
|
6332
6495
|
|
6333
|
-
def block(row: , column: , block_size: )
|
6496
|
+
def block(row: , column: , block_size: , &extra_content)
|
6334
6497
|
block = {}
|
6335
6498
|
bevel_pixel_size = 0.16 * block_size.to_f
|
6336
6499
|
color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
|
@@ -6369,32 +6532,89 @@ class Tetris
|
|
6369
6532
|
on_key_down do |key_event|
|
6370
6533
|
case key_event
|
6371
6534
|
in ext_key: :down
|
6372
|
-
game.down!
|
6535
|
+
@game.down!
|
6536
|
+
in key: ' '
|
6537
|
+
@game.down!(instant: true)
|
6373
6538
|
in ext_key: :up
|
6374
|
-
case game.up_arrow_action
|
6539
|
+
case @game.up_arrow_action
|
6375
6540
|
when :instant_down
|
6376
|
-
game.down!(instant: true)
|
6541
|
+
@game.down!(instant: true)
|
6377
6542
|
when :rotate_right
|
6378
|
-
game.rotate!(:right)
|
6543
|
+
@game.rotate!(:right)
|
6379
6544
|
when :rotate_left
|
6380
|
-
game.rotate!(:left)
|
6545
|
+
@game.rotate!(:left)
|
6381
6546
|
end
|
6382
6547
|
in ext_key: :left
|
6383
|
-
game.left!
|
6548
|
+
@game.left!
|
6384
6549
|
in ext_key: :right
|
6385
|
-
game.right!
|
6550
|
+
@game.right!
|
6386
6551
|
in modifier: :shift
|
6387
|
-
game.rotate!(:right)
|
6552
|
+
@game.rotate!(:right)
|
6388
6553
|
in modifier: :control
|
6389
|
-
game.rotate!(:left)
|
6554
|
+
@game.rotate!(:left)
|
6390
6555
|
else
|
6391
6556
|
# Do Nothing
|
6392
6557
|
end
|
6393
6558
|
end
|
6559
|
+
|
6560
|
+
extra_content&.call
|
6394
6561
|
}
|
6395
6562
|
block
|
6396
6563
|
end
|
6397
6564
|
|
6565
|
+
def score_board(block_size: , &extra_content)
|
6566
|
+
vertical_box {
|
6567
|
+
horizontal_box {
|
6568
|
+
label # filler
|
6569
|
+
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
|
6570
|
+
label # filler
|
6571
|
+
}
|
6572
|
+
|
6573
|
+
horizontal_box {
|
6574
|
+
label # filler
|
6575
|
+
grid {
|
6576
|
+
stretchy false
|
6577
|
+
|
6578
|
+
label('Score') {
|
6579
|
+
left 0
|
6580
|
+
top 0
|
6581
|
+
halign :fill
|
6582
|
+
}
|
6583
|
+
@score_label = label {
|
6584
|
+
left 0
|
6585
|
+
top 1
|
6586
|
+
halign :center
|
6587
|
+
}
|
6588
|
+
|
6589
|
+
label('Lines') {
|
6590
|
+
left 1
|
6591
|
+
top 0
|
6592
|
+
halign :fill
|
6593
|
+
}
|
6594
|
+
@lines_label = label {
|
6595
|
+
left 1
|
6596
|
+
top 1
|
6597
|
+
halign :center
|
6598
|
+
}
|
6599
|
+
|
6600
|
+
label('Level') {
|
6601
|
+
left 2
|
6602
|
+
top 0
|
6603
|
+
halign :fill
|
6604
|
+
}
|
6605
|
+
@level_label = label {
|
6606
|
+
left 2
|
6607
|
+
top 1
|
6608
|
+
halign :center
|
6609
|
+
}
|
6610
|
+
}
|
6611
|
+
label # filler
|
6612
|
+
}
|
6613
|
+
|
6614
|
+
extra_content&.call
|
6615
|
+
}
|
6616
|
+
end
|
6617
|
+
|
6398
6618
|
def start_moving_tetrominos_down
|
6399
6619
|
Glimmer::LibUI.timer(@game.delay) do
|
6400
6620
|
@game.down! if !@game.game_over? && !@game.paused?
|
@@ -6403,15 +6623,269 @@ class Tetris
|
|
6403
6623
|
|
6404
6624
|
def show_game_over_dialog
|
6405
6625
|
Glimmer::LibUI.queue_main do
|
6406
|
-
msg_box('Game Over', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
|
6626
|
+
msg_box('Game Over!', "Score: #{@game.high_scores.first.score}\nLines: #{@game.high_scores.first.lines}\nLevel: #{@game.high_scores.first.level}")
|
6407
6627
|
@game.restart!
|
6408
6628
|
end
|
6409
6629
|
end
|
6630
|
+
|
6631
|
+
def show_high_scores
|
6632
|
+
Glimmer::LibUI.queue_main do
|
6633
|
+
if @game.high_scores.empty?
|
6634
|
+
high_scores_string = "No games have been scored yet."
|
6635
|
+
else
|
6636
|
+
high_scores_string = @game.high_scores.map do |high_score|
|
6637
|
+
"#{high_score.name} | Score: #{high_score.score} | Lines: #{high_score.lines} | Level: #{high_score.level}"
|
6638
|
+
end.join("\n")
|
6639
|
+
end
|
6640
|
+
msg_box('High Scores', high_scores_string)
|
6641
|
+
end
|
6642
|
+
end
|
6643
|
+
|
6644
|
+
def show_about_dialog
|
6645
|
+
Glimmer::LibUI.queue_main do
|
6646
|
+
msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
|
6647
|
+
end
|
6648
|
+
end
|
6410
6649
|
end
|
6411
6650
|
|
6412
6651
|
Tetris.new.launch
|
6413
6652
|
```
|
6414
6653
|
|
6654
|
+
### Tic Tac Toe
|
6655
|
+
|
6656
|
+
[examples/tic_tac_toe.rb](examples/tic_tac_toe.rb)
|
6657
|
+
|
6658
|
+
Run with this command from the root of the project if you cloned the project:
|
6659
|
+
|
6660
|
+
```
|
6661
|
+
ruby -r './lib/glimmer-dsl-libui' examples/tic_tac_toe.rb
|
6662
|
+
```
|
6663
|
+
|
6664
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6665
|
+
|
6666
|
+
```
|
6667
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/tic_tac_toe'"
|
6668
|
+
```
|
6669
|
+
|
6670
|
+
Mac
|
6671
|
+
|
6672
|
+
![glimmer-dsl-libui-mac-tic-tac-toe.png](images/glimmer-dsl-libui-mac-tic-tac-toe.png)
|
6673
|
+
|
6674
|
+
![glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-o-wins.png)
|
6675
|
+
|
6676
|
+
![glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png](images/glimmer-dsl-libui-mac-tic-tac-toe-player-x-wins.png)
|
6677
|
+
|
6678
|
+
![glimmer-dsl-libui-mac-tic-tac-toe-draw.png](images/glimmer-dsl-libui-mac-tic-tac-toe-draw.png)
|
6679
|
+
|
6680
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6681
|
+
|
6682
|
+
```ruby
|
6683
|
+
require 'glimmer-dsl-libui'
|
6684
|
+
|
6685
|
+
require_relative "tic_tac_toe/board"
|
6686
|
+
|
6687
|
+
class TicTacToe
|
6688
|
+
include Glimmer
|
6689
|
+
|
6690
|
+
def initialize
|
6691
|
+
@tic_tac_toe_board = Board.new
|
6692
|
+
end
|
6693
|
+
|
6694
|
+
def launch
|
6695
|
+
create_gui
|
6696
|
+
register_observers
|
6697
|
+
@main_window.show
|
6698
|
+
end
|
6699
|
+
|
6700
|
+
def register_observers
|
6701
|
+
Glimmer::DataBinding::Observer.proc do |game_status|
|
6702
|
+
display_win_message if game_status == Board::WIN
|
6703
|
+
display_draw_message if game_status == Board::DRAW
|
6704
|
+
end.observe(@tic_tac_toe_board, :game_status)
|
6705
|
+
|
6706
|
+
3.times.map do |row|
|
6707
|
+
3.times.map do |column|
|
6708
|
+
Glimmer::DataBinding::Observer.proc do |sign|
|
6709
|
+
@cells[row][column].string = sign
|
6710
|
+
end.observe(@tic_tac_toe_board[row + 1, column + 1], :sign) # board model is 1-based
|
6711
|
+
end
|
6712
|
+
end
|
6713
|
+
end
|
6714
|
+
|
6715
|
+
def create_gui
|
6716
|
+
@main_window = window('Tic-Tac-Toe', 180, 180) {
|
6717
|
+
resizable false
|
6718
|
+
|
6719
|
+
@cells = []
|
6720
|
+
vertical_box {
|
6721
|
+
padded false
|
6722
|
+
|
6723
|
+
3.times.map do |row|
|
6724
|
+
@cells << []
|
6725
|
+
horizontal_box {
|
6726
|
+
padded false
|
6727
|
+
|
6728
|
+
3.times.map do |column|
|
6729
|
+
area {
|
6730
|
+
path {
|
6731
|
+
square(0, 0, 60)
|
6732
|
+
|
6733
|
+
stroke :black, thickness: 2
|
6734
|
+
}
|
6735
|
+
text(23, 19) {
|
6736
|
+
@cells[row] << string('') {
|
6737
|
+
font family: 'Arial', size: 20
|
6738
|
+
}
|
6739
|
+
}
|
6740
|
+
on_mouse_up do
|
6741
|
+
@tic_tac_toe_board.mark(row + 1, column + 1) # board model is 1-based
|
6742
|
+
end
|
6743
|
+
}
|
6744
|
+
end
|
6745
|
+
}
|
6746
|
+
end
|
6747
|
+
}
|
6748
|
+
}
|
6749
|
+
end
|
6750
|
+
|
6751
|
+
def display_win_message
|
6752
|
+
display_game_over_message("Player #{@tic_tac_toe_board.winning_sign} has won!")
|
6753
|
+
end
|
6754
|
+
|
6755
|
+
def display_draw_message
|
6756
|
+
display_game_over_message("Draw!")
|
6757
|
+
end
|
6758
|
+
|
6759
|
+
def display_game_over_message(message_text)
|
6760
|
+
Glimmer::LibUI.queue_main do
|
6761
|
+
msg_box('Game Over', message_text)
|
6762
|
+
@tic_tac_toe_board.reset!
|
6763
|
+
end
|
6764
|
+
end
|
6765
|
+
end
|
6766
|
+
|
6767
|
+
TicTacToe.new.launch
|
6768
|
+
```
|
6769
|
+
|
6770
|
+
### Snake
|
6771
|
+
|
6772
|
+
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.
|
6773
|
+
|
6774
|
+
[examples/snake.rb](examples/snake.rb)
|
6775
|
+
|
6776
|
+
Run with this command from the root of the project if you cloned the project:
|
6777
|
+
|
6778
|
+
```
|
6779
|
+
ruby -r './lib/glimmer-dsl-libui' examples/snake.rb
|
6780
|
+
```
|
6781
|
+
|
6782
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
6783
|
+
|
6784
|
+
```
|
6785
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/snake'"
|
6786
|
+
```
|
6787
|
+
|
6788
|
+
Mac
|
6789
|
+
|
6790
|
+
![glimmer-dsl-libui-mac-snake.png](images/glimmer-dsl-libui-mac-snake.png)
|
6791
|
+
|
6792
|
+
![glimmer-dsl-libui-mac-snake-game-over.png](images/glimmer-dsl-libui-mac-snake-game-over.png)
|
6793
|
+
|
6794
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
6795
|
+
|
6796
|
+
```ruby
|
6797
|
+
require 'glimmer-dsl-libui'
|
6798
|
+
require 'glimmer/data_binding/observer'
|
6799
|
+
|
6800
|
+
require_relative 'snake/presenter/grid'
|
6801
|
+
|
6802
|
+
class Snake
|
6803
|
+
CELL_SIZE = 15
|
6804
|
+
SNAKE_MOVE_DELAY = 0.1
|
6805
|
+
include Glimmer
|
6806
|
+
|
6807
|
+
def initialize
|
6808
|
+
@game = Model::Game.new
|
6809
|
+
@grid = Presenter::Grid.new(@game)
|
6810
|
+
@game.start
|
6811
|
+
create_gui
|
6812
|
+
register_observers
|
6813
|
+
end
|
6814
|
+
|
6815
|
+
def launch
|
6816
|
+
@main_window.show
|
6817
|
+
end
|
6818
|
+
|
6819
|
+
def register_observers
|
6820
|
+
@game.height.times do |row|
|
6821
|
+
@game.width.times do |column|
|
6822
|
+
Glimmer::DataBinding::Observer.proc do |new_color|
|
6823
|
+
@cell_grid[row][column].fill = new_color
|
6824
|
+
end.observe(@grid.cells[row][column], :color)
|
6825
|
+
end
|
6826
|
+
end
|
6827
|
+
|
6828
|
+
Glimmer::DataBinding::Observer.proc do |game_over|
|
6829
|
+
Glimmer::LibUI.queue_main do
|
6830
|
+
if game_over
|
6831
|
+
msg_box('Game Over!', "Score: #{@game.score}")
|
6832
|
+
@game.start
|
6833
|
+
end
|
6834
|
+
end
|
6835
|
+
end.observe(@game, :over)
|
6836
|
+
|
6837
|
+
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
6838
|
+
unless @game.over?
|
6839
|
+
@game.snake.move
|
6840
|
+
@main_window.title = "Glimmer Snake (Score: #{@game.score})"
|
6841
|
+
end
|
6842
|
+
end
|
6843
|
+
end
|
6844
|
+
|
6845
|
+
def create_gui
|
6846
|
+
@cell_grid = []
|
6847
|
+
@main_window = window('Glimmer Snake', @game.width * CELL_SIZE, @game.height * CELL_SIZE) {
|
6848
|
+
resizable false
|
6849
|
+
|
6850
|
+
vertical_box {
|
6851
|
+
padded false
|
6852
|
+
|
6853
|
+
@game.height.times do |row|
|
6854
|
+
@cell_grid << []
|
6855
|
+
horizontal_box {
|
6856
|
+
padded false
|
6857
|
+
|
6858
|
+
@game.width.times do |column|
|
6859
|
+
area {
|
6860
|
+
@cell_grid.last << path {
|
6861
|
+
square(0, 0, CELL_SIZE)
|
6862
|
+
|
6863
|
+
fill Presenter::Cell::COLOR_CLEAR
|
6864
|
+
}
|
6865
|
+
|
6866
|
+
on_key_up do |area_key_event|
|
6867
|
+
orientation_and_key = [@game.snake.head.orientation, area_key_event[:ext_key]]
|
6868
|
+
case orientation_and_key
|
6869
|
+
in [:north, :right] | [:east, :down] | [:south, :left] | [:west, :up]
|
6870
|
+
@game.snake.turn_right
|
6871
|
+
in [:north, :left] | [:west, :down] | [:south, :right] | [:east, :up]
|
6872
|
+
@game.snake.turn_left
|
6873
|
+
else
|
6874
|
+
# No Op
|
6875
|
+
end
|
6876
|
+
end
|
6877
|
+
}
|
6878
|
+
end
|
6879
|
+
}
|
6880
|
+
end
|
6881
|
+
}
|
6882
|
+
}
|
6883
|
+
end
|
6884
|
+
end
|
6885
|
+
|
6886
|
+
Snake.new.launch
|
6887
|
+
```
|
6888
|
+
|
6415
6889
|
## Applications
|
6416
6890
|
|
6417
6891
|
Here are some applications built with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui)
|
@@ -6442,9 +6916,10 @@ https://github.com/iraamaro/i3off-gtk-ruby
|
|
6442
6916
|
|
6443
6917
|
## Resources
|
6444
6918
|
|
6445
|
-
- [libui C Library](https://github.com/andlabs/libui)
|
6446
|
-
- [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
|
6447
6919
|
- [Code Master Blog](https://andymaleh.blogspot.com/search/label/LibUI)
|
6920
|
+
- [LibUI Ruby Bindings](https://github.com/kojix2/LibUI)
|
6921
|
+
- [libui C Library](https://github.com/andlabs/libui)
|
6922
|
+
- [Go UI (Golang LibUI) API Documentation](https://pkg.go.dev/github.com/andlabs/ui)
|
6448
6923
|
|
6449
6924
|
## Help
|
6450
6925
|
|