glimmer 0.5.2 → 0.5.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.markdown +47 -84
- data/lib/glimmer.rb +11 -7
- data/lib/glimmer/data_binding/model_binding.rb +38 -20
- data/lib/glimmer/data_binding/observable_model.rb +1 -1
- data/lib/glimmer/data_binding/widget_binding.rb +7 -2
- data/lib/glimmer/dsl/bind_expression.rb +4 -13
- data/lib/glimmer/dsl/custom_widget_expression.rb +1 -1
- data/lib/glimmer/dsl/data_binding_expression.rb +2 -3
- data/lib/glimmer/dsl/engine.rb +1 -1
- data/lib/glimmer/dsl/expression_handler.rb +3 -3
- data/lib/glimmer/dsl/layout_data_expression.rb +1 -1
- data/lib/glimmer/dsl/layout_expression.rb +1 -1
- data/lib/glimmer/dsl/static_expression.rb +1 -1
- data/lib/glimmer/dsl/tab_item_expression.rb +1 -1
- data/lib/glimmer/dsl/widget_expression.rb +1 -1
- data/lib/glimmer/dsl/widget_listener_expression.rb +11 -9
- data/lib/glimmer/swt/layout_data_proxy.rb +13 -2
- data/lib/glimmer/swt/layout_proxy.rb +15 -3
- data/lib/glimmer/swt/shell_proxy.rb +0 -1
- data/lib/glimmer/swt/widget_proxy.rb +7 -6
- data/lib/glimmer/ui/custom_widget.rb +4 -4
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e54e98c5ffbe3d68414f3d50103d1d4871ba6a7d9a620b78c57850df8cd7247
|
4
|
+
data.tar.gz: 445cccf31afff2be026317731bd08d3cf51fe79fc3ae166ffd75ed7d93291f5e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 712e885e76e573b302a961e00a21b680d236aa4d63567e57c6d4dd014f3f57cddb82f72259769695bea4f4a7eb3da768153827f0554fe73002605944f6e60701
|
7
|
+
data.tar.gz: 93152749050dd124163b074260dc1c587fff26726d157808436922cb99aca5acd2156377b8e32344ee9a53cc686bac5ada5127ee96161e81622e159b2bd999cc
|
data/README.markdown
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Glimmer 0.5.
|
1
|
+
# Glimmer 0.5.3 Beta (JRuby Desktop UI DSL + Data-Binding)
|
2
2
|
[![Coverage Status](https://coveralls.io/repos/github/AndyObtiva/glimmer/badge.svg?branch=master)](https://coveralls.io/github/AndyObtiva/glimmer?branch=master)
|
3
3
|
|
4
4
|
Glimmer is a native-UI cross-platform desktop development library written in Ruby. Glimmer's main innovation is a JRuby DSL that enables productive and efficient authoring of desktop application user-interfaces while relying on the robust platform-native Eclipse SWT library. Glimmer additionally innovates by having built-in data-binding support to greatly facilitate synchronizing the UI with domain models. As a result, that achieves true decoupling of object oriented components, enabling developers to solve business problems without worrying about UI concerns, or alternatively drive development UI-first, and then write clean business components test-first afterwards.
|
@@ -111,14 +111,14 @@ Please follow these instructions to make the `glimmer` command available on your
|
|
111
111
|
|
112
112
|
Run this command to install directly:
|
113
113
|
```
|
114
|
-
jgem install glimmer -v 0.5.
|
114
|
+
jgem install glimmer -v 0.5.3
|
115
115
|
```
|
116
116
|
|
117
117
|
### Option 2: Bundler
|
118
118
|
|
119
119
|
Add the following to `Gemfile`:
|
120
120
|
```
|
121
|
-
gem 'glimmer', '~> 0.5.
|
121
|
+
gem 'glimmer', '~> 0.5.3'
|
122
122
|
```
|
123
123
|
|
124
124
|
And, then run:
|
@@ -587,6 +587,8 @@ composite {
|
|
587
587
|
# ...
|
588
588
|
```
|
589
589
|
|
590
|
+
If you data-bind any layout properties, when they change, the shell containing their widget re-packs its children (calls `#pack` method automatically) to ensure proper relayout of all widgets.
|
591
|
+
|
590
592
|
Alternatively, a layout may be constructed by following the SWT API for the layout object. For example, a `RowLayout` can be constructed by passing it an SWT style constant (Glimmer automatically accepts symbols (e.g. `:horizontal`) for SWT style arguments like `SWT::HORIZONTAL`.)
|
591
593
|
|
592
594
|
```ruby
|
@@ -643,7 +645,7 @@ shell {
|
|
643
645
|
}
|
644
646
|
label {text "Age: "}
|
645
647
|
label {
|
646
|
-
text bind(@contact, :age, :
|
648
|
+
text bind(@contact, :age, on_write: :to_i, computed_by: [:year_of_birth])
|
647
649
|
layout_data {
|
648
650
|
horizontalAlignment :fill
|
649
651
|
grabExcessHorizontalSpace true
|
@@ -657,9 +659,9 @@ Check out the samples directory for more advanced examples of layouts in Glimmer
|
|
657
659
|
|
658
660
|
**Defaults**:
|
659
661
|
|
660
|
-
Glimmer composites always come with grid_layout by default, but you can still specify explicitly if you'd like to set specific properties on it.
|
662
|
+
Glimmer composites always come with `grid_layout` by default, but you can still specify explicitly if you'd like to set specific properties on it.
|
661
663
|
|
662
|
-
Glimmer shell always comes with fill_layout having
|
664
|
+
Glimmer shell always comes with `fill_layout` having `:horizontal` type.
|
663
665
|
|
664
666
|
This is a great guide for learning more about SWT layouts:
|
665
667
|
|
@@ -726,6 +728,8 @@ composite {
|
|
726
728
|
# ...
|
727
729
|
```
|
728
730
|
|
731
|
+
If you data-bind any layout data properties, when they change, the shell containing their widget re-packs its children (calls `#pack` method automatically) to ensure proper relayout of all widgets.
|
732
|
+
|
729
733
|
**NOTE**: Layout data must never be reused between widgets. Always specify or clone again for every widget.
|
730
734
|
|
731
735
|
This is a great guide for learning more about SWT layouts:
|
@@ -741,25 +745,43 @@ https://help.eclipse.org/2019-12/nftopic/org.eclipse.platform.doc.isv/reference/
|
|
741
745
|
Data-binding is done with `bind` command following widget property to bind and taking model and bindable attribute as arguments.
|
742
746
|
|
743
747
|
Data-binding examples:
|
744
|
-
- `text bind(contact, :first_name)`
|
745
|
-
- `text bind(contact, 'address.street')`
|
746
|
-
- `text bind(contact, 'addresses[1].street')`
|
747
|
-
- `text bind(contact, :age, computed_by: :date_of_birth)`
|
748
|
-
- `text bind(contact, :name, computed_by: [:first_name, :last_name])`
|
749
|
-
- `text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
|
750
748
|
|
751
|
-
|
749
|
+
`text bind(contact, :first_name)`
|
750
|
+
|
751
|
+
This example binds the text property of a widget like `label` to the first name of a contact model.
|
752
752
|
|
753
|
-
|
753
|
+
`text bind(contact, 'address.street')`
|
754
|
+
|
755
|
+
This example binds the text property of a widget like `label` to the nested street of
|
754
756
|
the address of a contact. This is called nested property data binding.
|
755
757
|
|
756
|
-
|
758
|
+
`text bind(contact, 'address.street', on_read: :upcase, on_write: :downcase)`
|
759
|
+
|
760
|
+
This example adds on the one above it by specifying converters on read and write of the model property, like in the case of a `text` widget. The text widget will then displays the street upper case and the model will store it lower case. When specifying converters, read and write operations must be symmetric (to avoid an infinite update loop between the widget and the model since the widget checks first if value changed before updating)
|
761
|
+
|
762
|
+
`text bind(contact, 'address.street', on_read: lambda { |s| s[0..10] })`
|
763
|
+
|
764
|
+
This example also specifies a converter on read of the model property, but via a lambda, which truncates the street to 10 characters only. Note that the read and write operations are assymetric. This is fine in the case of formatting data for a read-only widget like `label`
|
765
|
+
|
766
|
+
`text bind(contact, 'address.street') { |s| s[0..10] }`
|
767
|
+
|
768
|
+
This is a block shortcut version of the syntax above it. It facilitates formatting model data for read-only widgets since it's a very common view concern. It also saves the developer from having to create a separate formatter/presenter for the model when the view can be an active view that handles common simple formatting operations directly.
|
769
|
+
|
770
|
+
`text bind(contact, 'addresses[1].street')`
|
771
|
+
|
772
|
+
This example binds the text property of a widget like `label` to the nested indexed address street of a contact. This is called nested indexed property data binding.
|
757
773
|
|
758
|
-
|
774
|
+
`text bind(contact, :age, computed_by: :date_of_birth)`
|
759
775
|
|
760
|
-
|
776
|
+
This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
|
761
777
|
|
762
|
-
|
778
|
+
`text bind(contact, :name, computed_by: [:first_name, :last_name])`
|
779
|
+
|
780
|
+
This example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
|
781
|
+
|
782
|
+
`text bind(contact, 'profiles[0].name', computed_by: ['profiles[0].first_name', 'profiles[0].last_name'])`
|
783
|
+
|
784
|
+
This example demonstrates nested indexed computed value data binding whereby the value of `profiles[0].name` depends on changes to both nested `profiles[0].first_name` and `profiles[0].last_name`.
|
763
785
|
|
764
786
|
Example from [samples/hello_combo.rb](samples/hello_combo.rb) sample (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
765
787
|
|
@@ -1283,71 +1305,6 @@ shell { |app_shell|
|
|
1283
1305
|
}.open
|
1284
1306
|
```
|
1285
1307
|
|
1286
|
-
### Custom Shells
|
1287
|
-
|
1288
|
-
Custom shells are a kind of custom widgets that have shells only as the body root. They can be self-contained applications that may be opened and hidden/closed independently of the main app.
|
1289
|
-
|
1290
|
-
They may also be chained in a wizard fashion.
|
1291
|
-
|
1292
|
-
Example (you may copy/paste in [`girb`](#girb-glimmer-irb-command)):
|
1293
|
-
|
1294
|
-
```ruby
|
1295
|
-
class WizardStep
|
1296
|
-
include Glimmer::UI::CustomShell
|
1297
|
-
|
1298
|
-
options :number, :step_count
|
1299
|
-
|
1300
|
-
before_body {
|
1301
|
-
@title = "Step #{number}"
|
1302
|
-
}
|
1303
|
-
|
1304
|
-
body {
|
1305
|
-
shell {
|
1306
|
-
text "Wizard - #{@title}"
|
1307
|
-
minimum_size 200, 100
|
1308
|
-
fill_layout :vertical
|
1309
|
-
label(:center) {
|
1310
|
-
text @title
|
1311
|
-
font height: 30
|
1312
|
-
}
|
1313
|
-
if number < step_count
|
1314
|
-
button {
|
1315
|
-
text "Go To Next Step"
|
1316
|
-
on_widget_selected {
|
1317
|
-
body_root.hide
|
1318
|
-
}
|
1319
|
-
}
|
1320
|
-
end
|
1321
|
-
}
|
1322
|
-
}
|
1323
|
-
end
|
1324
|
-
|
1325
|
-
shell { |app_shell|
|
1326
|
-
text "Wizard"
|
1327
|
-
minimum_size 200, 100
|
1328
|
-
@current_step_number = 1
|
1329
|
-
@wizard_steps = 5.times.map { |n|
|
1330
|
-
wizard_step(number: n+1, step_count: 5) {
|
1331
|
-
on_event_hide {
|
1332
|
-
if @current_step_number < 5
|
1333
|
-
@current_step_number += 1
|
1334
|
-
app_shell.hide
|
1335
|
-
@wizard_steps[@current_step_number - 1].open
|
1336
|
-
end
|
1337
|
-
}
|
1338
|
-
}
|
1339
|
-
}
|
1340
|
-
button {
|
1341
|
-
text "Start"
|
1342
|
-
font height: 40
|
1343
|
-
on_widget_selected {
|
1344
|
-
app_shell.hide
|
1345
|
-
@wizard_steps[@current_step_number - 1].open
|
1346
|
-
}
|
1347
|
-
}
|
1348
|
-
}.open
|
1349
|
-
```
|
1350
|
-
|
1351
1308
|
### Miscellaneous
|
1352
1309
|
|
1353
1310
|
#### Video Widget
|
@@ -1527,7 +1484,7 @@ Glimmer comes with a Ruby Logger accessible via `Glimmer.logger`
|
|
1527
1484
|
Its level of logging defaults to `Logger::WARN`
|
1528
1485
|
It may be configured to show a different level of logging as follows:
|
1529
1486
|
```ruby
|
1530
|
-
Glimmer.logger
|
1487
|
+
Glimmer.logger&.level = Logger::DEBUG
|
1531
1488
|
```
|
1532
1489
|
This results in more verbose debugging log to `STDOUT`, which is helpful in troubleshooting Glimmer DSL syntax when needed.
|
1533
1490
|
|
@@ -1582,6 +1539,12 @@ Glimmer simplifies the process for general packaging on the Mac by providing a r
|
|
1582
1539
|
|
1583
1540
|
- Create `Rakefile` in your app root directory
|
1584
1541
|
- Add the following line to it: `require 'glimmer/rake_task'`
|
1542
|
+
- Create a Ruby script under bin to launch your application (e.g. `bin/math_bowling`) with the following content (replacing `'../app/my_application.rb'` with your application path):
|
1543
|
+
```ruby
|
1544
|
+
require 'glimmer/launcher'
|
1545
|
+
require Glimmer::Launcher.swt_jar_file
|
1546
|
+
require_relative '../app/my_application.rb'
|
1547
|
+
```
|
1585
1548
|
|
1586
1549
|
Now, you can run the following command to package your app into a Mac DMG file (using both Warbler and javapackager):
|
1587
1550
|
```
|
data/lib/glimmer.rb
CHANGED
@@ -42,10 +42,14 @@ module Glimmer
|
|
42
42
|
|
43
43
|
# Returns Glimmer logger (standard Ruby logger)
|
44
44
|
def logger
|
45
|
-
unless defined? @@logger
|
46
|
-
|
47
|
-
end
|
48
|
-
@@logger
|
45
|
+
# unless defined? @@logger
|
46
|
+
# @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
|
47
|
+
# end
|
48
|
+
@@logger if defined? @@logger
|
49
|
+
end
|
50
|
+
|
51
|
+
def enable_logging
|
52
|
+
@@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
|
49
53
|
end
|
50
54
|
end
|
51
55
|
|
@@ -54,13 +58,13 @@ module Glimmer
|
|
54
58
|
if method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
|
55
59
|
raise InvalidKeywordError, "Glimmer excluded keyword: #{method_symbol}"
|
56
60
|
end
|
57
|
-
Glimmer.logger
|
61
|
+
Glimmer.logger&.debug "keyword: " + method_symbol.to_s + " and args: " + args.to_s
|
58
62
|
Glimmer::DSL::Engine.interpret(method_symbol, *args, &block)
|
59
63
|
rescue InvalidKeywordError => e
|
60
64
|
if !method_symbol.to_s.match(REGEX_METHODS_EXCLUDED)
|
61
|
-
Glimmer.logger
|
65
|
+
Glimmer.logger&.error e.message
|
62
66
|
end
|
63
|
-
Glimmer.logger
|
67
|
+
Glimmer.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
|
64
68
|
super(method_symbol, *args, &block)
|
65
69
|
end
|
66
70
|
end
|
@@ -7,23 +7,15 @@ module Glimmer
|
|
7
7
|
include Observable
|
8
8
|
include Observer
|
9
9
|
|
10
|
-
attr_reader :
|
10
|
+
attr_reader :binding_options
|
11
11
|
|
12
|
-
|
13
|
-
:undefined => lambda { |value| value },
|
14
|
-
:fixnum => lambda { |value| value.to_i },
|
15
|
-
:array => lambda { |value| value.to_a }
|
16
|
-
}
|
17
|
-
|
18
|
-
def initialize(base_model, property_name_expression, property_type = :undefined, binding_options = nil)
|
19
|
-
property_type = :undefined if property_type.nil?
|
12
|
+
def initialize(base_model, property_name_expression, binding_options = nil)
|
20
13
|
@base_model = base_model
|
21
14
|
@property_name_expression = property_name_expression
|
22
|
-
@property_type = property_type
|
23
15
|
@binding_options = binding_options || {}
|
24
16
|
if computed?
|
25
17
|
@computed_model_bindings = computed_by.map do |computed_by_property_expression|
|
26
|
-
self.class.new(base_model, computed_by_property_expression
|
18
|
+
self.class.new(base_model, computed_by_property_expression)
|
27
19
|
end
|
28
20
|
end
|
29
21
|
end
|
@@ -59,6 +51,30 @@ module Glimmer
|
|
59
51
|
nested_property? ? nested_property_name : property_name_expression
|
60
52
|
end
|
61
53
|
|
54
|
+
def convert_on_read(value)
|
55
|
+
apply_converter(@binding_options[:on_read], value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def convert_on_write(value)
|
59
|
+
apply_converter(@binding_options[:on_write], value)
|
60
|
+
end
|
61
|
+
|
62
|
+
def apply_converter(converter, value)
|
63
|
+
if converter.nil?
|
64
|
+
value
|
65
|
+
elsif converter.is_a?(String) || converter.is_a?(Symbol)
|
66
|
+
if value.respond_to?(converter)
|
67
|
+
value.send(converter)
|
68
|
+
else
|
69
|
+
raise Glimmer::Error, "Unsupported bind converter: #{converter.inspect}"
|
70
|
+
end
|
71
|
+
elsif converter.respond_to?(:call, value)
|
72
|
+
converter.call(value)
|
73
|
+
else
|
74
|
+
raise Glimmer::Error, "Unsupported bind converter: #{converter.inspect}"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
62
78
|
# All nested property names
|
63
79
|
# e.g. property name expression "address.state" gives ['address', 'state']
|
64
80
|
# If there are any indexed property names, this returns indexes as properties.
|
@@ -95,10 +111,6 @@ module Glimmer
|
|
95
111
|
[@binding_options[:computed_by]].flatten.compact
|
96
112
|
end
|
97
113
|
|
98
|
-
def computed_binding_options
|
99
|
-
@binding_options.reject {|k,v| k == :computed_by}
|
100
|
-
end
|
101
|
-
|
102
114
|
def nested_property_observers_for(observer)
|
103
115
|
@nested_property_observers_collection ||= {}
|
104
116
|
unless @nested_property_observers_collection.has_key?(observer)
|
@@ -121,7 +133,10 @@ module Glimmer
|
|
121
133
|
elsif nested_property?
|
122
134
|
add_nested_observers(observer)
|
123
135
|
else
|
124
|
-
|
136
|
+
model_binding_observer = Observer.proc do |new_value|
|
137
|
+
observer.call(evaluate_property)
|
138
|
+
end
|
139
|
+
observer_registration = model_binding_observer.observe(model, property_name)
|
125
140
|
my_registration = observer.registration_for(self)
|
126
141
|
observer.add_dependent(my_registration => observer_registration)
|
127
142
|
end
|
@@ -183,7 +198,7 @@ module Glimmer
|
|
183
198
|
|
184
199
|
def call(value)
|
185
200
|
return if model.nil?
|
186
|
-
converted_value =
|
201
|
+
converted_value = value
|
187
202
|
invoke_property_writer(model, "#{property_name}=", converted_value) unless evaluate_property == converted_value
|
188
203
|
end
|
189
204
|
|
@@ -200,21 +215,24 @@ module Glimmer
|
|
200
215
|
end
|
201
216
|
|
202
217
|
def property_indexed?(property_expression)
|
203
|
-
property_expression.start_with?('[')
|
218
|
+
property_expression.to_s.start_with?('[')
|
204
219
|
end
|
205
220
|
|
206
221
|
def invoke_property_reader(object, property_expression)
|
222
|
+
value = nil
|
207
223
|
if property_indexed?(property_expression)
|
208
224
|
property_method = '[]'
|
209
225
|
property_argument = property_expression[1...-1]
|
210
226
|
property_argument = property_argument.to_i if property_argument.match(/\d+/)
|
211
|
-
object.send(property_method, property_argument)
|
227
|
+
value = object.send(property_method, property_argument)
|
212
228
|
else
|
213
|
-
object.send(property_expression)
|
229
|
+
value = object.send(property_expression)
|
214
230
|
end
|
231
|
+
convert_on_read(value)
|
215
232
|
end
|
216
233
|
|
217
234
|
def invoke_property_writer(object, property_expression, value)
|
235
|
+
value = convert_on_write(value)
|
218
236
|
if property_indexed?(property_expression)
|
219
237
|
property_method = '[]='
|
220
238
|
property_argument = property_expression[1...-2]
|
@@ -76,7 +76,7 @@ module Glimmer
|
|
76
76
|
end
|
77
77
|
rescue => e
|
78
78
|
# ignore writing if no property writer exists
|
79
|
-
Glimmer.logger
|
79
|
+
Glimmer.logger&.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
|
80
80
|
end
|
81
81
|
|
82
82
|
def unregister_dependent_observers(property_name, old_value)
|
@@ -15,8 +15,13 @@ module Glimmer
|
|
15
15
|
@property = property
|
16
16
|
@translator = translator || proc {|value| value}
|
17
17
|
|
18
|
-
|
19
|
-
|
18
|
+
begin
|
19
|
+
@widget.on_widget_disposed do |dispose_event|
|
20
|
+
unregister_all_observables
|
21
|
+
end
|
22
|
+
rescue => e
|
23
|
+
# No Op
|
24
|
+
Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
|
20
25
|
end
|
21
26
|
end
|
22
27
|
def call(value)
|
@@ -11,8 +11,6 @@ module Glimmer
|
|
11
11
|
def can_interpret?(parent, keyword, *args, &block)
|
12
12
|
(
|
13
13
|
keyword == 'bind' and
|
14
|
-
block.nil? and
|
15
|
-
widget?(parent) and
|
16
14
|
(
|
17
15
|
(
|
18
16
|
(args.size == 2) and
|
@@ -21,23 +19,16 @@ module Glimmer
|
|
21
19
|
(
|
22
20
|
(args.size == 3) and
|
23
21
|
textual?(args[1]) and
|
24
|
-
(
|
25
|
-
) ||
|
26
|
-
(
|
27
|
-
(args.size == 4) and
|
28
|
-
textual?(args[1]) and
|
29
|
-
textual?(args[2]) and
|
30
|
-
(args[3].is_a?(Hash))
|
22
|
+
(args[2].is_a?(Hash))
|
31
23
|
)
|
32
24
|
)
|
33
25
|
)
|
34
26
|
end
|
35
27
|
|
36
28
|
def interpret(parent, keyword, *args, &block)
|
37
|
-
|
38
|
-
binding_options =
|
39
|
-
|
40
|
-
DataBinding::ModelBinding.new(args[0], args[1].to_s, property_type, binding_options)
|
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)
|
41
32
|
end
|
42
33
|
end
|
43
34
|
end
|
@@ -18,7 +18,7 @@ module Glimmer
|
|
18
18
|
|
19
19
|
def interpret(parent, keyword, *args, &block)
|
20
20
|
options = args.last.is_a?(Hash) ? args.pop : {}
|
21
|
-
Glimmer.logger
|
21
|
+
Glimmer.logger&.debug "Custom widget #{keyword} styles are: [" + args.inspect + "] and options are: #{options}"
|
22
22
|
UI::CustomWidget.for(keyword).new(parent, *args, options, &block)
|
23
23
|
end
|
24
24
|
|
@@ -14,8 +14,7 @@ module Glimmer
|
|
14
14
|
class DataBindingExpression < Expression
|
15
15
|
def can_interpret?(parent, keyword, *args, &block)
|
16
16
|
args.size == 1 and
|
17
|
-
args[0].is_a?(DataBinding::ModelBinding)
|
18
|
-
widget?(parent)
|
17
|
+
args[0].is_a?(DataBinding::ModelBinding)
|
19
18
|
end
|
20
19
|
|
21
20
|
def interpret(parent, keyword, *args, &block)
|
@@ -26,7 +25,7 @@ module Glimmer
|
|
26
25
|
#TODO make this options observer dependent and all similar observers in widget specific data binding handlers
|
27
26
|
widget_binding.observe(model_binding)
|
28
27
|
# TODO simplify this logic and put it where it belongs
|
29
|
-
parent.add_observer(model_binding, keyword)
|
28
|
+
parent.add_observer(model_binding, keyword) if parent.respond_to?(:add_observer, [model_binding, keyword])
|
30
29
|
end
|
31
30
|
end
|
32
31
|
end
|
data/lib/glimmer/dsl/engine.rb
CHANGED
@@ -27,7 +27,7 @@ module Glimmer
|
|
27
27
|
# TODO rename to dynamic_expressions in the future when supporting static expressions
|
28
28
|
def dynamic_expressions=(expression_names)
|
29
29
|
@dynamic_expression_chain_of_responsibility = expression_names.reverse.reduce(nil) do |last_expresion_handler, expression_name|
|
30
|
-
Glimmer.logger
|
30
|
+
Glimmer.logger&.debug "Loading #{expression_class_name(expression_name)}..."
|
31
31
|
expression = expression_class(expression_name).new
|
32
32
|
expression_handler = ExpressionHandler.new(expression)
|
33
33
|
expression_handler.next = last_expresion_handler if last_expresion_handler
|
@@ -23,9 +23,9 @@ module Glimmer
|
|
23
23
|
# Otherwise, it forwards to the next handler configured via `#next=` method
|
24
24
|
# If there is no handler next, then it raises an error
|
25
25
|
def handle(parent, keyword, *args, &block)
|
26
|
-
Glimmer.logger
|
26
|
+
Glimmer.logger&.debug "Attempting to handle #{keyword}(#{args}) with #{@expression.class.name.split(":").last}"
|
27
27
|
if @expression.can_interpret?(parent, keyword, *args, &block)
|
28
|
-
Glimmer.logger
|
28
|
+
Glimmer.logger&.debug "#{@expression.class.name} will handle expression keyword #{keyword} with arguments #{args}"
|
29
29
|
return @expression
|
30
30
|
elsif @next_expression_handler
|
31
31
|
return @next_expression_handler.handle(parent, keyword, *args, &block)
|
@@ -34,7 +34,7 @@ module Glimmer
|
|
34
34
|
message = "Glimmer keyword #{keyword} with args #{args} cannot be handled"
|
35
35
|
message += " inside parent #{parent.inspect}" if parent
|
36
36
|
message += "! Check the validity of the code."
|
37
|
-
# Glimmer.logger
|
37
|
+
# Glimmer.logger&.error message
|
38
38
|
raise InvalidKeywordError, message
|
39
39
|
end
|
40
40
|
end
|
@@ -18,7 +18,7 @@ module Glimmer
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def interpret(parent, keyword, *args, &block)
|
21
|
-
Glimmer.logger
|
21
|
+
Glimmer.logger&.debug "Layout #{keyword} args are: #{args.inspect}"
|
22
22
|
SWT::LayoutProxy.new(keyword, parent, args)
|
23
23
|
end
|
24
24
|
end
|
@@ -21,7 +21,7 @@ module Glimmer
|
|
21
21
|
if !static_expression.can_interpret?(parent, keyword, *args, &block)
|
22
22
|
raise Error, "Invalid use of Glimmer keyword #{keyword} with args #{args.inspect} under parent #{parent.inspect}"
|
23
23
|
else
|
24
|
-
Glimmer.logger
|
24
|
+
Glimmer.logger&.debug "#{base.name} will handle expression keyword #{keyword} with arguments #{args}"
|
25
25
|
static_expression.interpret(parent, keyword, *args, &block).tap do |ui_object|
|
26
26
|
Glimmer::DSL::Engine.add_content(ui_object, static_expression, &block) unless block.nil?
|
27
27
|
end
|
@@ -17,7 +17,7 @@ module Glimmer
|
|
17
17
|
if parent.swt_widget.is_a?(TabFolder)
|
18
18
|
return true
|
19
19
|
else
|
20
|
-
Glimmer.logger
|
20
|
+
Glimmer.logger&.error "tab_item widget may only be used directly under a tab_folder widget!"
|
21
21
|
end
|
22
22
|
end
|
23
23
|
false
|
@@ -6,17 +6,19 @@ module Glimmer
|
|
6
6
|
include_package 'org.eclipse.swt.widgets'
|
7
7
|
|
8
8
|
def can_interpret?(parent, keyword, *args, &block)
|
9
|
-
Glimmer.logger
|
10
|
-
return unless keyword.start_with?('on_')
|
11
|
-
Glimmer.logger.debug "block exists?: #{!block.nil?}"
|
12
|
-
return unless !block.nil?
|
9
|
+
Glimmer.logger&.debug "keyword starts with on_: #{keyword.start_with?('on_')}"
|
10
|
+
return false unless keyword.start_with?('on_')
|
13
11
|
widget_parentage = widget?(parent)
|
14
|
-
Glimmer.logger
|
15
|
-
return unless widget_parentage
|
16
|
-
Glimmer.logger
|
17
|
-
|
12
|
+
Glimmer.logger&.debug "parent is a widget: #{widget_parentage}"
|
13
|
+
return false unless widget_parentage
|
14
|
+
Glimmer.logger&.debug "block exists?: #{!block.nil?}"
|
15
|
+
raise Glimmer::Error, "Listener is missing block for keyword: #{keyword}" unless block_given?
|
16
|
+
Glimmer.logger&.debug "args are empty?: #{args.empty?}"
|
17
|
+
raise Glimmer::Error, "Invalid listener arguments for keyword: #{keyword}(#{args.inspect})" unless args.empty?
|
18
18
|
result = parent.can_handle_observation_request?(keyword)
|
19
|
-
Glimmer.logger
|
19
|
+
Glimmer.logger&.debug "can add listener? #{result}"
|
20
|
+
raise Glimmer::Error, "Invalid listener keyword: #{keyword}" unless result
|
21
|
+
true
|
20
22
|
end
|
21
23
|
|
22
24
|
def interpret(parent, keyword, *args, &block)
|
@@ -31,7 +31,7 @@ module Glimmer
|
|
31
31
|
begin
|
32
32
|
@swt_layout_data = swt_layout_data_class.new(*args)
|
33
33
|
rescue => e
|
34
|
-
Glimmer.logger
|
34
|
+
Glimmer.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
|
35
35
|
@swt_layout_data = args.first if args.count == 1
|
36
36
|
end
|
37
37
|
@widget_proxy.swt_widget.setLayoutData(@swt_layout_data)
|
@@ -62,12 +62,23 @@ module Glimmer
|
|
62
62
|
|
63
63
|
def set_attribute(attribute_name, *args)
|
64
64
|
args = SWTProxy.constantify_args(args)
|
65
|
-
@swt_layout_data.send(
|
65
|
+
if args.first != @swt_layout_data.send(attribute_getter(attribute_name))
|
66
|
+
@swt_layout_data.send(attribute_setter(attribute_name), *args)
|
67
|
+
@widget_proxy.swt_widget.getShell.pack
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_attribute(attribute_name)
|
72
|
+
@swt_layout_data.send(attribute_getter(attribute_name))
|
66
73
|
end
|
67
74
|
|
68
75
|
def attribute_setter(attribute_name)
|
69
76
|
"#{attribute_name.to_s.camelcase(:lower)}="
|
70
77
|
end
|
78
|
+
|
79
|
+
def attribute_getter(attribute_name)
|
80
|
+
"#{attribute_name.to_s.camelcase(:lower)}"
|
81
|
+
end
|
71
82
|
end
|
72
83
|
end
|
73
84
|
end
|
@@ -34,8 +34,8 @@ module Glimmer
|
|
34
34
|
end
|
35
35
|
swt_layout_class
|
36
36
|
rescue => e
|
37
|
-
Glimmer.logger
|
38
|
-
# Glimmer.logger
|
37
|
+
Glimmer.logger&.debug e.message
|
38
|
+
# Glimmer.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
|
39
39
|
raise e
|
40
40
|
end
|
41
41
|
end
|
@@ -54,7 +54,15 @@ module Glimmer
|
|
54
54
|
|
55
55
|
def set_attribute(attribute_name, *args)
|
56
56
|
apply_property_type_converters(attribute_name, args)
|
57
|
-
@swt_layout.send(
|
57
|
+
if args.first != @swt_layout.send(attribute_getter(attribute_name))
|
58
|
+
@swt_layout.send(attribute_setter(attribute_name), *args)
|
59
|
+
@widget_proxy.swt_widget.getShell.pack
|
60
|
+
# TODO see if pack(true) is needed
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def get_attribute(attribute_name)
|
65
|
+
@swt_layout.send(attribute_getter(attribute_name))
|
58
66
|
end
|
59
67
|
|
60
68
|
def apply_property_type_converters(attribute_name, args)
|
@@ -66,6 +74,10 @@ module Glimmer
|
|
66
74
|
def attribute_setter(attribute_name)
|
67
75
|
"#{attribute_name.to_s.camelcase(:lower)}="
|
68
76
|
end
|
77
|
+
|
78
|
+
def attribute_getter(attribute_name)
|
79
|
+
"#{attribute_name.to_s.camelcase(:lower)}"
|
80
|
+
end
|
69
81
|
end
|
70
82
|
end
|
71
83
|
end
|
@@ -71,7 +71,7 @@ module Glimmer
|
|
71
71
|
widget_custom_attribute[:setter][:invoker].call(@swt_widget, args)
|
72
72
|
else
|
73
73
|
apply_property_type_converters(attribute_name, args)
|
74
|
-
@swt_widget.send(attribute_setter(attribute_name), *args)
|
74
|
+
@swt_widget.send(attribute_setter(attribute_name), *args) unless @swt_widget.send(attribute_getter(attribute_name)) == args.first
|
75
75
|
end
|
76
76
|
end
|
77
77
|
|
@@ -129,17 +129,17 @@ module Glimmer
|
|
129
129
|
swt_widget_name = underscored_widget_name.camelcase(:upper)
|
130
130
|
swt_widget_class = eval(swt_widget_name)
|
131
131
|
unless swt_widget_class.ancestors.include?(org.eclipse.swt.widgets.Widget)
|
132
|
-
Glimmer.logger
|
132
|
+
Glimmer.logger&.debug("Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget")
|
133
133
|
return nil
|
134
134
|
end
|
135
135
|
swt_widget_class
|
136
136
|
rescue NameError => e
|
137
|
-
Glimmer.logger
|
138
|
-
# Glimmer.logger
|
137
|
+
Glimmer.logger&.debug e.message
|
138
|
+
# Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
|
139
139
|
nil
|
140
140
|
rescue => e
|
141
|
-
Glimmer.logger
|
142
|
-
# Glimmer.logger
|
141
|
+
Glimmer.logger&.debug e.message
|
142
|
+
# Glimmer.logger&.debug("#{e.message}\n#{e.backtrace.join("\n")}")
|
143
143
|
nil
|
144
144
|
end
|
145
145
|
|
@@ -325,6 +325,7 @@ module Glimmer
|
|
325
325
|
value
|
326
326
|
end
|
327
327
|
end
|
328
|
+
# TODO consider detecting type on widget method and automatically invoking right converter (e.g. :to_s for String, :to_i for Integer)
|
328
329
|
@property_type_converters ||= {
|
329
330
|
:background => color_converter,
|
330
331
|
:background_image => proc do |value|
|
@@ -35,7 +35,7 @@ module Glimmer
|
|
35
35
|
begin
|
36
36
|
result.const_get(namespace)
|
37
37
|
rescue => e
|
38
|
-
# Glimmer.logger
|
38
|
+
# Glimmer.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
|
39
39
|
result
|
40
40
|
end
|
41
41
|
end
|
@@ -45,8 +45,8 @@ module Glimmer
|
|
45
45
|
raise "#{underscored_custom_widget_name} has no custom widget class!" if custom_widget_class.nil?
|
46
46
|
custom_widget_class if custom_widget_class.ancestors.include?(Glimmer::UI::CustomWidget)
|
47
47
|
rescue => e
|
48
|
-
Glimmer.logger
|
49
|
-
Glimmer.logger
|
48
|
+
Glimmer.logger&.debug e.message
|
49
|
+
Glimmer.logger&.debug "#{e.message}\n#{e.backtrace.join("\n")}"
|
50
50
|
nil
|
51
51
|
end
|
52
52
|
|
@@ -127,7 +127,7 @@ module Glimmer
|
|
127
127
|
|
128
128
|
def handle_observation_request(observation_request, &block)
|
129
129
|
if observation_request.start_with?('on_updated_')
|
130
|
-
property = observation_request.sub(/^on_updated_/, '')
|
130
|
+
property = observation_request.sub(/^on_updated_/, '') # TODO look into eliminating duplication from above
|
131
131
|
add_observer(DataBinding::Observer.proc(&block), property) if can_add_observer?(property)
|
132
132
|
else
|
133
133
|
body_root.handle_observation_request(observation_request, &block)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: glimmer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.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-04-
|
11
|
+
date: 2020-04-14 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
requirement: !ruby/object:Gem::Requirement
|
@@ -85,7 +85,7 @@ dependencies:
|
|
85
85
|
requirements:
|
86
86
|
- - ">="
|
87
87
|
- !ruby/object:Gem::Version
|
88
|
-
version: 10.
|
88
|
+
version: 10.1.0
|
89
89
|
name: rake
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
@@ -93,7 +93,7 @@ dependencies:
|
|
93
93
|
requirements:
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 10.
|
96
|
+
version: 10.1.0
|
97
97
|
- !ruby/object:Gem::Dependency
|
98
98
|
requirement: !ruby/object:Gem::Requirement
|
99
99
|
requirements:
|