glimmer-dsl-libui 0.10.2 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +75 -9
- data/VERSION +1 -1
- data/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md +28 -0
- data/examples/button_counter.rb +18 -11
- data/examples/dynamic_form.rb +69 -0
- data/glimmer-dsl-libui.gemspec +0 -0
- data/lib/glimmer/dsl/libui/content_expression.rb +41 -0
- data/lib/glimmer/dsl/libui/dsl.rb +1 -0
- data/lib/glimmer/libui/control_proxy.rb +13 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: effb65102f9db3aa2977bac55edd4f67f91f1eafdae26403b8c044c714048a03
|
4
|
+
data.tar.gz: 3923d80a19afd62a0220026f33720ee953ad166880e17d60883aed53f9977d86
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6e123f42fd61ce49b126e7f51caf738e0c769eca12b177c879007cedaba6ec38974d16ca864e8d8922546f79cfe2dc8946056935684f0bab9d88892e425c5d79
|
7
|
+
data.tar.gz: 8d0d79d7632e30568f747a3db5d9bbdef387e854f55e0cf6559a4d8d242075bc8c1836e00e2554a30eb5eded934bb23c267a764b127cfe768c39e679ff47671e
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## 0.11.0
|
4
|
+
|
5
|
+
- Control `content` data-binding to generate nested controls dynamically based on a model attribute change
|
6
|
+
- `examples/dynamic_form.rb` to demonstrate control `content` data-binding
|
7
|
+
|
3
8
|
## 0.10.2
|
4
9
|
|
5
10
|
- In Snake example, change snake direction on key press instead of key release to be more responsive for players who are not used to releasing pressed keys quickly
|
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.
|
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.11.0
|
2
2
|
## Prerequisite-Free Ruby Desktop Development Cross-Platform Native GUI Library ([Fukuoka Award Winning](http://www.digitalfukuoka.jp/topics/187?locale=ja))
|
3
3
|
### The Quickest Way From Zero To GUI
|
4
4
|
[](http://badge.fury.io/rb/glimmer-dsl-libui)
|
@@ -431,7 +431,7 @@ gem install glimmer-dsl-libui
|
|
431
431
|
Or install via Bundler `Gemfile`:
|
432
432
|
|
433
433
|
```ruby
|
434
|
-
gem 'glimmer-dsl-libui', '~> 0.
|
434
|
+
gem 'glimmer-dsl-libui', '~> 0.11.0'
|
435
435
|
```
|
436
436
|
|
437
437
|
Test that installation worked by running the [Glimmer Meta-Example](#examples):
|
@@ -452,8 +452,19 @@ Mac | Windows | Linux
|
|
452
452
|
|
453
453
|
## Usage
|
454
454
|
|
455
|
-
|
456
|
-
|
455
|
+
Start by requiring the [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) Ruby gem, whether through a Ruby `require 'glimmer-dsl-libui` statement or `Bundler`.
|
456
|
+
|
457
|
+
Afterwards, to access the Glimmer GUI DSL:
|
458
|
+
- If you are learning/experimenting/prototyping with Glimmer DSL for LibUI, include the `Glimmer` module into the top-level scope or a Ruby class.
|
459
|
+
- If you are building a serious application, include `Glimmer::LibUI::Application` into the main view Ruby class
|
460
|
+
- If you are building a custom control, include [`Glimmer::LibUI::CustomControl`](#custom-components) into a Ruby class
|
461
|
+
- If you are building a cusotm window, include [`Glimmer::LibUI::CustomWindow`](#custom-components) into a Ruby class
|
462
|
+
- If you are building a custom shape, include [`Glimmer::LibUI::CustomShape`](#custom-components) into a Ruby class.
|
463
|
+
|
464
|
+
You may learn more about the different options above with basic examples in the following subsections: [Experimentation Usage](#experimentation-usage), [Prototyping Usage](#prototyping-usage), [Serious Usage](#serious-usage).
|
465
|
+
|
466
|
+
If you are new to [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) (beginner), after going through the subsections below, check out the RubyConf 2022 talk ["Building Native GUI Apps in Ruby"](https://andymaleh.blogspot.com/2023/02/rubyconf-2022-talk-video-for-building.html), [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts), [Glimmer Command](#glimmer-command) (just the basics, how to run an app, and how to run examples to start), [Girb](#girb-glimmer-irb) and [Examples](#examples) to quickly learn through copy/paste. It is very important for beginners to go through all the [Examples](#examples) from the most basic to the most advanced while reading the README topics that relate to the examples. You may refer to the [API](#api) on once you have gotten your feet wet with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) and need more detailed reference information.
|
467
|
+
|
457
468
|
### Experimentation Usage
|
458
469
|
|
459
470
|
For experimenting and learning, add `include Glimmer` into the top-level main object and start using the Glimmer GUI DSL directly.
|
@@ -534,9 +545,6 @@ SomeGlimmerApp.launch
|
|
534
545
|
|
535
546
|
(note: `Glimmer::LibUI::Application` is an alias for `Glimmer::LibUI::CustomWindow` since that is what it represents)
|
536
547
|
|
537
|
-
If you are new to [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui), check out the [Glimmer GUI DSL Concepts](#glimmer-gui-dsl-concepts), [Glimmer Command](#glimmer-command), [Girb](#girb-glimmer-irb) and [Examples](#examples) to quickly learn through copy/paste. You may refer to the [API](#api) later on once you have gotten your feet wet with [Glimmer DSL for LibUI](https://rubygems.org/gems/glimmer-dsl-libui) and need more detailed reference information.
|
538
|
-
|
539
|
-
|
540
548
|
## Glimmer Command
|
541
549
|
|
542
550
|
The `glimmer` command allows you to conveniently run applications (`glimmer app_path`), run examples (`glimmer examples`), and scaffold applications (`glimmer "scaffold[app_name]"`).
|
@@ -1591,6 +1599,43 @@ button('greet') { |b|
|
|
1591
1599
|
}
|
1592
1600
|
```
|
1593
1601
|
|
1602
|
+
If there is ever a need to add more content to a control, you can re-open its content with the `control.content { ... }` method.
|
1603
|
+
|
1604
|
+
Example:
|
1605
|
+
|
1606
|
+
```ruby
|
1607
|
+
box1 = vertical_box {
|
1608
|
+
label('First Name')
|
1609
|
+
}
|
1610
|
+
# re-open content of box1 and add another control
|
1611
|
+
box1.content {
|
1612
|
+
entry {
|
1613
|
+
text 'fill in your first name'
|
1614
|
+
}
|
1615
|
+
}
|
1616
|
+
```
|
1617
|
+
|
1618
|
+
You can also use [data-binding](#data-binding) with content blocks to generate content dynamically based on changes in a model attribute. The only difference in syntax in this case would be to wrap the content with an explicit `content(*binding_args) { ... }` block that includes data-binding arguments for a model attribute.
|
1619
|
+
|
1620
|
+
Example:
|
1621
|
+
|
1622
|
+
```ruby
|
1623
|
+
form {
|
1624
|
+
stretchy false
|
1625
|
+
|
1626
|
+
content(@user, :customizable_attributes) {
|
1627
|
+
@user.customizable_attributes.each do |attribute|
|
1628
|
+
entry {
|
1629
|
+
label attribute.to_s.split('_').map(&:capitalize).join(' ')
|
1630
|
+
text <=> [@user, attribute]
|
1631
|
+
}
|
1632
|
+
end
|
1633
|
+
}
|
1634
|
+
}
|
1635
|
+
```
|
1636
|
+
|
1637
|
+
The form above will only display fields for a model's customizable attributes, so if they change, the form content will change too. Learn more at the [Dynamic Form](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#dynamic-form) example.
|
1638
|
+
|
1594
1639
|
**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.
|
1595
1640
|
|
1596
1641
|
**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).
|
@@ -3387,11 +3432,11 @@ Another example of bidirectional data-binding with an option:
|
|
3387
3432
|
|
3388
3433
|
```ruby
|
3389
3434
|
entry {
|
3390
|
-
text <=> [
|
3435
|
+
text <=> [model, :entered_text, after_write: ->(text) {puts text}]
|
3391
3436
|
}
|
3392
3437
|
```
|
3393
3438
|
|
3394
|
-
That is data-binding `entered_text` attribute on `
|
3439
|
+
That is data-binding `entered_text` attribute on `model` to `entry` `text` property and printing text after write to the model.
|
3395
3440
|
|
3396
3441
|
##### Table Data-Binding
|
3397
3442
|
|
@@ -3573,6 +3618,27 @@ window {
|
|
3573
3618
|
|
3574
3619
|
That is data-binding the `window` `title` property to the `score` attribute of a `@game`, but converting on read from the Model to a `String`.
|
3575
3620
|
|
3621
|
+
You can also use unidirectional [data-binding](#data-binding) with content blocks to generate content dynamically based on changes in a model attribute. The only difference in syntax in this case would be to wrap the content with an explicit `content(*binding_args) { ... }` block that includes data-binding arguments for a model attribute.
|
3622
|
+
|
3623
|
+
Example:
|
3624
|
+
|
3625
|
+
```ruby
|
3626
|
+
form {
|
3627
|
+
stretchy false
|
3628
|
+
|
3629
|
+
content(@user, :customizable_attributes) {
|
3630
|
+
@user.customizable_attributes.each do |attribute|
|
3631
|
+
entry {
|
3632
|
+
label attribute.to_s.split('_').map(&:capitalize).join(' ')
|
3633
|
+
text <=> [@user, attribute]
|
3634
|
+
}
|
3635
|
+
end
|
3636
|
+
}
|
3637
|
+
}
|
3638
|
+
```
|
3639
|
+
|
3640
|
+
The form above will only display fields for a model's customizable attributes, so if they change, the form content will change too. Learn more at the [Dynamic Form](/docs/examples/GLIMMER-DSL-LIBUI-ADVANCED-EXAMPLES.md#dynamic-form) example.
|
3641
|
+
|
3576
3642
|
#### Data-Binding API
|
3577
3643
|
|
3578
3644
|
To summarize the data-binding API:
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.11.0
|
@@ -8,6 +8,7 @@
|
|
8
8
|
- [CPU Percentage](#cpu-percentage)
|
9
9
|
- [Custom Draw Text](#custom-draw-text)
|
10
10
|
- [Dynamic Area](#dynamic-area)
|
11
|
+
- [Dynamic Form](#dynamic-form)
|
11
12
|
- [Editable Column Table](#editable-column-table)
|
12
13
|
- [Editable Table](#editable-table)
|
13
14
|
- [Form Table](#form-table)
|
@@ -211,6 +212,33 @@ Version 4 (declarative stable `path` approach without [data-binding](#data-bindi
|
|
211
212
|
|
212
213
|
[examples/dynamic_area4.rb](/examples/dynamic_area4.rb)
|
213
214
|
|
215
|
+
|
216
|
+
## Dynamic Form
|
217
|
+
|
218
|
+
[examples/dynamic_form.rb](/examples/dynamic_form.rb)
|
219
|
+
|
220
|
+
Run with this command from the root of the project if you cloned the project:
|
221
|
+
|
222
|
+
```
|
223
|
+
ruby -r './lib/glimmer-dsl-libui' examples/dynamic_form.rb
|
224
|
+
```
|
225
|
+
|
226
|
+
Run with this command if you installed the [Ruby gem](https://rubygems.org/gems/glimmer-dsl-libui):
|
227
|
+
|
228
|
+
```
|
229
|
+
ruby -r glimmer-dsl-libui -e "require 'examples/dynamic_form'"
|
230
|
+
```
|
231
|
+
|
232
|
+

|
233
|
+
|
234
|
+

|
235
|
+
|
236
|
+

|
237
|
+
|
238
|
+

|
239
|
+
|
240
|
+

|
241
|
+
|
214
242
|
## Editable Column Table
|
215
243
|
|
216
244
|
[examples/editable_column_table.rb](/examples/editable_column_table.rb)
|
data/examples/button_counter.rb
CHANGED
@@ -1,28 +1,35 @@
|
|
1
1
|
require 'glimmer-dsl-libui'
|
2
2
|
|
3
|
-
class
|
4
|
-
include Glimmer
|
5
|
-
|
3
|
+
class Counter
|
6
4
|
attr_accessor :count
|
7
|
-
|
5
|
+
|
8
6
|
def initialize
|
9
7
|
self.count = 0
|
10
8
|
end
|
9
|
+
end
|
11
10
|
|
12
|
-
|
11
|
+
class ButtonCounter
|
12
|
+
include Glimmer::LibUI::Application
|
13
|
+
|
14
|
+
before_body do
|
15
|
+
@counter = Counter.new
|
16
|
+
end
|
17
|
+
|
18
|
+
body {
|
13
19
|
window('Hello, Button!', 190, 20) {
|
14
20
|
vertical_box {
|
15
21
|
button {
|
16
|
-
# data-bind button text to
|
17
|
-
text <= [
|
22
|
+
# data-bind button text to @counter count, converting to string on read from model.
|
23
|
+
text <= [@counter, :count, on_read: ->(count) {"Count: #{count}"}]
|
18
24
|
|
19
25
|
on_clicked do
|
20
|
-
|
26
|
+
# This change will automatically propagate to button text through data-binding above
|
27
|
+
@counter.count += 1
|
21
28
|
end
|
22
29
|
}
|
23
30
|
}
|
24
|
-
}
|
25
|
-
|
31
|
+
}
|
32
|
+
}
|
26
33
|
end
|
27
34
|
|
28
|
-
ButtonCounter.
|
35
|
+
ButtonCounter.launch
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'glimmer-dsl-libui'
|
2
|
+
|
3
|
+
class User
|
4
|
+
ALL_ATTRIBUTES = [:first_name, :last_name, :email, :street, :city, :state, :zip_code, :country]
|
5
|
+
|
6
|
+
attr_accessor :customizable_attributes, *ALL_ATTRIBUTES
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
# allow customizing all attributes by default
|
10
|
+
self.customizable_attributes = ALL_ATTRIBUTES.dup
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class DynamicForm
|
15
|
+
include Glimmer::LibUI::Application
|
16
|
+
|
17
|
+
before_body do
|
18
|
+
@user = User.new
|
19
|
+
end
|
20
|
+
|
21
|
+
body {
|
22
|
+
window('Dynamic Form') {
|
23
|
+
margined true
|
24
|
+
|
25
|
+
vertical_box {
|
26
|
+
horizontal_box {
|
27
|
+
User::ALL_ATTRIBUTES.each do |attribute|
|
28
|
+
checkbox(attribute.to_s) {
|
29
|
+
checked <=> [@user, :customizable_attributes,
|
30
|
+
on_read: -> (attributes) { @user.customizable_attributes.include?(attribute) },
|
31
|
+
on_write: -> (checked_value) {
|
32
|
+
if checked_value
|
33
|
+
@user.customizable_attributes.push(attribute)
|
34
|
+
else
|
35
|
+
@user.customizable_attributes.delete(attribute)
|
36
|
+
end
|
37
|
+
@user.customizable_attributes.sort_by {|attribute| User::ALL_ATTRIBUTES.index(attribute)}
|
38
|
+
},
|
39
|
+
]
|
40
|
+
}
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
form {
|
45
|
+
stretchy false
|
46
|
+
|
47
|
+
# Control content data-binding allows dynamically changing content based on changes in a model attribute
|
48
|
+
content(@user, :customizable_attributes) {
|
49
|
+
@user.customizable_attributes.each do |attribute|
|
50
|
+
entry {
|
51
|
+
label attribute.to_s.split('_').map(&:capitalize).join(' ')
|
52
|
+
text <=> [@user, attribute]
|
53
|
+
}
|
54
|
+
end
|
55
|
+
}
|
56
|
+
}
|
57
|
+
|
58
|
+
button('Summarize') {
|
59
|
+
on_clicked do
|
60
|
+
summary = @user.customizable_attributes.map { |attribute| @user.send(attribute) }.join(', ')
|
61
|
+
msg_box('Summary', summary)
|
62
|
+
end
|
63
|
+
}
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
DynamicForm.launch
|
data/glimmer-dsl-libui.gemspec
CHANGED
Binary file
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# Copyright (c) 2021-2023 Andy Maleh
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
# a copy of this software and associated documentation files (the
|
5
|
+
# "Software"), to deal in the Software without restriction, including
|
6
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
# the following conditions:
|
10
|
+
#
|
11
|
+
# The above copyright notice and this permission notice shall be
|
12
|
+
# included in all copies or substantial portions of the Software.
|
13
|
+
#
|
14
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
21
|
+
|
22
|
+
require 'glimmer/dsl/static_expression'
|
23
|
+
|
24
|
+
module Glimmer
|
25
|
+
module DSL
|
26
|
+
module Libui
|
27
|
+
class ContentExpression < StaticExpression
|
28
|
+
def can_interpret?(parent, keyword, *args, &block)
|
29
|
+
keyword == 'content' &&
|
30
|
+
block_given? &&
|
31
|
+
args.size > 0 &&
|
32
|
+
parent&.respond_to?(:bind_content)
|
33
|
+
end
|
34
|
+
|
35
|
+
def interpret(parent, keyword, *args, &block)
|
36
|
+
parent.bind_content(*args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -384,6 +384,19 @@ module Glimmer
|
|
384
384
|
Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Libui::ControlExpression.new, @keyword, {post_add_content: @content_added}, &block)
|
385
385
|
end
|
386
386
|
|
387
|
+
# Data-binds the generation of nested content to a model/property (in binding args)
|
388
|
+
# consider providing an option to avoid initial rendering without any changes happening
|
389
|
+
def bind_content(*binding_args, &content_block)
|
390
|
+
# TODO in the future, consider optimizing code by diffing content if that makes sense
|
391
|
+
content_binding_work = proc do |*values|
|
392
|
+
children.dup.each { |child| child.destroy }
|
393
|
+
content(&content_block)
|
394
|
+
end
|
395
|
+
content_binding_observer = Glimmer::DataBinding::Observer.proc(&content_binding_work)
|
396
|
+
content_binding_observer.observe(*binding_args)
|
397
|
+
content_binding_work.call # TODO inspect if we need to pass args here (from observed attributes) [but it's simpler not to pass anything at first]
|
398
|
+
end
|
399
|
+
|
387
400
|
private
|
388
401
|
|
389
402
|
def build_control
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer-dsl-libui
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.11.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Maleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-05 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: glimmer
|
@@ -437,6 +437,7 @@ files:
|
|
437
437
|
- examples/dynamic_area2.rb
|
438
438
|
- examples/dynamic_area3.rb
|
439
439
|
- examples/dynamic_area4.rb
|
440
|
+
- examples/dynamic_form.rb
|
440
441
|
- examples/editable_column_table.rb
|
441
442
|
- examples/editable_table.rb
|
442
443
|
- examples/font_button.rb
|
@@ -500,6 +501,7 @@ files:
|
|
500
501
|
- lib/glimmer-dsl-libui/ext/rouge/theme/glimmer.rb
|
501
502
|
- lib/glimmer/Rakefile
|
502
503
|
- lib/glimmer/dsl/libui/bind_expression.rb
|
504
|
+
- lib/glimmer/dsl/libui/content_expression.rb
|
503
505
|
- lib/glimmer/dsl/libui/control_expression.rb
|
504
506
|
- lib/glimmer/dsl/libui/custom_control_expression.rb
|
505
507
|
- lib/glimmer/dsl/libui/custom_shape_expression.rb
|