glimmer-dsl-tk 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +96 -8
- data/VERSION +1 -1
- data/lib/glimmer-dsl-tk.rb +2 -2
- data/lib/glimmer/data_binding/tk/widget_binding.rb +28 -0
- data/lib/glimmer/dsl/tk/bind_expression.rb +36 -0
- data/lib/glimmer/dsl/tk/block_attribute_expression.rb +41 -0
- data/lib/glimmer/dsl/tk/data_binding_expression.rb +32 -0
- data/lib/glimmer/dsl/tk/dsl.rb +2 -0
- data/lib/glimmer/dsl/tk/widget_expression.rb +2 -1
- data/lib/glimmer/tk/button_proxy.rb +35 -0
- data/lib/glimmer/tk/notebook_proxy.rb +1 -1
- data/lib/glimmer/tk/root_proxy.rb +2 -2
- data/lib/glimmer/tk/widget_proxy.rb +61 -18
- data/samples/hello/hello_combo.rb +40 -0
- metadata +65 -31
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2f57063e358754b89fbd0a57cb118fb9764595a8965eba5884aeb2988ab2d6b1
|
4
|
+
data.tar.gz: f55e3fc96a9388dd218d7c674cdf1585831c44a3f6412741e0e65f53185e2d98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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.
|
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
|
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 '
|
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 -
|
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 -
|
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.
|
1
|
+
0.0.3
|
data/lib/glimmer-dsl-tk.rb
CHANGED
@@ -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
|
data/lib/glimmer/dsl/tk/dsl.rb
CHANGED
@@ -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?(:
|
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
|
@@ -29,7 +29,7 @@ module Glimmer
|
|
29
29
|
class RootProxy < WidgetProxy
|
30
30
|
|
31
31
|
def initialize(*args)
|
32
|
-
@
|
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::
|
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, :
|
28
|
+
attr_reader :parent_proxy, :tk, :args
|
29
29
|
|
30
30
|
DEFAULT_INITIALIZERS = {
|
31
|
-
'
|
32
|
-
|
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
|
-
@
|
65
|
-
|
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
|
-
@
|
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
|
-
|
114
|
-
|
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
|
-
|
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
|
-
|
176
|
+
tk.send(method, *args, &block)
|
134
177
|
rescue => e
|
135
|
-
Glimmer::Config.logger.debug {"Neither WidgetProxy nor #{
|
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
|
-
|
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.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
7
|
+
- AndyMaleh
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
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
|