glimmer-dsl-tk 0.0.2 → 0.0.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3f1d6377c707655c2ae9b49e60f3412e31ef27869aefee78492c30feeba21ef5
4
- data.tar.gz: 02bef3e445ba3e3fce1633d89af37c66860dadbe2ae5aa4472c6dbcbbcd3602c
3
+ metadata.gz: 2f57063e358754b89fbd0a57cb118fb9764595a8965eba5884aeb2988ab2d6b1
4
+ data.tar.gz: f55e3fc96a9388dd218d7c674cdf1585831c44a3f6412741e0e65f53185e2d98
5
5
  SHA512:
6
- metadata.gz: 30f1946ae7324c585309f0986f5a320a427250ca2e6925999856a2bd07958ab420b8ba67e6227d1991f7ab68e43627337fc6644f4b97d81899a7dafaccc8db61
7
- data.tar.gz: cba5744288234fe059aa9c5efe68d9c0404d81211d63096b07a45f89474fcaaa4c57b7faccb018003394c8a50a9750c5bfc4d26f17a5ee16b74f40f5dac60170
6
+ metadata.gz: 272ea64f1a285ebcef28edb4bc2092f1c8ac78bf9ea36e086f1555ba47a454cb09b8c698e06d5ed0aa76abab1fb7946535bae139e9d444663de8185483bba7f4
7
+ data.tar.gz: e6fa9a29aa2b6a7c096007489a0b0e07d0658f6c79ef836a4cbd4d2fc1300330ef19b97bb6ea62e5d7cac9e00f5a2fca32ee745b167cc8f0d826cacd98a13b1c
data/README.md CHANGED
@@ -1,19 +1,24 @@
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 Tk 0.0.2 (Desktop GUI)
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 Tk 0.0.3 (Desktop GUI)
2
2
  [![Gem Version](https://badge.fury.io/rb/glimmer-dsl-tk.svg)](http://badge.fury.io/rb/glimmer-dsl-tk)
3
+ [![Maintainability](https://api.codeclimate.com/v1/badges/ce2853efdbecf6ebdc73/maintainability)](https://codeclimate.com/github/AndyObtiva/glimmer-dsl-tk/maintainability)
3
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)
4
5
 
5
6
  [Glimmer](https://github.com/AndyObtiva/glimmer) DSL for [Tk](https://www.tcl.tk/) enables desktop development with [Glimmer](https://github.com/AndyObtiva/glimmer).
6
7
 
7
8
  [Tcl/Tk](https://www.tcl.tk/) has evolved into a practical desktop GUI toolkit due to gaining true native widgets on Mac, Windows, and Linux in [Tk version 8.5](https://www.tcl.tk/software/tcltk/8.5.html#:~:text=Highlights%20of%20Tk%208.5&text=Font%20rendering%3A%20Now%20uses%20anti,and%20window%20layout%2C%20and%20more.).
8
9
 
9
- Additionally, Ruby 3.0 supports truly parallel multi-threading, making both [MRI](https://github.com/ruby/ruby) and [Tk](https://www.tcl.tk/) finally viable for support in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library).
10
+ Additionally, [Ruby](https://www.ruby-lang.org/en/) 3.0 Ractor (formerly known as [Guilds](https://olivierlacan.com/posts/concurrency-in-ruby-3-with-guilds/)) supports truly parallel multi-threading, making both [MRI](https://github.com/ruby/ruby) and [Tk](https://www.tcl.tk/) finally viable for support in [Glimmer](https://github.com/AndyObtiva/glimmer) (Ruby Desktop Development GUI Library) as an alternative to [JRuby on SWT](https://github.com/AndyObtiva/glimmer-dsl-swt).
11
+
12
+ The trade-off is that while [SWT](https://www.eclipse.org/swt/) provides a plethora of high quality reusable widgets for the Enterprise (such as [Nebula](https://www.eclipse.org/nebula/)), [Tk](https://www.tcl.tk/) enables very fast app startup time via [MRI Ruby](https://www.ruby-lang.org/en/).
10
13
 
11
14
  [Glimmer](https://github.com/AndyObtiva/glimmer) provides a DSL to enable more productive desktop development in Ruby with:
12
15
  - Declarative DSL syntax that visually maps to the GUI widget hierarchy
13
16
  - Convention over configuration via smart defaults and automation of low-level details
14
17
  - Requiring the least amount of syntax possible to build GUI
18
+ - Bidirectional Data-Binding to declaratively wire and automatically synchronize GUI with Business Models
15
19
  - Scaffolding for new custom widgets, apps, and gems
16
20
  - Native-Executable packaging on Mac, Windows, and Linux
21
+ - Custom Widget support
17
22
 
18
23
  **Hello, World!**
19
24
 
@@ -30,7 +35,7 @@ root {
30
35
  Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
31
36
 
32
37
  ```
33
- ruby -e "require '../samples/hello/hello_world.rb'"
38
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
34
39
  ```
35
40
 
36
41
  Glimmer app:
@@ -38,7 +43,7 @@ Glimmer app:
38
43
  ![glimmer dsl tk screenshot sample hello world](images/glimmer-dsl-tk-screenshot-sample-hello-world.png)
39
44
 
40
45
  Other [Glimmer](https://github.com/AndyObtiva/glimmer) DSL gems:
41
- - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (Desktop GUI via JRuby on SWT)
46
+ - [glimmer-dsl-swt](https://github.com/AndyObtiva/glimmer-dsl-swt): Glimmer DSL for SWT (JRuby Desktop GUI)
42
47
  - [glimmer-dsl-opal](https://github.com/AndyObtiva/glimmer-dsl-opal): Glimmer DSL for Opal (Web GUI Adapter for Desktop Apps)
43
48
  - [glimmer-dsl-xml](https://github.com/AndyObtiva/glimmer-dsl-xml): Glimmer DSL for XML (& HTML)
44
49
  - [glimmer-dsl-css](https://github.com/AndyObtiva/glimmer-dsl-css): Glimmer DSL for CSS (Cascading Style Sheets)
@@ -61,6 +66,8 @@ Afterwards, if you open `irb`, you should be able to run `require 'tk'` successf
61
66
 
62
67
  Run this command to install directly:
63
68
  ```
69
+ gem install logging
70
+ gem install super_module
64
71
  gem install glimmer-dsl-tk
65
72
  ```
66
73
 
@@ -68,7 +75,9 @@ gem install glimmer-dsl-tk
68
75
 
69
76
  Add the following to `Gemfile`:
70
77
  ```
71
- gem 'glimmer-dsl-tk', '~> 0.0.2'
78
+ gem 'logging'
79
+ gem 'super_module'
80
+ gem 'glimmer-dsl-tk', '~> 0.0.3'
72
81
  ```
73
82
 
74
83
  And, then run:
@@ -99,7 +108,7 @@ The Glimmer GUI DSL provides a declarative syntax for [Tk](https://www.tcl.tk/)
99
108
  The Glimmer GUI DSL follows these simple concepts in mapping from Tk syntax:
100
109
  - **Widget Keyword**: Any Tk widget (e.g. `Tk::Tile::Label`) or toplevel window (e.g. `TkRoot`) may be declared by its lower-case underscored name without the namespace (e.g. `label` or `root`). This is called a keyword and is represented in the Glimmer GUI DSL by a Ruby method behind the scenes.
101
110
  - **Args**: Any keyword method may optionally take arguments surrounded by parentheses (e.g. a `frame` nested under a `notebook` may receive tab options like `frame(text: 'Users')`, which gets used behind the scenes by Tk code such as `notebook.add tab, text: 'Users'`)
102
- - **Content/Options Block**: Any keyword may optionally be followed by a Ruby curly-brace block
111
+ - **Content/Options Block**: Any keyword may optionally be followed by a Ruby curly-brace block containing nested widgets (content) and attributes (options). Attributes are simply Tk option keywords followed by arguments and no block (e.g. `title 'Hello, World!'` under a `root`)
103
112
 
104
113
  Example of an app written in [Tk](https://www.tcl.tk/) imperative syntax:
105
114
 
@@ -144,6 +153,46 @@ root {
144
153
  }.open
145
154
  ```
146
155
 
156
+ ### Bidirectional Data-Binding
157
+
158
+ Glimmer supports bidirectional data-binding via the `bind` keyword, which takes a model and an attribute.
159
+
160
+ Example:
161
+
162
+ This assumes a `Person` model with a `country` attribute representing their current country and a `country_options` attribute representing available options for the country attribute.
163
+
164
+ ```ruby
165
+ combobox { |proxy|
166
+ state 'readonly'
167
+ text bind(person, :country)
168
+ }
169
+ ```
170
+
171
+ That binds the `text` selection of the `combobox` to the `country` property on the `person` model.
172
+
173
+ It automatically handles all the Tk plumbing behind the scenes, such as using `TkVariable` and setting `combobox` `values` from `person.country_options` by convention (attribute_name + "_options").
174
+
175
+ More details can be found in the [Hello, Combo!](#hello-combo) sample below.
176
+
177
+ ### Command
178
+
179
+ Buttons can set a `command` option to trigger when the user clicks the button. This may be done with the `command` keyword, passing in a block directly (no need for `proc` as per Tk)
180
+
181
+ Example:
182
+
183
+ ```ruby
184
+ button { |proxy|
185
+ text "Reset Selection"
186
+ command {
187
+ person.reset_country
188
+ }
189
+ }
190
+ ```
191
+
192
+ This resets the person country.
193
+
194
+ More details can be found in the [Hello, Combo!](#hello-combo) sample below.
195
+
147
196
  ## Samples
148
197
 
149
198
  ### Hello, World!
@@ -151,6 +200,8 @@ root {
151
200
  Glimmer code (from [samples/hello/hello_world.rb](samples/hello/hello_world.rb)):
152
201
 
153
202
  ```ruby
203
+ include Glimmer
204
+
154
205
  root {
155
206
  label {
156
207
  text 'Hello, World!'
@@ -161,7 +212,7 @@ root {
161
212
  Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
162
213
 
163
214
  ```
164
- ruby -e "gem 'glimmer-dsl-tk'; require '../samples/hello/hello_world.rb'"
215
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_world.rb'"
165
216
  ```
166
217
 
167
218
  Glimmer app:
@@ -173,6 +224,8 @@ Glimmer app:
173
224
  Glimmer code (from [samples/hello/hello_tab.rb](samples/hello/hello_tab.rb)):
174
225
 
175
226
  ```ruby
227
+ include Glimmer
228
+
176
229
  root {
177
230
  title 'Hello, Tab!'
178
231
 
@@ -195,7 +248,7 @@ root {
195
248
  Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
196
249
 
197
250
  ```
198
- ruby -e "gem 'glimmer-dsl-tk'; require '../samples/hello/hello_tab.rb'"
251
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_tab.rb'"
199
252
  ```
200
253
 
201
254
  Glimmer app:
@@ -203,6 +256,41 @@ Glimmer app:
203
256
  ![glimmer dsl tk screenshot sample hello tab English](images/glimmer-dsl-tk-screenshot-sample-hello-tab-english.png)
204
257
  ![glimmer dsl tk screenshot sample hello tab French](images/glimmer-dsl-tk-screenshot-sample-hello-tab-french.png)
205
258
 
259
+ ### Hello, Combo!
260
+
261
+ Glimmer code (from [samples/hello/hello_combo.rb](samples/hello/hello_combo.rb)):
262
+
263
+ ```ruby
264
+ # ... more code precedes
265
+ root {
266
+ title 'Hello, Combo!'
267
+
268
+ combobox { |proxy|
269
+ state 'readonly'
270
+ text bind(person, :country)
271
+ }
272
+
273
+ button { |proxy|
274
+ text "Reset Selection"
275
+ command {
276
+ person.reset_country
277
+ }
278
+ }
279
+ }.open
280
+ # ... more code follows
281
+ ```
282
+
283
+ Run (with the [glimmer-dsl-tk](https://rubygems.org/gems/glimmer-dsl-tk) gem installed):
284
+
285
+ ```
286
+ ruby -r glimmer-dsl-tk -e "require '../samples/hello/hello_combo.rb'"
287
+ ```
288
+
289
+ Glimmer app:
290
+
291
+ ![glimmer dsl tk screenshot sample hello combo](images/glimmer-dsl-tk-screenshot-sample-hello-combo.png)
292
+ ![glimmer dsl tk screenshot sample hello combo dropdown](images/glimmer-dsl-tk-screenshot-sample-hello-combo-dropdown.png)
293
+
206
294
  ## Help
207
295
 
208
296
  ### Issues
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.2
1
+ 0.0.3
@@ -23,9 +23,9 @@ $LOAD_PATH.unshift(File.expand_path('..', __FILE__))
23
23
 
24
24
  # External requires
25
25
  require 'glimmer'
26
- require 'logging'
26
+ # require 'logging'
27
27
  require 'puts_debuggerer' if ENV['pd'].to_s.downcase == 'true'
28
- require 'super_module'
28
+ # require 'super_module'
29
29
  require 'tk'
30
30
 
31
31
  # Internal requires
@@ -0,0 +1,28 @@
1
+ require 'glimmer/data_binding/observable'
2
+ require 'glimmer/data_binding/observer'
3
+
4
+ module Glimmer
5
+ module DataBinding
6
+ module Tk
7
+ class WidgetBinding
8
+ include Glimmer
9
+ include Observable
10
+ include Observer
11
+
12
+ attr_reader :widget, :attribute
13
+ def initialize(widget, attribute)
14
+ @widget = widget
15
+ @attribute = attribute
16
+ end
17
+
18
+ def call(value)
19
+ @widget.set_attribute(@attribute, value) unless evaluate_attribute == value
20
+ end
21
+
22
+ def evaluate_attribute
23
+ @widget.get_attribute(@attribute)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,36 @@
1
+ require 'glimmer/dsl/static_expression'
2
+ require 'glimmer/data_binding/model_binding'
3
+
4
+ module Glimmer
5
+ module DSL
6
+ module Tk
7
+ # Responsible for setting up the return value of the bind keyword (command symbol)
8
+ # as a ModelBinding. It is then used by another command handler like
9
+ # DataBindingCommandHandler for text and other attributes
10
+ class BindExpression < StaticExpression
11
+ def can_interpret?(parent, keyword, *args, &block)
12
+ (
13
+ keyword == 'bind' and
14
+ (
15
+ (
16
+ (args.size == 2) and
17
+ textual?(args[1])
18
+ ) ||
19
+ (
20
+ (args.size == 3) and
21
+ textual?(args[1]) and
22
+ (args[2].is_a?(Hash))
23
+ )
24
+ )
25
+ )
26
+ end
27
+
28
+ def interpret(parent, keyword, *args, &block)
29
+ binding_options = args[2] || {}
30
+ binding_options[:on_read] = binding_options.delete(:on_read) || binding_options.delete('on_read') || block
31
+ DataBinding::ModelBinding.new(args[0], args[1].to_s, binding_options)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2020 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/expression'
23
+
24
+ module Glimmer
25
+ module DSL
26
+ module Tk
27
+ class BlockAttributeExpression < Expression
28
+ def can_interpret?(parent, keyword, *args, &block)
29
+ block_given? and
30
+ args.size == 0 and
31
+ parent.respond_to?("#{keyword}_block=")
32
+ end
33
+
34
+ def interpret(parent, keyword, *args, &block)
35
+ parent.send("#{keyword}_block=", block)
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,32 @@
1
+ require 'glimmer/dsl/expression'
2
+ require 'glimmer/data_binding/model_binding'
3
+ require 'glimmer/data_binding/tk/widget_binding'
4
+
5
+ module Glimmer
6
+ module DSL
7
+ module Tk
8
+ # Responsible for wiring two-way data-binding.
9
+ # Does so by using the output of the bind(model, property) command in the form
10
+ # of a ModelBinding, which is then connected to an anonymous widget observer
11
+ #
12
+ # Depends on BindCommandHandler
13
+ class DataBindingExpression < Expression
14
+ def can_interpret?(parent, keyword, *args, &block)
15
+ args.size == 1 and
16
+ args[0].is_a?(DataBinding::ModelBinding)
17
+ end
18
+
19
+ def interpret(parent, keyword, *args, &block)
20
+ model_binding = args[0]
21
+ widget_binding_parameters = [parent, keyword]
22
+ widget_binding = DataBinding::Tk::WidgetBinding.new(*widget_binding_parameters)
23
+ widget_binding.call(model_binding.evaluate_property)
24
+ #TODO make this options observer dependent and all similar observers in widget specific data binding handlers
25
+ widget_binding.observe(model_binding)
26
+ # TODO simplify this logic and put it where it belongs
27
+ parent.add_observer(model_binding, keyword) if parent.respond_to?(:add_observer, [model_binding, keyword])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -36,6 +36,8 @@ module Glimmer
36
36
  Engine.add_dynamic_expressions(
37
37
  Tk,
38
38
  %w[
39
+ data_binding
40
+ block_attribute
39
41
  attribute
40
42
  widget
41
43
  ]
@@ -33,7 +33,7 @@ module Glimmer
33
33
 
34
34
  def can_interpret?(parent, keyword, *args, &block)
35
35
  !EXCLUDED_KEYWORDS.include?(keyword) and
36
- parent.respond_to?(:tk_widget) and
36
+ parent.respond_to?(:tk) and
37
37
  Glimmer::Tk::WidgetProxy.widget_exists?(keyword)
38
38
  end
39
39
 
@@ -54,3 +54,4 @@ end
54
54
  require 'glimmer/tk/widget_proxy'
55
55
  require 'glimmer/tk/notebook_proxy'
56
56
  require 'glimmer/tk/frame_proxy'
57
+ require 'glimmer/tk/button_proxy'
@@ -0,0 +1,35 @@
1
+ # Copyright (c) 2020 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/tk/widget_proxy'
23
+
24
+ module Glimmer
25
+ module Tk
26
+ # Proxy for Tk::Tile::Button
27
+ #
28
+ # Follows the Proxy Design Pattern
29
+ class ButtonProxy < WidgetProxy
30
+ def command_block=(proc)
31
+ tk.command(proc)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -38,7 +38,7 @@ module Glimmer
38
38
 
39
39
  def post_initialize_child(child)
40
40
  @tab_proxies << child
41
- @tk_widget.add child.tk_widget, child.tab_options
41
+ @tk.add child.tk, child.tab_options
42
42
  end
43
43
  end
44
44
  end
@@ -29,7 +29,7 @@ module Glimmer
29
29
  class RootProxy < WidgetProxy
30
30
 
31
31
  def initialize(*args)
32
- @tk_widget = ::TkRoot.new
32
+ @tk = ::TkRoot.new
33
33
  end
34
34
 
35
35
  def open
@@ -37,7 +37,7 @@ module Glimmer
37
37
  end
38
38
 
39
39
  def content(&block)
40
- Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::SWT::RootExpression.new, &block)
40
+ Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::RootExpression.new, &block)
41
41
  end
42
42
 
43
43
  # Starts Tk mainloop
@@ -25,17 +25,11 @@ module Glimmer
25
25
  #
26
26
  # Follows the Proxy Design Pattern
27
27
  class WidgetProxy
28
- attr_reader :parent_proxy, :tk_widget, :args
28
+ attr_reader :parent_proxy, :tk, :args
29
29
 
30
30
  DEFAULT_INITIALIZERS = {
31
- 'label' => lambda do |widget|
32
- widget.grid
33
- end,
34
- 'frame' => lambda do |widget|
35
- widget.grid
36
- end,
37
- 'notebook' => lambda do |widget|
38
- widget.grid
31
+ 'combobox' => lambda do |tk|
32
+ tk.textvariable = ::TkVariable.new
39
33
  end,
40
34
  }
41
35
 
@@ -61,8 +55,15 @@ module Glimmer
61
55
  @parent_proxy = parent_proxy
62
56
  @args = args
63
57
  tk_widget_class = self.class.tk_widget_class_for(underscored_widget_name)
64
- @tk_widget = tk_widget_class.new(@parent_proxy.tk_widget, *args)
65
- DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@tk_widget)
58
+ @tk = tk_widget_class.new(@parent_proxy.tk, *args)
59
+ begin
60
+ # a common widget initializer
61
+ @tk.grid
62
+ rescue => e
63
+ # catching error just in case a widget doesn't support it
64
+ Glimmer::Config.logger.debug e.full_message
65
+ end
66
+ DEFAULT_INITIALIZERS[underscored_widget_name]&.call(@tk)
66
67
  @parent_proxy.post_initialize_child(self)
67
68
  end
68
69
 
@@ -97,7 +98,7 @@ module Glimmer
97
98
  result = nil
98
99
  begin
99
100
  # TK Widget currently doesn't support respond_to? properly, so I have to resort to this trick for now
100
- @tk_widget.send(attribute_setter(attribute_name), @tk_widget.send(attribute_name))
101
+ @tk.send(attribute_setter(attribute_name), @tk.send(attribute_name))
101
102
  result = true
102
103
  rescue => e
103
104
  result = false
@@ -110,35 +111,77 @@ module Glimmer
110
111
  end
111
112
 
112
113
  def set_attribute(attribute_name, *args)
113
- if tk_widget_has_attribute?(attribute_name)
114
- @tk_widget.send(attribute_setter(attribute_name), *args) unless @tk_widget.send(attribute_name) == args.first
114
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute_name.to_s]
115
+ if widget_custom_attribute
116
+ widget_custom_attribute[:setter][:invoker].call(@tk, args)
117
+ elsif tk_widget_has_attribute?(attribute_name)
118
+ @tk.send(attribute_setter(attribute_name), *args) unless @tk.send(attribute_name) == args.first
115
119
  else
116
120
  send(attribute_setter(attribute_name), args)
117
121
  end
118
122
  end
119
123
 
120
124
  def get_attribute(attribute_name)
121
- @tk_widget.send(attribute_name)
125
+ widget_custom_attribute = widget_custom_attribute_mapping[tk.class] && widget_custom_attribute_mapping[tk.class][attribute_name.to_s]
126
+ if widget_custom_attribute
127
+ widget_custom_attribute[:getter][:invoker].call(@tk, args)
128
+ else
129
+ @tk.send(attribute_name)
130
+ end
122
131
  end
123
132
 
124
133
  def attribute_setter(attribute_name)
125
134
  "#{attribute_name}="
126
135
  end
136
+
137
+ def widget_custom_attribute_mapping
138
+ @widget_custom_attribute_mapping ||= {
139
+ ::Tk::Tile::TCombobox => {
140
+ 'text' => {
141
+ getter: {name: 'text', invoker: lambda { |widget, args| @tk.textvariable&.value }},
142
+ setter: {name: 'text=', invoker: lambda { |widget, args| @tk.textvariable&.value = args.first }},
143
+ },
144
+ },
145
+ }
146
+ end
147
+
148
+ def widget_property_listener_installers
149
+ @tk_widget_property_listener_installers ||= {
150
+ ::Tk::Tile::TCombobox => {
151
+ :text => lambda do |observer|
152
+ if observer.is_a?(Glimmer::DataBinding::ModelBinding)
153
+ model = observer.model
154
+ options_model_property = observer.property_name + '_options'
155
+ @tk.values = model.send(options_model_property) if model.respond_to?(options_model_property)
156
+ end
157
+ @tk.bind('<ComboboxSelected>') {
158
+ observer.call(@tk.textvariable.value)
159
+ }
160
+ end,
161
+ }
162
+ }
163
+ end
164
+
165
+ def add_observer(observer, property_name)
166
+ property_listener_installers = @tk.class.ancestors.map {|ancestor| widget_property_listener_installers[ancestor]}.compact
167
+ widget_listener_installers = property_listener_installers.map{|installer| installer[property_name.to_s.to_sym]}.compact if !property_listener_installers.empty?
168
+ widget_listener_installers.to_a.first&.call(observer)
169
+ end
127
170
 
128
171
  def content(&block)
129
172
  Glimmer::DSL::Engine.add_content(self, Glimmer::DSL::Tk::WidgetExpression.new, &block)
130
173
  end
131
174
 
132
175
  def method_missing(method, *args, &block)
133
- tk_widget.send(method, *args, &block)
176
+ tk.send(method, *args, &block)
134
177
  rescue => e
135
- Glimmer::Config.logger.debug {"Neither WidgetProxy nor #{tk_widget.class.name} can handle the method ##{method}"}
178
+ Glimmer::Config.logger.debug {"Neither WidgetProxy nor #{tk.class.name} can handle the method ##{method}"}
136
179
  super
137
180
  end
138
181
 
139
182
  def respond_to?(method, *args, &block)
140
183
  super ||
141
- tk_widget.respond_to?(method, *args, &block)
184
+ tk.respond_to?(method, *args, &block)
142
185
  end
143
186
  end
144
187
  end
@@ -0,0 +1,40 @@
1
+ require 'glimmer-dsl-tk'
2
+
3
+ class Person
4
+ attr_accessor :country, :country_options
5
+
6
+ def initialize
7
+ self.country_options=["", "Canada", "US", "Mexico"]
8
+ self.country = "Canada"
9
+ end
10
+
11
+ def reset_country
12
+ self.country = "Canada"
13
+ end
14
+ end
15
+
16
+ class HelloCombo
17
+ include Glimmer
18
+
19
+ def launch
20
+ person = Person.new
21
+
22
+ root {
23
+ title 'Hello, Combo!'
24
+
25
+ combobox { |proxy|
26
+ state 'readonly'
27
+ text bind(person, :country)
28
+ }
29
+
30
+ button { |proxy|
31
+ text "Reset Selection"
32
+ command {
33
+ person.reset_country
34
+ }
35
+ }
36
+ }.open
37
+ end
38
+ end
39
+
40
+ HelloCombo.new.launch
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: glimmer-dsl-tk
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
- - andy_maleh
7
+ - AndyMaleh
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-09-11 00:00:00.000000000 Z
11
+ date: 2020-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: glimmer
@@ -24,20 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 0.10.3
27
- - !ruby/object:Gem::Dependency
28
- name: puts_debuggerer
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - "~>"
32
- - !ruby/object:Gem::Version
33
- version: 0.10.0
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - "~>"
39
- - !ruby/object:Gem::Version
40
- version: 0.10.0
41
27
  - !ruby/object:Gem::Dependency
42
28
  name: tk
43
29
  requirement: !ruby/object:Gem::Requirement
@@ -94,20 +80,6 @@ dependencies:
94
80
  - - ">="
95
81
  - !ruby/object:Gem::Version
96
82
  version: '1.0'
97
- - !ruby/object:Gem::Dependency
98
- name: simplecov
99
- requirement: !ruby/object:Gem::Requirement
100
- requirements:
101
- - - ">="
102
- - !ruby/object:Gem::Version
103
- version: '0'
104
- type: :development
105
- prerelease: false
106
- version_requirements: !ruby/object:Gem::Requirement
107
- requirements:
108
- - - ">="
109
- - !ruby/object:Gem::Version
110
- version: '0'
111
83
  - !ruby/object:Gem::Dependency
112
84
  name: jeweler
113
85
  requirement: !ruby/object:Gem::Requirement
@@ -142,6 +114,62 @@ dependencies:
142
114
  - - ">="
143
115
  - !ruby/object:Gem::Version
144
116
  version: 0.2.1
117
+ - !ruby/object:Gem::Dependency
118
+ name: puts_debuggerer
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: 0.10.0
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: 0.10.0
131
+ - !ruby/object:Gem::Dependency
132
+ name: coveralls
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - '='
136
+ - !ruby/object:Gem::Version
137
+ version: 0.8.23
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - '='
143
+ - !ruby/object:Gem::Version
144
+ version: 0.8.23
145
+ - !ruby/object:Gem::Dependency
146
+ name: simplecov
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: 0.16.1
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: 0.16.1
159
+ - !ruby/object:Gem::Dependency
160
+ name: simplecov-lcov
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: 0.7.0
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: 0.7.0
145
173
  description: Glimmer DSL for Tk (Ruby Desktop GUI)
146
174
  email: andy.am@gmail.com
147
175
  executables: []
@@ -156,14 +184,20 @@ files:
156
184
  - README.md
157
185
  - VERSION
158
186
  - lib/glimmer-dsl-tk.rb
187
+ - lib/glimmer/data_binding/tk/widget_binding.rb
159
188
  - lib/glimmer/dsl/tk/attribute_expression.rb
189
+ - lib/glimmer/dsl/tk/bind_expression.rb
190
+ - lib/glimmer/dsl/tk/block_attribute_expression.rb
191
+ - lib/glimmer/dsl/tk/data_binding_expression.rb
160
192
  - lib/glimmer/dsl/tk/dsl.rb
161
193
  - lib/glimmer/dsl/tk/root_expression.rb
162
194
  - lib/glimmer/dsl/tk/widget_expression.rb
195
+ - lib/glimmer/tk/button_proxy.rb
163
196
  - lib/glimmer/tk/frame_proxy.rb
164
197
  - lib/glimmer/tk/notebook_proxy.rb
165
198
  - lib/glimmer/tk/root_proxy.rb
166
199
  - lib/glimmer/tk/widget_proxy.rb
200
+ - samples/hello/hello_combo.rb
167
201
  - samples/hello/hello_tab.rb
168
202
  - samples/hello/hello_world.rb
169
203
  homepage: http://github.com/AndyObtiva/glimmer-dsl-tk