glimmer-dsl-libui 0.4.18 → 0.4.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 +22 -0
- data/LICENSE.txt +1 -1
- data/README.md +448 -74
- data/VERSION +1 -1
- data/bin/girb +0 -0
- data/bin/girb_runner.rb +1 -1
- data/examples/area_based_custom_controls.rb +282 -0
- data/examples/method_based_custom_keyword.rb +9 -9
- data/examples/method_based_custom_keyword2.rb +9 -9
- data/examples/snake/model/game.rb +18 -1
- data/examples/snake.rb +6 -2
- data/examples/snake2.rb +6 -2
- data/examples/tetris/model/block.rb +1 -1
- data/examples/tetris/model/game.rb +26 -24
- data/examples/tetris/model/past_game.rb +1 -1
- data/examples/tetris/model/tetromino.rb +1 -1
- data/examples/tetris.rb +51 -20
- data/examples/tic_tac_toe/board.rb +1 -1
- data/examples/tic_tac_toe/cell.rb +1 -1
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/bind_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/control_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/data_binding_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/dsl.rb +1 -1
- data/lib/glimmer/dsl/libui/file_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/listener_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/observe_expression.rb +2 -2
- data/lib/glimmer/dsl/libui/open_file_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/operation_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/property_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/save_file_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/shape_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/shine_data_binding_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/string_expression.rb +1 -1
- data/lib/glimmer/dsl/libui/tab_item_expression.rb +1 -1
- data/lib/glimmer/fiddle_consumer.rb +1 -1
- data/lib/glimmer/libui/attributed_string.rb +1 -1
- data/lib/glimmer/libui/control_proxy/area_proxy/scrolling_area_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/area_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/box/horizontal_box_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/box/vertical_box_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/box.rb +2 -1
- data/lib/glimmer/libui/control_proxy/button_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/checkbox_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/color_button_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/background_color_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/button_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/checkbox_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/checkbox_text_color_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/checkbox_text_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/image_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/image_text_color_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/image_text_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/progress_bar_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/text_color_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column/text_column_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/column.rb +1 -1
- data/lib/glimmer/libui/control_proxy/combobox_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/date_picker_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy/time_picker_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/date_time_picker_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/dual_column.rb +1 -1
- data/lib/glimmer/libui/control_proxy/editable_column.rb +1 -1
- data/lib/glimmer/libui/control_proxy/editable_combobox_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/enableable_column.rb +1 -1
- data/lib/glimmer/libui/control_proxy/entry_proxy/password_entry_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/entry_proxy/search_entry_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/entry_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/font_button_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/form_proxy.rb +2 -1
- data/lib/glimmer/libui/control_proxy/grid_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/group_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/image_part_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/image_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/label_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/matrix_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/about_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/check_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/preferences_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/quit_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/radio_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy/separator_menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/menu_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/message_box/msg_box_error_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/message_box/msg_box_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/message_box.rb +1 -1
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy/non_wrapping_multiline_entry_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/multiline_entry_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/open_type_features_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/open_type_tag_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/radio_buttons_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/slider_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/spinbox_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/tab_item_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/table_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/text_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy/transformable.rb +1 -1
- data/lib/glimmer/libui/control_proxy/triple_column.rb +1 -1
- data/lib/glimmer/libui/control_proxy/window_proxy.rb +1 -1
- data/lib/glimmer/libui/control_proxy.rb +2 -1
- data/lib/glimmer/libui/data_bindable.rb +1 -1
- data/lib/glimmer/libui/parent.rb +1 -1
- data/lib/glimmer/libui/shape/arc.rb +1 -1
- data/lib/glimmer/libui/shape/bezier.rb +21 -4
- data/lib/glimmer/libui/shape/circle.rb +1 -1
- data/lib/glimmer/libui/shape/figure.rb +1 -1
- data/lib/glimmer/libui/shape/line.rb +24 -4
- data/lib/glimmer/libui/shape/polybezier.rb +1 -1
- data/lib/glimmer/libui/shape/polygon.rb +1 -1
- data/lib/glimmer/libui/shape/polyline.rb +1 -1
- data/lib/glimmer/libui/shape/rectangle.rb +1 -1
- data/lib/glimmer/libui/shape/square.rb +1 -1
- data/lib/glimmer/libui/shape.rb +1 -1
- data/lib/glimmer/libui.rb +1 -1
- data/lib/glimmer-dsl-libui.rb +1 -1
- metadata +8 -6
data/README.md
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.4.
|
1
|
+
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.4.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)
|
5
5
|
|
6
|
-
[
|
6
|
+
[**(Fukuoka Ruby Award Competition 2022 Special Award Winner)**](http://www.digitalfukuoka.jp/topics/187?locale=ja)
|
7
|
+
|
8
|
+
[Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [LibUI](https://github.com/kojix2/LibUI) is a prerequisite-free [MRI Ruby](https://www.ruby-lang.org) desktop development GUI (Graphical User Interface) library. No need to pre-install any prerequisites. Just install the [gem](https://rubygems.org/gems/glimmer-dsl-libui) and have platform-independent native GUI that just works!
|
7
9
|
|
8
10
|
Mac | Windows | Linux
|
9
11
|
----|---------|------
|
@@ -22,6 +24,8 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
|
|
22
24
|
- [Far Future Plan] Scaffolding for new custom controls, apps, and gems
|
23
25
|
- [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
|
24
26
|
|
27
|
+
Note that currently, [LibUI](https://github.com/kojix2/LibUI) only includes x86_64 binaries out of the box, but there are plans to include ARM64/AARCH64 binaries too in the future.
|
28
|
+
|
25
29
|
Hello, World!
|
26
30
|
|
27
31
|
```ruby
|
@@ -320,19 +324,27 @@ Mac | Windows | Linux
|
|
320
324
|
|
321
325
|
[Check Out Many More Examples Over Here!](#examples)
|
322
326
|
|
323
|
-
|
324
|
-
|
325
|
-
![glimmer-dsl-libui-mac-
|
327
|
+
![glimmer-dsl-libui-mac-snake.gif](images/glimmer-dsl-libui-mac-snake.gif)
|
328
|
+
|
329
|
+
![glimmer-dsl-libui-mac-color-the-circles.gif](images/glimmer-dsl-libui-mac-color-the-circles.gif)
|
330
|
+
|
331
|
+
![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)
|
326
332
|
|
327
|
-
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is feature-complete and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
|
333
|
+
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is 100% feature-complete and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha). Please help make better by contributing, adopting for small or low risk projects, and providing feedback. The more feedback and issues you report the better.
|
328
334
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
-
|
334
|
-
-
|
335
|
-
|
335
|
+
**[Glimmer](https://rubygems.org/gems/glimmer) DSL Comparison Table:**
|
336
|
+
DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
337
|
+
----|-----------|---------|------------------|------|------|--------
|
338
|
+
[Glimmer DSL for SWT (JRuby Desktop Development GUI Framework)](https://github.com/AndyObtiva/glimmer-dsl-swt) | Mac / Windows / Linux | Yes | Yes (Canvas Shape DSL) | Very Mature / Scaffolding / Native Executable Packaging / Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint | Java / JRuby
|
339
|
+
[Glimmer DSL for Opal (Pure Ruby Web GUI and Auto-Webifier of Desktop Apps)](https://github.com/AndyObtiva/glimmer-dsl-opal) | All Web Browsers | No | Yes (Canvas Shape DSL) | Simpler than All JavaScript Technologies / Auto-Webify Desktop Apps | Setup Process / Only Rails 5 Support for Now | Rails
|
340
|
+
[Glimmer DSL for LibUI (Prerequisite-Free Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-libui) | Mac / Windows / Linux | Yes | Yes (Area API) | Fast Startup Time / Light Memory Footprint | LibUI is an Incomplete Mid-Alpha Only | None Other Than MRI Ruby
|
341
|
+
[Glimmer DSL for Tk (MRI Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-tk) | Mac / Windows / Linux | Some Native-Themed Widgets (Not Truly Native) | Yes (Canvas) | Fast Startup Time / Light Memory Footprint | Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
|
342
|
+
[Glimmer DSL for GTK (Ruby-GNOME Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-gtk) | Mac / Windows / Linux | Only on Linux | Yes (Cairo) | Complete Access to GNOME Features on Linux (Forte) | Not Native on Mac and Windows | None Other Than MRI Ruby on Linux / Brew Packages on Mac / MSYS & MING Toolchains on Windows / MRI Ruby
|
343
|
+
[Glimmer DSL for FX (FOX Toolkit Ruby Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-fx) | Mac (requires XQuartz) / Windows / Linux | No | Yes (Canvas) | No Prerequisites on Windows (Forte Since Binaries Are Included Out of The Box) | Widgets Do Not Look Native / Mac Usage Obtrusively Starts XQuartz | None Other Than MRI Ruby on Windows / XQuarts on Mac / MRI Ruby
|
344
|
+
[Glimmer DSL for JFX (JRuby JavaFX Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-jfx) | Mac / Windows / Linux | No | Yes (javafx.scene.shape and javafx.scene.canvas) | Rich in Custom Widgets | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby / JavaFX SDK
|
345
|
+
[Glimmer DSL for Swing (JRuby Swing Desktop Development GUI Library)](https://github.com/AndyObtiva/glimmer-dsl-swing) | Mac / Windows / Linux | No | Yes (Java2D) | Very Mature | Slow JRuby Startup Time / Heavy Memory Footprint / Widgets Do Not Look Native | Java / JRuby
|
346
|
+
[Glimmer DSL for XML (& HTML)](https://github.com/AndyObtiva/glimmer-dsl-xml) | All Web Browsers | No | Yes (SVG) | Programmable / Lighter-weight Than Actual XML | XML Elements Are Sometimes Not Well-Named (Many Types of Input) | None
|
347
|
+
[Glimmer DSL for CSS](https://github.com/AndyObtiva/glimmer-dsl-css) | All Web Browsers | No | Yes | Programmable | CSS Is Over-Engineered / Too Many Features To Learn | None
|
336
348
|
|
337
349
|
## Table of Contents
|
338
350
|
|
@@ -409,6 +421,7 @@ Other [Glimmer](https://rubygems.org/gems/glimmer) DSL gems you might be interes
|
|
409
421
|
- [Histogram](#histogram)
|
410
422
|
- [Login](#login)
|
411
423
|
- [Method-Based Custom Keyword](#method-based-custom-keyword)
|
424
|
+
- [Area-Based Custom Controls](#area-based-custom-controls)
|
412
425
|
- [Midi Player](#midi-player)
|
413
426
|
- [Snake](#snake)
|
414
427
|
- [Tetris](#tetris)
|
@@ -506,7 +519,7 @@ gem install glimmer-dsl-libui
|
|
506
519
|
Or install via Bundler `Gemfile`:
|
507
520
|
|
508
521
|
```ruby
|
509
|
-
gem 'glimmer-dsl-libui', '~> 0.4.
|
522
|
+
gem 'glimmer-dsl-libui', '~> 0.4.22'
|
510
523
|
```
|
511
524
|
|
512
525
|
Test that installation worked by running the [Meta-Example](#examples):
|
@@ -576,13 +589,14 @@ w.set_title 'aloha'
|
|
576
589
|
puts w.title # => aloha
|
577
590
|
```
|
578
591
|
|
579
|
-
Controls are wrapped as Ruby proxy objects, having a `#libui` method to obtain the wrapped [LibUI](https://github.com/kojix2/LibUI) Fiddle pointer object. Ruby proxy objects rely on composition (via [Proxy Design Pattern](https://en.wikipedia.org/wiki/Proxy_pattern)) instead of inheritance to shield consumers from having to deal with lower-level details unless absolutely needed.
|
592
|
+
Controls are wrapped as Ruby proxy objects, having a `#libui` method to obtain the wrapped [LibUI](https://github.com/kojix2/LibUI) Fiddle pointer object. Ruby proxy objects rely on composition (via [Proxy Design Pattern](https://en.wikipedia.org/wiki/Proxy_pattern)) instead of inheritance to shield consumers from having to deal with lower-level details unless absolutely needed. That said, you can invoke any [LibUI operation](#libui-operations) on the Glimmer proxy object directly and it gets proxied automatically to the wrapped Fiddle pointer object (e.g. `window_proxy.title` gets proxied to `LibUI.window_title(window_proxy.libui).to_s` automatically), so you rarely have to refer to the wrapped `#libui` Fiddle pointer object directly.
|
580
593
|
|
581
594
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
582
595
|
|
583
596
|
```ruby
|
584
597
|
w = window('hello world') # => #<Glimmer::LibUI::WindowProxy:0x00007fde4ea39fb0
|
585
598
|
w.libui # => #<Fiddle::Pointer:0x00007fde53997980 ptr=0x00007fde51352a60 size=0 free=0x0000000000000000>
|
599
|
+
w.title == LibUI.window_title(w.libui).to_s # => true
|
586
600
|
```
|
587
601
|
|
588
602
|
### Supported Keywords
|
@@ -595,7 +609,7 @@ Keyword(Args) | Properties | Listeners
|
|
595
609
|
`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)`
|
596
610
|
`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
|
597
611
|
`background_color_column` | None | None
|
598
|
-
`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
|
612
|
+
`bezier(x = nil as Numeric, y = nil as Numeric, c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `c1_x` (`Numeric`), `c1_y` (`Numeric`), `c2_x` (`Numeric`), `c2_y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
|
599
613
|
`button(text as String)` | `text` (`String`) | `on_clicked`
|
600
614
|
`button_column(name as String)` | `enabled` (Boolean) | None
|
601
615
|
`checkbox(text as String)` | `checked` (Boolean), `text` (`String`) | `on_toggled`
|
@@ -622,7 +636,7 @@ Keyword(Args) | Properties | Listeners
|
|
622
636
|
`image_text_column(name as String)` | None | None
|
623
637
|
`image_text_color_column(name as String)` | None | None
|
624
638
|
`label(text as String)` | `text` (`String`) | None
|
625
|
-
`line(x as Numeric, y as Numeric)` | `x` (`Numeric`), `y` (`Numeric`) | None
|
639
|
+
`line(x as Numeric, y as Numeric, end_x = nil as Numeric, end_y = nil as Numeric)` | `x` (`Numeric`), `y` (`Numeric`), `end_x` (`Numeric`), `end_y` (`Numeric`) | None
|
626
640
|
`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
|
627
641
|
`menu(text as String)` | None | None
|
628
642
|
`menu_item(text as String)` | None | `on_clicked`
|
@@ -958,8 +972,8 @@ Available `path` shapes (that can be nested explicitly under `path` or implicitl
|
|
958
972
|
- `square(x as Numeric, y as Numeric, length as Numeric)`
|
959
973
|
- `arc(x_center as Numeric, y_center as Numeric, radius as Numeric, start_angle as Numeric, sweep as Numeric, is_negative as Boolean)`
|
960
974
|
- `circle(x_center as Numeric, y_center as Numeric, radius as Numeric)`
|
961
|
-
- `line(x as Numeric, y as Numeric)`: must be placed in a figure (check `polyline`/`polygon` alternatives that do not require a `figure`)
|
962
|
-
- `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)`: must be placed in a figure (check `polybezier` alternative that does not require a `figure`)
|
975
|
+
- `line(x as Numeric, y as Numeric, end_x = nil as Numeric, end_y = nil as Numeric)`: must be placed in a `figure` if only `x`/`y` are specified or have `end_x`/`end_y` otherwise if outside of `figure` (check `polyline`/`polygon` alternatives that do not require a `figure`)
|
976
|
+
- `bezier(x = nil as Numeric, y = nil as Numeric, c1_x as Numeric, c1_y as Numeric, c2_x as Numeric, c2_y as Numeric, end_x as Numeric, end_y as Numeric)`: must be placed in a `figure` if `x`/`y` are not specified or have `x`/`y` as start point otherwise if outside of `figure` (check `polybezier` alternative that does not require a `figure`)
|
963
977
|
- `polygon(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for a closed figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
|
964
978
|
- `polyline(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for an open figure of lines; can receive points as [[x1, y1], [x2, y2], ...] or [x1, y1, x2, y2, ...]
|
965
979
|
- `polybezier(point_array as Array of Arrays of Numeric or Array of Numeric)`: shortcut for an 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, ...]
|
@@ -1365,8 +1379,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1365
1379
|
- All boolean property readers return `true` or `false` in Ruby instead of the [libui](https://github.com/andlabs/libui) original `0` or `1` in C.
|
1366
1380
|
- All boolean property writers accept `true`/`false` in addition to `1`/`0` in Ruby
|
1367
1381
|
- All string property readers return a `String` object in Ruby instead of the [libui](https://github.com/andlabs/libui) Fiddle pointer object.
|
1368
|
-
- Automatically allocate font descriptors upon instantiating `font_button` controls and free them when
|
1369
|
-
- Automatically allocate color value pointers upon instantiating `color_button` controls and free them when
|
1382
|
+
- Automatically allocate font descriptors upon instantiating `font_button` controls and free them when destroying `font_button` controls
|
1383
|
+
- Automatically allocate color value pointers upon instantiating `color_button` controls and free them when destroying `color_button` controls
|
1370
1384
|
- On the Mac, if no `menu` items were added, an automatic `quit_menu_item` is added to enable quitting with CTRL+Q
|
1371
1385
|
- When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
|
1372
1386
|
- When destroying a control nested under a `form`, it is automatically deleted from the form's children
|
@@ -1384,6 +1398,7 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1384
1398
|
- `scrolling_area` `#scroll_to` 3rd and 4th arguments (`width` and `height`) default to main window width and height if not specified.
|
1385
1399
|
- `area` paths are specified declaratively with shapes/figures underneath (e.g. `rectangle`), and `area` draw listener is automatically generated
|
1386
1400
|
- `area` path shapes can be added directly under `area` without declaring `path` explicitly as a convenient shorthand
|
1401
|
+
- `line` and `bezier` automatically start a new figure if placed outside of `figure`
|
1387
1402
|
- Observe figure properties (e.g. `rectangle` `width`) for changes and automatically redraw containing area accordingly
|
1388
1403
|
- Observe `path` `fill` and `stroke` hashes for changes and automatically redraw containing area accordingly
|
1389
1404
|
- Observe `text` and `string` properties for changes and automatically redraw containing area accordingly
|
@@ -1395,7 +1410,15 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1395
1410
|
|
1396
1411
|
### Custom Keywords
|
1397
1412
|
|
1398
|
-
|
1413
|
+
Custom keywords can be defined to represent custom controls (components) that provide new features or act as composites of [existing controls](#supported-keywords) that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
|
1414
|
+
|
1415
|
+
For example, you can define a custom `address` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
|
1416
|
+
|
1417
|
+
To define custom keywords, simply define a method representing the custom control you want (e.g. `address`) with any arguments needed (e.g. `address(address_model)`).
|
1418
|
+
|
1419
|
+
To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
|
1420
|
+
|
1421
|
+
It is OK to use terms "custom keyword" and "custom control" synonymously though "custom keyword" is a broader term that covers things other than controls too like custom shapes (e.g. `cylinder`), custom attributed strings (e.g. `alternating_color_string`), and custom transforms (`isometric_transform`).
|
1399
1422
|
|
1400
1423
|
Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1401
1424
|
|
@@ -1407,44 +1430,37 @@ include Glimmer
|
|
1407
1430
|
|
1408
1431
|
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
1409
1432
|
|
1410
|
-
def form_field(model,
|
1411
|
-
|
1433
|
+
def form_field(model, attribute)
|
1434
|
+
attribute = attribute.to_s
|
1412
1435
|
entry { |e|
|
1413
|
-
label
|
1414
|
-
text model
|
1415
|
-
|
1416
|
-
on_changed do
|
1417
|
-
model.send("#{property}=", e.text)
|
1418
|
-
end
|
1436
|
+
label attribute.underscore.split('_').map(&:capitalize).join(' ')
|
1437
|
+
text <=> [model, attribute]
|
1419
1438
|
}
|
1420
1439
|
end
|
1421
1440
|
|
1422
|
-
def address_form(
|
1441
|
+
def address_form(address_model)
|
1423
1442
|
form {
|
1424
|
-
form_field(
|
1425
|
-
form_field(
|
1426
|
-
form_field(
|
1427
|
-
form_field(
|
1428
|
-
form_field(
|
1443
|
+
form_field(address_model, :street)
|
1444
|
+
form_field(address_model, :p_o_box)
|
1445
|
+
form_field(address_model, :city)
|
1446
|
+
form_field(address_model, :state)
|
1447
|
+
form_field(address_model, :zip_code)
|
1429
1448
|
}
|
1430
1449
|
end
|
1431
1450
|
|
1432
1451
|
def label_pair(model, attribute, value)
|
1433
|
-
name_label = nil
|
1434
|
-
value_label = nil
|
1435
1452
|
horizontal_box {
|
1436
|
-
|
1437
|
-
|
1453
|
+
label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
1454
|
+
label(value.to_s) {
|
1455
|
+
text <= [model, attribute]
|
1456
|
+
}
|
1438
1457
|
}
|
1439
|
-
Glimmer::DataBinding::Observer.proc do
|
1440
|
-
value_label.text = model.send(attribute)
|
1441
|
-
end.observe(model, attribute)
|
1442
1458
|
end
|
1443
1459
|
|
1444
|
-
def address(
|
1460
|
+
def address(address_model)
|
1445
1461
|
vertical_box {
|
1446
|
-
|
1447
|
-
label_pair(
|
1462
|
+
address_model.each_pair do |attribute, value|
|
1463
|
+
label_pair(address_model, attribute, value)
|
1448
1464
|
end
|
1449
1465
|
}
|
1450
1466
|
end
|
@@ -1501,6 +1517,12 @@ window('Method-Based Custom Keyword') {
|
|
1501
1517
|
|
1502
1518
|
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
1503
1519
|
|
1520
|
+
The [`area`](#area-api) control can be utilized to build non-native custom controls from scratch by leveraging vector graphics, formattable text, keyboard events, and mouse events. This is demonstrated in the [Area-Based Custom Controls](#area-based-custom-controls) example.
|
1521
|
+
|
1522
|
+
Defining custom keywords enables unlimited extension of the [Glimmer GUI DSL](#glimmer-gui-dsl). The sky is the limit on what can be done with custom keywords as a result. You can compose new visual vocabulary to build applications in any domain from higher concepts rather than [mere standard controls](#supported-keywords). For example, in a traffic signaling app, you could define `street`, `light_signal`, `traffic_sign`, and `car` as custom keywords and build your application from these concepts directly, saving enormous time and achieving much higher productivity.
|
1523
|
+
|
1524
|
+
Learn more from custom keyword usage in [Method-Based Custom Keyword](#method-based-custom-keyword), [Area-Based Custom Controls](#area-based-custom-controls), [Basic Scrolling Area](#basic-scrolling-area), [Histogram](#histogram), and [Tetris](#tetris) examples.
|
1525
|
+
|
1504
1526
|
### Observer Pattern
|
1505
1527
|
|
1506
1528
|
The [Observer Design Pattern](https://en.wikipedia.org/wiki/Observer_pattern) (a.k.a. Observer Pattern) is fundamental to building GUIs (Graphical User Interfaces) following the [MVC (Model View Controller) Architectural Pattern](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) or any of its variations like [MVP (Model View Presenter)](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter). In the original Smalltalk-MVC, the View observes the Model for changes and updates itself accordingly.
|
@@ -1772,9 +1794,9 @@ Data-bound model attribute can be:
|
|
1772
1794
|
- **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
|
1773
1795
|
|
1774
1796
|
Data-binding options include:
|
1775
|
-
- `before_read {|value| ...}`: performs an operation before reading data from Model to update
|
1797
|
+
- `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
|
1776
1798
|
- `on_read {|value| ...}`: converts value read from Model to update the View.
|
1777
|
-
- `after_read {|converted_value| ...}`: performs an operation after read from Model
|
1799
|
+
- `after_read {|converted_value| ...}`: performs an operation after read from Model to update View.
|
1778
1800
|
- `before_write {|value| ...}`: performs an operation before writing data to Model from View.
|
1779
1801
|
- `on_write {|value| ...}`: converts value read from View to update the Model.
|
1780
1802
|
- `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
|
@@ -1805,8 +1827,8 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1805
1827
|
- `table` `checkbox_text_column` checkbox editing only works on Linux (not Mac or Windows) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
|
1806
1828
|
- `text` `align` property seems not to work on the Mac ([libui](https://github.com/andlabs/libui) has an [issue](https://github.com/andlabs/libui/pull/407) about it)
|
1807
1829
|
- `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
|
1808
|
-
- `table` controls on Windows intentionally get an extra empty row at the end because if any row were to be deleted for the first time, double-deletion happens due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
|
1809
1830
|
- `table` `progress_bar` column on Windows cannot be updated with a positive value if it started initially with `-1` (it ignores update to avoid crashing due to an issue in [libui](https://github.com/andlabs/libui) on Windows.
|
1831
|
+
- `radio_buttons` on Linux has an issue where it always selects the first item even if you did not set its `selected` value or set it to `-1` (meaning unselected). It works correctly on Mac and Windows.
|
1810
1832
|
- It seems that [libui](https://github.com/andlabs/libui) does not support nesting multiple `area` controls under a `grid` as only the first one shows up in that scenario. To workaround that limitation, use a `vertical_box` with nested `horizontal_box`s instead to include multiple `area`s in a GUI.
|
1811
1833
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1812
1834
|
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
@@ -8370,6 +8392,12 @@ window('Login') {
|
|
8370
8392
|
|
8371
8393
|
#### Method-Based Custom Keyword
|
8372
8394
|
|
8395
|
+
[Custom keywords](#custom-keywords) can be defined to represent custom controls (components) that provide new features or act as composites of existing controls that need to be reused multiple times in an application or across multiple applications. Custom keywords save a lot of development time, improving productivity and maintainability immensely.
|
8396
|
+
|
8397
|
+
This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
|
8398
|
+
|
8399
|
+
The custom keywords are defined via methods (thus are "method-based").
|
8400
|
+
|
8373
8401
|
[examples/method_based_custom_keyword.rb](examples/method_based_custom_keyword.rb)
|
8374
8402
|
|
8375
8403
|
Run with this command from the root of the project if you cloned the project:
|
@@ -8585,6 +8613,317 @@ window('Method-Based Custom Keyword') {
|
|
8585
8613
|
}.show
|
8586
8614
|
```
|
8587
8615
|
|
8616
|
+
#### Area-Based Custom Controls
|
8617
|
+
|
8618
|
+
[Custom keywords](#custom-keywords) can be defined for graphical custom controls (components) built completely from scratch as vector-graphics on top of the [`area`](#area-api) control while leveraging keyboard and mouse listeners.
|
8619
|
+
|
8620
|
+
This example defines `text_label` and `push_button` as [`area`](#area-api)-based graphical custom controls that can have width, height, font, fill, stroke, border, and custom text location.
|
8621
|
+
|
8622
|
+
[examples/area_based_custom_controls.rb](examples/area_based_custom_controls.rb)
|
8623
|
+
|
8624
|
+
Run with this command from the root of the project if you cloned the project:
|
8625
|
+
|
8626
|
+
```
|
8627
|
+
ruby -r './lib/glimmer-dsl-libui' examples/area_based_custom_controls.rb
|
8628
|
+
```
|
8629
|
+
|
8630
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
8631
|
+
|
8632
|
+
```
|
8633
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/area_based_custom_controls'"
|
8634
|
+
```
|
8635
|
+
|
8636
|
+
Mac | Windows | Linux
|
8637
|
+
----|---------|------
|
8638
|
+
![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-mac-area-based-custom-controls.png](images/glimmer-dsl-libui-mac-area-based-custom-controls-push-button-clicked.png) | ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-windows-area-based-custom-controls.png](images/glimmer-dsl-libui-windows-area-based-custom-controls-push-button-clicked.png) | ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-text-label.png) ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-push-button.png) ![glimmer-dsl-libui-linux-area-based-custom-controls.png](images/glimmer-dsl-libui-linux-area-based-custom-controls-push-button-clicked.png)
|
8639
|
+
|
8640
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
8641
|
+
|
8642
|
+
```ruby
|
8643
|
+
require 'glimmer-dsl-libui'
|
8644
|
+
|
8645
|
+
class AreaBasedCustomControls
|
8646
|
+
include Glimmer
|
8647
|
+
|
8648
|
+
attr_accessor :label_width, :label_height, :label_font_descriptor,
|
8649
|
+
:label_text_color, :label_background_fill, :label_border_stroke,
|
8650
|
+
:label_text_x, :label_text_y,
|
8651
|
+
:button_width, :button_height, :button_font_descriptor,
|
8652
|
+
:button_text_color, :button_background_fill, :button_border_stroke,
|
8653
|
+
:button_text_x, :button_text_y
|
8654
|
+
|
8655
|
+
def initialize
|
8656
|
+
self.label_width = 335
|
8657
|
+
self.label_height = 50
|
8658
|
+
self.label_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 16, weight: :bold, italic: :italic}
|
8659
|
+
self.label_text_color = :red
|
8660
|
+
self.label_background_fill = :yellow
|
8661
|
+
self.label_border_stroke = :limegreen
|
8662
|
+
|
8663
|
+
self.button_width = 150
|
8664
|
+
self.button_height = 50
|
8665
|
+
self.button_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 36, weight: OS.linux? ? :normal : :bold, italic: :italic}
|
8666
|
+
self.button_text_color = :green
|
8667
|
+
self.button_background_fill = :yellow
|
8668
|
+
self.button_border_stroke = :limegreen
|
8669
|
+
end
|
8670
|
+
|
8671
|
+
def rebuild_text_label
|
8672
|
+
@text_label.destroy
|
8673
|
+
@text_label_vertical_box.content { # re-open vertical box content and shove in a new label
|
8674
|
+
@text_label = text_label('This is a text label.',
|
8675
|
+
width: label_width, height: label_height, font_descriptor: label_font_descriptor,
|
8676
|
+
background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
|
8677
|
+
text_x: label_text_x, text_y: label_text_y)
|
8678
|
+
}
|
8679
|
+
end
|
8680
|
+
|
8681
|
+
def rebuild_push_button
|
8682
|
+
@push_button.destroy
|
8683
|
+
@push_button_vertical_box.content { # re-open vertical box content and shove in a new button
|
8684
|
+
@push_button = push_button('Push',
|
8685
|
+
width: button_width, height: button_height, font_descriptor: button_font_descriptor,
|
8686
|
+
background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
|
8687
|
+
text_x: button_text_x, text_y: button_text_y) {
|
8688
|
+
on_mouse_up do
|
8689
|
+
message_box('Button Pushed', 'Thank you for pushing the button!')
|
8690
|
+
end
|
8691
|
+
}
|
8692
|
+
}
|
8693
|
+
end
|
8694
|
+
|
8695
|
+
def launch
|
8696
|
+
window('Area-Based Custom Controls', 385, 385) { |w|
|
8697
|
+
margined true
|
8698
|
+
|
8699
|
+
tab {
|
8700
|
+
tab_item('Text Label') {
|
8701
|
+
@text_label_vertical_box = vertical_box {
|
8702
|
+
vertical_box {
|
8703
|
+
text_label('Text Label Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
|
8704
|
+
|
8705
|
+
horizontal_box {
|
8706
|
+
label('Width')
|
8707
|
+
spinbox(1, 1000) {
|
8708
|
+
value <=> [self, :label_width, after_write: method(:rebuild_text_label)]
|
8709
|
+
}
|
8710
|
+
}
|
8711
|
+
|
8712
|
+
horizontal_box {
|
8713
|
+
label('Height')
|
8714
|
+
spinbox(1, 1000) {
|
8715
|
+
value <=> [self, :label_height, after_write: method(:rebuild_text_label)]
|
8716
|
+
}
|
8717
|
+
}
|
8718
|
+
|
8719
|
+
horizontal_box {
|
8720
|
+
label('Font')
|
8721
|
+
font_button {
|
8722
|
+
font <=> [self, :label_font_descriptor, after_write: method(:rebuild_text_label)]
|
8723
|
+
}
|
8724
|
+
}
|
8725
|
+
|
8726
|
+
horizontal_box {
|
8727
|
+
label('Text Color')
|
8728
|
+
color_button {
|
8729
|
+
color <=> [self, :label_text_color, after_write: method(:rebuild_text_label)]
|
8730
|
+
}
|
8731
|
+
}
|
8732
|
+
|
8733
|
+
horizontal_box {
|
8734
|
+
label('Background Color')
|
8735
|
+
color_button {
|
8736
|
+
color <=> [self, :label_background_fill, after_write: method(:rebuild_text_label)]
|
8737
|
+
}
|
8738
|
+
}
|
8739
|
+
|
8740
|
+
horizontal_box {
|
8741
|
+
label('Border Color')
|
8742
|
+
color_button {
|
8743
|
+
color <=> [self, :label_border_stroke, after_write: method(:rebuild_text_label)]
|
8744
|
+
}
|
8745
|
+
}
|
8746
|
+
|
8747
|
+
horizontal_box {
|
8748
|
+
label('Text X (0=centered)')
|
8749
|
+
spinbox(0, 1000) {
|
8750
|
+
value <=> [self, :label_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_text_label)]
|
8751
|
+
}
|
8752
|
+
}
|
8753
|
+
|
8754
|
+
horizontal_box {
|
8755
|
+
label('Text Y (0=centered)')
|
8756
|
+
spinbox(0, 1000) {
|
8757
|
+
value <=> [self, :label_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_text_label)]
|
8758
|
+
}
|
8759
|
+
}
|
8760
|
+
}
|
8761
|
+
|
8762
|
+
@text_label = text_label('This is a text label.',
|
8763
|
+
width: label_width, height: label_height, font_descriptor: label_font_descriptor,
|
8764
|
+
background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
|
8765
|
+
text_x: label_text_x, text_y: label_text_y)
|
8766
|
+
}
|
8767
|
+
}
|
8768
|
+
|
8769
|
+
tab_item('Push Button') {
|
8770
|
+
@push_button_vertical_box = vertical_box {
|
8771
|
+
vertical_box {
|
8772
|
+
text_label('Push Button Form:', width: 385, height: 30, background_fill: OS.windows? ? :white : {a: 0}, border_stroke: OS.windows? ? :white : {a: 0}, font_descriptor: {size: 16, weight: :bold}, text_x: 0, text_y: OS.windows? ? 0 : 5)
|
8773
|
+
|
8774
|
+
horizontal_box {
|
8775
|
+
label('Width')
|
8776
|
+
spinbox(1, 1000) {
|
8777
|
+
value <=> [self, :button_width, after_write: method(:rebuild_push_button)]
|
8778
|
+
}
|
8779
|
+
}
|
8780
|
+
|
8781
|
+
horizontal_box {
|
8782
|
+
label('Height')
|
8783
|
+
spinbox(1, 1000) {
|
8784
|
+
value <=> [self, :button_height, after_write: method(:rebuild_push_button)]
|
8785
|
+
}
|
8786
|
+
}
|
8787
|
+
|
8788
|
+
horizontal_box {
|
8789
|
+
label('Font')
|
8790
|
+
font_button {
|
8791
|
+
font <=> [self, :button_font_descriptor, after_write: method(:rebuild_push_button)]
|
8792
|
+
}
|
8793
|
+
}
|
8794
|
+
|
8795
|
+
horizontal_box {
|
8796
|
+
label('Text Color')
|
8797
|
+
color_button {
|
8798
|
+
color <=> [self, :button_text_color, after_write: method(:rebuild_push_button)]
|
8799
|
+
}
|
8800
|
+
}
|
8801
|
+
|
8802
|
+
horizontal_box {
|
8803
|
+
label('Background Color')
|
8804
|
+
color_button {
|
8805
|
+
color <=> [self, :button_background_fill, after_write: method(:rebuild_push_button)]
|
8806
|
+
}
|
8807
|
+
}
|
8808
|
+
|
8809
|
+
horizontal_box {
|
8810
|
+
label('Border Color')
|
8811
|
+
color_button {
|
8812
|
+
color <=> [self, :button_border_stroke, after_write: method(:rebuild_push_button)]
|
8813
|
+
}
|
8814
|
+
}
|
8815
|
+
|
8816
|
+
horizontal_box {
|
8817
|
+
label('Text X (0=centered)')
|
8818
|
+
spinbox(0, 1000) {
|
8819
|
+
value <=> [self, :button_text_x, on_read: ->(x) {x.nil? ? 0 : x}, on_write: ->(x) {x == 0 ? nil : x}, after_write: method(:rebuild_push_button)]
|
8820
|
+
}
|
8821
|
+
}
|
8822
|
+
|
8823
|
+
horizontal_box {
|
8824
|
+
label('Text Y (0=centered)')
|
8825
|
+
spinbox(0, 1000) {
|
8826
|
+
value <=> [self, :button_text_y, on_read: ->(y) {y.nil? ? 0 : y}, on_write: ->(y) {y == 0 ? nil : y}, after_write: method(:rebuild_push_button)]
|
8827
|
+
}
|
8828
|
+
}
|
8829
|
+
}
|
8830
|
+
|
8831
|
+
@push_button = push_button('Push',
|
8832
|
+
width: button_width, height: button_height, font_descriptor: button_font_descriptor,
|
8833
|
+
background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
|
8834
|
+
text_x: button_text_x, text_y: button_text_y) {
|
8835
|
+
on_mouse_up do
|
8836
|
+
message_box('Button Pushed', 'Thank you for pushing the button!')
|
8837
|
+
end
|
8838
|
+
}
|
8839
|
+
}
|
8840
|
+
}
|
8841
|
+
}
|
8842
|
+
}.show
|
8843
|
+
end
|
8844
|
+
|
8845
|
+
# text label (area-based custom control) built with vector graphics on top of area.
|
8846
|
+
#
|
8847
|
+
# background_fill is transparent by default.
|
8848
|
+
# background_fill can accept a single color or gradient stops just as per `fill` property in README.
|
8849
|
+
# border_stroke is transparent by default.
|
8850
|
+
# border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
|
8851
|
+
# text_x and text_y are the offset of the label text in relation to its top-left corner.
|
8852
|
+
# When text_x, text_y are left nil, the text is automatically centered in the label area.
|
8853
|
+
# Sometimes, the centering calculation is not perfect due to using a custom font, so
|
8854
|
+
# in that case, pass in text_x, and text_y manually.
|
8855
|
+
def text_label(label_text,
|
8856
|
+
width: 80, height: 30, font_descriptor: {},
|
8857
|
+
background_fill: {a: 0}, text_color: :black, border_stroke: {a: 0},
|
8858
|
+
text_x: nil, text_y: nil,
|
8859
|
+
&content)
|
8860
|
+
area { |the_area|
|
8861
|
+
rectangle(1, 1, width, height) {
|
8862
|
+
fill background_fill
|
8863
|
+
}
|
8864
|
+
rectangle(1, 1, width, height) {
|
8865
|
+
stroke border_stroke
|
8866
|
+
}
|
8867
|
+
|
8868
|
+
text_height = (font_descriptor[:size] || 12) * (OS.mac? ? 0.75 : 1.35)
|
8869
|
+
text_width = (text_height * label_text.size) * (OS.mac? ? 0.75 : 0.60)
|
8870
|
+
text_x ||= (width - text_width) / 2.0
|
8871
|
+
text_y ||= (height - 4 - text_height) / 2.0
|
8872
|
+
text(text_x, text_y, width) {
|
8873
|
+
string(label_text) {
|
8874
|
+
color text_color
|
8875
|
+
font font_descriptor
|
8876
|
+
}
|
8877
|
+
}
|
8878
|
+
|
8879
|
+
content&.call(the_area)
|
8880
|
+
}
|
8881
|
+
end
|
8882
|
+
|
8883
|
+
# push button (area-based custom control) built with vector graphics on top of area.
|
8884
|
+
#
|
8885
|
+
# background_fill is white by default.
|
8886
|
+
# background_fill can accept a single color or gradient stops just as per `fill` property in README.
|
8887
|
+
# border_stroke is black by default.
|
8888
|
+
# border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
|
8889
|
+
# text_x and text_y are the offset of the button text in relation to its top-left corner.
|
8890
|
+
# When text_x, text_y are left nil, the text is automatically centered in the button area.
|
8891
|
+
# Sometimes, the centering calculation is not perfect due to using a custom font, so
|
8892
|
+
# in that case, pass in text_x, and text_y manually.
|
8893
|
+
#
|
8894
|
+
# reuses the text_label custom control
|
8895
|
+
def push_button(button_text,
|
8896
|
+
width: 80, height: 30, font_descriptor: {},
|
8897
|
+
background_fill: :white, text_color: :black, border_stroke: {r: 201, g: 201, b: 201},
|
8898
|
+
text_x: nil, text_y: nil,
|
8899
|
+
&content)
|
8900
|
+
text_label(button_text,
|
8901
|
+
width: width, height: height, font_descriptor: font_descriptor,
|
8902
|
+
background_fill: background_fill, text_color: text_color, border_stroke: border_stroke,
|
8903
|
+
text_x: text_x, text_y: text_y) { |the_area|
|
8904
|
+
|
8905
|
+
# dig into the_area content and grab elements to modify in mouse listeners below
|
8906
|
+
background_rectangle = the_area.children[0]
|
8907
|
+
button_string = the_area.children[2].children[0]
|
8908
|
+
|
8909
|
+
on_mouse_down do
|
8910
|
+
background_rectangle.fill = {x0: 0, y0: 0, x1: 0, y1: height, stops: [{pos: 0, r: 72, g: 146, b: 247}, {pos: 1, r: 12, g: 85, b: 214}]}
|
8911
|
+
button_string.color = :white
|
8912
|
+
end
|
8913
|
+
|
8914
|
+
on_mouse_up do
|
8915
|
+
background_rectangle.fill = background_fill
|
8916
|
+
button_string.color = text_color
|
8917
|
+
end
|
8918
|
+
|
8919
|
+
content&.call(the_area)
|
8920
|
+
}
|
8921
|
+
end
|
8922
|
+
end
|
8923
|
+
|
8924
|
+
AreaBasedCustomControls.new.launch
|
8925
|
+
```
|
8926
|
+
|
8588
8927
|
#### Midi Player
|
8589
8928
|
|
8590
8929
|
To run this example, install [TiMidity](http://timidity.sourceforge.net) and ensure `timidity` command is in `PATH` (can be installed via [Homebrew](https://brew.sh) on Mac or [apt-get](https://help.ubuntu.com/community/AptGet/Howto) on Linux).
|
@@ -8983,6 +9322,8 @@ TinyMidiPlayer.new
|
|
8983
9322
|
|
8984
9323
|
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.
|
8985
9324
|
|
9325
|
+
Use arrows to move and spacebar to pause/resume.
|
9326
|
+
|
8986
9327
|
[examples/snake.rb](examples/snake.rb)
|
8987
9328
|
|
8988
9329
|
Run with this command from the root of the project if you cloned the project:
|
@@ -9038,7 +9379,7 @@ class Snake
|
|
9038
9379
|
end
|
9039
9380
|
|
9040
9381
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
9041
|
-
unless @game.over?
|
9382
|
+
unless @game.paused? || @game.over?
|
9042
9383
|
process_queued_keypress
|
9043
9384
|
@game.snake.move
|
9044
9385
|
end
|
@@ -9079,7 +9420,11 @@ class Snake
|
|
9079
9420
|
}
|
9080
9421
|
|
9081
9422
|
on_key_up do |area_key_event|
|
9082
|
-
|
9423
|
+
if area_key_event[:key] == ' '
|
9424
|
+
@game.toggle_pause
|
9425
|
+
else
|
9426
|
+
@keypress_queue << area_key_event[:ext_key]
|
9427
|
+
end
|
9083
9428
|
end
|
9084
9429
|
}
|
9085
9430
|
end
|
@@ -9138,7 +9483,7 @@ class Snake
|
|
9138
9483
|
end
|
9139
9484
|
|
9140
9485
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
9141
|
-
unless @game.over?
|
9486
|
+
unless @game.paused? || @game.over?
|
9142
9487
|
process_queued_keypress
|
9143
9488
|
@game.snake.move
|
9144
9489
|
end
|
@@ -9181,7 +9526,11 @@ class Snake
|
|
9181
9526
|
}
|
9182
9527
|
|
9183
9528
|
on_key_up do |area_key_event|
|
9184
|
-
|
9529
|
+
if area_key_event[:key] == ' '
|
9530
|
+
@game.toggle_pause
|
9531
|
+
else
|
9532
|
+
@keypress_queue << area_key_event[:ext_key]
|
9533
|
+
end
|
9185
9534
|
end
|
9186
9535
|
}
|
9187
9536
|
end
|
@@ -9215,7 +9564,7 @@ ruby -r glimmer-dsl-libui -e "require 'examples/tetris'"
|
|
9215
9564
|
|
9216
9565
|
Mac | Windows | Linux
|
9217
9566
|
----|---------|------
|
9218
|
-
![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png)
|
9567
|
+
![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png) | ![glimmer-dsl-libui-windows-tetris.png](images/glimmer-dsl-libui-windows-tetris.png) | ![glimmer-dsl-libui-linux-tetris.png](images/glimmer-dsl-libui-linux-tetris.png)
|
9219
9568
|
|
9220
9569
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
9221
9570
|
|
@@ -9293,18 +9642,31 @@ class Tetris
|
|
9293
9642
|
|
9294
9643
|
Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
|
9295
9644
|
Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
|
9296
|
-
|
9645
|
+
preview_updater = proc do
|
9297
9646
|
Glimmer::LibUI.queue_main do
|
9647
|
+
new_color = @game.preview_playfield[row][column].color
|
9298
9648
|
color = Glimmer::LibUI.interpret_color(new_color)
|
9299
9649
|
block = @preview_playfield_blocks[row][column]
|
9300
|
-
|
9301
|
-
|
9302
|
-
|
9303
|
-
|
9304
|
-
|
9305
|
-
|
9650
|
+
if @game.show_preview_tetromino?
|
9651
|
+
block[:background_square].fill = color
|
9652
|
+
block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
|
9653
|
+
block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9654
|
+
block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9655
|
+
block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9656
|
+
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
9657
|
+
else
|
9658
|
+
transparent_color = {r: 255, g: 255, b: 255, a: 0}
|
9659
|
+
block[:background_square].fill = transparent_color
|
9660
|
+
block[:top_bevel_edge].fill = transparent_color
|
9661
|
+
block[:right_bevel_edge].fill = transparent_color
|
9662
|
+
block[:bottom_bevel_edge].fill = transparent_color
|
9663
|
+
block[:left_bevel_edge].fill = transparent_color
|
9664
|
+
block[:border_square].stroke = transparent_color
|
9665
|
+
end
|
9306
9666
|
end
|
9307
9667
|
end
|
9668
|
+
observe(@game.preview_playfield[row][column], :color, &preview_updater)
|
9669
|
+
observe(@game, :show_preview_tetromino, &preview_updater)
|
9308
9670
|
end
|
9309
9671
|
end
|
9310
9672
|
|
@@ -9352,6 +9714,12 @@ class Tetris
|
|
9352
9714
|
}
|
9353
9715
|
|
9354
9716
|
menu('View') {
|
9717
|
+
check_menu_item('Show Next Block Preview') {
|
9718
|
+
checked <=> [@game, :show_preview_tetromino]
|
9719
|
+
}
|
9720
|
+
|
9721
|
+
separator_menu_item
|
9722
|
+
|
9355
9723
|
menu_item('Show High Scores') {
|
9356
9724
|
on_clicked do
|
9357
9725
|
show_high_scores
|
@@ -9363,18 +9731,20 @@ class Tetris
|
|
9363
9731
|
@game.clear_high_scores!
|
9364
9732
|
}
|
9365
9733
|
}
|
9734
|
+
|
9735
|
+
separator_menu_item
|
9366
9736
|
}
|
9367
9737
|
|
9368
9738
|
menu('Options') {
|
9369
|
-
radio_menu_item('Instant Down on Up Arrow') {
|
9739
|
+
radio_menu_item('Instant Down on Up Arrow') {
|
9370
9740
|
checked <=> [@game, :instant_down_on_up]
|
9371
9741
|
}
|
9372
9742
|
|
9373
|
-
radio_menu_item('Rotate Right on Up Arrow') {
|
9743
|
+
radio_menu_item('Rotate Right on Up Arrow') {
|
9374
9744
|
checked <=> [@game, :rotate_right_on_up]
|
9375
9745
|
}
|
9376
9746
|
|
9377
|
-
radio_menu_item('Rotate Left on Up Arrow') {
|
9747
|
+
radio_menu_item('Rotate Left on Up Arrow') {
|
9378
9748
|
checked <=> [@game, :rotate_left_on_up]
|
9379
9749
|
}
|
9380
9750
|
}
|
@@ -9421,7 +9791,7 @@ class Tetris
|
|
9421
9791
|
block = {}
|
9422
9792
|
bevel_pixel_size = 0.16 * block_size.to_f
|
9423
9793
|
color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
|
9424
|
-
area {
|
9794
|
+
block[:area] = area {
|
9425
9795
|
block[:background_square] = square(0, 0, block_size) {
|
9426
9796
|
fill color
|
9427
9797
|
}
|
@@ -9497,12 +9867,6 @@ class Tetris
|
|
9497
9867
|
|
9498
9868
|
def score_board(block_size: , &extra_content)
|
9499
9869
|
vertical_box {
|
9500
|
-
horizontal_box {
|
9501
|
-
label # filler
|
9502
|
-
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
|
9503
|
-
label # filler
|
9504
|
-
}
|
9505
|
-
|
9506
9870
|
horizontal_box {
|
9507
9871
|
label # filler
|
9508
9872
|
grid {
|
@@ -9543,6 +9907,12 @@ class Tetris
|
|
9543
9907
|
}
|
9544
9908
|
label # filler
|
9545
9909
|
}
|
9910
|
+
|
9911
|
+
horizontal_box {
|
9912
|
+
label # filler
|
9913
|
+
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
|
9914
|
+
label # filler
|
9915
|
+
}
|
9546
9916
|
|
9547
9917
|
extra_content&.call
|
9548
9918
|
}
|
@@ -9582,7 +9952,7 @@ class Tetris
|
|
9582
9952
|
|
9583
9953
|
def show_about_dialog
|
9584
9954
|
Glimmer::LibUI.queue_main do
|
9585
|
-
msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
|
9955
|
+
msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021-2022 Andy Maleh')
|
9586
9956
|
end
|
9587
9957
|
end
|
9588
9958
|
end
|
@@ -10092,6 +10462,10 @@ https://github.com/AndyObtiva/befunge98/tree/gui
|
|
10092
10462
|
|
10093
10463
|
https://github.com/iraamaro/i3off-gtk-ruby
|
10094
10464
|
|
10465
|
+
### Chess
|
10466
|
+
|
10467
|
+
https://github.com/mikeweber/chess
|
10468
|
+
|
10095
10469
|
## Process
|
10096
10470
|
|
10097
10471
|
[Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
|
@@ -10107,7 +10481,7 @@ https://github.com/iraamaro/i3off-gtk-ruby
|
|
10107
10481
|
|
10108
10482
|
### Issues
|
10109
10483
|
|
10110
|
-
If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com). In the meantime, you may try older gem versions of [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) till you find one that works
|
10484
|
+
If you encounter [issues](https://github.com/AndyObtiva/glimmer-dsl-libui/issues) that are not reported, discover missing features that are not mentioned in [TODO.md](TODO.md), or think up better ways to use [libui](https://github.com/andlabs/libui) than what is possible with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), you may submit an [issue](https://github.com/AndyObtiva/glimmer-dsl-libui/issues/new) or [pull request](https://github.com/AndyObtiva/glimmer-dsl-libui/compare) on [GitHub](https://github.com). In the meantime, you may try older gem versions of [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) till you find one that works.
|
10111
10485
|
|
10112
10486
|
### Chat
|
10113
10487
|
|
@@ -10151,7 +10525,7 @@ Note that the latest development sometimes takes place in the [development](http
|
|
10151
10525
|
|
10152
10526
|
[MIT](LICENSE.txt)
|
10153
10527
|
|
10154
|
-
Copyright (c) 2021 Andy Maleh
|
10528
|
+
Copyright (c) 2021-2022 Andy Maleh
|
10155
10529
|
|
10156
10530
|
--
|
10157
10531
|
|