glimmer-dsl-libui 0.10.2 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a37cb8210f604b9307693af84b47ba0e21cf2a2b1059139af1d4ffa2eb727bf8
4
- data.tar.gz: 88443d069ff718e8d28c90dbb59fc6ff5e5a05a5f3e9f5be78418d1754868769
3
+ metadata.gz: effb65102f9db3aa2977bac55edd4f67f91f1eafdae26403b8c044c714048a03
4
+ data.tar.gz: 3923d80a19afd62a0220026f33720ee953ad166880e17d60883aed53f9977d86
5
5
  SHA512:
6
- metadata.gz: 1a2e8c3f162744fe2266d66a088bceb7595b04643a141b0dd526309b6155362e24fad6c760cc8eb060030fec35352ab96b9dfad7e601e5a168271e1150aa7789
7
- data.tar.gz: 90674bbf9623bcd0e985a94824698c3117a8a70bea79f7ee81d11364054c70d57f260466123717d2f292aa50f113cfb11201580a3f74bc74400a100f9c049d6d
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.10.2
1
+ # [<img src="https://raw.githubusercontent.com/AndyObtiva/glimmer/master/images/glimmer-logo-hi-res.png" height=85 />](https://github.com/AndyObtiva/glimmer) Glimmer DSL for LibUI 0.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
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-libui.svg)](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.10.2'
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
- Require [glimmer-dsl-libui](https://rubygems.org/gems/glimmer-dsl-libui) (whether through a Ruby `require` statement or `Bundler`) and then include the `Glimmer` or `Glimmer::LibUI::Application` module to enable access to the Glimmer GUI DSL in one of multiple approaches.
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 <=> [self, :entered_text, after_write: ->(text) {puts 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 `self` to `entry` `text` property and printing text after write to the model.
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.10.2
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
+ ![glimmer-dsl-libui-mac-dynamic-form.png](/images/glimmer-dsl-libui-mac-dynamic-form.png)
233
+
234
+ ![glimmer-dsl-libui-mac-dynamic-form-filled.png](/images/glimmer-dsl-libui-mac-dynamic-form-filled.png)
235
+
236
+ ![glimmer-dsl-libui-mac-dynamic-form-summarized.png](/images/glimmer-dsl-libui-mac-dynamic-form-summarized.png)
237
+
238
+ ![glimmer-dsl-libui-mac-dynamic-form-without-some-fields.png](/images/glimmer-dsl-libui-mac-dynamic-form-without-some-fields.png)
239
+
240
+ ![glimmer-dsl-libui-mac-dynamic-form-without-some-fields-summarized.png](/images/glimmer-dsl-libui-mac-dynamic-form-without-some-fields-summarized.png)
241
+
214
242
  ## Editable Column Table
215
243
 
216
244
  [examples/editable_column_table.rb](/examples/editable_column_table.rb)
@@ -1,28 +1,35 @@
1
1
  require 'glimmer-dsl-libui'
2
2
 
3
- class ButtonCounter
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
- def launch
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 self count, converting to string on read.
17
- text <= [self, :count, on_read: ->(count) {"Count: #{count}"}]
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
- self.count += 1
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
- }.show
25
- end
31
+ }
32
+ }
26
33
  end
27
34
 
28
- ButtonCounter.new.launch
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
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
@@ -40,6 +40,7 @@ module Glimmer
40
40
  data_binding
41
41
  shine_data_binding
42
42
  property
43
+ content
43
44
  string
44
45
  operation
45
46
  control
@@ -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.10.2
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-10-28 00:00:00.000000000 Z
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