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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0c305f675b946595a08012449c49518559acb8238f486c154b3720c0428b5c88
4
- data.tar.gz: 3f5d4ece86f4c737f7d0ae9cb1f6f235fca33694653c6a760afe33fcc6117692
3
+ metadata.gz: 7e54e98c5ffbe3d68414f3d50103d1d4871ba6a7d9a620b78c57850df8cd7247
4
+ data.tar.gz: 445cccf31afff2be026317731bd08d3cf51fe79fc3ae166ffd75ed7d93291f5e
5
5
  SHA512:
6
- metadata.gz: 42ea536ecec59e08a94f1dd561b614d6eb64f4d44c8c2ac9de8ef4170a3f6399659de613cd08acb52b2fea68524f65e28e578ecdfa64842f6faf8711efc3b076
7
- data.tar.gz: 245ace0d9f7f92efc9071c86b96de8a64b2b624c8194ae5af85c9f74ea77db924028f5cc57505807c7f309d5d8688a0d072e5a50ad40e9e4d0c01ae62dc6b32c
6
+ metadata.gz: 712e885e76e573b302a961e00a21b680d236aa4d63567e57c6d4dd014f3f57cddb82f72259769695bea4f4a7eb3da768153827f0554fe73002605944f6e60701
7
+ data.tar.gz: 93152749050dd124163b074260dc1c587fff26726d157808436922cb99aca5acd2156377b8e32344ee9a53cc686bac5ada5127ee96161e81622e159b2bd999cc
@@ -1,4 +1,4 @@
1
- # Glimmer 0.5.2 Beta (JRuby Desktop UI DSL + Data-Binding)
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.2
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.2'
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, :fixnum, computed_by: [:year_of_birth])
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 :horizontal type.
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
- The 1st example binds the text property of a widget like `label` to the first name of a contact model.
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
- The 2nd example binds the text property of a widget like `label` to the nested street of
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
- The 3rd 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.
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
- The 4th example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
774
+ `text bind(contact, :age, computed_by: :date_of_birth)`
759
775
 
760
- The 5th example demonstrates computed value data binding whereby the value of `name` depends on changes to both `first_name` and `last_name`.
776
+ This example demonstrates computed value data binding whereby the value of `age` depends on changes to `date_of_birth`.
761
777
 
762
- The 6th 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`.
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.level = Logger::DEBUG
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
  ```
@@ -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
- @@logger = Logger.new(STDOUT).tap {|logger| logger.level = Logger::WARN}
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.debug "keyword: " + method_symbol.to_s + " and args: " + args.to_s
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.error e.message
65
+ Glimmer.logger&.error e.message
62
66
  end
63
- Glimmer.logger.debug "#{e.message}\n#{e.backtrace.join("\n")}"
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 :property_type, :binding_options
10
+ attr_reader :binding_options
11
11
 
12
- PROPERTY_TYPE_CONVERTERS = {
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, :undefined, computed_binding_options)
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
- observer_registration = observer.observe(model, property_name)
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 = PROPERTY_TYPE_CONVERTERS[@property_type].call(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.debug "No need to observe property writer: #{property_writer_name}\n#{e.message}\n#{e.backtrace.join("\n")}"
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
- @widget.on_widget_disposed do |dispose_event|
19
- unregister_all_observables
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
- (textual?(args[2]) or args[2].is_a?(Hash))
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
- property_type = args[2] if (args.size == 3) and !args[2].is_a?(Hash)
38
- binding_options = args[2] if args[2].is_a?(Hash)
39
- binding_options = args[3] if args[3].is_a?(Hash)
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.debug "Custom widget #{keyword} styles are: [" + args.inspect + "] and options are: #{options}"
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) and
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
@@ -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.debug "Loading #{expression_class_name(expression_name)}..."
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.debug "Attempting to handle #{keyword}(#{args}) with #{@expression.class.name.split(":").last}"
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.debug "#{@expression.class.name} will handle expression keyword #{keyword} with arguments #{args}"
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.error message
37
+ # Glimmer.logger&.error message
38
38
  raise InvalidKeywordError, message
39
39
  end
40
40
  end
@@ -16,7 +16,7 @@ module Glimmer
16
16
  end
17
17
 
18
18
  def interpret(parent, keyword, *args, &block)
19
- Glimmer.logger.debug "Layout Data args are: #{args.inspect}"
19
+ Glimmer.logger&.debug "Layout Data args are: #{args.inspect}"
20
20
  SWT::LayoutDataProxy.new(parent, args)
21
21
  end
22
22
  end
@@ -18,7 +18,7 @@ module Glimmer
18
18
  end
19
19
 
20
20
  def interpret(parent, keyword, *args, &block)
21
- Glimmer.logger.debug "Layout #{keyword} args are: #{args.inspect}"
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.debug "#{base.name} will handle expression keyword #{keyword} with arguments #{args}"
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.error "tab_item widget may only be used directly under a tab_folder widget!"
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
@@ -16,7 +16,7 @@ module Glimmer
16
16
  end
17
17
 
18
18
  def interpret(parent, keyword, *args, &block)
19
- Glimmer.logger.debug "widget styles are: " + args.inspect
19
+ Glimmer.logger&.debug "widget styles are: " + args.inspect
20
20
  SWT::WidgetProxy.new(keyword, parent, args)
21
21
  end
22
22
  end
@@ -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.debug "keyword starts with on_: #{keyword.start_with?('on_')}"
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.debug "parent is a widget: #{widget_parentage}"
15
- return unless widget_parentage
16
- Glimmer.logger.debug "args are empty?: #{args.empty?}"
17
- return unless args.empty?
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.debug "can add listener? #{result}"
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.debug "#{e.message}\n#{e.backtrace.join("\n")}"
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(attribute_setter(attribute_name), *args)
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.debug e.message
38
- # Glimmer.logger.debug "#{e.message}\n#{e.backtrace.join("\n")}"
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(attribute_setter(attribute_name), *args)
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
@@ -52,7 +52,6 @@ module Glimmer
52
52
  center
53
53
  @swt_widget.open
54
54
  start_event_loop
55
- @display.dispose # TODO consider if it's more performant to reuse instead of disposing
56
55
  end
57
56
  end
58
57
  alias show open
@@ -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.debug("Class #{swt_widget_class} matching #{underscored_widget_name} is not a subclass of org.eclipse.swt.widgets.Widget")
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.debug e.message
138
- # Glimmer.logger.debug("#{e.message}\n#{e.backtrace.join("\n")}")
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.debug e.message
142
- # Glimmer.logger.debug("#{e.message}\n#{e.backtrace.join("\n")}")
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.debug "#{e.message}\n#{e.backtrace.join("\n")}"
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.debug e.message
49
- Glimmer.logger.debug "#{e.message}\n#{e.backtrace.join("\n")}"
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.2
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-12 00:00:00.000000000 Z
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.0.0
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.0.0
96
+ version: 10.1.0
97
97
  - !ruby/object:Gem::Dependency
98
98
  requirement: !ruby/object:Gem::Requirement
99
99
  requirements: