glimmer-dsl-libui 0.4.19 → 0.5.0
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 +452 -52
- data/VERSION +1 -1
- data/bin/girb_runner.rb +1 -1
- data/examples/area_based_custom_controls.rb +282 -0
- 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 +1 -1
- 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 +1 -1
- 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 +1 -1
- 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 +8 -3
- data/lib/glimmer/libui/shape/square.rb +10 -3
- data/lib/glimmer/libui/shape.rb +37 -1
- data/lib/glimmer/libui.rb +1 -1
- data/lib/glimmer-dsl-libui.rb +2 -1
- metadata +27 -11
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.
|
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.5.0
|
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)
|
326
330
|
|
327
|
-
|
331
|
+
![glimmer-dsl-libui-mac-tetris.gif](images/glimmer-dsl-libui-mac-tetris.gif)
|
328
332
|
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
-
|
333
|
+
NOTE: [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) is 100% feature-complete with regards to covering the C [libui](https://github.com/andlabs/libui) library API and in beta mode (though the C [libui](https://github.com/andlabs/libui) is still mid-alpha, which is why [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) cannot be declared v1.0.0 yet). 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.
|
334
|
+
|
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.
|
522
|
+
gem 'glimmer-dsl-libui', '~> 0.5.0'
|
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
|
@@ -994,15 +1008,49 @@ To draw `text` in an `area`, you simply nest a `text(x, y, width)` control direc
|
|
994
1008
|
Example (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
995
1009
|
|
996
1010
|
```ruby
|
1011
|
+
require 'glimmer-dsl-libui'
|
1012
|
+
|
1013
|
+
include Glimmer
|
1014
|
+
|
997
1015
|
window('area text drawing') {
|
998
1016
|
area {
|
999
1017
|
text {
|
1000
1018
|
default_font family: 'Helvetica', size: 12, weight: :normal, italic: :normal, stretch: :normal
|
1001
1019
|
|
1020
|
+
string('This ') {
|
1021
|
+
font size: 20, weight: :bold, italic: :normal, stretch: :normal
|
1022
|
+
color r: 128, g: 0, b: 0, a: 1
|
1023
|
+
}
|
1024
|
+
|
1025
|
+
string('is ') {
|
1026
|
+
font size: 20, weight: :bold, italic: :normal, stretch: :normal
|
1027
|
+
color r: 0, g: 128, b: 0, a: 1
|
1028
|
+
}
|
1029
|
+
|
1030
|
+
string('a ') {
|
1031
|
+
font size: 20, weight: :bold, italic: :normal, stretch: :normal
|
1032
|
+
color r: 0, g: 0, b: 128, a: 1
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
string('short ') {
|
1036
|
+
font size: 20, weight: :bold, italic: :italic, stretch: :normal
|
1037
|
+
color r: 128, g: 128, b: 0, a: 1
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
string('attributed ') {
|
1041
|
+
font size: 20, weight: :bold, italic: :normal, stretch: :normal
|
1042
|
+
color r: 0, g: 128, b: 128, a: 1
|
1043
|
+
}
|
1044
|
+
|
1045
|
+
string("string \n\n") {
|
1046
|
+
font size: 20, weight: :bold, italic: :normal, stretch: :normal
|
1047
|
+
color r: 128, g: 0, b: 128, a: 1
|
1048
|
+
}
|
1049
|
+
|
1002
1050
|
string {
|
1003
1051
|
font family: 'Georgia', size: 13, weight: :medium, italic: :normal, stretch: :normal
|
1004
|
-
color r:
|
1005
|
-
background r:
|
1052
|
+
color r: 0, g: 128, b: 255, a: 1
|
1053
|
+
background r: 255, g: 255, b: 0, a: 0.5
|
1006
1054
|
underline :single
|
1007
1055
|
underline_color :spelling
|
1008
1056
|
open_type_features {
|
@@ -1015,13 +1063,14 @@ window('area text drawing') {
|
|
1015
1063
|
"attributed string\n" \
|
1016
1064
|
"spanning multiple lines\n\n"
|
1017
1065
|
}
|
1018
|
-
|
1019
|
-
string('This is a short unattributed string')
|
1020
1066
|
}
|
1021
1067
|
}
|
1022
1068
|
}.show
|
1023
1069
|
```
|
1024
1070
|
|
1071
|
+
![glimmer-dsl-libui-mac-area-text-drawing.png](/images/glimmer-dsl-libui-mac-area-text-drawing.png)
|
1072
|
+
|
1073
|
+
|
1025
1074
|
You may checkout [examples/basic_draw_text.rb](#basic-draw-text) and [examples/custom_draw_text.rb](#custom-draw-text) for examples of using `text` inside `area`.
|
1026
1075
|
|
1027
1076
|
Mac | Windows | Linux
|
@@ -1365,8 +1414,8 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1365
1414
|
- 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
1415
|
- All boolean property writers accept `true`/`false` in addition to `1`/`0` in Ruby
|
1367
1416
|
- 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
|
1417
|
+
- Automatically allocate font descriptors upon instantiating `font_button` controls and free them when destroying `font_button` controls
|
1418
|
+
- Automatically allocate color value pointers upon instantiating `color_button` controls and free them when destroying `color_button` controls
|
1370
1419
|
- On the Mac, if no `menu` items were added, an automatic `quit_menu_item` is added to enable quitting with CTRL+Q
|
1371
1420
|
- When destroying a control nested under a `horizontal_box` or `vertical_box`, it is automatically deleted from the box's children
|
1372
1421
|
- When destroying a control nested under a `form`, it is automatically deleted from the form's children
|
@@ -1396,14 +1445,16 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1396
1445
|
|
1397
1446
|
### Custom Keywords
|
1398
1447
|
|
1399
|
-
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
|
1400
|
-
|
1448
|
+
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.
|
1449
|
+
|
1401
1450
|
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.
|
1402
1451
|
|
1403
1452
|
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)`).
|
1404
1453
|
|
1405
1454
|
To make custom keywords externally reusable, you can define in modules and simply include the modules in the view classes that need them.
|
1406
1455
|
|
1456
|
+
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`).
|
1457
|
+
|
1407
1458
|
Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1408
1459
|
|
1409
1460
|
```ruby
|
@@ -1501,11 +1552,11 @@ window('Method-Based Custom Keyword') {
|
|
1501
1552
|
|
1502
1553
|
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png)
|
1503
1554
|
|
1504
|
-
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.
|
1555
|
+
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.
|
1505
1556
|
|
1506
1557
|
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.
|
1507
1558
|
|
1508
|
-
Learn more from custom keyword usage in [Method-Based Custom Keyword](#method-based-custom-keyword), [Histogram](#histogram), and [Tetris](#tetris) examples.
|
1559
|
+
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.
|
1509
1560
|
|
1510
1561
|
### Observer Pattern
|
1511
1562
|
|
@@ -1778,9 +1829,9 @@ Data-bound model attribute can be:
|
|
1778
1829
|
- **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
|
1779
1830
|
|
1780
1831
|
Data-binding options include:
|
1781
|
-
- `before_read {|value| ...}`: performs an operation before reading data from Model to update
|
1832
|
+
- `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
|
1782
1833
|
- `on_read {|value| ...}`: converts value read from Model to update the View.
|
1783
|
-
- `after_read {|converted_value| ...}`: performs an operation after read from Model
|
1834
|
+
- `after_read {|converted_value| ...}`: performs an operation after read from Model to update View.
|
1784
1835
|
- `before_write {|value| ...}`: performs an operation before writing data to Model from View.
|
1785
1836
|
- `on_write {|value| ...}`: converts value read from View to update the Model.
|
1786
1837
|
- `after_write {|converted_value| ...}`: performs an operation after writing to Model from View.
|
@@ -1812,6 +1863,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1812
1863
|
- `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)
|
1813
1864
|
- `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
|
1814
1865
|
- `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.
|
1866
|
+
- `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.
|
1815
1867
|
- 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.
|
1816
1868
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1817
1869
|
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
@@ -1836,6 +1888,8 @@ For Mac, consider [Platypus](https://github.com/sveinbjornt/Platypus) (builds a
|
|
1836
1888
|
|
1837
1889
|
For Linux, simply package your app as a [Ruby Gem](https://guides.rubygems.org/what-is-a-gem/) and [build rpm package from Ruby Gem](https://www.redpill-linpro.com/sysadvent/2015/12/07/building-rpms-from-gems.html) or [build deb package from Ruby Gem](https://openpreservation.org/blogs/building-debian-package-ruby-program/).
|
1838
1890
|
|
1891
|
+
Also, there is a promising project called [ruby-packer](https://github.com/pmq20/ruby-packer) that supports all platforms.
|
1892
|
+
|
1839
1893
|
## Glimmer Style Guide
|
1840
1894
|
|
1841
1895
|
**1 - Control arguments are always wrapped by parentheses.**
|
@@ -8375,7 +8429,7 @@ window('Login') {
|
|
8375
8429
|
|
8376
8430
|
#### Method-Based Custom Keyword
|
8377
8431
|
|
8378
|
-
[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
|
8432
|
+
[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.
|
8379
8433
|
|
8380
8434
|
This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom control keywords.
|
8381
8435
|
|
@@ -8596,6 +8650,317 @@ window('Method-Based Custom Keyword') {
|
|
8596
8650
|
}.show
|
8597
8651
|
```
|
8598
8652
|
|
8653
|
+
#### Area-Based Custom Controls
|
8654
|
+
|
8655
|
+
[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.
|
8656
|
+
|
8657
|
+
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.
|
8658
|
+
|
8659
|
+
[examples/area_based_custom_controls.rb](examples/area_based_custom_controls.rb)
|
8660
|
+
|
8661
|
+
Run with this command from the root of the project if you cloned the project:
|
8662
|
+
|
8663
|
+
```
|
8664
|
+
ruby -r './lib/glimmer-dsl-libui' examples/area_based_custom_controls.rb
|
8665
|
+
```
|
8666
|
+
|
8667
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
8668
|
+
|
8669
|
+
```
|
8670
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/area_based_custom_controls'"
|
8671
|
+
```
|
8672
|
+
|
8673
|
+
Mac | Windows | Linux
|
8674
|
+
----|---------|------
|
8675
|
+
![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)
|
8676
|
+
|
8677
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
8678
|
+
|
8679
|
+
```ruby
|
8680
|
+
require 'glimmer-dsl-libui'
|
8681
|
+
|
8682
|
+
class AreaBasedCustomControls
|
8683
|
+
include Glimmer
|
8684
|
+
|
8685
|
+
attr_accessor :label_width, :label_height, :label_font_descriptor,
|
8686
|
+
:label_text_color, :label_background_fill, :label_border_stroke,
|
8687
|
+
:label_text_x, :label_text_y,
|
8688
|
+
:button_width, :button_height, :button_font_descriptor,
|
8689
|
+
:button_text_color, :button_background_fill, :button_border_stroke,
|
8690
|
+
:button_text_x, :button_text_y
|
8691
|
+
|
8692
|
+
def initialize
|
8693
|
+
self.label_width = 335
|
8694
|
+
self.label_height = 50
|
8695
|
+
self.label_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 16, weight: :bold, italic: :italic}
|
8696
|
+
self.label_text_color = :red
|
8697
|
+
self.label_background_fill = :yellow
|
8698
|
+
self.label_border_stroke = :limegreen
|
8699
|
+
|
8700
|
+
self.button_width = 150
|
8701
|
+
self.button_height = 50
|
8702
|
+
self.button_font_descriptor = {family: OS.linux? ? 'Monospace Bold Italic' : 'Courier New', size: 36, weight: OS.linux? ? :normal : :bold, italic: :italic}
|
8703
|
+
self.button_text_color = :green
|
8704
|
+
self.button_background_fill = :yellow
|
8705
|
+
self.button_border_stroke = :limegreen
|
8706
|
+
end
|
8707
|
+
|
8708
|
+
def rebuild_text_label
|
8709
|
+
@text_label.destroy
|
8710
|
+
@text_label_vertical_box.content { # re-open vertical box content and shove in a new label
|
8711
|
+
@text_label = text_label('This is a text label.',
|
8712
|
+
width: label_width, height: label_height, font_descriptor: label_font_descriptor,
|
8713
|
+
background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
|
8714
|
+
text_x: label_text_x, text_y: label_text_y)
|
8715
|
+
}
|
8716
|
+
end
|
8717
|
+
|
8718
|
+
def rebuild_push_button
|
8719
|
+
@push_button.destroy
|
8720
|
+
@push_button_vertical_box.content { # re-open vertical box content and shove in a new button
|
8721
|
+
@push_button = push_button('Push',
|
8722
|
+
width: button_width, height: button_height, font_descriptor: button_font_descriptor,
|
8723
|
+
background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
|
8724
|
+
text_x: button_text_x, text_y: button_text_y) {
|
8725
|
+
on_mouse_up do
|
8726
|
+
message_box('Button Pushed', 'Thank you for pushing the button!')
|
8727
|
+
end
|
8728
|
+
}
|
8729
|
+
}
|
8730
|
+
end
|
8731
|
+
|
8732
|
+
def launch
|
8733
|
+
window('Area-Based Custom Controls', 385, 385) { |w|
|
8734
|
+
margined true
|
8735
|
+
|
8736
|
+
tab {
|
8737
|
+
tab_item('Text Label') {
|
8738
|
+
@text_label_vertical_box = vertical_box {
|
8739
|
+
vertical_box {
|
8740
|
+
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)
|
8741
|
+
|
8742
|
+
horizontal_box {
|
8743
|
+
label('Width')
|
8744
|
+
spinbox(1, 1000) {
|
8745
|
+
value <=> [self, :label_width, after_write: method(:rebuild_text_label)]
|
8746
|
+
}
|
8747
|
+
}
|
8748
|
+
|
8749
|
+
horizontal_box {
|
8750
|
+
label('Height')
|
8751
|
+
spinbox(1, 1000) {
|
8752
|
+
value <=> [self, :label_height, after_write: method(:rebuild_text_label)]
|
8753
|
+
}
|
8754
|
+
}
|
8755
|
+
|
8756
|
+
horizontal_box {
|
8757
|
+
label('Font')
|
8758
|
+
font_button {
|
8759
|
+
font <=> [self, :label_font_descriptor, after_write: method(:rebuild_text_label)]
|
8760
|
+
}
|
8761
|
+
}
|
8762
|
+
|
8763
|
+
horizontal_box {
|
8764
|
+
label('Text Color')
|
8765
|
+
color_button {
|
8766
|
+
color <=> [self, :label_text_color, after_write: method(:rebuild_text_label)]
|
8767
|
+
}
|
8768
|
+
}
|
8769
|
+
|
8770
|
+
horizontal_box {
|
8771
|
+
label('Background Color')
|
8772
|
+
color_button {
|
8773
|
+
color <=> [self, :label_background_fill, after_write: method(:rebuild_text_label)]
|
8774
|
+
}
|
8775
|
+
}
|
8776
|
+
|
8777
|
+
horizontal_box {
|
8778
|
+
label('Border Color')
|
8779
|
+
color_button {
|
8780
|
+
color <=> [self, :label_border_stroke, after_write: method(:rebuild_text_label)]
|
8781
|
+
}
|
8782
|
+
}
|
8783
|
+
|
8784
|
+
horizontal_box {
|
8785
|
+
label('Text X (0=centered)')
|
8786
|
+
spinbox(0, 1000) {
|
8787
|
+
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)]
|
8788
|
+
}
|
8789
|
+
}
|
8790
|
+
|
8791
|
+
horizontal_box {
|
8792
|
+
label('Text Y (0=centered)')
|
8793
|
+
spinbox(0, 1000) {
|
8794
|
+
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)]
|
8795
|
+
}
|
8796
|
+
}
|
8797
|
+
}
|
8798
|
+
|
8799
|
+
@text_label = text_label('This is a text label.',
|
8800
|
+
width: label_width, height: label_height, font_descriptor: label_font_descriptor,
|
8801
|
+
background_fill: label_background_fill, text_color: label_text_color, border_stroke: label_border_stroke,
|
8802
|
+
text_x: label_text_x, text_y: label_text_y)
|
8803
|
+
}
|
8804
|
+
}
|
8805
|
+
|
8806
|
+
tab_item('Push Button') {
|
8807
|
+
@push_button_vertical_box = vertical_box {
|
8808
|
+
vertical_box {
|
8809
|
+
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)
|
8810
|
+
|
8811
|
+
horizontal_box {
|
8812
|
+
label('Width')
|
8813
|
+
spinbox(1, 1000) {
|
8814
|
+
value <=> [self, :button_width, after_write: method(:rebuild_push_button)]
|
8815
|
+
}
|
8816
|
+
}
|
8817
|
+
|
8818
|
+
horizontal_box {
|
8819
|
+
label('Height')
|
8820
|
+
spinbox(1, 1000) {
|
8821
|
+
value <=> [self, :button_height, after_write: method(:rebuild_push_button)]
|
8822
|
+
}
|
8823
|
+
}
|
8824
|
+
|
8825
|
+
horizontal_box {
|
8826
|
+
label('Font')
|
8827
|
+
font_button {
|
8828
|
+
font <=> [self, :button_font_descriptor, after_write: method(:rebuild_push_button)]
|
8829
|
+
}
|
8830
|
+
}
|
8831
|
+
|
8832
|
+
horizontal_box {
|
8833
|
+
label('Text Color')
|
8834
|
+
color_button {
|
8835
|
+
color <=> [self, :button_text_color, after_write: method(:rebuild_push_button)]
|
8836
|
+
}
|
8837
|
+
}
|
8838
|
+
|
8839
|
+
horizontal_box {
|
8840
|
+
label('Background Color')
|
8841
|
+
color_button {
|
8842
|
+
color <=> [self, :button_background_fill, after_write: method(:rebuild_push_button)]
|
8843
|
+
}
|
8844
|
+
}
|
8845
|
+
|
8846
|
+
horizontal_box {
|
8847
|
+
label('Border Color')
|
8848
|
+
color_button {
|
8849
|
+
color <=> [self, :button_border_stroke, after_write: method(:rebuild_push_button)]
|
8850
|
+
}
|
8851
|
+
}
|
8852
|
+
|
8853
|
+
horizontal_box {
|
8854
|
+
label('Text X (0=centered)')
|
8855
|
+
spinbox(0, 1000) {
|
8856
|
+
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)]
|
8857
|
+
}
|
8858
|
+
}
|
8859
|
+
|
8860
|
+
horizontal_box {
|
8861
|
+
label('Text Y (0=centered)')
|
8862
|
+
spinbox(0, 1000) {
|
8863
|
+
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)]
|
8864
|
+
}
|
8865
|
+
}
|
8866
|
+
}
|
8867
|
+
|
8868
|
+
@push_button = push_button('Push',
|
8869
|
+
width: button_width, height: button_height, font_descriptor: button_font_descriptor,
|
8870
|
+
background_fill: button_background_fill, text_color: button_text_color, border_stroke: button_border_stroke,
|
8871
|
+
text_x: button_text_x, text_y: button_text_y) {
|
8872
|
+
on_mouse_up do
|
8873
|
+
message_box('Button Pushed', 'Thank you for pushing the button!')
|
8874
|
+
end
|
8875
|
+
}
|
8876
|
+
}
|
8877
|
+
}
|
8878
|
+
}
|
8879
|
+
}.show
|
8880
|
+
end
|
8881
|
+
|
8882
|
+
# text label (area-based custom control) built with vector graphics on top of area.
|
8883
|
+
#
|
8884
|
+
# background_fill is transparent by default.
|
8885
|
+
# background_fill can accept a single color or gradient stops just as per `fill` property in README.
|
8886
|
+
# border_stroke is transparent by default.
|
8887
|
+
# border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
|
8888
|
+
# text_x and text_y are the offset of the label text in relation to its top-left corner.
|
8889
|
+
# When text_x, text_y are left nil, the text is automatically centered in the label area.
|
8890
|
+
# Sometimes, the centering calculation is not perfect due to using a custom font, so
|
8891
|
+
# in that case, pass in text_x, and text_y manually.
|
8892
|
+
def text_label(label_text,
|
8893
|
+
width: 80, height: 30, font_descriptor: {},
|
8894
|
+
background_fill: {a: 0}, text_color: :black, border_stroke: {a: 0},
|
8895
|
+
text_x: nil, text_y: nil,
|
8896
|
+
&content)
|
8897
|
+
area { |the_area|
|
8898
|
+
rectangle(1, 1, width, height) {
|
8899
|
+
fill background_fill
|
8900
|
+
}
|
8901
|
+
rectangle(1, 1, width, height) {
|
8902
|
+
stroke border_stroke
|
8903
|
+
}
|
8904
|
+
|
8905
|
+
text_height = (font_descriptor[:size] || 12) * (OS.mac? ? 0.75 : 1.35)
|
8906
|
+
text_width = (text_height * label_text.size) * (OS.mac? ? 0.75 : 0.60)
|
8907
|
+
text_x ||= (width - text_width) / 2.0
|
8908
|
+
text_y ||= (height - 4 - text_height) / 2.0
|
8909
|
+
text(text_x, text_y, width) {
|
8910
|
+
string(label_text) {
|
8911
|
+
color text_color
|
8912
|
+
font font_descriptor
|
8913
|
+
}
|
8914
|
+
}
|
8915
|
+
|
8916
|
+
content&.call(the_area)
|
8917
|
+
}
|
8918
|
+
end
|
8919
|
+
|
8920
|
+
# push button (area-based custom control) built with vector graphics on top of area.
|
8921
|
+
#
|
8922
|
+
# background_fill is white by default.
|
8923
|
+
# background_fill can accept a single color or gradient stops just as per `fill` property in README.
|
8924
|
+
# border_stroke is black by default.
|
8925
|
+
# border_stroke can accept thickness and dashes in addition to color just as per `stroke` property in README.
|
8926
|
+
# text_x and text_y are the offset of the button text in relation to its top-left corner.
|
8927
|
+
# When text_x, text_y are left nil, the text is automatically centered in the button area.
|
8928
|
+
# Sometimes, the centering calculation is not perfect due to using a custom font, so
|
8929
|
+
# in that case, pass in text_x, and text_y manually.
|
8930
|
+
#
|
8931
|
+
# reuses the text_label custom control
|
8932
|
+
def push_button(button_text,
|
8933
|
+
width: 80, height: 30, font_descriptor: {},
|
8934
|
+
background_fill: :white, text_color: :black, border_stroke: {r: 201, g: 201, b: 201},
|
8935
|
+
text_x: nil, text_y: nil,
|
8936
|
+
&content)
|
8937
|
+
text_label(button_text,
|
8938
|
+
width: width, height: height, font_descriptor: font_descriptor,
|
8939
|
+
background_fill: background_fill, text_color: text_color, border_stroke: border_stroke,
|
8940
|
+
text_x: text_x, text_y: text_y) { |the_area|
|
8941
|
+
|
8942
|
+
# dig into the_area content and grab elements to modify in mouse listeners below
|
8943
|
+
background_rectangle = the_area.children[0]
|
8944
|
+
button_string = the_area.children[2].children[0]
|
8945
|
+
|
8946
|
+
on_mouse_down do
|
8947
|
+
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}]}
|
8948
|
+
button_string.color = :white
|
8949
|
+
end
|
8950
|
+
|
8951
|
+
on_mouse_up do
|
8952
|
+
background_rectangle.fill = background_fill
|
8953
|
+
button_string.color = text_color
|
8954
|
+
end
|
8955
|
+
|
8956
|
+
content&.call(the_area)
|
8957
|
+
}
|
8958
|
+
end
|
8959
|
+
end
|
8960
|
+
|
8961
|
+
AreaBasedCustomControls.new.launch
|
8962
|
+
```
|
8963
|
+
|
8599
8964
|
#### Midi Player
|
8600
8965
|
|
8601
8966
|
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).
|
@@ -8994,6 +9359,8 @@ TinyMidiPlayer.new
|
|
8994
9359
|
|
8995
9360
|
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.
|
8996
9361
|
|
9362
|
+
Use arrows to move and spacebar to pause/resume.
|
9363
|
+
|
8997
9364
|
[examples/snake.rb](examples/snake.rb)
|
8998
9365
|
|
8999
9366
|
Run with this command from the root of the project if you cloned the project:
|
@@ -9049,7 +9416,7 @@ class Snake
|
|
9049
9416
|
end
|
9050
9417
|
|
9051
9418
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
9052
|
-
unless @game.over?
|
9419
|
+
unless @game.paused? || @game.over?
|
9053
9420
|
process_queued_keypress
|
9054
9421
|
@game.snake.move
|
9055
9422
|
end
|
@@ -9090,7 +9457,11 @@ class Snake
|
|
9090
9457
|
}
|
9091
9458
|
|
9092
9459
|
on_key_up do |area_key_event|
|
9093
|
-
|
9460
|
+
if area_key_event[:key] == ' '
|
9461
|
+
@game.toggle_pause
|
9462
|
+
else
|
9463
|
+
@keypress_queue << area_key_event[:ext_key]
|
9464
|
+
end
|
9094
9465
|
end
|
9095
9466
|
}
|
9096
9467
|
end
|
@@ -9149,7 +9520,7 @@ class Snake
|
|
9149
9520
|
end
|
9150
9521
|
|
9151
9522
|
Glimmer::LibUI.timer(SNAKE_MOVE_DELAY) do
|
9152
|
-
unless @game.over?
|
9523
|
+
unless @game.paused? || @game.over?
|
9153
9524
|
process_queued_keypress
|
9154
9525
|
@game.snake.move
|
9155
9526
|
end
|
@@ -9192,7 +9563,11 @@ class Snake
|
|
9192
9563
|
}
|
9193
9564
|
|
9194
9565
|
on_key_up do |area_key_event|
|
9195
|
-
|
9566
|
+
if area_key_event[:key] == ' '
|
9567
|
+
@game.toggle_pause
|
9568
|
+
else
|
9569
|
+
@keypress_queue << area_key_event[:ext_key]
|
9570
|
+
end
|
9196
9571
|
end
|
9197
9572
|
}
|
9198
9573
|
end
|
@@ -9226,7 +9601,7 @@ ruby -r glimmer-dsl-libui -e "require 'examples/tetris'"
|
|
9226
9601
|
|
9227
9602
|
Mac | Windows | Linux
|
9228
9603
|
----|---------|------
|
9229
|
-
![glimmer-dsl-libui-mac-tetris.png](images/glimmer-dsl-libui-mac-tetris.png)
|
9604
|
+
![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)
|
9230
9605
|
|
9231
9606
|
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version:
|
9232
9607
|
|
@@ -9304,18 +9679,31 @@ class Tetris
|
|
9304
9679
|
|
9305
9680
|
Model::Game::PREVIEW_PLAYFIELD_HEIGHT.times do |row|
|
9306
9681
|
Model::Game::PREVIEW_PLAYFIELD_WIDTH.times do |column|
|
9307
|
-
|
9682
|
+
preview_updater = proc do
|
9308
9683
|
Glimmer::LibUI.queue_main do
|
9684
|
+
new_color = @game.preview_playfield[row][column].color
|
9309
9685
|
color = Glimmer::LibUI.interpret_color(new_color)
|
9310
9686
|
block = @preview_playfield_blocks[row][column]
|
9311
|
-
|
9312
|
-
|
9313
|
-
|
9314
|
-
|
9315
|
-
|
9316
|
-
|
9687
|
+
if @game.show_preview_tetromino?
|
9688
|
+
block[:background_square].fill = color
|
9689
|
+
block[:top_bevel_edge].fill = {r: color[:r] + 4*BEVEL_CONSTANT, g: color[:g] + 4*BEVEL_CONSTANT, b: color[:b] + 4*BEVEL_CONSTANT}
|
9690
|
+
block[:right_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9691
|
+
block[:bottom_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9692
|
+
block[:left_bevel_edge].fill = {r: color[:r] - BEVEL_CONSTANT, g: color[:g] - BEVEL_CONSTANT, b: color[:b] - BEVEL_CONSTANT}
|
9693
|
+
block[:border_square].stroke = new_color == Model::Block::COLOR_CLEAR ? COLOR_GRAY : color
|
9694
|
+
else
|
9695
|
+
transparent_color = {r: 255, g: 255, b: 255, a: 0}
|
9696
|
+
block[:background_square].fill = transparent_color
|
9697
|
+
block[:top_bevel_edge].fill = transparent_color
|
9698
|
+
block[:right_bevel_edge].fill = transparent_color
|
9699
|
+
block[:bottom_bevel_edge].fill = transparent_color
|
9700
|
+
block[:left_bevel_edge].fill = transparent_color
|
9701
|
+
block[:border_square].stroke = transparent_color
|
9702
|
+
end
|
9317
9703
|
end
|
9318
9704
|
end
|
9705
|
+
observe(@game.preview_playfield[row][column], :color, &preview_updater)
|
9706
|
+
observe(@game, :show_preview_tetromino, &preview_updater)
|
9319
9707
|
end
|
9320
9708
|
end
|
9321
9709
|
|
@@ -9363,6 +9751,12 @@ class Tetris
|
|
9363
9751
|
}
|
9364
9752
|
|
9365
9753
|
menu('View') {
|
9754
|
+
check_menu_item('Show Next Block Preview') {
|
9755
|
+
checked <=> [@game, :show_preview_tetromino]
|
9756
|
+
}
|
9757
|
+
|
9758
|
+
separator_menu_item
|
9759
|
+
|
9366
9760
|
menu_item('Show High Scores') {
|
9367
9761
|
on_clicked do
|
9368
9762
|
show_high_scores
|
@@ -9374,18 +9768,20 @@ class Tetris
|
|
9374
9768
|
@game.clear_high_scores!
|
9375
9769
|
}
|
9376
9770
|
}
|
9771
|
+
|
9772
|
+
separator_menu_item
|
9377
9773
|
}
|
9378
9774
|
|
9379
9775
|
menu('Options') {
|
9380
|
-
radio_menu_item('Instant Down on Up Arrow') {
|
9776
|
+
radio_menu_item('Instant Down on Up Arrow') {
|
9381
9777
|
checked <=> [@game, :instant_down_on_up]
|
9382
9778
|
}
|
9383
9779
|
|
9384
|
-
radio_menu_item('Rotate Right on Up Arrow') {
|
9780
|
+
radio_menu_item('Rotate Right on Up Arrow') {
|
9385
9781
|
checked <=> [@game, :rotate_right_on_up]
|
9386
9782
|
}
|
9387
9783
|
|
9388
|
-
radio_menu_item('Rotate Left on Up Arrow') {
|
9784
|
+
radio_menu_item('Rotate Left on Up Arrow') {
|
9389
9785
|
checked <=> [@game, :rotate_left_on_up]
|
9390
9786
|
}
|
9391
9787
|
}
|
@@ -9432,7 +9828,7 @@ class Tetris
|
|
9432
9828
|
block = {}
|
9433
9829
|
bevel_pixel_size = 0.16 * block_size.to_f
|
9434
9830
|
color = Glimmer::LibUI.interpret_color(Model::Block::COLOR_CLEAR)
|
9435
|
-
area {
|
9831
|
+
block[:area] = area {
|
9436
9832
|
block[:background_square] = square(0, 0, block_size) {
|
9437
9833
|
fill color
|
9438
9834
|
}
|
@@ -9508,12 +9904,6 @@ class Tetris
|
|
9508
9904
|
|
9509
9905
|
def score_board(block_size: , &extra_content)
|
9510
9906
|
vertical_box {
|
9511
|
-
horizontal_box {
|
9512
|
-
label # filler
|
9513
|
-
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
|
9514
|
-
label # filler
|
9515
|
-
}
|
9516
|
-
|
9517
9907
|
horizontal_box {
|
9518
9908
|
label # filler
|
9519
9909
|
grid {
|
@@ -9554,6 +9944,12 @@ class Tetris
|
|
9554
9944
|
}
|
9555
9945
|
label # filler
|
9556
9946
|
}
|
9947
|
+
|
9948
|
+
horizontal_box {
|
9949
|
+
label # filler
|
9950
|
+
@preview_playfield_blocks = playfield(playfield_width: Model::Game::PREVIEW_PLAYFIELD_WIDTH, playfield_height: Model::Game::PREVIEW_PLAYFIELD_HEIGHT, block_size: block_size)
|
9951
|
+
label # filler
|
9952
|
+
}
|
9557
9953
|
|
9558
9954
|
extra_content&.call
|
9559
9955
|
}
|
@@ -9593,7 +9989,7 @@ class Tetris
|
|
9593
9989
|
|
9594
9990
|
def show_about_dialog
|
9595
9991
|
Glimmer::LibUI.queue_main do
|
9596
|
-
msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021 Andy Maleh')
|
9992
|
+
msg_box('About', 'Glimmer Tetris - Glimmer DSL for LibUI Example - Copyright (c) 2021-2022 Andy Maleh')
|
9597
9993
|
end
|
9598
9994
|
end
|
9599
9995
|
end
|
@@ -10103,6 +10499,10 @@ https://github.com/AndyObtiva/befunge98/tree/gui
|
|
10103
10499
|
|
10104
10500
|
https://github.com/iraamaro/i3off-gtk-ruby
|
10105
10501
|
|
10502
|
+
### Chess
|
10503
|
+
|
10504
|
+
https://github.com/mikeweber/chess
|
10505
|
+
|
10106
10506
|
## Process
|
10107
10507
|
|
10108
10508
|
[Glimmer Process](https://github.com/AndyObtiva/glimmer/blob/master/PROCESS.md)
|
@@ -10162,7 +10562,7 @@ Note that the latest development sometimes takes place in the [development](http
|
|
10162
10562
|
|
10163
10563
|
[MIT](LICENSE.txt)
|
10164
10564
|
|
10165
|
-
Copyright (c) 2021 Andy Maleh
|
10565
|
+
Copyright (c) 2021-2022 Andy Maleh
|
10166
10566
|
|
10167
10567
|
--
|
10168
10568
|
|