glimmer-dsl-libui 0.5.3 → 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +345 -50
- data/VERSION +1 -1
- data/examples/class_based_custom_controls.rb +113 -0
- data/examples/{method_based_custom_keyword.rb → method_based_custom_controls.rb} +4 -4
- data/examples/{method_based_custom_keyword2.rb → method_based_custom_controls2.rb} +4 -4
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/custom_control_expression.rb +58 -0
- data/lib/glimmer/dsl/libui/dsl.rb +1 -0
- data/lib/glimmer/dsl/libui/property_expression.rb +2 -1
- data/lib/glimmer/libui/control_proxy/path_proxy.rb +5 -1
- data/lib/glimmer/libui/custom_control.rb +249 -0
- data/lib/glimmer/libui/shape/bezier.rb +1 -1
- data/lib/glimmer/libui/shape/figure.rb +12 -0
- data/lib/glimmer/libui/shape/line.rb +1 -1
- data/lib/glimmer/proc_tracker.rb +39 -0
- metadata +26 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 48926d5aab22eec664c80bc9ebe5a94a841e4b6173cca343ad9876fb33924b03
|
4
|
+
data.tar.gz: 890dba3f171225448aad354ec767819faaf3cb59e48764adc63b0f35bcbc0fe3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 94650e60ccb1ed496dd682f937e4a91a3cffad37cfc3da17c462fd2db7349e06af9572c425f1b90d8a3a522c09eddd2b7eed6b4982c31a77fff97322aff6bbed
|
7
|
+
data.tar.gz: fc41c1e9882b42155729547ee2d83e43a40a64ae56c2523ff880cd2f818357fb42328a72b79810ccfb75fc7356ac70910e1d4202249488cb4f8aef9d3a8034fe
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,22 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.5.6
|
4
|
+
|
5
|
+
- Upgrade to glimmer 2.7.1 and document its support for keyed data-binding
|
6
|
+
- Support class-based custom controls
|
7
|
+
- examples/class_based_custom_controls.rb example
|
8
|
+
- Rename examples/method_based_custom_keyword.rb example to examples/method_based_custom_controls.rb
|
9
|
+
|
10
|
+
## 0.5.5
|
11
|
+
|
12
|
+
- Upgrade to libui 0.0.15 (with official Mac ARM64 support)
|
13
|
+
- Update [README](/README.md) with [Area Animation](/README.md#area-animation) `spinner` custom control
|
14
|
+
|
15
|
+
## 0.5.4
|
16
|
+
|
17
|
+
- Support `figure` `bounding_box` (minx, miny, width, height), `contain?` method (checking if shape contains point inside) and `include?` method (checking on outline if stroked and inside if filled?)
|
18
|
+
- Have `path` `draw_fill_mode` return `:winding` or `:alternate` while `draw_fill_mode_value` returns `0` or `1` respectively
|
19
|
+
|
3
20
|
## 0.5.3
|
4
21
|
|
5
22
|
- Support `polyline` `bounding_box` (minx, miny, width, height), `contain?` method (checking if shape contains point inside) and `include?` method (checking on outline if stroked and inside if filled?)
|
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.5.
|
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.6
|
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)
|
@@ -19,13 +19,11 @@ The main trade-off in using [Glimmer DSL for LibUI](https://rubygems.org/gems/gl
|
|
19
19
|
- [Declarative DSL syntax](#glimmer-gui-dsl-concepts) that visually maps to the GUI control hierarchy
|
20
20
|
- [Convention over configuration](#smart-defaults-and-conventions) via smart defaults and automation of low-level details
|
21
21
|
- Requiring the [least amount of syntax](#glimmer-gui-dsl-concepts) possible to build GUI
|
22
|
-
- [Custom
|
22
|
+
- [Custom Control](#custom-keywords) support
|
23
23
|
- [Bidirectional/Unidirectional Data-Binding](#data-binding) to declaratively wire and automatically synchronize GUI Views with Models
|
24
24
|
- [Far Future Plan] Scaffolding for new custom controls, apps, and gems
|
25
25
|
- [Far Future Plan] Native-Executable packaging on Mac, Windows, and Linux.
|
26
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
|
-
|
29
27
|
Hello, World!
|
30
28
|
|
31
29
|
```ruby
|
@@ -338,7 +336,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
|
338
336
|
[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
337
|
[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
338
|
[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 (
|
339
|
+
[Glimmer DSL for Tk (Ruby Tk 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 | Complicated Setup / Widgets Do Not Look Truly Native, Espcially on Linux | ActiveTcl / MRI Ruby
|
342
340
|
[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
341
|
[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
342
|
[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
|
@@ -370,6 +368,7 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
|
370
368
|
- [Area Listeners](#area-listeners)
|
371
369
|
- [Area Methods/Attributes](#area-methods-attributes)
|
372
370
|
- [Area Transform Matrix](#area-transform-matrix)
|
371
|
+
- [Area Animation](#area-animation)
|
373
372
|
- [Smart Defaults and Conventions](#smart-defaults-and-conventions)
|
374
373
|
- [Custom Keywords](#custom-keywords)
|
375
374
|
- [Observer Pattern](#observer-pattern)
|
@@ -420,7 +419,8 @@ DSL | Platforms | Native? | Vector Graphics? | Pros | Cons | Prereqs
|
|
420
419
|
- [Grid](#grid)
|
421
420
|
- [Histogram](#histogram)
|
422
421
|
- [Login](#login)
|
423
|
-
- [Method-Based Custom
|
422
|
+
- [Method-Based Custom Controls](#method-based-custom-controls)
|
423
|
+
- [Class-Based Custom Controls](#class-based-custom-controls)
|
424
424
|
- [Area-Based Custom Controls](#area-based-custom-controls)
|
425
425
|
- [Midi Player](#midi-player)
|
426
426
|
- [Snake](#snake)
|
@@ -452,10 +452,68 @@ The Glimmer GUI DSL provides object-oriented declarative hierarchical syntax for
|
|
452
452
|
- Requires the minimum amount of syntax needed to describe an app's GUI
|
453
453
|
|
454
454
|
The Glimmer GUI DSL follows these simple concepts in mapping from [LibUI](https://github.com/kojix2/LibUI) syntax:
|
455
|
-
|
456
|
-
|
457
|
-
|
458
|
-
|
455
|
+
|
456
|
+
**Keyword(args)**: [LibUI](https://github.com/kojix2/LibUI) controls may be declared by lower-case underscored name (aka keyword from list of [supported keywords](#supported-keywords)) (e.g. `window` or `button`). Behind the scenes, they are represented by keyword methods that map to corresponding `LibUI.new_keyword` methods receiving args (e.g. `window('hello world', 300, 200, true)`).
|
457
|
+
|
458
|
+
**Content Block** (Properties/Listeners/Controls): Any keyword may be optionally followed by a Ruby curly-brace multi-line content block containing properties (attributes), listeners, and/or nested controls.
|
459
|
+
|
460
|
+
Example:
|
461
|
+
|
462
|
+
```ruby
|
463
|
+
window {
|
464
|
+
title 'hello world' # property
|
465
|
+
|
466
|
+
on_closing do # listener (always has a do; end block to signify logic)
|
467
|
+
puts 'Bye'
|
468
|
+
end
|
469
|
+
|
470
|
+
button('greet') { # nested control
|
471
|
+
on_clicked do
|
472
|
+
puts 'hello world'
|
473
|
+
end
|
474
|
+
}
|
475
|
+
}
|
476
|
+
```
|
477
|
+
|
478
|
+
Content block optionally receives one arg representing the controll
|
479
|
+
|
480
|
+
Example:
|
481
|
+
|
482
|
+
```ruby
|
483
|
+
button('greet') { |b|
|
484
|
+
on_clicked do
|
485
|
+
puts b.text
|
486
|
+
end
|
487
|
+
}
|
488
|
+
```
|
489
|
+
|
490
|
+
**Property**: Control properties may be declared inside keyword blocks with lower-case underscored name followed by property value args (e.g. `title "hello world"` inside `group`). Behind the scenes, properties correspond to `LibUI.control_set_property` methods.
|
491
|
+
|
492
|
+
**Listener**: Control listeners may be declared inside keyword blocks with listener lower-case underscored name beginning with `on_` and receiving required block handler (always followed by a `do; end` style block to signify logic).
|
493
|
+
|
494
|
+
Example:
|
495
|
+
|
496
|
+
```ruby
|
497
|
+
button('click') {
|
498
|
+
on_clicked do
|
499
|
+
puts 'clicked'
|
500
|
+
end
|
501
|
+
}
|
502
|
+
```
|
503
|
+
|
504
|
+
Optionally, the listener block can receive an arg representing the control.
|
505
|
+
|
506
|
+
```ruby
|
507
|
+
button('click') {
|
508
|
+
on_clicked do |btn|
|
509
|
+
puts btn.text
|
510
|
+
end
|
511
|
+
}
|
512
|
+
```
|
513
|
+
|
514
|
+
Behind the scenes, listeners correspond to `LibUI.control_on_event` methods.
|
515
|
+
|
516
|
+
**Method**: Controls have methods that invoke certain operations on them. For example, `window` has a `#show` method that shows the window GUI. More methods are mentioned under [API](#api)
|
459
517
|
|
460
518
|
Example of an app written in [LibUI](https://github.com/kojix2/LibUI)'s procedural imperative syntax:
|
461
519
|
|
@@ -510,7 +568,7 @@ window('hello world', 300, 200) {
|
|
510
568
|
|
511
569
|
## Usage
|
512
570
|
|
513
|
-
Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly:
|
571
|
+
Install [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) gem directly into a [maintained Ruby version](https://www.ruby-lang.org/en/downloads/):
|
514
572
|
|
515
573
|
```
|
516
574
|
gem install glimmer-dsl-libui
|
@@ -519,7 +577,7 @@ gem install glimmer-dsl-libui
|
|
519
577
|
Or install via Bundler `Gemfile`:
|
520
578
|
|
521
579
|
```ruby
|
522
|
-
gem 'glimmer-dsl-libui', '~> 0.5.
|
580
|
+
gem 'glimmer-dsl-libui', '~> 0.5.6'
|
523
581
|
```
|
524
582
|
|
525
583
|
Test that installation worked by running the [Meta-Example](#examples):
|
@@ -1404,6 +1462,92 @@ You can set a `matrix`/`transform` on `area` directly to conveniently apply to a
|
|
1404
1462
|
|
1405
1463
|
Note that `area`, `path`, and nested shapes are all truly declarative, meaning they do not care about the ordering of calls to `fill`, `stroke`, and `transform`. Furthermore, any transform that is applied is reversed at the end of the block, so you never have to worry about the ordering of `transform` calls among different paths. You simply set a transform on the `path`s that need it and it is guaranteed to be called before all its content is drawn, and then undone afterwards to avoid affecting later paths. Matrix `transform` can be set on an entire `area` too, applying to all nested `path`s.
|
1406
1464
|
|
1465
|
+
#### Area Animation
|
1466
|
+
|
1467
|
+
If you need to animate `area` vector graphics, you just have to use the [`Glimmer::LibUI::timer`](#libui-operations) method along with making changes to shape attributes.
|
1468
|
+
|
1469
|
+
Spinner example that has a fully customizable method-based custom control called `spinner`, which is destroyed if you click on it (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1470
|
+
|
1471
|
+
```ruby
|
1472
|
+
require 'glimmer-dsl-libui'
|
1473
|
+
|
1474
|
+
class SpinnerExample
|
1475
|
+
include Glimmer
|
1476
|
+
|
1477
|
+
SIZE = 120
|
1478
|
+
|
1479
|
+
def initialize
|
1480
|
+
create_gui
|
1481
|
+
end
|
1482
|
+
|
1483
|
+
def launch
|
1484
|
+
@main_window.show
|
1485
|
+
end
|
1486
|
+
|
1487
|
+
def create_gui
|
1488
|
+
@main_window = window {
|
1489
|
+
title 'Spinner'
|
1490
|
+
content_size SIZE*2, SIZE*2
|
1491
|
+
|
1492
|
+
horizontal_box {
|
1493
|
+
padded false
|
1494
|
+
|
1495
|
+
vertical_box {
|
1496
|
+
padded false
|
1497
|
+
|
1498
|
+
spinner(size: SIZE)
|
1499
|
+
spinner(size: SIZE, fill_color: [42, 153, 214])
|
1500
|
+
}
|
1501
|
+
|
1502
|
+
vertical_box {
|
1503
|
+
padded false
|
1504
|
+
|
1505
|
+
spinner(size: SIZE/2.0, fill_color: :orange)
|
1506
|
+
spinner(size: SIZE/2.0, fill_color: {x0: 0, y0: 0, x1: SIZE/2.0, y1: SIZE/2.0, stops: [{pos: 0.25, r: 204, g: 102, b: 204}, {pos: 1, r: 2, g: 2, b: 254}]})
|
1507
|
+
spinner(size: SIZE/2.0, fill_color: :green, unfilled_color: :yellow)
|
1508
|
+
spinner(size: SIZE/2.0, fill_color: :white, unfilled_color: :gray, background_color: :black)
|
1509
|
+
}
|
1510
|
+
}
|
1511
|
+
}
|
1512
|
+
end
|
1513
|
+
|
1514
|
+
def spinner(size: 40.0, fill_color: :gray, background_color: :white, unfilled_color: {r: 243, g: 243, b: 243}, donut_percentage: 0.25)
|
1515
|
+
arc1 = arc2 = nil
|
1516
|
+
area { |the_area|
|
1517
|
+
rectangle(0, 0, size, size) {
|
1518
|
+
fill background_color
|
1519
|
+
}
|
1520
|
+
circle(size/2.0, size/2.0, size/2.0) {
|
1521
|
+
fill fill_color
|
1522
|
+
}
|
1523
|
+
arc1 = arc(size/2.0, size/2.0, size/2.0, 0, 180) {
|
1524
|
+
fill unfilled_color
|
1525
|
+
}
|
1526
|
+
arc2 = arc(size/2.0, size/2.0, size/2.0, 90, 180) {
|
1527
|
+
fill unfilled_color
|
1528
|
+
}
|
1529
|
+
circle(size/2.0, size/2.0, (size/2.0)*(1.0 - donut_percentage)) {
|
1530
|
+
fill background_color
|
1531
|
+
}
|
1532
|
+
|
1533
|
+
on_mouse_up do
|
1534
|
+
the_area.destroy
|
1535
|
+
end
|
1536
|
+
}.tap do
|
1537
|
+
Glimmer::LibUI.timer(0.05) do
|
1538
|
+
delta = 10
|
1539
|
+
arc1.start_angle += delta
|
1540
|
+
arc2.start_angle += delta
|
1541
|
+
end
|
1542
|
+
end
|
1543
|
+
end
|
1544
|
+
end
|
1545
|
+
|
1546
|
+
SpinnerExample.new.launch
|
1547
|
+
```
|
1548
|
+
|
1549
|
+
![mac spinner](/images/glimmer-dsl-libui-mac-spinner.gif)
|
1550
|
+
|
1407
1551
|
### Smart Defaults and Conventions
|
1408
1552
|
|
1409
1553
|
- `horizontal_box`, `vertical_box`, `grid`, and `form` controls have `padded` as `true` upon instantiation to ensure more user-friendly GUI by default
|
@@ -1455,15 +1599,15 @@ Note that `area`, `path`, and nested shapes are all truly declarative, meaning t
|
|
1455
1599
|
|
1456
1600
|
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.
|
1457
1601
|
|
1458
|
-
For example, you can define a custom `
|
1602
|
+
For example, you can define a custom `address_view` control as an aggregate of multiple `label` controls to reuse multiple times as a standard address View, displaying street, city, state, and zip code.
|
1459
1603
|
|
1460
|
-
|
1604
|
+
There are two ways to define custom keywords:
|
1605
|
+
- Method-Based: simply define a method representing the custom control you want (e.g. `address_view`) with any arguments needed (e.g. `address(address_model)`).
|
1606
|
+
- Class-Based: define a class matching the camelcased name of the custom control by convention (e.g. the `address_view` custom control keyword would have a class called `AddressView`) and `include Glimmer::LibUI::CustomControl`. Classes add the benefit of being able to distribute the custom controls into separate files and reuse externally from multiple places or share via Ruby gems.
|
1461
1607
|
|
1462
|
-
|
1608
|
+
It is OK to use the 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`).
|
1463
1609
|
|
1464
|
-
|
1465
|
-
|
1466
|
-
Example that defines `form_field`, `address_form`, `label_pair`, and `address` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1610
|
+
Example that defines `form_field`, `address_form`, `label_pair`, and `address_view` keywords (you may copy/paste in [`girb`](#girb-glimmer-irb)):
|
1467
1611
|
|
1468
1612
|
```ruby
|
1469
1613
|
require 'glimmer-dsl-libui'
|
@@ -1500,7 +1644,7 @@ def label_pair(model, attribute, value)
|
|
1500
1644
|
}
|
1501
1645
|
end
|
1502
1646
|
|
1503
|
-
def
|
1647
|
+
def address_view(address_model)
|
1504
1648
|
vertical_box {
|
1505
1649
|
address_model.each_pair do |attribute, value|
|
1506
1650
|
label_pair(address_model, attribute, value)
|
@@ -1530,7 +1674,7 @@ window('Method-Based Custom Keyword') {
|
|
1530
1674
|
stretchy false
|
1531
1675
|
}
|
1532
1676
|
|
1533
|
-
|
1677
|
+
address_view(address1)
|
1534
1678
|
}
|
1535
1679
|
|
1536
1680
|
vertical_separator {
|
@@ -1552,7 +1696,7 @@ window('Method-Based Custom Keyword') {
|
|
1552
1696
|
stretchy false
|
1553
1697
|
}
|
1554
1698
|
|
1555
|
-
|
1699
|
+
address_view(address2)
|
1556
1700
|
}
|
1557
1701
|
}
|
1558
1702
|
}.show
|
@@ -1835,6 +1979,7 @@ Data-bound model attribute can be:
|
|
1835
1979
|
- **Direct:** `Symbol` representing attribute reader/writer (e.g. `[person, :name`])
|
1836
1980
|
- **Nested:** `String` representing nested attribute path (e.g. `[company, 'address.street']`). That results in "nested data-binding"
|
1837
1981
|
- **Indexed:** `String` containing array attribute index (e.g. `[customer, 'addresses[0].street']`). That results in "indexed data-binding"
|
1982
|
+
- **Keyed:** `String` containing hash attribute key (e.g. `[customer, 'addresses[:main].street']`). That results in "keyed data-binding"
|
1838
1983
|
|
1839
1984
|
Data-binding options include:
|
1840
1985
|
- `before_read {|value| ...}`: performs an operation before reading data from Model to update View.
|
@@ -1868,6 +2013,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1868
2013
|
- There is no proper way to destroy `grid` children due to [libui](https://github.com/andlabs/libui) not offering any API for deleting them from `grid` (no `grid_delete` similar to `box_delete` for `horizontal_box` and `vertical_box`).
|
1869
2014
|
- `table` `checkbox_column` checkbox editing only works on Linux and Windows (not Mac) due to a current limitation in [libui](https://github.com/andlabs/ui/issues/357).
|
1870
2015
|
- `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).
|
2016
|
+
- `checkbox` only supports obtaining the `checked` property, but not updating it due to a current limitation in [libui](https://github.com/andlabs/libui).
|
1871
2017
|
- `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)
|
1872
2018
|
- `text` `string` `background` does not work on Windows due to an [issue in libui](https://github.com/andlabs/libui/issues/347).
|
1873
2019
|
- `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.
|
@@ -1875,6 +2021,7 @@ Learn more from data-binding usage in [Login](#login) (4 data-binding versions),
|
|
1875
2021
|
- 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.
|
1876
2022
|
- As per the code of [examples/basic_transform.rb](#basic-transform), Windows requires different ordering of transforms than Mac and Linux.
|
1877
2023
|
- `scrolling_area#scroll_to` does not seem to work on Windows and Linux, but works fine on Mac
|
2024
|
+
- When creating/showing a window other than the main window and then closing the secondary window, the entire app closes. This is a current limitation to the windowing system that should be fixed with [child window support](https://github.com/andlabs/libui/issues/137) in [libui](https://github.com/andlabs/libui)
|
1878
2025
|
|
1879
2026
|
### Original API
|
1880
2027
|
|
@@ -8435,26 +8582,26 @@ window('Login') {
|
|
8435
8582
|
}.show
|
8436
8583
|
```
|
8437
8584
|
|
8438
|
-
#### Method-Based Custom
|
8585
|
+
#### Method-Based Custom Controls
|
8439
8586
|
|
8440
8587
|
[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.
|
8441
8588
|
|
8442
|
-
This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom
|
8589
|
+
This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
|
8443
8590
|
|
8444
8591
|
The custom keywords are defined via methods (thus are "method-based").
|
8445
8592
|
|
8446
|
-
[examples/
|
8593
|
+
[examples/method_based_custom_controls.rb](examples/method_based_custom_controls.rb)
|
8447
8594
|
|
8448
8595
|
Run with this command from the root of the project if you cloned the project:
|
8449
8596
|
|
8450
8597
|
```
|
8451
|
-
ruby -r './lib/glimmer-dsl-libui' examples/
|
8598
|
+
ruby -r './lib/glimmer-dsl-libui' examples/method_based_custom_controls.rb
|
8452
8599
|
```
|
8453
8600
|
|
8454
8601
|
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
8455
8602
|
|
8456
8603
|
```
|
8457
|
-
ruby -r glimmer-dsl-libui -e "require 'examples/
|
8604
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/method_based_custom_controls'"
|
8458
8605
|
```
|
8459
8606
|
|
8460
8607
|
Mac | Windows | Linux
|
@@ -8479,13 +8626,13 @@ def form_field(model, attribute)
|
|
8479
8626
|
}
|
8480
8627
|
end
|
8481
8628
|
|
8482
|
-
def address_form(
|
8629
|
+
def address_form(address_model)
|
8483
8630
|
form {
|
8484
|
-
form_field(
|
8485
|
-
form_field(
|
8486
|
-
form_field(
|
8487
|
-
form_field(
|
8488
|
-
form_field(
|
8631
|
+
form_field(address_model, :street)
|
8632
|
+
form_field(address_model, :p_o_box)
|
8633
|
+
form_field(address_model, :city)
|
8634
|
+
form_field(address_model, :state)
|
8635
|
+
form_field(address_model, :zip_code)
|
8489
8636
|
}
|
8490
8637
|
end
|
8491
8638
|
|
@@ -8498,10 +8645,10 @@ def label_pair(model, attribute, value)
|
|
8498
8645
|
}
|
8499
8646
|
end
|
8500
8647
|
|
8501
|
-
def
|
8648
|
+
def address_view(address_model)
|
8502
8649
|
vertical_box {
|
8503
|
-
|
8504
|
-
label_pair(
|
8650
|
+
address_model.each_pair do |attribute, value|
|
8651
|
+
label_pair(address_model, attribute, value)
|
8505
8652
|
end
|
8506
8653
|
}
|
8507
8654
|
end
|
@@ -8509,7 +8656,7 @@ end
|
|
8509
8656
|
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
8510
8657
|
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
8511
8658
|
|
8512
|
-
window('Method-Based Custom
|
8659
|
+
window('Method-Based Custom Controls') {
|
8513
8660
|
margined true
|
8514
8661
|
|
8515
8662
|
horizontal_box {
|
@@ -8528,7 +8675,7 @@ window('Method-Based Custom Keyword') {
|
|
8528
8675
|
stretchy false
|
8529
8676
|
}
|
8530
8677
|
|
8531
|
-
|
8678
|
+
address_view(address1)
|
8532
8679
|
}
|
8533
8680
|
|
8534
8681
|
vertical_separator {
|
@@ -8550,7 +8697,7 @@ window('Method-Based Custom Keyword') {
|
|
8550
8697
|
stretchy false
|
8551
8698
|
}
|
8552
8699
|
|
8553
|
-
|
8700
|
+
address_view(address2)
|
8554
8701
|
}
|
8555
8702
|
}
|
8556
8703
|
}.show
|
@@ -8578,13 +8725,13 @@ def form_field(model, property)
|
|
8578
8725
|
}
|
8579
8726
|
end
|
8580
8727
|
|
8581
|
-
def address_form(
|
8728
|
+
def address_form(address_model)
|
8582
8729
|
form {
|
8583
|
-
form_field(
|
8584
|
-
form_field(
|
8585
|
-
form_field(
|
8586
|
-
form_field(
|
8587
|
-
form_field(
|
8730
|
+
form_field(address_model, :street)
|
8731
|
+
form_field(address_model, :p_o_box)
|
8732
|
+
form_field(address_model, :city)
|
8733
|
+
form_field(address_model, :state)
|
8734
|
+
form_field(address_model, :zip_code)
|
8588
8735
|
}
|
8589
8736
|
end
|
8590
8737
|
|
@@ -8600,10 +8747,10 @@ def label_pair(model, attribute, value)
|
|
8600
8747
|
end
|
8601
8748
|
end
|
8602
8749
|
|
8603
|
-
def
|
8750
|
+
def address_view(address_model)
|
8604
8751
|
vertical_box {
|
8605
|
-
|
8606
|
-
label_pair(
|
8752
|
+
address_model.each_pair do |attribute, value|
|
8753
|
+
label_pair(address_model, attribute, value)
|
8607
8754
|
end
|
8608
8755
|
}
|
8609
8756
|
end
|
@@ -8611,7 +8758,7 @@ end
|
|
8611
8758
|
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
8612
8759
|
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
8613
8760
|
|
8614
|
-
window('Method-Based Custom
|
8761
|
+
window('Method-Based Custom Controls') {
|
8615
8762
|
margined true
|
8616
8763
|
|
8617
8764
|
horizontal_box {
|
@@ -8630,7 +8777,7 @@ window('Method-Based Custom Keyword') {
|
|
8630
8777
|
stretchy false
|
8631
8778
|
}
|
8632
8779
|
|
8633
|
-
|
8780
|
+
address_view(address1)
|
8634
8781
|
}
|
8635
8782
|
|
8636
8783
|
vertical_separator {
|
@@ -8652,7 +8799,151 @@ window('Method-Based Custom Keyword') {
|
|
8652
8799
|
stretchy false
|
8653
8800
|
}
|
8654
8801
|
|
8655
|
-
|
8802
|
+
address_view(address2)
|
8803
|
+
}
|
8804
|
+
}
|
8805
|
+
}.show
|
8806
|
+
```
|
8807
|
+
|
8808
|
+
#### Class-Based Custom Controls
|
8809
|
+
|
8810
|
+
[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.
|
8811
|
+
|
8812
|
+
This example defines `form_field`, `address_form`, `label_pair`, and `address` as custom controls (keywords).
|
8813
|
+
|
8814
|
+
The custom keywords are defined via classes that include `Glimmer::LibUI::CustomControl` (thus are "class-based"), thus enabling offloading each custom control into its own file when needed for better code organization.
|
8815
|
+
|
8816
|
+
[examples/class_based_custom_controls.rb](examples/class_based_custom_controls.rb)
|
8817
|
+
|
8818
|
+
Run with this command from the root of the project if you cloned the project:
|
8819
|
+
|
8820
|
+
```
|
8821
|
+
ruby -r './lib/glimmer-dsl-libui' examples/class_based_custom_controls.rb
|
8822
|
+
```
|
8823
|
+
|
8824
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
8825
|
+
|
8826
|
+
```
|
8827
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/class_based_custom_controls'"
|
8828
|
+
```
|
8829
|
+
|
8830
|
+
Mac | Windows | Linux
|
8831
|
+
----|---------|------
|
8832
|
+
![glimmer-dsl-libui-mac-method-based-custom-keyword.png](images/glimmer-dsl-libui-mac-method-based-custom-keyword.png) | ![glimmer-dsl-libui-windows-method-based-custom-keyword.png](images/glimmer-dsl-libui-windows-method-based-custom-keyword.png) | ![glimmer-dsl-libui-linux-method-based-custom-keyword.png](images/glimmer-dsl-libui-linux-method-based-custom-keyword.png)
|
8833
|
+
|
8834
|
+
New [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) Version (with [data-binding](#data-binding)):
|
8835
|
+
|
8836
|
+
```ruby
|
8837
|
+
require 'glimmer-dsl-libui'
|
8838
|
+
require 'facets'
|
8839
|
+
|
8840
|
+
include Glimmer
|
8841
|
+
|
8842
|
+
Address = Struct.new(:street, :p_o_box, :city, :state, :zip_code)
|
8843
|
+
|
8844
|
+
class FormField
|
8845
|
+
include Glimmer::LibUI::CustomControl
|
8846
|
+
|
8847
|
+
options :model, :attribute
|
8848
|
+
|
8849
|
+
body {
|
8850
|
+
entry { |e|
|
8851
|
+
label attribute.to_s.underscore.split('_').map(&:capitalize).join(' ')
|
8852
|
+
text <=> [model, attribute]
|
8853
|
+
}
|
8854
|
+
}
|
8855
|
+
end
|
8856
|
+
|
8857
|
+
class AddressForm
|
8858
|
+
include Glimmer::LibUI::CustomControl
|
8859
|
+
|
8860
|
+
options :address
|
8861
|
+
|
8862
|
+
body {
|
8863
|
+
form {
|
8864
|
+
form_field(model: address, attribute: :street)
|
8865
|
+
form_field(model: address, attribute: :p_o_box)
|
8866
|
+
form_field(model: address, attribute: :city)
|
8867
|
+
form_field(model: address, attribute: :state)
|
8868
|
+
form_field(model: address, attribute: :zip_code)
|
8869
|
+
}
|
8870
|
+
}
|
8871
|
+
end
|
8872
|
+
|
8873
|
+
class LabelPair
|
8874
|
+
include Glimmer::LibUI::CustomControl
|
8875
|
+
|
8876
|
+
options :model, :attribute, :value
|
8877
|
+
|
8878
|
+
body {
|
8879
|
+
horizontal_box {
|
8880
|
+
label(attribute.to_s.underscore.split('_').map(&:capitalize).join(' '))
|
8881
|
+
label(value.to_s) {
|
8882
|
+
text <= [model, attribute]
|
8883
|
+
}
|
8884
|
+
}
|
8885
|
+
}
|
8886
|
+
end
|
8887
|
+
|
8888
|
+
class AddressView
|
8889
|
+
include Glimmer::LibUI::CustomControl
|
8890
|
+
|
8891
|
+
options :address
|
8892
|
+
|
8893
|
+
body {
|
8894
|
+
vertical_box {
|
8895
|
+
address.each_pair do |attribute, value|
|
8896
|
+
label_pair(model: address, attribute: attribute, value: value)
|
8897
|
+
end
|
8898
|
+
}
|
8899
|
+
}
|
8900
|
+
end
|
8901
|
+
|
8902
|
+
address1 = Address.new('123 Main St', '23923', 'Denver', 'Colorado', '80014')
|
8903
|
+
address2 = Address.new('2038 Park Ave', '83272', 'Boston', 'Massachusetts', '02101')
|
8904
|
+
|
8905
|
+
window('Class-Based Custom Keyword') {
|
8906
|
+
margined true
|
8907
|
+
|
8908
|
+
horizontal_box {
|
8909
|
+
vertical_box {
|
8910
|
+
label('Address 1') {
|
8911
|
+
stretchy false
|
8912
|
+
}
|
8913
|
+
|
8914
|
+
address_form(address: address1)
|
8915
|
+
|
8916
|
+
horizontal_separator {
|
8917
|
+
stretchy false
|
8918
|
+
}
|
8919
|
+
|
8920
|
+
label('Address 1 (Saved)') {
|
8921
|
+
stretchy false
|
8922
|
+
}
|
8923
|
+
|
8924
|
+
address_view(address: address1)
|
8925
|
+
}
|
8926
|
+
|
8927
|
+
vertical_separator {
|
8928
|
+
stretchy false
|
8929
|
+
}
|
8930
|
+
|
8931
|
+
vertical_box {
|
8932
|
+
label('Address 2') {
|
8933
|
+
stretchy false
|
8934
|
+
}
|
8935
|
+
|
8936
|
+
address_form(address: address2)
|
8937
|
+
|
8938
|
+
horizontal_separator {
|
8939
|
+
stretchy false
|
8940
|
+
}
|
8941
|
+
|
8942
|
+
label('Address 2 (Saved)') {
|
8943
|
+
stretchy false
|
8944
|
+
}
|
8945
|
+
|
8946
|
+
address_view(address: address2)
|
8656
8947
|
}
|
8657
8948
|
}
|
8658
8949
|
}.show
|
@@ -9369,6 +9660,8 @@ Snake provides an example of building a desktop application [test-first](/spec/e
|
|
9369
9660
|
|
9370
9661
|
Use arrows to move and spacebar to pause/resume.
|
9371
9662
|
|
9663
|
+
Note that Snake relies on the new [Ruby Pattern Matching feature](https://docs.ruby-lang.org/en/3.0/doc/syntax/pattern_matching_rdoc.html) available starting in Ruby 2.7 experimentally and in Ruby 3.0 officially.
|
9664
|
+
|
9372
9665
|
[examples/snake.rb](examples/snake.rb)
|
9373
9666
|
|
9374
9667
|
Run with this command from the root of the project if you cloned the project:
|
@@ -9593,6 +9886,8 @@ Snake.new.launch
|
|
9593
9886
|
|
9594
9887
|
Glimmer Tetris utilizes many small areas to represent Tetromino blocks because this ensures smaller redraws per tetromino block color change, thus achieving higher performance than redrawing one large area on every little change.
|
9595
9888
|
|
9889
|
+
Note that Tetris relies on the new [Ruby Pattern Matching feature](https://docs.ruby-lang.org/en/3.0/doc/syntax/pattern_matching_rdoc.html) available starting in Ruby 2.7 experimentally and in Ruby 3.0 officially.
|
9890
|
+
|
9596
9891
|
[examples/tetris.rb](examples/tetris.rb)
|
9597
9892
|
|
9598
9893
|
Run with this command from the root of the project if you cloned the project:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.6
|